【参考】
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)アカウント情報とバックテスト情報を保存する
口座情報:現金、保有株式
バックテスト情報:
- 開始/終了日
- 現在の日付
- ベンチマーク:一般的に、株式またはインデックスは、戦略の長所と短所を比較するためのベンチマークとして使用されます
- 開始から終了までのすべての取引日の情報
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)初期化機能
- 目標株を設定する
- ベンチマークを設定する
- 二重移動平均情報を設定する(g.p1&g.p2)
- 対象株式の履歴データを取得します。次のコードで
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つの順序付け関数が含まれています。
order
:一定数の株式を購入するorder_value
一定量の株を買うorder_target
一定数の株を買う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