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.
- LTtx/__init__.py +2 -0
- LTtx/tx/Config.txt +8 -0
- LTtx/tx/LTtx_server.py +1177 -0
- LTtx/tx/__init__.py +5 -0
- LTtx/tx/tx.py +1935 -0
- cfquant/__init__.py +26 -0
- cfquant/account_routing.py +119 -0
- cfquant/channels.py +76 -0
- cfquant/cli.py +266 -0
- cfquant/client.py +365 -0
- cfquant/config.py +86 -0
- cfquant/hub.py +304 -0
- cfquant/logging_i18n.py +348 -0
- cfquant/normal_bridge.py +676 -0
- cfquant/pipe_bridge.py +249 -0
- cfquant/pipe_client.py +223 -0
- cfquant/pipe_hub.py +602 -0
- cfquant/pipe_transport.py +493 -0
- cfquant/protocol.py +238 -0
- cfquant/qmt_bridge.py +1081 -0
- cfquant/runtime_report.py +334 -0
- cfquant/tx.py +1898 -0
- cfquant/tx_trade_bridge.py +1905 -0
- cfquant/version.py +5 -0
- cfquant/xtconstant.py +262 -0
- cfquant/xtdata.py +462 -0
- cfquant/xttrader.py +955 -0
- cfquant/xttype.py +586 -0
- cfquant-0.2.0.dist-info/LICENSE +21 -0
- cfquant-0.2.0.dist-info/METADATA +144 -0
- cfquant-0.2.0.dist-info/RECORD +90 -0
- cfquant-0.2.0.dist-info/WHEEL +5 -0
- cfquant-0.2.0.dist-info/entry_points.txt +4 -0
- cfquant-0.2.0.dist-info/top_level.txt +6 -0
- cfquant_pipe_hub.py +45 -0
- cfquant_web_server.py +12137 -0
- qmt_scripts/CFQUANT.py +507 -0
- qmt_scripts/CFQUANT_CTYPE_ALL_LOWLAT.py +830 -0
- qmt_scripts/CFQUANT_LITE.py +4928 -0
- qmt_scripts/CFQUANT_TRADE_LOWLAT.py +382 -0
- qmt_scripts/__init__.py +1 -0
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- web_dashboard/__init__.py +1 -0
- web_dashboard/app.js +9847 -0
- web_dashboard/avatars/amber-pulse.svg +15 -0
- web_dashboard/avatars/copper-grid.svg +15 -0
- web_dashboard/avatars/market-blue.svg +15 -0
- web_dashboard/avatars/rose-circuit.svg +15 -0
- web_dashboard/avatars/signal-green.svg +15 -0
- web_dashboard/avatars/slate-wave.svg +15 -0
- web_dashboard/avatars/teal-orbit.svg +16 -0
- web_dashboard/avatars/violet-node.svg +15 -0
- web_dashboard/favicon.svg +5 -0
- web_dashboard/image/qmt_deploy/image-10.png +0 -0
- web_dashboard/image/qmt_deploy/image-11.png +0 -0
- web_dashboard/image/qmt_deploy/image-12.png +0 -0
- web_dashboard/image/qmt_deploy/image-13.png +0 -0
- web_dashboard/image/qmt_deploy/image-14.png +0 -0
- web_dashboard/image/qmt_deploy/image-15.png +0 -0
- web_dashboard/image/qmt_deploy/image-16.png +0 -0
- web_dashboard/image/qmt_deploy/image-17.png +0 -0
- web_dashboard/image/qmt_deploy/image-18.png +0 -0
- web_dashboard/image/qmt_deploy/image-19.png +0 -0
- web_dashboard/image/qmt_deploy/image-20.png +0 -0
- web_dashboard/image/qmt_deploy/image-21.png +0 -0
- web_dashboard/image/qmt_deploy/image-4.png +0 -0
- web_dashboard/image/qmt_deploy/image-5.png +0 -0
- web_dashboard/image/qmt_deploy/image-6.png +0 -0
- web_dashboard/image/qmt_deploy/image-7.png +0 -0
- web_dashboard/image/qmt_deploy/image-8.png +0 -0
- web_dashboard/image/qmt_deploy/image-9.png +0 -0
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- web_dashboard/index.html +2317 -0
- web_dashboard/styles.css +7817 -0
- web_dashboard/vendor/mermaid.min.js +2029 -0
qmt_scripts/CFQUANT.py
ADDED
|
@@ -0,0 +1,507 @@
|
|
|
1
|
+
#coding:gbk
|
|
2
|
+
#! /usr/bin/python
|
|
3
|
+
|
|
4
|
+
import os
|
|
5
|
+
import sys
|
|
6
|
+
import importlib
|
|
7
|
+
import datetime as dt
|
|
8
|
+
import io
|
|
9
|
+
import json
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
_cf_bridge = None
|
|
13
|
+
_cf_timer_key = None
|
|
14
|
+
_TIMER_INTERVAL_MS = 500
|
|
15
|
+
_PUMP_MAX_COUNT = 20
|
|
16
|
+
_PUMP_MAX_MS = 0
|
|
17
|
+
DEFAULT_ACCOUNT_ID = str(os.environ.get("CFQUANT_ACCOUNT_ID") or "").strip()
|
|
18
|
+
DEFAULT_ACCOUNT_TYPE = str(os.environ.get("CFQUANT_ACCOUNT_TYPE") or "STOCK").strip().upper()
|
|
19
|
+
USER_BRIDGE_ID = "default"
|
|
20
|
+
BRIDGE_ID = os.environ.get("CFQUANT_BRIDGE_ID", USER_BRIDGE_ID)
|
|
21
|
+
RUNTIME_CONFIG = {}
|
|
22
|
+
RUNTIME_CHANNELS = {}
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def _entry_file_path():
|
|
26
|
+
path = globals().get("__file__") or ""
|
|
27
|
+
path = str(path or "").strip()
|
|
28
|
+
if path and not path.startswith("<"):
|
|
29
|
+
try:
|
|
30
|
+
return os.path.abspath(path)
|
|
31
|
+
except Exception:
|
|
32
|
+
return path
|
|
33
|
+
return ""
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def _entry_base_dir():
|
|
37
|
+
entry_file = _entry_file_path()
|
|
38
|
+
if entry_file:
|
|
39
|
+
return os.path.dirname(entry_file)
|
|
40
|
+
for name in ("CFQUANT_QMT_SCRIPT_DIR", "CFQUANT_SCRIPT_DIR", "CFQUANT_ENTRY_DIR"):
|
|
41
|
+
path = str(os.environ.get(name) or "").strip()
|
|
42
|
+
if path and os.path.isdir(path):
|
|
43
|
+
return os.path.abspath(path)
|
|
44
|
+
try:
|
|
45
|
+
cwd = os.path.abspath(os.getcwd())
|
|
46
|
+
if (
|
|
47
|
+
os.path.isfile(os.path.join(cwd, "CFQUANT.py"))
|
|
48
|
+
or os.path.isfile(os.path.join(cwd, "cfquant_bridge_config.json"))
|
|
49
|
+
or os.path.isdir(os.path.join(cwd, "cfquant"))
|
|
50
|
+
):
|
|
51
|
+
return cwd
|
|
52
|
+
except Exception:
|
|
53
|
+
pass
|
|
54
|
+
for path in sys.path:
|
|
55
|
+
path = str(path or "").strip()
|
|
56
|
+
if path and os.path.isdir(path):
|
|
57
|
+
base = os.path.abspath(path)
|
|
58
|
+
if (
|
|
59
|
+
os.path.isfile(os.path.join(base, "CFQUANT.py"))
|
|
60
|
+
or os.path.isfile(os.path.join(base, "cfquant_bridge_config.json"))
|
|
61
|
+
or os.path.isdir(os.path.join(base, "cfquant"))
|
|
62
|
+
):
|
|
63
|
+
return base
|
|
64
|
+
try:
|
|
65
|
+
return os.path.abspath(os.getcwd())
|
|
66
|
+
except Exception:
|
|
67
|
+
return ""
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def _runtime_config_paths():
|
|
71
|
+
try:
|
|
72
|
+
base_dir = _entry_base_dir()
|
|
73
|
+
parent_dir = os.path.dirname(base_dir)
|
|
74
|
+
candidates = []
|
|
75
|
+
env_path = os.environ.get("CFQUANT_BRIDGE_CONFIG_FILE")
|
|
76
|
+
if env_path:
|
|
77
|
+
candidates.append(env_path)
|
|
78
|
+
if os.path.basename(base_dir).lower() == "python":
|
|
79
|
+
candidates.append(os.path.join(parent_dir, "bin.x64", "cfquant_bridge_config.json"))
|
|
80
|
+
candidates.append(os.path.join(base_dir, "cfquant_bridge_config.json"))
|
|
81
|
+
else:
|
|
82
|
+
candidates.append(os.path.join(base_dir, "cfquant_bridge_config.json"))
|
|
83
|
+
candidates.append(os.path.join(base_dir, "bin.x64", "cfquant_bridge_config.json"))
|
|
84
|
+
candidates.append(os.path.join(parent_dir, "cfquant_bridge_config.json"))
|
|
85
|
+
result = []
|
|
86
|
+
seen = set()
|
|
87
|
+
for path in candidates:
|
|
88
|
+
if not path:
|
|
89
|
+
continue
|
|
90
|
+
path = os.path.abspath(os.path.expandvars(os.path.expanduser(path)))
|
|
91
|
+
key = os.path.normcase(path)
|
|
92
|
+
if key in seen:
|
|
93
|
+
continue
|
|
94
|
+
seen.add(key)
|
|
95
|
+
result.append(path)
|
|
96
|
+
return result
|
|
97
|
+
except Exception:
|
|
98
|
+
return []
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def _load_runtime_config():
|
|
102
|
+
for path in _runtime_config_paths():
|
|
103
|
+
if not os.path.isfile(path):
|
|
104
|
+
continue
|
|
105
|
+
for opener in (
|
|
106
|
+
lambda: io.open(path, "r", encoding="utf-8"),
|
|
107
|
+
lambda: open(path, "r"),
|
|
108
|
+
):
|
|
109
|
+
try:
|
|
110
|
+
with opener() as f:
|
|
111
|
+
data = json.loads(f.read())
|
|
112
|
+
if isinstance(data, dict):
|
|
113
|
+
return data
|
|
114
|
+
except Exception:
|
|
115
|
+
pass
|
|
116
|
+
return {}
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def _config_bool(value, default=True):
|
|
120
|
+
if isinstance(value, bool):
|
|
121
|
+
return value
|
|
122
|
+
if value is None:
|
|
123
|
+
return default
|
|
124
|
+
text = str(value).strip().lower()
|
|
125
|
+
if text in ("0", "false", "no", "off", "disable", "disabled", "closed", "close"):
|
|
126
|
+
return False
|
|
127
|
+
if text in ("1", "true", "yes", "on", "enable", "enabled", "open"):
|
|
128
|
+
return True
|
|
129
|
+
return default
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def _env_allows_runtime_override(name, default_value=""):
|
|
133
|
+
value = str(os.environ.get(name) or "").strip()
|
|
134
|
+
if not value:
|
|
135
|
+
return True
|
|
136
|
+
if str(os.environ.get("%s_SOURCE" % name) or "").strip() == "cfquant_entry":
|
|
137
|
+
return True
|
|
138
|
+
return bool(default_value and value == default_value)
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
def _apply_runtime_config():
|
|
142
|
+
global BRIDGE_ID, RUNTIME_CONFIG, RUNTIME_CHANNELS, DEFAULT_ACCOUNT_ID, DEFAULT_ACCOUNT_TYPE
|
|
143
|
+
|
|
144
|
+
data = _load_runtime_config()
|
|
145
|
+
RUNTIME_CONFIG = data
|
|
146
|
+
if not data:
|
|
147
|
+
return
|
|
148
|
+
if data.get("account_id") and not DEFAULT_ACCOUNT_ID:
|
|
149
|
+
DEFAULT_ACCOUNT_ID = str(data.get("account_id") or "").strip()
|
|
150
|
+
if data.get("account_type"):
|
|
151
|
+
DEFAULT_ACCOUNT_TYPE = str(data.get("account_type") or DEFAULT_ACCOUNT_TYPE or "STOCK").strip().upper()
|
|
152
|
+
if data.get("bridge_id") and _env_allows_runtime_override("CFQUANT_BRIDGE_ID", USER_BRIDGE_ID):
|
|
153
|
+
BRIDGE_ID = data.get("bridge_id")
|
|
154
|
+
channels = data.get("channels") or {}
|
|
155
|
+
if isinstance(channels, dict):
|
|
156
|
+
RUNTIME_CHANNELS = channels
|
|
157
|
+
if not os.environ.get("CFQUANT_QMT_LOG_LANGUAGE") and data.get("qmt_log_language"):
|
|
158
|
+
os.environ["CFQUANT_QMT_LOG_LANGUAGE"] = str(data.get("qmt_log_language") or "zh")
|
|
159
|
+
if not os.environ.get("CFQUANT_QMT_LOG_ENABLED") and "qmt_log_enabled" in data:
|
|
160
|
+
os.environ["CFQUANT_QMT_LOG_ENABLED"] = "1" if _config_bool(data.get("qmt_log_enabled"), True) else "0"
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
def _ensure_path():
|
|
164
|
+
try:
|
|
165
|
+
base_dir = _entry_base_dir()
|
|
166
|
+
parent_dir = os.path.dirname(base_dir)
|
|
167
|
+
env_paths = [p for p in os.environ.get("CFQUANT_PYTHONPATH", "").split(os.pathsep) if p]
|
|
168
|
+
if os.path.basename(base_dir).lower() == "python":
|
|
169
|
+
candidates = env_paths + [os.path.join(parent_dir, "bin.x64"), base_dir, parent_dir]
|
|
170
|
+
else:
|
|
171
|
+
candidates = env_paths + [
|
|
172
|
+
base_dir,
|
|
173
|
+
os.path.join(base_dir, "bin.x64"),
|
|
174
|
+
parent_dir,
|
|
175
|
+
os.path.join(parent_dir, "bin.x64"),
|
|
176
|
+
os.path.join(parent_dir, "python"),
|
|
177
|
+
]
|
|
178
|
+
ordered = []
|
|
179
|
+
seen = set()
|
|
180
|
+
for path in candidates:
|
|
181
|
+
if not path or not os.path.isdir(path):
|
|
182
|
+
continue
|
|
183
|
+
path = os.path.abspath(path)
|
|
184
|
+
key = os.path.normcase(path)
|
|
185
|
+
if key in seen:
|
|
186
|
+
continue
|
|
187
|
+
seen.add(key)
|
|
188
|
+
ordered.append(path)
|
|
189
|
+
if ordered:
|
|
190
|
+
sys.path[:] = ordered + [
|
|
191
|
+
path for path in sys.path
|
|
192
|
+
if os.path.normcase(os.path.abspath(path or os.curdir)) not in seen
|
|
193
|
+
]
|
|
194
|
+
except Exception:
|
|
195
|
+
pass
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
_ensure_path()
|
|
199
|
+
_apply_runtime_config()
|
|
200
|
+
|
|
201
|
+
from cfquant import __version__ as _ENTRY_VERSION
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
def _install_logging_compat():
|
|
205
|
+
"""Keep this entry compatible with older cfquant packages in QMT."""
|
|
206
|
+
logging_module = importlib.import_module("cfquant.logging_i18n")
|
|
207
|
+
|
|
208
|
+
if not hasattr(logging_module, "normalize_log_language"):
|
|
209
|
+
def _normalize_log_language(value=None):
|
|
210
|
+
value = str(value or "").strip().lower()
|
|
211
|
+
return "en" if value in ("en", "english") else "zh"
|
|
212
|
+
|
|
213
|
+
logging_module.normalize_log_language = _normalize_log_language
|
|
214
|
+
|
|
215
|
+
if not hasattr(logging_module, "normalize_log_enabled"):
|
|
216
|
+
def _normalize_log_enabled(value=None):
|
|
217
|
+
if isinstance(value, bool):
|
|
218
|
+
return value
|
|
219
|
+
if value is None:
|
|
220
|
+
return True
|
|
221
|
+
text = str(value).strip().lower()
|
|
222
|
+
return text not in ("0", "false", "no", "off", "disable", "disabled", "closed", "close")
|
|
223
|
+
|
|
224
|
+
logging_module.normalize_log_enabled = _normalize_log_enabled
|
|
225
|
+
|
|
226
|
+
if not hasattr(logging_module, "_CFQUANT_COMPAT_LOG_LANGUAGE"):
|
|
227
|
+
logging_module._CFQUANT_COMPAT_LOG_LANGUAGE = ""
|
|
228
|
+
if not hasattr(logging_module, "_CFQUANT_COMPAT_LOG_ENABLED"):
|
|
229
|
+
logging_module._CFQUANT_COMPAT_LOG_ENABLED = None
|
|
230
|
+
|
|
231
|
+
if not hasattr(logging_module, "get_log_language"):
|
|
232
|
+
def _get_log_language():
|
|
233
|
+
if logging_module._CFQUANT_COMPAT_LOG_LANGUAGE:
|
|
234
|
+
return logging_module._CFQUANT_COMPAT_LOG_LANGUAGE
|
|
235
|
+
return logging_module.normalize_log_language(
|
|
236
|
+
os.environ.get("CFQUANT_QMT_LOG_LANGUAGE")
|
|
237
|
+
or os.environ.get("CFQUANT_LOG_LANGUAGE")
|
|
238
|
+
or "zh"
|
|
239
|
+
)
|
|
240
|
+
|
|
241
|
+
logging_module.get_log_language = _get_log_language
|
|
242
|
+
|
|
243
|
+
if not hasattr(logging_module, "set_log_language"):
|
|
244
|
+
def _set_log_language(value):
|
|
245
|
+
language = logging_module.normalize_log_language(value)
|
|
246
|
+
logging_module._CFQUANT_COMPAT_LOG_LANGUAGE = language
|
|
247
|
+
return language
|
|
248
|
+
|
|
249
|
+
logging_module.set_log_language = _set_log_language
|
|
250
|
+
|
|
251
|
+
if not hasattr(logging_module, "get_log_enabled"):
|
|
252
|
+
def _get_log_enabled():
|
|
253
|
+
if logging_module._CFQUANT_COMPAT_LOG_ENABLED is not None:
|
|
254
|
+
return bool(logging_module._CFQUANT_COMPAT_LOG_ENABLED)
|
|
255
|
+
return logging_module.normalize_log_enabled(
|
|
256
|
+
os.environ.get("CFQUANT_QMT_LOG_ENABLED")
|
|
257
|
+
or os.environ.get("CFQUANT_LOG_ENABLED")
|
|
258
|
+
or "1"
|
|
259
|
+
)
|
|
260
|
+
|
|
261
|
+
logging_module.get_log_enabled = _get_log_enabled
|
|
262
|
+
|
|
263
|
+
if not hasattr(logging_module, "set_log_enabled"):
|
|
264
|
+
def _set_log_enabled(value):
|
|
265
|
+
enabled = logging_module.normalize_log_enabled(value)
|
|
266
|
+
logging_module._CFQUANT_COMPAT_LOG_ENABLED = enabled
|
|
267
|
+
return enabled
|
|
268
|
+
|
|
269
|
+
logging_module.set_log_enabled = _set_log_enabled
|
|
270
|
+
|
|
271
|
+
if not hasattr(logging_module, "translate_log"):
|
|
272
|
+
def _translate_log(message, language=None):
|
|
273
|
+
return str(message)
|
|
274
|
+
|
|
275
|
+
logging_module.translate_log = _translate_log
|
|
276
|
+
|
|
277
|
+
return logging_module
|
|
278
|
+
|
|
279
|
+
|
|
280
|
+
_logging_i18n = _install_logging_compat()
|
|
281
|
+
get_log_enabled = _logging_i18n.get_log_enabled
|
|
282
|
+
translate_log = _logging_i18n.translate_log
|
|
283
|
+
|
|
284
|
+
|
|
285
|
+
def _print_log(message):
|
|
286
|
+
if get_log_enabled():
|
|
287
|
+
print(translate_log(message))
|
|
288
|
+
|
|
289
|
+
|
|
290
|
+
def _load_bridge_starter():
|
|
291
|
+
import cfquant.tx_trade_bridge as tx_trade_bridge
|
|
292
|
+
import cfquant.normal_bridge as normal_bridge
|
|
293
|
+
|
|
294
|
+
try:
|
|
295
|
+
tx_trade_bridge = importlib.reload(tx_trade_bridge)
|
|
296
|
+
except Exception as e:
|
|
297
|
+
_print_log("tx trade bridge reload failed:%s" % e)
|
|
298
|
+
try:
|
|
299
|
+
normal_bridge = importlib.reload(normal_bridge)
|
|
300
|
+
except Exception as e:
|
|
301
|
+
_print_log("normal bridge reload failed:%s" % e)
|
|
302
|
+
return normal_bridge.start_normal_bridge
|
|
303
|
+
|
|
304
|
+
|
|
305
|
+
start_normal_bridge = _load_bridge_starter()
|
|
306
|
+
|
|
307
|
+
from cfquant.channels import channels_for_bridge, normalize_bridge_id
|
|
308
|
+
|
|
309
|
+
BRIDGE_ID = normalize_bridge_id(BRIDGE_ID)
|
|
310
|
+
BRIDGE_CHANNELS = channels_for_bridge(BRIDGE_ID)
|
|
311
|
+
for _channel_key in ("normal", "callback"):
|
|
312
|
+
_channel_value = RUNTIME_CHANNELS.get(_channel_key) or RUNTIME_CONFIG.get("%s_channel" % _channel_key)
|
|
313
|
+
if _channel_value:
|
|
314
|
+
BRIDGE_CHANNELS[_channel_key] = str(_channel_value).strip()
|
|
315
|
+
|
|
316
|
+
_cf_bridge = start_normal_bridge(
|
|
317
|
+
None,
|
|
318
|
+
ip="127.0.0.1",
|
|
319
|
+
port=2049,
|
|
320
|
+
token="LTtx",
|
|
321
|
+
request_channel=BRIDGE_CHANNELS["normal"],
|
|
322
|
+
callback_event_channel=BRIDGE_CHANNELS["callback"],
|
|
323
|
+
bridge_id=BRIDGE_ID,
|
|
324
|
+
account_id=DEFAULT_ACCOUNT_ID,
|
|
325
|
+
show=True,
|
|
326
|
+
schedule_timer=False,
|
|
327
|
+
pump_max_count=_PUMP_MAX_COUNT,
|
|
328
|
+
pump_max_ms=_PUMP_MAX_MS,
|
|
329
|
+
)
|
|
330
|
+
if DEFAULT_ACCOUNT_TYPE and _cf_bridge:
|
|
331
|
+
_cf_bridge.account_type = DEFAULT_ACCOUNT_TYPE
|
|
332
|
+
_print_log("cfquant normal bridge module loaded")
|
|
333
|
+
_print_log("cfquant entry version:%s" % _ENTRY_VERSION)
|
|
334
|
+
_print_log("cfquant bridge id:%s account:%s/%s normal_channel:%s callback_channel:%s" % (
|
|
335
|
+
BRIDGE_ID,
|
|
336
|
+
DEFAULT_ACCOUNT_ID or "-",
|
|
337
|
+
DEFAULT_ACCOUNT_TYPE or "-",
|
|
338
|
+
BRIDGE_CHANNELS["normal"],
|
|
339
|
+
BRIDGE_CHANNELS["callback"],
|
|
340
|
+
))
|
|
341
|
+
_print_log("cfquant normal bridge pump max_count:%s max_ms:%s" % (_PUMP_MAX_COUNT, _PUMP_MAX_MS))
|
|
342
|
+
|
|
343
|
+
|
|
344
|
+
def cfquant_normal_timer(*args, **kwargs):
|
|
345
|
+
if _cf_bridge:
|
|
346
|
+
_cf_bridge.on_timer(*args, **kwargs)
|
|
347
|
+
|
|
348
|
+
|
|
349
|
+
def _schedule_cf_timer(ContextInfo):
|
|
350
|
+
global _cf_timer_key
|
|
351
|
+
|
|
352
|
+
if _cf_timer_key or ContextInfo is None:
|
|
353
|
+
return
|
|
354
|
+
try:
|
|
355
|
+
first_time = dt.datetime.now() + dt.timedelta(seconds=1)
|
|
356
|
+
_cf_timer_key = ContextInfo.schedule_run(
|
|
357
|
+
cfquant_normal_timer,
|
|
358
|
+
first_time,
|
|
359
|
+
repeat_times=-1,
|
|
360
|
+
interval=dt.timedelta(milliseconds=_TIMER_INTERVAL_MS),
|
|
361
|
+
name="cfquant_normal_bridge_pump",
|
|
362
|
+
)
|
|
363
|
+
_print_log("cfquant normal bridge timer scheduled key:%s interval_ms:%s" % (_cf_timer_key, _TIMER_INTERVAL_MS))
|
|
364
|
+
except Exception as e:
|
|
365
|
+
_print_log("cfquant normal bridge timer schedule failed:%s" % e)
|
|
366
|
+
|
|
367
|
+
|
|
368
|
+
_QMT_TRADE_CALLBACK_REGISTERED = False
|
|
369
|
+
|
|
370
|
+
|
|
371
|
+
def _register_qmt_trade_callback(ContextInfo, stage):
|
|
372
|
+
global _QMT_TRADE_CALLBACK_REGISTERED
|
|
373
|
+
|
|
374
|
+
if ContextInfo is None or _QMT_TRADE_CALLBACK_REGISTERED:
|
|
375
|
+
return
|
|
376
|
+
func = getattr(ContextInfo, "register_callback", None)
|
|
377
|
+
if not callable(func):
|
|
378
|
+
_print_log("cfquant qmt trade callback register skipped stage=%s reason=missing register_callback" % stage)
|
|
379
|
+
return
|
|
380
|
+
try:
|
|
381
|
+
func(0)
|
|
382
|
+
_QMT_TRADE_CALLBACK_REGISTERED = True
|
|
383
|
+
_print_log("cfquant qmt trade callback registered stage=%s" % stage)
|
|
384
|
+
except Exception as e:
|
|
385
|
+
_print_log("cfquant qmt trade callback register failed stage=%s error=%s" % (stage, e))
|
|
386
|
+
|
|
387
|
+
|
|
388
|
+
def _refresh_auto_trade_callback(stage):
|
|
389
|
+
if _cf_bridge is None or not hasattr(_cf_bridge, "_enable_auto_trade_callback"):
|
|
390
|
+
return
|
|
391
|
+
try:
|
|
392
|
+
_cf_bridge.auto_trade_callback_enabled = False
|
|
393
|
+
_cf_bridge._enable_auto_trade_callback()
|
|
394
|
+
_print_log("cfquant auto trade callback refreshed stage=%s" % stage)
|
|
395
|
+
except Exception as e:
|
|
396
|
+
_print_log("cfquant auto trade callback refresh failed stage=%s error=%s" % (stage, e))
|
|
397
|
+
|
|
398
|
+
|
|
399
|
+
def _callback_brief(obj):
|
|
400
|
+
try:
|
|
401
|
+
parts = []
|
|
402
|
+
for name in ("account_id", "m_strAccountID", "m_strInstrumentID", "m_strExchangeID", "m_strOrderSysID", "m_nOrderID", "m_strRemark"):
|
|
403
|
+
value = getattr(obj, name, None)
|
|
404
|
+
if value is None and hasattr(obj, "get"):
|
|
405
|
+
value = obj.get(name)
|
|
406
|
+
if value not in (None, ""):
|
|
407
|
+
parts.append("%s=%s" % (name, value))
|
|
408
|
+
return " ".join(parts) or type(obj).__name__
|
|
409
|
+
except Exception:
|
|
410
|
+
return type(obj).__name__
|
|
411
|
+
|
|
412
|
+
|
|
413
|
+
def _object_to_callback_dict(obj):
|
|
414
|
+
if hasattr(obj, "items"):
|
|
415
|
+
return dict(obj)
|
|
416
|
+
if hasattr(obj, "__dict__"):
|
|
417
|
+
return dict(vars(obj))
|
|
418
|
+
return {"value": str(obj)}
|
|
419
|
+
|
|
420
|
+
|
|
421
|
+
def init(ContextInfo):
|
|
422
|
+
_register_qmt_trade_callback(ContextInfo, "init")
|
|
423
|
+
if _cf_bridge:
|
|
424
|
+
_cf_bridge.set_context(ContextInfo)
|
|
425
|
+
_schedule_cf_timer(ContextInfo)
|
|
426
|
+
_print_log("cfquant normal bridge context ready version:%s" % _ENTRY_VERSION)
|
|
427
|
+
|
|
428
|
+
|
|
429
|
+
def after_init(ContextInfo):
|
|
430
|
+
_register_qmt_trade_callback(ContextInfo, "after_init")
|
|
431
|
+
_refresh_auto_trade_callback("after_init")
|
|
432
|
+
|
|
433
|
+
|
|
434
|
+
def handlebar(ContextInfo):
|
|
435
|
+
if _cf_bridge:
|
|
436
|
+
_cf_bridge.pump()
|
|
437
|
+
|
|
438
|
+
|
|
439
|
+
def stop(ContextInfo):
|
|
440
|
+
global _cf_bridge, _cf_timer_key
|
|
441
|
+
|
|
442
|
+
if ContextInfo is not None and _cf_timer_key:
|
|
443
|
+
try:
|
|
444
|
+
ContextInfo.cancel_schedule_run(_cf_timer_key)
|
|
445
|
+
except Exception as e:
|
|
446
|
+
_print_log("cfquant normal bridge timer cancel failed:%s" % e)
|
|
447
|
+
_cf_timer_key = None
|
|
448
|
+
|
|
449
|
+
if _cf_bridge:
|
|
450
|
+
_cf_bridge.close()
|
|
451
|
+
_cf_bridge = None
|
|
452
|
+
_print_log("cfquant normal bridge stopped")
|
|
453
|
+
|
|
454
|
+
|
|
455
|
+
def _publish_callback(event_name, obj):
|
|
456
|
+
try:
|
|
457
|
+
_print_log("cfquant raw qmt callback received event=%s %s" % (event_name, _callback_brief(obj)))
|
|
458
|
+
if _cf_bridge:
|
|
459
|
+
_cf_bridge.publish_callback_event(event_name, obj)
|
|
460
|
+
except Exception as e:
|
|
461
|
+
_print_log("cfquant callback publish failed event=%s error=%s" % (event_name, e))
|
|
462
|
+
|
|
463
|
+
|
|
464
|
+
def account_callback(ContextInfo, accountInfo):
|
|
465
|
+
_publish_callback("trader:on_stock_asset", accountInfo)
|
|
466
|
+
|
|
467
|
+
|
|
468
|
+
def order_callback(ContextInfo, orderInfo):
|
|
469
|
+
_publish_callback("trader:on_stock_order", orderInfo)
|
|
470
|
+
|
|
471
|
+
|
|
472
|
+
def deal_callback(ContextInfo, dealInfo):
|
|
473
|
+
_publish_callback("trader:on_stock_trade", dealInfo)
|
|
474
|
+
|
|
475
|
+
|
|
476
|
+
def trade_callback(ContextInfo, tradeInfo):
|
|
477
|
+
_publish_callback("trader:on_stock_trade", tradeInfo)
|
|
478
|
+
|
|
479
|
+
|
|
480
|
+
def position_callback(ContextInfo, positionInfo):
|
|
481
|
+
_publish_callback("trader:on_stock_position", positionInfo)
|
|
482
|
+
|
|
483
|
+
|
|
484
|
+
def order_error_callback(ContextInfo, orderError):
|
|
485
|
+
_publish_callback("trader:on_order_error", orderError)
|
|
486
|
+
|
|
487
|
+
|
|
488
|
+
def orderError_callback(ContextInfo, passOrderInfo, msg):
|
|
489
|
+
data = _object_to_callback_dict(passOrderInfo)
|
|
490
|
+
data["error_msg"] = msg
|
|
491
|
+
_publish_callback("trader:on_order_error", data)
|
|
492
|
+
|
|
493
|
+
|
|
494
|
+
def cancel_error_callback(ContextInfo, cancelError):
|
|
495
|
+
_publish_callback("trader:on_cancel_error", cancelError)
|
|
496
|
+
|
|
497
|
+
|
|
498
|
+
def cancelError_callback(ContextInfo, cancelError):
|
|
499
|
+
_publish_callback("trader:on_cancel_error", cancelError)
|
|
500
|
+
|
|
501
|
+
|
|
502
|
+
def order_stock_async_response_callback(ContextInfo, response):
|
|
503
|
+
_publish_callback("trader:on_order_stock_async_response", response)
|
|
504
|
+
|
|
505
|
+
|
|
506
|
+
def cancel_order_stock_async_response_callback(ContextInfo, response):
|
|
507
|
+
_publish_callback("trader:on_cancel_order_stock_async_response", response)
|