simulation

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



原文地址:https://www.cnblogs.com/duan-qs/p/12913330.html