Back to Community
Funky flat basket strategy - depends on consistent oscillation
Clone Algorithm
41
Loading...
Backtest from to with initial capital
Total Returns
--
Alpha
--
Beta
--
Sharpe
--
Sortino
--
Max Drawdown
--
Benchmark Returns
--
Volatility
--
Returns 1 Month 3 Month 6 Month 12 Month
Alpha 1 Month 3 Month 6 Month 12 Month
Beta 1 Month 3 Month 6 Month 12 Month
Sharpe 1 Month 3 Month 6 Month 12 Month
Sortino 1 Month 3 Month 6 Month 12 Month
Volatility 1 Month 3 Month 6 Month 12 Month
Max Drawdown 1 Month 3 Month 6 Month 12 Month
import math
import numpy as np
from collections import deque
trendPeriods = 1
dollarUnit = 1000

def initialize(context):
    secMgr = SecurityManager()
    secMgr.Add("XLE", sid(19655), 1) # Energy Select Sector SPDR
    secMgr.Add("XLF", sid(19656), 1) # Financial Select Sector SPDR
    secMgr.Add("XLI", sid(19657), -1) # Industrial Select Sector SPDR
    secMgr.Add("XLK", sid(19658), -1) # Technology Select Sector SPDR
    secMgr.Add("XLP", sid(19659), 1) # Consumer Staples Select Sector SPDR
    secMgr.Add("XLU", sid(19660), 1) # Utilities Select Sector SPDR
    secMgr.Add("XLV", sid(19661), -1) # Healthcare Select Sector SPDR
    secMgr.Add("XLY", sid(19662), -1) # Consumer Discretionary Select Sector SPDR
    
    context.SecMgr = secMgr
    context.period = 0
    context.runningPnL = 0.0
    set_commission(commission.PerTrade(cost=1.0))
    
def handle_data(context, data):
    context.SecMgr.Update(data)    
    context.period += 1
    
    if (context.period < trendPeriods):
        return
    
    if (context.portfolio.pnl - context.runningPnL < (-1 * dollarUnit) or len(context.portfolio.positions) == 0):
        for security in context.SecMgr.GetSecurities():
            if (security.Enabled):
                order_value(security.Sid, security.Direction * 3 * dollarUnit)
        #print("Entry: pnl:{}  running:{}".format(context.portfolio.pnl, context.runningPnL))
        context.runningPnL = context.portfolio.pnl
        
    elif(context.portfolio.pnl - context.runningPnL > (2 * dollarUnit) and context.portfolio.pnl > 0.0):
        for security in context.SecMgr.GetSecurities():
            if (security.Enabled):
                security.Direction = -1 * security.Direction
                order_value(security.Sid, security.Direction * 3 * dollarUnit)
        #print("Reversal: pnl:{}  running:{}".format(context.portfolio.pnl, context.runningPnL))
        context.runningPnL = context.portfolio.pnl
            
    record(PnL=context.portfolio.pnl)
    
################################################################                
class SecurityManager(object):
    '''Class to wrap securities'''

    def __init__(self):
        self.stockList = {}

    def __str__(self):
        toString = "\tSymbols:{0}\n".format(self.stockList.keys())
        return toString 
    
    def Count(self):
        return len(self.GetSecurities())
    
    def Add(self, symbol, sid, portion):
         self.stockList[symbol] = Security(symbol, sid, portion)

    def Update(self, data):
        totalWeight = 0.0
        for sec in self.stockList.values():
            if sec.Sid not in data:
                sec.Weight = 0.0
                sec.Enabled = False
                continue
            sec.UpdatePrices(data)
            sec.SetWeight()
            totalWeight += sec.Weight
            
    def GetSecurities(self):
        return self.stockList.values()

   
#################################################################
class Security(object):
    '''Class to wrap security'''

    def __init__(self, symbol, sid, direction):
        self.Symbol = symbol
        self.Sid = sid
        self.Direction = direction        
        self.Open = deque(maxlen=trendPeriods)
        self.High = deque(maxlen=trendPeriods)
        self.Low = deque(maxlen=trendPeriods)
        self.Close = deque(maxlen=trendPeriods)
        self.Weight = 0.0
        self.Enabled = True
            
    def __str__(self):
        toString = "\tSymbol:{0} weight:{1}\n".format(self.Symbol, self.Weight)
        return toString 
    
    def UpdatePrices(self, data):
        self.Open.append(data[self.Sid].open_price)
        self.High.append(data[self.Sid].high)
        self.Low.append(data[self.Sid].low)
        self.Close.append(data[self.Sid].close_price)
            
    def SetWeight(self):
        pass        
There was a runtime error.
5 responses

Nice idea Anony, and the Sarpe index of 1.18 is truly remarcable!
I'd like to know how did you proceed to identify the basket of securities that result in a stationary group price... did you use some specific algorithm? or just by try and error ?

Thanks to both of you for the answers... the documents form Simon are really challenging... for the moment (next 10-20 years) I think I'll go with the approach from Anony...
Thanks again.

This is kinda interesting:

http://www.bespokeinvest.com/thinkbig/2014/4/28/2014-shifts-in-sp-500-sector-weightings.html

Around 2000, it appears that money shifted toward technology and telecom, at the expense of other sectors. And then we all know what happened... Perhaps the same kind of mean reversion happens, except on a shorter time scale?

I believe this is the rationale behind "equal weighting" indexes and ETFs, like RSP.