| Overall Statistics |
|
Total Orders 874 Average Win 0.52% Average Loss -0.23% Compounding Annual Return 158.776% Drawdown 21.200% Expectancy 0.493 Start Equity 1000000 End Equity 2050505.28 Net Profit 105.051% Sharpe Ratio 2.964 Sortino Ratio 4.078 Probabilistic Sharpe Ratio 90.143% Loss Rate 54% Win Rate 46% Profit-Loss Ratio 2.23 Alpha 1.02 Beta 0.922 Annual Standard Deviation 0.344 Annual Variance 0.119 Information Ratio 3.117 Tracking Error 0.327 Treynor Ratio 1.107 Total Fees $41063.33 Estimated Strategy Capacity $480000.00 Lowest Capacity Asset FNGO WWOP5M922LPH Portfolio Turnover 55.11% |
from AlgorithmImports import *
from datetime import datetime
from QuantConnect.Algorithm.Framework import *
from QuantConnect.Algorithm.Framework.Alphas import *
import numpy as np
import pandas as pd
from Risk.TrailingStopRiskManagementModel import TrailingStopRiskManagementModel
from Execution.StandardDeviationExecutionModel import StandardDeviationExecutionModel
class Two_2Algorithm(QCAlgorithm):
def Initialize(self):
self.SetStartDate(2022, 5, 1)
self.SetCash(80000)
self.SetRiskManagement(TrailingStopRiskManagementModel(0.046))
self.SetExecution(StandardDeviationExecutionModel(20, 4.0, Resolution.Minute))
# Define the universes of securities
self.universe_A = [self.AddEquity(ticker).Symbol
for ticker in ["SCHG", "TQQQ", "TNA", "UWM", "JPST", "VUG", "QLD", "QQQ", "SOXL", "FNGU", "TECL", "URTY", "USD", "IBIT", "ETHE", "BTF", "BETH", "XSD", "QQQU", "YBTC", "VGT", "DXD", "UVXY", "UVIX", "BITI", "SETH", "FNGO", "IWM", "SMH"]]
self.universe_C = [self.AddEquity(ticker).Symbol
for ticker in ["SVOL", "SPXU", "TZA", "UGL", "GLD", "GLTR", "AGQ"]]
self.universe_D = [self.AddEquity(ticker).Symbol
for ticker in ["JPST", "SPY", "TQQQ", "USMV", "SSO", "TNA", "UPRO", "BOXX", "QID", "SDS", "SSG"]]
self.universe_F = [self.AddEquity(ticker).Symbol
for ticker in ["SCHG", "TQQQ", "TNA", "UWM", "SVOL", "JPST", "SSO", "QLD", "SOXL", "MIDU", "FNGU", "URTY", "IBIT", "ETHE", "BTF", "BETH", "SDS", "QID", "UVXY", "UVIX"]]
self.matrix_1_dates = 18
self.reference = self.AddEquity("SVIX").Symbol
self.Schedule.On(self.DateRules.EveryDay(self.reference), self.TimeRules.AfterMarketOpen(self.reference, 20), self.rebalance)
#self.Schedule.On(self.DateRules.EveryDay(self.reference), self.TimeRules.BeforeMarketClose(self.reference, 10), self.rebalance)
# Set other initial states
self.shaken = []
self.toShort = []
self.toShort_A = []
self.toShort_C = []
self.toShort_D = []
self.toShort_F = []
self.SetWarmup(timedelta(hours=4))
self.started = False
def OnData(self, data):
return
def rebalance(self):
universes = {
"universe_A": self.universe_A,
"universe_C": self.universe_C,
"universe_D": self.universe_D,
"universe_F": self.universe_F
}
# Get the selected symbols for each universe
to_short_lists = {}
for name, universe in universes.items():
to_short_lists[name] = self.GetSymbols(universe, self.reference, self.matrix_1_dates, name)
# Liquidate symbols not in the selected lists
all_symbols = set(self.universe_A + self.universe_C + self.universe_D + self.universe_F)
selected_symbols = set(symbol for sublist in to_short_lists.values() for symbol in sublist)
for symbol in all_symbols - selected_symbols:
if self.Portfolio[symbol].Invested:
self.Liquidate(symbol)
# To ensure the least volatile security gets more than 24.8% allocation
total_allocation = 0.99 # Total allocation across selected securities
least_volatile_allocation = 0.00 # Allocate more than 24.8%
# First, find the least volatile security (the one with the lowest volatility)
all_selected = [symbol for sublist in to_short_lists.values() for symbol in sublist]
# Check if there are any selected securities
if not all_selected:
self.Debug("No selected securities. Skipping rebalance.")
return # Skip the rebalance if no symbols were selected
volatilities = {symbol: self.GetVolatility(symbol) for symbol in all_selected}
# Check if the volatilities dictionary is empty
if not volatilities:
self.Debug("No valid volatilities data. Skipping rebalance.")
return # Skip rebalance if there are no volatilities data
# Find the least volatile security
least_volatile_symbol = min(volatilities, key=volatilities.get)
# Allocate the least volatile security more than 24.8%
allocations = {least_volatile_symbol: least_volatile_allocation}
remaining_allocation = total_allocation - least_volatile_allocation
# Now, allocate the remaining securities with the rest of the percentage
other_selected = [symbol for symbol in all_selected if symbol != least_volatile_symbol]
# Exclude SVOL and JPST from allocations
other_selected = [symbol for symbol in other_selected if symbol not in ["SVOL", "JPST", "BOXX", "AGQ", "GLD", "UVXY", "USMV", "UVIX", "SSO", "GLTR", "XSD", "SPY", "YBTC", "VUG", "VGT", "SDS", "SPYI", "BITI", "SETH", "QQQ", "IWM", "SVXY", "SMH", "SSG", "LVHI"]]
num_other_selected = len(other_selected)
if num_other_selected > 0:
remaining_per_symbol_allocation = remaining_allocation / num_other_selected
for symbol in other_selected:
allocations[symbol] = remaining_per_symbol_allocation
# Set holdings based on the computed allocations
for symbol, allocation in allocations.items():
self.EmitInsights(Insight.Price(symbol, Expiry.EndOfWeek, InsightDirection.Up, None, None, None, allocation))
self.SetHoldings(symbol, allocation)
def GetSymbols(self, universe, reference, lookback, universe_name):
def matrix_1(first, second):
first = np.array(first)
first = np.diff(first) / first[:-1]
second = np.array(second)
second = np.diff(second) / second[:-1]
A = np.vstack([first, np.ones(len(first))]).T
basis = np.linalg.lstsq(A, second, rcond=None)[0]
return basis[1], basis[0]
history = self.History(universe + [reference], lookback, Resolution.Daily)
filter = []
for symbol in universe:
if str(symbol.ID) not in history.index.get_level_values(0):
continue
symbol_price = list(history.loc[symbol]['close'])
reference_price = list(history.loc[reference]['open'])
if len(symbol_price) == len(reference_price):
filter.append((symbol, matrix_1(symbol_price, reference_price)[0]))
# If filter list is empty, return an empty list
if not filter:
return []
filter.sort(key=lambda first: first[1], reverse=True)
# Log the ranked list of securities by ticker symbol
ranked_symbols = [(symbol.Value, score) for symbol, score in filter]
self.Debug(f"Ranked securities for {universe_name} on {self.Time}: {ranked_symbols}")
# Return the selected securities
return [x[0] for x in filter[-1:]]
def GetVolatility(self, symbol):
"""
A helper function to calculate the volatility of a security.
For simplicity, assuming volatility is the standard deviation of returns over the lookback period.
"""
history = self.History(symbol, self.matrix_1_dates, Resolution.Daily)
if history.empty:
return float('inf') # If no data, assume infinite volatility (avoid selection)
# Calculate daily returns
daily_returns = history['close'].pct_change().dropna()
# Return the standard deviation (volatility) of daily returns
volatility = daily_returns.std()
return volatility