17 using System.Collections.Generic;
25 using MathNet.Numerics.Statistics;
44 private readonly
bool _nonNegative;
45 private readonly
double _latency;
46 private readonly
double _impactTime;
47 private readonly
double _alpha;
48 private readonly
double _beta;
49 private readonly
double _gamma;
50 private readonly
double _eta;
51 private readonly
double _delta;
52 private readonly Random _random;
53 private SymbolData _symbolData;
69 double impactTime = 1800d,
double alpha = 0.891d,
double beta = 0.600d,
70 double gamma = 0.314d,
double eta = 0.142d,
double delta = 0.267d,
75 throw new Exception(
"Latency cannot be less than or equal to 0.");
79 throw new Exception(
"impactTime cannot be less than or equal to 0.");
82 _algorithm = algorithm;
83 _nonNegative = nonNegative;
85 _impactTime = impactTime;
91 _random =
new(randomSeed);
101 throw new Exception($
"Asset of {asset.Type} is not supported as MarketImpactSlippageModel requires volume data");
104 if (_symbolData ==
null)
106 _symbolData =
new SymbolData(_algorithm, asset, _latency, _impactTime);
109 if (_symbolData.AverageVolume == 0d)
115 var nu = (double)order.
AbsoluteQuantity / _symbolData.ExecutionTime / _symbolData.AverageVolume;
117 var liquidityAdjustment = asset.
Fundamentals.HasFundamentalData && asset.
Fundamentals.CompanyProfile.SharesOutstanding !=
default ?
118 Math.Pow(asset.
Fundamentals.CompanyProfile.SharesOutstanding / _symbolData.AverageVolume, _delta) :
121 var noise = _symbolData.Sigma * Math.Sqrt(_symbolData.ImpactTime);
124 var permanentImpact = _symbolData.Sigma * _symbolData.ExecutionTime * G(nu) * liquidityAdjustment + SampleGaussian() * noise;
126 var temporaryImpact = _symbolData.Sigma * H(nu) + SampleGaussian() * noise;
128 var realizedImpact = temporaryImpact + permanentImpact * 0.5d;
131 return SlippageFromImpactEstimation(realizedImpact) * asset.
Price;
139 private double G(
double absoluteOrderQuantity)
141 return _gamma * Math.Pow(absoluteOrderQuantity, _alpha);
149 private double H(
double absoluteOrderQuantity)
151 return _eta * Math.Pow(absoluteOrderQuantity, _beta);
159 private decimal SlippageFromImpactEstimation(
double impact)
162 var ultimateSlippage = (impact * _random.NextDouble()).SafeDecimalCast();
164 ultimateSlippage = Math.Min(ultimateSlippage, 1m);
168 return Math.Max(0m, ultimateSlippage);
171 return ultimateSlippage;
174 private double SampleGaussian(
double location = 0d,
double scale = 1d)
176 var randomVariable1 = 1 - _random.NextDouble();
177 var randomVariable2 = 1 - _random.NextDouble();
179 var deviation = Math.Sqrt(-2.0 * Math.Log(randomVariable1)) * Math.Cos(2.0 * Math.PI * randomVariable2);
180 return deviation * scale + location;
184 internal class SymbolData
187 private readonly Symbol _symbol;
192 public double Sigma {
get;
internal set; }
194 public double AverageVolume {
get;
internal set; }
196 public double ExecutionTime {
get;
internal set; }
198 public double ImpactTime {
get;
internal set; }
200 public SymbolData(
IAlgorithm algorithm,
Security asset,
double latency,
double impactTime)
202 _algorithm = algorithm;
206 _consolidator.DataConsolidated += OnDataConsolidated;
209 var configs = algorithm
213 var configToUse = configs.Where(x => x.TickType ==
TickType.Trade).First();
216 var historyRequest = historyRequestFactory.CreateHistoryRequest(configToUse,
217 algorithm.
Time - TimeSpan.FromDays(370),
223 _consolidator.Update(bar.Bars[_symbol]);
230 ExecutionTime = TimeSpan.FromSeconds(latency).TotalDays / normalTradeDayLength;
232 var adjustedImpactTime = TimeSpan.FromSeconds(impactTime).TotalDays / normalTradeDayLength;
233 ImpactTime = ExecutionTime + adjustedImpactTime;
236 public void OnDataConsolidated(
object _,
TradeBar bar)
238 _prices.Add(bar.
Close);
241 if (_prices.Samples < 2)
246 var rocp =
new double[_prices.Samples - 1];
247 for (var i = 0; i < _prices.Samples - 1; i++)
249 if (_prices[i + 1] == 0)
continue;
251 var roc = (_prices[i] - _prices[i + 1]) / _prices[i + 1];
252 rocp[i] = (double)roc;
255 var variance = rocp.Variance();
256 Sigma = Math.Sqrt(variance);
257 AverageVolume = (double)_volumes.Average();
260 public void Dispose()
265 _consolidator.DataConsolidated -= OnDataConsolidated;
266 _algorithm.SubscriptionManager.RemoveConsolidator(_symbol, _consolidator);