Skip to content

Commit

Permalink
0.9.29 update
Browse files Browse the repository at this point in the history
  • Loading branch information
zengbin93 committed Sep 21, 2023
1 parent 6848041 commit f68db2a
Show file tree
Hide file tree
Showing 12 changed files with 543 additions and 115 deletions.
1 change: 0 additions & 1 deletion czsc/cmd.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
https://click.palletsprojects.com/en/8.0.x/quickstart/
"""
import click
from loguru import logger


@click.group()
Expand Down
2 changes: 1 addition & 1 deletion czsc/data/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
"3600s": "60分钟", "1d": "日线"}
freq_cn2gm = {v: k for k, v in freq_gm2cn.items()}


def jq_symbol_to_gm(symbol: str) -> str:
"""聚宽代码转掘金代码"""
code, exchange = symbol.split(".")
Expand Down Expand Up @@ -154,4 +155,3 @@ def save_symbols_to_ebk(symbols, file_ebk, source='ts'):
tdx_symbols = [symbol_to_tdx(ts_code) for ts_code in symbols]
with open(file_ebk, encoding='utf-8', mode='w') as f:
f.write("\n".join(tdx_symbols))

7 changes: 1 addition & 6 deletions czsc/eda.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,11 @@
describe: 用于探索性分析的函数
"""
import numpy as np
import pandas as pd


def vwap(price: np.array, volume: np.array, **kwargs) -> float:
"""计算成交量加权平均价
:param price: 价格序列
:param volume: 成交量序列
:return: 平均价
Expand All @@ -26,7 +25,3 @@ def twap(price: np.array, **kwargs) -> float:
:return: 平均价
"""
return np.average(price)




8 changes: 8 additions & 0 deletions czsc/enum.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,18 @@ def __str__(self):
class Freq(Enum):
Tick = "Tick"
F1 = "1分钟"
F2 = "2分钟"
F3 = "3分钟"
F4 = "4分钟"
F5 = "5分钟"
F6 = "6分钟"
F10 = "10分钟"
F12 = "12分钟"
F15 = "15分钟"
F20 = "20分钟"
F30 = "30分钟"
F60 = "60分钟"
F120 = "120分钟"
D = "日线"
W = "周线"
M = "月线"
Expand Down
2 changes: 1 addition & 1 deletion czsc/envs.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,5 +50,5 @@ def get_bi_change_th(v: float = None) -> float:
"""
bi_change_th = v if v else os.environ.get('czsc_bi_change_th', '1')
bi_change_th = float(bi_change_th)
assert 2 >= bi_change_th >= 0.5 or bi_change_th == -1, f"czsc_bi_change_th not in [0.5, 2]"
assert 2 >= bi_change_th >= 0.5 or bi_change_th == -1, "czsc_bi_change_th not in [0.5, 2]"
return bi_change_th
139 changes: 104 additions & 35 deletions czsc/utils/bar_generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,109 @@
describe: 从任意周期K线开始合成更高周期K线的工具类
"""
import pandas as pd
from datetime import datetime, timedelta
from datetime import datetime, timedelta, date
from typing import List, Union, AnyStr
from czsc.objects import RawBar, Freq
from pathlib import Path
from loguru import logger
from czsc.utils.calendar import next_trading_date


mss = pd.read_feather(Path(__file__).parent / "minites_split.feather")
freq_market_times, freq_edt_map = {}, {}
for _m, dfg in mss.groupby('market'):
for _f in [x for x in mss.columns if x.endswith("分钟")]:
freq_market_times[f"{_f}_{_m}"] = list(dfg[_f].unique())
freq_edt_map[f"{_f}_{_m}"] = {k: v for k, v in dfg[["time", _f]].values}


def check_freq_and_market(time_seq: List[AnyStr]):
"""检查时间序列是否为同一周期,是否为同一市场
:param time_seq: 时间序列,如 ['11:00', '15:00', '23:00', '01:00', '02:30']
:return:
- freq K线周期
- market 交易市场
"""
assert len(time_seq) >= 2, "time_seq长度必须大于等于2"
res = {}
for key, tts in freq_market_times.items():
if set(tts) == set(time_seq):
freq, market = key.split("_")
return freq, market

if len(time_seq) < len(tts) * 0.9 or len(time_seq) > len(tts) * 1.1:
continue

res[key] = len(set(time_seq).intersection(set(tts))) / len(tts)
freq, market = sorted(res.items(), key=lambda x: x[1], reverse=True)[0][0].split("_")
return freq, market


def freq_end_date(dt, freq: Union[Freq, AnyStr]):
"""交易日结束时间计算"""
if not isinstance(dt, date):
dt = pd.to_datetime(dt).date()
if not isinstance(freq, Freq):
freq = Freq(freq)

dt = pd.to_datetime(dt)
if freq == Freq.D:
return dt

if freq == Freq.W:
return dt + timedelta(days=5 - dt.isoweekday())

if freq == Freq.Y:
return datetime(year=dt.year, month=12, day=31)

if freq == Freq.M:
if dt.month == 12:
edt = datetime(year=dt.year, month=12, day=31)
else:
edt = datetime(year=dt.year, month=dt.month + 1, day=1) - timedelta(days=1)
return edt

if freq == Freq.S:
dt_m = dt.month
if dt_m in [1, 2, 3]:
edt = datetime(year=dt.year, month=4, day=1) - timedelta(days=1)
elif dt_m in [4, 5, 6]:
edt = datetime(year=dt.year, month=7, day=1) - timedelta(days=1)
elif dt_m in [7, 8, 9]:
edt = datetime(year=dt.year, month=10, day=1) - timedelta(days=1)
else:
edt = datetime(year=dt.year, month=12, day=31)
return edt

logger.warning(f'error: {dt} - {freq}')
return dt


def freq_end_time_V230921(dt: datetime, freq: Union[Freq, AnyStr], market="A股") -> datetime:
"""A股与期货市场精确的获取 dt 对应的K线周期结束时间
:param dt: datetime
:param freq: Freq
:return: datetime
"""
assert market in ['A股', '期货', '默认'], "market 参数必须为 A股 或 期货 或 默认"
if not isinstance(freq, Freq):
freq = Freq(freq)
if dt.second > 0 or dt.microsecond > 0:
dt = dt.replace(second=0, microsecond=0) + timedelta(minutes=1)

hm = dt.strftime("%H:%M")
key = f"{freq.value}_{market}"
if freq.value.endswith("分钟"):
h, m = freq_edt_map[key][hm].split(":")
edt = dt.replace(hour=int(h), minute=int(m))
return edt

if not ("15:00" > hm > "09:00") and market == "期货":
dt = next_trading_date(dt.strftime("%Y-%m-%d"), 1)

return freq_end_date(dt.date(), freq)


def freq_end_time(dt: datetime, freq: Union[Freq, AnyStr]) -> datetime:
Expand Down Expand Up @@ -44,39 +144,7 @@ def freq_end_time(dt: datetime, freq: Union[Freq, AnyStr]) -> datetime:
return edt

# 处理 日、周、月、季、年 的结束时间
dt = dt.replace(hour=0, minute=0)

if freq == Freq.D:
return dt

if freq == Freq.W:
sdt = dt + timedelta(days=5 - dt.isoweekday())
return sdt

if freq == Freq.M:
if dt.month == 12:
sdt = datetime(year=dt.year + 1, month=1, day=1) - timedelta(days=1)
else:
sdt = datetime(year=dt.year, month=dt.month + 1, day=1) - timedelta(days=1)
return sdt

if freq == Freq.S:
dt_m = dt.month
if dt_m in [1, 2, 3]:
sdt = datetime(year=dt.year, month=4, day=1) - timedelta(days=1)
elif dt_m in [4, 5, 6]:
sdt = datetime(year=dt.year, month=7, day=1) - timedelta(days=1)
elif dt_m in [7, 8, 9]:
sdt = datetime(year=dt.year, month=10, day=1) - timedelta(days=1)
else:
sdt = datetime(year=dt.year + 1, month=1, day=1) - timedelta(days=1)
return sdt

if freq == Freq.Y:
return datetime(year=dt.year, month=12, day=31)

print(f'freq_end_time error: {dt} - {freq}')
return dt
return freq_end_date(dt.date(), freq)


def resample_bars(df: pd.DataFrame, target_freq: Union[Freq, AnyStr], raw_bars=True, **kwargs):
Expand All @@ -102,7 +170,8 @@ def resample_bars(df: pd.DataFrame, target_freq: Union[Freq, AnyStr], raw_bars=T
if not isinstance(target_freq, Freq):
target_freq = Freq(target_freq)

df['freq_edt'] = df['dt'].apply(lambda x: freq_end_time(x, target_freq))
market = kwargs.get("market", "默认")
df['freq_edt'] = df['dt'].apply(lambda x: freq_end_time_V230921(x, target_freq, market))
dfk1 = df.groupby('freq_edt').agg(
{'symbol': 'first', 'dt': 'last', 'open': 'first', 'close': 'last', 'high': 'max',
'low': 'min', 'vol': 'sum', 'amount': 'sum', 'freq_edt': 'last'})
Expand Down
Binary file added czsc/utils/minites_split.feather
Binary file not shown.
32 changes: 32 additions & 0 deletions czsc/utils/st_components.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import czsc
import pandas as pd
import streamlit as st
import plotly.express as px


def show_daily_return(df, **kwargs):
"""用streamlit展示日收益"""
assert df.index.dtype == 'datetime64[ns]', "index必须是datetime64[ns]类型, 请先使用 pd.to_datetime 进行转换"
type_ = "持有日" if kwargs.get("none_zero", False) else "交易日"

df = df.copy().fillna(0)
stats = []
for col in df.columns:
col_stats = czsc.daily_performance([x for x in df[col] if x != 0]) if type_ == '持有日' else czsc.daily_performance(df[col])
col_stats['日收益名称'] = col
stats.append(col_stats)

stats = pd.DataFrame(stats).set_index('日收益名称')
fmt_cols = ['年化', '夏普', '最大回撤', '卡玛', '年化波动率', '非零覆盖']
stats = stats.style.background_gradient(cmap='RdYlGn_r', axis=None).format('{:.4f}', subset=fmt_cols)

df = df.cumsum()
fig = px.line(df, y=df.columns.to_list(), title="日收益累计曲线")
for col in kwargs.get("legend_only_cols", []):
fig.update_traces(visible="legendonly", selector=dict(name=col))

with st.container():
st.subheader(f'{kwargs.get("title", "日收益表现评价")}{type_})')
st.divider()
st.dataframe(stats, use_container_width=True)
st.plotly_chart(fig, use_container_width=True)
Loading

0 comments on commit f68db2a

Please sign in to comment.