hikyuu 2.6.3__py3-none-win_amd64.whl → 2.6.6__py3-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.
Files changed (146) hide show
  1. hikyuu/__init__.py +6 -0
  2. hikyuu/__init__.pyi +548 -546
  3. hikyuu/analysis/__init__.pyi +519 -514
  4. hikyuu/analysis/analysis.pyi +520 -515
  5. hikyuu/core.pyi +521 -516
  6. hikyuu/cpp/__init__.pyi +2 -2
  7. hikyuu/cpp/boost_date_time-mt.dll +0 -0
  8. hikyuu/cpp/boost_serialization-mt.dll +0 -0
  9. hikyuu/cpp/boost_wserialization-mt.dll +0 -0
  10. hikyuu/cpp/core310.pyd +0 -0
  11. hikyuu/cpp/core310.pyi +167 -36
  12. hikyuu/cpp/core311.pyd +0 -0
  13. hikyuu/cpp/core311.pyi +167 -36
  14. hikyuu/cpp/core312.pyd +0 -0
  15. hikyuu/cpp/core312.pyi +167 -36
  16. hikyuu/cpp/core313.pyd +0 -0
  17. hikyuu/cpp/core313.pyi +167 -36
  18. hikyuu/cpp/core39.pyd +0 -0
  19. hikyuu/cpp/core39.pyi +167 -36
  20. hikyuu/cpp/hikyuu.dll +0 -0
  21. hikyuu/cpp/hikyuu.lib +0 -0
  22. hikyuu/cpp/i18n/__init__.py +0 -0
  23. hikyuu/cpp/i18n/zh_CN.mo +0 -0
  24. hikyuu/cpp/sqlite3.dll +0 -0
  25. hikyuu/data/clickhouse_upgrade/__init__.py +1 -0
  26. hikyuu/data/clickhouse_upgrade/createdb.sql +1085 -0
  27. hikyuu/data/common.py +1 -1
  28. hikyuu/data/common_clickhouse.py +512 -0
  29. hikyuu/data/common_mysql.py +19 -0
  30. hikyuu/data/common_pytdx.py +2 -0
  31. hikyuu/data/common_sqlite3.py +1 -0
  32. hikyuu/data/em_block_to_clickhouse.py +120 -0
  33. hikyuu/data/hku_config_template.py +70 -1
  34. hikyuu/data/mysql_upgrade/0028.sql +95 -0
  35. hikyuu/data/pytdx_finance_to_clickhouse.py +107 -0
  36. hikyuu/data/pytdx_to_clickhouse.py +841 -0
  37. hikyuu/data/pytdx_to_h5.py +53 -13
  38. hikyuu/data/pytdx_to_mysql.py +42 -9
  39. hikyuu/data/pytdx_to_taos.py +736 -0
  40. hikyuu/data/pytdx_weight_to_clickhouse.py +191 -0
  41. hikyuu/data/sqlite_upgrade/0028.sql +97 -0
  42. hikyuu/data/tdx_to_clickhouse.py +448 -0
  43. hikyuu/data/zh_bond10_to_clickhouse.py +49 -0
  44. hikyuu/draw/__init__.pyi +1 -1
  45. hikyuu/draw/drawplot/__init__.pyi +8 -8
  46. hikyuu/draw/drawplot/bokeh_draw.pyi +538 -536
  47. hikyuu/draw/drawplot/common.pyi +1 -1
  48. hikyuu/draw/drawplot/echarts_draw.pyi +540 -538
  49. hikyuu/draw/drawplot/matplotlib_draw.py +7 -7
  50. hikyuu/draw/drawplot/matplotlib_draw.pyi +550 -548
  51. hikyuu/draw/elder.pyi +11 -11
  52. hikyuu/draw/kaufman.pyi +18 -18
  53. hikyuu/draw/volume.pyi +10 -10
  54. hikyuu/examples/notebook/001-overview.ipynb +65 -100
  55. hikyuu/examples/notebook/004-IndicatorOverview.ipynb +34 -32
  56. hikyuu/examples/notebook/007-SystemDetails.ipynb +64 -50
  57. hikyuu/examples/notebook/010-Portfolio.ipynb +120 -124
  58. hikyuu/extend.py +1 -1
  59. hikyuu/extend.pyi +527 -527
  60. hikyuu/fetcher/stock/zh_block_em.py +349 -5
  61. hikyuu/fetcher/stock/zh_stock_a_pytdx.py +11 -21
  62. hikyuu/fetcher/stock/zh_stock_a_qmt.py +4 -5
  63. hikyuu/fetcher/stock/zh_stock_a_sina_qq.py +16 -60
  64. hikyuu/flat/Spot.py +96 -200
  65. hikyuu/gui/HikyuuTDX.py +175 -23
  66. hikyuu/gui/data/ImportBlockInfoTask.py +12 -1
  67. hikyuu/gui/data/ImportHistoryFinanceTask.py +62 -44
  68. hikyuu/gui/data/ImportPytdxTimeToH5Task.py +14 -2
  69. hikyuu/gui/data/ImportPytdxToH5Task.py +17 -3
  70. hikyuu/gui/data/ImportPytdxTransToH5Task.py +14 -2
  71. hikyuu/gui/data/ImportTdxToH5Task.py +13 -1
  72. hikyuu/gui/data/ImportWeightToSqliteTask.py +16 -2
  73. hikyuu/gui/data/ImportZhBond10Task.py +12 -1
  74. hikyuu/gui/data/MainWindow.py +191 -110
  75. hikyuu/gui/data/UsePytdxImportToH5Thread.py +52 -29
  76. hikyuu/gui/data/UseQmtImportToH5Thread.py +1 -0
  77. hikyuu/gui/data/UseTdxImportToH5Thread.py +21 -2
  78. hikyuu/gui/dataserver.py +12 -4
  79. hikyuu/gui/spot_server.py +30 -40
  80. hikyuu/gui/start_qmt.py +20 -3
  81. hikyuu/hub.pyi +6 -6
  82. hikyuu/include/hikyuu/DataType.h +11 -0
  83. hikyuu/include/hikyuu/MarketInfo.h +6 -0
  84. hikyuu/include/hikyuu/StockManager.h +8 -0
  85. hikyuu/include/hikyuu/data_driver/BaseInfoDriver.h +35 -0
  86. hikyuu/include/hikyuu/data_driver/kdata/mysql/KRecordTable.h +1 -0
  87. hikyuu/include/hikyuu/global/GlobalSpotAgent.h +1 -1
  88. hikyuu/include/hikyuu/global/SpotRecord.h +15 -31
  89. hikyuu/include/hikyuu/global/agent/spot_generated.h +48 -232
  90. hikyuu/include/hikyuu/global/schedule/scheduler.h +1 -1
  91. hikyuu/include/hikyuu/indicator/build_in.h +1 -0
  92. hikyuu/include/hikyuu/indicator/crt/BARSLASTCOUNT.h +33 -0
  93. hikyuu/include/hikyuu/indicator/imp/IBarsLastCount.h +27 -0
  94. hikyuu/include/hikyuu/plugin/KDataToHdf5Importer.h +3 -0
  95. hikyuu/include/hikyuu/plugin/backtest.h +2 -2
  96. hikyuu/include/hikyuu/plugin/dataserver.h +26 -1
  97. hikyuu/include/hikyuu/plugin/device.h +8 -4
  98. hikyuu/include/hikyuu/plugin/interface/BackTestPluginInterface.h +1 -1
  99. hikyuu/include/hikyuu/plugin/interface/DataDriverPluginInterface.h +27 -0
  100. hikyuu/include/hikyuu/plugin/interface/DataServerPluginInterface.h +2 -1
  101. hikyuu/include/hikyuu/plugin/interface/DevicePluginInterface.h +2 -1
  102. hikyuu/include/hikyuu/plugin/interface/ImportKDataToHdf5PluginInterface.h +3 -0
  103. hikyuu/include/hikyuu/plugin/interface/TMReportPluginInterface.h +80 -0
  104. hikyuu/include/hikyuu/plugin/interface/plugins.h +4 -0
  105. hikyuu/include/hikyuu/strategy/Strategy.h +0 -9
  106. hikyuu/include/hikyuu/trade_manage/Performance.h +17 -9
  107. hikyuu/include/hikyuu/trade_manage/PositionExtInfo.h +92 -0
  108. hikyuu/include/hikyuu/trade_manage/PositionRecord.h +7 -1
  109. hikyuu/include/hikyuu/trade_manage/TradeManagerBase.h +60 -1
  110. hikyuu/include/hikyuu/trade_sys/selector/crt/SE_Optimal.h +8 -0
  111. hikyuu/include/hikyuu/trade_sys/selector/imp/optimal/OptimalEvaluateSelector.h +28 -0
  112. hikyuu/include/hikyuu/trade_sys/selector/imp/optimal/OptimalSelectorBase.h +1 -0
  113. hikyuu/include/hikyuu/utilities/DllLoader.h +226 -0
  114. hikyuu/include/hikyuu/utilities/config.h +1 -1
  115. hikyuu/include/hikyuu/utilities/datetime/Datetime.h +20 -0
  116. hikyuu/include/hikyuu/utilities/datetime/TimeDelta.h +6 -0
  117. hikyuu/include/hikyuu/utilities/mo/mo.h +30 -14
  118. hikyuu/include/hikyuu/utilities/os.h +6 -0
  119. hikyuu/include/hikyuu/utilities/plugin/PluginLoader.h +10 -10
  120. hikyuu/include/hikyuu/utilities/thread/MQThreadPool.h +13 -7
  121. hikyuu/include/hikyuu/utilities/thread/ThreadPool.h +13 -6
  122. hikyuu/include/hikyuu/version.h +4 -4
  123. hikyuu/plugin/backtest.dll +0 -0
  124. hikyuu/plugin/clickhousedriver.dll +0 -0
  125. hikyuu/plugin/dataserver.dll +0 -0
  126. hikyuu/plugin/device.dll +0 -0
  127. hikyuu/plugin/extind.dll +0 -0
  128. hikyuu/plugin/import2hdf5.dll +0 -0
  129. hikyuu/plugin/tmreport.dll +0 -0
  130. hikyuu/trade_manage/__init__.pyi +537 -535
  131. hikyuu/trade_manage/broker.pyi +3 -3
  132. hikyuu/trade_manage/broker_easytrader.pyi +1 -1
  133. hikyuu/trade_manage/trade.pyi +537 -535
  134. hikyuu/util/__init__.py +1 -0
  135. hikyuu/util/__init__.pyi +4 -3
  136. hikyuu/util/check.py +8 -0
  137. hikyuu/util/check.pyi +5 -1
  138. hikyuu/util/singleton.pyi +1 -1
  139. {hikyuu-2.6.3.dist-info → hikyuu-2.6.6.dist-info}/METADATA +4 -3
  140. {hikyuu-2.6.3.dist-info → hikyuu-2.6.6.dist-info}/RECORD +144 -123
  141. {hikyuu-2.6.3.dist-info → hikyuu-2.6.6.dist-info}/top_level.txt +2 -2
  142. hikyuu/include/hikyuu/global/agent/hikyuu/__init__.py +0 -1
  143. hikyuu/include/hikyuu/global/agent/hikyuu/flat/__init__.py +0 -1
  144. {hikyuu-2.6.3.dist-info → hikyuu-2.6.6.dist-info}/LICENSE +0 -0
  145. {hikyuu-2.6.3.dist-info → hikyuu-2.6.6.dist-info}/WHEEL +0 -0
  146. {hikyuu-2.6.3.dist-info → hikyuu-2.6.6.dist-info}/entry_points.txt +0 -0
hikyuu/gui/HikyuuTDX.py CHANGED
@@ -22,6 +22,8 @@ import mysql.connector
22
22
  from mysql.connector import errorcode
23
23
  from mysql.connector.locales.eng import client_error # 此句仅为pyinstaller打包时能够自动引入
24
24
 
25
+ import clickhouse_connect
26
+
25
27
  from hikyuu.gui.data.MainWindow import *
26
28
  from hikyuu.gui.data.EscapetimeThread import EscapetimeThread
27
29
  from hikyuu.gui.data.UseTdxImportToH5Thread import UseTdxImportToH5Thread
@@ -34,7 +36,7 @@ from hikyuu.gui.data.CollectSpotThread import CollectSpotThread
34
36
  from hikyuu.gui.data.SchedImportThread import SchedImportThread
35
37
  from hikyuu.gui.spot_server import release_nng_senders
36
38
 
37
- from hikyuu import can_upgrade, get_last_version, fetch_trial_license, view_license
39
+ from hikyuu import can_upgrade, get_last_version, fetch_trial_license, view_license, is_valid_license
38
40
  from hikyuu.data import hku_config_template
39
41
  from hikyuu.util import *
40
42
 
@@ -93,6 +95,7 @@ class MyMainWindow(QMainWindow, Ui_MainWindow):
93
95
  def getHikyuuConfigFileName(self):
94
96
  return self.getUserConfigDir() + '/hikyuu.ini'
95
97
 
98
+ @hku_catch()
96
99
  def saveConfig(self):
97
100
  if not os.path.lexists(self.getUserConfigDir()):
98
101
  os.mkdir(self.getUserConfigDir())
@@ -105,7 +108,10 @@ class MyMainWindow(QMainWindow, Ui_MainWindow):
105
108
  if current_config.getboolean('hdf5', 'enable', fallback=True):
106
109
  data_dir = current_config['hdf5']['dir']
107
110
  if not os.path.lexists(data_dir + '/tmp'):
108
- os.mkdir(data_dir + '/tmp')
111
+ try:
112
+ os.mkdir(data_dir + '/tmp')
113
+ except:
114
+ pass
109
115
  # 此处不能使用 utf-8 参数,否则导致Windows下getBlock无法找到板块分类
110
116
  with open(filename, 'w', encoding='utf-8') as f:
111
117
  f.write(
@@ -140,8 +146,13 @@ class MyMainWindow(QMainWindow, Ui_MainWindow):
140
146
  )
141
147
  )
142
148
 
143
- else:
149
+ elif current_config.getboolean('mysql', 'enable', fallback=True):
144
150
  data_dir = current_config['mysql']['tmpdir']
151
+ if not os.path.lexists(data_dir + '/tmp'):
152
+ try:
153
+ os.mkdir(data_dir + '/tmp')
154
+ except:
155
+ pass
145
156
  with open(filename, 'w', encoding="utf-8") as f:
146
157
  f.write(
147
158
  hku_config_template.mysql_template.format(
@@ -178,16 +189,65 @@ class MyMainWindow(QMainWindow, Ui_MainWindow):
178
189
  hour2_max=current_config.getint('preload', 'hour2_max', fallback=4096),
179
190
  )
180
191
  )
192
+ elif current_config.getboolean('clickhouse', 'enable', fallback=True):
193
+ data_dir = current_config['clickhouse']['tmpdir']
194
+ if not os.path.lexists(data_dir + '/tmp'):
195
+ try:
196
+ os.mkdir(data_dir + '/tmp')
197
+ except:
198
+ pass
199
+ with open(filename, 'w', encoding="utf-8") as f:
200
+ f.write(
201
+ hku_config_template.clickhouse_template.format(
202
+ dir=data_dir,
203
+ quotation_server=current_config.get(
204
+ 'collect', 'quotation_server', fallback='ipc:///tmp/hikyuu_real.ipc'),
205
+ host=current_config['clickhouse']['host'],
206
+ port=current_config['clickhouse']['port'],
207
+ usr=current_config['clickhouse']['usr'],
208
+ pwd=current_config['clickhouse']['pwd'],
209
+ day=current_config.getboolean('preload', 'day', fallback=True),
210
+ week=current_config.getboolean('preload', 'week', fallback=False),
211
+ month=current_config.getboolean('preload', 'month', fallback=False),
212
+ quarter=current_config.getboolean('preload', 'quarter', fallback=False),
213
+ halfyear=current_config.getboolean('preload', 'halfyear', fallback=False),
214
+ year=current_config.getboolean('preload', 'year', fallback=False),
215
+ min1=current_config.getboolean('preload', 'min', fallback=False),
216
+ min5=current_config.getboolean('preload', 'min5', fallback=False),
217
+ min15=current_config.getboolean('preload', 'min15', fallback=False),
218
+ min30=current_config.getboolean('preload', 'min30', fallback=False),
219
+ min60=current_config.getboolean('preload', 'min60', fallback=False),
220
+ hour2=current_config.getboolean('preload', 'hour2', fallback=False),
221
+ day_max=current_config.getint('preload', 'day_max', fallback=100000),
222
+ week_max=current_config.getint('preload', 'week_max', fallback=100000),
223
+ month_max=current_config.getint('preload', 'month_max', fallback=100000),
224
+ quarter_max=current_config.getint('preload', 'quarter_max', fallback=100000),
225
+ halfyear_max=current_config.getint('preload', 'halfyear_max', fallback=100000),
226
+ year_max=current_config.getint('preload', 'year_max', fallback=100000),
227
+ min1_max=current_config.getint('preload', 'min_max', fallback=4096),
228
+ min5_max=current_config.getint('preload', 'min5_max', fallback=4096),
229
+ min15_max=current_config.getint('preload', 'min15_max', fallback=4096),
230
+ min30_max=current_config.getint('preload', 'min30_max', fallback=4096),
231
+ min60_max=current_config.getint('preload', 'min60_max', fallback=4096),
232
+ hour2_max=current_config.getint('preload', 'hour2_max', fallback=4096),
233
+ )
234
+ )
181
235
 
182
- if not os.path.lexists(data_dir):
183
- os.makedirs(data_dir)
236
+ try:
237
+ if not data_dir and not os.path.lexists(data_dir):
238
+ os.makedirs(data_dir)
239
+ except:
240
+ pass
184
241
 
185
- if not os.path.lexists(data_dir + '/block'):
186
- current_dir = os.path.dirname(os.path.abspath(__file__))
187
- dirname, _ = os.path.split(current_dir)
188
- dirname = os.path.join(dirname, 'config/block')
189
- shutil.copytree(dirname, data_dir + '/block')
190
- os.remove(data_dir + '/block/__init__.py')
242
+ try:
243
+ if not data_dir and not os.path.lexists(data_dir + '/block'):
244
+ current_dir = os.path.dirname(os.path.abspath(__file__))
245
+ dirname, _ = os.path.split(current_dir)
246
+ dirname = os.path.join(dirname, 'config/block')
247
+ shutil.copytree(dirname, data_dir + '/block')
248
+ os.remove(data_dir + '/block/__init__.py')
249
+ except:
250
+ pass
191
251
 
192
252
  @pyqtSlot()
193
253
  def on_save_pushButton_clicked(self):
@@ -267,6 +327,8 @@ class MyMainWindow(QMainWindow, Ui_MainWindow):
267
327
  sys.stderr = self._stream
268
328
  self.log_textEdit.document().setMaximumBlockCount(1000)
269
329
 
330
+ self.tabWidget.setCurrentIndex(0)
331
+
270
332
  current_dir = os.path.dirname(__file__)
271
333
  icon = QIcon(f"{current_dir}/images/hikyuu_small.png")
272
334
  star_img = QPixmap(f"{current_dir}/images/star.png")
@@ -309,6 +371,8 @@ class MyMainWindow(QMainWindow, Ui_MainWindow):
309
371
 
310
372
  # 初始化权息与财务数据设置
311
373
  self.import_weight_checkBox.setChecked(import_config.getboolean('weight', 'enable', fallback=True))
374
+ self.import_history_finance_checkBox.setChecked(import_config.getboolean('finance', 'enable', fallback=True))
375
+ self.import_block_checkBox.setChecked(import_config.getboolean('block', 'enable', fallback=True))
312
376
 
313
377
  # 初始化通道信目录配置
314
378
  tdx_enable = import_config.getboolean('tdx', 'enable', fallback=False)
@@ -350,7 +414,8 @@ class MyMainWindow(QMainWindow, Ui_MainWindow):
350
414
  if hdf5_enable:
351
415
  mysql_enable = False
352
416
  self.enable_mysql_radioButton.setChecked(mysql_enable)
353
- self.mysql_tmpdir_lineEdit.setText(import_config.get('mysql', 'tmpdir', fallback='c:/stock'))
417
+ self.mysql_tmpdir_lineEdit.setText(import_config.get('mysql', 'tmpdir', fallback='d:/stock'))
418
+ self.mysql_tmpdir_pushButton.setEnabled(mysql_enable)
354
419
  mysql_ip = import_config.get('mysql', 'host', fallback='127.0.0.1')
355
420
  self.mysql_ip_lineEdit.setText(mysql_ip)
356
421
  self.mysql_ip_lineEdit.setEnabled(mysql_enable)
@@ -365,6 +430,30 @@ class MyMainWindow(QMainWindow, Ui_MainWindow):
365
430
  self.mysql_pwd_lineEdit.setEnabled(mysql_enable)
366
431
  self.mysql_test_pushButton.setEnabled(mysql_enable)
367
432
 
433
+ # 初始化clickhouse设置
434
+ clickhouse_enable = import_config.getboolean('clickhouse', 'enable', fallback=False)
435
+ if (not is_valid_license()) or hdf5_enable or mysql_enable:
436
+ clickhouse_enable = False
437
+ self.enable_clickhouse_radioButton.setChecked(clickhouse_enable)
438
+ self.clickhouse_tmpdir_lineEdit.setText(import_config.get('clickhouse', 'tmpdir', fallback='d:/stock'))
439
+ self.clickhouse_tmpdir_pushButton.setEnabled(clickhouse_enable)
440
+ clickhouse_ip = import_config.get('clickhouse', 'host', fallback='127.0.0.1')
441
+ self.clickhouse_ip_lineEdit.setText(clickhouse_ip)
442
+ self.clickhouse_ip_lineEdit.setEnabled(clickhouse_enable)
443
+ clickhouse_port = import_config.get('clickhouse', 'port', fallback='9000')
444
+ self.clickhouse_port_lineEdit.setText(clickhouse_port)
445
+ self.clickhouse_port_lineEdit.setEnabled(clickhouse_enable)
446
+ clickhouse_http_port = import_config.get('clickhouse', 'http_port', fallback='8123')
447
+ self.clickhouse_http_port_lineEdit.setText(clickhouse_http_port)
448
+ self.clickhouse_http_port_lineEdit.setEnabled(clickhouse_enable)
449
+ clickhouse_usr = import_config.get('clickhouse', 'usr', fallback='default')
450
+ self.clickhouse_usr_lineEdit.setText(clickhouse_usr)
451
+ self.clickhouse_usr_lineEdit.setEnabled(clickhouse_enable)
452
+ clickhouse_pwd = import_config.get('clickhouse', 'pwd', fallback='test')
453
+ self.clickhouse_pwd_lineEdit.setText(clickhouse_pwd)
454
+ self.clickhouse_pwd_lineEdit.setEnabled(clickhouse_enable)
455
+ self.clickhouse_test_pushButton.setEnabled(clickhouse_enable)
456
+
368
457
  self.sched_import_timeEdit.setTime(
369
458
  datetime.time.fromisoformat(import_config.get('schec', 'time', fallback='18:00'))
370
459
  )
@@ -442,6 +531,12 @@ class MyMainWindow(QMainWindow, Ui_MainWindow):
442
531
  import_config['weight'] = {
443
532
  'enable': self.import_weight_checkBox.isChecked(),
444
533
  }
534
+ import_config['finance'] = {
535
+ 'enable': self.import_history_finance_checkBox.isChecked(),
536
+ }
537
+ import_config['block'] = {
538
+ 'enable': self.import_block_checkBox.isChecked(),
539
+ }
445
540
  import_config['tdx'] = {'enable': self.tdx_radioButton.isChecked(), 'dir': self.tdx_dir_lineEdit.text()}
446
541
  import_config['pytdx'] = {
447
542
  'enable': self.pytdx_radioButton.isChecked(),
@@ -460,6 +555,15 @@ class MyMainWindow(QMainWindow, Ui_MainWindow):
460
555
  'usr': self.mysql_usr_lineEdit.text(),
461
556
  'pwd': self.mysql_pwd_lineEdit.text()
462
557
  }
558
+ import_config['clickhouse'] = {
559
+ 'enable': is_valid_license() and self.enable_clickhouse_radioButton.isChecked(),
560
+ 'tmpdir': self.clickhouse_tmpdir_lineEdit.text(),
561
+ 'host': self.clickhouse_ip_lineEdit.text(),
562
+ 'http_port': self.clickhouse_http_port_lineEdit.text(),
563
+ 'port': self.clickhouse_port_lineEdit.text(),
564
+ 'usr': self.clickhouse_usr_lineEdit.text(),
565
+ 'pwd': self.clickhouse_pwd_lineEdit.text()
566
+ }
463
567
  import_config['sched'] = {
464
568
  'time': self.sched_import_timeEdit.time().toString(),
465
569
  }
@@ -597,23 +701,38 @@ class MyMainWindow(QMainWindow, Ui_MainWindow):
597
701
  def on_enable_hdf55_radioButton_clicked(self):
598
702
  if self.enable_hdf55_radioButton.isChecked():
599
703
  self.enable_mysql_radioButton.setChecked(False)
600
- self.on_enable_hdf5_or_mysql_toggled()
704
+ self.enable_clickhouse_radioButton.setChecked(False)
705
+ self.on_enable_database_toggled(hdf5=True, mysql=False, clickhouse=False)
601
706
 
602
707
  @pyqtSlot()
603
708
  def on_enable_mysql_radioButton_clicked(self):
604
709
  if self.enable_mysql_radioButton.isChecked():
605
710
  self.enable_hdf55_radioButton.setChecked(False)
606
- self.on_enable_hdf5_or_mysql_toggled()
711
+ self.enable_clickhouse_radioButton.setChecked(False)
712
+ self.on_enable_database_toggled(hdf5=False, mysql=True, clickhouse=False)
607
713
 
608
- def on_enable_hdf5_or_mysql_toggled(self):
609
- hdf5_enable = self.enable_hdf55_radioButton.isChecked()
610
- mysql_enable = not hdf5_enable
611
- self.hdf5_dir_lineEdit.setEnabled(hdf5_enable)
612
- self.mysql_ip_lineEdit.setEnabled(mysql_enable)
613
- self.mysql_port_lineEdit.setEnabled(mysql_enable)
614
- self.mysql_usr_lineEdit.setEnabled(mysql_enable)
615
- self.mysql_pwd_lineEdit.setEnabled(mysql_enable)
616
- self.mysql_test_pushButton.setEnabled(mysql_enable)
714
+ @pyqtSlot()
715
+ def on_enable_clickhouse_radioButton_clicked(self):
716
+ if self.enable_clickhouse_radioButton.isChecked():
717
+ self.enable_hdf55_radioButton.setChecked(False)
718
+ self.enable_mysql_radioButton.setChecked(False)
719
+ self.on_enable_database_toggled(hdf5=False, mysql=False, clickhouse=True)
720
+
721
+ def on_enable_database_toggled(self, hdf5, mysql, clickhouse):
722
+ self.hdf5_dir_lineEdit.setEnabled(hdf5)
723
+ self.mysql_ip_lineEdit.setEnabled(mysql)
724
+ self.mysql_port_lineEdit.setEnabled(mysql)
725
+ self.mysql_usr_lineEdit.setEnabled(mysql)
726
+ self.mysql_pwd_lineEdit.setEnabled(mysql)
727
+ self.mysql_test_pushButton.setEnabled(mysql)
728
+ self.mysql_tmpdir_pushButton.setEnabled(mysql)
729
+ self.clickhouse_ip_lineEdit.setEnabled(clickhouse)
730
+ self.clickhouse_http_port_lineEdit.setEnabled(clickhouse)
731
+ self.clickhouse_port_lineEdit.setEnabled(clickhouse)
732
+ self.clickhouse_usr_lineEdit.setEnabled(clickhouse)
733
+ self.clickhouse_pwd_lineEdit.setEnabled(clickhouse)
734
+ self.clickhouse_test_pushButton.setEnabled(clickhouse)
735
+ self.clickhouse_tmpdir_pushButton.setEnabled(clickhouse)
617
736
 
618
737
  @pyqtSlot()
619
738
  def on_mysql_tmpdir_pushButton_clicked(self):
@@ -625,6 +744,20 @@ class MyMainWindow(QMainWindow, Ui_MainWindow):
625
744
  dirname = dlg.selectedFiles()
626
745
  self.mysql_tmpdir_lineEdit.setText(dirname[0])
627
746
 
747
+ @pyqtSlot()
748
+ def on_clickhouse_tmpdir_pushButton_clicked(self):
749
+ if not is_valid_license():
750
+ QMessageBox.critical(self, "clickhouse引擎", "需要捐赠授权才能使用clickhouse引擎")
751
+ return
752
+
753
+ dlg = QFileDialog()
754
+ dlg.setFileMode(QFileDialog.Directory)
755
+ config = self.getCurrentConfig()
756
+ dlg.setDirectory(config['clickhouse']['tmpdir'])
757
+ if dlg.exec_():
758
+ dirname = dlg.selectedFiles()
759
+ self.clickhouse_tmpdir_lineEdit.setText(dirname[0])
760
+
628
761
  @pyqtSlot()
629
762
  def on_mysql_test_pushButton_clicked(self):
630
763
  """测试数据库连接"""
@@ -649,6 +782,25 @@ class MyMainWindow(QMainWindow, Ui_MainWindow):
649
782
 
650
783
  QMessageBox.about(self, "测试数据库连接", " 连接成功!")
651
784
 
785
+ @pyqtSlot()
786
+ def on_clickhouse_test_pushButton_clicked(self):
787
+ """测试数据库连接"""
788
+ db_config = {
789
+ 'username': self.clickhouse_usr_lineEdit.text(),
790
+ 'password': self.clickhouse_pwd_lineEdit.text(),
791
+ 'host': self.clickhouse_ip_lineEdit.text(),
792
+ 'port': self.clickhouse_http_port_lineEdit.text()
793
+ }
794
+
795
+ try:
796
+ cnx = clickhouse_connect.get_client(**db_config)
797
+ cnx.close()
798
+ except Exception as err:
799
+ QMessageBox.critical(self, "测试数据库连接失败", str(err))
800
+ return
801
+
802
+ QMessageBox.about(self, "测试数据库连接", " 连接成功!")
803
+
652
804
  def reset_progress_bar(self):
653
805
  self.hdf5_weight_label.setText('')
654
806
  self.hdf5_day_progressBar.setValue(0)
@@ -9,6 +9,7 @@ import mysql.connector
9
9
  from hikyuu.data.common import MARKET, get_stk_code_name_list
10
10
  from hikyuu.data.em_block_to_mysql import em_import_block_to_mysql
11
11
  from hikyuu.data.em_block_to_sqlite import em_import_block_to_sqlite
12
+ from hikyuu.data.em_block_to_clickhouse import em_import_block_to_clickhouse
12
13
  from hikyuu.util import *
13
14
 
14
15
 
@@ -29,7 +30,7 @@ class ImportBlockInfoTask:
29
30
  sqlite_file = "{}/stock.db".format(self.config['hdf5']['dir'])
30
31
  connect = sqlite3.connect(sqlite_file, timeout=1800)
31
32
  import_block = em_import_block_to_sqlite
32
- else:
33
+ elif self.config.getboolean('mysql', 'enable', fallback=True):
33
34
  db_config = {
34
35
  'user': self.config['mysql']['usr'],
35
36
  'password': self.config['mysql']['pwd'],
@@ -38,6 +39,16 @@ class ImportBlockInfoTask:
38
39
  }
39
40
  connect = mysql.connector.connect(**db_config)
40
41
  import_block = em_import_block_to_mysql
42
+ elif self.config.getboolean('clickhouse', 'enable', fallback=True):
43
+ db_config = {
44
+ 'username': self.config['clickhouse']['usr'],
45
+ 'password': self.config['clickhouse']['pwd'],
46
+ 'host': self.config['clickhouse']['host'],
47
+ 'port': self.config['clickhouse']['http_port']
48
+ }
49
+ import clickhouse_connect
50
+ connect = clickhouse_connect.get_client(**db_config)
51
+ import_block = em_import_block_to_clickhouse
41
52
 
42
53
  count = 0
43
54
  try:
@@ -27,10 +27,11 @@ import shutil
27
27
  import hashlib
28
28
  import sqlite3
29
29
  import mysql.connector
30
+ import clickhouse_connect
30
31
  from pytdx.hq import TdxHq_API
31
32
  from hikyuu.data.pytdx_finance_to_mysql import history_finance_import_mysql
32
33
  from hikyuu.data.pytdx_finance_to_sqlite import history_finance_import_sqlite
33
- from hikyuu.data.common_pytdx import search_best_tdx
34
+ from hikyuu.data.pytdx_finance_to_clickhouse import history_finance_import_clickhouse
34
35
  from hikyuu.util import *
35
36
 
36
37
 
@@ -46,13 +47,13 @@ class ImportHistoryFinanceTask:
46
47
  self.total_count = 0
47
48
  self.status = "no run"
48
49
 
49
- def connect(self):
50
- self.api = TdxHq_API()
50
+ def connect_to_pytdx(self):
51
+ api = TdxHq_API()
51
52
  # 不是所有服务器都提供下载
52
- hku_check(self.api.connect('120.76.152.87', 7709), "failed connect pytdx!")
53
+ hku_check(api.connect('120.76.152.87', 7709), "failed connect pytdx!")
54
+ return api
53
55
 
54
56
  def get_list_info(self):
55
- result = []
56
57
  content = self.api.get_report_file_by_size('tdxfin/gpcw.txt')
57
58
  content = content.decode('utf-8')
58
59
  content = content.strip().split('\r\n')
@@ -62,72 +63,89 @@ class ImportHistoryFinanceTask:
62
63
 
63
64
  return [l2d(i.strip().split(',')) for i in content]
64
65
 
65
- def import_to_db(self, filename):
66
+ def connect_db(self):
66
67
  if self.config.getboolean('hdf5', 'enable', fallback=True):
67
68
  sqlite_file = "{}/stock.db".format(self.config['hdf5']['dir'])
68
- connect = sqlite3.connect(sqlite_file, timeout=1800)
69
- history_finance_import = history_finance_import_sqlite
70
- else:
69
+ self.db_connect = sqlite3.connect(sqlite_file, timeout=1800)
70
+ self.history_finance_import = history_finance_import_sqlite
71
+ self.engine = 'hdf5'
72
+ elif self.config.getboolean('mysql', 'enable', fallback=True):
71
73
  db_config = {
72
74
  'user': self.config['mysql']['usr'],
73
75
  'password': self.config['mysql']['pwd'],
74
76
  'host': self.config['mysql']['host'],
75
77
  'port': self.config['mysql']['port']
76
78
  }
77
- connect = mysql.connector.connect(**db_config)
78
- history_finance_import = history_finance_import_mysql
79
+ self.db_connect = mysql.connector.connect(**db_config)
80
+ self.history_finance_import = history_finance_import_mysql
81
+ self.engine = 'mysql'
82
+ elif self.config.getboolean('clickhouse', 'enable', fallback=True):
83
+ db_config = {
84
+ 'username': self.config['clickhouse']['usr'],
85
+ 'password': self.config['clickhouse']['pwd'],
86
+ 'host': self.config['clickhouse']['host'],
87
+ 'port': self.config['clickhouse']['http_port']
88
+ }
89
+ self.db_connect = clickhouse_connect.get_client(**db_config)
90
+ self.history_finance_import = history_finance_import_clickhouse
91
+ self.engine = 'clickhouse'
79
92
 
93
+ def import_to_db(self, filename):
80
94
  try:
81
- history_finance_import(connect, filename)
95
+ self.history_finance_import(self.db_connect, filename)
82
96
  except Exception as e:
83
97
  hku_error(str(e))
84
- finally:
85
- connect.commit()
86
- connect.close()
87
98
 
88
- def download_file(self, item):
99
+ def download_file(self, item, dest_dir):
89
100
  filename = item['filename']
90
- # filesize = item['filesize']
91
- # get_report_file_by_size 传入实际的 filesize,实际会出错
92
- data = self.api.get_report_file_by_size(f'tdxfin/{filename}', 0)
93
- hku_info(f"Download finance file: {filename}")
94
- dest_file_name = self.dest_dir + "/" + filename
95
- with open(dest_file_name, 'wb') as f:
96
- f.write(data)
97
- shutil.unpack_archive(dest_file_name, extract_dir=self.dest_dir)
98
- self.import_to_db(f'{self.dest_dir}/{filename[0:-4]}.dat')
99
- hku_info(f"Import finance file: {self.dest_dir}/{filename[0:-4]}.dat")
101
+ dest_file_name = dest_dir + "/" + filename
102
+ need_download = False
103
+ if not os.path.exists(dest_file_name):
104
+ need_download = True
105
+ else:
106
+ old_md5 = ''
107
+ with open(dest_file_name, 'rb') as f:
108
+ old_md5 = hashlib.md5(f.read()).hexdigest()
109
+ if old_md5 != item['hash']:
110
+ need_download = True
111
+
112
+ if need_download:
113
+ api = TdxHq_API()
114
+ # 不是所有服务器都提供下载
115
+ hku_check(api.connect('120.76.152.87', 7709), "failed connect pytdx!")
116
+ data = api.get_report_file_by_size(f'tdxfin/{filename}', 0)
117
+ hku_info(f"Download finance file: {filename}")
118
+ with open(dest_file_name, 'wb') as f:
119
+ f.write(data)
120
+ api.disconnect()
121
+
122
+ shutil.unpack_archive(dest_file_name, extract_dir=dest_dir)
123
+ self.import_to_db(f'{dest_dir}/{filename[0:-4]}.dat')
124
+ hku_info(f"Import finance file: {dest_dir}/{filename[0:-4]}.dat")
100
125
 
101
126
  @hku_catch(trace=True)
102
127
  def __call__(self):
103
128
  self.status = "running"
104
129
  capture_multiprocess_all_logger(self.log_queue)
105
- self.connect()
130
+ self.api = self.connect_to_pytdx()
106
131
  data_list = self.get_list_info()
107
132
  self.total_count = len(data_list)
108
133
  count = 0
134
+ data_list.sort(key=lambda x: x['filename'])
135
+
136
+ self.connect_db()
109
137
  for item in data_list:
110
138
  try:
111
- dest_file = '{}/{}'.format(self.dest_dir, item['filename'])
112
- if not os.path.exists(dest_file):
113
- self.download_file(item)
114
- else:
115
- old_md5 = ''
116
- with open(dest_file, 'rb') as f:
117
- old_md5 = hashlib.md5(f.read()).hexdigest()
118
- if old_md5 != item['hash']:
119
- self.download_file(item)
120
- else:
121
- # 不管是否有变化,都导入一次,以便切换引擎时可以导入
122
- shutil.unpack_archive(dest_file, extract_dir=self.dest_dir)
123
- filename = item['filename']
124
- filename = f'{self.dest_dir}/{filename[0:-4]}.dat'
125
- self.import_to_db(filename)
126
- hku_info(f"Import finance file: {filename}")
139
+ self.download_file(item, self.dest_dir)
127
140
  count += 1
128
141
  self.queue.put([self.task_name, None, None, int(100 * count / self.total_count), self.total_count])
129
142
  except Exception as e:
130
143
  hku_error(str(e))
144
+ if self.engine == 'clickhouse':
145
+ hku_run_ignore_exception(self.db_connect.command, "OPTIMIZE TABLE hku_base.historyfinance FINAL")
146
+ self.db_connect.close()
147
+ self.api.disconnect()
148
+
131
149
  self.queue.put([self.task_name, None, None, None, self.total_count])
132
150
  self.status = "finished"
133
151
 
@@ -138,6 +156,6 @@ if __name__ == "__main__":
138
156
  this_dir = os.path.expanduser('~') + '/.hikyuu'
139
157
  import_config = ConfigParser()
140
158
  import_config.read(this_dir + '/importdata-gui.ini', encoding='utf-8')
141
- task = ImportHistoryFinanceTask(None, None, None, "c:\\stock")
159
+ task = ImportHistoryFinanceTask(None, None, import_config, "/Users/fasiondog/stock")
142
160
  task()
143
161
  print("over!")
@@ -25,9 +25,11 @@
25
25
  import logging
26
26
  import sqlite3
27
27
  import mysql.connector
28
+ import clickhouse_connect
28
29
  from pytdx.hq import TdxHq_API
29
30
  from hikyuu.data.pytdx_to_h5 import import_time as h5_import_time
30
31
  from hikyuu.data.pytdx_to_mysql import import_time as mysql_import_time
32
+ from hikyuu.data.pytdx_to_clickhouse import import_time as clickhouse_import_time
31
33
  from hikyuu.util import *
32
34
 
33
35
 
@@ -64,7 +66,7 @@ class ImportPytdxTimeToH5:
64
66
  sqlite_file = "{}/stock.db".format(self.config['hdf5']['dir'])
65
67
  connect = sqlite3.connect(sqlite_file, timeout=1800)
66
68
  import_time = h5_import_time
67
- else:
69
+ elif self.config.getboolean('mysql', 'enable', fallback=True):
68
70
  db_config = {
69
71
  'user': self.config['mysql']['usr'],
70
72
  'password': self.config['mysql']['pwd'],
@@ -73,6 +75,15 @@ class ImportPytdxTimeToH5:
73
75
  }
74
76
  connect = mysql.connector.connect(**db_config)
75
77
  import_time = mysql_import_time
78
+ elif self.config.getboolean('clickhouse', 'enable', fallback=True):
79
+ db_config = {
80
+ 'username': self.config['clickhouse']['usr'],
81
+ 'password': self.config['clickhouse']['pwd'],
82
+ 'host': self.config['clickhouse']['host'],
83
+ 'port': self.config['clickhouse']['http_port']
84
+ }
85
+ connect = clickhouse_connect.get_client(**db_config)
86
+ import_time = clickhouse_import_time
76
87
 
77
88
  count = 0
78
89
  try:
@@ -84,10 +95,11 @@ class ImportPytdxTimeToH5:
84
95
  connect, self.market, self.quotations, api, self.dest_dir, max_days=self.max_days, progress=progress
85
96
  )
86
97
  self.logger.info("导入 {} 分时记录数: {}".format(self.market, count))
98
+ api.disconnect()
87
99
  except Exception as e:
88
100
  self.logger.error(e)
89
101
  finally:
90
- connect.commit()
102
+ api.close()
91
103
  connect.close()
92
104
 
93
105
  self.queue.put([self.task_name, self.market, 'TIME', None, count])
@@ -28,6 +28,7 @@ import mysql.connector
28
28
  from pytdx.hq import TdxHq_API
29
29
  from hikyuu.data.pytdx_to_h5 import import_data as h5_import_data
30
30
  from hikyuu.data.pytdx_to_mysql import import_data as mysql_import_data
31
+ from hikyuu.data.pytdx_to_clickhouse import import_data as clickhouse_import_data
31
32
  from hikyuu.util import *
32
33
 
33
34
 
@@ -66,16 +67,28 @@ class ImportPytdxToH5:
66
67
  connect = sqlite3.connect(sqlite_file, timeout=1800)
67
68
  import_data = h5_import_data
68
69
  self.logger.debug('use hdf5 import kdata')
69
- else:
70
+ elif self.config.getboolean('mysql', 'enable', fallback=True):
70
71
  db_config = {
71
72
  'user': self.config['mysql']['usr'],
72
73
  'password': self.config['mysql']['pwd'],
73
74
  'host': self.config['mysql']['host'],
74
- 'port': self.config['mysql']['port']
75
+ 'port': int(self.config['mysql']['port'])
75
76
  }
76
77
  connect = mysql.connector.connect(**db_config)
77
78
  import_data = mysql_import_data
78
79
  self.logger.debug('use mysql import kdata')
80
+ elif self.config.getboolean('clickhouse', 'enable', fallback=True):
81
+ db_config = {
82
+ 'username': self.config['clickhouse']['usr'],
83
+ 'password': self.config['clickhouse']['pwd'],
84
+ 'host': self.config['clickhouse']['host'],
85
+ 'port': int(self.config['clickhouse']['http_port']),
86
+ 'send_receive_timeout': 60,
87
+ }
88
+ import clickhouse_connect
89
+ connect = clickhouse_connect.get_client(**db_config)
90
+ import_data = clickhouse_import_data
91
+ self.logger.debug('use clickhouse import kdata')
79
92
 
80
93
  count = 0
81
94
  try:
@@ -87,11 +100,12 @@ class ImportPytdxToH5:
87
100
  connect, self.market, self.ktype, self.quotations, api, self.dest_dir, self.startDatetime, progress
88
101
  )
89
102
  self.logger.info("导入 {} {} 记录数: {}".format(self.market, self.ktype, count))
103
+ api.disconnect()
90
104
  except Exception as e:
91
105
  self.logger.error("ImportPytdxToH5Task failed! {}".format(e))
92
106
  # self.queue.put([self.task_name, self.market, self.ktype, str(e), count])
93
107
  finally:
94
- connect.commit()
108
+ api.close()
95
109
  connect.close()
96
110
 
97
111
  self.queue.put([self.task_name, self.market, self.ktype, None, count])
@@ -28,6 +28,7 @@ import mysql.connector
28
28
  from pytdx.hq import TdxHq_API
29
29
  from hikyuu.data.pytdx_to_h5 import import_trans as h5_import_trans
30
30
  from hikyuu.data.pytdx_to_mysql import import_trans as mysql_import_trans
31
+ from hikyuu.data.pytdx_to_clickhouse import import_trans as clickhouse_import_trans
31
32
  from hikyuu.util import *
32
33
 
33
34
 
@@ -64,7 +65,7 @@ class ImportPytdxTransToH5:
64
65
  sqlite_file = "{}/stock.db".format(self.config['hdf5']['dir'])
65
66
  connect = sqlite3.connect(sqlite_file, timeout=1800)
66
67
  import_trans = h5_import_trans
67
- else:
68
+ elif self.config.getboolean('mysql', 'enable', fallback=True):
68
69
  db_config = {
69
70
  'user': self.config['mysql']['usr'],
70
71
  'password': self.config['mysql']['pwd'],
@@ -73,6 +74,16 @@ class ImportPytdxTransToH5:
73
74
  }
74
75
  connect = mysql.connector.connect(**db_config)
75
76
  import_trans = mysql_import_trans
77
+ elif self.config.getboolean('clickhouse', 'enable', fallback=True):
78
+ db_config = {
79
+ 'username': self.config['clickhouse']['usr'],
80
+ 'password': self.config['clickhouse']['pwd'],
81
+ 'host': self.config['clickhouse']['host'],
82
+ 'port': self.config['clickhouse']['http_port']
83
+ }
84
+ import clickhouse_connect
85
+ connect = clickhouse_connect.get_client(**db_config)
86
+ import_trans = clickhouse_import_trans
76
87
 
77
88
  count = 0
78
89
  try:
@@ -84,10 +95,11 @@ class ImportPytdxTransToH5:
84
95
  connect, self.market, self.quotations, api, self.dest_dir, max_days=self.max_days, progress=progress
85
96
  )
86
97
  self.logger.info("导入 {} 分笔记录数: {}".format(self.market, count))
98
+ api.disconnect()
87
99
  except Exception as e:
88
100
  self.logger.error(e)
89
101
  finally:
90
- connect.commit()
102
+ api.close()
91
103
  connect.close()
92
104
 
93
105
  self.queue.put([self.task_name, self.market, 'TRANS', None, count])