Skip to content

Commit db69c41

Browse files
committed
Add get_stock_tag_options api
1 parent 877e8da commit db69c41

File tree

8 files changed

+195
-29
lines changed

8 files changed

+195
-29
lines changed
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
GET http://127.0.0.1:8090/api/work/get_stock_tag_options?entity_id=stock_sh_600733
2+
accept: application/json
3+
4+

src/zvt/broker/qmt/data_manager.py

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
# -*- coding: utf-8 -*-
2+
import logging
3+
4+
import pandas as pd
5+
from xtquant import xtdata
6+
7+
from zvt import init_log
8+
9+
logger = logging.getLogger(__name__)
10+
11+
12+
def download_data():
13+
period = "1d"
14+
xtdata.download_sector_data()
15+
stock_codes = xtdata.get_stock_list_in_sector("沪深A股")
16+
stock_codes = sorted(stock_codes)
17+
count = len(stock_codes)
18+
19+
for index, stock_code in enumerate(stock_codes):
20+
logger.info(f"run to {index + 1}/{count}")
21+
22+
xtdata.download_history_data(stock_code, period=period)
23+
logger.info(f"download {stock_code} {period} kdata ok")
24+
records = xtdata.get_market_data(
25+
stock_list=[stock_code],
26+
period=period,
27+
count=5,
28+
dividend_type="front",
29+
fill_data=False,
30+
)
31+
dfs = []
32+
for col in records:
33+
df = records[col].T
34+
df.columns = [col]
35+
dfs.append(df)
36+
kdatas = pd.concat(dfs, axis=1)
37+
logger.info(kdatas)
38+
39+
start_time = kdatas.index.to_list()[0]
40+
xtdata.download_history_data(stock_code, period="tick", start_time=start_time)
41+
logger.info(f"download {stock_code} tick from {start_time} ok")
42+
# records = xtdata.get_market_data(
43+
# stock_list=[stock_code],
44+
# period="tick",
45+
# count=5,
46+
# fill_data=False,
47+
# )
48+
# logger.info(records[stock_code])
49+
50+
xtdata.download_financial_data2(
51+
stock_list=stock_codes, table_list=["Capital"], start_time="", end_time="", callback=lambda x: print(x)
52+
)
53+
logger.info("download capital data ok")
54+
55+
56+
if __name__ == "__main__":
57+
init_log("qmt_data_manager.log")
58+
download_data()

src/zvt/broker/qmt/qmt_quote.py

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,9 @@
99
from zvt.contract import IntervalLevel, AdjustType
1010
from zvt.contract.api import decode_entity_id, df_to_db, get_db_session
1111
from zvt.domain import StockQuote, Stock
12+
from zvt.domain.quotes.stock.stock_quote import Stock1mQuote
1213
from zvt.utils.pd_utils import pd_is_not_null
13-
from zvt.utils.time_utils import to_time_str, current_date, to_pd_timestamp, now_pd_timestamp
14+
from zvt.utils.time_utils import to_time_str, current_date, to_pd_timestamp, now_pd_timestamp, TIME_FORMAT_MINUTE
1415

1516
# https://dict.thinktrader.net/nativeApi/start_now.html?id=e2M5nZ
1617

@@ -197,6 +198,8 @@ def on_data(datas, stock_df=entity_df):
197198
lambda se: "{}_{}".format(se["entity_id"], to_time_str(se["timestamp"])), axis=1
198199
)
199200

201+
df["volume"] = df["pvolume"]
202+
df["avg_price"] = df["turnover"] / df["volume"]
200203
# 换手率
201204
df["turnover_rate"] = df["pvolume"] / df["float_volume"]
202205
# 涨跌幅
@@ -224,6 +227,11 @@ def on_data(datas, stock_df=entity_df):
224227
cost_time = time.time() - start_time
225228
logger.info(f"Quotes cost_time:{cost_time} for {len(datas.keys())} stocks")
226229

230+
df["id"] = df[["entity_id", "timestamp"]].apply(
231+
lambda se: "{}_{}".format(se["entity_id"], to_time_str(se["timestamp"], TIME_FORMAT_MINUTE)), axis=1
232+
)
233+
df_to_db(df, data_schema=Stock1mQuote, provider="qmt", force_update=True, drop_duplicates=False)
234+
227235
return on_data
228236

229237

@@ -256,13 +264,19 @@ def record_tick():
256264
if not client.is_connected():
257265
raise Exception("行情服务连接断开")
258266
current_timestamp = now_pd_timestamp()
259-
if current_timestamp.hour == 15 and current_timestamp.minute == 10:
267+
if current_timestamp.hour >= 15 and current_timestamp.minute >= 10:
260268
logger.info(f"record tick finished at: {current_timestamp}")
261269
break
262270

263271

264272
if __name__ == "__main__":
273+
from apscheduler.schedulers.background import BackgroundScheduler
274+
275+
sched = BackgroundScheduler()
265276
record_tick()
277+
sched.add_job(func=record_tick, trigger="cron", hour=9, minute=18, day_of_week="mon-fri")
278+
sched.start()
279+
sched._thread.join()
266280

267281

268282
# the __all__ is generated

src/zvt/domain/quotes/stock/stock_quote.py

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,29 @@ class StockQuote(StockQuoteBase, Mixin):
4141
total_cap = Column(Float)
4242

4343

44+
class Stock1mQuote(StockQuoteBase, Mixin):
45+
__tablename__ = "stock_1m_quote"
46+
code = Column(String(length=32))
47+
name = Column(String(length=32))
48+
49+
#: UNIX时间戳
50+
time = Column(Integer)
51+
#: 最新价
52+
price = Column(Float)
53+
#: 均价
54+
avg_price = Column(Float)
55+
# 涨跌幅
56+
change_pct = Column(Float)
57+
# 成交量
58+
volume = Column(Float)
59+
# 成交金额
60+
turnover = Column(Float)
61+
# 换手率
62+
turnover_rate = Column(Float)
63+
64+
4465
register_schema(providers=["qmt"], db_name="stock_quote", schema_base=StockQuoteBase, entity_type="stock")
4566

4667

4768
# the __all__ is generated
48-
__all__ = ["StockQuote"]
69+
__all__ = ["StockQuote", "Stock1mQuote"]

src/zvt/recorders/qmt/quotes/qmt_kdata_recorder.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -96,15 +96,15 @@ def record(self, entity, start, end, size, timestamps):
9696
self.logger.info(f"no kdata for {entity.id}")
9797

9898

99-
class EMStockKdataRecorder(BaseQmtKdataRecorder):
99+
class QMTStockKdataRecorder(BaseQmtKdataRecorder):
100100
entity_schema = Stock
101101
data_schema = StockKdataCommon
102102

103103

104104
if __name__ == "__main__":
105105
# Stock.record_data(provider="exchange")
106-
EMStockKdataRecorder(entity_id="stock_sz_000338", adjust_type=AdjustType.hfq).run()
106+
QMTStockKdataRecorder(entity_id="stock_sz_000338", adjust_type=AdjustType.hfq).run()
107107

108108

109109
# the __all__ is generated
110-
__all__ = ["BaseQmtKdataRecorder", "EMStockKdataRecorder"]
110+
__all__ = ["BaseQmtKdataRecorder", "QMTStockKdataRecorder"]

src/zvt/rest/work.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
ActivateSubTagsResultModel,
2525
ActivateSubTagsModel,
2626
BatchSetStockTagsModel,
27+
StockTagOptions,
2728
)
2829
from zvt.tag.tag_schemas import StockTags, MainTagInfo, SubTagInfo, HiddenTagInfo, StockPoolInfo, StockPools
2930
from zvt.utils.time_utils import current_date
@@ -152,6 +153,14 @@ def query_simple_stock_tags(query_simple_stock_tags_model: QuerySimpleStockTagsM
152153
return result_tags
153154

154155

156+
@work_router.get("/get_stock_tag_options", response_model=StockTagOptions)
157+
def get_stock_tag_options(entity_id: str):
158+
"""
159+
Get stock tag options
160+
"""
161+
return tag_service.get_stock_tag_options(entity_id=entity_id)
162+
163+
155164
@work_router.post("/set_stock_tags", response_model=StockTagsModel)
156165
def set_stock_tags(set_stock_tags_model: SetStockTagsModel):
157166
"""

src/zvt/tag/tag_models.py

Lines changed: 31 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
from zvt.contract.model import MixinModel, CustomModel
88
from zvt.tag.common import StockPoolType, TagType, TagStatsQueryType
9-
from zvt.tag.tag_utils import get_main_tags, get_sub_tags, get_hidden_tags, get_stock_pool_names
9+
from zvt.tag.tag_utils import get_stock_pool_names
1010

1111

1212
class TagInfoModel(MixinModel):
@@ -67,6 +67,13 @@ class TagParameter(CustomModel):
6767
sub_tag_reason: Optional[str] = None
6868

6969

70+
class StockTagOptions(CustomModel):
71+
main_tag: Optional[str] = None
72+
sub_tag: Optional[str] = None
73+
main_tag_options: List[CreateTagInfoModel]
74+
sub_tag_options: List[CreateTagInfoModel]
75+
76+
7077
class SetStockTagsModel(CustomModel):
7178
entity_id: str
7279
main_tag: str
@@ -75,28 +82,28 @@ class SetStockTagsModel(CustomModel):
7582
sub_tag_reason: Optional[str] = None
7683
active_hidden_tags: Optional[Dict[str, str]] = None
7784

78-
@field_validator("main_tag")
79-
@classmethod
80-
def main_tag_must_be_in(cls, v: str) -> str:
81-
if v not in get_main_tags():
82-
raise ValueError(f"main_tag: {v} must be created at main_tag_info at first")
83-
return v
84-
85-
@field_validator("sub_tag")
86-
@classmethod
87-
def sub_tag_must_be_in(cls, v: str) -> str:
88-
if v and (v not in get_sub_tags()):
89-
raise ValueError(f"sub_tag: {v} must be created at sub_tag_info at first")
90-
return v
91-
92-
@field_validator("active_hidden_tags")
93-
@classmethod
94-
def hidden_tag_must_be_in(cls, v: Union[Dict[str, str], None]) -> Union[Dict[str, str], None]:
95-
if v:
96-
for item in v.keys():
97-
if item not in get_hidden_tags():
98-
raise ValueError(f"hidden_tag: {v} must be created at hidden_tag_info at first")
99-
return v
85+
# @field_validator("main_tag")
86+
# @classmethod
87+
# def main_tag_must_be_in(cls, v: str) -> str:
88+
# if v not in get_main_tags():
89+
# raise ValueError(f"main_tag: {v} must be created at main_tag_info at first")
90+
# return v
91+
#
92+
# @field_validator("sub_tag")
93+
# @classmethod
94+
# def sub_tag_must_be_in(cls, v: str) -> str:
95+
# if v and (v not in get_sub_tags()):
96+
# raise ValueError(f"sub_tag: {v} must be created at sub_tag_info at first")
97+
# return v
98+
#
99+
# @field_validator("active_hidden_tags")
100+
# @classmethod
101+
# def hidden_tag_must_be_in(cls, v: Union[Dict[str, str], None]) -> Union[Dict[str, str], None]:
102+
# if v:
103+
# for item in v.keys():
104+
# if item not in get_hidden_tags():
105+
# raise ValueError(f"hidden_tag: {v} must be created at hidden_tag_info at first")
106+
# return v
100107

101108

102109
class StockPoolModel(MixinModel):
@@ -233,6 +240,7 @@ class ActivateSubTagsResultModel(CustomModel):
233240
"QuerySimpleStockTagsModel",
234241
"BatchSetStockTagsModel",
235242
"TagParameter",
243+
"StockTagOptions",
236244
"SetStockTagsModel",
237245
"StockPoolModel",
238246
"StockPoolInfoModel",

src/zvt/tag/tag_service.py

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
BatchSetStockTagsModel,
2020
TagParameter,
2121
CreateTagInfoModel,
22+
StockTagOptions,
2223
)
2324
from zvt.tag.tag_schemas import (
2425
StockTags,
@@ -53,10 +54,60 @@ def stock_tags_need_update(stock_tags: StockTags, set_stock_tags_model: SetStock
5354
return False
5455

5556

57+
def get_stock_tag_options(entity_id):
58+
with contract_api.DBSession(provider="zvt", data_schema=StockTags)() as session:
59+
datas: List[StockTags] = StockTags.query_data(
60+
entity_id=entity_id, order=StockTags.timestamp.desc(), limit=1, return_type="domain", session=session
61+
)
62+
main_tag_options = []
63+
sub_tag_options = []
64+
main_tag = None
65+
sub_tag = None
66+
stock_tags = None
67+
if datas:
68+
stock_tags = datas[0]
69+
main_tag = stock_tags.main_tag
70+
sub_tag = stock_tags.sub_tag
71+
main_tag_options = [
72+
CreateTagInfoModel(tag=tag, tag_reason=tag_reason) for tag, tag_reason in stock_tags.main_tags.items()
73+
]
74+
sub_tag_options = [
75+
CreateTagInfoModel(tag=tag, tag_reason=tag_reason) for tag, tag_reason in stock_tags.sub_tags.items()
76+
]
77+
78+
main_tags_info: List[MainTagInfo] = MainTagInfo.query_data(session=session, return_type="domain")
79+
main_tag_options = main_tag_options + [
80+
CreateTagInfoModel(tag=item.tag, tag_reason=item.tag_reason)
81+
for item in main_tags_info
82+
if not stock_tags or (item.tag not in stock_tags.main_tags)
83+
]
84+
85+
sub_tags_info: List[SubTagInfo] = SubTagInfo.query_data(session=session, return_type="domain")
86+
sub_tag_options = sub_tag_options + [
87+
CreateTagInfoModel(tag=item.tag, tag_reason=item.tag_reason)
88+
for item in sub_tags_info
89+
if not stock_tags or (item.tag not in stock_tags.sub_tags)
90+
]
91+
return StockTagOptions(
92+
main_tag=main_tag, sub_tag=sub_tag, main_tag_options=main_tag_options, sub_tag_options=sub_tag_options
93+
)
94+
95+
5696
def build_stock_tags(
5797
set_stock_tags_model: SetStockTagsModel, timestamp: pd.Timestamp, set_by_user: bool, keep_current=False
5898
):
5999
logger.info(set_stock_tags_model)
100+
101+
main_tag_info = CreateTagInfoModel(
102+
tag=set_stock_tags_model.main_tag, tag_reason=set_stock_tags_model.main_tag_reason
103+
)
104+
if not is_tag_info_existed(tag_info=main_tag_info, tag_type=TagType.main_tag):
105+
build_tag_info(tag_info=main_tag_info, tag_type=TagType.main_tag)
106+
107+
sub_tag_info = CreateTagInfoModel(tag=set_stock_tags_model.sub_tag, tag_reason=set_stock_tags_model.sub_tag_reason)
108+
if not is_tag_info_existed(tag_info=sub_tag_info, tag_type=TagType.sub_tag):
109+
build_tag_info(tag_info=sub_tag_info, tag_type=TagType.sub_tag)
110+
60111
with contract_api.DBSession(provider="zvt", data_schema=StockTags)() as session:
61112
entity_id = set_stock_tags_model.entity_id
62113
main_tags = {}
@@ -608,6 +659,7 @@ def activate_sub_tags(activate_sub_tags_model: ActivateSubTagsModel):
608659
# the __all__ is generated
609660
__all__ = [
610661
"stock_tags_need_update",
662+
"get_stock_tag_options",
611663
"build_stock_tags",
612664
"build_tag_parameter",
613665
"batch_set_stock_tags",

0 commit comments

Comments
 (0)