検証の全コード
第5回の部品(読み込み・列・for文・リターン計算)を、集計まで含めてつなげると、これで完成です。
import pandas as pd
import numpy as np
# TradingViewからエクスポートしたS&P500日足CSVを読む(第5・6回)
df = pd.read_csv("SP_SPX_1D.csv", parse_dates=["time"])
df = df.sort_values("time").reset_index(drop=True)
df["dom"] = df["time"].dt.day # 各行の日付(1〜31)
position = 0 # 0=ノーポジ, 1=ロング
entry_price = 0.0
returns = [] # 1回ごとの決済リターンをためる箱
for i in range(len(df)):
close = df.loc[i, "close"]
dom = df.loc[i, "dom"]
if position == 0 and dom >= 25:
position = 1
entry_price = close # その日の引けで買う
elif position == 1 and 3 <= dom <= 15:
returns.append(close / entry_price - 1) # その日の引けで決済
position = 0
returns = np.array(returns)
print(f"トレード数: {len(returns)}")
print(f"勝率: {(returns > 0).mean()*100:.1f}%")
print(f"平均リターン: {returns.mean()*100:.2f}%")
print(f"累積リターン: {((1 + returns).prod() - 1)*100:.1f}%")
1行ずつ読む
- 読み込みと下ごしらえ:CSVを読み、日付順に並べ、
dom(日付)列を作ります(第5回)。 position:今ロングを持っているかを 0/1 で覚えておく変数。MQL5のPositionSelect、Pineのposition_sizeにあたる役割を、自分で用意します。returns = []:決済のたびのリターンをためる空のリスト。MT5・TradingViewが自動でやっていた集計を、自分で持ちます。- 買い:ノーポジ(
position == 0)かつ 日付25以上なら、positionを1にして買値を記録。 - 決済:保有中(
position == 1)かつ 日付3〜15なら、close / entry_price - 1でリターンを求めてリストに足し、positionを0に戻します。 - 集計:最後に、トレード数・勝率・平均・累積を計算して表示します。
3 <= dom <= 15 は、Pythonでは「3以上かつ15以下」をこのままひとつなぎで書けます(MQL5の dom >= 3 && dom <= 15 と同じ意味)。
出力される結果
実行すると、次のような集計が表示されます(数字はデータ期間により変わります)。
トレード数: 120
勝率: 61.7%
平均リターン: 0.63%
累積リターン: 108.4%
これらはS&P500の値動きに、上のルールを機械的に当てはめた集計です。良い戦略という意味ではなく、実装が動いていることの確認です。第3回のとおり、成績の解釈には「何通り試したか」の視点が別に要ります。
なお、この最小限のコードは手数料・スプレッドを含めていません。より現実に近づけるなら、決済リターンから毎回一定のコスト(例:0.02%)を引きます。MT5やTradingViewのテスターでスプレッドを設定したのと同じ調整を、Pythonでは1行足して行います。
MQL5・Pineとの違い
これで3環境がそろいました。同じルールが、環境ごとにどう違う姿になったかを整理します。
- 集計を自分で書く:MT5・TradingViewはテスターが損益を出しましたが、Pythonは
returnsリストと最後の計算を自分で持ちます。 - データを自分で用意する:第6回のとおり、PythonだけCSVを読み込みます。どのS&P500(現物・先物・ETF・CFD)を使ったかで結果が変わります。
- 約定は「引け」で計算:このコードは、判定したその足の引けで約定したと仮定しています。第7回・第8回(前の足で判定→次の足の頭で約定)とは、約定の瞬間が1本ぶんずれます。
Pythonで間違えやすい点
Pythonは全部を自分で書くぶん、間違いも入りやすいです。代表的なのは2つ。
① 先読み:うっかり df.loc[i+1, "close"](次の行の価格)で決済してしまうと、未来の値を使うことになり、成績が不当に良くなります。使うのは今の行 i の値だけ、が原則です。
② 並び順:CSVが新しい順に入っていることがあります。sort_values("time") で古い順に直しておかないと、月末判定が逆走します。
きれいな数字が出ても、まず print で途中の売買と日付を数件表示し、意図どおりか目で確かめます。第1回からの姿勢は変わりません。
チェックリスト
次の記事に進む前に、以下が自分の言葉で説明できるか確認してください。
position変数が、MQL5・Pineのポジション判定に当たることreturnsリストで集計を自分で持つこと- リターンは
close / entry_price - 1で求めること - このコードは「その足の引けで約定」と仮定していること
- 手数料を含めるには決済リターンからコストを引くこと
- 先読み(次の行の使用)と並び順のミスに注意すること
ミニテスト
最後に3問だけ確認です。不正解なら別の選択肢を選び直せます。正解すると解説が表示されます。
Q1. Python版が3環境で一番長くなる理由は?
ルールは同じです。MT5・TradingViewが自動でやっていたデータ供給と損益集計を、Pythonでは自分で書くぶん長くなります。
Q2. 決済リターンを求める式として正しいのは?
「決済価格 ÷ 買った価格 − 1」で、+0.02なら+2%です(第5回)。差や和ではなく比で求めます。
Q3. Pythonで先読みになってしまう書き方は?
次の行 i+1 はまだ来ていない未来の値です。それで決済すると成績が不当に良くなります。使うのは今の行 i の値だけが原則です。