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,866 @@
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_CTYPE_ALL_LOWLAT_SZ.py
55
+ #coding:gbk
56
+ #! /usr/bin/python
57
+
58
+ import os
59
+ import sys
60
+ import importlib
61
+ import datetime as dt
62
+ import io
63
+ import json
64
+ import queue
65
+ import threading
66
+ import time
67
+
68
+
69
+ _normal_bridge = None
70
+ _trade_bridge = None
71
+ _trade_thread = None
72
+ _trade_request_queue = queue.Queue(maxsize=10000)
73
+ _trade_timer_key = None
74
+ _trade_loop_started_at = 0
75
+ _trade_loop_error = ""
76
+ _trade_recv_count = 0
77
+ _trade_dispatch_count = 0
78
+ _trade_direct_dispatch_count = 0
79
+ _trade_reroute_count = 0
80
+ _trade_queue_full_count = 0
81
+ _trade_last_recv_at = 0
82
+ _trade_last_dispatch_at = 0
83
+ DEFAULT_ACCOUNT_ID = str(os.environ.get("CFQUANT_ACCOUNT_ID") or "").strip()
84
+ DEFAULT_ACCOUNT_TYPE = str(os.environ.get("CFQUANT_ACCOUNT_TYPE") or "STOCK").strip().upper()
85
+ USER_BRIDGE_ID = "default"
86
+ BRIDGE_ID = os.environ.get("CFQUANT_BRIDGE_ID", USER_BRIDGE_ID)
87
+ PIPE_NAME = os.environ.get("CFQUANT_PIPE_NAME", r"\\.\pipe\cfquant_pipe_hub")
88
+ RUNTIME_CONFIG_PATH = ""
89
+ RUNTIME_CONFIG = {}
90
+ RUNTIME_CHANNELS = {}
91
+ QMT_MARKET = os.environ.get("CFQUANT_MARKET", "").strip().upper()
92
+ PIPE_CONNECT_TIMEOUT_MS = int(os.environ.get("CFQUANT_PIPE_CONNECT_TIMEOUT_MS", "3000"))
93
+ TRADE_LOOP_IN_THREAD = os.environ.get("CFQUANT_CTYPE_TRADE_THREAD", "1").strip().lower() in ("1", "true", "yes", "on")
94
+ NORMAL_PUMP_MAX_COUNT = int(os.environ.get("CFQUANT_CTYPE_NORMAL_PUMP_MAX_COUNT", "100"))
95
+ NORMAL_PUMP_MAX_MS = float(os.environ.get("CFQUANT_CTYPE_NORMAL_PUMP_MAX_MS", "0"))
96
+ TRADE_SLEEP_SECONDS = float(os.environ.get("CFQUANT_CTYPE_TRADE_SLEEP_SECONDS", "0.001"))
97
+ TRADE_PUMP_MAX_COUNT = int(os.environ.get("CFQUANT_CTYPE_TRADE_PUMP_MAX_COUNT", "100"))
98
+ TRADE_PUMP_MAX_MS = float(os.environ.get("CFQUANT_CTYPE_TRADE_PUMP_MAX_MS", "0"))
99
+ TRADE_TIMER_INTERVAL_MS = int(os.environ.get("CFQUANT_CTYPE_TRADE_TIMER_INTERVAL_MS", "20"))
100
+
101
+
102
+ def _entry_file_path():
103
+ path = globals().get("__file__") or ""
104
+ path = str(path or "").strip()
105
+ if path and not path.startswith("<"):
106
+ try:
107
+ return os.path.abspath(path)
108
+ except Exception:
109
+ return path
110
+ return ""
111
+
112
+
113
+ def _entry_base_dir():
114
+ entry_file = _entry_file_path()
115
+ if entry_file:
116
+ return os.path.dirname(entry_file)
117
+ for name in ("CFQUANT_QMT_SCRIPT_DIR", "CFQUANT_SCRIPT_DIR", "CFQUANT_ENTRY_DIR"):
118
+ path = str(os.environ.get(name) or "").strip()
119
+ if path and os.path.isdir(path):
120
+ return os.path.abspath(path)
121
+ try:
122
+ cwd = os.path.abspath(os.getcwd())
123
+ if (
124
+ os.path.isfile(os.path.join(cwd, "CFQUANT_CTYPE_ALL_LOWLAT_SZ.py"))
125
+ or os.path.isfile(os.path.join(cwd, "cfquant_bridge_config.json"))
126
+ or os.path.isdir(os.path.join(cwd, "cfquant"))
127
+ ):
128
+ return cwd
129
+ except Exception:
130
+ pass
131
+ for path in sys.path:
132
+ path = str(path or "").strip()
133
+ if path and os.path.isdir(path):
134
+ base = os.path.abspath(path)
135
+ if (
136
+ os.path.isfile(os.path.join(base, "CFQUANT_CTYPE_ALL_LOWLAT_SZ.py"))
137
+ or os.path.isfile(os.path.join(base, "cfquant_bridge_config.json"))
138
+ or os.path.isdir(os.path.join(base, "cfquant"))
139
+ ):
140
+ return base
141
+ try:
142
+ return os.path.abspath(os.getcwd())
143
+ except Exception:
144
+ return ""
145
+
146
+
147
+ def _runtime_log_path():
148
+ try:
149
+ base_dir = _entry_base_dir()
150
+ parent_dir = os.path.dirname(base_dir)
151
+ configured = os.environ.get("CFQUANT_QMT_LOG_DIR") or os.environ.get("CFQUANT_LOG_DIR")
152
+ if configured:
153
+ candidates = [configured]
154
+ elif os.path.basename(base_dir).lower() == "python":
155
+ candidates = [
156
+ os.path.join(parent_dir, "bin.x64", "log"),
157
+ os.path.join(parent_dir, "log"),
158
+ os.path.join(base_dir, "log"),
159
+ ]
160
+ else:
161
+ candidates = [
162
+ os.path.join(base_dir, "log"),
163
+ os.path.join(parent_dir, "bin.x64", "log"),
164
+ os.path.join(parent_dir, "log"),
165
+ ]
166
+ candidates.extend([
167
+ os.path.join(parent_dir, "bin.x64", "tx_log"),
168
+ os.path.join(base_dir, "tx_log"),
169
+ os.path.join(parent_dir, "tx_log"),
170
+ base_dir,
171
+ ])
172
+ for log_dir in candidates:
173
+ if not log_dir:
174
+ continue
175
+ try:
176
+ os.makedirs(log_dir, exist_ok=True)
177
+ return os.path.join(log_dir, "cfquant_ctype_bridge.log")
178
+ except Exception:
179
+ if os.path.isdir(log_dir):
180
+ return os.path.join(log_dir, "cfquant_ctype_bridge.log")
181
+ except Exception:
182
+ pass
183
+ return ""
184
+
185
+
186
+ def _write_runtime_log(message):
187
+ try:
188
+ path = _runtime_log_path()
189
+ if not path:
190
+ return
191
+ log_dir = os.path.dirname(path)
192
+ if log_dir and not os.path.isdir(log_dir):
193
+ os.makedirs(log_dir)
194
+ with open(path, "a") as f:
195
+ f.write("%s %s\n" % (dt.datetime.now().strftime("%Y-%m-%d %H:%M:%S"), message))
196
+ except Exception:
197
+ pass
198
+
199
+
200
+ def _install_logging_compat():
201
+ """Keep this entry compatible with older cfquant packages in QMT."""
202
+ logging_module = importlib.import_module("cfquant.logging_i18n")
203
+
204
+ if not hasattr(logging_module, "normalize_log_language"):
205
+ def _normalize_log_language(value=None):
206
+ value = str(value or "").strip().lower()
207
+ return "en" if value in ("en", "english") else "zh"
208
+
209
+ logging_module.normalize_log_language = _normalize_log_language
210
+
211
+ if not hasattr(logging_module, "normalize_log_enabled"):
212
+ def _normalize_log_enabled(value=None):
213
+ if isinstance(value, bool):
214
+ return value
215
+ if value is None:
216
+ return True
217
+ text = str(value).strip().lower()
218
+ return text not in ("0", "false", "no", "off", "disable", "disabled", "closed", "close")
219
+
220
+ logging_module.normalize_log_enabled = _normalize_log_enabled
221
+
222
+ if not hasattr(logging_module, "_CFQUANT_COMPAT_LOG_LANGUAGE"):
223
+ logging_module._CFQUANT_COMPAT_LOG_LANGUAGE = ""
224
+ if not hasattr(logging_module, "_CFQUANT_COMPAT_LOG_ENABLED"):
225
+ logging_module._CFQUANT_COMPAT_LOG_ENABLED = None
226
+
227
+ if not hasattr(logging_module, "get_log_language"):
228
+ def _get_log_language():
229
+ if logging_module._CFQUANT_COMPAT_LOG_LANGUAGE:
230
+ return logging_module._CFQUANT_COMPAT_LOG_LANGUAGE
231
+ return logging_module.normalize_log_language(
232
+ os.environ.get("CFQUANT_QMT_LOG_LANGUAGE")
233
+ or os.environ.get("CFQUANT_LOG_LANGUAGE")
234
+ or "zh"
235
+ )
236
+
237
+ logging_module.get_log_language = _get_log_language
238
+
239
+ if not hasattr(logging_module, "set_log_language"):
240
+ def _set_log_language(value):
241
+ language = logging_module.normalize_log_language(value)
242
+ logging_module._CFQUANT_COMPAT_LOG_LANGUAGE = language
243
+ return language
244
+
245
+ logging_module.set_log_language = _set_log_language
246
+
247
+ if not hasattr(logging_module, "get_log_enabled"):
248
+ def _get_log_enabled():
249
+ if logging_module._CFQUANT_COMPAT_LOG_ENABLED is not None:
250
+ return bool(logging_module._CFQUANT_COMPAT_LOG_ENABLED)
251
+ return logging_module.normalize_log_enabled(
252
+ os.environ.get("CFQUANT_QMT_LOG_ENABLED")
253
+ or os.environ.get("CFQUANT_LOG_ENABLED")
254
+ or "1"
255
+ )
256
+
257
+ logging_module.get_log_enabled = _get_log_enabled
258
+
259
+ if not hasattr(logging_module, "set_log_enabled"):
260
+ def _set_log_enabled(value):
261
+ enabled = logging_module.normalize_log_enabled(value)
262
+ logging_module._CFQUANT_COMPAT_LOG_ENABLED = enabled
263
+ return enabled
264
+
265
+ logging_module.set_log_enabled = _set_log_enabled
266
+
267
+ if not hasattr(logging_module, "translate_log"):
268
+ def _translate_log(message, language=None):
269
+ return str(message)
270
+
271
+ logging_module.translate_log = _translate_log
272
+
273
+ return logging_module
274
+
275
+
276
+ def _ensure_path():
277
+ try:
278
+ base_dir = _entry_base_dir()
279
+ parent_dir = os.path.dirname(base_dir)
280
+ env_paths = [p for p in os.environ.get("CFQUANT_PYTHONPATH", "").split(os.pathsep) if p]
281
+ if os.path.basename(base_dir).lower() == "python":
282
+ candidates = env_paths + [os.path.join(parent_dir, "bin.x64"), base_dir, parent_dir]
283
+ else:
284
+ candidates = env_paths + [
285
+ base_dir,
286
+ os.path.join(base_dir, "bin.x64"),
287
+ parent_dir,
288
+ os.path.join(parent_dir, "bin.x64"),
289
+ os.path.join(parent_dir, "python"),
290
+ ]
291
+ ordered = []
292
+ seen = set()
293
+ for path in candidates:
294
+ if not path or not os.path.isdir(path):
295
+ continue
296
+ path = os.path.abspath(path)
297
+ key = os.path.normcase(path)
298
+ if key in seen:
299
+ continue
300
+ seen.add(key)
301
+ ordered.append(path)
302
+ if ordered:
303
+ sys.path[:] = ordered + [
304
+ path for path in sys.path
305
+ if os.path.normcase(os.path.abspath(path or os.curdir)) not in seen
306
+ ]
307
+ except Exception:
308
+ pass
309
+
310
+
311
+ def _runtime_config_paths():
312
+ try:
313
+ base_dir = _entry_base_dir()
314
+ parent_dir = os.path.dirname(base_dir)
315
+ candidates = []
316
+ env_path = os.environ.get("CFQUANT_BRIDGE_CONFIG_FILE")
317
+ if env_path:
318
+ candidates.append(env_path)
319
+ market = str(os.environ.get("CFQUANT_MARKET") or QMT_MARKET or "").strip().upper()
320
+ market_filenames = []
321
+ if market in ("SH", "SZ"):
322
+ market_filenames.append("cfquant_bridge_config_%s.json" % market)
323
+ if os.path.basename(base_dir).lower() == "python":
324
+ for filename in market_filenames:
325
+ candidates.append(os.path.join(parent_dir, "bin.x64", filename))
326
+ candidates.append(os.path.join(base_dir, filename))
327
+ candidates.append(os.path.join(parent_dir, "bin.x64", "cfquant_bridge_config.json"))
328
+ candidates.append(os.path.join(base_dir, "cfquant_bridge_config.json"))
329
+ else:
330
+ for filename in market_filenames:
331
+ candidates.append(os.path.join(base_dir, filename))
332
+ candidates.append(os.path.join(base_dir, "bin.x64", filename))
333
+ candidates.append(os.path.join(base_dir, "cfquant_bridge_config.json"))
334
+ candidates.append(os.path.join(base_dir, "bin.x64", "cfquant_bridge_config.json"))
335
+ for filename in market_filenames:
336
+ candidates.append(os.path.join(parent_dir, filename))
337
+ candidates.append(os.path.join(parent_dir, "cfquant_bridge_config.json"))
338
+ result = []
339
+ seen = set()
340
+ for path in candidates:
341
+ if not path:
342
+ continue
343
+ path = os.path.abspath(os.path.expandvars(os.path.expanduser(path)))
344
+ key = os.path.normcase(path)
345
+ if key in seen:
346
+ continue
347
+ seen.add(key)
348
+ result.append(path)
349
+ return result
350
+ except Exception:
351
+ return []
352
+
353
+
354
+ def _load_runtime_config():
355
+ for path in _runtime_config_paths():
356
+ if not os.path.isfile(path):
357
+ continue
358
+ for index, opener in enumerate((
359
+ lambda: io.open(path, "r", encoding="utf-8"),
360
+ lambda: open(path, "r"),
361
+ )):
362
+ try:
363
+ with opener() as f:
364
+ data = json.loads(f.read())
365
+ if isinstance(data, dict):
366
+ return path, data
367
+ except Exception as e:
368
+ if index == 1:
369
+ _write_runtime_log("cfquant ctypes runtime config read failed path=%s error=%s" % (path, e))
370
+ return "", {}
371
+
372
+
373
+ def _config_bool(value, default=True):
374
+ if isinstance(value, bool):
375
+ return value
376
+ if value is None:
377
+ return default
378
+ text = str(value).strip().lower()
379
+ if text in ("0", "false", "no", "off", "disable", "disabled", "closed", "close"):
380
+ return False
381
+ if text in ("1", "true", "yes", "on", "enable", "enabled", "open"):
382
+ return True
383
+ return default
384
+
385
+
386
+ def _env_allows_runtime_override(name, default_value=""):
387
+ value = str(os.environ.get(name) or "").strip()
388
+ if not value:
389
+ return True
390
+ if str(os.environ.get("%s_SOURCE" % name) or "").strip() == "cfquant_entry":
391
+ return True
392
+ return bool(default_value and value == default_value)
393
+
394
+
395
+ def _apply_runtime_config():
396
+ global BRIDGE_ID, PIPE_NAME, RUNTIME_CONFIG_PATH, RUNTIME_CONFIG, RUNTIME_CHANNELS, QMT_MARKET, DEFAULT_ACCOUNT_ID, DEFAULT_ACCOUNT_TYPE
397
+
398
+ path, data = _load_runtime_config()
399
+ RUNTIME_CONFIG_PATH = path
400
+ RUNTIME_CONFIG = data
401
+ if not data:
402
+ _write_runtime_log("cfquant ctypes runtime config not found")
403
+ return
404
+ if data.get("account_id") and not DEFAULT_ACCOUNT_ID:
405
+ DEFAULT_ACCOUNT_ID = str(data.get("account_id") or "").strip()
406
+ if data.get("account_type"):
407
+ DEFAULT_ACCOUNT_TYPE = str(data.get("account_type") or DEFAULT_ACCOUNT_TYPE or "STOCK").strip().upper()
408
+ if data.get("bridge_id") and _env_allows_runtime_override("CFQUANT_BRIDGE_ID", USER_BRIDGE_ID):
409
+ BRIDGE_ID = data.get("bridge_id")
410
+ if data.get("market"):
411
+ market = str(data.get("market") or "").strip().upper()
412
+ if market in ("SH", "SZ"):
413
+ QMT_MARKET = market
414
+ if not os.environ.get("CFQUANT_MARKET"):
415
+ os.environ["CFQUANT_MARKET"] = market
416
+ os.environ["CFQUANT_MARKET_SOURCE"] = "cfquant_entry"
417
+ if data.get("pipe_name") and _env_allows_runtime_override("CFQUANT_PIPE_NAME", r"\\.\pipe\cfquant_pipe_hub"):
418
+ PIPE_NAME = data.get("pipe_name")
419
+ channels = data.get("channels") or {}
420
+ if isinstance(channels, dict):
421
+ RUNTIME_CHANNELS = channels
422
+ if not os.environ.get("CFQUANT_QMT_LOG_LANGUAGE") and data.get("qmt_log_language"):
423
+ os.environ["CFQUANT_QMT_LOG_LANGUAGE"] = str(data.get("qmt_log_language") or "zh")
424
+ if not os.environ.get("CFQUANT_QMT_LOG_ENABLED") and "qmt_log_enabled" in data:
425
+ os.environ["CFQUANT_QMT_LOG_ENABLED"] = "1" if _config_bool(data.get("qmt_log_enabled"), True) else "0"
426
+ _write_runtime_log(
427
+ "cfquant ctypes runtime config loaded path=%s bridge_id=%s pipe=%s"
428
+ % (path, BRIDGE_ID, PIPE_NAME)
429
+ )
430
+
431
+
432
+ _ensure_path()
433
+ _apply_runtime_config()
434
+ _write_runtime_log("cfquant ctypes entry executing file=%s sys_path_head=%s" % (_entry_file_path() or "<string>", sys.path[:5]))
435
+
436
+ _logging_i18n = _install_logging_compat()
437
+ get_log_enabled = _logging_i18n.get_log_enabled
438
+ translate_log = _logging_i18n.translate_log
439
+
440
+ try:
441
+ import cfquant as _cfquant
442
+ from cfquant import __version__ as _ENTRY_VERSION
443
+ from cfquant.protocol import loads_message
444
+ _write_runtime_log(
445
+ "cfquant import ok version=%s module_file=%s"
446
+ % (_ENTRY_VERSION, getattr(_cfquant, "__file__", "<unknown>"))
447
+ )
448
+ except Exception as e:
449
+ _write_runtime_log("cfquant import failed:%s sys_path_head=%s" % (e, sys.path[:8]))
450
+ raise
451
+
452
+
453
+ def _print_log(message):
454
+ if not get_log_enabled():
455
+ return
456
+ translated = translate_log(message)
457
+ print(translated)
458
+ _write_runtime_log(translated)
459
+
460
+
461
+ def _load_bridge_starters():
462
+ import cfquant.pipe_transport as pipe_transport
463
+ import cfquant.pipe_bridge as pipe_bridge
464
+
465
+ try:
466
+ pipe_transport = importlib.reload(pipe_transport)
467
+ except Exception as e:
468
+ _print_log("pipe transport reload failed:%s" % e)
469
+ try:
470
+ pipe_bridge = importlib.reload(pipe_bridge)
471
+ except Exception as e:
472
+ _print_log("pipe bridge reload failed:%s" % e)
473
+ return pipe_bridge.start_pipe_normal_bridge, pipe_bridge.start_pipe_trade_bridge
474
+
475
+
476
+ start_pipe_normal_bridge, start_pipe_trade_bridge = _load_bridge_starters()
477
+
478
+ from cfquant.channels import channels_for_bridge, normalize_bridge_id
479
+
480
+ BRIDGE_ID = normalize_bridge_id(BRIDGE_ID)
481
+ QMT_MARKET = str(QMT_MARKET or os.environ.get("CFQUANT_MARKET") or RUNTIME_CONFIG.get("market") or "").strip().upper()
482
+ if QMT_MARKET not in ("SH", "SZ"):
483
+ QMT_MARKET = ""
484
+ if not os.environ.get("CFQUANT_BRIDGE_ID"):
485
+ os.environ["CFQUANT_BRIDGE_ID"] = BRIDGE_ID
486
+ os.environ["CFQUANT_BRIDGE_ID_SOURCE"] = "cfquant_entry"
487
+ if QMT_MARKET and not os.environ.get("CFQUANT_MARKET"):
488
+ os.environ["CFQUANT_MARKET"] = QMT_MARKET
489
+ os.environ["CFQUANT_MARKET_SOURCE"] = "cfquant_entry"
490
+ if PIPE_NAME and not os.environ.get("CFQUANT_PIPE_NAME"):
491
+ os.environ["CFQUANT_PIPE_NAME"] = PIPE_NAME
492
+ os.environ["CFQUANT_PIPE_NAME_SOURCE"] = "cfquant_entry"
493
+ BRIDGE_CHANNELS = channels_for_bridge(BRIDGE_ID)
494
+ for _channel_key in ("normal", "trade", "callback"):
495
+ _channel_value = RUNTIME_CHANNELS.get(_channel_key) or RUNTIME_CONFIG.get("%s_channel" % _channel_key)
496
+ if _channel_value:
497
+ BRIDGE_CHANNELS[_channel_key] = str(_channel_value).strip()
498
+
499
+ _normal_bridge = start_pipe_normal_bridge(
500
+ None,
501
+ pipe_name=PIPE_NAME,
502
+ request_channel=BRIDGE_CHANNELS["normal"],
503
+ request_channels=[BRIDGE_CHANNELS["normal"]],
504
+ callback_event_channel=BRIDGE_CHANNELS["callback"],
505
+ bridge_id=BRIDGE_ID,
506
+ account_id=DEFAULT_ACCOUNT_ID,
507
+ show=True,
508
+ schedule_timer=True,
509
+ pump_max_count=NORMAL_PUMP_MAX_COUNT,
510
+ pump_max_ms=NORMAL_PUMP_MAX_MS,
511
+ connect_timeout_ms=PIPE_CONNECT_TIMEOUT_MS,
512
+ )
513
+ if DEFAULT_ACCOUNT_TYPE and _normal_bridge:
514
+ _normal_bridge.account_type = DEFAULT_ACCOUNT_TYPE
515
+
516
+ _trade_bridge = start_pipe_trade_bridge(
517
+ None,
518
+ pipe_name=PIPE_NAME,
519
+ request_channel=BRIDGE_CHANNELS["trade"],
520
+ bridge_id=BRIDGE_ID,
521
+ account_id=DEFAULT_ACCOUNT_ID,
522
+ show=True,
523
+ connect_timeout_ms=PIPE_CONNECT_TIMEOUT_MS,
524
+ )
525
+ if DEFAULT_ACCOUNT_TYPE and _trade_bridge:
526
+ _trade_bridge.account_type = DEFAULT_ACCOUNT_TYPE
527
+
528
+ _print_log("cfquant ctypes all-in-one lowlat bridge module loaded")
529
+ _print_log("cfquant ctypes all-in-one lowlat entry version:%s" % _ENTRY_VERSION)
530
+ _print_log("cfquant ctypes bridge id:%s account:%s/%s pipe:%s normal_channel:%s trade_channel:%s callback_channel:%s" % (
531
+ BRIDGE_ID,
532
+ DEFAULT_ACCOUNT_ID or "-",
533
+ DEFAULT_ACCOUNT_TYPE or "-",
534
+ PIPE_NAME,
535
+ BRIDGE_CHANNELS["normal"],
536
+ BRIDGE_CHANNELS["trade"],
537
+ BRIDGE_CHANNELS["callback"],
538
+ ))
539
+ if QMT_MARKET:
540
+ _print_log("cfquant ctypes market route market:%s bridge_id:%s" % (QMT_MARKET, BRIDGE_ID))
541
+ _print_log("cfquant ctypes trade loop in thread:%s sleep_seconds:%s" % (TRADE_LOOP_IN_THREAD, TRADE_SLEEP_SECONDS))
542
+
543
+
544
+ def _attach_normal_status_extra():
545
+ if not _normal_bridge:
546
+ return
547
+ original_status_extra = _normal_bridge._status_extra
548
+
549
+ def status_extra_with_trade():
550
+ data = original_status_extra()
551
+ data.update({
552
+ "qmt_runtime_core_version": _ENTRY_VERSION,
553
+ "qmt_runtime_module_file": getattr(_cfquant, "__file__", ""),
554
+ "qmt_runtime_entry_file": _entry_file_path() or "<string>",
555
+ "qmt_runtime_market": QMT_MARKET,
556
+ "qmt_runtime_market_role": RUNTIME_CONFIG.get("market_role") or ("trade" if QMT_MARKET else ""),
557
+ "qmt_runtime_market_parent_bridge_id": RUNTIME_CONFIG.get("market_route_parent_bridge_id") or "",
558
+ "ctype_trade_bridge_running": bool(_trade_bridge and _trade_bridge.running),
559
+ "ctype_trade_thread_alive": bool(_trade_thread and _trade_thread.is_alive()),
560
+ "ctype_trade_queue_size": _trade_request_queue.qsize(),
561
+ "ctype_trade_timer_key": _trade_timer_key,
562
+ "ctype_trade_loop_started_at": _trade_loop_started_at,
563
+ "ctype_trade_loop_error": _trade_loop_error,
564
+ "ctype_trade_recv_count": _trade_recv_count,
565
+ "ctype_trade_dispatch_count": _trade_dispatch_count,
566
+ "ctype_trade_direct_dispatch_count": _trade_direct_dispatch_count,
567
+ "ctype_trade_reroute_count": _trade_reroute_count,
568
+ "ctype_trade_queue_full_count": _trade_queue_full_count,
569
+ "ctype_trade_last_recv_at": _trade_last_recv_at,
570
+ "ctype_trade_last_dispatch_at": _trade_last_dispatch_at,
571
+ "ctype_trade_request_channel": BRIDGE_CHANNELS["trade"],
572
+ "ctype_trade_loop_in_thread": TRADE_LOOP_IN_THREAD,
573
+ "ctype_trade_sleep_seconds": TRADE_SLEEP_SECONDS,
574
+ "ctype_trade_pump_max_count": TRADE_PUMP_MAX_COUNT,
575
+ "ctype_trade_pump_max_ms": TRADE_PUMP_MAX_MS,
576
+ "ctype_trade_timer_interval_ms": TRADE_TIMER_INTERVAL_MS,
577
+ "ctype_trade_dispatch_thread": "qmt_timer_or_handlebar",
578
+ "ctype_trade_route_mode": "xttrader_to_normal_worker",
579
+ })
580
+ return data
581
+
582
+ _normal_bridge._status_extra = status_extra_with_trade
583
+
584
+
585
+ def _run_trade_loop():
586
+ global _trade_loop_error, _trade_recv_count, _trade_queue_full_count, _trade_last_recv_at
587
+
588
+ while _trade_bridge and _trade_bridge.running:
589
+ try:
590
+ tx = _trade_bridge.tx
591
+ if tx is None:
592
+ time.sleep(0.05)
593
+ continue
594
+ raw = tx.Q.get(timeout=TRADE_SLEEP_SECONDS)
595
+ if raw is None:
596
+ continue
597
+ _trade_request_queue.put_nowait(raw)
598
+ _trade_recv_count += 1
599
+ _trade_last_recv_at = time.time()
600
+ except Exception as e:
601
+ if isinstance(e, queue.Empty):
602
+ continue
603
+ if isinstance(e, queue.Full):
604
+ _trade_queue_full_count += 1
605
+ _trade_loop_error = "trade request queue full"
606
+ else:
607
+ _trade_loop_error = "%s:%s" % (type(e).__name__, e)
608
+ _print_log("cfquant ctypes lowlat trade loop error:%s" % _trade_loop_error)
609
+ try:
610
+ time.sleep(0.05)
611
+ except Exception:
612
+ pass
613
+
614
+
615
+ def _drain_trade_requests(source):
616
+ global _trade_dispatch_count, _trade_last_dispatch_at, _trade_loop_error
617
+
618
+ if not _trade_bridge:
619
+ return 0
620
+ start = time.perf_counter()
621
+ count = 0
622
+ while count < TRADE_PUMP_MAX_COUNT:
623
+ if TRADE_PUMP_MAX_MS > 0 and (time.perf_counter() - start) * 1000 >= TRADE_PUMP_MAX_MS:
624
+ break
625
+ try:
626
+ raw = _trade_request_queue.get_nowait()
627
+ except queue.Empty:
628
+ break
629
+ try:
630
+ _handle_trade_raw(raw)
631
+ _trade_dispatch_count += 1
632
+ _trade_last_dispatch_at = time.time()
633
+ except Exception as e:
634
+ _trade_loop_error = "%s:%s" % (type(e).__name__, e)
635
+ _print_log("cfquant ctypes lowlat trade dispatch error source=%s error=%s" % (source, _trade_loop_error))
636
+ count += 1
637
+ return count
638
+
639
+
640
+ def _handle_trade_raw(raw):
641
+ if _should_reroute_trade_raw(raw) and _normal_bridge:
642
+ return _reroute_trade_raw_to_normal(raw)
643
+ _handle_trade_raw_direct(raw)
644
+
645
+
646
+ def _should_reroute_trade_raw(raw):
647
+ msg = loads_message(raw)
648
+ if not msg or msg.get("type") != "request":
649
+ return False
650
+ action = str(msg.get("action") or "")
651
+ return action.startswith("xttrader.") or action == "cfquant.query_info"
652
+
653
+
654
+ def _reroute_trade_raw_to_normal(raw):
655
+ global _trade_reroute_count
656
+
657
+ _normal_bridge._handle_raw_from_thread(raw)
658
+ _trade_reroute_count += 1
659
+
660
+
661
+ def _handle_trade_raw_direct(raw):
662
+ global _trade_direct_dispatch_count
663
+
664
+ _trade_bridge._handle_raw(raw)
665
+ _trade_direct_dispatch_count += 1
666
+
667
+
668
+ def _start_trade_loop():
669
+ global _trade_thread, _trade_loop_started_at, _trade_loop_error
670
+
671
+ if not _trade_bridge:
672
+ return
673
+ if _trade_thread is not None and _trade_thread.is_alive():
674
+ return
675
+ if TRADE_LOOP_IN_THREAD:
676
+ _trade_loop_error = ""
677
+ _trade_loop_started_at = time.time()
678
+ _trade_thread = threading.Thread(target=_run_trade_loop)
679
+ _trade_thread.daemon = True
680
+ _trade_thread.start()
681
+ _print_log("cfquant ctypes lowlat trade loop started in worker thread")
682
+ return
683
+ _print_log("cfquant ctypes lowlat trade loop entering current QMT thread")
684
+ _run_trade_loop()
685
+
686
+
687
+ def cfquant_ctype_trade_timer(*args, **kwargs):
688
+ _drain_trade_requests("timer")
689
+
690
+
691
+ def _schedule_trade_timer(ContextInfo):
692
+ global _trade_timer_key
693
+
694
+ if _trade_timer_key or ContextInfo is None:
695
+ return
696
+ try:
697
+ first_time = dt.datetime.now() + dt.timedelta(seconds=1)
698
+ _trade_timer_key = ContextInfo.schedule_run(
699
+ cfquant_ctype_trade_timer,
700
+ first_time,
701
+ repeat_times=-1,
702
+ interval=dt.timedelta(milliseconds=TRADE_TIMER_INTERVAL_MS),
703
+ name="cfquant_ctype_trade_bridge_pump",
704
+ )
705
+ _print_log("cfquant ctypes lowlat trade timer scheduled key:%s interval_ms:%s" % (_trade_timer_key, TRADE_TIMER_INTERVAL_MS))
706
+ except Exception as e:
707
+ _print_log("cfquant ctypes lowlat trade timer schedule failed:%s" % e)
708
+
709
+
710
+ _attach_normal_status_extra()
711
+ if TRADE_LOOP_IN_THREAD:
712
+ _start_trade_loop()
713
+
714
+
715
+ _QMT_TRADE_CALLBACK_REGISTERED = False
716
+
717
+
718
+ def _register_qmt_trade_callback(ContextInfo, stage):
719
+ global _QMT_TRADE_CALLBACK_REGISTERED
720
+
721
+ if ContextInfo is None or _QMT_TRADE_CALLBACK_REGISTERED:
722
+ return
723
+ func = getattr(ContextInfo, "register_callback", None)
724
+ if not callable(func):
725
+ _print_log("cfquant ctypes qmt trade callback register skipped stage=%s reason=missing register_callback" % stage)
726
+ return
727
+ try:
728
+ func(0)
729
+ _QMT_TRADE_CALLBACK_REGISTERED = True
730
+ _print_log("cfquant ctypes qmt trade callback registered stage=%s" % stage)
731
+ except Exception as e:
732
+ _print_log("cfquant ctypes qmt trade callback register failed stage=%s error=%s" % (stage, e))
733
+
734
+
735
+ def _refresh_auto_trade_callback(stage):
736
+ for bridge_name, bridge in (("normal", _normal_bridge), ("trade", _trade_bridge)):
737
+ if bridge is None or not hasattr(bridge, "_enable_auto_trade_callback"):
738
+ continue
739
+ try:
740
+ bridge.auto_trade_callback_enabled = False
741
+ bridge._enable_auto_trade_callback()
742
+ _print_log("cfquant ctypes auto trade callback refreshed stage=%s bridge=%s" % (stage, bridge_name))
743
+ except Exception as e:
744
+ _print_log("cfquant ctypes auto trade callback refresh failed stage=%s bridge=%s error=%s" % (stage, bridge_name, e))
745
+
746
+
747
+ def _callback_brief(obj):
748
+ try:
749
+ parts = []
750
+ for name in ("account_id", "m_strAccountID", "m_strInstrumentID", "m_strExchangeID", "m_strOrderSysID", "m_nOrderID", "m_strRemark"):
751
+ value = getattr(obj, name, None)
752
+ if value is None and hasattr(obj, "get"):
753
+ value = obj.get(name)
754
+ if value not in (None, ""):
755
+ parts.append("%s=%s" % (name, value))
756
+ return " ".join(parts) or type(obj).__name__
757
+ except Exception:
758
+ return type(obj).__name__
759
+
760
+
761
+ def _object_to_callback_dict(obj):
762
+ if hasattr(obj, "items"):
763
+ return dict(obj)
764
+ if hasattr(obj, "__dict__"):
765
+ return dict(vars(obj))
766
+ return {"value": str(obj)}
767
+
768
+
769
+ def init(ContextInfo):
770
+ _register_qmt_trade_callback(ContextInfo, "init")
771
+ if _normal_bridge:
772
+ _normal_bridge.set_context(ContextInfo)
773
+ _print_log("cfquant ctypes normal context ready version:%s" % _ENTRY_VERSION)
774
+ if _trade_bridge:
775
+ _trade_bridge.set_context(ContextInfo)
776
+ _print_log("cfquant ctypes lowlat trade context ready version:%s" % _ENTRY_VERSION)
777
+ _start_trade_loop()
778
+ _schedule_trade_timer(ContextInfo)
779
+
780
+
781
+ def after_init(ContextInfo):
782
+ _register_qmt_trade_callback(ContextInfo, "after_init")
783
+ _refresh_auto_trade_callback("after_init")
784
+
785
+
786
+ def handlebar(ContextInfo):
787
+ if TRADE_LOOP_IN_THREAD:
788
+ _start_trade_loop()
789
+ _drain_trade_requests("handlebar")
790
+ if _normal_bridge:
791
+ _normal_bridge.pump()
792
+
793
+
794
+ def stop(ContextInfo):
795
+ global _normal_bridge, _trade_bridge, _trade_timer_key
796
+
797
+ if ContextInfo is not None and _trade_timer_key:
798
+ try:
799
+ ContextInfo.cancel_schedule_run(_trade_timer_key)
800
+ except Exception as e:
801
+ _print_log("cfquant ctypes lowlat trade timer cancel failed:%s" % e)
802
+ _trade_timer_key = None
803
+
804
+ if _trade_bridge:
805
+ _trade_bridge.close()
806
+ _trade_bridge = None
807
+ _print_log("cfquant ctypes lowlat trade bridge stopped")
808
+ if _normal_bridge:
809
+ _normal_bridge.close()
810
+ _normal_bridge = None
811
+ _print_log("cfquant ctypes normal bridge stopped")
812
+
813
+
814
+ def _publish_callback(event_name, obj):
815
+ try:
816
+ _print_log("cfquant ctypes raw qmt callback received event=%s %s" % (event_name, _callback_brief(obj)))
817
+ if _normal_bridge:
818
+ _normal_bridge.publish_callback_event(event_name, obj)
819
+ except Exception as e:
820
+ _print_log("cfquant ctypes lowlat callback publish failed event=%s error=%s" % (event_name, e))
821
+
822
+
823
+ def account_callback(ContextInfo, accountInfo):
824
+ _publish_callback("trader:on_stock_asset", accountInfo)
825
+
826
+
827
+ def order_callback(ContextInfo, orderInfo):
828
+ _publish_callback("trader:on_stock_order", orderInfo)
829
+
830
+
831
+ def deal_callback(ContextInfo, dealInfo):
832
+ _publish_callback("trader:on_stock_trade", dealInfo)
833
+
834
+
835
+ def trade_callback(ContextInfo, tradeInfo):
836
+ _publish_callback("trader:on_stock_trade", tradeInfo)
837
+
838
+
839
+ def position_callback(ContextInfo, positionInfo):
840
+ _publish_callback("trader:on_stock_position", positionInfo)
841
+
842
+
843
+ def order_error_callback(ContextInfo, orderError):
844
+ _publish_callback("trader:on_order_error", orderError)
845
+
846
+
847
+ def orderError_callback(ContextInfo, passOrderInfo, msg):
848
+ data = _object_to_callback_dict(passOrderInfo)
849
+ data["error_msg"] = msg
850
+ _publish_callback("trader:on_order_error", data)
851
+
852
+
853
+ def cancel_error_callback(ContextInfo, cancelError):
854
+ _publish_callback("trader:on_cancel_error", cancelError)
855
+
856
+
857
+ def cancelError_callback(ContextInfo, cancelError):
858
+ _publish_callback("trader:on_cancel_error", cancelError)
859
+
860
+
861
+ def order_stock_async_response_callback(ContextInfo, response):
862
+ _publish_callback("trader:on_order_stock_async_response", response)
863
+
864
+
865
+ def cancel_order_stock_async_response_callback(ContextInfo, response):
866
+ _publish_callback("trader:on_cancel_order_stock_async_response", response)