hikyuu 2.7.0__py3-none-manylinux2014_aarch64.whl → 2.7.3__py3-none-manylinux2014_aarch64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (149) hide show
  1. hikyuu/__init__.py +25 -7
  2. hikyuu/__init__.pyi +23 -12
  3. hikyuu/analysis/__init__.pyi +6 -1
  4. hikyuu/analysis/analysis.pyi +7 -2
  5. hikyuu/core.pyi +8 -3
  6. hikyuu/cpp/core310.pyi +94 -24
  7. hikyuu/cpp/core310.so +0 -0
  8. hikyuu/cpp/core311.pyi +94 -24
  9. hikyuu/cpp/core311.so +0 -0
  10. hikyuu/cpp/core312.pyi +94 -24
  11. hikyuu/cpp/core312.so +0 -0
  12. hikyuu/cpp/core313.pyi +94 -24
  13. hikyuu/cpp/core313.so +0 -0
  14. hikyuu/cpp/i18n/zh_CN/hikyuu.mo +0 -0
  15. hikyuu/cpp/i18n/zh_CN/hikyuu_plugin.mo +0 -0
  16. hikyuu/cpp/libboost_atomic.so +0 -0
  17. hikyuu/cpp/libboost_atomic.so.1.90.0 +0 -0
  18. hikyuu/cpp/{libboost_charconv-mt.so → libboost_charconv.so} +0 -0
  19. hikyuu/cpp/{libboost_charconv-mt.so.1.88.0 → libboost_charconv.so.1.90.0} +0 -0
  20. hikyuu/cpp/libboost_chrono.so +0 -0
  21. hikyuu/cpp/libboost_chrono.so.1.90.0 +0 -0
  22. hikyuu/cpp/libboost_container.so +0 -0
  23. hikyuu/cpp/libboost_container.so.1.90.0 +0 -0
  24. hikyuu/cpp/libboost_date_time.so +0 -0
  25. hikyuu/cpp/libboost_date_time.so.1.90.0 +0 -0
  26. hikyuu/cpp/libboost_locale.so +0 -0
  27. hikyuu/cpp/libboost_locale.so.1.90.0 +0 -0
  28. hikyuu/cpp/libboost_random.so +0 -0
  29. hikyuu/cpp/libboost_random.so.1.90.0 +0 -0
  30. hikyuu/cpp/libboost_serialization.so +0 -0
  31. hikyuu/cpp/libboost_serialization.so.1.90.0 +0 -0
  32. hikyuu/cpp/libboost_thread.so +0 -0
  33. hikyuu/cpp/libboost_thread.so.1.90.0 +0 -0
  34. hikyuu/cpp/libboost_wserialization.so +0 -0
  35. hikyuu/cpp/libboost_wserialization.so.1.90.0 +0 -0
  36. hikyuu/cpp/libhikyuu.so +0 -0
  37. hikyuu/cpp/libsqlite3.so +0 -0
  38. hikyuu/data/clickhouse_upgrade/0001.sql +2 -0
  39. hikyuu/data/common_clickhouse.py +1 -3
  40. hikyuu/data/download_block.py +1 -1
  41. hikyuu/data/hku_config_template.py +30 -3
  42. hikyuu/data/mysql_upgrade/0029.sql +2 -0
  43. hikyuu/data/pytdx_to_clickhouse.py +86 -32
  44. hikyuu/data/pytdx_to_h5.py +73 -28
  45. hikyuu/data/pytdx_to_mysql.py +65 -21
  46. hikyuu/data/pytdx_weight_to_clickhouse.py +2 -0
  47. hikyuu/data/pytdx_weight_to_mysql.py +2 -0
  48. hikyuu/data/pytdx_weight_to_sqlite.py +2 -0
  49. hikyuu/data/sqlite_upgrade/0029.sql +4 -0
  50. hikyuu/data/tdx_to_clickhouse.py +2 -2
  51. hikyuu/data/tdx_to_h5.py +11 -11
  52. hikyuu/data/tdx_to_mysql.py +2 -2
  53. hikyuu/draw/drawplot/bokeh_draw.pyi +14 -7
  54. hikyuu/draw/drawplot/echarts_draw.pyi +14 -7
  55. hikyuu/draw/drawplot/matplotlib_draw.py +8 -2
  56. hikyuu/draw/drawplot/matplotlib_draw.pyi +14 -7
  57. hikyuu/extend.pyi +8 -3
  58. hikyuu/gui/HikyuuTDX.py +42 -3
  59. hikyuu/gui/data/MainWindow.py +189 -129
  60. hikyuu/hub.pyi +6 -6
  61. hikyuu/include/hikyuu/StockManager.h +17 -2
  62. hikyuu/include/hikyuu/StrategyContext.h +4 -4
  63. hikyuu/include/hikyuu/data_driver/BaseInfoDriver.h +2 -1
  64. hikyuu/include/hikyuu/data_driver/KDataDriver.h +2 -4
  65. hikyuu/include/hikyuu/data_driver/kdata/mysql/MySQLKDataDriver.h +5 -1
  66. hikyuu/include/hikyuu/data_driver/kdata/sqlite/SQLiteKDataDriver.h +1 -1
  67. hikyuu/include/hikyuu/global/sysinfo.h +24 -5
  68. hikyuu/include/hikyuu/indicator/IndicatorImp.h +1 -1
  69. hikyuu/include/hikyuu/plugin/KDataToClickHouseImporter.h +40 -0
  70. hikyuu/include/hikyuu/plugin/KDataToMySQLImporter.h +40 -0
  71. hikyuu/include/hikyuu/plugin/checkdata.h +20 -0
  72. hikyuu/include/hikyuu/plugin/extind.h +3 -0
  73. hikyuu/include/hikyuu/plugin/hkuextra.h +2 -0
  74. hikyuu/include/hikyuu/plugin/interface/CheckDataPluginInterface.h +25 -0
  75. hikyuu/include/hikyuu/plugin/interface/HkuExtraPluginInterface.h +2 -0
  76. hikyuu/include/hikyuu/plugin/interface/ImportKDataToClickHousePluginInterface.h +44 -0
  77. hikyuu/include/hikyuu/plugin/interface/ImportKDataToMySQLPluginInterface.h +42 -0
  78. hikyuu/include/hikyuu/plugin/interface/plugins.h +6 -0
  79. hikyuu/include/hikyuu/python/convert_any.h +9 -6
  80. hikyuu/include/hikyuu/python/pybind_utils.h +1 -1
  81. hikyuu/include/hikyuu/strategy/Strategy.h +1 -1
  82. hikyuu/include/hikyuu/trade_manage/TradeManagerBase.h +0 -1
  83. hikyuu/include/hikyuu/trade_manage/TradeRecord.h +2 -1
  84. hikyuu/include/hikyuu/trade_sys/allocatefunds/AllocateFundsBase.h +0 -1
  85. hikyuu/include/hikyuu/trade_sys/allocatefunds/build_in.h +1 -0
  86. hikyuu/include/hikyuu/trade_sys/allocatefunds/crt/AF_FixedAmount.h +26 -0
  87. hikyuu/include/hikyuu/trade_sys/allocatefunds/imp/FixAmountFunds.h +18 -0
  88. hikyuu/include/hikyuu/trade_sys/condition/ConditionBase.h +0 -1
  89. hikyuu/include/hikyuu/trade_sys/environment/EnvironmentBase.h +0 -1
  90. hikyuu/include/hikyuu/trade_sys/moneymanager/MoneyManagerBase.h +0 -1
  91. hikyuu/include/hikyuu/trade_sys/multifactor/MultiFactorBase.h +0 -1
  92. hikyuu/include/hikyuu/trade_sys/multifactor/NormalizeBase.h +0 -1
  93. hikyuu/include/hikyuu/trade_sys/multifactor/ScoresFilterBase.h +0 -1
  94. hikyuu/include/hikyuu/trade_sys/portfolio/Portfolio.h +13 -13
  95. hikyuu/include/hikyuu/trade_sys/profitgoal/ProfitGoalBase.h +9 -11
  96. hikyuu/include/hikyuu/trade_sys/selector/SelectorBase.h +0 -1
  97. hikyuu/include/hikyuu/trade_sys/signal/SignalBase.h +0 -1
  98. hikyuu/include/hikyuu/trade_sys/slippage/SlippageBase.h +0 -1
  99. hikyuu/include/hikyuu/trade_sys/stoploss/StoplossBase.h +0 -1
  100. hikyuu/include/hikyuu/trade_sys/system/System.h +1 -2
  101. hikyuu/include/hikyuu/utilities/Log.h +6 -7
  102. hikyuu/include/hikyuu/utilities/Parameter.h +17 -0
  103. hikyuu/include/hikyuu/utilities/config.h +28 -0
  104. hikyuu/include/hikyuu/utilities/plugin/PluginBase.h +17 -2
  105. hikyuu/include/hikyuu/utilities/plugin/PluginManager.h +41 -22
  106. hikyuu/include/hikyuu/utilities/thread/GlobalStealThreadPool.h +1 -2
  107. hikyuu/include/hikyuu/utilities/thread/GlobalThreadPool.h +1 -1
  108. hikyuu/include/hikyuu/utilities/thread/MQStealThreadPool.h +286 -0
  109. hikyuu/include/hikyuu/utilities/thread/MQThreadPool.h +1 -0
  110. hikyuu/include/hikyuu/utilities/thread/StealThreadPool.h +297 -0
  111. hikyuu/include/hikyuu/utilities/thread/ThreadPool.h +1 -0
  112. hikyuu/include/hikyuu/utilities/thread/WorkStealQueue.h +9 -8
  113. hikyuu/include/hikyuu/utilities/thread/algorithm.h +64 -14
  114. hikyuu/include/hikyuu/version.h +4 -4
  115. hikyuu/plugin/libbacktest.so +0 -0
  116. hikyuu/plugin/libcheckdata.so +0 -0
  117. hikyuu/plugin/libclickhousedriver.so +0 -0
  118. hikyuu/plugin/libdataserver.so +0 -0
  119. hikyuu/plugin/libdataserver_parquet.so +0 -0
  120. hikyuu/plugin/libdevice.so +0 -0
  121. hikyuu/plugin/libextind.so +0 -0
  122. hikyuu/plugin/libhkuextra.so +0 -0
  123. hikyuu/plugin/libimport2ch.so +0 -0
  124. hikyuu/plugin/libimport2hdf5.so +0 -0
  125. hikyuu/plugin/libimport2mysql.so +0 -0
  126. hikyuu/plugin/libtmreport.so +0 -0
  127. hikyuu/trade_manage/__init__.pyi +12 -7
  128. hikyuu/trade_manage/trade.pyi +12 -7
  129. hikyuu/trade_sys/trade_sys.py +54 -5
  130. hikyuu/util/__init__.pyi +1 -1
  131. hikyuu/util/singleton.pyi +1 -1
  132. {hikyuu-2.7.0.dist-info → hikyuu-2.7.3.dist-info}/METADATA +10 -4
  133. {hikyuu-2.7.0.dist-info → hikyuu-2.7.3.dist-info}/RECORD +136 -114
  134. hikyuu/cpp/libboost_chrono-mt.so +0 -0
  135. hikyuu/cpp/libboost_chrono-mt.so.1.88.0 +0 -0
  136. hikyuu/cpp/libboost_date_time-mt.so +0 -0
  137. hikyuu/cpp/libboost_date_time-mt.so.1.88.0 +0 -0
  138. hikyuu/cpp/libboost_serialization-mt.so +0 -0
  139. hikyuu/cpp/libboost_serialization-mt.so.1.88.0 +0 -0
  140. hikyuu/cpp/libboost_system-mt.so +0 -0
  141. hikyuu/cpp/libboost_system-mt.so.1.88.0 +0 -0
  142. hikyuu/cpp/libboost_thread-mt.so +0 -0
  143. hikyuu/cpp/libboost_thread-mt.so.1.88.0 +0 -0
  144. hikyuu/cpp/libboost_wserialization-mt.so +0 -0
  145. hikyuu/cpp/libboost_wserialization-mt.so.1.88.0 +0 -0
  146. hikyuu/data/pytdx_to_taos.py +0 -736
  147. {hikyuu-2.7.0.dist-info → hikyuu-2.7.3.dist-info}/WHEEL +0 -0
  148. {hikyuu-2.7.0.dist-info → hikyuu-2.7.3.dist-info}/entry_points.txt +0 -0
  149. {hikyuu-2.7.0.dist-info → hikyuu-2.7.3.dist-info}/top_level.txt +0 -0
@@ -23,13 +23,15 @@
23
23
  # SOFTWARE.
24
24
 
25
25
  import sys
26
+ import os
26
27
  import math
27
28
  import datetime
28
29
  from pytdx.hq import TDXParams
30
+ from configparser import ConfigParser
29
31
 
30
32
  from hikyuu.util import hku_error, hku_debug, hku_run_ignore_exception
31
33
 
32
- from hikyuu import Datetime, roundEx
34
+ from hikyuu import Datetime, roundEx, is_valid_license, KDataToClickHouseImporter
33
35
  from hikyuu.data.common import *
34
36
  from hikyuu.data.common_pytdx import to_pytdx_market, pytdx_get_day_trans
35
37
  from hikyuu.data.common_clickhouse import (
@@ -134,6 +136,9 @@ def import_stock_name(connect, api, market, quotations=None):
134
136
  if code not in deSet:
135
137
  newStockDict[code] = stock["name"]
136
138
 
139
+ # 号码前缀
140
+ codepre_list = get_codepre_list(connect, market, quotations)
141
+
137
142
  stktype_list = get_stktype_list(quotations)
138
143
  stktype_list = list(stktype_list)
139
144
  stktype_list.remove(STOCKTYPE.INDEX) # 移除指数类型
@@ -149,23 +154,29 @@ def import_stock_name(connect, api, market, quotations=None):
149
154
 
150
155
  oldname, oldtype, oldvalid, oldstartDate, oldendDate = oldstock[2], oldstock[3], oldstock[4], oldstock[5], oldstock[6]
151
156
 
157
+ # 检测是否类型发生变化
158
+ newtype = oldtype
159
+ for codepre in codepre_list:
160
+ length = len(codepre[0])
161
+ if oldcode[:length] == codepre[0]:
162
+ if oldtype != codepre[1]:
163
+ newtype = codepre[1]
164
+ break
165
+
152
166
  # 新的代码表中无此股票,则置为无效
153
- # if (oldvalid == 1) and (oldcode not in newStockDict):
154
167
  if (oldvalid == 1) and ((oldcode not in newStockDict) or oldcode in deSet):
155
168
  sql = f"delete from hku_base.stock where market='{market}' and code='{oldcode}'"
156
169
  connect.command(sql)
157
- insert_records.append((market, oldcode, oldname, oldtype, 0, oldstartDate, oldendDate))
170
+ insert_records.append((market, oldcode, oldname, newtype, 0, oldstartDate, oldendDate))
158
171
 
159
172
  # 股票名称发生变化,更新股票名称;如果原无效,则置为有效
160
173
  if oldcode in newStockDict:
161
- if oldname != newStockDict[oldcode] or oldvalid == 0:
174
+ if oldname != newStockDict[oldcode] or oldvalid == 0 or oldtype != newtype:
162
175
  sql = f"delete from hku_base.stock where market='{market}' and code='{oldcode}'"
163
176
  connect.command(sql)
164
- insert_records.append((market, oldcode, newStockDict[oldcode], oldtype, 1, oldstartDate, 99999999))
177
+ insert_records.append((market, oldcode, newStockDict[oldcode], newtype, 1, oldstartDate, 99999999))
165
178
 
166
179
  # 处理新出现的股票
167
- codepre_list = get_codepre_list(connect, market, quotations)
168
-
169
180
  today = datetime.date.today()
170
181
  today = today.year * 10000 + today.month * 100 + today.day
171
182
  count = 0
@@ -274,10 +285,10 @@ def import_one_stock_data(
274
285
  today.year * 10000 + today.month * 100 + today.day
275
286
  ) * 10000 + 1500
276
287
  else:
277
- return 0
288
+ return (0, True, Datetime(last_datetime))
278
289
 
279
290
  if today_datetime <= last_datetime:
280
- return 0
291
+ return (0, True, Datetime(last_datetime))
281
292
 
282
293
  get_bars = (
283
294
  api.get_index_bars if stktype == STOCKTYPE.INDEX else api.get_security_bars
@@ -312,27 +323,27 @@ def import_one_stock_data(
312
323
  if abs(last_krecord[1] - bar["open"]) / last_krecord[1] > 0.01:
313
324
  hku_error(
314
325
  f"fetch data from tdx error! {bar_datetime} {ktype} {market}{code} last_krecord open: {last_krecord[1]}, bar: {bar['open']}")
315
- return 0
326
+ return (0, False, Datetime(last_datetime))
316
327
  if abs(last_krecord[2] - bar["high"]) / last_krecord[2] > 0.01:
317
328
  hku_error(
318
329
  f"fetch data from tdx error! {bar_datetime} {ktype} {market}{code} last_krecord high: {last_krecord[2]}, bar: {bar['high']}")
319
- return 0
320
- if abs(last_krecord[3] - bar["low"]) / last_krecord[3] > 0.01:
330
+ return (0, False, Datetime(last_datetime))
331
+ if abs(last_krecord[3] - bar["low"]) / last_krecord[3] > 0.01 and abs(last_krecord[3] - bar["low"]) > 0.01:
321
332
  hku_error(
322
333
  f"fetch data from tdx error! {bar_datetime} {ktype} {market}{code} last_krecord low: {last_krecord[3]}, bar: {bar['low']}")
323
- return 0
334
+ return (0, False, Datetime(last_datetime))
324
335
  if abs(last_krecord[4] - bar["close"]) / last_krecord[4] > 0.01:
325
336
  hku_error(
326
337
  f"fetch data from tdx error! {bar_datetime} {ktype} {market}{code} last_krecord close: {last_krecord[4]}, bar: {bar['close']}")
327
- return 0
328
- if ktype == 'DAY' and last_krecord[5] != 0.0 and abs(last_krecord[5] - bar["amount"]*0.001) / last_krecord[5] > 0.1:
338
+ return (0, False, Datetime(last_datetime))
339
+ if ktype == 'DAY' and last_krecord[5] != 0.0 and abs(last_krecord[5] - bar["amount"]) > 10000:
329
340
  hku_error(
330
- f"fetch data from tdx error! {bar_datetime} {ktype} {market}{code} last_krecord amount: {last_krecord[5]}, bar: {bar['amount']*0.001}")
331
- return 0
332
- if ktype == 'DAY' and last_krecord[6] != 0.0 and last_krecord[5] != 0.0 and abs(last_krecord[6] - bar["vol"]) / last_krecord[6] > 0.1:
341
+ f"fetch data from tdx error! {bar_datetime} {ktype} {market}{code} last_krecord amount: {last_krecord[5]}, bar: {bar['amount']}")
342
+ return (0, False, Datetime(last_datetime))
343
+ if ktype == 'DAY' and last_krecord[6] != 0.0 and last_krecord[6] != 0.0 and abs(last_krecord[6] - bar["vol"]) > 10000:
333
344
  hku_error(
334
345
  f"fetch data from tdx error! {bar_datetime} {ktype} {market}{code} last_krecord count: {last_krecord[6]}, bar: {bar['vol']}")
335
- return 0
346
+ return (0, False, Datetime(last_datetime))
336
347
  continue
337
348
 
338
349
  if (
@@ -365,7 +376,7 @@ def import_one_stock_data(
365
376
  data=buf)
366
377
  connect.insert(context=ic, settings={"prefer_warmed_unmerged_parts_seconds": 86400})
367
378
 
368
- return len(buf)
379
+ return (len(buf), True, Datetime(last_datetime))
369
380
 
370
381
 
371
382
  def update_stock_info(connect, market):
@@ -409,7 +420,7 @@ def clear_extern_data(connect, market, data_type):
409
420
  index_list = ('min15', 'min30', 'min60', 'hour2')
410
421
  lastdate = connect.command(
411
422
  f"select toInt32(max(date)) from hku_data.min5_k where market='SH' and code='000001'")
412
- lastdate = Datetime.from_timestamp_utc(lastdate*1000000).start_of_day()
423
+ lastdate = Datetime.from_timestamp_utc(int(lastdate*1000000)).start_of_day()
413
424
  last_timestamp = Datetime(lastdate).timestamp_utc()//1000000
414
425
  for index_type in index_list:
415
426
  sql = f"delete from hku_data.{index_type}_k where market='{market}' and date>={last_timestamp}"
@@ -420,6 +431,16 @@ def clear_extern_data(connect, market, data_type):
420
431
  hku_info(f"清理 {market} {data_type} 线扩展数据完毕")
421
432
 
422
433
 
434
+ @hku_catch(ret=None)
435
+ def get_clickhouse_importer():
436
+ filename = os.path.expanduser('~') + '/.hikyuu/hikyuu.ini'
437
+ config = ConfigParser()
438
+ config.read(filename, encoding='utf-8')
439
+ importer = KDataToClickHouseImporter()
440
+ return importer if importer.set_config(config.get("kdata", "host"), config.getint("kdata", "port", fallback=9000),
441
+ config.get("kdata", "usr"), config.get("kdata", "pwd")) else None
442
+
443
+
423
444
  @hku_catch(trace=True, re_raise=True)
424
445
  def import_data(
425
446
  connect,
@@ -460,6 +481,9 @@ def import_data(
460
481
  for index_type in index_list:
461
482
  update_data[index_type] = []
462
483
 
484
+ failed_limit = 20
485
+ failed_count = 0
486
+ failed_list = []
463
487
  total = len(stock_list)
464
488
  # market, code, valid, type
465
489
  for i, stock in enumerate(stock_list):
@@ -468,9 +492,15 @@ def import_data(
468
492
  progress(i, total)
469
493
  continue
470
494
 
471
- this_count = import_one_stock_data(
495
+ this_count, success, lastdate = import_one_stock_data(
472
496
  connect, api, market, ktype, stock, startDate
473
497
  )
498
+ if not success:
499
+ failed_count += 1
500
+ failed_list.append((market, stock[1], lastdate))
501
+ if failed_count >= failed_limit:
502
+ # hku_error(f"{market} {ktype} 连续失败20个股票,已停止导入, 建议重新导入")
503
+ break
474
504
  add_record_count += this_count
475
505
  if ktype in ("DAY", "5MIN"):
476
506
  if ktype == "DAY":
@@ -490,6 +520,9 @@ def import_data(
490
520
  if progress:
491
521
  progress(i, total)
492
522
 
523
+ if total > 0 and progress:
524
+ progress(total, total)
525
+
493
526
  if ktype in ("DAY", "5MIN"):
494
527
  for index_type in index_list:
495
528
  if len(update_data[index_type]) > 0:
@@ -501,6 +534,25 @@ def import_data(
501
534
  update_data[index_type].clear()
502
535
  update_data.clear()
503
536
 
537
+ if 0 < failed_count < failed_limit and is_valid_license():
538
+ # 删除最后记录
539
+ ktype_dict = {
540
+ 'DAY': 'DAY',
541
+ '1MIN': 'MIN',
542
+ '5MIN': 'MIN5'
543
+ }
544
+ nktype = ktype_dict[ktype]
545
+ ch_importer = get_clickhouse_importer()
546
+ if ch_importer is not None:
547
+ for r in failed_list:
548
+ hku_warn(f"清理 {r[0]} {r[1]} {nktype} {r[2].start_of_day()}")
549
+ ch_importer.remove(r[0], r[1], nktype, r[2].start_of_day())
550
+ hku_warn(f"已清理 {market} {failed_count} 个失败股票的最后记录,建议重新导入")
551
+
552
+ if failed_count >= failed_limit:
553
+ hku_error(f"{market} {ktype} 连续失败20个股票,已停止导入, 建议重新导入")
554
+ return add_record_count
555
+
504
556
  cur_year = Datetime.today().year
505
557
  if ktype == "DAY":
506
558
  update_stock_info(connect, market)
@@ -592,15 +644,16 @@ def import_on_stock_trans(connect, api, market, stock_record, max_days):
592
644
  if second > 59:
593
645
  continue
594
646
 
595
- trans_buf.append(
596
- (
597
- market, code,
598
- Datetime(cur_date * 1000000 + minute * 100 + second).timestamp_utc()//1000000,
599
- int(roundEx(record["price"], 3) * 1000.0),
600
- int(record["vol"]),
601
- record["buyorsell"],
647
+ if record['price'] > 0.0 and record['vol'] >= 0.0:
648
+ trans_buf.append(
649
+ (
650
+ market, code,
651
+ Datetime(cur_date * 1000000 + minute * 100 + second).timestamp_utc()//1000000,
652
+ int(roundEx(record["price"], 3) * 1000.0),
653
+ int(record["vol"]),
654
+ record["buyorsell"],
655
+ )
602
656
  )
603
- )
604
657
  except Exception as e:
605
658
  hku_error("Failed trans to record! {}", e)
606
659
 
@@ -701,8 +754,9 @@ def import_on_stock_time(connect, api, market, stock_record, max_days):
701
754
  elif time == 1360:
702
755
  time = 1400
703
756
  try:
704
- time_buf.append((market, code, Datetime(this_date + time).timestamp_utc() //
705
- ticks, int(roundEx(record['price'], 3) * 1000.0), int(record['vol'])))
757
+ if record['price'] > 0.0 and record['vol'] >= 0.0:
758
+ time_buf.append((market, code, Datetime(this_date + time).timestamp_utc() //
759
+ ticks, int(roundEx(record['price'], 3) * 1000.0), int(record['vol'])))
706
760
  time += 1
707
761
  except Exception as e:
708
762
  hku_error("Failed trans record {}! {}".format(record, e))
@@ -23,13 +23,15 @@
23
23
  # SOFTWARE.
24
24
 
25
25
  import sys
26
+ import os
26
27
  import math
27
28
  import datetime
28
29
  import sqlite3
29
30
  from pytdx.hq import TDXParams
31
+ from configparser import ConfigParser
30
32
 
31
33
  from hikyuu.util.mylog import get_default_logger, hku_error, hku_debug
32
- from hikyuu import Datetime
34
+ from hikyuu import Datetime, is_valid_license, KDataToHdf5Importer
33
35
  from hikyuu.data.common import *
34
36
  from hikyuu.data.common_pytdx import to_pytdx_market, pytdx_get_day_trans
35
37
  from hikyuu.data.common_sqlite3 import (
@@ -235,6 +237,7 @@ def guess_5min_n_step(last_datetime):
235
237
  return (n, step)
236
238
 
237
239
 
240
+ @hku_catch(trace=True)
238
241
  def import_one_stock_data(connect, api, h5file, market, ktype, stock_record, startDate=199012191500):
239
242
  market = market.upper()
240
243
  pytdx_market = to_pytdx_market(market)
@@ -246,9 +249,9 @@ def import_one_stock_data(connect, api, h5file, market, ktype, stock_record, sta
246
249
  table = get_h5table(h5file, market, code)
247
250
  if table is None:
248
251
  hku_error("Can't get table({}{})".format(market, code))
249
- return 0
252
+ return (0, True, Datetime())
250
253
 
251
- last_datetime = table[-1]['datetime'] if table.nrows > 0 else startDate
254
+ last_datetime = int(table[-1]['datetime']) if table.nrows > 0 else startDate
252
255
 
253
256
  today = datetime.date.today()
254
257
  if ktype == 'DAY':
@@ -266,10 +269,10 @@ def import_one_stock_data(connect, api, h5file, market, ktype, stock_record, sta
266
269
  pytdx_kline_type = TDXParams.KLINE_TYPE_5MIN
267
270
  today_datetime = (today.year * 10000 + today.month * 100 + today.day) * 10000 + 1500
268
271
  else:
269
- return 0
272
+ return (0, True, Datetime(last_datetime))
270
273
 
271
274
  if today_datetime <= last_datetime:
272
- return 0
275
+ return (0, True, Datetime(last_datetime))
273
276
 
274
277
  get_bars = api.get_index_bars if stktype == STOCKTYPE.INDEX else api.get_security_bars
275
278
 
@@ -311,27 +314,27 @@ def import_one_stock_data(connect, api, h5file, market, ktype, stock_record, sta
311
314
  if abs(last_krecord['openPrice']*0.001 - bar["open"]) / (last_krecord['openPrice']*0.001) > 0.02:
312
315
  hku_error(
313
316
  f"fetch data from tdx error! {bar_datetime} {ktype} {market}{code} last_krecord open: {last_krecord['openPrice']*0.001}, bar: {bar['open']}")
314
- return 0
317
+ return (0, False, Datetime(last_datetime))
315
318
  if abs(last_krecord['highPrice']*0.001 - bar["high"]) / (last_krecord['highPrice']*0.001) > 0.02:
316
319
  hku_error(
317
320
  f"fetch data from tdx error! {bar_datetime} {ktype} {market}{code} last_krecord high: {last_krecord['highPrice']*0.001}, bar: {bar['high']}")
318
- return 0
321
+ return (0, False, Datetime(last_datetime))
319
322
  if abs(last_krecord['lowPrice']*0.001 - bar["low"]) / (last_krecord['lowPrice']*0.001) > 0.02:
320
323
  hku_error(
321
324
  f"fetch data from tdx error! {bar_datetime} {ktype} {market}{code} last_krecord low: {last_krecord['lowPrice']*0.001}, bar: {bar['low']}")
322
- return 0
325
+ return (0, False, Datetime(last_datetime))
323
326
  if abs(last_krecord['closePrice']*0.001 - bar["close"]) / (last_krecord['closePrice']*0.001) > 0.02:
324
327
  hku_error(
325
328
  f"fetch data from tdx error! {bar_datetime} {ktype} {market}{code} last_krecord close: {last_krecord['closePrice']*0.001}, bar: {bar['close']}")
326
- return 0
327
- if ktype == 'DAY' and last_krecord['transAmount'] != 0.0 and abs(last_krecord['transAmount'] - round(bar["amount"]*0.001)) / last_krecord['transAmount'] > 0.1:
329
+ return (0, False, Datetime(last_datetime))
330
+ if ktype == 'DAY' and last_krecord['transAmount'] != 0 and (abs(float(last_krecord['transAmount']) - round(bar["amount"]*0.001))) > 10:
328
331
  hku_error(
329
- f"fetch data from tdx error! {bar_datetime} {ktype} {market}{code} last_krecord amount: {last_krecord['transAmount']}, bar: {bar['amount']*0.001}")
330
- return 0
331
- if ktype == 'DAY' and last_krecord['transCount'] != 0.0 and abs(last_krecord['transCount'] - round(bar["vol"])) / last_krecord['transCount'] > 0.1:
332
+ f"fetch data from tdx error! {bar_datetime} {ktype} {market}{code} last_krecord amount: {float(last_krecord['transAmount'])}, bar: {round(bar['amount']*0.001)}")
333
+ return (0, False, Datetime(last_datetime))
334
+ if ktype == 'DAY' and last_krecord['transCount'] != 0 and abs(float(last_krecord['transCount']) - bar["vol"]) > 10:
332
335
  hku_error(
333
336
  f"fetch data from tdx error! {bar_datetime} {ktype} {market}{code} last_krecord count: {last_krecord['transCount']}, bar: {bar['vol']}")
334
- return 0
337
+ return (0, False, Datetime(last_datetime))
335
338
  continue
336
339
 
337
340
  if today_datetime >= bar_datetime > last_datetime \
@@ -378,7 +381,16 @@ def import_one_stock_data(connect, api, h5file, market, ktype, stock_record, sta
378
381
  table.remove()
379
382
 
380
383
  # table.close()
381
- return add_record_count
384
+ return (add_record_count, True, Datetime(last_datetime))
385
+
386
+
387
+ @hku_catch(ret=None)
388
+ def get_hdf5_importer(market, ktype):
389
+ filename = os.path.expanduser('~') + '/.hikyuu/hikyuu.ini'
390
+ config = ConfigParser()
391
+ config.read(filename, encoding='utf-8')
392
+ importer = KDataToHdf5Importer()
393
+ return importer if importer.set_config(config.get("hikyuu", "datadir"), [market], [ktype]) else None
382
394
 
383
395
 
384
396
  def import_data(connect, market, ktype, quotations, api, dest_dir, startDate=199012190000, progress=ProgressBar):
@@ -401,6 +413,9 @@ def import_data(connect, market, ktype, quotations, api, dest_dir, startDate=199
401
413
  if not stock_list:
402
414
  return 0
403
415
 
416
+ failed_limit = 20
417
+ failed_count = 0
418
+ failed_list = []
404
419
  total = len(stock_list)
405
420
  for i, stock in enumerate(stock_list):
406
421
  if stock[3] == 0 or len(stock[2]) != 6:
@@ -408,7 +423,12 @@ def import_data(connect, market, ktype, quotations, api, dest_dir, startDate=199
408
423
  progress(i, total)
409
424
  continue
410
425
 
411
- this_count = import_one_stock_data(connect, api, h5file, market, ktype, stock, startDate)
426
+ this_count, success, lastdate = import_one_stock_data(connect, api, h5file, market, ktype, stock, startDate)
427
+ if not success:
428
+ failed_count += 1
429
+ failed_list.append((market, stock[2], lastdate))
430
+ if failed_count >= failed_limit:
431
+ break
412
432
  add_record_count += this_count
413
433
  if this_count > 0:
414
434
  if ktype == 'DAY':
@@ -418,8 +438,31 @@ def import_data(connect, market, ktype, quotations, api, dest_dir, startDate=199
418
438
  if progress:
419
439
  progress(i, total)
420
440
 
441
+ if total > 0 and progress:
442
+ progress(total, total)
443
+
421
444
  connect.commit()
422
445
  h5file.close()
446
+
447
+ if 0 < failed_count < failed_limit and is_valid_license():
448
+ # 删除最后记录
449
+ ktype_dict = {
450
+ 'DAY': 'DAY',
451
+ '1MIN': 'MIN',
452
+ '5MIN': 'MIN5'
453
+ }
454
+ nktype = ktype_dict[ktype]
455
+ h5_importer = get_hdf5_importer(market, nktype)
456
+ if h5_importer is not None:
457
+ for r in failed_list:
458
+ hku_warn("清理 {}{} {}: {}", r[0], r[1], nktype, r[2].start_of_day())
459
+ h5_importer.remove(r[0], r[1], nktype, r[2].start_of_day())
460
+ hku_warn(f"已清理 {market} {failed_count} 个失败股票的最后记录,建议重新导入")
461
+
462
+ if failed_count >= failed_limit:
463
+ hku_error(f"{market} {ktype} 连续失败20个股票,已停止导入, 建议重新导入")
464
+ return add_record_count
465
+
423
466
  return add_record_count
424
467
 
425
468
 
@@ -479,12 +522,13 @@ def import_on_stock_trans(connect, api, h5file, market, stock_record, max_days):
479
522
  second += 3
480
523
  if second > 59:
481
524
  continue
482
- row['datetime'] = cur_date * 1000000 + minute * 100 + second
483
- row['price'] = int(record['price'] * 1000)
484
- row['vol'] = record['vol']
485
- row['buyorsell'] = record['buyorsell']
486
- row.append()
487
- add_record_count += 1
525
+ if record['price'] > 0.0 and record['vol'] >= 0.0:
526
+ row['datetime'] = cur_date * 1000000 + minute * 100 + second
527
+ row['price'] = int(record['price'] * 1000)
528
+ row['vol'] = record['vol']
529
+ row['buyorsell'] = record['buyorsell']
530
+ row.append()
531
+ add_record_count += 1
488
532
  except Exception as e:
489
533
  hku_error("Failed trans to record! {}", e)
490
534
 
@@ -578,12 +622,13 @@ def import_on_stock_time(connect, api, h5file, market, stock_record, max_days):
578
622
  elif time == 1360:
579
623
  time = 1400
580
624
  try:
581
- row['datetime'] = this_date + time
582
- row['price'] = int(record['price'] * 1000)
583
- row['vol'] = record['vol']
584
- row.append()
625
+ if record['price'] > 0.0 and record['vol'] >= 0.0:
626
+ row['datetime'] = this_date + time
627
+ row['price'] = int(record['price'] * 1000)
628
+ row['vol'] = record['vol']
629
+ row.append()
630
+ add_record_count += 1
585
631
  time += 1
586
- add_record_count += 1
587
632
  except Exception as e:
588
633
  hku_error("Failed trans record {}! {}".format(record, e))
589
634
 
@@ -624,7 +669,7 @@ if __name__ == '__main__':
624
669
  import time
625
670
  starttime = time.time()
626
671
 
627
- dest_dir = "d:\\stock"
672
+ dest_dir = "/Users/fasiondog/stock"
628
673
  tdx_server = '180.101.48.170'
629
674
  tdx_port = 7709
630
675
  quotations = ['stock', 'fund']
@@ -23,15 +23,17 @@
23
23
  # SOFTWARE.
24
24
 
25
25
  import sys
26
+ import os
26
27
  import math
27
28
  import datetime
28
29
  from pytdx.hq import TDXParams
30
+ from configparser import ConfigParser
29
31
 
30
32
  from hikyuu.util.mylog import hku_error, hku_debug
31
33
 
32
34
  import mysql.connector
33
35
 
34
- from hikyuu import Datetime
36
+ from hikyuu import Datetime, is_valid_license, KDataToMySQLImporter
35
37
  from .common import *
36
38
  from .common_pytdx import to_pytdx_market, pytdx_get_day_trans
37
39
  from .common_mysql import (
@@ -318,10 +320,10 @@ def import_one_stock_data(
318
320
  today.year * 10000 + today.month * 100 + today.day
319
321
  ) * 10000 + 1500
320
322
  else:
321
- return 0
323
+ return (0, True, Datetime(last_datetime))
322
324
 
323
325
  if today_datetime <= last_datetime:
324
- return 0
326
+ return (0, True, Datetime(last_datetime))
325
327
 
326
328
  get_bars = (
327
329
  api.get_index_bars if stktype == STOCKTYPE.INDEX else api.get_security_bars
@@ -356,27 +358,27 @@ def import_one_stock_data(
356
358
  if abs(last_krecord[1] - bar["open"]) / last_krecord[1] > 0.01:
357
359
  hku_error(
358
360
  f"fetch data from tdx error! {bar_datetime} {ktype} {market}{code} last_krecord open: {last_krecord[1]}, bar: {bar['open']}")
359
- return 0
361
+ return (0, False, Datetime(last_datetime))
360
362
  if abs(last_krecord[2] - bar["high"]) / last_krecord[2] > 0.01:
361
363
  hku_error(
362
364
  f"fetch data from tdx error! {bar_datetime} {ktype} {market}{code} last_krecord high: {last_krecord[2]}, bar: {bar['high']}")
363
- return 0
365
+ return (0, False, Datetime(last_datetime))
364
366
  if abs(last_krecord[3] - bar["low"]) / last_krecord[3] > 0.01:
365
367
  hku_error(
366
368
  f"fetch data from tdx error! {bar_datetime} {ktype} {market}{code} last_krecord low: {last_krecord[3]}, bar: {bar['low']}")
367
- return 0
369
+ return (0, False, Datetime(last_datetime))
368
370
  if abs(last_krecord[4] - bar["close"]) / last_krecord[4] > 0.01:
369
371
  hku_error(
370
372
  f"fetch data from tdx error! {bar_datetime} {ktype} {market}{code} last_krecord close: {last_krecord[4]}, bar: {bar['close']}")
371
- return 0
372
- if ktype == 'DAY' and last_krecord[5] != 0.0 and abs(last_krecord[5] - bar["amount"]*0.001) / last_krecord[5] > 0.1:
373
+ return (0, False, Datetime(last_datetime))
374
+ if ktype == 'DAY' and last_krecord[5] != 0.0 and abs(last_krecord[5] - bar["amount"]*0.001) > 10:
373
375
  hku_error(
374
376
  f"fetch data from tdx error! {bar_datetime} {ktype} {market}{code} last_krecord amount: {last_krecord[5]}, bar: {bar['amount']*0.001}")
375
- return 0
376
- if ktype == 'DAY' and last_krecord[6] != 0.0 and abs(last_krecord[6] - bar["vol"]) / last_krecord[6] > 0.1:
377
+ return (0, False, Datetime(last_datetime))
378
+ if ktype == 'DAY' and last_krecord[6] != 0.0 and abs(last_krecord[6] - bar["vol"]) > 10:
377
379
  hku_error(
378
380
  f"fetch data from tdx error! {bar_datetime} {ktype} {market}{code} last_krecord count: {last_krecord[6]}, bar: {bar['vol']}")
379
- return 0
381
+ return (0, False, Datetime(last_datetime))
380
382
  continue
381
383
 
382
384
  if (
@@ -440,7 +442,17 @@ def import_one_stock_data(
440
442
  connect.commit()
441
443
  cur.close()
442
444
 
443
- return len(buf)
445
+ return (len(buf), True, Datetime(last_datetime))
446
+
447
+
448
+ @hku_catch(ret=None)
449
+ def get_mysql_importer():
450
+ filename = os.path.expanduser('~') + '/.hikyuu/hikyuu.ini'
451
+ config = ConfigParser()
452
+ config.read(filename, encoding='utf-8')
453
+ importer = KDataToMySQLImporter()
454
+ return importer if importer.set_config(config.get("kdata", "host"), config.getint("kdata", "port", fallback=3306),
455
+ config.get("kdata", "usr"), config.get("kdata", "pwd")) else None
444
456
 
445
457
 
446
458
  @hku_catch(trace=True, re_raise=True)
@@ -470,6 +482,9 @@ def import_data(
470
482
 
471
483
  stock_list = get_stock_list(connect, market, quotations)
472
484
 
485
+ failed_limit = 20
486
+ failed_count = 0
487
+ failed_list = []
473
488
  total = len(stock_list)
474
489
  for i, stock in enumerate(stock_list):
475
490
  if stock[3] == 0 or len(stock[2]) != 6:
@@ -477,9 +492,14 @@ def import_data(
477
492
  progress(i, total)
478
493
  continue
479
494
 
480
- this_count = import_one_stock_data(
495
+ this_count, success, lastdate = import_one_stock_data(
481
496
  connect, api, market, ktype, stock, startDate
482
497
  )
498
+ if not success:
499
+ failed_count += 1
500
+ failed_list.append((market, stock[2], lastdate))
501
+ if failed_count >= failed_limit:
502
+ break
483
503
  add_record_count += this_count
484
504
  if this_count > 0:
485
505
  if ktype == "DAY":
@@ -491,6 +511,28 @@ def import_data(
491
511
  progress(i, total)
492
512
 
493
513
  connect.commit()
514
+
515
+ if total > 0 and progress:
516
+ progress(total, total)
517
+
518
+ if 0 < failed_count < failed_limit and is_valid_license():
519
+ # 删除最后记录
520
+ ktype_dict = {
521
+ 'DAY': 'DAY',
522
+ '1MIN': 'MIN',
523
+ '5MIN': 'MIN5'
524
+ }
525
+ nktype = ktype_dict[ktype]
526
+ h5_importer = get_mysql_importer(market, nktype)
527
+ if h5_importer is not None:
528
+ for r in failed_list:
529
+ hku_warn("remove {}{} {}: {}", r[0], r[1], nktype, r[2].start_of_day())
530
+ h5_importer.remove(r[0], r[1], nktype, r[2].start_of_day())
531
+ hku_warn(f"已清理 {market} {failed_count} 个失败股票的最后记录,建议重新导入")
532
+
533
+ if failed_count >= failed_limit:
534
+ hku_error(f"{market} {ktype} 连续失败20个股票,已停止导入, 建议重新导入")
535
+ return add_record_count
494
536
  return add_record_count
495
537
 
496
538
 
@@ -588,14 +630,15 @@ def import_on_stock_trans(connect, api, market, stock_record, max_days):
588
630
  if second > 59:
589
631
  continue
590
632
 
591
- trans_buf.append(
592
- (
593
- cur_date * 1000000 + minute * 100 + second,
594
- record["price"],
595
- record["vol"],
596
- record["buyorsell"],
633
+ if record['price'] > 0.0 and record['vol'] >= 0.0:
634
+ trans_buf.append(
635
+ (
636
+ cur_date * 1000000 + minute * 100 + second,
637
+ record["price"],
638
+ record["vol"],
639
+ record["buyorsell"],
640
+ )
597
641
  )
598
- )
599
642
  except Exception as e:
600
643
  hku_error("Failed trans to record! {}", e)
601
644
 
@@ -721,7 +764,8 @@ def import_on_stock_time(connect, api, market, stock_record, max_days):
721
764
  elif time == 1360:
722
765
  time = 1400
723
766
  try:
724
- time_buf.append((this_date + time, record['price'], record['vol']))
767
+ if record['price'] > 0.0 and record['vol'] >= 0.0:
768
+ time_buf.append((this_date + time, record['price'], record['vol']))
725
769
  time += 1
726
770
  except Exception as e:
727
771
  hku_error("Failed trans record {}! {}".format(record, e))
@@ -55,6 +55,8 @@ def pytdx_import_weight_to_clickhouse(pytdx_api, connect, market):
55
55
  db_last_date, last_total_count, last_free_count = (0, 0, 0)
56
56
 
57
57
  xdxr_list = pytdx_api.get_xdxr_info(pytdx_market, code)
58
+ if xdxr_list is None:
59
+ continue
58
60
  update_last_db_weight = False
59
61
  new_last_db_weight = last_db_weight
60
62
  records = {}
@@ -67,6 +67,8 @@ def pytdx_import_weight_to_mysql(pytdx_api, connect, market):
67
67
  cur.close()
68
68
 
69
69
  xdxr_list = pytdx_api.get_xdxr_info(pytdx_market, code)
70
+ if xdxr_list is None:
71
+ continue
70
72
  update_last_db_weight = False
71
73
  new_last_db_weight = last_db_weight
72
74
  records = {}
@@ -64,6 +64,8 @@ def pytdx_import_weight_to_sqlite(pytdx_api, connect, market):
64
64
  cur.close()
65
65
 
66
66
  xdxr_list = pytdx_api.get_xdxr_info(pytdx_market, code)
67
+ if xdxr_list is None:
68
+ continue
67
69
  update_last_db_weight = False
68
70
  new_last_db_weight = last_db_weight
69
71
  records = {}
@@ -0,0 +1,4 @@
1
+ BEGIN TRANSACTION;
2
+ update `stocktypeinfo` set `minTradeNumber`=100 where `type`=5 and `description`='ETF';
3
+ UPDATE `version` set `version` = 29;
4
+ COMMIT;