def choose_pair( self, timestamp: Timestamp, price_query: Callable[[Asset, Asset, Timestamp], FVal], ) -> TradePair: """Choose a random pair to trade from the available pairs at the selected timestamp""" choices = set(self.asset_pairs['result'].keys()) found = False while len(choices) != 0: pair = random.choice(tuple(choices)) choices.remove(pair) pair = kraken_to_world_pair(pair) base, quote = pair_get_assets(pair) kbase = base.to_kraken() kquote = quote.to_kraken() if kbase in self.balances_dict or kquote in self.balances_dict: # Before choosing make sure that at the selected timestamp both of # the pair assets exist (had a price) if not assets_exist_at_time(base, quote, timestamp, price_query): continue found = True break if not found: raise ValueError( 'Could not find a pair to trade with the current funds') return trade_pair_from_assets(base, quote)
def trade_to_binance(self, trade: Trade) -> Dict[str, Any]: """Turns our trade into a binance trade""" base, quote = pair_get_assets(trade.pair) bbase = base.to_binance() bquote = quote.to_binance() binance_symbol = bbase + bquote # Binance trades have timestamps with 3 extra zeros at the end timestamp = trade.timestamp * 1000 msg = 'The given trade symbol is not a valid binance pair' assert binance_symbol in self._symbols_to_pair, msg trade_data = { 'symbol': binance_symbol, 'id': 1, 'orderId': 1, 'price': str(trade.rate), 'qty': str(trade.amount), 'commission': str(trade.fee), 'commissionAsset': str(trade.fee_currency.to_binance()), 'time': timestamp, 'isBuyer': trade.trade_type == TradeType.BUY, 'isMaker': True, 'isBestMatch': True, } return trade_data
def world_pair_to_bittrex(pair: TradePair) -> str: """Turns a rotkehlchen pair to a bittrex pair""" base_asset, quote_asset = pair_get_assets(pair) base_asset_str = base_asset.to_bittrex() quote_asset_str = quote_asset.to_bittrex() # In bittrex the pairs are inverted and use '-' return f'{quote_asset_str}-{base_asset_str}'
def world_to_kraken_pair(tradeable_pairs: List[str], pair: TradePair) -> str: base_asset, quote_asset = pair_get_assets(pair) base_asset_str = base_asset.to_kraken() quote_asset_str = quote_asset.to_kraken() pair1 = base_asset_str + quote_asset_str pair2 = quote_asset_str + base_asset_str # In some pairs, XXBT is XBT and ZEUR is EUR ... pair3 = None if 'XXBT' in pair1: pair3 = pair1.replace('XXBT', 'XBT') pair4 = None if 'XXBT' in pair2: pair4 = pair2.replace('XXBT', 'XBT') if 'ZEUR' in pair1: pair3 = pair1.replace('ZEUR', 'EUR') pair4 = None if 'ZEUR' in pair2: pair4 = pair2.replace('ZEUR', 'EUR') if pair1 in tradeable_pairs: new_pair = pair1 elif pair2 in tradeable_pairs: new_pair = pair2 elif pair3 in tradeable_pairs: new_pair = pair3 elif pair4 in tradeable_pairs: new_pair = pair4 else: raise ValueError( f'Unknown pair "{pair}" provided. Couldnt find {base_asset_str + quote_asset_str}' f' or {quote_asset_str + base_asset_str} in tradeable pairs', ) return new_pair
def create_action(self, index: int, ts: Timestamp): """Create a random trade action on a random exchange depending on the funds that are available in that exchange""" # choose an exchange at random exchange_name = random.choice(ALLOWED_EXCHANGES) exchange = getattr(self, exchange_name) # choose a random pair at that exchange pair = exchange.choose_pair( timestamp=ts, price_query=self.query_historical_price, ) print( f'Creating trade {index + 1} / {self.trades_number} in {exchange_name}' f' for the pair: {pair} at timestamp {ts}', ) # depending on our funds decide on what to do. Buy/sell base, quote = pair_get_assets(pair) if exchange.get_balance(base) is None: action_type = TradeType.BUY elif exchange.get_balance(quote) is None: action_type = TradeType.SELL else: # TODO: trade the one we have most of action_type = random.choice(list(TradeType)) # if we are buying we are going to spend from the quote asset if action_type == TradeType.BUY: spending_asset = quote else: # selling spends from the base asset spending_asset = base # get a spending asset amount within our per-trade equivalent range and # our available funds spending_usd_rate = self.query_historical_price( spending_asset, A_USD, ts) max_usd_in_spending_asset = spending_usd_rate * exchange.get_balance( spending_asset) max_usd_equivalent_to_spend = min(max_usd_in_spending_asset, MAX_TRADE_USD_VALUE) rate = self.query_historical_price(base, quote, ts) usd_to_spend = FVal( random.uniform(0.01, float(max_usd_equivalent_to_spend))) amount_in_spending_asset = usd_to_spend / spending_usd_rate # if we are buying then the amount is the amount of asset we bought if action_type == TradeType.BUY: amount = amount_in_spending_asset / rate # if we are selling the amount is the spending asset amount else: amount = amount_in_spending_asset quote_asset_usd_rate = self.query_historical_price(quote, A_USD, ts) fee_in_quote_currency = FVal(random.uniform( 0, MAX_FEE_USD_VALUE)) / quote_asset_usd_rate # create the trade trade = Trade( timestamp=ts, location=deserialize_location(exchange_name), pair=pair, trade_type=action_type, amount=amount, rate=rate, fee=fee_in_quote_currency, fee_currency=quote, link='', notes='', ) logger.info(f'Created trade: {trade}') # Adjust our global and per exchange accounting if action_type == TradeType.BUY: # we buy so we increase our base asset by amount self.increase_asset(base, amount, exchange_name) # and decrease quote by amount * rate self.decrease_asset(quote, amount * rate, exchange_name) else: # we sell so we increase our quote asset self.increase_asset(quote, amount * rate, exchange_name) # and decrease our base asset self.decrease_asset(base, amount, exchange_name) # finally add it to the exchange exchange.append_trade(trade)
def test_pair_get_assets(): a1, a2 = pair_get_assets('ETH_BTC') assert isinstance(a1, Asset) assert a1 == A_ETH assert isinstance(a2, Asset) assert a2 == A_BTC with pytest.raises(UnprocessableTradePair): pair_get_assets('_') with pytest.raises(UnprocessableTradePair): pair_get_assets('ETH_') with pytest.raises(UnprocessableTradePair): pair_get_assets('_BTC') with pytest.raises(UnprocessableTradePair): pair_get_assets('ETH_BTC_USD') with pytest.raises(UnknownAsset): pair_get_assets('ETH_FDFSFDSFDSF') with pytest.raises(UnknownAsset): pair_get_assets('FDFSFDSFDSF_BTC') a1, a2 = pair_get_assets('ETH_RDN') assert isinstance(a1, Asset) assert a1 == A_ETH assert isinstance(a2, Asset) assert a2 == A_RDN
def trade_get_assets(trade: Union[Trade, AMMTrade]) -> Tuple[Asset, Asset]: if isinstance(trade, Trade): return pair_get_assets(trade.pair) # else should only be AMMTrade return trade.base_asset, trade.quote_asset # type: ignore
def invert_pair(pair: TradePair) -> TradePair: left, right = pair_get_assets(pair) return trade_pair_from_assets(right, left)
def quote_asset(self) -> Asset: _, quote = pair_get_assets(self.pair) return quote
def base_asset(self) -> Asset: base, _ = pair_get_assets(self.pair) return base
# amount_key = 'size' if case == KucoinCase.TRADES else 'amount' # amount = deserialize_asset_amount(raw_result['size']) fee = deserialize_fee(raw_result['fee']) trade_pair_symbol = raw_result['symbol'] trade_pair = deserialize_trade_pair(trade_pair_symbol) if case == KucoinCase.TRADES: fee_currency_symbol = raw_result['feeCurrency'] fee_currency = asset_from_kucoin(fee_currency_symbol) amount = deserialize_asset_amount(raw_result['size']) rate = deserialize_price(raw_result['price']) # new trades have the timestamp in ms timestamp = Timestamp(int(timestamp / 1000)) trade_id = raw_result['tradeId'] else: # old v1 trades amount = deserialize_asset_amount(raw_result['amount']) base_asset, quote_asset = pair_get_assets(trade_pair) fee_currency = quote_asset if trade_type == TradeType.SELL else base_asset rate = deserialize_price(raw_result['dealPrice']) trade_id = raw_result['id'] except KeyError as e: raise DeserializationError(f'Missing key: {str(e)}.') from e trade = Trade( timestamp=timestamp, location=Location.KUCOIN, pair=trade_pair, trade_type=trade_type, amount=amount, rate=rate, fee=fee,
def trade_get_assets(trade: Union[Trade, AMMTrade]) -> Tuple[Asset, Asset]: if isinstance(trade, Trade): return pair_get_assets(trade.pair) else: # Should only be AMMTrade # can also be unknown ethereum token return trade.base_asset, trade.quote_asset # type: ignore
def trade_get_assets(trade: Trade) -> Tuple[Asset, Asset]: return pair_get_assets(trade.pair)
def query_online_trade_history( self, start_ts: Timestamp, end_ts: Timestamp, ) -> List[Trade]: """Queries coinbase pro for trades""" log.debug('Query coinbasepro trade history', start_ts=start_ts, end_ts=end_ts) trades = [] raw_trades = [] for batch in self._paginated_query( endpoint='orders', query_options={'status': 'done'}, ): raw_trades.extend(batch) for entry in raw_trades: timestamp = coinbasepro_deserialize_timestamp(entry, 'created_at') if timestamp < start_ts or timestamp > end_ts: continue try: pair = coinbasepro_to_worldpair(entry['product_id']) # Fee currency seems to always be quote asset # https://github.com/ccxt/ccxt/blob/ddf3a15cbff01541f0b37c35891aa143bb7f9d7b/python/ccxt/coinbasepro.py#L724 # noqa: E501 _, quote_asset = pair_get_assets(pair) trades.append( Trade( timestamp=timestamp, location=Location.COINBASEPRO, pair=coinbasepro_to_worldpair(entry['product_id']), trade_type=deserialize_trade_type(entry['side']), amount=deserialize_asset_amount(entry['size']), rate=deserialize_price(entry['price']), fee=deserialize_fee(entry['fill_fees']), fee_currency=quote_asset, link=entry['id'], notes='', )) except UnprocessableTradePair as e: self.msg_aggregator.add_warning( f'Found unprocessable Coinbasepro pair {e.pair}. Ignoring the trade.', ) continue except UnknownAsset as e: self.msg_aggregator.add_warning( f'Found unknown Coinbasepro asset {e.asset_name}. ' f'Ignoring the trade.', ) continue except (DeserializationError, KeyError) as e: msg = str(e) if isinstance(e, KeyError): msg = f'Missing key entry for {msg}.' self.msg_aggregator.add_error( 'Failed to deserialize a coinbasepro trade. ' 'Check logs for details. Ignoring it.', ) log.error( 'Error processing a coinbasepro trade.', raw_trade=entry, error=msg, ) continue return trades