ExecutionIntelligence

VPIN toxicity, inventory risk, order-flow microstructure

ExecutionIntelligence is v1’s microstructure module. It computes VPIN (Volume-synchronized Probability of Informed Trading), tracks inventory risk per market, and flags orders that are likely to face toxic counter-flow.

The key insight: if VPIN is elevated, the other side of your order probably knows something you don’t. Reducing size in those moments is free alpha.

Import

python
from horizon.fund._execution_intelligence import (
    ExecutionIntelligence,
    VPINResult,
    InventoryRisk,
)

What is VPIN?

VPIN is a microstructure metric from Easley, López de Prado, and O’Hara (2012). It estimates the probability that a given volume bucket contains informed trading by comparing buy vs sell volume imbalance.

The intuition: if informed traders have private information, they’ll trade aggressively in one direction. Buckets with high buy/sell imbalance ⇒ high probability of informed trading ⇒ the other side of the market (you) is probably being adversely selected.

The formula

Volume bucketing

Group trades into equal-volume buckets (e.g., each bucket contains V units of volume).

Classify each trade as buy or sell

Use a tick rule, Lee-Ready, or bulk volume classification to assign each trade to "buy" or "sell".

Compute imbalance

For each bucket:
imbalance = |V_buy - V_sell| / (V_buy + V_sell)

Average over a window

VPIN = rolling average of imbalance over the last n buckets. High VPIN (> 0.5) = likely informed trading.

API

python
class ExecutionIntelligence:
    def __init__(
        self,
        bucket_volume: float = 10_000,
        lookback_buckets: int = 50,
    ) -> None:
        ...

    def update_trade(
        self,
        market_id: str,
        price: float,
        volume: float,
        timestamp: float,
        is_buy: bool,
    ) -> None:
        """Feed a trade observation into the VPIN calculator."""

    def get_vpin(self, market_id: str) -> VPINResult | None:
        """Current VPIN for a market."""

    def is_toxic(
        self,
        market_id: str,
        threshold: float = 0.5,
    ) -> bool:
        """True if VPIN > threshold. don't place orders."""

    def inventory_risk(
        self,
        market_id: str,
        current_position: float,
    ) -> InventoryRisk:
        """How risky is the current inventory given microstructure state?"""

    def update_order_flow(
        self,
        market_id: str,
        buy_volume: float,
        sell_volume: float,
    ) -> None:
        """Alternative ingress: pre-classified buy/sell volumes."""

Result types

python
@dataclass(frozen=True)
class VPINResult:
    vpin: float                        # [0, 1]
    buy_volume: float
    sell_volume: float
    imbalance: float
    n_buckets: int
    timestamp: float

@dataclass(frozen=True)
class InventoryRisk:
    current_inventory: float
    risk_score: float                  # [0, 1], higher = riskier
    recommended_max_order_size: float  # based on VPIN and inventory
    reason: str

Usage

python
from horizon.fund._execution_intelligence import ExecutionIntelligence

exec_int = ExecutionIntelligence(
    bucket_volume=10_000,       # one VPIN bucket per 10k units
    lookback_buckets=50,         # 50-bucket rolling window
)

# Feed trade data
for trade in my_tick_feed:
    exec_int.update_trade(
        market_id=trade.market_id,
        price=trade.price,
        volume=trade.volume,
        timestamp=trade.timestamp,
        is_buy=trade.is_buy,
    )

# Before placing an order, check VPIN
vpin = exec_int.get_vpin("AAPL")
if vpin and vpin.vpin > 0.6:
    print(f"VPIN is {vpin.vpin:.2f}. skipping trade, likely toxic flow")
    return

# Check inventory risk for a specific size
inv_risk = exec_int.inventory_risk("AAPL", current_position=100)
if inv_risk.risk_score > 0.7:
    print(f"Inventory risky: {inv_risk.reason}")
    desired_size = min(desired_size, inv_risk.recommended_max_order_size)

Integration with Horizon

As a pre-trade risk check

Wrap ExecutionIntelligence as a custom RiskCheck that blocks orders when VPIN is elevated:

python
from horizon.types import Decision
from horizon.fund._execution_intelligence import ExecutionIntelligence

class VPINRiskCheck:
    def __init__(self, threshold: float = 0.6):
        self.exec_int = ExecutionIntelligence()
        self.threshold = threshold

    def check(self, action, state):
        vpin = self.exec_int.get_vpin(action.market_id)
        if vpin is None:
            return Decision.pass_()
        if vpin.vpin > self.threshold:
            return Decision.reject(f"VPIN {vpin.vpin:.2f} > {self.threshold}")
        return Decision.pass_()

Pass to RiskConfig(extra_checks=[VPINRiskCheck()]).

As a feature

Make VPIN available to strategies as a feature they can read:

python
from horizon.features.base import Feature, PriceHistory
from horizon.fund._execution_intelligence import ExecutionIntelligence

class VPINFeature(Feature):
    def __init__(self, market: str | None = None):
        super().__init__(market=market)
        self.exec_int = ExecutionIntelligence()

    def compute(self, market_id, history, feeds):
        # This requires that you've been feeding trades into exec_int
        vpin = self.exec_int.get_vpin(market_id)
        return vpin.vpin if vpin else 0.5   # neutral default

Use in a strategy:

python
class VPINAwareStrategy(Strategy):
    features = {
        "vpin": VPINFeature(),
        "z": Zscore(20),
    }

    def evaluate(self, f, universe):
        return [
            Signal.from_score(m, score=-f.z[m.id], edge_per_stdev=15)
            for m in universe
            if f.vpin[m.id] < 0.5           # only trade in low-toxicity regime
            and abs(f.z[m.id]) > 2
        ]

When to use

Market making Essential: the whole point of MM is to avoid trading against informed flow. VPIN is the standard detector for this.
High-frequency execution Microsecond-level strategies where every bps of adverse selection matters.
Large orders Before pushing a large order, check if VPIN is elevated. If it is, split into smaller orders or delay.
Post-trade diagnostics After a losing trade, look at what VPIN was at the time. If it was high, the loss was predictable adverse selection.

Pitfalls

Source

python/horizon/fund/_execution_intelligence.py. ~350 lines. Includes bucket management, rolling VPIN computation, and inventory risk scoring.

Next