Browse Source

exchange_rate: some clean-up and fixes

- generation of currencies.json was broken
- removed some dead exchanges
sqlite_db
SomberNight 6 years ago
parent
commit
4b3a285871
No known key found for this signature in database GPG Key ID: B33B5F232C6271E9
  1. 103
      electrum/exchange_rate.py
  2. 24
      electrum/network.py
  3. 2
      electrum/util.py

103
electrum/exchange_rate.py

@ -8,10 +8,11 @@ import time
import csv import csv
import decimal import decimal
from decimal import Decimal from decimal import Decimal
import concurrent.futures
import traceback import traceback
from typing import Sequence from typing import Sequence
from aiorpcx.curio import timeout_after, TaskTimeout, TaskGroup
from .bitcoin import COIN from .bitcoin import COIN
from .i18n import _ from .i18n import _
from .util import (PrintError, ThreadJob, make_dir, log_exceptions, from .util import (PrintError, ThreadJob, make_dir, log_exceptions,
@ -40,14 +41,18 @@ class ExchangeBase(PrintError):
async def get_raw(self, site, get_string): async def get_raw(self, site, get_string):
# APIs must have https # APIs must have https
url = ''.join(['https://', site, get_string]) url = ''.join(['https://', site, get_string])
async with make_aiohttp_session(Network.get_instance().proxy) as session: network = Network.get_instance()
proxy = network.proxy if network else None
async with make_aiohttp_session(proxy) as session:
async with session.get(url) as response: async with session.get(url) as response:
return await response.text() return await response.text()
async def get_json(self, site, get_string): async def get_json(self, site, get_string):
# APIs must have https # APIs must have https
url = ''.join(['https://', site, get_string]) url = ''.join(['https://', site, get_string])
async with make_aiohttp_session(Network.get_instance().proxy) as session: network = Network.get_instance()
proxy = network.proxy if network else None
async with make_aiohttp_session(proxy) as session:
async with session.get(url) as response: async with session.get(url) as response:
# set content_type to None to disable checking MIME type # set content_type to None to disable checking MIME type
return await response.json(content_type=None) return await response.json(content_type=None)
@ -60,7 +65,6 @@ class ExchangeBase(PrintError):
def name(self): def name(self):
return self.__class__.__name__ return self.__class__.__name__
@log_exceptions
async def update_safe(self, ccy): async def update_safe(self, ccy):
try: try:
self.print_error("getting fx quotes for", ccy) self.print_error("getting fx quotes for", ccy)
@ -71,9 +75,6 @@ class ExchangeBase(PrintError):
self.quotes = {} self.quotes = {}
self.on_quotes() self.on_quotes()
def update(self, ccy):
asyncio.get_event_loop().create_task(self.update_safe(ccy))
def read_historical_rates(self, ccy, cache_dir): def read_historical_rates(self, ccy, cache_dir):
filename = os.path.join(cache_dir, self.name() + '_'+ ccy) filename = os.path.join(cache_dir, self.name() + '_'+ ccy)
if os.path.exists(filename): if os.path.exists(filename):
@ -123,8 +124,8 @@ class ExchangeBase(PrintError):
def historical_rate(self, ccy, d_t): def historical_rate(self, ccy, d_t):
return self.history.get(ccy, {}).get(d_t.strftime('%Y-%m-%d'), 'NaN') return self.history.get(ccy, {}).get(d_t.strftime('%Y-%m-%d'), 'NaN')
def get_currencies(self): async def get_currencies(self):
rates = self.get_rates('') rates = await self.get_rates('')
return sorted([str(a) for (a, b) in rates.items() if b is not None and len(a)==3]) return sorted([str(a) for (a, b) in rates.items() if b is not None and len(a)==3])
class BitcoinAverage(ExchangeBase): class BitcoinAverage(ExchangeBase):
@ -229,20 +230,6 @@ class BlockchainInfo(ExchangeBase):
return dict([(r, Decimal(json[r]['15m'])) for r in json]) return dict([(r, Decimal(json[r]['15m'])) for r in json])
class BTCChina(ExchangeBase):
async def get_rates(self, ccy):
json = await self.get_json('data.btcchina.com', '/data/ticker')
return {'CNY': Decimal(json['ticker']['last'])}
class BTCParalelo(ExchangeBase):
async def get_rates(self, ccy):
json = await self.get_json('btcparalelo.com', '/api/price')
return {'VEF': Decimal(json['price'])}
class Coinbase(ExchangeBase): class Coinbase(ExchangeBase):
async def get_rates(self, ccy): async def get_rates(self, ccy):
@ -280,20 +267,6 @@ class CoinDesk(ExchangeBase):
return json['bpi'] return json['bpi']
class Coinsecure(ExchangeBase):
async def get_rates(self, ccy):
json = await self.get_json('api.coinsecure.in', '/v0/noauth/newticker')
return {'INR': Decimal(json['lastprice'] / 100.0 )}
class Foxbit(ExchangeBase):
async def get_rates(self,ccy):
json = await self.get_json('api.bitvalor.com', '/v1/ticker.json')
return {'BRL': Decimal(json['ticker_1h']['exchanges']['FOX']['last'])}
class itBit(ExchangeBase): class itBit(ExchangeBase):
async def get_rates(self, ccy): async def get_rates(self, ccy):
@ -344,23 +317,6 @@ class TheRockTrading(ExchangeBase):
'/v1/funds/BTCEUR/ticker') '/v1/funds/BTCEUR/ticker')
return {'EUR': Decimal(json['last'])} return {'EUR': Decimal(json['last'])}
class Unocoin(ExchangeBase):
async def get_rates(self, ccy):
json = await self.get_json('www.unocoin.com', 'trade?buy')
return {'INR': Decimal(json)}
class WEX(ExchangeBase):
async def get_rates(self, ccy):
json_eur = await self.get_json('wex.nz', '/api/3/ticker/btc_eur')
json_rub = await self.get_json('wex.nz', '/api/3/ticker/btc_rur')
json_usd = await self.get_json('wex.nz', '/api/3/ticker/btc_usd')
return {'EUR': Decimal(json_eur['btc_eur']['last']),
'RUB': Decimal(json_rub['btc_rur']['last']),
'USD': Decimal(json_usd['btc_usd']['last'])}
class Winkdex(ExchangeBase): class Winkdex(ExchangeBase):
@ -394,25 +350,39 @@ def dictinvert(d):
return inv return inv
def get_exchanges_and_currencies(): def get_exchanges_and_currencies():
# load currencies.json from disk
path = resource_path('currencies.json') path = resource_path('currencies.json')
try: try:
with open(path, 'r', encoding='utf-8') as f: with open(path, 'r', encoding='utf-8') as f:
return json.loads(f.read()) return json.loads(f.read())
except: except:
pass pass
# or if not present, generate it now.
print("cannot find currencies.json. will regenerate it now.")
d = {} d = {}
is_exchange = lambda obj: (inspect.isclass(obj) is_exchange = lambda obj: (inspect.isclass(obj)
and issubclass(obj, ExchangeBase) and issubclass(obj, ExchangeBase)
and obj != ExchangeBase) and obj != ExchangeBase)
exchanges = dict(inspect.getmembers(sys.modules[__name__], is_exchange)) exchanges = dict(inspect.getmembers(sys.modules[__name__], is_exchange))
for name, klass in exchanges.items():
exchange = klass(None, None) async def get_currencies_safe(name, exchange):
try: try:
d[name] = exchange.get_currencies() d[name] = await exchange.get_currencies()
print(name, "ok") print(name, "ok")
except: except:
print(name, "error") print(name, "error")
continue
async def query_all_exchanges_for_their_ccys_over_network():
async with timeout_after(10):
async with TaskGroup() as group:
for name, klass in exchanges.items():
exchange = klass(None, None)
await group.spawn(get_currencies_safe(name, exchange))
loop = asyncio.get_event_loop()
try:
loop.run_until_complete(query_all_exchanges_for_their_ccys_over_network())
except Exception as e:
pass
with open(path, 'w', encoding='utf-8') as f: with open(path, 'w', encoding='utf-8') as f:
f.write(json.dumps(d, indent=4, sort_keys=True)) f.write(json.dumps(d, indent=4, sort_keys=True))
return d return d
@ -478,17 +448,18 @@ class FxThread(ThreadJob):
async def run(self): async def run(self):
while True: while True:
# approx. every 2.5 minutes, refresh spot price
try: try:
await asyncio.wait_for(self._trigger.wait(), 150) async with timeout_after(150):
except concurrent.futures.TimeoutError: await self._trigger.wait()
self._trigger.clear()
# we were manually triggered, so get historical rates
if self.is_enabled() and self.show_history():
self.exchange.get_historical_rates(self.ccy, self.cache_dir)
except TaskTimeout:
pass pass
else:
self._trigger.clear()
if self.is_enabled():
if self.show_history():
self.exchange.get_historical_rates(self.ccy, self.cache_dir)
if self.is_enabled(): if self.is_enabled():
self.exchange.update(self.ccy) await self.exchange.update_safe(self.ccy)
def is_enabled(self): def is_enabled(self):
return bool(self.config.get('use_exchange_rate')) return bool(self.config.get('use_exchange_rate'))

24
electrum/network.py

@ -287,7 +287,7 @@ class Network(PrintError):
return fut.result() return fut.result()
@staticmethod @staticmethod
def get_instance(): def get_instance() -> Optional["Network"]:
return INSTANCE return INSTANCE
def with_recent_servers_lock(func): def with_recent_servers_lock(func):
@ -1147,8 +1147,8 @@ class Network(PrintError):
raise raise
await asyncio.sleep(0.1) await asyncio.sleep(0.1)
@classmethod
async def _send_http_on_proxy(self, method: str, url: str, params: str = None, body: bytes = None, json: dict = None, headers=None, on_finish=None): async def _send_http_on_proxy(cls, method: str, url: str, params: str = None, body: bytes = None, json: dict = None, headers=None, on_finish=None):
async def default_on_finish(resp: ClientResponse): async def default_on_finish(resp: ClientResponse):
resp.raise_for_status() resp.raise_for_status()
return await resp.text() return await resp.text()
@ -1156,7 +1156,9 @@ class Network(PrintError):
headers = {} headers = {}
if on_finish is None: if on_finish is None:
on_finish = default_on_finish on_finish = default_on_finish
async with make_aiohttp_session(self.proxy) as session: network = cls.get_instance()
proxy = network.proxy if network else None
async with make_aiohttp_session(proxy) as session:
if method == 'get': if method == 'get':
async with session.get(url, params=params, headers=headers) as resp: async with session.get(url, params=params, headers=headers) as resp:
return await on_finish(resp) return await on_finish(resp)
@ -1171,11 +1173,15 @@ class Network(PrintError):
else: else:
assert False assert False
@staticmethod @classmethod
def send_http_on_proxy(method, url, **kwargs): def send_http_on_proxy(cls, method, url, **kwargs):
network = Network.get_instance() network = cls.get_instance()
assert network._loop_thread is not threading.currentThread() if network:
coro = asyncio.run_coroutine_threadsafe(network._send_http_on_proxy(method, url, **kwargs), network.asyncio_loop) assert network._loop_thread is not threading.currentThread()
loop = network.asyncio_loop
else:
loop = asyncio.get_event_loop()
coro = asyncio.run_coroutine_threadsafe(cls._send_http_on_proxy(method, url, **kwargs), loop)
return coro.result(5) return coro.result(5)

2
electrum/util.py

@ -948,7 +948,7 @@ class TxMinedInfo(NamedTuple):
header_hash: Optional[str] = None # hash of block that mined tx header_hash: Optional[str] = None # hash of block that mined tx
def make_aiohttp_session(proxy: dict, headers=None, timeout=None): def make_aiohttp_session(proxy: Optional[dict], headers=None, timeout=None):
if headers is None: if headers is None:
headers = {'User-Agent': 'Electrum'} headers = {'User-Agent': 'Electrum'}
if timeout is None: if timeout is None:

Loading…
Cancel
Save