Overall Statistics
Total Orders
20559
Average Win
0.06%
Average Loss
-0.06%
Compounding Annual Return
4.122%
Drawdown
38.700%
Expectancy
0.013
Start Equity
100000
End Equity
122387.25
Net Profit
22.387%
Sharpe Ratio
0.015
Sortino Ratio
0.018
Probabilistic Sharpe Ratio
2.071%
Loss Rate
49%
Win Rate
51%
Profit-Loss Ratio
0.99
Alpha
-0.062
Beta
1.076
Annual Standard Deviation
0.166
Annual Variance
0.028
Information Ratio
-0.825
Tracking Error
0.07
Treynor Ratio
0.002
Total Fees
$14007.03
Estimated Strategy Capacity
$240000000.00
Lowest Capacity Asset
RIVN XTDCDCDRI6HX
Portfolio Turnover
6.67%
Drawdown Recovery
1477
############################################
# Researched and Implemented by: Ethan Huang
# Mentored by: Rudy Osuna
# Triton Quantitative Trading @ UC San Diego
############################################
# region imports
from AlgorithmImports import *
# endregion


class EqualWeightedUniverseBenchmark(QCAlgorithm):

    def initialize(self):
        self.set_start_date(self.end_date - timedelta(5 * 365))
        self.set_cash(100_000)
        self.settings.seed_initial_prices = True
        self.universe_settings.resolution = Resolution.DAILY
        self.universe_settings.schedule.on(self.date_rules.week_start())
        self._universe = self.add_universe(self._select)
        self.set_warm_up(1)

    def on_warmup_finished(self):
        time_rule = self.time_rules.at(8, 0)
        # Rebalance the portfolio weekly after warmup.
        self.schedule.on(self.date_rules.week_start("SPY"), time_rule, self._rebalance)
        # Rebalance the portfolio today too.
        if self.live_mode:
            self._rebalance()
        else:
            self.schedule.on(self.date_rules.today, time_rule, self._rebalance)

    def _select(self, fundamental):
        # Select the top 100 most liquid stocks by dollar volume with price above $10.
        return [f.symbol for f in sorted(
            [f for f in fundamental if f.has_fundamental_data and f.price > 10],
            key=lambda f: f.dollar_volume
        )[-100:]]

    def on_securities_changed(self, changes):
        # Liquidate symbols that leave the universe.
        for security in changes.removed_securities:
            if security.invested:
                self.liquidate(security.symbol)

    def _rebalance(self):
        if self.is_warming_up:
            return
        selected_assets = [symbol for symbol in self._universe.selected if symbol in self.securities and self.securities[symbol].has_data]
        if len(selected_assets) == 0:
            return
        weight = 1.0 / len(selected_assets)
        targets = [PortfolioTarget(symbol, weight) for symbol in selected_assets]
        self.set_holdings(targets, True)