hikyuu 2.6.5__py3-none-win_amd64.whl → 2.6.7__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.
- hikyuu/__init__.py +16 -0
- hikyuu/__init__.pyi +484 -474
- hikyuu/analysis/__init__.pyi +466 -445
- hikyuu/analysis/analysis.pyi +467 -446
- hikyuu/core.pyi +468 -447
- 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/core310.pyd +0 -0
- hikyuu/cpp/core310.pyi +1070 -878
- hikyuu/cpp/core311.pyd +0 -0
- hikyuu/cpp/core311.pyi +1070 -878
- hikyuu/cpp/core312.pyd +0 -0
- hikyuu/cpp/core312.pyi +1070 -878
- hikyuu/cpp/core313.pyd +0 -0
- hikyuu/cpp/core313.pyi +1070 -876
- hikyuu/cpp/core39.pyd +0 -0
- hikyuu/cpp/core39.pyi +1070 -878
- hikyuu/cpp/hikyuu.dll +0 -0
- hikyuu/cpp/hikyuu.lib +0 -0
- hikyuu/cpp/i18n/__init__.py +0 -0
- hikyuu/cpp/i18n/zh_CN/__init__.py +0 -0
- hikyuu/cpp/i18n/zh_CN/hikyuu.mo +0 -0
- hikyuu/cpp/sqlite3.dll +0 -0
- hikyuu/data/clickhouse_upgrade/__init__.py +1 -0
- hikyuu/data/clickhouse_upgrade/createdb.sql +1085 -0
- hikyuu/data/common_clickhouse.py +465 -0
- hikyuu/data/em_block_to_clickhouse.py +120 -0
- hikyuu/data/hku_config_template.py +58 -3
- hikyuu/data/pytdx_finance_to_clickhouse.py +107 -0
- hikyuu/data/pytdx_to_clickhouse.py +841 -0
- hikyuu/data/pytdx_to_mysql.py +4 -4
- hikyuu/data/pytdx_weight_to_clickhouse.py +191 -0
- hikyuu/data/tdx_to_clickhouse.py +448 -0
- hikyuu/data/tdx_to_h5.py +1 -1
- hikyuu/data/zh_bond10_to_clickhouse.py +49 -0
- hikyuu/draw/__init__.pyi +1 -1
- hikyuu/draw/drawplot/bokeh_draw.pyi +479 -471
- hikyuu/draw/drawplot/echarts_draw.py +9 -8
- hikyuu/draw/drawplot/echarts_draw.pyi +479 -471
- hikyuu/draw/drawplot/matplotlib_draw.py +3 -3
- hikyuu/draw/drawplot/matplotlib_draw.pyi +479 -471
- hikyuu/draw/elder.pyi +6 -6
- hikyuu/draw/kaufman.py +1 -1
- hikyuu/draw/kaufman.pyi +10 -10
- hikyuu/draw/volume.pyi +5 -5
- hikyuu/examples/notebook/000-Index.ipynb +1 -1
- hikyuu/examples/notebook/001-overview.ipynb +78 -63
- hikyuu/examples/notebook/002-HowToGetStock.ipynb +259 -40
- hikyuu/examples/notebook/003-HowToGetKDataAndDraw.ipynb +49 -41
- hikyuu/examples/notebook/004-IndicatorOverview.ipynb +29 -29
- hikyuu/examples/notebook/005-Drawplot.ipynb +66 -37
- hikyuu/examples/notebook/006-TradeManager.ipynb +808 -61
- hikyuu/examples/notebook/007-SystemDetails.ipynb +23 -23
- hikyuu/examples/notebook/009-RealData.ipynb +3 -3
- hikyuu/examples/notebook/010-Portfolio.ipynb +761 -122
- hikyuu/extend.py +15 -100
- hikyuu/extend.pyi +478 -493
- hikyuu/fetcher/stock/zh_stock_a_pytdx.py +9 -20
- hikyuu/fetcher/stock/zh_stock_a_qmt.py +4 -5
- hikyuu/fetcher/stock/zh_stock_a_sina_qq.py +16 -60
- hikyuu/flat/Spot.py +96 -200
- hikyuu/gui/HikyuuTDX.py +134 -7
- hikyuu/gui/data/ImportBlockInfoTask.py +11 -0
- hikyuu/gui/data/ImportHistoryFinanceTask.py +15 -1
- hikyuu/gui/data/ImportPytdxTimeToH5Task.py +11 -1
- hikyuu/gui/data/ImportPytdxToH5Task.py +13 -1
- hikyuu/gui/data/ImportPytdxTransToH5Task.py +11 -1
- hikyuu/gui/data/ImportTdxToH5Task.py +13 -1
- hikyuu/gui/data/ImportWeightToSqliteTask.py +14 -1
- hikyuu/gui/data/ImportZhBond10Task.py +11 -0
- hikyuu/gui/data/MainWindow.py +210 -135
- hikyuu/gui/data/UsePytdxImportToH5Thread.py +45 -26
- hikyuu/gui/data/UseTdxImportToH5Thread.py +19 -1
- hikyuu/gui/dataserver.py +12 -4
- hikyuu/gui/spot_server.py +30 -40
- hikyuu/gui/start_qmt.py +20 -3
- hikyuu/hub.pyi +6 -6
- hikyuu/include/hikyuu/DataType.h +2 -0
- hikyuu/include/hikyuu/KQuery.h +22 -28
- hikyuu/include/hikyuu/MarketInfo.h +1 -1
- hikyuu/include/hikyuu/Stock.h +15 -3
- hikyuu/include/hikyuu/StockManager.h +12 -3
- hikyuu/include/hikyuu/StockTypeInfo.h +6 -0
- hikyuu/include/hikyuu/TransRecord.h +2 -8
- hikyuu/include/hikyuu/data_driver/kdata/mysql/KRecordTable.h +1 -0
- hikyuu/include/hikyuu/doc.h +4 -0
- hikyuu/include/hikyuu/global/GlobalSpotAgent.h +1 -1
- hikyuu/include/hikyuu/global/SpotRecord.h +15 -31
- hikyuu/include/hikyuu/global/agent/spot_generated.h +48 -232
- hikyuu/include/hikyuu/global/schedule/scheduler.h +1 -1
- hikyuu/include/hikyuu/indicator/Indicator.h +37 -0
- hikyuu/include/hikyuu/lang.h +27 -0
- hikyuu/include/hikyuu/plugin/KDataToHdf5Importer.h +9 -1
- hikyuu/include/hikyuu/plugin/dataserver.h +26 -1
- hikyuu/include/hikyuu/plugin/device.h +2 -1
- hikyuu/include/hikyuu/plugin/hkuextra.h +56 -0
- hikyuu/include/hikyuu/plugin/interface/DataDriverPluginInterface.h +27 -0
- hikyuu/include/hikyuu/plugin/interface/DataServerPluginInterface.h +2 -1
- hikyuu/include/hikyuu/plugin/interface/DevicePluginInterface.h +1 -1
- hikyuu/include/hikyuu/plugin/interface/HkuExtraPluginInterface.h +38 -0
- hikyuu/include/hikyuu/plugin/interface/ImportKDataToHdf5PluginInterface.h +13 -1
- hikyuu/include/hikyuu/plugin/interface/plugins.h +4 -0
- hikyuu/include/hikyuu/python/pybind_utils.h +9 -0
- hikyuu/include/hikyuu/strategy/Strategy.h +0 -9
- hikyuu/include/hikyuu/trade_manage/TradeRecord.h +1 -1
- hikyuu/include/hikyuu/utilities/config.h +0 -2
- hikyuu/include/hikyuu/utilities/os.h +9 -0
- hikyuu/include/hikyuu/utilities/plugin/PluginLoader.h +2 -1
- hikyuu/include/hikyuu/version.h +4 -4
- hikyuu/include/hikyuu/view/MarketView.h +59 -0
- hikyuu/indicator/__init__.py +0 -1
- hikyuu/indicator/indicator.py +14 -53
- hikyuu/plugin/backtest.dll +0 -0
- hikyuu/plugin/clickhousedriver.dll +0 -0
- hikyuu/plugin/dataserver.dll +0 -0
- hikyuu/plugin/device.dll +0 -0
- hikyuu/plugin/extind.dll +0 -0
- hikyuu/plugin/hkuextra.dll +0 -0
- hikyuu/plugin/import2hdf5.dll +0 -0
- hikyuu/plugin/tmreport.dll +0 -0
- hikyuu/test/test_init.py +59 -0
- hikyuu/test/test_real_tdx_import.py +336 -0
- hikyuu/test/test_tdx_import.py +315 -0
- hikyuu/test/test_tdx_real_data_import.py +281 -0
- hikyuu/trade_manage/__init__.pyi +479 -471
- hikyuu/trade_manage/trade.py +4 -65
- hikyuu/trade_manage/trade.pyi +479 -483
- hikyuu/trade_sys/__init__.py +11 -0
- hikyuu/util/__init__.py +1 -0
- hikyuu/util/__init__.pyi +4 -4
- hikyuu/util/check.py +8 -0
- hikyuu/util/check.pyi +5 -1
- hikyuu/util/singleton.pyi +1 -1
- {hikyuu-2.6.5.dist-info → hikyuu-2.6.7.dist-info}/METADATA +7 -5
- {hikyuu-2.6.5.dist-info → hikyuu-2.6.7.dist-info}/RECORD +142 -124
- {hikyuu-2.6.5.dist-info → hikyuu-2.6.7.dist-info}/top_level.txt +4 -3
- hikyuu/include/hikyuu/global/agent/hikyuu/flat/__init__.py +0 -1
- hikyuu/include/hikyuu/utilities/mo/__init__.py +0 -1
- hikyuu/include/hikyuu/utilities/mo/mo.h +0 -48
- hikyuu/indicator/talib_wrap.py +0 -1273
- /hikyuu/include/hikyuu/utilities/{mo/moFileReader.h → moFileReader.h} +0 -0
- /hikyuu/include/hikyuu/{global/agent/hikyuu → view}/__init__.py +0 -0
- {hikyuu-2.6.5.dist-info → hikyuu-2.6.7.dist-info}/LICENSE +0 -0
- {hikyuu-2.6.5.dist-info → hikyuu-2.6.7.dist-info}/WHEEL +0 -0
- {hikyuu-2.6.5.dist-info → hikyuu-2.6.7.dist-info}/entry_points.txt +0 -0
|
@@ -0,0 +1,465 @@
|
|
|
1
|
+
# coding:utf-8
|
|
2
|
+
#
|
|
3
|
+
# The MIT License (MIT)
|
|
4
|
+
#
|
|
5
|
+
# Copyright (c) 2010-2019 fasiondog/hikyuu
|
|
6
|
+
#
|
|
7
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
8
|
+
# of this software and associated documentation files (the "Software"), to deal
|
|
9
|
+
# in the Software without restriction, including without limitation the rights
|
|
10
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
11
|
+
# copies of the Software, and to permit persons to whom the Software is
|
|
12
|
+
# furnished to do so, subject to the following conditions:
|
|
13
|
+
#
|
|
14
|
+
# The above copyright notice and this permission notice shall be included in all
|
|
15
|
+
# copies or substantial portions of the Software.
|
|
16
|
+
#
|
|
17
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
18
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
19
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
20
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
21
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
22
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
23
|
+
# SOFTWARE.
|
|
24
|
+
|
|
25
|
+
import os
|
|
26
|
+
import datetime
|
|
27
|
+
from pathlib import Path
|
|
28
|
+
|
|
29
|
+
import clickhouse_connect
|
|
30
|
+
|
|
31
|
+
from hikyuu import Datetime, UTCOffset
|
|
32
|
+
from hikyuu.data.common import get_stktype_list, get_new_holidays
|
|
33
|
+
from hikyuu.util import hku_catch, hku_debug, hku_info
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def is_exist_db(connect):
|
|
37
|
+
"""数据库是否已存在"""
|
|
38
|
+
name = connect.command("SELECT name FROM system.databases WHERE name = 'hku_base'")
|
|
39
|
+
return name == 'hku_base'
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
@hku_catch(ret=0)
|
|
43
|
+
def get_db_version(connect):
|
|
44
|
+
version = connect.command("select `version` from `hku_base`.`version`")
|
|
45
|
+
return version if type(version) == int else 0
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def create_database(connect):
|
|
49
|
+
"""创建数据库"""
|
|
50
|
+
sql_dir = os.path.dirname(__file__) + '/clickhouse_upgrade'
|
|
51
|
+
if not is_exist_db(connect):
|
|
52
|
+
filename = sql_dir + "/createdb.sql"
|
|
53
|
+
with open(filename, 'r', encoding='utf8') as f:
|
|
54
|
+
sql_string = f.read()
|
|
55
|
+
statements = sql_string.split(';')
|
|
56
|
+
for stmt in statements:
|
|
57
|
+
stmt = stmt.strip()
|
|
58
|
+
if stmt: # 跳过空语句
|
|
59
|
+
try:
|
|
60
|
+
connect.command(stmt)
|
|
61
|
+
except Exception as e:
|
|
62
|
+
print(f"执行失败: {stmt[:30]}... 错误: {e}")
|
|
63
|
+
|
|
64
|
+
db_version = get_db_version(connect)
|
|
65
|
+
files = [x for x in Path(sql_dir).iterdir()
|
|
66
|
+
if x.is_file()
|
|
67
|
+
and x.name != 'createdb.sql'
|
|
68
|
+
and x.name != '__init__.py'
|
|
69
|
+
and int(x.stem) > db_version and not x.is_dir()]
|
|
70
|
+
files.sort()
|
|
71
|
+
for file in files:
|
|
72
|
+
print(file)
|
|
73
|
+
sql = file.read_text(encoding='utf8')
|
|
74
|
+
statements = sql.split(';')
|
|
75
|
+
for stmt in statements:
|
|
76
|
+
print(stmt)
|
|
77
|
+
stmt = stmt.strip()
|
|
78
|
+
if stmt: # 跳过空语句
|
|
79
|
+
try:
|
|
80
|
+
connect.command(stmt)
|
|
81
|
+
except Exception as e:
|
|
82
|
+
print(f"执行失败: {stmt[:30]}... 错误: {e}")
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def get_codepre_list(connect, market, quotations):
|
|
86
|
+
"""获取前缀代码表"""
|
|
87
|
+
stktype_list = get_stktype_list(quotations)
|
|
88
|
+
sql = "select codepre, type from `hku_base`.`coderuletype` " \
|
|
89
|
+
"where market='{market}' and type in {type_list} ORDER by length(codepre) DESC"\
|
|
90
|
+
.format(market=market, type_list=stktype_list)
|
|
91
|
+
ret = connect.query(sql)
|
|
92
|
+
return ret.result_rows
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def get_stock_list(connect, market, quotations):
|
|
96
|
+
stktype_list = get_stktype_list(quotations)
|
|
97
|
+
sql = "select market, code, valid, type from `hku_base`.`stock` where market='{}' and type in {}"\
|
|
98
|
+
.format(market, stktype_list)
|
|
99
|
+
a = connect.query(sql)
|
|
100
|
+
return a.result_rows
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def import_new_holidays(connect):
|
|
104
|
+
"""导入新的交易所休假日历"""
|
|
105
|
+
last_date = connect.command("select date from `hku_base`.`holiday` order by date desc limit 1")
|
|
106
|
+
last_date = last_date if type(last_date) == int else 19901219
|
|
107
|
+
holidays = get_new_holidays()
|
|
108
|
+
new_holidays = [(int(v),) for v in holidays if int(v) > last_date]
|
|
109
|
+
if new_holidays:
|
|
110
|
+
ic = connect.create_insert_context(table='holiday', database='hku_base',
|
|
111
|
+
data=new_holidays)
|
|
112
|
+
connect.insert(context=ic)
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def get_table(connect, market, code, ktype):
|
|
116
|
+
ktype_dict = {
|
|
117
|
+
'day': 'day',
|
|
118
|
+
'week': 'week',
|
|
119
|
+
'month': 'month',
|
|
120
|
+
'quarter': 'quarter',
|
|
121
|
+
'halfyear': 'halfyear',
|
|
122
|
+
'year': 'year',
|
|
123
|
+
'min': 'min',
|
|
124
|
+
'1min': 'min',
|
|
125
|
+
'5min': 'min5',
|
|
126
|
+
'15min': 'min15',
|
|
127
|
+
'30min': 'min30',
|
|
128
|
+
'60min': 'min60',
|
|
129
|
+
'hour2': 'hour2',
|
|
130
|
+
'min1': 'min',
|
|
131
|
+
'min5': 'min5',
|
|
132
|
+
'min15': 'min15',
|
|
133
|
+
'min30': 'min30',
|
|
134
|
+
'min60': 'min60',
|
|
135
|
+
'timeline': 'timeline',
|
|
136
|
+
'transdata': 'transdata',
|
|
137
|
+
}
|
|
138
|
+
nktype = ktype_dict[ktype.lower()]
|
|
139
|
+
if nktype == 'timeline':
|
|
140
|
+
return (f'hku_data.timeline', market.upper(), code.upper())
|
|
141
|
+
elif nktype == 'transdata':
|
|
142
|
+
return (f'hku_data.transdata', market.upper(), code.upper())
|
|
143
|
+
else:
|
|
144
|
+
return (f'hku_data.{nktype}_k', market.upper(), code.upper())
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
@hku_catch(ret=Datetime(1970, 1, 1))
|
|
148
|
+
def get_lastdatetime(connect, tablename):
|
|
149
|
+
tmp = connect.command("select max(date) from {} where market='{}' and code='{}'".format(
|
|
150
|
+
tablename[0], tablename[1], tablename[2]))
|
|
151
|
+
tmp = Datetime(tmp)
|
|
152
|
+
return None if tmp == Datetime(1970, 1, 1) else tmp + UTCOffset()
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
def get_last_krecord(connect, tablename):
|
|
156
|
+
"""获取最后一条K线记录
|
|
157
|
+
返回:(date, open, close, high, low, amount, volume)
|
|
158
|
+
"""
|
|
159
|
+
try:
|
|
160
|
+
a = connect.query(
|
|
161
|
+
"select toUInt32(date), open, high, low, close, amount, volume from {} where market='{}' and code='{}' order by date desc limit 1".format(tablename[0], tablename[1], tablename[2]), settings={'optimize_skip_merged_partitions': 1})
|
|
162
|
+
a = a.result_rows
|
|
163
|
+
# hku_info(f"{tablename} {a}")
|
|
164
|
+
if not a:
|
|
165
|
+
return None
|
|
166
|
+
return (Datetime.from_timestamp_utc(a[0][0]*1000000).ymdhm, a[0][1], a[0][2], a[0][3], a[0][4], a[0][5], a[0][6])
|
|
167
|
+
except:
|
|
168
|
+
return None
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
def getWeekDate(olddate):
|
|
172
|
+
y = olddate // 100000000
|
|
173
|
+
m = olddate // 1000000 - y * 100
|
|
174
|
+
d = olddate // 10000 - (y * 10000 + m * 100)
|
|
175
|
+
tempdate = datetime.date(y, m, d)
|
|
176
|
+
# python中周一是第0天,周五的第4天
|
|
177
|
+
startdate = tempdate + datetime.timedelta(0 - tempdate.weekday())
|
|
178
|
+
enddate = tempdate + datetime.timedelta(4 - tempdate.weekday())
|
|
179
|
+
return (
|
|
180
|
+
startdate.year * 100000000 + startdate.month * 1000000 + startdate.day * 10000,
|
|
181
|
+
enddate.year * 100000000 + enddate.month * 1000000 + enddate.day * 10000
|
|
182
|
+
)
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
def getMonthDate(olddate):
|
|
186
|
+
y = olddate // 100000000
|
|
187
|
+
m = olddate // 1000000 - y * 100
|
|
188
|
+
import calendar
|
|
189
|
+
_, d = calendar.monthrange(y, m)
|
|
190
|
+
return (y * 100000000 + m * 1000000 + 10000, y * 100000000 + m * 1000000 + d * 10000)
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
def getQuarterDate(olddate):
|
|
194
|
+
startDict = {1: 1, 2: 1, 3: 1, 4: 4, 5: 4, 6: 4, 7: 7, 8: 7, 9: 7, 10: 10, 11: 10, 12: 10}
|
|
195
|
+
endDict = {1: 3, 2: 3, 3: 3, 4: 6, 5: 6, 6: 6, 7: 9, 8: 9, 9: 9, 10: 12, 11: 12, 12: 12}
|
|
196
|
+
d_dict = {3: 310000, 6: 300000, 9: 300000, 12: 310000}
|
|
197
|
+
y = olddate // 100000000
|
|
198
|
+
m = olddate // 1000000 - y * 100
|
|
199
|
+
start_m = startDict[m]
|
|
200
|
+
end_m = endDict[m]
|
|
201
|
+
return (y * 100000000 + start_m * 1000000 + 10000, y * 100000000 + end_m * 1000000 + d_dict[end_m])
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
def getHalfyearDate(olddate):
|
|
205
|
+
y = olddate // 100000000
|
|
206
|
+
m = olddate // 1000000 - y * 100
|
|
207
|
+
return (y * 100000000 + (1010000 if m < 7 else 7010000), y * 100000000 + (6300000 if m < 7 else 12310000))
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
def getYearDate(olddate):
|
|
211
|
+
y = olddate // 100000000
|
|
212
|
+
return (y * 100000000 + 1010000, y * 100000000 + 12310000)
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
def getMin60Date(olddate):
|
|
216
|
+
mint = olddate - olddate // 10000 * 10000
|
|
217
|
+
newdate = olddate // 10000 * 10000
|
|
218
|
+
if mint <= 1030:
|
|
219
|
+
startdate = newdate + 931
|
|
220
|
+
enddate = newdate + 1030
|
|
221
|
+
elif mint <= 1130:
|
|
222
|
+
startdate = newdate + 1031
|
|
223
|
+
enddate = newdate + 1130
|
|
224
|
+
elif mint <= 1400:
|
|
225
|
+
startdate = newdate + 1301
|
|
226
|
+
enddate = newdate + 1400
|
|
227
|
+
else:
|
|
228
|
+
startdate = newdate + 1401
|
|
229
|
+
enddate = newdate + 1500
|
|
230
|
+
return (startdate, enddate)
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
def getHour2Date(olddate):
|
|
234
|
+
mint = olddate - olddate // 10000 * 10000
|
|
235
|
+
newdate = olddate // 10000 * 10000
|
|
236
|
+
if mint <= 1130:
|
|
237
|
+
startdate = newdate + 931
|
|
238
|
+
enddate = newdate + 1130
|
|
239
|
+
else:
|
|
240
|
+
startdate = newdate + 1301
|
|
241
|
+
enddate = newdate + 1500
|
|
242
|
+
return (startdate, enddate)
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
def getMin15Date(olddate):
|
|
246
|
+
mint = olddate - olddate // 10000 * 10000
|
|
247
|
+
newdate = olddate // 10000 * 10000
|
|
248
|
+
if mint <= 945:
|
|
249
|
+
startdate = newdate + 931
|
|
250
|
+
enddate = newdate + 945
|
|
251
|
+
elif mint <= 1000:
|
|
252
|
+
startdate = newdate + 946
|
|
253
|
+
enddate = newdate + 1000
|
|
254
|
+
elif mint <= 1015:
|
|
255
|
+
startdate = newdate + 1001
|
|
256
|
+
enddate = newdate + 1015
|
|
257
|
+
elif mint <= 1030:
|
|
258
|
+
startdate = newdate + 1016
|
|
259
|
+
enddate = newdate + 1030
|
|
260
|
+
elif mint <= 1045:
|
|
261
|
+
startdate = newdate + 1031
|
|
262
|
+
enddate = newdate + 1045
|
|
263
|
+
elif mint <= 1100:
|
|
264
|
+
startdate = newdate + 1046
|
|
265
|
+
enddate = newdate + 1100
|
|
266
|
+
elif mint <= 1115:
|
|
267
|
+
startdate = newdate + 1101
|
|
268
|
+
enddate = newdate + 1115
|
|
269
|
+
elif mint <= 1130:
|
|
270
|
+
startdate = newdate + 1116
|
|
271
|
+
enddate = newdate + 1130
|
|
272
|
+
elif mint <= 1315:
|
|
273
|
+
startdate = newdate + 1301
|
|
274
|
+
enddate = newdate + 1315
|
|
275
|
+
elif mint <= 1330:
|
|
276
|
+
startdate = newdate + 1316
|
|
277
|
+
enddate = newdate + 1330
|
|
278
|
+
elif mint <= 1345:
|
|
279
|
+
startdate = newdate + 1331
|
|
280
|
+
enddate = newdate + 1345
|
|
281
|
+
elif mint <= 1400:
|
|
282
|
+
startdate = newdate + 1346
|
|
283
|
+
enddate = newdate + 1400
|
|
284
|
+
elif mint <= 1415:
|
|
285
|
+
startdate = newdate + 1401
|
|
286
|
+
enddate = newdate + 1415
|
|
287
|
+
elif mint <= 1430:
|
|
288
|
+
startdate = newdate + 1416
|
|
289
|
+
enddate = newdate + 1430
|
|
290
|
+
elif mint <= 1445:
|
|
291
|
+
startdate = newdate + 1431
|
|
292
|
+
enddate = newdate + 1445
|
|
293
|
+
else:
|
|
294
|
+
startdate = newdate + 1446
|
|
295
|
+
enddate = newdate + 1500
|
|
296
|
+
return (startdate, enddate)
|
|
297
|
+
|
|
298
|
+
|
|
299
|
+
def getMin30Date(olddate):
|
|
300
|
+
mint = olddate - olddate // 10000 * 10000
|
|
301
|
+
newdate = olddate // 10000 * 10000
|
|
302
|
+
if mint <= 1000:
|
|
303
|
+
startdate = newdate + 931
|
|
304
|
+
enddate = newdate + 1000
|
|
305
|
+
elif mint <= 1030:
|
|
306
|
+
startdate = newdate + 1001
|
|
307
|
+
enddate = newdate + 1030
|
|
308
|
+
elif mint <= 1100:
|
|
309
|
+
startdate = newdate + 1031
|
|
310
|
+
enddate = newdate + 1100
|
|
311
|
+
elif mint <= 1130:
|
|
312
|
+
startdate = newdate + 1101
|
|
313
|
+
enddate = newdate + 1130
|
|
314
|
+
elif mint <= 1330:
|
|
315
|
+
startdate = newdate + 1301
|
|
316
|
+
enddate = newdate + 1330
|
|
317
|
+
elif mint <= 1400:
|
|
318
|
+
startdate = newdate + 1331
|
|
319
|
+
enddate = newdate + 1400
|
|
320
|
+
elif mint <= 1430:
|
|
321
|
+
startdate = newdate + 1401
|
|
322
|
+
enddate = newdate + 1430
|
|
323
|
+
else:
|
|
324
|
+
startdate = newdate + 1431
|
|
325
|
+
enddate = newdate + 1500
|
|
326
|
+
return (startdate, enddate)
|
|
327
|
+
|
|
328
|
+
|
|
329
|
+
def getNewDate(index_type, olddate):
|
|
330
|
+
if index_type == 'week':
|
|
331
|
+
return getWeekDate(olddate)
|
|
332
|
+
elif index_type == 'month':
|
|
333
|
+
return getMonthDate(olddate)
|
|
334
|
+
elif index_type == 'quarter':
|
|
335
|
+
return getQuarterDate(olddate)
|
|
336
|
+
elif index_type == 'halfyear':
|
|
337
|
+
return getHalfyearDate(olddate)
|
|
338
|
+
elif index_type == 'year':
|
|
339
|
+
return getYearDate(olddate)
|
|
340
|
+
elif index_type == 'min15':
|
|
341
|
+
return getMin15Date(olddate)
|
|
342
|
+
elif index_type == 'min30':
|
|
343
|
+
return getMin30Date(olddate)
|
|
344
|
+
elif index_type == 'min60':
|
|
345
|
+
return getMin60Date(olddate)
|
|
346
|
+
elif index_type == 'hour2':
|
|
347
|
+
return getHour2Date(olddate)
|
|
348
|
+
else:
|
|
349
|
+
return None
|
|
350
|
+
|
|
351
|
+
|
|
352
|
+
@hku_catch(ret={'week': [], 'month': [], 'quarter': [], 'halfyear': [], 'year': [], 'min15': [], 'min30': [], 'min60': [], 'hour2': []}, trace=True)
|
|
353
|
+
def update_extern_data(connect, market, code, data_type):
|
|
354
|
+
"""更新周线、月线、15分钟线等扩展数据索引"""
|
|
355
|
+
if data_type.lower() == 'day':
|
|
356
|
+
index_list = ('week', 'month', 'quarter', 'halfyear', 'year')
|
|
357
|
+
base_table = get_table(connect, market, code, 'day')
|
|
358
|
+
else:
|
|
359
|
+
index_list = ('min15', 'min30', 'min60', 'hour2')
|
|
360
|
+
base_table = get_table(connect, market, code, 'min5')
|
|
361
|
+
|
|
362
|
+
index_data = {}
|
|
363
|
+
for index_type in index_list:
|
|
364
|
+
index_data[index_type] = []
|
|
365
|
+
|
|
366
|
+
base_lastdate = get_lastdatetime(connect, base_table)
|
|
367
|
+
if base_lastdate is None:
|
|
368
|
+
return index_data
|
|
369
|
+
|
|
370
|
+
for index_type in index_list:
|
|
371
|
+
hku_debug("{}{} update {} index".format(market, code, index_type))
|
|
372
|
+
index_table = get_table(connect, market, code, index_type)
|
|
373
|
+
index_last_date = get_lastdatetime(connect, index_table)
|
|
374
|
+
|
|
375
|
+
# 获取当前日期大于等于索引表最大日期的基础表日期列表
|
|
376
|
+
if index_last_date is None:
|
|
377
|
+
sql = f"select toUInt32(date), open, high, low, close, amount, volume from {base_table[0]} where market='{base_table[1]}' and code='{base_table[2]}' order by date asc"
|
|
378
|
+
else:
|
|
379
|
+
index_last_date = index_last_date.ymdhm
|
|
380
|
+
start_date, _ = getNewDate(index_type, index_last_date)
|
|
381
|
+
start_date = Datetime(start_date).timestamp_utc() // 1000000
|
|
382
|
+
sql = f"select toUInt32(date), open, high, low, close, amount, volume from {base_table[0]} where market='{base_table[1]}' and code='{base_table[2]}' and date>={start_date} order by date asc"
|
|
383
|
+
a = connect.query(sql, settings={'optimize_skip_merged_partitions': 1})
|
|
384
|
+
base_list = a.result_rows
|
|
385
|
+
|
|
386
|
+
last_start_date = 199012010000
|
|
387
|
+
last_end_date = 199012010000
|
|
388
|
+
|
|
389
|
+
insert_buffer = index_data[index_type]
|
|
390
|
+
length_base_all = len(base_list)
|
|
391
|
+
for x in range(length_base_all):
|
|
392
|
+
current_date = Datetime.from_timestamp_utc(base_list[x][0] * 1000000).ymdhm
|
|
393
|
+
if current_date <= last_end_date:
|
|
394
|
+
continue
|
|
395
|
+
last_start_date, last_end_date = getNewDate(index_type, current_date)
|
|
396
|
+
|
|
397
|
+
base_record_list = []
|
|
398
|
+
start_ix = x
|
|
399
|
+
ix_date = current_date
|
|
400
|
+
while start_ix < length_base_all and \
|
|
401
|
+
ix_date >= last_start_date and ix_date <= last_end_date:
|
|
402
|
+
base_record_list.append(base_list[start_ix])
|
|
403
|
+
ix_date = Datetime.from_timestamp_utc(base_list[start_ix][0]*1000000).ymdhm
|
|
404
|
+
start_ix += 1
|
|
405
|
+
|
|
406
|
+
if not base_record_list:
|
|
407
|
+
continue
|
|
408
|
+
|
|
409
|
+
length = len(base_record_list)
|
|
410
|
+
open_price = base_record_list[0][1]
|
|
411
|
+
high_price = base_record_list[0][2]
|
|
412
|
+
low_price = base_record_list[0][3]
|
|
413
|
+
close_price = base_record_list[length - 1][4]
|
|
414
|
+
amount = base_record_list[0][5]
|
|
415
|
+
count = base_record_list[0][6]
|
|
416
|
+
for i in range(1, length):
|
|
417
|
+
if base_record_list[i][2] > high_price:
|
|
418
|
+
high_price = base_record_list[i][2]
|
|
419
|
+
if base_record_list[i][3] < low_price:
|
|
420
|
+
low_price = base_record_list[i][3]
|
|
421
|
+
amount += base_record_list[i][5]
|
|
422
|
+
count += base_record_list[i][6]
|
|
423
|
+
|
|
424
|
+
last_timestamp = Datetime(last_end_date).timestamp_utc()//1000000
|
|
425
|
+
if last_end_date != index_last_date:
|
|
426
|
+
insert_buffer.append((index_table[1], index_table[2], last_timestamp, open_price,
|
|
427
|
+
high_price, low_price, close_price, amount, count))
|
|
428
|
+
|
|
429
|
+
if len(insert_buffer) > 200000:
|
|
430
|
+
hku_info(f"写入 {market} {index_table[0]} 扩展数据: {len(insert_buffer)} ...")
|
|
431
|
+
ic = connect.create_insert_context(table=index_table[0],
|
|
432
|
+
data=insert_buffer)
|
|
433
|
+
connect.insert(context=ic, settings={"prefer_warmed_unmerged_parts_seconds": 86400})
|
|
434
|
+
insert_buffer.clear()
|
|
435
|
+
return index_data
|
|
436
|
+
|
|
437
|
+
|
|
438
|
+
if __name__ == '__main__':
|
|
439
|
+
from configparser import ConfigParser
|
|
440
|
+
dev_config = ConfigParser()
|
|
441
|
+
dev_config.read(os.path.expanduser("~") + '/workspace/dev.ini')
|
|
442
|
+
# dev_config.read('d:/workspace/dev.ini')
|
|
443
|
+
db = 'clickhouse54-http'
|
|
444
|
+
user = dev_config.get(db, 'user')
|
|
445
|
+
password = dev_config.get(db, 'pwd')
|
|
446
|
+
host = dev_config.get(db, 'host')
|
|
447
|
+
port = dev_config.getint(db, 'port')
|
|
448
|
+
|
|
449
|
+
import clickhouse_connect
|
|
450
|
+
client = clickhouse_connect.get_client(
|
|
451
|
+
host=host, username=user, password=password)
|
|
452
|
+
|
|
453
|
+
print(is_exist_db(client))
|
|
454
|
+
create_database(client)
|
|
455
|
+
print(is_exist_db(client))
|
|
456
|
+
|
|
457
|
+
# x = get_codepre_list(client, 'SH', ['stock', 'fund'])
|
|
458
|
+
# print(x)
|
|
459
|
+
|
|
460
|
+
# import_new_holidays(client)
|
|
461
|
+
x = get_last_krecord(client, ('hku_data.min_k', 'SH', '000001'))
|
|
462
|
+
print(x)
|
|
463
|
+
|
|
464
|
+
update_extern_data(client, 'BJ', '430017', 'DAY')
|
|
465
|
+
client.close()
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
#!/usr/bin/python
|
|
2
|
+
# -*- coding: utf8 -*-
|
|
3
|
+
#
|
|
4
|
+
# Create on: 20240102
|
|
5
|
+
# Author: fasiondog
|
|
6
|
+
|
|
7
|
+
from concurrent.futures import ThreadPoolExecutor
|
|
8
|
+
from hikyuu.data.common import MARKET, get_stk_code_name_list
|
|
9
|
+
from hikyuu.util import *
|
|
10
|
+
from hikyuu.fetcher.stock.zh_block_em import *
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def em_import_block_to_clickhouse(connect, code_market_dict, categorys=('行业板块', '指数板块')):
|
|
14
|
+
all_block_info = {}
|
|
15
|
+
|
|
16
|
+
with ThreadPoolExecutor(4) as executor:
|
|
17
|
+
if '行业板块' in categorys:
|
|
18
|
+
t1 = executor.submit(get_all_hybk_info, code_market_dict)
|
|
19
|
+
|
|
20
|
+
if '概念板块' in categorys:
|
|
21
|
+
t2 = executor.submit(get_all_gnbk_info, code_market_dict)
|
|
22
|
+
|
|
23
|
+
if '地域板块' in categorys:
|
|
24
|
+
t3 = executor.submit(get_all_dybk_info, code_market_dict)
|
|
25
|
+
|
|
26
|
+
if '指数板块' in categorys:
|
|
27
|
+
t4 = executor.submit(get_all_zsbk_info, code_market_dict)
|
|
28
|
+
|
|
29
|
+
success_fetch_hy = False
|
|
30
|
+
if '行业板块' in categorys:
|
|
31
|
+
x = t1.result()
|
|
32
|
+
hku_info("获取行业板块信息完毕")
|
|
33
|
+
if x:
|
|
34
|
+
all_block_info["行业板块"] = x
|
|
35
|
+
success_fetch_hy = True
|
|
36
|
+
|
|
37
|
+
success_fetch_gn = False
|
|
38
|
+
if '概念板块' in categorys:
|
|
39
|
+
x = t2.result()
|
|
40
|
+
hku_info("获取概念板块信息完毕")
|
|
41
|
+
if x:
|
|
42
|
+
all_block_info["概念板块"] = x
|
|
43
|
+
success_fetch_gn = True
|
|
44
|
+
|
|
45
|
+
success_fetch_dy = False
|
|
46
|
+
if '地域板块' in categorys:
|
|
47
|
+
x = t3.result()
|
|
48
|
+
hku_info("获取地域板块信息完毕")
|
|
49
|
+
if x:
|
|
50
|
+
all_block_info["地域板块"] = x
|
|
51
|
+
success_fetch_dy = True
|
|
52
|
+
|
|
53
|
+
success_fetch_zs = False
|
|
54
|
+
if '指数板块' in categorys:
|
|
55
|
+
x = t4.result()
|
|
56
|
+
hku_info("获取指数板块信息完毕")
|
|
57
|
+
if x:
|
|
58
|
+
all_block_info["指数板块"] = x
|
|
59
|
+
success_fetch_zs = True
|
|
60
|
+
|
|
61
|
+
blks = []
|
|
62
|
+
if success_fetch_hy:
|
|
63
|
+
blks.append('行业板块')
|
|
64
|
+
if success_fetch_gn:
|
|
65
|
+
blks.append('概念板块')
|
|
66
|
+
if success_fetch_dy:
|
|
67
|
+
blks.append('地域板块')
|
|
68
|
+
if success_fetch_zs:
|
|
69
|
+
blks.append('指数板块')
|
|
70
|
+
|
|
71
|
+
if not blks:
|
|
72
|
+
return
|
|
73
|
+
|
|
74
|
+
hku_info("更新数据库")
|
|
75
|
+
if len(blks) == 1:
|
|
76
|
+
sql = f"delete from hku_base.block where category in ('{blks[0]}')"
|
|
77
|
+
else:
|
|
78
|
+
sql = f"delete from hku_base.block where category in {tuple(blks)}"
|
|
79
|
+
connect.command(sql)
|
|
80
|
+
|
|
81
|
+
insert_records = []
|
|
82
|
+
|
|
83
|
+
for category in all_block_info:
|
|
84
|
+
for name in all_block_info[category]:
|
|
85
|
+
for code in all_block_info[category][name]:
|
|
86
|
+
insert_records.append((category, name, code))
|
|
87
|
+
|
|
88
|
+
if insert_records:
|
|
89
|
+
ic = connect.create_insert_context(table='block', database='hku_base',
|
|
90
|
+
column_names=['category', 'name', 'market_code'],
|
|
91
|
+
data=insert_records)
|
|
92
|
+
connect.insert(context=ic)
|
|
93
|
+
|
|
94
|
+
return len(insert_records)
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
if __name__ == "__main__":
|
|
98
|
+
import os
|
|
99
|
+
from configparser import ConfigParser
|
|
100
|
+
dev_config = ConfigParser()
|
|
101
|
+
dev_config.read(os.path.expanduser("~") + '/workspace/dev.ini')
|
|
102
|
+
db = 'clickhouse54-http'
|
|
103
|
+
user = dev_config.get(db, 'user')
|
|
104
|
+
password = dev_config.get(db, 'pwd')
|
|
105
|
+
host = dev_config.get(db, 'host')
|
|
106
|
+
port = dev_config.getint(db, 'port')
|
|
107
|
+
|
|
108
|
+
import clickhouse_connect
|
|
109
|
+
client = clickhouse_connect.get_client(
|
|
110
|
+
host=host, username=user, password=password)
|
|
111
|
+
|
|
112
|
+
x = get_stk_code_name_list(MARKET.SH)
|
|
113
|
+
code_market_dict = {}
|
|
114
|
+
for v in x:
|
|
115
|
+
code_market_dict[v["code"]] = MARKET.SH
|
|
116
|
+
# print(code_market_dict)
|
|
117
|
+
|
|
118
|
+
em_import_block_to_clickhouse(client, code_market_dict, categorys=('行业板块', ))
|
|
119
|
+
|
|
120
|
+
client.close()
|
|
@@ -145,6 +145,60 @@ pwd = {pwd}
|
|
|
145
145
|
|
|
146
146
|
"""
|
|
147
147
|
|
|
148
|
+
clickhouse_template = """
|
|
149
|
+
[hikyuu]
|
|
150
|
+
tmpdir = {dir}
|
|
151
|
+
datadir = {dir}
|
|
152
|
+
quotation_server = {quotation_server}
|
|
153
|
+
|
|
154
|
+
[block]
|
|
155
|
+
type = clickhouse
|
|
156
|
+
host = {host}
|
|
157
|
+
port = {port}
|
|
158
|
+
usr = {usr}
|
|
159
|
+
pwd = {pwd}
|
|
160
|
+
|
|
161
|
+
[preload]
|
|
162
|
+
day = {day}
|
|
163
|
+
week = {week}
|
|
164
|
+
month = {month}
|
|
165
|
+
quarter = {quarter}
|
|
166
|
+
halfyear = {halfyear}
|
|
167
|
+
year = {year}
|
|
168
|
+
min = {min1}
|
|
169
|
+
min5 = {min5}
|
|
170
|
+
min15 = {min15}
|
|
171
|
+
min30 = {min30}
|
|
172
|
+
min60 = {min60}
|
|
173
|
+
hour2 = {hour2}
|
|
174
|
+
day_max = {day_max}
|
|
175
|
+
week_max = {week_max}
|
|
176
|
+
month_max = {month_max}
|
|
177
|
+
quarter_max = {quarter_max}
|
|
178
|
+
halfyear_max = {halfyear_max}
|
|
179
|
+
year_max = {year_max}
|
|
180
|
+
min_max = {min1_max}
|
|
181
|
+
min5_max = {min5_max}
|
|
182
|
+
min15_max = {min15_max}
|
|
183
|
+
min30_max = {min30_max}
|
|
184
|
+
min60_max = {min60_max}
|
|
185
|
+
hour2_max = {hour2_max}
|
|
186
|
+
|
|
187
|
+
[baseinfo]
|
|
188
|
+
type = clickhouse
|
|
189
|
+
host = {host}
|
|
190
|
+
port = {port}
|
|
191
|
+
usr = {usr}
|
|
192
|
+
pwd = {pwd}
|
|
193
|
+
|
|
194
|
+
[kdata]
|
|
195
|
+
type = clickhouse
|
|
196
|
+
host = {host}
|
|
197
|
+
port = {port}
|
|
198
|
+
usr = {usr}
|
|
199
|
+
pwd = {pwd}
|
|
200
|
+
"""
|
|
201
|
+
|
|
148
202
|
|
|
149
203
|
import_config_template = """
|
|
150
204
|
[quotation]
|
|
@@ -187,17 +241,18 @@ dir = {dir}
|
|
|
187
241
|
|
|
188
242
|
[mysql]
|
|
189
243
|
enable = False
|
|
190
|
-
;tmpdir = D:/stock
|
|
244
|
+
;tmpdir = D:/stock/
|
|
191
245
|
;host = 127.0.0.1
|
|
192
246
|
;port = 3306
|
|
193
247
|
;usr = root
|
|
194
248
|
;pwd =
|
|
195
249
|
|
|
196
|
-
[
|
|
250
|
+
[clickhouse]
|
|
197
251
|
enable = False
|
|
198
252
|
;tmpdir = D:/stock
|
|
199
253
|
;host = 127.0.0.1
|
|
200
|
-
|
|
254
|
+
:http_port = 8123
|
|
255
|
+
;port = 9000
|
|
201
256
|
;usr = root
|
|
202
257
|
;pwd =
|
|
203
258
|
|