hikyuu 2.1.1__cp39-none-win_amd64.whl → 2.1.2__cp39-none-win_amd64.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.
- hikyuu/cpp/boost_date_time-mt.dll +0 -0
- hikyuu/cpp/boost_serialization-mt.dll +0 -0
- hikyuu/cpp/boost_wserialization-mt.dll +0 -0
- hikyuu/cpp/core39.pyd +0 -0
- hikyuu/cpp/hikyuu.dll +0 -0
- hikyuu/cpp/sqlite3.dll +0 -0
- hikyuu/examples/notebook/006-TradeManager.ipynb +41 -39
- hikyuu/examples/notebook/008-Pickle.ipynb +27 -35
- hikyuu/fetcher/stock/zh_stock_a_qmt.py +49 -0
- hikyuu/gui/HikyuuTDX.py +2 -1
- hikyuu/gui/data/MainWindow.py +126 -126
- hikyuu/gui/spot_server.py +16 -7
- hikyuu/gui/start_qmt.py +36 -0
- hikyuu/include/hikyuu/Stock.h +1 -1
- hikyuu/include/hikyuu/StrategyContext.h +7 -2
- hikyuu/include/hikyuu/data_driver/base_info/table/HistoryFinanceTable.h +2 -2
- hikyuu/include/hikyuu/doc.h +2 -2
- hikyuu/include/hikyuu/global/GlobalSpotAgent.h +1 -0
- hikyuu/include/hikyuu/global/GlobalTaskGroup.h +4 -2
- hikyuu/include/hikyuu/global/SpotRecord.h +52 -0
- hikyuu/include/hikyuu/global/agent/SpotAgent.h +14 -41
- hikyuu/include/hikyuu/hikyuu.h +1 -0
- hikyuu/include/hikyuu/strategy/{AccountTradeManager.h → BrokerTradeManager.h} +98 -98
- hikyuu/include/hikyuu/strategy/RunPortfolioInStrategy.h +36 -0
- hikyuu/include/hikyuu/strategy/RunSystemInStrategy.h +37 -0
- hikyuu/include/hikyuu/strategy/Strategy.h +174 -0
- hikyuu/include/hikyuu/trade_manage/FundsRecord.h +8 -8
- hikyuu/include/hikyuu/trade_manage/OrderBrokerBase.h +66 -14
- hikyuu/include/hikyuu/trade_manage/PositionRecord.h +12 -12
- hikyuu/include/hikyuu/trade_manage/TradeManager.h +9 -0
- hikyuu/include/hikyuu/trade_manage/TradeManagerBase.h +19 -0
- hikyuu/include/hikyuu/utilities/TimerManager.h +23 -9
- hikyuu/include/hikyuu/utilities/arithmetic.h +2 -2
- hikyuu/include/hikyuu/utilities/datetime/Datetime.h +1 -0
- hikyuu/include/hikyuu/utilities/exception.h +15 -17
- hikyuu/include/hikyuu/utilities/http_client/HttpClient.h +1 -1
- hikyuu/include/hikyuu/utilities/mo/moFileReader.h +1 -1
- hikyuu/include/hikyuu/version.h +4 -4
- hikyuu/interactive.py +42 -137
- hikyuu/strategy/__init__.py +0 -1
- hikyuu/strategy/strategy_demo1.py +53 -0
- hikyuu/strategy/strategy_demo2.py +47 -0
- hikyuu/strategy/strategy_demo3.py +24 -0
- hikyuu/trade_manage/broker.py +27 -11
- hikyuu/trade_manage/broker_easytrader.py +52 -6
- hikyuu/trade_manage/broker_mail.py +17 -20
- {hikyuu-2.1.1.dist-info → hikyuu-2.1.2.dist-info}/METADATA +1 -1
- {hikyuu-2.1.1.dist-info → hikyuu-2.1.2.dist-info}/RECORD +52 -46
- {hikyuu-2.1.1.dist-info → hikyuu-2.1.2.dist-info}/top_level.txt +0 -1
- hikyuu/include/hikyuu/strategy/StrategyBase.h +0 -156
- hikyuu/strategy/demo/__init__.py +0 -3
- hikyuu/strategy/strategy.py +0 -27
- {hikyuu-2.1.1.dist-info → hikyuu-2.1.2.dist-info}/LICENSE +0 -0
- {hikyuu-2.1.1.dist-info → hikyuu-2.1.2.dist-info}/WHEEL +0 -0
- {hikyuu-2.1.1.dist-info → hikyuu-2.1.2.dist-info}/entry_points.txt +0 -0
hikyuu/interactive.py
CHANGED
|
@@ -255,44 +255,6 @@ def select(cond, start=Datetime(201801010000), end=Datetime.now(), print_out=Tru
|
|
|
255
255
|
# ==============================================================================
|
|
256
256
|
|
|
257
257
|
|
|
258
|
-
def UpdateOneRealtimeRecord_from_sina(tmpstr):
|
|
259
|
-
try:
|
|
260
|
-
if len(tmpstr) > 3 and tmpstr[:3] == 'var':
|
|
261
|
-
a = tmpstr.split(',')
|
|
262
|
-
if len(a) < 9:
|
|
263
|
-
return
|
|
264
|
-
|
|
265
|
-
open, close, high, low = float(a[1]), float(a[3]), float(a[4]), float(a[5])
|
|
266
|
-
transamount = float(a[9])
|
|
267
|
-
transcount = float(a[8])
|
|
268
|
-
|
|
269
|
-
try:
|
|
270
|
-
d = Datetime(a[-3])
|
|
271
|
-
except:
|
|
272
|
-
d = Datetime(a[-4])
|
|
273
|
-
temp = (open, high, low, close)
|
|
274
|
-
if 0 in temp:
|
|
275
|
-
return
|
|
276
|
-
|
|
277
|
-
stockstr = a[0].split('=')
|
|
278
|
-
stock = sm[stockstr[0][-8:]]
|
|
279
|
-
|
|
280
|
-
record = KRecord()
|
|
281
|
-
record.datetime = d
|
|
282
|
-
record.open = open
|
|
283
|
-
record.high = high
|
|
284
|
-
record.low = low
|
|
285
|
-
record.close = close
|
|
286
|
-
record.amount = transamount
|
|
287
|
-
record.volume = transcount / 100
|
|
288
|
-
|
|
289
|
-
stock.realtime_update(record)
|
|
290
|
-
|
|
291
|
-
except Exception as e:
|
|
292
|
-
print(tmpstr)
|
|
293
|
-
print(e)
|
|
294
|
-
|
|
295
|
-
|
|
296
258
|
def UpdateOneRealtimeRecord_from_qq(tmpstr):
|
|
297
259
|
try:
|
|
298
260
|
if len(tmpstr) > 3 and tmpstr[:2] == 'v_':
|
|
@@ -328,20 +290,6 @@ def UpdateOneRealtimeRecord_from_qq(tmpstr):
|
|
|
328
290
|
print(e)
|
|
329
291
|
|
|
330
292
|
|
|
331
|
-
def realtimePartUpdate_from_sina(queryStr):
|
|
332
|
-
result = urllib.request.urlopen(queryStr).read()
|
|
333
|
-
try:
|
|
334
|
-
result = result.decode('gbk')
|
|
335
|
-
except Exception as e:
|
|
336
|
-
print(result)
|
|
337
|
-
print(e)
|
|
338
|
-
return
|
|
339
|
-
|
|
340
|
-
result = result.split('\n')
|
|
341
|
-
for tmpstr in result:
|
|
342
|
-
UpdateOneRealtimeRecord_from_sina(tmpstr)
|
|
343
|
-
|
|
344
|
-
|
|
345
293
|
def realtimePartUpdate_from_qq(queryStr):
|
|
346
294
|
result = urllib.request.urlopen(queryStr).read()
|
|
347
295
|
try:
|
|
@@ -356,14 +304,8 @@ def realtimePartUpdate_from_qq(queryStr):
|
|
|
356
304
|
UpdateOneRealtimeRecord_from_qq(tmpstr)
|
|
357
305
|
|
|
358
306
|
|
|
359
|
-
def
|
|
360
|
-
if source == '
|
|
361
|
-
hku_error("sina已不支持获取实时数据")
|
|
362
|
-
return
|
|
363
|
-
# queryStr = "http://hq.sinajs.cn/list="
|
|
364
|
-
# update_func = realtimePartUpdate_from_sina
|
|
365
|
-
# max_size = 140
|
|
366
|
-
elif source == 'qq':
|
|
307
|
+
def realtime_update_from_website(source, stk_list=None):
|
|
308
|
+
if source == 'qq':
|
|
367
309
|
queryStr = "http://qt.gtimg.cn/q="
|
|
368
310
|
update_func = realtimePartUpdate_from_qq
|
|
369
311
|
max_size = 60
|
|
@@ -374,7 +316,9 @@ def realtime_update_from_sina_qq(source):
|
|
|
374
316
|
count = 0
|
|
375
317
|
# urls = []
|
|
376
318
|
tmpstr = queryStr
|
|
377
|
-
|
|
319
|
+
if stk_list is None:
|
|
320
|
+
stk_list = sm
|
|
321
|
+
for stock in stk_list:
|
|
378
322
|
if stock.valid and stock.type in (
|
|
379
323
|
constant.STOCKTYPE_A, constant.STOCKTYPE_INDEX, constant.STOCKTYPE_ETF, constant.STOCKTYPE_GEM, constant.STOCKTYPE_A_BJ,
|
|
380
324
|
):
|
|
@@ -402,99 +346,60 @@ def realtime_update_from_sina_qq(source):
|
|
|
402
346
|
# pool.join()
|
|
403
347
|
|
|
404
348
|
|
|
405
|
-
def
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
record.open = df.ix[i, 'open']
|
|
424
|
-
record.high = df.ix[i, 'high']
|
|
425
|
-
record.lowe = df.ix[i, 'low']
|
|
426
|
-
record.close = df.ix[i, 'trade']
|
|
427
|
-
record.amount = float(df.ix[i, 'amount'])
|
|
428
|
-
record.volume = float(df.ix[i, 'volume'])
|
|
429
|
-
|
|
430
|
-
from datetime import date
|
|
431
|
-
d = date.today()
|
|
432
|
-
record.datetime = Datetime(d)
|
|
433
|
-
stock.realtime_update(record)
|
|
434
|
-
|
|
435
|
-
# 更新指数行情
|
|
436
|
-
df = ts.get_index()
|
|
437
|
-
for i in range(len(df)):
|
|
438
|
-
code = df.ix[i][0]
|
|
439
|
-
stock = get_stock('sh' + code)
|
|
440
|
-
if stock.isNull() == True or stock.type != constant.STOCKTYPE_INDEX:
|
|
441
|
-
stock = get_stock('sz' + code)
|
|
442
|
-
if stock.isNull() == True:
|
|
443
|
-
continue
|
|
444
|
-
|
|
445
|
-
total = stock.getCount(Query.DAY)
|
|
446
|
-
if total == 0:
|
|
447
|
-
continue
|
|
448
|
-
|
|
449
|
-
last_record = stock.getKRecord(total - 1)
|
|
450
|
-
|
|
451
|
-
record = KRecord()
|
|
452
|
-
record.open = df.ix[i, 'open']
|
|
453
|
-
record.high = df.ix[i, 'high']
|
|
454
|
-
record.low = df.ix[i, 'low']
|
|
455
|
-
record.close = df.ix[i, 'close']
|
|
456
|
-
record.volume = float(df.ix[i, 'volume'])
|
|
457
|
-
record.amount = float(df.ix[i, 'amount'])
|
|
458
|
-
|
|
459
|
-
if (
|
|
460
|
-
last_record.close != record.close or last_record.high != record.high or last_record.low != record.low
|
|
461
|
-
or last_record.open != record.open
|
|
462
|
-
):
|
|
463
|
-
from datetime import date
|
|
464
|
-
d = date.today()
|
|
465
|
-
record.datetime = Datetime(d)
|
|
349
|
+
def realtime_update_from_qmt(stk_list=None):
|
|
350
|
+
from xtquant import xtdata
|
|
351
|
+
if stk_list is None:
|
|
352
|
+
stk_list = sm
|
|
353
|
+
code_list = [f'{s.code}.{s.market}' for s in stk_list if s.valid]
|
|
354
|
+
full_tick = xtdata.get_full_tick(code_list)
|
|
355
|
+
for qmt_code, data in full_tick.items():
|
|
356
|
+
try:
|
|
357
|
+
code, market = qmt_code.split(".")
|
|
358
|
+
stock = sm[f'{market}{code}']
|
|
359
|
+
record = KRecord()
|
|
360
|
+
record.datetime = Datetime(data['timetag'].split(' ')[0])
|
|
361
|
+
record.open = data['open']
|
|
362
|
+
record.high = data['high']
|
|
363
|
+
record.low = data['low']
|
|
364
|
+
record.close = data['lastPrice']
|
|
365
|
+
record.volume = data['volume'] * 0.1
|
|
366
|
+
record.amount = data['amount'] * 0.001
|
|
466
367
|
stock.realtime_update(record)
|
|
368
|
+
except Exception as e:
|
|
369
|
+
hku_error(str(e))
|
|
370
|
+
except:
|
|
371
|
+
pass
|
|
467
372
|
|
|
468
373
|
|
|
469
|
-
def realtime_update_inner(source='
|
|
470
|
-
if source == '
|
|
471
|
-
|
|
472
|
-
elif source == '
|
|
473
|
-
|
|
374
|
+
def realtime_update_inner(source='qq', stk_list=None):
|
|
375
|
+
if source == 'qq':
|
|
376
|
+
realtime_update_from_website(source, stk_list)
|
|
377
|
+
elif source == 'qmt':
|
|
378
|
+
realtime_update_from_qmt(stk_list)
|
|
474
379
|
else:
|
|
475
|
-
|
|
380
|
+
hku_error(f'Not support website source: {source}!')
|
|
476
381
|
|
|
477
382
|
|
|
478
383
|
def realtime_update_wrap():
|
|
479
384
|
pre_update_time = None
|
|
480
385
|
|
|
481
|
-
def realtime_update_closure(source='qq', delta=60):
|
|
386
|
+
def realtime_update_closure(source='qq', delta=60, stk_list=None):
|
|
482
387
|
"""
|
|
483
388
|
更新实时日线数据
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
389
|
+
:param str source: 数据源 ('qq' | 'qmt')
|
|
390
|
+
:param int delta: 最小更新间隔时间, 防止更新过于频繁
|
|
391
|
+
:param sequence stk_list: 待更新的stock列表, 如为 None 则更新全部
|
|
487
392
|
"""
|
|
488
393
|
from datetime import timedelta, datetime
|
|
489
394
|
nonlocal pre_update_time
|
|
490
395
|
now_update_time = datetime.now()
|
|
491
|
-
if (pre_update_time is None) or (now_update_time - pre_update_time) > timedelta(0, delta, 0):
|
|
492
|
-
realtime_update_inner(source)
|
|
396
|
+
if (source == 'qmt') or (pre_update_time is None) or (now_update_time - pre_update_time) > timedelta(0, delta, 0):
|
|
397
|
+
realtime_update_inner(source, stk_list)
|
|
493
398
|
pre_update_time = datetime.now()
|
|
494
|
-
print("
|
|
399
|
+
print(f"更新完毕!更新时间: {pre_update_time}")
|
|
495
400
|
else:
|
|
496
|
-
print("更新间隔小于
|
|
497
|
-
print("上次更新时间: "
|
|
401
|
+
print(f"更新间隔小于 {str(delta)} 秒,未更新")
|
|
402
|
+
print(f"上次更新时间: {pre_update_time}")
|
|
498
403
|
|
|
499
404
|
return realtime_update_closure
|
|
500
405
|
|
hikyuu/strategy/__init__.py
CHANGED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
#!/usr/bin/python
|
|
2
|
+
# -*- coding: utf8 -*-
|
|
3
|
+
# cp936
|
|
4
|
+
|
|
5
|
+
#
|
|
6
|
+
# 警告:Hikyuu 为量化研究工具,本身不包含程序化交易接口。此部分仅为策略调度运行时示例,
|
|
7
|
+
# 供自行实现程序化交易时参考,请自行负责程序化交易可能造成的损失。
|
|
8
|
+
#
|
|
9
|
+
|
|
10
|
+
from hikyuu import Strategy, Query, Datetime, Seconds, Minutes
|
|
11
|
+
from hikyuu import sm
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def on_change(stk, spot):
|
|
15
|
+
print("[on_change]:", stk.market_code, stk.name, spot.close, spot.bid1, spot.ask1)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def on_spot(rev_time):
|
|
19
|
+
print("[on_received_spot] rev_time:", rev_time)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def my_func1():
|
|
23
|
+
print("[my_func1]", str(Datetime.now()))
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def my_func2():
|
|
27
|
+
print("[my_func2] calculate:", Datetime.now())
|
|
28
|
+
for s in sm:
|
|
29
|
+
print(s)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
# 注意:
|
|
33
|
+
# 1.每一个Strategy 只能作为独立进程执行,即 python xxx.py 的方式执行!
|
|
34
|
+
# 2.请开启 HikyuuTdx 行情采集,否则接收不到数据
|
|
35
|
+
# Strategy 方式运行示例
|
|
36
|
+
if __name__ == '__main__':
|
|
37
|
+
# 创建策略运行时,必须指定 stock 和 ktype 列表
|
|
38
|
+
# strategy 只会加载指定的 stock, ktype 的数据,行情接收也只会更新这些数据
|
|
39
|
+
# 如需使用交易日历,请记得同时指定 sh000001
|
|
40
|
+
s = Strategy(['sh600000', 'sz000001'], [Query.MIN, Query.DAY])
|
|
41
|
+
|
|
42
|
+
# 当前自动10秒后执行,忽略节假日限制
|
|
43
|
+
s.run_daily_at(my_func1, Datetime.now() - Datetime.today() + Seconds(10), False)
|
|
44
|
+
|
|
45
|
+
# 收到指定 stock 的行情更新
|
|
46
|
+
s.on_change(on_change)
|
|
47
|
+
|
|
48
|
+
# 收到行情更新
|
|
49
|
+
s.on_received_spot(on_spot)
|
|
50
|
+
|
|
51
|
+
# 每隔 1 分钟循环一次 (ignore_market 忽略开闭市时间限制, 否则仅在开盘期间执行)
|
|
52
|
+
s.run_daily(my_func2, Minutes(1)) # , ignore_market=True)
|
|
53
|
+
s.start()
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
#!/usr/bin/python
|
|
2
|
+
# -*- coding: utf8 -*-
|
|
3
|
+
|
|
4
|
+
import easytrader
|
|
5
|
+
from hikyuu import *
|
|
6
|
+
|
|
7
|
+
#
|
|
8
|
+
# 警告:Hikyuu 为量化研究工具,本身不包含程序化交易接口。此部分仅为策略调度运行时示例,
|
|
9
|
+
# 供自行实现程序化交易时参考,请自行负责程序化交易可能造成的损失。
|
|
10
|
+
#
|
|
11
|
+
|
|
12
|
+
# 创建 easytrade 订单代理(示例仅支持华泰)可自行参照 EasyTraderOrderBroker 修改
|
|
13
|
+
# buy|sell 中已屏蔽实际通过easytrade下单,防止调试误操作
|
|
14
|
+
user = easytrader.use('ht_client')
|
|
15
|
+
user.connect(r'D:\htwt\xiadan.exe')
|
|
16
|
+
easy_ob = EasyTraderOrderBroker(user)
|
|
17
|
+
broker = crtOB(easy_ob)
|
|
18
|
+
|
|
19
|
+
# 获取交易系统策略
|
|
20
|
+
# 目前 run_in_strategy 只支持非延迟交易,即收盘时立刻买入/卖出
|
|
21
|
+
sys = get_part("default.sys.趋势双均线")
|
|
22
|
+
sys.set_param("buy_delay", False)
|
|
23
|
+
sys.set_param("sell_delay", False)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
# 执行策略主体
|
|
27
|
+
def my_func():
|
|
28
|
+
# 这里示例使用的是 TC_Zero() 零成本算法,但实际建议使用接近实际的成本算法
|
|
29
|
+
# 因为 sys 不依赖于成交单中的实际成本,而是使用成本算法预算需要买入的数量
|
|
30
|
+
run_in_strategy(sys, sm['sz000001'], Query(Datetime(20240101)), broker, TC_Zero())
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
# 注意:
|
|
34
|
+
# 1.每一个Strategy 只能作为独立进程执行,即 python xxx.py 的方式执行!
|
|
35
|
+
# 2.请开启 HikyuuTdx 行情采集,否则接收不到数据
|
|
36
|
+
# Strategy 方式运行示例
|
|
37
|
+
if __name__ == '__main__':
|
|
38
|
+
# 创建策略运行时,必须指定 stock 和 ktype 列表
|
|
39
|
+
# strategy 只会加载指定的 stock, ktype 的数据,行情接收也只会更新这些数据
|
|
40
|
+
# 如需使用交易日历,请记得同时指定 sh000001
|
|
41
|
+
s = Strategy(['sh000001', 'sz000001'], [Query.DAY])
|
|
42
|
+
|
|
43
|
+
# 每交易日 14点55分 执行
|
|
44
|
+
s.run_daily_at(my_func, TimeDelta(0, 14, 55))
|
|
45
|
+
s.start()
|
|
46
|
+
|
|
47
|
+
# 上述,也可以参见 strategy_demo3.py 中使用 crt_sys_strategy 快捷创建 strategy
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
#!/usr/bin/python
|
|
2
|
+
# -*- coding: utf8 -*-
|
|
3
|
+
|
|
4
|
+
#
|
|
5
|
+
# 警告:Hikyuu 为量化研究工具,本身不包含程序化交易接口。此部分仅为策略调度运行时示例,
|
|
6
|
+
# 供自行实现程序化交易时参考,请自行负责程序化交易可能造成的损失。
|
|
7
|
+
#
|
|
8
|
+
|
|
9
|
+
if __name__ == '__main__':
|
|
10
|
+
import easytrader
|
|
11
|
+
from hikyuu import *
|
|
12
|
+
|
|
13
|
+
# 创建 easytrade 订单代理(示例仅支持华泰)可自行参照 EasyTraderOrderBroker 修改
|
|
14
|
+
user = easytrader.use('ht_client')
|
|
15
|
+
user.connect(r'D:\htwt\xiadan.exe')
|
|
16
|
+
easy_ob = EasyTraderOrderBroker(user)
|
|
17
|
+
broker = crtOB(easy_ob)
|
|
18
|
+
|
|
19
|
+
sys = get_part("default.sys.趋势双均线")
|
|
20
|
+
|
|
21
|
+
# 直接使用 sys 创建 strategy 示例,如果为日线,则自动每日 14点55分 执行
|
|
22
|
+
# 如果 query 为日线以下(分钟线、5分钟线)则自动按对应间隔(分钟、5分钟)循环执行
|
|
23
|
+
stg = crt_sys_strategy(sys, "sz000001", Query(Datetime(20240101), ktype=Query.DAY), broker, TC_Zero(), "demo3")
|
|
24
|
+
stg.start()
|
hikyuu/trade_manage/broker.py
CHANGED
|
@@ -29,7 +29,9 @@
|
|
|
29
29
|
# 1. 20170704, Added by fasiondog
|
|
30
30
|
# ===============================================================================
|
|
31
31
|
|
|
32
|
+
import json
|
|
32
33
|
from hikyuu import OrderBrokerBase
|
|
34
|
+
from hikyuu.util import hku_error
|
|
33
35
|
|
|
34
36
|
|
|
35
37
|
class OrderBrokerWrap(OrderBrokerBase):
|
|
@@ -44,15 +46,29 @@ class OrderBrokerWrap(OrderBrokerBase):
|
|
|
44
46
|
super(OrderBrokerWrap, self).__init__(name)
|
|
45
47
|
self._broker = broker
|
|
46
48
|
|
|
47
|
-
def _buy(self, datetime, market, code, price, num):
|
|
48
|
-
"""
|
|
49
|
-
|
|
50
|
-
|
|
49
|
+
def _buy(self, datetime, market, code, price, num, stoploss, goal_price, part_from):
|
|
50
|
+
"""
|
|
51
|
+
实现 OrderBrokerBase 的 _buy 接口
|
|
52
|
+
:param str market: 证券市场
|
|
53
|
+
:param str code: 证券代码
|
|
54
|
+
:param float price: 买入价格
|
|
55
|
+
:param int num: 买入数量
|
|
56
|
+
"""
|
|
57
|
+
self._broker.buy(market, code, price, num, stoploss, goal_price, part_from)
|
|
51
58
|
|
|
52
|
-
def _sell(self, datetime, market, code, price, num):
|
|
59
|
+
def _sell(self, datetime, market, code, price, num, stoploss, goal_price, part_from):
|
|
53
60
|
"""实现 OrderBrokerBase 的 _sell 接口"""
|
|
54
|
-
self._broker.sell(
|
|
55
|
-
|
|
61
|
+
self._broker.sell(market, code, price, num, stoploss, goal_price, part_from)
|
|
62
|
+
|
|
63
|
+
def _get_asset_info(self):
|
|
64
|
+
try:
|
|
65
|
+
if hasattr(self._broker, "get_asset_info"):
|
|
66
|
+
ret = self._broker.get_asset_info()
|
|
67
|
+
return json.dumps(ret) if type(ret) == dict else str(ret)
|
|
68
|
+
return str()
|
|
69
|
+
except Exception as e:
|
|
70
|
+
hku_error(e)
|
|
71
|
+
return str()
|
|
56
72
|
|
|
57
73
|
|
|
58
74
|
class TestOrderBroker:
|
|
@@ -61,11 +77,11 @@ class TestOrderBroker:
|
|
|
61
77
|
def __init__(self):
|
|
62
78
|
pass
|
|
63
79
|
|
|
64
|
-
def buy(self, code, price, num):
|
|
65
|
-
print("
|
|
80
|
+
def buy(self, market, code, price, num, stoploss, goal_price, part_from):
|
|
81
|
+
print(f"买入:{market}{code}, 价格: {price}, 数量: {num} 预期止损价: {stoploss}, 预期目标价: {goal_price}, 信号来源: {part_from}")
|
|
66
82
|
|
|
67
|
-
def sell(self, code, price, num):
|
|
68
|
-
print("
|
|
83
|
+
def sell(self, market, code, price, num, stoploss, goal_price, part_from):
|
|
84
|
+
print(f"卖出:{market}{code}, 价格: {price}, 数量: {num}, 信号来源: {part_from}")
|
|
69
85
|
|
|
70
86
|
|
|
71
87
|
def crtOB(broker, name="NO_NAME"):
|
|
@@ -4,14 +4,60 @@
|
|
|
4
4
|
# Create on: 2024-01-30
|
|
5
5
|
# Author: fasiondog
|
|
6
6
|
|
|
7
|
+
from hikyuu import Datetime, hku_info
|
|
8
|
+
|
|
9
|
+
|
|
7
10
|
class EasyTraderOrderBroker:
|
|
11
|
+
'''
|
|
12
|
+
使用华泰客户端实例
|
|
13
|
+
注意:buy|sell 中已屏蔽实际通过easytrade下单,防止调试误操作,请自行根据需要打开
|
|
14
|
+
'''
|
|
15
|
+
|
|
8
16
|
def __init__(self, user):
|
|
9
17
|
self.user = user
|
|
18
|
+
self.buffer = {}
|
|
19
|
+
|
|
20
|
+
def buy(self, market, code, price, num, stoploss, goal_price, part_from):
|
|
21
|
+
# self.user.buy(code, price=price, amount=num)
|
|
22
|
+
market_code = f"{market}{code}"
|
|
23
|
+
print(f"计划买入:{market_code} {price} {num}")
|
|
24
|
+
self.buffer[market_code] = (num, stoploss, goal_price)
|
|
25
|
+
|
|
26
|
+
def sell(self, market, code, price, num, stoploss, goal_price, part_from):
|
|
27
|
+
# self.user.sell(code, price=price, amount=num)
|
|
28
|
+
market_code = f"{market}{code}"
|
|
29
|
+
print(f"计划卖出:{market_code} {price} {num}")
|
|
30
|
+
if market_code in self.buffer:
|
|
31
|
+
old_num = self.buffer[market_code][0]
|
|
32
|
+
if old_num == num:
|
|
33
|
+
self.buffer.pop(market_code)
|
|
34
|
+
else:
|
|
35
|
+
self.buffer[market_code] = (old_num - num, stoploss, goal_price)
|
|
36
|
+
|
|
37
|
+
def get_asset_info(self):
|
|
38
|
+
'''以下只适用于华泰'''
|
|
39
|
+
balance = self.user.balance
|
|
40
|
+
cash = balance['可用金额']
|
|
41
|
+
|
|
42
|
+
positions = []
|
|
43
|
+
for v in self.user.position:
|
|
44
|
+
if v["市场类别"] == "上海A":
|
|
45
|
+
market = "SH"
|
|
46
|
+
elif v["市场类别"] == "深圳A":
|
|
47
|
+
market = "SZ"
|
|
48
|
+
else:
|
|
49
|
+
hku_info(f"Ignored not supported market: {v['市场类别']}")
|
|
50
|
+
continue
|
|
10
51
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
52
|
+
code = v["证券代码"]
|
|
53
|
+
market_code = f"{market}{code}"
|
|
54
|
+
if market_code in self.buffer:
|
|
55
|
+
stoploss, goal_price = self.buffer[market_code]
|
|
56
|
+
else:
|
|
57
|
+
stoploss, goal_price = 0.0, 0.0
|
|
58
|
+
positions.append(dict(market=market, code=code,
|
|
59
|
+
number=v['可用余额'], stoploss=stoploss, goal_price=goal_price, cost_price=v['成本价']))
|
|
14
60
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
61
|
+
ret = dict(datetime=str(Datetime.now()), cash=cash, positions=positions)
|
|
62
|
+
print(ret)
|
|
63
|
+
return ret
|
|
@@ -24,24 +24,25 @@
|
|
|
24
24
|
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
25
25
|
# SOFTWARE.
|
|
26
26
|
|
|
27
|
-
|
|
27
|
+
# ===============================================================================
|
|
28
28
|
# History
|
|
29
29
|
# 1. 20170704, Added by fasiondog
|
|
30
|
-
|
|
30
|
+
# ===============================================================================
|
|
31
31
|
|
|
32
32
|
import smtplib
|
|
33
33
|
from email.mime.text import MIMEText
|
|
34
34
|
from email.header import Header
|
|
35
|
-
|
|
35
|
+
|
|
36
|
+
|
|
36
37
|
class MailOrderBroker:
|
|
37
38
|
"""
|
|
38
39
|
邮件订单代理
|
|
39
40
|
"""
|
|
40
|
-
|
|
41
|
+
|
|
41
42
|
def __init__(self, host, sender, pwd, receivers):
|
|
42
43
|
"""
|
|
43
44
|
邮件订单代理,执行买入/卖出操作时发送 Email
|
|
44
|
-
|
|
45
|
+
|
|
45
46
|
:param str host: smtp服务器地址
|
|
46
47
|
:param int port: smtp服务器端口
|
|
47
48
|
:param str sender: 发件邮箱(既用户名)
|
|
@@ -52,10 +53,10 @@ class MailOrderBroker:
|
|
|
52
53
|
self._pwd = pwd
|
|
53
54
|
self._sender = sender
|
|
54
55
|
self._receivers = receivers
|
|
55
|
-
|
|
56
|
+
|
|
56
57
|
def _sendmail(self, title, msg):
|
|
57
58
|
"""发送邮件
|
|
58
|
-
|
|
59
|
+
|
|
59
60
|
:param str title: 邮件标题
|
|
60
61
|
:param str msg: 邮件内容
|
|
61
62
|
"""
|
|
@@ -70,35 +71,31 @@ class MailOrderBroker:
|
|
|
70
71
|
smtpObj.connect(self._host, 25)
|
|
71
72
|
smtpObj.login(self._sender, self._pwd)
|
|
72
73
|
smtpObj.sendmail(self._sender, self._receivers, message.as_string())
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
def buy(self, code, price, num):
|
|
74
|
+
|
|
75
|
+
def buy(self, market, code, price, num, stoploss, goal_price, part_from):
|
|
76
76
|
"""执行买入操作,向指定的邮箱发送邮件,格式如下::
|
|
77
|
-
|
|
77
|
+
|
|
78
78
|
邮件标题:【Hkyuu提醒】买入 证券代码
|
|
79
79
|
邮件内容:买入:证券代码,价格:买入的价格,数量:买入数量
|
|
80
|
-
|
|
80
|
+
|
|
81
81
|
:param str code: 证券代码
|
|
82
82
|
:param float price: 买入价格
|
|
83
83
|
:param int num: 买入数量
|
|
84
84
|
"""
|
|
85
|
-
action = "买入:{},价格:{},数量:{} ".format(code, price, num)
|
|
85
|
+
action = "买入:{}{},价格:{},数量:{} ".format(market, code, price, num)
|
|
86
86
|
title = "【Hkyuu提醒】买入 {}".format(code)
|
|
87
87
|
self._sendmail(title, action)
|
|
88
|
-
|
|
89
88
|
|
|
90
|
-
def sell(self, code, price, num):
|
|
89
|
+
def sell(self, market, code, price, num, stoploss, goal_price, part_from):
|
|
91
90
|
"""执行卖出操作,向指定的邮箱发送邮件,格式如下::
|
|
92
|
-
|
|
91
|
+
|
|
93
92
|
邮件标题:【Hkyuu提醒】卖出 证券代码
|
|
94
93
|
邮件内容:卖出:证券代码,价格:卖出的价格,数量:卖出数量
|
|
95
|
-
|
|
94
|
+
|
|
96
95
|
:param str code: 证券代码
|
|
97
96
|
:param float price: 卖出价格
|
|
98
97
|
:param int num: 卖出数量
|
|
99
98
|
"""
|
|
100
|
-
title = "【Hkyuu提醒】卖出 {}".format(code)
|
|
99
|
+
title = "【Hkyuu提醒】卖出 {}{}".format(market, code)
|
|
101
100
|
action = "卖出:{},价格:{},数量:{} ".format(code, price, num)
|
|
102
101
|
self._sendmail(title, action)
|
|
103
|
-
|
|
104
|
-
|