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
LTtx/tx/tx.py
ADDED
|
@@ -0,0 +1,1935 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""
|
|
3
|
+
Created on Thu Sep 22 11:09:51 2022
|
|
4
|
+
|
|
5
|
+
@author: Administrator
|
|
6
|
+
"""
|
|
7
|
+
import socket
|
|
8
|
+
import threading
|
|
9
|
+
import json
|
|
10
|
+
import queue
|
|
11
|
+
import struct
|
|
12
|
+
import time
|
|
13
|
+
import os
|
|
14
|
+
import hashlib
|
|
15
|
+
import random
|
|
16
|
+
import datetime
|
|
17
|
+
import sys
|
|
18
|
+
import subprocess
|
|
19
|
+
import importlib
|
|
20
|
+
import pandas as pd
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def _hidden_subprocess_kwargs():
|
|
24
|
+
if os.name != "nt":
|
|
25
|
+
return {}
|
|
26
|
+
kwargs = {}
|
|
27
|
+
create_no_window = getattr(subprocess, "CREATE_NO_WINDOW", 0)
|
|
28
|
+
if create_no_window:
|
|
29
|
+
kwargs["creationflags"] = create_no_window
|
|
30
|
+
try:
|
|
31
|
+
startupinfo = subprocess.STARTUPINFO()
|
|
32
|
+
startupinfo.dwFlags |= getattr(subprocess, "STARTF_USESHOWWINDOW", 1)
|
|
33
|
+
startupinfo.wShowWindow = getattr(subprocess, "SW_HIDE", 0)
|
|
34
|
+
kwargs["startupinfo"] = startupinfo
|
|
35
|
+
except Exception:
|
|
36
|
+
pass
|
|
37
|
+
return kwargs
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class txl:
|
|
42
|
+
def __init__(self,ip,port,tocken,show=True,check_version=False,loss_callback=None):
|
|
43
|
+
'''
|
|
44
|
+
ip:服务器IP,
|
|
45
|
+
port:服务器端口,
|
|
46
|
+
tocken:认证秘钥,任意字符串
|
|
47
|
+
show:是否输出日志信息,默认为输出日志
|
|
48
|
+
check_version:检查版本,如果有新版本将会拉取最新的版本,但需要手动重启一次主程序,谨慎使用
|
|
49
|
+
loss_callback:(待开发)在运行过程中,如果和服务端发生断网时的回调函数,传入后有助于处理连接断开时的处理动作
|
|
50
|
+
|
|
51
|
+
'''
|
|
52
|
+
self.run_on = True
|
|
53
|
+
self.orjson_on = None
|
|
54
|
+
self.channel_list = False
|
|
55
|
+
self.sys_print_on = show
|
|
56
|
+
self.clean_day = None
|
|
57
|
+
self.id = time.strftime("%Y%m%d%H%M%S")+'_'+self.create_channel(5)
|
|
58
|
+
self.log_que = queue.Queue()
|
|
59
|
+
self.log_dir = self._default_log_dir()
|
|
60
|
+
self.mkdir(self.log_dir)
|
|
61
|
+
msg = '包引入成功'
|
|
62
|
+
self.save_log(msg)
|
|
63
|
+
self.loss_callback = loss_callback
|
|
64
|
+
self.ip = ip
|
|
65
|
+
self.port = port
|
|
66
|
+
self.tocken = tocken
|
|
67
|
+
self.start_tx_on = False
|
|
68
|
+
self.txg = False
|
|
69
|
+
self.init_txg = False#初始化txg
|
|
70
|
+
self.__judge_python_version()
|
|
71
|
+
self.txg_running = False
|
|
72
|
+
self.__tx = False
|
|
73
|
+
self.__tx_plus = False
|
|
74
|
+
self.push_count = 0
|
|
75
|
+
self.timeout = 2
|
|
76
|
+
self.heartbeat = 1
|
|
77
|
+
self.tx_que_plus = queue.Queue(maxsize=0)
|
|
78
|
+
self.tx_running = False
|
|
79
|
+
self.file_tx = None
|
|
80
|
+
#ZMQ模式
|
|
81
|
+
self.__ZMQ = None
|
|
82
|
+
self.__ZMQ_broadcast = None
|
|
83
|
+
self.__txg_heartbeat_on = True#通信系统Push模式心跳检测线程状态
|
|
84
|
+
self.__txg_heartbeat_time = time.time()
|
|
85
|
+
self.version = '8.0.1'
|
|
86
|
+
self.__version__ = self.version
|
|
87
|
+
self.check_version_on = check_version
|
|
88
|
+
self.dict_TradeDay = {}
|
|
89
|
+
self.check_version()
|
|
90
|
+
self.current_file = sys.modules[__name__].__file__ #当前文件名
|
|
91
|
+
self.current_dir = os.path.dirname(os.path.abspath(self.current_file))#当前目录
|
|
92
|
+
self.local_ip = self.get_local_ip()
|
|
93
|
+
self.__version__msg = '更新于2026-07-07'
|
|
94
|
+
self.txg_dict = {}#存储txg对象
|
|
95
|
+
msg = '通信系统V%s加载成功,Have fun!当前版本更新于%s'%(self.__version__,self.__version__msg)
|
|
96
|
+
self.sys_print(msg)
|
|
97
|
+
self.server2 = False#判断服务器是不是新版本,看是否要用批量发送
|
|
98
|
+
self.start()
|
|
99
|
+
self.td_df = pd.DataFrame()#存放交易日期数据,如果没有调用则不会产生数据
|
|
100
|
+
self.pre_TradeDay = {'now_date':''}#存放前一个交易日
|
|
101
|
+
self.d_push_tx = False
|
|
102
|
+
|
|
103
|
+
def start(self,):
|
|
104
|
+
'''
|
|
105
|
+
启动对应的组件
|
|
106
|
+
'''
|
|
107
|
+
threading.Thread(target = self.__main_log).start()
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def auto_import(self,package_name, import_name=None):
|
|
111
|
+
import_name = import_name or package_name
|
|
112
|
+
try:
|
|
113
|
+
self.orjson_on = True
|
|
114
|
+
return importlib.import_module(import_name)
|
|
115
|
+
except ImportError:
|
|
116
|
+
self.sys_price(f"[TxLink自动安装] 缺少依赖 {package_name},正在安装...请不要退出,安装完成后程序将正常运行")
|
|
117
|
+
try:
|
|
118
|
+
subprocess.check_call([sys.executable, "-m", "pip", "install", package_name], **_hidden_subprocess_kwargs())
|
|
119
|
+
self.orjson_on = True
|
|
120
|
+
return importlib.import_module(import_name)
|
|
121
|
+
except:
|
|
122
|
+
self.sys_price('TxLink自动安装orjson模块失败,采用json模块,若要体验高性能,请手动pip install orjson后重试')
|
|
123
|
+
import json
|
|
124
|
+
self.orjson_on = False
|
|
125
|
+
return json
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def __judge_python_version(self):
|
|
129
|
+
'''
|
|
130
|
+
python版本判断
|
|
131
|
+
-对于3.7以下的版本,用queue.Queue和普通的json
|
|
132
|
+
-对于3.7以上的版本,用deque加速
|
|
133
|
+
'''
|
|
134
|
+
if sys.version_info >= (3, 8):
|
|
135
|
+
try:
|
|
136
|
+
self.json = self.auto_import('orjson')
|
|
137
|
+
data = self.json.dumps('abc')
|
|
138
|
+
if isinstance(data, bytes):#二次验证导包的情况
|
|
139
|
+
msg = '采用orjson进行数据交互'
|
|
140
|
+
self.orjson_on = True
|
|
141
|
+
else:
|
|
142
|
+
msg = '采用json进行数据交互'
|
|
143
|
+
self.orjson_on = False
|
|
144
|
+
self.save_log(msg,)
|
|
145
|
+
except Exception as e:
|
|
146
|
+
msg = '自动引入orjson出错>>>%s,采用json进行数据交互'%(e)
|
|
147
|
+
self.save_log(msg,)
|
|
148
|
+
self.json = json
|
|
149
|
+
self.orjson_on = False
|
|
150
|
+
else:
|
|
151
|
+
msg = 'python版本小于3.8,采用json进行数据交互'
|
|
152
|
+
self.save_log(msg,)
|
|
153
|
+
self.json = json
|
|
154
|
+
self.orjson_on = False
|
|
155
|
+
if sys.version_info >= (3, 7):
|
|
156
|
+
msg = 'python版本大于3.7,采用SimpleQueue进行数据传输'
|
|
157
|
+
self.Q = queue.SimpleQueue()
|
|
158
|
+
self.tx_que = queue.SimpleQueue()
|
|
159
|
+
self.save_log(msg)
|
|
160
|
+
else:
|
|
161
|
+
msg = 'python版本大于3.7,采用Queue进行数据传输'
|
|
162
|
+
self.Q = queue.Queue()
|
|
163
|
+
self.tx_que = queue.Queue()
|
|
164
|
+
self.save_log(msg)
|
|
165
|
+
|
|
166
|
+
def clean_log(self):
|
|
167
|
+
'''
|
|
168
|
+
清理30天前的日志
|
|
169
|
+
'''
|
|
170
|
+
if self.clean_day != self.get_nowdate():
|
|
171
|
+
try:
|
|
172
|
+
retention_days = int(os.environ.get("CFQUANT_TX_LOG_RETENTION_DAYS") or os.environ.get("CFQUANT_LOG_RETENTION_DAYS") or "30")
|
|
173
|
+
cutoff = time.time() - max(1, retention_days) * 86400
|
|
174
|
+
for file in os.listdir(self.log_dir):
|
|
175
|
+
file_name = os.path.join(self.log_dir, file)
|
|
176
|
+
if os.path.isfile(file_name) and os.path.getmtime(file_name) < cutoff:
|
|
177
|
+
self.delete_file(file_name)
|
|
178
|
+
except Exception as e:
|
|
179
|
+
msg = '日志自动清楚报错了>>>>>为了不影响使用体验,请查看一下原因>>>>%s'%(e)
|
|
180
|
+
self.sys_print(msg,show_force=True)
|
|
181
|
+
self.clean_day = self.get_nowdate()
|
|
182
|
+
|
|
183
|
+
def _default_log_dir(self):
|
|
184
|
+
tx_log_dir = os.environ.get("CFQUANT_TX_LOG_DIR")
|
|
185
|
+
if tx_log_dir:
|
|
186
|
+
return os.path.abspath(tx_log_dir)
|
|
187
|
+
log_dir = os.environ.get("CFQUANT_LOG_DIR")
|
|
188
|
+
if log_dir:
|
|
189
|
+
return os.path.abspath(os.path.join(log_dir, "tx_log"))
|
|
190
|
+
return os.path.abspath(os.path.join(os.getcwd(), "log", "tx_log"))
|
|
191
|
+
|
|
192
|
+
def delete_file(self,file_name):
|
|
193
|
+
msg = '删除tx运行日志%s'%(file_name)
|
|
194
|
+
os.remove(file_name)
|
|
195
|
+
self.sys_print(msg,show_force=True)
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
def save_log(self,msg,):
|
|
200
|
+
self.log_que.put((self.id,msg))
|
|
201
|
+
|
|
202
|
+
def __main_log(self):
|
|
203
|
+
self.clean_log()
|
|
204
|
+
while self.run_on:
|
|
205
|
+
try:
|
|
206
|
+
data = str(self.log_que.get())+'\n'
|
|
207
|
+
if self.log_que.qsize() > 100:
|
|
208
|
+
self.clean_log()
|
|
209
|
+
for i in range(100):
|
|
210
|
+
data = data + self.log_que.get() +'\n'
|
|
211
|
+
with open(os.path.join(self.log_dir, '%s.log'%(self.get_nowdate())),'a+',encoding='utf-8') as f:
|
|
212
|
+
f.write(data)
|
|
213
|
+
except:#过滤掉日志记录出错
|
|
214
|
+
#直接跳过日志记录,不影响主程序
|
|
215
|
+
pass
|
|
216
|
+
|
|
217
|
+
def check_version(self,):
|
|
218
|
+
'''
|
|
219
|
+
检查本地客户端版本是不是最新的
|
|
220
|
+
'''
|
|
221
|
+
if self.check_version_on:
|
|
222
|
+
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
223
|
+
client.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, 8192*100)
|
|
224
|
+
client.connect(('pc.zltnet.top',2099))
|
|
225
|
+
tem_dict = {'con_type':'check_version',
|
|
226
|
+
'tocken':'LTtx',
|
|
227
|
+
}
|
|
228
|
+
client.sendall(json.dumps(tem_dict).encode('utf-8'))
|
|
229
|
+
server_msg = client.recv(1024).decode()
|
|
230
|
+
if server_msg['LTtx_lastest'] != self.__version__:
|
|
231
|
+
self.sys_print('从LTtx官网获取最新tx.py')
|
|
232
|
+
self.__get_lastest_file()
|
|
233
|
+
self.sys_print('最新版本获取完成,程序2秒后重启')
|
|
234
|
+
time.sleep(2)
|
|
235
|
+
os._exit(1)
|
|
236
|
+
else:
|
|
237
|
+
self.sys_print('当前版本已经是最新')
|
|
238
|
+
|
|
239
|
+
def update_tx_version(self):
|
|
240
|
+
'''
|
|
241
|
+
更新客户端到最新版本
|
|
242
|
+
|
|
243
|
+
'''
|
|
244
|
+
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
245
|
+
client.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, 8192*100)
|
|
246
|
+
client.connect(('pc.zltnet.top',2099))
|
|
247
|
+
tem_dict = {'con_type':'check_version',
|
|
248
|
+
'tocken':'LTtx',
|
|
249
|
+
}
|
|
250
|
+
client.sendall(json.dumps(tem_dict).encode('utf-8'))
|
|
251
|
+
server_msg = json.loads(client.recv(1024).decode())
|
|
252
|
+
if server_msg['LTtx_lastest'] != self.__version__:
|
|
253
|
+
self.sys_print('从LTtx官网获取最新tx.py')
|
|
254
|
+
self.__get_lastest_file()
|
|
255
|
+
self.sys_print('最新版本获取完成,程序2秒后重启')
|
|
256
|
+
time.sleep(2)
|
|
257
|
+
os._exit(1)
|
|
258
|
+
else:
|
|
259
|
+
self.sys_print('当前版本已经是最新')
|
|
260
|
+
|
|
261
|
+
def sys_print(self,data,show_force=False):
|
|
262
|
+
msg = '%sLTtx[info]>>>>:%s,'%(self.get_nowtime(),data)
|
|
263
|
+
if self.sys_print_on or show_force:
|
|
264
|
+
print(msg)
|
|
265
|
+
self.save_log(msg)
|
|
266
|
+
|
|
267
|
+
def send_file(self,file_name,show_on=True):
|
|
268
|
+
'''
|
|
269
|
+
将本地文件上传至数据中心
|
|
270
|
+
|
|
271
|
+
Parameters
|
|
272
|
+
----------
|
|
273
|
+
file_name : TYPE
|
|
274
|
+
文件路径,通常为./data/file.txt.
|
|
275
|
+
show_on : TYPE, optional
|
|
276
|
+
是否Print进度,默认开启. The default is True.
|
|
277
|
+
|
|
278
|
+
Returns
|
|
279
|
+
-------
|
|
280
|
+
code int.
|
|
281
|
+
返回0表示成功,其他表示错误,参见msg
|
|
282
|
+
msg string
|
|
283
|
+
提示信息
|
|
284
|
+
|
|
285
|
+
'''
|
|
286
|
+
if os.path.isfile(file_name):
|
|
287
|
+
if show_on:
|
|
288
|
+
self.sys_print('识别到文件存在')
|
|
289
|
+
self.sys_print(file_name)
|
|
290
|
+
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
291
|
+
client.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, 8192*100)
|
|
292
|
+
client.connect((self.ip,self.port))
|
|
293
|
+
hash_md5 = hashlib.md5()
|
|
294
|
+
with open(file_name, "rb") as f:
|
|
295
|
+
for chunk in iter(lambda: f.read(4096), b""):
|
|
296
|
+
hash_md5.update(chunk)
|
|
297
|
+
file_hash = hash_md5.hexdigest()
|
|
298
|
+
tem_dict = {'con_type':'file_mode',
|
|
299
|
+
'tocken':self.tocken,
|
|
300
|
+
'file_name':file_name.rsplit('/',1)[-1],
|
|
301
|
+
'file_hash':file_hash,
|
|
302
|
+
'file_mode':'upload_file',
|
|
303
|
+
}
|
|
304
|
+
client.sendall(json.dumps(tem_dict).encode('utf-8'))
|
|
305
|
+
server_msg = client.recv(1024).decode()
|
|
306
|
+
if server_msg == 'i am ok':
|
|
307
|
+
t1 = time.time()
|
|
308
|
+
file = open(file_name, 'rb')
|
|
309
|
+
file_data = file.read(1024)
|
|
310
|
+
while file_data:
|
|
311
|
+
client.send(file_data)
|
|
312
|
+
file_data = file.read(1024)
|
|
313
|
+
file.close()
|
|
314
|
+
# client.send(b'file send finish')
|
|
315
|
+
# client.recv(1024).decode()
|
|
316
|
+
client.close()
|
|
317
|
+
if show_on:
|
|
318
|
+
self.sys_print('file send done! usetime:%ss'%(round(time.time()-t1,6)))
|
|
319
|
+
else:
|
|
320
|
+
self.sys_print('服务端拒绝了本次文件传输请求')
|
|
321
|
+
else:
|
|
322
|
+
self.sys_print('文件不存在,请重新传入,当前收到的文件名:')
|
|
323
|
+
self.sys_print(file_name)
|
|
324
|
+
|
|
325
|
+
def __get_lastest_file(self,file_name='tx.py',file_path='./',show_on=False):
|
|
326
|
+
'''
|
|
327
|
+
从服务端下载文件
|
|
328
|
+
|
|
329
|
+
Parameters
|
|
330
|
+
----------
|
|
331
|
+
file_name : TYPE
|
|
332
|
+
要下载的文件名.
|
|
333
|
+
file_path : TYPE, optional
|
|
334
|
+
文件保存路径,不存在的路径将会被创建. The default is './'.
|
|
335
|
+
show_on : TYPE, optional
|
|
336
|
+
是否Print进度,默认开启. The default is True.
|
|
337
|
+
Returns
|
|
338
|
+
-------
|
|
339
|
+
code int.
|
|
340
|
+
返回0表示成功,其他表示错误,参见msg
|
|
341
|
+
msg string
|
|
342
|
+
提示信息
|
|
343
|
+
|
|
344
|
+
'''
|
|
345
|
+
file_path = self.current_dir
|
|
346
|
+
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
347
|
+
client.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, 8192*100)
|
|
348
|
+
client.connect(('pc.zltnet.top',2099))
|
|
349
|
+
tem_dict = {'con_type':'file_mode',
|
|
350
|
+
'tocken':'LTtx',
|
|
351
|
+
'file_name':file_name,
|
|
352
|
+
'file_mode':'download_file',
|
|
353
|
+
}
|
|
354
|
+
client.sendall(json.dumps(tem_dict).encode('utf-8'))
|
|
355
|
+
server_msg = client.recv(1024).decode()
|
|
356
|
+
if server_msg == 'file exist':
|
|
357
|
+
t1 = time.time()
|
|
358
|
+
file = open(file_path+file_name+'.tmp','wb')
|
|
359
|
+
file_data = client.recv(1024)
|
|
360
|
+
while file_data:
|
|
361
|
+
file.write(file_data)
|
|
362
|
+
file_data = client.recv(1024)
|
|
363
|
+
|
|
364
|
+
file.close()
|
|
365
|
+
if os.path.isfile(file_path+file_name):
|
|
366
|
+
os.remove(file_path+file_name)
|
|
367
|
+
os.rename(file_path+file_name+ '.tmp', file_path+file_name)
|
|
368
|
+
code = 0
|
|
369
|
+
msg = '%s文件接收完成,用时%ss'%(file_name,round(time.time()-t1,6))
|
|
370
|
+
if show_on:
|
|
371
|
+
print(code,msg)
|
|
372
|
+
return code,msg
|
|
373
|
+
else:
|
|
374
|
+
code = -1
|
|
375
|
+
msg = '服务端该文件不存在,请先上传'
|
|
376
|
+
print(code,msg)
|
|
377
|
+
return code,msg
|
|
378
|
+
|
|
379
|
+
|
|
380
|
+
def recv_file(self,file_name,file_path='./',show_on=False):
|
|
381
|
+
'''
|
|
382
|
+
从服务端下载文件
|
|
383
|
+
|
|
384
|
+
Parameters
|
|
385
|
+
----------
|
|
386
|
+
file_name : TYPE
|
|
387
|
+
要下载的文件名.
|
|
388
|
+
file_path : TYPE, optional
|
|
389
|
+
文件保存路径,不存在的路径将会被创建. The default is './'.
|
|
390
|
+
show_on : TYPE, optional
|
|
391
|
+
是否Print进度,默认开启. The default is True.
|
|
392
|
+
Returns
|
|
393
|
+
-------
|
|
394
|
+
code int.
|
|
395
|
+
返回0表示成功,其他表示错误,参见msg
|
|
396
|
+
msg string
|
|
397
|
+
提示信息
|
|
398
|
+
|
|
399
|
+
'''
|
|
400
|
+
if file_path[-1] != '/':
|
|
401
|
+
file_path = file_path + '/'
|
|
402
|
+
if os.path.isdir(file_path):
|
|
403
|
+
pass
|
|
404
|
+
else:
|
|
405
|
+
self.sys_print(('文件路径不存在,自动创建该路径'))
|
|
406
|
+
try:
|
|
407
|
+
os.mkdir(file_path)
|
|
408
|
+
except Exception as e:
|
|
409
|
+
raise TypeError('文件路径自动创建失败,失败原因:%s'%(e))
|
|
410
|
+
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
411
|
+
client.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, 8192*100)
|
|
412
|
+
client.connect((self.ip,self.port))
|
|
413
|
+
tem_dict = {'con_type':'file_mode',
|
|
414
|
+
'tocken':self.tocken,
|
|
415
|
+
'file_name':file_name,
|
|
416
|
+
'file_mode':'download_file',
|
|
417
|
+
}
|
|
418
|
+
client.sendall(json.dumps(tem_dict).encode('utf-8'))
|
|
419
|
+
server_msg = client.recv(1024).decode()
|
|
420
|
+
if server_msg == 'file exist':
|
|
421
|
+
t1 = time.time()
|
|
422
|
+
file = open(file_path+file_name+'.tmp','wb')
|
|
423
|
+
file_data = client.recv(1024)
|
|
424
|
+
while file_data:
|
|
425
|
+
file.write(file_data)
|
|
426
|
+
file_data = client.recv(1024)
|
|
427
|
+
file.close()
|
|
428
|
+
code = 0
|
|
429
|
+
msg = ('%s文件接收完成,文件MD5检验通过'%(file_name))
|
|
430
|
+
if os.path.isfile(file_path+file_name):
|
|
431
|
+
os.remove(file_path+file_name)
|
|
432
|
+
os.rename(file_path+file_name+ '.tmp', file_path+file_name)
|
|
433
|
+
code = 0
|
|
434
|
+
msg = '%s文件接收完成,,用时%ss'%(file_name,round(time.time()-t1,6))
|
|
435
|
+
if show_on:
|
|
436
|
+
print(code,msg)
|
|
437
|
+
return code,msg
|
|
438
|
+
else:
|
|
439
|
+
code = -1
|
|
440
|
+
msg = '服务端该文件不存在,请先上传'
|
|
441
|
+
self.sys_print((code,msg))
|
|
442
|
+
return code,msg
|
|
443
|
+
|
|
444
|
+
def start_tx(self,mode=None):
|
|
445
|
+
'''
|
|
446
|
+
- 开启数据推送功能,开启后可以使用get,put,push等函数
|
|
447
|
+
- 如果要订阅数据,通过start_txg进行开启订阅,即可收到对应的数据
|
|
448
|
+
'''
|
|
449
|
+
if not self.run_on:
|
|
450
|
+
msg = '识别到此前已经close过txl,重新运行self.run_on'
|
|
451
|
+
self.sys_print(msg,show_force=True)
|
|
452
|
+
self.run_on = True
|
|
453
|
+
if not mode:
|
|
454
|
+
self.tx_running = True
|
|
455
|
+
while self.run_on:
|
|
456
|
+
try:
|
|
457
|
+
msg = 'start_tx正在连接(%s,%s)LTtx服务器,请稍后'%(self.ip,self.port)
|
|
458
|
+
self.sys_print(msg,show_force=True)
|
|
459
|
+
if self.__tx == False:
|
|
460
|
+
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
461
|
+
client.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, 8192*100)
|
|
462
|
+
# 设置立即发送(关闭 Nagle 算法,降低延迟)
|
|
463
|
+
client.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
|
|
464
|
+
client.connect((self.ip,self.port))
|
|
465
|
+
tem_dict = {'con_type':'put_mode','tocken':self.tocken,
|
|
466
|
+
'local_ip':self.local_ip,
|
|
467
|
+
'current_file':self.current_file,'current_dir':self.current_dir,
|
|
468
|
+
}
|
|
469
|
+
self.sys_print(tem_dict)
|
|
470
|
+
client.sendall(json.dumps(tem_dict).encode('utf-8'))
|
|
471
|
+
if self.recv_msg_start_tx(client):
|
|
472
|
+
self.__tx = client
|
|
473
|
+
t0 = threading.Thread(target=self.main_tx_que)
|
|
474
|
+
t0.start()
|
|
475
|
+
thp0 = threading.Thread(target = self.start_tx_hearbeat)
|
|
476
|
+
thp0.start()
|
|
477
|
+
msg = 'start_tx连接成功,have fun'
|
|
478
|
+
self.sys_print(msg,show_force=True)
|
|
479
|
+
break
|
|
480
|
+
else:
|
|
481
|
+
self.close_connection(self.__tx)
|
|
482
|
+
self.__tx = False
|
|
483
|
+
else:
|
|
484
|
+
code = -1
|
|
485
|
+
msg = '请勿重复连接tx'
|
|
486
|
+
result = {'code':code,'msg':msg}
|
|
487
|
+
return result
|
|
488
|
+
except Exception as e:
|
|
489
|
+
msg = '服务端未启动,将在1秒后继续尝试start_tx,%s'%(e)
|
|
490
|
+
self.sys_print(msg,show_force=True)
|
|
491
|
+
time.sleep(1)
|
|
492
|
+
|
|
493
|
+
def start_plus(self,):
|
|
494
|
+
'''
|
|
495
|
+
开启高性能式,该模式打开后可以使用push_plus函数,推送数据的延迟和效率会更高
|
|
496
|
+
'''
|
|
497
|
+
if not self.run_on:
|
|
498
|
+
msg = '识别到此前已经close过txl,重新运行self.run_on'
|
|
499
|
+
self.sys_print(msg,show_force=True)
|
|
500
|
+
while self.run_on:
|
|
501
|
+
try:
|
|
502
|
+
msg = (self.get_nowtime(),'start_flash正在连接(%s,%s)LTtx服务器,请稍后'%(self.ip,self.port))
|
|
503
|
+
self.sys_print(msg)
|
|
504
|
+
if self.__tx_plus == False:
|
|
505
|
+
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
506
|
+
client.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, 8192*100)
|
|
507
|
+
client.connect((self.ip,self.port))
|
|
508
|
+
self.__tx_plus = client
|
|
509
|
+
tem_dict = {'con_type':'plus_mode','tocken':self.tocken,
|
|
510
|
+
'local_ip':self.local_ip,
|
|
511
|
+
'current_file':self.current_file,'current_dir':self.current_dir,
|
|
512
|
+
}
|
|
513
|
+
self.__tx_plus.sendall(json.dumps(tem_dict).encode('utf-8'))
|
|
514
|
+
if self.recv_msg_start_tx(client):
|
|
515
|
+
t0 = threading.Thread(target=self.main_tx_que_plus)
|
|
516
|
+
t0.start()
|
|
517
|
+
thp0 = threading.Thread(target = self.start_tx_hearbeat_plus)
|
|
518
|
+
thp0.start()
|
|
519
|
+
break
|
|
520
|
+
else:
|
|
521
|
+
self.close_connection(self.__tx_plus)
|
|
522
|
+
self.__tx_plus = False
|
|
523
|
+
else:
|
|
524
|
+
code = -1
|
|
525
|
+
msg = '请勿重复连接'
|
|
526
|
+
result = {'code':code,'msg':msg}
|
|
527
|
+
self.sys_print(result)
|
|
528
|
+
return result
|
|
529
|
+
|
|
530
|
+
except Exception as e:
|
|
531
|
+
if type(e) == ConnectionRefusedError:
|
|
532
|
+
msg = (self.get_nowtime(),'服务端未启动,将在1秒后继续尝试start_plus')
|
|
533
|
+
self.sys_print(msg)
|
|
534
|
+
msg = (self.get_nowtime(),str(e))
|
|
535
|
+
self.sys_print(msg)
|
|
536
|
+
time.sleep(1)
|
|
537
|
+
|
|
538
|
+
def start_txg(self,channel_list,pwd=''):
|
|
539
|
+
'''
|
|
540
|
+
- 订阅数据,多个频道用@进行连接,比如test@lttx5表示同时订阅两个频道的消息,
|
|
541
|
+
- 对应的数据都放在Q这个队列中,可以通过下方txl.Q.get()进行获取,txl这里指你实例化以后的对象
|
|
542
|
+
用法示例:
|
|
543
|
+
tx1 = txl('192.168.1.65',2049,'test')
|
|
544
|
+
tx1.start_txg('test@lttx5')
|
|
545
|
+
while True:
|
|
546
|
+
data = tx1.Q.get().split('|') #消息通过“|”进行切割,前面为key,后面为value
|
|
547
|
+
print(data)#这里出来的是一个列表
|
|
548
|
+
#下方即可做相应的数据处理
|
|
549
|
+
'''
|
|
550
|
+
if not self.tx_running:
|
|
551
|
+
self.start_tx()#启动tx,确保心跳正确
|
|
552
|
+
if not self.run_on:
|
|
553
|
+
msg = '识别到此前已经close过txl,重新运行self.run_on'
|
|
554
|
+
self.sys_print(msg,show_force=True)
|
|
555
|
+
while self.run_on:
|
|
556
|
+
try:
|
|
557
|
+
if self.init_txg==False:
|
|
558
|
+
self.channel_list = channel_list
|
|
559
|
+
for who in self.channel_list.split('@'):
|
|
560
|
+
if len(who) == 0:
|
|
561
|
+
continue
|
|
562
|
+
self.__connect_txg(who,pwd=pwd)
|
|
563
|
+
if self.txg != False:
|
|
564
|
+
break
|
|
565
|
+
else:
|
|
566
|
+
code = -1
|
|
567
|
+
msg = '请勿重复连接txg'
|
|
568
|
+
result = {'code':code,'msg':msg}
|
|
569
|
+
self.sys_print(result)
|
|
570
|
+
return result
|
|
571
|
+
except Exception as e:
|
|
572
|
+
if type(e) == ConnectionRefusedError:
|
|
573
|
+
msg = (self.get_nowtime(),'服务端未启动,将在1秒后继续尝试start_txg')
|
|
574
|
+
self.sys_print(msg)
|
|
575
|
+
time.sleep(1)
|
|
576
|
+
|
|
577
|
+
def __re_start_txg(self,channel_list,pwd=''):
|
|
578
|
+
'''
|
|
579
|
+
服务重连
|
|
580
|
+
'''
|
|
581
|
+
if not self.run_on:
|
|
582
|
+
msg = '识别到此前已经close过txl,重新运行self.run_on'
|
|
583
|
+
self.sys_print(msg,show_force=True)
|
|
584
|
+
while self.run_on:
|
|
585
|
+
try:
|
|
586
|
+
if self.init_txg==False:
|
|
587
|
+
msg = '通过start_tx的心跳包进行重连'
|
|
588
|
+
self.sys_print(msg,show_force=True)
|
|
589
|
+
self.channel_list = channel_list
|
|
590
|
+
for who in self.channel_list.split('@'):
|
|
591
|
+
if len(who) == 0:
|
|
592
|
+
continue
|
|
593
|
+
self.__connect_txg(who,pwd=pwd)
|
|
594
|
+
if self.txg != False:
|
|
595
|
+
break
|
|
596
|
+
else:
|
|
597
|
+
code = -1
|
|
598
|
+
msg = '请勿重复连接txg'
|
|
599
|
+
result = {'code':code,'msg':msg}
|
|
600
|
+
self.sys_price(result)
|
|
601
|
+
return result
|
|
602
|
+
except Exception as e:
|
|
603
|
+
if type(e) == ConnectionRefusedError:
|
|
604
|
+
msg = (self.get_nowtime(),'服务端未启动,将在1秒后继续尝试start_txg')
|
|
605
|
+
self.sys_print(msg)
|
|
606
|
+
time.sleep(1)
|
|
607
|
+
|
|
608
|
+
|
|
609
|
+
def __connect_txg(self,who,pwd=''):
|
|
610
|
+
'''
|
|
611
|
+
连接txg
|
|
612
|
+
'''
|
|
613
|
+
while self.run_on:
|
|
614
|
+
if who in self.txg_dict and self.txg_dict[who]['txg_running']:
|
|
615
|
+
self.sys_print((self.get_nowtime(),'订阅频道%s失败,该频道已经存在'%(who)))
|
|
616
|
+
return
|
|
617
|
+
try:
|
|
618
|
+
msg = '正在订阅%s频道'%(who)
|
|
619
|
+
self.sys_print(msg)
|
|
620
|
+
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
621
|
+
client.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, 1024*1024*100)
|
|
622
|
+
self.txg_running = True
|
|
623
|
+
client.connect((self.ip,self.port,))
|
|
624
|
+
self.channel_pwd = pwd
|
|
625
|
+
tem_dict = {'con_type':'push_mode','who':who,'pwd':pwd,'tocken':self.tocken,
|
|
626
|
+
'client_version':2,
|
|
627
|
+
'local_ip':self.local_ip,
|
|
628
|
+
'current_file':self.current_file,'current_dir':self.current_dir,
|
|
629
|
+
}
|
|
630
|
+
client.send(json.dumps(tem_dict).encode('utf-8'))
|
|
631
|
+
if self.recv_msg_start_txg(client,timeout=1) == True:
|
|
632
|
+
if who not in self.txg_dict:
|
|
633
|
+
self.txg = client
|
|
634
|
+
self.txg_dict[who] = {'con':client,'txg_running':True}
|
|
635
|
+
msg = 'start_txg连接成功,订阅频道%s'%(who)
|
|
636
|
+
self.sys_print(msg,show_force=True)
|
|
637
|
+
self.__start_thread(target = self.recv_msg_broadcast, args = (who,client,pwd))
|
|
638
|
+
self.__start_thread(target = self.main_broadcast_heartbeat,args=(client, who, pwd))
|
|
639
|
+
break
|
|
640
|
+
else:
|
|
641
|
+
if not self.txg_dict[who]['txg_running']:
|
|
642
|
+
self.txg = client
|
|
643
|
+
msg = 'start_txg连接成功,订阅频道%s'%(who)
|
|
644
|
+
self.txg_dict[who]['con'] = client
|
|
645
|
+
self.txg_dict[who]['txg_running'] = True
|
|
646
|
+
self.sys_print(msg,show_force=True)
|
|
647
|
+
self.__start_thread(target = self.recv_msg_broadcast, args = (who,client,pwd))
|
|
648
|
+
self.__start_thread(target = self.main_broadcast_heartbeat,args=(client, who, pwd))
|
|
649
|
+
break
|
|
650
|
+
else:
|
|
651
|
+
msg = '由于%s已经存在,本次连接主动断开'%(who)
|
|
652
|
+
self.sys_print(msg)
|
|
653
|
+
self.close_connection(client,name=msg)
|
|
654
|
+
break
|
|
655
|
+
else:
|
|
656
|
+
msg = (who,'start_txg连接失败,没有收到正常的数据,可能是认证不通过,请确认您的token是否正确')
|
|
657
|
+
self.sys_print(msg)
|
|
658
|
+
break
|
|
659
|
+
except Exception as e:
|
|
660
|
+
msg = '在订阅%s连接txg时服务器出错了>>>>>%s,将在1秒后重试'%(who,e)
|
|
661
|
+
self.sys_print(msg,show_force=1)
|
|
662
|
+
time.sleep(1)
|
|
663
|
+
|
|
664
|
+
|
|
665
|
+
def reconnect_callback(self,dict_data,callback=None):
|
|
666
|
+
'''
|
|
667
|
+
断线重连回调函数
|
|
668
|
+
|
|
669
|
+
待开发
|
|
670
|
+
'''
|
|
671
|
+
|
|
672
|
+
def reconnect_success_callback(self,dict_data,callback=None):
|
|
673
|
+
'''
|
|
674
|
+
断线重连成功回调函数
|
|
675
|
+
|
|
676
|
+
待开发
|
|
677
|
+
|
|
678
|
+
Parameters
|
|
679
|
+
----------
|
|
680
|
+
dict_data : TYPE
|
|
681
|
+
DESCRIPTION.
|
|
682
|
+
callback : TYPE, optional
|
|
683
|
+
DESCRIPTION. The default is None.
|
|
684
|
+
|
|
685
|
+
Returns
|
|
686
|
+
-------
|
|
687
|
+
None.
|
|
688
|
+
|
|
689
|
+
'''
|
|
690
|
+
pass
|
|
691
|
+
|
|
692
|
+
|
|
693
|
+
def add_txg(self,channel_name,pwd=''):
|
|
694
|
+
'''
|
|
695
|
+
增加txg的订阅,同txg,用@符号连接多个信道
|
|
696
|
+
如 'test@test1'
|
|
697
|
+
'''
|
|
698
|
+
if not self.run_on:
|
|
699
|
+
msg = '识别到此前已经close过txl,重新运行self.run_on'
|
|
700
|
+
self.sys_print(msg,show_force=True)
|
|
701
|
+
if '@' in channel_name:
|
|
702
|
+
for who in channel_name.split('@'):
|
|
703
|
+
self.__connect_txg(who,pwd=pwd)
|
|
704
|
+
else:
|
|
705
|
+
who = channel_name
|
|
706
|
+
self.__connect_txg(who,pwd=pwd)
|
|
707
|
+
|
|
708
|
+
def __close_txg(self,who):
|
|
709
|
+
txg = self.txg_dict[who]['con']
|
|
710
|
+
self.txg_dict[who]['txg_running'] = False
|
|
711
|
+
self.close_connection(txg,name=who)
|
|
712
|
+
|
|
713
|
+
def cancel_txg(self,who):
|
|
714
|
+
'''
|
|
715
|
+
取消txg的订阅,同txg,用@符号连接多个信道
|
|
716
|
+
'''
|
|
717
|
+
if who not in self.txg_dict:
|
|
718
|
+
tem_dict = {'code':-1,
|
|
719
|
+
'msg':'当前%s频道未连接,取消失败'%(who),
|
|
720
|
+
'value':{},
|
|
721
|
+
}
|
|
722
|
+
else:
|
|
723
|
+
tem_dict = {'code':0,
|
|
724
|
+
'msg':'%s频道取消成功'%(who),
|
|
725
|
+
'value':{},
|
|
726
|
+
}
|
|
727
|
+
self.__close_txg(who)
|
|
728
|
+
|
|
729
|
+
return tem_dict
|
|
730
|
+
|
|
731
|
+
def start_MQ(self,pub_port=5555):
|
|
732
|
+
import zmq
|
|
733
|
+
'''
|
|
734
|
+
开启ZMQ模式,该模式是将ZMQ与LTtx进行功能上的整合,当前版本的断线重连机制完全依赖于ZMQ自身的
|
|
735
|
+
断线重连机制
|
|
736
|
+
|
|
737
|
+
Parameters
|
|
738
|
+
----------
|
|
739
|
+
pub_port : int
|
|
740
|
+
传入服务端的ZMQ发布端口,通常为5555.
|
|
741
|
+
|
|
742
|
+
Returns
|
|
743
|
+
-------
|
|
744
|
+
None.
|
|
745
|
+
|
|
746
|
+
'''
|
|
747
|
+
if not self.__ZMQ:
|
|
748
|
+
context = zmq.Context()
|
|
749
|
+
|
|
750
|
+
# 创建发布者套接字并连接到代理
|
|
751
|
+
socket = context.socket(zmq.PUB)
|
|
752
|
+
socket.setsockopt(zmq.RCVHWM, 1000000)
|
|
753
|
+
socket.setsockopt(zmq.SNDHWM, 1000000)
|
|
754
|
+
socket.connect('tcp://%s:%s'%(self.ip,pub_port))
|
|
755
|
+
socket.send_string('test dsfadsafdsafsda')
|
|
756
|
+
self.__ZMQ = socket
|
|
757
|
+
print(self.get_nowtime(),'zmq connect ok')
|
|
758
|
+
else:
|
|
759
|
+
# code = -1
|
|
760
|
+
msg = 'start_MQ已经连接,请不要重复连接'
|
|
761
|
+
msg = (self.get_nowtime(),msg)
|
|
762
|
+
self.sys_print(msg)
|
|
763
|
+
|
|
764
|
+
|
|
765
|
+
|
|
766
|
+
def start_MQ_broadcast(self,channel_list,sub_port=5556):
|
|
767
|
+
'''
|
|
768
|
+
开启ZMQ的订阅模式,同start_MQ
|
|
769
|
+
|
|
770
|
+
Parameters
|
|
771
|
+
----------
|
|
772
|
+
channel_list : string
|
|
773
|
+
和start_txg()类似,传入要订阅的信道列表,用@进行分割.
|
|
774
|
+
sub_port : int, optional
|
|
775
|
+
传入服务端的ZMQ订阅端口,通常为5556
|
|
776
|
+
|
|
777
|
+
Returns
|
|
778
|
+
-------
|
|
779
|
+
None.
|
|
780
|
+
|
|
781
|
+
'''
|
|
782
|
+
import zmq
|
|
783
|
+
if not self.__ZMQ_broadcast:
|
|
784
|
+
context = zmq.Context()
|
|
785
|
+
|
|
786
|
+
# 创建订阅者套接字并连接到代理
|
|
787
|
+
socket = context.socket(zmq.SUB)
|
|
788
|
+
socket.setsockopt(zmq.RCVHWM, 1000000)
|
|
789
|
+
socket.setsockopt(zmq.SNDHWM, 1000000)
|
|
790
|
+
socket.connect("tcp://%s:%s"%(self.ip,sub_port))
|
|
791
|
+
self.zmq_channel_list = channel_list.split('@')
|
|
792
|
+
for channel in self.zmq_channel_list:
|
|
793
|
+
if len(channel) > 0:
|
|
794
|
+
# 订阅特定主题
|
|
795
|
+
socket.setsockopt_string(zmq.SUBSCRIBE, channel)
|
|
796
|
+
self.__ZMQ_broadcast = socket
|
|
797
|
+
threading.Thread(target = self.main_recv_msg_from_zmq).start()
|
|
798
|
+
else:
|
|
799
|
+
# code = -1
|
|
800
|
+
msg = 'MQ_broadcast已经连接,请不要重复连接'
|
|
801
|
+
print(self.get_nowtime(),msg)
|
|
802
|
+
|
|
803
|
+
def main_recv_msg_from_zmq(self):
|
|
804
|
+
print(self.get_nowtime(),'开始从ZMQ中接收数据')
|
|
805
|
+
while self.run_on:
|
|
806
|
+
data = self.__ZMQ_broadcast.recv_string().split('|',1)
|
|
807
|
+
if data[0] in self.zmq_channel_list:
|
|
808
|
+
self.Q.put(data[1])
|
|
809
|
+
|
|
810
|
+
def start_tx_hearbeat(self):
|
|
811
|
+
while self.run_on:
|
|
812
|
+
if self.__tx:
|
|
813
|
+
self.push('heartbeat','1','heartbeat0')
|
|
814
|
+
time.sleep(self.timeout)
|
|
815
|
+
else:
|
|
816
|
+
break
|
|
817
|
+
|
|
818
|
+
def start_tx_hearbeat_plus(self):
|
|
819
|
+
msg = ('start_tx_plus的heartbeat子线程启动')
|
|
820
|
+
self.sys_print(msg)
|
|
821
|
+
while self.run_on:
|
|
822
|
+
if self.__tx_plus:
|
|
823
|
+
self.push_plus('heartbeat','t','test22')
|
|
824
|
+
time.sleep(2)
|
|
825
|
+
else:
|
|
826
|
+
break
|
|
827
|
+
|
|
828
|
+
def __start_thread(self,target,args):
|
|
829
|
+
thp1 = threading.Thread(target=target,args=args)
|
|
830
|
+
# thp1.setDaemon(True)
|
|
831
|
+
thp1.start()
|
|
832
|
+
|
|
833
|
+
|
|
834
|
+
def recv_msg_start_tx(self,client):
|
|
835
|
+
|
|
836
|
+
data = client.recv(1024)
|
|
837
|
+
dict_data = json.loads(data)
|
|
838
|
+
code = dict_data['code']
|
|
839
|
+
self.id_code = str(dict_data['id_code'])+'@'
|
|
840
|
+
if 'server_version' in dict_data:
|
|
841
|
+
if dict_data['server_version'] > 2:
|
|
842
|
+
self.server2 = True
|
|
843
|
+
if code == 0:
|
|
844
|
+
return True
|
|
845
|
+
else:
|
|
846
|
+
return False
|
|
847
|
+
|
|
848
|
+
def recv_msg_start_txg(self,client,timeout=None):
|
|
849
|
+
if timeout:
|
|
850
|
+
client.settimeout(timeout)
|
|
851
|
+
data = client.recv(1024,)
|
|
852
|
+
dict_data = json.loads(data)
|
|
853
|
+
code = dict_data['code']
|
|
854
|
+
if timeout:
|
|
855
|
+
client.settimeout(None)
|
|
856
|
+
if code == 0:
|
|
857
|
+
return True
|
|
858
|
+
else:
|
|
859
|
+
return False
|
|
860
|
+
|
|
861
|
+
def recv_data_from_tx(self,client):
|
|
862
|
+
recv_bytes = client.recv(8,socket.MSG_WAITALL)
|
|
863
|
+
bytes_len = struct.unpack("Q",recv_bytes)[0]
|
|
864
|
+
recv_data = client.recv(bytes_len,socket.MSG_WAITALL).decode('utf-8',errors='replace')
|
|
865
|
+
recv_data = json.loads(recv_data)
|
|
866
|
+
return recv_data
|
|
867
|
+
|
|
868
|
+
def get(self,key):
|
|
869
|
+
'''
|
|
870
|
+
获取云变量'key'对应的值,如果不存在则返回None
|
|
871
|
+
'''
|
|
872
|
+
if self.__tx == False:
|
|
873
|
+
code = -1
|
|
874
|
+
msg = '当前tx未连接,请先执行start_tx()'
|
|
875
|
+
result = {'code':code,'msg':msg}
|
|
876
|
+
return result
|
|
877
|
+
else:
|
|
878
|
+
send_data = {'func':'get','value':key}
|
|
879
|
+
msg = self.json.dumps(send_data)
|
|
880
|
+
self.send_data(self.__tx,msg)
|
|
881
|
+
result = self.recv_data_from_tx(self.__tx)
|
|
882
|
+
result = json.loads(result)
|
|
883
|
+
if 'value' in result:
|
|
884
|
+
result = result['value']
|
|
885
|
+
self.heartbeat = 0
|
|
886
|
+
return result
|
|
887
|
+
|
|
888
|
+
def get_dict_value(self,var,key):
|
|
889
|
+
'''
|
|
890
|
+
获取云变量字典“key"中对应的key值,如果不存在则返回None
|
|
891
|
+
'''
|
|
892
|
+
if self.__tx == False:
|
|
893
|
+
code = -1
|
|
894
|
+
msg = '当前tx未连接,请先执行start_tx()'
|
|
895
|
+
result = {'code':code,'msg':msg}
|
|
896
|
+
return result
|
|
897
|
+
else:
|
|
898
|
+
send_data = {'func':'get_dict_value','value':(var,key)}
|
|
899
|
+
msg = self.json.dumps(send_data)
|
|
900
|
+
self.send_data(self.__tx,msg)
|
|
901
|
+
result = self.recv_data_from_tx(self.__tx)
|
|
902
|
+
result = json.loads(result)
|
|
903
|
+
if 'value' in result:
|
|
904
|
+
result = result['value']
|
|
905
|
+
self.heartbeat = 0
|
|
906
|
+
return result
|
|
907
|
+
|
|
908
|
+
def get_list_value(self,key:str,index:int):
|
|
909
|
+
'''
|
|
910
|
+
获取云变量列表"key"中对应下标的为index的值,index传入整型,和list使用方法一致,如果不存在则返回为None。
|
|
911
|
+
'''
|
|
912
|
+
if self.__tx == False:
|
|
913
|
+
code = -1
|
|
914
|
+
msg = '当前tx未连接,请先执行start_tx()'
|
|
915
|
+
result = {'code':code,'msg':msg}
|
|
916
|
+
return result
|
|
917
|
+
else:
|
|
918
|
+
send_data = {'func':'get_list_value','value':(key,index)}
|
|
919
|
+
msg = self.json.dumps(send_data)
|
|
920
|
+
self.send_data(self.__tx,msg)
|
|
921
|
+
result = self.recv_data_from_tx(self.__tx)
|
|
922
|
+
result = json.loads(result)
|
|
923
|
+
if 'value' in result:
|
|
924
|
+
result = result['value']
|
|
925
|
+
self.heartbeat = 0
|
|
926
|
+
return result
|
|
927
|
+
|
|
928
|
+
|
|
929
|
+
def get_df(self,key):
|
|
930
|
+
import pandas as pd
|
|
931
|
+
if self.__tx == False:
|
|
932
|
+
code = -1
|
|
933
|
+
msg = '当前tx未连接,请先执行start_tx()'
|
|
934
|
+
result = {'code':code,'msg':msg}
|
|
935
|
+
return result
|
|
936
|
+
else:
|
|
937
|
+
send_data = {'func':'get_dataframe','value':key}
|
|
938
|
+
msg = self.json.dumps(send_data)
|
|
939
|
+
self.send_data(self.__tx,msg)
|
|
940
|
+
result = self.recv_data_from_tx(self.__tx)
|
|
941
|
+
result = json.loads(result)
|
|
942
|
+
if 'value' in result:
|
|
943
|
+
result = result['value']
|
|
944
|
+
result = pd.DataFrame(json.loads(result))
|
|
945
|
+
self.heartbeat = 0
|
|
946
|
+
return result
|
|
947
|
+
|
|
948
|
+
|
|
949
|
+
def put(self,key,data):
|
|
950
|
+
try:
|
|
951
|
+
if self.__tx == False:
|
|
952
|
+
code = -1
|
|
953
|
+
msg = '当前tx未连接,请先执行start_tx()'
|
|
954
|
+
result = {'code':code,'msg':msg}
|
|
955
|
+
return result
|
|
956
|
+
else:
|
|
957
|
+
send_data = {'func':'put','value':(key,data)}
|
|
958
|
+
msg = self.json.dumps(send_data)
|
|
959
|
+
self.send_data(self.__tx,msg)
|
|
960
|
+
except:
|
|
961
|
+
msg = 'Error:和服务端失去连接,即将重连'
|
|
962
|
+
self.sys_print(msg,show_force=1)
|
|
963
|
+
self.__tx = False
|
|
964
|
+
self.start_tx()
|
|
965
|
+
|
|
966
|
+
def put_df(self,key,df):
|
|
967
|
+
try:
|
|
968
|
+
if self.__tx == False:
|
|
969
|
+
code = -1
|
|
970
|
+
msg = '当前tx未连接,请先执行start_tx()'
|
|
971
|
+
result = {'code':code,'msg':msg}
|
|
972
|
+
return result
|
|
973
|
+
else:
|
|
974
|
+
send_data = {'func':'put_dataframe','value':(key,json.dumps(df.to_dict(orient='records')))}
|
|
975
|
+
msg = self.json.dumps(send_data)
|
|
976
|
+
self.send_data(self.__tx,msg)
|
|
977
|
+
except:
|
|
978
|
+
msg = 'Error:put_df时和服务端失去连接,即将重连'
|
|
979
|
+
self.sys_print(msg,show_force=1)
|
|
980
|
+
self.__tx = False
|
|
981
|
+
self.start_tx()
|
|
982
|
+
|
|
983
|
+
|
|
984
|
+
|
|
985
|
+
def list_append(self,var,data):
|
|
986
|
+
'''
|
|
987
|
+
对云端变量类型为列表的var进行列表append操作,相当于本地列表的基础操作,无返回值,默认成功
|
|
988
|
+
|
|
989
|
+
Parameters
|
|
990
|
+
----------
|
|
991
|
+
key : TYPE
|
|
992
|
+
DESCRIPTION.
|
|
993
|
+
data : TYPE
|
|
994
|
+
DESCRIPTION.
|
|
995
|
+
|
|
996
|
+
Returns
|
|
997
|
+
-------
|
|
998
|
+
result : TYPE
|
|
999
|
+
DESCRIPTION.
|
|
1000
|
+
|
|
1001
|
+
'''
|
|
1002
|
+
try:
|
|
1003
|
+
if self.__tx == False:
|
|
1004
|
+
code = -1
|
|
1005
|
+
msg = '当前tx未连接,请先执行start_tx()'
|
|
1006
|
+
result = {'code':code,'msg':msg}
|
|
1007
|
+
return result
|
|
1008
|
+
else:
|
|
1009
|
+
send_data = {'func':'list_append','value':(var,data)}
|
|
1010
|
+
msg = self.json.dumps(send_data)
|
|
1011
|
+
self.send_data(self.__tx,msg)
|
|
1012
|
+
except:
|
|
1013
|
+
msg = 'Error:list_append和服务端失去连接,即将重连'
|
|
1014
|
+
self.sys_print(msg,show_force=1)
|
|
1015
|
+
self.__tx = False
|
|
1016
|
+
self.start_tx()
|
|
1017
|
+
|
|
1018
|
+
def list_remove(self,var,data):
|
|
1019
|
+
'''
|
|
1020
|
+
对云端变量类型为列表的var进行列表remove操作,相当于本地列表的基础操作,无返回值,默认成功
|
|
1021
|
+
|
|
1022
|
+
Parameters
|
|
1023
|
+
----------
|
|
1024
|
+
key : TYPE
|
|
1025
|
+
DESCRIPTION.
|
|
1026
|
+
data : TYPE
|
|
1027
|
+
DESCRIPTION.
|
|
1028
|
+
|
|
1029
|
+
Returns
|
|
1030
|
+
-------
|
|
1031
|
+
result : TYPE
|
|
1032
|
+
DESCRIPTION.
|
|
1033
|
+
|
|
1034
|
+
'''
|
|
1035
|
+
try:
|
|
1036
|
+
if self.__tx == False:
|
|
1037
|
+
code = -1
|
|
1038
|
+
msg = '当前tx未连接,请先执行start_tx()'
|
|
1039
|
+
result = {'code':code,'msg':msg}
|
|
1040
|
+
return result
|
|
1041
|
+
else:
|
|
1042
|
+
send_data = {'func':'list_remove','value':(var,data)}
|
|
1043
|
+
msg = self.json.dumps(send_data)
|
|
1044
|
+
self.send_data(self.__tx,msg)
|
|
1045
|
+
except:
|
|
1046
|
+
msg = 'Error:list_remove时和服务端失去连接,即将重连'
|
|
1047
|
+
self.sys_print(msg,show_force=1)
|
|
1048
|
+
self.__tx = False
|
|
1049
|
+
self.start_tx()
|
|
1050
|
+
|
|
1051
|
+
def list_pop(self,var,data):
|
|
1052
|
+
'''
|
|
1053
|
+
对云端变量类型为列表的var进行列表pop操作,相当于本地列表的基础操作,无返回值,默认成功
|
|
1054
|
+
|
|
1055
|
+
Parameters
|
|
1056
|
+
----------
|
|
1057
|
+
key : TYPE
|
|
1058
|
+
DESCRIPTION.
|
|
1059
|
+
data : TYPE
|
|
1060
|
+
DESCRIPTION.
|
|
1061
|
+
|
|
1062
|
+
Returns
|
|
1063
|
+
-------
|
|
1064
|
+
result : TYPE
|
|
1065
|
+
DESCRIPTION.
|
|
1066
|
+
|
|
1067
|
+
'''
|
|
1068
|
+
try:
|
|
1069
|
+
if self.__tx == False:
|
|
1070
|
+
code = -1
|
|
1071
|
+
msg = '当前tx未连接,请先执行start_tx()'
|
|
1072
|
+
result = {'code':code,'msg':msg}
|
|
1073
|
+
return result
|
|
1074
|
+
else:
|
|
1075
|
+
send_data = {'func':'list_pop','value':(var,data)}
|
|
1076
|
+
msg = self.json.dumps(send_data)
|
|
1077
|
+
self.send_data(self.__tx,msg)
|
|
1078
|
+
except:
|
|
1079
|
+
msg = 'Error:listpop和服务端失去连接,即将重连'
|
|
1080
|
+
self.sys_print(msg,show_force=1)
|
|
1081
|
+
self.__tx = False
|
|
1082
|
+
self.start_tx()
|
|
1083
|
+
|
|
1084
|
+
def dict_change(self,var,key,value):
|
|
1085
|
+
'''
|
|
1086
|
+
对云端变量为var的字典进行字典操作,同基础的字典操作,无返回值,默认成功
|
|
1087
|
+
|
|
1088
|
+
Parameters
|
|
1089
|
+
----------
|
|
1090
|
+
var : TYPE
|
|
1091
|
+
DESCRIPTION.
|
|
1092
|
+
key : TYPE
|
|
1093
|
+
DESCRIPTION.
|
|
1094
|
+
value : TYPE
|
|
1095
|
+
DESCRIPTION.
|
|
1096
|
+
|
|
1097
|
+
Returns
|
|
1098
|
+
-------
|
|
1099
|
+
result : TYPE
|
|
1100
|
+
DESCRIPTION.
|
|
1101
|
+
|
|
1102
|
+
'''
|
|
1103
|
+
try:
|
|
1104
|
+
if self.__tx == False:
|
|
1105
|
+
code = -1
|
|
1106
|
+
msg = '当前tx未连接,请先执行start_tx()'
|
|
1107
|
+
result = {'code':code,'msg':msg}
|
|
1108
|
+
return result
|
|
1109
|
+
else:
|
|
1110
|
+
send_data = {'func':'dict_change','value':(var,key,value)}
|
|
1111
|
+
msg = self.json.dumps(send_data)
|
|
1112
|
+
self.send_data(self.__tx,msg)
|
|
1113
|
+
except:
|
|
1114
|
+
msg = 'Error:dict_change和服务端失去连接,即将重连'
|
|
1115
|
+
self.sys_print(msg,show_force=1)
|
|
1116
|
+
self.__tx = False
|
|
1117
|
+
self.start_tx()
|
|
1118
|
+
|
|
1119
|
+
def push(self,key,data,who=None):
|
|
1120
|
+
try:
|
|
1121
|
+
if self.__tx == False:
|
|
1122
|
+
code = -1
|
|
1123
|
+
msg = '当前tx未连接,请先执行start_tx()'
|
|
1124
|
+
result = {'code':code,'msg':msg}
|
|
1125
|
+
return result
|
|
1126
|
+
else:
|
|
1127
|
+
send_data = {'func':'push','value':'%s|%s'%(key,data),'who':who}
|
|
1128
|
+
msg = self.json.dumps(send_data)
|
|
1129
|
+
self.send_data(self.__tx,msg)
|
|
1130
|
+
|
|
1131
|
+
except Exception as e:
|
|
1132
|
+
raise ConnectionAbortedError('与服务器连接断开,push函数出错,请注意你传入的数据类型必须为字符串,请查看上方的报错内容>>>%s'%(e))
|
|
1133
|
+
|
|
1134
|
+
def push_plus(self,key,data,who=''):
|
|
1135
|
+
'''
|
|
1136
|
+
超级push函数,会比push函数更快,效率更高,确保你传入的参数均为字符串,否则推送不成功
|
|
1137
|
+
|
|
1138
|
+
Parameters
|
|
1139
|
+
----------
|
|
1140
|
+
key : TYPE
|
|
1141
|
+
DESCRIPTION.
|
|
1142
|
+
data : TYPE
|
|
1143
|
+
DESCRIPTION.
|
|
1144
|
+
who : TYPE, optional
|
|
1145
|
+
DESCRIPTION. The default is None.
|
|
1146
|
+
|
|
1147
|
+
Raises
|
|
1148
|
+
------
|
|
1149
|
+
ConnectionAbortedError
|
|
1150
|
+
DESCRIPTION.
|
|
1151
|
+
|
|
1152
|
+
Returns
|
|
1153
|
+
-------
|
|
1154
|
+
result : TYPE
|
|
1155
|
+
DESCRIPTION.
|
|
1156
|
+
|
|
1157
|
+
'''
|
|
1158
|
+
try:
|
|
1159
|
+
if self.__tx_plus == False:
|
|
1160
|
+
code = -1
|
|
1161
|
+
msg = '当前tx_plus未连接,请先执行start_tx_plus()'
|
|
1162
|
+
result = {'code':code,'msg':msg}
|
|
1163
|
+
return result
|
|
1164
|
+
else:
|
|
1165
|
+
msg = key+'|'+data+':who:'+who
|
|
1166
|
+
self.send_data_plus(self.__tx_plus,msg)
|
|
1167
|
+
except Exception as e:
|
|
1168
|
+
self.sys_print('push_plus出错了>>>>>%s'%(e))
|
|
1169
|
+
raise ConnectionAbortedError('与服务器连接断开,push_plus函数出错,请确保你传入的参数均为字符串,请查看上方的报错内容')
|
|
1170
|
+
|
|
1171
|
+
def MQ_push(self,var,data,who):
|
|
1172
|
+
'''
|
|
1173
|
+
采用ZMQ模式进行push,速度更快,一百万次推送耗时在1.8秒左右,但对于数据安全性没有保障,适合大通量的行情推送,在测试时发现有数据不能
|
|
1174
|
+
完全到达的情况,请自行做好数据校验机制
|
|
1175
|
+
|
|
1176
|
+
Parameters
|
|
1177
|
+
----------
|
|
1178
|
+
var : TYPE
|
|
1179
|
+
DESCRIPTION.
|
|
1180
|
+
data : TYPE
|
|
1181
|
+
DESCRIPTION.
|
|
1182
|
+
who : TYPE
|
|
1183
|
+
DESCRIPTION.
|
|
1184
|
+
|
|
1185
|
+
Returns
|
|
1186
|
+
-------
|
|
1187
|
+
None.
|
|
1188
|
+
|
|
1189
|
+
'''
|
|
1190
|
+
if self.__ZMQ:
|
|
1191
|
+
self.__ZMQ.send_string('%s|%s|%s'%(who,var,data))
|
|
1192
|
+
else:
|
|
1193
|
+
return (-1,'请先执行start_MQ()')
|
|
1194
|
+
|
|
1195
|
+
def recv_msg_put(self,client):
|
|
1196
|
+
data = client.recv(1024)
|
|
1197
|
+
dict_data = json.loads(data)
|
|
1198
|
+
return dict_data
|
|
1199
|
+
|
|
1200
|
+
def send_data(self,client, msg):
|
|
1201
|
+
if not self.orjson_on:
|
|
1202
|
+
msg = msg.encode('utf-8')
|
|
1203
|
+
self.tx_que.put(msg)
|
|
1204
|
+
|
|
1205
|
+
def send_data_plus(self,client, msg):
|
|
1206
|
+
self.tx_que_plus.put(msg)
|
|
1207
|
+
|
|
1208
|
+
def __connect_d_push(self,):
|
|
1209
|
+
'''
|
|
1210
|
+
d_push连接
|
|
1211
|
+
'''
|
|
1212
|
+
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
1213
|
+
client.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, 8192*100)
|
|
1214
|
+
# 设置立即发送(关闭 Nagle 算法,降低延迟)
|
|
1215
|
+
client.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
|
|
1216
|
+
client.connect((self.ip,self.port))
|
|
1217
|
+
tem_dict = {'con_type':'put_mode','tocken':self.tocken,
|
|
1218
|
+
'local_ip':self.local_ip,
|
|
1219
|
+
'current_file':self.current_file,'current_dir':self.current_dir,
|
|
1220
|
+
}
|
|
1221
|
+
self.sys_print(tem_dict)
|
|
1222
|
+
client.sendall(json.dumps(tem_dict).encode('utf-8'))
|
|
1223
|
+
if self.recv_msg_start_tx(client):
|
|
1224
|
+
self.__d_push_tx = client
|
|
1225
|
+
self.sys_print('d_push模式启动成功')
|
|
1226
|
+
def close_d_push(self):
|
|
1227
|
+
'''
|
|
1228
|
+
主动关闭d_push
|
|
1229
|
+
'''
|
|
1230
|
+
self.close_connection(self.__d_push_tx)
|
|
1231
|
+
self.__d_push_tx = False
|
|
1232
|
+
self.sys_print('d_push模式主动关闭')
|
|
1233
|
+
|
|
1234
|
+
def d_push(self,key,data,who):
|
|
1235
|
+
'''
|
|
1236
|
+
绕过多线程直接在当前线程进行消息推送,适合在QMT里直接调用,只适合在127.0.0.1里使用
|
|
1237
|
+
'''
|
|
1238
|
+
if not self.__d_push_tx:
|
|
1239
|
+
# if self.ip != '127.0.0.1':
|
|
1240
|
+
# raise OSError('d_push只能在127.0.0.1使用')
|
|
1241
|
+
self.__connect_d_push()
|
|
1242
|
+
|
|
1243
|
+
send_data = {'func':'push','value':'%s|%s'%(key,data),'who':who}
|
|
1244
|
+
msg = self.json.dumps(send_data)
|
|
1245
|
+
if not self.orjson_on:
|
|
1246
|
+
msg = msg.encode('utf-8')
|
|
1247
|
+
data_len = len(msg)
|
|
1248
|
+
struct_bytes = struct.pack('Q', data_len)
|
|
1249
|
+
try:
|
|
1250
|
+
self.__d_push_tx.sendall(struct_bytes)
|
|
1251
|
+
self.__d_push_tx.sendall(msg)
|
|
1252
|
+
except Exception as e:
|
|
1253
|
+
try:
|
|
1254
|
+
self.close_d_push()
|
|
1255
|
+
except:
|
|
1256
|
+
msg = 'd_push关闭异常,请留意'
|
|
1257
|
+
self.__d_push_tx = False
|
|
1258
|
+
self.__connect_d_push()
|
|
1259
|
+
self.sys_print('d_push异常>>>%s'%(str(e)))
|
|
1260
|
+
|
|
1261
|
+
def main_tx_que(self):
|
|
1262
|
+
while self.run_on:
|
|
1263
|
+
try:
|
|
1264
|
+
msg = self.tx_que.get()
|
|
1265
|
+
data_len = len(msg)
|
|
1266
|
+
struct_bytes = struct.pack('Q', data_len)
|
|
1267
|
+
self.__tx.sendall(struct_bytes)
|
|
1268
|
+
self.__tx.sendall(msg)
|
|
1269
|
+
except Exception as e:
|
|
1270
|
+
if self.run_on:
|
|
1271
|
+
msg = 'start_tx在发送数据时出错了,开始重连>>>>%s'%(e)
|
|
1272
|
+
self.sys_print(msg,show_force=True)
|
|
1273
|
+
self.__tx = False
|
|
1274
|
+
self.close_tx()
|
|
1275
|
+
self.start_tx()
|
|
1276
|
+
time.sleep(2)
|
|
1277
|
+
break
|
|
1278
|
+
|
|
1279
|
+
def main_tx_que_plus(self):
|
|
1280
|
+
while self.run_on:
|
|
1281
|
+
try:
|
|
1282
|
+
old_msg = self.tx_que_plus.get()
|
|
1283
|
+
msg=old_msg.encode('utf-8')
|
|
1284
|
+
data_len = len(msg)
|
|
1285
|
+
struct_bytes = struct.pack('Q', data_len)
|
|
1286
|
+
self.__tx_plus.sendall(struct_bytes)
|
|
1287
|
+
self.__tx_plus.sendall(msg)
|
|
1288
|
+
except:
|
|
1289
|
+
self.__tx_plus = False
|
|
1290
|
+
self.start_plus()
|
|
1291
|
+
self.tx_que_plus.put(old_msg)
|
|
1292
|
+
break
|
|
1293
|
+
|
|
1294
|
+
def recv_msg_broadcast(self,who,client,pwd):
|
|
1295
|
+
while self.run_on:
|
|
1296
|
+
try:
|
|
1297
|
+
recv_data = self.recv_data_from_tx(client)
|
|
1298
|
+
if '|' in recv_data:
|
|
1299
|
+
self.Q.put(recv_data)
|
|
1300
|
+
else:
|
|
1301
|
+
self.__txg_heartbeat_time = time.time()
|
|
1302
|
+
except Exception as e:
|
|
1303
|
+
try:
|
|
1304
|
+
self.close_connection(client,name='txg')
|
|
1305
|
+
except:
|
|
1306
|
+
pass
|
|
1307
|
+
break
|
|
1308
|
+
# try:
|
|
1309
|
+
# if self.txg_dict[who]['txg_running'] == True:
|
|
1310
|
+
# self.txg_dict[who]['txg_running'] = False
|
|
1311
|
+
# data = {'txg断线时间':self.get_nowtime(),'断线原因':str(e)}
|
|
1312
|
+
# self.sys_print(data,show_force=True)
|
|
1313
|
+
# threading.Thread(target = self.loss_connect_callback,args=(data,)).start()
|
|
1314
|
+
# self.close_connection(client,name='txg')
|
|
1315
|
+
# msg = 'txg因报错主动关闭退出'
|
|
1316
|
+
# self.sys_print(msg,show_force=True)
|
|
1317
|
+
# break
|
|
1318
|
+
# else:
|
|
1319
|
+
# print('我断线了哦')
|
|
1320
|
+
# break
|
|
1321
|
+
# except Exception as e:
|
|
1322
|
+
# data = {'txg断线时间':self.get_nowtime(),'断线原因2':str(e),'断线逻辑':'处理断线时出错'}
|
|
1323
|
+
# self.sys_print(data,show_force = True)
|
|
1324
|
+
# break
|
|
1325
|
+
# time.sleep(2)#休息5秒后判断当前是不是直接的断线了,然后再重连
|
|
1326
|
+
# if self.__tx and self.txg_running:
|
|
1327
|
+
# self.sys_print("内部错误导致txg断开,即将重连")
|
|
1328
|
+
# self.__connect_txg(who=who,pwd=pwd)
|
|
1329
|
+
# self.sys_print('我已全部退出')
|
|
1330
|
+
def main_broadcast_heartbeat(self,client,who,pwd):
|
|
1331
|
+
while self.run_on:
|
|
1332
|
+
try:
|
|
1333
|
+
msg = b'1'
|
|
1334
|
+
client.sendall(msg)
|
|
1335
|
+
except Exception as e:
|
|
1336
|
+
msg = 'txg心跳失败,开始重连>>>>%s'%(e)
|
|
1337
|
+
self.txg_dict[who]['txg_running'] = False
|
|
1338
|
+
self.sys_print(msg)
|
|
1339
|
+
break
|
|
1340
|
+
time.sleep(1)
|
|
1341
|
+
print('退出心跳------')
|
|
1342
|
+
if who in self.txg_dict and not self.txg_dict[who]['txg_running']:
|
|
1343
|
+
self.__connect_txg(who,pwd=pwd)
|
|
1344
|
+
print('我全退出了')
|
|
1345
|
+
|
|
1346
|
+
|
|
1347
|
+
def create_channel(self,num=30):
|
|
1348
|
+
'''
|
|
1349
|
+
创建一个随机信道,默认长度为30位字符串
|
|
1350
|
+
|
|
1351
|
+
Parameters
|
|
1352
|
+
----------
|
|
1353
|
+
num : int
|
|
1354
|
+
要创建的随机信道长度,默认为30位
|
|
1355
|
+
|
|
1356
|
+
Returns
|
|
1357
|
+
-------
|
|
1358
|
+
None.
|
|
1359
|
+
|
|
1360
|
+
'''
|
|
1361
|
+
s='abcdefghijklmnopqrstuvwxz12345678901'
|
|
1362
|
+
str1 = ''
|
|
1363
|
+
for i in range(num):
|
|
1364
|
+
str1 = str1 + s[random.randint(0,35)]
|
|
1365
|
+
return str1
|
|
1366
|
+
|
|
1367
|
+
def close_connection(self,client,name='tx'):
|
|
1368
|
+
try:
|
|
1369
|
+
# 强制停止读写操作,确保 recv 不再阻塞
|
|
1370
|
+
client.shutdown(socket.SHUT_RDWR)
|
|
1371
|
+
except socket.error as e:
|
|
1372
|
+
self.sys_print('关闭socket出错了>>>>,类型为:%s'%(name),e)
|
|
1373
|
+
finally:
|
|
1374
|
+
client.close()
|
|
1375
|
+
|
|
1376
|
+
def close_tx(self):
|
|
1377
|
+
self.tx_running = False
|
|
1378
|
+
if self.__tx == False:
|
|
1379
|
+
code = -1
|
|
1380
|
+
msg = '当前tx未连接'
|
|
1381
|
+
else:
|
|
1382
|
+
if self.txg_running:
|
|
1383
|
+
self.close_txg()
|
|
1384
|
+
if type(self.__tx) != bool:
|
|
1385
|
+
self.close_connection(self.__tx)
|
|
1386
|
+
self.__tx = False
|
|
1387
|
+
self.tx_que.put('')
|
|
1388
|
+
code = 0
|
|
1389
|
+
msg = 'tx关闭成功,txg也关闭成功'
|
|
1390
|
+
result = {'code':code,'msg':msg}
|
|
1391
|
+
return result
|
|
1392
|
+
|
|
1393
|
+
def close_tx_plus(self,):
|
|
1394
|
+
'''
|
|
1395
|
+
关闭push_plus功能
|
|
1396
|
+
|
|
1397
|
+
Returns
|
|
1398
|
+
-------
|
|
1399
|
+
None.
|
|
1400
|
+
|
|
1401
|
+
'''
|
|
1402
|
+
if self.__tx_plus == False:
|
|
1403
|
+
code = -1
|
|
1404
|
+
msg = '当前tx_plus功能未开启'
|
|
1405
|
+
result = {'code':code,'msg':msg}
|
|
1406
|
+
self.sys_print(result)
|
|
1407
|
+
return result
|
|
1408
|
+
else:
|
|
1409
|
+
self.close_connection(self.__tx_plus)
|
|
1410
|
+
self.__tx_plus = False
|
|
1411
|
+
|
|
1412
|
+
code = 0
|
|
1413
|
+
msg = 'tx_plus关闭成功'
|
|
1414
|
+
result = {'code':code,'msg':msg}
|
|
1415
|
+
self.sys_print(result)
|
|
1416
|
+
return result
|
|
1417
|
+
|
|
1418
|
+
def close(self,):
|
|
1419
|
+
'''
|
|
1420
|
+
关闭所有的连接对象,该函数会直接关闭
|
|
1421
|
+
'''
|
|
1422
|
+
if self.run_on:
|
|
1423
|
+
self.run_on = False
|
|
1424
|
+
self.close_tx()
|
|
1425
|
+
self.close_txg()
|
|
1426
|
+
self.close_tx_plus()
|
|
1427
|
+
msg = '手动关闭所有连接,将在1秒后退出'
|
|
1428
|
+
self.sys_print(msg,show_force=True)
|
|
1429
|
+
time.sleep(1)
|
|
1430
|
+
|
|
1431
|
+
|
|
1432
|
+
|
|
1433
|
+
def close_txg(self):
|
|
1434
|
+
self.txg_running = False
|
|
1435
|
+
self.sys_print(self.tx_running,self.__tx)
|
|
1436
|
+
for who in list(self.txg_dict.keys()):
|
|
1437
|
+
self.__close_txg(who)
|
|
1438
|
+
if not self.tx_running:
|
|
1439
|
+
self.close_tx()
|
|
1440
|
+
self.sys_print('关闭跟随启动的tx')
|
|
1441
|
+
|
|
1442
|
+
self.txg_dict = {}
|
|
1443
|
+
result = {'code':0,'msg':'所有txg关闭成功','value':{}}
|
|
1444
|
+
return result
|
|
1445
|
+
|
|
1446
|
+
def get_nowdate(self,):
|
|
1447
|
+
'''
|
|
1448
|
+
返回当前的日期,格式为2024-04-04
|
|
1449
|
+
'''
|
|
1450
|
+
return time.strftime("%Y-%m-%d")
|
|
1451
|
+
|
|
1452
|
+
def get_nowmin(self,):
|
|
1453
|
+
'''
|
|
1454
|
+
返回当前的时间,格式为19:00:00
|
|
1455
|
+
'''
|
|
1456
|
+
return time.strftime('%H:%M:%S')
|
|
1457
|
+
|
|
1458
|
+
def get_nowtime(self,):
|
|
1459
|
+
'''
|
|
1460
|
+
返回当前时间,格式为2023-11-15 20:02:01
|
|
1461
|
+
|
|
1462
|
+
Returns
|
|
1463
|
+
-------
|
|
1464
|
+
TYPE
|
|
1465
|
+
DESCRIPTION.
|
|
1466
|
+
|
|
1467
|
+
'''
|
|
1468
|
+
return time.strftime('%Y-%m-%d %H:%M:%S',time.localtime())
|
|
1469
|
+
|
|
1470
|
+
def get_timestamp(self):
|
|
1471
|
+
'''
|
|
1472
|
+
返回当前数字时间戳
|
|
1473
|
+
|
|
1474
|
+
Returns
|
|
1475
|
+
-------
|
|
1476
|
+
None.
|
|
1477
|
+
|
|
1478
|
+
'''
|
|
1479
|
+
return time.time()
|
|
1480
|
+
|
|
1481
|
+
def DatestrtingToInt(self,time_str):
|
|
1482
|
+
'''
|
|
1483
|
+
把2023-10-15 20:02:01转为1697371321
|
|
1484
|
+
|
|
1485
|
+
Parameters
|
|
1486
|
+
----------
|
|
1487
|
+
time_str : string
|
|
1488
|
+
2023-10-15 20:02:01
|
|
1489
|
+
|
|
1490
|
+
Returns
|
|
1491
|
+
-------
|
|
1492
|
+
timestamp : int
|
|
1493
|
+
返回整形时间戳1697371321.
|
|
1494
|
+
|
|
1495
|
+
'''
|
|
1496
|
+
dt = datetime.datetime.strptime(time_str, '%Y-%m-%d %H:%M:%S')
|
|
1497
|
+
timestamp = int(dt.timestamp())
|
|
1498
|
+
return timestamp
|
|
1499
|
+
def IntTimeToString(self,ct):
|
|
1500
|
+
'''
|
|
1501
|
+
把
|
|
1502
|
+
conv_time(1697371321) --> '2023-10-15 20:02:01'
|
|
1503
|
+
'''
|
|
1504
|
+
local_time = time.localtime(ct)
|
|
1505
|
+
data_head = time.strftime('%Y-%m-%d %H:%M:%S', local_time)
|
|
1506
|
+
return data_head
|
|
1507
|
+
|
|
1508
|
+
def calc_datetime_day(self,str1,str2):
|
|
1509
|
+
'''
|
|
1510
|
+
计算2023-10-15 20:24:55和2023-10-14 20:24:55之间的天数
|
|
1511
|
+
|
|
1512
|
+
Parameters
|
|
1513
|
+
----------
|
|
1514
|
+
str1 : TYPE
|
|
1515
|
+
DESCRIPTION.
|
|
1516
|
+
str2 : TYPE
|
|
1517
|
+
DESCRIPTION.
|
|
1518
|
+
|
|
1519
|
+
Returns
|
|
1520
|
+
-------
|
|
1521
|
+
TYPE
|
|
1522
|
+
DESCRIPTION.
|
|
1523
|
+
|
|
1524
|
+
'''
|
|
1525
|
+
return (datetime.datetime.strptime(str1, '%Y-%m-%d %H:%M:%S') - datetime.datetime.strptime(str2, '%Y-%m-%d %H:%M:%S')).days
|
|
1526
|
+
|
|
1527
|
+
|
|
1528
|
+
|
|
1529
|
+
def loss_connect_callback(self,data):
|
|
1530
|
+
if self.loss_callback:
|
|
1531
|
+
self.loss_callback(data)
|
|
1532
|
+
|
|
1533
|
+
def regest_loss_connect_callback(self,func):
|
|
1534
|
+
'''
|
|
1535
|
+
该功能暂时不可用,待开发
|
|
1536
|
+
注册失去连接时的回调函数,callback会传入一个字典参数,
|
|
1537
|
+
'''
|
|
1538
|
+
# self.loss_callback = func
|
|
1539
|
+
# msg = '连接断开回调函数注册成功,将在断网时回调'
|
|
1540
|
+
# self.sys_print(msg)
|
|
1541
|
+
pass
|
|
1542
|
+
|
|
1543
|
+
|
|
1544
|
+
def calc_datetime_seconds(self,str1,str2):
|
|
1545
|
+
'''计算秒差'''
|
|
1546
|
+
return (datetime.datetime.strptime(str1, '%Y-%m-%d %H:%M:%S') - datetime.datetime.strptime(str2, '%Y-%m-%d %H:%M:%S')).seconds
|
|
1547
|
+
|
|
1548
|
+
def calc_datetime_minutes(self,str1,str2):
|
|
1549
|
+
'''计算分钟差'''
|
|
1550
|
+
return (datetime.datetime.strptime(str1, '%Y-%m-%d %H:%M:%S') - datetime.datetime.strptime(str2, '%Y-%m-%d %H:%M:%S')).seconds // 60
|
|
1551
|
+
|
|
1552
|
+
def calc_zdf(N1,N2):
|
|
1553
|
+
'''
|
|
1554
|
+
计算两个值的涨跌百分比,通常用在计算收益率中
|
|
1555
|
+
'''
|
|
1556
|
+
return (N1-N2)/N2 * 100
|
|
1557
|
+
|
|
1558
|
+
def get_day_before(self,datestr,N):
|
|
1559
|
+
'''向前取N天,传入2023-10-15的格式'''
|
|
1560
|
+
date = datetime.datetime.strptime(datestr, '%Y-%m-%d')
|
|
1561
|
+
date_before = date - datetime.timedelta(days=N)
|
|
1562
|
+
return str(date_before.date())
|
|
1563
|
+
|
|
1564
|
+
def get_day_next(self,datestr,N):
|
|
1565
|
+
'''
|
|
1566
|
+
向前取N天,传入2023-10-14的格式
|
|
1567
|
+
'''
|
|
1568
|
+
date = datetime.datetime.strptime(datestr, '%Y-%m-%d')
|
|
1569
|
+
date_before = date + datetime.timedelta(days=N)
|
|
1570
|
+
return str(date_before.date())
|
|
1571
|
+
|
|
1572
|
+
def get_day_cha(self,datestr,N):
|
|
1573
|
+
'''
|
|
1574
|
+
返回N天后的日期,注意N的正负号,传入"2024-04-16 15:00:00"或者"15:00:00"
|
|
1575
|
+
返回值为字符串,如果N为3,返回"2024-04-19 15:00:00"
|
|
1576
|
+
'''
|
|
1577
|
+
if ' ' in datestr:
|
|
1578
|
+
date = datetime.datetime.strptime(datestr, '%Y-%m-%d %H:%M:%S')
|
|
1579
|
+
else:
|
|
1580
|
+
date = datetime.datetime.strptime(datestr, '%H:%M:%S')
|
|
1581
|
+
if N > 0:
|
|
1582
|
+
date_before = date + datetime.timedelta(days=N)
|
|
1583
|
+
else:
|
|
1584
|
+
date_before = date - datetime.timedelta(days=-N)
|
|
1585
|
+
if ' ' in datestr:
|
|
1586
|
+
result = str(date_before)
|
|
1587
|
+
else:
|
|
1588
|
+
result = str(date_before).split(' ')[1]
|
|
1589
|
+
return result
|
|
1590
|
+
|
|
1591
|
+
|
|
1592
|
+
def get_min_cha(self,datestr,N):
|
|
1593
|
+
'''
|
|
1594
|
+
返回分钟差后的日期,注意N的正负号,传入"2024-04-16 15:00:00"或者"15:00:00"
|
|
1595
|
+
返回值为字符串
|
|
1596
|
+
'''
|
|
1597
|
+
if ' ' in datestr:
|
|
1598
|
+
date = datetime.datetime.strptime(datestr, '%Y-%m-%d %H:%M:%S')
|
|
1599
|
+
else:
|
|
1600
|
+
date = datetime.datetime.strptime(datestr, '%H:%M:%S')
|
|
1601
|
+
if N > 0:
|
|
1602
|
+
date_before = date + datetime.timedelta(minutes=N)
|
|
1603
|
+
else:
|
|
1604
|
+
date_before = date - datetime.timedelta(minutes=-N)
|
|
1605
|
+
if ' ' in datestr:
|
|
1606
|
+
result = str(date_before)
|
|
1607
|
+
else:
|
|
1608
|
+
result = str(date_before).split(' ')[1]
|
|
1609
|
+
return result
|
|
1610
|
+
|
|
1611
|
+
def get_second_cha(self,datestr,N):
|
|
1612
|
+
'''
|
|
1613
|
+
返回秒差后的日期,注意N的正负号,传入"2024-04-16 15:00:00"或者"15:00:00"
|
|
1614
|
+
返回值为字符串
|
|
1615
|
+
'''
|
|
1616
|
+
if ' ' in datestr:
|
|
1617
|
+
date = datetime.datetime.strptime(datestr, '%Y-%m-%d %H:%M:%S')
|
|
1618
|
+
else:
|
|
1619
|
+
date = datetime.datetime.strptime(datestr, '%H:%M:%S')
|
|
1620
|
+
if N > 0:
|
|
1621
|
+
date_before = date + datetime.timedelta(seconds=N)
|
|
1622
|
+
else:
|
|
1623
|
+
date_before = date - datetime.timedelta(seconds=-N)
|
|
1624
|
+
if ' ' in datestr:
|
|
1625
|
+
result = str(date_before)
|
|
1626
|
+
else:
|
|
1627
|
+
result = str(date_before).split(' ')[1]
|
|
1628
|
+
return result
|
|
1629
|
+
|
|
1630
|
+
def judge_is_TradeDay(self,datestr=None):
|
|
1631
|
+
'''
|
|
1632
|
+
判断datestr是不是A股交易日,需要先安装pandas_market_calendars库的支持
|
|
1633
|
+
如果没有传入日期,则返回判断当天是不是交易日
|
|
1634
|
+
|
|
1635
|
+
Parameters
|
|
1636
|
+
----------
|
|
1637
|
+
datestring : TYPE
|
|
1638
|
+
DESCRIPTION.
|
|
1639
|
+
|
|
1640
|
+
Returns
|
|
1641
|
+
bool
|
|
1642
|
+
-------
|
|
1643
|
+
如果时交易日则返回为True,否则为False.
|
|
1644
|
+
|
|
1645
|
+
'''
|
|
1646
|
+
if not datestr:
|
|
1647
|
+
datestr = time.strftime("%Y-%m-%d")
|
|
1648
|
+
if datestr in self.dict_TradeDay:
|
|
1649
|
+
return self.dict_TradeDay[datestr]
|
|
1650
|
+
|
|
1651
|
+
import pandas_market_calendars as mcal
|
|
1652
|
+
sse = mcal.get_calendar('SSE')#上海证券交易所日历
|
|
1653
|
+
td_df = sse.schedule(start_date=datestr, end_date=datestr)
|
|
1654
|
+
if len(td_df) > 0:
|
|
1655
|
+
result = True
|
|
1656
|
+
else:
|
|
1657
|
+
result = False
|
|
1658
|
+
self.dict_TradeDay[datestr] = result
|
|
1659
|
+
return result
|
|
1660
|
+
|
|
1661
|
+
def get_last_TradeDay(self,):
|
|
1662
|
+
'''
|
|
1663
|
+
返回中国A股市场最近的一个交易日
|
|
1664
|
+
'''
|
|
1665
|
+
import pandas_market_calendars as mcal
|
|
1666
|
+
sse = mcal.get_calendar('SSE')#上海证券交易所日历
|
|
1667
|
+
now_date = time.strftime("%Y-%m-%d")
|
|
1668
|
+
last_date = self.get_day_before(now_date,30)
|
|
1669
|
+
td_df = sse.schedule(start_date=last_date, end_date=now_date)
|
|
1670
|
+
result = str(td_df.index[-1]).split(' ')[0]
|
|
1671
|
+
return result
|
|
1672
|
+
|
|
1673
|
+
def get_TradeDay_List(self,N1,N2):
|
|
1674
|
+
'''
|
|
1675
|
+
获取N1到N2之间的中国交易日,传入字符串时间
|
|
1676
|
+
N1: 2025-01-01
|
|
1677
|
+
N2: 2025-01-12
|
|
1678
|
+
'''
|
|
1679
|
+
import pandas_market_calendars as mcal
|
|
1680
|
+
sse = mcal.get_calendar('SSE')#上海证券交易所日历
|
|
1681
|
+
td_df = sse.schedule(start_date=N1, end_date=N2)
|
|
1682
|
+
l1 = []
|
|
1683
|
+
for i in list(td_df.index):
|
|
1684
|
+
l1.append(str(i).split(' ')[0])
|
|
1685
|
+
|
|
1686
|
+
return l1
|
|
1687
|
+
|
|
1688
|
+
def cal_trade_day_cha(self,str1:str,str2:str)->int:
|
|
1689
|
+
'''
|
|
1690
|
+
获取两个日期之间的交易日天数,传入格式:
|
|
1691
|
+
str1:2025-12-30
|
|
1692
|
+
str2:2026-01-06
|
|
1693
|
+
返回整数
|
|
1694
|
+
'''
|
|
1695
|
+
if str1 == str2:
|
|
1696
|
+
return 0
|
|
1697
|
+
if self.td_df.empty:
|
|
1698
|
+
import pandas_market_calendars as mcal
|
|
1699
|
+
self.sse = mcal.get_calendar('SSE')#上海证券交易所日历
|
|
1700
|
+
self.td_df = self.sse.schedule(start_date=str1, end_date=str2)
|
|
1701
|
+
if str1 < str(self.td_df.index[0]) or str2 > str(self.td_df.index[-1]):
|
|
1702
|
+
self.td_df = self.sse.schedule(start_date=str1, end_date=str2)
|
|
1703
|
+
tem_df = pd.DataFrame()
|
|
1704
|
+
tem_df['交易日'] = self.td_df.index
|
|
1705
|
+
tem_df['交易日'].apply(lambda x:str(x))
|
|
1706
|
+
tem_td_df = tem_df[tem_df['交易日'] >= str1]
|
|
1707
|
+
tem_td_df = tem_td_df[tem_td_df['交易日'] <= str2]
|
|
1708
|
+
result = len(tem_td_df)
|
|
1709
|
+
return result
|
|
1710
|
+
|
|
1711
|
+
def get_pre_TradeDay(self,N=1):
|
|
1712
|
+
'''
|
|
1713
|
+
返回A股市场上N个交易日,默认为1,即返回昨日,0表示返回最近一个交易日
|
|
1714
|
+
'''
|
|
1715
|
+
import pandas_market_calendars as mcal
|
|
1716
|
+
now_date = self.get_nowdate()
|
|
1717
|
+
if now_date != self.pre_TradeDay['now_date']:
|
|
1718
|
+
self.pre_TradeDay = {'now_date':now_date}
|
|
1719
|
+
sse = mcal.get_calendar('SSE')#上海证券交易所日历
|
|
1720
|
+
now_date = time.strftime("%Y-%m-%d")
|
|
1721
|
+
last_date = self.get_day_before(now_date,N+30)
|
|
1722
|
+
td_df = sse.schedule(start_date=last_date, end_date=now_date)
|
|
1723
|
+
result = str(td_df.index[-(N+1)]).split(' ')[0]
|
|
1724
|
+
self.pre_TradeDay[N] = result
|
|
1725
|
+
if N not in self.pre_TradeDay:
|
|
1726
|
+
self.pre_TradeDay = {'now_date':now_date}
|
|
1727
|
+
sse = mcal.get_calendar('SSE')#上海证券交易所日历
|
|
1728
|
+
now_date = time.strftime("%Y-%m-%d")
|
|
1729
|
+
last_date = self.get_day_before(now_date,N+30)
|
|
1730
|
+
td_df = sse.schedule(start_date=last_date, end_date=now_date)
|
|
1731
|
+
result = str(td_df.index[-(N+1)]).split(' ')[0]
|
|
1732
|
+
self.pre_TradeDay[N] = result
|
|
1733
|
+
result = self.pre_TradeDay.get(N)
|
|
1734
|
+
return result
|
|
1735
|
+
|
|
1736
|
+
def get_local_ip(self,):
|
|
1737
|
+
'''返回局域网IPV4地址'''
|
|
1738
|
+
import socket
|
|
1739
|
+
try:
|
|
1740
|
+
# 创建一个socket对象
|
|
1741
|
+
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
|
1742
|
+
# 不需要真的连接,所以使用一个不存在的地址
|
|
1743
|
+
s.connect(("10.255.255.255", 1))
|
|
1744
|
+
IP = s.getsockname()[0]
|
|
1745
|
+
except Exception:
|
|
1746
|
+
IP = "127.0.0.1" # 如果出现异常,则返回回环地址
|
|
1747
|
+
finally:
|
|
1748
|
+
s.close() # 关闭socket
|
|
1749
|
+
return IP
|
|
1750
|
+
|
|
1751
|
+
def get_public_ip(self,):
|
|
1752
|
+
'''
|
|
1753
|
+
返回公网IPV4地址
|
|
1754
|
+
'''
|
|
1755
|
+
response = requests.get('https://httpbin.org/ip')
|
|
1756
|
+
ip = response.json().get('origin')
|
|
1757
|
+
return ip
|
|
1758
|
+
|
|
1759
|
+
def judge_Time_between(self,str1,str2):
|
|
1760
|
+
'''
|
|
1761
|
+
判断当前时间是不是介于str1和str2之间
|
|
1762
|
+
str1格式: '15:00:05'
|
|
1763
|
+
str2格式: '16:00:00'
|
|
1764
|
+
|
|
1765
|
+
'''
|
|
1766
|
+
return str1 < time.strftime('%H:%M:%S') < str2
|
|
1767
|
+
|
|
1768
|
+
|
|
1769
|
+
def save_json(self,file_name,obj):
|
|
1770
|
+
with open(file_name,'w',encoding='utf-8') as f:
|
|
1771
|
+
json.dump(obj,f,ensure_ascii=False,indent=2)
|
|
1772
|
+
|
|
1773
|
+
|
|
1774
|
+
def load_json(self,file_name):
|
|
1775
|
+
with open(file_name,'r',encoding='utf-8') as f :
|
|
1776
|
+
data = json.load(f)
|
|
1777
|
+
return data
|
|
1778
|
+
|
|
1779
|
+
def save_json_fix(self,file_name,dict_obj):
|
|
1780
|
+
'''
|
|
1781
|
+
保存json,会自动将不符合的json数据转为正常的,如numpy.float64转为正常的float64,
|
|
1782
|
+
传入的对象为字典
|
|
1783
|
+
|
|
1784
|
+
待开发
|
|
1785
|
+
'''
|
|
1786
|
+
|
|
1787
|
+
def mkdir(self,file_name):
|
|
1788
|
+
'''
|
|
1789
|
+
创建一个目录,如果已经存在则会跳过
|
|
1790
|
+
'''
|
|
1791
|
+
try:
|
|
1792
|
+
os.makedirs(file_name, exist_ok=True)
|
|
1793
|
+
except:
|
|
1794
|
+
pass
|
|
1795
|
+
|
|
1796
|
+
@staticmethod
|
|
1797
|
+
def proxy(ip, port, user='', passwd='', proxy_type='http', ssh_remote_host='', ssh_remote_port=0, ssh_bind_port=10800):
|
|
1798
|
+
'''
|
|
1799
|
+
实现在程序内部通过 http、socks5 或 ssh 建立代理。
|
|
1800
|
+
|
|
1801
|
+
- proxy_type: 可选 'http', 'socks5', 'ssh'
|
|
1802
|
+
- ssh_remote_host/ssh_remote_port: 仅在 proxy_type 为 ssh 时使用,表示远程跳板机地址和端口
|
|
1803
|
+
- ssh_bind_port: 本地映射端口(默认 10800)
|
|
1804
|
+
|
|
1805
|
+
使用示例:
|
|
1806
|
+
@tx1.proxy(ip='127.0.0.1', port=1080, proxy_type='socks5')
|
|
1807
|
+
def get_data():
|
|
1808
|
+
pass
|
|
1809
|
+
|
|
1810
|
+
@tx1.proxy(ip='ssh_server_ip', port=22, user='ssh_user', passwd='ssh_pass', proxy_type='ssh',
|
|
1811
|
+
ssh_remote_host='8.8.8.8', ssh_remote_port=80)
|
|
1812
|
+
def get_data():
|
|
1813
|
+
pass
|
|
1814
|
+
'''
|
|
1815
|
+
from functools import wraps
|
|
1816
|
+
import socket
|
|
1817
|
+
import socks
|
|
1818
|
+
|
|
1819
|
+
def decorator(func):
|
|
1820
|
+
@wraps(func)
|
|
1821
|
+
def wrapper(*args, **kwargs):
|
|
1822
|
+
if proxy_type.lower() == 'ssh':
|
|
1823
|
+
from sshtunnel import SSHTunnelForwarder
|
|
1824
|
+
|
|
1825
|
+
server = SSHTunnelForwarder(
|
|
1826
|
+
(ip, port), # SSH 跳板机的 IP 和端口
|
|
1827
|
+
ssh_username=user,
|
|
1828
|
+
ssh_password=passwd,
|
|
1829
|
+
remote_bind_address=(ssh_remote_host, ssh_remote_port),
|
|
1830
|
+
local_bind_address=('127.0.0.1', ssh_bind_port)
|
|
1831
|
+
)
|
|
1832
|
+
|
|
1833
|
+
server.start()
|
|
1834
|
+
print(f"[SSH代理] 本地代理端口: {server.local_bind_port}")
|
|
1835
|
+
|
|
1836
|
+
try:
|
|
1837
|
+
# 对于 SSH 场景,将请求转发到本地代理端口
|
|
1838
|
+
proxies = {
|
|
1839
|
+
'http': f'http://127.0.0.1:{server.local_bind_port}',
|
|
1840
|
+
'https': f'http://127.0.0.1:{server.local_bind_port}',
|
|
1841
|
+
}
|
|
1842
|
+
kwargs['proxies'] = proxies
|
|
1843
|
+
return func(*args, **kwargs)
|
|
1844
|
+
finally:
|
|
1845
|
+
server.stop()
|
|
1846
|
+
print("[SSH代理] 隧道已关闭")
|
|
1847
|
+
|
|
1848
|
+
elif proxy_type.lower() == 'socks5':
|
|
1849
|
+
socks.set_default_proxy(socks.SOCKS5, ip, port, username=user, password=passwd)
|
|
1850
|
+
socket.socket = socks.socksocket
|
|
1851
|
+
return func(*args, **kwargs)
|
|
1852
|
+
|
|
1853
|
+
else: # 默认 HTTP/HTTPS
|
|
1854
|
+
if user and passwd:
|
|
1855
|
+
proxy_url = f"{proxy_type}://{user}:{passwd}@{ip}:{port}"
|
|
1856
|
+
else:
|
|
1857
|
+
proxy_url = f"{proxy_type}://{ip}:{port}"
|
|
1858
|
+
proxies = {
|
|
1859
|
+
'http': proxy_url,
|
|
1860
|
+
'https': proxy_url,
|
|
1861
|
+
}
|
|
1862
|
+
kwargs['proxies'] = proxies
|
|
1863
|
+
return func(*args, **kwargs)
|
|
1864
|
+
|
|
1865
|
+
return wrapper
|
|
1866
|
+
return decorator
|
|
1867
|
+
|
|
1868
|
+
|
|
1869
|
+
|
|
1870
|
+
|
|
1871
|
+
if __name__=='__main__':
|
|
1872
|
+
import requests
|
|
1873
|
+
ip = 'dm432.zl45lkjrlewklrewnet.top'#把这里替换成你服务器实际的信息即可,可以是域名,也可以是IP地址
|
|
1874
|
+
ip = '192.168.1.70'#把这里替换成你服务器实际的信息即可
|
|
1875
|
+
# ip = '127.0.0.1'
|
|
1876
|
+
ip = 'tx.txquant.cn'
|
|
1877
|
+
port = 2049
|
|
1878
|
+
|
|
1879
|
+
tocken = 'LTtx'#替换成服务端的token,默认为LTtx
|
|
1880
|
+
def call_back(data):
|
|
1881
|
+
|
|
1882
|
+
print(data)
|
|
1883
|
+
|
|
1884
|
+
tx1=txl(ip,port,tocken,loss_callback=call_back)
|
|
1885
|
+
# tx1.start_tx()
|
|
1886
|
+
tx1.start_txg('litao@test20@GY_HQ_test')
|
|
1887
|
+
|
|
1888
|
+
dict_count = {'count':0}#用来计数统计的,不用理会
|
|
1889
|
+
# print('okkkk')
|
|
1890
|
+
def show():
|
|
1891
|
+
'''
|
|
1892
|
+
打印收到的数据,实际处理时不需要用这个,只是为了测试用
|
|
1893
|
+
'''
|
|
1894
|
+
while True:
|
|
1895
|
+
data = tx1.Q.get().split('|')
|
|
1896
|
+
print(data)
|
|
1897
|
+
try:
|
|
1898
|
+
dict_count['count'] = int(data[0].split('_')[-1])
|
|
1899
|
+
except:
|
|
1900
|
+
pass
|
|
1901
|
+
try:
|
|
1902
|
+
yanchi = round((time.time() - float(data[1])) * 1000,9)
|
|
1903
|
+
print('数据延迟>>>%s ms'%(yanchi))
|
|
1904
|
+
except:
|
|
1905
|
+
pass
|
|
1906
|
+
threading.Thread(target = show).start()
|
|
1907
|
+
|
|
1908
|
+
|
|
1909
|
+
#延迟测试
|
|
1910
|
+
send_len = 10
|
|
1911
|
+
send_len = 1 * 10**6
|
|
1912
|
+
tx1.push('afds_%s'%(2),time.time(),'litao')
|
|
1913
|
+
t1 = time.time()
|
|
1914
|
+
|
|
1915
|
+
for i in range(send_len):
|
|
1916
|
+
tx1.push('afds_%s'%(i),time.time(),'litao')
|
|
1917
|
+
# time.sleep(0.1)
|
|
1918
|
+
while 1:
|
|
1919
|
+
if tx1.tx_que.empty():
|
|
1920
|
+
break
|
|
1921
|
+
time.sleep(0.1)
|
|
1922
|
+
send_use_time = time.time() - t1
|
|
1923
|
+
while 1:
|
|
1924
|
+
if dict_count['count'] == send_len - 1:
|
|
1925
|
+
break
|
|
1926
|
+
time.sleep(0.1)
|
|
1927
|
+
recv_use_time = time.time() - t1
|
|
1928
|
+
print('发送%s万条time.time()数据用时>>>>>>>>>%s秒'%(send_len/10000,send_use_time))
|
|
1929
|
+
print('接收%s万条time.time()数据用时>>>>>>>>>%s秒'%(send_len/10000,recv_use_time))
|
|
1930
|
+
# while 1:
|
|
1931
|
+
# time.sleep(1)
|
|
1932
|
+
|
|
1933
|
+
|
|
1934
|
+
|
|
1935
|
+
|