| Overall Statistics |
|
Total Trades 1401 Average Win 0.34% Average Loss -0.13% Compounding Annual Return 29.575% Drawdown 11.900% Expectancy 0.738 Net Profit 68.698% Sharpe Ratio 1.135 Probabilistic Sharpe Ratio 52.176% Loss Rate 52% Win Rate 48% Profit-Loss Ratio 2.63 Alpha 0.215 Beta 0.044 Annual Standard Deviation 0.191 Annual Variance 0.037 Information Ratio 0.674 Tracking Error 0.24 Treynor Ratio 4.938 Total Fees $3413.54 Estimated Strategy Capacity $0 Lowest Capacity Asset CL Y3OJWLAECMGX |
#region imports
from datetime import datetime, timedelta
import datetime
from AlgorithmImports import *
import pandas as pd
from QuantConnect.Python import *
from QuantConnect.Indicators import RollingWindow
#endregion
class RetrospectiveTanButterfly(QCAlgorithm):
def Initialize(self):
self.SetStartDate(2020, 9, 17) # Set Start Date
self.SetEndDate(2022, 9, 23)
self.SetCash(1000000) # Set Strategy Cash
self.symbolData = {}
self.canLong = True
self.canShort = True
self.contract = self.AddFuture(Futures.Energies.CrudeOilWTI , Resolution.Tick, extendedMarketHours = False, dataNormalizationMode = DataNormalizationMode.BackwardsRatio, dataMappingMode = DataMappingMode.OpenInterest , contractDepthOffset = 0)
symbol = self.contract.Symbol
#symbol.SetFilter(0, 90)
#Futures.Grains.Corn, Futures.Indices.SP500EMini, Futures.Energies.CrudeOilWTI
#Energies.CrudeOilWTI
self.symbolData[symbol] = SymbolData()
self.symbolData[symbol].bidPrice = self.Securities[symbol].BidPrice
self.symbolData[symbol].askPrice = self.Securities[symbol].AskPrice
self.v_quantity_window = RollingWindow[float](10000)
self.v_price_window = RollingWindow[float](10000)
#symbol.SetFilter(lambda x: x.FrontMonth().OnlyApplyFilterAtMarketOpen())
self.marketclose = 17*60
self.tickcount = 0
#self.treset = datetime(2000,7,6,10,0,0)
self.askcooldown = True
self.bidcooldown = True
self.bidreset = 0
self.askreset = 0
self.pricehreset = 0
self.pricelreset = 0
self.tickcount = 0
self.orderIDs = pd.DataFrame([],columns=['id','fillprice','quantity','time'])
self.BuyFlag = False
self.trigger = False
self.ticklist = []
def OnData(self, data):
for changedEvent in data.SymbolChangedEvents.Values:
if changedEvent.Symbol == self.contract.Symbol:
self.Log(f"SymbolChanged event: {changedEvent}")
self.Log(f"contract mapped symbol: {self.contract.Mapped}")
for symbol, symbolData in self.symbolData.items():
if not data.Ticks.ContainsKey(symbol): continue
#underlying = symbol.Underlying
ticks = data.Ticks[symbol]
for tick in ticks:
if tick.TickType == TickType.Quote:
symbolData.bidPrice = tick.BidPrice if tick.BidPrice != 0 else symbolData.bidPrice
symbolData.askPrice = tick.AskPrice if tick.AskPrice != 0 else symbolData.askPrice
#symbolData.bidSize = tick.BidSize if tick.BidSize != 0 else symbolData.bidSize
#symbolData.askSize = tick.AskSize if tick.AskSize != 0 else symbolData.askSize
if tick.TickType == TickType.Trade:
self.tickcount += 1
if symbolData.bidIceberg == 0 and symbolData.bidPrice != 0: #fix this logic, how do i know if its a ask ice or bid ice
symbolData.bidIceberg = symbolData.bidPrice
symbolData.bidCounter = 0
if tick.Price < symbolData.bidIceberg:
symbolData.bidIceberg = tick.Price
symbolData.bidCounter = 0
self.pricelreset += 1
self.bidcooldown = True
if tick.Price == symbolData.bidIceberg:
symbolData.bidCounter += tick.Quantity
if tick.Price > symbolData.bidIceberg / 0.995:
symbolData.bidIceberg = 0
symbolData.bidCounter = 0
self.bidreset += 1 #delete
self.bidcooldown = True
if symbolData.askIceberg == 0 and symbolData.askPrice != 0:
symbolData.askIceberg = symbolData.askPrice
symbolData.askCounter = 0
if tick.Price > symbolData.askIceberg:
symbolData.askIceberg = tick.Price
symbolData.askCounter = 0
self.pricehreset += 1
self.askcooldown = True
if tick.Price == symbolData.askIceberg:
symbolData.askCounter += tick.Quantity
if tick.Price < symbolData.askIceberg * 0.995:
symbolData.askIceberg = 0
symbolData.askCounter = 0
self.askreset += 1 #delete
self.askcooldown = True
if symbolData.bidSize > 400:
symbolData.bidPacman = symbolData.bidPrice
if symbolData.askSize > 400:
symbolData.askPacman = symbolData.askPrice
#symbolData.bidPrice = symbolData
#mark bid price as variable, so that price cant be used as an iceberg
#if self.tickcount < 1075 and self.tickcount > 1000:
#self.Log(f"tick price->: {tick.Price} tick quantity->: {tick.Quantity} bid ice->: {symbolData.bidIceberg} ask ice->: {symbolData.askIceberg} bid count->: {symbolData.bidCounter} ask count->: {symbolData.askCounter} bid price->: {symbolData.bidPrice} ask price->: {symbolData.askPrice} tick sus->: {tick.Suspicious}")
#if tick.Time > datetime(2020,7,6,10,0,0) and tick.Time < datetime(2020,7,6,10,0,10):#2020-07-06 10:00:03
#self.Log(f"tick price->: {tick.Price} tick quantity->: {tick.Quantity} bid ice->: {symbolData.bidIceberg} ask ice->: {symbolData.askIceberg} bid count->: {symbolData.bidCounter} ask count->: {symbolData.askCounter} bid price->: {symbolData.bidPrice} ask price->: {symbolData.askPrice} tick sus->: {tick.Suspicious}")
#if symbolData.askCounter < 100:
# symbolData.askIceberg = 0
# symbolData.askCounter = 0
# self.askreset += 1 #delete
if symbolData.askCounter > 200 and self.askcooldown: #and symbolData.askPacman == symbolData.askIceberg:
self.BuyFlag = True
self.MarketOrder(self.contract.Mapped, 1)
self.askcooldown = False
self.Log(f"ask counter: {symbolData.askCounter}")
symbolData.askCounter = 0
symbolData.askIceberg = 0
if symbolData.bidCounter > 200 and self.bidcooldown: # and symbolData.bidPacman == symbolData.bidIceberg:
self.BuyFlag = True
self.MarketOrder(self.contract.Mapped, -1)
self.bidcooldown = False
self.Log(f"bid counter: {symbolData.bidCounter}")
symbolData.bidCounter = 0
symbolData.bidIceberg = 0
def OnEndOfDay(self, symbol):
#symbolData = self.symbolData[symbol]
x = 5
#self.Debug(f"{symbol.Value}'s buy volume is {symbolData.buyVolume} and sell volume is {symbolData.sellVolume} for today")
#self.Log(f"{symbol.Value}'s buy volume is {symbolData.buyVolume} and sell volume is {symbolData.sellVolume} for today")
#self.v_quantity_window.Reset()
#self.v_price_window.Reset()
#self.Log(f"bid resets for today {self.bidreset}")
#self.Log(f"ask resets for today {self.askreset}")
#self.bidreset = 0
#self.askreset = 0
#self.Log(f"bid higher resets for today {self.pricehreset}")
#self.Log(f"ask lower resets for today {self.pricelreset}")
#self.pricehreset = 0
#self.pricelreset = 0
class SymbolData:
def __init__(self):
self.buyVolume = 0
self.sellVolume = 0
self.bidPrice = 0
self.askPrice = 0
self.bidCounter = 0
self.askCounter = 0
self.bidIceberg = 0
self.askIceberg = 0
self.bidSize = 0
self.askSize = 0
self.bidPacman = 0
self.askPacman = 0