Search code examples
c#linqcurrency

How best to calculate derived currency rate conversions using C#/LINQ?


 class FxRate {
     string Base { get; set; }
     string Target  { get; set; }
     double Rate { get; set; }
 }

 private IList<FxRate> rates = new List<FxRate> {
          new FxRate {Base = "EUR", Target = "USD", Rate = 1.3668},
          new FxRate {Base = "GBP", Target = "USD", Rate = 1.5039},
          new FxRate {Base = "USD", Target = "CHF", Rate = 1.0694},
          new FxRate {Base = "CHF", Target = "SEK", Rate = 8.12}
          // ...
 };

Given a large yet incomplete list of exchange rates where all currencies appear at least once (either as a target or base currency): What algorithm would I use to be able to derive rates for exchanges that aren't directly listed?

I'm looking for a general purpose algorithm of the form:

 public double Rate(string baseCode, string targetCode, double currency)
 {
      return ...
 }

In the example above a derived rate would be GBP->CHF or EUR->SEK (which would require using the conversions for EUR->USD, USD->CHF, CHF->SEK)

Whilst I know how to do the conversions by hand I'm looking for a tidy way (perhaps using LINQ) to perform these derived conversions perhaps involving multiple currency hops, what's the nicest way to go about this?


Solution

  • First construct a graph of all your currencies:

    private Dictionary<string, List<string>> _graph
    public void ConstructGraph()
    {
        if (_graph == null) {
            _graph = new Dictionary<string, List<string>>();
            foreach (var rate in rates) {
                if (!_graph.ContainsKey(rate.Base))
                    _graph[rate.Base] = new List<string>();
                if (!_graph.ContainsKey(rate.Target))
                    _graph[rate.Target] = new List<string>();
    
                _graph[rate.Base].Add(rate.Target);
                _graph[rate.Target].Add(rate.Base);
            }
        }
    }
    

    Now traverse that graph using recursion:

    public double Rate(string baseCode, string targetCode)
    {
        if (_graph[baseCode].Contains(targetCode)) {
            // found the target code
            return GetKnownRate(baseCode, targetCode);
        }
        else {
            foreach (var code in _graph[baseCode]) {
                // determine if code can be converted to targetCode
                double rate = Rate(code, targetCode);
                if (rate != 0) // if it can than combine with returned rate
                    return rate * GetKnownRate(baseCode, code);
            }
        }
    
        return 0; // baseCode cannot be converted to the targetCode
    }
    public double GetKnownRate(string baseCode, string targetCode) 
    {
        var rate = rates.SingleOrDefault(fr => fr.Base == baseCode && fr.Target == targetCode);
        var rate_i rates.SingleOrDefault(fr => fr.Base == targetCode && fr.Target == baseCode));
        if (rate == null)
            return 1 / rate_i.Rate
        return rate.Rate;
    }
    

    Disclaimer: This is untested. Further, I'm sure this isn't the most performant approach to solve the problem (O(n) I think), but I believe it will work. There are a number of things you could add to improve the performance (e.g. saving every new combined rate calculation would eventually turn this into an effective O(1))