use median exchange rate when several exchange rates are available

This commit is contained in:
Lex Berezhny 2021-02-15 13:40:56 -05:00
parent 75ecea265d
commit db9856a8db
2 changed files with 26 additions and 9 deletions

View file

@ -2,6 +2,7 @@ import json
import time import time
import asyncio import asyncio
import logging import logging
from statistics import median
from decimal import Decimal from decimal import Decimal
from typing import Optional, Iterable, Type from typing import Optional, Iterable, Type
from aiohttp.client_exceptions import ContentTypeError from aiohttp.client_exceptions import ContentTypeError
@ -239,20 +240,23 @@ class ExchangeRateManager:
source.stop() source.stop()
def convert_currency(self, from_currency, to_currency, amount): def convert_currency(self, from_currency, to_currency, amount):
rates = [market.rate for market in self.market_feeds] log.debug(
log.debug("Converting %f %s to %s, rates: %s", amount, from_currency, to_currency, rates) "Converting %f %s to %s, rates: %s",
amount, from_currency, to_currency,
[market.rate for market in self.market_feeds]
)
if from_currency == to_currency: if from_currency == to_currency:
return round(amount, 8) return round(amount, 8)
rates = []
for market in self.market_feeds: for market in self.market_feeds:
if (market.has_rate and market.is_online and if (market.has_rate and market.is_online and
market.rate.currency_pair == (from_currency, to_currency)): market.rate.currency_pair == (from_currency, to_currency)):
return round(amount * Decimal(market.rate.spot), 8) rates.append(market.rate.spot)
for market in self.market_feeds:
if (market.has_rate and market.is_online and if rates:
market.rate.currency_pair[0] == from_currency): return round(amount * Decimal(median(rates)), 8)
return round(self.convert_currency(
market.rate.currency_pair[1], to_currency, amount * Decimal(market.rate.spot)), 8)
raise CurrencyConversionError( raise CurrencyConversionError(
f'Unable to convert {amount} from {from_currency} to {to_currency}') f'Unable to convert {amount} from {from_currency} to {to_currency}')

View file

@ -5,7 +5,8 @@ from lbry.schema.claim import Claim
from lbry.extras.daemon.exchange_rate_manager import ( from lbry.extras.daemon.exchange_rate_manager import (
ExchangeRate, ExchangeRateManager, CurrencyConversionError, ExchangeRate, ExchangeRateManager, CurrencyConversionError,
CryptonatorUSDFeed, CryptonatorBTCFeed, CryptonatorUSDFeed, CryptonatorBTCFeed,
BittrexUSDFeed, BittrexBTCFeed BittrexUSDFeed, BittrexBTCFeed,
CoinExBTCFeed
) )
from lbry.testcase import AsyncioTestCase, FakeExchangeRateManager, get_fake_exchange_rate_manager from lbry.testcase import AsyncioTestCase, FakeExchangeRateManager, get_fake_exchange_rate_manager
from lbry.error import InvalidExchangeRateResponseError from lbry.error import InvalidExchangeRateResponseError
@ -106,3 +107,15 @@ class ExchangeRateManagerTests(AsyncioTestCase):
manager.start() manager.start()
await asyncio.sleep(1) await asyncio.sleep(1)
self.addCleanup(manager.stop) self.addCleanup(manager.stop)
async def test_median_rate_used(self):
manager = ExchangeRateManager([BittrexBTCFeed, CryptonatorBTCFeed, CoinExBTCFeed])
for feed in manager.market_feeds:
feed.last_check = time()
bittrex, cryptonator, coinex = manager.market_feeds
bittrex.rate = ExchangeRate(bittrex.market, 1.0, time())
cryptonator.rate = ExchangeRate(cryptonator.market, 2.0, time())
coinex.rate = ExchangeRate(coinex.market, 3.0, time())
self.assertEqual(14.0, manager.convert_currency("BTC", "LBC", Decimal(7.0)))
cryptonator.rate.spot = 4.0
self.assertEqual(21.0, manager.convert_currency("BTC", "LBC", Decimal(7.0)))