目的
TOPIX Core30銘柄(2024/9/22時点)に対して、テクニカル指標を使ってバックテストの実施とパラメータの最適化を試してみる
※本記事は投資成績を担保するものではありません。投資はあくまで自己責任での範囲にてよろしくお願い致します。
環境
- Python:v3.11.5
- Anaconda:conda v23.7.4
作業メモ
環境整備
参考リンク
Anaconda の導入
以前、こちらで導入したため、そちらの記事を参照ください
必要ライブラリのインストール
- Anaconda Powershell 上で以下のライブラリをインストール
- pandas_datareader : Web上の情報を取得できる。株関連の情報取得に使うつもりだったが、yfinanceがあればいらないかも
- yfinance : yahoo financeから株関連データを取得することができる
- ta-lib : 様々なテクニカル分析を行うことができる1
- backtesting : バックテストを簡単に行うことができる
> pip.exe install pandas_datareader
> pip.exe install yfinance
> conda install -c conda-forge ta-lib
> pip.exe install backtesting
動作確認
参考リンク
- https://qiita.com/aguilarklyno/items/9a1c1a6f5e478c81c725
- https://qiita.com/royal_straight_flush/items/4c390b3c5a769a26dc88
簡単にインストールしたライブラリの動作確認を行う
yfinance
ソースコード
import yfinance as yf
import datetime
# 変数定義
stock_data = "3382.T"
start_date = datetime.datetime(2024, 1, 1)
end_date = datetime.datetime(2024, 9, 22)
# データ取得
ticker = yf.Ticker(stock_data)
data = yf.download(stock_data, start_date, end_date)
結果確認
各種株関連データを取得できることを確認
ticker.info
{'address1': '8-8, Nibancho',
'address2': 'Chiyoda-ku',
'city': 'Tokyo',
'zip': '102-8452',
'country': 'Japan',
'phone': '81 3 6238 3000',
'website': 'https://www.7andi.com',
'industry': 'Grocery Stores',
'industryKey': 'grocery-stores',
'industryDisp': 'Grocery Stores',
'sector': 'Consumer Defensive',
'sectorKey': 'consumer-defensive',
'sectorDisp': 'Consumer Defensive',
'longBusinessSummary': 'Seven & i Holdings Co., Ltd. operates convenience stores, superstores, department stores, supermarkets, and specialty stores. It operates through six segments: Domestic Convenience Store operations, Overseas Convenience Store Operations, Superstore Operations, Department and Specialty Store Operations, Financial Services, and Others. The company engages in the operation of convenience stores comprising directly managed corporate stores and franchised stores; retail business that provides daily life necessities, such as food and other daily necessities and dollar merchandise and services; banking, leasing, and credit card business; and real estate business. Seven & i Holdings Co., Ltd. was incorporated in 2005 and is headquartered in Tokyo, Japan.',
'fullTimeEmployees': 77902,
'companyOfficers': [{'maxAge': 1,
'name': 'Mr. Ryuichi Isaka',
'age': 66,
'title': 'CEO, President & Representative Director',
'yearBorn': 1957,
'fiscalYear': 2024,
'totalPay': 205000000,
'exercisedValue': 0,
'unexercisedValue': 0},
{'maxAge': 1,
'name': 'Mr. Junro Ito',
'age': 65,
'title': 'Sr. Managing Ex. Officer, CSuO, GM of ESG Dev, Supervising Off. of Sup. Op. & Rep. Director',
'yearBorn': 1958,
'fiscalYear': 2024,
'totalPay': 80000000,
'exercisedValue': 0,
'unexercisedValue': 0},
{'maxAge': 1,
'name': 'Mr. Fumihiko Nagamatsu',
'age': 66,
'title': 'Senior Managing Executive Officer & Director',
'yearBorn': 1957,
'fiscalYear': 2024,
'totalPay': 107000000,
'exercisedValue': 0,
'unexercisedValue': 0},
{'maxAge': 1,
'name': 'Mr. Yoshimichi Maruyama',
'age': 64,
'title': 'CFO, Managing Exe. Officer, Head of Corporate Finance & Accounting Division and Director',
'yearBorn': 1959,
'fiscalYear': 2024,
'exercisedValue': 0,
'unexercisedValue': 0},
{'maxAge': 1,
'name': 'Mr. Tamaki Wakita',
'age': 51,
'title': 'Operating Officer, Chief Strategy Officer, Head of Corporate planning Division & Director',
'yearBorn': 1972,
'fiscalYear': 2024,
'exercisedValue': 0,
'unexercisedValue': 0},
{'maxAge': 1,
'name': 'Mr. Joseph Michael DePinto',
'age': 61,
'title': 'Senior Managing Executive Officer & Director',
'yearBorn': 1962,
'fiscalYear': 2024,
'totalPay': 3786000000,
'exercisedValue': 0,
'unexercisedValue': 0},
{'maxAge': 1,
'name': 'Mr. Takuji Hayashi',
'title': 'Executive Managing Officer',
'fiscalYear': 2024,
'exercisedValue': 0,
'unexercisedValue': 0},
{'maxAge': 1,
'name': 'Mr. Tomihiro Saegusa',
'title': 'Executive Managing Officer',
'fiscalYear': 2024,
'exercisedValue': 0,
'unexercisedValue': 0},
{'maxAge': 1,
'name': 'Seiichiro Ishibashi',
'title': 'Executive Managing Officer',
'fiscalYear': 2024,
'exercisedValue': 0,
'unexercisedValue': 0},
{'maxAge': 1,
'name': 'Koichiro Otaka',
'title': 'Managing Executive Officer',
'fiscalYear': 2024,
'exercisedValue': 0,
'unexercisedValue': 0}],
'auditRisk': 1,
'boardRisk': 3,
'compensationRisk': 1,
'shareHolderRightsRisk': 4,
'overallRisk': 2,
'governanceEpochDate': 1726617600,
'compensationAsOfEpochDate': 1735603200,
'maxAge': 86400,
'priceHint': 2,
'previousClose': 2158.0,
'open': 2180.0,
'dayLow': 2151.5,
'dayHigh': 2203.5,
'regularMarketPreviousClose': 2158.0,
'regularMarketOpen': 2180.0,
'regularMarketDayLow': 2151.5,
'regularMarketDayHigh': 2203.5,
'dividendRate': 40.0,
'dividendYield': 0.0186,
'exDividendDate': 1740614400,
'payoutRatio': 0.4882,
'fiveYearAvgDividendYield': 2.11,
'beta': 0.302,
'trailingPE': 27.85474,
'forwardPE': 16.91165,
'volume': 9458000,
'regularMarketVolume': 9458000,
'averageVolume': 12713453,
'averageVolume10days': 13733800,
'averageDailyVolume10Day': 13733800,
'bid': 2151.0,
'ask': 2160.5,
'marketCap': 5581571620864,
'fiftyTwoWeekLow': 1600.0,
'fiftyTwoWeekHigh': 2275.0,
'priceToSalesTrailing12Months': 0.48300886,
'fiftyDayAverage': 1936.68,
'twoHundredDayAverage': 1996.5625,
'trailingAnnualDividendRate': 113.0,
'trailingAnnualDividendYield': 0.0523633,
'currency': 'JPY',
'enterpriseValue': 8568291983360,
'profitMargins': 0.01764,
'floatShares': 2378294860,
'sharesOutstanding': 2594269952,
'heldPercentInsiders': 0.13299,
'heldPercentInstitutions': 0.4375,
'impliedSharesOutstanding': 2594269952,
'bookValue': 1468.282,
'priceToBook': 1.465318,
'lastFiscalYearEnd': 1709164800,
'nextFiscalYearEnd': 1740700800,
'mostRecentQuarter': 1717113600,
'earningsQuarterlyGrowth': -0.493,
'netIncomeToCommon': 203831001088,
'trailingEps': 77.24,
'forwardEps': 127.22,
'pegRatio': 0.88,
'lastSplitFactor': '3:1',
'lastSplitDate': 1709078400,
'enterpriseToRevenue': 0.741,
'enterpriseToEbitda': 8.222,
'52WeekChange': 0.079167366,
'SandP52WeekChange': 0.31472707,
'lastDividendValue': 20.0,
'lastDividendDate': 1740614400,
'exchange': 'JPX',
'quoteType': 'EQUITY',
'symbol': '3382.T',
'underlyingSymbol': '3382.T',
'shortName': 'SEVEN & I HOLDINGS CO LTD',
'longName': 'Seven & i Holdings Co., Ltd.',
'firstTradeDateEpochUtc': 946944000,
'timeZoneFullName': 'Asia/Tokyo',
'timeZoneShortName': 'JST',
'uuid': 'abd08f46-ce41-3d3f-907e-462ae3d1c364',
'messageBoardId': 'finmb_23740668',
'gmtOffSetMilliseconds': 32400000,
'currentPrice': 2151.5,
'targetHighPrice': 2900.0,
'targetLowPrice': 1910.0,
'targetMeanPrice': 2341.9,
'targetMedianPrice': 2350.0,
'recommendationMean': 2.2,
'recommendationKey': 'buy',
'numberOfAnalystOpinions': 16,
'totalCash': 1370588971008,
'totalCashPerShare': 528.314,
'ebitda': 1042100977664,
'totalDebt': 4175185051648,
'quickRatio': 0.604,
'currentRatio': 0.905,
'totalRevenue': 11555837050880,
'debtToEquity': 104.609,
'revenuePerShare': 4384.036,
'returnOnAssets': 0.02931,
'returnOnEquity': 0.055689998,
'freeCashflow': 334798258176,
'operatingCashflow': 591511027712,
'earningsGrowth': -0.485,
'revenueGrowth': 0.032,
'grossMargins': 0.29577,
'ebitdaMargins': 0.090179995,
'operatingMargins': 0.0217,
'financialCurrency': 'JPY',
'trailingPegRatio': 1.4795}
data
Open High Low Close Adj Close Volume
Date
2024-01-04 1874.333374 1887.333374 1851.666626 1881.666626 1847.162964 6571200
2024-01-05 1908.333374 1913.333374 1877.666626 1881.666626 1847.162964 6573300
2024-01-09 1881.666626 1917.000000 1878.000000 1911.333374 1876.285767 11673600
2024-01-10 1914.666626 1920.000000 1894.333374 1904.000000 1869.086792 7838100
2024-01-11 1931.666626 1956.666626 1925.333374 1952.333374 1916.533936 10124100
... ... ... ... ... ... ...
2024-09-13 2198.000000 2201.000000 2168.000000 2168.000000 2168.000000 10290300
2024-09-17 2124.500000 2171.000000 2099.500000 2158.500000 2158.500000 11857900
2024-09-18 2167.500000 2192.500000 2145.000000 2151.500000 2151.500000 8331300
2024-09-19 2177.000000 2199.500000 2147.500000 2158.000000 2158.000000 7304000
2024-09-20 2180.000000 2203.500000 2151.500000 2151.500000 2151.500000 9458000
177 rows × 6 columns
ta-lib
今回はテクニカル指標として、ボリンジャーバンドを使いたいので、その動作を確認
ソースコード
import yfinance as yf
import talib as ta
import datetime
import matplotlib.pyplot as plt
%matplotlib inline
# 変数定義
stock_data = "3382.T"
start_date = datetime.datetime(2024, 1, 1)
end_date = datetime.datetime(2024, 9, 22)
# データ取得
ticker = yf.Ticker(stock_data)
data = yf.download(stock_data, start_date, end_date)
# 調整済み終値
close = data['Adj Close']
# 移動平均線(ここでは25日平均)
span = 25
# ボリンジャーバンド
# 上のボリンジャーバンド, 移動平均, 下のボリンジャーバンド = ta.BBANDS(調整済み終値, 日経平均日数, 上のラインの標準偏差, 下のラインの標準偏差, 移動平均の計算方法)インの標準偏差, ○○移動平均)
data['upper'], data['middle'], data['lower'] = ta.BBANDS(close, timeperiod=25, nbdevup=2, nbdevdn=2, matype=0)
結果確認
ボリンジャーバンドの情報を取得できたことを確認
# 日付情報
date = data.index
# グラフ表示
plt.figure(figsize=(30, 15))
plt.plot(date, close, label='Adj Close', color='#99b898')
# plt.fill_between(x軸, 塗りつぶし上側, 塗りつぶし下側, 色, 透過)
plt.fill_between(date, data['upper'], data['lower'], color='gray', alpha=0.3)
plt.legend()
バックテスト実施
参考リンク
- https://note.com/scilabcafe/n/n41c75c5223f4
- https://qiita.com/Fujinoinvestor/items/f2bdaabb766db443ddc0
- https://kernc.github.io/backtesting.py/doc/backtesting/
バックテストの評価をするため、持ちっぱなしの場合との比較を行う2
TOPIX Core30銘柄に対してバックテスト
2024年の現時点(2024/9/22)の期間を対象とする
25日平均線/±2σ のボリンジャーバンドを使用してバックテスト
ソースコード
from backtesting import Strategy
from talib import BBANDS
from backtesting.lib import crossover
from backtesting import Backtest
import yfinance as yf
import talib as ta
import datetime
# 変数定義
# 取得データリスト
stock_list = \
[
["3382.T"], # セブン&アイ
["4063.T"], # 信越化
["4502.T"], # 武田
["4503.T"], # アステラス
["4568.T"], # 第一三共
["6098.T"], # リクルート
["6273.T"], # SMC
["6367.T"], # ダイキン
["6501.T"], # 日立
["6594.T"], # ニデック
["6758.T"], # ソニーG
["6861.T"], # キーエンス
["6954.T"], # ファナック
["6981.T"], # 村田製
["7203.T"], # トヨタ
["7267.T"], # ホンダ
["7741.T"], # HOYA
["7974.T"], # 任天堂
["8001.T"], # 伊藤忠
["8031.T"], # 三井物
["8035.T"], # 東エレク
["8058.T"], # 三菱商
["8306.T"], # 三菱UFJ
["8316.T"], # 三井住友FG
["8411.T"], # みずほFG
["8766.T"], # 東京海上
["9432.T"], # NTT
["9433.T"], # KDDI
["9434.T"], # SB
["9984.T"] # SBG
]
# データ取得期間
start_date = datetime.datetime(2024, 1, 1)
end_date = datetime.datetime(2024, 9, 22)
# 標準偏差/売買高の平均
stddev = 0
volume_avg = 0
# 結果
result = []
bt = []
# 株価データ取得/売買ルールのための準備
def get_stockdata(stock_name):
ticker = yf.Ticker(stock_name)
data = yf.download(stock_name, start_date, end_date)
# 標準偏差を計算
stddev = ta.STDDEV(data['Adj Close'], timeperiod=25, nbdev=1)
stddev = stddev.dropna(how='all')
stddev = sum(stddev) / len(stddev)
# 売買高の平均を計算
volume_avg = data['Volume']
volume_avg = volume_avg.dropna(how='all')
volume_avg = sum(volume_avg) / len(volume_avg)
return ticker,data
# ボリンジャーバンドを使った売買ルールクラス
class BBsigma(Strategy):
timeperiod_num = 25
stddev_num = 2
def init(self):
# 上のボリンジャーバンド, 移動平均, 下のボリンジャーバンド = ta.BBANDS(調整済み終値, 日経平均日数, 上のラインの標準偏差, 下のラインの標準偏差, 移動平均の計算方法)インの標準偏差, ○○移動平均)
self.upper, self.middle, self.lower = self.I(BBANDS, self.data.Close, timeperiod=self.timeperiod_num, nbdevup=self.stddev_num, nbdevdn=self.stddev_num, matype=0)
def next(self): # チャートデータの行ごとに呼び出される
# ボリンジャーバンドを使って、売買ルールを作る
if crossover(self.data.Close, self.upper):
# 売買高が平均以上
if self.data.Volume > volume_avg:
# バンド幅:狭い かつ +2σより大きくなった場合は買い
if (self.middle + self.stddev_num * stddev) > self.upper:
self.position.close()
self.buy() # 買い
# バンド幅:広い かつ +2σより大きくなった場合は売り
elif (self.middle + self.stddev_num * stddev) < self.upper:
self.position.close()
self.sell() # 売り
if crossover(self.lower, self.data.Close):
# 売買高が平均以上
if self.data.Volume > volume_avg:
# バンド幅:狭い かつ -2σより小さくなった場合は売り
if (self.middle - self.stddev_num * stddev) < self.lower:
self.position.close()
self.sell() # 売り
# バンド幅:広い かつ -2σより小さくなった場合は買い
elif (self.middle - self.stddev_num * stddev) > self.lower:
self.position.close()
self.buy() # 買い
# データリストからデータを取得し、バックテスト実施
for i in range(len(stock_list)):
stock_name = stock_list[i][0]
ticker, data = get_stockdata(stock_name)
stock_list[i].append(ticker.info["shortName"])
# バックテストを設定。バックテストクラスを初期化してインスタンスを生成
bt_tmp = Backtest(
data, # チャートデータ
BBsigma, # 売買戦略
cash=1000000, # 最初の所持金
trade_on_close=True, # True:現在の終値で取引,False:次の時間の始値で取引
# exclusive_orders=True
)
# run()メソッドでバックテストを実行 → 結果をリストへ格納
result_tmp = bt_tmp.run()
result.append(result_tmp)
bt.append(bt_tmp)
結果確認
バックテストの結果 vs 持ちっぱなしの比較は11勝19敗とバックテストのやや負け越しとなった
※結果の Return
はバックテストでの利益率/ Buy & Hold Return
は持ちっぱなしの利益率を示している
# 結果の表示/比較
tech = 0
buyhold = 0
print("period = {} - {}\n".format(result[0][0], result[0][1]))
for i in range(len(stock_list)):
print("{} -------------------".format(stock_list[i][1]))
print("Return [%] = {}".format(result[i][6]))
print("Buy & Hold Return [%] = {}".format(result[i][7]))
print("Num of Trades = {}".format(result[i][17]))
print("--------------------------------------\n")
# テクニカル指標 or Buy & Hold(持ちっぱなし)のどちらがいい結果か?
if(result[i][6] > result[i][7]):
tech += 1
else:
buyhold += 1
# テクニカル指標 or Buy & Hold(持ちっぱなし)のトータル勝敗
print("Total Result ###################")
print("Technical Return = {}, Buy & Hold Return = {}".format(tech, buyhold))
period = 2024-01-04 00:00:00 - 2024-09-20 00:00:00
SEVEN & I HOLDINGS CO LTD -------------------
Return [%] = 10.366537280273437
Buy & Hold Return [%] = 14.340126476091225
Num of Trades = 9
--------------------------------------
SHIN-ETSU CHEMICAL CO -------------------
Return [%] = -4.0351
Buy & Hold Return [%] = 2.3930131004366815
Num of Trades = 12
--------------------------------------
TAKEDA PHARMACEUTICAL CO LTD -------------------
Return [%] = 9.4853
Buy & Hold Return [%] = 1.183431952662722
Num of Trades = 6
--------------------------------------
ASTELLAS PHARMA -------------------
Return [%] = -9.2705
Buy & Hold Return [%] = -0.6861063464837049
Num of Trades = 5
--------------------------------------
DAIICHI SANKYO COMPANY LIMITED -------------------
Return [%] = 11.5334
Buy & Hold Return [%] = 23.230309072781655
Num of Trades = 8
--------------------------------------
RECRUIT HOLDINGS CO LTD -------------------
Return [%] = 16.622899999999998
Buy & Hold Return [%] = 60.044912765589906
Num of Trades = 12
--------------------------------------
SMC CORP -------------------
Return [%] = 1.275
Buy & Hold Return [%] = -19.798435220793
Num of Trades = 9
--------------------------------------
DAIKIN INDUSTRIES -------------------
Return [%] = -17.6735
Buy & Hold Return [%] = -21.913043478260867
Num of Trades = 8
--------------------------------------
HITACHI -------------------
Return [%] = -9.7848
Buy & Hold Return [%] = 80.53097345132744
Num of Trades = 12
--------------------------------------
NIDEC CORPORATION -------------------
Return [%] = -18.2995
Buy & Hold Return [%] = 2.7088430618664803
Num of Trades = 6
--------------------------------------
SONY GROUP CORPORATION -------------------
Return [%] = 21.8405
Buy & Hold Return [%] = 2.0618556701030926
Num of Trades = 7
--------------------------------------
KEYENCE CORP -------------------
Return [%] = 30.218
Buy & Hold Return [%] = 13.74814325796336
Num of Trades = 9
--------------------------------------
FANUC CORPORATION -------------------
Return [%] = 14.871500000000001
Buy & Hold Return [%] = -4.180763653939101
Num of Trades = 10
--------------------------------------
MURATA MANUFACTURING CO -------------------
Return [%] = -24.5258
Buy & Hold Return [%] = -5.46767537826685
Num of Trades = 11
--------------------------------------
TOYOTA MOTOR CORP -------------------
Return [%] = -16.227800000000002
Buy & Hold Return [%] = -0.03795066413662239
Num of Trades = 7
--------------------------------------
HONDA MOTOR CO -------------------
Return [%] = 8.40985
Buy & Hold Return [%] = 3.9746158984635938
Num of Trades = 8
--------------------------------------
HOYA CORP -------------------
Return [%] = 30.771500000000003
Buy & Hold Return [%] = 16.122388937922917
Num of Trades = 9
--------------------------------------
NINTENDO CO LTD -------------------
Return [%] = 0.3338
Buy & Hold Return [%] = 6.884057971014493
Num of Trades = 7
--------------------------------------
ITOCHU CORP -------------------
Return [%] = 0.6439
Buy & Hold Return [%] = 31.528279181708786
Num of Trades = 6
--------------------------------------
MITSUI & CO -------------------
Return [%] = -35.25325
Buy & Hold Return [%] = 12.895467160037002
Num of Trades = 9
--------------------------------------
TOKYO ELECTRON -------------------
Return [%] = -7.667
Buy & Hold Return [%] = 0.9997917100604041
Num of Trades = 9
--------------------------------------
MITSUBISHI CORP -------------------
Return [%] = -16.08895
Buy & Hold Return [%] = 29.071038251366122
Num of Trades = 6
--------------------------------------
MITSUBISHI UFJ FINANCIAL GROUP -------------------
Return [%] = -7.7583
Buy & Hold Return [%] = 20.97959183673469
Num of Trades = 8
--------------------------------------
SUMITOMO MITSUI FINANCIAL GROUP -------------------
Return [%] = -26.1336
Buy & Hold Return [%] = 30.544412607449857
Num of Trades = 6
--------------------------------------
MIZUHO FINANCIAL GROUP -------------------
Return [%] = -15.10225
Buy & Hold Return [%] = 17.66741298595563
Num of Trades = 6
--------------------------------------
TOKIO MARINE HOLDINGS INC -------------------
Return [%] = -20.8246
Buy & Hold Return [%] = 49.87175833570818
Num of Trades = 11
--------------------------------------
NIPPON TEL & TEL CORP -------------------
Return [%] = -8.770042965698241
Buy & Hold Return [%] = -13.259985119367116
Num of Trades = 9
--------------------------------------
KDDI CORPORATION -------------------
Return [%] = 17.2773
Buy & Hold Return [%] = 5.65207829338025
Num of Trades = 7
--------------------------------------
SOFTBANK CORP. -------------------
Return [%] = 18.9373
Buy & Hold Return [%] = 10.85618354784555
Num of Trades = 7
--------------------------------------
SOFTBANK GROUP CORP -------------------
Return [%] = -3.2145
Buy & Hold Return [%] = 41.53719008264463
Num of Trades = 10
--------------------------------------
Total Result ###################
Technical Return = 11, Buy & Hold Return = 19
#result[0]["_equity_curve"]
ちなみに、結果をプロットすることもできる
# SEVEN & I HOLDINGSの結果をプロット
bt[0].plot()
パラメータを最適化してみる
ボリンジャーバンドのパラメータを以下のように変更してみて、パフォーマンスが良いものを調査する
- 移動平均線:5 - 70日(5日刻み。5,10,15日...と変更していく)
- 標準偏差:1σ - 3σ
ソースコード
from backtesting import Strategy
from talib import BBANDS
from backtesting.lib import crossover
from backtesting import Backtest
import yfinance as yf
import talib as ta
import datetime
# 変数定義
# 取得データリスト
stock_list = \
[
["3382.T"], # セブン&アイ
["4063.T"], # 信越化
["4502.T"], # 武田
["4503.T"], # アステラス
["4568.T"], # 第一三共
["6098.T"], # リクルート
["6273.T"], # SMC
["6367.T"], # ダイキン
["6501.T"], # 日立
["6594.T"], # ニデック
["6758.T"], # ソニーG
["6861.T"], # キーエンス
["6954.T"], # ファナック
["6981.T"], # 村田製
["7203.T"], # トヨタ
["7267.T"], # ホンダ
["7741.T"], # HOYA
["7974.T"], # 任天堂
["8001.T"], # 伊藤忠
["8031.T"], # 三井物
["8035.T"], # 東エレク
["8058.T"], # 三菱商
["8306.T"], # 三菱UFJ
["8316.T"], # 三井住友FG
["8411.T"], # みずほFG
["8766.T"], # 東京海上
["9432.T"], # NTT
["9433.T"], # KDDI
["9434.T"], # SB
["9984.T"] # SBG
]
# データ取得期間
start_date = datetime.datetime(2024, 1, 1)
end_date = datetime.datetime(2024, 9, 22)
# 標準偏差/売買高の平均
stddev = 0
volume_avg = 0
# 結果
result = []
bt = []
# 株価データ取得/売買ルールのための準備
def get_stockdata(stock_name):
ticker = yf.Ticker(stock_name)
data = yf.download(stock_name, start_date, end_date)
# 標準偏差を計算
stddev = ta.STDDEV(data['Adj Close'], timeperiod=25, nbdev=1)
stddev = stddev.dropna(how='all')
stddev = sum(stddev) / len(stddev)
# 売買高の平均を計算
volume_avg = data['Volume']
volume_avg = volume_avg.dropna(how='all')
volume_avg = sum(volume_avg) / len(volume_avg)
return ticker,data
# ボリンジャーバンドを使った売買ルールクラス
class BBsigma(Strategy):
timeperiod_num = 25
stddev_num = 2
def init(self):
# 上のボリンジャーバンド, 移動平均, 下のボリンジャーバンド = ta.BBANDS(調整済み終値, 日経平均日数, 上のラインの標準偏差, 下のラインの標準偏差, 移動平均の計算方法)インの標準偏差, ○○移動平均)
self.upper, self.middle, self.lower = self.I(BBANDS, self.data.Close, timeperiod=self.timeperiod_num, nbdevup=self.stddev_num, nbdevdn=self.stddev_num, matype=0)
def next(self): # チャートデータの行ごとに呼び出される
# ボリンジャーバンドを使って、売買ルールを作る
if crossover(self.data.Close, self.upper):
# 売買高が平均以上
if self.data.Volume > volume_avg:
# バンド幅:狭い かつ +2σより大きくなった場合は買い
if (self.middle + self.stddev_num * stddev) > self.upper:
self.position.close()
self.buy() # 買い
# バンド幅:広い かつ +2σより大きくなった場合は売り
elif (self.middle + self.stddev_num * stddev) < self.upper:
self.position.close()
self.sell() # 売り
if crossover(self.lower, self.data.Close):
# 売買高が平均以上
if self.data.Volume > volume_avg:
# バンド幅:狭い かつ -2σより小さくなった場合は売り
if (self.middle - self.stddev_num * stddev) < self.lower:
self.position.close()
self.sell() # 売り
# バンド幅:広い かつ -2σより小さくなった場合は買い
elif (self.middle - self.stddev_num * stddev) > self.lower:
self.position.close()
self.buy() # 買い
# データリストからデータを取得し、バックテスト実施
for i in range(len(stock_list)):
stock_name = stock_list[i][0]
ticker, data = get_stockdata(stock_name)
stock_list[i].append(ticker.info["shortName"])
# バックテストを設定。バックテストクラスを初期化してインスタンスを生成
bt_tmp = Backtest(
data, # チャートデータ
BBsigma, # 売買戦略
cash=1000000, # 最初の所持金
trade_on_close=True, # True:現在の終値で取引,False:次の時間の始値で取引
# exclusive_orders=True
)
# optimize()メソッドを実行して最適化
result_tmp = bt_tmp.optimize(
timeperiod_num=range(5, 75, 5), # 移動平均日数
stddev_num=range(1, 4, 1), # 標準偏差
maximize= 'Return [%]', # Returnを最大化する
)
# 結果をリストへ格納
result.append(result_tmp)
bt.append(bt_tmp)
結果確認
→ パラメータ最適化の結果、バックテストの結果 vs 持ちっぱなしの比較は27勝3敗と大きく勝ち越しできるようになった
※最適化されたパラメータはCondition
の内容から確認可能
Condition = BBsigma(timeperiod_num=10,stddev_num=1)
の場合は、移動平均線:10日/標準偏差:1σ になる
※結果の Return
はバックテストでの利益率/ Buy & Hold Return
は持ちっぱなしの利益率を示している
# 結果の表示/比較
tech = 0
buyhold = 0
print("period = {} - {}\n".format(result[0][0], result[0][1]))
for i in range(len(stock_list)):
print("{} -------------------".format(stock_list[i][1]))
print("Return [%] = {}".format(result[i][6]))
print("Buy & Hold Return [%] = {}".format(result[i][7]))
print("Num of Trades = {}".format(result[i][17]))
print("Condition = {}".format(result[i]["_strategy"]))
print("--------------------------------------\n")
# テクニカル指標 or Buy & Hold(持ちっぱなし)のどちらがいい結果か?
if(result[i][6] > result[i][7]):
tech += 1
else:
buyhold += 1
# テクニカル指標 or Buy & Hold(持ちっぱなし)のトータル勝敗
print("Total Result ###################")
print("Technical Return = {}, Buy & Hold Return = {}".format(tech, buyhold))
period = 2024-01-04 00:00:00 - 2024-09-20 00:00:00
SEVEN & I HOLDINGS CO LTD -------------------
Return [%] = 42.8526515625
Buy & Hold Return [%] = 14.340126476091225
Num of Trades = 26
Condition = BBsigma(timeperiod_num=10,stddev_num=1)
--------------------------------------
SHIN-ETSU CHEMICAL CO -------------------
Return [%] = 75.2737
Buy & Hold Return [%] = 2.3930131004366815
Num of Trades = 28
Condition = BBsigma(timeperiod_num=10,stddev_num=1)
--------------------------------------
TAKEDA PHARMACEUTICAL CO LTD -------------------
Return [%] = 35.9751
Buy & Hold Return [%] = 1.183431952662722
Num of Trades = 14
Condition = BBsigma(timeperiod_num=50,stddev_num=1)
--------------------------------------
ASTELLAS PHARMA -------------------
Return [%] = 31.371399999999998
Buy & Hold Return [%] = -0.6861063464837049
Num of Trades = 2
Condition = BBsigma(timeperiod_num=30,stddev_num=3)
--------------------------------------
DAIICHI SANKYO COMPANY LIMITED -------------------
Return [%] = 51.757600000000004
Buy & Hold Return [%] = 23.230309072781655
Num of Trades = 10
Condition = BBsigma(timeperiod_num=55,stddev_num=1)
--------------------------------------
RECRUIT HOLDINGS CO LTD -------------------
Return [%] = 44.813399999999994
Buy & Hold Return [%] = 60.044912765589906
Num of Trades = 9
Condition = BBsigma(timeperiod_num=35,stddev_num=2)
--------------------------------------
SMC CORP -------------------
Return [%] = 9.762
Buy & Hold Return [%] = -19.798435220793
Num of Trades = 10
Condition = BBsigma(timeperiod_num=20,stddev_num=2)
--------------------------------------
DAIKIN INDUSTRIES -------------------
Return [%] = 7.31
Buy & Hold Return [%] = -21.913043478260867
Num of Trades = 2
Condition = BBsigma(timeperiod_num=25,stddev_num=3)
--------------------------------------
HITACHI -------------------
Return [%] = 46.2783
Buy & Hold Return [%] = 80.53097345132744
Num of Trades = 3
Condition = BBsigma(timeperiod_num=60,stddev_num=1)
--------------------------------------
NIDEC CORPORATION -------------------
Return [%] = 34.058
Buy & Hold Return [%] = 2.7088430618664803
Num of Trades = 2
Condition = BBsigma(timeperiod_num=70,stddev_num=3)
--------------------------------------
SONY GROUP CORPORATION -------------------
Return [%] = 64.84100000000001
Buy & Hold Return [%] = 2.0618556701030926
Num of Trades = 19
Condition = BBsigma(timeperiod_num=20,stddev_num=1)
--------------------------------------
KEYENCE CORP -------------------
Return [%] = 62.248000000000005
Buy & Hold Return [%] = 13.74814325796336
Num of Trades = 2
Condition = BBsigma(timeperiod_num=35,stddev_num=3)
--------------------------------------
FANUC CORPORATION -------------------
Return [%] = 26.5008
Buy & Hold Return [%] = -4.180763653939101
Num of Trades = 15
Condition = BBsigma(timeperiod_num=45,stddev_num=1)
--------------------------------------
MURATA MANUFACTURING CO -------------------
Return [%] = 57.4
Buy & Hold Return [%] = -5.46767537826685
Num of Trades = 2
Condition = BBsigma(timeperiod_num=15,stddev_num=3)
--------------------------------------
TOYOTA MOTOR CORP -------------------
Return [%] = 53.7126
Buy & Hold Return [%] = -0.03795066413662239
Num of Trades = 2
Condition = BBsigma(timeperiod_num=20,stddev_num=3)
--------------------------------------
HONDA MOTOR CO -------------------
Return [%] = 23.8061
Buy & Hold Return [%] = 3.9746158984635938
Num of Trades = 6
Condition = BBsigma(timeperiod_num=40,stddev_num=2)
--------------------------------------
HOYA CORP -------------------
Return [%] = 75.233
Buy & Hold Return [%] = 16.122388937922917
Num of Trades = 10
Condition = BBsigma(timeperiod_num=20,stddev_num=2)
--------------------------------------
NINTENDO CO LTD -------------------
Return [%] = 72.7342
Buy & Hold Return [%] = 6.884057971014493
Num of Trades = 5
Condition = BBsigma(timeperiod_num=70,stddev_num=2)
--------------------------------------
ITOCHU CORP -------------------
Return [%] = 93.8463
Buy & Hold Return [%] = 31.528279181708786
Num of Trades = 4
Condition = BBsigma(timeperiod_num=30,stddev_num=3)
--------------------------------------
MITSUI & CO -------------------
Return [%] = 20.1515
Buy & Hold Return [%] = 12.895467160037002
Num of Trades = 1
Condition = BBsigma(timeperiod_num=15,stddev_num=3)
--------------------------------------
TOKYO ELECTRON -------------------
Return [%] = 31.03
Buy & Hold Return [%] = 0.9997917100604041
Num of Trades = 1
Condition = BBsigma(timeperiod_num=15,stddev_num=3)
--------------------------------------
MITSUBISHI CORP -------------------
Return [%] = 32.80165
Buy & Hold Return [%] = 29.071038251366122
Num of Trades = 2
Condition = BBsigma(timeperiod_num=15,stddev_num=3)
--------------------------------------
MITSUBISHI UFJ FINANCIAL GROUP -------------------
Return [%] = 52.01235
Buy & Hold Return [%] = 20.97959183673469
Num of Trades = 2
Condition = BBsigma(timeperiod_num=35,stddev_num=3)
--------------------------------------
SUMITOMO MITSUI FINANCIAL GROUP -------------------
Return [%] = 27.055
Buy & Hold Return [%] = 30.544412607449857
Num of Trades = 2
Condition = BBsigma(timeperiod_num=30,stddev_num=3)
--------------------------------------
MIZUHO FINANCIAL GROUP -------------------
Return [%] = 44.3695
Buy & Hold Return [%] = 17.66741298595563
Num of Trades = 3
Condition = BBsigma(timeperiod_num=55,stddev_num=3)
--------------------------------------
TOKIO MARINE HOLDINGS INC -------------------
Return [%] = 53.6467
Buy & Hold Return [%] = 49.87175833570818
Num of Trades = 4
Condition = BBsigma(timeperiod_num=70,stddev_num=2)
--------------------------------------
NIPPON TEL & TEL CORP -------------------
Return [%] = 28.788753268432615
Buy & Hold Return [%] = -13.259985119367116
Num of Trades = 5
Condition = BBsigma(timeperiod_num=60,stddev_num=1)
--------------------------------------
KDDI CORPORATION -------------------
Return [%] = 20.9369
Buy & Hold Return [%] = 5.65207829338025
Num of Trades = 9
Condition = BBsigma(timeperiod_num=20,stddev_num=2)
--------------------------------------
SOFTBANK CORP. -------------------
Return [%] = 34.06125
Buy & Hold Return [%] = 10.85618354784555
Num of Trades = 6
Condition = BBsigma(timeperiod_num=50,stddev_num=2)
--------------------------------------
SOFTBANK GROUP CORP -------------------
Return [%] = 64.9918
Buy & Hold Return [%] = 41.53719008264463
Num of Trades = 4
Condition = BBsigma(timeperiod_num=70,stddev_num=2)
--------------------------------------
Total Result ###################
Technical Return = 27, Buy & Hold Return = 3
結果のプロットも可能
# SEVEN & I HOLDINGSの結果をプロット
bt[0].plot()
まとめ
TOPIX Core30銘柄(2024/9/22時点)に対して、テクニカル指標を使ってバックテストの実施と条件の最適化を試してみる
とりあえずの目的は達成できた
ボリンジャーバンドを使ったバックテストとパラメータの最適化により、多くの銘柄で持ちっぱなしよりよい結果を得ることができた
今後は、以下のbacktesting.py
のドキュメントには機械学習を用いた方法がTutorialsとして載っているため、時間があればそちらも試してみたい
https://kernc.github.io/backtesting.py/doc/backtesting/
-
これだけpipでインストールできなかったため、https://note.com/memoran_yukiblog/n/n7bb18631e01f を参考にインストール ↩
-
「株で儲けられるのは買ったことを忘れた人だ」という話が有名だが、それもつまらないので、今回の調査をやってみた ↩