Files
foxhunt/config/training/dqn-hyperopt.toml
jgrusewski 93c77b91b7 refactor(reward): delete 8 behavioral shaping terms in one sweep
Mass deletion of the "v7 gem" reward terms identified in the Phase 1
inventory as category errors — each one rewarded an outcome-adjacent
behavior instead of encoding the underlying physics, and each
empirically hurt validation metrics more than it helped training
stability.

Deleted from experience_kernels.cu and all plumbing (Rust configs,
launch args, hyperopt logs, TOML entries):

  * order_credit_weight        - reward redundant with compute_tx_cost
                                 (order_type_idx already differentiates
                                 fills by order type)
  * risk_efficiency_weight     - reward double-counted drawdown penalty
                                 asymmetrically (only on winners)
  * urgency_credit_weight      - reward was vol-normalized unrealized P&L,
                                 pure rename of core return
  * commitment_lambda          - triple-counted churn + tx_cost
  * w_dsr                      - kernel wrote DSR EMA but no longer added
                                 to reward (dead); removed the EMA
                                 bookkeeping too
  * dsr_eta                    - kernel arg for the deleted DSR EMA
  * position_entropy_weight    - rewarded action-bucket diversity
                                 regardless of outcome; histogram buffer
                                 + zero-init removed too
  * exit_timing_weight         - already inactive (used raw_next future
                                 price, comment-deleted earlier)
  * ofi_reward_weight          - dead plumbing; OFI already passed as
                                 feature through state[OFI_START..]
  * opportunity_cost_scale     - penalized flat when Q-gap wide;
                                 redundant with Q-values themselves

Kernel arg count: experience_env_step_batch shrank from ~55 to ~45 args.
Rust-side config surface reduced correspondingly.

Results on E1 smoke test (20-epoch):
  BEFORE any Phase 2 work:
    Val Sharpe -120 to -150, MaxDD 10-15%, Sharpe_raw -0.39
  AFTER reward_noise + Kelly (both envs) + urgency + this sweep:
    Val Sharpe      -17 to -22       (7× better)
    Val MaxDD       0.27%            (40× better)
    Val Sharpe_raw  ~-0.09           (4× better)
    Training Sharpe_raw  ~0          (stabilized from ±20 swings)
    Final q_gap     0.1712           (highest yet, collapse mechanism fine)

The extreme train-Sharpe swings (+17 one epoch, -13 next) were not
learning dynamics — they were shaping-term noise. Core reward (P&L +
drawdown + churn + holding + tx_cost + Kelly physics cap) gives training
metrics that actually reflect what the model does.

Inventory doc (docs/superpowers/specs/2026-04-21-phase1-reward-inventory.md)
extended with a "better-form taxonomy" section: every deleted gem has
a correct layer it belongs to (physics, feature, diagnostic, gradient-
level regularization — not reward). Kelly cap and Q-target smoothing
are already relocated; others are scheduled per the taxonomy's P1/P2/P3
priority list.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-21 01:40:36 +02:00

166 lines
4.3 KiB
TOML

# DQN Hyperopt Profile — PSO search space definition
# All bounds are [min, max] ranges. The adapter reads these at runtime.
# To constrain the search space, narrow the ranges here — no code changes needed.
#
# Mean-reduced gradients (2026-04-09): LR range shifted down because gradient
# clipping no longer acts as a hidden LR reducer. Old range [1e-5, 3e-4] had
# effective LR of [~5e-10, ~1.5e-8] due to SUM-reduced clipping at batch=16384.
[search_space]
# Base parameters
learning_rate = [1e-7, 1e-4] # log scale in adapter — mean-reduced gradients
batch_size = [4096, 16384]
gamma = [0.90, 0.99] # wider range — v_range computed dynamically from gamma
buffer_size = [50000, 100000] # log scale in adapter
max_leverage = [2.0, 10.0] # leverage ratio; position computed from capital/price
huber_delta = [10.0, 40.0] # log scale in adapter
entropy_coefficient = [0.05, 0.5]
transaction_cost_multiplier = [0.5, 2.0]
per_alpha = [0.4, 0.8]
per_beta_start = [0.2, 0.6]
# Rainbow DQN extensions
v_range = [1.0, 1.0] # IGNORED: v_range now computed dynamically from gamma
noisy_sigma_init = [0.1, 1.0] # log scale in adapter
dueling_hidden_dim = [128, 512]
n_steps = [3, 5]
num_atoms = [52, 100] # 51=C51 paper minimum, 101=higher resolution (2x slower but better signal)
# Weight decay
weight_decay = [0.0001, 0.01] # log scale in adapter
# Kelly risk parameters
kelly_fractional = [0.25, 0.75]
kelly_max_fraction = [0.1, 0.5]
# Volatility
volatility_window = [10, 30]
# Soft update
tau = [0.005, 0.01] # log scale in adapter
# Network sizing
hidden_dim_base = [128, 256] # capped at production default: 512 is 4x slower for marginal benefit
# CQL regularization
cql_alpha = [0.5, 5.0]
# Training dynamics
lr_decay_type = [0, 2] # discrete: 0=constant, 1=linear, 2=cosine
minimum_profit_factor = [1.1, 2.0]
# Exploration
count_bonus_coefficient = [0.0, 0.3]
# Risk-adjusted returns
sharpe_weight = [0.0, 0.5]
# Branching DQN
branch_hidden_dim = [64, 256]
# Gradient accumulation
gradient_accumulation_steps = [1, 1] # fixed at 1 for now
# IQN dual-head
iqn_lambda = [0.0, 2.0]
# Spectral normalization
spectral_norm_sigma_max = [1.0, 10.0]
# C51 warmup
c51_warmup_epochs = [0, 10]
c51_alpha_max = [0.3, 0.9]
# HER ratio
her_ratio = [0.0, 0.8]
# Composite reward weights
w_pnl = [0.0, 1.0]
w_dd = [0.0, 5.0]
w_idle = [0.0, 0.1]
dd_threshold = [0.005, 0.03] # HFT: tight drawdown tolerance (0.5%-3%)
loss_aversion = [1.0, 1.0]
time_decay_rate = [0.0001, 0.005]
# Trade conviction filter
q_gap_threshold = [0.0, 0.5] # 0.0=trade every bar, 0.5=high conviction only
# Reward v7 — Counterfactual Branch Attribution
cea_weight = [0.1, 1.0]
# exposure_aux_weight removed (4-branch refactor)
b3_size = 3
# v8 search ranges
micro_reward_scale = [0.0, 0.05]
td_lambda = [0.5, 0.99]
hindsight_fraction = [0.0, 0.3]
hindsight_lookahead = [5, 20]
epsilon_start = [0.1, 0.5]
epsilon_end = [0.01, 0.05]
[experience]
initial_capital = 100000.0
[experience.fill_simulation]
ioc_fill_prob = 0.85
limit_fill_min = 0.30
limit_fill_max = 0.80
tx_cost_multiplier = 0.18 # IBKR ES: 0.18 bps = $4.50/contract RT
spread_cost_frac = 0.50
spread_capture_frac = 0.50
[risk]
q_clip_min = -200.0
q_clip_max = 200.0
[reward]
# v8 comprehensive training overhaul
micro_reward_scale = 0.01
td_lambda = 0.9
max_trace_length = 7
hindsight_fraction = 0.1
hindsight_lookahead = 10
w_pnl = 0.3
w_dd = 1.0
w_idle = 0.01
dd_threshold = 0.01
loss_aversion = 1.0
time_decay_rate = 0.0005
q_gap_threshold = 0.1
cea_weight = 0.3
b3_size = 3
[fixed]
[pso]
swarm_size = 20
max_iterations = 50
inertia = 0.7
cognitive = 1.5
social = 1.5
# Two-phase hyperopt: Phase 1 fixes architecture to small network,
# searches only learning dynamics (~15D). Phase 2 fixes best dynamics
# from Phase 1 JSON, searches architecture (~5D).
# See: docs/superpowers/specs/2026-03-22-two-phase-hyperopt-design.md
[phase_fast]
hidden_dim_base = 128
num_atoms = 101
branch_hidden_dim = 64
dueling_hidden_dim = 128
v_range = 1.0
# v8 phase_fast overrides
micro_reward_scale = 0.01
td_lambda = 0.9
hindsight_fraction = 0.1
hindsight_lookahead = 10
w_pnl = 0.3
w_dd = 1.0
w_idle = 0.01
dd_threshold = 0.01
loss_aversion = 1.0
time_decay_rate = 0.0005
q_gap_threshold = 0.1
cea_weight = 0.3
b3_size = 3