目次
株のデータ収集についての記事一覧をこちらに記載しております。
目的
- ゴールデンクロスが起きたら買い注文を入れ、デッドクロスが起きたら売り注文を出すロジックのバックテストを実施する
- Backtesting.pyを使用する
バックテストとは
システムトレード(売買ルール)の有効性を検証する際に、過去のデータを用いて、一定期間にどの程度のパフォーマンスが得られたかをシミュレーションすること。
Backtesting.pyのインストール
pip install backtesting
ゴールデンクロスとデッドクロスを算出する処理
過去の株価データと移動平均線は事前にDBに登録されている状態とします。
Djangoを使用します。modelは以下のようになっております。
class RawPrices(models.Model):
code = models.IntegerField("銘柄コード")
date = models.DateField("日付")
open_price = models.IntegerField("始値")
close_price = models.IntegerField("終値")
high_price = models.IntegerField("高値")
low_price = models.IntegerField("安値")
volume = models.IntegerField("出来高")
adjustment_close_price = models.FloatField("調整後終値")
moving_averages25 = models.FloatField("25日移動平均線")
moving_averages75 = models.FloatField("75日移動平均線")
ゴールデンクロスとデッドクロスはBacktesting.pyのcrossover関数で算出することが可能です。
DjangoのCommandでスクリプトを作成します。
サイボウズの株価情報を使用してバックテストを実施します。
from django.core.management.base import BaseCommand
from app.models import RawPrices
import pandas as pd
from backtesting import Backtest
from backtesting import Strategy
from backtesting.lib import crossover
import logging
logger = logging.getLogger(__name__)
class Command(BaseCommand):
help = ''
def handle(self, *args, **options):
self.backtest()
def backtest(self):
df = self.get_data()
# cashは運用資金になります。commissionで手数料を指定することも可能です。
bt = Backtest(df, SmaCross, cash=10000)
# バックテストの実行/結果を表示
print(bt.run())
def get_data(self):
# DBからデータを取得。4776はサイボウズの証券コード
raw_prices = RawPrices.objects.filter(code=4776)
raw_prices = raw_prices.filter(date__gte="2019-01-01")
raw_prices = raw_prices.exclude(moving_averages75=-1).order_by("date")
raw_price_list = list(
map(lambda x: [x.date, x.open_price, x.high_price, x.low_price, x.close_price, x.moving_averages25, x.moving_averages75], raw_prices))
"""
Backtesting.pyではdf['Open'] = df['High'] = df['Low'] = df['Close']を指定する必要があります。
オプションでVolumeも指定することが可能です。
"""
columns = ["date", "Open", "High", "Low", "Close", "sma25", "sma75"]
df = pd.DataFrame(raw_price_list, columns=columns)
df.date = pd.to_datetime(df.date)
df.set_index("date", inplace=True)
return df
class SmaCross(Strategy):
def init(self):
"""
sma1に25日平均線のデータをセット
sma2に75日平均線のデータをセット
:return:
"""
self.sma1 = self.I(sma, self.data.sma25) #Iの引数に関数と関数の引数を渡す
self.sma2 = self.I(sma, self.data.sma75)
def next(self):
"""
ゴールデンクロスの時に買い、デッドクロスの時に売却する
:return:
"""
# crossoverはゴールデンクロス/デッドクロスを算出する関数
if crossover(self.sma1, self.sma2):
self.buy()
elif crossover(self.sma2, self.sma1):
self.position.close()
def sma(data: pd.Series):
return data
実行結果
python manage.py backtesting_test
Start 2019-01-04 00:00:00
End 2019-09-27 00:00:00
Duration 266 days 00:00:00
Exposure [%] 63.5338
Equity Final [$] 15853.7
Equity Peak [$] 20200.9
Return [%] 58.5366
Buy & Hold Return [%] 56.1934
Max. Drawdown [%] -22.0881
Avg. Drawdown [%] -3.10143
Max. Drawdown Duration 10 days 00:00:00
Avg. Drawdown Duration 5 days 00:00:00
# Trades 1
Win Rate [%] 100
Best Trade [%] 57.0629
Worst Trade [%] 57.0629
Avg. Trade [%] 57.0629
Max. Trade Duration 169 days 00:00:00
Avg. Trade Duration 169 days 00:00:00
Expectancy [%] NaN
SQN NaN
Sharpe Ratio NaN
Sortino Ratio NaN
Calmar Ratio 2.58343
_strategy SmaCross
dtype: object
事項結果の概要
start/end -> バックテストで使った為替レートのデータの最初の最後の時間
Duration -> 期間
Equity Final [$] -> 最終的な金額
Trades -> トレード回数
Win Rate [%] -> トレードの勝率
今回のバックテストの結果では10000ドルの運用資金で開始して、最終的に16872.3ドルになっており、+6872.3ドルの利益になっております。
bt.plot()を実行することによって、実行結果を可視化してブラウザで表示することができます。

2019/3/8にゴールデンクロスが起きて購入し、2019/8/23にデッドクロスが起きて売却しております。
上記、チャートで気になる点があります。2019/8/23にデッドクロスが起きて売却しているのですが、それ以降もEquityが変動しております。
売却は空売り扱いのようになっているようです。空売り扱いなので売却後に株価が下がった場合に利益として計算されているようです。
空売り扱いにならないようなオプションが無いか探してみましたが見つけることができませんでした。
Strategyクラスのbuy()とsell()メソッドはオプションでストップリミットとテイクプロフィット価格を指定できるようです。
https://kernc.github.io/backtesting.py/doc/backtesting/backtesting.html#backtesting.backtesting.Strategy.buy
def buy(self, price=None, *, sl=None, tp=None)
def sell(self, price=None, *, sl=None, tp=None)