async def _candles(self, msg: dict, symbol: str, timestamp: float): """ { 'data': { 'symbol': 'BTC-USDT', 'candles': ['1619196960', '49885.4', '49821', '49890.5', '49821', '2.60137567', '129722.909001802'], 'time': 1619196997007846442 }, 'subject': 'trade.candles.update', 'topic': '/market/candles:BTC-USDT_1min', 'type': 'message' } """ symbol, interval = symbol.split("_") interval = self.normalize_interval[interval] start, open, close, high, low, vol, _ = msg['data']['candles'] end = int(start) + timedelta_str_to_sec(interval) - 1 await self.callback(CANDLES, feed=self.id, symbol=symbol, timestamp=msg['data']['time'] / 1000000000, receipt_timestamp=timestamp, start=int(start), stop=end, interval=interval, trades=None, open_price=Decimal(open), close_price=Decimal(close), high_price=Decimal(high), low_price=Decimal(low), volume=Decimal(vol), closed=None)
async def _candle(self, msg: dict, timestamp: float): ''' { 'action': 'update', 'arg': { 'instType': 'sp', 'channel': 'candle1m', 'instId': 'BTCUSDT' }, 'data': [['1649014920000', '46434.2', '46437.98', '46434.2', '46437.98', '0.9469']] } ''' for entry in msg['data']: t = Candle(self.id, self.exchange_symbol_to_std_symbol( msg['arg']['instId']), self.timestamp_normalize(int(entry[0])), self.timestamp_normalize(int(entry[0])) + timedelta_str_to_sec(self.candle_interval), self.candle_interval, None, Decimal(entry[1]), Decimal(entry[4]), Decimal(entry[2]), Decimal(entry[3]), Decimal(entry[5]), None, self.timestamp_normalize(int(entry[0])), raw=entry) await self.callback(CANDLES, t, timestamp)
async def _candles(self, msg: dict, timestamp: float): ''' { 'candle_start_time': 1638134700000000, 'close': None, 'high': None, 'last_updated': 1638134700318213, 'low': None, 'open': None, 'resolution': '1m', 'symbol': 'BTC_USDT', 'timestamp': 1638134708903082, 'type': 'candlestick_1m', 'volume': 0 } ''' interval = self.normalize_candle_interval[msg['resolution']] c = Candle(self.id, self.exchange_symbol_to_std_symbol(msg['symbol']), self.timestamp_normalize(msg['candle_start_time']), self.timestamp_normalize(msg['candle_start_time']) + timedelta_str_to_sec(interval) - 1, interval, None, Decimal(msg['open'] if msg['open'] else 0), Decimal(msg['close'] if msg['close'] else 0), Decimal(msg['high'] if msg['high'] else 0), Decimal(msg['low'] if msg['low'] else 0), Decimal(msg['volume'] if msg['volume'] else 0), False, self.timestamp_normalize(msg['timestamp']), raw=msg) await self.callback(CANDLES, c, timestamp)
def _candle_normalize(self, symbol: str, data: list, interval: str) -> dict: return Candle(self.id, symbol, data[0], data[0] + timedelta_str_to_sec(interval), interval, None, Decimal(data[3]), Decimal(data[4]), Decimal(data[2]), Decimal(data[1]), Decimal(data[5]), True, data[0], raw=data)
async def candles(self, symbol: str, start=None, end=None, interval='1m', retry_count=1, retry_delay=60): _interval = self.candle_mappings[interval] sym = self.std_symbol_to_exchange_symbol(symbol) base_endpoint = f"{self.api}candles/trade:{_interval}:{sym}" start, end = self._interval_normalize(start, end) offset = timedelta_str_to_sec(interval) while True: if start and end: endpoint = f"{base_endpoint}/hist?limit=10000&start={int(start * 1000)}&end={int(end * 1000)}&sort=1" else: endpoint = f"{base_endpoint}/last" r = await self.http_conn.read(endpoint, retry_delay=retry_delay, retry_count=retry_count) data = json.loads(r, parse_float=Decimal) if not isinstance(data[0], list): data = [data] data = [ Candle(self.id, symbol, self.timestamp_normalize(e[0]), self.timestamp_normalize(e[0]) + offset, interval, None, Decimal(e[1]), Decimal(e[2]), Decimal(e[3]), Decimal(e[4]), Decimal(e[5]), True, self.timestamp_normalize(e[0]), raw=e) for e in data ] yield data if not end or len(data) < 10000: break start = data[-1].start + offset
async def _candle(self, msg: dict, timestamp: float): ''' { 'instrument_name': 'BTC_USDT', 'subscription': 'candlestick.14D.BTC_USDT', 'channel': 'candlestick', 'depth': 300, 'interval': '14D', 'data': [ { 't': 1636934400000, 'o': Decimal('65502.68'), 'h': Decimal('66336.25'), 'l': Decimal('55313.95'), 'c': Decimal('57582.1'), 'v': Decimal('366802.492134') } ] } ''' interval = msg['interval'] if interval == '14D': interval = '2w' elif interval == '7D': interval = '1w' elif interval == '1D': interval = '1d' for entry in msg['data']: c = Candle(self.id, self.exchange_symbol_to_std_symbol(msg['instrument_name']), entry['t'] / 1000, entry['t'] / 1000 + timedelta_str_to_sec(interval) - 1, interval, None, entry['o'], entry['c'], entry['h'], entry['l'], entry['v'], None, None, raw=entry) await self.callback(CANDLES, c, timestamp)
async def _candles(self, msg: dict, symbol: str, interval: str, timestamp: float): interval = self.normalize_interval[interval] start = int(msg['tick']['id']) end = start + timedelta_str_to_sec(interval) - 1 await self.callback(CANDLES, feed=self.id, symbol= self.std_symbol_to_exchange_symbol(msg['ch'].split('.')[1]), timestamp=timestamp_normalize(self.id, msg['ts']), receipt_timestamp=timestamp, start=start, stop=end, interval=interval, trades=msg['tick']['count'], open_price=Decimal(msg['tick']['open']), close_price=Decimal(msg['tick']['close']), high_price=Decimal(msg['tick']['high']), low_price=Decimal(msg['tick']['low']), volume=Decimal(msg['tick']['amount']), closed='NULL')
async def _candles(self, msg: dict, timestamp: float): """ { "jsonrpc": "2.0", "method": "updateCandles", "params": { "data": [ { "timestamp": "2017-10-19T16:30:00.000Z", "open": "0.054614", "close": "0.054465", "min": "0.054339", "max": "0.054724", "volume": "141.268", "volumeQuote": "7.709353873" } ], "symbol": "ETHBTC", "period": "M30" } } """ interval = str(self.normalize_interval[msg['period']]) for candle in msg['data']: start = self.timestamp_normalize(candle['timestamp']) end = start + timedelta_str_to_sec(interval) - 1 c = Candle(self.id, self.exchange_symbol_to_std_symbol(msg['symbol']), start, end, interval, None, Decimal(candle['open']), Decimal(candle['close']), Decimal(candle['max']), Decimal(candle['min']), Decimal(candle['volume']), None, None, raw=candle) await self.callback(CANDLES, c, timestamp)
async def candles(self, symbol: str, start=None, end=None, interval='1m', retry_count=1, retry_delay=60): sym = self.std_symbol_to_exchange_symbol(symbol) interval_sec = timedelta_str_to_sec(interval) base = f'{self.api}ohlc/{sym}/?step={interval_sec}&limit=1000' start, end = self._interval_normalize(start, end) while True: endpoint = base if start and end: endpoint = f'{base}&start={int(start)}&end={int(end)}' r = await self.http_conn.read(endpoint, retry_count=retry_count, retry_delay=retry_delay) data = json.loads(r, parse_float=Decimal)['data']['ohlc'] data = [ Candle(self.id, symbol, float(e['timestamp']), float(e['timestamp']) + interval_sec, interval, None, Decimal(e['open']), Decimal(e['close']), Decimal(e['high']), Decimal(e['low']), Decimal(e['volume']), True, float(e['timestamp']), raw=e) for e in data ] yield data end = data[0].start - interval_sec if not start or start >= end: break await asyncio.sleep(1 / self.request_limit)
async def candles(self, symbol: str, start=None, end=None, interval='1m', retry_count=1, retry_delay=60): sym = self.std_symbol_to_exchange_symbol(symbol) interval_sec = timedelta_str_to_sec(interval) base = f'{self.api}/markets/{sym}/candles?resolution={interval_sec}' start, end = self._interval_normalize(start, end) while True: endpoint = base if start and end: endpoint = f'{base}&start_time={start}&end_time={end}' r = await self.http_conn.read(endpoint, retry_count=retry_count, retry_delay=retry_delay) data = json.loads(r, parse_float=Decimal)['result'] data = [ Candle(self.id, symbol, self.timestamp_normalize(e['startTime']), self.timestamp_normalize(e['startTime']) + interval_sec, interval, None, Decimal(e['open']), Decimal(e['close']), Decimal(e['high']), Decimal(e['low']), Decimal(e['volume']), True, self.timestamp_normalize(e['startTime']), raw=e) for e in data ] yield data end = data[0].start - interval_sec if not start or len(data) < 1501: break await asyncio.sleep(1 / self.request_limit)
async def _candle(self, msg: dict, ts: float): ''' { 'ch': 'candles/M1', 'update': { 'BTCUSDT': [{ 't': 1633805940000, 'o': '54849.03', 'c': '54849.03', 'h': '54849.03', 'l': '54849.03', 'v': '0.00766', 'q': '420.1435698' }] } } ''' interval = msg['ch'].split("/")[-1] for sym, updates in msg['update'].items(): symbol = self.exchange_symbol_to_std_symbol(sym) for u in updates: c = Candle( self.id, symbol, u['t'] / 1000, u['t'] / 1000 + timedelta_str_to_sec(self.normalize_interval[interval]) - 0.1, self.normalize_interval[interval], None, Decimal(u['o']), Decimal(u['c']), Decimal(u['h']), Decimal(u['l']), Decimal(u['v']), None, self.timestamp_normalize(u['t']), raw=msg) await self.callback(CANDLES, c, ts)
async def _candles(self, msg: dict, timestamp: float): """ { 'time': 1619092863, 'channel': 'spot.candlesticks', 'event': 'update', 'result': { 't': '1619092860', 'v': '1154.64627', 'c': '54992.64', 'h': '54992.64', 'l': '54976.29', 'o': '54976.29', 'n': '1m_BTC_USDT' } } """ interval, symbol = msg['result']['n'].split('_', 1) if interval == '7d': interval = '1w' c = Candle(self.id, self.exchange_symbol_to_std_symbol(symbol), float(msg['result']['t']), float(msg['result']['t']) + timedelta_str_to_sec(interval) - 0.1, interval, None, Decimal(msg['result']['o']), Decimal(msg['result']['c']), Decimal(msg['result']['h']), Decimal(msg['result']['l']), Decimal(msg['result']['v']), None, float(msg['time']), raw=msg) await self.callback(CANDLES, c, timestamp)
async def _candles(self, msg: dict, symbol: str, interval: str, timestamp: float): """ { 'ch': 'market.btcusdt.kline.1min', 'ts': 1618700872863, 'tick': { 'id': 1618700820, 'open': Decimal('60751.62'), 'close': Decimal('60724.73'), 'low': Decimal('60724.73'), 'high': Decimal('60751.62'), 'amount': Decimal('2.1990737759143966'), 'vol': Decimal('133570.944386'), 'count': 235} } } """ interval = self.normalize_interval[interval] start = int(msg['tick']['id']) end = start + timedelta_str_to_sec(interval) - 1 await self.callback(CANDLES, feed=self.id, symbol=self.exchange_symbol_to_std_symbol(symbol), timestamp=timestamp_normalize(self.id, msg['ts']), receipt_timestamp=timestamp, start=start, stop=end, interval=interval, trades=msg['tick']['count'], open_price=Decimal(msg['tick']['open']), close_price=Decimal(msg['tick']['close']), high_price=Decimal(msg['tick']['high']), low_price=Decimal(msg['tick']['low']), volume=Decimal(msg['tick']['amount']), closed=None)
async def _candles(self, msg: dict, symbol: str, interval: str, timestamp: float): """ { 'ch': 'market.btcusdt.kline.1min', 'ts': 1618700872863, 'tick': { 'id': 1618700820, 'open': Decimal('60751.62'), 'close': Decimal('60724.73'), 'low': Decimal('60724.73'), 'high': Decimal('60751.62'), 'amount': Decimal('2.1990737759143966'), 'vol': Decimal('133570.944386'), 'count': 235} } } """ interval = self.normalize_candle_interval[interval] start = int(msg['tick']['id']) end = start + timedelta_str_to_sec(interval) - 1 c = Candle(self.id, self.exchange_symbol_to_std_symbol(symbol), start, end, interval, msg['tick']['count'], Decimal(msg['tick']['open']), Decimal(msg['tick']['close']), Decimal(msg['tick']['high']), Decimal(msg['tick']['low']), Decimal(msg['tick']['amount']), None, self.timestamp_normalize(msg['ts']), raw=msg) await self.callback(CANDLES, c, timestamp)
async def candles(self, symbol: str, start=None, end=None, interval='1m', retry_count=1, retry_delay=60): ''' [ { "market": "KRW-BTC", "candle_date_time_utc": "2021-09-11T00:32:00", "candle_date_time_kst": "2021-09-11T09:32:00", "opening_price": 55130000, "high_price": 55146000, "low_price": 55104000, "trade_price": 55145000, "timestamp": 1631320340367, "candle_acc_trade_price": 136592120.21198, "candle_acc_trade_volume": 2.47785284, "unit": 1 }, ... ] ''' sym = self.std_symbol_to_exchange_symbol(symbol) offset = timedelta_str_to_sec(interval) interval_mins = int(timedelta_str_to_sec(interval) / 60) if interval == '1d': base = f'{self.api}candles/days/?market={sym}&count=200' elif interval == '1w': base = f'{self.api}candles/weeks/?market={sym}&count=200' elif interval == '1M': base = f'{self.api}candles/months/?market={sym}&count=200' else: base = f'{self.api}candles/minutes/{interval_mins}?market={sym}&count=200' start, end = self._interval_normalize(start, end) def _ts_norm(timestamp: datetime) -> float: # Upbit sends timezone naïve datetimes, so need to force to UTC before converting to timestamp assert timestamp.tzinfo is None return timestamp.replace(tzinfo=timezone.utc).timestamp() def retain(c: Candle, _last: set): if start and end: return c.start <= end and c.start not in _last return True _last = set() while True: endpoint = base if start and end: end_timestamp = datetime.utcfromtimestamp(start + offset * 200) end_timestamp = end_timestamp.replace( microsecond=0).isoformat() + 'Z' endpoint = f'{base}&to={end_timestamp}' r = await self.http_conn.read(endpoint, retry_count=retry_count, retry_delay=retry_delay) data = json.loads(r, parse_float=Decimal) data = [ Candle(self.id, symbol, _ts_norm(e['candle_date_time_utc']), _ts_norm(e['candle_date_time_utc']) + interval_mins * 60, interval, None, Decimal(e['opening_price']), None, Decimal(e['high_price']), Decimal(e['low_price']), Decimal(e['candle_acc_trade_volume']), True, float(e['timestamp']) / 1000, raw=e) for e in data ] data = list( sorted([c for c in data if retain(c, _last)], key=lambda x: x.start)) yield data # exchange downtime can cause gaps in candles, and because of the way pagination works, there will be overlap in ranges that # cover the downtime. Solution: remove duplicates by storing last values returned to client. _last = set([c.start for c in data]) start = data[-1].start + offset if not end or start >= end: break await asyncio.sleep(1 / self.request_limit)