cryptocom-exchange 2.2.0__tar.gz → 2.3.0__tar.gz
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.
- {cryptocom_exchange-2.2.0 → cryptocom_exchange-2.3.0}/PKG-INFO +13 -11
- {cryptocom_exchange-2.2.0 → cryptocom_exchange-2.3.0}/README.md +12 -10
- {cryptocom_exchange-2.2.0 → cryptocom_exchange-2.3.0}/pyproject.toml +1 -1
- {cryptocom_exchange-2.2.0 → cryptocom_exchange-2.3.0}/src/cryptocom/exchange/__init__.py +27 -28
- {cryptocom_exchange-2.2.0 → cryptocom_exchange-2.3.0}/src/cryptocom/exchange/account.py +82 -72
- {cryptocom_exchange-2.2.0 → cryptocom_exchange-2.3.0}/src/cryptocom/exchange/api.py +2 -2
- {cryptocom_exchange-2.2.0 → cryptocom_exchange-2.3.0}/src/cryptocom/exchange/exceptions.py +0 -8
- {cryptocom_exchange-2.2.0 → cryptocom_exchange-2.3.0}/src/cryptocom/exchange/structs.py +9 -9
- {cryptocom_exchange-2.2.0 → cryptocom_exchange-2.3.0}/LICENSE +0 -0
- {cryptocom_exchange-2.2.0 → cryptocom_exchange-2.3.0}/src/cryptocom/__init__.py +0 -0
- {cryptocom_exchange-2.2.0 → cryptocom_exchange-2.3.0}/src/cryptocom/exchange/helpers.py +0 -0
- {cryptocom_exchange-2.2.0 → cryptocom_exchange-2.3.0}/src/cryptocom/exchange/instruments.py +0 -0
- {cryptocom_exchange-2.2.0 → cryptocom_exchange-2.3.0}/src/cryptocom/exchange/market.py +0 -0
- {cryptocom_exchange-2.2.0 → cryptocom_exchange-2.3.0}/src/cryptocom/exchange/pairs.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: cryptocom-exchange
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.3.0
|
|
4
4
|
Summary: Python 3.11+ async library for crypto.com/exchange API using httpx and websockets
|
|
5
5
|
License: MIT
|
|
6
6
|
License-File: LICENSE
|
|
@@ -131,7 +131,11 @@ response.
|
|
|
131
131
|
### Orders
|
|
132
132
|
|
|
133
133
|
`Account` caches orders by `client_oid` in `Account.order_cache` (the snippets
|
|
134
|
-
here use `from cryptocom.exchange import exceptions`).
|
|
134
|
+
here use `from cryptocom.exchange import exceptions`). `get_order` returns
|
|
135
|
+
`None` when the exchange CONFIRMS absence (empty result or code 40401) — a
|
|
136
|
+
definite answer; unknown outcomes raise `ApiError`. (`OrderNotFoundError`
|
|
137
|
+
lives on for endpoints where absence IS the error reply: cancel, amend.)
|
|
138
|
+
Terminal
|
|
135
139
|
entries (`Order.is_terminal`, `TERMINAL_ORDER_STATUSES`) are final and live
|
|
136
140
|
forever; live entries expire after `orders_ttl` seconds (default 5, `0`
|
|
137
141
|
disables live caching). The cache is only written from observed answers: cancel
|
|
@@ -150,14 +154,12 @@ order = await account.wait_for_order(
|
|
|
150
154
|
timeout=5.0,
|
|
151
155
|
)
|
|
152
156
|
|
|
153
|
-
# Cancel, then poll to
|
|
154
|
-
#
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
await reconcile_later(client_id) #
|
|
159
|
-
else:
|
|
160
|
-
print("terminal:", order.status if order else "absent")
|
|
157
|
+
# Cancel, then poll to terminal. True = CONFIRMED not live (terminal or
|
|
158
|
+
# absent); False = not confirmed (timed out or still live) - never treat
|
|
159
|
+
# False as cancelled. Indeterminate cancel/lookup failures raise ApiError.
|
|
160
|
+
cancelled = await account.cancel_order_and_wait(pair, client_id, timeout=5.0)
|
|
161
|
+
if not cancelled:
|
|
162
|
+
await reconcile_later(client_id) # may still land after the last poll
|
|
161
163
|
```
|
|
162
164
|
|
|
163
165
|
A websocket feed can answer lookups directly: `updates` wins over cache and
|
|
@@ -171,7 +173,7 @@ order = await account.get_order(client_order_id=client_id, updates={client_id: s
|
|
|
171
173
|
Two guards worth knowing:
|
|
172
174
|
|
|
173
175
|
- A `client_oid` created moments ago that reads back empty raises an
|
|
174
|
-
indeterminate `ApiError
|
|
176
|
+
indeterminate `ApiError` instead of returning `None`, for `create_race_guard`
|
|
175
177
|
seconds (default 2). Absence right after create is not proof of absence.
|
|
176
178
|
- `wait_for_order` raises `exceptions.WaitNotConfirmedError` (indeterminate) on
|
|
177
179
|
timeout instead of returning `None`. `wait_for_status` is deprecated in its
|
|
@@ -105,7 +105,11 @@ response.
|
|
|
105
105
|
### Orders
|
|
106
106
|
|
|
107
107
|
`Account` caches orders by `client_oid` in `Account.order_cache` (the snippets
|
|
108
|
-
here use `from cryptocom.exchange import exceptions`).
|
|
108
|
+
here use `from cryptocom.exchange import exceptions`). `get_order` returns
|
|
109
|
+
`None` when the exchange CONFIRMS absence (empty result or code 40401) — a
|
|
110
|
+
definite answer; unknown outcomes raise `ApiError`. (`OrderNotFoundError`
|
|
111
|
+
lives on for endpoints where absence IS the error reply: cancel, amend.)
|
|
112
|
+
Terminal
|
|
109
113
|
entries (`Order.is_terminal`, `TERMINAL_ORDER_STATUSES`) are final and live
|
|
110
114
|
forever; live entries expire after `orders_ttl` seconds (default 5, `0`
|
|
111
115
|
disables live caching). The cache is only written from observed answers: cancel
|
|
@@ -124,14 +128,12 @@ order = await account.wait_for_order(
|
|
|
124
128
|
timeout=5.0,
|
|
125
129
|
)
|
|
126
130
|
|
|
127
|
-
# Cancel, then poll to
|
|
128
|
-
#
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
await reconcile_later(client_id) #
|
|
133
|
-
else:
|
|
134
|
-
print("terminal:", order.status if order else "absent")
|
|
131
|
+
# Cancel, then poll to terminal. True = CONFIRMED not live (terminal or
|
|
132
|
+
# absent); False = not confirmed (timed out or still live) - never treat
|
|
133
|
+
# False as cancelled. Indeterminate cancel/lookup failures raise ApiError.
|
|
134
|
+
cancelled = await account.cancel_order_and_wait(pair, client_id, timeout=5.0)
|
|
135
|
+
if not cancelled:
|
|
136
|
+
await reconcile_later(client_id) # may still land after the last poll
|
|
135
137
|
```
|
|
136
138
|
|
|
137
139
|
A websocket feed can answer lookups directly: `updates` wins over cache and
|
|
@@ -145,7 +147,7 @@ order = await account.get_order(client_order_id=client_id, updates={client_id: s
|
|
|
145
147
|
Two guards worth knowing:
|
|
146
148
|
|
|
147
149
|
- A `client_oid` created moments ago that reads back empty raises an
|
|
148
|
-
indeterminate `ApiError
|
|
150
|
+
indeterminate `ApiError` instead of returning `None`, for `create_race_guard`
|
|
149
151
|
seconds (default 2). Absence right after create is not proof of absence.
|
|
150
152
|
- `wait_for_order` raises `exceptions.WaitNotConfirmedError` (indeterminate) on
|
|
151
153
|
timeout instead of returning `None`. `wait_for_status` is deprecated in its
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
from . import exceptions, instruments, pairs
|
|
2
|
-
from .api import ApiError, ApiProvider, RecordApiProvider
|
|
3
|
-
from .market import Market
|
|
4
2
|
from .account import (
|
|
5
|
-
Account,
|
|
6
|
-
OrderKeys,
|
|
7
3
|
MAX_OPEN_ORDERS_PER_ACCOUNT,
|
|
8
4
|
MAX_OPEN_ORDERS_PER_PAIR,
|
|
5
|
+
Account,
|
|
6
|
+
OrderKeys,
|
|
9
7
|
)
|
|
8
|
+
from .api import ApiError, ApiProvider, RecordApiProvider
|
|
10
9
|
from .exceptions import ApiAuthError, ApiErrorCode, ExchangeError
|
|
10
|
+
from .market import Market
|
|
11
11
|
from .structs import (
|
|
12
12
|
Candle,
|
|
13
13
|
Deposit,
|
|
@@ -29,38 +29,37 @@ from .structs import (
|
|
|
29
29
|
WithdrawalStatus,
|
|
30
30
|
)
|
|
31
31
|
|
|
32
|
-
|
|
33
32
|
__all__ = [
|
|
33
|
+
"MAX_OPEN_ORDERS_PER_ACCOUNT",
|
|
34
|
+
"MAX_OPEN_ORDERS_PER_PAIR",
|
|
35
|
+
"Account",
|
|
36
|
+
"ApiAuthError",
|
|
37
|
+
"ApiError",
|
|
38
|
+
"ApiErrorCode",
|
|
39
|
+
"ApiProvider",
|
|
40
|
+
"Candle",
|
|
41
|
+
"Deposit",
|
|
42
|
+
"DepositStatus",
|
|
43
|
+
"ExchangeError",
|
|
44
|
+
"Instrument",
|
|
45
|
+
"InstrumentType",
|
|
46
|
+
"Market",
|
|
47
|
+
"MarketTrade",
|
|
34
48
|
"Order",
|
|
49
|
+
"OrderExecFlag",
|
|
50
|
+
"OrderForceType",
|
|
51
|
+
"OrderKeys",
|
|
35
52
|
"OrderSide",
|
|
36
53
|
"OrderStatus",
|
|
37
54
|
"OrderType",
|
|
38
|
-
"OrderForceType",
|
|
39
|
-
"OrderExecFlag",
|
|
40
|
-
"OrderKeys",
|
|
41
|
-
"exceptions",
|
|
42
|
-
"pairs",
|
|
43
55
|
"Pair",
|
|
44
|
-
"instruments",
|
|
45
|
-
"Instrument",
|
|
46
|
-
"InstrumentType",
|
|
47
|
-
"Timeframe",
|
|
48
|
-
"Candle",
|
|
49
|
-
"MarketTrade",
|
|
50
56
|
"PrivateTrade",
|
|
57
|
+
"RecordApiProvider",
|
|
51
58
|
"TimeDelta",
|
|
52
|
-
"
|
|
59
|
+
"Timeframe",
|
|
53
60
|
"Withdrawal",
|
|
54
|
-
"DepositStatus",
|
|
55
61
|
"WithdrawalStatus",
|
|
56
|
-
"
|
|
57
|
-
"
|
|
58
|
-
"
|
|
59
|
-
"ApiProvider",
|
|
60
|
-
"RecordApiProvider",
|
|
61
|
-
"ApiAuthError",
|
|
62
|
-
"ApiErrorCode",
|
|
63
|
-
"ExchangeError",
|
|
64
|
-
"MAX_OPEN_ORDERS_PER_PAIR",
|
|
65
|
-
"MAX_OPEN_ORDERS_PER_ACCOUNT",
|
|
62
|
+
"exceptions",
|
|
63
|
+
"instruments",
|
|
64
|
+
"pairs",
|
|
66
65
|
]
|
|
@@ -3,12 +3,11 @@ import logging
|
|
|
3
3
|
import time
|
|
4
4
|
import warnings
|
|
5
5
|
from dataclasses import dataclass
|
|
6
|
-
from uuid import uuid4
|
|
7
6
|
from typing import Any
|
|
7
|
+
from uuid import uuid4
|
|
8
8
|
|
|
9
9
|
from .api import ApiError, ApiProvider
|
|
10
10
|
from .exceptions import (
|
|
11
|
-
CancelNotConfirmedError,
|
|
12
11
|
OrderNotActiveError,
|
|
13
12
|
OrderNotFoundError,
|
|
14
13
|
WaitNotConfirmedError,
|
|
@@ -459,16 +458,14 @@ class Account:
|
|
|
459
458
|
deadline = loop.time() + timeout
|
|
460
459
|
last = "unknown"
|
|
461
460
|
while True:
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
except OrderNotFoundError:
|
|
469
|
-
order = None
|
|
461
|
+
order = await self.get_order(
|
|
462
|
+
order_id=order_id,
|
|
463
|
+
client_order_id=client_order_id,
|
|
464
|
+
ignore_cache=True,
|
|
465
|
+
)
|
|
466
|
+
if order is None:
|
|
470
467
|
last = "absent"
|
|
471
|
-
|
|
468
|
+
else:
|
|
472
469
|
last = str(order.status)
|
|
473
470
|
if order.status in statuses:
|
|
474
471
|
return order
|
|
@@ -593,7 +590,7 @@ class Account:
|
|
|
593
590
|
client_order_id: str | None = None,
|
|
594
591
|
updates: dict[str, Order] | None = None,
|
|
595
592
|
ignore_cache: bool = False,
|
|
596
|
-
) -> Order:
|
|
593
|
+
) -> Order | None:
|
|
597
594
|
"""Get order info by order_id or client_order_id.
|
|
598
595
|
|
|
599
596
|
Cache-backed for client_order_id lookups. Resolution priority:
|
|
@@ -601,16 +598,14 @@ class Account:
|
|
|
601
598
|
cache > API. Terminal orders are cached forever; live orders for
|
|
602
599
|
``orders_ttl`` seconds (0 disables caching).
|
|
603
600
|
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
ignore_cache: If True, query the exchange directly
|
|
601
|
+
Returns:
|
|
602
|
+
The Order, or None if the exchange CONFIRMS the order is absent
|
|
603
|
+
(empty result / code 40401). None is a definite answer; unknown
|
|
604
|
+
outcomes raise. Endpoints where "not found" is an error reply
|
|
605
|
+
(cancel, amend) still raise OrderNotFoundError.
|
|
610
606
|
|
|
611
607
|
Raises:
|
|
612
608
|
ValueError: If neither order_id nor client_order_id is provided
|
|
613
|
-
OrderNotFoundError: The order is confirmed absent (deterministic)
|
|
614
609
|
ApiError: Just-created order unreadable, or lookup failed -
|
|
615
610
|
outcome UNKNOWN, must be reconciled later (indeterminate)
|
|
616
611
|
"""
|
|
@@ -633,10 +628,15 @@ class Account:
|
|
|
633
628
|
if client_order_id:
|
|
634
629
|
params["client_oid"] = client_order_id
|
|
635
630
|
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
631
|
+
try:
|
|
632
|
+
data = await self.api.post(
|
|
633
|
+
"private/get-order-detail",
|
|
634
|
+
{"params": params},
|
|
635
|
+
)
|
|
636
|
+
except OrderNotFoundError:
|
|
637
|
+
# Coded 40401 is the same confirmed-absent answer as an empty
|
|
638
|
+
# result; get_order reports absence as None.
|
|
639
|
+
data = None
|
|
640
640
|
if not data:
|
|
641
641
|
# Structured success with empty result - normally "not there".
|
|
642
642
|
# Exception: an order created moments ago may not be readable
|
|
@@ -654,9 +654,7 @@ class Account:
|
|
|
654
654
|
"creation - state unknown, reconcile later",
|
|
655
655
|
method="private/get-order-detail",
|
|
656
656
|
)
|
|
657
|
-
|
|
658
|
-
"No order data", code=40401, method="private/get-order-detail"
|
|
659
|
-
)
|
|
657
|
+
return None
|
|
660
658
|
|
|
661
659
|
order = Order.create_from_api(
|
|
662
660
|
self.pairs[data["instrument_name"]],
|
|
@@ -729,70 +727,82 @@ class Account:
|
|
|
729
727
|
client_order_id: str,
|
|
730
728
|
timeout: float = 30.0,
|
|
731
729
|
poll_interval: float = 0.5,
|
|
732
|
-
) ->
|
|
733
|
-
"""Cancel an order and
|
|
730
|
+
) -> bool:
|
|
731
|
+
"""Cancel an order and confirm it is no longer live.
|
|
734
732
|
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
Args:
|
|
740
|
-
pair: Trading pair
|
|
741
|
-
client_order_id: Client order ID (client_oid) of the order
|
|
742
|
-
timeout: Max seconds to wait for a terminal status
|
|
743
|
-
poll_interval: Seconds between status polls
|
|
733
|
+
All exchange-side races are resolved here: an already-terminal or
|
|
734
|
+
confirmed-absent order needs no cancel; a cancel rejected as
|
|
735
|
+
not-active (316) re-queries once, because racing into
|
|
736
|
+
FILLED/CANCELED/absent is a confirmed outcome, not an error.
|
|
744
737
|
|
|
745
738
|
Returns:
|
|
746
|
-
|
|
747
|
-
|
|
739
|
+
True: CONFIRMED not live - cancel reached terminal status, the
|
|
740
|
+
order was already terminal, or the exchange confirmed it
|
|
741
|
+
absent. Safe to treat as cancelled.
|
|
742
|
+
False: cancellation NOT confirmed - cancel timed out (it may
|
|
743
|
+
still have landed after the last poll) or the order is
|
|
744
|
+
confirmed still live. Never read False as "cancelled".
|
|
748
745
|
|
|
749
746
|
Raises:
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
ApiError: Cancel or lookup failed with an indeterminate error -
|
|
753
|
-
outcome unknown, the caller must reconcile later.
|
|
747
|
+
ApiError: Cancel or lookup failed indeterminately - outcome
|
|
748
|
+
unknown, reconcile later.
|
|
754
749
|
"""
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
)
|
|
759
|
-
except OrderNotFoundError:
|
|
760
|
-
return None
|
|
761
|
-
if order.is_terminal:
|
|
762
|
-
return order
|
|
750
|
+
order = await self.get_order(client_order_id=client_order_id, ignore_cache=True)
|
|
751
|
+
if order is None or order.is_terminal:
|
|
752
|
+
return True
|
|
763
753
|
|
|
764
754
|
try:
|
|
765
755
|
await self.cancel_order(pair, client_order_id=client_order_id)
|
|
766
756
|
except OrderNotActiveError:
|
|
767
757
|
# Cancel raced a status change - re-query to learn the outcome.
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
except OrderNotFoundError:
|
|
773
|
-
return None
|
|
774
|
-
if order.is_terminal:
|
|
775
|
-
return order
|
|
776
|
-
raise
|
|
758
|
+
order = await self.get_order(
|
|
759
|
+
client_order_id=client_order_id, ignore_cache=True
|
|
760
|
+
)
|
|
761
|
+
return order is None or order.is_terminal
|
|
777
762
|
except OrderNotFoundError:
|
|
778
|
-
return
|
|
763
|
+
return True
|
|
779
764
|
|
|
780
765
|
loop = asyncio.get_running_loop()
|
|
781
766
|
deadline = loop.time() + timeout
|
|
782
767
|
while loop.time() < deadline:
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
return None
|
|
789
|
-
if order.is_terminal:
|
|
790
|
-
return order
|
|
768
|
+
order = await self.get_order(
|
|
769
|
+
client_order_id=client_order_id, ignore_cache=True
|
|
770
|
+
)
|
|
771
|
+
if order is None or order.is_terminal:
|
|
772
|
+
return True
|
|
791
773
|
await asyncio.sleep(poll_interval)
|
|
774
|
+
return False
|
|
775
|
+
|
|
776
|
+
async def resolve_stuck_order(
|
|
777
|
+
self,
|
|
778
|
+
pair: Pair,
|
|
779
|
+
order: Order,
|
|
780
|
+
stuck_after: float = 300.0,
|
|
781
|
+
timeout: float = 30.0,
|
|
782
|
+
poll_interval: float = 0.5,
|
|
783
|
+
) -> bool:
|
|
784
|
+
"""Resolve a possibly-stuck resting order (shadow-order mechanic).
|
|
785
|
+
|
|
786
|
+
An order is stuck when it sits in NEW (never activated) for longer
|
|
787
|
+
than ``stuck_after`` seconds AND cancel-and-wait cannot confirm its
|
|
788
|
+
removal. Anything else (healthy, terminal, NEW but still fresh, or
|
|
789
|
+
successfully cancelled) is clear to proceed.
|
|
790
|
+
|
|
791
|
+
Returns:
|
|
792
|
+
True: the order is STUCK (cancel attempted, not confirmed).
|
|
793
|
+
False: clear to proceed - no cancellation was needed or the
|
|
794
|
+
stuck order was successfully removed.
|
|
792
795
|
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
+
Raises:
|
|
797
|
+
ApiError: cancel or lookup failed indeterminately - unknown is
|
|
798
|
+
never reported as stuck or clear; reconcile later.
|
|
799
|
+
"""
|
|
800
|
+
if order.status != OrderStatus.NEW:
|
|
801
|
+
return False
|
|
802
|
+
if time.time() - order.created_at <= stuck_after:
|
|
803
|
+
return False
|
|
804
|
+
return not await self.cancel_order_and_wait(
|
|
805
|
+
pair, order.client_id, timeout, poll_interval
|
|
796
806
|
)
|
|
797
807
|
|
|
798
808
|
async def update_order(
|
|
@@ -193,9 +193,9 @@ class ApiProvider:
|
|
|
193
193
|
|
|
194
194
|
self.rate_limiters = {}
|
|
195
195
|
|
|
196
|
-
for urls in RATE_LIMITS:
|
|
196
|
+
for urls, limit in RATE_LIMITS.items():
|
|
197
197
|
for url in urls:
|
|
198
|
-
self.rate_limiters[url] = aiolimiter.AsyncLimiter(*
|
|
198
|
+
self.rate_limiters[url] = aiolimiter.AsyncLimiter(*limit)
|
|
199
199
|
|
|
200
200
|
# limits for not matched methods
|
|
201
201
|
# Private APIs: 3 requests per 100ms (REST API - authenticated)
|
|
@@ -326,14 +326,6 @@ class MaintenanceError(ExchangeError):
|
|
|
326
326
|
"""Exchange under maintenance (code 9010001). Transient."""
|
|
327
327
|
|
|
328
328
|
|
|
329
|
-
class CancelNotConfirmedError(ApiError):
|
|
330
|
-
"""Cancel was not confirmed terminal before the timeout.
|
|
331
|
-
|
|
332
|
-
Indeterminate (code is None): the cancel may still have landed after
|
|
333
|
-
the last poll - the caller must reconcile the order later.
|
|
334
|
-
"""
|
|
335
|
-
|
|
336
|
-
|
|
337
329
|
class WaitNotConfirmedError(ApiError):
|
|
338
330
|
"""An order did not reach a required status before the timeout.
|
|
339
331
|
|
|
@@ -921,15 +921,15 @@ class Transaction:
|
|
|
921
921
|
|
|
922
922
|
@staticmethod
|
|
923
923
|
def _prepare(data):
|
|
924
|
-
return
|
|
925
|
-
id
|
|
926
|
-
instrument
|
|
927
|
-
fee
|
|
928
|
-
create_time
|
|
929
|
-
update_time
|
|
930
|
-
amount
|
|
931
|
-
address
|
|
932
|
-
|
|
924
|
+
return {
|
|
925
|
+
"id": data["id"],
|
|
926
|
+
"instrument": Instrument(data["currency"]),
|
|
927
|
+
"fee": float(data["fee"]),
|
|
928
|
+
"create_time": datetime.fromtimestamp(int(data["create_time"]) / 1000),
|
|
929
|
+
"update_time": datetime.fromtimestamp(int(data["update_time"]) / 1000),
|
|
930
|
+
"amount": float(data["amount"]),
|
|
931
|
+
"address": data["address"],
|
|
932
|
+
}
|
|
933
933
|
|
|
934
934
|
|
|
935
935
|
@dataclass
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|