Overall Statistics
Total Orders
47981
Average Win
0.02%
Average Loss
-0.01%
Compounding Annual Return
32.954%
Drawdown
24.700%
Expectancy
0.522
Start Equity
1000000
End Equity
23102628.85
Net Profit
2210.263%
Sharpe Ratio
1.136
Sortino Ratio
1.046
Probabilistic Sharpe Ratio
71.778%
Loss Rate
30%
Win Rate
70%
Profit-Loss Ratio
1.17
Alpha
0.159
Beta
0.693
Annual Standard Deviation
0.188
Annual Variance
0.035
Information Ratio
0.822
Tracking Error
0.165
Treynor Ratio
0.309
Total Fees
$442593.74
Estimated Strategy Capacity
$140000.00
Lowest Capacity Asset
QLD TJNNZWL5I4IT
Portfolio Turnover
10.09%
Drawdown Recovery
628
from AlgorithmImports import *
from datetime import timedelta
from QuantConnect.Algorithm.Framework import *
from QuantConnect.Algorithm.Framework.Alphas import *
import numpy as np

from Risk.TrailingStopRiskManagementModel import TrailingStopRiskManagementModel
from Execution.StandardDeviationExecutionModel import StandardDeviationExecutionModel


class Two_2Algorithm(QCAlgorithm):

    def Initialize(self):
        self.SetStartDate(2015, 1, 1)
        self.SetCash(1_000_000)
        self.SetBrokerageModel(AlphaStreamsBrokerageModel())
        self.SetTimeZone("America/New_York")

        self.tickers = ["QLD", "PSQ", "QID"]
        self.symbols = []

        self._benchmark_symbol = self.AddEquity("SPY", Resolution.Minute).Symbol
        self.reference = self.AddEquity("SQQQ", Resolution.Minute).Symbol
        self.lookback = 18

        for t in self.tickers:
            s = self.AddEquity(t, Resolution.Minute).Symbol
            self.symbols.append(s)
            if t == "QLD":
                self.qld = s

        self.park_ticker = "JPST"
        self.park = self.AddEquity(self.park_ticker, Resolution.Minute).Symbol

        self.Schedule.On(
            self.DateRules.WeekStart(self._benchmark_symbol),
            self.TimeRules.AfterMarketOpen(self._benchmark_symbol, 10),
            self.Rebalance
        )

        self.SetRiskManagement(CompositeRiskManagementModel(
            MaximumUnrealizedProfitPercentPerSecurity(0.1325),
            TrailingStopRiskManagementModel(0.0735)
        ))

        self.SetExecution(StandardDeviationExecutionModel(40, 1.25, Resolution.Minute))
        self.SetWarmUp(timedelta(hours=4))

        self.started = False
        self.last_rebalance = None

        self._was_qld_invested = False

    def OnData(self, data):
        if not self.started and not self.IsWarmingUp and data.Bars.Count:
            self.Rebalance()
            self.started = True

        for d in data.Delistings:
            if d.Key in self.symbols:
                self.symbols.remove(d.Key)

        if self.IsWarmingUp:
            return

        qld_now = self.Portfolio[self.qld].Invested

        if self._was_qld_invested and not qld_now:
            if len(self.Transactions.GetOpenOrders()) == 0:
                if not self.Portfolio[self.park].Invested:
                    self.SetHoldings(self.park, 0.99)

        self._was_qld_invested = qld_now

    def RegressionScore(self, a, b):
        a = np.diff(a) / a[:-1]
        b = np.diff(b) / b[:-1]
        x = np.vstack([a, np.ones(len(a))]).T
        return float(np.linalg.lstsq(x, b, rcond=None)[0][1])

    def _LiquidateAllExcept(self, keep_symbol=None):
        for holding in self.Portfolio.Values:
            if holding.Invested and (keep_symbol is None or holding.Symbol != keep_symbol):
                self.Liquidate(holding.Symbol)

    def Rebalance(self):
        today = self.Time.date()
        if self.last_rebalance == today:
            return
        self.last_rebalance = today

        h = self.History(self.symbols + [self.reference], self.lookback, Resolution.Daily)
        if h.empty:
            return

        scores = []
        for s in self.symbols:
            try:
                px = h.loc[s]["open"].values
                ref = h.loc[self.reference]["open"].values
            except KeyError:
                continue

            if len(px) < 3 or len(px) != len(ref):
                continue

            scores.append((s, self.RegressionScore(px, ref)))

        if not scores:
            return

        scores.sort(key=lambda x: x[1], reverse=True)
        leader = scores[0][0]

        if leader == self.qld:
            if self.Portfolio[self.park].Invested:
                self.Liquidate(self.park)

            self._LiquidateAllExcept(keep_symbol=self.qld)

            self.SetHoldings(self.qld, 0.99)

        else:
            if self.Portfolio[self.qld].Invested:
                self.Liquidate(self.qld)

            self._LiquidateAllExcept(keep_symbol=self.park)

            self.SetHoldings(self.park, 0.99)