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
@@ -0,0 +1,436 @@
1
+ #coding:gbk
2
+ #! /usr/bin/python
3
+ # Embedded same-account market entry. Do not load parent entry files at runtime.
4
+
5
+ import os as _cfquant_market_os
6
+
7
+ _CFQUANT_MARKET = "SZ"
8
+ _CFQUANT_CONFIG_FILENAME = "cfquant_bridge_config_SZ.json"
9
+
10
+
11
+ def _cfquant_market_base_dir():
12
+ path = globals().get("__file__") or ""
13
+ if path and not str(path).startswith("<"):
14
+ try:
15
+ return _cfquant_market_os.path.dirname(_cfquant_market_os.path.abspath(path))
16
+ except Exception:
17
+ return ""
18
+ try:
19
+ return _cfquant_market_os.path.abspath(_cfquant_market_os.getcwd())
20
+ except Exception:
21
+ return ""
22
+
23
+
24
+ def _cfquant_market_config_path():
25
+ base = _cfquant_market_base_dir()
26
+ parent = _cfquant_market_os.path.dirname(base) if base else ""
27
+ if base and _cfquant_market_os.path.basename(base).lower() == "python" and parent:
28
+ return _cfquant_market_os.path.join(parent, "bin.x64", _CFQUANT_CONFIG_FILENAME)
29
+ if base:
30
+ return _cfquant_market_os.path.join(base, _CFQUANT_CONFIG_FILENAME)
31
+ return _CFQUANT_CONFIG_FILENAME
32
+
33
+
34
+ _cfquant_market_os.environ["CFQUANT_MARKET"] = _CFQUANT_MARKET
35
+ _cfquant_market_os.environ["CFQUANT_MARKET_SOURCE"] = "cfquant_market_entry"
36
+
37
+
38
+ def _cfquant_market_should_set_config_path():
39
+ current = str(_cfquant_market_os.environ.get("CFQUANT_BRIDGE_CONFIG_FILE") or "").strip()
40
+ if not current:
41
+ return True
42
+ source = str(_cfquant_market_os.environ.get("CFQUANT_BRIDGE_CONFIG_FILE_SOURCE") or "").strip()
43
+ if source in ("cfquant_market_entry", "cfquant_entry"):
44
+ return True
45
+ current_name = _cfquant_market_os.path.basename(current).lower()
46
+ market_names = ("cfquant_bridge_config_sh.json", "cfquant_bridge_config_sz.json")
47
+ return current_name in market_names and current_name != _CFQUANT_CONFIG_FILENAME.lower()
48
+
49
+
50
+ if _cfquant_market_should_set_config_path():
51
+ _cfquant_market_os.environ["CFQUANT_BRIDGE_CONFIG_FILE"] = _cfquant_market_config_path()
52
+ _cfquant_market_os.environ["CFQUANT_BRIDGE_CONFIG_FILE_SOURCE"] = "cfquant_market_entry"
53
+
54
+ # Embedded market entry: CFQUANT_TRADE_LOWLAT_SZ.py
55
+ #coding:gbk
56
+ #! /usr/bin/python
57
+
58
+ import os
59
+ import sys
60
+ import importlib
61
+ import io
62
+ import json
63
+
64
+
65
+ _trade_bridge = None
66
+ DEFAULT_ACCOUNT_ID = ""
67
+ USER_BRIDGE_ID = "default"
68
+ BRIDGE_ID = os.environ.get("CFQUANT_BRIDGE_ID", USER_BRIDGE_ID)
69
+ RUNTIME_CONFIG = {}
70
+ RUNTIME_CHANNELS = {}
71
+ QMT_MARKET = os.environ.get("CFQUANT_MARKET", "").strip().upper()
72
+
73
+
74
+ def _entry_file_path():
75
+ path = globals().get("__file__") or ""
76
+ path = str(path or "").strip()
77
+ if path and not path.startswith("<"):
78
+ try:
79
+ return os.path.abspath(path)
80
+ except Exception:
81
+ return path
82
+ return ""
83
+
84
+
85
+ def _entry_base_dir():
86
+ entry_file = _entry_file_path()
87
+ if entry_file:
88
+ return os.path.dirname(entry_file)
89
+ for name in ("CFQUANT_QMT_SCRIPT_DIR", "CFQUANT_SCRIPT_DIR", "CFQUANT_ENTRY_DIR"):
90
+ path = str(os.environ.get(name) or "").strip()
91
+ if path and os.path.isdir(path):
92
+ return os.path.abspath(path)
93
+ try:
94
+ cwd = os.path.abspath(os.getcwd())
95
+ if (
96
+ os.path.isfile(os.path.join(cwd, "CFQUANT_TRADE_LOWLAT_SZ.py"))
97
+ or os.path.isfile(os.path.join(cwd, "cfquant_bridge_config.json"))
98
+ or os.path.isdir(os.path.join(cwd, "cfquant"))
99
+ ):
100
+ return cwd
101
+ except Exception:
102
+ pass
103
+ for path in sys.path:
104
+ path = str(path or "").strip()
105
+ if path and os.path.isdir(path):
106
+ base = os.path.abspath(path)
107
+ if (
108
+ os.path.isfile(os.path.join(base, "CFQUANT_TRADE_LOWLAT_SZ.py"))
109
+ or os.path.isfile(os.path.join(base, "cfquant_bridge_config.json"))
110
+ or os.path.isdir(os.path.join(base, "cfquant"))
111
+ ):
112
+ return base
113
+ try:
114
+ return os.path.abspath(os.getcwd())
115
+ except Exception:
116
+ return ""
117
+
118
+
119
+ def _runtime_config_paths():
120
+ try:
121
+ base_dir = _entry_base_dir()
122
+ parent_dir = os.path.dirname(base_dir)
123
+ candidates = []
124
+ env_path = os.environ.get("CFQUANT_BRIDGE_CONFIG_FILE")
125
+ if env_path:
126
+ candidates.append(env_path)
127
+ market = str(os.environ.get("CFQUANT_MARKET") or QMT_MARKET or "").strip().upper()
128
+ market_filenames = []
129
+ if market in ("SH", "SZ"):
130
+ market_filenames.append("cfquant_bridge_config_%s.json" % market)
131
+ if os.path.basename(base_dir).lower() == "python":
132
+ for filename in market_filenames:
133
+ candidates.append(os.path.join(parent_dir, "bin.x64", filename))
134
+ candidates.append(os.path.join(base_dir, filename))
135
+ candidates.append(os.path.join(parent_dir, "bin.x64", "cfquant_bridge_config.json"))
136
+ candidates.append(os.path.join(base_dir, "cfquant_bridge_config.json"))
137
+ else:
138
+ for filename in market_filenames:
139
+ candidates.append(os.path.join(base_dir, filename))
140
+ candidates.append(os.path.join(base_dir, "bin.x64", filename))
141
+ candidates.append(os.path.join(base_dir, "cfquant_bridge_config.json"))
142
+ candidates.append(os.path.join(base_dir, "bin.x64", "cfquant_bridge_config.json"))
143
+ for filename in market_filenames:
144
+ candidates.append(os.path.join(parent_dir, filename))
145
+ candidates.append(os.path.join(parent_dir, "cfquant_bridge_config.json"))
146
+ result = []
147
+ seen = set()
148
+ for path in candidates:
149
+ if not path:
150
+ continue
151
+ path = os.path.abspath(os.path.expandvars(os.path.expanduser(path)))
152
+ key = os.path.normcase(path)
153
+ if key in seen:
154
+ continue
155
+ seen.add(key)
156
+ result.append(path)
157
+ return result
158
+ except Exception:
159
+ return []
160
+
161
+
162
+ def _load_runtime_config():
163
+ for path in _runtime_config_paths():
164
+ if not os.path.isfile(path):
165
+ continue
166
+ for opener in (
167
+ lambda: io.open(path, "r", encoding="utf-8"),
168
+ lambda: open(path, "r"),
169
+ ):
170
+ try:
171
+ with opener() as f:
172
+ data = json.loads(f.read())
173
+ if isinstance(data, dict):
174
+ return data
175
+ except Exception:
176
+ pass
177
+ return {}
178
+
179
+
180
+ def _config_bool(value, default=True):
181
+ if isinstance(value, bool):
182
+ return value
183
+ if value is None:
184
+ return default
185
+ text = str(value).strip().lower()
186
+ if text in ("0", "false", "no", "off", "disable", "disabled", "closed", "close"):
187
+ return False
188
+ if text in ("1", "true", "yes", "on", "enable", "enabled", "open"):
189
+ return True
190
+ return default
191
+
192
+
193
+ def _env_allows_runtime_override(name, default_value=""):
194
+ value = str(os.environ.get(name) or "").strip()
195
+ if not value:
196
+ return True
197
+ if str(os.environ.get("%s_SOURCE" % name) or "").strip() == "cfquant_entry":
198
+ return True
199
+ return bool(default_value and value == default_value)
200
+
201
+
202
+ def _apply_runtime_config():
203
+ global BRIDGE_ID, RUNTIME_CONFIG, RUNTIME_CHANNELS, QMT_MARKET
204
+
205
+ data = _load_runtime_config()
206
+ RUNTIME_CONFIG = data
207
+ if not data:
208
+ return
209
+ if data.get("bridge_id") and _env_allows_runtime_override("CFQUANT_BRIDGE_ID", USER_BRIDGE_ID):
210
+ BRIDGE_ID = data.get("bridge_id")
211
+ if data.get("market"):
212
+ market = str(data.get("market") or "").strip().upper()
213
+ if market in ("SH", "SZ"):
214
+ QMT_MARKET = market
215
+ if not os.environ.get("CFQUANT_MARKET"):
216
+ os.environ["CFQUANT_MARKET"] = market
217
+ os.environ["CFQUANT_MARKET_SOURCE"] = "cfquant_entry"
218
+ channels = data.get("channels") or {}
219
+ if isinstance(channels, dict):
220
+ RUNTIME_CHANNELS = channels
221
+ if not os.environ.get("CFQUANT_QMT_LOG_LANGUAGE") and data.get("qmt_log_language"):
222
+ os.environ["CFQUANT_QMT_LOG_LANGUAGE"] = str(data.get("qmt_log_language") or "zh")
223
+ if not os.environ.get("CFQUANT_QMT_LOG_ENABLED") and "qmt_log_enabled" in data:
224
+ os.environ["CFQUANT_QMT_LOG_ENABLED"] = "1" if _config_bool(data.get("qmt_log_enabled"), True) else "0"
225
+
226
+
227
+ def _ensure_path():
228
+ try:
229
+ base_dir = _entry_base_dir()
230
+ parent_dir = os.path.dirname(base_dir)
231
+ env_paths = [p for p in os.environ.get("CFQUANT_PYTHONPATH", "").split(os.pathsep) if p]
232
+ if os.path.basename(base_dir).lower() == "python":
233
+ candidates = env_paths + [os.path.join(parent_dir, "bin.x64"), base_dir, parent_dir]
234
+ else:
235
+ candidates = env_paths + [
236
+ base_dir,
237
+ os.path.join(base_dir, "bin.x64"),
238
+ parent_dir,
239
+ os.path.join(parent_dir, "bin.x64"),
240
+ os.path.join(parent_dir, "python"),
241
+ ]
242
+ ordered = []
243
+ seen = set()
244
+ for path in candidates:
245
+ if not path or not os.path.isdir(path):
246
+ continue
247
+ path = os.path.abspath(path)
248
+ key = os.path.normcase(path)
249
+ if key in seen:
250
+ continue
251
+ seen.add(key)
252
+ ordered.append(path)
253
+ if ordered:
254
+ sys.path[:] = ordered + [
255
+ path for path in sys.path
256
+ if os.path.normcase(os.path.abspath(path or os.curdir)) not in seen
257
+ ]
258
+ except Exception:
259
+ pass
260
+
261
+
262
+ def _install_logging_compat():
263
+ """Keep this entry compatible with older cfquant packages in QMT."""
264
+ logging_module = importlib.import_module("cfquant.logging_i18n")
265
+
266
+ if not hasattr(logging_module, "normalize_log_language"):
267
+ def _normalize_log_language(value=None):
268
+ value = str(value or "").strip().lower()
269
+ return "en" if value in ("en", "english") else "zh"
270
+
271
+ logging_module.normalize_log_language = _normalize_log_language
272
+
273
+ if not hasattr(logging_module, "normalize_log_enabled"):
274
+ def _normalize_log_enabled(value=None):
275
+ if isinstance(value, bool):
276
+ return value
277
+ if value is None:
278
+ return True
279
+ text = str(value).strip().lower()
280
+ return text not in ("0", "false", "no", "off", "disable", "disabled", "closed", "close")
281
+
282
+ logging_module.normalize_log_enabled = _normalize_log_enabled
283
+
284
+ if not hasattr(logging_module, "_CFQUANT_COMPAT_LOG_LANGUAGE"):
285
+ logging_module._CFQUANT_COMPAT_LOG_LANGUAGE = ""
286
+ if not hasattr(logging_module, "_CFQUANT_COMPAT_LOG_ENABLED"):
287
+ logging_module._CFQUANT_COMPAT_LOG_ENABLED = None
288
+
289
+ if not hasattr(logging_module, "get_log_language"):
290
+ def _get_log_language():
291
+ if logging_module._CFQUANT_COMPAT_LOG_LANGUAGE:
292
+ return logging_module._CFQUANT_COMPAT_LOG_LANGUAGE
293
+ return logging_module.normalize_log_language(
294
+ os.environ.get("CFQUANT_QMT_LOG_LANGUAGE")
295
+ or os.environ.get("CFQUANT_LOG_LANGUAGE")
296
+ or "zh"
297
+ )
298
+
299
+ logging_module.get_log_language = _get_log_language
300
+
301
+ if not hasattr(logging_module, "set_log_language"):
302
+ def _set_log_language(value):
303
+ language = logging_module.normalize_log_language(value)
304
+ logging_module._CFQUANT_COMPAT_LOG_LANGUAGE = language
305
+ return language
306
+
307
+ logging_module.set_log_language = _set_log_language
308
+
309
+ if not hasattr(logging_module, "get_log_enabled"):
310
+ def _get_log_enabled():
311
+ if logging_module._CFQUANT_COMPAT_LOG_ENABLED is not None:
312
+ return bool(logging_module._CFQUANT_COMPAT_LOG_ENABLED)
313
+ return logging_module.normalize_log_enabled(
314
+ os.environ.get("CFQUANT_QMT_LOG_ENABLED")
315
+ or os.environ.get("CFQUANT_LOG_ENABLED")
316
+ or "1"
317
+ )
318
+
319
+ logging_module.get_log_enabled = _get_log_enabled
320
+
321
+ if not hasattr(logging_module, "set_log_enabled"):
322
+ def _set_log_enabled(value):
323
+ enabled = logging_module.normalize_log_enabled(value)
324
+ logging_module._CFQUANT_COMPAT_LOG_ENABLED = enabled
325
+ return enabled
326
+
327
+ logging_module.set_log_enabled = _set_log_enabled
328
+
329
+ if not hasattr(logging_module, "translate_log"):
330
+ def _translate_log(message, language=None):
331
+ return str(message)
332
+
333
+ logging_module.translate_log = _translate_log
334
+
335
+ return logging_module
336
+
337
+
338
+ _ensure_path()
339
+ _apply_runtime_config()
340
+
341
+ _logging_i18n = _install_logging_compat()
342
+ get_log_enabled = _logging_i18n.get_log_enabled
343
+ translate_log = _logging_i18n.translate_log
344
+
345
+ from cfquant import __version__ as _ENTRY_VERSION
346
+
347
+
348
+ def _print_log(message):
349
+ if get_log_enabled():
350
+ print(translate_log(message))
351
+
352
+
353
+ def _load_bridge_starter():
354
+ import cfquant.tx_trade_bridge as tx_trade_bridge
355
+
356
+ try:
357
+ tx_trade_bridge = importlib.reload(tx_trade_bridge)
358
+ except Exception as e:
359
+ _print_log("tx trade bridge reload failed:%s" % e)
360
+ return tx_trade_bridge.start_tx_trade_bridge
361
+
362
+
363
+ start_tx_trade_bridge = _load_bridge_starter()
364
+
365
+ from cfquant.channels import channels_for_bridge, normalize_bridge_id
366
+
367
+ BRIDGE_ID = normalize_bridge_id(BRIDGE_ID)
368
+ QMT_MARKET = str(QMT_MARKET or os.environ.get("CFQUANT_MARKET") or RUNTIME_CONFIG.get("market") or "").strip().upper()
369
+ if QMT_MARKET not in ("SH", "SZ"):
370
+ QMT_MARKET = ""
371
+ if not os.environ.get("CFQUANT_BRIDGE_ID"):
372
+ os.environ["CFQUANT_BRIDGE_ID"] = BRIDGE_ID
373
+ os.environ["CFQUANT_BRIDGE_ID_SOURCE"] = "cfquant_entry"
374
+ if QMT_MARKET and not os.environ.get("CFQUANT_MARKET"):
375
+ os.environ["CFQUANT_MARKET"] = QMT_MARKET
376
+ os.environ["CFQUANT_MARKET_SOURCE"] = "cfquant_entry"
377
+ BRIDGE_CHANNELS = channels_for_bridge(BRIDGE_ID)
378
+ _trade_channel_value = RUNTIME_CHANNELS.get("trade") or RUNTIME_CONFIG.get("trade_channel")
379
+ if _trade_channel_value:
380
+ BRIDGE_CHANNELS["trade"] = str(_trade_channel_value).strip()
381
+
382
+ _trade_bridge = start_tx_trade_bridge(
383
+ None,
384
+ ip="127.0.0.1",
385
+ port=2049,
386
+ token="LTtx",
387
+ request_channel=BRIDGE_CHANNELS["trade"],
388
+ bridge_id=BRIDGE_ID,
389
+ account_id=DEFAULT_ACCOUNT_ID,
390
+ show=True,
391
+ )
392
+ _print_log("cfquant lowlat trade bridge module loaded")
393
+ _print_log("cfquant lowlat entry version:%s" % _ENTRY_VERSION)
394
+ _print_log("cfquant bridge id:%s trade_channel:%s" % (BRIDGE_ID, BRIDGE_CHANNELS["trade"]))
395
+ if QMT_MARKET:
396
+ _print_log("cfquant lowlat market route market:%s bridge_id:%s" % (QMT_MARKET, BRIDGE_ID))
397
+
398
+
399
+ def _attach_trade_status_extra():
400
+ if not _trade_bridge:
401
+ return
402
+ original_status_extra = _trade_bridge._status_extra
403
+
404
+ def status_extra_with_market():
405
+ data = original_status_extra()
406
+ data.update({
407
+ "qmt_runtime_market": QMT_MARKET,
408
+ "qmt_runtime_market_role": RUNTIME_CONFIG.get("market_role") or ("trade" if QMT_MARKET else ""),
409
+ "qmt_runtime_market_parent_bridge_id": RUNTIME_CONFIG.get("market_route_parent_bridge_id") or "",
410
+ })
411
+ return data
412
+
413
+ _trade_bridge._status_extra = status_extra_with_market
414
+
415
+
416
+ _attach_trade_status_extra()
417
+
418
+
419
+ def init(ContextInfo):
420
+ if _trade_bridge:
421
+ _trade_bridge.set_context(ContextInfo)
422
+ _print_log("cfquant lowlat trade context ready version:%s" % _ENTRY_VERSION)
423
+ _trade_bridge.run_forever(sleep_seconds=0.001)
424
+
425
+
426
+ def handlebar(ContextInfo):
427
+ pass
428
+
429
+
430
+ def stop(ContextInfo):
431
+ global _trade_bridge
432
+
433
+ if _trade_bridge:
434
+ _trade_bridge.close()
435
+ _trade_bridge = None
436
+ _print_log("cfquant lowlat trade bridge stopped")
@@ -0,0 +1,95 @@
1
+ # 同账号独立市场部署说明
2
+
3
+ ## 适用场景
4
+
5
+ 有些券商的大 QMT 会把上海市场和深圳市场拆成两个独立交易端,但两个交易端使用同一个资金账号。本目录用于这种场景:
6
+
7
+ - `000001.SZ` 这类深圳标的自动路由到深圳交易端。
8
+ - `601222.SH` 这类上海标的自动路由到上海交易端。
9
+ - 账号仍然是同一个账号,系统内部只按标的市场切换交易桥接。
10
+
11
+ ## 网页端配置
12
+
13
+ 在网页的账号绑定页面中:
14
+
15
+ 1. 编辑对应资金账号。
16
+ 2. 开启“同账号独立市场路由”。
17
+ 3. 填写“上海交易端目录”和“深圳交易端目录”,建议填写各自 QMT 的 `bin.x64` 目录。
18
+ 4. `bridge_id` 可以留空,系统会自动生成,例如 `acct_xxx_sh`、`acct_xxx_sz`。
19
+ 5. 保存绑定。
20
+
21
+ 保存后,系统会分别写入独立配置文件:
22
+
23
+ - 上海交易端:`cfquant_bridge_config_SH.json`
24
+ - 深圳交易端:`cfquant_bridge_config_SZ.json`
25
+
26
+ 这两个 JSON 会声明各自的 `bridge_id`、请求通道和 `market`。
27
+
28
+ ## QMT 端运行哪个脚本
29
+
30
+ 根据当前账号使用的模式选择对应脚本。
31
+
32
+ ### 通用 ctypes / 单文件低延迟模式
33
+
34
+ - 上海 QMT 运行:`CFQUANT_CTYPE_ALL_LOWLAT_SH.py`
35
+ - 深圳 QMT 运行:`CFQUANT_CTYPE_ALL_LOWLAT_SZ.py`
36
+
37
+ ### 高级模式 LTtx 交易端
38
+
39
+ - 上海 QMT 交易端运行:`CFQUANT_TRADE_LOWLAT_SH.py`
40
+ - 深圳 QMT 交易端运行:`CFQUANT_TRADE_LOWLAT_SZ.py`
41
+
42
+ ### 极致模式
43
+
44
+ - 上海 QMT 运行:`CFQUANT_LITE_SH.py`
45
+ - 深圳 QMT 运行:`CFQUANT_LITE_SZ.py`
46
+
47
+ ## 手工复制时的注意事项
48
+
49
+ 这些 `_SH` / `_SZ` 文件现在是完整市场入口,已经内置对应主入口代码,不会在 QMT 运行时读取上层目录里的原始入口脚本。手工复制时只需要复制当前模式对应的市场文件:
50
+
51
+ - 通用 ctypes:`CFQUANT_CTYPE_ALL_LOWLAT_SH.py` / `CFQUANT_CTYPE_ALL_LOWLAT_SZ.py`
52
+ - 高级模式交易端:`CFQUANT_TRADE_LOWLAT_SH.py` / `CFQUANT_TRADE_LOWLAT_SZ.py`
53
+ - 极致模式:`CFQUANT_LITE_SH.py` / `CFQUANT_LITE_SZ.py`
54
+
55
+ 通用 ctypes 和高级模式交易端仍按原模式要求部署 `cfquant` 核心包;极致模式市场入口不需要复制 `cfquant` 核心包。
56
+
57
+ ## 账号数据和回调
58
+
59
+ 同账号独立市场开启后,主桥可以只作为账号配置和路由身份存在,上海、深圳两个市场入口分别在线即可。
60
+
61
+ - 下单、撤单按证券代码市场发送到对应 SH/SZ 子桥。
62
+ - 持仓、委托、成交会分别查询 SH/SZ 子桥,并在 Web 端合并展示。
63
+ - 资金属于同一个资金账号,Web 端不会把 SH/SZ 两边的资金简单相加;会取第一个成功子桥的资金结果,并保留分市场查询明细用于排查。
64
+ - 交易回调按 SH/SZ 子桥分别上报,Web 端按主账号订阅时会同时接收两个子桥的回调。
65
+
66
+ ## 路由规则
67
+
68
+ 系统优先按完整代码后缀判断市场:
69
+
70
+ - `.SH` 走上海交易端。
71
+ - `.SZ` 走深圳交易端。
72
+
73
+ 如果没有后缀,会按代码开头推断:
74
+
75
+ - `5`、`6`、`9` 开头默认上海。
76
+ - `0`、`1`、`2`、`3` 开头默认深圳。
77
+
78
+ 批量下单中如果同时包含上海和深圳标的,系统会自动拆分为两组请求,分别发送到对应交易端,再合并返回结果。
79
+
80
+ ## 排查要点
81
+
82
+ 如果 QMT 日志里仍然显示 `bridge_id=default`,通常说明没有读到对应市场 JSON,重点检查:
83
+
84
+ - 网页绑定是否已经开启“同账号独立市场路由”并保存。
85
+ - SH/SZ 两个 QMT 的 `bin.x64` 目录是否填写正确。
86
+ - 对应目录里是否生成了 `cfquant_bridge_config_SH.json` 或 `cfquant_bridge_config_SZ.json`。
87
+ - 上海 QMT 是否运行 `_SH.py`,深圳 QMT 是否运行 `_SZ.py`。
88
+ - 是否复制的是最新完整市场入口文件,而不是旧包装脚本。
89
+
90
+ 正常启动后,日志中会出现类似信息:
91
+
92
+ ```text
93
+ cfquant ctypes market route market:SH bridge_id:acct_xxx_sh
94
+ cfquant lowlat market route market:SZ bridge_id:acct_xxx_sz
95
+ ```
@@ -0,0 +1 @@
1
+ """Static assets for the cfquant local Web dashboard."""