walrasquant-lib 0.4.20__py3-none-any.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.
- walrasquant/__init__.py +7 -0
- walrasquant/aggregation.py +449 -0
- walrasquant/backends/__init__.py +5 -0
- walrasquant/backends/db.py +109 -0
- walrasquant/backends/db_memory.py +61 -0
- walrasquant/backends/db_postgresql.py +321 -0
- walrasquant/backends/db_sqlite.py +310 -0
- walrasquant/base/__init__.py +24 -0
- walrasquant/base/api_client.py +46 -0
- walrasquant/base/connector.py +863 -0
- walrasquant/base/ems.py +794 -0
- walrasquant/base/exchange.py +213 -0
- walrasquant/base/oms.py +428 -0
- walrasquant/base/retry.py +220 -0
- walrasquant/base/sms.py +545 -0
- walrasquant/base/ws_client.py +408 -0
- walrasquant/config.py +284 -0
- walrasquant/constants.py +413 -0
- walrasquant/core/__init__.py +0 -0
- walrasquant/core/cache.py +688 -0
- walrasquant/core/clock.py +59 -0
- walrasquant/core/connection.py +41 -0
- walrasquant/core/entity.py +504 -0
- walrasquant/core/nautilius_core.py +103 -0
- walrasquant/core/registry.py +41 -0
- walrasquant/engine.py +745 -0
- walrasquant/error.py +34 -0
- walrasquant/exchange/__init__.py +13 -0
- walrasquant/exchange/base_factory.py +172 -0
- walrasquant/exchange/binance/__init__.py +30 -0
- walrasquant/exchange/binance/connector.py +1093 -0
- walrasquant/exchange/binance/constants.py +934 -0
- walrasquant/exchange/binance/ems.py +140 -0
- walrasquant/exchange/binance/error.py +48 -0
- walrasquant/exchange/binance/exchange.py +144 -0
- walrasquant/exchange/binance/factory.py +115 -0
- walrasquant/exchange/binance/oms.py +1807 -0
- walrasquant/exchange/binance/rest_api.py +1653 -0
- walrasquant/exchange/binance/schema.py +1063 -0
- walrasquant/exchange/binance/websockets.py +389 -0
- walrasquant/exchange/bitget/__init__.py +28 -0
- walrasquant/exchange/bitget/connector.py +578 -0
- walrasquant/exchange/bitget/constants.py +392 -0
- walrasquant/exchange/bitget/ems.py +202 -0
- walrasquant/exchange/bitget/error.py +36 -0
- walrasquant/exchange/bitget/exchange.py +128 -0
- walrasquant/exchange/bitget/factory.py +135 -0
- walrasquant/exchange/bitget/oms.py +1619 -0
- walrasquant/exchange/bitget/rest_api.py +610 -0
- walrasquant/exchange/bitget/schema.py +885 -0
- walrasquant/exchange/bitget/websockets.py +753 -0
- walrasquant/exchange/bybit/__init__.py +32 -0
- walrasquant/exchange/bybit/connector.py +819 -0
- walrasquant/exchange/bybit/constants.py +479 -0
- walrasquant/exchange/bybit/ems.py +93 -0
- walrasquant/exchange/bybit/error.py +36 -0
- walrasquant/exchange/bybit/exchange.py +108 -0
- walrasquant/exchange/bybit/factory.py +128 -0
- walrasquant/exchange/bybit/oms.py +1195 -0
- walrasquant/exchange/bybit/rest_api.py +570 -0
- walrasquant/exchange/bybit/schema.py +867 -0
- walrasquant/exchange/bybit/websockets.py +307 -0
- walrasquant/exchange/hyperliquid/__init__.py +28 -0
- walrasquant/exchange/hyperliquid/connector.py +370 -0
- walrasquant/exchange/hyperliquid/constants.py +371 -0
- walrasquant/exchange/hyperliquid/ems.py +156 -0
- walrasquant/exchange/hyperliquid/error.py +48 -0
- walrasquant/exchange/hyperliquid/exchange.py +120 -0
- walrasquant/exchange/hyperliquid/factory.py +135 -0
- walrasquant/exchange/hyperliquid/oms.py +1081 -0
- walrasquant/exchange/hyperliquid/rest_api.py +348 -0
- walrasquant/exchange/hyperliquid/schema.py +583 -0
- walrasquant/exchange/hyperliquid/websockets.py +592 -0
- walrasquant/exchange/okx/__init__.py +25 -0
- walrasquant/exchange/okx/connector.py +931 -0
- walrasquant/exchange/okx/constants.py +518 -0
- walrasquant/exchange/okx/ems.py +144 -0
- walrasquant/exchange/okx/error.py +66 -0
- walrasquant/exchange/okx/exchange.py +102 -0
- walrasquant/exchange/okx/factory.py +138 -0
- walrasquant/exchange/okx/oms.py +1199 -0
- walrasquant/exchange/okx/rest_api.py +799 -0
- walrasquant/exchange/okx/schema.py +1449 -0
- walrasquant/exchange/okx/websockets.py +420 -0
- walrasquant/exchange/registry.py +201 -0
- walrasquant/execution/__init__.py +24 -0
- walrasquant/execution/algorithm.py +968 -0
- walrasquant/execution/algorithms/__init__.py +3 -0
- walrasquant/execution/algorithms/twap.py +392 -0
- walrasquant/execution/config.py +34 -0
- walrasquant/execution/constants.py +27 -0
- walrasquant/execution/schema.py +62 -0
- walrasquant/indicator.py +382 -0
- walrasquant/push.py +77 -0
- walrasquant/schema.py +755 -0
- walrasquant/strategy.py +1805 -0
- walrasquant/tools/__init__.py +0 -0
- walrasquant/tools/pm2_wrapper.py +1016 -0
- walrasquant/web/__init__.py +26 -0
- walrasquant/web/app.py +157 -0
- walrasquant/web/server.py +92 -0
- walrasquant_lib-0.4.20.dist-info/METADATA +162 -0
- walrasquant_lib-0.4.20.dist-info/RECORD +105 -0
- walrasquant_lib-0.4.20.dist-info/WHEEL +4 -0
- walrasquant_lib-0.4.20.dist-info/entry_points.txt +3 -0
|
@@ -0,0 +1,934 @@
|
|
|
1
|
+
from enum import Enum
|
|
2
|
+
from datetime import timedelta
|
|
3
|
+
from walrasquant.constants import (
|
|
4
|
+
KlineInterval,
|
|
5
|
+
AccountType,
|
|
6
|
+
OrderStatus,
|
|
7
|
+
OrderType,
|
|
8
|
+
PositionSide,
|
|
9
|
+
OrderSide,
|
|
10
|
+
TimeInForce,
|
|
11
|
+
TriggerType,
|
|
12
|
+
RateLimiter,
|
|
13
|
+
)
|
|
14
|
+
from walrasquant.error import KlineSupportedError
|
|
15
|
+
from walrasquant.exchange.binance.error import BinanceRateLimitError
|
|
16
|
+
from throttled.asyncio import Throttled, rate_limiter, RateLimiterType
|
|
17
|
+
from throttled import RateLimitResult
|
|
18
|
+
# from throttled import Throttled as ThrottledSync
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class BinancePriceMatch(Enum):
|
|
22
|
+
"""
|
|
23
|
+
NONE (No price match)
|
|
24
|
+
OPPONENT (counterparty best price)
|
|
25
|
+
OPPONENT_5 (the 5th best price from the counterparty)
|
|
26
|
+
OPPONENT_10 (the 10th best price from the counterparty)
|
|
27
|
+
OPPONENT_20 (the 20th best price from the counterparty)
|
|
28
|
+
QUEUE (the best price on the same side of the order book)
|
|
29
|
+
QUEUE_5 (the 5th best price on the same side of the order book)
|
|
30
|
+
QUEUE_10 (the 10th best price on the same side of the order book)
|
|
31
|
+
QUEUE_20 (the 20th best price on the same side of the order book)
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
NONE = "NONE"
|
|
35
|
+
OPPONENT = "OPPONENT"
|
|
36
|
+
OPPONENT_5 = "OPPONENT_5"
|
|
37
|
+
OPPONENT_10 = "OPPONENT_10"
|
|
38
|
+
OPPONENT_20 = "OPPONENT_20"
|
|
39
|
+
QUEUE = "QUEUE"
|
|
40
|
+
QUEUE_5 = "QUEUE_5"
|
|
41
|
+
QUEUE_10 = "QUEUE_10"
|
|
42
|
+
QUEUE_20 = "QUEUE_20"
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class BinanceTriggerType(Enum):
|
|
46
|
+
MARK_PRICE = "MARK_PRICE"
|
|
47
|
+
CONTRACT_PRICE = "CONTRACT_PRICE"
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class BinanceAccountEventReasonType(Enum):
|
|
51
|
+
DEPOSIT = "DEPOSIT"
|
|
52
|
+
WITHDRAW = "WITHDRAW"
|
|
53
|
+
ORDER = "ORDER"
|
|
54
|
+
FUNDING_FEE = "FUNDING_FEE"
|
|
55
|
+
WITHDRAW_REJECT = "WITHDRAW_REJECT"
|
|
56
|
+
ADJUSTMENT = "ADJUSTMENT"
|
|
57
|
+
INSURANCE_CLEAR = "INSURANCE_CLEAR"
|
|
58
|
+
ADMIN_DEPOSIT = "ADMIN_DEPOSIT"
|
|
59
|
+
ADMIN_WITHDRAW = "ADMIN_WITHDRAW"
|
|
60
|
+
MARGIN_TRANSFER = "MARGIN_TRANSFER"
|
|
61
|
+
MARGIN_TYPE_CHANGE = "MARGIN_TYPE_CHANGE"
|
|
62
|
+
ASSET_TRANSFER = "ASSET_TRANSFER"
|
|
63
|
+
OPTIONS_PREMIUM_FEE = "OPTIONS_PREMIUM_FEE"
|
|
64
|
+
OPTIONS_SETTLE_PROFIT = "OPTIONS_SETTLE_PROFIT"
|
|
65
|
+
AUTO_EXCHANGE = "AUTO_EXCHANGE"
|
|
66
|
+
COIN_SWAP_DEPOSIT = "COIN_SWAP_DEPOSIT"
|
|
67
|
+
COIN_SWAP_WITHDRAW = "COIN_SWAP_WITHDRAW"
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
class BinanceBusinessUnit(Enum):
|
|
71
|
+
"""
|
|
72
|
+
Represents a Binance business unit.
|
|
73
|
+
"""
|
|
74
|
+
|
|
75
|
+
UM = "UM"
|
|
76
|
+
CM = "CM"
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
class BinanceFuturesWorkingType(Enum):
|
|
80
|
+
"""
|
|
81
|
+
Represents a Binance Futures working type.
|
|
82
|
+
"""
|
|
83
|
+
|
|
84
|
+
MARK_PRICE = "MARK_PRICE"
|
|
85
|
+
CONTRACT_PRICE = "CONTRACT_PRICE"
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
class BinanceTimeInForce(Enum):
|
|
89
|
+
"""
|
|
90
|
+
Represents a Binance order time in force.
|
|
91
|
+
"""
|
|
92
|
+
|
|
93
|
+
GTC = "GTC"
|
|
94
|
+
IOC = "IOC"
|
|
95
|
+
FOK = "FOK"
|
|
96
|
+
GTX = "GTX" # FUTURES only, Good-Till-Crossing (Post Only)
|
|
97
|
+
GTD = "GTD" # FUTURES only
|
|
98
|
+
GTE_GTC = "GTE_GTC" # Undocumented
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
class BinanceOrderSide(Enum):
|
|
102
|
+
"""
|
|
103
|
+
Represents a Binance order side.
|
|
104
|
+
"""
|
|
105
|
+
|
|
106
|
+
BUY = "BUY"
|
|
107
|
+
SELL = "SELL"
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
class BinanceKlineInterval(Enum):
|
|
111
|
+
"""
|
|
112
|
+
Represents a Binance kline chart interval.
|
|
113
|
+
"""
|
|
114
|
+
|
|
115
|
+
SECOND_1 = "1s"
|
|
116
|
+
MINUTE_1 = "1m"
|
|
117
|
+
MINUTE_3 = "3m"
|
|
118
|
+
MINUTE_5 = "5m"
|
|
119
|
+
MINUTE_15 = "15m"
|
|
120
|
+
MINUTE_30 = "30m"
|
|
121
|
+
HOUR_1 = "1h"
|
|
122
|
+
HOUR_2 = "2h"
|
|
123
|
+
HOUR_4 = "4h"
|
|
124
|
+
HOUR_6 = "6h"
|
|
125
|
+
HOUR_8 = "8h"
|
|
126
|
+
HOUR_12 = "12h"
|
|
127
|
+
DAY_1 = "1d"
|
|
128
|
+
DAY_3 = "3d"
|
|
129
|
+
WEEK_1 = "1w"
|
|
130
|
+
MONTH_1 = "1M"
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
class BinanceWsEventType(Enum):
|
|
134
|
+
TRADE = "trade"
|
|
135
|
+
AGG_TRADE = "aggTrade"
|
|
136
|
+
BOOK_TICKER = "bookTicker"
|
|
137
|
+
KLINE = "kline"
|
|
138
|
+
MARK_PRICE_UPDATE = "markPriceUpdate"
|
|
139
|
+
DEPTH_UPDATE = "depthUpdate"
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
class BinanceUserDataStreamWsEventType(Enum):
|
|
143
|
+
TRADE_LITE = "TRADE_LITE"
|
|
144
|
+
MARGIN_CALL = "MARGIN_CALL"
|
|
145
|
+
ACCOUNT_UPDATE = "ACCOUNT_UPDATE"
|
|
146
|
+
ORDER_TRADE_UPDATE = "ORDER_TRADE_UPDATE"
|
|
147
|
+
POSITION_HISTORY_UPDATE = "POSITION_HISTORY_UPDATE"
|
|
148
|
+
ACCOUNT_CONFIG_UPDATE = "ACCOUNT_CONFIG_UPDATE"
|
|
149
|
+
STRATEGY_UPDATE = "STRATEGY_UPDATE"
|
|
150
|
+
GRID_UPDATE = "GRID_UPDATE"
|
|
151
|
+
CONDITIONAL_ORDER_TIGGER_REJECT = "CONDITIONAL_ORDER_TIGGER_REJECT"
|
|
152
|
+
OUT_BOUND_ACCOUNT_POSITION = "outboundAccountPosition"
|
|
153
|
+
BALANCE_UPDATE = "balanceUpdate"
|
|
154
|
+
EXECUTION_REPORT = "executionReport"
|
|
155
|
+
LISTING_STATUS = "listingStatus"
|
|
156
|
+
LISTEN_KEY_EXPIRED = "listenKeyExpired"
|
|
157
|
+
OPEN_ORDER_LOSS = "openOrderLoss"
|
|
158
|
+
LIABILITY_CHANGE = "liabilityChange"
|
|
159
|
+
RISK_LEVEL_CHANGE = "RISK_LEVEL_CHANGE"
|
|
160
|
+
CONDITIONAL_ORDER_TRADE_UPDATE = "CONDITIONAL_ORDER_TRADE_UPDATE"
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
class BinanceOrderType(Enum):
|
|
164
|
+
LIMIT = "LIMIT"
|
|
165
|
+
MARKET = "MARKET"
|
|
166
|
+
|
|
167
|
+
STOP = "STOP" # futures only
|
|
168
|
+
TAKE_PROFIT = "TAKE_PROFIT" # futures/spot in spot it is MARKET order in futures it is LIMIT order
|
|
169
|
+
TAKE_PROFIT_MARKET = "TAKE_PROFIT_MARKET" # futures only
|
|
170
|
+
STOP_MARKET = "STOP_MARKET" # futures only
|
|
171
|
+
|
|
172
|
+
STOP_LOSS = "STOP_LOSS" # spot only
|
|
173
|
+
STOP_LOSS_LIMIT = "STOP_LOSS_LIMIT" # spot only
|
|
174
|
+
TAKE_PROFIT_LIMIT = "TAKE_PROFIT_LIMIT" # spot only
|
|
175
|
+
|
|
176
|
+
LIMIT_MAKER = "LIMIT_MAKER" # spot only
|
|
177
|
+
TRAILING_STOP_MARKET = "TRAILING_STOP_MARKET"
|
|
178
|
+
|
|
179
|
+
@property
|
|
180
|
+
def is_market(self):
|
|
181
|
+
return self in (
|
|
182
|
+
self.STOP_MARKET,
|
|
183
|
+
self.TAKE_PROFIT_MARKET,
|
|
184
|
+
self.STOP_LOSS,
|
|
185
|
+
self.TAKE_PROFIT,
|
|
186
|
+
self.MARKET,
|
|
187
|
+
)
|
|
188
|
+
|
|
189
|
+
@property
|
|
190
|
+
def is_limit(self):
|
|
191
|
+
return self in (
|
|
192
|
+
self.TAKE_PROFIT_LIMIT,
|
|
193
|
+
self.STOP_LOSS_LIMIT,
|
|
194
|
+
self.STOP,
|
|
195
|
+
self.TAKE_PROFIT,
|
|
196
|
+
self.LIMIT,
|
|
197
|
+
)
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
class BinanceExecutionType(Enum):
|
|
201
|
+
NEW = "NEW"
|
|
202
|
+
CANCELED = "CANCELED"
|
|
203
|
+
REJECTED = "REJECTED"
|
|
204
|
+
TRADE = "TRADE"
|
|
205
|
+
EXPIRED = "EXPIRED"
|
|
206
|
+
CALCULATED = "CALCULATED"
|
|
207
|
+
TRADE_PREVENTION = "TRADE_PREVENTION"
|
|
208
|
+
AMENDMENT = "AMENDMENT"
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
class BinanceOrderStatus(Enum):
|
|
212
|
+
NEW = "NEW"
|
|
213
|
+
PARTIALLY_FILLED = "PARTIALLY_FILLED"
|
|
214
|
+
FILLED = "FILLED"
|
|
215
|
+
CANCELED = "CANCELED"
|
|
216
|
+
EXPIRED = "EXPIRED"
|
|
217
|
+
EXPIRED_IN_MATCH = "EXPIRED_IN_MATCH"
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
class BinancePositionSide(Enum):
|
|
221
|
+
BOTH = "BOTH"
|
|
222
|
+
LONG = "LONG"
|
|
223
|
+
SHORT = "SHORT"
|
|
224
|
+
|
|
225
|
+
def parse_to_position_side(self) -> PositionSide:
|
|
226
|
+
if self == self.BOTH:
|
|
227
|
+
return PositionSide.FLAT
|
|
228
|
+
elif self == self.LONG:
|
|
229
|
+
return PositionSide.LONG
|
|
230
|
+
elif self == self.SHORT:
|
|
231
|
+
return PositionSide.SHORT
|
|
232
|
+
raise RuntimeError(f"Invalid position side: {self}")
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
class BinanceAccountType(AccountType):
|
|
236
|
+
SPOT = "SPOT"
|
|
237
|
+
MARGIN = "MARGIN"
|
|
238
|
+
ISOLATED_MARGIN = "ISOLATED_MARGIN"
|
|
239
|
+
USD_M_FUTURE = "USD_M_FUTURE"
|
|
240
|
+
COIN_M_FUTURE = "COIN_M_FUTURE"
|
|
241
|
+
PORTFOLIO_MARGIN = "PORTFOLIO_MARGIN"
|
|
242
|
+
SPOT_TESTNET = "SPOT_TESTNET"
|
|
243
|
+
USD_M_FUTURE_TESTNET = "USD_M_FUTURE_TESTNET"
|
|
244
|
+
COIN_M_FUTURE_TESTNET = "COIN_M_FUTURE_TESTNET"
|
|
245
|
+
|
|
246
|
+
@property
|
|
247
|
+
def exchange_id(self):
|
|
248
|
+
return "binance"
|
|
249
|
+
|
|
250
|
+
@property
|
|
251
|
+
def is_spot(self):
|
|
252
|
+
return self in (self.SPOT, self.SPOT_TESTNET)
|
|
253
|
+
|
|
254
|
+
@property
|
|
255
|
+
def is_margin(self):
|
|
256
|
+
return self in (self.MARGIN,)
|
|
257
|
+
|
|
258
|
+
@property
|
|
259
|
+
def is_isolated_margin(self):
|
|
260
|
+
return self in (self.ISOLATED_MARGIN,)
|
|
261
|
+
|
|
262
|
+
@property
|
|
263
|
+
def is_isolated_margin_or_margin(self):
|
|
264
|
+
return self in (self.MARGIN, self.ISOLATED_MARGIN)
|
|
265
|
+
|
|
266
|
+
@property
|
|
267
|
+
def is_spot_or_margin(self):
|
|
268
|
+
return self in (self.SPOT, self.MARGIN, self.ISOLATED_MARGIN, self.SPOT_TESTNET)
|
|
269
|
+
|
|
270
|
+
@property
|
|
271
|
+
def is_future(self):
|
|
272
|
+
return self in (
|
|
273
|
+
self.USD_M_FUTURE,
|
|
274
|
+
self.COIN_M_FUTURE,
|
|
275
|
+
self.USD_M_FUTURE_TESTNET,
|
|
276
|
+
self.COIN_M_FUTURE_TESTNET,
|
|
277
|
+
)
|
|
278
|
+
|
|
279
|
+
@property
|
|
280
|
+
def is_linear(self):
|
|
281
|
+
return self in (self.USD_M_FUTURE, self.USD_M_FUTURE_TESTNET)
|
|
282
|
+
|
|
283
|
+
@property
|
|
284
|
+
def is_inverse(self):
|
|
285
|
+
return self in (self.COIN_M_FUTURE, self.COIN_M_FUTURE_TESTNET)
|
|
286
|
+
|
|
287
|
+
@property
|
|
288
|
+
def is_portfolio_margin(self):
|
|
289
|
+
return self in (self.PORTFOLIO_MARGIN,)
|
|
290
|
+
|
|
291
|
+
@property
|
|
292
|
+
def is_testnet(self):
|
|
293
|
+
return self in (
|
|
294
|
+
self.SPOT_TESTNET,
|
|
295
|
+
self.USD_M_FUTURE_TESTNET,
|
|
296
|
+
self.COIN_M_FUTURE_TESTNET,
|
|
297
|
+
)
|
|
298
|
+
|
|
299
|
+
@property
|
|
300
|
+
def base_url(self):
|
|
301
|
+
return BASE_URLS[self]
|
|
302
|
+
|
|
303
|
+
@property
|
|
304
|
+
def ws_url(self):
|
|
305
|
+
return STREAM_URLS[self]
|
|
306
|
+
|
|
307
|
+
@property
|
|
308
|
+
def ws_order_url(self):
|
|
309
|
+
return WS_ORDER_URLS.get(self, None)
|
|
310
|
+
|
|
311
|
+
@property
|
|
312
|
+
def ws_public_url(self):
|
|
313
|
+
"""Channel URL for high-freq public streams (bookTicker, depth). USD-M Futures only."""
|
|
314
|
+
return FUTURES_PUBLIC_STREAM_URLS.get(self)
|
|
315
|
+
|
|
316
|
+
@property
|
|
317
|
+
def ws_market_url(self):
|
|
318
|
+
"""Channel URL for regular market streams (aggTrade, markPrice, kline). USD-M Futures only."""
|
|
319
|
+
return FUTURES_MARKET_STREAM_URLS.get(self)
|
|
320
|
+
|
|
321
|
+
@property
|
|
322
|
+
def ws_private_url(self):
|
|
323
|
+
"""Channel URL for private user-data streams (listenKey). USD-M Futures only."""
|
|
324
|
+
return FUTURES_PRIVATE_STREAM_URLS.get(self)
|
|
325
|
+
|
|
326
|
+
|
|
327
|
+
class EndpointsType(Enum):
|
|
328
|
+
USER_DATA_STREAM = "USER_DATA_STREAM"
|
|
329
|
+
ACCOUNT = "ACCOUNT"
|
|
330
|
+
TRADING = "TRADING"
|
|
331
|
+
MARKET = "MARKET"
|
|
332
|
+
GENERAL = "GENERAL"
|
|
333
|
+
|
|
334
|
+
|
|
335
|
+
BASE_URLS = {
|
|
336
|
+
BinanceAccountType.SPOT: "https://api.binance.com",
|
|
337
|
+
BinanceAccountType.MARGIN: "https://api.binance.com",
|
|
338
|
+
BinanceAccountType.ISOLATED_MARGIN: "https://api.binance.com",
|
|
339
|
+
BinanceAccountType.USD_M_FUTURE: "https://fapi.binance.com",
|
|
340
|
+
BinanceAccountType.COIN_M_FUTURE: "https://dapi.binance.com",
|
|
341
|
+
BinanceAccountType.PORTFOLIO_MARGIN: "https://papi.binance.com",
|
|
342
|
+
BinanceAccountType.SPOT_TESTNET: "https://demo-api.binance.com",
|
|
343
|
+
BinanceAccountType.USD_M_FUTURE_TESTNET: "https://demo-fapi.binance.com",
|
|
344
|
+
BinanceAccountType.COIN_M_FUTURE_TESTNET: "https://demo-dapi.binance.com",
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
STREAM_URLS = {
|
|
348
|
+
BinanceAccountType.SPOT: "wss://stream.binance.com:9443",
|
|
349
|
+
BinanceAccountType.MARGIN: "wss://stream.binance.com:9443",
|
|
350
|
+
BinanceAccountType.ISOLATED_MARGIN: "wss://stream.binance.com:9443",
|
|
351
|
+
BinanceAccountType.USD_M_FUTURE: "wss://fstream.binance.com",
|
|
352
|
+
BinanceAccountType.COIN_M_FUTURE: "wss://dstream.binance.com",
|
|
353
|
+
BinanceAccountType.PORTFOLIO_MARGIN: "wss://fstream.binance.com/pm",
|
|
354
|
+
BinanceAccountType.SPOT_TESTNET: "wss://demo-stream.binance.com",
|
|
355
|
+
BinanceAccountType.USD_M_FUTURE_TESTNET: "wss://fstream.binancefuture.com",
|
|
356
|
+
BinanceAccountType.COIN_M_FUTURE_TESTNET: "wss://dstream.binancefuture.com",
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
# USD-M Futures channel-specific base URLs (Binance WebSocket URL migration)
|
|
360
|
+
# Public → high-freq book data: bookTicker, depth
|
|
361
|
+
# Market → regular market data: aggTrade, markPrice, kline, ticker, etc.
|
|
362
|
+
# Private → user data stream: listenKey / ORDER_TRADE_UPDATE / ACCOUNT_UPDATE
|
|
363
|
+
FUTURES_PUBLIC_STREAM_URLS = {
|
|
364
|
+
BinanceAccountType.USD_M_FUTURE: "wss://fstream.binance.com/public",
|
|
365
|
+
BinanceAccountType.USD_M_FUTURE_TESTNET: "wss://fstream.binancefuture.com/public",
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
FUTURES_MARKET_STREAM_URLS = {
|
|
369
|
+
BinanceAccountType.USD_M_FUTURE: "wss://fstream.binance.com/market",
|
|
370
|
+
BinanceAccountType.USD_M_FUTURE_TESTNET: "wss://fstream.binancefuture.com/market",
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
FUTURES_PRIVATE_STREAM_URLS = {
|
|
374
|
+
BinanceAccountType.USD_M_FUTURE: "wss://fstream.binance.com/private",
|
|
375
|
+
BinanceAccountType.USD_M_FUTURE_TESTNET: "wss://fstream.binancefuture.com/private",
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
WS_ORDER_URLS = {
|
|
379
|
+
BinanceAccountType.SPOT: "wss://ws-api.binance.com:443/ws-api/v3",
|
|
380
|
+
BinanceAccountType.SPOT_TESTNET: "wss://demo-ws-api.binance.com/ws-api/v3",
|
|
381
|
+
BinanceAccountType.USD_M_FUTURE: "wss://ws-fapi.binance.com/ws-fapi/v1",
|
|
382
|
+
BinanceAccountType.USD_M_FUTURE_TESTNET: "wss://testnet.binancefuture.com/ws-fapi/v1",
|
|
383
|
+
BinanceAccountType.COIN_M_FUTURE: "wss://ws-dapi.binance.com/ws-dapi/v1",
|
|
384
|
+
BinanceAccountType.COIN_M_FUTURE_TESTNET: "wss://testnet.binancefuture.com/ws-dapi/v1",
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
|
|
388
|
+
ENDPOINTS = {
|
|
389
|
+
EndpointsType.USER_DATA_STREAM: {
|
|
390
|
+
BinanceAccountType.SPOT: "/api/v3/userDataStream",
|
|
391
|
+
BinanceAccountType.MARGIN: "/sapi/v1/userDataStream",
|
|
392
|
+
BinanceAccountType.ISOLATED_MARGIN: "/sapi/v1/userDataStream/isolated",
|
|
393
|
+
BinanceAccountType.USD_M_FUTURE: "/fapi/v1/listenKey",
|
|
394
|
+
BinanceAccountType.COIN_M_FUTURE: "/dapi/v1/listenKey",
|
|
395
|
+
BinanceAccountType.PORTFOLIO_MARGIN: "/papi/v1/listenKey",
|
|
396
|
+
BinanceAccountType.SPOT_TESTNET: "/api/v3/userDataStream",
|
|
397
|
+
BinanceAccountType.USD_M_FUTURE_TESTNET: "/fapi/v1/listenKey",
|
|
398
|
+
BinanceAccountType.COIN_M_FUTURE_TESTNET: "/dapi/v1/listenKey",
|
|
399
|
+
},
|
|
400
|
+
EndpointsType.TRADING: {
|
|
401
|
+
BinanceAccountType.SPOT: "/api/v3",
|
|
402
|
+
BinanceAccountType.MARGIN: "/sapi/v1",
|
|
403
|
+
BinanceAccountType.ISOLATED_MARGIN: "/sapi/v1",
|
|
404
|
+
BinanceAccountType.USD_M_FUTURE: "/fapi/v1",
|
|
405
|
+
BinanceAccountType.COIN_M_FUTURE: "/dapi/v1",
|
|
406
|
+
BinanceAccountType.PORTFOLIO_MARGIN: "/papi/v1",
|
|
407
|
+
BinanceAccountType.SPOT_TESTNET: "/api/v3",
|
|
408
|
+
BinanceAccountType.USD_M_FUTURE_TESTNET: "/fapi/v1",
|
|
409
|
+
BinanceAccountType.COIN_M_FUTURE_TESTNET: "/dapi/v1",
|
|
410
|
+
},
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
|
|
414
|
+
class BinanceEnumParser:
|
|
415
|
+
_binance_trigger_type_map = {
|
|
416
|
+
BinanceTriggerType.MARK_PRICE: TriggerType.MARK_PRICE,
|
|
417
|
+
BinanceTriggerType.CONTRACT_PRICE: TriggerType.LAST_PRICE,
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
_binance_kline_interval_map = {
|
|
421
|
+
BinanceKlineInterval.SECOND_1: KlineInterval.SECOND_1,
|
|
422
|
+
BinanceKlineInterval.MINUTE_1: KlineInterval.MINUTE_1,
|
|
423
|
+
BinanceKlineInterval.MINUTE_3: KlineInterval.MINUTE_3,
|
|
424
|
+
BinanceKlineInterval.MINUTE_5: KlineInterval.MINUTE_5,
|
|
425
|
+
BinanceKlineInterval.MINUTE_15: KlineInterval.MINUTE_15,
|
|
426
|
+
BinanceKlineInterval.MINUTE_30: KlineInterval.MINUTE_30,
|
|
427
|
+
BinanceKlineInterval.HOUR_1: KlineInterval.HOUR_1,
|
|
428
|
+
BinanceKlineInterval.HOUR_2: KlineInterval.HOUR_2,
|
|
429
|
+
BinanceKlineInterval.HOUR_4: KlineInterval.HOUR_4,
|
|
430
|
+
BinanceKlineInterval.HOUR_6: KlineInterval.HOUR_6,
|
|
431
|
+
BinanceKlineInterval.HOUR_8: KlineInterval.HOUR_8,
|
|
432
|
+
BinanceKlineInterval.HOUR_12: KlineInterval.HOUR_12,
|
|
433
|
+
BinanceKlineInterval.DAY_1: KlineInterval.DAY_1,
|
|
434
|
+
BinanceKlineInterval.DAY_3: KlineInterval.DAY_3,
|
|
435
|
+
BinanceKlineInterval.WEEK_1: KlineInterval.WEEK_1,
|
|
436
|
+
BinanceKlineInterval.MONTH_1: KlineInterval.MONTH_1,
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
_binance_order_status_map = {
|
|
440
|
+
BinanceOrderStatus.NEW: OrderStatus.ACCEPTED,
|
|
441
|
+
BinanceOrderStatus.PARTIALLY_FILLED: OrderStatus.PARTIALLY_FILLED,
|
|
442
|
+
BinanceOrderStatus.FILLED: OrderStatus.FILLED,
|
|
443
|
+
BinanceOrderStatus.CANCELED: OrderStatus.CANCELED,
|
|
444
|
+
BinanceOrderStatus.EXPIRED: OrderStatus.EXPIRED,
|
|
445
|
+
BinanceOrderStatus.EXPIRED_IN_MATCH: OrderStatus.EXPIRED,
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
_binance_position_side_map = {
|
|
449
|
+
BinancePositionSide.LONG: PositionSide.LONG,
|
|
450
|
+
BinancePositionSide.SHORT: PositionSide.SHORT,
|
|
451
|
+
BinancePositionSide.BOTH: PositionSide.FLAT,
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
_binance_order_side_map = {
|
|
455
|
+
BinanceOrderSide.BUY: OrderSide.BUY,
|
|
456
|
+
BinanceOrderSide.SELL: OrderSide.SELL,
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
_binance_order_time_in_force_map = {
|
|
460
|
+
BinanceTimeInForce.IOC: TimeInForce.IOC,
|
|
461
|
+
BinanceTimeInForce.GTC: TimeInForce.GTC,
|
|
462
|
+
BinanceTimeInForce.FOK: TimeInForce.FOK,
|
|
463
|
+
BinanceTimeInForce.GTX: TimeInForce.GTC, # FUTURES only
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
_binance_order_type_map = {
|
|
467
|
+
BinanceOrderType.LIMIT: OrderType.LIMIT,
|
|
468
|
+
BinanceOrderType.MARKET: OrderType.MARKET,
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
# ref1: https://developers.binance.com/docs/zh-CN/derivatives/usds-margined-futures/trade/rest-api
|
|
472
|
+
# ref2: https://developers.binance.com/docs/zh-CN/derivatives/coin-margined-futures/trade
|
|
473
|
+
_binance_futures_order_type_map = {
|
|
474
|
+
BinanceOrderType.LIMIT: OrderType.LIMIT,
|
|
475
|
+
BinanceOrderType.MARKET: OrderType.MARKET,
|
|
476
|
+
BinanceOrderType.STOP: OrderType.STOP_LOSS_LIMIT,
|
|
477
|
+
BinanceOrderType.TAKE_PROFIT: OrderType.TAKE_PROFIT_LIMIT,
|
|
478
|
+
BinanceOrderType.STOP_MARKET: OrderType.STOP_LOSS_MARKET,
|
|
479
|
+
BinanceOrderType.TAKE_PROFIT_MARKET: OrderType.TAKE_PROFIT_MARKET,
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
# ref: https://developers.binance.com/docs/zh-CN/binance-spot-api-docs/rest-api/trading-endpoints
|
|
483
|
+
_binance_spot_order_type_map = {
|
|
484
|
+
BinanceOrderType.LIMIT: OrderType.LIMIT,
|
|
485
|
+
BinanceOrderType.MARKET: OrderType.MARKET,
|
|
486
|
+
BinanceOrderType.STOP_LOSS: OrderType.STOP_LOSS_MARKET,
|
|
487
|
+
BinanceOrderType.STOP_LOSS_LIMIT: OrderType.STOP_LOSS_LIMIT,
|
|
488
|
+
BinanceOrderType.TAKE_PROFIT: OrderType.TAKE_PROFIT_MARKET,
|
|
489
|
+
BinanceOrderType.TAKE_PROFIT_LIMIT: OrderType.TAKE_PROFIT_LIMIT,
|
|
490
|
+
BinanceOrderType.LIMIT_MAKER: OrderType.POST_ONLY,
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
_order_status_to_binance_map = {v: k for k, v in _binance_order_status_map.items()}
|
|
494
|
+
_order_status_to_binance_map[OrderStatus.EXPIRED] = BinanceOrderStatus.EXPIRED
|
|
495
|
+
_position_side_to_binance_map = {
|
|
496
|
+
v: k for k, v in _binance_position_side_map.items()
|
|
497
|
+
}
|
|
498
|
+
_order_side_to_binance_map = {v: k for k, v in _binance_order_side_map.items()}
|
|
499
|
+
_time_in_force_to_binance_map = {
|
|
500
|
+
v: k for k, v in _binance_order_time_in_force_map.items()
|
|
501
|
+
}
|
|
502
|
+
_time_in_force_to_binance_map[TimeInForce.GTC] = BinanceTimeInForce.GTC
|
|
503
|
+
_order_type_to_binance_map = {v: k for k, v in _binance_order_type_map.items()}
|
|
504
|
+
_kline_interval_to_binance_map = {
|
|
505
|
+
v: k for k, v in _binance_kline_interval_map.items()
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
_futures_order_type_to_binance_map = {
|
|
509
|
+
v: k for k, v in _binance_futures_order_type_map.items()
|
|
510
|
+
}
|
|
511
|
+
_spot_order_type_to_binance_map = {
|
|
512
|
+
v: k for k, v in _binance_spot_order_type_map.items()
|
|
513
|
+
}
|
|
514
|
+
_trigger_type_to_binance_map = {v: k for k, v in _binance_trigger_type_map.items()}
|
|
515
|
+
|
|
516
|
+
@classmethod
|
|
517
|
+
def parse_kline_interval(cls, interval: BinanceKlineInterval) -> KlineInterval:
|
|
518
|
+
return cls._binance_kline_interval_map[interval]
|
|
519
|
+
|
|
520
|
+
@classmethod
|
|
521
|
+
def parse_order_status(cls, status: BinanceOrderStatus) -> OrderStatus:
|
|
522
|
+
return cls._binance_order_status_map[status]
|
|
523
|
+
|
|
524
|
+
@classmethod
|
|
525
|
+
def parse_futures_order_type(
|
|
526
|
+
cls,
|
|
527
|
+
order_type: BinanceOrderType,
|
|
528
|
+
time_in_force: BinanceTimeInForce | None = None,
|
|
529
|
+
) -> OrderType:
|
|
530
|
+
if time_in_force == BinanceTimeInForce.GTX:
|
|
531
|
+
# GTX is a special case for futures, it is a post-only order
|
|
532
|
+
return OrderType.POST_ONLY
|
|
533
|
+
return cls._binance_futures_order_type_map[order_type]
|
|
534
|
+
|
|
535
|
+
@classmethod
|
|
536
|
+
def parse_spot_order_type(cls, order_type: BinanceOrderType) -> OrderType:
|
|
537
|
+
return cls._binance_spot_order_type_map[order_type]
|
|
538
|
+
|
|
539
|
+
@classmethod
|
|
540
|
+
def parse_trigger_type(cls, trigger_type: BinanceTriggerType) -> TriggerType:
|
|
541
|
+
return cls._binance_trigger_type_map[trigger_type]
|
|
542
|
+
|
|
543
|
+
@classmethod
|
|
544
|
+
def parse_position_side(cls, side: BinancePositionSide) -> PositionSide:
|
|
545
|
+
return cls._binance_position_side_map[side]
|
|
546
|
+
|
|
547
|
+
@classmethod
|
|
548
|
+
def parse_order_side(cls, side: BinanceOrderSide) -> OrderSide:
|
|
549
|
+
return cls._binance_order_side_map[side]
|
|
550
|
+
|
|
551
|
+
@classmethod
|
|
552
|
+
def parse_time_in_force(cls, tif: BinanceTimeInForce) -> TimeInForce:
|
|
553
|
+
return cls._binance_order_time_in_force_map[tif]
|
|
554
|
+
|
|
555
|
+
@classmethod
|
|
556
|
+
def parse_order_type(cls, order_type: BinanceOrderType) -> OrderType:
|
|
557
|
+
return cls._binance_order_type_map[order_type]
|
|
558
|
+
|
|
559
|
+
@classmethod
|
|
560
|
+
def to_binance_order_status(cls, status: OrderStatus) -> BinanceOrderStatus:
|
|
561
|
+
return cls._order_status_to_binance_map[status]
|
|
562
|
+
|
|
563
|
+
@classmethod
|
|
564
|
+
def to_binance_position_side(cls, side: PositionSide) -> BinancePositionSide:
|
|
565
|
+
return cls._position_side_to_binance_map[side]
|
|
566
|
+
|
|
567
|
+
@classmethod
|
|
568
|
+
def to_binance_order_side(cls, side: OrderSide) -> BinanceOrderSide:
|
|
569
|
+
return cls._order_side_to_binance_map[side]
|
|
570
|
+
|
|
571
|
+
@classmethod
|
|
572
|
+
def to_binance_time_in_force(cls, tif: TimeInForce) -> BinanceTimeInForce:
|
|
573
|
+
return cls._time_in_force_to_binance_map[tif]
|
|
574
|
+
|
|
575
|
+
@classmethod
|
|
576
|
+
def to_binance_order_type(cls, order_type: OrderType) -> BinanceOrderType:
|
|
577
|
+
return cls._order_type_to_binance_map[order_type]
|
|
578
|
+
|
|
579
|
+
@classmethod
|
|
580
|
+
def to_binance_futures_order_type(cls, order_type: OrderType) -> BinanceOrderType:
|
|
581
|
+
return cls._futures_order_type_to_binance_map[order_type]
|
|
582
|
+
|
|
583
|
+
@classmethod
|
|
584
|
+
def to_binance_spot_order_type(cls, order_type: OrderType) -> BinanceOrderType:
|
|
585
|
+
return cls._spot_order_type_to_binance_map[order_type]
|
|
586
|
+
|
|
587
|
+
@classmethod
|
|
588
|
+
def to_binance_trigger_type(cls, trigger_type: TriggerType) -> BinanceTriggerType:
|
|
589
|
+
return cls._trigger_type_to_binance_map[trigger_type]
|
|
590
|
+
|
|
591
|
+
@classmethod
|
|
592
|
+
def to_binance_kline_interval(cls, interval: KlineInterval) -> BinanceKlineInterval:
|
|
593
|
+
if interval not in cls._kline_interval_to_binance_map:
|
|
594
|
+
raise KlineSupportedError(
|
|
595
|
+
f"Kline interval {interval} is not supported by Binance"
|
|
596
|
+
)
|
|
597
|
+
return cls._kline_interval_to_binance_map[interval]
|
|
598
|
+
|
|
599
|
+
|
|
600
|
+
class BinanceRateLimitType(Enum):
|
|
601
|
+
ORDERS = "ORDERS"
|
|
602
|
+
REQUEST_WEIGHT = "REQUEST_WEIGHT"
|
|
603
|
+
RAW_REQUESTS = "RAW_REQUESTS"
|
|
604
|
+
|
|
605
|
+
|
|
606
|
+
class BinanceRateLimiter(RateLimiter):
|
|
607
|
+
# /api/v3 rate limits
|
|
608
|
+
# [
|
|
609
|
+
# {
|
|
610
|
+
# "rateLimitType": "REQUEST_WEIGHT",
|
|
611
|
+
# "interval": "MINUTE",
|
|
612
|
+
# "intervalNum": 1,
|
|
613
|
+
# "limit": 6000,
|
|
614
|
+
# },
|
|
615
|
+
# {
|
|
616
|
+
# "rateLimitType": "ORDERS",
|
|
617
|
+
# "interval": "SECOND",
|
|
618
|
+
# "intervalNum": 10,
|
|
619
|
+
# "limit": 100,
|
|
620
|
+
# },
|
|
621
|
+
# {
|
|
622
|
+
# "rateLimitType": "ORDERS",
|
|
623
|
+
# "interval": "DAY",
|
|
624
|
+
# "intervalNum": 1,
|
|
625
|
+
# "limit": 200000,
|
|
626
|
+
# },
|
|
627
|
+
# {
|
|
628
|
+
# "rateLimitType": "RAW_REQUESTS",
|
|
629
|
+
# "interval": "MINUTE",
|
|
630
|
+
# "intervalNum": 5,
|
|
631
|
+
# "limit": 61000,
|
|
632
|
+
# },
|
|
633
|
+
# ]
|
|
634
|
+
|
|
635
|
+
# /fapi/v1 rate limits
|
|
636
|
+
# [
|
|
637
|
+
# {
|
|
638
|
+
# "rateLimitType": "REQUEST_WEIGHT",
|
|
639
|
+
# "interval": "MINUTE",
|
|
640
|
+
# "intervalNum": 1,
|
|
641
|
+
# "limit": 2400,
|
|
642
|
+
# },
|
|
643
|
+
# {
|
|
644
|
+
# "rateLimitType": "ORDERS",
|
|
645
|
+
# "interval": "MINUTE",
|
|
646
|
+
# "intervalNum": 1,
|
|
647
|
+
# "limit": 1200,
|
|
648
|
+
# },
|
|
649
|
+
# {
|
|
650
|
+
# "rateLimitType": "ORDERS",
|
|
651
|
+
# "interval": "SECOND",
|
|
652
|
+
# "intervalNum": 10,
|
|
653
|
+
# "limit": 300,
|
|
654
|
+
# },
|
|
655
|
+
# ]
|
|
656
|
+
|
|
657
|
+
# [
|
|
658
|
+
# {
|
|
659
|
+
# "rateLimitType": "REQUEST_WEIGHT",
|
|
660
|
+
# "interval": "MINUTE",
|
|
661
|
+
# "intervalNum": 1,
|
|
662
|
+
# "limit": 2400,
|
|
663
|
+
# },
|
|
664
|
+
# {
|
|
665
|
+
# "rateLimitType": "ORDERS",
|
|
666
|
+
# "interval": "MINUTE",
|
|
667
|
+
# "intervalNum": 1,
|
|
668
|
+
# "limit": 1200,
|
|
669
|
+
# },
|
|
670
|
+
# ]
|
|
671
|
+
|
|
672
|
+
_BLOCKING_WEIGHT_TIMEOUT = 60
|
|
673
|
+
_BLOCKING_RAW_REQUEST_TIMEOUT = 300
|
|
674
|
+
_NON_BLOCKING_TIMEOUT = -1
|
|
675
|
+
|
|
676
|
+
def __init__(self, enable_rate_limit: bool = True):
|
|
677
|
+
self._enabled = enable_rate_limit
|
|
678
|
+
self._api_weight_limit = Throttled(
|
|
679
|
+
quota=rate_limiter.per_min(6000),
|
|
680
|
+
timeout=self._NON_BLOCKING_TIMEOUT,
|
|
681
|
+
using=RateLimiterType.FIXED_WINDOW.value,
|
|
682
|
+
)
|
|
683
|
+
self._api_raw_req_limit = Throttled(
|
|
684
|
+
quota=rate_limiter.per_duration(timedelta(minutes=5), limit=61000),
|
|
685
|
+
timeout=self._NON_BLOCKING_TIMEOUT,
|
|
686
|
+
using=RateLimiterType.FIXED_WINDOW.value,
|
|
687
|
+
)
|
|
688
|
+
self._api_order_sec_limit = Throttled(
|
|
689
|
+
quota=rate_limiter.per_duration(timedelta(seconds=10), limit=100),
|
|
690
|
+
timeout=self._NON_BLOCKING_TIMEOUT,
|
|
691
|
+
using=RateLimiterType.FIXED_WINDOW.value,
|
|
692
|
+
)
|
|
693
|
+
self._api_order_day_limit = Throttled(
|
|
694
|
+
quota=rate_limiter.per_day(200000),
|
|
695
|
+
timeout=self._NON_BLOCKING_TIMEOUT,
|
|
696
|
+
using=RateLimiterType.FIXED_WINDOW.value,
|
|
697
|
+
)
|
|
698
|
+
|
|
699
|
+
self._fapi_weight_limit = Throttled(
|
|
700
|
+
quota=rate_limiter.per_min(2400),
|
|
701
|
+
timeout=self._NON_BLOCKING_TIMEOUT,
|
|
702
|
+
using=RateLimiterType.FIXED_WINDOW.value,
|
|
703
|
+
)
|
|
704
|
+
self._fapi_order_sec_limit = Throttled(
|
|
705
|
+
quota=rate_limiter.per_duration(timedelta(seconds=10), limit=300),
|
|
706
|
+
timeout=self._NON_BLOCKING_TIMEOUT,
|
|
707
|
+
using=RateLimiterType.FIXED_WINDOW.value,
|
|
708
|
+
)
|
|
709
|
+
self._fapi_order_min_limit = Throttled(
|
|
710
|
+
quota=rate_limiter.per_min(1200),
|
|
711
|
+
timeout=self._NON_BLOCKING_TIMEOUT,
|
|
712
|
+
using=RateLimiterType.FIXED_WINDOW.value,
|
|
713
|
+
)
|
|
714
|
+
self._dapi_weight_limit = Throttled(
|
|
715
|
+
quota=rate_limiter.per_min(2400),
|
|
716
|
+
timeout=self._NON_BLOCKING_TIMEOUT,
|
|
717
|
+
using=RateLimiterType.FIXED_WINDOW.value,
|
|
718
|
+
)
|
|
719
|
+
self._dapi_order_min_limit = Throttled(
|
|
720
|
+
quota=rate_limiter.per_min(1200),
|
|
721
|
+
timeout=self._NON_BLOCKING_TIMEOUT,
|
|
722
|
+
using=RateLimiterType.FIXED_WINDOW.value,
|
|
723
|
+
)
|
|
724
|
+
|
|
725
|
+
self._papi_weight_limit = Throttled(
|
|
726
|
+
quota=rate_limiter.per_min(6000),
|
|
727
|
+
timeout=self._NON_BLOCKING_TIMEOUT,
|
|
728
|
+
using=RateLimiterType.FIXED_WINDOW.value,
|
|
729
|
+
)
|
|
730
|
+
self._papi_order_min_limit = Throttled(
|
|
731
|
+
quota=rate_limiter.per_min(1200),
|
|
732
|
+
timeout=self._NON_BLOCKING_TIMEOUT,
|
|
733
|
+
using=RateLimiterType.FIXED_WINDOW.value,
|
|
734
|
+
)
|
|
735
|
+
|
|
736
|
+
@staticmethod
|
|
737
|
+
def _raise_if_limited(
|
|
738
|
+
result: RateLimitResult, message: str, api_type: str, rate_limit_type: str
|
|
739
|
+
):
|
|
740
|
+
if result.limited:
|
|
741
|
+
raise BinanceRateLimitError(
|
|
742
|
+
message,
|
|
743
|
+
retry_after=result.state.retry_after,
|
|
744
|
+
api_type=api_type,
|
|
745
|
+
rate_limit_type=rate_limit_type,
|
|
746
|
+
)
|
|
747
|
+
|
|
748
|
+
async def api_weight_limit(self, cost: int):
|
|
749
|
+
if not self._enabled:
|
|
750
|
+
return
|
|
751
|
+
result = await self._api_weight_limit.limit(
|
|
752
|
+
key="/api",
|
|
753
|
+
cost=cost,
|
|
754
|
+
timeout=self._BLOCKING_WEIGHT_TIMEOUT,
|
|
755
|
+
)
|
|
756
|
+
self._raise_if_limited(
|
|
757
|
+
result, "SPOT API weight limit exceeded", "api", "weight"
|
|
758
|
+
)
|
|
759
|
+
result = await self._api_raw_req_limit.limit(
|
|
760
|
+
key="/api",
|
|
761
|
+
cost=1,
|
|
762
|
+
timeout=self._BLOCKING_RAW_REQUEST_TIMEOUT,
|
|
763
|
+
)
|
|
764
|
+
self._raise_if_limited(
|
|
765
|
+
result,
|
|
766
|
+
"SPOT API raw request limit exceeded",
|
|
767
|
+
"api",
|
|
768
|
+
"raw_requests",
|
|
769
|
+
)
|
|
770
|
+
|
|
771
|
+
async def api_order_limit(
|
|
772
|
+
self,
|
|
773
|
+
cost: int,
|
|
774
|
+
order_sec_cost: int = 1,
|
|
775
|
+
order_day_cost: int = 1,
|
|
776
|
+
):
|
|
777
|
+
if not self._enabled:
|
|
778
|
+
return
|
|
779
|
+
result = await self._api_order_sec_limit.limit(
|
|
780
|
+
key="/api",
|
|
781
|
+
cost=order_sec_cost,
|
|
782
|
+
timeout=self._NON_BLOCKING_TIMEOUT,
|
|
783
|
+
)
|
|
784
|
+
self._raise_if_limited(
|
|
785
|
+
result, "SPOT API order limit (10s) exceeded", "api", "orders"
|
|
786
|
+
)
|
|
787
|
+
result = await self._api_order_day_limit.limit(
|
|
788
|
+
key="/api",
|
|
789
|
+
cost=order_day_cost,
|
|
790
|
+
timeout=self._NON_BLOCKING_TIMEOUT,
|
|
791
|
+
)
|
|
792
|
+
self._raise_if_limited(
|
|
793
|
+
result, "SPOT API order limit (day) exceeded", "api", "orders"
|
|
794
|
+
)
|
|
795
|
+
result = await self._api_weight_limit.limit(
|
|
796
|
+
key="/api",
|
|
797
|
+
cost=cost,
|
|
798
|
+
timeout=self._NON_BLOCKING_TIMEOUT,
|
|
799
|
+
)
|
|
800
|
+
self._raise_if_limited(
|
|
801
|
+
result,
|
|
802
|
+
"SPOT API weight limit exceeded (order)",
|
|
803
|
+
"api",
|
|
804
|
+
"weight",
|
|
805
|
+
)
|
|
806
|
+
result = await self._api_raw_req_limit.limit(
|
|
807
|
+
key="/api",
|
|
808
|
+
cost=1,
|
|
809
|
+
timeout=self._NON_BLOCKING_TIMEOUT,
|
|
810
|
+
)
|
|
811
|
+
self._raise_if_limited(
|
|
812
|
+
result,
|
|
813
|
+
"SPOT API raw request limit exceeded (order)",
|
|
814
|
+
"api",
|
|
815
|
+
"raw_requests",
|
|
816
|
+
)
|
|
817
|
+
|
|
818
|
+
async def fapi_weight_limit(self, cost: int):
|
|
819
|
+
if not self._enabled:
|
|
820
|
+
return
|
|
821
|
+
result = await self._fapi_weight_limit.limit(
|
|
822
|
+
key="/fapi",
|
|
823
|
+
cost=cost,
|
|
824
|
+
timeout=self._BLOCKING_WEIGHT_TIMEOUT,
|
|
825
|
+
)
|
|
826
|
+
self._raise_if_limited(
|
|
827
|
+
result, "USD-M Futures weight limit exceeded", "fapi", "weight"
|
|
828
|
+
)
|
|
829
|
+
|
|
830
|
+
async def fapi_order_limit(
|
|
831
|
+
self,
|
|
832
|
+
cost: int = 1,
|
|
833
|
+
order_sec_cost: int = 1,
|
|
834
|
+
order_min_cost: int = 1,
|
|
835
|
+
):
|
|
836
|
+
if not self._enabled:
|
|
837
|
+
return
|
|
838
|
+
result = await self._fapi_order_sec_limit.limit(
|
|
839
|
+
key="/fapi",
|
|
840
|
+
cost=order_sec_cost,
|
|
841
|
+
timeout=self._NON_BLOCKING_TIMEOUT,
|
|
842
|
+
)
|
|
843
|
+
self._raise_if_limited(
|
|
844
|
+
result, "USD-M Futures order limit (10s) exceeded", "fapi", "orders"
|
|
845
|
+
)
|
|
846
|
+
result = await self._fapi_order_min_limit.limit(
|
|
847
|
+
key="/fapi",
|
|
848
|
+
cost=order_min_cost,
|
|
849
|
+
timeout=self._NON_BLOCKING_TIMEOUT,
|
|
850
|
+
)
|
|
851
|
+
self._raise_if_limited(
|
|
852
|
+
result, "USD-M Futures order limit (1m) exceeded", "fapi", "orders"
|
|
853
|
+
)
|
|
854
|
+
result = await self._fapi_weight_limit.limit(
|
|
855
|
+
key="/fapi",
|
|
856
|
+
cost=cost,
|
|
857
|
+
timeout=self._NON_BLOCKING_TIMEOUT,
|
|
858
|
+
)
|
|
859
|
+
self._raise_if_limited(
|
|
860
|
+
result,
|
|
861
|
+
"USD-M Futures weight limit exceeded (order)",
|
|
862
|
+
"fapi",
|
|
863
|
+
"weight",
|
|
864
|
+
)
|
|
865
|
+
|
|
866
|
+
async def dapi_weight_limit(self, cost: int):
|
|
867
|
+
if not self._enabled:
|
|
868
|
+
return
|
|
869
|
+
result = await self._dapi_weight_limit.limit(
|
|
870
|
+
key="/dapi",
|
|
871
|
+
cost=cost,
|
|
872
|
+
timeout=self._BLOCKING_WEIGHT_TIMEOUT,
|
|
873
|
+
)
|
|
874
|
+
self._raise_if_limited(
|
|
875
|
+
result, "COIN-M Futures weight limit exceeded", "dapi", "weight"
|
|
876
|
+
)
|
|
877
|
+
|
|
878
|
+
async def dapi_order_limit(self, cost: int = 1, order_min_cost: int = 1):
|
|
879
|
+
if not self._enabled:
|
|
880
|
+
return
|
|
881
|
+
result = await self._dapi_order_min_limit.limit(
|
|
882
|
+
key="/dapi",
|
|
883
|
+
cost=order_min_cost,
|
|
884
|
+
timeout=self._NON_BLOCKING_TIMEOUT,
|
|
885
|
+
)
|
|
886
|
+
self._raise_if_limited(
|
|
887
|
+
result, "COIN-M Futures order limit (1m) exceeded", "dapi", "orders"
|
|
888
|
+
)
|
|
889
|
+
result = await self._dapi_weight_limit.limit(
|
|
890
|
+
key="/dapi",
|
|
891
|
+
cost=cost,
|
|
892
|
+
timeout=self._NON_BLOCKING_TIMEOUT,
|
|
893
|
+
)
|
|
894
|
+
self._raise_if_limited(
|
|
895
|
+
result,
|
|
896
|
+
"COIN-M Futures weight limit exceeded (order)",
|
|
897
|
+
"dapi",
|
|
898
|
+
"weight",
|
|
899
|
+
)
|
|
900
|
+
|
|
901
|
+
async def papi_weight_limit(self, cost: int):
|
|
902
|
+
if not self._enabled:
|
|
903
|
+
return
|
|
904
|
+
result = await self._papi_weight_limit.limit(
|
|
905
|
+
key="/papi",
|
|
906
|
+
cost=cost,
|
|
907
|
+
timeout=self._BLOCKING_WEIGHT_TIMEOUT,
|
|
908
|
+
)
|
|
909
|
+
self._raise_if_limited(
|
|
910
|
+
result, "Portfolio Margin weight limit exceeded", "papi", "weight"
|
|
911
|
+
)
|
|
912
|
+
|
|
913
|
+
async def papi_order_limit(self, cost: int = 1, order_min_cost: int = 1):
|
|
914
|
+
if not self._enabled:
|
|
915
|
+
return
|
|
916
|
+
result = await self._papi_order_min_limit.limit(
|
|
917
|
+
key="/papi",
|
|
918
|
+
cost=order_min_cost,
|
|
919
|
+
timeout=self._NON_BLOCKING_TIMEOUT,
|
|
920
|
+
)
|
|
921
|
+
self._raise_if_limited(
|
|
922
|
+
result, "Portfolio Margin order limit (1m) exceeded", "papi", "orders"
|
|
923
|
+
)
|
|
924
|
+
result = await self._papi_weight_limit.limit(
|
|
925
|
+
key="/papi",
|
|
926
|
+
cost=cost,
|
|
927
|
+
timeout=self._NON_BLOCKING_TIMEOUT,
|
|
928
|
+
)
|
|
929
|
+
self._raise_if_limited(
|
|
930
|
+
result,
|
|
931
|
+
"Portfolio Margin weight limit exceeded (order)",
|
|
932
|
+
"papi",
|
|
933
|
+
"weight",
|
|
934
|
+
)
|