PINE LIBRARY
Cập nhật

ICOptimizerLib

212
Library "ICOptimizerLib"
ICOptimizerLib v2 — IC-based parameter optimization with 4 Bayesian strategies.
Publish target: ICOptimizer/2 (hard break from v1 — see §A below).
Layer 1: primitive IC estimators (Pearson, Spearman, Kendall, Partial).
Layer 2: Optimizer UDT with 4 strategies: argmax | ucb | thompson | bayesian.
Layer 3: RegimeGate, ObjectiveWeights, composite scoring, serialize/restore.
Layer 4: diagnostics table and panel.
L2 library — depends only on NumLib.

─── §A v1 BACKWARD-COMPAT DECISION (follow-up 1) ────────────────────────

HARD BREAK. v1 (ICOptimizer/1) used bare strings ("argmax", "ucb", …).
v2 uses the OptimizerKind enum. Reason: Pine v6 enums are type-safe and
produce CE10 errors at compile time if a caller passes an invalid string,
whereas bare strings fail silently at runtime. The compat shim route
(string→enum dispatch wrapper) was considered and rejected: it would
re-introduce series-string branching inside a hot method, defeating the
purpose of the enum migration.

Migration for v1 callers:
OLD: f_find_optimal_param(params, ics, cur, 0.2) ← v1 API
NEW: opt = f_optimizer_new(OptimizerKind.ARGMAX, …) ← v2 API
idx = opt.propose()
opt.observe(idx, ic)
The free function f_find_optimal_param() is retained in §4 as a one-line
compat wrapper producing identical output to v1 findOptimalParam() for
callers that only used ARGMAX and do not need the UDT.

Publish target: ICOptimizer/2 (same publisher namespace as kNNLib/28,
LearningLib/1, etc. Parallel to v1, not a rename.)

─── §B UDT INDEPENDENCE AUDIT (follow-up 2) ─────────────────────────────

All 4 UDTs are independently constructable with no required coupling:

UDT Constructor Depends on
─────────────── ─────────────────────────── ────────────────────────────
Optimizer f_optimizer_new(…) nothing (grid is caller-owned)
RollingIC f_rolling_ic_new(capacity) nothing
RegimeGate f_regime_gate_new(…) nothing
ObjectiveWeights f_obj_weights_new(…) nothing

Valid combinations:
• Optimizer alone — minimal usage (ARGMAX strategy, no IC classification)
• Optimizer + RollingIC — IC classification per bar, classify() method
• Optimizer + ObjectiveWeights — composite scoring for multi-objective grids
• Optimizer + RegimeGate — gate-filtered observe() calls
• All 4 — full stack

Initialization order: any order; there are no cross-UDT init dependencies.
The caller is responsible for pushing IC values into RollingIC before
calling classify(); a fresh buffer returns 0.0 thresholds (safe default).

─── §C GP MATH VERIFICATION (follow-up 3) ───────────────────────────────

Jacobi solver convergence domain: guaranteed for diagonally dominant K.
K is diagonally dominant when kernel_noise > 0 (K[i,i] = kernel(xi,xi) +
noise ≥ 1 + noise > Σ_{j≠i} kernel(xi,xj) for RBF/Matern52 with ls > 0).
NaN propagation guard: f_optimizer_new() enforces noise ≥ 1e-6 at
construction (see implementation below). NaN in ic_sample is gated by
the na(ic_sample) check in observe() before any array writes.

Grid size constraints (enforced at f_optimizer_new):
grid_size == 1 → runtime.error (BAYESIAN is undefined for a single cell)
grid_size > 30 → runtime.error for BAYESIAN only (Jacobi O(n²×20) budget)
grid_size ≥ 2 → all strategies valid
ARGMAX/UCB/THOMPSON have no upper grid-size constraint.

Unit test specification (see test_icoptimizer_v2_unit.pine):
T1: grid=[1.0] + BAYESIAN → should hit error log (na guard)
T2: grid=[1.0,2.0] + BAYESIAN, 50 observe() calls → ic_var shrinks
T3: grid size=30 + BAYESIAN → no silent NaN on bar 500 / 1000
T4: rising IC synthetic trajectory → propose() returns idx 6 after warmup
T5: peak-in-middle IC → propose() converges to idx 3 (center)

─── §D v5→v6 DELTA (Phase E-2a) ─────────────────────────────────────────

1. //version=5 → //version=6
2. type ICOptimizer → decomposed to 4 independent UDTs (§B)
3. `series float` qualifiers explicit; `simple int` for all ta.* lengths (CE10297)
4. Enum OptimizerKind / KernelKind / ReturnMode replaces bare strings
5. classifyIC scalar bug → RollingIC ring buffer + sort-based percentile
6. detectAndAdjustDomination orphan → method check_domination on Optimizer
7. Monotonic counter → reset_decay(decay) method
8. S9: all multi-line ternaries collapsed to single lines
9. f_ma_for_idx() dispatch in demo for simple-int ta.sma constraint
10. Nested array.get() in f_build_gram / observe() split to locals (COMMA_STATEMENTS)


f_ic_pearson(signal, ret, n)
  Pearson IC: correlation of signal with forward return
  Parameters:
    signal (float): Signal series (e.g. z-score oscillator)
    ret (float): Forward return series (aligned: ret[0] = realized return for signal[horizon])
    n (simple int): Rolling window (simple int — required by ta.correlation)

f_ic_spearman(signal, ret, n)
  Spearman IC via rank correlation approximation
  Parameters:
    signal (float): Signal series
    ret (float): Forward return series
    n (simple int): Rolling window
  Returns: Spearman rank-correlation approximation

f_ic_kendall(signal, ret, n)
  Kendall IC approximation (via concordant/discordant sign correlation)
  Parameters:
    signal (float): Signal series
    ret (float): Forward return series
    n (simple int): Rolling window
  Returns: Kendall tau approximation

f_ic_partial(signal, ret, control, n)
  Partial IC: correlation of signal with ret after removing control variable
  Parameters:
    signal (float): Signal series
    ret (float): Forward return series
    control (float): Control variable to partial out
    n (simple int): Rolling window
  Returns: Partial Pearson IC

f_forward_return(src, horizon, mode, benchmark)
  Compute forward return from source series
  Parameters:
    src (float): Source price series
    horizon (simple int): Look-forward bars
    mode (series ReturnMode): ReturnMode enum
    benchmark (float): Optional benchmark (used only in EXCESS mode; pass na otherwise)

f_label_from_signal(sig, ret, eps)
  Label from signal × return sign match
  Parameters:
    sig (float): Signal value
    ret (float): Realized return
    eps (float): Dead-zone threshold (returns within ±eps labelled 0)
  Returns: 1 = correct direction, -1 = wrong direction, 0 = inside dead-zone

f_rolling_ic_new(capacity)
  Create a new RollingIC buffer
  Parameters:
    capacity (simple int): Number of IC samples to retain

method push(self, ic_val)
  Push a new IC observation into the ring buffer
  Namespace types: RollingIC
  Parameters:
    self (RollingIC)
    ic_val (float)

method classify(self, ic_val, good_pct, bad_pct)
  Classify current IC against ring buffer distribution
  Namespace types: RollingIC
  Parameters:
    self (RollingIC): RollingIC buffer (must have been pushed at least once)
    ic_val (float): Current IC to classify
    good_pct (float): Percentile above which IC is "good" (0–100)
    bad_pct (float): Percentile below which IC is "bad" (0–100)
  Returns: [is_good, is_bad, good_thresh, bad_thresh]

f_optimizer_new(kind, grid, lr, c_ucb, cooldown, kernel_kind, kernel_ls, kernel_noise)
  Create a new Optimizer
  Parameters:
    kind (series OptimizerKind): Strategy
    grid (array<float>): Parameter grid (array<float>, size ≤ 30 for BAYESIAN)
    lr (float): EWM learning rate for ic_ema / ic_var updates (0–1)
    c_ucb (float): UCB exploration constant (ignored for non-UCB)
    cooldown (simple int): Minimum bars between switches
    kernel_kind (series KernelKind): Kernel for BAYESIAN (ignored otherwise)
    kernel_ls (float): Kernel lengthscale (ignored otherwise)
    kernel_noise (float): Observation noise (ignored otherwise)

method propose(self)
  Propose next parameter index to try
  Namespace types: Optimizer
  Parameters:
    self (Optimizer)
  Returns: Selected grid index

method observe(self, idx, ic_sample)
  Record observed IC for a grid cell and update posterior
  Namespace types: Optimizer
  Parameters:
    self (Optimizer)
    idx (int): Grid index that was evaluated
    ic_sample (float): Observed IC value

method reset_decay(self, decay)
  Apply exponential decay to ic_ema and ic_var (prevents monotonic drift)
  Namespace types: Optimizer
  Parameters:
    self (Optimizer)
    decay (float): Decay factor 0..1 (e.g. 0.95 = retain 95% of past)

method check_domination(self, long_n, short_n, ratio_threshold)
  Detect directional signal domination and bump current grid index
  Namespace types: Optimizer
  Parameters:
    self (Optimizer)
    long_n (int): Count of long signals in evaluation window
    short_n (int): Count of short signals in evaluation window
    ratio_threshold (float): Domination ratio (e.g. 4 = 4:1 imbalance)
  Returns: [was_dominated, direction_str] direction_str = "long" | "short" | "none"

method current_param(self)
  Get current parameter value from grid
  Namespace types: Optimizer
  Parameters:
    self (Optimizer)

f_find_optimal_param(testParams, icValues, currentParam, smoothing)
  Find optimal parameter from arrays (v1-compatible, wraps Optimizer.propose)
  Parameters:
    testParams (array<float>): Grid array
    icValues (array<float>): IC values for each grid cell (same size)
    currentParam (float): Current param (for EWM smoothing)
    smoothing (simple float): EWM lr (0–1)
  Returns: [newParam, bestIC, bestIdx]

f_regime_gate_new(mode, threshold, confirm_bars)
  Create RegimeGate
  Parameters:
    mode (string)
    threshold (float)
    confirm_bars (simple int)

method is_open(self, ic_val, bars_above)
  Check if gate is open given current IC and a rolling counter
  Namespace types: RegimeGate
  Parameters:
    self (RegimeGate): RegimeGate
    ic_val (float): Current IC
    bars_above (int): Rolling bars-above-threshold counter (caller maintains)
  Returns: bool gate_open

f_obj_weights_new(w_ic, w_hitrate, w_freq_penalty, w_drawdown_penalty)
  Create ObjectiveWeights
  Parameters:
    w_ic (float)
    w_hitrate (float)
    w_freq_penalty (float)
    w_drawdown_penalty (float)

f_composite_score(w, ic, hitrate, freq, drawdown)
  Compute composite score for a grid cell
  Parameters:
    w (ObjectiveWeights): ObjectiveWeights
    ic (float): IC value for cell
    hitrate (float): Hit rate 0..1 for cell
    freq (float): Signal frequency 0..1 (higher = more signals = penalized)
    drawdown (float): Max drawdown magnitude (positive float)
  Returns: Composite score (higher = better)

f_optimizer_serialize(self)
  Serialize Optimizer state to a compact CSV string
  Parameters:
    self (Optimizer): Optimizer to serialize
  Returns: string blob (pass to f_optimizer_restore to reconstruct ic_ema/ic_var)

f_optimizer_restore(self, blob)
  Restore ic_ema/ic_var/visits from serialized blob into an existing Optimizer
  Parameters:
    self (Optimizer): Optimizer (grid must already be initialized with correct size)
    blob (string): String from f_optimizer_serialize
  Returns: self (mutated in place)

f_diag_table(self, gate, weights, pos, max_rows)
  Render diagnostics table for Optimizer state
  Parameters:
    self (Optimizer): Optimizer
    gate (RegimeGate): RegimeGate (pass na if unused)
    weights (ObjectiveWeights): ObjectiveWeights (pass na if unused)
    pos (string): Table position (e.g. position.bottom_right)
    max_rows (simple int): Maximum grid rows to display (capped at array.size(grid))
  Returns: table reference

f_diag_panel(self, height)
  Render sparkline-style panel (one plot bar per grid cell, height = ic_ema)
  Parameters:
    self (Optimizer): Optimizer
    height (float): Panel height in price units (caller scales)
  Returns: label(na) (renders labels directly)

f_kind_str(k)
  Parameters:
    k (series OptimizerKind)

f_kernel_str(k)
  Parameters:
    k (series KernelKind)

f_return_mode_str(m)
  Parameters:
    m (series ReturnMode)

RollingIC
  Rolling IC ring buffer for proper percentile computation
  Fields:
    samples (array<float>): Circular buffer of IC observations
    head (series int): Write head (mod capacity)
    capacity (series int): Max samples to retain

Optimizer
  Optimizer — unified UDT for all 4 strategies
  Fields:
    kind (series OptimizerKind): Strategy: ARGMAX | UCB | THOMPSON | BAYESIAN
    grid (array<float>): Discrete parameter grid (size ≤ 30 for BAYESIAN)
    ic_ema (array<float>): Posterior mean per cell (EWM updated)
    ic_var (array<float>): Posterior variance per cell (UCB/Thompson/Bayes)
    visits (array<int>): Visit count per cell
    lr (series float): EWM learning rate for ic_ema / ic_var updates
    c_ucb (series float): Exploration coefficient (UCB only)
    cooldown (series int): Minimum bars between parameter changes
    last_change_bar (series int): Bar index of last change
    current_idx (series int): Currently selected grid index
    kernel_matrix (array<float>): Flattened len(grid)² Gram matrix (BAYESIAN only)
    kernel_kind (series KernelKind): RBF | MATERN52 (BAYESIAN only)
    kernel_ls (series float): Kernel lengthscale (BAYESIAN only)
    kernel_noise (series float): Observation noise σ² (BAYESIAN only)
    total_visits (series int): Cumulative visit count (for UCB log normalizer)
    decay_factor (series float): EWM decay applied by reset_decay (0..1; 1=no decay)

RegimeGate
  RegimeGate — IC regime filter
  Fields:
    mode (series string): "positive" | "any" | "top_pct"
    threshold (series float): IC threshold for "positive" or percentile for "top_pct"
    confirm_bars (series int): Bars IC must stay above threshold before gate opens

ObjectiveWeights
  ObjectiveWeights — composite scoring weights
  Fields:
    w_ic (series float): Weight on IC component
    w_hitrate (series float): Weight on hit-rate component
    w_freq_penalty (series float): Penalty for excessive signal frequency
    w_drawdown_penalty (series float): Penalty for drawdown
Phát hành các Ghi chú
v2

Thông báo miễn trừ trách nhiệm

Thông tin và các ấn phẩm này không nhằm mục đích, và không cấu thành, lời khuyên hoặc khuyến nghị về tài chính, đầu tư, giao dịch hay các loại khác do TradingView cung cấp hoặc xác nhận. Đọc thêm tại Điều khoản Sử dụng.