Added gateio and okex isolated liquidation formulas

This commit is contained in:
Sam Germain 2021-11-13 21:12:36 -06:00
parent ba02605d77
commit 5796d95a95
2 changed files with 228 additions and 57 deletions

View File

@ -6,18 +6,74 @@ from freqtrade.exceptions import OperationalException
def liquidation_price(
exchange_name: str,
open_rate: float,
open_rate: float, # (b) Entry price of position
is_short: bool,
leverage: float,
trading_mode: TradingMode,
collateral: Optional[Collateral],
wallet_balance: Optional[float],
mm_ex_1: Optional[float],
upnl_ex_1: Optional[float],
maintenance_amt: Optional[float],
position: Optional[float],
mm_rate: Optional[float]
# Binance
collateral_amount: Optional[float] = None, # (bg)
mm_ex_1: Optional[float] = None, # (b)
upnl_ex_1: Optional[float] = None, # (b)
maintenance_amt: Optional[float] = None, # (b) (cum_b)
position: Optional[float] = None, # (b) Absolute value of position size
mm_rate: Optional[float] = None, # (b)
# Gateio & Okex
mm_ratio: Optional[float] = None, # (go)
taker_fee_rate: Optional[float] = None, # (go)
# Gateio
base_size: Optional[float] = None, # (g)
# Okex
liability: Optional[float] = None, # (o)
interest: Optional[float] = None, # (o)
position_assets: Optional[float] = None, # (o)
) -> Optional[float]:
'''
exchange_name
is_short
leverage
trading_mode
collateral
#
open_rate - b
collateral_amount - bg
In Cross margin mode, WB is crossWalletBalance
In Isolated margin mode, WB is isolatedWalletBalance of the isolated position,
TMM=0, UPNL=0, substitute the position quantity, MMR, cum into the formula to calculate.
Under the cross margin mode, the same ticker/symbol,
both long and short position share the same liquidation price except in the isolated mode.
Under the isolated mode, each isolated position will have different liquidation prices depending
on the margin allocated to the positions.
mm_ex_1 - b
Maintenance Margin of all other contracts, excluding Contract 1
If it is an isolated margin mode, then TMM=0UPNL=0
upnl_ex_1 - b
Unrealized PNL of all other contracts, excluding Contract 1
If it is an isolated margin mode, then UPNL=0
maintenance_amt (cumb) - b
Maintenance Amount of position
position - b
Absolute value of position size
mm_rate - b
Maintenance margin rate of position
# Gateio & okex
mm_ratio - go
- [assets in the position - (liability +interest) * mark price] / (maintenance margin + liquidation fee) (okex)
taker_fee_rate - go
# Gateio
base_size - g
The size of the position in base currency
# Okex
liability - o
Initial liabilities + deducted interest
Long positions: Liability is calculated in quote currency.
Short positions: Liability is calculated in trading currency.
interest - o
Interest that has not been deducted yet.
position_assets - o
# * I think this is the same as collateral_amount
Total position assets on-hold by pending order
'''
if trading_mode == TradingMode.SPOT:
return None
@ -29,7 +85,7 @@ def liquidation_price(
if exchange_name.lower() == "binance":
if (
wallet_balance is None or
collateral_amount is None or
mm_ex_1 is None or
upnl_ex_1 is None or
maintenance_amt is None or
@ -48,7 +104,7 @@ def liquidation_price(
leverage=leverage,
trading_mode=trading_mode,
collateral=collateral, # type: ignore
wallet_balance=wallet_balance,
wallet_balance=collateral_amount,
mm_ex_1=mm_ex_1,
upnl_ex_1=upnl_ex_1,
maintenance_amt=maintenance_amt, # type: ignore
@ -59,6 +115,51 @@ def liquidation_price(
return kraken(open_rate, is_short, leverage, trading_mode, collateral)
elif exchange_name.lower() == "ftx":
return ftx(open_rate, is_short, leverage, trading_mode, collateral)
elif exchange_name.lower() == "gateio":
if (
not collateral_amount or
not base_size or
not mm_ratio or
not taker_fee_rate
):
raise OperationalException(
f"{exchange_name} {collateral} {trading_mode} requires parameters "
f"collateral_amount, contract_size, num_contracts, mm_ratio and taker_fee"
)
else:
return gateio(
open_rate=open_rate,
is_short=is_short,
trading_mode=trading_mode,
collateral=collateral,
collateral_amount=collateral_amount,
base_size=base_size,
mm_ratio=mm_ratio,
taker_fee_rate=taker_fee_rate
)
elif exchange_name.lower() == "okex":
if (
not mm_ratio or
not liability or
not interest or
not taker_fee_rate or
not position_assets
):
raise OperationalException(
f"{exchange_name} {collateral} {trading_mode} requires parameters "
f"mm_ratio, liability, interest, taker_fee_rate, position_assets"
)
else:
return okex(
is_short=is_short,
trading_mode=trading_mode,
collateral=collateral,
mm_ratio=mm_ratio,
liability=liability,
interest=interest,
taker_fee_rate=taker_fee_rate,
position_assets=position_assets,
)
raise OperationalException(
f"liquidation_price is not implemented for {exchange_name}"
)
@ -99,24 +200,16 @@ def binance(
:param leverage: leverage in float
:param trading_mode: spot, margin, futures
:param collateral: cross, isolated
:param wallet_balance: Wallet Balance is crossWalletBalance in Cross-Margin Mode.
Wallet Balance is isolatedWalletBalance in Isolated Margin Mode
:param mm_ex_1: Maintenance Margin of all other contracts,
excluding Contract 1. If it is an isolated margin mode, then TMM=0
:param upnl_ex_1: Unrealized PNL of all other contracts, excluding Contract 1.
If it is an isolated margin mode, then UPNL=0
:param maintenance_amt: Maintenance Amount of position (one-way mode)
:param position: Absolute value of position size (one-way mode)
:param open_rate: Entry Price of position (one-way mode)
:param mm_rate: Maintenance margin rate of position (one-way mode)
"""
# TODO-lev: Additional arguments, fill in formulas
wb = wallet_balance
@ -162,7 +255,6 @@ def kraken(
):
"""
Calculates the liquidation price on Kraken
:param name: Name of the exchange
:param trading_mode: spot, margin, futures
:param collateral: cross, isolated
"""
@ -189,7 +281,6 @@ def ftx(
):
"""
Calculates the liquidation price on FTX
:param name: Name of the exchange
:param trading_mode: spot, margin, futures
:param collateral: cross, isolated
"""
@ -201,18 +292,99 @@ def ftx(
exception("ftx", trading_mode, collateral)
if __name__ == '__main__':
print(liquidation_price(
"binance",
32481.980,
False,
1,
TradingMode.FUTURES,
Collateral.ISOLATED,
1535443.01,
356512.508,
0.0,
16300.000,
109.488,
0.025
))
def gateio(
open_rate: float,
is_short: bool,
trading_mode: TradingMode,
collateral: Collateral,
collateral_amount: float,
base_size: float,
mm_ratio: float,
taker_fee_rate: float,
is_inverse: bool = False
):
"""
Calculates the liquidation price on Gate.io
:param open_rate: Entry Price of position
:param is_short: True for short trades
:param trading_mode: spot, margin, futures
:param collateral: cross, isolated
:param collateral_amount: Also called margin
:param base_size: size of position in base currency
contract_size / num_contracts
contract_size: How much one contract is worth
num_contracts: Also called position
:param mm_ratio: Viewed in contract details
:param taker_fee_rate:
:param is_inverse: True if settle currency matches base currency
( Opening Price ± Margin/Contract Multiplier/Position ) / [ 1 ± ( MMR + Taker Fee)]
'±' in the formula refers to the direction of the contract,
go long refers to '-'
go short refers to '+'
Position refers to the number of contracts.
Maintenance Margin Ratio and Contract Multiplier can be viewed in the Contract Details.
https://www.gate.io/help/futures/perpetual/22160/calculation-of-liquidation-price
"""
if trading_mode == TradingMode.FUTURES and collateral == Collateral.ISOLATED:
if is_inverse:
raise OperationalException(
"Freqtrade does not support inverse contracts at the moment")
value = collateral_amount / base_size
mm_ratio_taker = (mm_ratio + taker_fee_rate)
if is_short:
return (open_rate + value) / (1 + mm_ratio_taker)
else:
return (open_rate - value) / (1 - mm_ratio_taker)
else:
exception("gatio", trading_mode, collateral)
def okex(
is_short: bool,
trading_mode: TradingMode,
collateral: Collateral,
liability: float,
interest: float,
mm_ratio: float,
taker_fee_rate: float,
position_assets: float
):
'''
https://www.okex.com/support/hc/en-us/articles/
360053909592-VI-Introduction-to-the-isolated-mode-of-Single-Multi-currency-Portfolio-margin
Initial liabilities + deducted interest
Long positions: Liability is calculated in quote currency.
Short positions: Liability is calculated in trading currency.
interest: Interest that has not been deducted yet.
Margin ratio
Long: [position_assets - (liability + interest) / mark_price] / (maintenance_margin + fees)
Short: [position_assets - (liability + interest) * mark_price] / (maintenance_margin + fees)
'''
if trading_mode == TradingMode.FUTURES and collateral == Collateral.ISOLATED:
if is_short:
return (liability + interest) * (1 + mm_ratio) * (1 + taker_fee_rate)
else:
return (liability + interest) * (1 + mm_ratio) * (1 + taker_fee_rate) / position_assets
else:
exception("okex", trading_mode, collateral)
# if __name__ == '__main__':
# print(liquidation_price(
# "binance",
# 32481.980,
# False,
# 1,
# TradingMode.FUTURES,
# Collateral.ISOLATED,
# 1535443.01,
# 356512.508,
# 0.0,
# 16300.000,
# 109.488,
# 0.025
# ))

View File

@ -16,8 +16,7 @@ from sqlalchemy.sql.schema import UniqueConstraint
from freqtrade.constants import DATETIME_PRINT_FORMAT, NON_OPEN_EXCHANGE_STATES
from freqtrade.enums import Collateral, SellType, TradingMode
from freqtrade.exceptions import DependencyException, OperationalException
from freqtrade.leverage import interest
from freqtrade.leverage import liquidation_price
from freqtrade.leverage import interest, liquidation_price
from freqtrade.misc import safe_value_fallback
from freqtrade.persistence.migrations import check_migrate