Created with Highcharts 12.1.2EquityJan 2020Jul 2020Jan 2021Jul 2021Jan 2022Jul 2022Jan 2023Jul 2023Jan 2024Jul 2024Jan 2025Jul 2025750k1,000k1,250k-10-5000.5-1002.5M5M05M10M01001020
Overall Statistics
Total Orders
426
Average Win
0.47%
Average Loss
-0.38%
Compounding Annual Return
2.878%
Drawdown
7.600%
Expectancy
0.190
Start Equity
1000000
End Equity
1159540.23
Net Profit
15.954%
Sharpe Ratio
-0.181
Sortino Ratio
-0.115
Probabilistic Sharpe Ratio
8.148%
Loss Rate
47%
Win Rate
53%
Profit-Loss Ratio
1.24
Alpha
-0.005
Beta
-0.032
Annual Standard Deviation
0.041
Annual Variance
0.002
Information Ratio
-0.454
Tracking Error
0.183
Treynor Ratio
0.229
Total Fees
$7387.16
Estimated Strategy Capacity
$1300000.00
Lowest Capacity Asset
SNDK YQ9V5TFTJSKL
Portfolio Turnover
2.22%
# region imports
from AlgorithmImports import *

from sklearn.cluster import KMeans
from sklearn.preprocessing import MinMaxScaler
# endregion


class MuscularFluorescentPinkPanda(QCAlgorithm):

    def initialize(self):
        self.set_start_date(2020, 1, 1)
        self.set_cash(1_000_000)
        self.universe_settings.extended_market_hours = True
        self._etf = self.add_equity('SPY')
        self.add_universe(self.universe.etf(self._etf.symbol, universe_filter_func=self._select_assets))
        self._previous_symbols = set()
        self._hold_duration = 3  # Trading days
        self._trades = []
        self.schedule.on(self.date_rules.every_day(self._etf.symbol), self.time_rules.before_market_open(self._etf.symbol, 30), self._rebalance)
        self.set_warm_up(timedelta(7))

    def _select_assets(self, constituents):
        symbols = set([c.symbol for c in constituents])
        if not (400 < len(symbols) < 600):
            return []  # To avoid data issues.
        self._etf_additions = list(symbols - self._previous_symbols)
        self._previous_symbols = symbols
        return self._etf_additions

    def _rebalance(self):
        if self.is_warming_up:
            return
        # Scan for exits.
        closed_trades = []
        for i, trade in enumerate(self._trades):
            trade.scan(self)
            if trade.closed:
                closed_trades.append(i)
        # Delete closed trades
        for i in closed_trades[::-1]:
            del self._trades[i]
        # Scan for entries.
        for symbol in self._etf_additions:
            self._trades.append(Trade(self, symbol, self._hold_duration))
        self._etf_additions = []
        self.plot('Trades', 'Open', len(self._trades))


class Trade:

    def __init__(self, algorithm, symbol, hold_duration):
        self._symbol = symbol
       
        # Determine position size
        self.closed = True
        price = algorithm.securities[symbol].price
        if not price:
            return
        self._quantity = -int(0.1 * algorithm.portfolio.total_portfolio_value / algorithm.securities[symbol].price)
        if self._quantity == 0:
            return
        self.closed = False
       
        # Enter trade
        algorithm.market_order(symbol, self._quantity)
       
        # Set variable for exit logic
        self._hold_duration = hold_duration
       
    def scan(self, algorithm):
        if self.closed:
            return
        self._hold_duration -= 1
        if self._hold_duration <= 0:
           algorithm.market_order(self._symbol, -self._quantity)    
           self.closed = True