(定量的)Tushareパッケージを使用して単純なバックテストフレームワークを実装する

【参考】
1.ステーションB:清華大学博士号取得-Python財務定量分析
2. Tushare公式サイト(著者ID:492952)

このブログに含まれているプロジェクトコードは基本的にReference1を参照しており、それに応じてtushareAPIの更新が変更されています。すべてのコードは作成者のgithubにアップロードされています。以下は、コードのアイデアに関するナレッジカードにすぎません。

1.バックテストの要件と効果の表示

China Ping An(601318.SH)を取引対象として、2020-05-10から2021-01-01までの二重移動平均戦略の収入状況をテストします。効果は次のとおりです。
画像の説明を追加してください

2.コードフレームワーク

2.1オブジェクト

(1)アカウント情報とバックテスト情報を保存する

口座情報:現金、保有株式

バックテスト情報:

  1. 開始/終了日
  2. 現在の日付
  3. ベンチマーク:一般的に、株式またはインデックスは、戦略の長所と短所を比較するためのベンチマークとして使用されます
  4. 開始から終了までのすべての取引日の情報
class Context:
    def __init__(self, cash, start_date, end_date):
        # 账户信息
        self.cash = cash    # 现金
        self.positions = {
    
    } # 持有的股票信息
        # 回测信息
        self.start_date = start_date
        self.end_date = end_date
        self.dt = start_date
        self.benchmark = None
        self.date_range = trade_cal[(trade_cal['is_open'] == '1') & \
                                    (trade_cal['cal_date'] >= start_date) & \
                                    (trade_cal['cal_date'] <= end_date)]['cal_date'].values

その中で、trade_calすべての取引日の日付情報が保存されます。pro.trade_cal()tushareパッケージから入手可能

(2)他のグローバル変数を保存する

class G:
	pass

2.2機能

(0)主な機能

def run():
    initialize(context) # 初始化

	# 创建一个DataFrame,储存画图所需数据
    plt_df = pd.DataFrame(index=pd.to_datetime(context.date_range), columns=['value'])
    # 存储股票的上一个交易日的价格信息,避免以为股票停牌而无法获取价格
    last_prices = {
    
    }
    # 回测开始时的股票价格,用于计算之后的价格变动曲线
    initial_value = context.cash
    
    for dt in context.date_range:
        context.dt = dt # 更新当前时间
        handle_data(context)
		# 计算账户价值 = 现金 + 持仓股票
        value = context.cash
        for stock in context.positions.keys():
            try:
                data = get_today_data(stock)
                last_prices[stock] = data['open']
            except KeyError:
                # 如果取不到,说明当日停牌,取上一个交易日的价格
                price = last_prices[stock]
            value += price * context.positions[stock].amount
        plt_df.loc[dt, 'value'] = value
        
    # 绘制策略
    plt_df['ratio'] = (plt_df['value'] - initial_value) / initial_value
    # 绘制基准
    bm_df = attribute_daterange_history(context.benchmark, context.start_date, context.end_date)
    bm_init = bm_df['open'][0]
    plt_df['benchmark_ratio'] = (bm_df['open'] - bm_init) / bm_init

    plt_df[['ratio', 'benchmark_ratio']].plot()
    plt.show()

(1)初期化機能

  1. 目標株を設定する
  2. ベンチマークを設定する
  3. 二重移動平均情報を設定する(g.p1&g.p2)
  4. 対象株式の履歴データを取得します。次のコードでhist_1は、バックテストの開始日のg.p2前日のデータをhist_2取得し、バックテストの開始から終了までのデータを取得します。2つを組み合わせると、二重移動平均バックテストに必要なすべての株価データを取得できます。
def initialize(context):
	g.security = '601318.SH'
    set_benchmark('601318.SH')
    g.p1 = 5
    g.p2 = 60

    hist_1 = attribute_history(g.security, g.p2)
    hist_2 = attribute_daterange_history(g.security, context.start_date, context.end_date)
    g.hist = hist_1.append(hist_2) 

(2)取引日ごとに実行する必要のある機能

def handle_data(context):
    hist = g.hist[:dateutil.parser.parse(context.dt)][-g.p2:]
    ma5 = hist['close'][-g.p1:].mean()
    ma60 = hist['close'].mean()

	# 实现双均线策略:
	# 如果短均线高于长均线,且股票不在持仓中,则买入
	# 反之,且股票在持仓中,则卖出
    if ma5 > ma60 and g.security not in context.positions.keys():
        order_value(g.security, context.cash)
    elif ma5 < ma60 and g.security in context.positions.keys():
        order_target(g.security, 0)

(3)注文機能

githubにアップロードされたコードには、合計4つの順序付け関数が含まれています。

  1. order:一定数の株式を購入する
  2. order_value一定量の株を買う
  3. order_target一定数の株を買う
  4. order_target_value一定量の株を買う

そして、これらの4つの順序付け機能は以下に基づいています。

def _order(today_data, security, amount):
    if today_data.empty: return
    # 获取股票价格
    price = today_data['open']
    # 判断是否持有该股票
    try:
        test = context.positions[security]
    except KeyError:
        # 如果卖出操作,直接退出函数
        if amount <= 0: return
        # 如果买入操作,创建position
        context.positions[security] = pd.Series(dtype=float)
        context.positions[security]['amount'] = 0

    # 买入/卖出操作时,必须以100的倍数购买,除非全部卖出
    if (amount % 100 != 0) and (amount != -context.positions[security].amount):
        amount = int(amount/100) * 100

    # 更新持仓
    context.positions[security].amount = context.positions[security].get('amount') + amount
    if context.positions[security].amount == 0: # 如果持仓股数为0,删除
        del context.positions[security]
    # 更新现金
    context.cash -= amount * price

おすすめ

転載: blog.csdn.net/weixin_43728138/article/details/122859016