| Overall Statistics |
|
Total Orders 898 Average Win 0.55% Average Loss -0.21% Compounding Annual Return 31.414% Drawdown 42.200% Expectancy 1.498 Start Equity 100000 End Equity 384958.61 Net Profit 284.959% Sharpe Ratio 0.875 Sortino Ratio 0.957 Probabilistic Sharpe Ratio 37.251% Loss Rate 32% Win Rate 68% Profit-Loss Ratio 2.66 Alpha 0.106 Beta 1.23 Annual Standard Deviation 0.252 Annual Variance 0.063 Information Ratio 0.926 Tracking Error 0.137 Treynor Ratio 0.179 Total Fees $1095.04 Estimated Strategy Capacity $420000000.00 Lowest Capacity Asset COST R735QTJ8XC9X Portfolio Turnover 4.13% |
# region imports
from AlgorithmImports import *
import math
# endregion
class DeterminedGreenKangaroo(QCAlgorithm):
def initialize(self):
# self.set_start_date(2007, 1, 2)
# self.set_start_date(2008, 4, 2)
# self.set_start_date(2009, 7, 2)
self.set_start_date(2020, 1, 2)
# self.set_start_date(2022, 1, 2)
# self.set_start_date(2024, 1, 2)
# self.set_start_date(2024, 10, 2)
self.set_cash(100000)
self.universe_settings.resolution = Resolution.Daily # Hour #
self._universe = self.add_universe(self.universe.dollar_volume.top(100))
self.creamOnTop = 10
# self.min_portfolio_size = 20
self._portfolio = []
self.vwap_size = 1
self.ema_size = 63 # int(3 * 6.5) # 5, 21, 63, 252
self.set_warmup(2 * self.ema_size)
self.expected_value_threshold = 1 # 1 + (0.96 - 1) / (252 * 6.5)
# self.settings.free_portfolio_value_percentage = 0.1
# # https://www.quantconnect.com/docs/v2/writing-algorithms/trading-and-orders/position-sizing
self.buy_spy = True
self.spy_symbol = self.add_equity("SPY", Resolution.DAILY).symbol
self.portfolio_target_len = self.creamOnTop
def on_securities_changed(self, changes: SecurityChanges):
for security in changes.added_securities:
# indicator outline
# rocp_of_vwap = rocp( vwap )
# i.e. how fast the price is changing
# ema_of_rocp = ema( rocp_of_vwap)
# i.e. exponential moving average
# ema_of_roc = ema( roc( rocp_of_vwap))
# i.e. how fast the price is accelerating
# ema_of_std = sqrt( ema( square( minus( rocp_of_vwap, ema_of_rocp ))))
# i.e. exponential moving average of standard deviation of rocp_of_vwap
vwap = self.vwap(security.symbol, self.vwap_size)
rocp_of_vwap = IndicatorExtensions.of(
RateOfChangePercent(1), vwap)
security.ema_of_rocp = IndicatorExtensions.of(
ExponentialMovingAverage(self.ema_size), rocp_of_vwap)
roc_of_rocp = IndicatorExtensions.of(
RateOfChange(1), rocp_of_vwap)
# security.ema_of_roc = IndicatorExtensions.of(
# ExponentialMovingAverage(self.ema_size), roc_of_rocp)
# security.ema_of_std = 0 # sqrt( ema( square( minus( rocp_of_vwap, ema_of_rocp ))))
rocp_minus_ema = IndicatorExtensions.minus(rocp_of_vwap, security.ema_of_rocp)
square_of_minus = IndicatorExtensions.times(rocp_minus_ema, rocp_minus_ema)
ema_of_square = IndicatorExtensions.of(
ExponentialMovingAverage(self.ema_size), square_of_minus)
security.ema_of_variance = ema_of_square
security.initialize = True
for security in changes.removed_securities:
if security.Invested:
self.Liquidate(security.Symbol)
if security.Symbol in self._portfolio:
self._portfolio.remove(security.Symbol)
def on_data(self, data: Slice):
if not self.is_warming_up and data.bars:
cash = self.portfolio.cash
price = data[self.spy_symbol].price
shares = math.floor(cash / price)
self.market_order("SPY", shares)
securities = [self.securities[symbol] for symbol in self._universe.selected]
currentValues = {}
for security in securities:
conditions = []
conditions.append(security.ema_of_rocp.is_ready)
# conditions.append(security.ema_of_roc.is_ready)
conditions.append(security.ema_of_variance.is_ready)
if all(conditions):
ema_of_rocp = security.ema_of_rocp.Current.Value
# ema_of_roc = security.ema_of_roc.Current.Value
ema_of_variance = security.ema_of_variance.Current.Value
ema_of_standard_deviation = math.sqrt(ema_of_variance)
uncertainty_of_rocp = 1.14 * ema_of_standard_deviation / self.ema_size
# if security.initialize and ema_of_rocp >= 0:
# if security.Symbol not in self._portfolio:
# self._portfolio.append(security.Symbol)
# if ema_of_rocp <= 0 and ema_of_rocp + ema_of_roc > 0:
# if security.Symbol not in self._portfolio:
# self._portfolio.append(security.Symbol)
# if ema_of_rocp >= 0 and ema_of_rocp + ema_of_roc < 0:
# if security.Symbol in self._portfolio:
# self._portfolio.remove(security.Symbol)
# if ema_of_rocp > uncertainty_of_rocp:
# if security.Symbol not in self._portfolio:
# self._portfolio.append(security.Symbol)
# if ema_of_rocp < uncertainty_of_rocp:
# if security.Symbol in self._portfolio:
# self._portfolio.remove(security.Symbol)
# currentValues[security.Symbol] = ema_of_rocp / uncertainty_of_rocp
next_step = ema_of_rocp - 2 * uncertainty_of_rocp #+ ema_of_roc
rocp_fraction = 1 + next_step / 100
log_rocp_fraction = np.log(rocp_fraction)
std_fraction = 1 + (next_step + ema_of_standard_deviation) / 100
rocp_std = log_rocp_fraction - np.log(std_fraction)
expected_value = np.exp(log_rocp_fraction + rocp_std **2 / 2)
currentValues[security.Symbol] = expected_value
currentValues = dict(sorted(currentValues.items(), key=lambda item: item[1]))
current_value_keys = list(currentValues.keys())
# current_value_keys.reverse()
notCream = len(currentValues) - self.creamOnTop
ranks = {}
for symbol in current_value_keys[:notCream]:
# self.SetHoldings(symbol, 0)
if symbol in self._portfolio:
self._portfolio.remove(symbol)
for n, symbol in enumerate(current_value_keys[notCream:]):
ranks[symbol] = n
if symbol not in self._portfolio and symbol != self.spy_symbol:
self._portfolio.append(symbol)
if currentValues[symbol] < self.expected_value_threshold:
if symbol in self._portfolio:
self._portfolio.remove(symbol)
self.Plot("len(self._portfolio)", "len", len(self._portfolio))
# if len(self._portfolio) > 0:
# # cash_on_hand = 0.5 / self.creamOnTop # percent of portfolio to remain cash
# # gld_weight = 0
# # if len(self._portfolio) < self.min_portfolio_size:
# # gld_weight = self.min_portfolio_size - len(self._portfolio)
# # symbol_weight = (1 - cash_on_hand) / (len(self._portfolio) )#+ gld_weight)
# # gld_weight = (1 - cash_on_hand) * gld_weight / self.min_portfolio_size
# # if len(self._portfolio) < self.min_portfolio_size:
# # pass
# # symbol_weight = (1 - cash_on_hand) / self.creamOnTop
# portfolio_targets = []
# for symbol in self._portfolio:
# # self.SetHoldings(symbol, 1 / self.creamOnTop)
# # symbol_weight = 2 * ranks[symbol] / self.creamOnTop ** 2
# # self.SetHoldings(symbol, symbol_weight)
# portfolio_targets.append(PortfolioTarget(symbol, 1 / self.creamOnTop))
# # portfolio_targets.append(PortfolioTarget(symbol, symbol_weight))
# # self.SetHoldings("GLD", gld_weight)
# self.set_holdings(portfolio_targets)
spy_price = self.portfolio["SPY"].price
spy_holding = self.portfolio["SPY"].quantity * spy_price
if len(self._portfolio) > 0:
for symbol in self._portfolio:
already_invested = self.portfolio[symbol].quantity > 0
if not already_invested:
value = self.portfolio.total_portfolio_value
target = value / self.portfolio_target_len
if spy_holding > target and symbol in data.keys():
sell_spy = math.floor(target / spy_price)
self.market_order("SPY", -sell_spy)
cash_value = sell_spy * spy_price
spy_holding -= cash_value
buy_symbol = math.floor(target / data[symbol].price)
self.market_order(symbol, buy_symbol)
for symbol in self.portfolio.keys():
if symbol not in self._portfolio and symbol != self.spy_symbol:
if self.portfolio[symbol].holdings_cost < self.portfolio[symbol].holdings_value:
quantity = self.portfolio[symbol].quantity
self.market_order(symbol, -quantity)
cash_value = quantity * self.portfolio[symbol].price
buy_spy = math.floor(cash_value / data[self.spy_symbol].price)
self.market_order("SPY", buy_spy)
cash_value = buy_spy * spy_price
spy_holding += cash_value
# vwap
# https://www.quantconnect.com/docs/v2/writing-algorithms/indicators/supported-indicators/volume-weighted-average-price-indicator
# rocp
# https://www.quantconnect.com/docs/v2/writing-algorithms/indicators/supported-indicators/rate-of-change-percent
# ema
# https://www.quantconnect.com/docs/v2/writing-algorithms/indicators/supported-indicators/exponential-moving-average
# Combining Indicators
# https://www.quantconnect.com/docs/v2/writing-algorithms/indicators/combining-indicators
# weighted standard deviation
# https://www.itl.nist.gov/div898/software/dataplot/refman2/ch2/weightsd.pdf