Indicator

Equalhigh - Lepage Dual-Regime DetectorEqualhigh — Lepage Dual-Regime Detector
User Manual
Overview
The Equalhigh Lepage Dual-Regime Detector is a non-parametric change-point indicator for PulseWire. It is designed to identify recent changes in either:
Location: the central level of the return distribution.
Scale: the dispersion of the return distribution.
Both simultaneously: a mixed structural break.
Unlike a conventional momentum oscillator, the indicator does not ask whether price is overbought or oversold. It asks whether recent return behavior is statistically different from earlier return behavior inside the active window.
This is a diagnostic regime detector, not an automatic buy-and-sell system.
Why use a location-scale test?
A market transition does not always begin with an obvious directional move. Sometimes the median return changes while volatility remains stable. In other cases, volatility expands or contracts before a clear directional shift becomes visible.
The Lepage framework combines two rank-based components:
The Wilcoxon rank-sum component measures a change in location.
The Ansari–Bradley component measures a change in scale.
The combined statistic can therefore detect more types of structural change than a location-only test.
Observation series
The test is applied to multi-bar logarithmic returns:
Observation = 100 × ln(Source / Source )
Using returns instead of raw prices reduces the tendency to classify the normal upward drift of an asset as a permanent structural break.
Logarithmic returns require positive source values. The indicator remains unavailable when the active window contains invalid or non-positive source observations.
Core calculation
For every active window, the script:
Stores the return observations chronologically.
Assigns average Wilcoxon ranks to equal observations.
Assigns average Ansari–Bradley center-weighted scores to equal observations.
Tests every split that leaves at least the selected Minimum segment size on both sides.
Standardizes the location and scale score sums at each split.
Calculates the Lepage statistic:
L = Z_location² + Z_scale²
Selects the split with the highest Lepage statistic.
Calculates the fixed-split asymptotic p-value:
p_fixed ≈ exp(−L / 2)
Applies a conservative Bonferroni correction for all admissible splits:
p_scan = min(1, Number of tested splits × p_fixed)
Uses medians and median absolute deviations to classify the type and practical size of the detected change.
The scan correction is important because selecting the strongest result from many candidate splits would otherwise make the displayed p-value too optimistic.
Understanding the components
Location Z
The location component is displayed with an intuitive directional sign:
Location Z > 0: the later segment shifted upward.
Location Z < 0: the later segment shifted downward.
A larger absolute value represents stronger rank-based location evidence.
Scale Z
The scale component describes the change in return dispersion:
Scale Z > 0: the later segment became more dispersed.
Scale Z < 0: the later segment became less dispersed.
A larger absolute value represents stronger rank-based scale evidence.
The combined statistic squares both components, so the p-value measures the strength of the overall break. The signs are used to interpret its direction.
Color system
Color or marker
Interpretation
Green — LEVEL +
Confirmed positive location shift without a qualifying scale shift
Red — LEVEL −
Confirmed negative location shift without a qualifying scale shift
Purple — VOL +
Confirmed scale expansion without a qualifying location shift
Blue — VOL −
Confirmed scale compression without a qualifying location shift
Orange — MIXED
Confirmed location and scale shift occurring together
Yellow — ?
Possible break with incomplete statistical confirmation
Gray
No currently actionable break
A volatility expansion is not automatically bearish, and a volatility compression is not automatically bullish. These states describe dispersion, not market direction.
The orange mixed state does not encode direction by itself. Use Median Shift and MAD Scale Shift in the dashboard to determine whether the mixed change combines an upward or downward level shift with expansion or compression.
Confidence line
The main line is calculated as:
Scan-adjusted confidence = 100 × (1 − p_scan)
The default boundaries are:
95: confirmed statistical zone when the confirmed p-value is 0.05.
85: possible statistical zone when the possible-break p-value is 0.15.
The line color reflects the currently classified regime.
Important: this confidence value is not the probability that price will rise, the probability that a trade will be profitable, a win rate, or a forecast-accuracy score.
Confirmation logic
A confirmed regime requires all of the following:
The scan-adjusted p-value is less than or equal to Confirmed scan p-value.
The estimated break is no older than Maximum actionable break age.
At least one component passes its practical-effect threshold.
The contributing component also passes Minimum component Z.
Positive or negative location shift
The robust location effect reaches Minimum location shift.
The absolute Location Z reaches Minimum component Z.
The scale component does not independently pass all its confirmation filters.
The sign of the median shift determines positive or negative classification.
Scale expansion or compression
The symmetric MAD scale-ratio change reaches Minimum scale-ratio change.
The absolute Scale Z reaches Minimum component Z.
The location component does not independently pass all its confirmation filters.
The MAD ratio determines expansion or compression.
Mixed break
Both the location and scale components pass their effect-size and component-Z filters.
Possible break
The scan-adjusted p-value is above the confirmed threshold but no higher than the possible-break threshold. At least one component must also reach half of its normal effect-size and component-Z requirements.
Dashboard
Dashboard field
Meaning
Lepage State
Current regime classification
Scan-Adj P
Bonferroni-adjusted approximate p-value for the split scan
Break Age
Estimated number of bars since the selected split
Location Z
Directional standardized Wilcoxon component
Scale Z
Directional standardized Ansari–Bradley component
Median Shift
Post-break median return minus pre-break median return, in percentage points
Location Effect
Median shift divided by a robust sigma estimate
MAD Scale Shift
Conventional percentage change from pre-break MAD to post-break MAD
Additional dashboard states include:
FILTERED BREAK: the combined statistic is significant and recent, but neither component passes all practical-effect and Z filters.
OLD BREAK: the combined statistic remains significant inside the window, but the estimated split is older than Maximum actionable break age.
STABLE REGIME: no currently actionable or possible break.
Input guide
1. Observations
Price sourceSeries used to calculate logarithmic returns. Close is the standard setting.
Log-return horizonNumber of bars covered by each return observation. Lower values react to short moves. Higher values emphasize slower market behavior but create more overlap between consecutive observations.
Lepage windowNumber of return observations in each rolling test. Short windows react faster but are noisier. Long windows are more stable but respond later.
Minimum segment sizeMinimum number of observations required before and after every candidate split. Increasing it reduces unstable edge detections but prevents the test from selecting extremely recent breaks.
2. Validation
Confirmed scan p-valueMaximum adjusted p-value for confirmation. The default is 0.05. Lower values produce fewer and more selective events.
Possible-break scan p-valueMaximum adjusted p-value for the yellow early-warning state. The default is 0.15.
Maximum actionable break ageMaximum number of bars allowed between the estimated split and the current bar.
Minimum location shiftMinimum median shift measured in robust sigma units. The robust sigma is 1.4826 × window MAD, with standard deviation used as a fallback when necessary.
Minimum scale-ratio change (%)Minimum symmetric difference between pre-break and post-break MAD. Symmetric measurement treats a doubling and a halving of scale as equally large changes for filtering purposes.
Minimum component ZPrevents a regime label from being attributed to a component that contributed too little to the combined Lepage statistic. The default is 1.00.
Confirm signals at bar closeWhen enabled, new markers and alert events are confirmed only when the current bar closes. This is the recommended setting.
3. Display
These settings independently control regime backgrounds, confirmed labels, possible-break markers, and the dashboard.
Suggested starting profiles
Use case
Return horizon
Window
Minimum segment
Maximum age
Location effect
Scale change
Component Z
General swing trading
5
60
10
10
0.25
25%
1.00
Faster monitoring
3
50
8
7
0.30
30%
1.25
Slower regime analysis
10
90
15
15
0.35
30%
1.00
These profiles are starting points, not optimized trading parameters. Test settings across different assets and unseen market periods.
Interpretation examples
Green location event
Suppose the dashboard shows:
Scan-adjusted p-value: 0.03
Break age: 6
Median shift: +0.80 pp
Location effect: +0.55 sigma
MAD scale shift: +10%
The evidence supports a recent upward change in the central return level, while the scale change remains below its filter.
Purple volatility-expansion event
Suppose the location effect is small, but post-break MAD is 60% higher, Scale Z is strongly positive, and the adjusted p-value is below 0.05. The indicator classifies a volatility expansion. Market direction must be determined separately.
Orange mixed event
If both median returns and dispersion change materially, the indicator displays MIXED. A positive Median Shift with a positive MAD Scale Shift represents improving returns accompanied by expanding volatility. A negative Median Shift with expanding volatility can represent a more hostile risk regime.
Practical workflow
Use ordinary candlesticks on a liquid instrument.
Keep Confirm signals at bar close enabled.
Treat yellow as an observation state rather than an entry instruction.
When a confirmed event appears, inspect Location Z, Scale Z, Median Shift, and MAD Scale Shift.
Confirm the interpretation with price structure, volume, liquidity, and higher-timeframe context.
Define entry, invalidation, position sizing, and exit rules independently.
The indicator is particularly useful as a regime filter. For example, a trend strategy may be treated differently during purple volatility expansion than during blue volatility compression.
Alerts
Six alert conditions are available:
Lepage — Possible break
Lepage — Positive level shift
Lepage — Negative level shift
Lepage — Volatility expansion
Lepage — Volatility compression
Lepage — Mixed regime break
A confirmed alert fires when a qualifying state first appears, when the confirmed regime type changes, or when the estimated split resets to a more recent point. A possible alert follows equivalent first-appearance and break-reset logic.
When bar-close confirmation is enabled, configure PulseWire alerts as Once Per Bar Close.
Repainting and event timing
The script does not use future data, lookahead, or a negative plot offset. It places a marker on the bar where the break is detected and never moves that marker backward to the estimated historical split.
However, the estimator is rolling. As a new bar enters the window and an old bar leaves it, the selected split, component scores, p-value, break age, and current state can change. Values can also fluctuate on an open real-time bar. Bar-close confirmation prevents provisional intrabar markers from being treated as confirmed events.
Historical events are calculated only from information available on their respective bars.
Statistical limitations
The fixed-split chi-square p-value is asymptotic rather than exact.
Bonferroni correction is conservative because the candidate splits are dependent.
The correction covers the splits inside one window, not repeated testing across every bar in the chart.
Consecutive multi-bar returns overlap and are not independent. The adjusted p-value should therefore be interpreted as comparative evidence rather than a perfectly calibrated probability.
The classical Lepage components are most naturally interpreted as location and scale tests under regular distributional conditions. Strong skew changes or complex distribution changes can affect both components.
The detector selects one dominant split per rolling window. Multiple rapid changes can interfere with one another.
A statistically significant regime change does not guarantee persistence, directional continuation, or trading profitability.
Median absolute deviation can be close to zero on discrete or insufficiently variable data. The script uses a small numerical floor, but scale percentages can still become unusually large.
Results on Heikin Ashi, Renko, Range, Kagi, Point & Figure, or other synthetic charts describe transformed data rather than ordinary traded prices.
Always evaluate the indicator on unseen data and combine it with independent risk controls.
Data Window outputs
The script exposes:
State code.
Scan-adjusted p-value.
Estimated break age.
Location Z component.
Scale Z component.
Median shift in percentage points.
Robust location effect.
MAD scale change percentage.
Lepage statistic.
State codes are:
Code
State
4
Mixed location-scale break
3
Scale expansion
2
Positive location shift
1
Possible break
0
Stable, filtered, or old break
−2
Negative location shift
−3
Scale compression
PulseWire publication metadata
Primary category: Oscillators
Secondary category: Trend Analysis
Suggested tags: Lepage Test, Change Point, Regime Detection, Statistics, Non-Parametric, Volatility, Structural Break
References
Y. Lepage, “A Combination of Wilcoxon's and Ansari-Bradley's Statistics,” Biometrika, 1971.
F. Rublík, “The Multisample Version of the Lepage Test,” Kybernetika, Vol. 41, No. 6, 2005, pp. 713–733: paper.
G. J. Ross, D. K. Tasoulis and N. M. Adams, “Nonparametric Monitoring of Data Streams for Changes in Location and Scale,” Technometrics, Vol. 53, No. 4, 2011, pp. 379–389: DOI.
H. Murakami, “A Nonparametric Location–Scale Statistic for Detecting a Change Point,” The International Journal of Advanced Manufacturing Technology, Vol. 61, 2012, pp. 449–455: DOI.
Disclaimer
This indicator is provided for research and educational purposes. It does not constitute investment advice, a recommendation, or a guarantee of future performance. Trading involves risk, including the possible loss of capital. Indicator

Equalhigh JAPANESE TRIPLE RCIEQUALHIGH — JAPANESE TRIPLE RCI 9/26/52
OVERVIEW
This indicator implements the triple Rank Correlation Index configuration commonly used in Japanese technical analysis.
It combines three RCI horizons:
• RCI 9 — short-term momentum
• RCI 26 — swing direction
• RCI 52 — underlying trend
Unlike RSI, RCI does not primarily measure the magnitude of price changes. It measures how closely the chronological order of the bars corresponds to the ranked order of their prices.
The indicator is designed to identify:
• Progressive market reversals
• Momentum recoveries after pullbacks
• Bullish or bearish multi-horizon alignment
• Trend deterioration
• Choppy and conflicting market conditions
CALCULATION
RCI is based on Spearman’s rank correlation between:
1. The chronological rank of each bar
2. The price rank of each bar
The result is scaled from −100 to +100.
• +100 indicates a perfectly ordered upward movement.
• −100 indicates a perfectly ordered downward movement.
• Values near zero indicate weak directional organization or conflicting price action.
This implementation calculates the full Spearman rank correlation and assigns an average rank to tied prices.
INDICATOR LINES
CYAN — RCI 9: SHORT-TERM IMPULSE
RCI 9 reacts quickly to changes in momentum. It is useful for detecting early rebounds, short-term exhaustion and the first phase of a possible reversal.
ORANGE — RCI 26: SWING DIRECTION
RCI 26 confirms whether the short-term movement is developing into a more meaningful swing.
PURPLE — RCI 52: UNDERLYING TREND
RCI 52 is the slowest component. It represents the broader directional structure and acts as the main trend filter.
KEY LEVELS
+80: Upper extreme zone
+50: Strong positive momentum
0: Directional equilibrium
−50: Strong negative momentum
−80: Lower extreme zone
An extreme RCI reading does not automatically mean that price must reverse. A strong trend can keep the RCI near +80 or −80 for an extended period.
SIGNALS
R+ — EARLY BULLISH REVERSAL
An R+ signal appears when:
• RCI 9 crosses upward out of the lower extreme zone
• RCI 26 is already rising
This identifies an early improvement in price organization. It is not a complete trend confirmation and should ideally be supported by price action, volume or a support level.
R− — EARLY BEARISH REVERSAL
An R− signal appears when:
• RCI 9 crosses downward out of the upper extreme zone
• RCI 26 is already falling
This indicates early deterioration in short-term momentum.
A+ — NEW BULLISH ALIGNMENT
An A+ signal appears when RCI 9, RCI 26 and RCI 52 become positive simultaneously.
This confirms that short-term momentum, the swing structure and the underlying trend are all on the bullish side of equilibrium.
A− — NEW BEARISH ALIGNMENT
An A− signal appears when all three RCI horizons become negative simultaneously.
This confirms bearish alignment across the three observed time horizons.
PRACTICAL INTERPRETATION
STRONG BULLISH REGIME
• RCI 52 is above zero
• RCI 26 is above zero or recovering
• RCI 9 moves out of a temporary pullback
• An R+ or A+ signal is supported by bullish price action
STRONG BEARISH REGIME
• RCI 52 is below zero
• RCI 26 is below zero or deteriorating
• RCI 9 turns down after a temporary recovery
• An R− or A− signal is supported by bearish price action
POSSIBLE PROGRESSIVE REVERSAL
A bullish reversal often develops in stages:
1. RCI 9 turns upward
2. RCI 26 begins to recover
3. RCI 52 stabilizes or turns upward
4. All three RCIs eventually move above zero
The bearish sequence is the opposite.
CHOPPY OR LOW-CONVICTION MARKET
When the three lines repeatedly cross each other around zero, the market lacks a stable directional structure. Trend-following signals are generally less reliable in this environment.
SUGGESTED WORKFLOW FOR SWING TRADING
For a potential long setup:
1. Confirm that price is near support or breaking above resistance.
2. Look for an R+ early reversal signal.
3. Check that RCI 26 is rising.
4. Prefer situations where RCI 52 is positive, stabilizing or improving.
5. Use A+ as stronger multi-horizon confirmation.
6. Define risk with price structure or an ATR-based stop.
For a potential short setup, apply the opposite conditions.
DEFAULT SETTINGS
• Short RCI: 9
• Medium RCI: 26
• Long RCI: 52
• Source: Close
• Extreme level: 80
• Reversal trigger: 80
• Signal confirmation: Bar close
The default 9/26/52 configuration is suitable for swing analysis on daily and four-hour charts. Because the periods represent bars, their actual duration changes with the selected timeframe.
USER SETTINGS
RCI Short
Controls the sensitivity of short-term momentum. A lower value reacts faster but produces more noise.
RCI Medium
Represents the intermediate swing structure.
RCI Long
Acts as the broader trend filter. Higher values provide a slower and more stable reading.
Extreme Level
Defines the upper and lower visual zones. The default setting is +80 and −80.
Reversal Trigger
Determines the level used to generate early R+ and R− reversal signals.
Confirm Only at Bar Close
When enabled, signals are validated only after the current candle closes. This helps prevent temporary intrabar signals.
Show Early Reversals
Displays the R+ and R− markers.
Show 9/26/52 Alignments
Displays the A+ and A− markers.
Shade Extreme Zones
Highlights the upper and lower RCI extreme areas.
Shade Background by Alignment
Optionally colors the indicator background according to bullish or bearish triple alignment.
ALERTS
Four PulseWire alert conditions are included:
• RCI — Early Bullish Reversal
• RCI — Early Bearish Reversal
• RCI — New Bullish Alignment
• RCI — New Bearish Alignment
For more stable signals, alerts should normally be configured “Once Per Bar Close.”
REPAINTING BEHAVIOR
The indicator uses only current and historical price data. It does not use future bars.
RCI values can naturally change while the current candle is still forming. When “Confirm Only at Bar Close” is enabled, signal markers and alerts are confirmed at the candle close and do not subsequently repaint on completed bars.
LIMITATIONS
RCI is a market-structure and momentum indicator, not a standalone trading system.
It does not account for:
• Fundamental valuation
• Earnings announcements
• Liquidity conditions
• Volatility regime changes
• Support and resistance
• Position sizing
• Transaction costs
Extreme readings should not automatically be interpreted as buy or sell signals. The indicator is most effective when combined with price structure, volume, volatility and disciplined risk management.
DISCLAIMER
This indicator is provided for educational and analytical purposes only. It does not constitute financial advice or a recommendation to buy or sell any financial instrument. Past performance does not guarantee future results.
Indicator

Market Leadership Structure 3D [NeuralMarkets]OVERVIEW
Market Leadership Structure 3D compares six assets to show who is driving the group, who is following, and whether leadership is persistent or rotating.
It separates relative rank from absolute evidence. The top-ranked asset is always shown as the relative candidate, but the script reports NO CLEAR LEADER unless that candidate has sufficient evidence, separation from the runner-up, and at least one qualified outgoing relationship.
The indicator provides three views:
• Summary
• Leadership Terrain
• Parameter Stability
HOW IT WORKS
For every asset pair, the model compares both possible lead-lag directions.
Directional evidence blends the strongest positive lagged correlation with the average positive correlation across the tested lags.
Directional advantage A → B = Evidence A → B − Evidence B → A
An edge is retained only when it passes Minimum Forward Evidence and exceeds the reverse direction by Minimum Directional Asymmetry.
Qualified edges form a directed network using NeuralMarketsNetworkToolkit:
Net influence = Outbound influence − Inbound influence
The asset with the highest smoothed net influence receives rank #1. Recognition requires separate absolute-evidence and rank-separation thresholds, so being ranked first does not automatically imply meaningful leadership.
READING THE SUMMARY
Recognized — The accepted leader or NO CLEAR LEADER.
Relative candidate — The asset currently ranked #1, even when evidence is insufficient for recognition.
Absolute evidence — Strength and coverage of the candidate’s qualified outgoing relationships. It is not min-maxed and does not force the strongest asset to score 100.
Rank separation — Normalized gap between the top two assets. A small gap means leadership is closely contested.
Leader persistence — Share of the history window occupied by the current recognized leader. No-clear-leader bars remain separate states.
Clear-state share — Percentage of the history window in which any clear leader existed.
Rotation risk — LOW, MEDIUM, HIGH, or UNDEFINED when no leader is recognized.
Concentration — Whether directional influence is concentrated or broadly distributed.
The optional ranking table shows all six assets with net influence, absolute evidence, and normalized leadership.
LEADERSHIP TERRAIN
The waterfall mesh displays all six assets through recent history:
• X-axis: ticker
• Depth: historical slices from NOW toward older bars
• Height: normalized leadership, approximately −1 to +1
Above zero indicates more outbound than inbound influence. Below zero indicates follower behavior. Each historical ridge is drawn as a colored curtain from the zero plane, while rails connect each ticker through time. Older slices fade to keep the current structure prominent.
Look for:
• A sustained elevated ridge — persistent leadership
• A ridge rising toward NOW — strengthening leadership
• A ridge falling toward zero — fading leadership
• Two similar current peaks — close competition
• Rapidly alternating peaks — unstable rotation
• A flat surface near zero — weak directional structure
A leader marker appears only when the recognition requirements are satisfied.
PARAMETER STABILITY
This view rebuilds the network across a 6 × 6 grid:
• X-axis: relationship lookback
• Depth: maximum lag from 1 to 6 bars
• Height and color: robustness
Robustness combines 50% absolute evidence, 30% rank separation, and 20% agreement with the currently recognized leader. Cells without qualified outgoing coverage score zero.
Broad elevated regions indicate that leadership survives several parameter choices. An isolated peak suggests that the result is parameter-sensitive.
HOW TO USE IT
1. Choose a coherent universe
Use a preset or select six related assets. Interpretation is clearest when the group represents one theme, such as cross-asset ETFs, US sectors, or mega-cap stocks.
2. Check the recognized state
If the script reports NO CLEAR LEADER, do not treat the relative candidate as confirmed leadership.
3. Confirm evidence and separation
Prefer cases where the candidate has both meaningful absolute evidence and adequate distance from the runner-up.
4. Check persistence and rotation
Established leadership is generally more credible than a one-bar rank change. Falling persistence, a young leader age, or HIGH rotation risk signals a less settled structure.
5. Inspect Leadership Terrain
Check whether the leader remains above zero through history and whether its ridge strengthens toward NOW. Watch for challengers rising beneath it.
6. Inspect Parameter Stability
Prefer a broad plateau over one sharp peak. If leadership disappears after a small lookback or lag change, it is fragile.
7. Use alerts to trigger review
Alerts identify structural transitions. Combine them with price action, trend, liquidity, and risk management rather than treating them as automatic entries.
UNIVERSE PRESETS
Cross-Asset — SPY, QQQ, IWM, HYG, TLT, DBC
US Sectors — XLK, XLF, XLY, XLI, XLE, XLV
Mega-Cap — NVDA, MSFT, AAPL, META, AMZN, GOOGL
Custom — Six user-selected symbols
IMPORTANT SETTINGS
Relationship Lookback — Estimation window. Shorter values react faster but are noisier.
Maximum Lead Lag — Earlier bars tested. One lag equals one chart bar.
Rank Smoothing — Reduces rank churn at the cost of slower response.
Leadership History — Window used for persistence and rotation statistics.
Minimum Forward Evidence — Minimum blended relationship required for an edge.
Minimum Directional Asymmetry — Required advantage over the reverse direction.
Minimum Absolute Evidence / Rank Separation — Requirements for recognizing a clear leader.
Terrain spacing, skew, separation, and height settings change only the drawing—not the model.
KEY DEFAULTS
Relationship Lookback: 80
Maximum Lead Lag: 5
Directional Weight Power: 1.25
Rank Smoothing: 3
Leadership History: 100
Minimum Forward Evidence: 0.18
Minimum Directional Asymmetry: 0.02
Minimum Absolute Evidence: 15
Minimum Rank Separation: 5%
Terrain History: 8 slices spaced 5 bars apart
ALERTS
Clear Leader Rotation — Fires only on a direct transition between two different recognized leaders. A transition through NO CLEAR LEADER is not counted.
High Rotation Risk — Fires when risk changes to HIGH while a clear leader exists.
Clear Leader Established — Fires when the candidate first satisfies the recognition requirements.
Clear Leader Lost — Fires when the recognized leader no longer satisfies them.
LIMITATIONS
This indicator is descriptive market-structure research, not a calibrated probability or a claim of predictive alpha.
Lagged correlation and directional asymmetry do not establish causality. The model focuses on positive lead-lag relationships and does not explicitly represent inverse edges.
Parameter Stability measures current in-sample robustness, not out-of-sample forecasting performance. The terrain is a 2D perspective projection whose appearance depends on chart zoom.
Results depend on timeframe, available history, liquidity, and alignment between trading sessions. All six symbols should have sufficient data.
Use rank to identify the relative candidate. Use evidence, separation, persistence, terrain, and parameter stability to decide how seriously that ranking should be taken.
Indicator

OptiPine: High-Performance Caching and Data PipelinesOptiPine is a high performance architecture library for Pine Script™, built for algorithms that push beyond ordinary indicator workloads. It turns caching, sparse updates, reusable storage and workload-aware data structures into practical APIs that stay small at the call site.
In a small indicator, optimization is often optional. In a rendering engine, machine learning library, simulation, dashboard or object system, it can determine whether a feature runs at all. The problem is rarely one slow formula. It is the thousands of unnecessary operations around it: recalculating unchanged results, shifting rolling arrays, scanning large collections for a few changes, and moving stored objects when one disappears.
OptiPine attacks that layer with techniques used in projects such as Pine3D and NeuraLib . The idea is simple: do less work, move less data, and let the representation follow the workload.
Compared with conventional Pine implementations of the same task, OptiPine's optimized paths commonly ran 15% to 40% faster . Sparse updates and indexed lookups exceeded 90% when the alternative scanned or searched the full collection.
Most users can stay entirely within the high-level API. A Memo cache with several dependencies looks like this:
// Pseudocode: trendRegime and volatilityRegime are floats;
// rebuildModel() is a pure calculation.
var op.FloatMemo model = op.floatMemo()
if model.staleOn(trendRegime, volatilityRegime)
model.store(rebuildModel(trendRegime, volatilityRegime))
float result = model.get()
// Output: rebuildModel() runs once, then only when either regime changes.
Memo owns the previous dependencies, first-run state, validity and cached result. The caller only declares what the result depends on.
----------------------------------------------------------------------------------------------------------------
🔷 DO NOT CALCULATE THE SAME THING TWICE
The fastest expensive calculation is the one that never needed to run. Models, simulations and generated geometry often remain valid across many script executions.
Memo is the direct choice when the cached result is an int, float, bool, string or color. staleOn() checks up to four floats, two integers, one Boolean and one string; store() saves a rebuilt value, and get() returns it.
Many models respond to regimes rather than every tiny change in raw data. Round the inputs into meaningful regimes, pass them to staleOn() , and the model runs only when a regime changes.
In practice: Memo is useful for scenario models, parameter sweeps, numerical solvers and other expensive pure calculations that reduce to one primitive result. If its dependencies repeat on nine out of ten executions, it avoids roughly 90% of those model runs.
For collections or a variable dependency list, use Memo's explicit begin() , dependencies.watch*() and miss() lifecycle.
Keep guarded work pure: Stateful ta.* and similar history-dependent calls must remain outside Memo and Watch guards. Compute them every bar, then pass their results into the guarded calculation.
🔸 WATCH: CHANGE DETECTION WITHOUT RESULT STORAGE
Watch is the lighter choice when the caller already owns the result. Several consumers can observe the same producer independently by giving each its own Watch. changed() returns true on the first observation and whenever one scalar, primitive array or OptiPine row ring changes. Row rings expose an internal revision, so checking them is O(1).
For a single source, the dependency check should take less attention than the calculation it protects. Here another component supplies one caller-owned feature array:
// Pseudocode: getFeatureSnapshot() supplies an array.
array features = getFeatureSnapshot()
var op.Watch featureWatch = op.watch()
var float modelScore = na
if featureWatch.changed(features)
modelScore := evaluateModel(features)
// Output: modelScore is rebuilt only when the features array changes.
Because OptiPine does not own features , it compares the array with a retained snapshot and rewrites that snapshot only after a change. Supported row rings use their internal revision instead. The call stays the same, and this compare-first array pattern measured roughly 35% to 60% faster than rewriting the snapshot every time.
The array comparison is still O(N), so use it when the avoided calculation costs more than the comparison. If the producer already provides one reliable change flag, use the flag directly.
For several dependencies, use an explicit pass. begin() starts the comparison, the typed watch*() methods add dependencies, and finish() returns true if the completed set changed. A Watch remembers dependencies; it does not store the result.
// Pseudocode dependencies: int length, float multiplier,
// and array features.
var op.Watch settingsWatch = op.watch()
var float result = na
settingsWatch.begin()
settingsWatch.watchInt(length)
settingsWatch.watchFloat(multiplier)
settingsWatch.watchFloats(features)
bool dependenciesChanged = settingsWatch.finish()
if dependenciesChanged
result := rebuild(length, multiplier, features)
// Output: result is rebuilt when any observed dependency changes.
Construct the Watch once with var , then run begin() and finish() on every comparison pass. For one dependency, changed(source) is the shorter path.
In practice: Watch fits module boundaries: a model can observe a feature array, a renderer can observe a managed ring, or a cache can observe several mixed settings without duplicating the producer's change logic.
CadenceGate limits how often work may run. due() is periodic; dueWhenChanged() also requires a producer revision and remembers changes until the cadence opens. Use it for intentionally delayed work such as periodic model fitting, not results that must update immediately.
----------------------------------------------------------------------------------------------------------------
🔷 ROLLING HISTORY WITHOUT SHIFTING IT
Rolling histories often perform work that adds nothing to the result. If an array keeps the latest 200 events, removing the oldest one and shifting the other 199 entries is unnecessary.
FloatRowRing and IntRowRing keep fixed-width rows in reusable storage. Once full, the next row overwrites the oldest physical slot while reads remain chronological.
var op.FloatRowRing history = op.floatRowRing(200, 3)
float atr14 = ta.atr(14)
if barstate.isconfirmed
history.push(array.from(close, volume, atr14))
float oldestPrice = history.at(0, 0)
float latestPrice = history.newestAt(0, 0)
// Output: after a confirmed push, these are the oldest and newest retained closes.
A push costs O(width), or O(1) through pushValue() for a width-one ring. Rings also provide chronological windows and gathered rows. When several producers can mutate a ring, a separate consumer can detect its revision with Watch.changed(ring) in O(1).
In practice: Row rings fit pivots, completed trades, sampled features and other fixed event histories.
Performance: A full ring overwrites one row instead of shifting every retained row. Its chronological output uses at most two native contiguous copies, which measured 90% faster than rebuilding a 512-cell, width-four output row by row.
Use RingCursor when several caller-owned arrays need the same circular layout. Ordinary series history such as close should remain native Pine.
----------------------------------------------------------------------------------------------------------------
🔷 KEEP DYNAMIC OBJECTS STABLE
Dynamic objects become surprisingly expensive when identity is tied to array position. If one object is removed from several parallel arrays, every later entry shifts, every synchronized payload array needs the same removal, and every external reference to those positions becomes fragile.
StablePool is not the zone storage itself. It keeps one association: an object ID supplied by the script points to a reusable array slot. The ID answers "which zone is this?" while the slot answers "where is this zone's data stored?"
The example has three different logical zones named A, B and C. Their IDs, 1001, 1002 and 1003, are arbitrary unique values chosen for readability. Real IDs may come from a pivot bar, timestamp, order number or incrementing counter.
const int ZONE_A_ID = 1001
const int ZONE_B_ID = 1002
const int ZONE_C_ID = 1003
var op.StablePool zonePool = op.stablePool()
// This example never has more than two active zones.
var array prices = array.new(2, na)
if barstate.isfirst
// A receives slot 0. B receives slot 1.
= zonePool.acquire(ZONE_A_ID)
= zonePool.acquire(ZONE_B_ID)
prices.set(slotA, 100.0)
prices.set(slotB, 200.0)
// Zone A no longer exists. Its slot becomes available.
zonePool.release(ZONE_A_ID)
// C is a new zone with a new identity, but it can reuse A's old slot.
= zonePool.acquire(ZONE_C_ID)
prices.set(slotC, 300.0)
// Output: B keeps slot 1. C has ID 1003 but reuses A's released slot 0.
// prices is .
Why C needs a new ID: C is a different zone, even though it occupies the same array position A once used. Reusing 1001 would describe A returning, not a new zone C. IDs preserve object identity; slots are only reusable storage addresses.
Several fields, one slot: In production, the same slot usually addresses every field belonging to the object. Continuing the A, B and C lifecycle with four parallel arrays:
const int ZONE_A_ID = 1001
const int ZONE_B_ID = 1002
const int ZONE_C_ID = 1003
var op.StablePool zonePool = op.stablePool()
var array zonePrices = array.new()
var array zoneTimes = array.new()
var array zoneStrengths = array.new()
var array zoneColors = array.new()
if barstate.isfirst
= zonePool.acquire(ZONE_A_ID)
= zonePool.acquire(ZONE_B_ID)
// Grow every payload array to cover the allocated slots.
int required = zonePool.slotCount()
op.ensureSizeFloat(zonePrices, required, na)
op.ensureSizeInt(zoneTimes, required, na)
op.ensureSizeFloat(zoneStrengths, required, na)
op.ensureSizeColor(zoneColors, required, na)
zonePrices.set(slotA, 100.0)
zoneTimes.set(slotA, 10)
zoneStrengths.set(slotA, 0.40)
zoneColors.set(slotA, color.blue)
zonePrices.set(slotB, 200.0)
zoneTimes.set(slotB, 20)
zoneStrengths.set(slotB, 0.80)
zoneColors.set(slotB, color.red)
zonePool.release(ZONE_A_ID)
= zonePool.acquire(ZONE_C_ID)
// C reuses A's slot, so every field at that slot must be overwritten.
zonePrices.set(slotC, 300.0)
zoneTimes.set(slotC, 30)
zoneStrengths.set(slotC, 0.60)
zoneColors.set(slotC, color.lime)
// Output: B keeps slot 1 in every array. C owns slot 0 in every array.
// Nothing is removed or shifted.
acquire(id) returns the slot and whether the ID was newly added. Calling it again for an active ID returns the same slot. release(id) frees the slot, but does not erase its array data, so every field must be overwritten when that slot is reused.
The example preallocates two values because it has at most two active zones. A dynamic script can grow its payload arrays with ensureSize*() whenever acquire() reports a new ID. zonePool.slots() returns the currently active slots as a read-only view.
In practice: One zone slot can index its price, time, color, strength and line across several arrays. In the complete example later, the pivot bar and event type form each zone ID. Releasing one zone frees its slot without shifting other zones or breaking saved positions.
Performance: StablePool is independent of payload layout: its slots can index parallel arrays or one array of UDTs. acquire() , release() and find() are O(1), and releasing an object never shifts caller-owned payloads.
For a few fixed objects, manual indices are simpler. StablePool becomes useful when IDs appear and disappear over time, several payload arrays share the same slots, or other parts of the script retain those positions.
SlotCache is the frame-based alternative. Call begin() , acquire every active key, then call finish() ; previously active keys that were not touched are retired automatically.
----------------------------------------------------------------------------------------------------------------
🔷 UPDATE ONLY WHAT CHANGED
Large state does not imply large change. A dashboard may contain 10,000 cells while only a few change on one bar, or a large object system may need to refresh only a handful of entries.
A conventional dirty-flag array must be cleared and scanned in full. DirtySet stores only the changed indices, removes duplicate marks and begins a new cycle without clearing the entire universe. It is a work list, not payload storage or an ID-to-slot map.
Here StablePool resolves zoneId , the arrays store zone data, and DirtySet schedules the slots that need rebuilding. The event values are pseudocode:
int MAX_ZONES = 50000
var op.StablePool zones = op.stablePool()
var op.DirtySet dirtySlots = op.dirtySet(MAX_ZONES)
var array tops = array.new()
var array bottoms = array.new()
var array midpoints = array.new()
// Start this bar's sparse-work cycle.
dirtySlots.begin()
if zoneGeometryChanged
// StablePool converts the logical ID into a reusable physical slot.
= zones.acquire(zoneId)
if created
op.ensureSizeFloat(tops, zoneSlot + 1, na)
op.ensureSizeFloat(bottoms, zoneSlot + 1, na)
op.ensureSizeFloat(midpoints, zoneSlot + 1, na)
tops.set(zoneSlot, newTop)
bottoms.set(zoneSlot, newBottom)
dirtySlots.mark(zoneSlot)
if zoneStyleChanged
int styleSlot = zones.find(zoneId)
if styleSlot >= 0
dirtySlots.mark(styleSlot) // A second mark of the same slot is ignored.
// Process only the distinct physical slots marked during this bar.
for dirtySlot in dirtySlots.values()
float midpoint = (tops.get(dirtySlot) + bottoms.get(dirtySlot)) * 0.5
midpoints.set(dirtySlot, midpoint)
redrawZone(zones.keyAt(dirtySlot), midpoint)
// Output: one zone is rebuilt once even if geometry and style both mark it.
Repeated marks are deduplicated, and unmarked zones are never visited. Work scales with the number of changed slots, not the size of the collection. If the natural address is already a dense index, mark it directly without StablePool.
In practice: Several producers can mark work, then one consumer updates each affected cell, drawing or record once. With 1% of entries changed, this measured 93% faster than clearing and scanning the full universe.
----------------------------------------------------------------------------------------------------------------
🔷 KEYED LOOKUP WITHOUT GUESSWORK
Keyed lookup appears throughout object systems, caches and grouped data, but no structure fits every key set. Distribution, rebuild frequency and query volume change the best choice. OptiPine sees the completed keys at build() , then selects the lookup shape that fits them.
🔸 TYPED STORES: ONE VALUE PER KEY
A typed store maps each integer key to one primitive value. build() pairs entries at matching positions in the key and value arrays. Consecutive IDs allow direct addressing:
var op.IntFloatStore scores = op.intFloatStore()
if barstate.isfirst
// Four entries are shown for readability; both arrays may be much larger.
scores.build(
array.from(410, 411, 412, 413),
array.from(0.80, 0.30, 0.95, 0.50))
float selected = scores.get(412)
// Output: integer key 412 resolves to float value 0.95.
Lookup is one-way: get(412) returns 0.95 , but values may repeat, so get(0.95) has no general meaning.
What automatic mode chooses:
Consecutive ascending keys: Direct arithmetic indexing.
Compact key ranges: A dense lookup table.
Other unordered keys: A native map when within Pine's map limit.
Ascending sparse keys: Binary search, or a map within that limit when expectedQueries justifies its build cost.
Linear lookup remains available for unusual workloads that rebuild far more often than they query. Automatic mode only selects it for non-empty stores when linearMaxEntries is deliberately configured.
The same API avoids hashing when direct addressing fits, uses a map when it pays, and remains usable beyond Pine's map capacity. Automatic mode is the normal default. Use op.indexConfigDynamic() when future query volume is unknown and the store may need to promote itself later.
build(keys, values, expectedQueries) accepts two same-length arrays. The optional hint tells OptiPine how many lookups to expect before the next build. Stores support int, float, bool, string and color values. Use one IntIndex for several payload fields, or IntBuckets when a key owns several integers.
In practice: Batch-build IDs to scores, states or metadata, then query them without committing to a representation. Direct integer addressing measured 21% faster than a map, while a map measured 91% faster than repeated linear lookup with 32 entries.
🔸 INTBUCKETS: ONE KEY TO MANY INTEGER VALUES
A Store returns one value for each key. IntBuckets returns a group of integers, usually object IDs or physical slots. Repeating a key adds another member instead of replacing the previous one.
var op.IntBuckets cellMembers = op.intBuckets()
var array matches = array.new()
if barstate.isfirst
// Six (cell, object slot) pairs. Cell 7 appears three times.
array cellKeys = array.from(7, 2, 7, 5, 2, 7)
array objectSlots = array.from(101, 205, 412, 990, 777, 888)
cellMembers.buildFromPairs(cellKeys, objectSlots)
// Read cell 7's group from flat storage. matches is only demo output.
= cellMembers.rangeByKey(7)
if count > 0
for position = start to start + count - 1
matches.push(cellMembers.valueAt(position))
// Output: matches contains , the object slots assigned to cell 7.
What happens: Each key is paired with the slot at the same array position. Cell 7 appears three times, so its group contains 101, 412 and 888. rangeByKey() returns where that group starts and how many values it contains. A missing key returns a count of 0.
Lifecycle: buildFromPairs() replaces all previous groups. Use buildBegin() , add() and buildFinish() only when pairs arrive one at a time.
In practice: A price cell can own several zone slots, a graph node can own several neighbors, or a category can own several record IDs. One query visits only that group.
Why use it: A native map stores one value per key, and Pine does not allow an array directly as that value. Giving one key several values therefore requires a small wrapper UDT containing an array. IntBuckets provides that relationship directly, packing every group into shared contiguous storage. It suits batch rebuilds followed by repeated traversal, while the wrapper approach is more convenient when individual groups change constantly. In the tested 64-key traversal workload, IntBuckets averaged 19% faster across four runs.
----------------------------------------------------------------------------------------------------------------
🔷 REUSE STATE INSTEAD OF REBUILDING IT
IntDoubleBuffer and FloatDoubleBuffer retain current and previous arrays. swap() exchanges their references in O(1), preserves the old result and clears the new current buffer for reuse. That clear still costs O(N).
This is useful when one pass must remain readable while the next is built. In this small search, node n has children 2n and 2n + 1 . Each pass reads the active level and writes the next one:
var op.IntDoubleBuffer searchFrontier = op.intDoubleBuffer()
if barstate.isfirst
searchFrontier.current.push(1)
for depth = 1 to 3
= searchFrontier.swap()
for nodeId in activeFrontier
nextFrontier.push(nodeId * 2)
nextFrontier.push(nodeId * 2 + 1)
// Output: current contains .
// previous contains .
What happens: swap() makes the completed level available as activeFrontier and returns the other retained array, already empty, as nextFrontier . No level is copied and no replacement array is created. The same pattern supports graph searches, flood fills, iterative clustering and simulations. Use swapSized() when every pass needs a fixed-size output.
A var array can also be reused. The ensureSize*() , resize*() and refill*() families modify existing storage, while sameExact*() compares primitive arrays without Pine's float-comparison rounding.
Revision handles caller-owned state that OptiPine cannot observe. The producer calls bump() after a change; each consumer compares its own saved token with changedSince() instead of keeping a snapshot.
----------------------------------------------------------------------------------------------------------------
🔷 WEIGHTED SELECTION FOR STATIC AND DYNAMIC SYSTEMS
Weighted selection chooses entries in proportion to their weights. It is useful in simulations, randomized search and priority sampling.
WeightedSampler is the high-level interface. Set weights, then supply a fraction to select a slot. The sampler does not generate randomness; use math.random() or a repeatable fraction sequence:
var op.WeightedSampler sampler = op.weightedSampler(512)
if barstate.isfirst
sampler.setWeight(10, 0.25)
sampler.setWeight(11, 0.80)
sampler.setWeight(12, 0.10)
float fraction = 0.50
int selected = sampler.sample(fraction)
// Output: selected is 11 for the supplied fraction of 0.50.
The default cumulative prefix suits stable weights. Pass op.weightConfigSparseUpdates() and the sampler can move to an update-friendly Fenwick tree as the workload changes. sample() stays the same. Use WeightedIndex for circular ranges or explicit policy control.
In practice: Each slot can represent a candidate model, simulation outcome or work item. Update its weight when its score changes, then sample repeatedly through the same interface.
----------------------------------------------------------------------------------------------------------------
🔷 THREE LEVELS OF CONTROL
OptiPine is layered so high-level code describes the problem rather than the mechanism. Start with Tier 1 and move deeper only when the workload requires more control:
Tier 1, Quick: Ready-to-use APIs with automatic defaults, including Watch, Memo, CadenceGate, typed stores, StablePool, DirtySet, row rings, double buffers and WeightedSampler.
Tier 2, Composable: Explicit lifecycles, configuration and representation policies through IntIndex, IntBuckets, SlotCache, RingCursor and Revision.
Tier 3, Expert: Physical addressing, unchecked operations and scoped raw mutation for measured hot paths. Ordinary read-only views are not Tier 3.
Editor warnings: Methods such as get() , set() , push() and clear() intentionally match Pine's collection vocabulary. Any shadowing-method warning is cosmetic; the receiver's type determines which method runs.
----------------------------------------------------------------------------------------------------------------
🔷 COMPLETE, COPY-PASTE EXAMPLES
The fragments above isolate one idea at a time. These two copy-paste indicators combine them in practical workflows, using native Pine where it is simpler and OptiPine where it removes real work.
🔸 Complete example 1: high-level cached stress model
What it does: The indicator plots a probability-weighted downside estimate for the current trend and volatility regime, while exposing both regime values in the Data Window.
The EMA and ATR calculations run normally on every bar. Their rounded regimes change less often, so Memo recalculates the 401-scenario model only when one of those regimes changes and serves the cached result between changes.
//@version=6
indicator("OptiPine - Cached Regime Stress", overlay = false)
import Alien_Algorithms/OptiPine/1 as op
// Test 401 possible moves, giving more weight to common moves.
// This function is pure: its result depends only on its inputs.
estimateDownside(float trendInAtr, float atrPercent) =>
float result = na
if not na(trendInAtr) and not na(atrPercent) and atrPercent > 0
float weightedDownside = 0.0
float totalWeight = 0.0
for scenario = -200 to 200
float standardShock = scenario / 40.0
float weight = math.exp(-0.5 * standardShock * standardShock)
float projectedMove = (trendInAtr + standardShock) * atrPercent
float downside = math.max(-projectedMove, 0.0)
weightedDownside += downside * weight
totalWeight += weight
result := totalWeight > 0 ? weightedDownside / totalWeight : na
result
// Stateful Pine calculations stay outside the Memo guard.
float ema20 = ta.ema(close, 20)
float ema50 = ta.ema(close, 50)
float atr14 = ta.atr(14)
float trendInAtr = atr14 > 0 ? (ema20 - ema50) / atr14 : na
float atrPercent = close > 0 ? atr14 / close * 100.0 : na
// Quantization makes the dependencies describe a regime, not every tick.
float trendRegime = math.round(
math.max(-3.0, math.min(3.0, trendInAtr)) * 10.0) / 10.0
float volatilityRegime = math.round(atrPercent * 4.0) / 4.0
var op.FloatMemo downsideStress = op.floatMemo()
if downsideStress.staleOn(trendRegime, volatilityRegime)
downsideStress.store(
estimateDownside(trendRegime, volatilityRegime))
float stress = downsideStress.get()
plot(stress, "Expected downside (%)", color.orange, linewidth = 2)
plot(trendRegime, "Trend regime (ATR units)", display = display.data_window)
plot(volatilityRegime, "Volatility regime (%)", display = display.data_window)
🔸 Complete example 2: advanced zone-cluster engine
What it does: The indicator draws recent pivot levels, thickens those near the current price, plots the strongest price cluster and reports its key statistics in the Data Window.
StablePool preserves drawing slots, the ring tracks retirement order, DirtySet queues redraws, IntBuckets forms price clusters and IntFloatStore looks up their strength.
Relevant benchmarks: These are component results, not a total for this 32-zone indicator. In larger matching workloads, DirtySet saved 93% at 1% dirty and IntBuckets averaged 19% with 64 keys. For typed lookup, direct addressing saved 21% over a map on compact keys, while a map saved 91% over linear search at 32 entries. Automatic mode selects the representation.
StablePool and the ring manage recycling. The script still scans live zones for proximity changes, then DirtySet avoids unnecessary drawing updates.
//@version=6
indicator("OptiPine - Zone Cluster Engine", overlay = true, max_lines_count = 100)
import Alien_Algorithms/OptiPine/1 as op
int pivotLength = input.int(5, "Pivot length", minval = 1)
int maxZones = input.int(32, "Maximum zones", minval = 4, maxval = 100)
int bucketTicks = input.int(25, "Cluster size in ticks", minval = 1)
float bucketSize = syminfo.mintick * bucketTicks
// Stateful Pine calculations remain outside every conditional rebuild.
float pivotHigh = ta.pivothigh(high, pivotLength, pivotLength)
float pivotLow = ta.pivotlow(low, pivotLength, pivotLength)
float pivotStrength = math.max(nz(volume , 1.0), 1.0)
float highlightDistance = ta.atr(14)
var op.StablePool zones = op.stablePool()
var op.IntRowRing zoneOrder = op.intRowRing(maxZones, 1)
var op.DirtySet dirtyZones = op.dirtySet(maxZones)
var array zonePrices = array.new(maxZones, na)
var array zoneStrengths = array.new(maxZones, 0.0)
var array zoneTimes = array.new(maxZones, na)
var array resistance = array.new(maxZones, false)
var array highlighted = array.new(maxZones, false)
var array zoneLines = array.new(maxZones)
var op.IntBuckets zonesByBucket = op.intBuckets()
var op.IntFloatStore strengthByBucket = op.intFloatStore()
// Retained build storage is resized and overwritten, never cleared and repopulated.
var array bucketKeyByPosition = array.new()
var array aggregateKeys = array.new()
var array aggregateStrengths = array.new()
var int strongestBucketKey = na
var float strongestBucketStrength = na
var int strongestZoneCount = 0
dirtyZones.begin()
bool topologyChanged = barstate.isfirst
// Logical pivot IDs receive stable, reusable physical drawing slots.
for event = 0 to 1
float level = event == 0 ? pivotHigh : pivotLow
if barstate.isconfirmed and not na(level)
int pivotBar = bar_index - pivotLength
int pivotTime = time
int zoneId = pivotBar * 2 + event
int slot = zones.find(zoneId)
// Only a new logical pivot enters the retirement queue.
if slot < 0
if zoneOrder.rowCount() == maxZones
int oldestId = zoneOrder.at(0, 0)
zones.release(oldestId)
= zones.acquire(zoneId)
slot := newSlot
zoneOrder.pushValue(zoneId)
zonePrices.set(slot, level)
zoneStrengths.set(slot, pivotStrength)
zoneTimes.set(slot, pivotTime)
resistance.set(slot, event == 0)
highlighted.set(slot, false)
dirtyZones.mark(slot)
topologyChanged := true
// Proximity can mark a newly created slot again; DirtySet still stores it once.
for slot in zones.slots()
bool isHighlighted = math.abs(close - zonePrices.get(slot)) <= highlightDistance
if isHighlighted != highlighted.get(slot)
highlighted.set(slot, isHighlighted)
dirtyZones.mark(slot)
// Only changed drawings cross the line API boundary.
for slot in dirtyZones.values()
float level = zonePrices.get(slot)
color baseColor = resistance.get(slot) ? color.red : color.lime
line zoneLine = zoneLines.get(slot)
if na(zoneLine)
zoneLine := line.new(zoneTimes.get(slot), level, time, level,
xloc = xloc.bar_time)
zoneLines.set(slot, zoneLine)
line.set_xy1(zoneLine, zoneTimes.get(slot), level)
line.set_xy2(zoneLine, time, level)
line.set_extend(zoneLine, extend.right)
line.set_width(zoneLine, highlighted.get(slot) ? 3 : 1)
line.set_color(zoneLine,
color.new(baseColor, highlighted.get(slot) ? 0 : 55))
// Rebuild grouped lookup only after the explicit creation event.
if topologyChanged
array liveSlots = zones.slots()
int liveCount = liveSlots.size()
op.resizeInt(bucketKeyByPosition, liveCount, 0)
if liveCount > 0
for position = 0 to liveCount - 1
int slot = liveSlots.get(position)
int bucketKey = int(math.round(zonePrices.get(slot) / bucketSize))
bucketKeyByPosition.set(position, bucketKey)
// Repeated bucket keys accumulate several physical zone slots.
zonesByBucket.buildFromPairs(bucketKeyByPosition, liveSlots)
int bucketCount = zonesByBucket.bucketCount()
op.resizeInt(aggregateKeys, bucketCount, 0)
op.resizeFloat(aggregateStrengths, bucketCount, 0.0)
strongestBucketKey := na
strongestBucketStrength := na
strongestZoneCount := 0
if bucketCount > 0
for bucketSlot = 0 to bucketCount - 1
int bucketKey = zonesByBucket.keyAt(bucketSlot)
= zonesByBucket.rangeBySlot(bucketSlot)
float totalStrength = 0.0
if count > 0
for position = start to start + count - 1
int zoneSlot = zonesByBucket.valueAt(position)
totalStrength += zoneStrengths.get(zoneSlot)
aggregateKeys.set(bucketSlot, bucketKey)
aggregateStrengths.set(bucketSlot, totalStrength)
if na(strongestBucketStrength) or totalStrength > strongestBucketStrength
strongestBucketKey := bucketKey
strongestBucketStrength := totalStrength
strongestZoneCount := count
strengthByBucket.build(aggregateKeys, aggregateStrengths)
// Query the current price cluster directly and display the strongest cluster.
int currentBucketKey = int(math.round(close / bucketSize))
float nearbyStrength = strengthByBucket.get(currentBucketKey, 0.0)
float strongestClusterPrice = na(strongestBucketKey) ?
na : strongestBucketKey * bucketSize
plot(strongestClusterPrice, "Strongest zone cluster", color.orange,
linewidth = 2, style = plot.style_stepline)
plot(nearbyStrength, "Strength near current price", display = display.data_window)
plot(strongestBucketStrength, "Strongest cluster strength",
display = display.data_window)
plot(strongestZoneCount, "Zones in strongest cluster",
display = display.data_window)
plot(dirtyZones.size(), "Drawings updated", display = display.data_window)
----------------------------------------------------------------------------------------------------------------
🔷 API REFERENCE
This is a compact index of the main public entry points.
🔸 Watch and Memo: changed(source) handles one scalar, primitive array or row ring. For several dependencies, use begin() , watch*() and finish() . Typed Memos add staleOn() , store() , get() and invalidate() .
🔸 Revision and Cadence: revision() exposes bump() , current() and changedSince() for manual change tracking. cadenceGate() provides due() and change-aware dueWhenChanged() scheduling.
🔸 Row Rings: floatRowRing() and intRowRing() provide push() , width-one pushValue() , at() , setAt() , newestAt() , chronological() and gather() .
🔸 RingCursor: Circular addressing for caller-owned arrays. Use reserve() to advance, physical() and logical() to translate positions, and newest() or oldest() to locate retained rows.
🔸 StablePool: acquire() and release() manage stable key-to-slot assignments. Lookup and traversal use find() , contains() , keyAt() , slots() and size() . Recycled slots retain their caller-owned payload until overwritten.
🔸 SlotCache: Frame-based stable allocation follows begin() , acquire() , finish() . active() , retired() and size() expose its state.
🔸 DirtySet: begin() starts a cycle; mark() , markMany() and markRange() add entries. Read the distinct work list with values() and size() .
🔸 Typed Stores: intIntStore() , intFloatStore() , intBoolStore() , intStringStore() and intColorStore() map integer keys to primitive values. Build with build() , then use get() , set() , contains() or getMany() .
🔸 IntIndex: A shared integer key-to-slot directory for custom payloads and explicit lookup policy. Build with buildBegin() , add() or addMany() and buildFinish() ; query with find() , keyAt() and findMany() . IndexConfig controls representation and duplicate policy.
🔸 IntBuckets: A one-key-to-many-integers index. Build directly with buildFromPairs() , or incrementally with buildBegin() , add() or addMany() and buildFinish() . Read groups with rangeByKey() and valueAt() .
🔸 Double Buffers: intDoubleBuffer() and floatDoubleBuffer() retain current and previous arrays. swap() exchanges them; swapSized() also sizes and refills the new current buffer.
🔸 Weighted Sampling: weightedSampler() provides weight updates, sample() , sampleMany() , probability() and total() . It maps caller-supplied fractions; it does not generate randomness. weightedIndex() adds circular ranges and explicit policy control.
🔸 Storage Utilities: ensureSize*() , resize*() , refill*() and sameExact*() handle primitive arrays. Other helpers cover flat/matrix conversion, transposition and bulk ring reads.
----------------------------------------------------------------------------------------------------------------
🔷 WHY OPTIPINE EXISTS
Pine's limits are real, but standard architecture often reaches them long before the idea itself has to. Repeating unchanged calculations, shifting rolling storage, scanning mostly untouched collections and rebuilding state all consume the same execution budget the feature needs to exist.
OptiPine reclaims that budget. Expensive models can run only when their inputs change. Large dashboards can refresh only what moved. Dynamic object systems can grow and recycle storage without reorganizing everything around them. The APIs stay approachable, while the architecture underneath is built for workloads that would normally force a Pine project to scale back.
At large scale, optimization is no longer simply about feature speed. It is the factor that dictates whether an ambitious idea can ship at all.
----------------------------------------------------------------------------------------------------------------
This work is licensed under (CC BY-NC-SA 4.0) , meaning usage is free for non-commercial purposes given that Alien_Algorithms is credited in the description for the underlying software. For commercial use licensing, contact Alien_Algorithms
The publication diagram has been rendered natively by Pine3D .
Library

Indicator

TT Backtest Compare## TT Backtest Compare — Two Lorentzian Engines, One Chart, No Excuses
Every optimized strategy makes the same claim: "better than the defaults."
TT Backtest Compare is the tool that makes that claim checkable — on your
chart, your symbol, your timeframe, with both engines running live in front
of you.
It runs two complete Machine Learning: Lorentzian Classification engines
(original k-NN core by jdehorty, used under MPL-2.0) side by side in a single
indicator pane:
- ARM A — TT-Autotune: machine-evolved parameters delivered as Regime Sync
Codes, hot-swapped bar-by-bar as the market regime changes, scanning a
sliding pool of the most RECENT bars.
- ARM B — TT-Lorentzian (control): the verbatim stock jdehorty v2 defaults,
the original oldest-bars neighbor pool, the official MLExtensions and
KernelFunctions library calls. Untouched, on purpose.
Both arms see the same bars, pay the same costs, and obey the same exit
rules. The only difference is the thing being tested: where the parameters
came from.
### Why it's an indicator that runs its own backtests
A jdehorty-style "Backtest Adapter" can't do this job: adapters read another
script's plot through a Source input, and PulseWire only exposes INDICATOR
plots that way — both of our production arms are strategy() scripts. So this
indicator embeds both engines and simulates the trades itself, with one shared
trade simulator so neither arm can cheat.
### Accounting you can audit
The trade loop is not a loose approximation of a backtest — it is a
transcription of the exact accounting used by TensorTrader's Python
genetic optimizer:
- Single position per arm, flip on the opposite signal.
- ATR stop-loss / take-profit bracket evaluated against the CURRENT bar's
ATR before signals (matching how the live strategy re-issues its exit
orders every bar).
- Per-trade ROI net of a round-trip cost you control (default 5 bps taker
fee + 2 bps slippage per side, 14 bps round trip).
- Trade-gated Sortino on per-trade ROI (MAR = 0, bounded, requires 2+
trades) — the same fitness statistic the optimizer breeds against.
On the server, a line-by-line Python transcription of this Pine trade loop is
continuously tested against the optimizer's canonical simulator — trades,
total ROI, compound ROI, drawdown, Sortino, and per-regime attribution must
all match or the build fails. When this chart says the evolved parameters
earned +X%, that number means the same thing as the optimizer's report.
### What you see
- Two equity curves (% return), with a green/red fill showing which arm is
ahead at every bar, plus an optional "edge" plot of the running difference.
- A stats table: compound return, total ROI, trade count, win rate, average
trade, Sortino, max drawdown, profit factor, and time in market — for each
arm, with the A−B delta in its own column.
- A per-regime table: the embedded six-state regime classifier (the same
ADX + regression-slope + ATR% classifier with hysteresis that runs in
TensorTrader's Python engine and inside TT-Autotune) buckets every trade
by the regime at its ENTRY bar, so you can see exactly where the evolved
parameters earn their edge — and where they don't. Regimes with no
deployed champion are marked honestly instead of hidden.
- A header card with the active sync source, backtest window, and the cost /
exit configuration, so screenshots are self-documenting.
Controls include a backtest start date, fee and slippage inputs, an
indicator-exits toggle (mirroring the optimizer's setting), full ATR bracket
settings, and an arms selector — each arm is a full k-NN scan per bar, so you
can drop to a single arm if a very long chart hits Pine's time limit.
### What this does NOT show
Honesty section: the comparison runs one position per arm because that is
what the optimizer's fitness function measures. The live TT-Autotune strategy
can pyramid up to 5 DCA legs, so its realized P&L scales differently — this
chart compares the SIGNAL edge, not the position ladder. All figures are
backtests net of modeled costs, not live results; past performance never
guarantees future returns.
### The TensorTrader side
Without a sync code, Arm A runs the same defaults as Arm B and the race is a
tie by construction — the indicator is fully functional but you're comparing
a thing to itself.
The evolved parameters come from TensorTrader's deterministic genetic
optimizer: populations of candidate genomes evolved over walk-forward,
out-of-sample folds, scored on net Sortino after fees and slippage, with
minimum-trade eligibility rules so no 6-trade miracle ever ships. A separate
champion is bred for each of the six market regimes and packed into one
Regime Sync Code per symbol × timeframe cell.
1. Create a free account at tensortrader.agent-swarm.net.
2. Pick an optimized symbol × timeframe cell from the catalog.
3. The TensorTrader browser extension pastes the packed Regime Sync Code into
the indicator's input for you — the same code that arms the TT-Autotune
strategy — and keeps it current when a newer champion is bred for your
cell.
Use this indicator BEFORE you trust any cell: load your market, arm A with
its code, and watch whether the edge is real on the window you care about.
That is what it was built for.
### Credits and license
Original Lorentzian Classification logic © jdehorty, used under the Mozilla
Public License 2.0 — Arm B imports his MLExtensions and KernelFunctions
libraries directly and runs his published defaults unmodified. The dual-arm
simulator, cost model, regime classifier, sync-code system, and comparison
tables by TensorTrader.
---
## Categories and tags (publication notes, not part of the description)
PulseWire's publish dialog allows up to two categories plus free-form tags.
- Primary category: **Statistics** — the script's product is comparative
performance analytics (equity curves, Sortino, drawdown, profit factor),
not a trade signal.
- Secondary category: **Trend Analysis** — the underlying engines are
trend-classification (Lorentzian k-NN + kernel regression), and it's where
the jdehorty original and TT-Autotune audiences browse.
- Alternative secondary if you prefer positioning it as a research tool:
**Educational**.
Suggested tags: `machinelearning`, `lorentzian`, `knn`, `backtesting`,
`equitycurve`, `sortino`, `regime`, `abtest`, `tensortrader`.
Other notes:
- House Rules: as a derivative of an open-source script it must credit the
original and describe meaningful changes — the description does both, and
the MPL-2.0 header is already in the source.
- Publish as an INDICATOR (it is `indicator()`, not `strategy()` — the
Strategy Tester will not appear, which is expected and explained in the
description).
- The parity claim ("same numbers as the optimizer") is backed by
`test_pine_backtest_compare_accounting.py`; keep that test green before
republishing after any accounting change.
Indicator

TRADLEWARE-HODL
Buy and Hold Benchmark
This is a passive reference strategy, not a signal-based trading system. It exists to give an honest baseline: buy once, hold through everything, and see what an active strategy actually needs to beat.
How it works
Buy-and-hold ("HODL") is the simplest possible approach to markets: put the money in once and do nothing else, regardless of what price does afterward. There is no attempt to time entries or exits, no reaction to drawdowns, and no risk management of any kind. Any active strategy that cannot beat this, risk-adjusted, over the same period has not demonstrated an edge.
Entry
The entire starting capital is deployed in a single buy, on the first bar at or after the start date.
Exit
The position is held until the end date, or the end of the chart's available history, whichever comes first — at which point it is closed once so the backtest can report a final equity figure. This is bookkeeping, not a trading decision; the whole point of the strategy is that it does not exit early.
Parameters
Start Date / End Date: the single buy fires on the first bar at or after the start date; the position is held until the end date
Position sizing is set to 99.95% of equity rather than a full 100%. That small gap avoids a PulseWire position-sizing rounding artifact that can otherwise show up as an extra "Margin call" row even on a strategy with only one real trade; the effect on the actual result is negligible.
Costs modelled
0.1% commission per side, 3 ticks slippage.
Intended assets and timeframe
Works on any asset or timeframe — there is no technical logic to adapt, just a buy date and a hold period.
Known limitations
Full exposure to every drawdown the asset experiences, for the entire holding period, by design. This is not a flaw to fix — it is the deliberate point of comparison for any strategy that claims to manage risk better than doing nothing.
Strategy

TRADLEWARE-DCA
Dollar-Cost Averaging Benchmark
This is a passive reference strategy, not a signal-based trading system. It exists to give an honest, apples-to-apples comparison for active strategies: instead of trying to time entries, it buys a fixed amount of the asset on a regular schedule until a set capital budget is fully deployed.
How it works
Dollar-cost averaging (DCA) means investing a fixed amount of money at regular intervals, regardless of price. Some buys land at high prices, some at low prices, and over time the average purchase price smooths out. There is no attempt to predict direction — the schedule is the whole strategy.
This script buys on either a fixed day of the week (e.g. every Monday) or every fixed number of bars (e.g. every 30 daily bars, roughly monthly), and keeps buying until the total amount invested reaches the strategy's starting capital. After that, no more buys are placed — the same total capital pool as whatever active strategy this is being compared against, with no extra money added along the way.
Entry
A buy is placed each time the schedule fires, as long as the running total invested plus the next buy amount does not exceed the starting capital. If a scheduled buy would push the total over budget, it is skipped, but the schedule keeps advancing rather than getting stuck retrying.
Exit
There is no exit signal in the usual sense — the strategy only ever adds to its position. The full position is closed out once, on the final bar of the chart's history, purely so the backtest can report a final equity figure. This is bookkeeping, not a trading decision.
Parameters
Start Date / End Date: window during which buys are allowed
Use Day of Week Mode: switch between "buy on a specific weekday" and "buy every N bars"
Day of Week: which weekday to buy on, when day-of-week mode is on
Every X Bars: how many bars between buys, when day-of-week mode is off (30 on a daily chart is roughly monthly)
Amount per buy: fixed amount invested at each scheduled buy
The strategy allows up to 500 stacked buy layers to accumulate into a single overall position — that number just needs to be large enough to never run out before the capital budget is spent; it is not a trading parameter to tune.
Costs modelled
0.1% commission per side, 3 ticks slippage, fills at the same bar's close (this benchmark intentionally fills immediately rather than waiting for the next bar's open, since there is no signal timing to protect).
Intended assets and timeframe
Works on any asset or timeframe — the frequency inputs just need to be set to match (e.g. 30 bars on a daily chart for roughly monthly buys, 7 for weekly). For higher-priced assets, check that the per-buy amount converts to at least a fraction PulseWire will actually simulate.
Known limitations
The starting capital, buy amount, and buy frequency together decide how long full deployment actually takes — and depending on the chart's date range, that can run out in either direction. With the default settings (10,000 starting capital, 100 per buy, roughly monthly), full deployment takes 100 buys — about 8 years of monthly investing. Starting from 2018-01-01, that budget is exhausted by roughly mid-2026, so on a chart that runs through mid-2026 or later, this script will have already placed its last scheduled buy weeks or months before the present: it simply holds the fully-invested position afterward and stops buying, exactly as designed by the "never invest more than the starting capital" rule, not because of an error. On a shorter chart window relative to the amount and frequency chosen, the opposite can happen instead — the window ends before the full budget is spent, leaving some capital undeployed. Either way, check the strategy's equity and invested-capital tracking rather than assuming full deployment by the end of the chart. This script also has no risk management of any kind by design: it never sells until the very end, so it carries full exposure to any drawdown the asset experiences. That is the intended comparison point for an active strategy, not a flaw to fix.
Strategy

TT-Lorentzian## TT-Lorentzian — The Stock Lorentzian Classifier, Made Executable and Honest
Machine Learning: Lorentzian Classification by jdehorty is one of the most
popular indicators on PulseWire — and it is exactly that: an indicator.
It paints signals, but it can't hold a position, size an order, bracket a
trade, or fire a single alert that a bot can actually execute.
TT-Lorentzian is that classifier turned into a complete, automatable
strategy — with one strict rule we imposed on ourselves: DON'T TOUCH THE
BRAIN. The k-nearest-neighbors core with Lorentzian distance, the five
feature slots (RSI / WT / CCI / ADX), the kernel regression filter, the
volatility / regime / ADX filters, the published v2 defaults, even the
original oldest-bars neighbor pool — all of it runs verbatim through
jdehorty's own MLExtensions and KernelFunctions libraries (used under
MPL-2.0, full credit to the original author). If you know the original,
every input here will look familiar, because it is the original.
### Why publish an unmodified classifier?
Because this script has a second job: it is the CONTROL ARM of TensorTrader's
live A/B experiment. Our optimized fork, TT-Autotune, trades machine-evolved,
regime-switched parameters; TT-Lorentzian trades the stock defaults on the
same markets under the same execution shell. When we claim the optimizer adds
edge, this script is the baseline that claim is measured against — publicly,
not in a private spreadsheet. The companion indicator TT Backtest Compare
runs both engines side by side on any chart and shows you the equity curves,
Sortino, drawdown, and per-regime attribution of one against the other.
A control you can't trade isn't a control. So this one trades.
### What the strategy shell adds (the brain stays stock)
1 — REAL ORDERS, ONE ALERT
strategy.entry / strategy.close with machine-readable order comments
(open_long, open_short, close_long, close_short), fixed-USD position sizing
with your choice of base currency (USDT / USDC / USD), pyramiding locked to
1, and orders processed on bar close. One alert on this strategy carries
every entry and exit — no juggling four separate alert conditions.
2 — ATR RISK BRACKETS
Optional ATR-based stop-loss and take-profit (defaults: 14-period ATR,
2.0x SL / 3.0x TP), re-issued every bar against the live position's average
price. The original indicator has no concept of a stop; this shell does.
3 — REGIME AWARENESS (DISPLAY-ONLY, BY DESIGN)
The same six-state market-regime classifier that runs in TensorTrader's
Python engine and in TT-Autotune (ADX + linear-regression slope + ATR%, with
confirmation-bar and minimum-hold hysteresis) is embedded and shown as an
on-chart chip panel: which regime is live and how long it has held. But
every row reads CTRL — the control arm deliberately trades the same default
parameters in every regime. That is the whole point: when TT-Autotune swaps
champions per regime and this script doesn't, the difference you measure is
the optimization, nothing else.
4 — EVERYTHING YOU ALREADY LIKE
The original visuals are intact: bar-prediction labels with gradient
coloring, kernel regression estimate plot, buy/sell arrows, and jdehorty's
own trade-stats table. Table positions are pre-arranged so this script and
TT-Autotune can share one chart without overlapping panels.
### Automation via TensorTrader
The alert payload this strategy emits is understood natively by the
TensorTrader webhook bridge: create a free account at
tensortrader.agent-swarm.net, install the TensorTrader browser extension,
and it will create and manage the PulseWire alert for you — wiring the
webhook, keeping it enrolled and heartbeat-fresh, and routing the signals
to paper or live execution on your own exchange keys across the venues the
platform supports. Every venue starts in paper mode; going live is an
explicit opt-in.
And if the A/B data convinces you the evolved parameters earn their keep,
the same extension arms TT-Autotune with your market's Regime Sync Code —
the upgrade path is one click, and the receipts are on the chart first.
### Honesty section
This strategy trades the published stock defaults — the same defaults that
scored a NEGATIVE median walk-forward Sortino (−0.15, net of 5 bps fees +
2 bps slippage) across our 50-symbol × 3-timeframe optimizer sweep. We
publish it anyway, because that's what a baseline is. If it beats the
optimized arm on your market, you'll see that too — TT Backtest Compare
doesn't take sides. Backtests are not live results; past performance never
guarantees future returns. Trade small, trade paper first.
### Credits and license
Original Lorentzian Classification logic, feature engineering, kernel
functions, and default parameters © jdehorty, used under the Mozilla Public
License 2.0 — imported directly via his MLExtensions/2 and KernelFunctions/2
libraries. Strategy shell, ATR brackets, alert plumbing, regime chip panel,
and A/B instrumentation by TensorTrader.
---
## Categories and tags (publication notes, not part of the description)
PulseWire's publish dialog allows up to two categories plus free-form tags.
- Primary category: **Trend Analysis** — the Lorentzian k-NN + kernel
regression engine is a trend classifier, and it's the category where the
jdehorty original lives and its audience browses.
- Secondary category: **Statistics** — keeps all three scripts of the series
discoverable together and reflects the k-NN / control-arm framing.
Suggested tags: `machinelearning`, `lorentzian`, `knn`, `strategy`,
`automation`, `webhooks`, `atr`, `riskmanagement`, `regime`, `tensortrader`.
Other notes:
- Publish as a STRATEGY (it is `strategy()`), so the Strategy Tester tab
appears — expected, unlike the Compare indicator.
- House Rules: open-source fork of an open-source script must credit the
original and describe meaningful changes. The description credits jdehorty
explicitly and frames the changes accurately: execution shell added,
classifier core unmodified. Emphasizing "the brain is untouched" is both
the marketing hook and the compliance statement.
- The −0.15 median default Sortino figure is the same one verified 2026-08-07
for the TT-Autotune description (148 runs, admin-646…admin-793 sweep,
asof 2026-07-31). Keep the two descriptions in sync if that number is
regenerated.
- Cross-link the three publications in each description's comments after all
are live: Autotune (the optimized arm) ↔ TT-Lorentzian (the control) ↔
TT Backtest Compare (the referee).
Strategy

Macro Event Radar JP FreeMacro Event Radar JP Free is a Japanese-focused economic calendar overlay for FX traders.
Features:
• Automatically imports upcoming economic events from the public Pine Seeds feed maintained by toodegrees (source data: Forex Factory).
• Displays event time in JST, currency, expected impact, event name, countdown and warning state.
• Adds Japanese helper text to major event names.
• Optional vertical event lines, release labels, risk-window background, dynamic alerts and post-release reaction statistics (5/15/30/60 min, MFE/MAE).
• Currency can follow the chart automatically or be selected manually.
• Manual JST schedule input remains available as a fallback.
Data can be delayed, incomplete or changed. Always verify important release times with the official source. This indicator is not financial advice.
Credits:
Data feed and public libraries: toodegrees
Source data: Forex Factory
Japanese UI, JST presentation and reaction-analysis features: a4gete02b
日本語:
FX向けの日本語経済指標カレンダーです。標準設定は「自動」で、公開フィードから予定を取得し、JST時刻・通貨・重要度・指標名・残り時間・警戒状態を表示します。主要指標には日本語補助名を付けます。縦線、発表済みラベル、警戒背景、アラート、発表後5/15/30/60分とMFE/MAEの反応分析を利用できます。
使い方:
1. チャートに追加します。
2. 「予定データ取得」は通常「自動」のまま使用します。
3. 初期設定の「対象通貨=すべて」では全通貨を表示します。チャート関連通貨だけなら「自動」に変更します。
4. 重要指標だけなら「最低重要度=3」にします。
5. PulseWireのアラート作成で本インジケーターを選び、「Any alert() function call」を選ぶと事前通知を受け取れます。
6. 小さい画面では予定表=右上、分析=右下または左下にすると重なりを避けられます。
経済指標データは遅延・欠落・変更の可能性があります。重要な発表時刻は必ず公式情報でも確認してください。 Indicator

Indicator

Target Trend ProEnhanced trend-following tool with automated entry signals, stop loss, three profit targets, filters, and live dashboard.
🎯 Target Trend Pro
An enhanced and expanded version of the original Target Trend concept by BigBeluga.
This indicator helps traders identify trend direction and manage trades visually with clear entry signals, stop loss, and three customizable take-profit levels — all displayed directly on the chart.
══════════════════════════════════════
🔵 KEY FEATURES
══════════════════════════════════════
• Adaptive SMA ± ATR bands for trend detection
• Automatic Long / Short entry triangles
• Three fixed Take Profit levels (ATR-based)
• Dynamic or fixed Stop Loss (with optional trailing)
• Live Dashboard showing:
- Entry, SL, TP1/TP2/TP3 with distance %
- Risk:Reward ratio
- ADX status
- Bars in trade
- Current trade status
• Filters:
- ADX Filter
- Higher Timeframe confirmation
- Volume Filter
• Clean visual management (lines, labels, fills)
• Full alert support (Entry + TP hits + SL hit)
══════════════════════════════════════
🔵 HOW IT WORKS
══════════════════════════════════════
1. Trend is detected when price crosses the adaptive SMA bands.
2. On a confirmed trend change, the indicator plots:
- Entry level
- Stop Loss
- Three Take Profit targets
3. Targets are calculated using ATR at the moment of the signal (fixed).
4. The dashboard updates in real time with trade progress.
5. Optional filters help reduce low-quality signals.
══════════════════════════════════════
🔵 SETTINGS OVERVIEW
══════════════════════════════════════
• Trend Length & ATR settings → Control sensitivity
• TP1 / TP2 / TP3 Multipliers → Customize target distances
• Trailing Stop → Optional dynamic stop loss
• ADX / HTF / Volume filters → Improve signal quality
• Display options → Dashboard position, line extension, etc.
══════════════════════════════════════
🔵 CREDITS
══════════════════════════════════════
Original concept: Target Trend by BigBeluga
This version is a heavily enhanced and expanded modification released for free under the same Creative Commons Attribution-NonCommercial-ShareAlike 4.0 license.
Please keep credits if you share or modify this script.
══════════════════════════════════════
⚠️ DISCLAIMER
══════════════════════════════════════
This indicator is for educational and informational purposes only.
It does not constitute financial advice. Always do your own research and manage risk properly. Indicator

Indicator

Climatic Volume + RSI + Bollinger MULTI-TF (tus valores)**Climatic Volume + RSI + Bollinger — Multi-Timeframe (30m / 1h / 4h)**
Indicador de detección de puntos de entrada basado en la combinación de **volumen anómalo**, **momentum de precio (RSI)** y **estructura de rango (Bandas de Bollinger)**, con configuración calibrada de forma independiente para cada uno de tres timeframes: 30 minutos, 1 hora y 4 horas.
**Cómo funciona:**
- Detecta picos de **volumen climático**: momentos donde el volumen supera de forma significativa su promedio reciente, señal de que algo relevante está pasando en el mercado.
- Combina ese pico de volumen con una lectura de **RSI en zona extrema** (sobrecompra/sobreventa) y una condición de **Bandas de Bollinger**, configurable entre tres modos: toque de banda, cierre fuera de la banda, o reingreso tras haberla roto.
- Marca cada señal con una etiqueta y una línea vertical en el gráfico, para ubicar visualmente el momento exacto de la confluencia.
- Detecta automáticamente el timeframe del gráfico y aplica la configuración correspondiente — sin que tengas que ajustar nada manualmente al cambiar de plazo.
**Valores de referencia usados por defecto en cada timeframe:**
| Parámetro | 30 min | 1 hora | 4 horas |
|---|---|---|---|
| RSI Sobrecompra | 66 | 63 | 66 |
| RSI Sobreventa | 34 | 38 | 33 |
| Multiplicador Bandas de Bollinger | 2.5 | 2.5 | 2.4 |
| Longitud Bandas de Bollinger | 6 | 6 | 6 |
| Multiplicador de volumen climático | 2.0 | 2.0 | 2.0 |
| Longitud SMA de volumen | 20 | 20 | 20 |
Todos estos valores son ajustables desde el panel de configuración si quieres experimentar con tus propios criterios.
**Totalmente personalizable:** el modo de Bollinger, los colores de las señales, y la cantidad de líneas históricas visibles también se ajustan desde el panel de configuración.
**Guía rápida para principiantes — cómo leerlo:**
1. **¿Apareció una etiqueta verde con una "C" o roja con una "V"?** Marca el momento en que las tres condiciones (volumen, RSI, Bollinger) coincidieron a la vez — la "C" indica una señal de posible compra, la "V" una de posible venta.
2. **¿Ves una línea vertical junto a la etiqueta?** Es solo una marca visual para ubicar la señal con más claridad en el gráfico, no representa un nivel de precio.
3. **Las bandas de colores** (naranja, teal y gris) son las Bandas de Bollinger tradicionales: la banda superior, inferior y la línea central — te ayudan a ver el contexto de rango del precio en cada momento.
4. **Este indicador solo está calibrado para 30 minutos, 1 hora y 4 horas** — en otros timeframes usa una configuración genérica de respaldo, no una calibrada específicamente.
**Cómo usarlo:** aplica el indicador en cualquiera de los tres timeframes soportados y configura una alarma sobre la señal Long o Short para recibir notificaciones automáticas.
*Este contenido es informativo y educativo, no constituye asesoría financiera ni recomendación de inversión. Los indicadores técnicos no garantizan resultados futuros — usa siempre tu propia gestión de riesgo.* Indicator

TT-Autotune## TT-Autotune — Machine-Optimized Lorentzian Classification with Live Regime Switching
Most published strategies ship one set of parameters and hope the market
cooperates. TT-Autotune ships six — one per market regime — and each one was
bred, not guessed.
TT-Autotune is a strategy fork of the well-known Machine Learning: Lorentzian
Classification by jdehorty (used under MPL-2.0, full credit to the original
author — the k-nearest-neighbors core with Lorentzian distance is his work).
What we changed is everything around it: how the parameters are chosen, when
they apply, and how the signals become orders.
### The problem with the original defaults
Lorentzian Classification is a brilliant classifier wrapped around ~30 tunable
inputs: neighbor count, lookback depth, five feature slots (RSI / WT / CCI /
ADX with two periods each), kernel regression settings, and four filters.
The published defaults are one point in a 23-dimensional search space,
calibrated by hand, for no particular symbol, on no particular timeframe.
We measured that point. Across 50 crypto perpetual symbols on 15m, 1h, and 4h,
the stock defaults produced a NEGATIVE median walk-forward Sortino (−0.15)
net of fees. Not because the classifier is bad — because one parameter set
cannot fit every market.
### What TT-Autotune does differently
1 — SERVER-BRED PARAMETERS (Sync Code)
Every symbol × timeframe cell is optimized by TensorTrader's deterministic
genetic optimizer: populations of 32 candidate genomes evolved through
tournament selection, blend crossover, and decaying mutation (with fresh Sobol
immigrants every generation so the search never tunnel-visions), for a minimum
of 12 and up to 128 generations. Fitness is not "biggest backtest profit" —
it is walk-forward Sortino on net per-trade ROI after 5 bps fees + 2 bps
slippage, evaluated across 4 sequential out-of-sample folds on a pinned
5,000-bar window, with a consistency penalty (mean fold Sortino minus half its
dispersion). A candidate needs 40+ trades and activity in every fold or it is
discarded as statistically ineligible — no cherry-picked 6-trade miracles.
The winning genome is serialized into a compact Sync Code you paste into one
input field. Thirty parameters, injected at once.
2 — LIVE REGIME SWITCHING
Markets change character; your parameters should too. TT-Autotune embeds the
same six-state regime classifier that runs in TensorTrader's Python engine
(ADX + linear-regression slope + ATR%, with confirmation-bar and minimum-hold
hysteresis so it doesn't flip-flop): BULL_STRONG, BULL_WEAK, BEAR_STRONG,
BEAR_WEAK, SIDEWAYS_QUIET, SIDEWAYS_CHOP. A packed Regime Sync Code carries a
separately-evolved champion for each regime, and the script hot-swaps the
entire parameter set bar-by-bar as the live regime changes. An on-chart chip
panel shows which regime is live, how long it has held, and which regimes have
a deployed champion.
3 — A FIXED NEIGHBOR POOL
The original script scans the OLDEST maxBarsBack bars of whatever history
PulseWire happened to load — so the same settings give different signals
depending on your chart's loaded history. TT-Autotune scans a sliding pool of
the most RECENT maxBarsBack bars, making signals reproducible and matching the
Python simulator the optimizer trains against, bar for bar.
4 — A COMPLETE STRATEGY SHELL
This is a strategy, not an indicator: one alert covers long/short entries and
exits, with optional pyramiding (up to 5 DCA legs across timeframes), ATR-based
stop-loss / take-profit brackets, session and date windows, and three signal
modes (DEFAULT ML entries/exits, KERNEL_RAW, KERNEL_SMOOTH kernel-flip
triggers).
### The receipts
Every optimization run evaluates the stock-default parameter set as a baseline
on the exact same data, fees, folds, and eligibility rules as the candidates.
Publication is fail-closed: a champion is only released if it is statistically
eligible AND strictly beats the eligible default. From the most recent
completed sweep (148 populations, 50 symbols × 15m / 1h / 4h, ~607,000
walk-forward backtests):
- Champion beat the stock default in 148 of 148 populations.
- 136 of 148 cells flipped from a NEGATIVE default walk-forward Sortino to a
positive champion score.
- Median walk-forward fold Sortino: default −0.15 → champion +0.82.
- Median consistency-adjusted improvement: +0.67 aggregate score per cell.
- 825 of 888 per-regime panels produced an eligible regime champion; the rest
honestly report a baseline fallback instead of faking a winner.
Example (HBAR/USDT 4h): default settings scored 0.07 aggregate / +0.23 mean
fold Sortino over 78 trades; the evolved champion scored 0.91 / +1.73 over 66
trades — same window, same fees, same rules.
These are walk-forward backtests net of realistic costs, not live results.
Past performance never guarantees future returns. Trade small, trade paper
first.
### How to arm it
Without a Sync Code the script runs with the stock Lorentzian defaults — fine
for exploring, but you are leaving the entire optimization layer on the table.
To arm TT-Autotune with evolved parameters for your token:
1. Create a free account at tensortrader.agent-swarm.net.
2. Pick a symbol × timeframe cell from the optimized catalog (the free tier
includes one active cell; paid plans scale with your PulseWire alert
quota).
3. The TensorTrader browser extension delivers the packed Regime Sync Codes
into the script's inputs and keeps your alert enrolled and heartbeat-fresh
automatically — when a newer champion is bred for your cell, it rolls out
to you.
The codes are cell-specific on purpose: a champion evolved on HBAR 4h data has
no business trading DOGE 15m. The platform only arms the script for the market
it was actually trained on — that constraint is a feature, not a limitation.
### Credits and license
Original Lorentzian Classification logic © jdehorty, used under the Mozilla
Public License 2.0. TT-Autotune's regime classifier, sync-code system,
sliding neighbor pool, and strategy shell by TensorTrader.
---
## Publication notes (not part of the description)
- PulseWire House Rules require open-source forks of open-source scripts to
credit the original and describe meaningful changes — the sections above do
both explicitly.
- Domain in the description is `tensortrader.agent-swarm.net` (the request
contained a typo, "tnesortrader").
- Figures verified 2026-08-07 against the 148 runs with `results.json`
(admin-646 … admin-793 sweep, asof 2026-07-31). Regenerate with the
aggregation snippet in the chat transcript before republishing if new runs
have landed.
Strategy

Library

Trender [IQ]IQ Trender - TradingIQ
🔹 OVERVIEW
IQ Trender is a non-repainting trend rail built around one simple visual language:
Flat = range. Ramp = trend. Brightness = conviction.
Most trend tools try to follow every movement in price. In sideways conditions, that can leave you reading a line that bends, twitches, and changes direction inside the same noise you were trying to filter.
IQ Trender is designed to behave differently. While the market remains inside its adaptive hold zone, the rail stays deliberately flat. When the underlying trend evidence becomes strong enough, it commits to a rising or falling leg and moves in one direction until that condition genuinely changes.
The result is a clean distinction between three market states:
Holding - the rail is flat and the market is being treated as a range or consolidation.
Rising - the rail has committed to an upward leg.
Falling - the rail has committed to a downward leg.
Direction is shown by color. Conviction is shown by color intensity and glow. The Trender Radar explains the current state numerically, while the Ghost Forecast extends the rail's present trajectory into a fading uncertainty cone.
This is a trend-reading and visualization tool, not a signal service. It does not issue buy or sell calls, and it makes no claim of profitability or predictive certainty.
🔹 THE ONE-LINE MENTAL MODEL
The fastest way to read IQ Trender is to ignore the mathematics at first and watch the shape of the rail:
A flat rail means the model is holding through noise.
An upward ramp means the model has committed to a rising leg.
A downward ramp means the model has committed to a falling leg.
A stronger glow means the estimated trend is showing greater statistical conviction.
This is the same sequence demonstrated in the walkthrough: a directional leg can flatten during a pause, pullback, or consolidation, then recommit if the broader move resumes. The bearish interpretation is the mirror image - falling leg, flat hold, then a renewed falling leg if downside evidence returns.
The flat section is important. It is not a prediction that a breakout is about to happen. It is the indicator saying that current movement has not earned a directional commitment.
🔸 HOW THE ENGINE WORKS
IQ Trender combines three separate jobs: estimating the trend beneath price, deciding whether that trend is statistically meaningful, and drawing a rail that cannot wiggle backward within a committed leg.
Track the underlying trend
A robust local-linear Kalman filter estimates the level and slope beneath the candles. Unlike a conventional moving average that applies a fixed weighting pattern, this is a state-estimation model: it updates its estimate from the difference between expected and observed price.
Large isolated deviations are reduced with a robust update, so a single wick cannot directly yank the rail to a new location. The model also adapts its measurement-noise estimate as conditions change.
⬞
Measure the uncertainty
The filter calculates an innovation deviation - a live estimate of how much movement is normal relative to its current model. IQ Trender uses that value to size the hold band.
When conditions are noisy, the tolerance can widen. When conditions are calmer, it can tighten. This lets the same mental model adapt across different symbols, price levels, and timeframes without using one fixed distance everywhere.
⬞
Test for commitment and change
The estimated slope is compared with its own uncertainty to produce conviction. Hysteresis uses separate thresholds for entering and leaving a committed trend, helping prevent repeated state changes near one boundary.
A two-sided cumulative change test also monitors standardized price surprises. That evidence helps the rail distinguish a genuine opposing change from ordinary counter-movement when a leg is already active.
⬞
Draw the rail
The visible rail is a separate, slew-limited ratchet guided by the Kalman center. Once an upward leg begins, the rail can only move upward until a valid reversal or hold condition is reached. Once a downward leg begins, it can only move downward.
That monotone-within-leg behavior is what creates IQ Trender's signature geometry: flat holds connected by clean directional ramps instead of a line that bends around every candle.
🔹 THE ADAPTIVE HOLD BAND
The shaded band is the rail's live range corridor.
While the rail is holding, the band opens around it to show the volatility-adjusted area in which price can move without forcing a directional leg. When the rail commits to a trend, the displayed band eases shut onto the rail because the model has left its holding state. When the rail becomes flat again, the band gradually reopens.
The band should be read as a model tolerance, not as conventional support and resistance. Price moving within it means the model can continue to hold. Movement beyond it contributes evidence for a new leg, but it is not, by itself, a guaranteed breakout or trade entry.
🔸 COLOR, GLOW & CONVICTION
IQ Trender communicates direction and commitment through one coordinated visual system:
Rising color - active upward leg.
Falling color - active downward leg.
Holding color - neutral, flat state.
Glow intensity - visual emphasis derived from the current conviction reading.
Conviction measures how strongly the estimated slope differs from zero relative to the model's uncertainty. It is a statistical strength reading, not the probability that a trade will win.
The palette is generated in the Oklab perceptual color space. Hue, lightness, and vibrancy can be adjusted as a coordinated system, while out-of-gamut colors are compressed toward neutral instead of clipping harshly.
Accessibility controls include deuteranopia, protanopia, and tritanopia modes, plus automatic contrast correction against the chart background. A selectable contrast target helps keep the rail and directional Radar accents legible across light and dark themes.
🔹 TRENDER RADAR
The Trender Radar is the live scorecard in the corner of the chart. It reports:
State - HOLDING, RISING, or FALLING.
Conviction - normalized trend commitment from 0-100%.
Slope - the rail's current rate of change per bar.
Hold Band - the current full width of the adaptive range corridor.
Behavior - the active Speed and Pursuit combination.
With Log Geometry enabled, slope is displayed as a percentage per bar and band width is expressed as a percentage of the rail. With linear geometry, both are shown in price units.
The Radar can be moved to any chart corner or disabled entirely.
🔸 GHOST FORECAST
The Ghost Forecast is a translucent forward projection of the rail's current slope.
Its centerline extends the rail's recent trajectory. The surrounding cone widens with distance to communicate increasing uncertainty, then fades away toward the horizon. Two growth modes are available:
√h - tighter near the live bar, then gradually widening like a random-walk spread.
Linear - uncertainty expands at a constant rate.
The forecast is rebuilt only at the live edge and never painted into historical bars. It can also be displayed while the rail is holding, where its centerline remains flat.
This feature is a trajectory read, not a price target. It answers, Where is the rail currently heading if its present slope persists? It does not answer, Where will price trade?
🔹 FLIP MARKERS & ALERTS
Optional markers identify confirmed changes in rail state:
▲ - committed to a rising leg.
▼ - committed to a falling leg.
◇ - flattened back into a hold, when hold markers are enabled.
Markers are created only on confirmed bars. Once printed, they do not move.
Matching alert conditions are included for:
Trender committed to a rising trend.
Trender committed to a falling trend.
Trender flattened into a hold.
These alerts report state changes in the model. They are not automated trade recommendations and should be interpreted in the context of the symbol, timeframe, market structure, and the user's own risk process.
🔸 SPEED - THE OVERALL TEMPO
Speed changes the rail's pursuit rate and the width of its hold zone together:
Glacier - calm, structural behavior for slower or higher-timeframe reading.
Slow - patient swing behavior with a wider hold zone.
Balanced - the recommended reference setting, balancing hold and tracking.
Fast - more reactive behavior for shorter intraday movement.
Scalp - the tightest and quickest micro follower.
Slower settings generally require more displacement and move the rail more gradually. Faster settings use a tighter band and pursue price more aggressively. A faster preset is not automatically better: responsiveness and noise rejection are opposing trade-offs.
🔸 PURSUIT - HOW A COMMITTED LEG MOVES
Pursuit changes the shape of an active leg without changing the underlying trend evidence:
Steady - a constant-speed ramp established when the leg begins.
Eased - pursuit speed scales with conviction and feathers toward the estimated center.
Snap - the most decisive pursuit, with a higher movement rate and faster conviction scaling.
On slower Speed presets, Snap can appear more step-like. Steady produces the cleanest constant ramps, while Eased creates a softer approach.
🔹 HOW TO READ IQ TRENDER
Start with state
Flat rail means the model is holding. Rising or falling rail means it has committed directionally. This gives the chart an immediate range-versus-trend read before any number is considered.
⬞
Weigh the leg
Use conviction, glow, and slope together. A bright rail with firm slope represents stronger model commitment. Fading conviction says the trend estimate is becoming less distinct from noise; it does not guarantee an immediate reversal.
⬞
Watch the sequence
One useful continuation framework is:
Rising rail.
Flat hold during consolidation or pullback.
New rising marker and renewed upward rail.
The bearish sequence is the inverse. This is a way to organize market context, not a complete entry system.
⬞
Keep the forecast in its proper role
Use the Ghost Forecast to visualize current trajectory and uncertainty. Do not treat the cone edge or centerline as a promised future level.
⬞
Confirm with your own process
IQ Trender can be combined with price structure, volume, liquidity, momentum, or a trader's existing risk framework. No single state, marker, or Radar value should replace position sizing and independent confirmation.
🔸 INPUTS
Behavior
Speed
Pursuit
Source & Geometry
Price Source
Use Log Geometry
Close with Log Geometry enabled is the recommended general-purpose setup for ordinary positive price series. Log mode keeps slope and band behavior proportional across different price levels.
Rail, Band & Glow
Hold Band on/off
Band transparency
Rail Glow on/off
Glow intensity
Glow spread
Rail line width
Colors
Rising, Falling, and Holding anchors
Global hue rotation
Lightness adjustment
Vibrancy adjustment
Conviction Color response
Accessibility
Color-Blind Mode
Auto Contrast
Contrast Ratio
State Readout
Show Trender Radar
Radar location
Forecast
Ghost Forecast on/off
Horizon in bars
√h or Linear cone growth
Show While Holding
Markers
Flip Markers on/off
Optional hold markers
Marker size
🔹 NON-REPAINTING BEHAVIOR
IQ Trender is calculated causally with no future-bar lookahead.
Confirmed historical rail values and confirmed flip markers remain where they were calculated. The current, still-open bar can update as new price arrives, as any live indicator can. The Ghost Forecast is intentionally rebuilt at the live edge because it represents the rail's current slope and uncertainty; it does not rewrite historical bars.
What was confirmed in history stays confirmed. What is still live remains live.
🔸 LIMITATIONS & HONEST NOTES
IQ Trender is an indicator, not a validated trading strategy. It makes no performance, win-rate, profit, or edge claim.
Kalman filtering is still a causal estimation process. It reduces noise but cannot remove lag, uncertainty, or false transitions.
Faster settings react sooner but can respond to more noise. Slower settings filter more movement but can confirm later.
A Holding state identifies insufficient directional commitment in this model; it does not guarantee that price will remain inside a range or that a breakout is imminent.
Conviction measures the strength of the estimated slope relative to uncertainty. It is not a probability of future direction or trade success.
The Ghost Forecast extrapolates the rail, not price. It is a visual scenario if the current trajectory persists, not a target or prediction.
Alerts and markers identify model state transitions only. They should not be treated as standalone entries or exits.
Results depend on symbol behavior, timeframe, data quality, and the selected Speed/Pursuit combination.
IQ Trender is built to make one difficult market question easier to see:
Is the market still ranging, or has a trend actually committed?
One rail. Three states. No hindsight redraws.
Indicator

Machine Learning Trend Channels [FEELS]Trend channels placed by a machine learning model (change-point detection) instead of a length you have to guess. The model decides where one period of price behaviour ends and the next begins, how many periods the chart has, and how wide each channel should be. There is no length input anywhere in this script — the whole history comes out as a chain of channels handing over to one another, with no gaps and no overlaps.
FEATURES
- Periods found by an online change-point search, one channel per period, covering the history continuously
- No length setting to guess — the model chooses every boundary and how many periods there are
- The cut score carries no units, so the same setting behaves the same way on a quiet index and on a coin in free fall
- Fitted in log price, so one long trend is not split apart by its own curvature
- Channel width learned from the spread of that period's own bars, not an ATR multiple and not a fixed number of deviations
- Colour from slope measured against the period's own width: up, down, or sideways
- Panel comparing the period now forming with the median of this symbol's own past periods of the same kind
- A closed period is frozen at the moment it closes and is never recalculated
- Alert when a period closes and a new one opens
- Every model parameter, colour and size adjustable, every input has a tooltip
HOW IT WORKS
For the stretch of price it is currently holding, the model asks one question on every closed bar: is this better described by one straight line, or by two?
It scores every possible place to cut that stretch and takes the best one. The score is how much the cut improves the fit, divided by how badly the two resulting lines still fit. That second half is the important part. Dividing by the stretch's own leftover spread is what strips the units out of the number, so a violent market does not get chopped more finely than a calm one merely for being violent. When the score clears the Detail threshold, the left piece is closed permanently and the right piece becomes the new forming period.
Everything is fitted on the logarithm of price. In plain price, one long exponential trend gets broken into a dozen channels purely by its own curvature, which is a measurement artefact rather than market structure.
The width is measured, not assumed. Each channel takes its width from how far its own bars actually strayed from its own line, drawn just wide enough to hold the share you set under "Channel covers". A period whose bars hugged the line is thin; a period that swung around it is wide.
HOW TO READ IT
1. A solid channel is a closed period. Its slope, width and endpoints were fixed the moment it closed. An outlined channel is the period still forming, shown together with the cut the model is currently leaning towards.
2. Colour is slope. A period is called sideways when its whole rise or fall is smaller than its own width, that is, when the drift is smaller than the noise around it.
3. Width is dispersion, not a boundary. A wide channel says that period was noisy. It does not say price will turn there.
4. The panel puts the forming period next to what this symbol's own periods of the same kind have typically looked like. "down, 21 bars, usually 31, moved -14.7%, usually 50.6%" reads as: shorter and far smaller than this symbol's usual decline, so far. The sample count is shown next to it, because five periods is a hint and sixty is a distribution.
ORIGINALITY
Every channel tool on this platform asks you for a length. Fifty bars, two hundred, and the entire picture changes with that one number. The better ones automate it by scanning lengths and keeping the best-fitting window, which still produces a single channel measured backwards from today.
This one treats the chart as a segmentation problem instead. The whole history is a chain of periods that hand over to one another, the boundaries are found rather than set, and the number of periods is an output rather than an input. The scale-free cut criterion, the log-space fitting, the learned width and the comparison of the live period against this symbol's own past periods are written from scratch for this script.
HONESTY
- Closed periods never change. Once a cut is confirmed, that channel's numbers are frozen and the drawing is rebuilt from those frozen numbers, so stepping through bar replay will not move a solid channel.
- The forming period does change, and it is the whole forming period, not only its last few bars. Its cut stays provisional until confirmed, which typically takes twenty to thirty bars after the fact. That is why it is drawn as an outline. Any tool that finds structure behaves this way.
- The channel edges are not support and resistance, and I checked rather than assumed. Asking only about the very next bar, price leaves a band built to hold ninety per cent of its own bars far more often than that width suggests, and the bars that escape go out of the top and the bottom in roughly equal numbers. There is no bounce hiding in the edges.
- Nothing here predicts anything. A closed period is a statement about bars that have already closed.
- The panel medians describe past periods on the current symbol and timeframe. They are not performance figures and small samples move them a great deal, which is why the count is on screen.
- PulseWire allows a script five hundred drawing objects and drops the oldest past that, so only the most recent periods are drawn. Raise "Periods kept on screen" if you want more history covered.
ALERTS
A period closed and a new one opened.
SETTINGS
Every input has a tooltip. The main ones: "Detail" sets how much better two lines must fit than one before a period is closed, and because it carries no units the same value transfers across symbols and timeframes. "Shortest period" and "Longest period" are hard bounds in bars. "Channel covers" is the share of a period's own bars the channel is drawn wide enough to hold. "Call it sideways below" controls how small a move must be, relative to its own width, to be coloured sideways. "Periods kept on screen" trades history for drawing budget.
This is a descriptive tool for reading price structure. It is not financial advice and does not predict price.
Indicator

Market Network Confirmation [NeuralMarkets]Market Network Confirmation
Price tells you where the market moved. The network tells you whether the market moved together.
A strong index move can look convincing on the surface and still be structurally weak underneath.
Sometimes SPY rallies while only a handful of sectors participate.
Sometimes the index is flat while participation quietly broadens.
Sometimes the market looks healthy, but the sector network is already fragmenting.
This indicator was built to measure that difference.
What this indicator is designed to answer
Not:
"Is SPY up or down?"
But:
"Is the current move actually supported by the market underneath it?"
Market Network Confirmation analyzes the internal structure of the S&P 500 through its major sector ETFs and combines breadth with graph-based network analytics.
The result is a structural read on whether the current move is broad, narrow, deteriorating, recovering, or fragmented.
The Market as a Network
The indicator models the major SPDR sector ETFs as nodes in a financial network:
XLK - Technology
XLF - Financials
XLY - Consumer Discretionary
XLC - Communication Services
XLI - Industrials
XLV - Health Care
XLP - Consumer Staples
XLE - Energy
XLU - Utilities
XLB - Materials
XLRE - Real Estate
Rolling relationships between sector returns form the edges of the network.
The indicator then evaluates the structure using multiple graph-theory measures instead of relying on a single breadth statistic.
Market States
Broad Confirmation
The index move is supported by broad sector participation and a healthy underlying network.
This is the cleanest confirmation state.
Narrow Advance
SPY is moving higher, but participation is limited or leadership is overly concentrated.
The move may still continue, but the internal structure is less convincing.
Internal Divergence
SPY continues to advance while network health deteriorates underneath.
Price strength and internal structure are moving in opposite directions.
Recovery Broadening
SPY remains weak, but internal network conditions are improving.
Participation may be strengthening before the index itself fully recovers.
Distribution
Selling is broadly confirmed while network health remains weak.
The decline is not isolated to a small part of the market.
Fragmentation
The sector network breaks into weakly connected groups.
In this environment, the market behaves less like one coherent system and more like a collection of disconnected sectors.
Healthy Rotation
No major structural warning is present, but the market is rotating rather than moving with strong broad confirmation.
Network Health
The indicator creates a composite Network Health score from several graph measures:
• Mean network connectivity
• Strong-edge density
• Clustering coefficient
• Minimum Spanning Tree compactness
• Network entropy
• Decentralization
• Connected components
The score is normalized from 0 to 100.
A high Network Health score means the sector network is structurally coherent and broadly connected.
A low score means relationships are weaker, more fragmented, or overly concentrated.
Important:
High Network Health is not automatically bullish.
A market can be strongly connected while rising or while falling.
Network Health measures structural coherence, not direction.
Participation
Participation measures how many sectors are moving in the same direction as SPY.
For example:
SPY rising + 9 of 11 sectors rising
indicates broad bullish participation.
SPY rising + only 4 of 11 sectors rising
indicates a narrow advance.
Participation tells you how many sectors agree.
Network Health tells you how structurally connected the market is.
Those are not the same thing.
Move Confirmation
Move Confirmation combines:
• Network Health
• Sector Participation
• Distribution of influence across the network
This creates a 0-100 measure of how strongly the internal market structure supports the current index move.
The dashboard classifies confirmation as:
• High
• Moderate
• Low
This is not a probability of future returns.
It is a measure of structural agreement behind the current move.
Internal Trend
The indicator also tracks whether network health is:
• Improving
• Stable
• Deteriorating
This becomes useful when price and internal structure start moving in opposite directions.
For example:
SPY making new highs while Network Health declines
is very different from:
SPY making new highs while Network Health strengthens.
Sector Network
The Sector Network panel shows which sectors are:
• Supporting the current SPY direction
• Diverging from it
This gives a fast view of whether the move is broad or being carried by only a few sectors.
Network Diagnostics
For users who want to inspect the underlying graph structure, the indicator exposes the individual network metrics.
Connectivity
Average strength of relationships across the sector network.
Strong Edge Density
Percentage of strong relationships currently present in the network.
Clustering
Measures whether sectors are forming tightly connected groups.
MST Compactness
Uses a Minimum Spanning Tree to measure how efficiently the full sector network can be connected.
Entropy
Measures how broadly network influence is distributed.
Decentralization
Shows whether the network is broadly distributed rather than dominated by a small number of nodes.
Connectedness
Measures how close the system is to behaving as one connected network.
Fragmentation
Measures how disconnected the market has become.
Why use network analysis?
Traditional breadth tools usually count:
• Advancers vs decliners
• Positive vs negative sectors
• Stocks above moving averages
• New highs vs new lows
Those are useful.
But they do not measure how relationships between market components are changing.
Two markets can both have 8 bullish sectors.
One may be tightly connected and behaving as a coherent market.
The other may contain several disconnected clusters with very weak relationships.
Graph analysis can distinguish between those structures.
Practical Use
Market Network Confirmation can be used as a second layer of analysis when evaluating:
• Breakouts
• Trend continuation
• Rally quality
• Selloff confirmation
• Market breadth
• Sector rotation
• Internal divergence
• Recovery attempts
Research tool only. Not a standalone buy/sell signal. Indicator

COT Commercial Hedger ExtremeIn commodities, the crowd and the smart money sit on opposite sides of the same report every week. The Commitment of Traders breaks open interest into commercials -- the producers, merchants and processors who hedge physical -- and large speculators, who are mostly trend-following money. The commercials are the ones who actually touch the barrel, the bushel, the bar. When they move to an extreme, it pays to listen.
The pattern that marks real commodity bottoms is simple to say and hard to wait for: commercial hedgers covering shorts en masse, from a multi-year net-short extreme, and curling back toward flat or net long. That is the producers deciding price has fallen far enough that they no longer need to hedge aggressively. It happened at the 2008 low, it happened into the 2018-19 lows, and I used this exact tell to call the September 2022 gold bottom near $1,640 in real time on PulseWire. Gold peaked above $5,500 in January 2026, a move of more than 200% from that low.
This indicator puts that read on your chart.
The colored net line is the commercial position: red when they're at an extreme and still pressing shorts (no bottom), yellow when they start covering up off that extreme, green when they curl to flat or net long (the bottom tell). A triangle marks the first week they begin covering, a diamond marks the cross to net long. The blue line is large speculators for context -- they are usually heaviest long right as commercials are heaviest short, which is the whole point. Optional small-trader line too.
The dashboard shows commercial net, where it sits in its multi-year percentile, the spec and small-trader nets, and whether covering is underway. Alerts fire when hedgers start covering and when they flip toward net long.
Works on any futures with a CFTC code -- gold, silver, copper, oil, grains, even the index futures. Set the code in the settings to match the contract you're charting. Default is gold.
One read in isolation is a tell, not a trigger. I pair it with trend and price structure -- it tells you the tank is full of fuel, not that the match is lit. But when commercials cover their shorts, I want to know. Indicator

NeuralMarketsNetworkToolkitLibrary "NeuralMarketsNetworkToolkit"
Open-source network analysis toolkit for Pine Script.
This library provides reusable graph algorithms, matrix utilities and network analytics for building advanced multi-asset indicators. Rather than treating markets as isolated charts, it enables developers to model relationships between assets as weighted networks and extract structural characteristics such as connectivity, centrality, clustering and influence.
Current Modules
• Matrix utilities
• Directed & undirected graphs
• Network analytics
• Node analytics
• Graph algorithms
• Experimental financial network tools
Example Applications
• Correlation networks
• Market leadership analysis
• Sector relationship maps
• Cross-asset dependency analysis
• Financial network research
Design Philosophy
This toolkit provides reusable quantitative building blocks rather than trading signals. Functions are intentionally modular so they can be combined into custom indicators and research projects.
Markets are networks. This toolkit provides the building blocks to analyze them as such.
--------------------------------------------------------------------
matrixIndex(row, col, n)
Converts row/column coordinates into a flat matrix index.
Parameters:
row (int) : Row index.
col (int) : Column index.
n (int) : Matrix dimension.
Returns: Flat-array index.
clamp(x, lo, hi)
Clamp a float.
Parameters:
x (float) : Value.
lo (float) : Minimum.
hi (float) : Maximum.
Returns: Clamped value.
newMatrix(n, initialValue)
Creates an n x n flat matrix initialized to a value.
Parameters:
n (int) : Number of nodes.
initialValue (float) : Initial cell value.
Returns: Flat float array.
setCell(matrix, row, col, n, value)
Sets a matrix cell.
Parameters:
matrix (array) : Flat matrix.
row (int) : Row.
col (int) : Column.
n (int) : Matrix dimension.
value (float) : New value.
getCell(matrix, row, col, n)
Gets a matrix cell.
Parameters:
matrix (array) : Flat matrix.
row (int) : Row.
col (int) : Column.
n (int) : Matrix dimension.
Returns: Cell value.
setUndirectedEdge(matrix, a, b, n, weight)
Sets both directions of an undirected edge.
Parameters:
matrix (array) : Flat matrix.
a (int) : Node A.
b (int) : Node B.
n (int) : Matrix dimension.
weight (float) : Edge weight.
meanAbsoluteConnectivity(matrix, n)
Average absolute pairwise edge weight.
Parameters:
matrix (array) : Symmetric adjacency/weight matrix.
n (int) : Number of nodes.
Returns: Average absolute connectivity from 0 upward.
meanSignedConnectivity(matrix, n)
Average signed pairwise weight.
Parameters:
matrix (array) : Symmetric matrix.
n (int) : Number of nodes.
Returns: Mean signed relationship.
density(matrix, n, threshold)
Proportion of possible edges whose absolute weight exceeds threshold.
Parameters:
matrix (array) : Symmetric weight matrix.
n (int) : Number of nodes.
threshold (float) : Absolute edge threshold.
Returns: Network density from 0 to 1.
fragmentation(matrix, n, threshold)
Network fragmentation as inverse threshold density.
Parameters:
matrix (array) : Symmetric weight matrix.
n (int) : Number of nodes.
threshold (float) : Edge threshold.
Returns: Fragmentation from 0 to 1.
nodeDegree(matrix, n, node, threshold)
Number of strong edges attached to a node.
Parameters:
matrix (array) : Weight matrix.
n (int) : Number of nodes.
node (int) : Node index.
threshold (float) : Absolute edge threshold.
Returns: Degree count.
nodeStrength(matrix, n, node)
Sum of absolute edge weights attached to node.
Parameters:
matrix (array) : Weight matrix.
n (int) : Number of nodes.
node (int) : Node index.
Returns: Node strength.
strongestNode(matrix, n)
Node with greatest absolute network strength.
Parameters:
matrix (array) : Weight matrix.
n (int) : Number of nodes.
Returns: Strongest node index.
averageNodeStrength(matrix, n)
Average node strength.
Parameters:
matrix (array) : Weight matrix.
n (int) : Number of nodes.
Returns: Mean strength.
centralization(matrix, n)
Measures how much one node dominates the network.
Parameters:
matrix (array) : Weight matrix.
n (int) : Number of nodes.
Returns: Strength centralization approximately 0 to 1.
strengthEntropy(matrix, n)
Shannon entropy of node-strength distribution.
Parameters:
matrix (array) : Weight matrix.
n (int) : Number of nodes.
Returns: Normalized entropy from 0 to 1.
mstDistance(matrix, n)
Computes total Prim minimum-spanning-tree distance.
Similarity is converted to distance using 1 - abs(similarity).
Parameters:
matrix (array) : Similarity matrix.
n (int) : Number of nodes.
Returns: Total MST distance.
mstCompactness(matrix, n)
Converts MST distance to compactness.
Parameters:
matrix (array) : Similarity matrix.
n (int) : Number of nodes.
Returns: Network compactness from approximately 0 to 1.
setDirectedEdge(matrix, fromNode, toNode, n, weight)
Sets one directed edge.
Parameters:
matrix (array) : Flat directed adjacency matrix.
fromNode (int) : Source node.
toNode (int) : Destination node.
n (int) : Number of nodes.
weight (float) : Directed edge weight.
outStrength(matrix, n, node)
Sum of outgoing positive influence from a node.
Parameters:
matrix (array) : Directed matrix.
n (int) : Number of nodes.
node (int) : Source node.
Returns: Total outbound influence.
inStrength(matrix, n, node)
Sum of incoming positive influence to a node.
Parameters:
matrix (array) : Directed matrix.
n (int) : Number of nodes.
node (int) : Destination node.
Returns: Total inbound influence.
netInfluence(matrix, n, node)
Net directional leadership.
Positive means the node influences others more than it follows them.
Negative means the node behaves more like a follower.
Parameters:
matrix (array) : Directed matrix.
n (int) : Number of nodes.
node (int) : Node index.
Returns: Outbound minus inbound influence.
normalizedLeadership(matrix, n, node)
Normalized directional leadership score.
Parameters:
matrix (array) : Directed matrix.
n (int) : Number of nodes.
node (int) : Node index.
Returns: Score approximately from -1 to +1.
leadingNode(matrix, n)
Node with the largest net directional influence.
Parameters:
matrix (array) : Directed matrix.
n (int) : Number of nodes.
Returns: Node index.
followingNode(matrix, n)
Node with the greatest incoming influence.
Parameters:
matrix (array) : Directed matrix.
n (int) : Number of nodes.
Returns: Node index.
meanDirectedInfluence(matrix, n)
Average directed influence in the network.
Parameters:
matrix (array) : Directed matrix.
n (int) : Number of nodes.
Returns: Mean positive directed edge weight.
leadershipConcentration(matrix, n)
Concentration of outbound influence.
High values mean leadership is concentrated in fewer nodes.
Parameters:
matrix (array) : Directed matrix.
n (int) : Number of nodes.
Returns: Herfindahl-style concentration from 0 to 1.
connectedComponentsCount(matrix, n, threshold)
Counts connected components in an undirected threshold graph.
Parameters:
matrix (array) : Symmetric adjacency / similarity matrix.
n (int) : Number of nodes.
threshold (float) : Minimum absolute edge weight required to connect nodes.
Returns: Number of connected components.
localClusteringCoefficient(matrix, n, node, threshold)
Computes local clustering coefficient for one node.
Measures how interconnected the node's neighbors are.
Parameters:
matrix (array) : Symmetric similarity matrix.
n (int) : Number of nodes.
node (int) : Node index.
threshold (float) : Minimum absolute edge weight to define a connection.
Returns: Local clustering coefficient from 0 to 1.
averageClusteringCoefficient(matrix, n, threshold)
Computes mean clustering coefficient across all nodes.
Parameters:
matrix (array) : Symmetric similarity matrix.
n (int) : Number of nodes.
threshold (float) : Minimum absolute edge weight.
Returns: Average clustering coefficient from 0 to 1.
similarityDistance(similarity)
Converts similarity to graph distance.
Higher similarity becomes shorter distance.
Parameters:
similarity (float) : Edge similarity, typically from 0 to 1 in magnitude.
Returns: Distance from 0 to 1.
shortestPathDistance(matrix, n, source, target)
Dijkstra shortest-path distance between two nodes.
Uses distance = 1 - abs(similarity).
Parameters:
matrix (array) : Weighted matrix.
n (int) : Number of nodes.
source (int) : Start node.
target (int) : End node.
Returns: Shortest path distance.
averagePathLength(matrix, n)
Average shortest-path distance across all node pairs.
Parameters:
matrix (array) : Weighted matrix.
n (int) : Number of nodes.
Returns: Mean shortest path distance.
eigenvectorCentrality(matrix, n, node, iterations)
Approximate eigenvector centrality for one node using power iteration.
Parameters:
matrix (array) : Weighted matrix.
n (int) : Number of nodes.
node (int) : Node index.
iterations (int) : Number of power iterations.
Returns: Approximate normalized centrality from 0 to 1.
eigenvectorLeader(matrix, n, iterations)
Returns node with highest eigenvector centrality.
Parameters:
matrix (array) : Weighted matrix.
n (int) : Number of nodes.
iterations (int) : Number of power iterations.
Returns: Node index.
nodeStrengthPercentile(matrix, n, node)
Cross-sectional percentile rank for a node's strength.
Parameters:
matrix (array) : Weighted matrix.
n (int) : Number of nodes.
node (int) : Node index.
Returns: Percentile rank from 0 to 100.
nodeStrengthRank(matrix, n, node)
Returns the rank position of a node by strength.
Rank 1 means strongest.
Parameters:
matrix (array) : Weighted matrix.
n (int) : Number of nodes.
node (int) : Node index.
Returns: One-based rank.
networkCohesion(matrix, n, threshold)
Composite network cohesion score.
Combines connectivity, density and clustering coefficient.
Parameters:
matrix (array) : Symmetric similarity matrix.
n (int) : Number of nodes.
threshold (float) : Edge threshold.
Returns: Composite cohesion from 0 to 1. Library

Indicator
