gj-unitrad 0.0.1__tar.gz
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.
- gj_unitrad-0.0.1/PKG-INFO +10 -0
- gj_unitrad-0.0.1/gj_unitrad/__init__.py +4 -0
- gj_unitrad-0.0.1/gj_unitrad/unitrad.py +398 -0
- gj_unitrad-0.0.1/gj_unitrad.egg-info/PKG-INFO +10 -0
- gj_unitrad-0.0.1/gj_unitrad.egg-info/SOURCES.txt +8 -0
- gj_unitrad-0.0.1/gj_unitrad.egg-info/dependency_links.txt +1 -0
- gj_unitrad-0.0.1/gj_unitrad.egg-info/requires.txt +2 -0
- gj_unitrad-0.0.1/gj_unitrad.egg-info/top_level.txt +1 -0
- gj_unitrad-0.0.1/setup.cfg +4 -0
- gj_unitrad-0.0.1/setup.py +22 -0
|
@@ -0,0 +1,398 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import aiohttp
|
|
3
|
+
import json
|
|
4
|
+
import yaml
|
|
5
|
+
import threading
|
|
6
|
+
import time
|
|
7
|
+
import datetime
|
|
8
|
+
|
|
9
|
+
FUNCTION_ID_RTN_STRATEGY_LOG = 11004
|
|
10
|
+
|
|
11
|
+
gstop_event = asyncio.Event()
|
|
12
|
+
class WebSocketClient:
|
|
13
|
+
def __init__(self, uri, user, passwd, strategy_name, strategy_param):
|
|
14
|
+
self.uri = uri
|
|
15
|
+
self.user = user
|
|
16
|
+
self.passwd = passwd
|
|
17
|
+
self.strategy_name = strategy_name
|
|
18
|
+
self.strategy_param = strategy_param
|
|
19
|
+
self.session = None
|
|
20
|
+
self.websocket = None
|
|
21
|
+
self.connected = False
|
|
22
|
+
self.strategy_id = -1
|
|
23
|
+
self.select_strategy = ""
|
|
24
|
+
self.results = {"position": [], "trades" : []}
|
|
25
|
+
self.condition = threading.Condition()
|
|
26
|
+
self.lock = asyncio.Lock() # 创建一个锁
|
|
27
|
+
self.ready = False
|
|
28
|
+
self._isFinish = False
|
|
29
|
+
self.func_map = {
|
|
30
|
+
10001: self.handle_login_response,
|
|
31
|
+
10005: self.handle_create_strategy_response,
|
|
32
|
+
10020: self.handle_rtn_strategy_status,
|
|
33
|
+
12029: self.handle_query_trade_list,
|
|
34
|
+
11004: self.handle_rtn_strategy_log,
|
|
35
|
+
12030: self.handle_query_position_list
|
|
36
|
+
}
|
|
37
|
+
# 日志级别映射
|
|
38
|
+
self.log_levels = {
|
|
39
|
+
0: 'Verbose',
|
|
40
|
+
1: 'Debug',
|
|
41
|
+
2: 'Info',
|
|
42
|
+
3: 'Warn',
|
|
43
|
+
4: 'Error',
|
|
44
|
+
5: 'Fatal'
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
async def update_positions(self, new_data):
|
|
48
|
+
# 处理接收到的新数据并更新结果
|
|
49
|
+
for item in new_data:
|
|
50
|
+
self.results["position"].extend(item['positionList'])
|
|
51
|
+
|
|
52
|
+
async def isFinish(self):
|
|
53
|
+
async with self.lock: # 加锁以确保线程安全
|
|
54
|
+
return self._isFinish
|
|
55
|
+
|
|
56
|
+
async def setFinish(self, value):
|
|
57
|
+
async with self.lock: # 加锁以确保线程安全
|
|
58
|
+
self._isFinish = value
|
|
59
|
+
print(f"setFinish called with value: {self._isFinish}")
|
|
60
|
+
|
|
61
|
+
def wait_for_condition(self):
|
|
62
|
+
with self.condition:
|
|
63
|
+
print("Waiting for condition to be met...")
|
|
64
|
+
while not self.ready:
|
|
65
|
+
self.condition.wait()
|
|
66
|
+
def set_condition(self):
|
|
67
|
+
time.sleep(2) # 模拟一些工作
|
|
68
|
+
with self.condition:
|
|
69
|
+
self.ready = True
|
|
70
|
+
self.condition.notify_all() # 通知所有等待的线程
|
|
71
|
+
|
|
72
|
+
async def connect(self):
|
|
73
|
+
"""建立 WebSocket 连接并保持活动状态"""
|
|
74
|
+
self.session = aiohttp.ClientSession()
|
|
75
|
+
try:
|
|
76
|
+
self.websocket = await self.session.ws_connect(self.uri)
|
|
77
|
+
self.connected = True
|
|
78
|
+
print("连接成功!")
|
|
79
|
+
|
|
80
|
+
# 登录请求
|
|
81
|
+
await self.login_request()
|
|
82
|
+
|
|
83
|
+
# 监听消息
|
|
84
|
+
await self.listen()
|
|
85
|
+
|
|
86
|
+
except Exception as e:
|
|
87
|
+
print(f"连接失败: {e}")
|
|
88
|
+
await self.close_session() # 确保关闭会话
|
|
89
|
+
await self.reconnect()
|
|
90
|
+
|
|
91
|
+
async def listen(self):
|
|
92
|
+
"""监听服务器消息"""
|
|
93
|
+
try:
|
|
94
|
+
async for msg in self.websocket:
|
|
95
|
+
if msg.type == aiohttp.WSMsgType.TEXT:
|
|
96
|
+
await self.process_response(msg.data)
|
|
97
|
+
elif msg.type == aiohttp.WSMsgType.ERROR:
|
|
98
|
+
print(f"WebSocket 错误: {self.websocket.exception()}")
|
|
99
|
+
break
|
|
100
|
+
except Exception as e:
|
|
101
|
+
print(f"监听时发生异常: {e}")
|
|
102
|
+
finally:
|
|
103
|
+
self.connected = False
|
|
104
|
+
|
|
105
|
+
async def process_response(self, response):
|
|
106
|
+
"""处理接收到的消息"""
|
|
107
|
+
try:
|
|
108
|
+
data = json.loads(response)
|
|
109
|
+
funcion_id = data.get("funcionId")
|
|
110
|
+
err_id = data.get("errId")
|
|
111
|
+
err_msg = data.get("errMsg")
|
|
112
|
+
response_data = data.get("data")
|
|
113
|
+
|
|
114
|
+
if err_id != 0:
|
|
115
|
+
print(f"错误代码 {err_id}: {err_msg}")
|
|
116
|
+
return
|
|
117
|
+
|
|
118
|
+
# 处理对应的 funcionId
|
|
119
|
+
if funcion_id in self.func_map:
|
|
120
|
+
await self.func_map[funcion_id](response_data)
|
|
121
|
+
else:
|
|
122
|
+
print(f"未找到处理函数 для funcionId: {funcion_id}")
|
|
123
|
+
|
|
124
|
+
except json.JSONDecodeError:
|
|
125
|
+
print("接收到的消息不是有效的 JSON 格式")
|
|
126
|
+
|
|
127
|
+
async def send_request(self, request_data):
|
|
128
|
+
"""发送请求"""
|
|
129
|
+
if self.connected:
|
|
130
|
+
print("发送请求:", request_data)
|
|
131
|
+
await self.websocket.send_str(json.dumps(request_data))
|
|
132
|
+
else:
|
|
133
|
+
print("WebSocket 尚未连接,无法发送请求")
|
|
134
|
+
async def sub_strategy_log(self):
|
|
135
|
+
request_data = {
|
|
136
|
+
"funcionId": 12014,
|
|
137
|
+
"finished": True,
|
|
138
|
+
"dataType": 1,
|
|
139
|
+
"requestId": 1,
|
|
140
|
+
"errId": 0,
|
|
141
|
+
"errMsg": "",
|
|
142
|
+
"data": [
|
|
143
|
+
{
|
|
144
|
+
"userId": f"{self.user}"
|
|
145
|
+
}
|
|
146
|
+
]
|
|
147
|
+
}
|
|
148
|
+
await self.send_request(request_data)
|
|
149
|
+
|
|
150
|
+
async def login_request(self):
|
|
151
|
+
"""发送登录请求"""
|
|
152
|
+
request_data = {
|
|
153
|
+
"funcionId": 10001,
|
|
154
|
+
"finished": True,
|
|
155
|
+
"dataType": 1,
|
|
156
|
+
"requestId": 1,
|
|
157
|
+
"errId": 0,
|
|
158
|
+
"errMsg": "",
|
|
159
|
+
"data": [
|
|
160
|
+
{
|
|
161
|
+
"userName": self.user,
|
|
162
|
+
"passWord": self.passwd
|
|
163
|
+
}
|
|
164
|
+
]
|
|
165
|
+
}
|
|
166
|
+
await self.send_request(request_data)
|
|
167
|
+
|
|
168
|
+
async def handle_login_response(self, data):
|
|
169
|
+
"""处理登录响应"""
|
|
170
|
+
print(f"处理登录响应: {data}")
|
|
171
|
+
if isinstance(data, list) and data:
|
|
172
|
+
response_data = data[0]
|
|
173
|
+
print(response_data)
|
|
174
|
+
if response_data.get("msg") == "welcome":
|
|
175
|
+
await self.sub_strategy_log()
|
|
176
|
+
print("登录成功!")
|
|
177
|
+
await self.create_strategy()
|
|
178
|
+
else:
|
|
179
|
+
print("登录失败!")
|
|
180
|
+
else:
|
|
181
|
+
print("无效的响应数据格式")
|
|
182
|
+
async def convert_to_json(self, data):
|
|
183
|
+
"""将 Python 对象转换为 JSON 字符串"""
|
|
184
|
+
return json.dumps(data, ensure_ascii=False, indent=4)
|
|
185
|
+
async def create_strategy(self):
|
|
186
|
+
"""创建策略请求"""
|
|
187
|
+
print("开始创建策略")
|
|
188
|
+
param = await self.convert_to_json(self.strategy_param)
|
|
189
|
+
request = {
|
|
190
|
+
"funcionId": 10005,
|
|
191
|
+
"finished": True,
|
|
192
|
+
"dataType": 1,
|
|
193
|
+
"requestId": 2,
|
|
194
|
+
"errId": 0,
|
|
195
|
+
"errMsg": "",
|
|
196
|
+
"data": [
|
|
197
|
+
{
|
|
198
|
+
"soName": self.strategy_name,
|
|
199
|
+
"param": param,
|
|
200
|
+
"operationType": 1,
|
|
201
|
+
"strategyId": 0,
|
|
202
|
+
"strategyType": 2,
|
|
203
|
+
"frequencyType": 1,
|
|
204
|
+
"status": 0
|
|
205
|
+
}
|
|
206
|
+
]
|
|
207
|
+
}
|
|
208
|
+
await self.send_request(request)
|
|
209
|
+
|
|
210
|
+
async def query_trade_list(self):
|
|
211
|
+
|
|
212
|
+
request_data = {
|
|
213
|
+
"funcionId": 12029,
|
|
214
|
+
"finished": True,
|
|
215
|
+
"dataType": 1,
|
|
216
|
+
"requestId": 1,
|
|
217
|
+
"errId": 0,
|
|
218
|
+
"errMsg": "",
|
|
219
|
+
"data": [{
|
|
220
|
+
"strategyId": self.select_strategy["strategyId"]
|
|
221
|
+
}]
|
|
222
|
+
}
|
|
223
|
+
await self.send_request(request_data)
|
|
224
|
+
async def query_position_list(self):
|
|
225
|
+
|
|
226
|
+
request_data = {
|
|
227
|
+
"funcionId": 12030,
|
|
228
|
+
"finished": True,
|
|
229
|
+
"dataType": 1,
|
|
230
|
+
"requestId": 1,
|
|
231
|
+
"errId": 0,
|
|
232
|
+
"errMsg": "",
|
|
233
|
+
"data": [{
|
|
234
|
+
"strategyId": self.select_strategy["strategyId"]
|
|
235
|
+
}]
|
|
236
|
+
}
|
|
237
|
+
await self.send_request(request_data)
|
|
238
|
+
|
|
239
|
+
async def start_strategy(self):
|
|
240
|
+
"""创建策略请求"""
|
|
241
|
+
print("开始创建策略")
|
|
242
|
+
param = await self.convert_to_json(self.strategy_param)
|
|
243
|
+
request = {
|
|
244
|
+
"funcionId": 10005,
|
|
245
|
+
"finished": True,
|
|
246
|
+
"dataType": 1,
|
|
247
|
+
"requestId": 2,
|
|
248
|
+
"errId": 0,
|
|
249
|
+
"errMsg": "",
|
|
250
|
+
"data": [
|
|
251
|
+
{
|
|
252
|
+
"soName": self.strategy_name,
|
|
253
|
+
"param": self.select_strategy["param"],
|
|
254
|
+
"operationType": 5,
|
|
255
|
+
"strategyId": self.select_strategy["strategyId"],
|
|
256
|
+
"strategyType": 2,
|
|
257
|
+
"frequencyType": 1,
|
|
258
|
+
"status": self.select_strategy["status"]
|
|
259
|
+
}
|
|
260
|
+
]
|
|
261
|
+
}
|
|
262
|
+
await self.send_request(request)
|
|
263
|
+
async def handle_create_strategy_response(self, data):
|
|
264
|
+
"""处理创建策略响应"""
|
|
265
|
+
#print(f"处理创建策略响应: {data}")
|
|
266
|
+
# 过滤出 soName 为 self.strategy_name 的对象
|
|
267
|
+
filtered_data = [item for item in data if item['soName'] == self.strategy_name]
|
|
268
|
+
|
|
269
|
+
# 如果有匹配的对象,则找到 strategyid 最大的对象
|
|
270
|
+
if filtered_data:
|
|
271
|
+
self.select_strategy = max(filtered_data, key=lambda x: x['strategyId'])
|
|
272
|
+
print(self.select_strategy)
|
|
273
|
+
|
|
274
|
+
if self.select_strategy["status"] == 2:
|
|
275
|
+
await self.start_strategy()
|
|
276
|
+
self.strategy_id = int(self.select_strategy["strategyId"])
|
|
277
|
+
else:
|
|
278
|
+
print("没有找到匹配的对象")
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
|
|
282
|
+
async def close_session(self):
|
|
283
|
+
"""关闭客户端会话"""
|
|
284
|
+
if self.session:
|
|
285
|
+
await self.session.close()
|
|
286
|
+
print("客户端会话已关闭")
|
|
287
|
+
|
|
288
|
+
|
|
289
|
+
async def handle_rtn_strategy_status(self, data):
|
|
290
|
+
print(f"处理策略状态推送: ", data)
|
|
291
|
+
filtered_data = [item for item in data if item['strategyId'] == self.strategy_id]
|
|
292
|
+
|
|
293
|
+
|
|
294
|
+
if filtered_data:
|
|
295
|
+
self.select_strategy = filtered_data[0]
|
|
296
|
+
|
|
297
|
+
if self.select_strategy["status"] == 6:
|
|
298
|
+
print("策略更新状态: 回测完成", filtered_data)
|
|
299
|
+
await self.query_trade_list()
|
|
300
|
+
|
|
301
|
+
async def handle_query_trade_list(self, data):
|
|
302
|
+
#print(f"处理策略成交推送: ", data)
|
|
303
|
+
self.results["trades"] = data
|
|
304
|
+
await self.query_position_list()
|
|
305
|
+
|
|
306
|
+
async def handle_rtn_strategy_log(self, data):
|
|
307
|
+
for entry in data:
|
|
308
|
+
log_level = entry['logLevel']
|
|
309
|
+
log_message = entry['logMessage']
|
|
310
|
+
log_time = entry['logTime']
|
|
311
|
+
|
|
312
|
+
# 转换 logTime 为可视化时间
|
|
313
|
+
# 假设 logTime 是以微秒为单位的时间戳
|
|
314
|
+
readable_time = datetime.datetime.fromtimestamp(log_time / 1_000_000).strftime('%Y-%m-%d %H:%M:%S')
|
|
315
|
+
|
|
316
|
+
|
|
317
|
+
# 打印格式化的日志信息
|
|
318
|
+
print(f"时间: {readable_time}")
|
|
319
|
+
print(f"日志级别: {self.log_levels.get(log_level, 'Unknown')}")
|
|
320
|
+
print("日志信息:")
|
|
321
|
+
print(log_message.strip()) # 去除首尾空白
|
|
322
|
+
|
|
323
|
+
async def handle_query_position_list(self, data):
|
|
324
|
+
#print(f"处理策略持仓推送: ", data)
|
|
325
|
+
await self.update_positions(data)
|
|
326
|
+
#self.results["position"] = data
|
|
327
|
+
gstop_event.set()
|
|
328
|
+
self.set_condition()
|
|
329
|
+
await self.setFinish(True)
|
|
330
|
+
|
|
331
|
+
|
|
332
|
+
uri = ""
|
|
333
|
+
user = ""
|
|
334
|
+
passwd = ""
|
|
335
|
+
strategy_name = ""
|
|
336
|
+
strategy_param = ""
|
|
337
|
+
|
|
338
|
+
gclient = WebSocketClient("", "", "", "", "")
|
|
339
|
+
|
|
340
|
+
def read_yaml_file(filepath):
|
|
341
|
+
"""读取 YAML 文件"""
|
|
342
|
+
with open(filepath, 'r', encoding='utf-8') as file:
|
|
343
|
+
return yaml.safe_load(file)
|
|
344
|
+
|
|
345
|
+
async def run_func(config):
|
|
346
|
+
"""运行函数,读取配置并执行相关逻辑"""
|
|
347
|
+
try:
|
|
348
|
+
config_data = read_yaml_file(config)
|
|
349
|
+
except FileNotFoundError:
|
|
350
|
+
print(f"文件 {config} 不存在!")
|
|
351
|
+
except yaml.YAMLError as e:
|
|
352
|
+
print("解析 YAML 文件时出错:", e)
|
|
353
|
+
|
|
354
|
+
env_config = config_data.get("env", {})
|
|
355
|
+
uri = env_config.get("uri")
|
|
356
|
+
user = env_config.get("user")
|
|
357
|
+
passwd = env_config.get("passwd")
|
|
358
|
+
strategy_name = env_config.get("pystrategy")
|
|
359
|
+
strategy_param = config_data
|
|
360
|
+
|
|
361
|
+
global gclient
|
|
362
|
+
gclient = WebSocketClient(uri, user, passwd, strategy_name, strategy_param)
|
|
363
|
+
await gclient.connect()
|
|
364
|
+
|
|
365
|
+
async def monitor_condition():
|
|
366
|
+
global gclient
|
|
367
|
+
while True:
|
|
368
|
+
# 检查 gclient.isFinish() 的返回值
|
|
369
|
+
if await gclient.isFinish():
|
|
370
|
+
print("Condition met, stopping the execution.")
|
|
371
|
+
gstop_event.set() # 设置事件,通知 run_func 停止
|
|
372
|
+
break # 退出循环
|
|
373
|
+
await asyncio.sleep(0.5) # 每 0.5 秒检查一次
|
|
374
|
+
|
|
375
|
+
async def run_strategy(config):
|
|
376
|
+
global gclient
|
|
377
|
+
# 创建任务
|
|
378
|
+
task = asyncio.create_task(run_func(config))
|
|
379
|
+
monitor_task = asyncio.create_task(monitor_condition())
|
|
380
|
+
# 等待任务完成或条件满足
|
|
381
|
+
await asyncio.wait([task, monitor_task], return_when=asyncio.FIRST_COMPLETED)
|
|
382
|
+
|
|
383
|
+
# 如果条件满足,取消任务
|
|
384
|
+
if gstop_event.is_set():
|
|
385
|
+
task.cancel()
|
|
386
|
+
try:
|
|
387
|
+
await task # 等待任务取消完成
|
|
388
|
+
except asyncio.CancelledError:
|
|
389
|
+
print("run_func was cancelled.")
|
|
390
|
+
#await gclient.close_session()
|
|
391
|
+
|
|
392
|
+
def run_unitrade(config):
|
|
393
|
+
global gclient
|
|
394
|
+
asyncio.run(run_strategy(config))
|
|
395
|
+
gclient.wait_for_condition()
|
|
396
|
+
|
|
397
|
+
return gclient.results
|
|
398
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
gj_unitrad
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
from setuptools import setup, find_packages
|
|
2
|
+
|
|
3
|
+
with open('description_info.md', 'r', encoding='utf-8') as f:
|
|
4
|
+
long_description = f.read()
|
|
5
|
+
|
|
6
|
+
setup(
|
|
7
|
+
name='gj_unitrad', # 你的包名
|
|
8
|
+
version='0.0.1', # 版本号
|
|
9
|
+
description='unitrad打包', # 包的简要描述
|
|
10
|
+
long_description=long_description, # 包的详细描述
|
|
11
|
+
long_description_content_type='text/markdown', # 描述文件的类型
|
|
12
|
+
include_package_data=True, # 包含包数据
|
|
13
|
+
package_data={'gj_unitrad': ['*.py']}, # 指定数据文件
|
|
14
|
+
author='yin', # 作者姓名
|
|
15
|
+
author_email='2018209921@qq.com', # 作者邮箱
|
|
16
|
+
packages=find_packages(), # 自动查找包目录
|
|
17
|
+
python_requires='>3.6', # python版本要求
|
|
18
|
+
install_requires=[
|
|
19
|
+
'yaml==6.0.2',
|
|
20
|
+
'aiohttp==3.11.10'
|
|
21
|
+
], # 依赖库列表 (除开python自带的包外的其他依赖库(代码中如果缺少了对应的库会导致无法运行的包))
|
|
22
|
+
)
|