| Overall Statistics |
|
Total Trades 5811 Average Win 0.00% Average Loss 0% Compounding Annual Return 14.339% Drawdown 1.200% Expectancy 0 Net Profit 7.452% Sharpe Ratio 4.347 Probabilistic Sharpe Ratio 97.548% Loss Rate 0% Win Rate 100% Profit-Loss Ratio 0 Alpha 0.058 Beta 0.144 Annual Standard Deviation 0.026 Annual Variance 0.001 Information Ratio -2.237 Tracking Error 0.121 Treynor Ratio 0.785 Total Fees $7786.74 Estimated Strategy Capacity $0 Lowest Capacity Asset ES XRX5ODZTG8OX |
from math import floor
from collections import deque
import numpy as np
from AlgorithmImports import *
# import requests
class FatRedOrangeLeopard(QCAlgorithm):
def Initialize(self):
#1. Initialize the start, end dates and cash
self.SetStartDate(2021, 1, 1)
self.SetEndDate(2021, 7, 15)
self.SetCash(1000000)
self.SetBenchmark("SPY")
#1. Request /ES futures and save the ES security
self.EMini = self.AddFuture(Futures.Indices.SP500EMini, Resolution.Tick)
self.EMini.FeeModel = ConstantFeeModel(1.34)
self.EMini.SetFilter(0, 90)
# MISC
self.Bid = 0
self.Ask = 0
self.BidSize = 0
self.AskSize = 0
self.LastPrice = 0
self.Volume = 0
# Entry Parameters
self.entryPrice = 0
self.entryTicket = None
self.entryTime = datetime.min
self.nextEntryTime = self.Time
# Exit Parameters
self.exitPrice = 0
self.exitTicket = None
self.exitTime = datetime.min
self.nextExitTime = self.Time
self.liquidContract = None
self.timeToExpiryLimit = 5 * 24 * 60 * 60 # 5 days, in seconds
self.minDelay = 1 * 60 # seconds
self.maxDelay = 10 * 60 # seconds
self.minLatency = 10 # seconds
self.maxLatency = 60 # seconds
self.chaseBid = 60 # seconds
self.contractsToBuy = 1
self.lastHour = 0 # hours
def OnMarginCallWarning(self):
self.Error("You received a margin call warning. The assets will be liquidated to cover losses.")
def OnData(self, slice):
if self.Time.hour != self.lastHour:
self.Debug(str(self.Time))
self.lastHour = self.Time.hour
self.liquidContract = first([sorted([contract for contract in chain.Value if contract.OpenInterest > 10000 and contract.Expiry > self.Time], key=lambda k : k.OpenInterest, reverse=True) for chain in slice.FutureChains])
# If a contract exists and we're not too close to expiry, attempt to scalp
if self.liquidContract and (self.liquidContract.Expiry - self.Time).total_seconds() > self.timeToExpiryLimit:
self.Securities[self.liquidContract.Symbol].FeeModel = ConstantFeeModel(1.34)
self.Bid = self.liquidContract.BidPrice if self.liquidContract.BidPrice else self.Bid
self.Ask = self.liquidContract.AskPrice if self.liquidContract.AskPrice else self.Ask
self.BidSize = self.liquidContract.BidSize if self.liquidContract.BidSize else self.BidSize
self.AskSize = self.liquidContract.AskSize if self.liquidContract.AskSize else self.AskSize
self.LastPrice = self.liquidContract.LastPrice if self.liquidContract.LastPrice else self.LastPrice
self.Volume = self.liquidContract.Volume if self.liquidContract.Volume else self.Volume
# send entry limit order
if not self.Portfolio.Invested and not self.Transactions.GetOpenOrders(self.liquidContract.Symbol) and self.Time >= self.nextEntryTime:
#4. Make a limit order for the number of contracts we calculated for that symbol
self.entryPrice = self.Bid
if self.entryPrice:
self.entryTicket = self.LimitOrder(self.liquidContract.Symbol, self.contractsToBuy, self.entryPrice, "Entry Order")
self.entryTime = self.Time
# self.Debug("BUY /ES @" + str(self.entryPrice))
# move limit price if not filled after N bars / seconds / etc
if self.entryTicket is not None and (self.Time - self.entryTime).seconds > self.chaseBid and self.entryTicket.Status != OrderStatus.Filled:
self.entryTime = self.Time
updateFields = UpdateOrderFields()
updateFields.LimitPrice = self.Bid
self.entryTicket.Update(updateFields)
# Send a protective exit order to cover any orphan positions
if self.Portfolio.Invested and self.Securities[self.liquidContract.Symbol].Holdings.Quantity > 0 and not self.Transactions.GetOpenOrders(self.liquidContract.Symbol) and self.Time >= self.nextExitTime:
self.exitPrice = self.entryTicket.AverageFillPrice + 0.25 if self.entryTicket is not None else self.Ask
if self.exitPrice:
self.exitTicket = self.LimitOrder(self.liquidContract.Symbol, -self.contractsToBuy, self.exitPrice, "Exit Order")
self.exitTime = self.Time
# self.Debug("SELL /ES @" + str(self.exitPrice))
def OnOrderEvent(self, orderEvent):
order = self.Transactions.GetOrderById(orderEvent.OrderId)
if orderEvent.Status == OrderStatus.Canceled:
self.Debug("{0}: {1}: {2}".format(self.Time, order.Type, orderEvent))
if orderEvent.Status != OrderStatus.Filled:
return
if self.liquidContract is not None:
# Specify earliest we can send our exit order in order to approximate some kind of latency / queuing
if self.entryTicket is not None and self.entryTicket.OrderId == orderEvent.OrderId and self.Securities[self.liquidContract.Symbol].Holdings.Quantity > 0:
self.entryTicketFillTime = self.Time
self.nextExitTime = self.Time + timedelta(seconds=np.random.randint(self.minLatency,self.maxLatency))
# If we exit, specify the new lag if necessary, many minutes delay
if self.exitTicket is not None and self.exitTicket.OrderId == orderEvent.OrderId:
self.exitTicketFillTime = self.Time
self.nextEntryTime = self.Time + timedelta(seconds=np.random.randint(self.minDelay,self.maxDelay))
def ExitPositions(self):
self.Transactions.CancelOpenOrders()
self.Liquidate()
def first(t):
res = [item for sublist in t for item in sublist]
if len(res) > 0:
return res[0]
else:
return False
#0. TODO: Delete the old code for manually calculating the position size (lines 30-33)
#LEGACY Save the notional value of the futures contract to self.notionalValue
#LEGACY Calculate the number of contracts we can afford based on the margin required, Divide the margin remaining by the initial margin and save to self.contractsToBuy
# self.notionalValue = self.liquidContract.AskPrice*self.symbol.SymbolProperties.ContractMultiplier
# self.contractsToBuy = floor( self.Portfolio.MarginRemaining / future.BuyingPowerModel.InitialOvernightMarginRequirement )
# self.contractsToBuy = self.CalculateOrderQuantity(self.symbol, 1)
#MISC:
# Set up a fail safe to ensure we're flat before market close
# self.Schedule.On(self.DateRules.EveryDay(),
# self.TimeRules.BeforeMarketClose(self.symbol, 15),
# self.ExitPositions)
# Enforce time constraints
# if not (self.Time.hour == 9 and self.Time.minute == 31): # or not self.symbol in data
# return