Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save kccheung/b5dcbcbe3b1479a1ad14d7fa0e8301b9 to your computer and use it in GitHub Desktop.
Save kccheung/b5dcbcbe3b1479a1ad14d7fa0e8301b9 to your computer and use it in GitHub Desktop.
cli bitmex liquidation calculation formula, minimal implementation
#!/usr/bin/env python3
import argparse
from math import ceil, floor
sign = lambda amt: -1 if amt<0 else (1 if amt>0 else 0)
class dobj:
def __init__(self, **kwargs):
for k,v in kwargs.items():
setattr(self, k, v)
def target_liquidation(qty, price):
round_up = sign(qty) # make positive for round up
s = round_up * ceil(round_up * price / qty)
l = s if s else sign(qty / price)
return round(-100000000 / l * 2) * 0.5
def tousd(qty, price):
a = round(-100000000 / price)
return round(qty * a)
def step_pos(qty, usd, current_qty, cost):
return round(cost * min(1, -qty / current_qty)) + round(usd * min(1, -current_qty / qty)) if sign(qty) == -sign(current_qty) else 0
def new_pos_data(inst, pos, qty, price):
total_qty = pos.current_qty + qty
mark_usd = tousd(total_qty, inst.mark_price)
#if div: pos.init_margin_req = 1 / div
order_usd = tousd(qty, price)
total_usd = pos.current_cost + order_usd
new_pos_cross = pos.pos_cross - step_pos(qty, 0, pos.current_qty, pos.pos_cross);
#if div: new_pos_cross += Math.max(0, -pos.unrealised_pnl)
additional_cost = step_pos(qty, order_usd, pos.current_qty, pos.current_cost - pos.realised_cost)
net_total_usd = total_usd - (pos.realised_cost + additional_cost)
init_margin_usd = abs(net_total_usd) * pos.init_margin_req
additional_commission = round((abs(net_total_usd) + max(0, new_pos_cross + init_margin_usd)) * pos.commission)
new_unrealized_pnl = mark_usd - net_total_usd
return dobj(new_current_qty = total_qty,
new_mark_value = mark_usd,
new_pos_cross = new_pos_cross,
new_pos_init = init_margin_usd,
new_unrealised_pnl = new_unrealized_pnl,
new_pos_comm = additional_commission,
new_pos_cost = net_total_usd,
new_realised_pnl = pos.realised_pnl - additional_cost - round(abs(order_usd) * pos.commission),
new_maint_margin = max(0, new_pos_cross + init_margin_usd + additional_commission + new_unrealized_pnl))
def liquidation_price(inst, pos, marg, qty, price):
d = new_pos_data(inst, pos, qty, price)
new_mark_sign = sign(d.new_mark_value)
margin_req = pos.maint_margin_req
if margin_req > inst.maint_margin:
f = pos.risk_value - abs(pos.mark_value) + abs(d.new_mark_value);
p = d.new_pos_comm + abs(d.new_pos_cost) * min(pos.init_margin_req, min(margin_req, inst.maint_margin * (1 + ceil((f - inst.risk_limit) / inst.risk_step))) + max(0, inst.funding_rate * sign(d.new_current_qty)))
else:
p = d.new_pos_comm + abs(d.new_pos_cost) * min(pos.init_margin_req, margin_req + max(0, inst.funding_rate * sign(d.new_current_qty)));
x = d.new_pos_cross + d.new_pos_init + d.new_pos_comm + d.new_unrealised_pnl - p
if pos.cross_margin:
a = marg.margin_balance - pos.unrealised_pnl + d.new_unrealised_pnl - pos.realised_pnl + d.new_realised_pnl - marg.init_margin - (marg.maint_margin - pos.maint_margin + d.new_maint_margin);
x += max(0, floor(a / (1 + pos.commission)))
usd = target_liquidation(d.new_current_qty, new_mark_sign * max(0, new_mark_sign * (d.new_mark_value - x)));
return round(usd*2)*0.5
def xbt_calc(wallet_balance, contract_qty, entry_price, mark_price, cross_margin, leverage=None):
instrument = dobj(mark_price=mark_price, funding_rate=0.000169, risk_limit=20000000000, risk_step=10000000000, maint_margin=0.005)
init_margin_req = 0.01 if cross_margin else 1 / leverage
position = dobj(commission=0.00075, maint_margin_req=0.005, cross_margin=cross_margin, init_margin_req=init_margin_req, \
current_qty=0, current_cost=0, pos_cross=0, realised_cost=0, realised_pnl=0, unrealised_pnl=0, maint_margin=0)
margin = dobj(margin_balance=int((wallet_balance or 0)*1e8), init_margin=0, maint_margin=0)
return liquidation_price(instrument, position, margin, contract_qty, entry_price)
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument('--wallet-balance', type=float, help='wallet balance in BTC')
parser.add_argument('--contract-quantity', required=True, type=float, help='contract quantity in USD, negative for shorts')
parser.add_argument('--entry-price', required=True, type=float, help='entry price in USD')
parser.add_argument('--mark-price', type=float, help='mark price in USD')
parser.add_argument('--cross-margin', action='store_true', help='cross margin, alternatively use isolated')
parser.add_argument('--isolated-margin', action='store_true', help='isolated margin, alternatively use cross margin')
parser.add_argument('--leverage', type=int, help='leverage to use with isolated margin')
options = parser.parse_args()
if not options.cross_margin:
options.isolated_margin = True
if options.isolated_margin:
assert options.leverage
if options.cross_margin:
assert options.wallet_balance
if not options.mark_price:
options.mark_price = options.entry_price
print(xbt_calc(options.wallet_balance, options.contract_quantity, options.entry_price, options.mark_price, options.cross_margin, options.leverage))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment