Overall Statistics |
Total Trades 96 Average Win 0.00% Average Loss -0.01% Compounding Annual Return -0.053% Drawdown 0.000% Expectancy -0.070 Net Profit -0.008% Sharpe Ratio -1.359 Probabilistic Sharpe Ratio 16.541% Loss Rate 37% Win Rate 63% Profit-Loss Ratio 0.47 Alpha -0.001 Beta 0.001 Annual Standard Deviation 0 Annual Variance 0 Information Ratio 0.343 Tracking Error 0.192 Treynor Ratio -0.728 Total Fees $118.50 |
from collections import defaultdict from dataManager import DataManager from tradeManager import TradeManager from strategy import Strategy class Manager(): def __init__( self, algo ): self._algo = algo self.dataManager = DataManager( algo ) self.tradeManager = TradeManager( algo, self.dataManager ) self.strategy = Strategy(self.tradeManager, self.dataManager ) def addUniverse( self, ticker ): self.dataManager.addNewTicker(ticker) def run( self ): ''' this should run every tick ''' self.dataManager.run() self.tradeManager.run() self.strategy.run()
from manager import Manager class BasicFW(QCAlgorithm): def Initialize(self): self.SetStartDate(2020, 9, 1) #self.SetEndDate(2020, 8, 1) self.SetCash(10000000) resolution = Resolution.Hour self.SetBrokerageModel(BrokerageName.AlphaStreams) self.UniverseSettings.Resolution = resolution self.AddUniverse(self.CoarseSelectionFunction, self.FineSelectionFunction ) self.AddEquity("SPY", resolution) # Consolidate 1min SPY -> 1-Day Bars self.manager = Manager( self ) self.universe = [] def OnData( self, slice ): self.manager.run() def OnSecuritiesChanged(self, changes): #On adding securities, creating SymbolData for each. for security in changes.AddedSecurities: symbol = security.Symbol self.manager.addUniverse( symbol ) def CoarseSelectionFunction( self, coarse ): top = [x for x in coarse if x.HasFundamentalData and 100 > x.Price > 30 and x.Volume > 1000000 and x.DollarVolume > 1000000] return [x.Symbol for x in top] def FineSelectionFunction( self, fine ): top = [x for x in fine if x.OperationRatios.QuickRatio.ThreeMonths >= 1.0 and x.OperationRatios.TotalDebtEquityRatioGrowth.OneYear <= 1.0 and x.ValuationRatios.SustainableGrowthRate > 0.07 and x.ValuationRatios.PEGRatio < 1] return [x.Symbol for x in top][:10]
class TradeManager(): def __init__( self, algo, dataManager, minutesForTrade = 60*3, maxOpenTrades = 5): self._algo = algo self.tradeTime = timedelta( minutes = minutesForTrade ) self.tradeDetails = {} self._dataManager = dataManager self._maxOpenTrades = maxOpenTrades def openTrade( self, ticker, numOfShares, sl = None, tp = None ): numOfTrades = sum( 1 for inst in self._algo.Portfolio if inst.Value.Invested ) if numOfTrades < self._maxOpenTrades: self._algo.MarketOrder(ticker, numOfShares ) self.tradeDetails[ ticker ] = { 'time' : self._algo.Time, 'sl' : sl, 'tp' : tp } def run( self ): ''' this should run every tick ''' self.manageOpenTrades() def closeTrade( self, ticker ): self._algo.Liquidate( ticker ) self.tradeDetails.pop( ticker ) def rollingWindow( self, ticker ): return self._dataManager.getData( ticker ) def manageOpenTrades( self ): for inst in self._algo.Portfolio: symbol = inst.Value.Symbol if inst.Value.Invested and symbol in self.tradeDetails: #Time limit if self.tradeDetails[ symbol ]['time'] + self.tradeTime < self._algo.Time: self.closeTrade( symbol ) if symbol in self.tradeDetails: # Long position if inst.Value.Quantity > 0: if self.tradeDetails[ symbol ]['sl'] is not None and self.rollingWindow( symbol )[0].Close <= self.tradeDetails[ symbol ][ 'sl' ] : self.closeTrade( symbol ) elif self.tradeDetails[ symbol ]['tp'] is not None and self.rollingWindow( symbol )[0].Close >= self.tradeDetails[ symbol ][ 'tp' ] : self.closeTrade( symbol ) # short position if inst.Value.Quantity < 0: if self.tradeDetails[ symbol ]['sl'] is not None and self.rollingWindow( symbol )[0].Close >= self.tradeDetails[ symbol ][ 'sl' ] : self.closeTrade( symbol ) elif self.tradeDetails[ symbol ]['tp'] is not None and self.rollingWindow( symbol )[0].Close <= self.tradeDetails[ symbol ][ 'tp' ] : self.closeTrade( symbol )
from collections import defaultdict import statistics DATA_SIZE = 60*10 class DataManager(): def __init__( self, algo, minutesToStore = 60*10 ): self._algo = algo self._dataTime = timedelta( minutes = minutesToStore ) #for how long to store the data self._data = defaultdict( lambda : RollingWindow[TradeBar]( DATA_SIZE ) ) now = self._algo.Time self._dataStartTime = defaultdict( lambda : now ) def run( self ): ''' this should run every tick ''' self.collectSingleBars() self.removeStaleData() def getData( self, ticker ): if ticker in self._data: return self._data[ ticker ] def getAllTickers( self ): return list( self._data.keys() ) def collectSingleBars( self ): for ticker in self._data.keys(): if not self._algo.CurrentSlice.ContainsKey( ticker ) or self._algo.CurrentSlice[ ticker ] is None: #self._algo.Debug(ticker.Value + ' Not available') continue self._data[ ticker ].Add( self._algo.CurrentSlice[ ticker ] ) def addNewTicker( self, ticker ): self._dataStartTime[ ticker ] = self._algo.Time if self._data[ ticker ].Count == 0: df = self._algo.History( ticker, DATA_SIZE, Resolution.Minute) for barTuple in df.itertuples(): data = barTuple[1] tradeBar = TradeBar() tradeBar.Close = barTuple.close tradeBar.Open = barTuple.open tradeBar.High = barTuple.high tradeBar.Low = barTuple.low tradeBar.Volume = getattr( barTuple, 'volume', 0 ) tradeBar.Time = barTuple.Index[1] tradeBar.Symbol = barTuple.Index[0] tradeBar.Period = Resolution.Minute self._data[ ticker ].Add( tradeBar ) def removeStaleData( self ): #return toRemove = [] for ticker, startTime in self._dataStartTime.items(): if startTime + self._dataTime < self._algo.Time and self._algo.Portfolio[ ticker ].Invested == False : toRemove.append( ticker) for ticker in toRemove: self._dataStartTime.pop( ticker, None ) self._data.pop( ticker, None )
from dataManager import DataManager from tradeManager import TradeManager class Strategy(): def __init__( self, tradeManager, dataManager ): self._tradeManager = tradeManager self._dataManager = dataManager def run( self ): ''' this should run every tick ''' self.openTradeBySignal() def rollingWindow( self, ticker ): return self._dataManager.getData( ticker ) def openTradeBySignal( self ): winSize = 15 for ticker in self._dataManager.getAllTickers(): lowest = lambda x : min( self.rollingWindow( ticker )[i].Low for i in range( x * winSize, (x+1) * winSize ) ) lastClose = self.rollingWindow( ticker )[0].Close if lowest(30) < lastClose < lowest(10) : self._tradeManager.openTrade( ticker, 100, sl = lowest(30) , tp = lowest(10) )