/*
* 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;
}
}
}