Overall Statistics
Total Trades
954
Average Win
0.14%
Average Loss
-0.05%
Compounding Annual Return
89.283%
Drawdown
5.700%
Expectancy
0.250
Net Profit
5.692%
Sharpe Ratio
3.167
Probabilistic Sharpe Ratio
69.047%
Loss Rate
68%
Win Rate
32%
Profit-Loss Ratio
2.93
Alpha
0.557
Beta
0.61
Annual Standard Deviation
0.188
Annual Variance
0.035
Information Ratio
3.022
Tracking Error
0.176
Treynor Ratio
0.976
Total Fees
$1507.25
Estimated Strategy Capacity
$6700000.00
Lowest Capacity Asset
MSFT R735QTJ8XC9X
# region imports
from AlgorithmImports import *
# endregion

class JumpingMagentaKitten(QCAlgorithm):

    def Initialize(self) -> None:
        self.SetStartDate(2021, 1, 1)  # Set Start Date
        self.SetEndDate(2021, 2, 1)    # Set End Date
        self.SetCash(100000)  # Set Strategy Cash

        [self.AddEquity(x) for x in ["AAPL", "MSFT"]]
        
        self.AddAlpha(CustomAlpha())
        self.SetPortfolioConstruction(EqualWeightingPortfolioConstructionModel())

class CustomAlpha(AlphaModel):

    def __init__(self):
        self.symbol_data = {}
    
    def Update(self, algorithm, data):
        insight = []

        for symbol, symbol_data in self.symbol_data.items():
            if not data.ContainsKey(symbol): continue

            sma = symbol_data.SMA
            if sma.IsReady and sma.Current.Value > data[symbol].Close:
                insight.append(Insight.Price(symbol, timedelta(minutes=1), InsightDirection.Up))

        return insight

    def OnSecuritiesChanged(self, algorithm, changes):
        for added in changes.AddedSecurities:
            symbol = added.Symbol
            self.symbol_data[symbol] = SymbolData(algorithm, symbol)

        for removed in changes.RemovedSecurities:
            symbol_data = self.symbol_data.pop(removed.Symbol, None)
            if symbol_data:
                symbol_data.Dispose()

class SymbolData:

    def __init__(self, algo, symbol):
        self.algo = algo
        self.symbol = symbol

        # Let's say we have a 10-SMA strategy of 30-minute bar
        self.SMA = SimpleMovingAverage(10)
        
        self.consolidator = TradeBarConsolidator(timedelta(minutes=30))
        self.consolidator.DataConsolidated += self.ConsolidationHandler

        # Register the consolidator for automatic updates
        algo.SubscriptionManager.AddConsolidator(symbol, self.consolidator)

        # Warmup SMA indicator
        history = algo.History(symbol, 300, Resolution.Minute)
        for row in history.itertuples():
            trade_bar = TradeBar(row.Index[1], symbol, row.open, row.high, row.low, row.close, row.volume)
            self.consolidator.Update(trade_bar)

    def ConsolidationHandler(self, sender, bar):
        self.SMA.Update(bar.EndTime, bar.Close)

    # Dispose method to release memory from indicator and remove consolidator from consuming resources
    def Dispose(self):
        self.SMA.reset()
        self.algo.SubscriptionManager.RemoveConsolidator(self.symbol, self.consolidator)