| Overall Statistics |
|
Total Trades 374 Average Win 0.58% Average Loss -0.49% Compounding Annual Return 8.715% Drawdown 6.400% Expectancy 0.144 Net Profit 13.359% Sharpe Ratio 0.07 Sortino Ratio 0.067 Probabilistic Sharpe Ratio 34.164% Loss Rate 48% Win Rate 52% Profit-Loss Ratio 1.18 Alpha -0.015 Beta 0.203 Annual Standard Deviation 0.071 Annual Variance 0.005 Information Ratio -0.753 Tracking Error 0.126 Treynor Ratio 0.024 Total Fees $585.96 Estimated Strategy Capacity $83000000.00 Lowest Capacity Asset QQQ RIWIV7K5Z9LX Portfolio Turnover 67.95% |
#region imports
from AlgorithmImports import *
#endregion
class Strategy:
def __init__(self, algo, symbol):
self.algo = algo
self.symbol = symbol
self.day_close = None
self.day_open = None
self.entries_by_date = {}
self.high_of_day = None
self.low_of_day = None
# Just to see how it's done -- not too bad.
self.bb = self.algo.BB(self.symbol, self.algo.length, self.algo.nstd, Resolution.Minute)
self.vwap = self.algo.VWAP(self.symbol)
self.sma = self.algo.SMA(self.symbol, self.algo.length)
self.sigma = self.algo.STD(self.symbol, self.algo.length)
self.smas = RollingWindow[float](self.algo.n + 1)
self.vwaps = RollingWindow[float](self.algo.n + 1)
# Decided on a delegate method, rather than a consolidator
# just simpler, less boilerplate.
def OnData(self, data: Slice):
data = data.Bars
if not data.ContainsKey(self.symbol): return
bar = data[self.symbol]
tm = bar.EndTime
self.UpdateWindows(bar)
if self.IsReady:
self.EntryLogic(bar)
if self.Invested:
self.ExitPackage()
def UpdateWindows(self, bar):
if self.sma.IsReady:
self.smas.Add(self.sma.Current.Value)
if self.vwap.IsReady:
self.vwaps.Add(self.vwap.Current.Value)
def EntryLogic(self, bar):
alloc_pct = 1.0 / len(self.algo.strats)
long_ok = self.algo.direction >= 0
sma_ok = bar.Close > self.sma.Current.Value
sma_trend = self.smas[0] > self.smas[self.algo.n]
# A bit hacked -- just using what we have available.
below_lower = bar.Close < self.vwaps[0] - self.sigma.Current.Value * self.algo.nstd
if long_ok and sma_ok and sma_trend and below_lower:
self.algo.SetHoldings(self.symbol, alloc_pct)
def ExitPackage(self):
self.StopPct(self.algo.stop_pct)
self.TgtPct(self.algo.tgt_pct)
def StopPct(self, pct):
if pct == 0.0: return
urpct = self.algo.Portfolio[self.symbol].UnrealizedProfitPercent
ur = self.algo.Portfolio[self.symbol].UnrealizedProfit
if self.algo.Portfolio[self.symbol].UnrealizedProfitPercent < -1 * pct:
self.algo.Liquidate(self.symbol, tag=f"Stop -- {ur}")
def TgtPct(self, pct):
if pct == 0.0: return
ur = self.algo.Portfolio[self.symbol].UnrealizedProfit
if self.algo.Portfolio[self.symbol].UnrealizedProfitPercent > pct:
self.algo.Liquidate(self.symbol, tag=f"Tgt -- {ur}")
@property
def Sigma(self):
if self.IsReady:
return self.bb.Upper.Current.Value - self.bb.Middle.Current.Value
@property
def IsReady(self):
return self.bb.IsReady and self.vwap.IsReady and self.sma.IsReady and self.vwaps.IsReady
@property
def Invested(self):
return self.algo.Portfolio[self.symbol].Invested# region imports
from AlgorithmImports import *
# endregion
from Strategy import Strategy
class SwimmingRedTermite(QCAlgorithm):
tickers = ['QQQ']
entry_style_1 = True
length = 50
nstd = 2
direction = 1
# Bars back, used for slope measure.
n = 7
# If 0, will not set.
# .01 == 1%
tgt_pct = 0
stop_pct = 0
symbols = []
strats = {}
def Initialize(self):
self.SetStartDate(2022, 9, 1)
self.SetCash(100000)
# Set benchmark -- SPY (most common, need it unrelated to tickers)
self.AddEquity('SPY', Resolution.Minute)
self.SetBenchmark("SPY")
# symbol = self.AddEquity("QQQ", Resolution.Minute).Symbol
for t in self.tickers:
s = self.AddEquity(t, Resolution.Minute).Symbol
self.symbols.append(s)
self.strats[s] = Strategy(self, s)
self.Schedule.On(self.DateRules.EveryDay("SPY"),
self.TimeRules.BeforeMarketClose("SPY", 1),
self.EODx)
def EODx(self):
self.Liquidate(tag="EOD")
def OnData(self, data: Slice):
gap_pcts = []
for symbol, strat in self.strats.items():
strat.OnData(data)