Overall Statistics
Total Trades
290
Average Win
0.11%
Average Loss
-0.11%
Compounding Annual Return
23.103%
Drawdown
1.900%
Expectancy
0.219
Net Profit
3.473%
Sharpe Ratio
2.276
Probabilistic Sharpe Ratio
71.667%
Loss Rate
39%
Win Rate
61%
Profit-Loss Ratio
0.99
Alpha
0.028
Beta
0.24
Annual Standard Deviation
0.08
Annual Variance
0.006
Information Ratio
-3.767
Tracking Error
0.122
Treynor Ratio
0.759
Total Fees
$992.95
Estimated Strategy Capacity
$32000000.00
Lowest Capacity Asset
AMD R735QTJ8XC9X
from datetime import datetime, timedelta


class DynamicOptimizedContainmentField(QCAlgorithm):
    
    def Initialize(self):
        
        self.stopMarketTicket = None
        self.stopMarketOrderFillTime = datetime.min
        self.highestPrice = 0
        self.SetStartDate(2021, 2, 26)  # Set Start Date\
        self.SetEndDate(2021, 4, 26)
        self.SetCash(1000000)  # Set Strategy Cash
        self.UniverseSettings.Resolution = Resolution.Minute
        self.AddUniverse(self.SelectCoarse)
        self.UniverseSettings.Leverage = 2
        self.symbols = {}
        self.Schedule.On(self.DateRules.EveryDay(), self.TimeRules.At(13,30), self.ClosePositions)
        
        self.AddRiskManagement(TrailingStopRiskManagementModel(0.05))
    
    def SelectCoarse(self, coarse):
        sortedByDollarVolume = sorted(coarse, key=lambda c: c.DollarVolume, reverse=True)
        filteredByPrice = [c.Symbol for c in sortedByDollarVolume if c.Price > 10 ]
        return filteredByPrice[:10]
    
    def OnSecuritiesChanged(self, changes):
        
        for security in changes.AddedSecurities:
            symbol = security.Symbol
            if symbol not in self.symbols:
                self.symbols[symbol] = SymbolData(self, symbol)
                
                
        for security in changes.RemovedSecurities:
            symbol = security.Symbol
            if symbol in self.symbols:
                symbolData = self.symbols.pop(symbol, None)
                self.SubscriptionManager.RemoveConsolidator(symbol, symbolData.consolidator)
                
    def OnData(self, data):
        if self.Time.time()<time(11,0) or self.Time.time()>time(11,30):return
        
        for symbol, symbol_data in self.symbols.items():
            
            if symbol_data.openingBar is None: continue
        
            if not data.Bars.ContainsKey(symbol):
                continue
            
            if data.Bars[symbol].Close > symbol_data.openingBar.High and not self.Securities[symbol].Invested:
                quantity = self.CalculateOrderQuantity(symbol, 0.18) # orders 18% of portfolio
                self.MarketOrder(symbol, quantity) 
                continue
                
    def OnOrderEvent(self, orderEvent):
        if orderEvent.Status != OrderStatus.Filled:
            self.symbols[orderEvent.Symbol].openingBar = None
            return
       
                       
    def ClosePositions(self):
        for symbolData in self.symbols.values():
            symbolData.openingBar = None
        self.Liquidate() # liquidate entire portfolio
        
class SymbolData:
    
    def __init__(self, algorithm, symbol):
        
        self.algorithm = algorithm
        self.symbol = symbol
        self.consolidator = TradeBarConsolidator(timedelta(minutes = 30))
        self.consolidator.DataConsolidated += self.OnDataConsolidated
        self.openingBar = None

        algorithm.SubscriptionManager.AddConsolidator(symbol, self.consolidator)
        
    def OnDataConsolidated(self, sender, bar):
        if bar.Time.hour == 9 and bar.Time.minute == 30:
            self.openingBar = bar