Overall Statistics
Total Trades
288
Average Win
0.09%
Average Loss
-0.17%
Compounding Annual Return
28.767%
Drawdown
12.100%
Expectancy
0.293
Net Profit
27.967%
Sharpe Ratio
1.113
Sortino Ratio
1.549
Probabilistic Sharpe Ratio
66.904%
Loss Rate
16%
Win Rate
84%
Profit-Loss Ratio
0.55
Alpha
0
Beta
0
Annual Standard Deviation
0.135
Annual Variance
0.018
Information Ratio
1.5
Tracking Error
0.135
Treynor Ratio
0
Total Fees
$454.03
Estimated Strategy Capacity
$13000000.00
Lowest Capacity Asset
SGEN S2TCB9V1OIG5
Portfolio Turnover
0.53%
# region imports
from AlgorithmImports import *
# endregion


TICKER = "QQQ"

class ETF_Debug(QCAlgorithm):
    def Initialize(self):
        self.SetStartDate(2023, 1, 8)  # Set Start Date
        self.SetEndDate(2024, 1, 1)
        self.SetCash(1000000)  # Set Strategy Cash
        
        self.SetBrokerageModel(BrokerageName.InteractiveBrokersBrokerage, AccountType.Margin)
        self.SetSecurityInitializer(self.CustomSecurityInitializer)

        self.UniverseSettings.Resolution = Resolution.Minute
        self.AddUniverse(self.Universe.ETF(TICKER, Market.USA, self.UniverseSettings, self.ETFConstituentsFilter))
        self.Schedule.On(self.DateRules.EveryDay(), self.TimeRules.At(9, 30), self.CheckSecurities)
        self.Schedule.On(self.DateRules.EveryDay(), self.TimeRules.At(15,30), self.Rebalance)
        self.selected = []

    def CustomSecurityInitializer(self, security: Security):
        seeder = FuncSecuritySeeder(self.GetLastKnownPrices)
        seeder.SeedSecurity(security)

    def ETFConstituentsFilter(self, constituents: List[ETFConstituentData]) -> List[Symbol]:
        for c in constituents:
            if c.Weight is None:
                self.Log(f"{c.Symbol} weight is None, skipping!")
        if len(list(constituents)) > 100:
            self.Log(f"{len(list(constituents))} constituents in filter!")            
        self.selected = [c.Symbol for c in constituents]
        return self.selected

    def OnSecuritiesChanged(self, changes: SecurityChanges) -> None:
        if 0 < len(changes.AddedSecurities) < 90:
            line = ", ".join([f"{x.Symbol.Value}" for x in changes.AddedSecurities])
            self.Log(f"{line} were added to universe")                

        if len(changes.RemovedSecurities) > 0:
            line = ", ".join([f"{x.Symbol.Value}" for x in changes.RemovedSecurities])
            self.Log(f"{line} were removed from universe")                
    
    def CheckSecurities(self):
        if self.ActiveSecurities.Count > 0 and self.ActiveSecurities.Count > 100:
            self.Log(f"Active securities count = {self.ActiveSecurities.Count}!")
        for s in self.selected:
            if s not in self.ActiveSecurities:
                self.Log(f"{s.Value} is not in ActiveSecurities!")

    def Rebalance(self):
        if self.Securities.Count == 0 or len(self.selected) == 0:
            return
        
        if self.Securities.Values[0].Exchange.DateIsOpen(self.Time+timedelta(1)):
            return

        targets = [PortfolioTarget(x, 1/len(self.selected)) for x in self.selected]
        self.SetHoldings(targets, liquidateExistingHoldings=True)