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) )