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,140 @@
|
|
|
1
|
+
from decimal import Decimal
|
|
2
|
+
from typing import Dict, cast
|
|
3
|
+
from walrasquant.constants import AccountType
|
|
4
|
+
from walrasquant.schema import InstrumentId, BaseMarket
|
|
5
|
+
from walrasquant.core.cache import AsyncCache
|
|
6
|
+
from walrasquant.core.nautilius_core import MessageBus, LiveClock
|
|
7
|
+
from walrasquant.core.entity import TaskManager
|
|
8
|
+
from walrasquant.core.registry import OrderRegistry
|
|
9
|
+
from walrasquant.exchange.binance import BinanceAccountType
|
|
10
|
+
from walrasquant.exchange.binance.schema import BinanceMarket
|
|
11
|
+
from walrasquant.base import ExecutionManagementSystem
|
|
12
|
+
from walrasquant.base.ems import PriorityOrderQueue
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class BinanceExecutionManagementSystem(ExecutionManagementSystem):
|
|
16
|
+
_market: Dict[str, BinanceMarket]
|
|
17
|
+
_BATCH_CHUNK_SIZE = 5
|
|
18
|
+
|
|
19
|
+
BINANCE_SPOT_PRIORITY = [
|
|
20
|
+
BinanceAccountType.ISOLATED_MARGIN,
|
|
21
|
+
BinanceAccountType.MARGIN,
|
|
22
|
+
BinanceAccountType.SPOT_TESTNET,
|
|
23
|
+
BinanceAccountType.SPOT,
|
|
24
|
+
]
|
|
25
|
+
|
|
26
|
+
def __init__(
|
|
27
|
+
self,
|
|
28
|
+
market: Dict[str, BinanceMarket],
|
|
29
|
+
cache: AsyncCache,
|
|
30
|
+
msgbus: MessageBus,
|
|
31
|
+
clock: LiveClock,
|
|
32
|
+
task_manager: TaskManager,
|
|
33
|
+
registry: OrderRegistry,
|
|
34
|
+
queue_maxsize: int = 100_000,
|
|
35
|
+
):
|
|
36
|
+
super().__init__(
|
|
37
|
+
market=market,
|
|
38
|
+
cache=cache,
|
|
39
|
+
msgbus=msgbus,
|
|
40
|
+
clock=clock,
|
|
41
|
+
task_manager=task_manager,
|
|
42
|
+
registry=registry,
|
|
43
|
+
queue_maxsize=queue_maxsize,
|
|
44
|
+
)
|
|
45
|
+
self._binance_spot_account_type: BinanceAccountType | None = None
|
|
46
|
+
self._binance_linear_account_type: BinanceAccountType | None = None
|
|
47
|
+
self._binance_inverse_account_type: BinanceAccountType | None = None
|
|
48
|
+
self._binance_pm_account_type: BinanceAccountType | None = None
|
|
49
|
+
|
|
50
|
+
def _set_account_type(self):
|
|
51
|
+
account_types = self._private_connectors.keys()
|
|
52
|
+
|
|
53
|
+
if BinanceAccountType.PORTFOLIO_MARGIN in account_types:
|
|
54
|
+
self._binance_pm_account_type = BinanceAccountType.PORTFOLIO_MARGIN
|
|
55
|
+
return
|
|
56
|
+
|
|
57
|
+
for account_type in self.BINANCE_SPOT_PRIORITY:
|
|
58
|
+
if account_type in account_types:
|
|
59
|
+
self._binance_spot_account_type = account_type
|
|
60
|
+
break
|
|
61
|
+
|
|
62
|
+
self._binance_linear_account_type = (
|
|
63
|
+
BinanceAccountType.USD_M_FUTURE_TESTNET
|
|
64
|
+
if BinanceAccountType.USD_M_FUTURE_TESTNET in account_types
|
|
65
|
+
else BinanceAccountType.USD_M_FUTURE
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
self._binance_inverse_account_type = (
|
|
69
|
+
BinanceAccountType.COIN_M_FUTURE_TESTNET
|
|
70
|
+
if BinanceAccountType.COIN_M_FUTURE_TESTNET in account_types
|
|
71
|
+
else BinanceAccountType.COIN_M_FUTURE
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
def _instrument_id_to_account_type(
|
|
75
|
+
self, instrument_id: InstrumentId
|
|
76
|
+
) -> AccountType:
|
|
77
|
+
if self._binance_pm_account_type:
|
|
78
|
+
return self._binance_pm_account_type
|
|
79
|
+
|
|
80
|
+
if instrument_id.is_spot:
|
|
81
|
+
account_type = self._binance_spot_account_type
|
|
82
|
+
elif instrument_id.is_linear:
|
|
83
|
+
account_type = self._binance_linear_account_type
|
|
84
|
+
elif instrument_id.is_inverse:
|
|
85
|
+
account_type = self._binance_inverse_account_type
|
|
86
|
+
else:
|
|
87
|
+
raise ValueError(f"Unsupported instrument type: {instrument_id.type}")
|
|
88
|
+
|
|
89
|
+
if account_type is None:
|
|
90
|
+
raise ValueError(f"No Binance account type configured for {instrument_id}")
|
|
91
|
+
return account_type
|
|
92
|
+
|
|
93
|
+
def _build_order_submit_queues(self):
|
|
94
|
+
for account_type in self._private_connectors.keys():
|
|
95
|
+
if isinstance(account_type, BinanceAccountType):
|
|
96
|
+
self._order_submit_queues[account_type] = PriorityOrderQueue(
|
|
97
|
+
maxsize=self._queue_maxsize
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
def _get_min_order_amount(
|
|
101
|
+
self, symbol: str, market: BaseMarket, px: float
|
|
102
|
+
) -> Decimal:
|
|
103
|
+
binance_market = cast(BinanceMarket, market)
|
|
104
|
+
# book = self._cache.bookl1(symbol)
|
|
105
|
+
cost_limits = binance_market.limits.cost
|
|
106
|
+
amount_limits = binance_market.limits.amount
|
|
107
|
+
cost_min = cost_limits.min if cost_limits is not None else 0.0
|
|
108
|
+
amount_min = amount_limits.min if amount_limits is not None else 0.0
|
|
109
|
+
cost_min = cost_min or 0.0
|
|
110
|
+
amount_min = amount_min or 0.0
|
|
111
|
+
|
|
112
|
+
min_order_amount = max(cost_min * 1.01 / px, amount_min)
|
|
113
|
+
min_order_amount = self._amount_to_precision(
|
|
114
|
+
symbol, min_order_amount, mode="ceil"
|
|
115
|
+
)
|
|
116
|
+
return min_order_amount
|
|
117
|
+
|
|
118
|
+
def _get_max_order_amount(
|
|
119
|
+
self, symbol: str, market: BaseMarket, is_market: bool, px: float
|
|
120
|
+
) -> Decimal:
|
|
121
|
+
binance_market = cast(BinanceMarket, market)
|
|
122
|
+
cost = binance_market.limits.cost.max
|
|
123
|
+
_cost = (
|
|
124
|
+
self._amount_to_precision(symbol, cost / px, mode="floor")
|
|
125
|
+
if cost
|
|
126
|
+
else Decimal("Infinity")
|
|
127
|
+
)
|
|
128
|
+
amount = (
|
|
129
|
+
Decimal(str(binance_market.limits.amount.max))
|
|
130
|
+
if binance_market.limits.amount.max
|
|
131
|
+
else Decimal("Infinity")
|
|
132
|
+
)
|
|
133
|
+
if is_market:
|
|
134
|
+
mkt_max = (
|
|
135
|
+
Decimal(str(binance_market.limits.market.max))
|
|
136
|
+
if binance_market.limits.market and binance_market.limits.market.max
|
|
137
|
+
else Decimal("Infinity")
|
|
138
|
+
)
|
|
139
|
+
return min(_cost, amount, mkt_max)
|
|
140
|
+
return min(_cost, amount)
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
class BinanceClientError(Exception):
|
|
2
|
+
"""
|
|
3
|
+
The base class for all Binance specific errors.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
def __init__(self, code: int, message: str):
|
|
7
|
+
super().__init__(message)
|
|
8
|
+
self.code = code
|
|
9
|
+
self.message = message
|
|
10
|
+
|
|
11
|
+
def __repr__(self) -> str:
|
|
12
|
+
return f"{type(self).__name__}(code={self.code}, message='{self.message}')"
|
|
13
|
+
|
|
14
|
+
__str__ = __repr__
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class BinanceServerError(Exception):
|
|
18
|
+
"""
|
|
19
|
+
The base class for all Binance specific errors.
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
def __init__(self, code: int, message: str):
|
|
23
|
+
super().__init__(message)
|
|
24
|
+
self.code = code
|
|
25
|
+
self.message = message
|
|
26
|
+
|
|
27
|
+
def __repr__(self) -> str:
|
|
28
|
+
return f"{type(self).__name__}(code={self.code}, message='{self.message}')"
|
|
29
|
+
|
|
30
|
+
__str__ = __repr__
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class BinanceRateLimitError(Exception):
|
|
34
|
+
"""
|
|
35
|
+
Raised when Binance API rate limit is exceeded.
|
|
36
|
+
"""
|
|
37
|
+
|
|
38
|
+
def __init__(
|
|
39
|
+
self,
|
|
40
|
+
message: str,
|
|
41
|
+
retry_after: float = 0.0,
|
|
42
|
+
api_type: str | None = None,
|
|
43
|
+
rate_limit_type: str | None = None,
|
|
44
|
+
):
|
|
45
|
+
super().__init__(message)
|
|
46
|
+
self.retry_after = retry_after
|
|
47
|
+
self.api_type = api_type
|
|
48
|
+
self.rate_limit_type = rate_limit_type
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import ccxt
|
|
2
|
+
import msgspec
|
|
3
|
+
from typing import Any, Dict, List
|
|
4
|
+
from walrasquant.base import ExchangeManager
|
|
5
|
+
from walrasquant.exchange.binance.schema import BinanceMarket
|
|
6
|
+
from walrasquant.exchange.binance.constants import BinanceAccountType
|
|
7
|
+
from walrasquant.schema import InstrumentId
|
|
8
|
+
from walrasquant.constants import AccountType, ConfigType
|
|
9
|
+
from walrasquant.error import EngineBuildError
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class BinanceExchangeManager(ExchangeManager):
|
|
13
|
+
api: ccxt.binance
|
|
14
|
+
market: Dict[str, BinanceMarket]
|
|
15
|
+
market_id: Dict[str, str]
|
|
16
|
+
|
|
17
|
+
def __init__(self, config: ConfigType | None = None):
|
|
18
|
+
resolved_config: ConfigType = {
|
|
19
|
+
"apiKey": config.get("apiKey", "") if config else "",
|
|
20
|
+
"secret": config.get("secret", "") if config else "",
|
|
21
|
+
"exchange_id": config.get("exchange_id", "binance")
|
|
22
|
+
if config
|
|
23
|
+
else "binance",
|
|
24
|
+
"sandbox": config.get("sandbox", False) if config else False,
|
|
25
|
+
}
|
|
26
|
+
if config and "password" in config:
|
|
27
|
+
resolved_config["password"] = config["password"]
|
|
28
|
+
super().__init__(resolved_config)
|
|
29
|
+
|
|
30
|
+
def load_markets(self, reload: bool = False) -> None:
|
|
31
|
+
market = self.api.load_markets(reload=reload)
|
|
32
|
+
for symbol, mkt in market.items():
|
|
33
|
+
try:
|
|
34
|
+
mkt_json = msgspec.json.encode(mkt)
|
|
35
|
+
mkt = msgspec.json.decode(mkt_json, type=BinanceMarket)
|
|
36
|
+
|
|
37
|
+
if (
|
|
38
|
+
mkt.spot or mkt.linear or mkt.inverse or mkt.future
|
|
39
|
+
) and not mkt.option:
|
|
40
|
+
symbol = self._parse_symbol(mkt, exchange_suffix="BINANCE")
|
|
41
|
+
mkt.symbol = symbol
|
|
42
|
+
self.market[symbol] = mkt
|
|
43
|
+
if mkt.type.value == "spot":
|
|
44
|
+
self.market_id[f"{mkt.id}_spot"] = symbol
|
|
45
|
+
elif mkt.linear:
|
|
46
|
+
self.market_id[f"{mkt.id}_linear"] = symbol
|
|
47
|
+
elif mkt.inverse:
|
|
48
|
+
self.market_id[f"{mkt.id}_inverse"] = symbol
|
|
49
|
+
|
|
50
|
+
except msgspec.ValidationError as ve:
|
|
51
|
+
self._log.warning(f"Symbol Format Error: {ve}, {symbol}, {mkt}")
|
|
52
|
+
continue
|
|
53
|
+
|
|
54
|
+
def option(
|
|
55
|
+
self,
|
|
56
|
+
base: str | None = None,
|
|
57
|
+
quote: str | None = None,
|
|
58
|
+
exclude: List[str] | None = None,
|
|
59
|
+
) -> List[str]:
|
|
60
|
+
raise NotImplementedError("Option is not supported for Binance")
|
|
61
|
+
|
|
62
|
+
def validate_public_connector_config(
|
|
63
|
+
self, account_type: AccountType, basic_config: Any
|
|
64
|
+
) -> None:
|
|
65
|
+
"""Validate public connector configuration for Binance exchange"""
|
|
66
|
+
if not isinstance(account_type, BinanceAccountType):
|
|
67
|
+
raise EngineBuildError(
|
|
68
|
+
f"Expected BinanceAccountType, got {type(account_type)}"
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
if (
|
|
72
|
+
account_type.is_isolated_margin_or_margin
|
|
73
|
+
or account_type.is_portfolio_margin
|
|
74
|
+
):
|
|
75
|
+
raise EngineBuildError(
|
|
76
|
+
f"{account_type} is not supported for public connector."
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
if basic_config.testnet != account_type.is_testnet:
|
|
80
|
+
raise EngineBuildError(
|
|
81
|
+
f"The `testnet` setting of Binance is not consistent with the public connector's account type `{account_type}`."
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
def validate_public_connector_limits(
|
|
85
|
+
self, existing_connectors: Dict[AccountType, Any]
|
|
86
|
+
) -> None:
|
|
87
|
+
"""Validate public connector limits for Binance exchange"""
|
|
88
|
+
# Binance has no specific connector limits
|
|
89
|
+
pass
|
|
90
|
+
|
|
91
|
+
def instrument_id_to_account_type(self, instrument_id: InstrumentId) -> AccountType:
|
|
92
|
+
"""Convert an instrument ID to the appropriate account type for Binance exchange"""
|
|
93
|
+
if instrument_id.is_spot:
|
|
94
|
+
return (
|
|
95
|
+
BinanceAccountType.SPOT_TESTNET
|
|
96
|
+
if self.is_testnet
|
|
97
|
+
else BinanceAccountType.SPOT
|
|
98
|
+
)
|
|
99
|
+
elif instrument_id.is_linear:
|
|
100
|
+
return (
|
|
101
|
+
BinanceAccountType.USD_M_FUTURE_TESTNET
|
|
102
|
+
if self.is_testnet
|
|
103
|
+
else BinanceAccountType.USD_M_FUTURE
|
|
104
|
+
)
|
|
105
|
+
elif instrument_id.is_inverse:
|
|
106
|
+
return (
|
|
107
|
+
BinanceAccountType.COIN_M_FUTURE_TESTNET
|
|
108
|
+
if self.is_testnet
|
|
109
|
+
else BinanceAccountType.COIN_M_FUTURE
|
|
110
|
+
)
|
|
111
|
+
else:
|
|
112
|
+
raise ValueError(f"Unsupported instrument type: {instrument_id.type}")
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def check():
|
|
116
|
+
bnc = BinanceExchangeManager()
|
|
117
|
+
market = bnc.market
|
|
118
|
+
market_id = bnc.market_id # noqa: F841
|
|
119
|
+
|
|
120
|
+
for symbol, mkt in market.items():
|
|
121
|
+
instrument_id = InstrumentId.from_str(symbol)
|
|
122
|
+
if mkt.subType:
|
|
123
|
+
assert instrument_id.type == mkt.subType
|
|
124
|
+
else:
|
|
125
|
+
assert instrument_id.type == mkt.type
|
|
126
|
+
|
|
127
|
+
if mkt.limits.market is None:
|
|
128
|
+
print(f"Market limits is None for {symbol} ({mkt.type})")
|
|
129
|
+
|
|
130
|
+
assert mkt.limits.amount is not None
|
|
131
|
+
|
|
132
|
+
if mkt.limits.cost.max is None:
|
|
133
|
+
print(f"Cost max limit is None for {symbol} ({mkt.type})")
|
|
134
|
+
|
|
135
|
+
if mkt.limits.amount.max is None:
|
|
136
|
+
print(f"Amount max limit is None for {symbol} ({mkt.type})")
|
|
137
|
+
|
|
138
|
+
assert mkt.limits.cost is not None
|
|
139
|
+
|
|
140
|
+
print("All checks passed")
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
if __name__ == "__main__":
|
|
144
|
+
check()
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Binance exchange factory implementation.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from typing import cast
|
|
6
|
+
|
|
7
|
+
from walrasquant.constants import ExchangeType, AccountType, ConfigType
|
|
8
|
+
from walrasquant.config import (
|
|
9
|
+
PublicConnectorConfig,
|
|
10
|
+
PrivateConnectorConfig,
|
|
11
|
+
BasicConfig,
|
|
12
|
+
)
|
|
13
|
+
from walrasquant.exchange.base_factory import ExchangeFactory, BuildContext
|
|
14
|
+
from walrasquant.base import (
|
|
15
|
+
ExchangeManager,
|
|
16
|
+
PublicConnector,
|
|
17
|
+
PrivateConnector,
|
|
18
|
+
ExecutionManagementSystem,
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
from walrasquant.exchange.binance.exchange import BinanceExchangeManager
|
|
22
|
+
from walrasquant.exchange.binance.constants import BinanceAccountType
|
|
23
|
+
from walrasquant.exchange.binance.connector import (
|
|
24
|
+
BinancePublicConnector,
|
|
25
|
+
BinancePrivateConnector,
|
|
26
|
+
)
|
|
27
|
+
from walrasquant.exchange.binance.ems import BinanceExecutionManagementSystem
|
|
28
|
+
from walrasquant.exchange.binance.schema import BinanceMarket
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class BinanceFactory(ExchangeFactory):
|
|
32
|
+
"""Factory for creating Binance exchange components."""
|
|
33
|
+
|
|
34
|
+
@property
|
|
35
|
+
def exchange_type(self) -> ExchangeType:
|
|
36
|
+
return ExchangeType.BINANCE
|
|
37
|
+
|
|
38
|
+
def create_manager(self, basic_config: BasicConfig) -> ExchangeManager:
|
|
39
|
+
"""Create BinanceExchangeManager."""
|
|
40
|
+
ccxt_config: ConfigType = {
|
|
41
|
+
"apiKey": basic_config.api_key or "",
|
|
42
|
+
"secret": basic_config.secret or "",
|
|
43
|
+
"exchange_id": "binance",
|
|
44
|
+
"sandbox": basic_config.testnet,
|
|
45
|
+
}
|
|
46
|
+
if basic_config.passphrase:
|
|
47
|
+
ccxt_config["password"] = basic_config.passphrase
|
|
48
|
+
|
|
49
|
+
return BinanceExchangeManager(ccxt_config)
|
|
50
|
+
|
|
51
|
+
def create_public_connector(
|
|
52
|
+
self,
|
|
53
|
+
config: PublicConnectorConfig,
|
|
54
|
+
exchange: ExchangeManager,
|
|
55
|
+
context: BuildContext,
|
|
56
|
+
) -> PublicConnector:
|
|
57
|
+
"""Create BinancePublicConnector."""
|
|
58
|
+
if not isinstance(config.account_type, BinanceAccountType):
|
|
59
|
+
raise TypeError("Binance public connector requires BinanceAccountType")
|
|
60
|
+
return BinancePublicConnector(
|
|
61
|
+
account_type=config.account_type,
|
|
62
|
+
exchange=cast(BinanceExchangeManager, exchange),
|
|
63
|
+
msgbus=context.msgbus,
|
|
64
|
+
clock=context.clock,
|
|
65
|
+
task_manager=context.task_manager,
|
|
66
|
+
enable_rate_limit=config.enable_rate_limit,
|
|
67
|
+
custom_url=config.custom_url,
|
|
68
|
+
max_subscriptions_per_client=config.max_subscriptions_per_client,
|
|
69
|
+
max_clients=config.max_clients,
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
def create_private_connector(
|
|
73
|
+
self,
|
|
74
|
+
config: PrivateConnectorConfig,
|
|
75
|
+
exchange: ExchangeManager,
|
|
76
|
+
context: BuildContext,
|
|
77
|
+
account_type: AccountType | None = None,
|
|
78
|
+
) -> PrivateConnector:
|
|
79
|
+
"""Create BinancePrivateConnector."""
|
|
80
|
+
# Use provided account_type or fall back to config
|
|
81
|
+
final_account_type = account_type or config.account_type
|
|
82
|
+
if not isinstance(final_account_type, BinanceAccountType):
|
|
83
|
+
raise TypeError("Binance private connector requires BinanceAccountType")
|
|
84
|
+
|
|
85
|
+
return BinancePrivateConnector(
|
|
86
|
+
exchange=cast(BinanceExchangeManager, exchange),
|
|
87
|
+
account_type=final_account_type,
|
|
88
|
+
cache=context.cache,
|
|
89
|
+
clock=context.clock,
|
|
90
|
+
msgbus=context.msgbus,
|
|
91
|
+
registry=context.registry,
|
|
92
|
+
enable_rate_limit=config.enable_rate_limit,
|
|
93
|
+
task_manager=context.task_manager,
|
|
94
|
+
max_retries=config.max_retries,
|
|
95
|
+
delay_initial_ms=config.delay_initial_ms,
|
|
96
|
+
delay_max_ms=config.delay_max_ms,
|
|
97
|
+
backoff_factor=config.backoff_factor,
|
|
98
|
+
max_subscriptions_per_client=config.max_subscriptions_per_client,
|
|
99
|
+
max_clients=config.max_clients,
|
|
100
|
+
order_query_config=context.order_query_config,
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
def create_ems(
|
|
104
|
+
self, exchange: ExchangeManager, context: BuildContext
|
|
105
|
+
) -> ExecutionManagementSystem:
|
|
106
|
+
"""Create BinanceExecutionManagementSystem."""
|
|
107
|
+
return BinanceExecutionManagementSystem(
|
|
108
|
+
market=cast(dict[str, BinanceMarket], exchange.market),
|
|
109
|
+
cache=context.cache,
|
|
110
|
+
msgbus=context.msgbus,
|
|
111
|
+
clock=context.clock,
|
|
112
|
+
task_manager=context.task_manager,
|
|
113
|
+
registry=context.registry,
|
|
114
|
+
queue_maxsize=context.queue_maxsize,
|
|
115
|
+
)
|