Overall Statistics |
Total Trades 1735 Average Win 0.00% Average Loss 0% Compounding Annual Return 53.244% Drawdown 0.300% Expectancy 0 Net Profit 1.750% Sharpe Ratio 20.416 Probabilistic Sharpe Ratio 99.675% Loss Rate 0% Win Rate 100% Profit-Loss Ratio 0 Alpha 0.576 Beta 0.363 Annual Standard Deviation 0.03 Annual Variance 0.001 Information Ratio 13.75 Tracking Error 0.038 Treynor Ratio 1.667 Total Fees $2324.90 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, 7, 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.liquidContract = None self.timeToExpiryLimit = 5 * 24 * 60 * 60 # 5 days, in seconds self.minDelay = 1 * 60 # seconds self.maxDelay = 10 * 60 # seconds self.chaseBid = 60 # seconds self.contractsToBuy = 1 self.lastHour = 0 # hours #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) def OnMarginCallWarning(self): self.Error("You received a margin call warning. The assets will be liquidated to cover losses.") def OnData(self, slice): # Enforce time constraints # if not (self.Time.hour == 9 and self.Time.minute == 31): # or not self.symbol in data # return 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): self.exitPrice = 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: # send stop loss order if entry limit order is filled 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.exitPrice = self.entryTicket.AverageFillPrice + 0.25 self.exitTicket = self.LimitOrder(self.liquidContract.Symbol, -self.entryTicket.Quantity, self.exitPrice, "Exit Order") self.exitTime = self.Time # 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)