ビットコインの「値動きの勢い」作戦! うまくいった? 徹底チェック
この作戦は、ビットコインの値段が激しく動く「勢い」をヒントに、売り買いのタイミングをはかる方法です。1時間ごとの値動きで試してみたら、勝てたのは10回のうち2回くらい(勝率 約18%)でした。この結果から何がわかるか、一緒に見ていきましょう!
導入と前提条件
この作戦は、ビットコインの値段が激しく動く「勢い」をヒントに、売り買いのタイミングをはかる方法です。1時間ごとの値動きで試してみたら、勝てたのは10回のうち2回くらい(勝率 約18%)でした。この結果から何がわかるか、一緒に見ていきましょう!
【検証】戦略のバックテスト概要
- 戦略名: Volatility System を使用したトレンド追従戦略
- 対象銘柄: BTC/USDT
- 時間足: 1h
- 期間: 2025-04-28〜2025-08-26(119日間)
- 初期資金: $10,000
- 手数料・スリッページ: 0.1% / 0.1%
- 取引所: binance
Volatility System の理論的背景
この作戦は、値段の動きの「勢い」に注目します。勢いが強い時は、値段が大きく動くチャンスです。逆に、動きがおだやかな時は、大きな変化は期待しにくいと考えます。この作戦では、最近の勢いと少し前の勢いを比べて、その差が決められたラインをこえたら「チャンスが来た!」と判断します。そして、値段が平均のラインより上なら買い、下なら売る、というルールで取引します。
具体的な売買ルール(今回の検証)
エントリー条件
- 値段の勢いが強くなって、今の値段が平均ラインより上にある時に「買う」
- 値段の勢いが強くなって、今の値段が平均ラインより下にある時に「売る」
エグジット条件
- 「買い」で持っている時に、値段の勢いが弱くなったら「売って手じまい」する
- 「売り」で持っている時に、値段の勢いが弱くなったら「買い戻して手じまい」する
リスク管理
大損しないためのルールも大事です。この作戦では、お金が一番減ってしまった時、元のお金の44%近くまで減ってしまいました。これは結構大きな数字です。なので、一回の取引で使うお金を少なく調整することが、とても大切になります。
再現手順(HowTo)
- Python/依存(ccxt, pandas, ta)をインストール
- ccxtでBTC/USDTのOHLCVを取得して前処理
- 『Volatility System』に必要な指標を算出(ta 等)
- 閾値・クロス条件から売買シグナルを生成
- 手数料・スリッページを加味して検証・評価
【結果】パフォーマンス
価格の推移
資産の推移
パフォーマンス指標
指標 | 値 |
---|---|
総トレード数 | 141回 |
勝率 | 17.73% |
平均利益 | 1.51% |
平均損失 | -0.79% |
期待値 | -0.38% |
プロフィットファクター | 0.4 |
最大ドローダウン | 43.52% |
最終リターン | -42.21% |
シャープレシオ | -1.16 |
HODL(Buy&Hold) | 16.56% |
HODL戦略との比較
実装コード(Python)
"""
Volatility System Trading Signal
ボラティリティ拡大・縮小を利用した戦略
"""
import pandas as pd
import numpy as np
def calculate_volatility_system_signals(df: pd.DataFrame,
short_period: int = 6,
long_period: int = 100,
threshold: float = 0.5) -> pd.DataFrame:
"""
Volatility System戦略のシグナル生成
Parameters:
-----------
df : pd.DataFrame
OHLCVデータ
short_period : int
短期ボラティリティ期間(デフォルト: 6)
long_period : int
長期ボラティリティ期間(デフォルト: 100)
threshold : float
ボラティリティ比率閾値(デフォルト: 0.5)
Returns:
--------
pd.DataFrame
シグナルが追加されたDataFrame
"""
df = df.copy()
# Historical Volatility計算
df['returns'] = np.log(df['close'] / df['close'].shift(1))
df['hv_short'] = df['returns'].rolling(window=short_period).std() * np.sqrt(252)
df['hv_long'] = df['returns'].rolling(window=long_period).std() * np.sqrt(252)
# ボラティリティ比率
df['vol_ratio'] = df['hv_short'] / df['hv_long']
# Volatility Cone(ボラティリティの分布)
percentiles = [10, 25, 50, 75, 90]
for p in percentiles:
df[f'vol_p{p}'] = df['hv_long'].rolling(window=252).quantile(p/100)
# 現在のボラティリティ位置
df['vol_rank'] = df['hv_short'].rolling(window=252).rank(pct=True)
# ATRベースのボラティリティ
df['h_l'] = df['high'] - df['low']
df['h_c'] = np.abs(df['high'] - df['close'].shift(1))
df['l_c'] = np.abs(df['low'] - df['close'].shift(1))
df['true_range'] = df[['h_l', 'h_c', 'l_c']].max(axis=1)
df['atr'] = df['true_range'].rolling(window=short_period).mean()
df['atr_long'] = df['true_range'].rolling(window=long_period).mean()
df['atr_ratio'] = df['atr'] / df['atr_long']
# ボリンジャーバンドによるスクイーズ検出
df['bb_middle'] = df['close'].rolling(window=20).mean()
df['bb_std'] = df['close'].rolling(window=20).std()
df['bb_upper'] = df['bb_middle'] + 2 * df['bb_std']
df['bb_lower'] = df['bb_middle'] - 2 * df['bb_std']
df['bb_width'] = df['bb_upper'] - df['bb_lower']
df['bb_width_ma'] = df['bb_width'].rolling(window=long_period).mean()
df['squeeze'] = df['bb_width'] < df['bb_width_ma'] * threshold
# シグナル初期化
df['signal'] = 0
df['is_buy'] = False
df['is_sell'] = False
# 現在のポジション状態を追跡
position = 0
for i in range(long_period, len(df)):
# 買いシグナル(ボラティリティ拡大+価格上昇)
if position <= 0 and df['vol_ratio'].iloc[i] > threshold and df['close'].iloc[i] > df['bb_middle'].iloc[i]:
df.loc[df.index[i], 'is_buy'] = True
df.loc[df.index[i], 'signal'] = 1
position = 1
# 売りシグナル(ボラティリティ拡大+価格下落)
elif position >= 0 and df['vol_ratio'].iloc[i] > threshold and df['close'].iloc[i] < df['bb_middle'].iloc[i]:
df.loc[df.index[i], 'is_sell'] = True
df.loc[df.index[i], 'signal'] = -1
position = -1
else:
# ポジション維持
df.loc[df.index[i], 'signal'] = position
# NaN値を0で埋める
df['signal'] = df['signal'].fillna(0)
# エグジットシグナル
df['exit_signal'] = 0
# ボラティリティ縮小でエグジット
df.loc[(df['signal'] == 1) & (df['vol_ratio'] < threshold), 'exit_signal'] = -1
df.loc[(df['signal'] == -1) & (df['vol_ratio'] < threshold), 'exit_signal'] = 1
return df
def get_strategy_name() -> str:
"""戦略名を返す"""
return "Volatility System"
def get_strategy_description() -> str:
"""戦略の説明を返す"""
return "ボラティリティの拡大・縮小サイクルを利用した戦略"
なぜこの結果になったのか(3つの理由)
- 1勝てたのが10回のうち2回くらい(勝率 約18%)と低かったのは、「これから動きが激しくなるぞ!」と思ったのに、すぐに動きがおとなしくなってしまったことが多かったからみたいです。
- 2最終的に損してしまったのは、勝った時の利益よりも、負けた時の損失のほうが、少しだけ大きかったことが積み重なったからです。
- 3「もうけの合計」と「そんの合計」を比べると、そんの合計のほうが大きかったので、全体としてマイナスになってしまいました。
この結果から学べる3つの教訓
- 1値段の勢いが強くなった!というだけでは、カンタンには勝てないことがわかりました。
- 2勝つ回数が少なくても、一回で大きく勝てばプラスになることもありますが、この作戦ではうまくいきませんでした。
- 3昔のデータでうまくいかないなら、やり方を変えたり、もっと研究したりする必要がある、ということがわかりました。
リスク管理の具体的手法
取引量の決め方
「1回の取引で使うお金の決め方」です。例えば、100万円持っていたら、1回の取引で損してもいいのは2万円まで、というように、失っても大丈夫な金額を決めておきます。
損失が大きくなったときの対処法
「もし負けが続いたらどうするか」です。もしお財布の中身がどんどん減ってきたら、いったん取引をお休みしたり、使うお金をさらに減らしたりして、大きな損を防ぎます。この作戦では特に大事なことです。
資金管理の方法
「お金の上手な管理方法」です。勝って増えたお金はすぐには使わず、次への準備のためにとっておきます。負けてしまった時も、あわてて取り返そうとせず、落ち着いてお財布を管理します。
改良案の具体的提案
- やめる時のルールに、「値段の勢いが弱まったら」だけじゃなく、「値段が平均ラインまで戻ってきたら」というのを追加してみる。
- 「勢い」だけでなく、他のヒント(例えば「買われすぎ」「売られすぎ」を示す道具)も組み合わせて、もっと正確に判断できるようにする。
- 作戦の細かい設定を、その時のビットコインの状況に合わせて、自動で変えてくれる仕組みを考えてみる。
実用性の向上(運用上の注意)
- この作戦は、ビットコインの値段が活発に動いている時にはチャンスがありますが、逆に大きく損する可能性もあります。まずは、なくなっても困らない少額で試してみるのがおすすめです。
- 今回うまくいかなかったのは、ある期間のデータだからです。未来も同じ結果になるとは限りません。いつも今の状況をチェックすることが大切です。
- 1時間ごとのデータで試しましたが、1日ごとや5分ごとなど、見る時間を変えたり、ビットコイン以外のコインで試したりすると、違う結果になるかもしれません。
検証の透明性と信頼性
- データの出所: 昔のビットコインの値段のデータ(始まりの値段、一番高かった値段、一番安かった値段、終わりの値段)を使いました。
- 検証のやり方: 公開されている計算プログラムを使って、決まった期間のデータで「もしこの作戦を使っていたらどうなっていたか」というお試し計算(シミュレーション)をしました。
- コード: このシミュレーションに使った計算プログラム(Pythonコード)は、誰でも見ることができます。
- 注意事項: このお話は、昔のデータにもとづいたもので、将来も同じようにうまくいくことを約束するものではありません。投資は、ご自身の判断と責任で行ってください。特にこの作戦は、大きく損をする可能性もあるので、十分に気をつけてください。