def trade_from_bittrex(bittrex_trade: Dict[str, Any]) -> Trade: """Turn a bittrex trade returned from bittrex trade history to our common trade history format Throws: - UnsupportedAsset due to bittrex_pair_to_world() - DeserializationError due to unexpected format of dict entries - KeyError due to dict entries missing an expected entry """ amount = ( deserialize_asset_amount(bittrex_trade['Quantity']) - deserialize_asset_amount(bittrex_trade['QuantityRemaining']) ) timestamp = deserialize_timestamp_from_bittrex_date(bittrex_trade['TimeStamp']) rate = deserialize_price(bittrex_trade['PricePerUnit']) order_type = deserialize_trade_type(bittrex_trade['OrderType']) bittrex_price = deserialize_price(bittrex_trade['Price']) fee = deserialize_fee(bittrex_trade['Commission']) pair = bittrex_pair_to_world(bittrex_trade['Exchange']) quote_currency = get_pair_position_asset(pair, 'second') log.debug( 'Processing bittrex Trade', sensitive_log=True, amount=amount, rate=rate, order_type=order_type, price=bittrex_price, fee=fee, bittrex_pair=bittrex_trade['Exchange'], pair=pair, ) return Trade( timestamp=timestamp, location='bittrex', pair=pair, trade_type=order_type, amount=amount, rate=rate, fee=fee, fee_currency=quote_currency, )
def trade_from_kraken(kraken_trade: Dict[str, Any]) -> Trade: """Turn a kraken trade returned from kraken trade history to our common trade history format""" currency_pair = kraken_to_world_pair(kraken_trade['pair']) quote_currency = get_pair_position_asset(currency_pair, 'second') # Kraken timestamps have floating point timestamp = Timestamp( convert_to_int(kraken_trade['time'], accept_only_exact=False)) amount = FVal(kraken_trade['vol']) cost = FVal(kraken_trade['cost']) fee = FVal(kraken_trade['fee']) order_type = trade_type_from_string(kraken_trade['type']) rate = FVal(kraken_trade['price']) if cost != amount * rate: log.warning( 'cost ({cost}) != amount ({amount}) * rate ({rate}) for kraken trade' ) log.debug( 'Processing kraken Trade', sensitive_log=True, timestamp=timestamp, order_type=order_type, kraken_pair=kraken_trade['pair'], pair=currency_pair, quote_currency=quote_currency, amount=amount, cost=cost, fee=fee, rate=rate, ) return Trade( timestamp=timestamp, location='kraken', pair=currency_pair, trade_type=order_type, amount=amount, rate=rate, fee=fee, fee_currency=quote_currency, )
def trade_from_poloniex(poloniex_trade, pair): """Turn a poloniex trade returned from poloniex trade history to our common trade history format""" trade_type = poloniex_trade['type'] amount = FVal(poloniex_trade['amount']) rate = FVal(poloniex_trade['rate']) perc_fee = FVal(poloniex_trade['fee']) base_currency = get_pair_position(pair, 'first') quote_currency = get_pair_position(pair, 'second') if trade_type == 'buy': cost = rate * amount cost_currency = base_currency fee = amount * perc_fee fee_currency = quote_currency elif trade_type == 'sell': cost = amount * rate cost_currency = base_currency fee = cost * perc_fee fee_currency = base_currency else: raise ValueError( 'Got unexpected trade type "{}" for poloniex trade'.format( trade_type)) if poloniex_trade['category'] == 'settlement': trade_type = "settlement_%s" % trade_type return Trade(timestamp=createTimeStamp(poloniex_trade['date'], formatstr="%Y-%m-%d %H:%M:%S"), pair=pair, type=trade_type, rate=rate, cost=cost, cost_currency=cost_currency, fee=fee, fee_currency=fee_currency, amount=amount, location='poloniex')
def trade_from_kraken(kraken_trade): """Turn a kraken trade returned from kraken trade history to our common trade history format""" currency_pair = kraken_to_world_pair(kraken_trade['pair']) quote_currency = get_pair_position(currency_pair, 'second') # Kraken timestamps have floating point timestamp = convert_to_int(kraken_trade['time'], accept_only_exact=False) amount = FVal(kraken_trade['vol']) cost = FVal(kraken_trade['cost']) fee = FVal(kraken_trade['fee']) order_type = kraken_trade['type'] rate = FVal(kraken_trade['price']) log.debug( 'Processing kraken Trade', sensitive_log=True, timestamp=timestamp, order_type=order_type, kraken_pair=kraken_trade['pair'], pair=currency_pair, quote_currency=quote_currency, amount=amount, cost=cost, fee=fee, rate=rate, ) return Trade( timestamp=timestamp, pair=currency_pair, type=order_type, rate=rate, cost=cost, cost_currency=quote_currency, fee=fee, fee_currency=quote_currency, amount=amount, location='kraken', )
def verify_otctrade_data( data: ExternalTrade, ) -> Tuple[Optional[Trade], str]: """ Takes in the trade data dictionary, validates it and returns a trade instance If there is an error it returns an error message in the second part of the tuple """ for field in otc_fields: if field not in data: return None, f'{field} was not provided' if data[field] in ('', None) and field not in otc_optional_fields: return None, f'{field} was empty' if field in otc_numerical_fields and not is_number(data[field]): return None, f'{field} should be a number' # Satisfy mypy typing assert isinstance(data['otc_pair'], str) assert isinstance(data['otc_fee_currency'], str) assert isinstance(data['otc_fee'], str) pair = TradePair(data['otc_pair']) try: first = get_pair_position_asset(pair, 'first') second = get_pair_position_asset(pair, 'second') fee_currency = Asset(data['otc_fee_currency']) except UnknownAsset as e: return None, f'Provided asset {e.asset_name} is not known to Rotkehlchen' try: trade_type = deserialize_trade_type(str(data['otc_type'])) amount = deserialize_asset_amount(data['otc_amount']) rate = deserialize_price(data['otc_rate']) fee = deserialize_fee(data['otc_fee']) except DeserializationError as e: return None, f'Deserialization Error: {str(e)}' try: assert isinstance(data['otc_timestamp'], str) timestamp = create_timestamp(data['otc_timestamp'], formatstr='%d/%m/%Y %H:%M') except ValueError as e: return None, f'Could not process the given datetime: {e}' log.debug( 'Creating OTC trade data', sensitive_log=True, pair=pair, trade_type=trade_type, amount=amount, rate=rate, fee=fee, fee_currency=fee_currency, ) if data['otc_fee_currency'] not in (first, second): return None, 'Trade fee currency should be one of the two in the currency pair' if data['otc_type'] not in ('buy', 'sell'): return None, 'Trade type can only be buy or sell' trade = Trade( timestamp=timestamp, location='external', pair=pair, trade_type=trade_type, amount=amount, rate=rate, fee=fee, fee_currency=fee_currency, link=str(data['otc_link']), notes=str(data['otc_notes']), ) return trade, ''
def test_trade_from_binance(mock_binance): binance_trades_list = [ { 'symbol': 'RDNETH', 'id': 1, 'orderId': 1, 'price': FVal(0.0063213), 'qty': FVal(5.0), 'commission': FVal(0.005), 'commissionAsset': 'RDN', 'time': 1512561941, 'isBuyer': True, 'isMaker': False, 'isBestMatch': True, }, { 'symbol': 'ETHUSDT', 'id': 1, 'orderId': 1, 'price': FVal(481.0), 'qty': FVal(0.505), 'commission': FVal(0.242905), 'commissionAsset': 'USDT', 'time': 1531117990, 'isBuyer': False, 'isMaker': True, 'isBestMatch': True, }, { 'symbol': 'BTCUSDT', 'id': 1, 'orderId': 1, 'price': FVal(6376.39), 'qty': FVal(0.051942), 'commission': FVal(0.00005194), 'commissionAsset': 'BTC', 'time': 1531728338, 'isBuyer': True, 'isMaker': False, 'isBestMatch': True, }, { 'symbol': 'ADAUSDT', 'id': 1, 'orderId': 1, 'price': FVal(0.17442), 'qty': FVal(285.2), 'commission': FVal(0.00180015), 'commissionAsset': 'BNB', 'time': 1531871806, 'isBuyer': False, 'isMaker': True, 'isBestMatch': True, }, ] our_expected_list = [ Trade( timestamp=1512561941, location='binance', pair='RDN_ETH', trade_type=TradeType.BUY, amount=FVal(5.0), rate=FVal(0.0063213), fee=FVal(0.005), fee_currency='RDN', ), Trade( timestamp=1531117990, location='binance', pair='ETH_USDT', trade_type=TradeType.SELL, amount=FVal(0.505), rate=FVal(481.0), fee=FVal(0.242905), fee_currency='USDT', ), Trade( timestamp=1531728338, location='binance', pair='BTC_USDT', trade_type=TradeType.BUY, amount=FVal(0.051942), rate=FVal(6376.39), fee=FVal(0.00005194), fee_currency='BTC', ), Trade( timestamp=1531871806, location='binance', pair='ADA_USDT', trade_type=TradeType.SELL, amount=FVal(285.2), rate=FVal(0.17442), fee=FVal(0.00180015), fee_currency='BNB', ), ] for idx, binance_trade in enumerate(binance_trades_list): our_trade = trade_from_binance(binance_trade, mock_binance.symbols_to_pair) assert our_trade == our_expected_list[idx]
def trade_from_poloniex(poloniex_trade: Dict[str, Any], pair: TradePair) -> Trade: """Turn a poloniex trade returned from poloniex trade history to our common trade history format Throws: - UnsupportedAsset due to asset_from_poloniex() - DeserializationError due to the data being in unexpected format """ try: trade_type = deserialize_trade_type(poloniex_trade['type']) amount = deserialize_asset_amount(poloniex_trade['amount']) rate = deserialize_price(poloniex_trade['rate']) perc_fee = deserialize_fee(poloniex_trade['fee']) base_currency = asset_from_poloniex( get_pair_position_str(pair, 'first')) quote_currency = asset_from_poloniex( get_pair_position_str(pair, 'second')) timestamp = deserialize_timestamp_from_poloniex_date( poloniex_trade['date']) except KeyError as e: raise DeserializationError( f'Poloniex trade deserialization error. Missing key entry for {str(e)} in trade dict', ) cost = rate * amount if trade_type == TradeType.BUY: fee = amount * perc_fee fee_currency = quote_currency elif trade_type == TradeType.SELL: fee = cost * perc_fee fee_currency = base_currency else: raise ValueError( 'Got unexpected trade type "{}" for poloniex trade'.format( trade_type)) if poloniex_trade['category'] == 'settlement': if trade_type == TradeType.BUY: trade_type = TradeType.SETTLEMENT_BUY else: trade_type = TradeType.SETTLEMENT_SELL log.debug( 'Processing poloniex Trade', sensitive_log=True, timestamp=timestamp, order_type=trade_type, pair=pair, base_currency=base_currency, quote_currency=quote_currency, amount=amount, fee=fee, rate=rate, ) # Use the converted assets in our pair pair = trade_pair_from_assets(base_currency, quote_currency) # Since in Poloniex the base currency is the cost currency, iow in poloniex # for BTC_ETH we buy ETH with BTC and sell ETH for BTC, we need to turn it # into the Rotkehlchen way which is following the base/quote approach. pair = invert_pair(pair) return Trade( timestamp=timestamp, location='poloniex', pair=pair, trade_type=trade_type, amount=amount, rate=rate, fee=fee, fee_currency=fee_currency, )
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, '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, '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=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 trade_from_binance( binance_trade: Dict, binance_symbols_to_pair: Dict[str, BinancePair], ) -> Trade: """Turn a binance trade returned from trade history to our common trade history format From the official binance api docs (01/09/18): https://github.com/binance-exchange/binance-official-api-docs/blob/62ff32d27bb32d9cc74d63d547c286bb3c9707ef/rest-api.md#terminology base asset refers to the asset that is the quantity of a symbol. quote asset refers to the asset that is the price of a symbol. Throws: - UnsupportedAsset due to asset_from_binance - DeserializationError due to unexpected format of dict entries - KeyError due to dict entries missing an expected entry """ amount = deserialize_asset_amount(binance_trade['qty']) rate = deserialize_price(binance_trade['price']) if binance_trade['symbol'] not in binance_symbols_to_pair: raise DeserializationError( f'Error reading a binance trade. Could not find ' f'{binance_trade["symbol"]} in binance_symbols_to_pair', ) binance_pair = binance_symbols_to_pair[binance_trade['symbol']] timestamp = deserialize_timestamp_from_binance(binance_trade['time']) base_asset = asset_from_binance(binance_pair.binance_base_asset) quote_asset = asset_from_binance(binance_pair.binance_quote_asset) if binance_trade['isBuyer']: order_type = TradeType.BUY # e.g. in RDNETH we buy RDN by paying ETH else: order_type = TradeType.SELL fee_currency = asset_from_binance(binance_trade['commissionAsset']) fee = deserialize_fee(binance_trade['commission']) log.debug( 'Processing binance Trade', sensitive_log=True, amount=amount, rate=rate, timestamp=timestamp, pair=binance_trade['symbol'], base_asset=base_asset, quote=quote_asset, order_type=order_type, commision_asset=binance_trade['commissionAsset'], fee=fee, ) return Trade( timestamp=timestamp, location='binance', pair=trade_pair_from_assets(base_asset, quote_asset), trade_type=order_type, amount=amount, rate=rate, fee=fee, fee_currency=fee_currency, )