Overall Statistics
Total Orders
209
Average Win
2.06%
Average Loss
-2.31%
Compounding Annual Return
35.059%
Drawdown
27.200%
Expectancy
0.534
Start Equity
100000
End Equity
449623.45
Net Profit
349.623%
Sharpe Ratio
1.046
Sortino Ratio
1.343
Probabilistic Sharpe Ratio
61.586%
Loss Rate
19%
Win Rate
81%
Profit-Loss Ratio
0.89
Alpha
0.162
Beta
0.9
Annual Standard Deviation
0.206
Annual Variance
0.043
Information Ratio
0.957
Tracking Error
0.163
Treynor Ratio
0.24
Total Fees
$282.93
Estimated Strategy Capacity
$430000000.00
Lowest Capacity Asset
XLK RGRPZX100F39
Portfolio Turnover
1.69%
Drawdown Recovery
541
from AlgorithmImports import *
from datetime import timedelta


class TradingCompetitionAlgorithm(QCAlgorithm):

    def initialize(self) -> None:
        self.set_cash(100000)
        self.set_start_date(self.end_date - timedelta(5 * 365))

        self._symbols = []
        self._mom_3m = {}
        self._mom_6m = {}
        self._mom_12m = {}
        self._sma_200 = {}

        tickers = [
            "SPY",
            "QQQ",
            "XLK",
            "SMH",
            "NVDA",
            "MSFT",
            "AAPL",
            "LLY",
            "COST",
            "UNH",
            "GLD",
            "SHY"
        ]

        for ticker in tickers:
            security = self.add_equity(ticker, Resolution.DAILY)
            security.set_data_normalization_mode(DataNormalizationMode.ADJUSTED)

            symbol = security.symbol
            self._symbols.append(symbol)

            self._mom_3m[symbol] = self.roc(symbol, 63, Resolution.DAILY)
            self._mom_6m[symbol] = self.roc(symbol, 126, Resolution.DAILY)
            self._mom_12m[symbol] = self.roc(symbol, 252, Resolution.DAILY)
            self._sma_200[symbol] = self.sma(symbol, 200, Resolution.DAILY)

        self.set_warm_up(260, Resolution.DAILY)

        self.schedule.on(
            self.date_rules.month_start("SPY"),
            self.time_rules.at(8, 0),
            self.rebalance
        )

    def on_warmup_finished(self) -> None:
        self.rebalance()

    def rebalance(self) -> None:
        if self.is_warming_up:
            return

        ranked_symbols = []

        for symbol in self._symbols:
            security = self.securities[symbol]
            mom_3m = self._mom_3m[symbol]
            mom_6m = self._mom_6m[symbol]
            mom_12m = self._mom_12m[symbol]
            sma_200 = self._sma_200[symbol]

            if not security.has_data:
                continue

            if security.price <= 0:
                continue

            if not mom_3m.is_ready or not mom_6m.is_ready or not mom_12m.is_ready or not sma_200.is_ready:
                continue

            if security.price < sma_200.current.value and symbol.value != "SHY":
                continue

            score = 0.45 * mom_3m.current.value
            score += 0.35 * mom_6m.current.value
            score += 0.20 * mom_12m.current.value

            ranked_symbols.append((symbol, score))

        ranked_symbols.sort(key=lambda item: item[1], reverse=True)

        selected_symbols = []

        for symbol, score in ranked_symbols:
            if score > 0 and symbol.value != "SHY":
                selected_symbols.append(symbol)

            if len(selected_symbols) == 3:
                break

        if len(selected_symbols) == 0:
            selected_symbols = [
                self.symbol("SHY"),
                self.symbol("GLD")
            ]

        targets = []
        total_weight = 0.97
        target_weight = total_weight / len(selected_symbols)

        for symbol in self._symbols:
            weight = 0

            if symbol in selected_symbols:
                weight = target_weight

            targets.append(PortfolioTarget(symbol, weight))

        self.set_holdings(targets)