Overall Statistics
Total Orders
21977
Average Win
0.03%
Average Loss
-0.02%
Compounding Annual Return
18.224%
Drawdown
33.600%
Expectancy
1.216
Start Equity
10000000
End Equity
126907710.04
Net Profit
1169.077%
Sharpe Ratio
0.798
Sortino Ratio
0.848
Probabilistic Sharpe Ratio
24.562%
Loss Rate
8%
Win Rate
92%
Profit-Loss Ratio
1.41
Alpha
0.026
Beta
1.006
Annual Standard Deviation
0.149
Annual Variance
0.022
Information Ratio
0.635
Tracking Error
0.042
Treynor Ratio
0.118
Total Fees
$118462.64
Estimated Strategy Capacity
$1400000000.00
Lowest Capacity Asset
SPY R735QTJ8XC9X
Portfolio Turnover
0.41%
from AlgorithmImports import *

class ElectionCycleSectorRotationAlgorithm(QCAlgorithm):
    def initialize(self):
        self.set_start_date(2009, 8, 1)
        self.set_cash(10_000_000)
        self.settings.minimum_order_margin_portfolio_percentage = 0
        self.universe_settings.resolution = Resolution.DAILY
        self.set_security_initializer(BrokerageModelSecurityInitializer(self.brokerage_model, FuncSecuritySeeder(self.get_last_known_prices)))
        self._symbol = self.add_equity("SPY", Resolution.DAILY).symbol
        self._universe = self.add_universe(self.universe.etf(self._symbol))
        self._winning_party_by_election_date = {date(2008, 11, 5): "D", date(2012, 11, 7): "D", date(2016, 11, 9): "R", date(2020, 11, 8): "D"}
        self._last_election_date = datetime.min.date()
        self.schedule.on(self.date_rules.month_start(self._symbol), self.time_rules.midnight, self._rebalance)
        for d in self._winning_party_by_election_date.keys(): self.schedule.on(self.date_rules.on(d.year, d.month, d.day), self.time_rules.midnight, self._get_election_result)
        self.set_warm_up(timedelta(2*365))

    def _get_election_result(self):
        self._last_election_date = self.time.date()
        self._sectors_to_boost = [MorningstarSectorCode.HEALTHCARE, MorningstarSectorCode.TECHNOLOGY] if self._winning_party_by_election_date[self._last_election_date] == 'D' else [MorningstarSectorCode.ENERGY, MorningstarSectorCode.FINANCIAL_SERVICES]
        self._rebalance()

    def on_warmup_finished(self):
        self._rebalance()

    def _rebalance(self):
        if self.is_warming_up:
            return
        targets = [PortfolioTarget(self._symbol, 1)]
        if self.time.date() - self._last_election_date <= timedelta(18*30):
            securities = [self.securities[symbol] for symbol in self._universe.selected if symbol in self.securities]
            assets_to_boost = [s for s in securities if s.fundamentals.asset_classification.morningstar_sector_code not in self._sectors_to_boost and s.price and s.fundamentals.market_cap]
            market_cap_sum = sum([1/s.fundamentals.market_cap for s in assets_to_boost])
            targets = [PortfolioTarget(self._symbol, 0)] + [PortfolioTarget(s.symbol, 1/s.fundamentals.market_cap/market_cap_sum) for s in assets_to_boost]
        self.set_holdings(targets, True)