ddx-python 1.0.4__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 +2685 -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 +23 -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 +526 -0
- ddx/auditor/README.md +32 -0
- ddx/auditor/__init__.py +0 -0
- ddx/auditor/auditor_driver.py +1043 -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 +141 -0
- ddx/common/logging.py +184 -0
- ddx/common/market_aware_account.py +259 -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 +96 -0
- ddx/common/transactions/event.py +48 -0
- ddx/common/transactions/fee_distribution.py +119 -0
- ddx/common/transactions/funding.py +292 -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 +232 -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 +292 -0
- ddx/common/transactions/insurance_fund_update.py +138 -0
- ddx/common/transactions/insurance_fund_withdraw.py +100 -0
- ddx/common/transactions/liquidation.py +353 -0
- ddx/common/transactions/partial_fill.py +125 -0
- ddx/common/transactions/pnl_realization.py +120 -0
- ddx/common/transactions/post.py +72 -0
- ddx/common/transactions/post_order.py +95 -0
- ddx/common/transactions/price_checkpoint.py +97 -0
- ddx/common/transactions/signer_registered.py +62 -0
- ddx/common/transactions/specs_update.py +61 -0
- ddx/common/transactions/strategy_update.py +158 -0
- ddx/common/transactions/tradable_product_update.py +98 -0
- ddx/common/transactions/trade_mining.py +147 -0
- ddx/common/transactions/trader_update.py +131 -0
- ddx/common/transactions/withdraw.py +90 -0
- ddx/common/transactions/withdraw_ddx.py +74 -0
- ddx/common/utils.py +176 -0
- ddx/config.py +17 -0
- ddx/derivadex_client.py +270 -0
- ddx/models/__init__.py +0 -0
- ddx/models/base.py +132 -0
- ddx/py.typed +0 -0
- ddx/realtime_client/__init__.py +2 -0
- ddx/realtime_client/config.py +2 -0
- ddx/realtime_client/models/__init__.py +611 -0
- ddx/realtime_client/realtime_client.py +646 -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 +1243 -0
- ddx/rest_client/clients/on_chain_client.py +439 -0
- ddx/rest_client/clients/signed_client.py +292 -0
- ddx/rest_client/clients/system_client.py +843 -0
- ddx/rest_client/clients/trade_client.py +357 -0
- ddx/rest_client/constants/__init__.py +0 -0
- ddx/rest_client/constants/endpoints.py +66 -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 +336 -0
- ddx/rest_client/models/__init__.py +0 -0
- ddx/rest_client/models/market.py +693 -0
- ddx/rest_client/models/signed.py +61 -0
- ddx/rest_client/models/system.py +311 -0
- ddx/rest_client/models/trade.py +185 -0
- ddx/rest_client/utils/__init__.py +0 -0
- ddx/rest_client/utils/encryption_utils.py +26 -0
- ddx/utils/__init__.py +0 -0
- ddx_python-1.0.4.dist-info/METADATA +63 -0
- ddx_python-1.0.4.dist-info/RECORD +106 -0
- ddx_python-1.0.4.dist-info/WHEEL +5 -0
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Genesis module
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from typing import Optional
|
|
6
|
+
|
|
7
|
+
from attrs import define, field
|
|
8
|
+
from ddx.common.logging import auditor_logger
|
|
9
|
+
from ddx.common.transactions.event import Event
|
|
10
|
+
from ddx._rust.common.specs import SpecsKind
|
|
11
|
+
from ddx._rust.common.state import Balance, DerivadexSMT
|
|
12
|
+
from ddx._rust.common.state.keys import InsuranceFundKey, SpecsKey
|
|
13
|
+
from ddx._rust.decimal import Decimal
|
|
14
|
+
|
|
15
|
+
logger = auditor_logger(__name__)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@define
|
|
19
|
+
class Genesis(Event):
|
|
20
|
+
"""
|
|
21
|
+
Defines a Genesis
|
|
22
|
+
|
|
23
|
+
A Genesis is a non-transitioning transaction that indicates
|
|
24
|
+
the start of the first epoch.
|
|
25
|
+
|
|
26
|
+
Attributes:
|
|
27
|
+
state_root_hash (str): State root hash at time of marker
|
|
28
|
+
request_index (int): Sequenced request index of transaction
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
state_root_hash: Optional[str] = field(eq=str.lower)
|
|
32
|
+
request_index: int = field(default=-1, eq=False)
|
|
33
|
+
|
|
34
|
+
@classmethod
|
|
35
|
+
def decode_value_into_cls(cls, raw_tx_log_event: dict):
|
|
36
|
+
"""
|
|
37
|
+
Decode a raw transaction log event (dict) into a Genesis
|
|
38
|
+
instance.
|
|
39
|
+
|
|
40
|
+
Parameters
|
|
41
|
+
----------
|
|
42
|
+
raw_tx_log_event : dict
|
|
43
|
+
Raw transaction log event being processed
|
|
44
|
+
"""
|
|
45
|
+
|
|
46
|
+
genesis_tx_event = raw_tx_log_event["event"]["c"]
|
|
47
|
+
|
|
48
|
+
return cls(
|
|
49
|
+
genesis_tx_event.get("stateRootHash"),
|
|
50
|
+
raw_tx_log_event["requestIndex"],
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
def process_tx(
|
|
54
|
+
self,
|
|
55
|
+
**kwargs,
|
|
56
|
+
):
|
|
57
|
+
"""
|
|
58
|
+
Process an EpochMarker transaction of type Genesis. This
|
|
59
|
+
indicates the very first event in the transaction log, although
|
|
60
|
+
it is not state-transitioning in the way typical transactions
|
|
61
|
+
are.
|
|
62
|
+
|
|
63
|
+
Parameters
|
|
64
|
+
----------
|
|
65
|
+
**kwargs
|
|
66
|
+
Additional args specific to Genesis transactions
|
|
67
|
+
"""
|
|
68
|
+
|
|
69
|
+
insurance_fund_balance = Balance.default()
|
|
70
|
+
ddx_fee_pool = Decimal("0")
|
|
71
|
+
|
|
72
|
+
specs = {}
|
|
73
|
+
for key, val in kwargs["genesis_params"]["specs"].items():
|
|
74
|
+
if key.startswith("SINGLENAMEPERP"):
|
|
75
|
+
kind = SpecsKind.SingleNamePerpetual
|
|
76
|
+
elif key.startswith("INDEXFUNDPERP"):
|
|
77
|
+
kind = SpecsKind.IndexFundPerpetual
|
|
78
|
+
elif key.startswith("BINARYFUTURE"):
|
|
79
|
+
kind = SpecsKind.BinaryPredictionFuture
|
|
80
|
+
elif key.startswith("QUARTERLYFUTURE"):
|
|
81
|
+
kind = SpecsKind.QuarterlyExpiryFuture
|
|
82
|
+
elif key.startswith("SPOTGATEWAY"):
|
|
83
|
+
kind = SpecsKind.SpotGateway
|
|
84
|
+
elif key.startswith("SPOTINDEX"):
|
|
85
|
+
kind = SpecsKind.SpotIndex
|
|
86
|
+
elif key.startswith("BINARYGATEWAY"):
|
|
87
|
+
kind = SpecsKind.BinaryPredictionGateway
|
|
88
|
+
elif key.startswith("BINARYINDEX"):
|
|
89
|
+
kind = SpecsKind.BinaryIndex
|
|
90
|
+
else:
|
|
91
|
+
raise Exception("Invalid spec in Genesis params")
|
|
92
|
+
# only split once to avoid splitting on the second dash
|
|
93
|
+
specs[SpecsKey(kind, key.split("-", 1)[1])] = val
|
|
94
|
+
|
|
95
|
+
logger.info(
|
|
96
|
+
f"Initializing SMT from genesis:\n\tInsurance fund: {insurance_fund_balance}\n\tDDX fee pool: {ddx_fee_pool}\n\tSpecs: {specs}\n\tCurrent datetime: {kwargs['current_time']}"
|
|
97
|
+
)
|
|
98
|
+
smt = DerivadexSMT.from_genesis(
|
|
99
|
+
insurance_fund_balance, ddx_fee_pool, specs, kwargs["current_time"]
|
|
100
|
+
)
|
|
101
|
+
kwargs["smt"](kwargs["auditor_instance"], smt)
|
|
102
|
+
|
|
103
|
+
# Set the expected epoch ID to be 1 and the expected tx ordinal
|
|
104
|
+
# to be -1, because we immediately increment this by 1, thus
|
|
105
|
+
# setting it to 0, which will be the first tx ordinal of the
|
|
106
|
+
# next epoch
|
|
107
|
+
kwargs["expected_epoch_id"](kwargs["auditor_instance"], 1)
|
|
108
|
+
kwargs["expected_tx_ordinal"](kwargs["auditor_instance"], -1)
|
|
File without changes
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"""
|
|
2
|
+
AdlOutcome module
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from attrs import define, field
|
|
6
|
+
from ddx._rust.decimal import Decimal
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@define(hash=True)
|
|
10
|
+
class AdlOutcome:
|
|
11
|
+
"""
|
|
12
|
+
Defines an AdlOutcome
|
|
13
|
+
|
|
14
|
+
An AdlOutcome is a scenario where a strategy has been auto-deleveraged
|
|
15
|
+
due to a liquidation.
|
|
16
|
+
|
|
17
|
+
Attributes:
|
|
18
|
+
trader_address (str): Auto-deleveraged trader's ethereum address
|
|
19
|
+
strategy_id_hash (str): Auto-deleveraged strategy ID hash
|
|
20
|
+
request_index (int): Sequenced request index of transaction
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
trader_address: str = field(eq=str.lower)
|
|
24
|
+
strategy_id_hash: str = field(eq=str.lower)
|
|
25
|
+
request_index: int = field(default=-1, eq=False, hash=False)
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Fill module
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import logging
|
|
6
|
+
from enum import Enum
|
|
7
|
+
from typing import Optional
|
|
8
|
+
|
|
9
|
+
from attrs import define, field
|
|
10
|
+
from ddx.common.fill_context import (MAX_DDX_PRICE_CHECKPOINT_AGE_IN_TICKS,
|
|
11
|
+
FillContext, apply_trade)
|
|
12
|
+
from ddx.common.transaction_utils import get_most_recent_price
|
|
13
|
+
from ddx.common.transactions.event import Event
|
|
14
|
+
from ddx.common.transactions.inner.outcome import Outcome
|
|
15
|
+
from ddx._rust.common import ProductSymbol, TokenSymbol
|
|
16
|
+
from ddx._rust.common.enums import OrderSide, TradeSide
|
|
17
|
+
from ddx._rust.common.state import (DerivadexSMT, InsuranceFund, Position,
|
|
18
|
+
Price, Stats)
|
|
19
|
+
from ddx._rust.common.state.keys import (EpochMetadataKey, InsuranceFundKey,
|
|
20
|
+
PositionKey, PriceKey, StatsKey,
|
|
21
|
+
StrategyKey, TraderKey)
|
|
22
|
+
from ddx._rust.decimal import Decimal
|
|
23
|
+
|
|
24
|
+
logger = logging.getLogger(__name__)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
@define(hash=True)
|
|
28
|
+
class Fill(Event):
|
|
29
|
+
"""
|
|
30
|
+
Defines a Fill
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
symbol: ProductSymbol
|
|
34
|
+
amount: Decimal = field(hash=False)
|
|
35
|
+
price: Decimal = field(hash=False)
|
|
36
|
+
taker_side: OrderSide = field(hash=False)
|
|
37
|
+
time_value: int
|
|
38
|
+
request_index: int = field(eq=False, hash=False)
|
|
39
|
+
|
|
40
|
+
def adjust_for_maker_taker(
|
|
41
|
+
self,
|
|
42
|
+
smt: DerivadexSMT,
|
|
43
|
+
epoch_id: int,
|
|
44
|
+
is_maker: bool,
|
|
45
|
+
outcome: Outcome,
|
|
46
|
+
trade_mining_active: bool = False,
|
|
47
|
+
maker_book_order_time_value: Optional[int] = None,
|
|
48
|
+
reconcile_fees: bool = True,
|
|
49
|
+
):
|
|
50
|
+
"""
|
|
51
|
+
Make some adjustments to the SMT based on whether we are
|
|
52
|
+
considering the maker or the taker component of the Trade.
|
|
53
|
+
In this method, we will be adjusting the the Strategy,
|
|
54
|
+
Position, and Stats leaves.
|
|
55
|
+
|
|
56
|
+
Parameters
|
|
57
|
+
----------
|
|
58
|
+
smt: DerivadexSMT
|
|
59
|
+
DerivaDEX Sparse Merkle Tree
|
|
60
|
+
epoch_id: int
|
|
61
|
+
Epoch ID used for recording fees if any
|
|
62
|
+
is_maker : bool
|
|
63
|
+
Whether outcome is for the maker or taker
|
|
64
|
+
outcome: Outcome
|
|
65
|
+
Outcome to adjust
|
|
66
|
+
trade_mining_active : bool
|
|
67
|
+
Whether trade mining is active
|
|
68
|
+
maker_book_order_time_value: Optional[int]
|
|
69
|
+
Maker book order time value
|
|
70
|
+
"""
|
|
71
|
+
|
|
72
|
+
context = FillContext(outcome)
|
|
73
|
+
|
|
74
|
+
position_key: PositionKey = PositionKey(
|
|
75
|
+
outcome.trader,
|
|
76
|
+
outcome.strategy_id_hash,
|
|
77
|
+
self.symbol,
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
position: Optional[Position] = smt.position(position_key)
|
|
81
|
+
|
|
82
|
+
position = context.apply_fill(
|
|
83
|
+
position,
|
|
84
|
+
(
|
|
85
|
+
(OrderSide.Ask if self.taker_side == OrderSide.Bid else OrderSide.Bid)
|
|
86
|
+
if is_maker
|
|
87
|
+
else self.taker_side
|
|
88
|
+
),
|
|
89
|
+
(TradeSide.Maker if is_maker else TradeSide.Taker),
|
|
90
|
+
self.amount,
|
|
91
|
+
self.price,
|
|
92
|
+
)
|
|
93
|
+
smt.store_position(
|
|
94
|
+
position_key,
|
|
95
|
+
position,
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
trader_key: TraderKey = TraderKey(outcome.trader)
|
|
99
|
+
trader = smt.trader(trader_key)
|
|
100
|
+
|
|
101
|
+
if outcome.fee > Decimal("0") and trader.pay_fees_in_ddx:
|
|
102
|
+
# Gets the most recent DDX price within MAX_DDX_PRICE_CHECKPOINT_AGE_IN_TICKS
|
|
103
|
+
_, recent_ddx_price = get_most_recent_price(
|
|
104
|
+
smt, ProductSymbol("DDXP"), self.time_value
|
|
105
|
+
)
|
|
106
|
+
if (
|
|
107
|
+
recent_ddx_price.time_value
|
|
108
|
+
>= self.time_value - MAX_DDX_PRICE_CHECKPOINT_AGE_IN_TICKS
|
|
109
|
+
):
|
|
110
|
+
if context.apply_ddx_fee_and_mutate_trader(
|
|
111
|
+
trader, recent_ddx_price.index_price
|
|
112
|
+
):
|
|
113
|
+
smt.store_trader(trader_key, trader)
|
|
114
|
+
else:
|
|
115
|
+
raise Exception(
|
|
116
|
+
"DDX fee election enabled but no DDX price checkpoint within the max age was found, should have been caught by the operator"
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
strategy_key: StrategyKey = position_key.as_strategy_key()
|
|
120
|
+
strategy = smt.strategy(strategy_key)
|
|
121
|
+
context.realize_trade_and_mutate_strategy(strategy)
|
|
122
|
+
smt.store_strategy(strategy_key, strategy)
|
|
123
|
+
|
|
124
|
+
if reconcile_fees and outcome.fee > Decimal("0"):
|
|
125
|
+
if outcome.pay_fee_in_ddx:
|
|
126
|
+
logger.info(f"Fee of {outcome.fee} DDX to be paid")
|
|
127
|
+
epoch_metadata_key: EpochMetadataKey = EpochMetadataKey(epoch_id)
|
|
128
|
+
epoch_metadata = smt.epoch_metadata(epoch_metadata_key)
|
|
129
|
+
epoch_metadata.ddx_fee_pool = (
|
|
130
|
+
epoch_metadata.ddx_fee_pool + outcome.fee
|
|
131
|
+
).recorded_amount()
|
|
132
|
+
smt.store_epoch_metadata(epoch_metadata_key, epoch_metadata)
|
|
133
|
+
else:
|
|
134
|
+
logger.info(f"Fee of {outcome.fee} USDC to be paid")
|
|
135
|
+
insurance_fund_key: InsuranceFundKey = InsuranceFundKey()
|
|
136
|
+
insurance_fund = smt.insurance_fund(insurance_fund_key)
|
|
137
|
+
insurance_fund[TokenSymbol.USDC] += outcome.fee
|
|
138
|
+
smt.store_insurance_fund(insurance_fund_key, insurance_fund)
|
|
139
|
+
|
|
140
|
+
if (
|
|
141
|
+
trade_mining_active
|
|
142
|
+
and maker_book_order_time_value
|
|
143
|
+
and self.time_value > maker_book_order_time_value + 1
|
|
144
|
+
):
|
|
145
|
+
stats_key: StatsKey = StatsKey(outcome.trader)
|
|
146
|
+
stats = smt.stats(stats_key)
|
|
147
|
+
notional_amount = self.amount * self.price
|
|
148
|
+
if stats is None:
|
|
149
|
+
# If Stats leaf doesn't exist in the tree, we need
|
|
150
|
+
# to create/add a new one
|
|
151
|
+
if is_maker:
|
|
152
|
+
# Initialize the trader's maker volume
|
|
153
|
+
stats = Stats(notional_amount, Decimal("0"))
|
|
154
|
+
else:
|
|
155
|
+
# Initialize the trader's taker volume
|
|
156
|
+
stats = Stats(Decimal("0"), notional_amount)
|
|
157
|
+
else:
|
|
158
|
+
# If Stats leaf does exist, we update the existing leaf
|
|
159
|
+
if is_maker:
|
|
160
|
+
# Increment the trader's maker volume
|
|
161
|
+
stats.maker_volume += notional_amount
|
|
162
|
+
else:
|
|
163
|
+
# Increment the trader's taker volume
|
|
164
|
+
stats.taker_volume += notional_amount
|
|
165
|
+
|
|
166
|
+
smt.store_stats(stats_key, stats)
|
|
167
|
+
|
|
168
|
+
def adjust_for_maker(
|
|
169
|
+
self,
|
|
170
|
+
smt: DerivadexSMT,
|
|
171
|
+
epoch_id: int,
|
|
172
|
+
trade_mining_active: bool = False,
|
|
173
|
+
maker_book_order_time_value: Optional[int] = None,
|
|
174
|
+
reconcile_fees: bool = True,
|
|
175
|
+
):
|
|
176
|
+
"""
|
|
177
|
+
Make some adjustments to the SMT based on the maker side
|
|
178
|
+
of the Trade.
|
|
179
|
+
|
|
180
|
+
Parameters
|
|
181
|
+
----------
|
|
182
|
+
smt: DerivadexSMT
|
|
183
|
+
DerivaDEX Sparse Merkle Tree
|
|
184
|
+
epoch_id: int
|
|
185
|
+
Epoch ID used for recording fees if any
|
|
186
|
+
trade_mining_active : bool
|
|
187
|
+
Whether trade mining is active
|
|
188
|
+
maker_book_order_time_value: Optional[int]
|
|
189
|
+
Maker book order time value
|
|
190
|
+
"""
|
|
191
|
+
self.adjust_for_maker_taker(
|
|
192
|
+
smt,
|
|
193
|
+
epoch_id,
|
|
194
|
+
True,
|
|
195
|
+
self.maker_outcome,
|
|
196
|
+
trade_mining_active,
|
|
197
|
+
maker_book_order_time_value,
|
|
198
|
+
reconcile_fees,
|
|
199
|
+
)
|
|
200
|
+
|
|
201
|
+
def adjust_for_taker(
|
|
202
|
+
self,
|
|
203
|
+
smt: DerivadexSMT,
|
|
204
|
+
epoch_id: int,
|
|
205
|
+
trade_mining_active: bool = False,
|
|
206
|
+
maker_book_order_time_value: Optional[int] = None,
|
|
207
|
+
reconcile_fees: bool = True,
|
|
208
|
+
):
|
|
209
|
+
"""
|
|
210
|
+
Make some adjustments to the SMT based on the taker side
|
|
211
|
+
of the Trade.
|
|
212
|
+
|
|
213
|
+
Parameters
|
|
214
|
+
----------
|
|
215
|
+
smt: DerivadexSMT
|
|
216
|
+
DerivaDEX Sparse Merkle Tree
|
|
217
|
+
epoch_id: int
|
|
218
|
+
Epoch ID used for recording fees if any
|
|
219
|
+
maker_book_order_time_value: int
|
|
220
|
+
Maker book order time value
|
|
221
|
+
trade_mining_active : bool
|
|
222
|
+
Whether trade mining is active
|
|
223
|
+
"""
|
|
224
|
+
self.adjust_for_maker_taker(
|
|
225
|
+
smt,
|
|
226
|
+
epoch_id,
|
|
227
|
+
False,
|
|
228
|
+
self.taker_outcome,
|
|
229
|
+
trade_mining_active,
|
|
230
|
+
maker_book_order_time_value,
|
|
231
|
+
reconcile_fees,
|
|
232
|
+
)
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"""
|
|
2
|
+
LiquidatedPosition module
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from attrs import define, field
|
|
6
|
+
from ddx.common.transactions.cancel import Cancel
|
|
7
|
+
from ddx.common.transactions.inner.adl_outcome import AdlOutcome
|
|
8
|
+
from ddx.common.transactions.inner.liquidation_fill import LiquidationFill
|
|
9
|
+
from ddx._rust.decimal import Decimal
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@define
|
|
13
|
+
class LiquidatedPosition:
|
|
14
|
+
"""
|
|
15
|
+
Defines a LiquidatedPosition
|
|
16
|
+
|
|
17
|
+
A LiquidatedPosition has data pertaining to a liquidated position.
|
|
18
|
+
|
|
19
|
+
Attributes:
|
|
20
|
+
amount (Decimal): Liquidated balance amount
|
|
21
|
+
trade_outcomes (list[LiquidationFill | Cancel]): A list of trade outcome objects
|
|
22
|
+
adl_outcomes (list[AdlOutcome]): Positions that were ADL'd as a result of the liquidation
|
|
23
|
+
new_insurance_fund_cap (Decimal): Insurance fund capitalization after the liquidation
|
|
24
|
+
request_index (int): Sequenced request index of transaction
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
amount: Decimal
|
|
28
|
+
trade_outcomes: list[LiquidationFill | Cancel] = field(eq=set)
|
|
29
|
+
adl_outcomes: list[AdlOutcome] = field(eq=set)
|
|
30
|
+
new_insurance_fund_cap: Decimal
|
|
31
|
+
request_index: int = field(default=-1, eq=False)
|
|
32
|
+
|
|
33
|
+
def __hash__(self):
|
|
34
|
+
return hash(
|
|
35
|
+
(
|
|
36
|
+
self.amount,
|
|
37
|
+
frozenset(self.trade_outcomes),
|
|
38
|
+
frozenset(self.adl_outcomes),
|
|
39
|
+
self.new_insurance_fund_cap,
|
|
40
|
+
)
|
|
41
|
+
)
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"""
|
|
2
|
+
LiquidationEntry module
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from attrs import define, field
|
|
6
|
+
from ddx.common.transactions.cancel import Cancel
|
|
7
|
+
from ddx.common.transactions.inner.liquidated_position import LiquidatedPosition
|
|
8
|
+
from ddx._rust.common import ProductSymbol
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@define
|
|
12
|
+
class LiquidationEntry:
|
|
13
|
+
"""
|
|
14
|
+
Defines a LiquidationEntry
|
|
15
|
+
|
|
16
|
+
A LiquidationEntry contains data pertaining to individual trader and
|
|
17
|
+
strategy liquidations.
|
|
18
|
+
|
|
19
|
+
Attributes:
|
|
20
|
+
trader_address (str): Liquidated trader's Ethereum address
|
|
21
|
+
strategy_id_hash (str): Liquidated strategy ID hash
|
|
22
|
+
canceled_orders (list[Cancel]): Canceled orders for liquidated trader
|
|
23
|
+
positions (list[tuple[str, LiquidatedPosition]]): Contains information pertaining to individual liquidated positions by symbol
|
|
24
|
+
request_index (int): Sequenced request index of transaction
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
trader_address: str = field(eq=str.lower)
|
|
28
|
+
strategy_id_hash: str = field(eq=str.lower)
|
|
29
|
+
canceled_orders: list[Cancel] = field(eq=set)
|
|
30
|
+
positions: list[tuple[ProductSymbol, LiquidatedPosition]] = field(eq=set)
|
|
31
|
+
request_index: int = field(default=-1, eq=False)
|
|
32
|
+
|
|
33
|
+
def __hash__(self):
|
|
34
|
+
return hash(
|
|
35
|
+
(
|
|
36
|
+
self.trader_address,
|
|
37
|
+
self.strategy_id_hash,
|
|
38
|
+
frozenset(self.canceled_orders),
|
|
39
|
+
frozenset(self.positions),
|
|
40
|
+
)
|
|
41
|
+
)
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
"""
|
|
2
|
+
LiquidationFill module
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import logging
|
|
6
|
+
|
|
7
|
+
from attrs import define, field
|
|
8
|
+
from ddx.common.transactions.inner.fill import Fill
|
|
9
|
+
from ddx.common.transactions.inner.outcome import Outcome
|
|
10
|
+
from ddx._rust.common import ProductSymbol
|
|
11
|
+
from ddx._rust.common.enums import OrderSide
|
|
12
|
+
from ddx._rust.common.state import DerivadexSMT
|
|
13
|
+
from ddx._rust.common.state.keys import BookOrderKey
|
|
14
|
+
from ddx._rust.decimal import Decimal
|
|
15
|
+
|
|
16
|
+
logger = logging.getLogger(__name__)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@define(hash=True)
|
|
20
|
+
class LiquidationFill(Fill):
|
|
21
|
+
"""
|
|
22
|
+
Defines a LiquidationFill
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
maker_order_hash: str = field(eq=str.lower)
|
|
26
|
+
maker_outcome: Outcome = field(hash=False)
|
|
27
|
+
maker_order_remaining_amount: Decimal = field(hash=False)
|
|
28
|
+
index_price_hash: str = field(eq=str.lower)
|
|
29
|
+
request_index: int = field(default=-1, eq=False, hash=False)
|
|
30
|
+
|
|
31
|
+
def __init__(
|
|
32
|
+
self,
|
|
33
|
+
symbol: ProductSymbol,
|
|
34
|
+
index_price_hash: str,
|
|
35
|
+
maker_order_hash: str,
|
|
36
|
+
maker_order_remaining_amount: Decimal,
|
|
37
|
+
amount: Decimal,
|
|
38
|
+
price: Decimal,
|
|
39
|
+
taker_side: OrderSide,
|
|
40
|
+
maker_outcome: Outcome,
|
|
41
|
+
time_value: int,
|
|
42
|
+
request_index: int = -1,
|
|
43
|
+
):
|
|
44
|
+
"""
|
|
45
|
+
Initialize a LiquidationFill instance
|
|
46
|
+
Parameters
|
|
47
|
+
----------
|
|
48
|
+
symbol : ProductSymbol
|
|
49
|
+
Product symbol
|
|
50
|
+
index_price_hash : str
|
|
51
|
+
Index price hash
|
|
52
|
+
maker_order_hash : str
|
|
53
|
+
Maker order hash
|
|
54
|
+
maker_order_remaining_amount : Decimal
|
|
55
|
+
Maker order remaining amount
|
|
56
|
+
amount : Decimal
|
|
57
|
+
Amount
|
|
58
|
+
price : Decimal
|
|
59
|
+
Price
|
|
60
|
+
taker_side : OrderSide
|
|
61
|
+
Taker side
|
|
62
|
+
maker_outcome : Outcome
|
|
63
|
+
Maker outcome
|
|
64
|
+
time_value : int
|
|
65
|
+
Time value
|
|
66
|
+
request_index : int
|
|
67
|
+
Request index
|
|
68
|
+
"""
|
|
69
|
+
super().__init__(
|
|
70
|
+
symbol,
|
|
71
|
+
amount,
|
|
72
|
+
price,
|
|
73
|
+
taker_side,
|
|
74
|
+
time_value,
|
|
75
|
+
request_index,
|
|
76
|
+
)
|
|
77
|
+
self.maker_order_hash = maker_order_hash
|
|
78
|
+
self.maker_outcome = maker_outcome
|
|
79
|
+
self.maker_order_remaining_amount = maker_order_remaining_amount
|
|
80
|
+
self.index_price_hash = index_price_hash
|
|
81
|
+
|
|
82
|
+
def process_tx(
|
|
83
|
+
self,
|
|
84
|
+
smt: DerivadexSMT,
|
|
85
|
+
**kwargs,
|
|
86
|
+
):
|
|
87
|
+
"""
|
|
88
|
+
Process a LiquidationFill transaction. These are Fill
|
|
89
|
+
transactions that have risen from either a Liquidation.
|
|
90
|
+
|
|
91
|
+
Parameters
|
|
92
|
+
----------
|
|
93
|
+
smt: DerivadexSMT
|
|
94
|
+
DerivaDEX Sparse Merkle Tree
|
|
95
|
+
**kwargs
|
|
96
|
+
Additional args specific to Liquidation transactions
|
|
97
|
+
"""
|
|
98
|
+
|
|
99
|
+
maker_book_order_key: BookOrderKey = BookOrderKey(
|
|
100
|
+
self.symbol, self.maker_order_hash
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
maker_book_order = smt.book_order(maker_book_order_key)
|
|
104
|
+
maker_book_order_time_value = maker_book_order.time_value
|
|
105
|
+
|
|
106
|
+
maker_book_order.amount = self.maker_order_remaining_amount
|
|
107
|
+
smt.store_book_order(maker_book_order_key, maker_book_order)
|
|
108
|
+
|
|
109
|
+
# Adjust the maker-related position. Take note that
|
|
110
|
+
# in a liquidation, there is no taker component, so
|
|
111
|
+
# unlike its counterpart (TradeFill), a
|
|
112
|
+
# LiquidationFill only considers the maker
|
|
113
|
+
self.adjust_for_maker(
|
|
114
|
+
smt,
|
|
115
|
+
kwargs["epoch_id"],
|
|
116
|
+
kwargs["trade_mining_active"],
|
|
117
|
+
maker_book_order_time_value,
|
|
118
|
+
)
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Outcome module
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from typing import Optional
|
|
6
|
+
|
|
7
|
+
from attrs import define, field
|
|
8
|
+
from ddx._rust.decimal import Decimal
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@define(hash=True)
|
|
12
|
+
class Outcome:
|
|
13
|
+
"""
|
|
14
|
+
Defines a Outcome
|
|
15
|
+
|
|
16
|
+
An Outcome is a part of a Fill transaction. It holds information
|
|
17
|
+
including the fees paid, the strategy ID and trader address, and
|
|
18
|
+
whether fees are being paid in DDX or not (i.e. USDC).
|
|
19
|
+
|
|
20
|
+
Attributes:
|
|
21
|
+
trader (str): Trader's Ethereum address
|
|
22
|
+
strategy_id_hash (str): Strategy ID hash
|
|
23
|
+
fee (Decimal): Fees paid for filled trade
|
|
24
|
+
pay_fee_in_ddx (bool): Whether the trader has elected to pay the fee in DDX
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
trader: str = field(eq=str.lower, default="")
|
|
28
|
+
strategy_id_hash: str = field(eq=str.lower, default="")
|
|
29
|
+
# The below fields do not exist in the txlog (and thus are not checked in transaction
|
|
30
|
+
# equality), and are only used when executing/processing transactions.
|
|
31
|
+
fee: Optional[Decimal] = field(eq=False, default=None)
|
|
32
|
+
pay_fee_in_ddx: Optional[bool] = field(eq=False, default=None)
|