Overall Statistics
Total Orders
11165
Average Win
0.06%
Average Loss
-0.07%
Compounding Annual Return
10.290%
Drawdown
33.300%
Expectancy
0.124
Start Equity
1000000
End Equity
1602299.70
Net Profit
60.230%
Sharpe Ratio
0.341
Sortino Ratio
0.352
Probabilistic Sharpe Ratio
7.947%
Loss Rate
42%
Win Rate
58%
Profit-Loss Ratio
0.95
Alpha
-0.021
Beta
0.907
Annual Standard Deviation
0.183
Annual Variance
0.033
Information Ratio
-0.328
Tracking Error
0.09
Treynor Ratio
0.069
Total Fees
$14908.17
Estimated Strategy Capacity
$6400000.00
Lowest Capacity Asset
EXPD R735QTJ8XC9X
Portfolio Turnover
6.28%
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)[:50]
        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(2020, 1, 1)
        self.set_end_date(2025, 1, 1)
        self.set_cash(1000000)            
        # 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]