Overall Statistics
Total Orders
957
Average Win
0.75%
Average Loss
-0.68%
Compounding Annual Return
10.139%
Drawdown
25.000%
Expectancy
0.122
Start Equity
1000000
End Equity
1434577.72
Net Profit
43.458%
Sharpe Ratio
0.331
Sortino Ratio
0.286
Probabilistic Sharpe Ratio
12.571%
Loss Rate
46%
Win Rate
54%
Profit-Loss Ratio
1.09
Alpha
0.017
Beta
0.488
Annual Standard Deviation
0.157
Annual Variance
0.024
Information Ratio
-0.129
Tracking Error
0.158
Treynor Ratio
0.106
Total Fees
$22743.32
Estimated Strategy Capacity
$29000000.00
Lowest Capacity Asset
INTC R735QTJ8XC9X
Portfolio Turnover
22.79%
# region imports
import openai
import pytz
import requests
import json
from datetime import timedelta
from AlgorithmImports import *
# endregion

class JumpingTanManatee(QCAlgorithm):

    def Initialize(self):
        self.SetCash(1000000)
        self.SetStartDate(2021,1,1)
        self.rebalanceTime = datetime.min  
        self.UniverseSettings.Resolution = Resolution.Daily
        self.SetPortfolioConstruction(EqualWeightingPortfolioConstructionModel(portfolioBias=PortfolioBias.Long))
        self.AddRiskManagement(NullRiskManagementModel())
        self.SetExecution(ImmediateExecutionModel())
        self.lastDateSaved = date.min
        self.SetWarmUp(60)
        self.Settings.FreePortfolioValuePercentage = 0.10 #cash buffer to 10%


        self.index = self.AddEquity("IWV", Resolution.Daily)
        self.AddUniverse(self.Universe.ETF(self.index.Symbol, self.UniverseSettings, self.Filter))

    def Filter(self, constituents: List[ETFConstituentData]) -> List[Symbol]:
        if self.Time < self.rebalanceTime:
            return Universe.Unchanged
        self.rebalanceTime = self.Time + timedelta(days=30)

        active_symbols   = [s.Symbol for s in self.ActiveSecurities.Values]
        incoming_symbols = sorted([c  for c in constituents if c.Weight], key=lambda c: c.Weight, reverse=True)[:50]
        incoming_symbols = [c.Symbol for c in incoming_symbols]
        grouped =  sorted(list(set(active_symbols)) + list(incoming_symbols))
        return list(set(active_symbols + incoming_symbols))

    def OnSecuritiesChanged(self, changes):
        for security in changes.AddedSecurities: 
            security.STD = self.STD(security.Symbol, 22)
            security.ATR = self.ATR(security.Symbol, 22)
            security.EMA = self.EMA(security.Symbol, 22)
            identity = self.Identity(security.Symbol)
            identity.Window.Size = 15
            security.History = identity

    def OnData(self, slice: Slice) -> None: 
        if not self.index.History.Window.IsReady:
            return       

        trade_securities = []
        signals = {'symbol': '', 'sentiment': [] }

        for security in self.ActiveSecurities.Values: 
            if not security.History.Window.IsReady:
                continue
    
            if security.Symbol == self.index.Symbol or not hasattr(security, 'STD'):
                continue

            threshold   = 2.5
            std = threshold*security.STD.Current.Value
            avg = security.EMA.Current.Value

            if self.index.History[5] is not None and security.History[5] is not None and security.History[5].Value != 0:
                factor = self.index.History[5].Value/security.History[5].Value
                scaled_stockprice = factor*security.Close
                divergence = 1 - scaled_stockprice/self.index.Close
                divergence = abs(divergence)

                if divergence > 0.03 and divergence < 0.20:
                    if (security.Close < (-std+avg)):
                        trade_securities.append(Insight.Price(security.Symbol, Resolution.Daily, 2, InsightDirection.Up))  
        
        if self.IsWarmingUp:
            return

        self.EmitInsights(trade_securities)