Overall Statistics
Total Orders
3835
Average Win
0.23%
Average Loss
-0.30%
Compounding Annual Return
12.925%
Drawdown
37.000%
Expectancy
0.144
Start Equity
100000
End Equity
212465.23
Net Profit
112.465%
Sharpe Ratio
0.434
Sortino Ratio
0.476
Probabilistic Sharpe Ratio
7.568%
Loss Rate
35%
Win Rate
65%
Profit-Loss Ratio
0.76
Alpha
0.019
Beta
0.831
Annual Standard Deviation
0.199
Annual Variance
0.04
Information Ratio
0.037
Tracking Error
0.145
Treynor Ratio
0.104
Total Fees
$4597.53
Estimated Strategy Capacity
$3400000.00
Lowest Capacity Asset
SAI TMRWYKAE84TH
Portfolio Turnover
7.24%
from AlgorithmImports import *

class conservative_reblancing(AlphaModel):

    def __init__(self, benchmark, v_lookback, m_lookback):
        self.benchmark = benchmark
        self.v_lookback = v_lookback
        self.m_lookback = m_lookback
        self.symbols = []
        self.month = -1

    def on_securities_changed(self, algorithm, changes):
        for added in changes.added_securities:
            self.symbols.append(added.symbol)
            # algorithm.Log(f"Added {added.symbol} to universe")

        for removed in changes.removed_securities:
            symbol = removed.symbol
            if symbol in self.symbols:
                self.symbols.remove(symbol)
                # algorithm.Log(f"Removed {symbol} from universe")

    def update(self, algorithm, data):
        # algorithm.Debug(f"Update method called for month {algorithm.time.month}, universe size: {len(self.symbols)}")
        if algorithm.time.month == self.month: return []
        self.month = algorithm.time.month

        # Initialize the data
        alphas = dict()

        # Fetch indicator data
        for symbol in self.symbols:

            # Create the indicators
            roc = algorithm.roc(symbol, 1, Resolution.Daily)
            std = algorithm.std(symbol, self.v_lookback, Resolution.DAILY)
            momp = algorithm.momp(symbol, self.m_lookback, Resolution.DAILY)

            # Get historical data for warm-up
            history = algorithm.History(symbol, max(self.v_lookback, self.m_lookback) + 10, Resolution.DAILY)
            # algorithm.Log(f"History size for {symbol}: {len(history)}")
            # Warm up the indicators
            for idx, row in history.loc[symbol].iterrows():
                roc.Update(idx, row["close"])
                std.Update(idx, roc.current.value)
                momp.Update(idx, row["close"])

            # Compute the rank value
            alphas[symbol] = max(momp.Current.Value / std.Current.Value, 0)
            # algorithm.Log(f"Processing symbol {symbol} with alpha value: {alphas[symbol]}")

        # Rank the symbol by the value of mom/vol
        selected = sorted(alphas.items(), key=lambda x: x[1], reverse=True)[:5]
        selected_symbols = [x[0] for x in selected]
        # algorithm.Debug(f"Selected symbols at {algorithm.Time}: {', '.join([str(symbol) for symbol in selected_symbols])}")        
        return [
            Insight.price(symbol, Expiry.END_OF_MONTH, InsightDirection.UP) for symbol in selected_symbols
        ]
#region imports
from AlgorithmImports import *
from universe import *
from alpha import *
#endregion

class ConservativeApgorithm(QCAlgorithm):

    def initialize(self):
        self.set_start_date(2018, 1, 1)
        self.set_end_date(2024, 5, 1)
        self.set_cash(100000)            
        # Set number days to trace back
        v_lookback = self.get_parameter("v_lookback", 36)
        m_lookback = self.get_parameter("m_lookback", 12)
        
        self.set_brokerage_model(BrokerageName.INTERACTIVE_BROKERS_BROKERAGE, AccountType.MARGIN)

        # SPY 500 companies
        spy = self.add_equity("SPY",
            resolution = self.universe_settings.resolution,
            data_normalization_mode = self.universe_settings.data_normalization_mode).symbol
        self.set_benchmark(spy)

        # # DOW 30 Companies
        # dia = self.add_equity("DIA",
        #     resolution = self.universe_settings.resolution,
        #     data_normalization_mode = self.universe_settings.data_normalization_mode).symbol
        # self.set_benchmark(dia)
        
        self.set_universe_selection(etf_constituents_universe(spy, self.universe_settings))
        self.add_alpha(conservative_reblancing(spy, v_lookback, m_lookback))
        self.Settings.RebalancePortfolioOnInsightChanges = False
        self.Settings.RebalancePortfolioOnSecurityChanges = False
        self.set_portfolio_construction(EqualWeightingPortfolioConstructionModel())
        self.set_risk_management(NullRiskManagementModel())
        self.set_execution(ImmediateExecutionModel())
from AlgorithmImports import *

class etf_constituents_universe(ETFConstituentsUniverseSelectionModel):
    def __init__(self, benchmark, universe_settings: UniverseSettings = None) -> None:
        super().__init__(benchmark, universe_settings, self.etf_constituents_filter)

    def etf_constituents_filter(self, constituents):
        return [c.symbol for c in constituents]