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