ACD Duration Models
Autoregressive Conditional Duration for inter-event timing analysis
Trade arrivals cluster: fast trades follow fast trades, quiet periods follow quiet periods. The Autoregressive Conditional Duration (ACD) model captures this pattern, letting you detect when trade intensity is abnormally high or low. Unusual acceleration in trade arrivals often signals informed trading activity.
API
acd = hz.AcdModel(
omega=0.1, # baseline duration component
alpha=0.2, # reaction to last duration surprise
beta=0.7, # persistence of expected duration
)
# Feed trade-to-trade durations (seconds between consecutive trades)
expected_duration = acd.update(duration)
# Returns: float -- the model's expected duration for the next trade
print(acd.expected_duration()) # current conditional expectation
print(acd.residual()) # last duration / expected_duration
Parameters
The ACD model follows the structure E[d_i] = omega + alpha * d_{i-1} + beta * E[d_{i-1}]:
| Parameter | Role | Constraint |
|---|---|---|
omega | Unconditional baseline | > 0 |
alpha | Sensitivity to last observed duration | >= 0 |
beta | Persistence of the expected duration | >= 0 |
alpha + beta | Must be less than 1 for stationarity | Sum under 1 |
Higher beta means the expected duration changes slowly. Higher alpha means it reacts sharply to the most recent trade.
Detecting informed trading
When trades arrive much faster than the model expects, someone may be acting on private information:
acd = hz.AcdModel(omega=0.5, alpha=0.15, beta=0.75)
for i in range(1, len(trade_times)):
duration = trade_times[i] - trade_times[i - 1]
expected = acd.update(duration)
residual = duration / expected
if residual < 0.3:
# Trade arrived 3x faster than expected
print(f"Abnormally fast trade at {trade_times[i]}")
# Consider: widen spreads, reduce offered size, flag for review
Trade intensity as a feature
Use the ACD residual as a feature in your strategy — it measures whether the market is “hot” or “cold” relative to its own history:
acd = hz.AcdModel(omega=0.5, alpha=0.2, beta=0.7)
# In your strategy loop:
def on_trade(self, trade):
duration = trade.timestamp - self.last_trade_time
self.last_trade_time = trade.timestamp
expected = self.acd.update(duration)
intensity = expected / max(duration, 0.001) # > 1 means faster than expected
if intensity > 3.0:
# Market is unusually active -- tighten quotes, reduce edge target
pass
elif intensity < 0.3:
# Market is quiet -- widen quotes, increase edge target
pass
When to use
- Market making: adjust quote width and size based on trade intensity.
- Informed trading detection: abnormally fast trade clusters suggest someone knows something.
- Event monitoring: prediction markets often see trade acceleration before public information release.
- Execution timing: submit orders during quiet periods (low intensity) for better fills.