ddx-python 1.0.5__cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- ddx/.gitignore +1 -0
- ddx/__init__.py +58 -0
- ddx/_rust/__init__.pyi +2009 -0
- ddx/_rust/common/__init__.pyi +17 -0
- ddx/_rust/common/accounting.pyi +6 -0
- ddx/_rust/common/enums.pyi +3 -0
- ddx/_rust/common/requests/__init__.pyi +21 -0
- ddx/_rust/common/requests/intents.pyi +19 -0
- ddx/_rust/common/specs.pyi +17 -0
- ddx/_rust/common/state/__init__.pyi +41 -0
- ddx/_rust/common/state/keys.pyi +29 -0
- ddx/_rust/common/transactions.pyi +7 -0
- ddx/_rust/decimal.pyi +3 -0
- ddx/_rust/h256.pyi +3 -0
- ddx/_rust.abi3.so +0 -0
- ddx/app_config/ethereum/addresses.json +541 -0
- ddx/auditor/README.md +32 -0
- ddx/auditor/__init__.py +0 -0
- ddx/auditor/auditor_driver.py +1034 -0
- ddx/auditor/websocket_message.py +54 -0
- ddx/common/__init__.py +0 -0
- ddx/common/epoch_params.py +28 -0
- ddx/common/fill_context.py +144 -0
- ddx/common/item_utils.py +38 -0
- ddx/common/logging.py +184 -0
- ddx/common/market_specs.py +64 -0
- ddx/common/trade_mining_params.py +19 -0
- ddx/common/transaction_utils.py +85 -0
- ddx/common/transactions/__init__.py +0 -0
- ddx/common/transactions/advance_epoch.py +91 -0
- ddx/common/transactions/advance_settlement_epoch.py +63 -0
- ddx/common/transactions/all_price_checkpoints.py +84 -0
- ddx/common/transactions/cancel.py +76 -0
- ddx/common/transactions/cancel_all.py +88 -0
- ddx/common/transactions/complete_fill.py +103 -0
- ddx/common/transactions/disaster_recovery.py +97 -0
- ddx/common/transactions/event.py +48 -0
- ddx/common/transactions/fee_distribution.py +119 -0
- ddx/common/transactions/funding.py +294 -0
- ddx/common/transactions/futures_expiry.py +123 -0
- ddx/common/transactions/genesis.py +108 -0
- ddx/common/transactions/inner/__init__.py +0 -0
- ddx/common/transactions/inner/adl_outcome.py +25 -0
- ddx/common/transactions/inner/fill.py +227 -0
- ddx/common/transactions/inner/liquidated_position.py +41 -0
- ddx/common/transactions/inner/liquidation_entry.py +41 -0
- ddx/common/transactions/inner/liquidation_fill.py +118 -0
- ddx/common/transactions/inner/outcome.py +32 -0
- ddx/common/transactions/inner/trade_fill.py +125 -0
- ddx/common/transactions/insurance_fund_update.py +142 -0
- ddx/common/transactions/insurance_fund_withdraw.py +99 -0
- ddx/common/transactions/liquidation.py +357 -0
- ddx/common/transactions/partial_fill.py +125 -0
- ddx/common/transactions/pnl_realization.py +122 -0
- ddx/common/transactions/post.py +72 -0
- ddx/common/transactions/post_order.py +95 -0
- ddx/common/transactions/price_checkpoint.py +96 -0
- ddx/common/transactions/signer_registered.py +62 -0
- ddx/common/transactions/specs_update.py +61 -0
- ddx/common/transactions/strategy_update.py +156 -0
- ddx/common/transactions/tradable_product_update.py +98 -0
- ddx/common/transactions/trade_mining.py +147 -0
- ddx/common/transactions/trader_update.py +105 -0
- ddx/common/transactions/withdraw.py +91 -0
- ddx/common/transactions/withdraw_ddx.py +74 -0
- ddx/common/utils.py +176 -0
- ddx/config.py +17 -0
- ddx/derivadex_client.py +254 -0
- ddx/py.typed +0 -0
- ddx/realtime_client/__init__.py +2 -0
- ddx/realtime_client/config.py +2 -0
- ddx/realtime_client/logs/pytest.log +0 -0
- ddx/realtime_client/models/__init__.py +683 -0
- ddx/realtime_client/realtime_client.py +567 -0
- ddx/rest_client/__init__.py +0 -0
- ddx/rest_client/clients/__init__.py +0 -0
- ddx/rest_client/clients/base_client.py +60 -0
- ddx/rest_client/clients/market_client.py +1241 -0
- ddx/rest_client/clients/on_chain_client.py +432 -0
- ddx/rest_client/clients/signed_client.py +301 -0
- ddx/rest_client/clients/system_client.py +843 -0
- ddx/rest_client/clients/trade_client.py +335 -0
- ddx/rest_client/constants/__init__.py +0 -0
- ddx/rest_client/constants/endpoints.py +67 -0
- ddx/rest_client/contracts/__init__.py +0 -0
- ddx/rest_client/contracts/checkpoint/__init__.py +560 -0
- ddx/rest_client/contracts/ddx/__init__.py +1949 -0
- ddx/rest_client/contracts/dummy_token/__init__.py +1014 -0
- ddx/rest_client/contracts/i_collateral/__init__.py +1414 -0
- ddx/rest_client/contracts/i_stake/__init__.py +696 -0
- ddx/rest_client/exceptions/__init__.py +0 -0
- ddx/rest_client/exceptions/exceptions.py +32 -0
- ddx/rest_client/http/__init__.py +0 -0
- ddx/rest_client/http/http_client.py +305 -0
- ddx/rest_client/models/__init__.py +0 -0
- ddx/rest_client/models/market.py +683 -0
- ddx/rest_client/models/signed.py +60 -0
- ddx/rest_client/models/system.py +390 -0
- ddx/rest_client/models/trade.py +140 -0
- ddx/rest_client/utils/__init__.py +0 -0
- ddx/rest_client/utils/encryption_utils.py +26 -0
- ddx_python-1.0.5.dist-info/METADATA +63 -0
- ddx_python-1.0.5.dist-info/RECORD +104 -0
- ddx_python-1.0.5.dist-info/WHEEL +4 -0
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
"""
|
|
2
|
+
InsuranceFundUpdate
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from attrs import define, field
|
|
6
|
+
from ddx.common.item_utils import update_avail_balance, update_locked_balance
|
|
7
|
+
from ddx.common.transactions.event import Event
|
|
8
|
+
from ddx._rust.common import TokenSymbol
|
|
9
|
+
from ddx._rust.common.state import DerivadexSMT, InsuranceFundContribution
|
|
10
|
+
from ddx._rust.common.state.keys import InsuranceFundContributionKey, InsuranceFundKey
|
|
11
|
+
from ddx._rust.common.transactions import InsuranceFundUpdateKind
|
|
12
|
+
from ddx._rust.decimal import Decimal
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@define
|
|
16
|
+
class InsuranceFundUpdate(Event):
|
|
17
|
+
"""
|
|
18
|
+
Defines an InsuranceFundUpdate
|
|
19
|
+
|
|
20
|
+
An InsuranceFundUpdate is an update to a trader's insurance fund
|
|
21
|
+
contribution (such as depositing or withdrawing
|
|
22
|
+
collateral).
|
|
23
|
+
|
|
24
|
+
Attributes:
|
|
25
|
+
address (str): Trader's Ethereum address this insurance fund update belongs to
|
|
26
|
+
collateral_address (str): Collateral's Ethereum address a deposit/withdrawal has been made with
|
|
27
|
+
amount (Decimal): The amount of collateral deposited or withdrawn
|
|
28
|
+
update_kind (InsuranceFundUpdateKind): Update kind (Deposit=0, Withdraw=1)
|
|
29
|
+
tx_hash (str): The Ethereum transaction's hash
|
|
30
|
+
request_index (int): Sequenced request index of transaction
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
address: str = field(eq=str.lower)
|
|
34
|
+
collateral_address: str = field(eq=str.lower)
|
|
35
|
+
amount: Decimal
|
|
36
|
+
update_kind: InsuranceFundUpdateKind
|
|
37
|
+
tx_hash: str = field(eq=str.lower)
|
|
38
|
+
request_index: int = field(default=-1, eq=False)
|
|
39
|
+
|
|
40
|
+
@classmethod
|
|
41
|
+
def decode_value_into_cls(cls, raw_tx_log_event: dict):
|
|
42
|
+
"""
|
|
43
|
+
Decode a raw transaction log event (dict) into an
|
|
44
|
+
InsuranceFundUpdate instance.
|
|
45
|
+
|
|
46
|
+
Parameters
|
|
47
|
+
----------
|
|
48
|
+
raw_tx_log_event : dict
|
|
49
|
+
Raw transaction log event being processed
|
|
50
|
+
"""
|
|
51
|
+
|
|
52
|
+
insurance_fund_update_tx_event = raw_tx_log_event["event"]["c"]
|
|
53
|
+
|
|
54
|
+
return cls(
|
|
55
|
+
insurance_fund_update_tx_event["address"],
|
|
56
|
+
insurance_fund_update_tx_event["collateralAddress"],
|
|
57
|
+
Decimal(insurance_fund_update_tx_event["amount"]),
|
|
58
|
+
InsuranceFundUpdateKind(insurance_fund_update_tx_event["updateKind"]),
|
|
59
|
+
insurance_fund_update_tx_event["txHash"],
|
|
60
|
+
raw_tx_log_event["requestIndex"],
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
def process_tx(
|
|
64
|
+
self,
|
|
65
|
+
smt: DerivadexSMT,
|
|
66
|
+
**kwargs,
|
|
67
|
+
):
|
|
68
|
+
"""
|
|
69
|
+
Process an InsuranceFundUpdate transaction. An
|
|
70
|
+
InsuranceFundUpdate consists of information relating to updates
|
|
71
|
+
for a trader's insurance fund contribution, such
|
|
72
|
+
as when their free or frozen balance has changed due to a
|
|
73
|
+
deposit or withdrawal. This will update the
|
|
74
|
+
InsuranceFundContribution leaf in the SMT.
|
|
75
|
+
|
|
76
|
+
Parameters
|
|
77
|
+
----------
|
|
78
|
+
smt: DerivadexSMT
|
|
79
|
+
DerivaDEX Sparse Merkle Tree
|
|
80
|
+
**kwargs
|
|
81
|
+
Additional args specific to InsuranceFundUpdate transactions
|
|
82
|
+
"""
|
|
83
|
+
|
|
84
|
+
insurance_fund_contribution_key: InsuranceFundContributionKey = (
|
|
85
|
+
InsuranceFundContributionKey(self.address)
|
|
86
|
+
)
|
|
87
|
+
insurance_fund_contribution = smt.insurance_fund_contribution(
|
|
88
|
+
insurance_fund_contribution_key
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
symbol = TokenSymbol.from_address(self.collateral_address)
|
|
92
|
+
if self.update_kind == InsuranceFundUpdateKind.Deposit:
|
|
93
|
+
# If InsuranceFundUpdate is of deposit type
|
|
94
|
+
|
|
95
|
+
if insurance_fund_contribution is None:
|
|
96
|
+
# If we haven't yet seen the InsuranceFundContribution
|
|
97
|
+
# leaf, create a new one
|
|
98
|
+
insurance_fund_contribution = InsuranceFundContribution.default()
|
|
99
|
+
|
|
100
|
+
# Adjust the existing strategy leaf by incrementing the
|
|
101
|
+
# free collateral by the amount in the deposit event
|
|
102
|
+
update_avail_balance(
|
|
103
|
+
insurance_fund_contribution,
|
|
104
|
+
symbol,
|
|
105
|
+
insurance_fund_contribution.avail_balance[symbol] + self.amount,
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
insurance_fund_key: InsuranceFundKey = InsuranceFundKey()
|
|
109
|
+
insurance_fund = smt.insurance_fund(insurance_fund_key)
|
|
110
|
+
|
|
111
|
+
insurance_fund[symbol] += self.amount
|
|
112
|
+
|
|
113
|
+
# Update the SMT with the H256 repr of the key and the
|
|
114
|
+
# InsuranceFundContribution leaf
|
|
115
|
+
smt.store_insurance_fund_contribution(
|
|
116
|
+
insurance_fund_contribution_key, insurance_fund_contribution
|
|
117
|
+
)
|
|
118
|
+
smt.store_insurance_fund(insurance_fund_key, insurance_fund)
|
|
119
|
+
elif self.update_kind == InsuranceFundUpdateKind.Withdraw:
|
|
120
|
+
# If InsuranceFundUpdate is of withdrawal (claimed) type
|
|
121
|
+
if (
|
|
122
|
+
insurance_fund_contribution is None
|
|
123
|
+
or insurance_fund_contribution.locked_balance[symbol] < self.amount
|
|
124
|
+
):
|
|
125
|
+
raise Exception(
|
|
126
|
+
"InsuranceFundContribution leaf either non-existent or insufficiently capitalized to facilitate withdrawal"
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
# Adjust the existing InsuranceFundContribution leaf by decrementing the
|
|
130
|
+
# free balance by the amount in the withdrawal event
|
|
131
|
+
insurance_fund_contribution.locked_balance[symbol] -= self.amount
|
|
132
|
+
update_locked_balance(
|
|
133
|
+
insurance_fund_contribution,
|
|
134
|
+
symbol,
|
|
135
|
+
insurance_fund_contribution.locked_balance[symbol] - self.amount,
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
# Update the SMT with the H256 repr of the key and the
|
|
139
|
+
# Strategy leaf
|
|
140
|
+
smt.store_insurance_fund_contribution(
|
|
141
|
+
insurance_fund_contribution_key, insurance_fund_contribution
|
|
142
|
+
)
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
"""
|
|
2
|
+
InsuranceFundWithdraw module
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from attrs import define, field
|
|
6
|
+
from ddx.common.item_utils import update_avail_balance, update_locked_balance
|
|
7
|
+
from ddx.common.transactions.event import Event
|
|
8
|
+
from ddx._rust.common import TokenSymbol
|
|
9
|
+
from ddx._rust.common.state import DerivadexSMT, InsuranceFundContribution
|
|
10
|
+
from ddx._rust.common.state.keys import InsuranceFundContributionKey, InsuranceFundKey
|
|
11
|
+
from ddx._rust.decimal import Decimal
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@define
|
|
15
|
+
class InsuranceFundWithdraw(Event):
|
|
16
|
+
"""
|
|
17
|
+
Defines an InsuranceFundWithdraw Update
|
|
18
|
+
|
|
19
|
+
An InsuranceFundWithdraw is when a withdrawal of collateral is **signaled**.
|
|
20
|
+
|
|
21
|
+
Attributes:
|
|
22
|
+
recipient_address (str): Trader address DDX is being withdrawn to
|
|
23
|
+
amount (Decimal): The amount of DDX being withdrawn
|
|
24
|
+
request_index (int): Sequenced request index of transaction
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
recipient_address: str = field(eq=str.lower)
|
|
28
|
+
currency: str = field(eq=str.lower)
|
|
29
|
+
amount: Decimal
|
|
30
|
+
request_index: int = field(default=-1, eq=False)
|
|
31
|
+
|
|
32
|
+
@classmethod
|
|
33
|
+
def decode_value_into_cls(cls, raw_tx_log_event: dict):
|
|
34
|
+
"""
|
|
35
|
+
Decode a raw transaction log event (dict) into a Withdraw
|
|
36
|
+
instance.
|
|
37
|
+
|
|
38
|
+
Parameters
|
|
39
|
+
----------
|
|
40
|
+
raw_tx_log_event : dict
|
|
41
|
+
Raw transaction log event being processed
|
|
42
|
+
"""
|
|
43
|
+
|
|
44
|
+
insurance_fund_withdraw_tx_event = raw_tx_log_event["event"]["c"]
|
|
45
|
+
|
|
46
|
+
return cls(
|
|
47
|
+
insurance_fund_withdraw_tx_event["recipientAddress"],
|
|
48
|
+
insurance_fund_withdraw_tx_event["currency"],
|
|
49
|
+
Decimal(insurance_fund_withdraw_tx_event["amount"]),
|
|
50
|
+
raw_tx_log_event["requestIndex"],
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
def process_tx(
|
|
54
|
+
self,
|
|
55
|
+
smt: DerivadexSMT,
|
|
56
|
+
**kwargs,
|
|
57
|
+
):
|
|
58
|
+
"""
|
|
59
|
+
Process a Withdraw transaction. A Withdraw event consists
|
|
60
|
+
of consists of information relating to withdrawal of collateral.
|
|
61
|
+
|
|
62
|
+
Parameters
|
|
63
|
+
----------
|
|
64
|
+
smt: DerivadexSMT
|
|
65
|
+
DerivaDEX Sparse Merkle Tree
|
|
66
|
+
**kwargs
|
|
67
|
+
Additional args specific to InsuranceFundWithdraw transactions
|
|
68
|
+
"""
|
|
69
|
+
|
|
70
|
+
insurance_fund_key: InsuranceFundKey = InsuranceFundKey()
|
|
71
|
+
insurance_fund = smt.insurance_fund(insurance_fund_key)
|
|
72
|
+
|
|
73
|
+
contributor_key: InsuranceFundContributionKey = InsuranceFundContributionKey(
|
|
74
|
+
self.recipient_address
|
|
75
|
+
)
|
|
76
|
+
contributor: InsuranceFundContribution = smt.insurance_fund_contribution(
|
|
77
|
+
contributor_key
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
# Decrement the free balance by the withdrawn amount
|
|
81
|
+
symbol = TokenSymbol.from_address(self.currency)
|
|
82
|
+
update_avail_balance(
|
|
83
|
+
contributor, symbol, contributor.avail_balance[symbol] - self.amount
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
# Increment the frozen balance by the withdrawn amount
|
|
87
|
+
update_locked_balance(
|
|
88
|
+
contributor, symbol, contributor.locked_balance[symbol] + self.amount
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
# Update the SMT with the H256 repr of the key and
|
|
92
|
+
# the InsuranceFundContribution leaf for the signer
|
|
93
|
+
smt.store_insurance_fund_contribution(contributor_key, contributor)
|
|
94
|
+
|
|
95
|
+
insurance_fund[symbol] -= self.amount
|
|
96
|
+
|
|
97
|
+
# Update the SMT with the H256 repr of the key and the
|
|
98
|
+
# Strategy leaf
|
|
99
|
+
smt.store_insurance_fund(insurance_fund_key, insurance_fund)
|
|
@@ -0,0 +1,357 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Liquidation module
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from attrs import define, field
|
|
6
|
+
from ddx.common.item_utils import update_avail_collateral
|
|
7
|
+
from ddx.common.transactions.cancel import Cancel
|
|
8
|
+
from ddx.common.transactions.event import Event
|
|
9
|
+
from ddx.common.transactions.inner.adl_outcome import AdlOutcome
|
|
10
|
+
from ddx.common.transactions.inner.liquidated_position import LiquidatedPosition
|
|
11
|
+
from ddx.common.transactions.inner.liquidation_entry import LiquidationEntry
|
|
12
|
+
from ddx.common.transactions.inner.liquidation_fill import LiquidationFill
|
|
13
|
+
from ddx.common.transactions.inner.outcome import Outcome
|
|
14
|
+
from ddx._rust.common import ProductSymbol, TokenSymbol
|
|
15
|
+
from ddx._rust.common.enums import OrderSide, PositionSide
|
|
16
|
+
from ddx._rust.common.state import (DerivadexSMT, InsuranceFund, Position,
|
|
17
|
+
Price, Strategy)
|
|
18
|
+
from ddx._rust.common.state.keys import (InsuranceFundKey, PositionKey,
|
|
19
|
+
PriceKey, StrategyKey)
|
|
20
|
+
from ddx._rust.decimal import Decimal
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def compute_strategy_total_value(
|
|
24
|
+
strategy: Strategy,
|
|
25
|
+
position_leaves: dict[ProductSymbol, Position],
|
|
26
|
+
prices: dict[ProductSymbol, tuple[PriceKey, Price]],
|
|
27
|
+
):
|
|
28
|
+
# Compute Strategy total value prior to liquidation
|
|
29
|
+
strategy_total_value = strategy.avail_collateral[TokenSymbol.USDC]
|
|
30
|
+
|
|
31
|
+
for symbol in position_leaves:
|
|
32
|
+
# Obtain latest mark price for the symbol
|
|
33
|
+
mark_price = prices[symbol][1].mark_price
|
|
34
|
+
|
|
35
|
+
# Compute unrealized PNL for Position
|
|
36
|
+
unrealized_pnl = position_leaves[symbol].unrealized_pnl(mark_price)
|
|
37
|
+
|
|
38
|
+
# Adjust the Strategy's total value to account for
|
|
39
|
+
# the Position's unrealized pnl
|
|
40
|
+
strategy_total_value += unrealized_pnl
|
|
41
|
+
|
|
42
|
+
return strategy_total_value
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
@define
|
|
46
|
+
class Liquidation(Event):
|
|
47
|
+
"""
|
|
48
|
+
Defines an Liquidation
|
|
49
|
+
|
|
50
|
+
A Liquidation contains a list of LiquidationEntry objects.
|
|
51
|
+
|
|
52
|
+
Attributes:
|
|
53
|
+
liquidation_entries (list[LiquidationEntry]): A list of LiquidationEntry objects
|
|
54
|
+
request_index (int): Sequenced request index of transaction
|
|
55
|
+
"""
|
|
56
|
+
|
|
57
|
+
liquidation_entries: list[LiquidationEntry] = field(eq=set)
|
|
58
|
+
request_index: int = field(default=-1, eq=False)
|
|
59
|
+
|
|
60
|
+
@classmethod
|
|
61
|
+
def decode_value_into_cls(cls, raw_tx_log_event: dict):
|
|
62
|
+
"""
|
|
63
|
+
Decode a raw transaction log event (dict) into a Liquidation
|
|
64
|
+
instance.
|
|
65
|
+
|
|
66
|
+
Parameters
|
|
67
|
+
----------
|
|
68
|
+
raw_tx_log_event : dict
|
|
69
|
+
Raw transaction log event being processed
|
|
70
|
+
"""
|
|
71
|
+
|
|
72
|
+
event = raw_tx_log_event["event"]["c"]
|
|
73
|
+
liquidation_tx_event = event["strategies"]
|
|
74
|
+
|
|
75
|
+
return cls(
|
|
76
|
+
[
|
|
77
|
+
LiquidationEntry(
|
|
78
|
+
liquidation_entry["traderAddress"],
|
|
79
|
+
liquidation_entry["strategyIdHash"],
|
|
80
|
+
[
|
|
81
|
+
Cancel(
|
|
82
|
+
ProductSymbol(canceled_order["symbol"]),
|
|
83
|
+
canceled_order["orderHash"],
|
|
84
|
+
Decimal(canceled_order["amount"]),
|
|
85
|
+
raw_tx_log_event["requestIndex"],
|
|
86
|
+
)
|
|
87
|
+
for canceled_order in liquidation_entry["canceledOrders"]
|
|
88
|
+
],
|
|
89
|
+
[
|
|
90
|
+
(
|
|
91
|
+
ProductSymbol(liquidated_position_key),
|
|
92
|
+
LiquidatedPosition(
|
|
93
|
+
Decimal(liquidated_position_val["amount"]),
|
|
94
|
+
[
|
|
95
|
+
(
|
|
96
|
+
LiquidationFill(
|
|
97
|
+
ProductSymbol(
|
|
98
|
+
trade_outcome["Fill"]["symbol"]
|
|
99
|
+
),
|
|
100
|
+
trade_outcome["Fill"]["indexPriceHash"],
|
|
101
|
+
trade_outcome["Fill"]["makerOrderHash"],
|
|
102
|
+
Decimal(
|
|
103
|
+
trade_outcome["Fill"][
|
|
104
|
+
"makerOrderRemainingAmount"
|
|
105
|
+
]
|
|
106
|
+
),
|
|
107
|
+
Decimal(trade_outcome["Fill"]["amount"]),
|
|
108
|
+
Decimal(trade_outcome["Fill"]["price"]),
|
|
109
|
+
OrderSide(
|
|
110
|
+
trade_outcome["Fill"]["takerSide"]
|
|
111
|
+
),
|
|
112
|
+
Outcome(
|
|
113
|
+
trade_outcome["Fill"]["makerOutcome"][
|
|
114
|
+
"trader"
|
|
115
|
+
],
|
|
116
|
+
trade_outcome["Fill"]["makerOutcome"][
|
|
117
|
+
"strategyIdHash"
|
|
118
|
+
],
|
|
119
|
+
),
|
|
120
|
+
raw_tx_log_event["timeValue"],
|
|
121
|
+
raw_tx_log_event["requestIndex"],
|
|
122
|
+
)
|
|
123
|
+
if "Fill" in trade_outcome
|
|
124
|
+
else Cancel(
|
|
125
|
+
ProductSymbol(
|
|
126
|
+
trade_outcome["Cancel"]["symbol"]
|
|
127
|
+
),
|
|
128
|
+
trade_outcome["Cancel"]["orderHash"],
|
|
129
|
+
Decimal(trade_outcome["Cancel"]["amount"]),
|
|
130
|
+
raw_tx_log_event["requestIndex"],
|
|
131
|
+
)
|
|
132
|
+
)
|
|
133
|
+
for trade_outcome in liquidated_position_val[
|
|
134
|
+
"tradeOutcomes"
|
|
135
|
+
]
|
|
136
|
+
],
|
|
137
|
+
[
|
|
138
|
+
AdlOutcome(
|
|
139
|
+
adl_outcome["traderAddress"],
|
|
140
|
+
adl_outcome["strategyIdHash"],
|
|
141
|
+
raw_tx_log_event["requestIndex"],
|
|
142
|
+
)
|
|
143
|
+
for adl_outcome in liquidated_position_val[
|
|
144
|
+
"adlOutcomes"
|
|
145
|
+
]
|
|
146
|
+
],
|
|
147
|
+
Decimal(liquidated_position_val["newInsuranceFundCap"]),
|
|
148
|
+
raw_tx_log_event["requestIndex"],
|
|
149
|
+
),
|
|
150
|
+
)
|
|
151
|
+
for (
|
|
152
|
+
liquidated_position_key,
|
|
153
|
+
liquidated_position_val,
|
|
154
|
+
) in liquidation_entry["positions"]
|
|
155
|
+
],
|
|
156
|
+
raw_tx_log_event["requestIndex"],
|
|
157
|
+
)
|
|
158
|
+
for liquidation_entry in liquidation_tx_event
|
|
159
|
+
],
|
|
160
|
+
raw_tx_log_event["requestIndex"],
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
def process_tx(
|
|
164
|
+
self,
|
|
165
|
+
smt: DerivadexSMT,
|
|
166
|
+
**kwargs,
|
|
167
|
+
):
|
|
168
|
+
"""
|
|
169
|
+
Process a Liquidation transaction. A Liquidation is when a
|
|
170
|
+
trader's account is under-collateralized and forcibly closed.
|
|
171
|
+
Their collateral is removed and positions are closed, either
|
|
172
|
+
against the order book with other market participants, or if
|
|
173
|
+
the insurance fund is insufficiently-capitalized, ADL'd vs
|
|
174
|
+
winning traders. A liquidated trader's open orders are canceled
|
|
175
|
+
and the insurance fund is adjusted, along with the relevant
|
|
176
|
+
Stats leaves for the maker traders taking on the liquidated
|
|
177
|
+
position.
|
|
178
|
+
|
|
179
|
+
Parameters
|
|
180
|
+
----------
|
|
181
|
+
smt: DerivadexSMT
|
|
182
|
+
DerivaDEX Sparse Merkle Tree
|
|
183
|
+
**kwargs
|
|
184
|
+
Additional args specific to Liquidation transactions
|
|
185
|
+
"""
|
|
186
|
+
|
|
187
|
+
# Loop through each liquidation entry and process the cancels
|
|
188
|
+
for liquidation_entry in self.liquidation_entries:
|
|
189
|
+
# Loop through the canceled orders to remove them from the
|
|
190
|
+
# SMT
|
|
191
|
+
for cancel_tx in liquidation_entry.canceled_orders:
|
|
192
|
+
cancel_tx.process_tx(smt, **kwargs)
|
|
193
|
+
|
|
194
|
+
# Loop through each liquidation entry and process them individually
|
|
195
|
+
for liquidation_entry in self.liquidation_entries:
|
|
196
|
+
liquidated_strategy_key: StrategyKey = StrategyKey(
|
|
197
|
+
liquidation_entry.trader_address, liquidation_entry.strategy_id_hash
|
|
198
|
+
)
|
|
199
|
+
liquidated_strategy: Strategy = smt.strategy(liquidated_strategy_key)
|
|
200
|
+
|
|
201
|
+
position_leaves_by_symbol = {}
|
|
202
|
+
for symbol, liquidated_position in liquidation_entry.positions:
|
|
203
|
+
liquidated_position_key: PositionKey = PositionKey(
|
|
204
|
+
liquidation_entry.trader_address,
|
|
205
|
+
liquidation_entry.strategy_id_hash,
|
|
206
|
+
symbol,
|
|
207
|
+
)
|
|
208
|
+
|
|
209
|
+
liquidated_position: Position = smt.position(liquidated_position_key)
|
|
210
|
+
|
|
211
|
+
# Store position in dict
|
|
212
|
+
position_leaves_by_symbol[symbol] = liquidated_position
|
|
213
|
+
|
|
214
|
+
# Loop through the positions of the liquidated Strategy
|
|
215
|
+
for symbol, liquidated_position in liquidation_entry.positions:
|
|
216
|
+
# Get collateral for liquidated strategy
|
|
217
|
+
collateral = liquidated_strategy.avail_collateral[TokenSymbol.USDC]
|
|
218
|
+
|
|
219
|
+
liquidated_position_key: PositionKey = PositionKey(
|
|
220
|
+
liquidation_entry.trader_address,
|
|
221
|
+
liquidation_entry.strategy_id_hash,
|
|
222
|
+
symbol,
|
|
223
|
+
)
|
|
224
|
+
liquidated_position_leaf: Position = smt.position(
|
|
225
|
+
liquidated_position_key
|
|
226
|
+
)
|
|
227
|
+
|
|
228
|
+
# Obtain latest mark price
|
|
229
|
+
mark_price = kwargs["latest_price_leaves"][symbol][1].mark_price
|
|
230
|
+
|
|
231
|
+
liquidated_strategy_total_value = compute_strategy_total_value(
|
|
232
|
+
liquidated_strategy,
|
|
233
|
+
position_leaves_by_symbol,
|
|
234
|
+
kwargs["latest_price_leaves"],
|
|
235
|
+
)
|
|
236
|
+
|
|
237
|
+
# Compute the bankruptcy price for the liquidated Position
|
|
238
|
+
bankruptcy_price = mark_price - (
|
|
239
|
+
Decimal("1")
|
|
240
|
+
if liquidated_position_leaf.side == PositionSide.Long
|
|
241
|
+
else Decimal("-1")
|
|
242
|
+
) * (liquidated_strategy_total_value / liquidated_position_leaf.balance)
|
|
243
|
+
|
|
244
|
+
# Loop through each trade outcome event and process them individually
|
|
245
|
+
for trade_outcome in liquidated_position.trade_outcomes:
|
|
246
|
+
trade_outcome.process_tx(smt, **kwargs)
|
|
247
|
+
|
|
248
|
+
# If we're dealing with a Liquidation fill vs.
|
|
249
|
+
# a cancel...
|
|
250
|
+
if isinstance(trade_outcome, LiquidationFill):
|
|
251
|
+
# Update the collateral's intermediate value
|
|
252
|
+
collateral += (
|
|
253
|
+
trade_outcome.amount
|
|
254
|
+
* liquidated_position_leaf.avg_pnl(bankruptcy_price)
|
|
255
|
+
)
|
|
256
|
+
|
|
257
|
+
# Update liquidated Position's balance by the
|
|
258
|
+
# liquidated amount
|
|
259
|
+
liquidated_position_leaf.balance -= trade_outcome.amount
|
|
260
|
+
|
|
261
|
+
# Loop through each ADL outcome and process them individually
|
|
262
|
+
for adl_outcome in liquidated_position.adl_outcomes:
|
|
263
|
+
adl_position_key: PositionKey = PositionKey(
|
|
264
|
+
adl_outcome.trader_address,
|
|
265
|
+
adl_outcome.strategy_id_hash,
|
|
266
|
+
symbol,
|
|
267
|
+
)
|
|
268
|
+
adl_position: Position = smt.position(adl_position_key)
|
|
269
|
+
|
|
270
|
+
adl_strategy_key: StrategyKey = adl_position_key.as_strategy_key()
|
|
271
|
+
adl_strategy: Strategy = smt.strategy(adl_strategy_key)
|
|
272
|
+
|
|
273
|
+
# Compute ADL amount
|
|
274
|
+
adl_amount = min(
|
|
275
|
+
liquidated_position_leaf.balance, adl_position.balance
|
|
276
|
+
)
|
|
277
|
+
|
|
278
|
+
# Compute ADL'd realized PNL
|
|
279
|
+
adl_realized_pnl = adl_amount * adl_position.avg_pnl(
|
|
280
|
+
bankruptcy_price,
|
|
281
|
+
)
|
|
282
|
+
|
|
283
|
+
# Adjust ADL'd Strategy's free collateral
|
|
284
|
+
update_avail_collateral(
|
|
285
|
+
adl_strategy,
|
|
286
|
+
TokenSymbol.USDC,
|
|
287
|
+
adl_strategy.avail_collateral[TokenSymbol.USDC]
|
|
288
|
+
+ adl_realized_pnl,
|
|
289
|
+
)
|
|
290
|
+
|
|
291
|
+
# Store ADL'd Strategy in the SMT
|
|
292
|
+
smt.store_strategy(
|
|
293
|
+
adl_strategy_key,
|
|
294
|
+
adl_strategy,
|
|
295
|
+
)
|
|
296
|
+
|
|
297
|
+
# Adjust and store ADL'd Position in the SMT
|
|
298
|
+
adl_position.balance -= adl_amount
|
|
299
|
+
smt.store_position(
|
|
300
|
+
adl_position_key,
|
|
301
|
+
adl_position,
|
|
302
|
+
)
|
|
303
|
+
|
|
304
|
+
# Compute liquidated Strategy's realized pnl
|
|
305
|
+
liquidated_realized_pnl = (
|
|
306
|
+
adl_amount
|
|
307
|
+
* liquidated_position_leaf.avg_pnl(
|
|
308
|
+
bankruptcy_price,
|
|
309
|
+
)
|
|
310
|
+
)
|
|
311
|
+
|
|
312
|
+
# Update the collateral's intermediate value
|
|
313
|
+
collateral += liquidated_realized_pnl
|
|
314
|
+
|
|
315
|
+
# Adjust liquidated Position's balance by the
|
|
316
|
+
# ADL'd amount
|
|
317
|
+
liquidated_position_leaf.balance -= adl_amount
|
|
318
|
+
|
|
319
|
+
# Update liquidated Strategy's free collateral
|
|
320
|
+
# with the realized PNL from liquidation fills and ADL's
|
|
321
|
+
update_avail_collateral(
|
|
322
|
+
liquidated_strategy,
|
|
323
|
+
TokenSymbol.USDC,
|
|
324
|
+
collateral,
|
|
325
|
+
)
|
|
326
|
+
|
|
327
|
+
# Remove Position from the SMT
|
|
328
|
+
smt.store_position(
|
|
329
|
+
liquidated_position_key,
|
|
330
|
+
None,
|
|
331
|
+
)
|
|
332
|
+
|
|
333
|
+
insurance_fund_key: InsuranceFundKey = InsuranceFundKey()
|
|
334
|
+
insurance_fund: InsuranceFund = smt.insurance_fund(insurance_fund_key)
|
|
335
|
+
|
|
336
|
+
# Overwrite the insurance fund with the new insurance fund
|
|
337
|
+
# capitalization.
|
|
338
|
+
insurance_fund[TokenSymbol.USDC] = (
|
|
339
|
+
liquidated_position.new_insurance_fund_cap
|
|
340
|
+
)
|
|
341
|
+
|
|
342
|
+
smt.store_insurance_fund(insurance_fund_key, insurance_fund)
|
|
343
|
+
|
|
344
|
+
# Delete symbol/Position from dict
|
|
345
|
+
del position_leaves_by_symbol[symbol]
|
|
346
|
+
|
|
347
|
+
# Clear out the liquidated Strategy's free collateral and
|
|
348
|
+
# store in the SMT
|
|
349
|
+
update_avail_collateral(
|
|
350
|
+
liquidated_strategy,
|
|
351
|
+
TokenSymbol.USDC,
|
|
352
|
+
Decimal("0"),
|
|
353
|
+
)
|
|
354
|
+
smt.store_strategy(
|
|
355
|
+
liquidated_strategy_key,
|
|
356
|
+
liquidated_strategy,
|
|
357
|
+
)
|