19 using System.Collections.Generic;
38 public Security RateSecurity {
get; }
43 public bool Inverted {
get; }
50 public Step(
Security rateSecurity,
bool inverted)
52 RateSecurity = rateSecurity;
57 private readonly List<Step> _steps;
59 private decimal _conversionRate;
60 private bool _conversionRateNeedsUpdate;
84 if (_conversionRateNeedsUpdate)
86 var newConversionRate = 1m;
87 var stepWithoutDataFound =
false;
89 _steps.ForEach(step =>
91 if (stepWithoutDataFound)
96 var lastData = step.RateSecurity.GetLastData();
97 if (lastData ==
null || lastData.Price == 0m)
99 newConversionRate = 0m;
100 stepWithoutDataFound = true;
106 newConversionRate /= lastData.Price;
110 newConversionRate *= lastData.Price;
114 _conversionRateNeedsUpdate =
false;
115 _conversionRate = newConversionRate;
119 return _conversionRate;
123 if (_conversionRate != value)
126 _conversionRate = value;
127 _conversionRateNeedsUpdate =
false;
160 _conversionRateNeedsUpdate =
true;
174 string sourceCurrency,
175 string destinationCurrency,
176 IList<Security> existingSecurities,
177 IEnumerable<Symbol> potentialSymbols,
178 Func<Symbol, Security> makeNewSecurity)
180 var allSymbols = existingSecurities.Select(sec => sec.Symbol).Concat(potentialSymbols)
184 var securitiesBySymbol = existingSecurities.Aggregate(
new Dictionary<Symbol, Security>(),
185 (mapping, security) =>
187 if (!mapping.ContainsKey(security.Symbol))
189 mapping[security.Symbol] = security;
196 foreach (var potentialConversionRateSymbol
in allSymbols)
198 var leg1Match = potentialConversionRateSymbol.ComparePair(sourceCurrency, destinationCurrency);
207 CreateStep(potentialConversionRateSymbol, inverted, securitiesBySymbol, makeNewSecurity)
212 foreach (var potentialConversionRateSymbol1
in allSymbols)
214 var middleCurrency = potentialConversionRateSymbol1.CurrencyPairDual(sourceCurrency);
215 if (middleCurrency ==
null)
220 foreach (var potentialConversionRateSymbol2
in allSymbols)
222 var leg2Match = potentialConversionRateSymbol2.ComparePair(middleCurrency, destinationCurrency);
229 var steps =
new List<Step>(2);
233 string quoteCurrency;
236 potentialConversionRateSymbol1,
240 steps.Add(CreateStep(potentialConversionRateSymbol1,
241 sourceCurrency == quoteCurrency,
246 steps.Add(CreateStep(potentialConversionRateSymbol2,
255 throw new ArgumentException(
256 $
"No conversion path found between source currency {sourceCurrency} and destination currency {destinationCurrency}");
266 private static Step CreateStep(
269 IDictionary<Symbol, Security> existingSecurities,
270 Func<Symbol, Security> makeNewSecurity)
273 if (existingSecurities.TryGetValue(symbol, out security))
275 return new Step(security, inverted);
278 return new Step(makeNewSecurity(symbol), inverted);