/* * QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals. * Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ using QuantConnect.Data.Market; using QuantConnect.Logging; using QuantConnect.Securities; using System; namespace QuantConnect.Orders.Slippage { /// /// Represents a slippage model that is calculated by multiplying the price impact constant /// by the square of the ratio of the order to the total volume. /// public class VolumeShareSlippageModel : ISlippageModel { private readonly decimal _priceImpact; private readonly decimal _volumeLimit; /// /// Initializes a new instance of the class /// /// /// Defines how large of an impact the order will have on the price calculation public VolumeShareSlippageModel(decimal volumeLimit = 0.025m, decimal priceImpact = 0.1m) { _priceImpact = priceImpact; _volumeLimit = volumeLimit; } /// /// Slippage Model. Return a decimal cash slippage approximation on the order. /// public decimal GetSlippageApproximation(Security asset, Order order) { var lastData = asset.GetLastData(); if (lastData == null) return 0; var barVolume = 0m; var slippagePercent = _volumeLimit * _volumeLimit * _priceImpact; switch (lastData.DataType) { case MarketDataType.TradeBar: barVolume = ((TradeBar)lastData).Volume; break; case MarketDataType.QuoteBar: barVolume = order.Direction == OrderDirection.Buy ? ((QuoteBar)lastData).LastBidSize : ((QuoteBar)lastData).LastAskSize; break; default: throw new InvalidOperationException(Messages.VolumeShareSlippageModel.InvalidMarketDataType(lastData)); } // If volume is zero or negative, we use the maximum slippage percentage since the impact of any quantity is infinite // In FX/CFD case, we issue a warning and return zero slippage if (barVolume <= 0) { var securityType = asset.Symbol.ID.SecurityType; if (securityType == SecurityType.Cfd || securityType == SecurityType.Forex || securityType == SecurityType.Crypto) { Log.Error(Messages.VolumeShareSlippageModel.VolumeNotReportedForMarketDataType(securityType)); return 0; } Log.Error(Messages.VolumeShareSlippageModel.NegativeOrZeroBarVolume(barVolume, slippagePercent)); } else { // Ratio of the order to the total volume var volumeShare = Math.Min(order.AbsoluteQuantity / barVolume, _volumeLimit); slippagePercent = volumeShare * volumeShare * _priceImpact; } return slippagePercent * lastData.Value; } } }