vectorbt-expert について
vectorbt-expertは、VectorBTによるバックテストの開発と確認を支援するリファレンススキルです。クオンツ調査向けに、データ読み込み、インジケーター、コスト、リスク分析、可視化の規則と戦略テンプレートをまとめています。
主な機能
- 01データソースと読み込み規則を整理
- 02既定の指標ライブラリとしてOpenAlgo taを使用
- 03ポジションサイズ、コスト、損切り規則を網羅
- 04ベンチマーク比較と堅牢性分析に対応
- 05戦略テンプレートとチャート規則を提供
活用シーン
- 01市場ごとのデータ取得方法を計画
- 02指標の選択と売買シグナルの整理
- 03バックテストの手数料とポジション設定を確認
- 04ベンチマーク比較や堅牢性テストを実施
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ marketcalls/ vectorbt-backtesting-skills --skill vectorbt-expertインストール後、Claude Code で/vectorbt-expertを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
rules/crypto-market-costs.mdcsv-data-resampling.mddata-fetching.mdduckdb-data.mdfutures-backtesting.mdindian-market-costs.mdindicators-signals.mdlong-short-trading.mdopenalgo-ta-helpers.mdopenstatz-tearsheet.mdparameter-optimization.mdperformance-analysis.mdpitfalls.mdplotting.mdposition-sizing.mdrobustness-testing.mdsimulation-modes.mdstop-loss-take-profit.mdrules/assets/buy_hold/backtest.pyrules/assets/donchian/backtest.pyrules/assets/dual_momentum/backtest.pyrules/assets/ema_crossover/backtest.pyrules/assets/macd/backtest.pyrules/assets/momentum/backtest.pyrules/assets/realistic_costs/template.pyrules/assets/rsi_accumulation/backtest.pyrules/assets/rsi/backtest.pyrules/assets/sda2/backtest.pyrules/assets/supertrend/backtest.pyrules/assets/walk_forward/template.py詳細
VectorBT バックテストエキスパートスキル
環境
- vectorbt、pandas、numpy、plotly を使用する Python
- データソース:OpenAlgo(インド市場)、DuckDB(データベースに直接接続)、yfinance(米国/グローバル)、CCXT(暗号資産)、カスタムプロバイダー
- DuckDB のサポート:カスタム DuckDB と OpenAlgo Historify 形式の両方に対応
- API キーは
python-dotenvとfind_dotenv()を使って単一のルート.envから読み込みます。キーをハードコードしないでください - テクニカル指標:OpenAlgo ta(デフォルト。
from openalgo import ta。トレンド/モメンタム/ボラティリティ/出来高/オシレーター/統計/ハイブリッドを網羅する 100 以上の指標)。TA-Lib を使うのは、ユーザーが TA-Lib/talib を明示的に指定した場合のみです。どちらの場合も、VectorBT の組み込み指標は絶対に使わないでください - 特殊な指標(TA-Lib に相当するものがなく、常に
openalgo.taを使用):Supertrend、Donchian、Ichimoku、HMA、KAMA、ALMA、ZLEMA、VWMA - シグナルのクリーニング:
openalgo.taの exrem、crossover、crossunder、flip を使用します(指標ライブラリに関係なく常に使用) - 手数料モデル:インド市場標準(STT + 法定諸費用 + 1 注文あたり Rs 20)
- ベンチマーク:デフォルトでは OpenAlgo 経由の NIFTY 50(
NSE_INDEX) - チャート:
template="plotly_dark"を指定した Plotly - 環境変数は、
find_dotenv()(スクリプトのディレクトリから上位へ検索)を使ってプロジェクトルートの単一.envから読み込みます - スクリプトは
backtesting/{strategy_name}/ディレクトリに配置します(事前に作成せず、必要に応じて作成) - コードやロガーの出力にアイコンや絵文字を使わないでください
重要なルール
- すべてのテクニカル指標(EMA、SMA、RSI、MACD、BBANDS、ATR、ADX、STDDEV、MOM など 90 種類以上)には、デフォルトで OpenAlgo ta(
from openalgo import ta)を使用します。プロンプトでユーザーが「talib」/「TA-Lib」を明示的に指定した場合に限り、TA-Lib を使用してください。どちらのライブラリを使う場合も、vbt.MA.run()、vbt.RSI.run()、その他の VectorBT 組み込み指標は絶対に使わないでください。 - TA-Lib に存在しない指標には、常に OpenAlgo ta を使用します:Supertrend、Donchian、Ichimoku、HMA、KAMA、ALMA、ZLEMA、VWMA。これらには TA-Lib に相当するものがないため、TA-Lib を明示的に使用するスクリプトでも
openalgo.taを使います。 - シグナルユーティリティには OpenAlgo ta を使用します:
ta.exrem()、ta.crossover()、ta.crossunder()、ta.flip()。openalgo.taをインポートできない場合(単独の DuckDB 使用時)は、インラインのexrem()フォールバックを使用します。duckdb-data を参照してください。 - 生の買い/売りシグナルを生成した後は、必ず
ta.exrem()でシグナルをクリーニングしてください。exrem の前に必ず.fillna(False)を適用してください。 - 市場ごとに手数料を設定します:インド(indian-market-costs)、米国(us-market-costs)、暗号資産(crypto-market-costs)。ユーザーの市場に基づいて自動選択してください。
- デフォルトのベンチマーク:インド=OpenAlgo 経由の NIFTY、米国=S&P 500(
^GSPC)、暗号資産=Bitcoin(BTC-USD)。data-fetching の「市場選択ガイド」を参照してください。 - バックテストの後は必ず、戦略とベンチマークの比較表を作成してください。
- バックテストレポートについて、一般のトレーダーにもリスクと強みが伝わるよう、平易な言葉で必ず説明してください。
- Plotly のローソク足チャートでは、週末による間隔を避けるため、
xaxis type="category"を必ず使用してください。 - 株式は整数株:株式には必ず
min_size=1, size_granularity=1を設定してください。 - DuckDB のデータ読み込み:ユーザーが DuckDB のパスを指定した場合は、
duckdb.connect()にread_only=Trueを指定してデータを直接読み込みます。形式を自動検出してください:OpenAlgo Historify(テーブルmarket_data、エポックタイムスタンプ)またはカスタム形式(テーブルohlcv、date 列と time 列)。duckdb-data を参照してください。
モジュール式ルールファイル
各トピックの詳細なリファレンスは rules/ にあります。
| ルールファイル | トピック |
|---|---|
| data-fetching | OpenAlgo(インド)、yfinance(米国)、CCXT(暗号資産)、カスタムプロバイダー、.env の設定 |
| simulation-modes | from_signals、from_orders、from_holding、方向の種類 |
| position-sizing | 金額/価値/割合/目標割合によるサイズ設定 |
| indicators-signals | OpenAlgo ta インジケーターのリファレンス(デフォルト)、TA-Lib のオプトイン、シグナル生成 |
| openalgo-ta-helpers | OpenAlgo ta の完全なカタログ(100 以上のインジケーター):exrem、crossover、Supertrend、Donchian、Ichimoku、MA |
| stop-loss-take-profit | 固定 SL、TP、トレーリングストップ |
| parameter-optimization | ブロードキャストとループベースの最適化 |
| performance-analysis | 統計、指標、ベンチマークとの比較、CAGR |
| plotting | ローソク足(カテゴリ軸)、VectorBT のプロット、カスタム Plotly |
| indian-market-costs | セグメント別のインド市場手数料モデル |
| us-market-costs | 米国市場の手数料モデル(株式、オプション、先物) |
| crypto-market-costs | 暗号資産の手数料モデル(現物、USDT-M、COIN-M 先物) |
| futures-backtesting | ロットサイズ(SEBI が 2025 年 12 月に改定)、価値によるサイズ設定 |
| long-short-trading | ロング/ショートの同時取引、方向の比較 |
| duckdb-data | DuckDB からの直接読み込み、Historify 形式、自動検出、リサンプリング、複数銘柄 |
| csv-data-resampling | CSV の読み込み、インド市場に合わせたリサンプリング |
| walk-forward | ウォークフォワード分析、WFE 比率 |
| robustness-testing | モンテカルロ、ノイズテスト、パラメーター感度、遅延テスト |
| pitfalls | よくある間違いと実運用開始前のチェックリスト |
| strategy-catalog | コードスニペット付きの戦略リファレンス |
| openstatz-tearsheet | OpenStatz のインタラクティブなオフラインダッシュボード、指標、モンテカルロ(QuantStats に代わるもの) |
戦略テンプレート(rules/assets/ 内)
現実的な手数料、NIFTY ベンチマーク、比較表、平易な言葉によるレポートを含む、本番環境向けのスクリプトです。
| テンプレート | パス | 説明 |
|---|---|---|
| EMA クロスオーバー | assets/ema_crossover/backtest.py | EMA 10/20 のクロスオーバー |
| RSI | assets/rsi/backtest.py | RSI(14) の売られ過ぎ/買われ過ぎ |
| Donchian | assets/donchian/backtest.py | Donchian チャネルブレイクアウト |
| Supertrend | assets/supertrend/backtest.py | 日中セッション対応の Supertrend |
| MACD | assets/macd/backtest.py | MACD シグナルローソク足のブレイクアウト |
| SDA2 | assets/sda2/backtest.py | SDA2 トレンドフォロー |
| モメンタム | assets/momentum/backtest.py | ダブルモメンタム(MOM + MOM-of-MOM) |
| デュアルモメンタム | assets/dual_momentum/backtest.py | 四半期ごとの ETF ローテーション |
| バイ・アンド・ホールド | assets/buy_hold/backtest.py | 静的なマルチアセット配分 |
| RSI 積み立て | assets/rsi_accumulation/backtest.py | RSI の区分に応じた週次の積み立て |
| ウォークフォワード | assets/walk_forward/template.py | ウォークフォワード分析テンプレート |
| 現実的なコスト | assets/realistic_costs/template.py | 取引コストの影響を比較 |
クイックテンプレート:標準的なバックテストスクリプト
import os
from datetime import datetime, timedelta
from pathlib import Path
import numpy as np
import pandas as pd
import vectorbt as vbt
from dotenv import find_dotenv, load_dotenv
from openalgo import api, ta
# --- Config ---
script_dir = Path(__file__).resolve().parent
load_dotenv(find_dotenv(), override=False)
SYMBOL = "SBIN"
EXCHANGE = "NSE"
INTERVAL = "D"
INIT_CASH = 1_000_000
FEES = 0.00111 # Indian delivery equity (STT + statutory)
FIXED_FEES = 20 # Rs 20 per order
ALLOCATION = 0.75
BENCHMARK_SYMBOL = "NIFTY"
BENCHMARK_EXCHANGE = "NSE_INDEX"
# --- Fetch Data ---
client = api(
api_key=os.getenv("OPENALGO_API_KEY"),
host=os.getenv("OPENALGO_HOST", "http://127.0.0.1:5000"),
)
end_date = datetime.now().date()
start_date = end_date - timedelta(days=365 * 3)
df = client.history(
symbol=SYMBOL, exchange=EXCHANGE, interval=INTERVAL,
start_date=start_date.strftime("%Y-%m-%d"),
end_date=end_date.strftime("%Y-%m-%d"),
)
if "timestamp" in df.columns:
df["timestamp"] = pd.to_datetime(df["timestamp"])
df = df.set_index("timestamp")
else:
df.index = pd.to_datetime(df.index)
df = df.sort_index()
if df.index.tz is not None:
df.index = df.index.tz_convert(None)
close = df["close"]
# --- Strategy: EMA Crossover (OpenAlgo ta - default indicator library) ---
ema_fast = ta.ema(close, 10)
ema_slow = ta.ema(close, 20)
buy_raw = (ema_fast > ema_slow) & (ema_fast.shift(1) <= ema_slow.shift(1))
sell_raw = (ema_fast < ema_slow) & (ema_fast.shift(1) >= ema_slow.shift(1))
entries = ta.exrem(buy_raw.fillna(False), sell_raw.fillna(False))
exits = ta.exrem(sell_raw.fillna(False), buy_raw.fillna(False))
# --- Backtest ---
pf = vbt.Portfolio.from_signals(
close, entries, exits,
init_cash=INIT_CASH, size=ALLOCATION, size_type="percent",
fees=FEES, fixed_fees=FIXED_FEES, direction="longonly",
min_size=1, size_granularity=1, freq="1D",
)
# --- Benchmark ---
df_bench = client.history(
symbol=BENCHMARK_SYMBOL, exchange=BENCHMARK_EXCHANGE, interval=INTERVAL,
start_date=start_date.strftime("%Y-%m-%d"),
end_date=end_date.strftime("%Y-%m-%d"),
)
if "timestamp" in df_bench.columns:
df_bench["timestamp"] = pd.to_datetime(df_bench["timestamp"])
df_bench = df_bench.set_index("timestamp")
else:
df_bench.index = pd.to_datetime(df_bench.index)
df_bench = df_bench.sort_index()
if df_bench.index.tz is not None:
df_bench.index = df_bench.index.tz_convert(None)
bench_close = df_bench["close"].reindex(close.index).ffill().bfill()
pf_bench = vbt.Portfolio.from_holding(bench_close, init_cash=INIT_CASH, fees=FEES, freq="1D")
# --- Results ---
print(pf.stats())
# --- Strategy vs Benchmark ---
comparison = pd.DataFrame({
"Strategy": [
f"{pf.total_return() * 100:.2f}%", f"{pf.sharpe_ratio():.2f}",
f"{pf.sortino_ratio():.2f}", f"{pf.max_drawdown() * 100:.2f}%",
f"{pf.trades.win_rate() * 100:.1f}%", f"{pf.trades.count()}",
f"{pf.trades.profit_factor():.2f}",
],
f"Benchmark ({BENCHMARK_SYMBOL})": [
f"{pf_bench.total_return() * 100:.2f}%", f"{pf_bench.sharpe_ratio():.2f}",
f"{pf_bench.sortino_ratio():.2f}", f"{pf_bench.max_drawdown() * 100:.2f}%",
"-", "-", "-",
],
}, index=["Total Return", "Sharpe Ratio", "Sortino Ratio", "Max Drawdown",
"Win Rate", "Total Trades", "Profit Factor"])
print(comparison.to_string())
# --- Explain ---
print(f"* Total Return: {pf.total_return() * 100:.2f}% vs NIFTY {pf_bench.total_return() * 100:.2f}%")
print(f"* Max Drawdown: {pf.max_drawdown() * 100:.2f}%")
print(f" -> On Rs {INIT_CASH:,}, worst temporary loss = Rs {abs(pf.max_drawdown()) * INIT_CASH:,.0f}")
# --- Plot ---
fig = pf.plot(subplots=['value', 'underwater', 'cum_returns'], template="plotly_dark")
fig.show()
# --- Export ---
pf.positions.records_readable.to_csv(script_dir / f"{SYMBOL}_trades.csv", index=False)
クイックテンプレート:DuckDB バックテストスクリプト
import datetime as dt
from pathlib import Path
import duckdb
import numpy as np
import pandas as pd
import vectorbt as vbt
try:
# Default: OpenAlgo ta for both indicators and signal cleaning
from openalgo import ta
exrem = ta.exrem
ema = ta.ema
except ImportError:
# Fallback ONLY when the openalgo package itself is not installed
# (standalone DuckDB with no OpenAlgo). Use TA-Lib for indicators
# and this inline exrem() replacement for signal cleaning.
import talib as tl
def ema(data, period):
return pd.Series(tl.EMA(data.values, timeperiod=period), index=data.index)
def exrem(signal1, signal2):
result = signal1.copy()
active = False
for i in range(len(signal1)):
if active:
result.iloc[i] = False
if signal1.iloc[i] and not active:
active = True
if signal2.iloc[i]:
active = False
return result
# --- Config ---
SYMBOL = "SBIN"
DB_PATH = r"path/to/market_data.duckdb"
INIT_CASH = 1_000_000
FEES = 0.000225 # Intraday equity
FIXED_FEES = 20
# --- Load from DuckDB ---
con = duckdb.connect(DB_PATH, read_only=True)
df = con.execute("""
SELECT date, time, open, high, low, close, volume
FROM ohlcv WHERE symbol = ? ORDER BY date, time
""", [SYMBOL]).fetchdf()
con.close()
df["datetime"] = pd.to_datetime(df["date"].astype(str) + " " + df["time"].astype(str))
df = df.set_index("datetime").sort_index()
df = df.drop(columns=["date", "time"])
# --- Resample to 5min ---
df_5m = df.resample("5min", origin="start_day", offset="9h15min",
label="right", closed="right").agg({
"open": "first", "high": "max", "low": "min", "close": "last", "volume": "sum"
}).dropna()
close = df_5m["close"]
# --- Strategy + Backtest (same as OpenAlgo template, but use the ema()/exrem() resolved above) ---
ユーザーが TA-Lib を明示的に指定した場合は、上記の try/except を省略し、代わりに import talib as tl を直接実行してください。exrem のフォールバックは、openalgo 自体を利用できない場合に限ります。
コメント
Markdown 対応