Compared to all this qualified covariance quant quaffing recently debuting here simplistic technical balancing strats just don't hold a statistically significant candle. However, they are tractable for the layman (me), and are entertaining to toy with. To wit, this variation uses a technique by which when 59% of the instruments in the group have failed to make their monthly filter -- the whole group gets out. That is, when 41% show a waning market, it's time to exit, stage right.
Also note the rebalance period is 7 days before the EOM. As previously been discussed here, the last week of the month is the critical month in which to perform one's examinations and reassessments.
You'll also note the multiple calls to symbols(). Works.
Also added is a Trailing stop.
Note: By posting the code in the body it become searchable. Code in the attached strats is not.
import talib
DayPeriods = 170
MonthPeriods = 8
TrailingStopPct = 10
def initialize(context):
set_symbol_lookup_date("2015-01-01")
#symbols("IDU","IYC","IYE","IYG","IYH","IYJ","IYK","IYM","IWY","PXI")
symbols("SPY","DIA","QQQ")
symbols("XLP","XLU","XLV","XLY","AGG","XLB","XLE","XLF","XLI","XLK")
symbols("DVY","VIS","VDC","VXF","IYT")
context.hedge = symbol("TLT")
schedule_function(func=CalcIndicators, date_rule=date_rules.month_end(days_offset=7))
schedule_function(func=HandleExits, date_rule=date_rules.month_end(days_offset=7))
schedule_function(func=HandleEntries, date_rule=date_rules.month_end(days_offset=7))
schedule_function(TrailingStop, date_rule=date_rules.every_day())
def handle_data(context, data):
record(PositionCount = len([1 for stock in context.portfolio.positions if context.portfolio.positions[stock].amount > 0] ))
record(SettledCash = context.account.settled_cash)
def HandleEntries(context, data):
eligibleStocks = []
for stock in data:
if (stock == context.hedge): continue
if (data[stock].close_price > data[stock].tenMonthMA):
eligibleStocks.append(stock)
eligibleStocksCount = float(len(eligibleStocks))
if (len(eligibleStocks) <= len(data) * .6):
order_target_percent(context.hedge, .5)
print(">>> EnterHedge {0} @ {1:<7.2f}".format(context.hedge.symbol, data[context.hedge].close_price))
return
for stock in eligibleStocks:
order_target_percent(stock, 1.0 / eligibleStocksCount)
print("HandleEntries {0} @ {1:<7.2f}".format(stock.symbol, data[stock].close_price))
### To here means we've re-entered an up trend, get rid of the hedge
if (context.portfolio.positions[context.hedge].amount):
order_target_percent(context.hedge, 0.0)
print("<<< ExitHedge {0} @ {1:<7.2f}".format(context.hedge.symbol, data[context.hedge].close_price))
def HandleExits(context, data):
inEligibleStocks = []
for stock in data:
if (stock == context.hedge): continue
if (data[stock].close_price < data[stock].tenMonthMA):
inEligibleStocks.append(stock)
if (len(inEligibleStocks) > len(data) * .4):
for stock in data:
if (stock == context.hedge): continue
if (context.portfolio.positions[stock].amount):
order_target_percent(stock, 0.0)
print("HandleExits {0} @ {1:<7.2f}".format(stock.symbol, data[stock].close_price))
def CalcIndicators(context, data):
closes = history(DayPeriods, "1d", "close_price").resample("1m")
closes = closes.dropna()
tenMonthMA = closes.apply(talib.MA, timeperiod=MonthPeriods)
for stock in data:
data[stock].tenMonthMA = tenMonthMA[stock][-1]
record(TenMonthMA = data[stock].tenMonthMA)
record(ClosePrice = data[stock].close_price)
def TrailingStop(context, data):
for stock in data:
if context.portfolio.positions[stock].amount > 0:
offset = (TrailingStopPct * data[stock].close_price) / 100.0
if ('trailingStop' in data[stock]):
if (data[stock].close_price < data[stock].trailingStop):
order_target_percent(stock, 0.0)
print("TrailingStop {0} @ {1:<7.2f}".format(stock.symbol, data[stock].close_price))
del data[stock].trailingStop
continue
else:
data[stock].trailingStop = max(data[stock].trailingStop, data[stock].low - offset)
else:
data[stock].trailingStop = data[stock].low - offset