Overall Statistics
Total Orders
5206
Average Win
0.21%
Average Loss
-0.21%
Compounding Annual Return
10.938%
Drawdown
43.300%
Expectancy
0.188
Start Equity
1000000
End Equity
3273530.99
Net Profit
227.353%
Sharpe Ratio
0.347
Sortino Ratio
0.377
Probabilistic Sharpe Ratio
1.353%
Loss Rate
40%
Win Rate
60%
Profit-Loss Ratio
0.99
Alpha
-0.026
Beta
1.21
Annual Standard Deviation
0.205
Annual Variance
0.042
Information Ratio
-0.085
Tracking Error
0.108
Treynor Ratio
0.059
Total Fees
$12128.70
Estimated Strategy Capacity
$650000000.00
Lowest Capacity Asset
NWSAV VHJF6S7EZRL1
Portfolio Turnover
1.14%
Drawdown Recovery
1427
# region imports
from AlgorithmImports import *
# endregion


class LazyPricesStrategy(QCAlgorithm):

    def initialize(self):
        self.set_start_date(2015, 1, 1)
        self.set_end_date(2026, 6, 1)
        self.set_cash(1_000_000)
        self.settings.seed_initial_prices = True
        self.universe_settings.resolution = Resolution.DAILY
        self._style_boxes = [
            StyleBox.MID_VALUE, StyleBox.MID_CORE, StyleBox.MID_GROWTH,
            StyleBox.SMALL_VALUE, StyleBox.SMALL_CORE, StyleBox.SMALL_GROWTH
        ]
        self._universe = self.add_universe(self._fundamental_filter)

    def _fundamental_filter(self, fundamental):
        # Keep the most liquid mid/small-cap names that have fundamental data.
        eligible = [
            f for f in fundamental
            if f.has_fundamental_data and f.asset_classification.style_box in self._style_boxes
        ]
        return [f.symbol for f in sorted(eligible, key=lambda f: f.dollar_volume)[-100:]]

    def on_warmup_finished(self):
        # Rebalance on the first trading day of each quarter at 8 AM.
        time_rule = self.time_rules.at(8, 0)
        self.schedule.on(self.date_rules.quarter_start("SPY"), time_rule, self._rebalance)
        # Rebalance today too.
        if self.live_mode:
            self._rebalance()
        else:
            self.schedule.on(self.date_rules.today, time_rule, self._rebalance)

    def _rebalance(self):
        if not self._universe.selected:
            return
        weight = 1 / len(self._universe.selected)
        self.set_holdings([PortfolioTarget(security, weight) for security in self._universe.selected], True)