simulation
# -*- coding: utf-8 -*-
"""strat_sz100iw.py
一种趋势策略的尝试, 针对深证100指数周线数据
"""
import os, pathlib, datetime; type(os); type(pathlib)
import pandas as pd; type(pd)
import numpy as np; type(np)
import matplotlib.pyplot as plt
from copy import deepcopy
from collections import OrderedDict ; type(OrderedDict)
import toolkit.myDataIO as mdio
import util.ttr as ttr
import strat_sz100iw_report as wr; type(wr)
import imp; type(imp)
#imp.reload(mdio)
#imp.reload(ttr)
class GlobalEnv:
pass
global g
g=GlobalEnv() # 初始化一个全局对象, 用于存储全局变量
g.params=OrderedDict(
atr_n=14, delta=0.5, sig_filter_n=3,
start_year=None,
code='399330',
maxloss_pct=0.20,
)
#def simulation_LineMode(atr_n, delta, sig_filter_n, start_year=None, code='399330'):
def simulation_LineMode(**kwargs):
'''
'''
global g
atr_n =kwargs['atr_n']
delta =kwargs['delta']
sig_filter_n=kwargs['sig_filter_n']
start_year =kwargs['start_year']
code =kwargs['code']
with_plot =kwargs['with_plot']
fname = 'd:/new_haitong/T0002/export/{}.txt'.format(code)
with open(fname, encoding='gbk') as f:
first_line_data=f.readline()
asset_name=first_line_data.strip()
title_='标的资产的代码和名称是: {}'.format(asset_name)
print(title_)
print('+'*(len(title_) + ttr.cchar(title_)))
ohlc_ = mdio.read_tdxExport_txtFile(fname)
ohlc=ohlc_[0]
ohlc=ohlc.iloc[:, 0:5]
if start_year:
ohlc = ohlc[start_year:]
out, perf_dict = ttr.strat_sz100iw(ohlc, atr_n, delta, sig_filter_n, start_year)
if with_plot:
fig, ax=plt.subplots(1,1)
ohlc.close.plot(ax=ax)
out.bprice.plot(ax=ax)
out.sprice.plot(ax=ax)
out.equity.plot()
print('End equity : {:12.2f}'.format(out.equity[-1] ,))
[print('{0:{2:}s} : {1:}'.format(k, v, (15-ttr.cchar(k)))) for k, v in perf_dict.items()]
print()
return out
def simulation_BarMode(df, **kwargs): #maxloss_pct=0.20):
global g
#g.params.append(maxloss_pct)
maxloss_pct= kwargs['maxloss_pct']
code = kwargs['code']
c=df.close
trend=df.trend; type(trend)
ctx_bsig=df.b_sig
ctx_ssig=df.s_sig
ctx_check, hold_dict, ctx_pos, pos_dict = [], {}, [], {}
order='empty'
days = df.index[40:-1]
_15hours = datetime.timedelta(0,15*3600); type(_15hours)
_9hours = datetime.timedelta(0,9*3600)
_930hours = datetime.timedelta(0,9.5*3600)
#ohlc.index.to_pydatetime() + _15hours
for enum, tdate in enumerate(days):
loc = df.index.get_loc(tdate) + 1
the_days = df.index[ (loc-5):loc]
# 回测当天
today = the_days[-1]
# 上一个交易日
yesterday = the_days[-2]
if enum%60==0:
#math.fmod(5, 3), 取模数, 求取除运算余数
# every 60 bars, print a timestamps of the process
print('''+++交易日期是: {}, {}'''.format(today,yesterday))
############### 检查交易信号
ssig = ctx_ssig.loc[yesterday]
bsig = ctx_bsig.loc[yesterday]
preclose = c.loc[yesterday]
todayopen_ = df.open[today]
todayclose = df.close[today]
ydtrend = df.trend[yesterday]
check_dict = OrderedDict(
dt=today.to_pydatetime() + _9hours,
bsig=bsig,
ssig=ssig,
preclose=preclose,
todayopen=todayopen_,
todayclose=todayclose,
comment= '牛股:伺机做多' if preclose > ydtrend else '熊股:少碰'
)
ctx_check.append(check_dict)
############### 制定/给出下单命令: 检查头寸, 检查浮动盈亏/持仓天数, 然后定出结论
if hold_dict=={}:
if bsig:
order = '开仓'
else:
order ='空仓寻进'; # print('order=wait',yesterday,wait)
else:
if ssig:
order = '清仓'
elif pos_dict!={} and pos_dict['fpnl']<-maxloss_pct :
order = '止损'
else:
order = '满仓寻出'
# 4种情况汇总起来应该是一个完整的集合, 不能有遗漏; 否则逻辑上有漏洞
pass
if hold_dict!={}:
fpnl, hold_days = pos_dict['fpnl'], pos_dict['hold_days']
stoploss_hp = abs(fpnl)<5 and hold_days>5 # 横盘太久应该出局观望
stoploss_5pct = fpnl<-5 # 建仓之后很快亏损, 到达一定幅度的话应该亏本出局
stopprft_cg = fpnl>20 # 快速冲高, 浮盈很快到达一定幅度后应该卖出, 等回落后再买进
type((stoploss_5pct,stoploss_hp, stopprft_cg))
############### 照单执行命令
if order=='开仓':
buy_date = today.to_pydatetime() + _930hours
pos_dict = OrderedDict(
order = order,
dt = today.to_pydatetime(),
buy_date=buy_date,
buy_price=todayopen_,
hold_days=0, position=1,
fpnl = 0.0, # preclose/buy_price,
)
### 保留头寸字典到列表里(头寸字典在循环过程中, 每天都在变化的, 而且在清仓时会变成空字典)
### 一定要用上deepcopy方法,
### 字典的深复制对象不会遭受到源字典的动态变化(依据收盘价不断修改键值)的影响
ctx_pos.append(deepcopy(pos_dict))
#hold_dict[g.code]=[buy_date, todayopen_]
hold_dict[code]=[buy_date, todayopen_]
if order=='清仓' or order=='止损':
pos_dict['order'] = order
pos_dict['dt'] = today.to_pydatetime() #+ _930hours
pos_dict['sell_date'] = today.to_pydatetime() + _930hours
pos_dict['sell_price'] = round(todayopen_,3)
pos_dict['ratio'] = round(100*((todayopen_/pos_dict['buy_price']) - 1),2)
pos_dict['fpnl'] = todayopen_/pos_dict['buy_price'] -1
pos_dict['params'] = g.params#(3,20, 1.5, 1, 5)
pos_dict['position']=0.0
ctx_pos.append(deepcopy(pos_dict))
del hold_dict[code]
if order=='满仓寻出':
pos_dict['order'] = order
pos_dict['dt'] = today.to_pydatetime() #+ _9hours
pos_dict['hold_days'] += 1
pos_dict['fpnl'] = preclose/pos_dict['buy_price'] -1
pos_dict['position']=1.0
ctx_pos.append(deepcopy(pos_dict))
if order=='空仓寻进':
del pos_dict; pos_dict=OrderedDict()
pos_dict['order'] = order
pos_dict['dt'] = today.to_pydatetime() #+ _930hours
pos_dict['position']=0.0
ctx_pos.append(deepcopy(pos_dict))
check_df = pd.DataFrame(ctx_check); type(check_df)
pos = pd.DataFrame(ctx_pos)
return pos
def simu_with_stoploss(atr_n=14, delta=0.5, sig_filter_n=3,
start_year=None,
code='399330',
maxloss_pct=0.20,
trx_timing='closing',
with_plot=False,
):
'''
>>> pos,cagr=simu_with_stoploss(maxloss_pct=0.12) #### 不错的止损值
>>> pos,cagr=simu_with_stoploss(maxloss_pct=0.12, trx_timing='opening') #### 不错的止损值
>>> pos=simu_with_stoploss(maxloss_pct=0.2)
>>> pos=simu_with_stoploss(maxloss_pct=0.11)
>>> pos=simu_with_stoploss(atr_n=13, maxloss_pct=0.12)
>>> pos=simu_with_stoploss(atr_n=16, maxloss_pct=0.12)
>>> pos=simu_with_stoploss(maxloss_pct=0.15)
参数: OrderedDict(
[('atr_n', 14), ('delta', 0.5), ('sig_filter_n', 3), ('start_year', None), ('code', '399330'),
('maxloss_pct', 0.12), ('trx_timing', 'closing'), ('with_plot', False)])
下单指令集合: {'满仓寻出', '止损', '开仓', '空仓寻进', '清仓'}
dk 的次数: 49
dp 的次数: 45
gr 的次数: 4
hold_equity 的次数: 264
hold_cash 的次数: 326
end_equity : 11541.354 if trx_timing=='opening'
end_nav : 12.003 7.996
cagr : 0.19 0.156
mdd : 0.235 0.274
sharpe : 1.151
annu_volatility : 0.165
净值的标准差 : 0.023
'''
global g
g.params=OrderedDict(
atr_n=atr_n, delta=delta, sig_filter_n=sig_filter_n,
start_year=start_year,
code=code,
maxloss_pct=maxloss_pct,
trx_timing=trx_timing,
with_plot=with_plot,
)
#df = simulation_LineMode(atr_n, delta, sig_filter_n, start_year, '399330')
#pos = simulation_BarMode(df, maxloss_pct)
df = simulation_LineMode(**g.params)
pos = simulation_BarMode(df, **g.params)
pos = pos.set_index('dt', drop=False)
print('
参数: {}'.format(g.params))
print('
下单指令集合: {}
'.format(set(pos.order)))
order_group_dict = OrderedDict(
dk = pos.order[pos.order=='开仓'].count(), # 多开,
dp = pos.order[pos.order=='清仓'].count(), # 多平
gr = pos.order[pos.order=='止损'].count(), # 割肉, stoploss
hold_equity = pos.order[pos.order=='满仓寻出'].count(),
hold_cash = pos.order[pos.order=='空仓寻进'].count(),
)
[print('{:12s}的次数: {:6d}'.format(k,v)) for k,v in order_group_dict.items()]
pos_=pos.reindex(df.index)
pos_.position.fillna(0.0, inplace=True)
#print(set(pos_.position))
roc1=df.close.pct_change().fillna(0)
#交割时机的把握逻辑:
#收盘时刻观察信号, 按收盘价成交:
if trx_timing=='closing':
eq = (1+roc1*pos_.position).cumprod() * df.close[0]
elif trx_timing=='opening':
roc1_B = ttr.IF((df.b_sig.shift(1)>0, (df.open/df.close.shift(1)-1), roc1))
roc1_C = ttr.IF((df.s_sig>0, (df.open.shift(-1)/df.close.shift(1)-1), roc1_B))
eq = (1+roc1_C * pos_.position).cumprod() * df.close[0]
pos_['open']=df.open
pos_['close']=df.close
pos_['equity']=eq
perf_dict=ttr.perf(pos_)
print()
#[print('{:15s} : {}'.format(k, v)) for k, v in perf_dict.items()]
for k, v in perf_dict.items():
pwidth = 17 - ttr.cchar(k)
print('{1:{0:}s} : {2:}'.format(pwidth, k, v))
print()
if with_plot:
fig, ax=plt.subplots(1,1)
pos_.close.plot(ax=ax)
#out.bprice.plot(ax=ax)
#out.sprice.plot(ax=ax)
pos_.equity.plot()
return pos_, perf_dict['cagr']
#%%
def opt(trx_timing='closing'):
'''参数寻优
最佳参数大概为: 14 atr_n, 12 maxloss_pct, 'closing' trx_timing
>>> opt(trx_timing='closing')
>>> opt(trx_timing='opening')
atr_n maxloss_pct cagr
0 12 0.05 0.141
1 12 0.10 0.149
2 12 0.15 0.148
3 13 0.05 0.158
4 13 0.10 0.176
5 13 0.15 0.175
6 14 0.05 0.151
7 14 0.10 0.179
8 14 0.15 0.180
9 15 0.05 0.140
10 15 0.10 0.145
11 15 0.15 0.155
12 16 0.05 0.138
13 16 0.10 0.157
14 16 0.15 0.156
15 17 0.05 0.120
16 17 0.10 0.139
17 17 0.15 0.135
18 18 0.05 0.132
19 18 0.10 0.143
20 18 0.15 0.143
if 次日开盘成交: 那么结果如下: 结论是: 成交时机应该选择当日尾盘(cagr能有3%的提升)
atr_n maxloss_pct cagr
0 12 0.05 0.096
1 12 0.10 0.103
2 12 0.15 0.103
3 13 0.05 0.130
4 13 0.10 0.146
5 13 0.15 0.145
6 14 0.05 0.118
7 14 0.10 0.146
8 14 0.15 0.147
9 15 0.05 0.102
10 15 0.10 0.105
11 15 0.15 0.114
12 16 0.05 0.107
13 16 0.10 0.125
14 16 0.15 0.124
15 17 0.05 0.088
16 17 0.10 0.105
17 17 0.15 0.102
18 18 0.05 0.089
19 18 0.10 0.098
20 18 0.15 0.098
'''
opt_summary=pd.DataFrame()
for atr_n in range(12, 19):
for maxloss_pct in [0.05, 0.10, 0.15]:
pos, cagr=simu_with_stoploss(atr_n=atr_n, maxloss_pct=maxloss_pct, trx_timing=trx_timing)
opt_summary = opt_summary.append(
pd.DataFrame(OrderedDict(
atr_n=atr_n,
maxloss_pct=maxloss_pct,
cagr=cagr),
index=[1,] ),
ignore_index=True,
)
print(opt_summary)
#%%
#wr.write_report_md()
#%%
if __name__=="__main__":
#atr_n=int(input('Please enter parameter: atr_n: '))
#for atr_n in range(12, 18):
pass