Pool Hooks

Hooks are mixin classes that extend pool functionality by intercepting and modifying pool behavior at key points. They enable features like custom fee logic, performance tracking, and weight constraints without modifying the base pool implementations.

Using Hooks

Hooks are applied using the hookname__poolrule naming convention:

from quantammsim.pools.creator import create_pool

# Create a momentum pool with LVR tracking
pool = create_pool("lvr__momentum")

# Create a balancer pool with rebalancing comparison
pool = create_pool("rvr__balancer")

# Create a momentum pool with per-asset weight bounds
pool = create_pool("bounded__momentum")

Available Hooks

Loss Versus Rebalancing (LVR)

The lvr hook tracks Loss-Versus-Rebalancing, measuring how much value the pool loses to arbitrageurs compared to a continuously rebalancing portfolio.

pool = create_pool("lvr__mean_reversion_channel")

This is useful for:

  • Measuring arbitrage extraction

  • Comparing pool efficiency across strategies

  • Understanding the cost of providing liquidity

Rebalancing Versus Rebalancing (RVR)

The rvr hook compares pool performance against a periodically rebalancing benchmark portfolio.

pool = create_pool("rvr__balancer")

This helps evaluate whether the pool’s dynamic behavior adds value compared to simple periodic rebalancing.

Bounded Weights

The bounded hook adds per-asset minimum and maximum weight constraints. See Per-Asset Weight Bounds for full documentation.

pool = create_pool("bounded__momentum")

params = {
    "min_weights_per_asset": jnp.array([0.2, 0.2, 0.1]),
    "max_weights_per_asset": jnp.array([0.5, 0.5, 0.3]),
    # ... other parameters
}

Ensemble Averaging

The ensemble hook trains multiple parameter sets (“members”) simultaneously and averages their weight outputs, providing implicit regularisation through diversity. Members start from different initial parameters (using structured sampling methods) and converge to different local optima.

pool = create_pool("ensemble__momentum")

run_fingerprint["optimisation_settings"].update({
    "n_ensemble_members": 4,            # Number of members
    "ensemble_init_method": "lhs",      # Initialisation sampling method
    "ensemble_init_scale": 1.0,         # Perturbation scale
})

Available initialisation methods:

  • "lhs" — Latin Hypercube Sampling (default, good space coverage)

  • "sobol" — Sobol quasi-random sequence (low discrepancy)

  • "grid" — Regular grid (deterministic, evenly spaced)

  • "gaussian" — Gaussian perturbations around initial values

The ensemble hook averages the weight outputs of all members, not the parameters. This means each member produces its own weight trajectory, and the final weights are the arithmetic mean. Gradients flow through all members during backpropagation.

Multi-Hook Chaining

Multiple hooks can be combined using the double-underscore syntax. Hooks are applied left-to-right (leftmost = highest priority in MRO):

# Ensemble + bounded weights + mean reversion channel
pool = create_pool("ensemble__bounded__mean_reversion_channel")

# Ensemble + LVR tracking + momentum
pool = create_pool("ensemble__lvr__momentum")

This is equivalent to constructing the class manually:

from quantammsim.pools.creator import create_hooked_pool_instance
from quantammsim.hooks.ensemble_averaging_hook import EnsembleAveragingHook
from quantammsim.hooks.bounded_weights_hook import BoundedWeightsHook

pool = create_hooked_pool_instance(
    MeanReversionChannelPool,
    BoundedWeightsHook,
    EnsembleAveragingHook,
)

Dynamic Fee Hooks

Fee hooks allow pools to adjust their fees based on market conditions.

Base Dynamic Fee Hook

The dynamic_fee_base_hook provides infrastructure for volatility-adjusted fees:

from quantammsim.hooks.dynamic_fee_base_hook import DynamicFeeBaseHook

Momentum Dynamic Fee Hook

Adjusts fees based on recent price momentum:

from quantammsim.hooks.momentum_dynamic_fee_hook import MomentumDynamicFeeHook

Creating Custom Hooks

Hooks are implemented as mixin classes that override specific pool methods. The hook must appear before the base pool class in the inheritance order:

from quantammsim.pools.G3M.quantamm.momentum_pool import MomentumPool

class MyCustomHook:
    """Custom hook that modifies pool behavior."""

    def some_pool_method(self, *args, **kwargs):
        # Custom logic before
        result = super().some_pool_method(*args, **kwargs)
        # Custom logic after
        return result

# Create hooked pool class
class HookedMomentumPool(MyCustomHook, MomentumPool):
    pass

# Or use the creator utility
from quantammsim.pools.creator import create_hooked_pool_instance

pool = create_hooked_pool_instance(MomentumPool, MyCustomHook)

Key methods that can be overridden:

  • calculate_fine_weights - Post-process calculated weights

  • calculate_fees - Custom fee calculation

  • post_trade_hook - Logic after each trade

Hook Ordering

When multiple hooks are applied, they are processed right-to-left in the inheritance order. The first hook in the list has the highest priority (its methods are called first).

# Hook1's methods take precedence over Hook2's
class MultiHookedPool(Hook1, Hook2, BasePool):
    pass