private ExchangeRateDto TryIndirectConversion( IReadOnlyCollection <ExchangeRateDto> exchangeRates, Currency fixedCurrency, Currency variableCurrency, DateTime dayOfConversion, ISystemProcessOperationRunRuleContext ruleCtx) { var fixedExchangeRates = this.GetExchangeRates(exchangeRates, fixedCurrency); var variableExchangeRates = this.GetExchangeRates(exchangeRates, variableCurrency); var sharedVariableRateInitial = fixedExchangeRates.FirstOrDefault( ier => variableExchangeRates.Select(ter => ter.VariableCurrency).Contains(ier.VariableCurrency)); if (sharedVariableRateInitial == null) { this._logger.LogError( $"could not find a shared common currency using a one step approach for {fixedCurrency.Code} and {variableCurrency.Code} on {dayOfConversion}"); ruleCtx.EventException( $"could not find a shared common currency using a one step approach for {fixedCurrency.Code} and {variableCurrency.Code} on {dayOfConversion}"); return(null); } var sharedVariableRateTarget = variableExchangeRates.FirstOrDefault( tec => string.Equals( tec.VariableCurrency, sharedVariableRateInitial.VariableCurrency, StringComparison.InvariantCultureIgnoreCase)); if (sharedVariableRateTarget == null) { this._logger.LogError( $"could not find a shared common currency using a one step approach for {fixedCurrency.Code} and {variableCurrency.Code} on {dayOfConversion}"); ruleCtx.EventException( $"could not find a shared common currency using a one step approach for {fixedCurrency.Code} and {variableCurrency.Code} on {dayOfConversion}"); return(null); } var reciprocalExchangeRate = // ReSharper disable once CompareOfFloatsByEqualityOperator sharedVariableRateTarget.Rate != 0 ? 1 / (decimal)sharedVariableRateTarget.Rate : 0; var completeRate = (double)reciprocalExchangeRate * sharedVariableRateInitial.Rate; return(new ExchangeRateDto { DateTime = sharedVariableRateTarget.DateTime, FixedCurrency = fixedCurrency.Code, VariableCurrency = variableCurrency.Code, Rate = completeRate }); }
private Money?TryIndirectConversion( IReadOnlyCollection <ExchangeRateDto> exchangeRates, Money initialMoney, Currency targetCurrency, DateTime dayOfConversion, ISystemProcessOperationRunRuleContext ruleCtx) { var initialExchangeRate = this.GetExchangeRates(exchangeRates, initialMoney.Currency); var targetExchangeRate = this.GetExchangeRates(exchangeRates, targetCurrency); var sharedVariableRateInitial = initialExchangeRate.FirstOrDefault( ier => targetExchangeRate.Select(ter => ter.VariableCurrency).Contains(ier.VariableCurrency)); if (sharedVariableRateInitial == null) { this._logger.LogError( $"could not find a shared common currency using a one step approach for {initialMoney.Currency.Code} and {targetCurrency.Code} on {dayOfConversion}"); ruleCtx.EventException( $"could not find a shared common currency using a one step approach for {initialMoney.Currency.Code} and {targetCurrency.Code} on {dayOfConversion}"); return(null); } var sharedVariableRateTarget = targetExchangeRate.FirstOrDefault( tec => string.Equals( tec.VariableCurrency, sharedVariableRateInitial.VariableCurrency, StringComparison.InvariantCultureIgnoreCase)); if (sharedVariableRateTarget == null) { this._logger.LogError( $"could not find a shared common currency using a one step approach for {initialMoney.Currency.Code} and {targetCurrency.Code} on {dayOfConversion}"); ruleCtx.EventException( $"could not find a shared common currency using a one step approach for {initialMoney.Currency.Code} and {targetCurrency.Code} on {dayOfConversion}"); return(null); } var variableCurrencyInitial = new Money( initialMoney.Value * (decimal)sharedVariableRateInitial.Rate, sharedVariableRateInitial.VariableCurrency); var reciprocalExchangeRate = // ReSharper disable once CompareOfFloatsByEqualityOperator sharedVariableRateTarget.Rate != 0 ? 1 / (decimal)sharedVariableRateTarget.Rate : 0; var fixedTargetCurrencyInitial = new Money( variableCurrencyInitial.Value * reciprocalExchangeRate, targetCurrency); return(fixedTargetCurrencyInitial); }
private ExchangeRateDto Convert( IReadOnlyCollection <ExchangeRateDto> exchangeRates, Currency fixedCurrency, Currency variableCurrency, DateTime dayOfConversion, ISystemProcessOperationRunRuleContext ruleCtx) { // direct exchange rate i.e. we want to do USD to GBP and we have USD / GBP var directConversion = this.TryDirectConversion(exchangeRates, fixedCurrency, variableCurrency); if (directConversion != null) { this._logger.LogInformation( $"was able to directly convert {fixedCurrency} to {variableCurrency} at rate of {directConversion.Rate} on {directConversion.DateTime}"); return(directConversion); } // reciprocal exchange rate i.e. we want to do USD to GBP but we have GBP / USD var reciprocalConversion = this.TryReciprocalConversion(exchangeRates, fixedCurrency, variableCurrency); if (reciprocalConversion != null) { this._logger.LogInformation( $"was able to reciprocally convert {fixedCurrency} to {variableCurrency} at rate of {reciprocalConversion.Rate} on {reciprocalConversion.DateTime}"); return(reciprocalConversion); } // implicit exchange rate i.e. we want to do EUR to GBP but we have EUR / USD and GBP / USD var indirectConversion = this.TryIndirectConversion( exchangeRates, fixedCurrency, variableCurrency, dayOfConversion, ruleCtx); if (indirectConversion == null) { this._logger.LogError( $"was unable to convert {fixedCurrency.Code} to {variableCurrency.Code} on {dayOfConversion}"); ruleCtx.EventException( $"was unable to convert {fixedCurrency.Code} to {variableCurrency.Code} on {dayOfConversion}"); return(null); } this._logger.LogInformation( $"was able to indirectly convert {fixedCurrency} to {variableCurrency} at rate of {indirectConversion.Rate} on {indirectConversion.DateTime}"); return(indirectConversion); }
private async Task <IReadOnlyCollection <ExchangeRateDto> > GetExchangeRatesNearestToDate( DateTime dayOfRate, ISystemProcessOperationRunRuleContext ruleCtx) { dayOfRate = dayOfRate.Date; var exchRate = await this._exchangeRateApiRepository.GetAsync(dayOfRate, dayOfRate); // cycle through last two weeks of exchange rates var offset = 0; var cycleDate = dayOfRate; while (!exchRate.ContainsKey(cycleDate) && offset < 15) { offset += 1; cycleDate = cycleDate.AddDays(-1); } if (offset > 14) { this._logger.LogError($"could not find an exchange rate in the date range around {dayOfRate}."); ruleCtx.EventException($"could not find an exchange rate in the date range around {dayOfRate}."); return(new ExchangeRateDto[0]); } if (!exchRate.TryGetValue(cycleDate, out var rates)) { this._logger.LogError( $"could not find an exchange rate in the date range around {dayOfRate} in the dictionary."); ruleCtx.EventException( $"could not find an exchange rate in the date range around {dayOfRate} in the dictionary."); return(new ExchangeRateDto[0]); } return(rates); }
public async Task <Money?> Convert( IReadOnlyCollection <Money> monies, Currency targetCurrency, DateTime dayOfConversion, ISystemProcessOperationRunRuleContext ruleCtx) { if (monies == null || !monies.Any()) { this._logger.LogInformation( $"received null or empty currency amounts. Returning 0 currency amount in target currency of {targetCurrency} for rule {ruleCtx?.Id()}"); return(new Money(0, targetCurrency)); } if (string.IsNullOrWhiteSpace(targetCurrency.Code)) { this._logger.LogError("asked to convert to a null or empty currency"); return(monies.Aggregate((i, o) => new Money(i.Value + o.Value, i.Currency))); } if (monies.All(ca => Equals(ca.Currency, targetCurrency))) { this._logger.LogInformation( "inferred all currency amounts matched the target currency. Aggregating trades and returning."); return(monies.Aggregate((i, o) => new Money(i.Value + o.Value, i.Currency))); } this._logger.LogInformation($"about to fetch exchange rates on {dayOfConversion}"); var rates = await this.ExchangeRates(dayOfConversion, ruleCtx); if (rates == null || !rates.Any()) { this._logger.LogError( $"unable to change rates to {targetCurrency.Code} on {dayOfConversion.ToShortDateString()} due to missing rates"); ruleCtx.EventException( $"unable to change rates to {targetCurrency.Code} on {dayOfConversion.ToShortDateString()} due to missing rates"); return(null); } var convertedToTargetCurrency = monies.Select( currency => this.Convert(rates, currency, targetCurrency, dayOfConversion, ruleCtx)).ToList(); var totalInConvertedCurrency = convertedToTargetCurrency.Where(cc => cc.HasValue).Select(cc => cc.Value) .Sum(cc => cc.Value); this._logger.LogInformation($"returning {totalInConvertedCurrency} ({targetCurrency})"); return(new Money(totalInConvertedCurrency, targetCurrency)); }
// fixed is basically from // variable is to // so EUR/USD 1.3225 means 1 euro buys 1.3225 dollars // with eur = fixed and usd = variable currencies public async Task <ExchangeRateDto> GetRate( Currency fixedCurrency, Currency variableCurrency, DateTime dayOfConversion, ISystemProcessOperationRunRuleContext ruleCtx) { if (string.IsNullOrWhiteSpace(fixedCurrency.Code) || string.IsNullOrWhiteSpace(variableCurrency.Code)) { this._logger.LogError( $"was asked to convert two currencies. Once of which was null or empty {fixedCurrency} {variableCurrency}"); return(null); } if (string.Equals(fixedCurrency.Code, variableCurrency.Code, StringComparison.InvariantCultureIgnoreCase)) { var noConversionRate = new ExchangeRateDto { DateTime = dayOfConversion, FixedCurrency = fixedCurrency.Code, VariableCurrency = variableCurrency.Code, Rate = 1 }; this._logger.LogInformation( $"was asked to convert two currencies but they were equal. Returning a rate of 1 for {fixedCurrency} and {variableCurrency}"); return(noConversionRate); } var rates = await this.GetExchangeRatesNearestToDate(dayOfConversion, ruleCtx); if (rates == null || !rates.Any()) { this._logger.LogError($"unable to find any rates on {dayOfConversion.ToShortDateString()}"); ruleCtx.EventException( $"unable to change rates from {fixedCurrency.Code} to {variableCurrency.Code} on {dayOfConversion.ToShortDateString()}"); return(null); } var rate = this.Convert(rates, fixedCurrency, variableCurrency, dayOfConversion, ruleCtx); this._logger.LogInformation( $"was asked to convert two currencies {fixedCurrency} and {variableCurrency} on {dayOfConversion}. Returning {rate.Rate} as the exchange rate"); return(rate); }
private Money?Convert( IReadOnlyCollection <ExchangeRateDto> exchangeRates, Money initialMoney, Currency targetCurrency, DateTime dayOfConversion, ISystemProcessOperationRunRuleContext ruleCtx) { if (Equals(initialMoney.Currency, targetCurrency)) { this._logger.LogInformation( $"asked to convert {initialMoney.Currency} to {targetCurrency} and found they were the same. Returning initial amount."); return(initialMoney); } // direct exchange rate i.e. we want to do USD to GBP and we have USD / GBP var directConversion = this.TryDirectConversion(exchangeRates, initialMoney, targetCurrency); if (directConversion != null) { this._logger.LogInformation( $"managed to directly convert {initialMoney.Currency} {initialMoney.Value} to {targetCurrency} {directConversion.Value.Value}."); return(directConversion); } this._logger.LogInformation( $"failed to directly convert {initialMoney.Currency} to {targetCurrency}. Trying reciprocal conversion."); // reciprocal exchange rate i.e. we want to do USD to GBP but we have GBP / USD var reciprocalConversion = this.TryReciprocalConversion(exchangeRates, initialMoney, targetCurrency); if (reciprocalConversion != null) { this._logger.LogInformation( $"managed to reciprocally convert {initialMoney.Currency} {initialMoney.Value} to {targetCurrency} {reciprocalConversion.Value.Value}."); return(reciprocalConversion); } this._logger.LogInformation( $"failed to reciprocally convert {initialMoney.Currency} to {targetCurrency}. Trying indirect conversion."); // implicit exchange rate i.e. we want to do EUR to GBP but we have EUR / USD and GBP / USD var indirectConversion = this.TryIndirectConversion( exchangeRates, initialMoney, targetCurrency, dayOfConversion, ruleCtx); if (indirectConversion == null) { this._logger.LogError( $"was unable to convert {initialMoney.Currency.Code} to {targetCurrency.Code} on {dayOfConversion} after attempting an indirect conversion. Returning null."); ruleCtx.EventException( $"was unable to convert {initialMoney.Currency.Code} to {targetCurrency.Code} on {dayOfConversion}"); return(null); } this._logger.LogInformation( $"managed to indirectly convert {initialMoney.Currency} {initialMoney.Value} to {targetCurrency} {indirectConversion.Value.Value}."); return(indirectConversion); }