Trade execution modeling (backtest only) — slippage formulas (linear / square-root impact), VWAP/TWAP execution logic, market-impact cost estimation, and execution-assumption configuration.
60
71%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
View guide
Passed
No findings from the security scan
Fix and improve this skill with Tessl
tessl review fix ./agent/src/skills/execution-model/SKILL.mdProvide more realistic execution assumptions for backtests, including slippage models, market-impact estimation, and execution-algorithm principles. This skill is for backtest simulation only and does not involve live order execution.
Idealized backtest: filled at the close, zero slippage
Real world:
1. The order book has a bid-ask spread
2. Large orders push prices (market impact)
3. Execution is delayed (there is latency from signal to fill)
No slippage model -> overly optimistic backtest -> losses in live tradingDo not retype these models. All four are implemented and tested in
src/quantlib/impact.py; import them. The tested versions validate their inputs —
a zero ADV raises instead of dividing by zero, and a negative delay_bars raises
instead of silently introducing look-ahead bias.
from src.quantlib.impact import fixed_slippage, linear_impact, sqrt_impact, delayed_executionfixed_slippage(price=100.0, direction=1, bps=5.0) # 100.05 (buy pays up)
fixed_slippage(price=100.0, direction=-1, bps=5.0) # 99.95 (sell receives less)direction is 1 to buy or -1 to sell, and must be exactly one of those — it
multiplies the impact, so an unchecked 2 would silently double the modelled cost.
bps defaults to DEFAULT_SLIPPAGE_BPS (5.0).
Reference fixed-slippage assumptions by market:
| Market | Instrument | Suggested Slippage (bps) | Notes |
|---|---|---|---|
| China A-share large cap | CSI 300 constituents | 3-5 | Good liquidity |
| China A-share small cap | CSI 1000 constituents | 5-10 | Average liquidity |
| China micro-cap | market cap < 5 billion RMB | 10-30 | Poor liquidity |
| US large cap | AAPL / MSFT | 1-3 | Excellent liquidity |
| Hong Kong stocks | Hang Seng constituents | 5-10 | Less liquid than A / US |
| BTC spot | BTC-USDT | 2-5 | Good OKX liquidity |
| ETH spot | ETH-USDT | 3-8 | Slightly worse than BTC |
| Small altcoins | other -USDT pairs | 10-50 | Liquidity varies widely |
impact = impact_coeff × volume_traded / adv
# 100k shares against 1M ADV = 10% participation; at coeff 0.1 that is a 1% move.
linear_impact(price=100.0, direction=1, volume_traded=100_000, adv=1_000_000, impact_coeff=0.1)
# 101.0Marginal impact is constant here, which overstates the cost of very large orders.
impact_coeff defaults to DEFAULT_LINEAR_IMPACT_COEFF (0.1).
Reference impact coefficients:
| Market | impact_coeff | Notes |
|---|---|---|
| China A-share large cap | 0.05-0.10 | 10% daily price-limit system |
| China A-share small cap | 0.10-0.20 | Liquidity premium |
| US equities | 0.03-0.08 | Market-maker buffering |
| Crypto | 0.05-0.15 | 24h trading is dispersed |
impact = η × σ × sqrt(volume_traded / adv)
# 250k against 1M ADV = 25% participation; 0.5 × 0.02 × sqrt(0.25) = 0.005 = 50bps.
sqrt_impact(price=100.0, direction=1, volume_traded=250_000, adv=1_000_000,
volatility=0.02, eta=0.5)
# 100.5 (100.49999999999999 in binary floating point)volatility is daily return volatility as a decimal fraction. eta defaults to
DEFAULT_SQRT_IMPACT_ETA (0.5); 0.3-0.8 is the usual calibrated range.
Advantages of the square-root model:
Naming. This impact term is often labelled "Almgren-Chriss", and it does come from that literature, but it is not Almgren-Chriss optimal execution. There is no trading trajectory, no permanent/temporary impact split and no risk-aversion parameter here, and none is implemented anywhere in this repository. Call it a square-root impact function, and do not claim an optimal schedule was computed.
Backtest capital vs instrument ADV:
├── Capital < 0.5% of ADV -> fixed slippage (5bps) is enough
├── Capital 0.5-5% -> linear impact model
└── Capital > 5% -> square-root impact model (required)Goal: execute at the day's volume-weighted average price
VWAP = Σ(Price_i × Volume_i) / Σ(Volume_i)
Execution logic:
1. Forecast the intraday volume profile (typically U-shaped)
2. Split the order according to the predicted profile
3. Execute proportionally in each time slice
Typical China A-share VWAP volume profile (U-shaped):
09:30-10:00 15% (active open)
10:00-11:30 25% (normal morning session)
13:00-14:00 15% (weak afternoon session)
14:00-14:30 15% (afternoon recovery)
14:30-15:00 30% (active close)
VWAP in backtests:
- Daily backtest: use the VWAP field directly as the fill price
- Minute backtest: simulate VWAP order slicingGoal: execute evenly over a specified time window
TWAP = simple time-sliced execution
Execution logic:
1. Define an execution window (for example 09:30-11:30)
2. Divide it into N time buckets
3. Execute total_size / N in each bucket
Pros and cons:
+ Simple, no need to forecast volume
- Easier to cause impact during low-volume periods
- Less adaptive than VWAPsignals = delayed_execution(raw_signal, delay_bars=1) # T+1: trade tomorrow on today's signal
signals = delayed_execution(raw_signal, delay_bars=0) # same-bar executiondelay_bars=1 (T+1 rule)delay_bars=0 or 1A negative delay_bars raises. It would pull future signal values into the past,
which is look-ahead bias and silently inflates every backtest containing it — the
tested implementation refuses rather than letting that pass unnoticed.
Total trading cost = explicit cost + implicit cost
Explicit cost:
- Commission: China A-shares 2-3 bps, crypto 0.02-0.1%
- Stamp duty (China A-share sell side): 0.05% (sell orders only)
- Transfer fee: negligible
Implicit cost:
- Bid-ask spread: 0.5-5bps
- Market impact: depends on trade size and liquidity
- Opportunity cost: loss from not filling at the best price| Cost Item | China A-shares | Hong Kong | US | Crypto (OKX) |
|---|---|---|---|---|
| Commission (one way) | 0.025% | 0.05% | 0 (zero commission) | 0.08% (maker) |
| Stamp duty | 0.05% (sell) | 0.1% (both sides) | 0 | 0 |
| Bid-ask spread | 0.03-0.1% | 0.05-0.2% | 0.01-0.05% | 0.01-0.05% |
| Total one-way | ~0.1% | ~0.2% | ~0.03% | ~0.1% |
| Total round-trip | ~0.2% | ~0.4% | ~0.06% | ~0.2% |
{
"commission": 0.001,
"comment": "0.1% one-way commission, already includes stamp duty and spread"
}Recommendations:
commission = 0.001 (conservative, includes all costs)commission = 0.001 (including slippage)commission = 0.001-0.002config.json Settings{
"commission": 0.001,
"engine": "daily",
"interval": "1D"
}signal_engine.py)from src.quantlib.impact import delayed_execution
class SignalEngine:
def __init__(self):
# Execution assumption parameters
self.execution_delay = 1 # T+1 delay
self.slippage_bps = 5 # Fixed 5bps slippage
self.max_participation = 0.05 # Maximum participation rate 5%
def generate(self, data_map):
for code, df in data_map.items():
# 1. Generate raw signal
raw_signal = self._compute_signal(df)
# 2. Apply execution delay
delayed_signal = delayed_execution(raw_signal, self.execution_delay)
# 3. Apply volume filter (do not trade when liquidity is too low)
volume_ok = df['volume'] > df['volume'].rolling(20).mean() * 0.3
delayed_signal[~volume_ok] = 0
signals[code] = delayed_signalStep 1: Estimate annual turnover
Annual turnover = annual trade count × 2 (buy + sell) / number of positions
Step 2: Compute annual cost drag
Annual cost = annual turnover × total one-way cost
Step 3: Evaluate the impact on returns
Net return = gross return - annual cost
Example:
Annual turnover = 12 (monthly rebalance)
One-way cost = 0.1%
Annual cost = 12 × 0.1% = 1.2%
If annualized return is only 5% -> costs eat 24% of returns!### Backtest Results Under Different Slippage Assumptions
| Slippage (bps) | Annual Return | Sharpe | Max Drawdown |
|-----------|---------|--------|---------|
| 0 (ideal) | 15.2% | 1.35 | -18.5% |
| 3 | 13.8% | 1.22 | -19.0% |
| 5 | 12.9% | 1.15 | -19.2% |
| 10 | 11.1% | 0.98 | -19.8% |
| 20 | 7.5% | 0.65 | -20.5% |
Conclusion: the strategy still has meaningful profitability under 10bps slippage## Execution Cost Analysis
### Strategy Trading Characteristics
| Metric | Value |
|------|-----|
| Average annual trade count | 48 |
| Annual turnover | 4.8x |
| Average holding days | 25 |
| Average order size | ¥50,000 |
### Cost Estimate
| Cost Item | Per Trade | Annualized |
|--------|------|------|
| Commission | 0.025% | 0.24% |
| Stamp duty | 0.025% | 0.12% |
| Estimated slippage | 0.03% | 0.29% |
| **Total** | **0.08%** | **0.65%** |
### Cost Impact
- Gross return: 12.5%
- Net return: 11.85%
- Cost drag: -0.65% (5.2% of gross return)
- Conclusion: cost impact is manageable
### Optimization Suggestions
1. Lower turnover (lengthen holding period)
2. Avoid trading during low-liquidity windows
3. Use limit orders instead of market orderscommission in config: the default 0.001 (0.1%) is a reasonable all-in cost estimatesrc/quantlib/impact.py holds all four, tested. Import them rather than retyping; the tested versions reject a zero ADV, a negative order size and a negative execution delay, all of which the retyped versions used to accept silentlyd71ed8d
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.