目次
VDPとVWAP解説
📊 VWAP(Volume Weighted Average Price)とは
概要
VWAP(出来高加重平均価格)は、一定期間における取引価格を出来高で加重平均した価格指標です。機関投資家やアルゴリズム取引で広く使用される、最も重要な執行ベンチマークの一つです。
計算方法
# 基本的なVWAP計算
def calculate_vwap(trades):
"""
VWAP = Σ(価格 × 出来高) / Σ(出来高)
"""
price_volume_sum = sum(trade['price'] * trade['volume'] for trade in trades)
total_volume = sum(trade['volume'] for trade in trades)
return price_volume_sum / total_volume if total_volume > 0 else 0
時間軸別VWAP
class VWAPCalculator:
def __init__(self):
self.trades = []
self.cumulative_pv = 0 # 累積(価格×出来高)
self.cumulative_volume = 0 # 累積出来高
def add_trade(self, price: float, volume: float, timestamp: datetime):
"""リアルタイムでVWAPを更新"""
self.trades.append({
'price': price,
'volume': volume,
'timestamp': timestamp
})
# 累積値の更新
self.cumulative_pv += price * volume
self.cumulative_volume += volume
# 現在のVWAP
current_vwap = self.cumulative_pv / self.cumulative_volume
return current_vwap
def get_intraday_vwap(self):
"""日中VWAP(最も一般的)"""
today_trades = [t for t in self.trades
if t['timestamp'].date() == datetime.now().date()]
return calculate_vwap(today_trades)
def get_rolling_vwap(self, window_minutes: int = 60):
"""ローリングVWAP(直近N分間)"""
cutoff_time = datetime.now() - timedelta(minutes=window_minutes)
recent_trades = [t for t in self.trades
if t['timestamp'] > cutoff_time]
return calculate_vwap(recent_trades)
VWAPの活用方法
1. 執行ベンチマーク
def evaluate_execution_quality(execution_price: float, vwap: float):
"""執行品質の評価"""
slippage = (execution_price - vwap) / vwap * 100
if slippage < 0:
return f"VWAP比 {abs(slippage):.2f}% 良い執行"
else:
return f"VWAP比 {slippage:.2f}% 悪い執行"
2. トレンド判断
def determine_trend(current_price: float, vwap: float):
"""価格とVWAPの位置関係からトレンドを判断"""
if current_price > vwap * 1.001: # 0.1%以上上
return "上昇トレンド"
elif current_price < vwap * 0.999: # 0.1%以上下
return "下降トレンド"
else:
return "横ばい"
3. サポート/レジスタンスレベル
class VWAPBands:
def calculate_vwap_bands(self, vwap: float, std_dev: float):
"""VWAPを中心としたバンドを計算"""
return {
'upper_band_2sd': vwap + 2 * std_dev,
'upper_band_1sd': vwap + std_dev,
'vwap': vwap,
'lower_band_1sd': vwap - std_dev,
'lower_band_2sd': vwap - 2 * std_dev
}
VWAPトレーディング戦略
class VWAPStrategy:
def __init__(self, threshold: float = 0.002): # 0.2%
self.threshold = threshold
def generate_signal(self, current_price: float, vwap: float,
volume_profile: dict) -> str:
"""VWAPベースの取引シグナル生成"""
deviation = (current_price - vwap) / vwap
# 基本シグナル
if deviation < -self.threshold and volume_profile['increasing']:
return "BUY" # VWAPより安く、出来高増加
elif deviation > self.threshold and volume_profile['decreasing']:
return "SELL" # VWAPより高く、出来高減少
# リバージョン戦略
if abs(deviation) > 0.01: # 1%以上の乖離
return "REVERSION_EXPECTED"
return "HOLD"
🔍 VDP(Volume Distribution Profile)とは
概要
VDP(出来高分布プロファイル)は、価格帯別の出来高分布を可視化した指標です。どの価格レベルで多くの取引が行われたかを示し、重要なサポート/レジスタンスレベルの特定に使用されます。
基本的な実装
class VolumeDistributionProfile:
def __init__(self, price_bins: int = 50):
self.price_bins = price_bins
self.volume_profile = {}
def add_trade(self, price: float, volume: float):
"""取引を価格帯別に集計"""
# 価格を離散化(ビンに分類)
price_bin = round(price, 2) # 0.01単位で丸める
if price_bin not in self.volume_profile:
self.volume_profile[price_bin] = 0
self.volume_profile[price_bin] += volume
def calculate_vdp(self, trades: List[dict]) -> dict:
"""完全なVDPを計算"""
# 価格範囲を決定
prices = [t['price'] for t in trades]
min_price = min(prices)
max_price = max(prices)
# 価格ビンを作成
price_range = max_price - min_price
bin_size = price_range / self.price_bins
# ビンごとに出来高を集計
vdp = {}
for i in range(self.price_bins):
bin_start = min_price + i * bin_size
bin_end = bin_start + bin_size
bin_center = (bin_start + bin_end) / 2
# このビンに含まれる取引の出来高を合計
bin_volume = sum(
t['volume'] for t in trades
if bin_start <= t['price'] < bin_end
)
vdp[bin_center] = bin_volume
return vdp
高度なVDP分析
class AdvancedVDP:
def __init__(self):
self.vdp = {}
def identify_key_levels(self, vdp: dict, threshold_percentile: float = 70):
"""重要な価格レベル(高出来高ゾーン)を特定"""
volumes = list(vdp.values())
threshold = np.percentile(volumes, threshold_percentile)
key_levels = []
for price, volume in vdp.items():
if volume > threshold:
key_levels.append({
'price': price,
'volume': volume,
'strength': volume / sum(volumes) * 100 # 全体に占める割合
})
return sorted(key_levels, key=lambda x: x['volume'], reverse=True)
def find_poc(self, vdp: dict) -> float:
"""POC(Point of Control)= 最大出来高の価格を特定"""
return max(vdp.items(), key=lambda x: x[1])[0]
def calculate_value_area(self, vdp: dict, percentage: float = 70):
"""バリューエリア(全出来高の70%が集中する価格帯)を計算"""
total_volume = sum(vdp.values())
target_volume = total_volume * percentage / 100
# POCから始めて両側に拡張
poc = self.find_poc(vdp)
sorted_prices = sorted(vdp.keys())
poc_index = sorted_prices.index(poc)
value_area_volume = vdp[poc]
lower_index = poc_index
upper_index = poc_index
# 目標出来高に達するまで範囲を拡張
while value_area_volume < target_volume:
# 上下どちらに拡張するか決定
upper_volume = vdp.get(sorted_prices[upper_index + 1], 0) if upper_index < len(sorted_prices) - 1 else 0
lower_volume = vdp.get(sorted_prices[lower_index - 1], 0) if lower_index > 0 else 0
if upper_volume > lower_volume:
upper_index += 1
value_area_volume += upper_volume
else:
lower_index -= 1
value_area_volume += lower_volume
return {
'vah': sorted_prices[upper_index], # Value Area High
'poc': poc, # Point of Control
'val': sorted_prices[lower_index], # Value Area Low
'volume_percentage': value_area_volume / total_volume * 100
}
VDPの可視化
import matplotlib.pyplot as plt
def visualize_vdp(vdp: dict, current_price: float):
"""VDPを横向きヒストグラムで可視化"""
prices = list(vdp.keys())
volumes = list(vdp.values())
fig, ax = plt.subplots(figsize=(10, 8))
# 横向きバーチャート
bars = ax.barh(prices, volumes, alpha=0.6)
# POCをハイライト
poc_price = max(vdp.items(), key=lambda x: x[1])[0]
poc_index = prices.index(poc_price)
bars[poc_index].set_color('red')
# 現在価格をマーク
ax.axhline(y=current_price, color='green', linestyle='--',
label=f'Current Price: {current_price:.2f}')
# バリューエリアを塗りつぶし
vdp_analyzer = AdvancedVDP()
value_area = vdp_analyzer.calculate_value_area(vdp)
ax.axhspan(value_area['val'], value_area['vah'],
alpha=0.2, color='blue', label='Value Area (70%)')
ax.set_xlabel('Volume')
ax.set_ylabel('Price')
ax.set_title('Volume Distribution Profile')
ax.legend()
plt.tight_layout()
return fig
🔄 VWAPとVDPの組み合わせ戦略
統合分析クラス
class IntegratedVolumeAnalysis:
def __init__(self):
self.vwap_calc = VWAPCalculator()
self.vdp_analyzer = AdvancedVDP()
def analyze_market_structure(self, trades: List[dict]) -> dict:
"""VWAPとVDPを統合した市場構造分析"""
# VWAP計算
vwap = calculate_vwap(trades)
# VDP計算
vdp = {}
for trade in trades:
price_bin = round(trade['price'], 2)
vdp[price_bin] = vdp.get(price_bin, 0) + trade['volume']
# キーレベル特定
poc = self.vdp_analyzer.find_poc(vdp)
value_area = self.vdp_analyzer.calculate_value_area(vdp)
key_levels = self.vdp_analyzer.identify_key_levels(vdp)
# 統合分析
current_price = trades[-1]['price']
return {
'vwap': vwap,
'current_price': current_price,
'price_vs_vwap': (current_price - vwap) / vwap * 100,
'poc': poc,
'value_area': value_area,
'key_support_levels': [k for k in key_levels if k['price'] < current_price],
'key_resistance_levels': [k for k in key_levels if k['price'] > current_price],
'market_balance': self._assess_market_balance(current_price, vwap, poc)
}
def _assess_market_balance(self, price: float, vwap: float, poc: float) -> str:
"""市場のバランス状態を評価"""
if abs(vwap - poc) / poc < 0.001: # VWAPとPOCが近い
return "均衡状態"
elif price > vwap and price > poc:
return "強気(上方ブレイクアウト可能性)"
elif price < vwap and price < poc:
return "弱気(下方ブレイクアウト可能性)"
else:
return "レンジ相場"
実践的なトレーディング戦略
class VolumeBasedTradingStrategy:
def __init__(self):
self.analyzer = IntegratedVolumeAnalysis()
def generate_trading_signal(self, trades: List[dict]) -> dict:
"""VWAPとVDPを組み合わせた取引シグナル"""
analysis = self.analyzer.analyze_market_structure(trades)
signal = {
'action': 'HOLD',
'confidence': 0,
'reasons': []
}
# 条件1: VWAPからの乖離
vwap_deviation = analysis['price_vs_vwap']
if vwap_deviation < -1: # VWAPより1%以上安い
signal['action'] = 'BUY'
signal['confidence'] += 30
signal['reasons'].append(f"VWAP下方乖離: {vwap_deviation:.2f}%")
# 条件2: バリューエリアとの関係
current_price = analysis['current_price']
value_area = analysis['value_area']
if current_price < value_area['val']: # バリューエリア下限を下回る
signal['action'] = 'BUY'
signal['confidence'] += 40
signal['reasons'].append("バリューエリア下限でのサポート期待")
# 条件3: キーレベルとの関係
nearest_support = min(analysis['key_support_levels'],
key=lambda x: abs(x['price'] - current_price))
if abs(current_price - nearest_support['price']) / current_price < 0.002:
signal['confidence'] += 30
signal['reasons'].append(f"強いサポートレベル付近: {nearest_support['price']}")
return signal
📈 実装例:リアルタイムVWAP/VDP計算
// Rust実装例
pub struct VolumeAnalyzer {
vwap_calculator: VwapCalculator,
vdp_analyzer: VdpAnalyzer,
trades_buffer: VecDeque<Trade>,
}
impl VolumeAnalyzer {
pub fn process_trade(&mut self, trade: Trade) {
// バッファに追加
self.trades_buffer.push_back(trade.clone());
// 古いデータを削除(24時間分のみ保持)
let cutoff = Utc::now() - Duration::hours(24);
while let Some(front) = self.trades_buffer.front() {
if front.timestamp < cutoff {
self.trades_buffer.pop_front();
} else {
break;
}
}
// VWAP更新
self.vwap_calculator.update(trade.price, trade.volume);
// VDP更新
self.vdp_analyzer.add_trade(trade.price, trade.volume);
}
pub fn get_analysis(&self) -> VolumeAnalysisResult {
VolumeAnalysisResult {
vwap: self.vwap_calculator.get_current_vwap(),
poc: self.vdp_analyzer.get_poc(),
value_area: self.vdp_analyzer.get_value_area(),
key_levels: self.vdp_analyzer.get_key_levels(),
}
}
}
まとめ
VWAP(出来高加重平均価格)
- 定義: 取引価格を出来高で加重平均した価格
- 用途: 執行ベンチマーク、トレンド判断、サポート/レジスタンス
- 特徴: 大口取引の平均取得価格を反映
VDP(出来高分布プロファイル)
- 定義: 価格帯別の出来高分布
- 用途: キーレベル特定、市場構造分析
- 特徴: POC(最大出来高価格)とバリューエリアが重要
組み合わせの威力
- VWAPは「時間軸での平均」、VDPは「価格軸での分布」を示す
- 両者を組み合わせることで、より精度の高い市場分析が可能
- 機関投資家の動向とマーケット構造を同時に把握できる