def calculate_margin(user, session, safe_prices={}, order_id=None, withdrawals=None, trial_period=False, position_overrides={}, cash_overrides={}): """ calculates the low and high margin for a given user :param order_id: order we're considering throwing in :type order_id: int :param user: the user :type user: User :returns: tuple - low and high margin """ low_margin = high_margin = 0 cash_position = collections.defaultdict(int) # let's start with positions positions = {position.contract.ticker: { 'position': position.position, 'reference_price': position.reference_price, 'contract': position.contract } for position in session.query(models.Position).filter_by(user=user)} # Override some positions positions.update(position_overrides) open_orders = session.query(models.Order).filter_by(user=user).filter( models.Order.quantity_left > 0).filter_by(is_cancelled=False, accepted=True).all() if order_id: open_orders += session.query(models.Order).filter_by(id=order_id).all() # Make a blank position for all contracts which have an open order but no position for order in open_orders: if order.contract.ticker not in positions: positions[order.contract.ticker] = { 'position': 0, 'reference_price': None, 'contract': order.contract } for position in positions.values(): max_position = position['position'] + sum( order.quantity_left for order in open_orders if order.contract == position['contract'] and order.side == 'BUY') min_position = position['position'] - sum( order.quantity_left for order in open_orders if order.contract == position['contract'] and order.side == 'SELL') contract = position['contract'] if contract.contract_type == 'futures': if contract.ticker not in safe_prices: log.err("%s not in safe_prices, marking margin high" % contract.ticker) high_margin += 2**48 low_margin += 2**48 else: SAFE_PRICE = safe_prices[contract.ticker] #log.msg(low_margin) # print 'max position:', max_position # print 'contract.margin_low :', contract.margin_low # print 'SAFE_PRICE :', SAFE_PRICE # print 'position.reference_price :', position['reference_price'] # print position if position['reference_price'] is None: if position['position'] != 0: raise MarginException("No reference price with non-zero position") reference_price = SAFE_PRICE else: reference_price = position['reference_price'] # We divide by 100 because contract.margin_low and contract.margin_high are percentages from 0-100 low_max = abs(max_position) * contract.margin_low * SAFE_PRICE * contract.lot_size / contract.denominator / 100 + max_position * ( reference_price - SAFE_PRICE) * contract.lot_size / contract.denominator low_min = abs(min_position) * contract.margin_low * SAFE_PRICE * contract.lot_size / contract.denominator / 100 + min_position * ( reference_price - SAFE_PRICE) * contract.lot_size / contract.denominator high_max = abs(max_position) * contract.margin_high * SAFE_PRICE * contract.lot_size / contract.denominator / 100 + max_position * ( reference_price - SAFE_PRICE) * contract.lot_size / contract.denominator high_min = abs(min_position) * contract.margin_high * SAFE_PRICE * contract.lot_size / contract.denominator / 100 + min_position * ( reference_price - SAFE_PRICE) * contract.lot_size / contract.denominator # log.msg(low_max) # log.msg(low_min) log.msg("%s" % ["Margin:", contract.ticker, max_position, min_position, low_max, low_min, high_max, high_min]) high_margin += max(high_max, high_min) low_margin += max(low_max, low_min) if contract.contract_type == 'prediction': payoff = contract.lot_size # case where all our buy orders are hit max_spent = sum(order.quantity_left * order.price * order.contract.lot_size / order.contract.denominator for order in open_orders if order.contract == contract and order.side == 'BUY') # case where all our sell orders are hit max_received = sum(order.quantity_left * order.price * order.contract.lot_size / order.contract.denominator for order in open_orders if order.contract == contract and order.side == 'SELL') worst_short_cover = -min_position * payoff if min_position < 0 else 0 best_short_cover = -max_position * payoff if max_position < 0 else 0 additional_margin = max(max_spent + best_short_cover, -max_received + worst_short_cover) low_margin += additional_margin high_margin += additional_margin if contract.contract_type == 'cash': cash_position[contract.ticker] = position['position'] # Override cash position cash_position.update(cash_overrides) max_cash_spent = collections.defaultdict(int) # Deal with cash_pair orders separately because there are no cash_pair positions for order in open_orders: fees = accounting.get_fees(user, order.contract, order.price, order.quantity, trial_period=trial_period) if order.contract.contract_type == 'cash_pair': transaction_size = accounting.get_cash_spent(order.contract, order.price, order.quantity) if order.side == 'BUY': max_cash_spent[order.contract.denominated_contract.ticker] += transaction_size if order.contract.payout_contract.ticker in fees: fees[order.contract.payout_contract.ticker] = max(0, fees[order.contract.payout_contract.ticker] - order.quantity_left) if order.side == 'SELL': max_cash_spent[order.contract.payout_contract.ticker] += order.quantity_left if order.contract.denominated_contract.ticker in fees: fees[order.contract.denominated_contract.ticker] = max(0, fees[order.contract.denominated_contract.ticker] - transaction_size) for ticker, fee in fees.iteritems(): max_cash_spent[ticker] += fee # Make sure max_cash_spent has something in it for every cash contract for ticker in cash_position.iterkeys(): if ticker not in max_cash_spent: max_cash_spent[ticker] = 0 # Deal with withdrawals if withdrawals: for ticker, amount in withdrawals.iteritems(): max_cash_spent[ticker] += amount # The fee is deducted from the withdrawal amount, not added to the withdrawal # contract = util.get_contract(session, ticker) # fees = util.get_withdraw_fees(user, contract, amount, trial_period=trial_period) # for fee_ticker, fee in fees.iteritems(): # max_cash_spent[fee_ticker] += fee for cash_ticker, max_spent in max_cash_spent.iteritems(): if cash_ticker == 'BTC': additional_margin = max_spent else: if max_spent <= cash_position[cash_ticker]: additional_margin = 0 else: # TODO: We should fix this hack and just check max_cash_spent in check_margin log.msg("max_spent (%d) > cash_position[%s] (%d)" % (max_spent, cash_ticker, cash_position[cash_ticker])) additional_margin = 2**48 low_margin += additional_margin high_margin += additional_margin return low_margin, high_margin, max_cash_spent
def test_trade_fees(self): BTCMXN = self.get_contract('BTC/MXN') NETS2015 = self.get_contract('NETS2015') BTCHUF = self.get_contract('BTC/HUF') NETS2014 = self.get_contract('NETS2014') marketmaker = self.get_user('marketmaker') randomtrader = self.get_user('randomtrader') m2 = self.get_user('m2') customer = self.get_user('customer') fees_result = {} for user in [marketmaker, randomtrader, m2, customer]: for contract in [BTCMXN, NETS2015, BTCHUF, NETS2014]: for ap in [None, "aggressive", "passive"]: cash_spent = 1000000000 price = 1000000 if contract.contract_type == "futures" or contract.contract_type == "prediction": quantity = cash_spent * contract.denominator / price / contract.lot_size else: payout_contract = contract.payout_contract quantity = cash_spent * contract.denominator * payout_contract.denominator / price fees_result[(user.username, contract.ticker, ap)] = get_fees(user, contract, price, quantity, ap=ap) self.assertDictEqual( fees_result, { (u'customer', u'BTC/HUF', None): { u'HUF': 10000000 }, (u'customer', u'BTC/HUF', 'aggressive'): { u'HUF': 10000000 }, (u'customer', u'BTC/HUF', 'passive'): { u'HUF': 10000000 }, (u'customer', u'BTC/MXN', None): { u'MXN': 5000000 }, (u'customer', u'BTC/MXN', 'aggressive'): { u'MXN': 5000000 }, (u'customer', u'BTC/MXN', 'passive'): { u'MXN': 5000000 }, (u'customer', u'NETS2014', None): { u'BTC': 20000000 }, (u'customer', u'NETS2014', 'aggressive'): { u'BTC': 20000000 }, (u'customer', u'NETS2014', 'passive'): { u'BTC': 20000000 }, (u'customer', u'NETS2015', None): { u'BTC': 35000000 }, (u'customer', u'NETS2015', 'aggressive'): { u'BTC': 35000000 }, (u'customer', u'NETS2015', 'passive'): { u'BTC': 35000000 }, (u'm2', u'BTC/HUF', None): { u'HUF': 0 }, (u'm2', u'BTC/HUF', 'aggressive'): { u'HUF': 0 }, (u'm2', u'BTC/HUF', 'passive'): { u'HUF': 0 }, (u'm2', u'BTC/MXN', None): { u'MXN': 0 }, (u'm2', u'BTC/MXN', 'aggressive'): { u'MXN': 0 }, (u'm2', u'BTC/MXN', 'passive'): { u'MXN': 0 }, (u'm2', u'NETS2014', None): { u'BTC': 0 }, (u'm2', u'NETS2014', 'aggressive'): { u'BTC': 0 }, (u'm2', u'NETS2014', 'passive'): { u'BTC': 0 }, (u'm2', u'NETS2015', None): { u'BTC': 0 }, (u'm2', u'NETS2015', 'aggressive'): { u'BTC': 0 }, (u'm2', u'NETS2015', 'passive'): { u'BTC': 0 }, (u'marketmaker', u'BTC/HUF', None): { u'HUF': 10000000 }, (u'marketmaker', u'BTC/HUF', 'aggressive'): { u'HUF': 10000000 }, (u'marketmaker', u'BTC/HUF', 'passive'): { u'HUF': -5000000 }, (u'marketmaker', u'BTC/MXN', None): { u'MXN': 5000000 }, (u'marketmaker', u'BTC/MXN', 'aggressive'): { u'MXN': 5000000 }, (u'marketmaker', u'BTC/MXN', 'passive'): { u'MXN': -2500000 }, (u'marketmaker', u'NETS2014', None): { u'BTC': 20000000 }, (u'marketmaker', u'NETS2014', 'aggressive'): { u'BTC': 20000000 }, (u'marketmaker', u'NETS2014', 'passive'): { u'BTC': -10000000 }, (u'marketmaker', u'NETS2015', None): { u'BTC': 35000000 }, (u'marketmaker', u'NETS2015', 'aggressive'): { u'BTC': 35000000 }, (u'marketmaker', u'NETS2015', 'passive'): { u'BTC': -17500000 }, (u'randomtrader', u'BTC/HUF', None): { u'HUF': 20000000 }, (u'randomtrader', u'BTC/HUF', 'aggressive'): { u'HUF': 20000000 }, (u'randomtrader', u'BTC/HUF', 'passive'): { u'HUF': 20000000 }, (u'randomtrader', u'BTC/MXN', None): { u'MXN': 10000000 }, (u'randomtrader', u'BTC/MXN', 'aggressive'): { u'MXN': 10000000 }, (u'randomtrader', u'BTC/MXN', 'passive'): { u'MXN': 10000000 }, (u'randomtrader', u'NETS2014', None): { u'BTC': 40000000 }, (u'randomtrader', u'NETS2014', 'aggressive'): { u'BTC': 40000000 }, (u'randomtrader', u'NETS2014', 'passive'): { u'BTC': 40000000 }, (u'randomtrader', u'NETS2015', None): { u'BTC': 70000000 }, (u'randomtrader', u'NETS2015', 'aggressive'): { u'BTC': 70000000 }, (u'randomtrader', u'NETS2015', 'passive'): { u'BTC': 70000000 } })
def test_trade_fees(self): BTCMXN = self.get_contract('BTC/MXN') NETS2015 = self.get_contract('NETS2015') BTCHUF = self.get_contract('BTC/HUF') NETS2014 = self.get_contract('NETS2014') marketmaker = self.get_user('marketmaker') randomtrader = self.get_user('randomtrader') m2 = self.get_user('m2') customer = self.get_user('customer') fees_result = {} for user in [marketmaker, randomtrader, m2, customer]: for contract in [BTCMXN, NETS2015, BTCHUF, NETS2014]: for ap in [None, "aggressive", "passive"]: cash_spent = 1000000000 price = 1000000 if contract.contract_type == "futures" or contract.contract_type == "prediction": quantity = cash_spent * contract.denominator / price / contract.lot_size else: payout_contract = contract.payout_contract quantity = cash_spent * contract.denominator * payout_contract.denominator / price fees_result[(user.username, contract.ticker, ap)] = get_fees(user, contract, price, quantity, ap=ap) self.assertDictEqual(fees_result, {(u'customer', u'BTC/HUF', None): {u'HUF': 10000000}, (u'customer', u'BTC/HUF', 'aggressive'): {u'HUF': 10000000}, (u'customer', u'BTC/HUF', 'passive'): {u'HUF': 10000000}, (u'customer', u'BTC/MXN', None): {u'MXN': 5000000}, (u'customer', u'BTC/MXN', 'aggressive'): {u'MXN': 5000000}, (u'customer', u'BTC/MXN', 'passive'): {u'MXN': 5000000}, (u'customer', u'NETS2014', None): {u'BTC': 20000000}, (u'customer', u'NETS2014', 'aggressive'): {u'BTC': 20000000}, (u'customer', u'NETS2014', 'passive'): {u'BTC': 20000000}, (u'customer', u'NETS2015', None): {u'BTC': 35000000}, (u'customer', u'NETS2015', 'aggressive'): {u'BTC': 35000000}, (u'customer', u'NETS2015', 'passive'): {u'BTC': 35000000}, (u'm2', u'BTC/HUF', None): {u'HUF': 0}, (u'm2', u'BTC/HUF', 'aggressive'): {u'HUF': 0}, (u'm2', u'BTC/HUF', 'passive'): {u'HUF': 0}, (u'm2', u'BTC/MXN', None): {u'MXN': 0}, (u'm2', u'BTC/MXN', 'aggressive'): {u'MXN': 0}, (u'm2', u'BTC/MXN', 'passive'): {u'MXN': 0}, (u'm2', u'NETS2014', None): {u'BTC': 0}, (u'm2', u'NETS2014', 'aggressive'): {u'BTC': 0}, (u'm2', u'NETS2014', 'passive'): {u'BTC': 0}, (u'm2', u'NETS2015', None): {u'BTC': 0}, (u'm2', u'NETS2015', 'aggressive'): {u'BTC': 0}, (u'm2', u'NETS2015', 'passive'): {u'BTC': 0}, (u'marketmaker', u'BTC/HUF', None): {u'HUF': 10000000}, (u'marketmaker', u'BTC/HUF', 'aggressive'): {u'HUF': 10000000}, (u'marketmaker', u'BTC/HUF', 'passive'): {u'HUF': -5000000}, (u'marketmaker', u'BTC/MXN', None): {u'MXN': 5000000}, (u'marketmaker', u'BTC/MXN', 'aggressive'): {u'MXN': 5000000}, (u'marketmaker', u'BTC/MXN', 'passive'): {u'MXN': -2500000}, (u'marketmaker', u'NETS2014', None): {u'BTC': 20000000}, (u'marketmaker', u'NETS2014', 'aggressive'): {u'BTC': 20000000}, (u'marketmaker', u'NETS2014', 'passive'): {u'BTC': -10000000}, (u'marketmaker', u'NETS2015', None): {u'BTC': 35000000}, (u'marketmaker', u'NETS2015', 'aggressive'): {u'BTC': 35000000}, (u'marketmaker', u'NETS2015', 'passive'): {u'BTC': -17500000}, (u'randomtrader', u'BTC/HUF', None): {u'HUF': 20000000}, (u'randomtrader', u'BTC/HUF', 'aggressive'): {u'HUF': 20000000}, (u'randomtrader', u'BTC/HUF', 'passive'): {u'HUF': 20000000}, (u'randomtrader', u'BTC/MXN', None): {u'MXN': 10000000}, (u'randomtrader', u'BTC/MXN', 'aggressive'): {u'MXN': 10000000}, (u'randomtrader', u'BTC/MXN', 'passive'): {u'MXN': 10000000}, (u'randomtrader', u'NETS2014', None): {u'BTC': 40000000}, (u'randomtrader', u'NETS2014', 'aggressive'): {u'BTC': 40000000}, (u'randomtrader', u'NETS2014', 'passive'): {u'BTC': 40000000}, (u'randomtrader', u'NETS2015', None): {u'BTC': 70000000}, (u'randomtrader', u'NETS2015', 'aggressive'): {u'BTC': 70000000}, (u'randomtrader', u'NETS2015', 'passive'): {u'BTC': 70000000}} )