Overall Statistics
Total Orders
2462
Average Win
0.23%
Average Loss
-0.12%
Compounding Annual Return
18.983%
Drawdown
35.700%
Expectancy
1.642
Start Equity
100000
End Equity
1761095.53
Net Profit
1661.096%
Sharpe Ratio
0.842
Sortino Ratio
0.885
Probabilistic Sharpe Ratio
36.224%
Loss Rate
8%
Win Rate
92%
Profit-Loss Ratio
1.87
Alpha
0.041
Beta
0.895
Annual Standard Deviation
0.14
Annual Variance
0.02
Information Ratio
0.524
Tracking Error
0.06
Treynor Ratio
0.132
Total Fees
$4090.47
Estimated Strategy Capacity
$19000000.00
Lowest Capacity Asset
AIV R735QTJ8XC9X
Portfolio Turnover
0.21%
Drawdown Recovery
695
from AlgorithmImports import *

class BuyLowAndKeepForeverAlgorithm(QCAlgorithm):

    _ETF_TICKER = "SPY"
    _TIME_PERIOD = 80

    def initialize(self):
        self.universe_settings.leverage = 1.0
        self.universe_settings.resolution = Resolution.DAILY

        self.set_start_date(2010, 1, 1)
        self.set_brokerage_model(BrokerageName.INTERACTIVE_BROKERS_BROKERAGE, AccountType.MARGIN)

        self.add_universe_selection(ETFConstituentsUniverseSelectionModel(self._ETF_TICKER))

        self.set_benchmark(self._ETF_TICKER)
        self.set_warm_up(self._TIME_PERIOD, Resolution.DAILY)
        
    def on_securities_changed(self, changes):
        for security in changes.added_securities:
            security.indicator = self.std(security.symbol, self._TIME_PERIOD, resolution=Resolution.DAILY)

        for security in changes.removed_securities:
            self.deregister_indicator(security.indicator)

            if self.portfolio[security.symbol].invested:
                self.liquidate(security.symbol)

    def on_data(self, data):
        if self.is_warming_up:
            return

        available_securities = [
            s for s in self.active_securities.values()
            if hasattr(s, 'indicator') and s.indicator.is_ready
        ]

        if not available_securities:
            return

        selected_securities = sorted(available_securities, key=lambda s: s.indicator.current.value, reverse=False)[:5]
        targets_symbols = [s.symbol for s in selected_securities]

        current_holdings = {
            symbol for symbol, holding in self.portfolio.items() if holding.invested
        }

        targets = list(current_holdings.union(targets_symbols))

        for symbol in targets:
            if not self.portfolio[symbol].invested:
                holdings = [
                PortfolioTarget(symbol, 1.0 / len(targets))
                for symbol in targets
            ]

                self.set_holdings(holdings, liquidate_existing_holdings=True)
                break