ML Documentation

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(出来高分布プロファイル)

組み合わせの威力