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.
Files changed (104) hide show
  1. ddx/.gitignore +1 -0
  2. ddx/__init__.py +58 -0
  3. ddx/_rust/__init__.pyi +2009 -0
  4. ddx/_rust/common/__init__.pyi +17 -0
  5. ddx/_rust/common/accounting.pyi +6 -0
  6. ddx/_rust/common/enums.pyi +3 -0
  7. ddx/_rust/common/requests/__init__.pyi +21 -0
  8. ddx/_rust/common/requests/intents.pyi +19 -0
  9. ddx/_rust/common/specs.pyi +17 -0
  10. ddx/_rust/common/state/__init__.pyi +41 -0
  11. ddx/_rust/common/state/keys.pyi +29 -0
  12. ddx/_rust/common/transactions.pyi +7 -0
  13. ddx/_rust/decimal.pyi +3 -0
  14. ddx/_rust/h256.pyi +3 -0
  15. ddx/_rust.abi3.so +0 -0
  16. ddx/app_config/ethereum/addresses.json +541 -0
  17. ddx/auditor/README.md +32 -0
  18. ddx/auditor/__init__.py +0 -0
  19. ddx/auditor/auditor_driver.py +1034 -0
  20. ddx/auditor/websocket_message.py +54 -0
  21. ddx/common/__init__.py +0 -0
  22. ddx/common/epoch_params.py +28 -0
  23. ddx/common/fill_context.py +144 -0
  24. ddx/common/item_utils.py +38 -0
  25. ddx/common/logging.py +184 -0
  26. ddx/common/market_specs.py +64 -0
  27. ddx/common/trade_mining_params.py +19 -0
  28. ddx/common/transaction_utils.py +85 -0
  29. ddx/common/transactions/__init__.py +0 -0
  30. ddx/common/transactions/advance_epoch.py +91 -0
  31. ddx/common/transactions/advance_settlement_epoch.py +63 -0
  32. ddx/common/transactions/all_price_checkpoints.py +84 -0
  33. ddx/common/transactions/cancel.py +76 -0
  34. ddx/common/transactions/cancel_all.py +88 -0
  35. ddx/common/transactions/complete_fill.py +103 -0
  36. ddx/common/transactions/disaster_recovery.py +97 -0
  37. ddx/common/transactions/event.py +48 -0
  38. ddx/common/transactions/fee_distribution.py +119 -0
  39. ddx/common/transactions/funding.py +294 -0
  40. ddx/common/transactions/futures_expiry.py +123 -0
  41. ddx/common/transactions/genesis.py +108 -0
  42. ddx/common/transactions/inner/__init__.py +0 -0
  43. ddx/common/transactions/inner/adl_outcome.py +25 -0
  44. ddx/common/transactions/inner/fill.py +227 -0
  45. ddx/common/transactions/inner/liquidated_position.py +41 -0
  46. ddx/common/transactions/inner/liquidation_entry.py +41 -0
  47. ddx/common/transactions/inner/liquidation_fill.py +118 -0
  48. ddx/common/transactions/inner/outcome.py +32 -0
  49. ddx/common/transactions/inner/trade_fill.py +125 -0
  50. ddx/common/transactions/insurance_fund_update.py +142 -0
  51. ddx/common/transactions/insurance_fund_withdraw.py +99 -0
  52. ddx/common/transactions/liquidation.py +357 -0
  53. ddx/common/transactions/partial_fill.py +125 -0
  54. ddx/common/transactions/pnl_realization.py +122 -0
  55. ddx/common/transactions/post.py +72 -0
  56. ddx/common/transactions/post_order.py +95 -0
  57. ddx/common/transactions/price_checkpoint.py +96 -0
  58. ddx/common/transactions/signer_registered.py +62 -0
  59. ddx/common/transactions/specs_update.py +61 -0
  60. ddx/common/transactions/strategy_update.py +156 -0
  61. ddx/common/transactions/tradable_product_update.py +98 -0
  62. ddx/common/transactions/trade_mining.py +147 -0
  63. ddx/common/transactions/trader_update.py +105 -0
  64. ddx/common/transactions/withdraw.py +91 -0
  65. ddx/common/transactions/withdraw_ddx.py +74 -0
  66. ddx/common/utils.py +176 -0
  67. ddx/config.py +17 -0
  68. ddx/derivadex_client.py +254 -0
  69. ddx/py.typed +0 -0
  70. ddx/realtime_client/__init__.py +2 -0
  71. ddx/realtime_client/config.py +2 -0
  72. ddx/realtime_client/logs/pytest.log +0 -0
  73. ddx/realtime_client/models/__init__.py +683 -0
  74. ddx/realtime_client/realtime_client.py +567 -0
  75. ddx/rest_client/__init__.py +0 -0
  76. ddx/rest_client/clients/__init__.py +0 -0
  77. ddx/rest_client/clients/base_client.py +60 -0
  78. ddx/rest_client/clients/market_client.py +1241 -0
  79. ddx/rest_client/clients/on_chain_client.py +432 -0
  80. ddx/rest_client/clients/signed_client.py +301 -0
  81. ddx/rest_client/clients/system_client.py +843 -0
  82. ddx/rest_client/clients/trade_client.py +335 -0
  83. ddx/rest_client/constants/__init__.py +0 -0
  84. ddx/rest_client/constants/endpoints.py +67 -0
  85. ddx/rest_client/contracts/__init__.py +0 -0
  86. ddx/rest_client/contracts/checkpoint/__init__.py +560 -0
  87. ddx/rest_client/contracts/ddx/__init__.py +1949 -0
  88. ddx/rest_client/contracts/dummy_token/__init__.py +1014 -0
  89. ddx/rest_client/contracts/i_collateral/__init__.py +1414 -0
  90. ddx/rest_client/contracts/i_stake/__init__.py +696 -0
  91. ddx/rest_client/exceptions/__init__.py +0 -0
  92. ddx/rest_client/exceptions/exceptions.py +32 -0
  93. ddx/rest_client/http/__init__.py +0 -0
  94. ddx/rest_client/http/http_client.py +305 -0
  95. ddx/rest_client/models/__init__.py +0 -0
  96. ddx/rest_client/models/market.py +683 -0
  97. ddx/rest_client/models/signed.py +60 -0
  98. ddx/rest_client/models/system.py +390 -0
  99. ddx/rest_client/models/trade.py +140 -0
  100. ddx/rest_client/utils/__init__.py +0 -0
  101. ddx/rest_client/utils/encryption_utils.py +26 -0
  102. ddx_python-1.0.5.dist-info/METADATA +63 -0
  103. ddx_python-1.0.5.dist-info/RECORD +104 -0
  104. 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
+ )