def test_position_delta_with_btc_fees(self): self.bid.fee = Money('0.01', 'BTC') self.ask.fee = Money('0.02', 'BTC') position = positions.position_delta(self.trades) position['BTC'].should.equal(Money('0', 'BTC')) position['fiat'].should.equal(Money('-7.5', 'USD'))
def get_pl_from_open_orders(position_trades, exchange_name, open_orders): """ Given our current position, and the open orders on an exchange, what would our realized p&l be if a market order came in and consumed our order entirely? Details: we naively assume that our order is sized such that it would completely close our position. If there is no open order on that exchange, we return None. """ bids = open_orders['bids'] asks = open_orders['asks'] pl = Money('0', 'USD') open_position = positions.position_delta(position_trades) unmatched_fees = revenue_lib.all_fees(position_trades)[0].to('USD') fiat_position = open_position['fiat'].to('USD') btc_position = open_position['BTC'] value_of_btc = Money(0, 'USD') if btc_position > 0 and len(asks) > 0: close_order = asks[ 0] # Naively assume the first order is what we care about. close_price = Money(close_order[0], 'USD') value_of_btc = close_price * btc_position.amount elif btc_position <= 0 and len(bids) > 0: close_order = bids[0] close_price = Money(close_order[0], 'USD') value_of_btc = close_price * btc_position.amount else: return Money(0, 'USD'), Money(0, 'USD') revenue = fiat_position + unmatched_fees + value_of_btc.to('USD') exchange = exchange_factory.make_exchange_from_key(exchange_name) new_fee = exchange.limit_order_fee * abs(value_of_btc.to('USD')) fees = unmatched_fees + new_fee return revenue, fees
def get_pl_from_limit_order(position_trades, orderbook, exchange_name): """ Given our current position and an exchange orderbook, what would our realized p&l be on that position if we were the top order on this exchange, in the amount and side that would exactly close our position, and a market order came through and took the whole order? """ pl = Money('0', 'USD') open_position = positions.position_delta(position_trades) unmatched_fees = revenue_lib.all_fees(position_trades)[0].to('USD') fiat_position = open_position['fiat'].to('USD') btc_position = open_position['BTC'] value_of_btc = Money(0, 'USD') if btc_position > 0: # Then we're in a long position, we want to sell, so place an order in front # of the top ask. top_ask = orderbook['asks'][0] close_price = top_ask.price - Money('0.01', top_ask.price.currency) value_of_btc = close_price * btc_position.amount else: # Then we're in a long position, we want to sell, so place an order in front # of the top bid. top_bid = orderbook['bids'][0] close_price = top_bid.price + Money('0.01', top_bid.price.currency) value_of_btc = close_price * btc_position.amount revenue = fiat_position + unmatched_fees + value_of_btc.to('USD') exchange = exchange_factory.make_exchange_from_key(exchange_name) new_fee = exchange.market_order_fee * abs(value_of_btc.to('USD')) fees = unmatched_fees + new_fee return revenue, fees
def realized_pl(matched_trades, price_currency=None, volume_currency='BTC'): """ Take a matched_trades object and return our realized profit in the price currency over those trades. We do this with the insight that for a matched_trades object, the position_delta in the volume currency will be zero, and the position delta in the price currency will be our realized profit. """ realized_position = positions.position_delta( matched_trades, price_currency, volume_currency, ) if realized_position[volume_currency] != Money('0', volume_currency): raise ValueError('Matched Trades must sum up to 0 %s' % volume_currency) realized_pl = realized_position['fiat'] return realized_pl
def get_current_trading_result(self, matched_trades, position_trades, currency, fundamental_value=None): profit, revenue, fees, volume_currency_fees = revenue_lib.profit_data( matched_trades, currency, volume_currency=self.volume_currency, ) if fundamental_value: open_pl = revenue_lib.open_pl( position_trades, fundamental_value, price_currency=currency, volume_currency=self.volume_currency, ) else: open_pl = None open_position = positions.position_delta( position_trades, volume_currency=self.volume_currency, )[self.volume_currency] return profit, revenue, fees, volume_currency_fees, open_pl, open_position
def get_pl_from_market_order(position_trades, orderbook, exchange_name): """ Given our current position and an exchange orderbook, what would our realized p&l be if we closed that position right now with a market order? """ pl = Money('0', 'USD') open_position = positions.position_delta(position_trades) unmatched_fees = revenue_lib.all_fees(position_trades)[0].to('USD') fiat_position = open_position['fiat'].to('USD') btc_position = open_position['BTC'] value_of_btc = Money(0, 'BTC') if btc_position > 0: value_of_btc = quote_lib.price_quote_from_orderbook( orderbook, Order.ASK, btc_position, )['total_price'] else: value_of_btc = quote_lib.price_quote_from_orderbook( orderbook, Order.BID, btc_position, )['total_price'] revenue = fiat_position + unmatched_fees + value_of_btc.to('USD') exchange = exchange_factory.make_exchange_from_key(exchange_name) new_fee = exchange.market_order_fee * abs(value_of_btc.to('USD')) fees = unmatched_fees + new_fee return revenue, fees
def test_position_delta(self): position = positions.position_delta(self.trades) position['BTC'].should.equal(Money('0', 'BTC')) position['fiat'].should.equal(Money('0', 'USD'))
def split_trades(trades, price_currency='USD', volume_currency='BTC'): """ This function creates the matched_trades and position_trades objects. Matched_trades is a list of pairs of semi-real trades, in which the first fake-trade represents a chunk of a real trade that opened a position, and the second fake-trade represents the chunk of another real trade that closed the first position. In this way the pair represents a complete loop of an open-and-closing of a position. This structure is very useful for several purposes. """ # This is slow but safer for now, don't want to modify original array. trades_copy = [] for t in trades: # We don't need orders here, so we could probaby use SimpleTrade. tc = copy_trade(t) trades_copy.append(tc) trades = trades_copy trades.sort(key=lambda t: t.time_created) total_volume = sum([t.volume for t in trades]) total_price = sum([t.price_in_currency(price_currency) for t in trades]) bids = [] asks = [] for t in trades: if t.trade_type == Consts.BID: bids.append(t) else: asks.append(t) matched_trades = [] while bids and asks: if bids[0].volume < asks[0].volume: active = bids.pop(0) trades_to_match = asks else: active = asks.pop(0) trades_to_match = bids if active.volume > 0: matched_trades.append(active) volume_to_match = active.volume while volume_to_match > 0: match = trades_to_match[0] if match.volume <= volume_to_match: # We totally match. volume_to_match -= match.volume match = trades_to_match.pop(0) # Actually remove it. if match.volume > 0: matched_trades.append(match) else: # We match part of the opposite. orig_price = match.price orig_volume = match.volume # Split the partial match into 2 trades. fraction = volume_to_match / match.volume matched_partial_trade = copy_trade(match) matched_partial_trade.price = match.price * fraction matched_partial_trade.fee = match.fee * fraction matched_partial_trade.volume = volume_to_match old_fraction = (match.volume - volume_to_match) / match.volume match.price *= old_fraction match.fee *= old_fraction match.volume -= volume_to_match assert abs(match.price + matched_partial_trade.price - orig_price) < 1e-10 assert abs(match.volume + matched_partial_trade.volume - orig_volume) < 1e-10 if matched_partial_trade.volume > 0: matched_trades.append(matched_partial_trade) volume_to_match = 0 assert bids == [] or asks == [] # One should be empty. position_trades = bids + asks # Assert that our matched_trades are matched. matched_position = positions.position_delta( matched_trades, price_currency=price_currency, volume_currency=volume_currency, ) assert matched_position[volume_currency] == Money('0', volume_currency) # Assert that our total price and volume haven't changed. new_total_volume = sum( [t.volume for t in (matched_trades + position_trades)]) new_total_price = sum([ t.price_in_currency(price_currency) for t in (matched_trades + position_trades) ]) assert abs(new_total_volume - total_volume) < 1e-10, \ "%s != %s" % (new_total_volume, total_volume) assert abs(new_total_price - total_price) < 1e-10, \ "%s != %s" % (new_total_price, total_price) return matched_trades, position_trades