SMA Percent Bands for Close-Based Trend Signals
Summary
This strategy places an upper and lower threshold around a simple moving average (SMA). The thresholds are formed by multiplying the SMA by one plus or minus a chosen band percentage. A close above the upper band triggers a buy, while a close below the lower band triggers a sell. The example describes a 200-period SMA and a 5% band, but the implementation’s defaults use a 200-period average and a zero-width band, so users must set the band explicitly to create a gap around the average.
The code tracks whether the price is above or below the indicator’s regime and enters when that regime changes from negative to positive; it exits when the regime turns negative or at the end of the data. No performance results, transaction cost assumptions, or stop-loss rules are provided. The document therefore explains a basic signal and its implementation, but does not establish its profitability or how it behaves across markets and parameter choices.
Key ideas
- An upper and lower threshold are created by scaling an SMA by a percentage.
- A close above the upper threshold initiates a long position.
- A close below the lower threshold changes the regime and closes an open position.
- The default band is zero, so a nonzero band must be selected to create separated thresholds.
- The document gives no backtest evidence or explicit protective exit rules.
Tags
Full text
# sma-percent-band
# sma-percent-band
The SMA percent band stategy.
Create a percent band around a SMA. For example, if the SMA is 200
and the percent band is 5%, then multiply the 200 by 1.05 and 0.95
to create the upper and lower band, respectively. Buy if the price
closes above the upper band and sell if the price closes below the
lower band.
## Source (MIT)
```python
"""
The SMA percent band stategy.
Create a percent band around a SMA. For example, if the SMA is 200
and the percent band is 5%, then multiply the 200 by 1.05 and 0.95
to create the upper and lower band, respectively. Buy if the price
closes above the upper band and sell if the price closes below the
lower band.
"""
import pinkfish as pf
default_options = {
'use_adj' : False,
'use_cache' : True,
'sma' : 200,
'band' : 0
}
class Strategy:
def __init__(self, symbol, capital, start, end, options=default_options):
self.symbol = symbol
self.capital = capital
self.start = start
self.end = end
self.options = options.copy()
self.ts = None
self.rlog = None
self.tlog = None
self.dbal = None
self.stats = None
def _algo(self):
pf.TradeLog.cash = self.capital
for i, row in enumerate(self.ts.itertuples()):
date = row.Index.to_pydatetime()
close = row.close;
end_flag = pf.is_last_row(self.ts, i)
# Buy
if self.tlog.shares == 0:
if row.regime > 0 and self.ts['regime'].iloc[i-1] < 0:
self.tlog.buy(date, close)
# Sell
else:
if row.regime < 0 or end_flag:
self.tlog.sell(date, close)
# Record daily balance.
self.dbal.append(date, close)
def run(self):
# Fetch and select timeseries
self.ts = pf.fetch_timeseries(self.symbol, use_cache=self.options['use_cache'])
self.ts = pf.select_tradeperiod(self.ts, self.start, self.end,
use_adj=self.options['use_adj'])
# Add technical indicator: sma regime filter
self.ts['regime'] = \
pf.CROSSOVER(self.ts, timeperiod_fast=1, timeperiod_slow=self.options['sma'],
band=self.options['band'])
# Finalize timeseries
self.ts, self.start = pf.finalize_timeseries(self.ts, self.start,
dropna=True, drop_columns=['open', 'high', 'low'])
self.tlog = pf.TradeLog(self.symbol)
self.dbal = pf.DailyBal()
self._algo()
self._get_logs()
self._get_stats()
def _get_logs(self):
self.rlog = self.tlog.get_log_raw()
self.tlog = self.tlog.get_log()
self.dbal = self.dbal.get_log(self.tlog)
def _get_stats(self):
self.stats = pf.stats(self.ts, self.tlog, self.dbal, self.capital)
```Shown in full with attribution under the source's licence. Licence: MIT
This summary was written by Stratmill's research agent from the original; it is not a copy of the source.