Source code for czsc.traders.performance
# -*- coding: utf-8 -*-
"""
author: zengbin93
email: zeng_bin8888@163.com
create_dt: 2022/5/10 15:19
describe: 请描述文件用途
"""
import glob
import traceback
import pandas as pd
from tqdm import tqdm
from loguru import logger
from czsc.traders.advanced import CzscAdvancedTrader
from czsc.utils import dill_load
from czsc.objects import cal_break_even_point
[docs]class PairsPerformance:
"""交易对效果评估"""
def __init__(self, df_pairs: pd.DataFrame, ):
"""
:param df_pairs: 全部交易对,数据样例如下
标的代码 交易方向 最大仓位 开仓时间 累计开仓 平仓时间 \
0 000001.SH 多头 1 2020-02-06 09:45:00 2820.014893 2020-02-10 13:15:00
1 000001.SH 多头 1 2020-03-20 14:15:00 2733.164062 2020-03-27 14:15:00
2 000001.SH 多头 1 2020-03-30 13:30:00 2747.813965 2020-03-31 13:15:00
3 000001.SH 多头 1 2020-04-01 10:45:00 2765.350098 2020-04-02 09:45:00
4 000001.SH 多头 1 2020-04-02 14:15:00 2757.827881 2020-04-09 11:15:00
累计平仓 累计换手 持仓K线数 事件序列 持仓天数 盈亏金额 交易盈亏 \
0 2872.166992 2 40 开多@低吸 > 平多@60分钟顶背驰 4.145833 52.152100 0.0184
1 2786.754883 2 80 开多@低吸 > 平多@60分钟顶背驰 7.000000 53.590820 0.0196
2 2752.198975 2 15 开多@低吸 > 平多@持有资金 0.989583 4.385010 0.0015
3 2721.335938 2 12 开多@低吸 > 平多@持有资金 0.958333 -44.014160 -0.0159
4 2821.693115 2 58 开多@低吸 > 平多@60分钟顶背驰 6.875000 63.865234 0.0231
盈亏比例
0 0.0184
1 0.0196
2 0.0015
3 -0.0159
4 0.0231
"""
time_convert = lambda x: (x.strftime("%Y年"), x.strftime("%Y年%m月"), x.strftime("%Y-%m-%d"),
f"{x.year}年第{x.weekofyear}周" if x.weekofyear >= 10 else f"{x.year}年第0{x.weekofyear}周",
)
df_pairs[['开仓年', '开仓月', '开仓日', '开仓周']] = list(df_pairs['开仓时间'].apply(time_convert))
df_pairs[['平仓年', '平仓月', '平仓日', '平仓周']] = list(df_pairs['平仓时间'].apply(time_convert))
self.df_pairs = df_pairs
# 指定哪些列可以用来进行聚合分析
self.agg_columns = ['标的代码', '交易方向', '平仓年', '平仓月', '平仓周', '平仓日', '开仓年', '开仓月', '开仓日', '开仓周']
[docs] @staticmethod
def get_pairs_statistics(df_pairs: pd.DataFrame):
"""统计一组交易的基本信息
:param df_pairs:
:return:
"""
if len(df_pairs) == 0:
info = {
"开始时间": None,
"结束时间": None,
"交易标的数量": 0,
"总体交易次数": 0,
"平均持仓天数": 0,
"平均单笔收益": 0,
"单笔收益标准差": 0,
"最大单笔收益": 0,
"最小单笔收益": 0,
"交易胜率": 0,
"累计盈亏比": 0,
"单笔盈亏比": 0,
"交易得分": 0,
"赢面": 0,
"每自然日收益": 0,
"每根K线收益": 0,
"盈亏平衡点": 0,
}
return info
win_pct = round(len(df_pairs[df_pairs['盈亏比例'] > 0]) / len(df_pairs), 4)
df_gain = df_pairs[df_pairs['盈亏比例'] > 0]
df_loss = df_pairs[df_pairs['盈亏比例'] <= 0]
# 限制盈亏比最大有效值为 5
single_gain_loss_rate = min(round(df_gain['盈亏比例'].mean() / (abs(df_loss['盈亏比例'].mean()) + 1e-8), 2), 5)
total_gain_loss_rate = min(round(df_gain['盈亏比例'].sum() / (abs(df_loss['盈亏比例'].sum()) + 1e-8), 2), 5)
info = {
"开始时间": df_pairs['开仓时间'].min(),
"结束时间": df_pairs['平仓时间'].max(),
"交易标的数量": df_pairs['标的代码'].nunique(),
"总体交易次数": len(df_pairs),
"平均持仓天数": round(df_pairs['持仓天数'].mean(), 2),
"平均持仓K线数": round(df_pairs['持仓K线数'].mean(), 2),
"平均单笔收益": round(df_pairs['盈亏比例'].mean() * 10000, 2),
"单笔收益标准差": round(df_pairs['盈亏比例'].std() * 10000, 2),
"最大单笔收益": round(df_pairs['盈亏比例'].max() * 10000, 2),
"最小单笔收益": round(df_pairs['盈亏比例'].min() * 10000, 2),
"交易胜率": win_pct,
"单笔盈亏比": single_gain_loss_rate,
"累计盈亏比": total_gain_loss_rate,
"交易得分": round(total_gain_loss_rate * win_pct, 4),
"赢面": round(single_gain_loss_rate * win_pct - (1 - win_pct), 4),
"盈亏平衡点": round(cal_break_even_point(df_pairs['盈亏比例'].to_list()), 4),
}
info['每自然日收益'] = round(info['平均单笔收益'] / info['平均持仓天数'], 2)
info['每根K线收益'] = round(info['平均单笔收益'] / info['平均持仓K线数'], 2)
return info
[docs] def agg_statistics(self, col: str):
"""按列聚合进行交易对评价"""
df_pairs = self.df_pairs.copy()
assert col in self.agg_columns, f"{col} 不是支持聚合的列,参考:{self.agg_columns}"
results = []
for name, dfg in df_pairs.groupby(col):
if dfg.empty:
continue
res = {col: name}
res.update(self.get_pairs_statistics(dfg))
results.append(res)
df = pd.DataFrame(results)
return df
@property
def basic_info(self):
"""写入基础信息"""
df_pairs = self.df_pairs.copy()
return self.get_pairs_statistics(df_pairs)
[docs] def agg_to_excel(self, file_xlsx):
"""遍历聚合列,保存结果到 Excel 文件中"""
f = pd.ExcelWriter(file_xlsx)
for col in ['标的代码', '交易方向', '平仓年', '平仓月', '平仓周', '平仓日']:
df_ = self.agg_statistics(col)
df_.to_excel(f, sheet_name=f"{col}聚合", index=False)
f.close()
logger.info(f"交易次数:{len(self.df_pairs)}; 聚合分析结果文件:{file_xlsx}")
[docs]class TradersPerformance:
"""Trader Strategy 的效果评估"""
def __init__(self, traders_pat):
self.file_traders = glob.glob(traders_pat)
[docs] def get_pairs(self, sdt, edt):
"""获取一段时间内的所有交易对
:param sdt: 开始时间
:param edt: 结束时间
:return:
"""
sdt = pd.to_datetime(sdt)
edt = pd.to_datetime(edt)
_results = []
for file in tqdm(self.file_traders, desc=f"get_pairs | {sdt} | {edt}"):
try:
trader: CzscAdvancedTrader = dill_load(file)
_pairs = [x for x in trader.long_pos.pairs if edt >= x['平仓时间'] > x['开仓时间'] >= sdt]
_results.extend(_pairs)
except:
print(file)
traceback.print_exc()
df = pd.DataFrame(_results)
return df
[docs] def get_holds(self, sdt, edt):
"""获取一段时间内的所有持仓信号
:param sdt: 开始时间
:param edt: 结束时间
:return: 返回数据样例如下
dt symbol long_pos n1b
0 2020-01-02 09:45:00 000001.SH 0 0.004154
1 2020-01-02 10:00:00 000001.SH 0 0.001472
2 2020-01-02 10:15:00 000001.SH 0 0.001291
3 2020-01-02 10:30:00 000001.SH 0 0.001558
4 2020-01-02 10:45:00 000001.SH 0 -0.001355
"""
sdt = pd.to_datetime(sdt)
edt = pd.to_datetime(edt)
_results = []
for file in tqdm(self.file_traders, desc=f"get_holds | {sdt} | {edt}"):
try:
trader: CzscAdvancedTrader = dill_load(file)
_lh = [x for x in trader.long_holds if edt >= x['dt'] >= sdt]
_results.extend(_lh)
except:
logger.exception(f"分析失败:{file}")
df = pd.DataFrame(_results)
return df