-
Notifications
You must be signed in to change notification settings - Fork 863
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
10 changed files
with
456 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
# -*- coding: utf-8 -*- | ||
# the __all__ is generated | ||
__all__ = [] | ||
|
||
# __init__.py structure: | ||
# common code of the package | ||
# export interface in __all__ which contains __all__ of its sub modules | ||
|
||
# import all from submodule emotion | ||
from .emotion import * | ||
from .emotion import __all__ as _emotion_all | ||
|
||
__all__ += _emotion_all |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
# -*- coding: utf-8 -*- | ||
from sqlalchemy import Column, String, Integer, DateTime, Boolean, Float | ||
from sqlalchemy.orm import declarative_base | ||
|
||
from zvt.contract import Mixin | ||
from zvt.contract.register import register_schema | ||
|
||
EmotionBase = declarative_base() | ||
|
||
|
||
class LimitUpInfo(EmotionBase, Mixin): | ||
__tablename__ = "limit_up_info" | ||
|
||
code = Column(String(length=32)) | ||
name = Column(String(length=32)) | ||
#: 是否新股 | ||
is_new = Column(Boolean) | ||
#: 是否回封,是就是打开过,否相反 | ||
is_again_limit = Column(Boolean) | ||
#: 涨停打开次数,0代表封住就没开板 | ||
open_count = Column(Integer) | ||
#: 首次封板时间 | ||
first_limit_up_time = Column(DateTime) | ||
#: 最后封板时间 | ||
last_limit_up_time = Column(DateTime) | ||
#: 涨停类型:换手板,一字板 | ||
limit_up_type = Column(String) | ||
#: 封单金额 | ||
order_amount = Column(String) | ||
#: 最近一年封板成功率 | ||
success_rate = Column(Float) | ||
#: 流通市值 | ||
currency_value = Column(Float) | ||
#: 涨幅 | ||
change_pct = Column(Float) | ||
#: 换手率 | ||
turnover_rate = Column(Float) | ||
#: 涨停原因 | ||
reason = Column(String) | ||
#: 几天几板 | ||
high_days = Column(String) | ||
#: 最近几板,不一定是连板 | ||
high_days_count = Column(Integer) | ||
|
||
|
||
class LimitDownInfo(EmotionBase, Mixin): | ||
__tablename__ = "limit_down_info" | ||
|
||
code = Column(String(length=32)) | ||
name = Column(String(length=32)) | ||
#: 是否新股 | ||
is_new = Column(Boolean) | ||
#: 是否回封,是就是打开过,否相反 | ||
is_again_limit = Column(Boolean) | ||
#: 流通市值 | ||
currency_value = Column(Float) | ||
#: 涨幅 | ||
change_pct = Column(Float) | ||
#: 换手率 | ||
turnover_rate = Column(Float) | ||
|
||
|
||
class Emotion(EmotionBase, Mixin): | ||
__tablename__ = "emotion" | ||
#: 涨停数量 | ||
limit_up_count = Column(Integer) | ||
#: 炸板数 | ||
limit_up_open_count = Column(Integer) | ||
#: 涨停封板成功率 | ||
limit_up_success_rate = Column(Float) | ||
|
||
#: 连板高度 | ||
max_height = Column(Integer) | ||
#: 连板数x个数 相加 | ||
continuous_power = Column(Integer) | ||
|
||
#: 跌停数量 | ||
limit_down_count = Column(Integer) | ||
#: 跌停打开 | ||
limit_down_open_count = Column(Integer) | ||
#: 跌停封板成功率 | ||
limit_down_success_rate = Column(Float) | ||
|
||
|
||
register_schema(providers=["jqka"], db_name="emotion", schema_base=EmotionBase) | ||
# the __all__ is generated | ||
__all__ = ["LimitUpInfo", "LimitDownInfo", "Emotion"] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
# -*- coding: utf-8 -*- | ||
# the __all__ is generated | ||
__all__ = [] | ||
|
||
# __init__.py structure: | ||
# common code of the package | ||
# export interface in __all__ which contains __all__ of its sub modules | ||
|
||
# import all from submodule jqka_api | ||
from .jqka_api import * | ||
from .jqka_api import __all__ as _jqka_api_all | ||
|
||
__all__ += _jqka_api_all | ||
|
||
# import all from submodule emotion | ||
from .emotion import * | ||
from .emotion import __all__ as _emotion_all | ||
|
||
__all__ += _emotion_all |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,207 @@ | ||
# -*- coding: utf-8 -*- | ||
from typing import List | ||
|
||
import pandas as pd | ||
|
||
from zvt.api.utils import china_stock_code_to_id | ||
from zvt.contract.api import df_to_db | ||
from zvt.contract.recorder import TimestampsDataRecorder | ||
from zvt.domain import Stock | ||
from zvt.domain.emotion.emotion import LimitUpInfo, LimitDownInfo, Emotion | ||
from zvt.recorders.jqka import jqka_api | ||
from zvt.utils import to_time_str, next_date, current_date, to_pd_timestamp | ||
|
||
|
||
def _get_high_days_count(high_days_str: str): | ||
if not high_days_str or (high_days_str == "首板"): | ||
return 1 | ||
return int(high_days_str[3]) | ||
|
||
|
||
class JqkaLimitUpRecorder(TimestampsDataRecorder): | ||
entity_provider = "em" | ||
entity_schema = Stock | ||
|
||
provider = "jqka" | ||
data_schema = LimitUpInfo | ||
|
||
def init_entities(self): | ||
# fake entity to for trigger run | ||
self.entities = [Stock(id="stock_sz_000001")] | ||
|
||
def init_timestamps(self, entity_item) -> List[pd.Timestamp]: | ||
latest_infos = LimitUpInfo.query_data( | ||
provider=self.provider, order=LimitUpInfo.timestamp.desc(), limit=1, return_type="domain" | ||
) | ||
if latest_infos: | ||
start_date = latest_infos[0].timestamp | ||
else: | ||
# 最近一年半的数据 | ||
start_date = next_date(current_date(), -365 - 366 / 2) | ||
return pd.date_range(start=start_date, end=pd.Timestamp.now(), freq="B").tolist() | ||
|
||
def record(self, entity, start, end, size, timestamps): | ||
for timestamp in timestamps: | ||
the_date = to_time_str(timestamp) | ||
self.logger.info(f"record {self.data_schema} to {the_date}") | ||
limit_ups = jqka_api.get_limit_up(date=the_date) | ||
if limit_ups: | ||
records = [] | ||
for data in limit_ups: | ||
entity_id = china_stock_code_to_id(code=data["code"]) | ||
record = { | ||
"id": "{}_{}".format(entity_id, the_date), | ||
"entity_id": entity_id, | ||
"timestamp": to_pd_timestamp(the_date), | ||
"code": data["code"], | ||
"name": data["name"], | ||
"is_new": data["is_new"], | ||
"is_again_limit": data["is_again_limit"], | ||
"open_count": data["open_num"] if data["open_num"] else 0, | ||
"first_limit_up_time": pd.Timestamp.fromtimestamp(int(data["first_limit_up_time"])), | ||
"last_limit_up_time": pd.Timestamp.fromtimestamp(int(data["last_limit_up_time"])), | ||
"limit_up_type": data["limit_up_type"], | ||
"order_amount": data["order_amount"], | ||
"success_rate": data["limit_up_suc_rate"], | ||
"currency_value": data["currency_value"], | ||
"change_pct": data["change_rate"] / 100, | ||
"turnover_rate": data["turnover_rate"] / 100, | ||
"reason": data["reason_type"], | ||
"high_days": data["high_days"], | ||
"high_days_count": _get_high_days_count(data["high_days"]), | ||
} | ||
records.append(record) | ||
df = pd.DataFrame.from_records(records) | ||
df_to_db( | ||
data_schema=self.data_schema, | ||
df=df, | ||
provider=self.provider, | ||
force_update=True, | ||
drop_duplicates=True, | ||
) | ||
|
||
|
||
class JqkaLimitDownRecorder(TimestampsDataRecorder): | ||
entity_provider = "em" | ||
entity_schema = Stock | ||
|
||
provider = "jqka" | ||
data_schema = LimitDownInfo | ||
|
||
def init_entities(self): | ||
# fake entity to for trigger run | ||
self.entities = [Stock(id="stock_sz_000001")] | ||
|
||
def init_timestamps(self, entity_item) -> List[pd.Timestamp]: | ||
latest_infos = LimitDownInfo.query_data( | ||
provider=self.provider, order=LimitDownInfo.timestamp.desc(), limit=1, return_type="domain" | ||
) | ||
if latest_infos: | ||
start_date = latest_infos[0].timestamp | ||
else: | ||
# 最近一年半的数据 | ||
start_date = next_date(current_date(), -365 - 366 / 2) | ||
return pd.date_range(start=start_date, end=pd.Timestamp.now(), freq="B").tolist() | ||
|
||
def record(self, entity, start, end, size, timestamps): | ||
for timestamp in timestamps: | ||
the_date = to_time_str(timestamp) | ||
self.logger.info(f"record {self.data_schema} to {the_date}") | ||
limit_downs = jqka_api.get_limit_down(date=the_date) | ||
if limit_downs: | ||
records = [] | ||
for data in limit_downs: | ||
entity_id = china_stock_code_to_id(code=data["code"]) | ||
record = { | ||
"id": "{}_{}".format(entity_id, the_date), | ||
"entity_id": entity_id, | ||
"timestamp": to_pd_timestamp(the_date), | ||
"code": data["code"], | ||
"name": data["name"], | ||
"is_new": data["is_new"], | ||
"is_again_limit": data["is_again_limit"], | ||
"currency_value": data["currency_value"], | ||
"change_pct": data["change_rate"] / 100, | ||
"turnover_rate": data["turnover_rate"] / 100, | ||
} | ||
records.append(record) | ||
df = pd.DataFrame.from_records(records) | ||
df_to_db( | ||
data_schema=self.data_schema, | ||
df=df, | ||
provider=self.provider, | ||
force_update=True, | ||
drop_duplicates=True, | ||
) | ||
|
||
|
||
def _cal_power_and_max_height(continuous_limit_up: dict): | ||
max_height = 0 | ||
power = 0 | ||
for item in continuous_limit_up: | ||
if max_height < item["height"]: | ||
max_height = item["height"] | ||
power = power + item["height"] * item["number"] | ||
return max_height, power | ||
|
||
|
||
class JqkaEmotionRecorder(TimestampsDataRecorder): | ||
entity_provider = "em" | ||
entity_schema = Stock | ||
|
||
provider = "jqka" | ||
data_schema = Emotion | ||
|
||
def init_entities(self): | ||
# fake entity to for trigger run | ||
self.entities = [Stock(id="stock_sz_000001")] | ||
|
||
def init_timestamps(self, entity_item) -> List[pd.Timestamp]: | ||
latest_infos = Emotion.query_data( | ||
provider=self.provider, order=Emotion.timestamp.desc(), limit=1, return_type="domain" | ||
) | ||
if latest_infos: | ||
start_date = latest_infos[0].timestamp | ||
else: | ||
# 最近一年半的数据 | ||
start_date = next_date(current_date(), -365 - 366 / 2) | ||
return pd.date_range(start=start_date, end=pd.Timestamp.now(), freq="B").tolist() | ||
|
||
def record(self, entity, start, end, size, timestamps): | ||
for timestamp in timestamps: | ||
the_date = to_time_str(timestamp) | ||
self.logger.info(f"record {self.data_schema} to {the_date}") | ||
limit_stats = jqka_api.get_limit_stats(date=the_date) | ||
continuous_limit_up = jqka_api.get_continuous_limit_up(date=the_date) | ||
max_height, continuous_power = _cal_power_and_max_height(continuous_limit_up=continuous_limit_up) | ||
|
||
if limit_stats: | ||
# 大盘 | ||
entity_id = "stock_sh_000001" | ||
record = { | ||
"id": "{}_{}".format(entity_id, the_date), | ||
"entity_id": entity_id, | ||
"timestamp": to_pd_timestamp(the_date), | ||
"limit_up_count": limit_stats["limit_up_count"]["today"]["num"], | ||
"limit_up_open_count": limit_stats["limit_up_count"]["today"]["open_num"], | ||
"limit_up_success_rate": limit_stats["limit_up_count"]["today"]["rate"], | ||
"limit_down_count": limit_stats["limit_down_count"]["today"]["num"], | ||
"limit_down_open_count": limit_stats["limit_down_count"]["today"]["open_num"], | ||
"limit_down_success_rate": limit_stats["limit_down_count"]["today"]["rate"], | ||
"max_height": max_height, | ||
"continuous_power": continuous_power, | ||
} | ||
df = pd.DataFrame.from_records([record]) | ||
df_to_db( | ||
data_schema=self.data_schema, | ||
df=df, | ||
provider=self.provider, | ||
force_update=True, | ||
drop_duplicates=True, | ||
) | ||
|
||
|
||
if __name__ == "__main__": | ||
JqkaEmotionRecorder().run() | ||
# the __all__ is generated | ||
__all__ = ["JqkaLimitUpRecorder", "JqkaLimitDownRecorder", "JqkaEmotionRecorder"] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
# -*- coding: utf-8 -*- | ||
# the __all__ is generated | ||
__all__ = [] | ||
|
||
# __init__.py structure: | ||
# common code of the package | ||
# export interface in __all__ which contains __all__ of its sub modules | ||
|
||
# import all from submodule JqkaEmotionRecorder | ||
from .JqkaEmotionRecorder import * | ||
from .JqkaEmotionRecorder import __all__ as _JqkaEmotionRecorder_all | ||
|
||
__all__ += _JqkaEmotionRecorder_all |
Oops, something went wrong.