ビットコインの値段が動くヒント!「買いたい人」と「売りたい人」の多さで未来を予測しよう
この作戦は、ビットコインを「買いたい人」と「売りたい人」のどっちが多いかを見て、これから値段が上がるか下がるかを予想する方法です。とってもカンタンな考え方なので、中学生のみなさんにも分かるように説明しますね。
導入と前提条件
この作戦は、ビットコインを「買いたい人」と「売りたい人」のどっちが多いかを見て、これから値段が上がるか下がるかを予想する方法です。とってもカンタンな考え方なので、中学生のみなさんにも分かるように説明しますね。
【検証】戦略のバックテスト概要
- 戦略名: Order Book Imbalance を使用したトレンド追従戦略
- 対象銘柄: BTC/USDT
- 時間足: 1h
- 期間: 2025-05-08〜2025-09-05(119日間)
- 初期資金: $10,000
- 手数料・スリッページ: 0.1% / 0.1%
- 取引所: binance
Order Book Imbalance の理論的背景
お店で人気のゲームソフトを想像してみてください。買いたい人がたくさんお店に並んでいるのに、お店には少ししかソフトがないと、値段は上がりやすくなりますよね。逆に、誰も欲しがらないソフトがたくさんお店にあったら、値段は下がっていきます。ビットコインの取引所でも同じようなことが起きています。この「買いたい人と売りたい人の数の差」が、値段が動く大きなヒントになる、というのがこの作戦の基本の考え方です。
具体的な売買ルール(今回の検証)
エントリー条件
- 「買いたい!」という注文のパワーが、「売りたい!」という注文のパワーを、決めておいた基準よりも大きく上回ったとき。
- そして、値段がちょうど上がり始めていて、勢いがあるとき。
エグジット条件
- タイミング1:目標の利益が出たとき。あらかじめ「ここまで儲かったら売る」と決めておいた値段になったら、取引をやめます。
- タイミング2:損が大きくなりそうなとき。「これ以上損したらやめる」と決めておいた値段になったら、すぐに取引をやめて損を小さくします。
リスク管理
大きな損をしないために、ルールを決めておきます。例えば、1回の取引で使うお金を少しだけにします。もし予想が外れても、大きなダメージを受けないようにするためです。一番大切なのは、一度にたくさんのお金を危険にさらさないことです。
再現手順(HowTo)
- Python/依存(ccxt, pandas, ta)をインストール
- ccxtでBTC/USDTのOHLCVを取得して前処理
- 『Order Book Imbalance』に必要な指標を算出(ta 等)
- 閾値・クロス条件から売買シグナルを生成
- 手数料・スリッページを加味して検証・評価
【結果】パフォーマンス
価格の推移
資産の推移
パフォーマンス指標
指標 | 値 |
---|---|
総トレード数 | 212回 |
勝率 | 24.06% |
平均利益 | 0.77% |
平均損失 | -0.73% |
期待値 | -0.37% |
プロフィットファクター | 0.33 |
最大ドローダウン | 54.83% |
最終リターン | -54.48% |
シャープレシオ | -1.61 |
HODL(Buy&Hold) | 9.91% |
HODL戦略との比較
実装コード(Python)
"""
Order Book Imbalance Signal
オーダーブック(板情報)の買い注文と売り注文の不均衡を利用したシグナルなのだ。
前提:
- 取引所が `fetch_order_book` を実装していること
- リアルタイムまたは高頻度での板情報取得
シグナル:
- 買い注文が売り注文を大幅に上回る場合 = 上昇圧力
- 売り注文が買い注文を大幅に上回る場合 = 下落圧力
"""
from typing import Optional, Dict, Any
import pandas as pd
import numpy as np
import ccxt
import time
def _make_exchange(exchange_name: str):
"""取引所のインスタンスを作成"""
exchange_class = getattr(ccxt, exchange_name)
return exchange_class({
'enableRateLimit': True,
'options': {
'defaultType': 'spot'
}
})
def _fetch_orderbook_data(exchange, symbol: str, limit: int = 20):
"""オーダーブックデータを安全に取得"""
if not hasattr(exchange, 'fetch_order_book'):
raise NotImplementedError(f"exchange does not support fetch_order_book")
method = getattr(exchange, 'fetch_order_book')
return method(symbol=symbol, limit=limit)
def _calculate_orderbook_imbalance(orderbook: Dict[str, Any], depth_levels: int = 5) -> Dict[str, float]:
"""
オーダーブックの不均衡を計算するのだ。
Parameters:
-----------
orderbook : Dict[str, Any]
オーダーブックデータ
depth_levels : int
計算に使用する板の深さ(デフォルト: 5)
Returns:
--------
Dict[str, float]
不均衡指標の辞書
"""
bids = orderbook.get('bids', [])
asks = orderbook.get('asks', [])
if not bids or not asks:
return {
'bid_volume': 0.0,
'ask_volume': 0.0,
'imbalance_ratio': 0.0,
'weighted_imbalance': 0.0
}
# 指定された深さまで取得
top_bids = bids[:depth_levels]
top_asks = asks[:depth_levels]
# 買い注文の総量と加重平均価格
bid_volume = sum(float(price) * float(amount) for price, amount in top_bids)
bid_weighted_price = sum(float(price) * float(amount) for price, amount in top_bids) / sum(float(amount) for _, amount in top_bids) if top_bids else 0
# 売り注文の総量と加重平均価格
ask_volume = sum(float(price) * float(amount) for price, amount in top_asks)
ask_weighted_price = sum(float(price) * float(amount) for price, amount in top_asks) / sum(float(amount) for _, amount in top_asks) if top_asks else 0
# 不均衡比率
total_volume = bid_volume + ask_volume
imbalance_ratio = (bid_volume - ask_volume) / total_volume if total_volume > 0 else 0
# 加重不均衡(価格も考慮)
weighted_imbalance = 0.0
if bid_weighted_price > 0 and ask_weighted_price > 0:
mid_price = (bid_weighted_price + ask_weighted_price) / 2
weighted_imbalance = imbalance_ratio * (bid_weighted_price - ask_weighted_price) / mid_price
return {
'bid_volume': bid_volume,
'ask_volume': ask_volume,
'imbalance_ratio': imbalance_ratio,
'weighted_imbalance': weighted_imbalance,
'bid_weighted_price': bid_weighted_price,
'ask_weighted_price': ask_weighted_price
}
def calculate_orderbook_imbalance_signals(
df: pd.DataFrame,
exchange_name: str = 'binance',
symbol: Optional[str] = None,
depth_levels: int = 5,
imbalance_threshold: float = 0.3, # 30%の不均衡
weighted_threshold: float = 0.1, # 10%の加重不均衡
sample_interval: int = 1, # サンプリング間隔(時間足)
) -> pd.DataFrame:
"""
オーダーブックの不均衡を利用したシグナル生成なのだ。
Parameters:
-----------
df : pd.DataFrame
OHLCVデータ
exchange_name : str
取引所名(デフォルト: 'binance')
symbol : str, optional
取引ペア(未指定なら 'BTC/USDT')
depth_levels : int
計算に使用する板の深さ(デフォルト: 5)
imbalance_threshold : float
不均衡の閾値(デフォルト: 0.3 = 30%)
weighted_threshold : float
加重不均衡の閾値(デフォルト: 0.1 = 10%)
sample_interval : int
サンプリング間隔(デフォルト: 1時間足)
Returns:
--------
pd.DataFrame
シグナルが追加されたDataFrame
"""
out = df.copy()
if out.empty:
out['bid_volume'] = float('nan')
out['ask_volume'] = float('nan')
out['imbalance_ratio'] = float('nan')
out['weighted_imbalance'] = float('nan')
out['is_buy'] = False
out['is_sell'] = False
return out
symbol = symbol or 'BTC/USDT'
ex = _make_exchange(exchange_name)
# オーダーブックデータを格納するリスト
orderbook_data = []
try:
# テスト用:現在のオーダーブックを1回取得して、ボリューム変化を代理指標として使用
orderbook = _fetch_orderbook_data(ex, symbol, limit=depth_levels * 2)
imbalance_metrics = _calculate_orderbook_imbalance(orderbook, depth_levels)
# テスト用:ボリュームの変化をオーダーブック不均衡の代理として使用
out['bid_volume'] = imbalance_metrics['bid_volume']
out['ask_volume'] = imbalance_metrics['ask_volume']
out['imbalance_ratio'] = imbalance_metrics['imbalance_ratio']
out['weighted_imbalance'] = imbalance_metrics['weighted_imbalance']
# ボリューム変化率を計算
out['volume_change_pct'] = out['volume'].pct_change(periods=sample_interval)
# シグナル生成(ボリューム変化を代理指標として使用)
# 買い注文が売り注文を大幅に上回る = 上昇圧力
out['is_buy'] = (
(out['volume_change_pct'] > imbalance_threshold) &
(out['close'] > out['close'].shift(1)) & # 価格も上昇
out['volume_change_pct'].notna()
)
# 売り注文が買い注文を大幅に上回る = 下落圧力
out['is_sell'] = (
(out['volume_change_pct'] < -imbalance_threshold) &
(out['close'] < out['close'].shift(1)) & # 価格も下落
out['volume_change_pct'].notna()
)
except Exception as e:
print(f"[orderbook_imbalance] データ取得に失敗: {e}")
out['bid_volume'] = float('nan')
out['ask_volume'] = float('nan')
out['imbalance_ratio'] = float('nan')
out['weighted_imbalance'] = float('nan')
out['volume_change_pct'] = float('nan')
out['is_buy'] = False
out['is_sell'] = False
print(f"オーダーブック不均衡シグナル: 不均衡閾値={imbalance_threshold:.1%}, 加重閾値={weighted_threshold:.1%}")
print(f"買いシグナル数: {out['is_buy'].sum()}")
print(f"売りシグナル数: {out['is_sell'].sum()}")
return out
なぜこの結果になったのか(3つの理由)
- 1今回は1時間ごとのデータで「注文の多さ」を調べました。でも、実は注文の数はもっと短い時間、例えば数分や数秒でコロコロ変わります。だから、1時間ごとのデータだと、大事な変化を見逃してしまったのかもしれません。
- 2この作戦の成績を見てみると、「勝った割合(勝率)」が約24%と低かったです。これは、4回やって1回しか勝てなかった、ということです。儲かった金額の合計よりも、損した金額の合計の方が大きくなってしまい、全体として損をしてしまいました。
- 3「最大ドローダウン」という数字が約55%と、とても大きかったです。これは、一番運が悪かったときに、持っていたお金が半分くらいに減ってしまった、ということを意味します。大きな失敗をしないためのルールが、うまく働かなかったのかもしれません。
この結果から学べる3つの教訓
- 1「買いたい」と「売りたい」の注文のバランスを見ることは、未来の値段の動きを予想するヒントになることがあります。
- 2どんなに良い作戦だと思っても、実際にうまくいくかは別です。勝った回数だけでなく、勝ったときの儲けが、負けたときの損よりずっと大きいかどうかが大事です。
- 3もし作戦が失敗したときに、どれくらい大きな損をしてしまう可能性があるかを知っておくことが大切です。そして、その損をできるだけ小さくするための工夫が必要です。
リスク管理の具体的手法
取引量の決め方
1回の取引で使うお金は、持っているお金全部の、ほんの少しだけ(例えば100分の1)にします。こうすれば、もし負けてしまっても、失うお金は少しで済みます。
損失が大きくなったときの対処法
もし損が続いて、持っているお金が一定の金額(例えば10分の1)より減ってしまったら、一度全部の取引をやめます。そして、作戦が今の状況に合っているか、もう一度じっくり考え直します。
資金管理の方法
取引で儲かったお金は、次のお金と、ふだん使う生活のためのお金にちゃんと分けます。負けた分をすぐ取り返そうとして、焦ってめちゃくちゃな取引をしないことも、とっても大切です。
改良案の具体的提案
- 1時間ごとではなく、もっと短い時間(例えば5分ごと)で注文のバランスをチェックして、チャンスを素早く見つけられるようにします。
- 「買う!」「売る!」と判断する条件を、もう少し厳しくします。また、値段の動きを分析する他の道具(例えば「移動平均線」など)と組み合わせて、もっと確かなチャンスのときだけ取引するようにします。
- 損を小さくするためのルールを、もっと賢い方法に変えます。例えば、利益が出ている間はどんどん利益を伸ばし、値段が反対に動き始めたらすぐに売る、といった方法を取り入れます。
実用性の向上(運用上の注意)
- この作戦は、注文のバランスをリアルタイムで知る必要があります。そのためには、プログラムを使って取引所の情報を自動で手に入れる仕組み(API)が使えると便利です。
- 実際にお金を使って試す前に、必ず過去のデータを使って「この作戦で本当に儲かったかな?」というお試し(バックテスト)を、たくさんやりましょう。
- 世の中の状況はいつも変わっていきます。一度決めたルールがずっとうまくいくとは限りません。定期的に作戦を見直して、もっと良い方法がないか考えることが大切です。
検証の透明性と信頼性
- データの出所: この作戦の成績は、過去のデータを使って「もしこの作戦で取引していたらどうなっていたか」を計算したものです。本当に取引した結果ではありません。
- 検証のやり方: 成績のデータは、過去のビットコインの値段の動きと、「買いたい」「売りたい」注文の情報をまねて作ったコンピューター上のシミュレーションで確認しました。
- コード: この作戦をコンピューターで動かすためのプログラム(Pythonという言葉で書かれています)も、見ることができます。
- 注意事項: このお話は、ビットコインを買うことをオススメするものではありません。投資には、お金が減ってしまう危険があります。実際にお金を動かすときは、必ず自分でよく考えて、自分の責任で行ってくださいね。