feat: add min_hold_bars config (default 5) — prevents per-bar churning

Add min_hold_bars to DQNHyperparameters (usize, default 5), ExperienceSection
in training_profile, and both TOML configs (smoketest=3, production=5).
Wired through apply_to() so TOML overrides land correctly.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
jgrusewski
2026-03-25 23:06:28 +01:00
parent d71bf5805e
commit 2b85fa2fdf
4 changed files with 13 additions and 0 deletions

View File

@@ -68,6 +68,7 @@ min_loss_improvement_pct = 0.1
[experience]
initial_capital = 100000.0
tx_cost_multiplier = 0.0001
min_hold_bars = 5
[experience.fill_simulation]
ioc_fill_prob = 0.85

View File

@@ -37,6 +37,7 @@ min_epochs_before_stopping = 5
[experience]
gpu_n_episodes = 32
gpu_timesteps_per_episode = 100
min_hold_bars = 3
[experience.fill_simulation]
ioc_fill_prob = 0.85

View File

@@ -904,6 +904,11 @@ pub struct DQNHyperparameters {
/// Derived from data pipeline's BarSize when available.
pub bars_per_day: f64,
/// Minimum bars to hold a position before allowing exit or reversal.
/// Prevents per-bar churning. Trailing stop can override after this period.
/// Default: 5 (about 5 minutes for 1-min bars). Range: [2, 20].
pub min_hold_bars: usize,
// P2-B Enhancement: Cash reserve requirement
/// Cash reserve requirement as percentage of portfolio value (0-100)
/// Default: 0.0 (no reserve, backward compatible)
@@ -1374,6 +1379,7 @@ impl DQNHyperparameters {
// P2-A Enhancement
initial_capital: 100_000.0, // $100K default
bars_per_day: 390.0, // 1-minute bars (6.5h × 60min)
min_hold_bars: 5,
// P2-B Enhancement
cash_reserve_percent: 0.0, // Default: no reserve (backward compatible)

View File

@@ -195,6 +195,8 @@ pub struct ExperienceSection {
pub initial_capital: Option<f64>,
/// Maps to `DQNHyperparameters::transaction_cost_multiplier`.
pub tx_cost_multiplier: Option<f64>,
/// Minimum bars to hold a position before allowing exit or reversal.
pub min_hold_bars: Option<usize>,
/// Fill simulation sub-section for order routing realism.
pub fill_simulation: Option<FillSimulationSection>,
}
@@ -801,6 +803,9 @@ impl DqnTrainingProfile {
if let Some(v) = ex.tx_cost_multiplier {
hp.transaction_cost_multiplier = v;
}
if let Some(v) = ex.min_hold_bars {
hp.min_hold_bars = v;
}
// [experience.fill_simulation]
if let Some(ref fs) = ex.fill_simulation {
if let Some(v) = fs.ioc_fill_prob {