Overall Statistics |
Total Trades 1 Average Win 0% Average Loss 0% Compounding Annual Return 33.876% Drawdown 0.400% Expectancy 0 Net Profit 1.422% Sharpe Ratio 7.753 Probabilistic Sharpe Ratio 91.731% Loss Rate 0% Win Rate 0% Profit-Loss Ratio 0 Alpha 0.21 Beta 0.137 Annual Standard Deviation 0.041 Annual Variance 0.002 Information Ratio -3.252 Tracking Error 0.139 Treynor Ratio 2.298 Total Fees $1.19 Estimated Strategy Capacity $20000000.00 |
import numpy as np class CustomModelsAlgorithm(QCAlgorithm): '''Demonstration of using custom fee, slippage, fill, and buying power models for modelling transactions in backtesting. QuantConnect allows you to model all orders as deeply and accurately as you need.''' def Initialize(self): self.SetStartDate(2013,10,1) # Set Start Date self.SetEndDate(2013,10,20) # Set End Date self.security = self.AddEquity("SPY", Resolution.Hour) self.spy = self.security.Symbol # set our models self.security.SetFeeModel(CustomFeeModel(self)) #self.security.SetFillModel(CustomFillModel(self)) #self.security.SetSlippageModel(CustomSlippageModel(self)) #self.security.SetBuyingPowerModel(CustomBuyingPowerModel(self)) def OnData(self, data): open_orders = self.Transactions.GetOpenOrders(self.spy) if len(open_orders) != 0: return if self.Time.day > 10 and self.security.Holdings.Quantity <= 0: quantity = self.CalculateOrderQuantity(self.spy, .5) self.Log(f"MarketOrder: {quantity}") self.MarketOrder(self.spy, quantity, True) # async needed for partial fill market orders elif self.Time.day > 20 and self.security.Holdings.Quantity >= 0: quantity = self.CalculateOrderQuantity(self.spy, -.5) self.Log(f"MarketOrder: {quantity}") self.MarketOrder(self.spy, quantity, True) # async needed for partial fill market orders # If we want to use methods from other models, you need to inherit from one of them class CustomFillModel(ImmediateFillModel): def __init__(self, algorithm): self.algorithm = algorithm self.absoluteRemainingByOrderId = {} self.random = Random(387510346) def MarketFill(self, asset, order): absoluteRemaining = order.AbsoluteQuantity if order.Id in self.absoluteRemainingByOrderId.keys(): absoluteRemaining = self.absoluteRemainingByOrderId[order.Id] fill = super().MarketFill(asset, order) absoluteFillQuantity = int(min(absoluteRemaining, self.random.Next(0, 2*int(order.AbsoluteQuantity)))) fill.FillQuantity = np.sign(order.Quantity) * absoluteFillQuantity if absoluteRemaining == absoluteFillQuantity: fill.Status = OrderStatus.Filled if self.absoluteRemainingByOrderId.get(order.Id): self.absoluteRemainingByOrderId.pop(order.Id) else: absoluteRemaining = absoluteRemaining - absoluteFillQuantity self.absoluteRemainingByOrderId[order.Id] = absoluteRemaining fill.Status = OrderStatus.PartiallyFilled self.algorithm.Log(f"CustomFillModel: {fill}") return fill class CustomFeeModel(FeeModel): def __init__(self, algorithm): self.algorithm = algorithm def GetOrderFee(self, parameters): # $0.0035 per share; Minimum of $0.35 per order minfee = max(0.35, parameters.Order.AbsoluteQuantity * 0.0035 ) # Maximum fee is 1.0% of trade value maxfee = parameters.Security.Price * parameters.Order.AbsoluteQuantity * 0.01 fee = min(minfee,maxfee) self.algorithm.Log("CustomFeeModel: " + str(fee)) return OrderFee(CashAmount(fee, "USD")) class CustomSlippageModel: def __init__(self, algorithm): self.algorithm = algorithm def GetSlippageApproximation(self, asset, order): # custom slippage math slippage = asset.Price * 0.0001 * np.log10(2*float(order.AbsoluteQuantity)) self.algorithm.Log(f"CustomSlippageModel: {slippage}") return slippage class CustomBuyingPowerModel(BuyingPowerModel): def __init__(self, algorithm): self.algorithm = algorithm def HasSufficientBuyingPowerForOrder(self, parameters): # custom behavior: this model will assume that there is always enough buying power hasSufficientBuyingPowerForOrderResult = HasSufficientBuyingPowerForOrderResult(True) self.algorithm.Log(f"CustomBuyingPowerModel: {hasSufficientBuyingPowerForOrderResult.IsSufficient}") return hasSufficientBuyingPowerForOrderResult