Skip to content

Commit

Permalink
0.9.61 新增 pyd 打包支持
Browse files Browse the repository at this point in the history
  • Loading branch information
zengbin93 committed Nov 25, 2024
1 parent 596abdb commit 1f3be8e
Show file tree
Hide file tree
Showing 5 changed files with 33 additions and 14 deletions.
16 changes: 10 additions & 6 deletions czsc/traders/weight_backtest.py
Original file line number Diff line number Diff line change
Expand Up @@ -231,10 +231,13 @@ class WeightBacktest:
更新日志:
- V240627: 增加dailys属性,品种每日的交易信息
#### 20241125
1. 新增 yearly_days 参数,用于指定每年的交易日天数,默认为 252。
"""

version = "V240627"
version = "V241125"

def __init__(self, dfw, digits=2, **kwargs) -> None:
"""持仓权重回测
Expand Down Expand Up @@ -285,7 +288,8 @@ def __init__(self, dfw, digits=2, **kwargs) -> None:
self.dfw["weight"] = self.dfw["weight"].astype("float").round(digits)
self.symbols = list(self.dfw["symbol"].unique().tolist())
self._dailys = None
self.results = self.backtest(n_jobs=kwargs.get("n_jobs", 1))
self.yearly_days = kwargs.pop("yearly_days", 252)
self.results = self.backtest(n_jobs=kwargs.pop("n_jobs", 1))

@property
def stats(self):
Expand Down Expand Up @@ -332,7 +336,7 @@ def alpha(self) -> pd.DataFrame:
def alpha_stats(self):
"""策略超额收益统计"""
df = self.alpha.copy()
stats = czsc.daily_performance(df["超额"].to_list())
stats = czsc.daily_performance(df["超额"].to_list(), yearly_days=self.yearly_days)
stats["开始日期"] = df["date"].min().strftime("%Y-%m-%d")
stats["结束日期"] = df["date"].max().strftime("%Y-%m-%d")
return stats
Expand All @@ -341,7 +345,7 @@ def alpha_stats(self):
def bench_stats(self):
"""基准收益统计"""
df = self.alpha.copy()
stats = czsc.daily_performance(df["基准"].to_list())
stats = czsc.daily_performance(df["基准"].to_list(), yearly_days=self.yearly_days)
stats["开始日期"] = df["date"].min().strftime("%Y-%m-%d")
stats["结束日期"] = df["date"].max().strftime("%Y-%m-%d")
return stats
Expand Down Expand Up @@ -559,7 +563,7 @@ def backtest(self, n_jobs=1):
res["品种等权日收益"] = dret

stats = {"开始日期": dret["date"].min().strftime("%Y%m%d"), "结束日期": dret["date"].max().strftime("%Y%m%d")}
stats.update(daily_performance(dret["total"]))
stats.update(daily_performance(dret["total"], yearly_days=self.yearly_days))
dfp = pd.concat([v["pairs"] for k, v in res.items() if k in symbols], ignore_index=True)
pairs_stats = evaluate_pairs(dfp)
pairs_stats = {k: v for k, v in pairs_stats.items() if k in ["单笔收益", "持仓K线数", "交易胜率", "持仓天数"]}
Expand Down
12 changes: 10 additions & 2 deletions czsc/utils/st_components.py
Original file line number Diff line number Diff line change
Expand Up @@ -486,8 +486,15 @@ def show_weight_backtest(dfw, **kwargs):
- n_jobs: int, 并行计算的进程数,默认为 1
"""
from czsc.eda import cal_yearly_days

fee = kwargs.get("fee", 2)
digits = kwargs.get("digits", 2)
yearly_days = kwargs.pop("yearly_days", None)

if not yearly_days:
yearly_days = cal_yearly_days(dts=dfw["dt"].unique())

if (dfw.isnull().sum().sum() > 0) or (dfw.isna().sum().sum() > 0):
st.warning("show_weight_backtest :: 持仓权重数据中存在空值,请检查数据后再试;空值数据如下:")
st.dataframe(dfw[dfw.isnull().sum(axis=1) > 0], use_container_width=True)
Expand All @@ -509,13 +516,14 @@ def show_weight_backtest(dfw, **kwargs):
c9.metric("年化波动率", f"{stat['年化波动率']:.2%}")
c10.metric("多头占比", f"{stat['多头占比']:.2%}")
c11.metric("空头占比", f"{stat['空头占比']:.2%}")
st.caption(f"回测参数:单边手续费 {fee} BP,权重小数位数 {digits} ,年交易天数 {yearly_days}")
st.divider()

dret = wb.results["品种等权日收益"].copy()
dret["dt"] = pd.to_datetime(dret["date"])
dret = dret.set_index("dt").drop(columns=["date"])
# dret.index = pd.to_datetime(dret.index)
show_daily_return(dret, legend_only_cols=dfw["symbol"].unique().tolist(), **kwargs)
show_daily_return(dret, legend_only_cols=dfw["symbol"].unique().tolist(), yearly_days=yearly_days, **kwargs)

if kwargs.get("show_drawdowns", False):
show_drawdowns(dret, ret_col="total", sub_title="")
Expand All @@ -532,7 +540,7 @@ def show_weight_backtest(dfw, **kwargs):

if kwargs.get("show_splited_daily", False):
with st.expander("品种等权日收益分段表现", expanded=False):
show_splited_daily(dret[["total"]].copy(), ret_col="total")
show_splited_daily(dret[["total"]].copy(), ret_col="total", yearly_days=yearly_days)

if kwargs.get("show_yearly_stats", False):
with st.expander("年度绩效指标", expanded=False):
Expand Down
8 changes: 6 additions & 2 deletions czsc/utils/stats.py
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ def __min_max(x, min_val, max_val, digits=4):


def rolling_daily_performance(df: pd.DataFrame, ret_col, window=252, min_periods=100, **kwargs):
"""计算滚动日收益
"""计算滚动日收益的各项指标
:param df: pd.DataFrame, 日收益数据,columns=['dt', ret_col]
:param ret_col: str, 收益列名
Expand All @@ -173,19 +173,23 @@ def rolling_daily_performance(df: pd.DataFrame, ret_col, window=252, min_periods
- yearly_days: int, 252, 一年的交易日数
"""
from czsc.eda import cal_yearly_days

if not df.index.dtype == "datetime64[ns]":
df["dt"] = pd.to_datetime(df["dt"])
df.set_index("dt", inplace=True)
assert df.index.dtype == "datetime64[ns]", "index必须是datetime64[ns]类型, 请先使用 pd.to_datetime 进行转换"

yearly_days = kwargs.get("yearly_days", cal_yearly_days(df.index))

df = df[[ret_col]].copy().fillna(0)
df.sort_index(inplace=True, ascending=True)
dts = sorted(df.index.to_list())
res = []
for edt in dts[min_periods:]:
sdt = edt - pd.Timedelta(days=window)
dfg = df[(df.index >= sdt) & (df.index <= edt)].copy()
s = daily_performance(dfg[ret_col].to_list(), yearly_days=kwargs.get("yearly_days", 252))
s = daily_performance(dfg[ret_col].to_list(), yearly_days=yearly_days)
s["sdt"] = sdt
s["edt"] = edt
res.append(s)
Expand Down
8 changes: 4 additions & 4 deletions examples/develop/weight_backtest.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ def test_ensemble_weight():
from czsc import WeightBacktest

dfw = pd.read_feather(r"C:\Users\zengb\Downloads\weight_example.feather")
wb = WeightBacktest(dfw, digits=1, fee_rate=0.0002)
wb = WeightBacktest(dfw, digits=2, fee_rate=0.0002, n_jobs=1)
ss = sorted(wb.stats.items())
print(ss)

Expand All @@ -24,7 +24,7 @@ def test_rust_weight_backtest():

dfw = pd.read_feather(r"C:\Users\zengb\Downloads\weight_example.feather")

wb = WeightBacktest(czsc.to_arrow(dfw), digits=1, fee_rate=0.0002, n_jobs=1)
wb = WeightBacktest(czsc.to_arrow(dfw), digits=2, fee_rate=0.0002, n_jobs=1)

ss = sorted(wb.stats.items())
print(ss)
# ss = sorted(wb.stats.items())
# print(ss)
3 changes: 3 additions & 0 deletions examples/期货套利因子样例.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ def main():
import czsc
from czsc.connectors import cooperation as coo

# 构建策略
df1 = coo.get_raw_bars(symbol="DLy9001", freq="日线", sdt="20170101", edt="20221231", raw_bars=False, fq="后复权")
df2 = coo.get_raw_bars(symbol="DLp9001", freq="日线", sdt="20170101", edt="20221231", raw_bars=False, fq="后复权")
df = pd.concat([df1, df2], axis=0)
Expand All @@ -76,6 +77,8 @@ def main():
df["price"] = df["close"]

dfw = df[["dt", "symbol", "price", "weight"]].copy()

# 执行回测
st.title("期货套利研究")
czsc.show_weight_backtest(
dfw, fee_rate=0.0002, show_drawdowns=True, show_yearly_stats=True, show_monthly_return=True
Expand Down

0 comments on commit 1f3be8e

Please sign in to comment.