Advanced approaches to evaluating trading strategy quality with consideration of turnover and commissions.
Comparison of strategy evaluation metrics
When training trading strategies with reinforcement learning, properly evaluating model quality is essential. Traditional metrics have limitations that may lead to suboptimal decisions in real trading.
Main evaluation metrics:
- train_idx — dpnl / (1+DD) — "Return-on-risk": how much profit remains after accounting for maximum drawdown
- pnl / turnover — profit divided by total trade volume — Trading efficiency: how many dollars are earned per dollar traded (a Sharpe/IRR proxy on "capital-turnover")
Limitations of train_idx
The train_idx metric shows risk-adjusted return but ignores the amount of money "pushed" through the market. This can lead to serious problems when transitioning to real trading.
Combined score (prod_score)
To address these problems, a combined approach is proposed that considers both risk-adjusted return quality and trading efficiency.
prod_score = train_idx * 0.7 + (pnl / turnover) * 0.3
Volume decrease bonus (use_volume_decrease_bonus)
use_volume_decrease_bonus does indeed push the policy toward lower turnover, but it has important limitations:
How it works
- During training — bonus-shaping (use_volume_decrease_bonus) gently nudges gradients toward "less volume"
- During model selection — the combined prod_score looks at the fact: "How much did the strategy actually earn and at what cost in turnover"
Practical example
Consider a comparison of two models with hypothetical numbers:
- Model A — PNL $1,200 — Max DD 6% — Turnover $600,000 — train_idx 0.113 — PNL/Turnover 0.0020 — prod_score 0.083
- Model B — PNL $1,050 — Max DD 4% — Turnover $200,000 — train_idx 0.100 — PNL/Turnover 0.0053 — prod_score 0.085
Implementation
In the save block (_train_ppo → _ppo_iteration), after obtaining train_metrics:
idx = train_metrics["index"] pnl = train_metrics["pnl"] turnover = max(train_metrics["turnover"], 1e-8) # protection against /0 efficiency = pnl / turnover # $ per $ of turnover prod_score = idx * 0.7 + efficiency * 0.3
We store the best pair (best_prod_score, best_checkpoint) and compare against it:
if prod_score > best_prod_score:
best_prod_score = prod_score
save_checkpoint(...)Optionally, both metrics can be logged to track the trade-off.
Summary: complementary mechanisms
The two mechanisms operate at different levels and complement each other:
- use_volume_decrease_bonus — an on-policy switch that forces the agent to remember the "cost" of turnover; works during training, steering gradients toward smaller volume
- prod_score — an off-policy filter that picks the final model by the PNL / volume ratio; evaluates the already-trained model in terms of real trading efficiency