Sunday, November 26, 2017

Real time currency conversion using web service for .Net

Sometimes you need to automatically convert currencies in the app. In order to do that you need to get current conversion ratios for appropriate currencies. There are many web services which provide this data: free and chargeable. In this post I will use free web service provided by Central bank of Russia, in your code you may use other services, e.g. service provided by Central European bank. There is convenient web service which returns currencies conversion ratios for the current day:

http://www.cbr.ru/scripts/XML_daily.asp?date_req=dd/MM/yyyy

E.g. if we want to get it for 26 Nov 2017 we need to use the following url:

http://www.cbr.ru/scripts/XML_daily.asp?date_req=26/11/2017

Here is result:

   1: <?xml version="1.0" encoding="windows-1251"?>
   2: <ValCurs Date="25.11.2017" name="Foreign Currency Market">
   3: <Valute ID="R01010">
   4:     <NumCode>036</NumCode>
   5:     <CharCode>AUD</CharCode>
   6:     <Nominal>1</Nominal>
   7:     <Name>Австралийский доллар</Name>
   8:     <Value>44,5778</Value>
   9: </Valute>
  10: <Valute ID="R01020A">
  11:     <NumCode>944</NumCode>
  12:     <CharCode>AZN</CharCode>
  13:     <Nominal>1</Nominal>
  14:     <Name>Азербайджанский манат</Name>
  15:     <Value>34,4609</Value>
  16: </Valute>
  17: <Valute ID="R01035">
  18:     <NumCode>826</NumCode>
  19:     <CharCode>GBP</CharCode>
  20:     <Nominal>1</Nominal>
  21:     <Name>Фунт стерлингов Соединенного королевства</Name>
  22:     <Value>77,9644</Value>
  23: </Valute>
  24: <Valute ID="R01060">
  25:     <NumCode>051</NumCode>
  26:     <CharCode>AMD</CharCode>
  27:     <Nominal>100</Nominal>
  28:     <Name>Армянских драмов</Name>
  29:     <Value>12,0933</Value>
  30: </Valute>
  31: <Valute ID="R01090B">
  32:     <NumCode>933</NumCode>
  33:     <CharCode>BYN</CharCode>
  34:     <Nominal>1</Nominal>
  35:     <Name>Белорусский рубль</Name>
  36:     <Value>29,2835</Value>
  37: </Valute>
  38: <Valute ID="R01100">
  39:     <NumCode>975</NumCode>
  40:     <CharCode>BGN</CharCode>
  41:     <Nominal>1</Nominal>
  42:     <Name>Болгарский лев</Name>
  43:     <Value>35,4717</Value>
  44: </Valute>
  45: <Valute ID="R01115">
  46:     <NumCode>986</NumCode>
  47:     <CharCode>BRL</CharCode>
  48:     <Nominal>1</Nominal>
  49:     <Name>Бразильский реал</Name>
  50:     <Value>18,1669</Value>
  51: </Valute>
  52: <Valute ID="R01135">
  53:     <NumCode>348</NumCode>
  54:     <CharCode>HUF</CharCode>
  55:     <Nominal>100</Nominal>
  56:     <Name>Венгерских форинтов</Name>
  57:     <Value>22,2428</Value>
  58: </Valute>
  59: <Valute ID="R01200">
  60:     <NumCode>344</NumCode>
  61:     <CharCode>HKD</CharCode>
  62:     <Nominal>10</Nominal>
  63:     <Name>Гонконгских долларов</Name>
  64:     <Value>74,9648</Value>
  65: </Valute>
  66: <Valute ID="R01215">
  67:     <NumCode>208</NumCode>
  68:     <CharCode>DKK</CharCode>
  69:     <Nominal>10</Nominal>
  70:     <Name>Датских крон</Name>
  71:     <Value>93,2302</Value>
  72: </Valute>
  73: <Valute ID="R01235">
  74:     <NumCode>840</NumCode>
  75:     <CharCode>USD</CharCode>
  76:     <Nominal>1</Nominal>
  77:     <Name>Доллар США</Name>
  78:     <Value>58,5318</Value>
  79: </Valute>
  80: <Valute ID="R01239">
  81:     <NumCode>978</NumCode>
  82:     <CharCode>EUR</CharCode>
  83:     <Nominal>1</Nominal>
  84:     <Name>Евро</Name>
  85:     <Value>69,3309</Value>
  86: </Valute>
  87: <Valute ID="R01270">
  88:     <NumCode>356</NumCode>
  89:     <CharCode>INR</CharCode>
  90:     <Nominal>100</Nominal>
  91:     <Name>Индийских рупий</Name>
  92:     <Value>90,5539</Value>
  93: </Valute>
  94: <Valute ID="R01335">
  95:     <NumCode>398</NumCode>
  96:     <CharCode>KZT</CharCode>
  97:     <Nominal>100</Nominal>
  98:     <Name>Казахстанских тенге</Name>
  99:     <Value>17,7114</Value>
 100: </Valute>
 101: <Valute ID="R01350">
 102:     <NumCode>124</NumCode>
 103:     <CharCode>CAD</CharCode>
 104:     <Nominal>1</Nominal>
 105:     <Name>Канадский доллар</Name>
 106:     <Value>45,9975</Value>
 107: </Valute>
 108: <Valute ID="R01370">
 109:     <NumCode>417</NumCode>
 110:     <CharCode>KGS</CharCode>
 111:     <Nominal>100</Nominal>
 112:     <Name>Киргизских сомов</Name>
 113:     <Value>83,9194</Value>
 114: </Valute>
 115: <Valute ID="R01375">
 116:     <NumCode>156</NumCode>
 117:     <CharCode>CNY</CharCode>
 118:     <Nominal>10</Nominal>
 119:     <Name>Китайских юаней</Name>
 120:     <Value>88,6349</Value>
 121: </Valute>
 122: <Valute ID="R01500">
 123:     <NumCode>498</NumCode>
 124:     <CharCode>MDL</CharCode>
 125:     <Nominal>10</Nominal>
 126:     <Name>Молдавских леев</Name>
 127:     <Value>33,7359</Value>
 128: </Valute>
 129: <Valute ID="R01535">
 130:     <NumCode>578</NumCode>
 131:     <CharCode>NOK</CharCode>
 132:     <Nominal>10</Nominal>
 133:     <Name>Норвежских крон</Name>
 134:     <Value>71,9099</Value>
 135: </Valute>
 136: <Valute ID="R01565">
 137:     <NumCode>985</NumCode>
 138:     <CharCode>PLN</CharCode>
 139:     <Nominal>1</Nominal>
 140:     <Name>Польский злотый</Name>
 141:     <Value>16,4902</Value>
 142: </Valute>
 143: <Valute ID="R01585F">
 144:     <NumCode>946</NumCode>
 145:     <CharCode>RON</CharCode>
 146:     <Nominal>1</Nominal>
 147:     <Name>Румынский лей</Name>
 148:     <Value>14,9107</Value>
 149: </Valute>
 150: <Valute ID="R01589">
 151:     <NumCode>960</NumCode>
 152:     <CharCode>XDR</CharCode>
 153:     <Nominal>1</Nominal>
 154:     <Name>СДР (специальные права заимствования)</Name>
 155:     <Value>82,5796</Value>
 156: </Valute>
 157: <Valute ID="R01625">
 158:     <NumCode>702</NumCode>
 159:     <CharCode>SGD</CharCode>
 160:     <Nominal>1</Nominal>
 161:     <Name>Сингапурский доллар</Name>
 162:     <Value>43,4502</Value>
 163: </Valute>
 164: <Valute ID="R01670">
 165:     <NumCode>972</NumCode>
 166:     <CharCode>TJS</CharCode>
 167:     <Nominal>10</Nominal>
 168:     <Name>Таджикских сомони</Name>
 169:     <Value>66,3656</Value>
 170: </Valute>
 171: <Valute ID="R01700J">
 172:     <NumCode>949</NumCode>
 173:     <CharCode>TRY</CharCode>
 174:     <Nominal>1</Nominal>
 175:     <Name>Турецкая лира</Name>
 176:     <Value>14,8728</Value>
 177: </Valute>
 178: <Valute ID="R01710A">
 179:     <NumCode>934</NumCode>
 180:     <CharCode>TMT</CharCode>
 181:     <Nominal>1</Nominal>
 182:     <Name>Новый туркменский манат</Name>
 183:     <Value>16,7243</Value>
 184: </Valute>
 185: <Valute ID="R01717">
 186:     <NumCode>860</NumCode>
 187:     <CharCode>UZS</CharCode>
 188:     <Nominal>10000</Nominal>
 189:     <Name>Узбекских сумов</Name>
 190:     <Value>72,3946</Value>
 191: </Valute>
 192: <Valute ID="R01720">
 193:     <NumCode>980</NumCode>
 194:     <CharCode>UAH</CharCode>
 195:     <Nominal>10</Nominal>
 196:     <Name>Украинских гривен</Name>
 197:     <Value>21,7429</Value>
 198: </Valute>
 199: <Valute ID="R01760">
 200:     <NumCode>203</NumCode>
 201:     <CharCode>CZK</CharCode>
 202:     <Nominal>10</Nominal>
 203:     <Name>Чешских крон</Name>
 204:     <Value>27,2844</Value>
 205: </Valute>
 206: <Valute ID="R01770">
 207:     <NumCode>752</NumCode>
 208:     <CharCode>SEK</CharCode>
 209:     <Nominal>10</Nominal>
 210:     <Name>Шведских крон</Name>
 211:     <Value>70,4829</Value>
 212: </Valute>
 213: <Valute ID="R01775">
 214:     <NumCode>756</NumCode>
 215:     <CharCode>CHF</CharCode>
 216:     <Nominal>1</Nominal>
 217:     <Name>Швейцарский франк</Name>
 218:     <Value>59,6350</Value>
 219: </Valute>
 220: <Valute ID="R01810">
 221:     <NumCode>710</NumCode>
 222:     <CharCode>ZAR</CharCode>
 223:     <Nominal>10</Nominal>
 224:     <Name>Южноафриканских рэндов</Name>
 225:     <Value>41,9715</Value>
 226: </Valute>
 227: <Valute ID="R01815">
 228:     <NumCode>410</NumCode>
 229:     <CharCode>KRW</CharCode>
 230:     <Nominal>1000</Nominal>
 231:     <Name>Вон Республики Корея</Name>
 232:     <Value>53,9061</Value>
 233: </Valute>
 234: <Valute ID="R01820">
 235:     <NumCode>392</NumCode>
 236:     <CharCode>JPY</CharCode>
 237:     <Nominal>100</Nominal>
 238:     <Name>Японских иен</Name>
 239:     <Value>52,5538</Value>
 240: </Valute>
 241: </ValCurs>

It shows conversion ratios related with Russian Ruble, e.g. if we check EUR:

   1: <Valute ID="R01239">
   2:     <NumCode>978</NumCode>
   3:     <CharCode>EUR</CharCode>
   4:     <Nominal>1</Nominal>
   5:     <Name>Евро</Name>
   6:     <Value>69,3309</Value>
   7: </Valute>

it shows that 1 (Nominal) Euro is equal to 69,3309 (Value) Rub. Having conversion ratios for all currencies and Ruble it is possible to get conversion ratios between any 2 currencies using simple proportions math. The following code shows how to convert value between RUR, EUR and USD in any order and direction:

   1:  
   2: public decimal? Convert(decimal sourceValue, Currency sourceCurrency, Currency targetCurrency)
   3: {
   4:     try
   5:     {
   6:         if (sourceCurrency == targetCurrency)
   7:         {
   8:             return sourceValue;
   9:         }
  10:  
  11:         var now = DateTime.Now;
  12:         string url = string.Format("http://www.cbr.ru/scripts/XML_daily.asp?date_req={0}",
  13:             now.ToString("dd/MM/yyyy"));
  14:         string str = this.webClient.Download(url, Encoding.GetEncoding("windows-1251"));
  15:         if (string.IsNullOrEmpty(str))
  16:         {
  17:             return null;
  18:         }
  19:         var doc = XDocument.Parse(str);
  20:         var elEUR = doc.Root.Elements("Valute").FirstOrDefault(e =>
  21:             e.Elements("CharCode").ElementAt(0).Value == "EUR");
  22:         decimal? nominalEUR =
  23:             DecimalHelper.ParseWithDotSeparator(elEUR.Element("Nominal").Value);
  24:         decimal? valueEUR =
  25:             DecimalHelper.ParseWithDotSeparator(elEUR.Element("Value").Value);
  26:  
  27:         var elUSD = doc.Root.Elements("Valute").FirstOrDefault(e =>
  28:             e.Elements("CharCode").ElementAt(0).Value == "USD");
  29:         decimal? nominalUSD =
  30:             DecimalHelper.ParseWithDotSeparator(elUSD.Element("Nominal").Value);
  31:         decimal? valueUSD =
  32:             DecimalHelper.ParseWithDotSeparator(elUSD.Element("Value").Value);
  33:  
  34:         double koefFromEURtoRUB = (double)valueEUR.Value / (double)nominalEUR.Value;
  35:         double koefFromUSDtoRUB = (double)valueUSD.Value / (double)nominalUSD.Value;
  36:  
  37:         double koefFromRUBtoEUR = 1 / koefFromEURtoRUB;
  38:         double koefFromUSDtoEUR = koefFromUSDtoRUB / koefFromEURtoRUB;
  39:  
  40:         double koefFromRUBtoUSD = 1 / koefFromUSDtoRUB;
  41:         double koefFromEURtoUSD = koefFromEURtoRUB / koefFromUSDtoRUB;
  42:  
  43:         double? koeff = null;
  44:         if (sourceCurrency == Currency.Rub)
  45:         {
  46:             if (targetCurrency == Currency.Eur)
  47:             {
  48:                 koeff = koefFromRUBtoEUR;
  49:             }
  50:             else if (targetCurrency == Currency.Usd)
  51:             {
  52:                 koeff = koefFromRUBtoUSD;
  53:             }
  54:         }
  55:         else if (sourceCurrency == Currency.Eur)
  56:         {
  57:             if (targetCurrency == Currency.Rub)
  58:             {
  59:                 koeff = koefFromEURtoRUB;
  60:             }
  61:             else if (targetCurrency == Currency.Usd)
  62:             {
  63:                 koeff = koefFromEURtoUSD;
  64:             }
  65:         }
  66:         else if (sourceCurrency == Currency.Usd)
  67:         {
  68:             if (targetCurrency == Currency.Rub)
  69:             {
  70:                 koeff = koefFromUSDtoRUB;
  71:             }
  72:             else if (targetCurrency == Currency.Eur)
  73:             {
  74:                 koeff = koefFromUSDtoEUR;
  75:             }
  76:         }
  77:         if (koeff == null)
  78:         {
  79:             return null;
  80:         }
  81:  
  82:         var targetValue = (decimal)Math.Round(((double)sourceValue) * koeff.Value, 2);
  83:         return targetValue;
  84:     }
  85:     catch (Exception x)
  86:     {
  87:         return null;
  88:     }
  89: }

Helper classes WebClient and DecimalHelper look like this:

   1: public class WebClient
   2: {
   3:     public string Download(string url, Encoding encoding)
   4:     {
   5:         try
   6:         {
   7:             if (string.IsNullOrEmpty(url))
   8:             {
   9:                 return string.Empty;
  10:             }
  11:             byte[] data;
  12:             using (var webClient = new System.Net.WebClient())
  13:             {
  14:                 data = webClient.DownloadData(url);
  15:             }
  16:             if (data == null)
  17:             {
  18:                 return string.Empty;
  19:             }
  20:             return encoding.GetString(data);
  21:         }
  22:         catch (Exception x)
  23:         {
  24:             return string.Empty;
  25:         }
  26:     }
  27: }

and

   1: public class DecimalHelper
   2: {
   3:     public static decimal? ParseWithDotSeparator(string val)
   4:     {
   5:         if (string.IsNullOrEmpty(val))
   6:         {
   7:             return null;
   8:         }
   9:         return decimal.Parse(val.Replace(",", "."), CultureInfo.InvariantCulture);
  10:     }
  11: }

Convert method receives 3 parameters:

  1. source value
  2. source currency
  3. target currency

At first we download current ratios from web service (lines 10-17). Then we calculate ratios for all required conversions EUR to RUR, USD to RUR, RUR to EUR, USD to EUR, RUR to USD, EUR to USD (lines 35-42) and choice correct ratio depending on arguments. If you will compare result with one get from Google (try to google e.g. eur to usd – Google will show conversion calculator), you will see that it is almost the same. Of course if conversion accuracy is crucial for your app then you need to use another web service which is updated more frequently.

No comments:

Post a Comment