Skip to content

Commit

Permalink
add emotion data from provider jqka
Browse files Browse the repository at this point in the history
  • Loading branch information
foolcage committed Jan 17, 2023
1 parent 3650cfd commit f58937b
Show file tree
Hide file tree
Showing 10 changed files with 456 additions and 3 deletions.
6 changes: 6 additions & 0 deletions src/zvt/domain/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -190,3 +190,9 @@ def get_future_name(code):
from .actor import __all__ as _actor_all

__all__ += _actor_all

# import all from submodule emotion
from .emotion import *
from .emotion import __all__ as _emotion_all

__all__ += _emotion_all
13 changes: 13 additions & 0 deletions src/zvt/domain/emotion/__init__.py
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
87 changes: 87 additions & 0 deletions src/zvt/domain/emotion/emotion.py
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"]
6 changes: 3 additions & 3 deletions src/zvt/fill_project.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,10 +90,10 @@ def gen_kdata_schemas():
# gen_exports("ml")
# gen_exports("utils")
# gen_exports('informer')
gen_exports("api")
# gen_exports("api")
# gen_exports('trader')
# gen_exports('autocode')
# gen_exports("ml")
# gen_kdata_schemas()
# gen_exports("recorders")
# gen_exports("domain")
gen_exports("recorders")
gen_exports("domain")
6 changes: 6 additions & 0 deletions src/zvt/recorders/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,12 @@ def init_main_index(provider="exchange"):

__all__ += _consts_all

# import all from submodule jqka
from .jqka import *
from .jqka import __all__ as _jqka_all

__all__ += _jqka_all

# import all from submodule eastmoney
from .eastmoney import *
from .eastmoney import __all__ as _eastmoney_all
Expand Down
1 change: 1 addition & 0 deletions src/zvt/recorders/em/em_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -720,6 +720,7 @@ def to_zvt_code(code):
__all__ = [
"get_treasury_yield",
"get_ii_holder_report_dates",
"get_dragon_and_tiger_list",
"get_dragon_and_tiger",
"get_holder_report_dates",
"get_free_holder_report_dates",
Expand Down
19 changes: 19 additions & 0 deletions src/zvt/recorders/jqka/__init__.py
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
207 changes: 207 additions & 0 deletions src/zvt/recorders/jqka/emotion/JqkaEmotionRecorder.py
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"]
13 changes: 13 additions & 0 deletions src/zvt/recorders/jqka/emotion/__init__.py
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
Loading

0 comments on commit f58937b

Please sign in to comment.