Skip to content

Commit

Permalink
0.8.29 update (#65)
Browse files Browse the repository at this point in the history
  • Loading branch information
zengbin93 committed Sep 8, 2022
1 parent f308719 commit 2d9faf9
Show file tree
Hide file tree
Showing 4 changed files with 22 additions and 135 deletions.
3 changes: 2 additions & 1 deletion czsc/traders/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@

from .advanced import CzscAdvancedTrader, create_advanced_trader, CzscDummyTrader
from .ts_backtest import TsStocksBacktest
from .ts_simulator import TradeSimulator, PairsPerformance
from .performance import TradersPerformance, PairsPerformance
from .ts_simulator import TradeSimulator
from .utils import trader_fast_backtest


25 changes: 13 additions & 12 deletions czsc/traders/performance.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
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
Expand Down Expand Up @@ -49,9 +50,7 @@ def __init__(self, df_pairs: pd.DataFrame, ):

self.df_pairs = df_pairs
# 指定哪些列可以用来进行聚合分析
self.agg_columns = ['标的代码', '交易方向',
'平仓年', '平仓月', '平仓周', '平仓日',
'开仓年', '开仓月', '开仓日', '开仓周']
self.agg_columns = ['标的代码', '交易方向', '平仓年', '平仓月', '平仓周', '平仓日', '开仓年', '开仓月', '开仓日', '开仓周']

@staticmethod
def get_pairs_statistics(df_pairs: pd.DataFrame):
Expand All @@ -75,7 +74,9 @@ def get_pairs_statistics(df_pairs: pd.DataFrame):

"交易胜率": 0,
"累计盈亏比": 0,
"单笔盈亏比": 0,
"交易得分": 0,
"赢面": 0,
"每自然日收益": 0,
"盈亏平衡点": 0,
}
Expand All @@ -84,11 +85,10 @@ def get_pairs_statistics(df_pairs: pd.DataFrame):
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]
gain = df_gain['盈亏比例'].sum()
loss = abs(df_loss['盈亏比例'].sum())

# 限制累计盈亏比最大有效值
gain_loss_rate = min(round(gain / (loss + 0.000001), 2), 5)
# 限制盈亏比最大有效值为 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(),
Expand All @@ -104,8 +104,10 @@ def get_pairs_statistics(df_pairs: pd.DataFrame):
"最小单笔收益": round(df_pairs['盈亏比例'].min() * 10000, 2),

"交易胜率": win_pct,
"累计盈亏比": gain_loss_rate,
"交易得分": round(gain_loss_rate * win_pct, 4),
"单笔盈亏比": 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),
}

Expand Down Expand Up @@ -141,7 +143,7 @@ def agg_to_excel(self, file_xlsx):
df_ = self.agg_statistics(col)
df_.to_excel(f, sheet_name=f"{col}聚合", index=False)
f.close()
print(f"聚合分析结果文件:{file_xlsx}")
logger.info(f"交易次数:{self.df_pairs}; 聚合分析结果文件:{file_xlsx}")


class TradersPerformance:
Expand Down Expand Up @@ -192,8 +194,7 @@ def get_holds(self, sdt, edt):
_lh = [x for x in trader.long_holds if edt >= x['dt'] >= sdt]
_results.extend(_lh)
except:
print(file)
traceback.print_exc()
logger.exception(f"分析失败:{file}")
df = pd.DataFrame(_results)
return df

111 changes: 0 additions & 111 deletions czsc/traders/ts_backtest.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,10 @@
import pandas as pd
from tqdm import tqdm
from typing import Callable
from deprecated import deprecated
from czsc import envs
from czsc.data import TsDataCache, freq_cn2ts
from czsc.traders.utils import trader_fast_backtest
from czsc.traders.performance import PairsPerformance
from czsc.utils import x_round
from czsc.objects import cal_break_even_point


def read_raw_results(raw_path, trade_dir="long"):
Expand Down Expand Up @@ -46,114 +43,6 @@ def read_raw_results(raw_path, trade_dir="long"):
return df_pairs, df_p


@deprecated(reason="use czsc.traders.performance.PairsPerformance instead", version='0.9.0')
class TraderPerformance:
"""择时交易效果评估"""

def __init__(self, df_pairs: pd.DataFrame, ):
"""
:param df_pairs: 全部交易对
"""
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 = ['标的代码', '交易方向', '平仓年', '平仓月', '平仓周', '平仓日']

@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,
}
return info

win_pct = x_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]
gain = df_gain['盈亏比例'].sum()
loss = abs(df_loss['盈亏比例'].sum())

# 限制累计盈亏比最大有效值
gain_loss_rate = min(x_round(gain / (loss + 0.000001), 2), 5)

info = {
"开始时间": df_pairs['开仓时间'].min(),
"结束时间": df_pairs['平仓时间'].max(),

"交易标的数量": df_pairs['标的代码'].nunique(),
"总体交易次数": len(df_pairs),
"平均持仓天数": x_round(df_pairs['持仓天数'].mean(), 2),

"平均单笔收益": x_round(df_pairs['盈亏比例'].mean() * 10000, 2),
"最大单笔收益": x_round(df_pairs['盈亏比例'].max() * 10000, 2),
"最小单笔收益": x_round(df_pairs['盈亏比例'].min() * 10000, 2),

"交易胜率": win_pct,
"累计盈亏比": gain_loss_rate,
"交易得分": x_round(gain_loss_rate * win_pct, 4),
"盈亏平衡点": x_round(cal_break_even_point(df_pairs['盈亏比例'].to_list()), 4),
}

info['每自然日收益'] = x_round(info['平均单笔收益'] / info['平均持仓天数'], 2)
return info

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)

def agg_to_excel(self, file_xlsx):
"""遍历聚合列,保存结果到 Excel 文件中"""
f = pd.ExcelWriter(file_xlsx)
for col in self.agg_columns:
df_ = self.agg_statistics(col)
df_.to_excel(f, sheet_name=f"{col}聚合", index=False)
f.close()
print(f"聚合分析结果文件:{file_xlsx}")


class TsStocksBacktest:
"""基于 Tushare 数据的择时回测系统(股票市场)"""

Expand Down
18 changes: 7 additions & 11 deletions czsc/traders/ts_simulator.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@
"""
import os
import inspect
import traceback
import pandas as pd
from tqdm import tqdm
from loguru import logger
from typing import Callable, List
from czsc import envs
from czsc.data import TsDataCache, freq_cn2ts
Expand All @@ -34,7 +34,6 @@ def __init__(self, dc: TsDataCache, strategy: Callable, res_path=None, init_n=50
self.base_freq = self.tactic['base_freq']
self.freqs = self.tactic['freqs']
self.init_n = init_n
self.verbose = envs.get_verbose()

self.data_path = dc.data_path
if not res_path:
Expand All @@ -47,7 +46,7 @@ def __init__(self, dc: TsDataCache, strategy: Callable, res_path=None, init_n=50
file_strategy = os.path.join(self.res_path, f'{strategy.__name__}_strategy.txt')
with open(file_strategy, 'w', encoding='utf-8') as f:
f.write(inspect.getsource(strategy))
print(f"strategy saved into {file_strategy}")
logger.info(f"strategy saved into {file_strategy}")

def get_bars(self, ts_code: str, asset: str, sdt=None) -> List[RawBar]:
"""获取指定周期K线序列
Expand Down Expand Up @@ -102,8 +101,7 @@ def get_trader(self, ts_code, asset="E"):

def update_trader(self, ts_code, asset="E"):
"""更新单个标的"""
if self.verbose:
print(f"{os.getpid()} TradeSimulator::update_trader: {ts_code}#{asset} start updating")
logger.info(f"{os.getpid()} {ts_code}#{asset} start updating")

file_trader = self.get_file_trader(ts_code, asset)
if os.path.exists(file_trader):
Expand All @@ -113,8 +111,7 @@ def update_trader(self, ts_code, asset="E"):

bars = self.get_bars(ts_code, asset, trader.end_dt)
bars = [x for x in bars if x.dt > trader.bg.end_dt]
if self.verbose:
print(f"{os.getpid()} TradeSimulator::update_trader: {ts_code}#{asset}{len(bars)}根K线需要更新")
logger.info(f"{os.getpid()} {ts_code}#{asset}{len(bars)}根K线需要更新")

for bar in bars:
trader.update(bar)
Expand All @@ -132,16 +129,15 @@ def update_traders(self, ts_codes, asset="E"):
for ts_code in tqdm(ts_codes, desc=f"update_traders | {asset}"):
try:
trader = self.update_trader(ts_code, asset)
print(f"\n{self.strategy.__name__} : {trader.results['long_performance']}")
logger.info(f"\n{self.strategy.__name__} : {trader.results['long_performance']}")
if trader.long_pos:
long_pairs.extend(trader.long_pos.pairs)
except:
print(f"update_traders: fail on {ts_code}#{asset}")
traceback.print_exc()
logger.exception(f"fail on {ts_code}#{asset}")

if long_pairs:
# lpp = Long Pairs Performance
lpp = PairsPerformance(pd.DataFrame(long_pairs))
lpp.agg_to_excel(os.path.join(self.res_path, f'long_pairs_{asset}_report.xlsx'))
lpp.df_pairs.to_feather(os.path.join(self.res_path, f'long_pairs_{asset}.feather'))
print(lpp.basic_info, '\n\n', lpp.agg_statistics('平仓年'))
logger.info(f"{lpp.basic_info}\n\n{lpp.agg_statistics('平仓年')}")

0 comments on commit 2d9faf9

Please sign in to comment.