What is the best way to calculate exchange 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 but incomplete list of exchange rates, where all currencies appear at least once (either as the target or base currency): Which algorithm would I use to be able to receive bets for exchanges that are not directly indicated?

I am looking for a general-purpose form algorithm:

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

In the above example, the derivative rate will be GBP-> CHF or EUR-> SEK (which will require the use of conversions for EUR-> USD, USD-> CHF, CHF-> SEK)

While I know how to do conversions manually, I am looking for a neat way (possibly using LINQ) to perform these derivative conversions, possibly involving several changes in the currency, what is the best way to do this?

+3
6

:

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);
        }
    }
}

:

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

: . , , (O (n), ), , . , (, O (1))

+2

, ? , - ( ):

var conversionsToUSD = new Dictionary<string, decimal>();

public decimal Rate ( string baseCode, string targetCode )
{
    if ( targetCode == "USD" )
        return conversionsToUSD[baseCode];

    if ( baseCode == "USD" )
        return 1 / conversionsToUSD[targetCode];

    return conversionsToUSD[baseCode] / conversionsToUSD[targetCode]
}

, . I.e., EUR- > USD- > GBP, , EUR- > GBP. , .

+2

!

-, / ..NET Decimal type ! , , Fx .

, , , / , Target [ ] . , , .

, [] ,

  • , ( ), () . ( , 2, , , ).
  • ( , ), ...
  • , .
  • ... , ;-)
+1

, " "... .

: List<List<FxRate>> res = Rates("EUR", "CHF"); {EUR-USD, USD-CHF}.
!:)

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

    private List<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}
                                        // ...
                                    };

    public List<List<FxRate>> Rates(string baseCode, string targetCode)
    {
        return Rates(baseCode, targetCode, rates.ToArray());
    }
    public List<List<FxRate>> Rates(string baseCode, string targetCode, FxRate[] toSee)
    {
        List<List<FxRate>> results = new List<List<FxRate>>();

        List<FxRate> possible = toSee.Where(r => r.Base == baseCode).ToList();
        List<FxRate> hits = possible.Where(p => p.Target == targetCode).ToList();
        if (hits.Count > 0)
        {
            possible.RemoveAll(hits.Contains);
            results.AddRange(hits.Select(hit => new List<FxRate> { hit }));
        }

        FxRate[] newToSee = toSee.Where( item => !possible.Contains(item)).ToArray();
        foreach (FxRate posRate in possible)
        {
            List<List<FxRate>> otherConversions = Rates(posRate.Target, targetCode, newToSee);
            FxRate rate = posRate;
            otherConversions.ForEach(result => result.Insert(0, rate));
            results.AddRange(otherConversions);
        }
        return results;
    }

?

PS: double minConvertion = res.Min(r => r.Sum(convertion => convertion.Rate));

+1

, , Dijkstra - , . , , , LINQ. ( , , , , , .)

, , , (.. USD > EUR USD > CHF, EUR > CHF ), - . , , LINQ.

0
source

Generate all of them and cache them. At a given initial setting, this function will generate all existing pairs (inside the same list) without graphs or recursion, by simply expanding the original list as it is iterated.

public static void CrossRates(List<FxRate> rates)
{
    for (int i = 0; i < rates.Count; i++)
    {
        FxRate rate = rates[i];
        for (int j = i + 1; j < rates.Count; j++)
        {
            FxRate rate2 = rates[j];
            FxRate cross = CanCross(rate, rate2);
            if (cross != null)
                if (rates.FirstOrDefault(r => r.Ccy1.Equals(cross.Ccy1) && r.Ccy2.Equals(cross.Ccy2)) == null)
                    rates.Add(cross);
        }
    }
}

This utility function will generate individual cross-speed.

public static FxRate CanCross(FxRate r1, FxRate r2)
{
    FxRate nr = null;

    if (r1.Ccy1.Equals(r2.Ccy1) && r1.Ccy2.Equals(r2.Ccy2) ||
        r1.Ccy1.Equals(r2.Ccy2) && r1.Ccy2.Equals(r2.Ccy1)
        ) return null; // Same with same.

    if (r1.Ccy1.Equals(r2.Ccy1))
    { // a/b / a/c = c/b
        nr = new FxRate()
        {
            Ccy1 = r2.Ccy2,
            Ccy2 = r1.Ccy2,
            Rate = r1.Rate / r2.Rate
        };
    }
    else if (r1.Ccy1.Equals(r2.Ccy2))
    {
        // a/b * c/a = c/b
        nr = new FxRate()
        {
            Ccy1 = r2.Ccy1,
            Ccy2 = r1.Ccy2,
            Rate = r2.Rate * r1.Rate
        };
    }
    else if (r1.Ccy2.Equals(r2.Ccy2))
    {
        // a/c / b/c = a/b
        nr = new FxRate()
        {
            Ccy1 = r1.Ccy1,
            Ccy2 = r2.Ccy1,
            Rate = r1.Rate / r2.Rate
        };
    }
    else if (r1.Ccy2.Equals(r2.Ccy1))
    {
        // a/c * c/b = a/b
        nr = new FxRate()
        {
            Ccy1 = r1.Ccy1,
            Ccy2 = r2.Ccy2,
            Rate = r1.Rate * r2.Rate
        };
    }
    return nr;
}
0
source

Source: https://habr.com/ru/post/1736545/


All Articles