cfquant 0.2.0__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.
Files changed (90) hide show
  1. LTtx/__init__.py +2 -0
  2. LTtx/tx/Config.txt +8 -0
  3. LTtx/tx/LTtx_server.py +1177 -0
  4. LTtx/tx/__init__.py +5 -0
  5. LTtx/tx/tx.py +1935 -0
  6. cfquant/__init__.py +26 -0
  7. cfquant/account_routing.py +119 -0
  8. cfquant/channels.py +76 -0
  9. cfquant/cli.py +266 -0
  10. cfquant/client.py +365 -0
  11. cfquant/config.py +86 -0
  12. cfquant/hub.py +304 -0
  13. cfquant/logging_i18n.py +348 -0
  14. cfquant/normal_bridge.py +676 -0
  15. cfquant/pipe_bridge.py +249 -0
  16. cfquant/pipe_client.py +223 -0
  17. cfquant/pipe_hub.py +602 -0
  18. cfquant/pipe_transport.py +493 -0
  19. cfquant/protocol.py +238 -0
  20. cfquant/qmt_bridge.py +1081 -0
  21. cfquant/runtime_report.py +334 -0
  22. cfquant/tx.py +1898 -0
  23. cfquant/tx_trade_bridge.py +1905 -0
  24. cfquant/version.py +5 -0
  25. cfquant/xtconstant.py +262 -0
  26. cfquant/xtdata.py +462 -0
  27. cfquant/xttrader.py +955 -0
  28. cfquant/xttype.py +586 -0
  29. cfquant-0.2.0.dist-info/LICENSE +21 -0
  30. cfquant-0.2.0.dist-info/METADATA +144 -0
  31. cfquant-0.2.0.dist-info/RECORD +90 -0
  32. cfquant-0.2.0.dist-info/WHEEL +5 -0
  33. cfquant-0.2.0.dist-info/entry_points.txt +4 -0
  34. cfquant-0.2.0.dist-info/top_level.txt +6 -0
  35. cfquant_pipe_hub.py +45 -0
  36. cfquant_web_server.py +12137 -0
  37. qmt_scripts/CFQUANT.py +507 -0
  38. qmt_scripts/CFQUANT_CTYPE_ALL_LOWLAT.py +830 -0
  39. qmt_scripts/CFQUANT_LITE.py +4928 -0
  40. qmt_scripts/CFQUANT_TRADE_LOWLAT.py +382 -0
  41. qmt_scripts/__init__.py +1 -0
  42. qmt_scripts//321/205/320/240/320/234/321/210/342/224/244/320/266/321/205/320/237/342/225/226/321/207/320/233/320/274/321/207/320/273/320/233/321/205/342/225/225/320/222/321/205/320/254/342/225/221/CFQUANT_CTYPE_ALL_LOWLAT_SH.py +866 -0
  43. qmt_scripts//321/205/320/240/320/234/321/210/342/224/244/320/266/321/205/320/237/342/225/226/321/207/320/233/320/274/321/207/320/273/320/233/321/205/342/225/225/320/222/321/205/320/254/342/225/221/CFQUANT_CTYPE_ALL_LOWLAT_SZ.py +866 -0
  44. qmt_scripts//321/205/320/240/320/234/321/210/342/224/244/320/266/321/205/320/237/342/225/226/321/207/320/233/320/274/321/207/320/273/320/233/321/205/342/225/225/320/222/321/205/320/254/342/225/221/CFQUANT_LITE_SH.py +5020 -0
  45. qmt_scripts//321/205/320/240/320/234/321/210/342/224/244/320/266/321/205/320/237/342/225/226/321/207/320/233/320/274/321/207/320/273/320/233/321/205/342/225/225/320/222/321/205/320/254/342/225/221/CFQUANT_LITE_SZ.py +5020 -0
  46. qmt_scripts//321/205/320/240/320/234/321/210/342/224/244/320/266/321/205/320/237/342/225/226/321/207/320/233/320/274/321/207/320/273/320/233/321/205/342/225/225/320/222/321/205/320/254/342/225/221/CFQUANT_TRADE_LOWLAT_SH.py +436 -0
  47. qmt_scripts//321/205/320/240/320/234/321/210/342/224/244/320/266/321/205/320/237/342/225/226/321/207/320/233/320/274/321/207/320/273/320/233/321/205/342/225/225/320/222/321/205/320/254/342/225/221/CFQUANT_TRADE_LOWLAT_SZ.py +436 -0
  48. qmt_scripts//321/205/320/240/320/234/321/210/342/224/244/320/266/321/205/320/237/342/225/226/321/207/320/233/320/274/321/207/320/273/320/233/321/205/342/225/225/320/222/321/205/320/254/342/225/221/readme.md +95 -0
  49. web_dashboard/__init__.py +1 -0
  50. web_dashboard/app.js +9847 -0
  51. web_dashboard/avatars/amber-pulse.svg +15 -0
  52. web_dashboard/avatars/copper-grid.svg +15 -0
  53. web_dashboard/avatars/market-blue.svg +15 -0
  54. web_dashboard/avatars/rose-circuit.svg +15 -0
  55. web_dashboard/avatars/signal-green.svg +15 -0
  56. web_dashboard/avatars/slate-wave.svg +15 -0
  57. web_dashboard/avatars/teal-orbit.svg +16 -0
  58. web_dashboard/avatars/violet-node.svg +15 -0
  59. web_dashboard/favicon.svg +5 -0
  60. web_dashboard/image/qmt_deploy/image-10.png +0 -0
  61. web_dashboard/image/qmt_deploy/image-11.png +0 -0
  62. web_dashboard/image/qmt_deploy/image-12.png +0 -0
  63. web_dashboard/image/qmt_deploy/image-13.png +0 -0
  64. web_dashboard/image/qmt_deploy/image-14.png +0 -0
  65. web_dashboard/image/qmt_deploy/image-15.png +0 -0
  66. web_dashboard/image/qmt_deploy/image-16.png +0 -0
  67. web_dashboard/image/qmt_deploy/image-17.png +0 -0
  68. web_dashboard/image/qmt_deploy/image-18.png +0 -0
  69. web_dashboard/image/qmt_deploy/image-19.png +0 -0
  70. web_dashboard/image/qmt_deploy/image-20.png +0 -0
  71. web_dashboard/image/qmt_deploy/image-21.png +0 -0
  72. web_dashboard/image/qmt_deploy/image-4.png +0 -0
  73. web_dashboard/image/qmt_deploy/image-5.png +0 -0
  74. web_dashboard/image/qmt_deploy/image-6.png +0 -0
  75. web_dashboard/image/qmt_deploy/image-7.png +0 -0
  76. web_dashboard/image/qmt_deploy/image-8.png +0 -0
  77. web_dashboard/image/qmt_deploy/image-9.png +0 -0
  78. web_dashboard/image//321/211/320/220/320/252/321/207/320/244/320/270/321/206/320/270/320/261/321/205/342/225/235/320/237/321/211/320/223/320/270/321/207/342/225/234/342/226/223/321/206/320/234/320/227/321/205/320/235/320/247/image-1.png +0 -0
  79. web_dashboard/image//321/211/320/220/320/252/321/207/320/244/320/270/321/206/320/270/320/261/321/205/342/225/235/320/237/321/211/320/223/320/270/321/207/342/225/234/342/226/223/321/206/320/234/320/227/321/205/320/235/320/247/image-2.png +0 -0
  80. web_dashboard/image//321/211/320/220/320/252/321/207/320/244/320/270/321/206/320/270/320/261/321/205/342/225/235/320/237/321/211/320/223/320/270/321/207/342/225/234/342/226/223/321/206/320/234/320/227/321/205/320/235/320/247/image-3.png +0 -0
  81. web_dashboard/image//321/211/320/220/320/252/321/207/320/244/320/270/321/206/320/270/320/261/321/205/342/225/235/320/237/321/211/320/223/320/270/321/207/342/225/234/342/226/223/321/206/320/234/320/227/321/205/320/235/320/247/image.png +0 -0
  82. web_dashboard/image//321/211/320/223/320/270/321/207/342/225/234/342/226/223/321/206/320/234/320/227/321/205/320/235/320/247/1783479325179.png +0 -0
  83. web_dashboard/image//321/211/320/223/320/270/321/207/342/225/234/342/226/223/321/206/320/234/320/227/321/205/320/235/320/247/1783479745262.png +0 -0
  84. web_dashboard/image//321/211/320/223/320/270/321/207/342/225/234/342/226/223/321/206/320/234/320/227/321/205/320/235/320/247/1783480178700.png +0 -0
  85. web_dashboard/image//321/211/320/223/320/270/321/207/342/225/234/342/226/223/321/206/320/234/320/227/321/205/320/235/320/247/1783480428759.png +0 -0
  86. web_dashboard/image//321/211/320/223/320/270/321/207/342/225/234/342/226/223/321/206/320/234/320/227/321/205/320/235/320/247/1783489807217.png +0 -0
  87. web_dashboard/image//321/211/320/223/320/270/321/207/342/225/234/342/226/223/321/206/320/234/320/227/321/205/320/235/320/247/1783490503491.png +0 -0
  88. web_dashboard/index.html +2317 -0
  89. web_dashboard/styles.css +7817 -0
  90. web_dashboard/vendor/mermaid.min.js +2029 -0
cfquant/client.py ADDED
@@ -0,0 +1,365 @@
1
+ # -*- coding: utf-8 -*-
2
+ import json
3
+ import queue
4
+ import socket
5
+ import threading
6
+ import time
7
+
8
+ from .config import get_config
9
+ from .protocol import decode_value, dumps_message, loads_message, new_id, pack_request
10
+
11
+
12
+ class CfquantError(RuntimeError):
13
+ pass
14
+
15
+
16
+ class CfquantTimeout(TimeoutError):
17
+ pass
18
+
19
+
20
+ PIPE_TRANSPORTS = ("pipe", "ctypes", "named_pipe", "named-pipe")
21
+ WEB_LTTX_TRANSPORTS = ("web", "web_lttx", "lttx_web", "web-lttx", "lttx-web")
22
+
23
+
24
+ def _load_txl():
25
+ errors = []
26
+ try:
27
+ from .tx import txl
28
+ return txl
29
+ except Exception as e:
30
+ errors.append("cfquant.tx: %s" % e)
31
+ try:
32
+ from tx import txl
33
+ return txl
34
+ except Exception as e:
35
+ errors.append("tx.py: %s" % e)
36
+ try:
37
+ from LTtx.tx import txl
38
+ return txl
39
+ except Exception as e:
40
+ errors.append("LTtx.tx: %s" % e)
41
+ raise CfquantError("failed to import LTtx txl; tried %s" % "; ".join(errors))
42
+
43
+
44
+ def _tcp_reachable(host, port, timeout=0.35):
45
+ try:
46
+ sock = socket.create_connection((host, int(port)), timeout=float(timeout))
47
+ sock.close()
48
+ return True
49
+ except Exception:
50
+ return False
51
+
52
+
53
+ def _normalize_registry(value):
54
+ if isinstance(value, str):
55
+ text = value.strip()
56
+ if not text:
57
+ return {}
58
+ try:
59
+ value = json.loads(text)
60
+ except Exception:
61
+ return {}
62
+ if not isinstance(value, dict):
63
+ return {}
64
+ if value.get("schema") != "cfquant.lttx.registry":
65
+ return {}
66
+ return value
67
+
68
+
69
+ def load_lttx_registry(cfg=None):
70
+ cfg = cfg or get_config()
71
+ host = cfg.get("host") or "127.0.0.1"
72
+ port = int(cfg.get("port") or 2049)
73
+ if not _tcp_reachable(host, port):
74
+ return {}
75
+ tx = None
76
+ try:
77
+ txl = _load_txl()
78
+ tx = txl(host, port, cfg.get("token") or "LTtx", show=False)
79
+ tx.start_tx()
80
+ value = tx.get(cfg.get("discovery_key") or "cfquant.runtime")
81
+ return _normalize_registry(value)
82
+ except Exception:
83
+ return {}
84
+ finally:
85
+ if tx is not None:
86
+ try:
87
+ tx.close()
88
+ except Exception:
89
+ pass
90
+
91
+
92
+ class LTtxRpcClient(object):
93
+ """
94
+ cfquant 外部端 LTtx/TX RPC 客户端。
95
+
96
+ - 通过 start_tx() 向大 QMT 的固定请求频道发送 request。
97
+ - 通过 start_txg(client_id) 订阅自己的专属回包频道。
98
+ - 大 QMT 按请求里的 client_id 原路 push response/event。
99
+ """
100
+
101
+ def __init__(self, host=None, port=None, token=None, request_channel=None, timeout=None, client_id=None):
102
+ cfg = get_config()
103
+ self.host = host or cfg["host"]
104
+ self.port = int(port or cfg["port"])
105
+ self.token = token or cfg["token"]
106
+ self.request_channel = request_channel or cfg["request_channel"]
107
+ self.timeout = float(timeout or cfg["timeout"])
108
+ self.client_id = client_id or cfg.get("client_id") or new_id("client")
109
+ self.reply_channel = self.client_id
110
+ self._tx = None
111
+ self._pending = {}
112
+ self._callbacks = {}
113
+ self._lock = threading.RLock()
114
+ self._pending_lock = threading.RLock()
115
+ self._started = False
116
+ self._recv_thread = None
117
+
118
+ def start(self):
119
+ with self._lock:
120
+ if self._started:
121
+ return
122
+ txl = self._load_txl()
123
+ self._tx = txl(self.host, self.port, self.token)
124
+ self._tx.start_tx()
125
+ self._tx.start_txg(self.client_id)
126
+ self._started = True
127
+ self._recv_thread = threading.Thread(target=self._recv_loop)
128
+ self._recv_thread.daemon = True
129
+ self._recv_thread.start()
130
+
131
+ def close(self):
132
+ with self._lock:
133
+ self._started = False
134
+ tx = self._tx
135
+ self._tx = None
136
+ if tx is not None:
137
+ try:
138
+ tx.Q.put(None)
139
+ except Exception:
140
+ pass
141
+ try:
142
+ tx.close()
143
+ except Exception:
144
+ pass
145
+ with self._pending_lock:
146
+ for q in list(self._pending.values()):
147
+ try:
148
+ q.put_nowait({"ok": False, "error": {"message": "cfquant client closed"}})
149
+ except Exception:
150
+ pass
151
+ self._pending.clear()
152
+
153
+ def request(self, action, params=None, timeout=None, request_channel=None):
154
+ self.start()
155
+ effective_timeout = float(timeout or self.timeout)
156
+ request_id = new_id("req")
157
+ q = queue.Queue(maxsize=1)
158
+ with self._pending_lock:
159
+ self._pending[request_id] = q
160
+ raw = pack_request(
161
+ action,
162
+ params=params or {},
163
+ reply_channel=self.reply_channel,
164
+ client_id=self.client_id,
165
+ request_id=request_id,
166
+ timeout=effective_timeout,
167
+ )
168
+ self._push("request", raw, request_channel or self.request_channel)
169
+ try:
170
+ msg = q.get(timeout=effective_timeout)
171
+ except queue.Empty:
172
+ with self._pending_lock:
173
+ self._pending.pop(request_id, None)
174
+ raise CfquantTimeout("cfquant request timeout: %s" % action)
175
+ if not msg.get("ok"):
176
+ err = msg.get("error") or {}
177
+ raise CfquantError(err.get("message") or str(err))
178
+ return decode_value(msg.get("result"))
179
+
180
+ def publish_event(self, channel, payload):
181
+ self.start()
182
+ self._push("event", dumps_message(payload), channel)
183
+
184
+ def add_callback(self, event, callback):
185
+ if callback is None:
186
+ return
187
+ self._callbacks.setdefault(event, []).append(callback)
188
+
189
+ def remove_callback(self, event, callback):
190
+ callbacks = self._callbacks.get(event) or []
191
+ if callback in callbacks:
192
+ callbacks.remove(callback)
193
+
194
+ def _recv_loop(self):
195
+ while self._started:
196
+ try:
197
+ tx = self._tx
198
+ if tx is None:
199
+ break
200
+ raw = tx.Q.get()
201
+ if raw is None:
202
+ break
203
+ msg = loads_message(raw)
204
+ if not msg:
205
+ continue
206
+ msg_type = msg.get("type")
207
+ if msg_type == "response":
208
+ with self._pending_lock:
209
+ q = self._pending.pop(msg.get("id"), None)
210
+ if q:
211
+ q.put(msg)
212
+ elif msg_type == "event":
213
+ self._dispatch_event(msg)
214
+ except Exception:
215
+ time.sleep(0.05)
216
+ if not self._started:
217
+ break
218
+
219
+ def _dispatch_event(self, msg):
220
+ event = msg.get("event")
221
+ data = decode_value(msg.get("data"))
222
+ full_msg = dict(msg)
223
+ full_msg["data"] = data
224
+ for callback in list(self._callbacks.get("__event__", [])):
225
+ try:
226
+ callback(full_msg)
227
+ except Exception:
228
+ pass
229
+ for callback in list(self._callbacks.get(event, [])):
230
+ try:
231
+ callback(data)
232
+ except Exception:
233
+ pass
234
+ if event and event.startswith("quote:"):
235
+ quote_msg = dict(msg)
236
+ quote_msg["data"] = data
237
+ if quote_msg.get("subscription_id") is not None and quote_msg.get("subscribe_id") is None:
238
+ quote_msg["subscribe_id"] = quote_msg.get("subscription_id")
239
+ for callback in list(self._callbacks.get("quote", [])):
240
+ try:
241
+ callback(quote_msg)
242
+ except Exception:
243
+ pass
244
+
245
+ def _push(self, key, payload, channel):
246
+ tx = self._tx
247
+ if tx is None:
248
+ raise CfquantError("cfquant LTtx client not started")
249
+ result = tx.push(key, payload, channel)
250
+ if isinstance(result, dict) and result.get("code", 0) != 0:
251
+ raise CfquantError(result.get("msg") or "LTtx push failed")
252
+ return result
253
+
254
+ def _load_txl(self):
255
+ return _load_txl()
256
+
257
+
258
+ class WebLttxRpcClient(LTtxRpcClient):
259
+ """
260
+ 外部 Python 默认路由客户端。
261
+
262
+ 请求仍然通过 LTtx 发送,但目标频道是 Web 服务注册的统一路由频道。
263
+ Web 服务再根据账号配置选择 ctypes 通用桥或 LTtx 高级桥。
264
+ """
265
+
266
+ def __init__(
267
+ self,
268
+ host=None,
269
+ port=None,
270
+ token=None,
271
+ request_channel=None,
272
+ timeout=None,
273
+ client_id=None,
274
+ registry=None,
275
+ ):
276
+ cfg = get_config()
277
+ self.registry = registry or load_lttx_registry(cfg)
278
+ channel = (
279
+ request_channel
280
+ or self.registry.get("web_request_channel")
281
+ or cfg.get("web_request_channel")
282
+ or "cfquant.web.request"
283
+ )
284
+ super(WebLttxRpcClient, self).__init__(
285
+ host=host,
286
+ port=port,
287
+ token=token,
288
+ request_channel=channel,
289
+ timeout=timeout,
290
+ client_id=client_id,
291
+ )
292
+
293
+
294
+ def _registry_has_web_route(registry):
295
+ if not isinstance(registry, dict):
296
+ return False
297
+ web_route = registry.get("web_route") if isinstance(registry.get("web_route"), dict) else {}
298
+ return bool(registry.get("web_request_channel") and web_route.get("enabled", True))
299
+
300
+
301
+ def create_rpc_client(request_channel=None, timeout=None, client_id=None, transport=None, bridge_id=None):
302
+ cfg = get_config()
303
+ mode = str(transport or cfg.get("transport") or "auto").lower()
304
+ registry = {}
305
+ if mode == "auto":
306
+ registry = load_lttx_registry(cfg)
307
+ if _registry_has_web_route(registry):
308
+ return WebLttxRpcClient(
309
+ timeout=timeout or cfg.get("timeout"),
310
+ client_id=client_id,
311
+ registry=registry,
312
+ )
313
+ mode = str(registry.get("direct_fallback_transport") or "ctypes").lower()
314
+ if mode in WEB_LTTX_TRANSPORTS:
315
+ return WebLttxRpcClient(
316
+ timeout=timeout or cfg.get("timeout"),
317
+ client_id=client_id,
318
+ registry=registry or None,
319
+ )
320
+ if mode in PIPE_TRANSPORTS:
321
+ from .pipe_client import PipeRpcClient
322
+
323
+ return PipeRpcClient(
324
+ pipe_name=cfg.get("pipe_name"),
325
+ request_channel=request_channel or cfg.get("request_channel"),
326
+ timeout=timeout or cfg.get("timeout"),
327
+ client_id=client_id,
328
+ connect_timeout_ms=cfg.get("pipe_connect_timeout_ms"),
329
+ )
330
+ return LTtxRpcClient(
331
+ host=cfg.get("host"),
332
+ port=cfg.get("port"),
333
+ token=cfg.get("token"),
334
+ request_channel=request_channel or cfg.get("request_channel"),
335
+ timeout=timeout or cfg.get("timeout"),
336
+ client_id=client_id,
337
+ )
338
+
339
+
340
+ _default_client = None
341
+ _client_lock = threading.Lock()
342
+
343
+
344
+ def get_client():
345
+ global _default_client
346
+ with _client_lock:
347
+ if _default_client is None:
348
+ cfg = get_config()
349
+ _default_client = create_rpc_client(
350
+ request_channel=cfg.get("request_channel"),
351
+ timeout=cfg.get("timeout"),
352
+ client_id=cfg.get("client_id"),
353
+ )
354
+ return _default_client
355
+
356
+
357
+ def configure(**kwargs):
358
+ from .config import configure as configure_config
359
+
360
+ configure_config(**kwargs)
361
+ global _default_client
362
+ with _client_lock:
363
+ if _default_client is not None:
364
+ _default_client.close()
365
+ _default_client = None
cfquant/config.py ADDED
@@ -0,0 +1,86 @@
1
+ # -*- coding: utf-8 -*-
2
+ import os
3
+
4
+ from .channels import bridge_id_from_env, channels_for_bridge
5
+
6
+
7
+ DEFAULT_HOST = os.environ.get("CFQUANT_LTTX_HOST", os.environ.get("CFQUANT_SOCKET_HOST", "127.0.0.1"))
8
+ DEFAULT_PORT = int(os.environ.get("CFQUANT_LTTX_PORT", os.environ.get("CFQUANT_SOCKET_PORT", "2049")))
9
+ DEFAULT_TOKEN = os.environ.get("CFQUANT_LTTX_TOKEN", os.environ.get("CFQUANT_SOCKET_TOKEN", "LTtx"))
10
+ DEFAULT_TRANSPORT = os.environ.get("CFQUANT_TRANSPORT", "auto")
11
+ DEFAULT_PIPE_NAME = os.environ.get("CFQUANT_PIPE_NAME", r"\\.\pipe\cfquant_pipe_hub")
12
+ DEFAULT_PIPE_CONNECT_TIMEOUT_MS = int(os.environ.get("CFQUANT_PIPE_CONNECT_TIMEOUT_MS", "3000"))
13
+ DEFAULT_DISCOVERY_KEY = os.environ.get("CFQUANT_DISCOVERY_KEY", "cfquant.runtime")
14
+ DEFAULT_WEB_REQUEST_CHANNEL = os.environ.get("CFQUANT_WEB_REQUEST_CHANNEL", "cfquant.web.request")
15
+ DEFAULT_BRIDGE_ID = bridge_id_from_env()
16
+ DEFAULT_REQUEST_CHANNEL = os.environ.get(
17
+ "CFQUANT_REQUEST_CHANNEL",
18
+ channels_for_bridge(DEFAULT_BRIDGE_ID)["normal"],
19
+ )
20
+ DEFAULT_TIMEOUT = float(os.environ.get("CFQUANT_TIMEOUT", "15"))
21
+ DEFAULT_CLIENT_ID = os.environ.get("CFQUANT_CLIENT_ID")
22
+
23
+
24
+ _config = {
25
+ "host": DEFAULT_HOST,
26
+ "port": DEFAULT_PORT,
27
+ "token": DEFAULT_TOKEN,
28
+ "bridge_id": DEFAULT_BRIDGE_ID,
29
+ "request_channel": DEFAULT_REQUEST_CHANNEL,
30
+ "timeout": DEFAULT_TIMEOUT,
31
+ "client_id": DEFAULT_CLIENT_ID,
32
+ "transport": DEFAULT_TRANSPORT,
33
+ "pipe_name": DEFAULT_PIPE_NAME,
34
+ "pipe_connect_timeout_ms": DEFAULT_PIPE_CONNECT_TIMEOUT_MS,
35
+ "discovery_key": DEFAULT_DISCOVERY_KEY,
36
+ "web_request_channel": DEFAULT_WEB_REQUEST_CHANNEL,
37
+ }
38
+
39
+
40
+ def configure(
41
+ host=None,
42
+ port=None,
43
+ token=None,
44
+ request_channel=None,
45
+ timeout=None,
46
+ client_id=None,
47
+ bridge_id=None,
48
+ transport=None,
49
+ pipe_name=None,
50
+ pipe_connect_timeout_ms=None,
51
+ discovery_key=None,
52
+ web_request_channel=None,
53
+ ):
54
+ """配置 cfquant 连接到 LTtx/TX 桥接端的参数。"""
55
+ if host is not None:
56
+ _config["host"] = host
57
+ if port is not None:
58
+ _config["port"] = int(port)
59
+ if token is not None:
60
+ _config["token"] = token
61
+ if bridge_id is not None:
62
+ from .channels import normalize_bridge_id
63
+
64
+ _config["bridge_id"] = normalize_bridge_id(bridge_id)
65
+ if request_channel is None:
66
+ _config["request_channel"] = channels_for_bridge(_config["bridge_id"])["normal"]
67
+ if request_channel is not None:
68
+ _config["request_channel"] = request_channel
69
+ if timeout is not None:
70
+ _config["timeout"] = float(timeout)
71
+ if client_id is not None:
72
+ _config["client_id"] = client_id
73
+ if transport is not None:
74
+ _config["transport"] = str(transport or "ctypes").lower()
75
+ if pipe_name is not None:
76
+ _config["pipe_name"] = pipe_name
77
+ if pipe_connect_timeout_ms is not None:
78
+ _config["pipe_connect_timeout_ms"] = int(pipe_connect_timeout_ms)
79
+ if discovery_key is not None:
80
+ _config["discovery_key"] = str(discovery_key or DEFAULT_DISCOVERY_KEY)
81
+ if web_request_channel is not None:
82
+ _config["web_request_channel"] = str(web_request_channel or DEFAULT_WEB_REQUEST_CHANNEL)
83
+
84
+
85
+ def get_config():
86
+ return dict(_config)