Overall Statistics
Total Trades
128
Average Win
43.74%
Average Loss
-4.67%
Compounding Annual Return
81.908%
Drawdown
83.600%
Expectancy
1.755
Net Profit
732.817%
Sharpe Ratio
1.333
Probabilistic Sharpe Ratio
46.398%
Loss Rate
73%
Win Rate
27%
Profit-Loss Ratio
9.37
Alpha
0.579
Beta
0.873
Annual Standard Deviation
0.654
Annual Variance
0.427
Information Ratio
1.132
Tracking Error
0.473
Treynor Ratio
0.999
Total Fees
$118108.51
Estimated Strategy Capacity
$1400000.00
Lowest Capacity Asset
BTCUSD 2MN
#region imports
from AlgorithmImports import *
#endregion
class VerticalNadionShield(QCAlgorithm):

    def Initialize(self):
        self.SetStartDate(2019, 1, 1)  # Set Start Date
        self.SetCash(100000)  # Set Strategy Cash
        self.leverage = 1
        self.SetBrokerageModel(BrokerageName.FTX, AccountType.Margin)
        self.AddRiskManagement(MaximumDrawdownPercentPerSecurity(0.05))
    
        self.cryptos = ["ETHUSD", "BTCUSD"]
        self.cryptoCombinedMomentum = {}
        
        for crypto in self.cryptos:
            self.AddCrypto(crypto, Resolution.Hour).Symbol
            self.Securities[crypto].SetDataNormalizationMode(DataNormalizationMode.TotalReturn)
            self.cryptoCombinedMomentum[crypto] = CombinedMomentum(self, crypto)
            
        self.SetWarmUp(125)

    def shiftAssets(self, target):
        if not (self.Portfolio[target].Invested):
            for symbol in self.Portfolio.Keys:
                self.Liquidate(symbol)
            if not self.Portfolio.Invested:
                for symbol in self.Portfolio.Keys:
                    self.MarketOrder(symbol, self.CalculateOrderQuantity(symbol, 1 * self.leverage))

    def getMonthLastTradingDay(self):
        month_last_day = DateTime(self.Time.year, self.Time.month, DateTime.DaysInMonth(self.Time.year, self.Time.month))
        tradingDays = self.TradingCalendar.GetDaysByType(TradingDayType.BusinessDay, DateTime(self.Time.year, self.Time.month, 1), month_last_day)
        tradingDays = [day.Date.date() for day in tradingDays]
        return tradingDays[-1]
        
    def OnData(self, data):
        if self.IsWarmingUp:
            return
        self.Schedule.On(self.DateRules.EveryDay(), self.TimeRules.At(15,55,0), Action(self.Rebalance))

    def Rebalance(self):
        
        topEquities = sorted(self.cryptoCombinedMomentum.items(), key=lambda x: x[1].getValue(), reverse=True)
        if (topEquities[0][1].getValue() > 0):
            self.shiftAssets(topEquities[0][0])
            #self.shiftAssets(topEquities[0][1])
        else:
            self.Liquidate()        

class CombinedMomentum():
    def __init__(self, algo, symbol):
        self.fst = algo.MOMP(symbol,  30, Resolution.Daily)
        #self.med = algo.MOMP(symbol,  90, Resolution.Daily)
        #self.slw = algo.MOMP(symbol,  180, Resolution.Daily)
        
    def getValue(self):
        #value = (self.fst.Current.Value + self.med.Current.Value + self.slw.Current.Value) / 3
        value = (self.fst.Current.Value)
        return value