converting from USD, BTC to LBC is now done via several exchange rate providers: Bittrex, Cryptonator, CoinEx, hotbit and UPbit

This commit is contained in:
Lex Berezhny 2021-02-10 13:29:05 -05:00
parent b13a121915
commit dee494e12f
4 changed files with 105 additions and 45 deletions

View file

@ -58,9 +58,12 @@ class MarketFeed:
raise NotImplementedError() raise NotImplementedError()
async def get_response(self): async def get_response(self):
async with aiohttp_request('get', self.url, params=self.params, timeout=self.request_timeout) as response: headers = {"User-Agent": "lbrynet"}
async with aiohttp_request(
'get', self.url, params=self.params, timeout=self.request_timeout, headers=headers
) as response:
try: try:
self._last_response = await response.json() self._last_response = await response.json(content_type=None)
except ContentTypeError as e: except ContentTypeError as e:
self._last_response = {} self._last_response = {}
log.warning("Could not parse exchange rate response from %s: %s", self.name, e.message) log.warning("Could not parse exchange rate response from %s: %s", self.name, e.message)
@ -104,10 +107,10 @@ class MarketFeed:
self.event.clear() self.event.clear()
class BittrexFeed(MarketFeed): class BaseBittrexFeed(MarketFeed):
name = "Bittrex" name = "Bittrex"
market = "BTCLBC" market = None
url = "https://api.bittrex.com/v3/markets/LBC-BTC/ticker" url = None
fee = 0.0025 fee = 0.0025
def get_rate_from_response(self, json_response): def get_rate_from_response(self, json_response):
@ -116,49 +119,103 @@ class BittrexFeed(MarketFeed):
return 1.0 / float(json_response['lastTradeRate']) return 1.0 / float(json_response['lastTradeRate'])
class LBRYFeed(MarketFeed): class BittrexBTCFeed(BaseBittrexFeed):
name = "lbry.com"
market = "BTCLBC" market = "BTCLBC"
url = "https://api.lbry.com/lbc/exchange_rate" url = "https://api.bittrex.com/v3/markets/LBC-BTC/ticker"
class BittrexUSDFeed(BaseBittrexFeed):
market = "USDLBC"
url = "https://api.bittrex.com/v3/markets/LBC-USD/ticker"
class BaseCryptonatorFeed(MarketFeed):
name = "Cryptonator"
market = None
url = None
def get_rate_from_response(self, json_response): def get_rate_from_response(self, json_response):
if 'data' not in json_response: if 'ticker' not in json_response or 'price' not in json_response['ticker']:
raise InvalidExchangeRateResponseError(self.name, 'result not found')
return 1.0 / json_response['data']['lbc_btc']
class LBRYBTCFeed(LBRYFeed):
market = "USDBTC"
def get_rate_from_response(self, json_response):
if 'data' not in json_response:
raise InvalidExchangeRateResponseError(self.name, 'result not found')
return 1.0 / json_response['data']['btc_usd']
class CryptonatorFeed(MarketFeed):
name = "cryptonator.com"
market = "BTCLBC"
url = "https://api.cryptonator.com/api/ticker/btc-lbc"
def get_rate_from_response(self, json_response):
if 'ticker' not in json_response or len(json_response['ticker']) == 0 or \
'success' not in json_response or json_response['success'] is not True:
raise InvalidExchangeRateResponseError(self.name, 'result not found') raise InvalidExchangeRateResponseError(self.name, 'result not found')
return float(json_response['ticker']['price']) return float(json_response['ticker']['price'])
class CryptonatorBTCFeed(CryptonatorFeed): class CryptonatorBTCFeed(BaseCryptonatorFeed):
market = "USDBTC" market = "BTCLBC"
url = "https://api.cryptonator.com/api/ticker/usd-btc" url = "https://api.cryptonator.com/api/ticker/btc-lbc"
class CryptonatorUSDFeed(BaseCryptonatorFeed):
market = "USDLBC"
url = "https://api.cryptonator.com/api/ticker/usd-lbc"
class BaseCoinExFeed(MarketFeed):
name = "CoinEx"
market = None
url = None
def get_rate_from_response(self, json_response):
if 'data' not in json_response or \
'ticker' not in json_response['data'] or \
'last' not in json_response['data']['ticker']:
raise InvalidExchangeRateResponseError(self.name, 'result not found')
return 1.0 / float(json_response['data']['ticker']['last'])
class CoinExBTCFeed(BaseCoinExFeed):
market = "BTCLBC"
url = "https://api.coinex.com/v1/market/ticker?market=LBCBTC"
class CoinExUSDFeed(BaseCoinExFeed):
market = "USDLBC"
url = "https://api.coinex.com/v1/market/ticker?market=LBCUSDT"
class BaseHotbitFeed(MarketFeed):
name = "hotbit"
market = None
url = "https://api.hotbit.io/api/v1/market.last"
def get_rate_from_response(self, json_response):
if 'result' not in json_response:
raise InvalidExchangeRateResponseError(self.name, 'result not found')
return 1.0 / float(json_response['result'])
class HotbitBTCFeed(BaseHotbitFeed):
market = "BTCLBC"
params = {"market": "LBC/BTC"}
class HotbitUSDFeed(BaseHotbitFeed):
market = "USDLBC"
params = {"market": "LBC/USDT"}
class UPbitBTCFeed(MarketFeed):
name = "UPbit"
market = "BTCLBC"
url = "https://api.upbit.com/v1/ticker"
params = {"markets": "BTC-LBC"}
def get_rate_from_response(self, json_response):
if len(json_response) != 1 or 'trade_price' not in json_response[0]:
raise InvalidExchangeRateResponseError(self.name, 'result not found')
return 1.0 / float(json_response[0]['trade_price'])
FEEDS: Iterable[Type[MarketFeed]] = ( FEEDS: Iterable[Type[MarketFeed]] = (
LBRYFeed, BittrexBTCFeed,
LBRYBTCFeed, BittrexUSDFeed,
BittrexFeed, CryptonatorBTCFeed,
# CryptonatorFeed, CryptonatorUSDFeed,
# CryptonatorBTCFeed, CoinExBTCFeed,
CoinExUSDFeed,
HotbitBTCFeed,
HotbitUSDFeed,
UPbitBTCFeed,
) )

View file

@ -29,7 +29,7 @@ from lbry.extras.daemon.components import (
) )
from lbry.extras.daemon.componentmanager import ComponentManager from lbry.extras.daemon.componentmanager import ComponentManager
from lbry.extras.daemon.exchange_rate_manager import ( from lbry.extras.daemon.exchange_rate_manager import (
ExchangeRateManager, ExchangeRate, LBRYFeed, LBRYBTCFeed ExchangeRateManager, ExchangeRate, BittrexBTCFeed, BittrexUSDFeed
) )
from lbry.extras.daemon.storage import SQLiteStorage from lbry.extras.daemon.storage import SQLiteStorage
from lbry.blob.blob_manager import BlobManager from lbry.blob.blob_manager import BlobManager
@ -282,8 +282,8 @@ class FakeExchangeRateManager(ExchangeRateManager):
def get_fake_exchange_rate_manager(rates=None): def get_fake_exchange_rate_manager(rates=None):
return FakeExchangeRateManager( return FakeExchangeRateManager(
[LBRYFeed(), LBRYBTCFeed()], [BittrexBTCFeed(), BittrexUSDFeed()],
rates or {'BTCLBC': 3.0, 'USDBTC': 2.0} rates or {'BTCLBC': 3.0, 'USDLBC': 2.0}
) )

View file

@ -375,7 +375,7 @@ def get_ssl_context() -> ssl.SSLContext:
@contextlib.asynccontextmanager @contextlib.asynccontextmanager
async def aiohttp_request(method, url, **kwargs) -> typing.AsyncContextManager[aiohttp.ClientResponse]: async def aiohttp_request(method, url, **kwargs) -> typing.AsyncContextManager[aiohttp.ClientResponse]:
async with aiohttp.ClientSession() as session: async with aiohttp.ClientSession() as session:
async with session.request(method, url, ssl=get_ssl_context(), **kwargs) as response: async with session.request(method, url, **kwargs) as response:
yield response yield response

View file

@ -5,7 +5,6 @@ from lbry.extras.daemon.exchange_rate_manager import ExchangeRate, ExchangeRateM
class TestExchangeRateManager(AsyncioTestCase): class TestExchangeRateManager(AsyncioTestCase):
async def test_exchange_rate_manager(self): async def test_exchange_rate_manager(self):
# TODO: re-enable cryptonator.com
manager = ExchangeRateManager(FEEDS) manager = ExchangeRateManager(FEEDS)
manager.start() manager.start()
self.addCleanup(manager.stop) self.addCleanup(manager.stop)
@ -16,6 +15,10 @@ class TestExchangeRateManager(AsyncioTestCase):
for feed in manager.market_feeds: for feed in manager.market_feeds:
self.assertTrue(feed.is_online) self.assertTrue(feed.is_online)
self.assertIsInstance(feed.rate, ExchangeRate) self.assertIsInstance(feed.rate, ExchangeRate)
lbc = manager.convert_currency('USD', 'LBC', Decimal('0.01')) # print(f'{feed.name} - {feed.market} - {feed.rate.spot}')
self.assertGreaterEqual(lbc, 0.01) lbc = manager.convert_currency('USD', 'LBC', Decimal('1.0'))
self.assertGreaterEqual(lbc, 2.0)
self.assertLessEqual(lbc, 10.0) self.assertLessEqual(lbc, 10.0)
lbc = manager.convert_currency('BTC', 'LBC', Decimal('0.01'))
self.assertGreaterEqual(lbc, 1_000)
self.assertLessEqual(lbc, 4_000)