CTDMM · Codex

Pine Script Optimization Guide

Optimizing TradingView Pine Scripts for high-frequency strategies — a CTDMM technical how-to focused on performance, confluence logic, and a lineage-grade pine script generator pattern.

Why Pine Script needs a discipline

Pine v5 runs one bar at a time inside TradingView's sandbox. Every tick your script wastes on redundant math, orphan series, or over-drawn plot() calls widens the gap between signal and execution. For high-frequency confluence work — the CTDMM way — you engineer the script like a protocol: Input → Process → Output, with state that survives repaint and logic that survives your own future edits.

1. Structure: the CTDMM engine layout

Split every strategy into four blocks in this exact order. This is the same shape the MioS Codex uses for pattern artifacts.

//@version=5
strategy("CTDMM Confluence Engine", overlay=true, process_orders_on_close=true,
     calc_on_every_tick=false, max_labels_count=50)

// 1) INPUTS — grouped, typed, documented
grpTrend = "Trend Engine"
emaFast  = input.int(21,  "EMA fast",  minval=2, group=grpTrend)
emaSlow  = input.int(55,  "EMA slow",  minval=3, group=grpTrend)
useHTF   = input.bool(true, "Confirm on HTF",    group=grpTrend)
htf      = input.timeframe("240", "HTF",         group=grpTrend)

// 2) STATE — series computed once per bar
[emaF, emaS] = [ta.ema(close, emaFast), ta.ema(close, emaSlow)]
htfClose     = request.security(syminfo.tickerid, htf, close, lookahead=barmerge.lookahead_off)

// 3) LOGIC — pure booleans, no side effects
bullTrend = emaF > emaS and (not useHTF or close > htfClose)
bearTrend = emaF < emaS and (not useHTF or close < htfClose)
longSig   = ta.crossover(close, emaF) and bullTrend
shortSig  = ta.crossunder(close, emaF) and bearTrend

// 4) ACTIONS — orders and rendering only
if longSig  and strategy.position_size <= 0
    strategy.entry("L", strategy.long)
if shortSig and strategy.position_size >= 0
    strategy.entry("S", strategy.short)

plot(emaF, "EMA fast", color=color.new(color.aqua, 0))
plot(emaS, "EMA slow", color=color.new(color.orange, 0))

2. Performance rules that actually matter

  • Compute once, reuse. Assign every ta.* call to a variable at bar top. Calling ta.ema() twice compiles twice.
  • Never branch a series. x = cond ? ta.rsi(close,14) : nabreaks Pine's series recursion. Compute rsi = ta.rsi(close, 14)unconditionally, then gate its use.
  • lookahead_off on every HTF fetch. Prevents future-bar leaks — the #1 cause of a strategy that backtests like a god and fails live.
  • calc_on_every_tick=false for backtests; only flip on for live intrabar signals you actually consume.
  • Cap max_labels_count / max_lines_count.Uncapped drawing crashes the runtime on symbols with dense history.
  • Prefer math.* over ternaries. math.max, math.sign, math.avg vectorize and read cleaner than nested ? : chains.

3. CTDMM confluence logic

A confluence signal is not a single indicator — it's a vote across orthogonal axes. Score each axis 0 or 1, sum, and fire when the sum crosses a threshold. This is the same rubric MioS uses for its risk radar.

// Axes — pick 4-6, no more. Correlated axes double-count and lie.
trend   = emaF > emaS ? 1 : 0
mom     = ta.rsi(close, 14) > 55 ? 1 : 0
vol     = ta.atr(14) > ta.sma(ta.atr(14), 50) ? 1 : 0
struct  = close > ta.highest(high, 20)[1] ? 1 : 0
htfOk   = close > htfClose ? 1 : 0

confluence = trend + mom + vol + struct + htfOk
longFire   = ta.crossover(confluence, 3)   // 3 of 5 axes agree

Rule: never add an axis you cannot explain in one sentence. A CTDMM script has a lineage; every input has a reason.

4. The Pine Script generator pattern

When you build many strategies (as MioS does), stop hand-writing them. Treat Pine as an output format from a template. A minimal generator has three layers:

  1. Schema — a typed spec (JSON / Zod / Pydantic) describing inputs, axes, weights, thresholds, and execution rules.
  2. Renderer — a pure function that walks the schema and emits Pine v5 source in the four-block layout above.
  3. Verifier — a smoke test that pastes the output into TradingView's compiler via API-less lint (or a local Pine linter) and refuses to ship on error.

Every generated script inherits the same header, the same guardrails (lookahead_off, capped draws, computed-once series), and the same confluence rubric. The generator is the only place a rule can change — the fleet updates in one edit.

5. Shipping checklist

  • Every request.security uses lookahead_off.
  • No ta.* call appears twice with identical arguments.
  • No indicator is computed inside a conditional branch.
  • strategy(...) declares process_orders_on_close=true for backtests.
  • max_labels_count and max_lines_count are set.
  • Confluence axes ≤ 6, each explainable in one sentence.
  • Alert conditions mirror entry conditions exactly — no drift.