From 6e5f796779e65dce5a8946482f98540068abdd9b Mon Sep 17 00:00:00 2001 From: MeridianAlgo Date: Mon, 15 Jun 2026 20:51:34 -0500 Subject: [PATCH 1/2] Add bar-by-bar execution engine so converted scripts produce real per-bar series --- README.md | 68 +- ROADMAP.md | 24 +- converted/3D_MACD_Bar_Plot_LuxAlgo.js | 656 ++++++++++-- converted/Arbitrage_Matrix_LuxAlgo.js | 710 +++++++++++-- converted/Asset_Drift_Model.js | 686 ++++++++++-- converted/Automatic_Trendline_Metrify.js | 680 ++++++++++-- converted/CE_1M_3M_Buy_Sell_Hold.js | 646 +++++++++++- ...rossing_Moving_Averages_with_ADX_Filter.js | 644 +++++++++++- converted/Folded_RSI.js | 658 ++++++++++-- converted/Golden_Pocket_Zones.js | 648 +++++++++++- converted/Hyperfork_Matrix.js | 774 +++++++++++--- converted/Impulse_Trend_Arrows.js | 648 +++++++++++- ..._Order_Flow_Strength_Classifier_LuxAlgo.js | 682 ++++++++++-- converted/Liquidity_Sweep_Detector.js | 704 +++++++++++-- converted/Manias_Panics_and_Crashes.js | 702 +++++++++++-- ...m_Orb_-_Statistics_LTF_Candle_structure.js | 782 +++++++++++--- converted/Net_Buy_Volume_theUltimator5.js | 634 +++++++++++- .../Peak_Trading_Activity_Graphs_LuxAlgo.js | 666 ++++++++++-- .../Pro_Pattern_Long-Short_High_Winrate.js | 730 +++++++++++-- converted/Proximal_Range_Filter_LuxAlgo.js | 650 +++++++++++- converted/Quantum_Structure_jjart.err.txt | 5 +- converted/RBT_strategy.js | 662 ++++++++++-- converted/SMC_Trade_with_Dhruv.js | 838 ++++++++++++--- converted/Scalping_Strategy_3_minutes.js | 978 ++++++++++++++---- ...Singular_Spectrum_Decomposition_LuxAlgo.js | 674 ++++++++++-- ...3_by_Ata_Sabanci_Candles_and_Tradelines.js | 926 +++++++++++++---- ...mas_-_Aligned_Timeframe_Fair_Value_Gaps.js | 668 ++++++++++-- converted/VSA_Pro_Trend_and_Rejection.js | 654 +++++++++++- converted/Volatility_Contraction_Score.js | 670 ++++++++++-- converted/Volume_Surprise_LuxAlgo.js | 660 ++++++++++-- converted/Volume_Weighted_Trend_QuantAlgo.js | 666 ++++++++++-- converted/status.json | 62 +- package.json | 4 +- src/builtins.js | 120 ++- src/generator.js | 214 +++- src/reviewer.js | 4 +- test/engine_tests.js | 178 ++++ tools/batch_convert.js | 4 +- 38 files changed, 18039 insertions(+), 2340 deletions(-) create mode 100644 test/engine_tests.js diff --git a/README.md b/README.md index c0c0b18..fe2c941 100644 --- a/README.md +++ b/README.md @@ -268,26 +268,45 @@ Note: Small models can produce repetitive or low-quality output. The reviewer sa | `strategy.entry`, `strategy.close`, `strategy.exit`, `strategy.order` | Logged but not backtested | | `strategy.long`, `strategy.short` | Direction constants | -### How Series Semantics Work +### How the Execution Engine Works -PineScript treats data as series -- arrays where the last element is the current bar. This transpiler preserves that behavior: +This is the heart of correct PineScript behavior. PineScript does **not** run a script once over a whole dataset -- it runs the entire script **once per bar**, building up time series as it goes. On bar `i`, `close` is that bar's close, `close[1]` is the previous bar, `ta.sma(close, 20)` is the moving average *at bar i*, and a variable like `fast = ta.sma(close, 20)` is itself a series whose history (`fast[1]`) is available on later bars. -- **History indexing**: `x[1]` in Pine becomes `pinescript.offset(x, 1)` in JavaScript. This safely grabs the value from one bar ago. -- **Current bar arithmetic**: When you write `close + open` in Pine, both operands use the most recent bar value. The runtime wraps OHLCV arrays in a `SeriesArray` class whose `valueOf()` returns the last element, so arithmetic just works. -- **Null safety**: `na(x)` checks for null/undefined/NaN. `nz(x, default)` replaces bad values. These work exactly like their Pine counterparts. +The generated code reproduces this exactly: + +- Every converted file exports a **`run(data)`** function. It loops over the bars, growing the OHLCV series one bar at a time, and calls the script body (`main()`) for each bar. +- **Series, not scalars.** Because the body runs per bar, window built-ins (`ta.sma`, `ta.highest`, ...) compute the correct value *at each bar*, and plotting a value collects a full output series -- one value per bar -- instead of a single final number. +- **History indexing**: `x[n]` is compiled to `pinescript.hist(id, x, n)`. The runtime records the current-bar value of `x` at a unique call-site `id` and reads back `n` bars, so lookback works even when `x` is computed on the current bar. +- **Crossover / change / lookback**: functions like `ta.crossover`, `ta.change`, `ta.rising`, `ta.barssince` have their series arguments wrapped in `pinescript.series(id, expr)`, which accumulates per-bar history. This is what makes them actually fire on the right bar rather than always returning `false`. +- **Current-bar arithmetic**: `close + open` uses each bar's value. OHLCV are `SeriesArray` objects whose `valueOf()` returns the current bar, so plain arithmetic works. +- **Null safety**: `na(x)` checks for null/undefined/NaN; `nz(x, default)` replaces bad values, just like Pine. + +#### Running converted output + +```javascript +import { run } from './converted/my_indicator.js'; + +// data is an object of equal-length OHLCV arrays +const result = run({ + open, high, low, close, volume, time, +}); + +// result.plots and result.plotshapes are named series, one value per bar +console.log(result.plots['Fast MA'].data); // [na, na, 2, 3, 4, ...] +``` ### How State Persistence Works -Variables declared with `var` or `varip` keep their values across bar executions: +Variables declared with `var` or `varip` keep their values across bars. Because `run()` calls `main()` once per bar without resetting state between bars, accumulation works naturally: ```javascript -// What the transpiler generates for: var float lastCross = na -if (state.lastCross === undefined) state.lastCross = null; -// Reads become: state.lastCross -// Writes become: state.lastCross = newValue; +// What the transpiler generates for: var float total = 0.0 +if (state.total === undefined) state.total = 0.0; +// Reads become: state.total +// Writes become: state.total = newValue; ``` -The `state` object is stored in `globalThis.__pineState` so it survives across calls to `main()`. +The `state` object lives in `globalThis.__pineState` and is reset once at the start of each `run()` call, so repeated runs are independent. ### How Multi-Timeframe Works @@ -307,15 +326,20 @@ The `state` object is stored in `globalThis.__pineState` so it survives across c These PineScript features are not yet fully implemented. The transpiler will handle scripts that use them, but the runtime behavior may not match TradingView exactly. ### Not yet implemented +- **Function-scope hoisting**: PineScript variables are function-scoped, but the generator currently declares them with `let` at first use. A variable first assigned inside an `if`/`for` block and read outside it can become a JS block-scope error. This is the top priority for the next release. +- **`if`/`switch` as a function's return value**: when the last statement of a user function is an `if`/`else` (or `switch`), its value isn't returned yet -- such helpers currently return `undefined`. - **Strategy backtesting**: Entry/exit functions log actions but do not simulate trades or generate equity curves +- **Unimplemented stdlib degrades gracefully**: unmapped namespaces (e.g. `matrix.*`, `chart.*`, exotic `request.*`) resolve to safe no-ops that return `null` rather than crashing, so scripts run end-to-end. The reviewer reports which builtins are missing. - **Pine v6 maps**: `map.new`, `map.get`, `map.set` have basic Map-backed implementations but are not battle-tested - **User-defined type methods**: `type Foo` with `method` blocks has limited support - **Library imports**: `import` statements are parsed but the imported functions are not resolved - **Session calendars**: `timeframe.session` and session-based resampling -- **Polyline animations**: `polyline.set_*` methods -- **Line fills**: `linefill.new` and related functions -- **Tooltip formatting**: `format.*` options for `plot` -- **Plot/CSV export**: No built-in way to export plotted values +- **Drawings**: `line`/`box`/`label`/`polyline`/`linefill` objects are created but not rendered or fully updated +- **Plot/CSV export**: `run()` returns plot series in memory; there is no file export helper yet + +### Verified working + +The bar-by-bar engine is covered by a numeric test suite (`npm run test:engine`) that runs generated code over known data and asserts the full output series against hand-computed values: `ta.sma`, `ta.ema`, `ta.change`, `ta.highest`, `x[1]` lookback, `var` accumulation, `bar_index`, and `ta.crossover` firing on the exact bar. ## CLI Reference @@ -404,11 +428,21 @@ alertcondition(ta.crossover(fastMA, slowMA), "MA Crossover", "Fast crossed above alertcondition(ta.crossunder(fastMA, slowMA), "MA Crossunder", "Fast crossed below slow") ``` -Convert and run: +Convert it: ```bash node src/cli.js test_script.pine test_script.js -node test_script.js # Needs OHLCV data loaded into globalThis +``` + +Then run it bar-by-bar over your OHLCV data: + +```javascript +import { run } from './test_script.js'; + +const result = run({ open, high, low, close, volume, time }); +// result.plots['Fast MA'].data -> full per-bar series +// result.plotshapes['Crossover'].data -> per-bar booleans +// result.alerts -> alertcondition() hits with their bar index ``` ## Contributing diff --git a/ROADMAP.md b/ROADMAP.md index 8842ea4..d786256 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -5,19 +5,21 @@ Current version: 1.2.0 ## Phase 1 -- Critical Fixes (v1.3.0) [In Progress] ### Done +- **Bar-by-bar execution engine** -- the big one. Converted scripts now export `run(data)` and execute once per bar, so indicators produce correct full time series instead of a single final-bar scalar. This was the core reason output was wrong before. +- **Call-site history** -- `x[n]` compiles to `pinescript.hist(id, x, n)`; lookback built-ins (`crossover`, `change`, `rising`, `barssince`, ...) wrap their series args with `pinescript.series(id, ...)`. History-dependent logic now actually works. +- **Numeric correctness test suite** (`test/engine_tests.js`) -- runs generated code over known data and asserts full output series against hand-computed values (sma, ema, change, highest, `[1]`, var accumulation, bar_index, crossover). +- **Graceful degradation** -- unimplemented namespaces resolve to safe no-ops instead of throwing, so scripts run end-to-end; safe tuple unpacking for `[a, b] = na`. +- Added missing built-ins: `time()`, `sign`, `alertcondition`, `barcolor`, `bgcolor`, `fill`, `hline`. +- Fixed `batch_convert.js` pass/fail detection (was matching the wrong review string and reporting UNKNOWN for everything). - Fixed crossover/crossunder mapping bug (were both mapping to the same function) -- Added 40+ missing built-in functions (supertrend, DMI/ADX, input stubs, drawing functions, etc.) - Upgraded post-conversion reviewer with confidence scoring, completeness checks, series safety analysis -- Humanized all code comments across the codebase -- Expanded function mapping table from ~180 to ~240 entries -- Fixed test suite assertions - -### Remaining -- Fix TokenType.INPUT parser reference -- Improve parser error messages with construct context -- Add method keyword support in lexer -- Create real indicator test fixtures (replacing hardcoded missing files) -- Add array.first(), array.last(), and other commonly-used array functions +- Expanded function mapping table from ~180 to ~240+ entries + +### Remaining (highest priority first) +- **Function-scope hoisting**: declare user variables at function scope, not block scope, so a variable first assigned inside an `if`/`for` and read outside it doesn't become a JS reference error. (Biggest remaining correctness gap for real scripts.) +- **`if`/`switch` as a function's last expression**: return the branch value instead of `undefined`. +- Improve parser error messages with construct context (e.g. the `Unexpected token: )` failure on `Quantum_Structure_jjart.pine`). +- Add real indicator test fixtures and broaden numeric coverage (rsi, atr, macd, bb). ## Phase 2 -- Parser Hardening (v1.4.0) - Support switch with `case > value =>` Pine syntax diff --git a/converted/3D_MACD_Bar_Plot_LuxAlgo.js b/converted/3D_MACD_Bar_Plot_LuxAlgo.js index 4ae7bcb..75f6720 100644 --- a/converted/3D_MACD_Bar_Plot_LuxAlgo.js +++ b/converted/3D_MACD_Bar_Plot_LuxAlgo.js @@ -1,12 +1,63 @@ -// PineScript to JavaScript Transpiled Code +// Auto-generated JavaScript from PineScript source -// Generated automatically +// Do not edit by hand -- re-run the converter instead -// Built-in PineScript functions +// All the built-in PineScript functions that scripts depend on at runtime const pinescript = { + __scalar: function(v) { + if (v === null || v === undefined) return v; + if (typeof v === 'object' && typeof v.valueOf === 'function') { + const s = v.valueOf(); + if (typeof s === 'number' || typeof s === 'boolean' || s === null) return s; + } + return v; + }, + series: function(id, value) { + const rt = this.__rt || (this.__rt = {}); + const store = rt.series || (rt.series = {}); + let buf = store[id]; + if (!buf) buf = store[id] = []; + buf[this.__bar | 0] = this.__scalar(value); + return buf; + }, + hist: function(id, value, n) { + const buf = this.series(id, value); + const idx = (this.__bar | 0) - (n | 0); + return idx >= 0 && idx < buf.length ? buf[idx] : null; + }, + time: function(timeframe, session) { + const t = globalThis.time; + if (t && typeof t.valueOf === 'function') return t.valueOf(); + return Array.isArray(t) ? t[t.length - 1] : t; + }, + sign: function(x) { + if (x === null || x === undefined || (typeof x === 'number' && isNaN(x))) return null; + return Math.sign(Number(x)); + }, + unpack: function(value, count) { + if (Array.isArray(value)) return value; + if (value != null && typeof value[Symbol.iterator] === 'function') return Array.from(value); + return new Array(count || 0).fill(null); + }, + alertcondition: function(condition, ...rest) { + if (globalThis.__pineRuntime) { + globalThis.__pineRuntime.alerts.push({ condition, args: rest }); + } + return null; + }, + barcolor: function(color) { return null; }, + bgcolor: function(color, title, editable, showLast) { + return null; + }, + fill: function(series1, series2, color, title, editable) { + return null; + }, + hline: function(_price, _title, _opts) { + return null; + }, sma: function(series, length) { if (series === null || series === undefined) return null; if (!Array.isArray(series)) series = [series]; @@ -84,6 +135,13 @@ const pinescript = { } return weightSum > 0 ? sum / weightSum : null; }, + swma: function(series) { + if (series === null || series === undefined) return null; + if (!Array.isArray(series)) series = [series]; + if (series.length < 4) return null; + const s = series.slice(-4); + return (s[0] * 1 + s[1] * 2 + s[2] * 2 + s[3] * 1) / 6; + }, atr: function(high, low, close, length = 14) { if (!Array.isArray(high) || !Array.isArray(low) || !Array.isArray(close)) return null; if (high.length < length + 1 || low.length < length + 1 || close.length < length + 1) return null; @@ -114,6 +172,53 @@ const pinescript = { const atr = builtins.get('atr')(high, low, close, length); return { middle: sma, upper: sma + mult * atr, lower: sma - mult * atr }; }, + supertrend: function(factor, atrPeriod, high, low, close) { + if (!Array.isArray(high) || !Array.isArray(low) || !Array.isArray(close)) return [null, null]; + if (high.length < atrPeriod + 1) return [null, null]; + const atrVal = builtins.get('atr')(high, low, close, atrPeriod); + if (atrVal === null) return [null, null]; + const lastIdx = close.length - 1; + const hl2 = (high[lastIdx] + low[lastIdx]) / 2; + const upperBand = hl2 + factor * atrVal; + const lowerBand = hl2 - factor * atrVal; + // Determine direction: 1 means downtrend (price below band), -1 means uptrend + const direction = close[lastIdx] > upperBand ? -1 : close[lastIdx] < lowerBand ? 1 : -1; + const supertrendValue = direction === -1 ? lowerBand : upperBand; + return [supertrendValue, direction]; + }, + ta_supertrend: function(factor, atrPeriod) { + const H = globalThis.high; + const L = globalThis.low; + const C = globalThis.close; + return builtins.get('supertrend')(factor, atrPeriod, H, L, C); + }, + dmi: function(diLength, adxSmoothing, high, low, close) { + if (!Array.isArray(high) || !Array.isArray(low) || !Array.isArray(close)) return { plusDI: null, minusDI: null, adx: null }; + if (high.length < diLength + 1) return { plusDI: null, minusDI: null, adx: null }; + let plusDMSum = 0, minusDMSum = 0, trSum = 0; + for (let i = 1; i <= diLength; i++) { + const idx = high.length - 1 - diLength + i; + const upMove = high[idx] - high[idx - 1]; + const downMove = low[idx - 1] - low[idx]; + plusDMSum += (upMove > downMove && upMove > 0) ? upMove : 0; + minusDMSum += (downMove > upMove && downMove > 0) ? downMove : 0; + const tr = Math.max( + high[idx] - low[idx], + Math.abs(high[idx] - close[idx - 1]), + Math.abs(low[idx] - close[idx - 1]) + ); + trSum += tr; + } + const plusDI = trSum > 0 ? (plusDMSum / trSum) * 100 : 0; + const minusDI = trSum > 0 ? (minusDMSum / trSum) * 100 : 0; + const diSum = plusDI + minusDI; + const dx = diSum > 0 ? Math.abs(plusDI - minusDI) / diSum * 100 : 0; + return { plusDI, minusDI, adx: dx }; + }, + adx: function(diLength, adxSmoothing, high, low, close) { + const result = builtins.get('dmi')(diLength, adxSmoothing, high, low, close); + return result ? result.adx : null; + }, rsi: function(close, length = 14) { if (close === null || close === undefined) return null; if (!Array.isArray(close)) close = [close]; @@ -171,9 +276,7 @@ const pinescript = { return { macd: macdLine, signal: signalLine, histogram: histogram }; }, cci: function(a, b, c, d) { - // Pine signatures: - // - ta.cci(source, length) - // - ta.cci(high, low, close, length) + // Supports both ta.cci(source, length) and ta.cci(high, low, close, length) signatures let source; let length; if (Array.isArray(a) && typeof b === 'number' && c === undefined) { @@ -304,6 +407,15 @@ const pinescript = { stdev: function(source, length) { return Math.sqrt(builtins.get('variance')(source, length)); }, + median: function(source, length) { + if (!source || source.length < length) return null; + const sorted = [...source.slice(-length)].sort((a, b) => a - b); + const mid = Math.floor(sorted.length / 2); + return sorted.length % 2 !== 0 ? sorted[mid] : (sorted[mid - 1] + sorted[mid]) / 2; + }, + ta_median: function(source, length) { + return builtins.get('median')(source, length); + }, hl2: function(high, low) { return high && low ? (high[high.length - 1] + low[low.length - 1]) / 2 : null; }, @@ -420,6 +532,24 @@ const pinescript = { return (series1[i - 1] < series2[i - 1] && series1[i] >= series2[i]) || (series1[i - 1] > series2[i - 1] && series1[i] <= series2[i]); }, + crossover: function(series1, series2) { + if (series1 === null || series1 === undefined) return false; + if (series2 === null || series2 === undefined) return false; + if (!Array.isArray(series1)) series1 = [series1]; + if (!Array.isArray(series2)) series2 = [series2]; + if (series1.length < 2 || series2.length < 2) return false; + const i = series1.length - 1; + return series1[i - 1] < series2[i - 1] && series1[i] >= series2[i]; + }, + crossunder: function(series1, series2) { + if (series1 === null || series1 === undefined) return false; + if (series2 === null || series2 === undefined) return false; + if (!Array.isArray(series1)) series1 = [series1]; + if (!Array.isArray(series2)) series2 = [series2]; + if (series1.length < 2 || series2.length < 2) return false; + const i = series1.length - 1; + return series1[i - 1] > series2[i - 1] && series1[i] <= series2[i]; + }, offset: function(series, offset) { if (!Array.isArray(series)) return null; const idx = series.length - 1 - offset; @@ -500,12 +630,14 @@ const pinescript = { return builtins.get('nz')(value, replacement); }, plot: function(series, title = '', color = null, linewidth = 1) { - globalThis.__pineRuntime.plots.push({ - series, - title, - color, - linewidth - }); + const rt = globalThis.__pineRuntime; + if (!rt) return series; + const bar = rt.__barIndex | 0; + const ord = (rt.__plotIdx = (rt.__plotIdx | 0) + 1) - 1; + const key = (title && String(title)) || ('plot_' + ord); + let p = rt.plots[key]; + if (!p) p = rt.plots[key] = { title: key, color, linewidth, data: [] }; + p.data[bar] = this.__scalar(series); return series; }, lineNew: function(x1, y1, x2, y2, opts = {}) { @@ -514,20 +646,72 @@ const pinescript = { lineDelete: function(l) { return null; }, - labelNew: function(...args) { - if (globalThis.__pineRuntime) { - globalThis.__pineRuntime.labels = globalThis.__pineRuntime.labels || []; - globalThis.__pineRuntime.labels.push({ args }); - } - return { args }; + lineSetXY: function(line, point, x, y) { + if (!line) return null; + if (point === 0 || point === 'x1') { line.x1 = x; line.y1 = y; } + else { line.x2 = x; line.y2 = y; } + return line; + }, + lineGetX: function(line, point) { + if (!line) return null; + return point === 0 || point === 'x1' ? line.x1 : line.x2; + }, + lineGetY: function(line, point) { + if (!line) return null; + return point === 0 || point === 'y1' ? line.y1 : line.y2; + }, + labelNew: function(x, y, text = '', opts = {}) { + return { x, y, text, opts, _type: 'label' }; }, labelDelete: function(l) { return null; }, + labelSetText: function(label, text) { + if (!label) return null; + label.text = text; + return label; + }, + labelGetText: function(label) { + if (!label) return ''; + return label.text || ''; + }, + boxNew: function(left, top, right, bottom, opts = {}) { + return { left, top, right, bottom, opts, _type: 'box' }; + }, + boxDelete: function(box) { + return null; + }, + boxSetLeftTop: function(box, left, top) { + if (!box) return null; + box.left = left; + box.top = top; + return box; + }, + boxSetRightBottom: function(box, right, bottom) { + if (!box) return null; + box.right = right; + box.bottom = bottom; + return box; + }, + polylineNew: function(points, opts = {}) { + return { points: points || [], opts, _type: 'polyline' }; + }, + polylineDelete: function(poly) { + return null; + }, plotshape: function(condition, ...rest) { - if (globalThis.__pineRuntime) { - globalThis.__pineRuntime.plotshapes.push({ condition, args: rest }); + const rt = globalThis.__pineRuntime; + if (!rt) return condition; + const bar = rt.__barIndex | 0; + const ord = (rt.__shapeIdx = (rt.__shapeIdx | 0) + 1) - 1; + let key = 'shape_' + ord; + for (const r of rest) { + if (typeof r === 'string') { key = r; break; } + if (r && typeof r === 'object' && r.title) { key = String(r.title); break; } } + let s = rt.plotshapes[key]; + if (!s) s = rt.plotshapes[key] = { title: key, data: [] }; + s.data[bar] = !!this.__scalar(condition); return condition; }, indicator: function(title, shorttitle, overlay, opts) { @@ -542,27 +726,12 @@ const pinescript = { } return null; }, - alertcondition: function(condition, ...rest) { - if (globalThis.__pineRuntime) { - globalThis.__pineRuntime.alerts.push({ condition, args: rest }); - } - return null; - }, plotbar: function(open, high, low, close, title, color, editable, showLast) { return { open, high, low, close }; }, plotcandle: function(open, high, low, close, title, color, wickColor, borderColor, editable, showLast) { return { open, high, low, close }; }, - hline: function(_price, _title, _opts) { - return null; - }, - bgcolor: function(color, title, editable, showLast) { - return null; - }, - fill: function(series1, series2, color, title, editable) { - return null; - }, alert: function(condition, message, frequency) { console.log('Alert:', condition ? message : 'Condition not met'); return condition; @@ -828,6 +997,9 @@ const pinescript = { arrayIndexOf: function(arr, value) { return arr ? arr.indexOf(value) : -1; }, + arrayLastIndexOf: function(arr, value) { + return arr ? arr.lastIndexOf(value) : -1; + }, arraySum: function(arr) { return arr ? arr.reduce((a, b) => a + b, 0) : 0; }, @@ -840,6 +1012,42 @@ const pinescript = { arrayMax: function(arr) { return arr && arr.length > 0 ? Math.max(...arr) : null; }, + arrayStdev: function(arr) { + if (!arr || arr.length === 0) return null; + const mean = arr.reduce((a, b) => a + b, 0) / arr.length; + const variance = arr.reduce((sum, val) => sum + Math.pow(val - mean, 2), 0) / arr.length; + return Math.sqrt(variance); + }, + arrayVariance: function(arr) { + if (!arr || arr.length === 0) return null; + const mean = arr.reduce((a, b) => a + b, 0) / arr.length; + return arr.reduce((sum, val) => sum + Math.pow(val - mean, 2), 0) / arr.length; + }, + arrayCovariance: function(arr1, arr2) { + if (!arr1 || !arr2 || arr1.length === 0 || arr1.length !== arr2.length) return null; + const mean1 = arr1.reduce((a, b) => a + b, 0) / arr1.length; + const mean2 = arr2.reduce((a, b) => a + b, 0) / arr2.length; + let cov = 0; + for (let i = 0; i < arr1.length; i++) { + cov += (arr1[i] - mean1) * (arr2[i] - mean2); + } + return cov / arr1.length; + }, + mode: function(arr) { + if (!arr || arr.length === 0) return null; + const counts = new Map(); + let maxCount = 0; + let modeVal = arr[0]; + for (const val of arr) { + const c = (counts.get(val) || 0) + 1; + counts.set(val, c); + if (c > maxCount) { + maxCount = c; + modeVal = val; + } + } + return modeVal; + }, strLength: function(str) { return str ? str.length : 0; }, @@ -895,6 +1103,84 @@ const pinescript = { strReverse: function(str) { return str ? str.split('').reverse().join('') : ''; }, + strFormat: function(formatStr, ...args) { + if (!formatStr) return ''; + return formatStr.replace(/\{(\d+)\}/g, (match, idx) => { + const i = parseInt(idx, 10); + return i < args.length ? String(args[i]) : match; + }); + }, + input: function(defval, title, tooltip, inline, group) { + return defval; + }, + inputInt: function(defval = 0, title, minval, maxval, step, tooltip, inline, group) { + return defval; + }, + inputFloat: function(defval = 0.0, title, minval, maxval, step, tooltip, inline, group) { + return defval; + }, + inputBool: function(defval = false, title, tooltip, inline, group) { + return defval; + }, + inputString: function(defval = '', title, options, tooltip, inline, group) { + return defval; + }, + inputSource: function(defval = null, title, tooltip, inline, group) { + return defval; + }, + inputColor: function(defval = '#000000', title, tooltip, inline, group) { + return defval; + }, + inputTime: function(defval = 0, title, tooltip, inline, group) { + return defval; + }, + mathSign: function(value) { + return Math.sign(value); + }, + mathAvg: function(...args) { + const valid = args.filter(v => v !== null && v !== undefined && !isNaN(v)); + return valid.length > 0 ? valid.reduce((a, b) => a + b, 0) / valid.length : null; + }, + mathSum: function(series, length) { + if (!series || series.length < length) return null; + return series.slice(-length).reduce((a, b) => a + b, 0); + }, + mathRandom: function(min = 0, max = 1) { + return min + Math.random() * (max - min); + }, + mathTodegrees: function(radians) { + return radians * (180 / Math.PI); + }, + mathToradians: function(degrees) { + return degrees * (Math.PI / 180); + }, + colorT: function(color, transparency) { + if (transparency !== undefined) { + return { color, transparency }; + } + if (color && typeof color === 'object' && 'transparency' in color) { + return color.transparency; + } + return 0; + }, + tableCellSetText: function(table, column, row, text) { + return null; + }, + tableCellSetBgcolor: function(table, column, row, bgcolor) { + return null; + }, + tableMergeCells: function(table, startColumn, startRow, endColumn, endRow) { + return null; + }, + tableDelete: function(table) { + return null; + }, + tableClear: function(table, startColumn, startRow, endColumn, endRow) { + return null; + }, + maxBarsBack: function(series, length) { + return null; + }, pi: 3.141592653589793, e: 2.718281828459045, true: true, @@ -905,8 +1191,7 @@ const pinescript = { const info = { ticker: 'AAPL', tickerid: 'NASDAQ:AAPL', prefix: 'NASDAQ', root: 'AAPL', suffix: '' }; return info[type] || ''; }, - time: 1771178050801, - timenow: 1771178050801, + timenow: 1781574526356, barstate: "LAST", dividends: {}, splits: {}, @@ -923,7 +1208,7 @@ const pinescript = { return null; }, tr: function(high, low, close) { - // Pine signature: ta.tr(true) + // Handles both the boolean shorthand ta.tr(true) and explicit high/low/close arguments if (typeof high === 'boolean') { const H = globalThis.high; const L = globalThis.low; @@ -934,7 +1219,7 @@ const pinescript = { return Math.max(H[i] - L[i], Math.abs(H[i] - prevClose), Math.abs(L[i] - prevClose)); } - // Fallback: treat inputs as scalars for current bar + // When arrays are passed, compute true range from the last bar using the previous close if (Array.isArray(close)) { const prevClose = close[close.length - 2] || close[0]; const currHigh = Array.isArray(high) ? high[high.length - 1] : high; @@ -945,6 +1230,92 @@ const pinescript = { return Math.max(high - low, Math.abs(high - close), Math.abs(low - close)); }, + arrayFirst: function(arr) { + if (!arr || arr.length === 0) return null; + return arr[0]; + }, + arrayLast: function(arr) { + if (!arr || arr.length === 0) return null; + return arr[arr.length - 1]; + }, + arrayJoin: function(arr, separator = ',') { + if (!arr) return ''; + return arr.join(separator); + }, + arrayConcat: function(arr1, arr2) { + if (!arr1) return arr2 || []; + if (!arr2) return arr1; + return arr1.concat(arr2); + }, + arrayCopy: function(arr) { + if (!arr) return []; + return [...arr]; + }, + arrayBinarySearch: function(arr, value) { + if (!arr || arr.length === 0) return -1; + let lo = 0, hi = arr.length - 1; + while (lo <= hi) { + const mid = (lo + hi) >>> 1; + if (arr[mid] === value) return mid; + if (arr[mid] < value) lo = mid + 1; + else hi = mid - 1; + } + return -1; + }, + arrayRange: function(arr) { + if (!arr || arr.length === 0) return 0; + return Math.max(...arr) - Math.min(...arr); + }, + arrayMedian: function(arr) { + if (!arr || arr.length === 0) return null; + const sorted = [...arr].sort((a, b) => a - b); + const mid = Math.floor(sorted.length / 2); + return sorted.length % 2 !== 0 ? sorted[mid] : (sorted[mid - 1] + sorted[mid]) / 2; + }, + arrayMode: function(arr) { + if (!arr || arr.length === 0) return null; + const freq = {}; + let maxCount = 0; + let mode = arr[0]; + for (const v of arr) { + freq[v] = (freq[v] || 0) + 1; + if (freq[v] > maxCount) { maxCount = freq[v]; mode = v; } + } + return mode; + }, + arrayPercentile: function(arr, percentile) { + if (!arr || arr.length === 0) return null; + const sorted = [...arr].sort((a, b) => a - b); + const idx = (percentile / 100) * (sorted.length - 1); + const lo = Math.floor(idx); + const hi = Math.ceil(idx); + if (lo === hi) return sorted[lo]; + return sorted[lo] + (sorted[hi] - sorted[lo]) * (idx - lo); + }, + arrayPercentileLinearInterpolation: function(arr, percentile) { + return builtins.get('arrayPercentile')(arr, percentile); + }, + arrayPercentileNearestRank: function(arr, percentile) { + if (!arr || arr.length === 0) return null; + const sorted = [...arr].sort((a, b) => a - b); + const idx = Math.ceil((percentile / 100) * sorted.length) - 1; + return sorted[Math.max(0, idx)]; + }, + arrayAbs: function(arr) { + if (!arr) return []; + return arr.map(v => Math.abs(v)); + }, + arrayEvery: function(arr, callback) { + if (!arr) return false; + return arr.every(callback); + }, + arraySome: function(arr, callback) { + if (!arr) return false; + return arr.some(callback); + }, + requestFinancial: function() { + return null; + }, }; @@ -1101,6 +1472,115 @@ globalThis.barmerge = globalThis.barmerge || { }; +function __pineNS(real) { + + const base = real || {}; + + return new Proxy(base, { + + get(target, key) { + + if (key in target) return target[key]; + + if (typeof key === "symbol") return target[key]; + + // Unknown member: usable as a value (null), a constant, or a no-op function. + + const noop = function() { return null; }; + + noop.toString = () => ""; + + return noop; + + } + + }); + +} + +globalThis.ta = globalThis.ta || __pineNS({}); + +globalThis.math = globalThis.math || __pineNS({ pi: Math.PI, e: Math.E, phi: 1.618033988749895, rphi: 0.618033988749895 }); + +globalThis.chart = globalThis.chart || __pineNS({ point: __pineNS({}), bg_color: null, fg_color: null }); + +globalThis.line = globalThis.line || __pineNS({ style_solid: "solid", style_dashed: "dashed", style_dotted: "dotted" }); + +globalThis.box = globalThis.box || __pineNS({}); + +globalThis.label = globalThis.label || __pineNS({ style_label_down: "label_down", style_label_up: "label_up", style_none: "none" }); + +globalThis.polyline = globalThis.polyline || __pineNS({}); + +globalThis.linefill = globalThis.linefill || __pineNS({}); + +globalThis.matrix = globalThis.matrix || __pineNS({}); + +globalThis.map = globalThis.map || __pineNS({}); + +globalThis.session = globalThis.session || __pineNS({ regular: "regular", extended: "extended" }); + +globalThis.dayofweek = globalThis.dayofweek || __pineNS({ sunday: 1, monday: 2, tuesday: 3, wednesday: 4, thursday: 5, friday: 6, saturday: 7 }); + +globalThis.timeframe = __pineNS(Object.assign(globalThis.timeframe || {}, { period: (globalThis.timeframe && globalThis.timeframe.period) || "D", isintraday: false, isdaily: true, multiplier: 1 })); + +globalThis.request = __pineNS(globalThis.request || {}); + +globalThis.input = __pineNS(globalThis.input || {}); + +globalThis.adjustment = globalThis.adjustment || __pineNS({}); + +globalThis.earnings = globalThis.earnings || __pineNS({}); + +globalThis.dividends = globalThis.dividends || __pineNS({}); + +globalThis.splits = globalThis.splits || __pineNS({}); + +globalThis.currency = globalThis.currency || __pineNS({}); + +globalThis.display = globalThis.display || __pineNS({ none: "none", all: "all", pane: "pane", data_window: "data_window", status_line: "status_line", price_scale: "price_scale" }); + +globalThis.format = globalThis.format || __pineNS({ price: "price", volume: "volume", percent: "percent", mintick: "mintick", inherit: "inherit" }); + +globalThis.scale = globalThis.scale || __pineNS({ left: "left", right: "right", none: "none" }); + +globalThis.font = globalThis.font || __pineNS({ family_default: "default", family_monospace: "monospace" }); + +globalThis.xloc = globalThis.xloc || __pineNS({ bar_index: "bar_index", bar_time: "bar_time" }); + +globalThis.yloc = globalThis.yloc || __pineNS({ price: "price", abovebar: "abovebar", belowbar: "belowbar" }); + +globalThis.extend = globalThis.extend || __pineNS({ none: "none", left: "left", right: "right", both: "both" }); + +globalThis.order = globalThis.order || __pineNS({ ascending: "ascending", descending: "descending" }); + +globalThis.alert = globalThis.alert || Object.assign(function() { return null; }, { freq_once_per_bar: "once_per_bar", freq_once_per_bar_close: "once_per_bar_close", freq_all: "all" }); + +globalThis.dayofmonth = globalThis.dayofmonth || function(t) { return new Date(t || 0).getUTCDate(); }; + +globalThis.plot = globalThis.plot || __pineNS({ style_line: "line", style_linebr: "linebr", style_stepline: "stepline", style_histogram: "histogram", style_cross: "cross", style_area: "area", style_areabr: "areabr", style_columns: "columns", style_circles: "circles" }); + +globalThis.int = globalThis.int || function(x) { return x == null ? null : Math.trunc(Number(x)); }; + +globalThis.float = globalThis.float || function(x) { return x == null ? null : Number(x); }; + +globalThis.bool = globalThis.bool || function(x) { return Boolean(x); }; + +globalThis.string = globalThis.string || function(x) { return x == null ? null : String(x); }; + +globalThis.dayofweek = globalThis.dayofweek || __pineNS({ sunday: 1, monday: 2, tuesday: 3, wednesday: 4, thursday: 5, friday: 6, saturday: 7 }); + +globalThis.str = globalThis.str || __pineNS({}); + +pinescript.math = globalThis.math; + +pinescript.ta = globalThis.ta; + +pinescript.str = globalThis.str; + +pinescript.array = __pineNS(globalThis.array); + + pinescript.color = { hex: function(s) { @@ -1190,10 +1670,10 @@ pinescript.table = { }; -// Input parameters +// Script input parameters and their defaults -// Main script logic +// The transpiled script logic, called once per bar function main() { @@ -1209,20 +1689,20 @@ function main() { // Study: 3D MACD Bar Plot [LuxAlgo] // Options: {"overlay":false,"max_lines_count":500,"max_labels_count":500,"max_polylines_count":100} let GRID_GRP = "Grid Configuration"; - let fastMinInput = input.int(5, "Fast Min Length", ({ minval: 2, group: GRID_GRP })); - let fastMaxInput = input.int(25, "Fast Max Length", ({ minval: 2, group: GRID_GRP })); - let fastStepInput = input.int(5, "Fast Step", ({ minval: 1, group: GRID_GRP })); - let slowMinInput = input.int(10, "Slow Min Length", ({ minval: 2, group: GRID_GRP })); - let slowMaxInput = input.int(50, "Slow Max Length", ({ minval: 2, group: GRID_GRP })); - let slowStepInput = input.int(10, "Slow Step", ({ minval: 1, group: GRID_GRP })); + let fastMinInput = pinescript.inputInt(5, "Fast Min Length", ({ minval: 2, group: GRID_GRP })); + let fastMaxInput = pinescript.inputInt(25, "Fast Max Length", ({ minval: 2, group: GRID_GRP })); + let fastStepInput = pinescript.inputInt(5, "Fast Step", ({ minval: 1, group: GRID_GRP })); + let slowMinInput = pinescript.inputInt(10, "Slow Min Length", ({ minval: 2, group: GRID_GRP })); + let slowMaxInput = pinescript.inputInt(50, "Slow Max Length", ({ minval: 2, group: GRID_GRP })); + let slowStepInput = pinescript.inputInt(10, "Slow Step", ({ minval: 1, group: GRID_GRP })); let PROJ_GRP = "3D Projection Settings"; - let yOffsetInput = input.float(0, "Y Baseline Price", ({ group: PROJ_GRP })); - let scaleXInput = input.float(3, "X Spacing (bars)", ({ group: PROJ_GRP })); - let scaleZInput = input.float(5, "Z Height (Relative to Y Spacing)", ({ group: PROJ_GRP })); + let yOffsetInput = pinescript.inputFloat(0, "Y Baseline Price", ({ group: PROJ_GRP })); + let scaleXInput = pinescript.inputFloat(3, "X Spacing (bars)", ({ group: PROJ_GRP })); + let scaleZInput = pinescript.inputFloat(5, "Z Height (Relative to Y Spacing)", ({ group: PROJ_GRP })); let STYLE_GRP = "Style"; - let bullColorInput = input.color(pinescript.color.hex("#089981"), "Bullish Color", ({ group: STYLE_GRP })); - let bearColorInput = input.color(pinescript.color.hex("#f23645"), "Bearish Color", ({ group: STYLE_GRP })); - let gridColorInput = input.color(pinescript.color.hex("#808080"), "Grid Color", ({ group: STYLE_GRP })); + let bullColorInput = pinescript.inputColor(pinescript.color.hex("#089981"), "Bullish Color", ({ group: STYLE_GRP })); + let bearColorInput = pinescript.inputColor(pinescript.color.hex("#f23645"), "Bearish Color", ({ group: STYLE_GRP })); + let gridColorInput = pinescript.inputColor(pinescript.color.hex("#808080"), "Grid Color", ({ group: STYLE_GRP })); if (state.emaStates === undefined) state.emaStates = pinescript.mapNew(); if (state.uniqueLengths === undefined) state.uniqueLengths = pinescript.arrayNew(); if ((bar_index === 0)) { @@ -1299,7 +1779,7 @@ function main() { pinescript.labelDelete(pinescript.arrayPop(state.labelsArray)); } while ((pinescript.arraySize(state.polylinesArray) > 0)) { - polyline.delete(pinescript.arrayPop(state.polylinesArray)); + pinescript.polylineDelete(pinescript.arrayPop(state.polylinesArray)); } let basePrice = yOffsetInput; let baseBar = bar_index; @@ -1361,13 +1841,73 @@ function main() { } let surfacePath = generateGridPath(topPoints, fCount, sCount); let gridPath = generateGridPath(basePoints, fCount, sCount); - polylinesArray.push(polyline.new(surfacePath, ({ line_color: pinescript.color.new(gridColorInput, 40) }))); - polylinesArray.push(polyline.new(gridPath, ({ line_color: pinescript.color.new(gridColorInput, 70) }))); + polylinesArray.push(pinescript.polylineNew(surfacePath, ({ line_color: pinescript.color.new(gridColorInput, 40) }))); + polylinesArray.push(pinescript.polylineNew(gridPath, ({ line_color: pinescript.color.new(gridColorInput, 70) }))); } } -// Export for use -export { main +// The bar-by-bar engine: runs main() once per bar over the dataset. +function run(data, options = {}) { + data = data || {}; + const inClose = (data.close || []).map(Number); + const n = inClose.length; + const inOpen = (data.open || inClose).map(Number); + const inHigh = (data.high || inClose).map(Number); + const inLow = (data.low || inClose).map(Number); + const inVol = (data.volume || new Array(n).fill(0)).map(Number); + const inTime = (data.time || inClose.map((_, i) => i)).map(Number); + + // Reset all persistent and per-run state so repeated runs are independent. + globalThis.__pineState = {}; + globalThis.__pineRuntime = { plots: {}, plotshapes: {}, alerts: [], bars: n, inputs: options.inputs || {} }; + pinescript.__rt = {}; + pinescript.__bar = 0; + + // Growing OHLCV windows: each is a SeriesArray that gains one element per bar, + // so window built-ins like sma and highest see history up to the current bar. + const O = pinescript.asSeries([]), H = pinescript.asSeries([]), L = pinescript.asSeries([]); + const C = pinescript.asSeries([]), V = pinescript.asSeries([]), T = pinescript.asSeries([]); + const HL2 = pinescript.asSeries([]), HLC3 = pinescript.asSeries([]), OHLC4 = pinescript.asSeries([]); + globalThis.open = O; globalThis.high = H; globalThis.low = L; globalThis.close = C; + globalThis.volume = V; globalThis.time = T; + globalThis.hl2 = HL2; globalThis.hlc3 = HLC3; globalThis.ohlc4 = OHLC4; + globalThis.syminfo = globalThis.syminfo || { tickerid: 'SYNTHETIC', ticker: 'SYNTHETIC', mintick: 0.01 }; + + const rt = globalThis.__pineRuntime; + for (let i = 0; i < n; i++) { + O.push(inOpen[i]); H.push(inHigh[i]); L.push(inLow[i]); C.push(inClose[i]); V.push(inVol[i]); T.push(inTime[i]); + HL2.push((inHigh[i] + inLow[i]) / 2); + HLC3.push((inHigh[i] + inLow[i] + inClose[i]) / 3); + OHLC4.push((inOpen[i] + inHigh[i] + inLow[i] + inClose[i]) / 4); + + pinescript.__bar = i; + rt.__barIndex = i; + rt.__plotIdx = 0; + rt.__shapeIdx = 0; + globalThis.bar_index = i; + globalThis.last_bar_index = n - 1; + globalThis.barstate = { + isfirst: i === 0, islast: i === n - 1, isrealtime: false, ishistory: true, + isconfirmed: true, isnew: true, islastconfirmedhistory: i === n - 1, + }; + + main(); + } + + // Backfill skipped bars so every output series has exactly n entries. + for (const k of Object.keys(rt.plots)) { + const d = rt.plots[k].data; + for (let i = 0; i < n; i++) if (d[i] === undefined) d[i] = null; + } + for (const k of Object.keys(rt.plotshapes)) { + const d = rt.plotshapes[k].data; + for (let i = 0; i < n; i++) if (d[i] === undefined) d[i] = false; + } + return rt; +} + + +export { main, run }; diff --git a/converted/Arbitrage_Matrix_LuxAlgo.js b/converted/Arbitrage_Matrix_LuxAlgo.js index 136809a..9049458 100644 --- a/converted/Arbitrage_Matrix_LuxAlgo.js +++ b/converted/Arbitrage_Matrix_LuxAlgo.js @@ -1,12 +1,63 @@ -// PineScript to JavaScript Transpiled Code +// Auto-generated JavaScript from PineScript source -// Generated automatically +// Do not edit by hand -- re-run the converter instead -// Built-in PineScript functions +// All the built-in PineScript functions that scripts depend on at runtime const pinescript = { + __scalar: function(v) { + if (v === null || v === undefined) return v; + if (typeof v === 'object' && typeof v.valueOf === 'function') { + const s = v.valueOf(); + if (typeof s === 'number' || typeof s === 'boolean' || s === null) return s; + } + return v; + }, + series: function(id, value) { + const rt = this.__rt || (this.__rt = {}); + const store = rt.series || (rt.series = {}); + let buf = store[id]; + if (!buf) buf = store[id] = []; + buf[this.__bar | 0] = this.__scalar(value); + return buf; + }, + hist: function(id, value, n) { + const buf = this.series(id, value); + const idx = (this.__bar | 0) - (n | 0); + return idx >= 0 && idx < buf.length ? buf[idx] : null; + }, + time: function(timeframe, session) { + const t = globalThis.time; + if (t && typeof t.valueOf === 'function') return t.valueOf(); + return Array.isArray(t) ? t[t.length - 1] : t; + }, + sign: function(x) { + if (x === null || x === undefined || (typeof x === 'number' && isNaN(x))) return null; + return Math.sign(Number(x)); + }, + unpack: function(value, count) { + if (Array.isArray(value)) return value; + if (value != null && typeof value[Symbol.iterator] === 'function') return Array.from(value); + return new Array(count || 0).fill(null); + }, + alertcondition: function(condition, ...rest) { + if (globalThis.__pineRuntime) { + globalThis.__pineRuntime.alerts.push({ condition, args: rest }); + } + return null; + }, + barcolor: function(color) { return null; }, + bgcolor: function(color, title, editable, showLast) { + return null; + }, + fill: function(series1, series2, color, title, editable) { + return null; + }, + hline: function(_price, _title, _opts) { + return null; + }, sma: function(series, length) { if (series === null || series === undefined) return null; if (!Array.isArray(series)) series = [series]; @@ -84,6 +135,13 @@ const pinescript = { } return weightSum > 0 ? sum / weightSum : null; }, + swma: function(series) { + if (series === null || series === undefined) return null; + if (!Array.isArray(series)) series = [series]; + if (series.length < 4) return null; + const s = series.slice(-4); + return (s[0] * 1 + s[1] * 2 + s[2] * 2 + s[3] * 1) / 6; + }, atr: function(high, low, close, length = 14) { if (!Array.isArray(high) || !Array.isArray(low) || !Array.isArray(close)) return null; if (high.length < length + 1 || low.length < length + 1 || close.length < length + 1) return null; @@ -114,6 +172,53 @@ const pinescript = { const atr = builtins.get('atr')(high, low, close, length); return { middle: sma, upper: sma + mult * atr, lower: sma - mult * atr }; }, + supertrend: function(factor, atrPeriod, high, low, close) { + if (!Array.isArray(high) || !Array.isArray(low) || !Array.isArray(close)) return [null, null]; + if (high.length < atrPeriod + 1) return [null, null]; + const atrVal = builtins.get('atr')(high, low, close, atrPeriod); + if (atrVal === null) return [null, null]; + const lastIdx = close.length - 1; + const hl2 = (high[lastIdx] + low[lastIdx]) / 2; + const upperBand = hl2 + factor * atrVal; + const lowerBand = hl2 - factor * atrVal; + // Determine direction: 1 means downtrend (price below band), -1 means uptrend + const direction = close[lastIdx] > upperBand ? -1 : close[lastIdx] < lowerBand ? 1 : -1; + const supertrendValue = direction === -1 ? lowerBand : upperBand; + return [supertrendValue, direction]; + }, + ta_supertrend: function(factor, atrPeriod) { + const H = globalThis.high; + const L = globalThis.low; + const C = globalThis.close; + return builtins.get('supertrend')(factor, atrPeriod, H, L, C); + }, + dmi: function(diLength, adxSmoothing, high, low, close) { + if (!Array.isArray(high) || !Array.isArray(low) || !Array.isArray(close)) return { plusDI: null, minusDI: null, adx: null }; + if (high.length < diLength + 1) return { plusDI: null, minusDI: null, adx: null }; + let plusDMSum = 0, minusDMSum = 0, trSum = 0; + for (let i = 1; i <= diLength; i++) { + const idx = high.length - 1 - diLength + i; + const upMove = high[idx] - high[idx - 1]; + const downMove = low[idx - 1] - low[idx]; + plusDMSum += (upMove > downMove && upMove > 0) ? upMove : 0; + minusDMSum += (downMove > upMove && downMove > 0) ? downMove : 0; + const tr = Math.max( + high[idx] - low[idx], + Math.abs(high[idx] - close[idx - 1]), + Math.abs(low[idx] - close[idx - 1]) + ); + trSum += tr; + } + const plusDI = trSum > 0 ? (plusDMSum / trSum) * 100 : 0; + const minusDI = trSum > 0 ? (minusDMSum / trSum) * 100 : 0; + const diSum = plusDI + minusDI; + const dx = diSum > 0 ? Math.abs(plusDI - minusDI) / diSum * 100 : 0; + return { plusDI, minusDI, adx: dx }; + }, + adx: function(diLength, adxSmoothing, high, low, close) { + const result = builtins.get('dmi')(diLength, adxSmoothing, high, low, close); + return result ? result.adx : null; + }, rsi: function(close, length = 14) { if (close === null || close === undefined) return null; if (!Array.isArray(close)) close = [close]; @@ -171,9 +276,7 @@ const pinescript = { return { macd: macdLine, signal: signalLine, histogram: histogram }; }, cci: function(a, b, c, d) { - // Pine signatures: - // - ta.cci(source, length) - // - ta.cci(high, low, close, length) + // Supports both ta.cci(source, length) and ta.cci(high, low, close, length) signatures let source; let length; if (Array.isArray(a) && typeof b === 'number' && c === undefined) { @@ -304,6 +407,15 @@ const pinescript = { stdev: function(source, length) { return Math.sqrt(builtins.get('variance')(source, length)); }, + median: function(source, length) { + if (!source || source.length < length) return null; + const sorted = [...source.slice(-length)].sort((a, b) => a - b); + const mid = Math.floor(sorted.length / 2); + return sorted.length % 2 !== 0 ? sorted[mid] : (sorted[mid - 1] + sorted[mid]) / 2; + }, + ta_median: function(source, length) { + return builtins.get('median')(source, length); + }, hl2: function(high, low) { return high && low ? (high[high.length - 1] + low[low.length - 1]) / 2 : null; }, @@ -420,6 +532,24 @@ const pinescript = { return (series1[i - 1] < series2[i - 1] && series1[i] >= series2[i]) || (series1[i - 1] > series2[i - 1] && series1[i] <= series2[i]); }, + crossover: function(series1, series2) { + if (series1 === null || series1 === undefined) return false; + if (series2 === null || series2 === undefined) return false; + if (!Array.isArray(series1)) series1 = [series1]; + if (!Array.isArray(series2)) series2 = [series2]; + if (series1.length < 2 || series2.length < 2) return false; + const i = series1.length - 1; + return series1[i - 1] < series2[i - 1] && series1[i] >= series2[i]; + }, + crossunder: function(series1, series2) { + if (series1 === null || series1 === undefined) return false; + if (series2 === null || series2 === undefined) return false; + if (!Array.isArray(series1)) series1 = [series1]; + if (!Array.isArray(series2)) series2 = [series2]; + if (series1.length < 2 || series2.length < 2) return false; + const i = series1.length - 1; + return series1[i - 1] > series2[i - 1] && series1[i] <= series2[i]; + }, offset: function(series, offset) { if (!Array.isArray(series)) return null; const idx = series.length - 1 - offset; @@ -500,12 +630,14 @@ const pinescript = { return builtins.get('nz')(value, replacement); }, plot: function(series, title = '', color = null, linewidth = 1) { - globalThis.__pineRuntime.plots.push({ - series, - title, - color, - linewidth - }); + const rt = globalThis.__pineRuntime; + if (!rt) return series; + const bar = rt.__barIndex | 0; + const ord = (rt.__plotIdx = (rt.__plotIdx | 0) + 1) - 1; + const key = (title && String(title)) || ('plot_' + ord); + let p = rt.plots[key]; + if (!p) p = rt.plots[key] = { title: key, color, linewidth, data: [] }; + p.data[bar] = this.__scalar(series); return series; }, lineNew: function(x1, y1, x2, y2, opts = {}) { @@ -514,20 +646,72 @@ const pinescript = { lineDelete: function(l) { return null; }, - labelNew: function(...args) { - if (globalThis.__pineRuntime) { - globalThis.__pineRuntime.labels = globalThis.__pineRuntime.labels || []; - globalThis.__pineRuntime.labels.push({ args }); - } - return { args }; + lineSetXY: function(line, point, x, y) { + if (!line) return null; + if (point === 0 || point === 'x1') { line.x1 = x; line.y1 = y; } + else { line.x2 = x; line.y2 = y; } + return line; + }, + lineGetX: function(line, point) { + if (!line) return null; + return point === 0 || point === 'x1' ? line.x1 : line.x2; + }, + lineGetY: function(line, point) { + if (!line) return null; + return point === 0 || point === 'y1' ? line.y1 : line.y2; + }, + labelNew: function(x, y, text = '', opts = {}) { + return { x, y, text, opts, _type: 'label' }; }, labelDelete: function(l) { return null; }, + labelSetText: function(label, text) { + if (!label) return null; + label.text = text; + return label; + }, + labelGetText: function(label) { + if (!label) return ''; + return label.text || ''; + }, + boxNew: function(left, top, right, bottom, opts = {}) { + return { left, top, right, bottom, opts, _type: 'box' }; + }, + boxDelete: function(box) { + return null; + }, + boxSetLeftTop: function(box, left, top) { + if (!box) return null; + box.left = left; + box.top = top; + return box; + }, + boxSetRightBottom: function(box, right, bottom) { + if (!box) return null; + box.right = right; + box.bottom = bottom; + return box; + }, + polylineNew: function(points, opts = {}) { + return { points: points || [], opts, _type: 'polyline' }; + }, + polylineDelete: function(poly) { + return null; + }, plotshape: function(condition, ...rest) { - if (globalThis.__pineRuntime) { - globalThis.__pineRuntime.plotshapes.push({ condition, args: rest }); - } + const rt = globalThis.__pineRuntime; + if (!rt) return condition; + const bar = rt.__barIndex | 0; + const ord = (rt.__shapeIdx = (rt.__shapeIdx | 0) + 1) - 1; + let key = 'shape_' + ord; + for (const r of rest) { + if (typeof r === 'string') { key = r; break; } + if (r && typeof r === 'object' && r.title) { key = String(r.title); break; } + } + let s = rt.plotshapes[key]; + if (!s) s = rt.plotshapes[key] = { title: key, data: [] }; + s.data[bar] = !!this.__scalar(condition); return condition; }, indicator: function(title, shorttitle, overlay, opts) { @@ -542,27 +726,12 @@ const pinescript = { } return null; }, - alertcondition: function(condition, ...rest) { - if (globalThis.__pineRuntime) { - globalThis.__pineRuntime.alerts.push({ condition, args: rest }); - } - return null; - }, plotbar: function(open, high, low, close, title, color, editable, showLast) { return { open, high, low, close }; }, plotcandle: function(open, high, low, close, title, color, wickColor, borderColor, editable, showLast) { return { open, high, low, close }; }, - hline: function(_price, _title, _opts) { - return null; - }, - bgcolor: function(color, title, editable, showLast) { - return null; - }, - fill: function(series1, series2, color, title, editable) { - return null; - }, alert: function(condition, message, frequency) { console.log('Alert:', condition ? message : 'Condition not met'); return condition; @@ -828,6 +997,9 @@ const pinescript = { arrayIndexOf: function(arr, value) { return arr ? arr.indexOf(value) : -1; }, + arrayLastIndexOf: function(arr, value) { + return arr ? arr.lastIndexOf(value) : -1; + }, arraySum: function(arr) { return arr ? arr.reduce((a, b) => a + b, 0) : 0; }, @@ -840,6 +1012,42 @@ const pinescript = { arrayMax: function(arr) { return arr && arr.length > 0 ? Math.max(...arr) : null; }, + arrayStdev: function(arr) { + if (!arr || arr.length === 0) return null; + const mean = arr.reduce((a, b) => a + b, 0) / arr.length; + const variance = arr.reduce((sum, val) => sum + Math.pow(val - mean, 2), 0) / arr.length; + return Math.sqrt(variance); + }, + arrayVariance: function(arr) { + if (!arr || arr.length === 0) return null; + const mean = arr.reduce((a, b) => a + b, 0) / arr.length; + return arr.reduce((sum, val) => sum + Math.pow(val - mean, 2), 0) / arr.length; + }, + arrayCovariance: function(arr1, arr2) { + if (!arr1 || !arr2 || arr1.length === 0 || arr1.length !== arr2.length) return null; + const mean1 = arr1.reduce((a, b) => a + b, 0) / arr1.length; + const mean2 = arr2.reduce((a, b) => a + b, 0) / arr2.length; + let cov = 0; + for (let i = 0; i < arr1.length; i++) { + cov += (arr1[i] - mean1) * (arr2[i] - mean2); + } + return cov / arr1.length; + }, + mode: function(arr) { + if (!arr || arr.length === 0) return null; + const counts = new Map(); + let maxCount = 0; + let modeVal = arr[0]; + for (const val of arr) { + const c = (counts.get(val) || 0) + 1; + counts.set(val, c); + if (c > maxCount) { + maxCount = c; + modeVal = val; + } + } + return modeVal; + }, strLength: function(str) { return str ? str.length : 0; }, @@ -895,6 +1103,84 @@ const pinescript = { strReverse: function(str) { return str ? str.split('').reverse().join('') : ''; }, + strFormat: function(formatStr, ...args) { + if (!formatStr) return ''; + return formatStr.replace(/\{(\d+)\}/g, (match, idx) => { + const i = parseInt(idx, 10); + return i < args.length ? String(args[i]) : match; + }); + }, + input: function(defval, title, tooltip, inline, group) { + return defval; + }, + inputInt: function(defval = 0, title, minval, maxval, step, tooltip, inline, group) { + return defval; + }, + inputFloat: function(defval = 0.0, title, minval, maxval, step, tooltip, inline, group) { + return defval; + }, + inputBool: function(defval = false, title, tooltip, inline, group) { + return defval; + }, + inputString: function(defval = '', title, options, tooltip, inline, group) { + return defval; + }, + inputSource: function(defval = null, title, tooltip, inline, group) { + return defval; + }, + inputColor: function(defval = '#000000', title, tooltip, inline, group) { + return defval; + }, + inputTime: function(defval = 0, title, tooltip, inline, group) { + return defval; + }, + mathSign: function(value) { + return Math.sign(value); + }, + mathAvg: function(...args) { + const valid = args.filter(v => v !== null && v !== undefined && !isNaN(v)); + return valid.length > 0 ? valid.reduce((a, b) => a + b, 0) / valid.length : null; + }, + mathSum: function(series, length) { + if (!series || series.length < length) return null; + return series.slice(-length).reduce((a, b) => a + b, 0); + }, + mathRandom: function(min = 0, max = 1) { + return min + Math.random() * (max - min); + }, + mathTodegrees: function(radians) { + return radians * (180 / Math.PI); + }, + mathToradians: function(degrees) { + return degrees * (Math.PI / 180); + }, + colorT: function(color, transparency) { + if (transparency !== undefined) { + return { color, transparency }; + } + if (color && typeof color === 'object' && 'transparency' in color) { + return color.transparency; + } + return 0; + }, + tableCellSetText: function(table, column, row, text) { + return null; + }, + tableCellSetBgcolor: function(table, column, row, bgcolor) { + return null; + }, + tableMergeCells: function(table, startColumn, startRow, endColumn, endRow) { + return null; + }, + tableDelete: function(table) { + return null; + }, + tableClear: function(table, startColumn, startRow, endColumn, endRow) { + return null; + }, + maxBarsBack: function(series, length) { + return null; + }, pi: 3.141592653589793, e: 2.718281828459045, true: true, @@ -905,8 +1191,7 @@ const pinescript = { const info = { ticker: 'AAPL', tickerid: 'NASDAQ:AAPL', prefix: 'NASDAQ', root: 'AAPL', suffix: '' }; return info[type] || ''; }, - time: 1771178050882, - timenow: 1771178050882, + timenow: 1781574526454, barstate: "LAST", dividends: {}, splits: {}, @@ -923,7 +1208,7 @@ const pinescript = { return null; }, tr: function(high, low, close) { - // Pine signature: ta.tr(true) + // Handles both the boolean shorthand ta.tr(true) and explicit high/low/close arguments if (typeof high === 'boolean') { const H = globalThis.high; const L = globalThis.low; @@ -934,7 +1219,7 @@ const pinescript = { return Math.max(H[i] - L[i], Math.abs(H[i] - prevClose), Math.abs(L[i] - prevClose)); } - // Fallback: treat inputs as scalars for current bar + // When arrays are passed, compute true range from the last bar using the previous close if (Array.isArray(close)) { const prevClose = close[close.length - 2] || close[0]; const currHigh = Array.isArray(high) ? high[high.length - 1] : high; @@ -945,6 +1230,92 @@ const pinescript = { return Math.max(high - low, Math.abs(high - close), Math.abs(low - close)); }, + arrayFirst: function(arr) { + if (!arr || arr.length === 0) return null; + return arr[0]; + }, + arrayLast: function(arr) { + if (!arr || arr.length === 0) return null; + return arr[arr.length - 1]; + }, + arrayJoin: function(arr, separator = ',') { + if (!arr) return ''; + return arr.join(separator); + }, + arrayConcat: function(arr1, arr2) { + if (!arr1) return arr2 || []; + if (!arr2) return arr1; + return arr1.concat(arr2); + }, + arrayCopy: function(arr) { + if (!arr) return []; + return [...arr]; + }, + arrayBinarySearch: function(arr, value) { + if (!arr || arr.length === 0) return -1; + let lo = 0, hi = arr.length - 1; + while (lo <= hi) { + const mid = (lo + hi) >>> 1; + if (arr[mid] === value) return mid; + if (arr[mid] < value) lo = mid + 1; + else hi = mid - 1; + } + return -1; + }, + arrayRange: function(arr) { + if (!arr || arr.length === 0) return 0; + return Math.max(...arr) - Math.min(...arr); + }, + arrayMedian: function(arr) { + if (!arr || arr.length === 0) return null; + const sorted = [...arr].sort((a, b) => a - b); + const mid = Math.floor(sorted.length / 2); + return sorted.length % 2 !== 0 ? sorted[mid] : (sorted[mid - 1] + sorted[mid]) / 2; + }, + arrayMode: function(arr) { + if (!arr || arr.length === 0) return null; + const freq = {}; + let maxCount = 0; + let mode = arr[0]; + for (const v of arr) { + freq[v] = (freq[v] || 0) + 1; + if (freq[v] > maxCount) { maxCount = freq[v]; mode = v; } + } + return mode; + }, + arrayPercentile: function(arr, percentile) { + if (!arr || arr.length === 0) return null; + const sorted = [...arr].sort((a, b) => a - b); + const idx = (percentile / 100) * (sorted.length - 1); + const lo = Math.floor(idx); + const hi = Math.ceil(idx); + if (lo === hi) return sorted[lo]; + return sorted[lo] + (sorted[hi] - sorted[lo]) * (idx - lo); + }, + arrayPercentileLinearInterpolation: function(arr, percentile) { + return builtins.get('arrayPercentile')(arr, percentile); + }, + arrayPercentileNearestRank: function(arr, percentile) { + if (!arr || arr.length === 0) return null; + const sorted = [...arr].sort((a, b) => a - b); + const idx = Math.ceil((percentile / 100) * sorted.length) - 1; + return sorted[Math.max(0, idx)]; + }, + arrayAbs: function(arr) { + if (!arr) return []; + return arr.map(v => Math.abs(v)); + }, + arrayEvery: function(arr, callback) { + if (!arr) return false; + return arr.every(callback); + }, + arraySome: function(arr, callback) { + if (!arr) return false; + return arr.some(callback); + }, + requestFinancial: function() { + return null; + }, }; @@ -1101,6 +1472,115 @@ globalThis.barmerge = globalThis.barmerge || { }; +function __pineNS(real) { + + const base = real || {}; + + return new Proxy(base, { + + get(target, key) { + + if (key in target) return target[key]; + + if (typeof key === "symbol") return target[key]; + + // Unknown member: usable as a value (null), a constant, or a no-op function. + + const noop = function() { return null; }; + + noop.toString = () => ""; + + return noop; + + } + + }); + +} + +globalThis.ta = globalThis.ta || __pineNS({}); + +globalThis.math = globalThis.math || __pineNS({ pi: Math.PI, e: Math.E, phi: 1.618033988749895, rphi: 0.618033988749895 }); + +globalThis.chart = globalThis.chart || __pineNS({ point: __pineNS({}), bg_color: null, fg_color: null }); + +globalThis.line = globalThis.line || __pineNS({ style_solid: "solid", style_dashed: "dashed", style_dotted: "dotted" }); + +globalThis.box = globalThis.box || __pineNS({}); + +globalThis.label = globalThis.label || __pineNS({ style_label_down: "label_down", style_label_up: "label_up", style_none: "none" }); + +globalThis.polyline = globalThis.polyline || __pineNS({}); + +globalThis.linefill = globalThis.linefill || __pineNS({}); + +globalThis.matrix = globalThis.matrix || __pineNS({}); + +globalThis.map = globalThis.map || __pineNS({}); + +globalThis.session = globalThis.session || __pineNS({ regular: "regular", extended: "extended" }); + +globalThis.dayofweek = globalThis.dayofweek || __pineNS({ sunday: 1, monday: 2, tuesday: 3, wednesday: 4, thursday: 5, friday: 6, saturday: 7 }); + +globalThis.timeframe = __pineNS(Object.assign(globalThis.timeframe || {}, { period: (globalThis.timeframe && globalThis.timeframe.period) || "D", isintraday: false, isdaily: true, multiplier: 1 })); + +globalThis.request = __pineNS(globalThis.request || {}); + +globalThis.input = __pineNS(globalThis.input || {}); + +globalThis.adjustment = globalThis.adjustment || __pineNS({}); + +globalThis.earnings = globalThis.earnings || __pineNS({}); + +globalThis.dividends = globalThis.dividends || __pineNS({}); + +globalThis.splits = globalThis.splits || __pineNS({}); + +globalThis.currency = globalThis.currency || __pineNS({}); + +globalThis.display = globalThis.display || __pineNS({ none: "none", all: "all", pane: "pane", data_window: "data_window", status_line: "status_line", price_scale: "price_scale" }); + +globalThis.format = globalThis.format || __pineNS({ price: "price", volume: "volume", percent: "percent", mintick: "mintick", inherit: "inherit" }); + +globalThis.scale = globalThis.scale || __pineNS({ left: "left", right: "right", none: "none" }); + +globalThis.font = globalThis.font || __pineNS({ family_default: "default", family_monospace: "monospace" }); + +globalThis.xloc = globalThis.xloc || __pineNS({ bar_index: "bar_index", bar_time: "bar_time" }); + +globalThis.yloc = globalThis.yloc || __pineNS({ price: "price", abovebar: "abovebar", belowbar: "belowbar" }); + +globalThis.extend = globalThis.extend || __pineNS({ none: "none", left: "left", right: "right", both: "both" }); + +globalThis.order = globalThis.order || __pineNS({ ascending: "ascending", descending: "descending" }); + +globalThis.alert = globalThis.alert || Object.assign(function() { return null; }, { freq_once_per_bar: "once_per_bar", freq_once_per_bar_close: "once_per_bar_close", freq_all: "all" }); + +globalThis.dayofmonth = globalThis.dayofmonth || function(t) { return new Date(t || 0).getUTCDate(); }; + +globalThis.plot = globalThis.plot || __pineNS({ style_line: "line", style_linebr: "linebr", style_stepline: "stepline", style_histogram: "histogram", style_cross: "cross", style_area: "area", style_areabr: "areabr", style_columns: "columns", style_circles: "circles" }); + +globalThis.int = globalThis.int || function(x) { return x == null ? null : Math.trunc(Number(x)); }; + +globalThis.float = globalThis.float || function(x) { return x == null ? null : Number(x); }; + +globalThis.bool = globalThis.bool || function(x) { return Boolean(x); }; + +globalThis.string = globalThis.string || function(x) { return x == null ? null : String(x); }; + +globalThis.dayofweek = globalThis.dayofweek || __pineNS({ sunday: 1, monday: 2, tuesday: 3, wednesday: 4, thursday: 5, friday: 6, saturday: 7 }); + +globalThis.str = globalThis.str || __pineNS({}); + +pinescript.math = globalThis.math; + +pinescript.ta = globalThis.ta; + +pinescript.str = globalThis.str; + +pinescript.array = __pineNS(globalThis.array); + + pinescript.color = { hex: function(s) { @@ -1190,12 +1670,12 @@ pinescript.table = { }; -// Input parameters +// Script input parameters and their defaults const asset = { new: function(prices, volumes) { return { prices: prices, volumes: volumes }; } }; const data = { new: function(lastPrices, lastVolumes, avgPrices, avgVolumes) { return { lastPrices: lastPrices, lastVolumes: lastVolumes, avgPrices: avgPrices, avgVolumes: avgVolumes }; } }; -// Main script logic +// The transpiled script logic, called once per bar function main() { @@ -1249,46 +1729,46 @@ function main() { let FS = FOUR_PER_EM_SPACE; let SS = SIX_PER_EM_SPACE; let HS = HAIR_SPACE; - let sourceInput = input.string(AUTO, "Sources", ({ tooltip: sourceTooltip, options: [AUTO, CRYPTO_EXCHANGES, FX_BROKERS] })); - let averageLengthInput = input.int(20, "Average Length", ({ tooltip: averageLengthTooltip })); - let enableCRYPTOCOMInput = input.bool(true, "CRYPTOCOM", ({ group: EXCHANGES_GROUP, inline: "row1" })); - let enableBINANCEInput = input.bool(true, "BINANCE", ({ group: EXCHANGES_GROUP, inline: "row1" })); - let enableBYBITInput = input.bool(true, "BYBIT", ({ group: EXCHANGES_GROUP, inline: "row1" })); - let enableWEBULLPAYInput = input.bool(true, (("WEBULLPAY" + FS) + HS), ({ group: EXCHANGES_GROUP, inline: "row2" })); - let enableGEMINIInput = input.bool(true, (("GEMINI" + NS) + FS), ({ group: EXCHANGES_GROUP, inline: "row2" })); - let enableCRYPTOInput = input.bool(true, "CRYPTO", ({ group: EXCHANGES_GROUP, inline: "row2" })); - let enableBINANCEUSInput = input.bool(true, ((("BINANCEUS" + FS) + SS) + HS), ({ group: EXCHANGES_GROUP, inline: "row3" })); - let enableKRAKENInput = input.bool(true, (("KRAKEN" + FS) + HS), ({ group: EXCHANGES_GROUP, inline: "row3" })); - let enableBTSEInput = input.bool(true, "BTSE", ({ group: EXCHANGES_GROUP, inline: "row3" })); - let enableBITSTAMPInput = input.bool(true, (("BITSTAMP" + MS) + SS), ({ group: EXCHANGES_GROUP, inline: "row4" })); - let enableKUCOINInput = input.bool(true, ("KUCOIN" + NS), ({ group: EXCHANGES_GROUP, inline: "row4" })); - let enableHTXInput = input.bool(true, "HTX", ({ group: EXCHANGES_GROUP, inline: "row4" })); - let enableCOINBASEInput = input.bool(true, (("COINBASE" + MS) + HS), ({ group: EXCHANGES_GROUP, inline: "row5" })); - let enableBITGETInput = input.bool(true, ((("BITGET" + NS) + HS) + HS), ({ group: EXCHANGES_GROUP, inline: "row5" })); - let enableGATEInput = input.bool(true, "GATE", ({ group: EXCHANGES_GROUP, inline: "row5" })); - let enableWHITEBITInput = input.bool(true, ((("WHITEBIT" + MS) + HS) + HS), ({ group: EXCHANGES_GROUP, inline: "row6" })); - let enableCOINEXInput = input.bool(true, (("COINEX" + NS) + HS), ({ group: EXCHANGES_GROUP, inline: "row6" })); - let enableMEXCInput = input.bool(true, "MEXC", ({ group: EXCHANGES_GROUP, inline: "row6" })); - let enableOKXInput = input.bool(true, "OKX", ({ group: EXCHANGES_GROUP, inline: "row7" })); - let enablePEPPERSTONEInput = input.bool(true, "PEPPERSTONE", ({ group: BROKERS_GROUP, inline: "row8" })); - let enableEIGHTCAPInput = input.bool(true, (("EIGHTCAP" + FS) + SS), ({ group: BROKERS_GROUP, inline: "row8" })); - let enableOANDAInput = input.bool(true, "OANDA", ({ group: BROKERS_GROUP, inline: "row8" })); - let enableCMCMARKETSInput = input.bool(true, ("CMCMARKETS" + FS), ({ group: BROKERS_GROUP, inline: "row9" })); - let enableTICKMILLInput = input.bool(true, (((("TICKMILL" + NS) + FS) + HS) + HS), ({ group: BROKERS_GROUP, inline: "row9" })); - let enableSAXOInput = input.bool(true, "SAXO", ({ group: BROKERS_GROUP, inline: "row9" })); - let enableCAPITALCOMInput = input.bool(true, ((("CAPITALCOM" + NS) + HS) + HS), ({ group: BROKERS_GROUP, inline: "row10" })); - let enableFOREXCOMInput = input.bool(true, "FOREXCOM", ({ group: BROKERS_GROUP, inline: "row10" })); - let enableIBKRInput = input.bool(true, "IBKR", ({ group: BROKERS_GROUP, inline: "row10" })); - let enableICMARKETSInput = input.bool(true, (("ICMARKETS" + MS) + FS), ({ group: BROKERS_GROUP, inline: "row11" })); - let enableVANTAGEInput = input.bool(true, (((("VANTAGE" + NS) + SS) + SS) + HS), ({ group: BROKERS_GROUP, inline: "row11" })); - let enableFXInput = input.bool(true, "FX", ({ group: BROKERS_GROUP, inline: "row11" })); - let enableIGInput = input.bool(true, "IG", ({ group: BROKERS_GROUP, inline: "row12" })); - let dashboardDataInput = input.string(PRICE_LAST, "Data", ({ group: DASHBOARD_GROUP, tooltip: dashboardDataTooltip, options: [PRICE_LAST, PRICE_AVG, VOLUME_LAST, VOLUME_AVG] })); - let dashboardPositionInput = input.string(TOP_RIGHT, "Position", ({ group: DASHBOARD_GROUP, tooltip: dashboardPositionTooltip, options: [TOP_RIGHT, BOTTOM_RIGHT, BOTTOM_LEFT] })); - let dashboardSizeInput = input.string(SMALL, "Size", ({ group: DASHBOARD_GROUP, tooltip: dashboardSizeTooltip, options: [TINY, SMALL, NORMAL, LARGE, HUGE] })); - let bullishColorInput = input.color(GREEN, "Bullish", ({ group: STYLE_GROUP, tooltip: bullishColorTooltip })); - let bearishColorInput = input.color(RED, "Bearish", ({ group: STYLE_GROUP, tooltip: bearishColorTooltip })); - let backgroundInput = input.bool(true, "Background Gradient", ({ group: STYLE_GROUP, tooltip: backgroundTooltip })); + let sourceInput = pinescript.inputString(AUTO, "Sources", ({ tooltip: sourceTooltip, options: [AUTO, CRYPTO_EXCHANGES, FX_BROKERS] })); + let averageLengthInput = pinescript.inputInt(20, "Average Length", ({ tooltip: averageLengthTooltip })); + let enableCRYPTOCOMInput = pinescript.inputBool(true, "CRYPTOCOM", ({ group: EXCHANGES_GROUP, inline: "row1" })); + let enableBINANCEInput = pinescript.inputBool(true, "BINANCE", ({ group: EXCHANGES_GROUP, inline: "row1" })); + let enableBYBITInput = pinescript.inputBool(true, "BYBIT", ({ group: EXCHANGES_GROUP, inline: "row1" })); + let enableWEBULLPAYInput = pinescript.inputBool(true, (("WEBULLPAY" + FS) + HS), ({ group: EXCHANGES_GROUP, inline: "row2" })); + let enableGEMINIInput = pinescript.inputBool(true, (("GEMINI" + NS) + FS), ({ group: EXCHANGES_GROUP, inline: "row2" })); + let enableCRYPTOInput = pinescript.inputBool(true, "CRYPTO", ({ group: EXCHANGES_GROUP, inline: "row2" })); + let enableBINANCEUSInput = pinescript.inputBool(true, ((("BINANCEUS" + FS) + SS) + HS), ({ group: EXCHANGES_GROUP, inline: "row3" })); + let enableKRAKENInput = pinescript.inputBool(true, (("KRAKEN" + FS) + HS), ({ group: EXCHANGES_GROUP, inline: "row3" })); + let enableBTSEInput = pinescript.inputBool(true, "BTSE", ({ group: EXCHANGES_GROUP, inline: "row3" })); + let enableBITSTAMPInput = pinescript.inputBool(true, (("BITSTAMP" + MS) + SS), ({ group: EXCHANGES_GROUP, inline: "row4" })); + let enableKUCOINInput = pinescript.inputBool(true, ("KUCOIN" + NS), ({ group: EXCHANGES_GROUP, inline: "row4" })); + let enableHTXInput = pinescript.inputBool(true, "HTX", ({ group: EXCHANGES_GROUP, inline: "row4" })); + let enableCOINBASEInput = pinescript.inputBool(true, (("COINBASE" + MS) + HS), ({ group: EXCHANGES_GROUP, inline: "row5" })); + let enableBITGETInput = pinescript.inputBool(true, ((("BITGET" + NS) + HS) + HS), ({ group: EXCHANGES_GROUP, inline: "row5" })); + let enableGATEInput = pinescript.inputBool(true, "GATE", ({ group: EXCHANGES_GROUP, inline: "row5" })); + let enableWHITEBITInput = pinescript.inputBool(true, ((("WHITEBIT" + MS) + HS) + HS), ({ group: EXCHANGES_GROUP, inline: "row6" })); + let enableCOINEXInput = pinescript.inputBool(true, (("COINEX" + NS) + HS), ({ group: EXCHANGES_GROUP, inline: "row6" })); + let enableMEXCInput = pinescript.inputBool(true, "MEXC", ({ group: EXCHANGES_GROUP, inline: "row6" })); + let enableOKXInput = pinescript.inputBool(true, "OKX", ({ group: EXCHANGES_GROUP, inline: "row7" })); + let enablePEPPERSTONEInput = pinescript.inputBool(true, "PEPPERSTONE", ({ group: BROKERS_GROUP, inline: "row8" })); + let enableEIGHTCAPInput = pinescript.inputBool(true, (("EIGHTCAP" + FS) + SS), ({ group: BROKERS_GROUP, inline: "row8" })); + let enableOANDAInput = pinescript.inputBool(true, "OANDA", ({ group: BROKERS_GROUP, inline: "row8" })); + let enableCMCMARKETSInput = pinescript.inputBool(true, ("CMCMARKETS" + FS), ({ group: BROKERS_GROUP, inline: "row9" })); + let enableTICKMILLInput = pinescript.inputBool(true, (((("TICKMILL" + NS) + FS) + HS) + HS), ({ group: BROKERS_GROUP, inline: "row9" })); + let enableSAXOInput = pinescript.inputBool(true, "SAXO", ({ group: BROKERS_GROUP, inline: "row9" })); + let enableCAPITALCOMInput = pinescript.inputBool(true, ((("CAPITALCOM" + NS) + HS) + HS), ({ group: BROKERS_GROUP, inline: "row10" })); + let enableFOREXCOMInput = pinescript.inputBool(true, "FOREXCOM", ({ group: BROKERS_GROUP, inline: "row10" })); + let enableIBKRInput = pinescript.inputBool(true, "IBKR", ({ group: BROKERS_GROUP, inline: "row10" })); + let enableICMARKETSInput = pinescript.inputBool(true, (("ICMARKETS" + MS) + FS), ({ group: BROKERS_GROUP, inline: "row11" })); + let enableVANTAGEInput = pinescript.inputBool(true, (((("VANTAGE" + NS) + SS) + SS) + HS), ({ group: BROKERS_GROUP, inline: "row11" })); + let enableFXInput = pinescript.inputBool(true, "FX", ({ group: BROKERS_GROUP, inline: "row11" })); + let enableIGInput = pinescript.inputBool(true, "IG", ({ group: BROKERS_GROUP, inline: "row12" })); + let dashboardDataInput = pinescript.inputString(PRICE_LAST, "Data", ({ group: DASHBOARD_GROUP, tooltip: dashboardDataTooltip, options: [PRICE_LAST, PRICE_AVG, VOLUME_LAST, VOLUME_AVG] })); + let dashboardPositionInput = pinescript.inputString(TOP_RIGHT, "Position", ({ group: DASHBOARD_GROUP, tooltip: dashboardPositionTooltip, options: [TOP_RIGHT, BOTTOM_RIGHT, BOTTOM_LEFT] })); + let dashboardSizeInput = pinescript.inputString(SMALL, "Size", ({ group: DASHBOARD_GROUP, tooltip: dashboardSizeTooltip, options: [TINY, SMALL, NORMAL, LARGE, HUGE] })); + let bullishColorInput = pinescript.inputColor(GREEN, "Bullish", ({ group: STYLE_GROUP, tooltip: bullishColorTooltip })); + let bearishColorInput = pinescript.inputColor(RED, "Bearish", ({ group: STYLE_GROUP, tooltip: bearishColorTooltip })); + let backgroundInput = pinescript.inputBool(true, "Background Gradient", ({ group: STYLE_GROUP, tooltip: backgroundTooltip })); if (state.crypto === undefined) state.crypto = pinescript.arrayNew(); if (state.forex === undefined) state.forex = pinescript.arrayNew(); if (barstate.isfirst) { @@ -1412,7 +1892,7 @@ function main() { let ticker = syminfo.ticker; for (const [index, eachExchange] of state.exchanges) { let tickerid = ((eachExchange + ":") + ticker); - let [assetPrice, assetVolume] = pinescript.requestSecurity(tickerid, "", [close, volume], ({ ignore_invalid_symbol: true, calc_bars_count: averageLengthInput })); + let [assetPrice, assetVolume] = pinescript.unpack(pinescript.requestSecurity(tickerid, "", [close, volume], ({ ignore_invalid_symbol: true, calc_bars_count: averageLengthInput })), 2); if (!pinescript.na(assetPrice)) { assets.get(index).prices.push(pinescript.nz(assetPrice)); if ((assets.get(index).prices.size() > averageLengthInput)) { @@ -1534,7 +2014,67 @@ function main() { } -// Export for use -export { main +// The bar-by-bar engine: runs main() once per bar over the dataset. +function run(data, options = {}) { + data = data || {}; + const inClose = (data.close || []).map(Number); + const n = inClose.length; + const inOpen = (data.open || inClose).map(Number); + const inHigh = (data.high || inClose).map(Number); + const inLow = (data.low || inClose).map(Number); + const inVol = (data.volume || new Array(n).fill(0)).map(Number); + const inTime = (data.time || inClose.map((_, i) => i)).map(Number); + + // Reset all persistent and per-run state so repeated runs are independent. + globalThis.__pineState = {}; + globalThis.__pineRuntime = { plots: {}, plotshapes: {}, alerts: [], bars: n, inputs: options.inputs || {} }; + pinescript.__rt = {}; + pinescript.__bar = 0; + + // Growing OHLCV windows: each is a SeriesArray that gains one element per bar, + // so window built-ins like sma and highest see history up to the current bar. + const O = pinescript.asSeries([]), H = pinescript.asSeries([]), L = pinescript.asSeries([]); + const C = pinescript.asSeries([]), V = pinescript.asSeries([]), T = pinescript.asSeries([]); + const HL2 = pinescript.asSeries([]), HLC3 = pinescript.asSeries([]), OHLC4 = pinescript.asSeries([]); + globalThis.open = O; globalThis.high = H; globalThis.low = L; globalThis.close = C; + globalThis.volume = V; globalThis.time = T; + globalThis.hl2 = HL2; globalThis.hlc3 = HLC3; globalThis.ohlc4 = OHLC4; + globalThis.syminfo = globalThis.syminfo || { tickerid: 'SYNTHETIC', ticker: 'SYNTHETIC', mintick: 0.01 }; + + const rt = globalThis.__pineRuntime; + for (let i = 0; i < n; i++) { + O.push(inOpen[i]); H.push(inHigh[i]); L.push(inLow[i]); C.push(inClose[i]); V.push(inVol[i]); T.push(inTime[i]); + HL2.push((inHigh[i] + inLow[i]) / 2); + HLC3.push((inHigh[i] + inLow[i] + inClose[i]) / 3); + OHLC4.push((inOpen[i] + inHigh[i] + inLow[i] + inClose[i]) / 4); + + pinescript.__bar = i; + rt.__barIndex = i; + rt.__plotIdx = 0; + rt.__shapeIdx = 0; + globalThis.bar_index = i; + globalThis.last_bar_index = n - 1; + globalThis.barstate = { + isfirst: i === 0, islast: i === n - 1, isrealtime: false, ishistory: true, + isconfirmed: true, isnew: true, islastconfirmedhistory: i === n - 1, + }; + + main(); + } + + // Backfill skipped bars so every output series has exactly n entries. + for (const k of Object.keys(rt.plots)) { + const d = rt.plots[k].data; + for (let i = 0; i < n; i++) if (d[i] === undefined) d[i] = null; + } + for (const k of Object.keys(rt.plotshapes)) { + const d = rt.plotshapes[k].data; + for (let i = 0; i < n; i++) if (d[i] === undefined) d[i] = false; + } + return rt; +} + + +export { main, run }; diff --git a/converted/Asset_Drift_Model.js b/converted/Asset_Drift_Model.js index a6ef943..60b5c58 100644 --- a/converted/Asset_Drift_Model.js +++ b/converted/Asset_Drift_Model.js @@ -1,12 +1,63 @@ -// PineScript to JavaScript Transpiled Code +// Auto-generated JavaScript from PineScript source -// Generated automatically +// Do not edit by hand -- re-run the converter instead -// Built-in PineScript functions +// All the built-in PineScript functions that scripts depend on at runtime const pinescript = { + __scalar: function(v) { + if (v === null || v === undefined) return v; + if (typeof v === 'object' && typeof v.valueOf === 'function') { + const s = v.valueOf(); + if (typeof s === 'number' || typeof s === 'boolean' || s === null) return s; + } + return v; + }, + series: function(id, value) { + const rt = this.__rt || (this.__rt = {}); + const store = rt.series || (rt.series = {}); + let buf = store[id]; + if (!buf) buf = store[id] = []; + buf[this.__bar | 0] = this.__scalar(value); + return buf; + }, + hist: function(id, value, n) { + const buf = this.series(id, value); + const idx = (this.__bar | 0) - (n | 0); + return idx >= 0 && idx < buf.length ? buf[idx] : null; + }, + time: function(timeframe, session) { + const t = globalThis.time; + if (t && typeof t.valueOf === 'function') return t.valueOf(); + return Array.isArray(t) ? t[t.length - 1] : t; + }, + sign: function(x) { + if (x === null || x === undefined || (typeof x === 'number' && isNaN(x))) return null; + return Math.sign(Number(x)); + }, + unpack: function(value, count) { + if (Array.isArray(value)) return value; + if (value != null && typeof value[Symbol.iterator] === 'function') return Array.from(value); + return new Array(count || 0).fill(null); + }, + alertcondition: function(condition, ...rest) { + if (globalThis.__pineRuntime) { + globalThis.__pineRuntime.alerts.push({ condition, args: rest }); + } + return null; + }, + barcolor: function(color) { return null; }, + bgcolor: function(color, title, editable, showLast) { + return null; + }, + fill: function(series1, series2, color, title, editable) { + return null; + }, + hline: function(_price, _title, _opts) { + return null; + }, sma: function(series, length) { if (series === null || series === undefined) return null; if (!Array.isArray(series)) series = [series]; @@ -84,6 +135,13 @@ const pinescript = { } return weightSum > 0 ? sum / weightSum : null; }, + swma: function(series) { + if (series === null || series === undefined) return null; + if (!Array.isArray(series)) series = [series]; + if (series.length < 4) return null; + const s = series.slice(-4); + return (s[0] * 1 + s[1] * 2 + s[2] * 2 + s[3] * 1) / 6; + }, atr: function(high, low, close, length = 14) { if (!Array.isArray(high) || !Array.isArray(low) || !Array.isArray(close)) return null; if (high.length < length + 1 || low.length < length + 1 || close.length < length + 1) return null; @@ -114,6 +172,53 @@ const pinescript = { const atr = builtins.get('atr')(high, low, close, length); return { middle: sma, upper: sma + mult * atr, lower: sma - mult * atr }; }, + supertrend: function(factor, atrPeriod, high, low, close) { + if (!Array.isArray(high) || !Array.isArray(low) || !Array.isArray(close)) return [null, null]; + if (high.length < atrPeriod + 1) return [null, null]; + const atrVal = builtins.get('atr')(high, low, close, atrPeriod); + if (atrVal === null) return [null, null]; + const lastIdx = close.length - 1; + const hl2 = (high[lastIdx] + low[lastIdx]) / 2; + const upperBand = hl2 + factor * atrVal; + const lowerBand = hl2 - factor * atrVal; + // Determine direction: 1 means downtrend (price below band), -1 means uptrend + const direction = close[lastIdx] > upperBand ? -1 : close[lastIdx] < lowerBand ? 1 : -1; + const supertrendValue = direction === -1 ? lowerBand : upperBand; + return [supertrendValue, direction]; + }, + ta_supertrend: function(factor, atrPeriod) { + const H = globalThis.high; + const L = globalThis.low; + const C = globalThis.close; + return builtins.get('supertrend')(factor, atrPeriod, H, L, C); + }, + dmi: function(diLength, adxSmoothing, high, low, close) { + if (!Array.isArray(high) || !Array.isArray(low) || !Array.isArray(close)) return { plusDI: null, minusDI: null, adx: null }; + if (high.length < diLength + 1) return { plusDI: null, minusDI: null, adx: null }; + let plusDMSum = 0, minusDMSum = 0, trSum = 0; + for (let i = 1; i <= diLength; i++) { + const idx = high.length - 1 - diLength + i; + const upMove = high[idx] - high[idx - 1]; + const downMove = low[idx - 1] - low[idx]; + plusDMSum += (upMove > downMove && upMove > 0) ? upMove : 0; + minusDMSum += (downMove > upMove && downMove > 0) ? downMove : 0; + const tr = Math.max( + high[idx] - low[idx], + Math.abs(high[idx] - close[idx - 1]), + Math.abs(low[idx] - close[idx - 1]) + ); + trSum += tr; + } + const plusDI = trSum > 0 ? (plusDMSum / trSum) * 100 : 0; + const minusDI = trSum > 0 ? (minusDMSum / trSum) * 100 : 0; + const diSum = plusDI + minusDI; + const dx = diSum > 0 ? Math.abs(plusDI - minusDI) / diSum * 100 : 0; + return { plusDI, minusDI, adx: dx }; + }, + adx: function(diLength, adxSmoothing, high, low, close) { + const result = builtins.get('dmi')(diLength, adxSmoothing, high, low, close); + return result ? result.adx : null; + }, rsi: function(close, length = 14) { if (close === null || close === undefined) return null; if (!Array.isArray(close)) close = [close]; @@ -171,9 +276,7 @@ const pinescript = { return { macd: macdLine, signal: signalLine, histogram: histogram }; }, cci: function(a, b, c, d) { - // Pine signatures: - // - ta.cci(source, length) - // - ta.cci(high, low, close, length) + // Supports both ta.cci(source, length) and ta.cci(high, low, close, length) signatures let source; let length; if (Array.isArray(a) && typeof b === 'number' && c === undefined) { @@ -304,6 +407,15 @@ const pinescript = { stdev: function(source, length) { return Math.sqrt(builtins.get('variance')(source, length)); }, + median: function(source, length) { + if (!source || source.length < length) return null; + const sorted = [...source.slice(-length)].sort((a, b) => a - b); + const mid = Math.floor(sorted.length / 2); + return sorted.length % 2 !== 0 ? sorted[mid] : (sorted[mid - 1] + sorted[mid]) / 2; + }, + ta_median: function(source, length) { + return builtins.get('median')(source, length); + }, hl2: function(high, low) { return high && low ? (high[high.length - 1] + low[low.length - 1]) / 2 : null; }, @@ -420,6 +532,24 @@ const pinescript = { return (series1[i - 1] < series2[i - 1] && series1[i] >= series2[i]) || (series1[i - 1] > series2[i - 1] && series1[i] <= series2[i]); }, + crossover: function(series1, series2) { + if (series1 === null || series1 === undefined) return false; + if (series2 === null || series2 === undefined) return false; + if (!Array.isArray(series1)) series1 = [series1]; + if (!Array.isArray(series2)) series2 = [series2]; + if (series1.length < 2 || series2.length < 2) return false; + const i = series1.length - 1; + return series1[i - 1] < series2[i - 1] && series1[i] >= series2[i]; + }, + crossunder: function(series1, series2) { + if (series1 === null || series1 === undefined) return false; + if (series2 === null || series2 === undefined) return false; + if (!Array.isArray(series1)) series1 = [series1]; + if (!Array.isArray(series2)) series2 = [series2]; + if (series1.length < 2 || series2.length < 2) return false; + const i = series1.length - 1; + return series1[i - 1] > series2[i - 1] && series1[i] <= series2[i]; + }, offset: function(series, offset) { if (!Array.isArray(series)) return null; const idx = series.length - 1 - offset; @@ -500,12 +630,14 @@ const pinescript = { return builtins.get('nz')(value, replacement); }, plot: function(series, title = '', color = null, linewidth = 1) { - globalThis.__pineRuntime.plots.push({ - series, - title, - color, - linewidth - }); + const rt = globalThis.__pineRuntime; + if (!rt) return series; + const bar = rt.__barIndex | 0; + const ord = (rt.__plotIdx = (rt.__plotIdx | 0) + 1) - 1; + const key = (title && String(title)) || ('plot_' + ord); + let p = rt.plots[key]; + if (!p) p = rt.plots[key] = { title: key, color, linewidth, data: [] }; + p.data[bar] = this.__scalar(series); return series; }, lineNew: function(x1, y1, x2, y2, opts = {}) { @@ -514,20 +646,72 @@ const pinescript = { lineDelete: function(l) { return null; }, - labelNew: function(...args) { - if (globalThis.__pineRuntime) { - globalThis.__pineRuntime.labels = globalThis.__pineRuntime.labels || []; - globalThis.__pineRuntime.labels.push({ args }); - } - return { args }; + lineSetXY: function(line, point, x, y) { + if (!line) return null; + if (point === 0 || point === 'x1') { line.x1 = x; line.y1 = y; } + else { line.x2 = x; line.y2 = y; } + return line; + }, + lineGetX: function(line, point) { + if (!line) return null; + return point === 0 || point === 'x1' ? line.x1 : line.x2; + }, + lineGetY: function(line, point) { + if (!line) return null; + return point === 0 || point === 'y1' ? line.y1 : line.y2; + }, + labelNew: function(x, y, text = '', opts = {}) { + return { x, y, text, opts, _type: 'label' }; }, labelDelete: function(l) { return null; }, + labelSetText: function(label, text) { + if (!label) return null; + label.text = text; + return label; + }, + labelGetText: function(label) { + if (!label) return ''; + return label.text || ''; + }, + boxNew: function(left, top, right, bottom, opts = {}) { + return { left, top, right, bottom, opts, _type: 'box' }; + }, + boxDelete: function(box) { + return null; + }, + boxSetLeftTop: function(box, left, top) { + if (!box) return null; + box.left = left; + box.top = top; + return box; + }, + boxSetRightBottom: function(box, right, bottom) { + if (!box) return null; + box.right = right; + box.bottom = bottom; + return box; + }, + polylineNew: function(points, opts = {}) { + return { points: points || [], opts, _type: 'polyline' }; + }, + polylineDelete: function(poly) { + return null; + }, plotshape: function(condition, ...rest) { - if (globalThis.__pineRuntime) { - globalThis.__pineRuntime.plotshapes.push({ condition, args: rest }); + const rt = globalThis.__pineRuntime; + if (!rt) return condition; + const bar = rt.__barIndex | 0; + const ord = (rt.__shapeIdx = (rt.__shapeIdx | 0) + 1) - 1; + let key = 'shape_' + ord; + for (const r of rest) { + if (typeof r === 'string') { key = r; break; } + if (r && typeof r === 'object' && r.title) { key = String(r.title); break; } } + let s = rt.plotshapes[key]; + if (!s) s = rt.plotshapes[key] = { title: key, data: [] }; + s.data[bar] = !!this.__scalar(condition); return condition; }, indicator: function(title, shorttitle, overlay, opts) { @@ -542,27 +726,12 @@ const pinescript = { } return null; }, - alertcondition: function(condition, ...rest) { - if (globalThis.__pineRuntime) { - globalThis.__pineRuntime.alerts.push({ condition, args: rest }); - } - return null; - }, plotbar: function(open, high, low, close, title, color, editable, showLast) { return { open, high, low, close }; }, plotcandle: function(open, high, low, close, title, color, wickColor, borderColor, editable, showLast) { return { open, high, low, close }; }, - hline: function(_price, _title, _opts) { - return null; - }, - bgcolor: function(color, title, editable, showLast) { - return null; - }, - fill: function(series1, series2, color, title, editable) { - return null; - }, alert: function(condition, message, frequency) { console.log('Alert:', condition ? message : 'Condition not met'); return condition; @@ -828,6 +997,9 @@ const pinescript = { arrayIndexOf: function(arr, value) { return arr ? arr.indexOf(value) : -1; }, + arrayLastIndexOf: function(arr, value) { + return arr ? arr.lastIndexOf(value) : -1; + }, arraySum: function(arr) { return arr ? arr.reduce((a, b) => a + b, 0) : 0; }, @@ -840,6 +1012,42 @@ const pinescript = { arrayMax: function(arr) { return arr && arr.length > 0 ? Math.max(...arr) : null; }, + arrayStdev: function(arr) { + if (!arr || arr.length === 0) return null; + const mean = arr.reduce((a, b) => a + b, 0) / arr.length; + const variance = arr.reduce((sum, val) => sum + Math.pow(val - mean, 2), 0) / arr.length; + return Math.sqrt(variance); + }, + arrayVariance: function(arr) { + if (!arr || arr.length === 0) return null; + const mean = arr.reduce((a, b) => a + b, 0) / arr.length; + return arr.reduce((sum, val) => sum + Math.pow(val - mean, 2), 0) / arr.length; + }, + arrayCovariance: function(arr1, arr2) { + if (!arr1 || !arr2 || arr1.length === 0 || arr1.length !== arr2.length) return null; + const mean1 = arr1.reduce((a, b) => a + b, 0) / arr1.length; + const mean2 = arr2.reduce((a, b) => a + b, 0) / arr2.length; + let cov = 0; + for (let i = 0; i < arr1.length; i++) { + cov += (arr1[i] - mean1) * (arr2[i] - mean2); + } + return cov / arr1.length; + }, + mode: function(arr) { + if (!arr || arr.length === 0) return null; + const counts = new Map(); + let maxCount = 0; + let modeVal = arr[0]; + for (const val of arr) { + const c = (counts.get(val) || 0) + 1; + counts.set(val, c); + if (c > maxCount) { + maxCount = c; + modeVal = val; + } + } + return modeVal; + }, strLength: function(str) { return str ? str.length : 0; }, @@ -895,6 +1103,84 @@ const pinescript = { strReverse: function(str) { return str ? str.split('').reverse().join('') : ''; }, + strFormat: function(formatStr, ...args) { + if (!formatStr) return ''; + return formatStr.replace(/\{(\d+)\}/g, (match, idx) => { + const i = parseInt(idx, 10); + return i < args.length ? String(args[i]) : match; + }); + }, + input: function(defval, title, tooltip, inline, group) { + return defval; + }, + inputInt: function(defval = 0, title, minval, maxval, step, tooltip, inline, group) { + return defval; + }, + inputFloat: function(defval = 0.0, title, minval, maxval, step, tooltip, inline, group) { + return defval; + }, + inputBool: function(defval = false, title, tooltip, inline, group) { + return defval; + }, + inputString: function(defval = '', title, options, tooltip, inline, group) { + return defval; + }, + inputSource: function(defval = null, title, tooltip, inline, group) { + return defval; + }, + inputColor: function(defval = '#000000', title, tooltip, inline, group) { + return defval; + }, + inputTime: function(defval = 0, title, tooltip, inline, group) { + return defval; + }, + mathSign: function(value) { + return Math.sign(value); + }, + mathAvg: function(...args) { + const valid = args.filter(v => v !== null && v !== undefined && !isNaN(v)); + return valid.length > 0 ? valid.reduce((a, b) => a + b, 0) / valid.length : null; + }, + mathSum: function(series, length) { + if (!series || series.length < length) return null; + return series.slice(-length).reduce((a, b) => a + b, 0); + }, + mathRandom: function(min = 0, max = 1) { + return min + Math.random() * (max - min); + }, + mathTodegrees: function(radians) { + return radians * (180 / Math.PI); + }, + mathToradians: function(degrees) { + return degrees * (Math.PI / 180); + }, + colorT: function(color, transparency) { + if (transparency !== undefined) { + return { color, transparency }; + } + if (color && typeof color === 'object' && 'transparency' in color) { + return color.transparency; + } + return 0; + }, + tableCellSetText: function(table, column, row, text) { + return null; + }, + tableCellSetBgcolor: function(table, column, row, bgcolor) { + return null; + }, + tableMergeCells: function(table, startColumn, startRow, endColumn, endRow) { + return null; + }, + tableDelete: function(table) { + return null; + }, + tableClear: function(table, startColumn, startRow, endColumn, endRow) { + return null; + }, + maxBarsBack: function(series, length) { + return null; + }, pi: 3.141592653589793, e: 2.718281828459045, true: true, @@ -905,8 +1191,7 @@ const pinescript = { const info = { ticker: 'AAPL', tickerid: 'NASDAQ:AAPL', prefix: 'NASDAQ', root: 'AAPL', suffix: '' }; return info[type] || ''; }, - time: 1771178050970, - timenow: 1771178050970, + timenow: 1781574526555, barstate: "LAST", dividends: {}, splits: {}, @@ -923,7 +1208,7 @@ const pinescript = { return null; }, tr: function(high, low, close) { - // Pine signature: ta.tr(true) + // Handles both the boolean shorthand ta.tr(true) and explicit high/low/close arguments if (typeof high === 'boolean') { const H = globalThis.high; const L = globalThis.low; @@ -934,7 +1219,7 @@ const pinescript = { return Math.max(H[i] - L[i], Math.abs(H[i] - prevClose), Math.abs(L[i] - prevClose)); } - // Fallback: treat inputs as scalars for current bar + // When arrays are passed, compute true range from the last bar using the previous close if (Array.isArray(close)) { const prevClose = close[close.length - 2] || close[0]; const currHigh = Array.isArray(high) ? high[high.length - 1] : high; @@ -945,6 +1230,92 @@ const pinescript = { return Math.max(high - low, Math.abs(high - close), Math.abs(low - close)); }, + arrayFirst: function(arr) { + if (!arr || arr.length === 0) return null; + return arr[0]; + }, + arrayLast: function(arr) { + if (!arr || arr.length === 0) return null; + return arr[arr.length - 1]; + }, + arrayJoin: function(arr, separator = ',') { + if (!arr) return ''; + return arr.join(separator); + }, + arrayConcat: function(arr1, arr2) { + if (!arr1) return arr2 || []; + if (!arr2) return arr1; + return arr1.concat(arr2); + }, + arrayCopy: function(arr) { + if (!arr) return []; + return [...arr]; + }, + arrayBinarySearch: function(arr, value) { + if (!arr || arr.length === 0) return -1; + let lo = 0, hi = arr.length - 1; + while (lo <= hi) { + const mid = (lo + hi) >>> 1; + if (arr[mid] === value) return mid; + if (arr[mid] < value) lo = mid + 1; + else hi = mid - 1; + } + return -1; + }, + arrayRange: function(arr) { + if (!arr || arr.length === 0) return 0; + return Math.max(...arr) - Math.min(...arr); + }, + arrayMedian: function(arr) { + if (!arr || arr.length === 0) return null; + const sorted = [...arr].sort((a, b) => a - b); + const mid = Math.floor(sorted.length / 2); + return sorted.length % 2 !== 0 ? sorted[mid] : (sorted[mid - 1] + sorted[mid]) / 2; + }, + arrayMode: function(arr) { + if (!arr || arr.length === 0) return null; + const freq = {}; + let maxCount = 0; + let mode = arr[0]; + for (const v of arr) { + freq[v] = (freq[v] || 0) + 1; + if (freq[v] > maxCount) { maxCount = freq[v]; mode = v; } + } + return mode; + }, + arrayPercentile: function(arr, percentile) { + if (!arr || arr.length === 0) return null; + const sorted = [...arr].sort((a, b) => a - b); + const idx = (percentile / 100) * (sorted.length - 1); + const lo = Math.floor(idx); + const hi = Math.ceil(idx); + if (lo === hi) return sorted[lo]; + return sorted[lo] + (sorted[hi] - sorted[lo]) * (idx - lo); + }, + arrayPercentileLinearInterpolation: function(arr, percentile) { + return builtins.get('arrayPercentile')(arr, percentile); + }, + arrayPercentileNearestRank: function(arr, percentile) { + if (!arr || arr.length === 0) return null; + const sorted = [...arr].sort((a, b) => a - b); + const idx = Math.ceil((percentile / 100) * sorted.length) - 1; + return sorted[Math.max(0, idx)]; + }, + arrayAbs: function(arr) { + if (!arr) return []; + return arr.map(v => Math.abs(v)); + }, + arrayEvery: function(arr, callback) { + if (!arr) return false; + return arr.every(callback); + }, + arraySome: function(arr, callback) { + if (!arr) return false; + return arr.some(callback); + }, + requestFinancial: function() { + return null; + }, }; @@ -1101,6 +1472,115 @@ globalThis.barmerge = globalThis.barmerge || { }; +function __pineNS(real) { + + const base = real || {}; + + return new Proxy(base, { + + get(target, key) { + + if (key in target) return target[key]; + + if (typeof key === "symbol") return target[key]; + + // Unknown member: usable as a value (null), a constant, or a no-op function. + + const noop = function() { return null; }; + + noop.toString = () => ""; + + return noop; + + } + + }); + +} + +globalThis.ta = globalThis.ta || __pineNS({}); + +globalThis.math = globalThis.math || __pineNS({ pi: Math.PI, e: Math.E, phi: 1.618033988749895, rphi: 0.618033988749895 }); + +globalThis.chart = globalThis.chart || __pineNS({ point: __pineNS({}), bg_color: null, fg_color: null }); + +globalThis.line = globalThis.line || __pineNS({ style_solid: "solid", style_dashed: "dashed", style_dotted: "dotted" }); + +globalThis.box = globalThis.box || __pineNS({}); + +globalThis.label = globalThis.label || __pineNS({ style_label_down: "label_down", style_label_up: "label_up", style_none: "none" }); + +globalThis.polyline = globalThis.polyline || __pineNS({}); + +globalThis.linefill = globalThis.linefill || __pineNS({}); + +globalThis.matrix = globalThis.matrix || __pineNS({}); + +globalThis.map = globalThis.map || __pineNS({}); + +globalThis.session = globalThis.session || __pineNS({ regular: "regular", extended: "extended" }); + +globalThis.dayofweek = globalThis.dayofweek || __pineNS({ sunday: 1, monday: 2, tuesday: 3, wednesday: 4, thursday: 5, friday: 6, saturday: 7 }); + +globalThis.timeframe = __pineNS(Object.assign(globalThis.timeframe || {}, { period: (globalThis.timeframe && globalThis.timeframe.period) || "D", isintraday: false, isdaily: true, multiplier: 1 })); + +globalThis.request = __pineNS(globalThis.request || {}); + +globalThis.input = __pineNS(globalThis.input || {}); + +globalThis.adjustment = globalThis.adjustment || __pineNS({}); + +globalThis.earnings = globalThis.earnings || __pineNS({}); + +globalThis.dividends = globalThis.dividends || __pineNS({}); + +globalThis.splits = globalThis.splits || __pineNS({}); + +globalThis.currency = globalThis.currency || __pineNS({}); + +globalThis.display = globalThis.display || __pineNS({ none: "none", all: "all", pane: "pane", data_window: "data_window", status_line: "status_line", price_scale: "price_scale" }); + +globalThis.format = globalThis.format || __pineNS({ price: "price", volume: "volume", percent: "percent", mintick: "mintick", inherit: "inherit" }); + +globalThis.scale = globalThis.scale || __pineNS({ left: "left", right: "right", none: "none" }); + +globalThis.font = globalThis.font || __pineNS({ family_default: "default", family_monospace: "monospace" }); + +globalThis.xloc = globalThis.xloc || __pineNS({ bar_index: "bar_index", bar_time: "bar_time" }); + +globalThis.yloc = globalThis.yloc || __pineNS({ price: "price", abovebar: "abovebar", belowbar: "belowbar" }); + +globalThis.extend = globalThis.extend || __pineNS({ none: "none", left: "left", right: "right", both: "both" }); + +globalThis.order = globalThis.order || __pineNS({ ascending: "ascending", descending: "descending" }); + +globalThis.alert = globalThis.alert || Object.assign(function() { return null; }, { freq_once_per_bar: "once_per_bar", freq_once_per_bar_close: "once_per_bar_close", freq_all: "all" }); + +globalThis.dayofmonth = globalThis.dayofmonth || function(t) { return new Date(t || 0).getUTCDate(); }; + +globalThis.plot = globalThis.plot || __pineNS({ style_line: "line", style_linebr: "linebr", style_stepline: "stepline", style_histogram: "histogram", style_cross: "cross", style_area: "area", style_areabr: "areabr", style_columns: "columns", style_circles: "circles" }); + +globalThis.int = globalThis.int || function(x) { return x == null ? null : Math.trunc(Number(x)); }; + +globalThis.float = globalThis.float || function(x) { return x == null ? null : Number(x); }; + +globalThis.bool = globalThis.bool || function(x) { return Boolean(x); }; + +globalThis.string = globalThis.string || function(x) { return x == null ? null : String(x); }; + +globalThis.dayofweek = globalThis.dayofweek || __pineNS({ sunday: 1, monday: 2, tuesday: 3, wednesday: 4, thursday: 5, friday: 6, saturday: 7 }); + +globalThis.str = globalThis.str || __pineNS({}); + +pinescript.math = globalThis.math; + +pinescript.ta = globalThis.ta; + +pinescript.str = globalThis.str; + +pinescript.array = __pineNS(globalThis.array); + + pinescript.color = { hex: function(s) { @@ -1190,10 +1670,10 @@ pinescript.table = { }; -// Input parameters +// Script input parameters and their defaults -// Main script logic +// The transpiled script logic, called once per bar function main() { @@ -1218,17 +1698,17 @@ function main() { let MAX_DEV_SD = 1.5; let SIGN_Z_CRIT = 1.65; let ANN_FACTOR = (252 / HORIZON); - let i_bgMode = input.string("On", "Background", ({ options: ["Off", "On"], group: "Display", tooltip: "Background color based on drift classification" })); - let i_bgInt = input.int(75, "BG Intensity", ({ minval: 70, maxval: 95, group: "Display", tooltip: "Transparency of background color" })); - let i_barColor = input.bool(true, "Bar Coloring", ({ group: "Display", tooltip: "Color bars based on drift direction" })); - let i_showDash = input.bool(true, "Dashboard", ({ group: "Dashboard", tooltip: "Show statistical details panel" })); - let i_dashPos = input.string("Top Right", "Position", ({ options: ["Top Right", "Top Left", "Bottom Right", "Bottom Left"], group: "Dashboard" })); - let i_dashSize = input.string("Small", "Size", ({ options: ["Tiny", "Small", "Normal"], group: "Dashboard", tooltip: "Text size of dashboard" })); - let i_showWM = input.bool(true, "Watermark", ({ group: "Watermark", tooltip: "Show ticker and classification summary" })); - let i_wmPos = input.string("Bottom Right", "Position", ({ options: ["Top Right", "Top Left", "Bottom Right", "Bottom Left"], group: "Watermark" })); - let i_wmSize = input.string("Normal", "Size", ({ options: ["Small", "Normal", "Large"], group: "Watermark", tooltip: "Text size of watermark" })); - let i_dark = input.bool(true, "Dark Mode", ({ group: "Style", tooltip: "Optimize colors for dark charts" })); - let i_alerts = input.bool(false, "Alerts", ({ group: "Alerts", tooltip: "Trigger alerts on classification changes" })); + let i_bgMode = pinescript.inputString("On", "Background", ({ options: ["Off", "On"], group: "Display", tooltip: "Background color based on drift classification" })); + let i_bgInt = pinescript.inputInt(75, "BG Intensity", ({ minval: 70, maxval: 95, group: "Display", tooltip: "Transparency of background color" })); + let i_barColor = pinescript.inputBool(true, "Bar Coloring", ({ group: "Display", tooltip: "Color bars based on drift direction" })); + let i_showDash = pinescript.inputBool(true, "Dashboard", ({ group: "Dashboard", tooltip: "Show statistical details panel" })); + let i_dashPos = pinescript.inputString("Top Right", "Position", ({ options: ["Top Right", "Top Left", "Bottom Right", "Bottom Left"], group: "Dashboard" })); + let i_dashSize = pinescript.inputString("Small", "Size", ({ options: ["Tiny", "Small", "Normal"], group: "Dashboard", tooltip: "Text size of dashboard" })); + let i_showWM = pinescript.inputBool(true, "Watermark", ({ group: "Watermark", tooltip: "Show ticker and classification summary" })); + let i_wmPos = pinescript.inputString("Bottom Right", "Position", ({ options: ["Top Right", "Top Left", "Bottom Right", "Bottom Left"], group: "Watermark" })); + let i_wmSize = pinescript.inputString("Normal", "Size", ({ options: ["Small", "Normal", "Large"], group: "Watermark", tooltip: "Text size of watermark" })); + let i_dark = pinescript.inputBool(true, "Dark Mode", ({ group: "Style", tooltip: "Optimize colors for dark charts" })); + let i_alerts = pinescript.inputBool(false, "Alerts", ({ group: "Alerts", tooltip: "Trigger alerts on classification changes" })); let cBull = (i_dark ? pinescript.color.hex("#22c55e") : pinescript.color.hex("#16a34a")); let cBear = (i_dark ? pinescript.color.hex("#ef4444") : pinescript.color.hex("#dc2626")); let cWeak = (i_dark ? pinescript.color.hex("#f59e0b") : pinescript.color.hex("#d97706")); @@ -1254,7 +1734,7 @@ function main() { if ((n === 0)) { 0; } else { - let s = array.copy(arr); + let s = pinescript.arrayCopy(arr); pinescript.arraySort(s); let m = (n / 2); (((n % 2) === 0) ? ((pinescript.arrayGet(s, (m - 1)) + pinescript.arrayGet(s, m)) / 2) : pinescript.arrayGet(s, m)); @@ -1263,15 +1743,15 @@ function main() { function f_collectAllReturns(src, horizon, maxBars) { if (state.arr === undefined) state.arr = pinescript.arrayNew(0); pinescript.arrayClear(state.arr); - let nPeriods = math.floor((maxBars / horizon)); + let nPeriods = pinescript.floor((maxBars / horizon)); for (let i = 0; i <= (nPeriods - 1); i++) { let recentBar = (i * horizon); let pastBar = ((i + 1) * horizon); if ((pastBar <= maxBars)) { - let pRecent = pinescript.nz(pinescript.offset(src, recentBar), src); - let pPast = pinescript.nz(pinescript.offset(src, pastBar), src); + let pRecent = pinescript.nz(pinescript.hist(0, src, recentBar), src); + let pPast = pinescript.nz(pinescript.hist(1, src, pastBar), src); if (((pPast > 0) && (pRecent > 0))) { - pinescript.arrayPush(state.arr, math.log((pRecent / pPast))); + pinescript.arrayPush(state.arr, pinescript.log((pRecent / pPast))); } } } @@ -1326,7 +1806,7 @@ function main() { if ((n < 2)) { state.result; } else { - let halfN = math.floor((n / 2)); + let halfN = pinescript.floor((n / 2)); if (isFirstHalf) { for (let i = 0; i <= (halfN - 1); i++) { pinescript.arrayPush(state.result, pinescript.arrayGet(state.arr, i)); @@ -1342,8 +1822,8 @@ function main() { function f_vrTest(src, horizon, k, sampleBars) { let arr1 = f_collectAllReturns(src, horizon, sampleBars); let arrK = f_collectAllReturns(src, (horizon * k), sampleBars); - let [m1, md1, sd1, n1, pc1] = f_arrayStats(arr1); - let [mK, mdK, sdK, nK, pcK] = f_arrayStats(arrK); + let [m1, md1, sd1, n1, pc1] = pinescript.unpack(f_arrayStats(arr1), 5); + let [mK, mdK, sdK, nK, pcK] = pinescript.unpack(f_arrayStats(arrK), 5); if ((((sd1 < 1e-10) || (n1 < MIN_OBS_STRICT)) || (nK < 3))) { [1, 0, false]; } else { @@ -1363,23 +1843,23 @@ function main() { return ((n < MIN_OBS_STRICT) ? 0 : ((float(posCount) - (float(n) / 2)) / pinescript.sqrt((float(n) / 4)))); } let allReturns = f_collectAllReturns(close, HORIZON, SAMPLE_TOTAL); - let [mF, mdF, sdF, nF, pcF] = f_arrayStats(allReturns); + let [mF, mdF, sdF, nF, pcF] = pinescript.unpack(f_arrayStats(allReturns), 5); let seF = f_hacSE(allReturns, mF); let tF = ((seF > 1e-10) ? (mF / seF) : 0); arr1 = f_splitArray(allReturns, true); let arr2 = f_splitArray(allReturns, false); - [m1, md1, sd1, n1, pc1] = f_arrayStats(arr1); + [m1, md1, sd1, n1, pc1] = pinescript.unpack(f_arrayStats(arr1), 5); let se1 = f_hacSE(arr1, m1); let t1 = ((se1 > 1e-10) ? (m1 / se1) : 0); - let [m2, md2, sd2, n2, pc2] = f_arrayStats(arr2); + let [m2, md2, sd2, n2, pc2] = pinescript.unpack(f_arrayStats(arr2), 5); let se2 = f_hacSE(arr2, m2); let t2 = ((se2 > 1e-10) ? (m2 / se2) : 0); - [vr, zVR, vrSig] = f_vrTest(close, HORIZON, 5, SAMPLE_TOTAL); + [vr, zVR, vrSig] = pinescript.unpack(f_vrTest(close, HORIZON, 5, SAMPLE_TOTAL), 3); let mde95 = f_mde(sdF, nF, T_CRIT_95); let annDrift = (mF * ANN_FACTOR); let zSign = f_signTestZ(pcF, nF); let hitRate = ((nF > 0) ? (float(pcF) / float(nF)) : 0.5); - let inferenceAllowed = (((nF >= MIN_OBS_STRICT) && (n1 >= math.floor((MIN_OBS_STRICT / 2)))) && (n2 >= math.floor((MIN_OBS_STRICT / 2)))); + let inferenceAllowed = (((nF >= MIN_OBS_STRICT) && (n1 >= pinescript.floor((MIN_OBS_STRICT / 2)))) && (n2 >= pinescript.floor((MIN_OBS_STRICT / 2)))); let isRobust = (nF >= MIN_OBS_ROBUST); let statSig = (inferenceAllowed && (pinescript.abs(tF) > T_CRIT_95)); let econSig = (inferenceAllowed && (pinescript.abs(annDrift) >= MIN_DRIFT_ECON)); @@ -1460,7 +1940,7 @@ function main() { if (i_barColor) { barClr = ((classification >= 2) ? cBarLong : ((classification <= -2) ? cBarShort : ((pinescript.abs(classification) === 1) ? ((direction > 0) ? cBarLong : cBarShort) : cBarNeut))); } - barcolor(barClr); + pinescript.barcolor(barClr); if (state.wm === undefined) state.wm = pinescript.table.new(f_pos(i_wmPos), 1, 5, ({ bgcolor: pinescript.color.new(pinescript.color.black, 100) })); if ((i_showWM && barstate.islast)) { let evClr = (!inferenceAllowed ? cNeut : (strongEvidence ? cBull : (weakEvidence ? cWeak : cNeut))); @@ -1517,18 +1997,78 @@ function main() { pinescript.table.cell(state.d, 0, 17, "**p<.05 *p<.10", ({ text_color: pinescript.color.new(cText, 50), bgcolor: pinescript.color.new(cNeut, 95), text_size: dashSizeF })); pinescript.table.cell(state.d, 1, 17, (isRobust ? "" : "Heuristic"), ({ text_color: pinescript.color.new(cWeak, 30), bgcolor: pinescript.color.new(cNeut, 95), text_size: dashSizeF })); } - let changed = (classification !== pinescript.offset(classification, 1)); + let changed = (classification !== pinescript.hist(2, classification, 1)); if ((changed && i_alerts)) { pinescript.alert((((("ADM: " + biasStr) + " (") + evidenceStr) + ")"), alert.freq_once_per_bar); } - alertcondition((strongEvidence && (direction > 0)), "Positive Drift", "Positive Drift detected"); - alertcondition((strongEvidence && (direction < 0)), "Negative Drift", "Negative Drift detected"); - alertcondition(weakEvidence, "Weak", "Weak evidence"); - alertcondition(!inferenceAllowed, "Blocked", "Inference blocked"); + pinescript.alertcondition((strongEvidence && (direction > 0)), "Positive Drift", "Positive Drift detected"); + pinescript.alertcondition((strongEvidence && (direction < 0)), "Negative Drift", "Negative Drift detected"); + pinescript.alertcondition(weakEvidence, "Weak", "Weak evidence"); + pinescript.alertcondition(!inferenceAllowed, "Blocked", "Inference blocked"); } -// Export for use -export { main +// The bar-by-bar engine: runs main() once per bar over the dataset. +function run(data, options = {}) { + data = data || {}; + const inClose = (data.close || []).map(Number); + const n = inClose.length; + const inOpen = (data.open || inClose).map(Number); + const inHigh = (data.high || inClose).map(Number); + const inLow = (data.low || inClose).map(Number); + const inVol = (data.volume || new Array(n).fill(0)).map(Number); + const inTime = (data.time || inClose.map((_, i) => i)).map(Number); + + // Reset all persistent and per-run state so repeated runs are independent. + globalThis.__pineState = {}; + globalThis.__pineRuntime = { plots: {}, plotshapes: {}, alerts: [], bars: n, inputs: options.inputs || {} }; + pinescript.__rt = {}; + pinescript.__bar = 0; + + // Growing OHLCV windows: each is a SeriesArray that gains one element per bar, + // so window built-ins like sma and highest see history up to the current bar. + const O = pinescript.asSeries([]), H = pinescript.asSeries([]), L = pinescript.asSeries([]); + const C = pinescript.asSeries([]), V = pinescript.asSeries([]), T = pinescript.asSeries([]); + const HL2 = pinescript.asSeries([]), HLC3 = pinescript.asSeries([]), OHLC4 = pinescript.asSeries([]); + globalThis.open = O; globalThis.high = H; globalThis.low = L; globalThis.close = C; + globalThis.volume = V; globalThis.time = T; + globalThis.hl2 = HL2; globalThis.hlc3 = HLC3; globalThis.ohlc4 = OHLC4; + globalThis.syminfo = globalThis.syminfo || { tickerid: 'SYNTHETIC', ticker: 'SYNTHETIC', mintick: 0.01 }; + + const rt = globalThis.__pineRuntime; + for (let i = 0; i < n; i++) { + O.push(inOpen[i]); H.push(inHigh[i]); L.push(inLow[i]); C.push(inClose[i]); V.push(inVol[i]); T.push(inTime[i]); + HL2.push((inHigh[i] + inLow[i]) / 2); + HLC3.push((inHigh[i] + inLow[i] + inClose[i]) / 3); + OHLC4.push((inOpen[i] + inHigh[i] + inLow[i] + inClose[i]) / 4); + + pinescript.__bar = i; + rt.__barIndex = i; + rt.__plotIdx = 0; + rt.__shapeIdx = 0; + globalThis.bar_index = i; + globalThis.last_bar_index = n - 1; + globalThis.barstate = { + isfirst: i === 0, islast: i === n - 1, isrealtime: false, ishistory: true, + isconfirmed: true, isnew: true, islastconfirmedhistory: i === n - 1, + }; + + main(); + } + + // Backfill skipped bars so every output series has exactly n entries. + for (const k of Object.keys(rt.plots)) { + const d = rt.plots[k].data; + for (let i = 0; i < n; i++) if (d[i] === undefined) d[i] = null; + } + for (const k of Object.keys(rt.plotshapes)) { + const d = rt.plotshapes[k].data; + for (let i = 0; i < n; i++) if (d[i] === undefined) d[i] = false; + } + return rt; +} + + +export { main, run }; diff --git a/converted/Automatic_Trendline_Metrify.js b/converted/Automatic_Trendline_Metrify.js index e47a44b..9346205 100644 --- a/converted/Automatic_Trendline_Metrify.js +++ b/converted/Automatic_Trendline_Metrify.js @@ -1,12 +1,63 @@ -// PineScript to JavaScript Transpiled Code +// Auto-generated JavaScript from PineScript source -// Generated automatically +// Do not edit by hand -- re-run the converter instead -// Built-in PineScript functions +// All the built-in PineScript functions that scripts depend on at runtime const pinescript = { + __scalar: function(v) { + if (v === null || v === undefined) return v; + if (typeof v === 'object' && typeof v.valueOf === 'function') { + const s = v.valueOf(); + if (typeof s === 'number' || typeof s === 'boolean' || s === null) return s; + } + return v; + }, + series: function(id, value) { + const rt = this.__rt || (this.__rt = {}); + const store = rt.series || (rt.series = {}); + let buf = store[id]; + if (!buf) buf = store[id] = []; + buf[this.__bar | 0] = this.__scalar(value); + return buf; + }, + hist: function(id, value, n) { + const buf = this.series(id, value); + const idx = (this.__bar | 0) - (n | 0); + return idx >= 0 && idx < buf.length ? buf[idx] : null; + }, + time: function(timeframe, session) { + const t = globalThis.time; + if (t && typeof t.valueOf === 'function') return t.valueOf(); + return Array.isArray(t) ? t[t.length - 1] : t; + }, + sign: function(x) { + if (x === null || x === undefined || (typeof x === 'number' && isNaN(x))) return null; + return Math.sign(Number(x)); + }, + unpack: function(value, count) { + if (Array.isArray(value)) return value; + if (value != null && typeof value[Symbol.iterator] === 'function') return Array.from(value); + return new Array(count || 0).fill(null); + }, + alertcondition: function(condition, ...rest) { + if (globalThis.__pineRuntime) { + globalThis.__pineRuntime.alerts.push({ condition, args: rest }); + } + return null; + }, + barcolor: function(color) { return null; }, + bgcolor: function(color, title, editable, showLast) { + return null; + }, + fill: function(series1, series2, color, title, editable) { + return null; + }, + hline: function(_price, _title, _opts) { + return null; + }, sma: function(series, length) { if (series === null || series === undefined) return null; if (!Array.isArray(series)) series = [series]; @@ -84,6 +135,13 @@ const pinescript = { } return weightSum > 0 ? sum / weightSum : null; }, + swma: function(series) { + if (series === null || series === undefined) return null; + if (!Array.isArray(series)) series = [series]; + if (series.length < 4) return null; + const s = series.slice(-4); + return (s[0] * 1 + s[1] * 2 + s[2] * 2 + s[3] * 1) / 6; + }, atr: function(high, low, close, length = 14) { if (!Array.isArray(high) || !Array.isArray(low) || !Array.isArray(close)) return null; if (high.length < length + 1 || low.length < length + 1 || close.length < length + 1) return null; @@ -114,6 +172,53 @@ const pinescript = { const atr = builtins.get('atr')(high, low, close, length); return { middle: sma, upper: sma + mult * atr, lower: sma - mult * atr }; }, + supertrend: function(factor, atrPeriod, high, low, close) { + if (!Array.isArray(high) || !Array.isArray(low) || !Array.isArray(close)) return [null, null]; + if (high.length < atrPeriod + 1) return [null, null]; + const atrVal = builtins.get('atr')(high, low, close, atrPeriod); + if (atrVal === null) return [null, null]; + const lastIdx = close.length - 1; + const hl2 = (high[lastIdx] + low[lastIdx]) / 2; + const upperBand = hl2 + factor * atrVal; + const lowerBand = hl2 - factor * atrVal; + // Determine direction: 1 means downtrend (price below band), -1 means uptrend + const direction = close[lastIdx] > upperBand ? -1 : close[lastIdx] < lowerBand ? 1 : -1; + const supertrendValue = direction === -1 ? lowerBand : upperBand; + return [supertrendValue, direction]; + }, + ta_supertrend: function(factor, atrPeriod) { + const H = globalThis.high; + const L = globalThis.low; + const C = globalThis.close; + return builtins.get('supertrend')(factor, atrPeriod, H, L, C); + }, + dmi: function(diLength, adxSmoothing, high, low, close) { + if (!Array.isArray(high) || !Array.isArray(low) || !Array.isArray(close)) return { plusDI: null, minusDI: null, adx: null }; + if (high.length < diLength + 1) return { plusDI: null, minusDI: null, adx: null }; + let plusDMSum = 0, minusDMSum = 0, trSum = 0; + for (let i = 1; i <= diLength; i++) { + const idx = high.length - 1 - diLength + i; + const upMove = high[idx] - high[idx - 1]; + const downMove = low[idx - 1] - low[idx]; + plusDMSum += (upMove > downMove && upMove > 0) ? upMove : 0; + minusDMSum += (downMove > upMove && downMove > 0) ? downMove : 0; + const tr = Math.max( + high[idx] - low[idx], + Math.abs(high[idx] - close[idx - 1]), + Math.abs(low[idx] - close[idx - 1]) + ); + trSum += tr; + } + const plusDI = trSum > 0 ? (plusDMSum / trSum) * 100 : 0; + const minusDI = trSum > 0 ? (minusDMSum / trSum) * 100 : 0; + const diSum = plusDI + minusDI; + const dx = diSum > 0 ? Math.abs(plusDI - minusDI) / diSum * 100 : 0; + return { plusDI, minusDI, adx: dx }; + }, + adx: function(diLength, adxSmoothing, high, low, close) { + const result = builtins.get('dmi')(diLength, adxSmoothing, high, low, close); + return result ? result.adx : null; + }, rsi: function(close, length = 14) { if (close === null || close === undefined) return null; if (!Array.isArray(close)) close = [close]; @@ -171,9 +276,7 @@ const pinescript = { return { macd: macdLine, signal: signalLine, histogram: histogram }; }, cci: function(a, b, c, d) { - // Pine signatures: - // - ta.cci(source, length) - // - ta.cci(high, low, close, length) + // Supports both ta.cci(source, length) and ta.cci(high, low, close, length) signatures let source; let length; if (Array.isArray(a) && typeof b === 'number' && c === undefined) { @@ -304,6 +407,15 @@ const pinescript = { stdev: function(source, length) { return Math.sqrt(builtins.get('variance')(source, length)); }, + median: function(source, length) { + if (!source || source.length < length) return null; + const sorted = [...source.slice(-length)].sort((a, b) => a - b); + const mid = Math.floor(sorted.length / 2); + return sorted.length % 2 !== 0 ? sorted[mid] : (sorted[mid - 1] + sorted[mid]) / 2; + }, + ta_median: function(source, length) { + return builtins.get('median')(source, length); + }, hl2: function(high, low) { return high && low ? (high[high.length - 1] + low[low.length - 1]) / 2 : null; }, @@ -420,6 +532,24 @@ const pinescript = { return (series1[i - 1] < series2[i - 1] && series1[i] >= series2[i]) || (series1[i - 1] > series2[i - 1] && series1[i] <= series2[i]); }, + crossover: function(series1, series2) { + if (series1 === null || series1 === undefined) return false; + if (series2 === null || series2 === undefined) return false; + if (!Array.isArray(series1)) series1 = [series1]; + if (!Array.isArray(series2)) series2 = [series2]; + if (series1.length < 2 || series2.length < 2) return false; + const i = series1.length - 1; + return series1[i - 1] < series2[i - 1] && series1[i] >= series2[i]; + }, + crossunder: function(series1, series2) { + if (series1 === null || series1 === undefined) return false; + if (series2 === null || series2 === undefined) return false; + if (!Array.isArray(series1)) series1 = [series1]; + if (!Array.isArray(series2)) series2 = [series2]; + if (series1.length < 2 || series2.length < 2) return false; + const i = series1.length - 1; + return series1[i - 1] > series2[i - 1] && series1[i] <= series2[i]; + }, offset: function(series, offset) { if (!Array.isArray(series)) return null; const idx = series.length - 1 - offset; @@ -500,12 +630,14 @@ const pinescript = { return builtins.get('nz')(value, replacement); }, plot: function(series, title = '', color = null, linewidth = 1) { - globalThis.__pineRuntime.plots.push({ - series, - title, - color, - linewidth - }); + const rt = globalThis.__pineRuntime; + if (!rt) return series; + const bar = rt.__barIndex | 0; + const ord = (rt.__plotIdx = (rt.__plotIdx | 0) + 1) - 1; + const key = (title && String(title)) || ('plot_' + ord); + let p = rt.plots[key]; + if (!p) p = rt.plots[key] = { title: key, color, linewidth, data: [] }; + p.data[bar] = this.__scalar(series); return series; }, lineNew: function(x1, y1, x2, y2, opts = {}) { @@ -514,20 +646,72 @@ const pinescript = { lineDelete: function(l) { return null; }, - labelNew: function(...args) { - if (globalThis.__pineRuntime) { - globalThis.__pineRuntime.labels = globalThis.__pineRuntime.labels || []; - globalThis.__pineRuntime.labels.push({ args }); - } - return { args }; + lineSetXY: function(line, point, x, y) { + if (!line) return null; + if (point === 0 || point === 'x1') { line.x1 = x; line.y1 = y; } + else { line.x2 = x; line.y2 = y; } + return line; + }, + lineGetX: function(line, point) { + if (!line) return null; + return point === 0 || point === 'x1' ? line.x1 : line.x2; + }, + lineGetY: function(line, point) { + if (!line) return null; + return point === 0 || point === 'y1' ? line.y1 : line.y2; + }, + labelNew: function(x, y, text = '', opts = {}) { + return { x, y, text, opts, _type: 'label' }; }, labelDelete: function(l) { return null; }, + labelSetText: function(label, text) { + if (!label) return null; + label.text = text; + return label; + }, + labelGetText: function(label) { + if (!label) return ''; + return label.text || ''; + }, + boxNew: function(left, top, right, bottom, opts = {}) { + return { left, top, right, bottom, opts, _type: 'box' }; + }, + boxDelete: function(box) { + return null; + }, + boxSetLeftTop: function(box, left, top) { + if (!box) return null; + box.left = left; + box.top = top; + return box; + }, + boxSetRightBottom: function(box, right, bottom) { + if (!box) return null; + box.right = right; + box.bottom = bottom; + return box; + }, + polylineNew: function(points, opts = {}) { + return { points: points || [], opts, _type: 'polyline' }; + }, + polylineDelete: function(poly) { + return null; + }, plotshape: function(condition, ...rest) { - if (globalThis.__pineRuntime) { - globalThis.__pineRuntime.plotshapes.push({ condition, args: rest }); + const rt = globalThis.__pineRuntime; + if (!rt) return condition; + const bar = rt.__barIndex | 0; + const ord = (rt.__shapeIdx = (rt.__shapeIdx | 0) + 1) - 1; + let key = 'shape_' + ord; + for (const r of rest) { + if (typeof r === 'string') { key = r; break; } + if (r && typeof r === 'object' && r.title) { key = String(r.title); break; } } + let s = rt.plotshapes[key]; + if (!s) s = rt.plotshapes[key] = { title: key, data: [] }; + s.data[bar] = !!this.__scalar(condition); return condition; }, indicator: function(title, shorttitle, overlay, opts) { @@ -542,27 +726,12 @@ const pinescript = { } return null; }, - alertcondition: function(condition, ...rest) { - if (globalThis.__pineRuntime) { - globalThis.__pineRuntime.alerts.push({ condition, args: rest }); - } - return null; - }, plotbar: function(open, high, low, close, title, color, editable, showLast) { return { open, high, low, close }; }, plotcandle: function(open, high, low, close, title, color, wickColor, borderColor, editable, showLast) { return { open, high, low, close }; }, - hline: function(_price, _title, _opts) { - return null; - }, - bgcolor: function(color, title, editable, showLast) { - return null; - }, - fill: function(series1, series2, color, title, editable) { - return null; - }, alert: function(condition, message, frequency) { console.log('Alert:', condition ? message : 'Condition not met'); return condition; @@ -828,6 +997,9 @@ const pinescript = { arrayIndexOf: function(arr, value) { return arr ? arr.indexOf(value) : -1; }, + arrayLastIndexOf: function(arr, value) { + return arr ? arr.lastIndexOf(value) : -1; + }, arraySum: function(arr) { return arr ? arr.reduce((a, b) => a + b, 0) : 0; }, @@ -840,6 +1012,42 @@ const pinescript = { arrayMax: function(arr) { return arr && arr.length > 0 ? Math.max(...arr) : null; }, + arrayStdev: function(arr) { + if (!arr || arr.length === 0) return null; + const mean = arr.reduce((a, b) => a + b, 0) / arr.length; + const variance = arr.reduce((sum, val) => sum + Math.pow(val - mean, 2), 0) / arr.length; + return Math.sqrt(variance); + }, + arrayVariance: function(arr) { + if (!arr || arr.length === 0) return null; + const mean = arr.reduce((a, b) => a + b, 0) / arr.length; + return arr.reduce((sum, val) => sum + Math.pow(val - mean, 2), 0) / arr.length; + }, + arrayCovariance: function(arr1, arr2) { + if (!arr1 || !arr2 || arr1.length === 0 || arr1.length !== arr2.length) return null; + const mean1 = arr1.reduce((a, b) => a + b, 0) / arr1.length; + const mean2 = arr2.reduce((a, b) => a + b, 0) / arr2.length; + let cov = 0; + for (let i = 0; i < arr1.length; i++) { + cov += (arr1[i] - mean1) * (arr2[i] - mean2); + } + return cov / arr1.length; + }, + mode: function(arr) { + if (!arr || arr.length === 0) return null; + const counts = new Map(); + let maxCount = 0; + let modeVal = arr[0]; + for (const val of arr) { + const c = (counts.get(val) || 0) + 1; + counts.set(val, c); + if (c > maxCount) { + maxCount = c; + modeVal = val; + } + } + return modeVal; + }, strLength: function(str) { return str ? str.length : 0; }, @@ -895,6 +1103,84 @@ const pinescript = { strReverse: function(str) { return str ? str.split('').reverse().join('') : ''; }, + strFormat: function(formatStr, ...args) { + if (!formatStr) return ''; + return formatStr.replace(/\{(\d+)\}/g, (match, idx) => { + const i = parseInt(idx, 10); + return i < args.length ? String(args[i]) : match; + }); + }, + input: function(defval, title, tooltip, inline, group) { + return defval; + }, + inputInt: function(defval = 0, title, minval, maxval, step, tooltip, inline, group) { + return defval; + }, + inputFloat: function(defval = 0.0, title, minval, maxval, step, tooltip, inline, group) { + return defval; + }, + inputBool: function(defval = false, title, tooltip, inline, group) { + return defval; + }, + inputString: function(defval = '', title, options, tooltip, inline, group) { + return defval; + }, + inputSource: function(defval = null, title, tooltip, inline, group) { + return defval; + }, + inputColor: function(defval = '#000000', title, tooltip, inline, group) { + return defval; + }, + inputTime: function(defval = 0, title, tooltip, inline, group) { + return defval; + }, + mathSign: function(value) { + return Math.sign(value); + }, + mathAvg: function(...args) { + const valid = args.filter(v => v !== null && v !== undefined && !isNaN(v)); + return valid.length > 0 ? valid.reduce((a, b) => a + b, 0) / valid.length : null; + }, + mathSum: function(series, length) { + if (!series || series.length < length) return null; + return series.slice(-length).reduce((a, b) => a + b, 0); + }, + mathRandom: function(min = 0, max = 1) { + return min + Math.random() * (max - min); + }, + mathTodegrees: function(radians) { + return radians * (180 / Math.PI); + }, + mathToradians: function(degrees) { + return degrees * (Math.PI / 180); + }, + colorT: function(color, transparency) { + if (transparency !== undefined) { + return { color, transparency }; + } + if (color && typeof color === 'object' && 'transparency' in color) { + return color.transparency; + } + return 0; + }, + tableCellSetText: function(table, column, row, text) { + return null; + }, + tableCellSetBgcolor: function(table, column, row, bgcolor) { + return null; + }, + tableMergeCells: function(table, startColumn, startRow, endColumn, endRow) { + return null; + }, + tableDelete: function(table) { + return null; + }, + tableClear: function(table, startColumn, startRow, endColumn, endRow) { + return null; + }, + maxBarsBack: function(series, length) { + return null; + }, pi: 3.141592653589793, e: 2.718281828459045, true: true, @@ -905,8 +1191,7 @@ const pinescript = { const info = { ticker: 'AAPL', tickerid: 'NASDAQ:AAPL', prefix: 'NASDAQ', root: 'AAPL', suffix: '' }; return info[type] || ''; }, - time: 1771178051057, - timenow: 1771178051057, + timenow: 1781574526661, barstate: "LAST", dividends: {}, splits: {}, @@ -923,7 +1208,7 @@ const pinescript = { return null; }, tr: function(high, low, close) { - // Pine signature: ta.tr(true) + // Handles both the boolean shorthand ta.tr(true) and explicit high/low/close arguments if (typeof high === 'boolean') { const H = globalThis.high; const L = globalThis.low; @@ -934,7 +1219,7 @@ const pinescript = { return Math.max(H[i] - L[i], Math.abs(H[i] - prevClose), Math.abs(L[i] - prevClose)); } - // Fallback: treat inputs as scalars for current bar + // When arrays are passed, compute true range from the last bar using the previous close if (Array.isArray(close)) { const prevClose = close[close.length - 2] || close[0]; const currHigh = Array.isArray(high) ? high[high.length - 1] : high; @@ -945,6 +1230,92 @@ const pinescript = { return Math.max(high - low, Math.abs(high - close), Math.abs(low - close)); }, + arrayFirst: function(arr) { + if (!arr || arr.length === 0) return null; + return arr[0]; + }, + arrayLast: function(arr) { + if (!arr || arr.length === 0) return null; + return arr[arr.length - 1]; + }, + arrayJoin: function(arr, separator = ',') { + if (!arr) return ''; + return arr.join(separator); + }, + arrayConcat: function(arr1, arr2) { + if (!arr1) return arr2 || []; + if (!arr2) return arr1; + return arr1.concat(arr2); + }, + arrayCopy: function(arr) { + if (!arr) return []; + return [...arr]; + }, + arrayBinarySearch: function(arr, value) { + if (!arr || arr.length === 0) return -1; + let lo = 0, hi = arr.length - 1; + while (lo <= hi) { + const mid = (lo + hi) >>> 1; + if (arr[mid] === value) return mid; + if (arr[mid] < value) lo = mid + 1; + else hi = mid - 1; + } + return -1; + }, + arrayRange: function(arr) { + if (!arr || arr.length === 0) return 0; + return Math.max(...arr) - Math.min(...arr); + }, + arrayMedian: function(arr) { + if (!arr || arr.length === 0) return null; + const sorted = [...arr].sort((a, b) => a - b); + const mid = Math.floor(sorted.length / 2); + return sorted.length % 2 !== 0 ? sorted[mid] : (sorted[mid - 1] + sorted[mid]) / 2; + }, + arrayMode: function(arr) { + if (!arr || arr.length === 0) return null; + const freq = {}; + let maxCount = 0; + let mode = arr[0]; + for (const v of arr) { + freq[v] = (freq[v] || 0) + 1; + if (freq[v] > maxCount) { maxCount = freq[v]; mode = v; } + } + return mode; + }, + arrayPercentile: function(arr, percentile) { + if (!arr || arr.length === 0) return null; + const sorted = [...arr].sort((a, b) => a - b); + const idx = (percentile / 100) * (sorted.length - 1); + const lo = Math.floor(idx); + const hi = Math.ceil(idx); + if (lo === hi) return sorted[lo]; + return sorted[lo] + (sorted[hi] - sorted[lo]) * (idx - lo); + }, + arrayPercentileLinearInterpolation: function(arr, percentile) { + return builtins.get('arrayPercentile')(arr, percentile); + }, + arrayPercentileNearestRank: function(arr, percentile) { + if (!arr || arr.length === 0) return null; + const sorted = [...arr].sort((a, b) => a - b); + const idx = Math.ceil((percentile / 100) * sorted.length) - 1; + return sorted[Math.max(0, idx)]; + }, + arrayAbs: function(arr) { + if (!arr) return []; + return arr.map(v => Math.abs(v)); + }, + arrayEvery: function(arr, callback) { + if (!arr) return false; + return arr.every(callback); + }, + arraySome: function(arr, callback) { + if (!arr) return false; + return arr.some(callback); + }, + requestFinancial: function() { + return null; + }, }; @@ -1101,6 +1472,115 @@ globalThis.barmerge = globalThis.barmerge || { }; +function __pineNS(real) { + + const base = real || {}; + + return new Proxy(base, { + + get(target, key) { + + if (key in target) return target[key]; + + if (typeof key === "symbol") return target[key]; + + // Unknown member: usable as a value (null), a constant, or a no-op function. + + const noop = function() { return null; }; + + noop.toString = () => ""; + + return noop; + + } + + }); + +} + +globalThis.ta = globalThis.ta || __pineNS({}); + +globalThis.math = globalThis.math || __pineNS({ pi: Math.PI, e: Math.E, phi: 1.618033988749895, rphi: 0.618033988749895 }); + +globalThis.chart = globalThis.chart || __pineNS({ point: __pineNS({}), bg_color: null, fg_color: null }); + +globalThis.line = globalThis.line || __pineNS({ style_solid: "solid", style_dashed: "dashed", style_dotted: "dotted" }); + +globalThis.box = globalThis.box || __pineNS({}); + +globalThis.label = globalThis.label || __pineNS({ style_label_down: "label_down", style_label_up: "label_up", style_none: "none" }); + +globalThis.polyline = globalThis.polyline || __pineNS({}); + +globalThis.linefill = globalThis.linefill || __pineNS({}); + +globalThis.matrix = globalThis.matrix || __pineNS({}); + +globalThis.map = globalThis.map || __pineNS({}); + +globalThis.session = globalThis.session || __pineNS({ regular: "regular", extended: "extended" }); + +globalThis.dayofweek = globalThis.dayofweek || __pineNS({ sunday: 1, monday: 2, tuesday: 3, wednesday: 4, thursday: 5, friday: 6, saturday: 7 }); + +globalThis.timeframe = __pineNS(Object.assign(globalThis.timeframe || {}, { period: (globalThis.timeframe && globalThis.timeframe.period) || "D", isintraday: false, isdaily: true, multiplier: 1 })); + +globalThis.request = __pineNS(globalThis.request || {}); + +globalThis.input = __pineNS(globalThis.input || {}); + +globalThis.adjustment = globalThis.adjustment || __pineNS({}); + +globalThis.earnings = globalThis.earnings || __pineNS({}); + +globalThis.dividends = globalThis.dividends || __pineNS({}); + +globalThis.splits = globalThis.splits || __pineNS({}); + +globalThis.currency = globalThis.currency || __pineNS({}); + +globalThis.display = globalThis.display || __pineNS({ none: "none", all: "all", pane: "pane", data_window: "data_window", status_line: "status_line", price_scale: "price_scale" }); + +globalThis.format = globalThis.format || __pineNS({ price: "price", volume: "volume", percent: "percent", mintick: "mintick", inherit: "inherit" }); + +globalThis.scale = globalThis.scale || __pineNS({ left: "left", right: "right", none: "none" }); + +globalThis.font = globalThis.font || __pineNS({ family_default: "default", family_monospace: "monospace" }); + +globalThis.xloc = globalThis.xloc || __pineNS({ bar_index: "bar_index", bar_time: "bar_time" }); + +globalThis.yloc = globalThis.yloc || __pineNS({ price: "price", abovebar: "abovebar", belowbar: "belowbar" }); + +globalThis.extend = globalThis.extend || __pineNS({ none: "none", left: "left", right: "right", both: "both" }); + +globalThis.order = globalThis.order || __pineNS({ ascending: "ascending", descending: "descending" }); + +globalThis.alert = globalThis.alert || Object.assign(function() { return null; }, { freq_once_per_bar: "once_per_bar", freq_once_per_bar_close: "once_per_bar_close", freq_all: "all" }); + +globalThis.dayofmonth = globalThis.dayofmonth || function(t) { return new Date(t || 0).getUTCDate(); }; + +globalThis.plot = globalThis.plot || __pineNS({ style_line: "line", style_linebr: "linebr", style_stepline: "stepline", style_histogram: "histogram", style_cross: "cross", style_area: "area", style_areabr: "areabr", style_columns: "columns", style_circles: "circles" }); + +globalThis.int = globalThis.int || function(x) { return x == null ? null : Math.trunc(Number(x)); }; + +globalThis.float = globalThis.float || function(x) { return x == null ? null : Number(x); }; + +globalThis.bool = globalThis.bool || function(x) { return Boolean(x); }; + +globalThis.string = globalThis.string || function(x) { return x == null ? null : String(x); }; + +globalThis.dayofweek = globalThis.dayofweek || __pineNS({ sunday: 1, monday: 2, tuesday: 3, wednesday: 4, thursday: 5, friday: 6, saturday: 7 }); + +globalThis.str = globalThis.str || __pineNS({}); + +pinescript.math = globalThis.math; + +pinescript.ta = globalThis.ta; + +pinescript.str = globalThis.str; + +pinescript.array = __pineNS(globalThis.array); + + pinescript.color = { hex: function(s) { @@ -1190,10 +1670,10 @@ pinescript.table = { }; -// Input parameters +// Script input parameters and their defaults -// Main script logic +// The transpiled script logic, called once per bar function main() { @@ -1215,19 +1695,19 @@ function main() { let atrLenTooltip = "Controls how ATR is calculated.nnATR measures average recent candle movement (volatility).nHigher value = smoother, slower adaptation.nLower value = faster, more reactive adaptation."; let breakTolTooltip = "How far price must move through a trendline before the script treats it as a real break.nHigher value reduces false breaks from small wicks/noise."; let maxRelevanceTooltip = "Keeps selected trendlines close enough to current price.nnIf a candidate line is too far from current price (in ATR units), it is ignored.nLower value = more actionable nearby lines.nHigher value = allows farther/older lines."; - let pivotLen = input.int(10, "Pivot Length", ({ minval: 2, maxval: 120, group: GROUP_ENGINE })); - let lookbackBars = input.int(1000, "Lookback Bars", ({ minval: 150, maxval: 4000, group: GROUP_ENGINE })); - let atrLen = input.int(100, "ATR Length", ({ minval: 10, maxval: 500, group: GROUP_ENGINE, tooltip: atrLenTooltip })); - let maxViolations = input.int(2, "Max Violations", ({ minval: 0, maxval: 6, group: GROUP_FILTER })); - let touchTolATR = input.float(0.3, "Touch Tolerance (ATR)", ({ step: 0.05, minval: 0.05, maxval: 2, group: GROUP_FILTER })); - let maxSlopeDeg = input.float(6, "Max Slope Angle", ({ step: 1, minval: 1, maxval: 30, group: GROUP_FILTER })); - let maxRelevanceATR = input.float(8, "Max Relevance Distance (ATR)", ({ step: 0.5, minval: 1, maxval: 30, group: GROUP_FILTER, tooltip: maxRelevanceTooltip })); - let extendBars = input.int(60, "Line Extend (Bars)", ({ minval: 10, maxval: 300, group: GROUP_VISUAL })); - let lineWidth = input.int(2, "Line Width", ({ minval: 1, maxval: 4, group: GROUP_VISUAL })); - let showFill = input.bool(true, "Show Fill Between Lines", ({ group: GROUP_VISUAL })); - let supportColor = input.color(pinescript.color.new(pinescript.color.hex("#20A56C"), 0), "Support Color", ({ group: GROUP_VISUAL })); - let resistColor = input.color(pinescript.color.new(pinescript.color.hex("#C24545"), 0), "Resistance Color", ({ group: GROUP_VISUAL })); - let neutralColor = input.color(pinescript.color.new(pinescript.color.hex("#607D8B"), 0), "Fill Base Color", ({ group: GROUP_VISUAL })); + let pivotLen = pinescript.inputInt(10, "Pivot Length", ({ minval: 2, maxval: 120, group: GROUP_ENGINE })); + let lookbackBars = pinescript.inputInt(1000, "Lookback Bars", ({ minval: 150, maxval: 4000, group: GROUP_ENGINE })); + let atrLen = pinescript.inputInt(100, "ATR Length", ({ minval: 10, maxval: 500, group: GROUP_ENGINE, tooltip: atrLenTooltip })); + let maxViolations = pinescript.inputInt(2, "Max Violations", ({ minval: 0, maxval: 6, group: GROUP_FILTER })); + let touchTolATR = pinescript.inputFloat(0.3, "Touch Tolerance (ATR)", ({ step: 0.05, minval: 0.05, maxval: 2, group: GROUP_FILTER })); + let maxSlopeDeg = pinescript.inputFloat(6, "Max Slope Angle", ({ step: 1, minval: 1, maxval: 30, group: GROUP_FILTER })); + let maxRelevanceATR = pinescript.inputFloat(8, "Max Relevance Distance (ATR)", ({ step: 0.5, minval: 1, maxval: 30, group: GROUP_FILTER, tooltip: maxRelevanceTooltip })); + let extendBars = pinescript.inputInt(60, "Line Extend (Bars)", ({ minval: 10, maxval: 300, group: GROUP_VISUAL })); + let lineWidth = pinescript.inputInt(2, "Line Width", ({ minval: 1, maxval: 4, group: GROUP_VISUAL })); + let showFill = pinescript.inputBool(true, "Show Fill Between Lines", ({ group: GROUP_VISUAL })); + let supportColor = pinescript.inputColor(pinescript.color.new(pinescript.color.hex("#20A56C"), 0), "Support Color", ({ group: GROUP_VISUAL })); + let resistColor = pinescript.inputColor(pinescript.color.new(pinescript.color.hex("#C24545"), 0), "Resistance Color", ({ group: GROUP_VISUAL })); + let neutralColor = pinescript.inputColor(pinescript.color.new(pinescript.color.hex("#607D8B"), 0), "Fill Base Color", ({ group: GROUP_VISUAL })); let FAST_LOAD_BUFFER_BARS = 100; let BAR_SCAN_WINDOW = 250; let BAR_SCAN_STEP = 5; @@ -1289,7 +1769,7 @@ function main() { let safeAtr = pinescript.max(atrValue, syminfo.mintick); let touchTol = (safeAtr * touchTolMult); let breakTol = (safeAtr * breakTolMult); - let maxSlopeAtrPerBar = math.tan(((maxDeg * pinescript.math.pi) / 180)); + let maxSlopeAtrPerBar = pinescript.tan(((maxDeg * pinescript.math.pi) / 180)); let maxRelDist = (safeAtr * maxRelAtrMult); if ((n >= needTouches)) { let localBest = -10000000000; @@ -1365,7 +1845,7 @@ function main() { let off = 0; while ((off <= scanLen)) { let yBar = (p1Price + (slope * ((bar_index - off) - p1Idx))); - let barBreak = (support ? (pinescript.offset(low, off) < (yBar - breakTol)) : (pinescript.offset(high, off) > (yBar + breakTol))); + let barBreak = (support ? (pinescript.hist(0, low, off) < (yBar - breakTol)) : (pinescript.hist(1, high, off) > (yBar + breakTol))); barSamples = (barSamples + 1); if (barBreak) { barBreaks = (barBreaks + 1); @@ -1383,7 +1863,7 @@ function main() { if ((barBreakRatio > maxBarBreakRatio)) { finalViol = ((finalViol + allowViol) + 1); } else { - finalViol = (finalViol + int(math.floor((barBreakRatio * 2)))); + finalViol = (finalViol + int(pinescript.floor((barBreakRatio * 2)))); } if ((finalViol <= allowViol)) { let score = preScore; @@ -1417,7 +1897,7 @@ function main() { if (state.supportLine === undefined) state.supportLine = pinescript.lineNew(null, null, null, null, ({ xloc: xloc.bar_index })); if (state.resistanceLine === undefined) state.resistanceLine = pinescript.lineNew(null, null, null, null, ({ xloc: xloc.bar_index })); if (state.channelFill === undefined) state.channelFill = linefill.new(state.supportLine, state.resistanceLine, pinescript.color.new(neutralColor, 95)); - let atrValue = ta.atr(atrLen); + let atrValue = pinescript.atr(atrLen); let requiredHistoryBars = (((lookbackBars + BAR_SCAN_WINDOW) + (pivotLen * 3)) + FAST_LOAD_BUFFER_BARS); let processStartBar = pinescript.max(0, (last_bar_index - requiredHistoryBars)); let processEngine = (bar_index >= processStartBar); @@ -1433,8 +1913,8 @@ function main() { f_trim_lookback(state.lowPivotIdxs, state.lowPivotPrices, lookbackBars); f_trim_lookback(state.highPivotIdxs, state.highPivotPrices, lookbackBars); } - let [supValid, supX1, supY1, supX2, supY2, supScore] = f_find_best_line(state.lowPivotIdxs, state.lowPivotPrices, true, atrValue, lookbackBars, minAnchorBars, minTouches, maxViolations, touchTolATR, breakTolATR, maxSlopeDeg, maxRelevanceATR, BAR_SCAN_WINDOW, BAR_SCAN_STEP, maxBarViolRatio); - let [resValid, resX1, resY1, resX2, resY2, resScore] = f_find_best_line(state.highPivotIdxs, state.highPivotPrices, false, atrValue, lookbackBars, minAnchorBars, minTouches, maxViolations, touchTolATR, breakTolATR, maxSlopeDeg, maxRelevanceATR, BAR_SCAN_WINDOW, BAR_SCAN_STEP, maxBarViolRatio); + let [supValid, supX1, supY1, supX2, supY2, supScore] = pinescript.unpack(f_find_best_line(state.lowPivotIdxs, state.lowPivotPrices, true, atrValue, lookbackBars, minAnchorBars, minTouches, maxViolations, touchTolATR, breakTolATR, maxSlopeDeg, maxRelevanceATR, BAR_SCAN_WINDOW, BAR_SCAN_STEP, maxBarViolRatio), 6); + let [resValid, resX1, resY1, resX2, resY2, resScore] = pinescript.unpack(f_find_best_line(state.highPivotIdxs, state.highPivotPrices, false, atrValue, lookbackBars, minAnchorBars, minTouches, maxViolations, touchTolATR, breakTolATR, maxSlopeDeg, maxRelevanceATR, BAR_SCAN_WINDOW, BAR_SCAN_STEP, maxBarViolRatio), 6); let supRight = null; if (supValid) { supRight = f_line_y(supX1, supY1, supX2, supY2, (bar_index + extendBars)); @@ -1444,25 +1924,25 @@ function main() { resRight = f_line_y(resX1, resY1, resX2, resY2, (bar_index + extendBars)); } if (supValid) { - line.set_xy1(state.supportLine, supX1, supY1); - line.set_xy2(state.supportLine, (bar_index + extendBars), supRight); + pinescript.lineSetXY(state.supportLine, supX1, supY1); + pinescript.lineSetXY(state.supportLine, (bar_index + extendBars), supRight); line.set_color(state.supportLine, supportColor); line.set_width(state.supportLine, lineWidth); line.set_style(state.supportLine, line.style_solid); } else { - line.set_xy1(state.supportLine, bar_index, close); - line.set_xy2(state.supportLine, (bar_index + 1), close); + pinescript.lineSetXY(state.supportLine, bar_index, close); + pinescript.lineSetXY(state.supportLine, (bar_index + 1), close); line.set_color(state.supportLine, pinescript.color.new(supportColor, 100)); } if (resValid) { - line.set_xy1(state.resistanceLine, resX1, resY1); - line.set_xy2(state.resistanceLine, (bar_index + extendBars), resRight); + pinescript.lineSetXY(state.resistanceLine, resX1, resY1); + pinescript.lineSetXY(state.resistanceLine, (bar_index + extendBars), resRight); line.set_color(state.resistanceLine, resistColor); line.set_width(state.resistanceLine, lineWidth); line.set_style(state.resistanceLine, line.style_solid); } else { - line.set_xy1(state.resistanceLine, bar_index, close); - line.set_xy2(state.resistanceLine, (bar_index + 1), close); + pinescript.lineSetXY(state.resistanceLine, bar_index, close); + pinescript.lineSetXY(state.resistanceLine, (bar_index + 1), close); line.set_color(state.resistanceLine, pinescript.color.new(resistColor, 100)); } if (((showFill && supValid) && resValid)) { @@ -1477,7 +1957,67 @@ function main() { } -// Export for use -export { main +// The bar-by-bar engine: runs main() once per bar over the dataset. +function run(data, options = {}) { + data = data || {}; + const inClose = (data.close || []).map(Number); + const n = inClose.length; + const inOpen = (data.open || inClose).map(Number); + const inHigh = (data.high || inClose).map(Number); + const inLow = (data.low || inClose).map(Number); + const inVol = (data.volume || new Array(n).fill(0)).map(Number); + const inTime = (data.time || inClose.map((_, i) => i)).map(Number); + + // Reset all persistent and per-run state so repeated runs are independent. + globalThis.__pineState = {}; + globalThis.__pineRuntime = { plots: {}, plotshapes: {}, alerts: [], bars: n, inputs: options.inputs || {} }; + pinescript.__rt = {}; + pinescript.__bar = 0; + + // Growing OHLCV windows: each is a SeriesArray that gains one element per bar, + // so window built-ins like sma and highest see history up to the current bar. + const O = pinescript.asSeries([]), H = pinescript.asSeries([]), L = pinescript.asSeries([]); + const C = pinescript.asSeries([]), V = pinescript.asSeries([]), T = pinescript.asSeries([]); + const HL2 = pinescript.asSeries([]), HLC3 = pinescript.asSeries([]), OHLC4 = pinescript.asSeries([]); + globalThis.open = O; globalThis.high = H; globalThis.low = L; globalThis.close = C; + globalThis.volume = V; globalThis.time = T; + globalThis.hl2 = HL2; globalThis.hlc3 = HLC3; globalThis.ohlc4 = OHLC4; + globalThis.syminfo = globalThis.syminfo || { tickerid: 'SYNTHETIC', ticker: 'SYNTHETIC', mintick: 0.01 }; + + const rt = globalThis.__pineRuntime; + for (let i = 0; i < n; i++) { + O.push(inOpen[i]); H.push(inHigh[i]); L.push(inLow[i]); C.push(inClose[i]); V.push(inVol[i]); T.push(inTime[i]); + HL2.push((inHigh[i] + inLow[i]) / 2); + HLC3.push((inHigh[i] + inLow[i] + inClose[i]) / 3); + OHLC4.push((inOpen[i] + inHigh[i] + inLow[i] + inClose[i]) / 4); + + pinescript.__bar = i; + rt.__barIndex = i; + rt.__plotIdx = 0; + rt.__shapeIdx = 0; + globalThis.bar_index = i; + globalThis.last_bar_index = n - 1; + globalThis.barstate = { + isfirst: i === 0, islast: i === n - 1, isrealtime: false, ishistory: true, + isconfirmed: true, isnew: true, islastconfirmedhistory: i === n - 1, + }; + + main(); + } + + // Backfill skipped bars so every output series has exactly n entries. + for (const k of Object.keys(rt.plots)) { + const d = rt.plots[k].data; + for (let i = 0; i < n; i++) if (d[i] === undefined) d[i] = null; + } + for (const k of Object.keys(rt.plotshapes)) { + const d = rt.plotshapes[k].data; + for (let i = 0; i < n; i++) if (d[i] === undefined) d[i] = false; + } + return rt; +} + + +export { main, run }; diff --git a/converted/CE_1M_3M_Buy_Sell_Hold.js b/converted/CE_1M_3M_Buy_Sell_Hold.js index ab50deb..adfd82a 100644 --- a/converted/CE_1M_3M_Buy_Sell_Hold.js +++ b/converted/CE_1M_3M_Buy_Sell_Hold.js @@ -1,12 +1,63 @@ -// PineScript to JavaScript Transpiled Code +// Auto-generated JavaScript from PineScript source -// Generated automatically +// Do not edit by hand -- re-run the converter instead -// Built-in PineScript functions +// All the built-in PineScript functions that scripts depend on at runtime const pinescript = { + __scalar: function(v) { + if (v === null || v === undefined) return v; + if (typeof v === 'object' && typeof v.valueOf === 'function') { + const s = v.valueOf(); + if (typeof s === 'number' || typeof s === 'boolean' || s === null) return s; + } + return v; + }, + series: function(id, value) { + const rt = this.__rt || (this.__rt = {}); + const store = rt.series || (rt.series = {}); + let buf = store[id]; + if (!buf) buf = store[id] = []; + buf[this.__bar | 0] = this.__scalar(value); + return buf; + }, + hist: function(id, value, n) { + const buf = this.series(id, value); + const idx = (this.__bar | 0) - (n | 0); + return idx >= 0 && idx < buf.length ? buf[idx] : null; + }, + time: function(timeframe, session) { + const t = globalThis.time; + if (t && typeof t.valueOf === 'function') return t.valueOf(); + return Array.isArray(t) ? t[t.length - 1] : t; + }, + sign: function(x) { + if (x === null || x === undefined || (typeof x === 'number' && isNaN(x))) return null; + return Math.sign(Number(x)); + }, + unpack: function(value, count) { + if (Array.isArray(value)) return value; + if (value != null && typeof value[Symbol.iterator] === 'function') return Array.from(value); + return new Array(count || 0).fill(null); + }, + alertcondition: function(condition, ...rest) { + if (globalThis.__pineRuntime) { + globalThis.__pineRuntime.alerts.push({ condition, args: rest }); + } + return null; + }, + barcolor: function(color) { return null; }, + bgcolor: function(color, title, editable, showLast) { + return null; + }, + fill: function(series1, series2, color, title, editable) { + return null; + }, + hline: function(_price, _title, _opts) { + return null; + }, sma: function(series, length) { if (series === null || series === undefined) return null; if (!Array.isArray(series)) series = [series]; @@ -84,6 +135,13 @@ const pinescript = { } return weightSum > 0 ? sum / weightSum : null; }, + swma: function(series) { + if (series === null || series === undefined) return null; + if (!Array.isArray(series)) series = [series]; + if (series.length < 4) return null; + const s = series.slice(-4); + return (s[0] * 1 + s[1] * 2 + s[2] * 2 + s[3] * 1) / 6; + }, atr: function(high, low, close, length = 14) { if (!Array.isArray(high) || !Array.isArray(low) || !Array.isArray(close)) return null; if (high.length < length + 1 || low.length < length + 1 || close.length < length + 1) return null; @@ -114,6 +172,53 @@ const pinescript = { const atr = builtins.get('atr')(high, low, close, length); return { middle: sma, upper: sma + mult * atr, lower: sma - mult * atr }; }, + supertrend: function(factor, atrPeriod, high, low, close) { + if (!Array.isArray(high) || !Array.isArray(low) || !Array.isArray(close)) return [null, null]; + if (high.length < atrPeriod + 1) return [null, null]; + const atrVal = builtins.get('atr')(high, low, close, atrPeriod); + if (atrVal === null) return [null, null]; + const lastIdx = close.length - 1; + const hl2 = (high[lastIdx] + low[lastIdx]) / 2; + const upperBand = hl2 + factor * atrVal; + const lowerBand = hl2 - factor * atrVal; + // Determine direction: 1 means downtrend (price below band), -1 means uptrend + const direction = close[lastIdx] > upperBand ? -1 : close[lastIdx] < lowerBand ? 1 : -1; + const supertrendValue = direction === -1 ? lowerBand : upperBand; + return [supertrendValue, direction]; + }, + ta_supertrend: function(factor, atrPeriod) { + const H = globalThis.high; + const L = globalThis.low; + const C = globalThis.close; + return builtins.get('supertrend')(factor, atrPeriod, H, L, C); + }, + dmi: function(diLength, adxSmoothing, high, low, close) { + if (!Array.isArray(high) || !Array.isArray(low) || !Array.isArray(close)) return { plusDI: null, minusDI: null, adx: null }; + if (high.length < diLength + 1) return { plusDI: null, minusDI: null, adx: null }; + let plusDMSum = 0, minusDMSum = 0, trSum = 0; + for (let i = 1; i <= diLength; i++) { + const idx = high.length - 1 - diLength + i; + const upMove = high[idx] - high[idx - 1]; + const downMove = low[idx - 1] - low[idx]; + plusDMSum += (upMove > downMove && upMove > 0) ? upMove : 0; + minusDMSum += (downMove > upMove && downMove > 0) ? downMove : 0; + const tr = Math.max( + high[idx] - low[idx], + Math.abs(high[idx] - close[idx - 1]), + Math.abs(low[idx] - close[idx - 1]) + ); + trSum += tr; + } + const plusDI = trSum > 0 ? (plusDMSum / trSum) * 100 : 0; + const minusDI = trSum > 0 ? (minusDMSum / trSum) * 100 : 0; + const diSum = plusDI + minusDI; + const dx = diSum > 0 ? Math.abs(plusDI - minusDI) / diSum * 100 : 0; + return { plusDI, minusDI, adx: dx }; + }, + adx: function(diLength, adxSmoothing, high, low, close) { + const result = builtins.get('dmi')(diLength, adxSmoothing, high, low, close); + return result ? result.adx : null; + }, rsi: function(close, length = 14) { if (close === null || close === undefined) return null; if (!Array.isArray(close)) close = [close]; @@ -171,9 +276,7 @@ const pinescript = { return { macd: macdLine, signal: signalLine, histogram: histogram }; }, cci: function(a, b, c, d) { - // Pine signatures: - // - ta.cci(source, length) - // - ta.cci(high, low, close, length) + // Supports both ta.cci(source, length) and ta.cci(high, low, close, length) signatures let source; let length; if (Array.isArray(a) && typeof b === 'number' && c === undefined) { @@ -304,6 +407,15 @@ const pinescript = { stdev: function(source, length) { return Math.sqrt(builtins.get('variance')(source, length)); }, + median: function(source, length) { + if (!source || source.length < length) return null; + const sorted = [...source.slice(-length)].sort((a, b) => a - b); + const mid = Math.floor(sorted.length / 2); + return sorted.length % 2 !== 0 ? sorted[mid] : (sorted[mid - 1] + sorted[mid]) / 2; + }, + ta_median: function(source, length) { + return builtins.get('median')(source, length); + }, hl2: function(high, low) { return high && low ? (high[high.length - 1] + low[low.length - 1]) / 2 : null; }, @@ -420,6 +532,24 @@ const pinescript = { return (series1[i - 1] < series2[i - 1] && series1[i] >= series2[i]) || (series1[i - 1] > series2[i - 1] && series1[i] <= series2[i]); }, + crossover: function(series1, series2) { + if (series1 === null || series1 === undefined) return false; + if (series2 === null || series2 === undefined) return false; + if (!Array.isArray(series1)) series1 = [series1]; + if (!Array.isArray(series2)) series2 = [series2]; + if (series1.length < 2 || series2.length < 2) return false; + const i = series1.length - 1; + return series1[i - 1] < series2[i - 1] && series1[i] >= series2[i]; + }, + crossunder: function(series1, series2) { + if (series1 === null || series1 === undefined) return false; + if (series2 === null || series2 === undefined) return false; + if (!Array.isArray(series1)) series1 = [series1]; + if (!Array.isArray(series2)) series2 = [series2]; + if (series1.length < 2 || series2.length < 2) return false; + const i = series1.length - 1; + return series1[i - 1] > series2[i - 1] && series1[i] <= series2[i]; + }, offset: function(series, offset) { if (!Array.isArray(series)) return null; const idx = series.length - 1 - offset; @@ -500,12 +630,14 @@ const pinescript = { return builtins.get('nz')(value, replacement); }, plot: function(series, title = '', color = null, linewidth = 1) { - globalThis.__pineRuntime.plots.push({ - series, - title, - color, - linewidth - }); + const rt = globalThis.__pineRuntime; + if (!rt) return series; + const bar = rt.__barIndex | 0; + const ord = (rt.__plotIdx = (rt.__plotIdx | 0) + 1) - 1; + const key = (title && String(title)) || ('plot_' + ord); + let p = rt.plots[key]; + if (!p) p = rt.plots[key] = { title: key, color, linewidth, data: [] }; + p.data[bar] = this.__scalar(series); return series; }, lineNew: function(x1, y1, x2, y2, opts = {}) { @@ -514,20 +646,72 @@ const pinescript = { lineDelete: function(l) { return null; }, - labelNew: function(...args) { - if (globalThis.__pineRuntime) { - globalThis.__pineRuntime.labels = globalThis.__pineRuntime.labels || []; - globalThis.__pineRuntime.labels.push({ args }); - } - return { args }; + lineSetXY: function(line, point, x, y) { + if (!line) return null; + if (point === 0 || point === 'x1') { line.x1 = x; line.y1 = y; } + else { line.x2 = x; line.y2 = y; } + return line; + }, + lineGetX: function(line, point) { + if (!line) return null; + return point === 0 || point === 'x1' ? line.x1 : line.x2; + }, + lineGetY: function(line, point) { + if (!line) return null; + return point === 0 || point === 'y1' ? line.y1 : line.y2; + }, + labelNew: function(x, y, text = '', opts = {}) { + return { x, y, text, opts, _type: 'label' }; }, labelDelete: function(l) { return null; }, + labelSetText: function(label, text) { + if (!label) return null; + label.text = text; + return label; + }, + labelGetText: function(label) { + if (!label) return ''; + return label.text || ''; + }, + boxNew: function(left, top, right, bottom, opts = {}) { + return { left, top, right, bottom, opts, _type: 'box' }; + }, + boxDelete: function(box) { + return null; + }, + boxSetLeftTop: function(box, left, top) { + if (!box) return null; + box.left = left; + box.top = top; + return box; + }, + boxSetRightBottom: function(box, right, bottom) { + if (!box) return null; + box.right = right; + box.bottom = bottom; + return box; + }, + polylineNew: function(points, opts = {}) { + return { points: points || [], opts, _type: 'polyline' }; + }, + polylineDelete: function(poly) { + return null; + }, plotshape: function(condition, ...rest) { - if (globalThis.__pineRuntime) { - globalThis.__pineRuntime.plotshapes.push({ condition, args: rest }); + const rt = globalThis.__pineRuntime; + if (!rt) return condition; + const bar = rt.__barIndex | 0; + const ord = (rt.__shapeIdx = (rt.__shapeIdx | 0) + 1) - 1; + let key = 'shape_' + ord; + for (const r of rest) { + if (typeof r === 'string') { key = r; break; } + if (r && typeof r === 'object' && r.title) { key = String(r.title); break; } } + let s = rt.plotshapes[key]; + if (!s) s = rt.plotshapes[key] = { title: key, data: [] }; + s.data[bar] = !!this.__scalar(condition); return condition; }, indicator: function(title, shorttitle, overlay, opts) { @@ -542,27 +726,12 @@ const pinescript = { } return null; }, - alertcondition: function(condition, ...rest) { - if (globalThis.__pineRuntime) { - globalThis.__pineRuntime.alerts.push({ condition, args: rest }); - } - return null; - }, plotbar: function(open, high, low, close, title, color, editable, showLast) { return { open, high, low, close }; }, plotcandle: function(open, high, low, close, title, color, wickColor, borderColor, editable, showLast) { return { open, high, low, close }; }, - hline: function(_price, _title, _opts) { - return null; - }, - bgcolor: function(color, title, editable, showLast) { - return null; - }, - fill: function(series1, series2, color, title, editable) { - return null; - }, alert: function(condition, message, frequency) { console.log('Alert:', condition ? message : 'Condition not met'); return condition; @@ -828,6 +997,9 @@ const pinescript = { arrayIndexOf: function(arr, value) { return arr ? arr.indexOf(value) : -1; }, + arrayLastIndexOf: function(arr, value) { + return arr ? arr.lastIndexOf(value) : -1; + }, arraySum: function(arr) { return arr ? arr.reduce((a, b) => a + b, 0) : 0; }, @@ -840,6 +1012,42 @@ const pinescript = { arrayMax: function(arr) { return arr && arr.length > 0 ? Math.max(...arr) : null; }, + arrayStdev: function(arr) { + if (!arr || arr.length === 0) return null; + const mean = arr.reduce((a, b) => a + b, 0) / arr.length; + const variance = arr.reduce((sum, val) => sum + Math.pow(val - mean, 2), 0) / arr.length; + return Math.sqrt(variance); + }, + arrayVariance: function(arr) { + if (!arr || arr.length === 0) return null; + const mean = arr.reduce((a, b) => a + b, 0) / arr.length; + return arr.reduce((sum, val) => sum + Math.pow(val - mean, 2), 0) / arr.length; + }, + arrayCovariance: function(arr1, arr2) { + if (!arr1 || !arr2 || arr1.length === 0 || arr1.length !== arr2.length) return null; + const mean1 = arr1.reduce((a, b) => a + b, 0) / arr1.length; + const mean2 = arr2.reduce((a, b) => a + b, 0) / arr2.length; + let cov = 0; + for (let i = 0; i < arr1.length; i++) { + cov += (arr1[i] - mean1) * (arr2[i] - mean2); + } + return cov / arr1.length; + }, + mode: function(arr) { + if (!arr || arr.length === 0) return null; + const counts = new Map(); + let maxCount = 0; + let modeVal = arr[0]; + for (const val of arr) { + const c = (counts.get(val) || 0) + 1; + counts.set(val, c); + if (c > maxCount) { + maxCount = c; + modeVal = val; + } + } + return modeVal; + }, strLength: function(str) { return str ? str.length : 0; }, @@ -895,6 +1103,84 @@ const pinescript = { strReverse: function(str) { return str ? str.split('').reverse().join('') : ''; }, + strFormat: function(formatStr, ...args) { + if (!formatStr) return ''; + return formatStr.replace(/\{(\d+)\}/g, (match, idx) => { + const i = parseInt(idx, 10); + return i < args.length ? String(args[i]) : match; + }); + }, + input: function(defval, title, tooltip, inline, group) { + return defval; + }, + inputInt: function(defval = 0, title, minval, maxval, step, tooltip, inline, group) { + return defval; + }, + inputFloat: function(defval = 0.0, title, minval, maxval, step, tooltip, inline, group) { + return defval; + }, + inputBool: function(defval = false, title, tooltip, inline, group) { + return defval; + }, + inputString: function(defval = '', title, options, tooltip, inline, group) { + return defval; + }, + inputSource: function(defval = null, title, tooltip, inline, group) { + return defval; + }, + inputColor: function(defval = '#000000', title, tooltip, inline, group) { + return defval; + }, + inputTime: function(defval = 0, title, tooltip, inline, group) { + return defval; + }, + mathSign: function(value) { + return Math.sign(value); + }, + mathAvg: function(...args) { + const valid = args.filter(v => v !== null && v !== undefined && !isNaN(v)); + return valid.length > 0 ? valid.reduce((a, b) => a + b, 0) / valid.length : null; + }, + mathSum: function(series, length) { + if (!series || series.length < length) return null; + return series.slice(-length).reduce((a, b) => a + b, 0); + }, + mathRandom: function(min = 0, max = 1) { + return min + Math.random() * (max - min); + }, + mathTodegrees: function(radians) { + return radians * (180 / Math.PI); + }, + mathToradians: function(degrees) { + return degrees * (Math.PI / 180); + }, + colorT: function(color, transparency) { + if (transparency !== undefined) { + return { color, transparency }; + } + if (color && typeof color === 'object' && 'transparency' in color) { + return color.transparency; + } + return 0; + }, + tableCellSetText: function(table, column, row, text) { + return null; + }, + tableCellSetBgcolor: function(table, column, row, bgcolor) { + return null; + }, + tableMergeCells: function(table, startColumn, startRow, endColumn, endRow) { + return null; + }, + tableDelete: function(table) { + return null; + }, + tableClear: function(table, startColumn, startRow, endColumn, endRow) { + return null; + }, + maxBarsBack: function(series, length) { + return null; + }, pi: 3.141592653589793, e: 2.718281828459045, true: true, @@ -905,8 +1191,7 @@ const pinescript = { const info = { ticker: 'AAPL', tickerid: 'NASDAQ:AAPL', prefix: 'NASDAQ', root: 'AAPL', suffix: '' }; return info[type] || ''; }, - time: 1771178051144, - timenow: 1771178051144, + timenow: 1781574526762, barstate: "LAST", dividends: {}, splits: {}, @@ -923,7 +1208,7 @@ const pinescript = { return null; }, tr: function(high, low, close) { - // Pine signature: ta.tr(true) + // Handles both the boolean shorthand ta.tr(true) and explicit high/low/close arguments if (typeof high === 'boolean') { const H = globalThis.high; const L = globalThis.low; @@ -934,7 +1219,7 @@ const pinescript = { return Math.max(H[i] - L[i], Math.abs(H[i] - prevClose), Math.abs(L[i] - prevClose)); } - // Fallback: treat inputs as scalars for current bar + // When arrays are passed, compute true range from the last bar using the previous close if (Array.isArray(close)) { const prevClose = close[close.length - 2] || close[0]; const currHigh = Array.isArray(high) ? high[high.length - 1] : high; @@ -945,6 +1230,92 @@ const pinescript = { return Math.max(high - low, Math.abs(high - close), Math.abs(low - close)); }, + arrayFirst: function(arr) { + if (!arr || arr.length === 0) return null; + return arr[0]; + }, + arrayLast: function(arr) { + if (!arr || arr.length === 0) return null; + return arr[arr.length - 1]; + }, + arrayJoin: function(arr, separator = ',') { + if (!arr) return ''; + return arr.join(separator); + }, + arrayConcat: function(arr1, arr2) { + if (!arr1) return arr2 || []; + if (!arr2) return arr1; + return arr1.concat(arr2); + }, + arrayCopy: function(arr) { + if (!arr) return []; + return [...arr]; + }, + arrayBinarySearch: function(arr, value) { + if (!arr || arr.length === 0) return -1; + let lo = 0, hi = arr.length - 1; + while (lo <= hi) { + const mid = (lo + hi) >>> 1; + if (arr[mid] === value) return mid; + if (arr[mid] < value) lo = mid + 1; + else hi = mid - 1; + } + return -1; + }, + arrayRange: function(arr) { + if (!arr || arr.length === 0) return 0; + return Math.max(...arr) - Math.min(...arr); + }, + arrayMedian: function(arr) { + if (!arr || arr.length === 0) return null; + const sorted = [...arr].sort((a, b) => a - b); + const mid = Math.floor(sorted.length / 2); + return sorted.length % 2 !== 0 ? sorted[mid] : (sorted[mid - 1] + sorted[mid]) / 2; + }, + arrayMode: function(arr) { + if (!arr || arr.length === 0) return null; + const freq = {}; + let maxCount = 0; + let mode = arr[0]; + for (const v of arr) { + freq[v] = (freq[v] || 0) + 1; + if (freq[v] > maxCount) { maxCount = freq[v]; mode = v; } + } + return mode; + }, + arrayPercentile: function(arr, percentile) { + if (!arr || arr.length === 0) return null; + const sorted = [...arr].sort((a, b) => a - b); + const idx = (percentile / 100) * (sorted.length - 1); + const lo = Math.floor(idx); + const hi = Math.ceil(idx); + if (lo === hi) return sorted[lo]; + return sorted[lo] + (sorted[hi] - sorted[lo]) * (idx - lo); + }, + arrayPercentileLinearInterpolation: function(arr, percentile) { + return builtins.get('arrayPercentile')(arr, percentile); + }, + arrayPercentileNearestRank: function(arr, percentile) { + if (!arr || arr.length === 0) return null; + const sorted = [...arr].sort((a, b) => a - b); + const idx = Math.ceil((percentile / 100) * sorted.length) - 1; + return sorted[Math.max(0, idx)]; + }, + arrayAbs: function(arr) { + if (!arr) return []; + return arr.map(v => Math.abs(v)); + }, + arrayEvery: function(arr, callback) { + if (!arr) return false; + return arr.every(callback); + }, + arraySome: function(arr, callback) { + if (!arr) return false; + return arr.some(callback); + }, + requestFinancial: function() { + return null; + }, }; @@ -1101,6 +1472,115 @@ globalThis.barmerge = globalThis.barmerge || { }; +function __pineNS(real) { + + const base = real || {}; + + return new Proxy(base, { + + get(target, key) { + + if (key in target) return target[key]; + + if (typeof key === "symbol") return target[key]; + + // Unknown member: usable as a value (null), a constant, or a no-op function. + + const noop = function() { return null; }; + + noop.toString = () => ""; + + return noop; + + } + + }); + +} + +globalThis.ta = globalThis.ta || __pineNS({}); + +globalThis.math = globalThis.math || __pineNS({ pi: Math.PI, e: Math.E, phi: 1.618033988749895, rphi: 0.618033988749895 }); + +globalThis.chart = globalThis.chart || __pineNS({ point: __pineNS({}), bg_color: null, fg_color: null }); + +globalThis.line = globalThis.line || __pineNS({ style_solid: "solid", style_dashed: "dashed", style_dotted: "dotted" }); + +globalThis.box = globalThis.box || __pineNS({}); + +globalThis.label = globalThis.label || __pineNS({ style_label_down: "label_down", style_label_up: "label_up", style_none: "none" }); + +globalThis.polyline = globalThis.polyline || __pineNS({}); + +globalThis.linefill = globalThis.linefill || __pineNS({}); + +globalThis.matrix = globalThis.matrix || __pineNS({}); + +globalThis.map = globalThis.map || __pineNS({}); + +globalThis.session = globalThis.session || __pineNS({ regular: "regular", extended: "extended" }); + +globalThis.dayofweek = globalThis.dayofweek || __pineNS({ sunday: 1, monday: 2, tuesday: 3, wednesday: 4, thursday: 5, friday: 6, saturday: 7 }); + +globalThis.timeframe = __pineNS(Object.assign(globalThis.timeframe || {}, { period: (globalThis.timeframe && globalThis.timeframe.period) || "D", isintraday: false, isdaily: true, multiplier: 1 })); + +globalThis.request = __pineNS(globalThis.request || {}); + +globalThis.input = __pineNS(globalThis.input || {}); + +globalThis.adjustment = globalThis.adjustment || __pineNS({}); + +globalThis.earnings = globalThis.earnings || __pineNS({}); + +globalThis.dividends = globalThis.dividends || __pineNS({}); + +globalThis.splits = globalThis.splits || __pineNS({}); + +globalThis.currency = globalThis.currency || __pineNS({}); + +globalThis.display = globalThis.display || __pineNS({ none: "none", all: "all", pane: "pane", data_window: "data_window", status_line: "status_line", price_scale: "price_scale" }); + +globalThis.format = globalThis.format || __pineNS({ price: "price", volume: "volume", percent: "percent", mintick: "mintick", inherit: "inherit" }); + +globalThis.scale = globalThis.scale || __pineNS({ left: "left", right: "right", none: "none" }); + +globalThis.font = globalThis.font || __pineNS({ family_default: "default", family_monospace: "monospace" }); + +globalThis.xloc = globalThis.xloc || __pineNS({ bar_index: "bar_index", bar_time: "bar_time" }); + +globalThis.yloc = globalThis.yloc || __pineNS({ price: "price", abovebar: "abovebar", belowbar: "belowbar" }); + +globalThis.extend = globalThis.extend || __pineNS({ none: "none", left: "left", right: "right", both: "both" }); + +globalThis.order = globalThis.order || __pineNS({ ascending: "ascending", descending: "descending" }); + +globalThis.alert = globalThis.alert || Object.assign(function() { return null; }, { freq_once_per_bar: "once_per_bar", freq_once_per_bar_close: "once_per_bar_close", freq_all: "all" }); + +globalThis.dayofmonth = globalThis.dayofmonth || function(t) { return new Date(t || 0).getUTCDate(); }; + +globalThis.plot = globalThis.plot || __pineNS({ style_line: "line", style_linebr: "linebr", style_stepline: "stepline", style_histogram: "histogram", style_cross: "cross", style_area: "area", style_areabr: "areabr", style_columns: "columns", style_circles: "circles" }); + +globalThis.int = globalThis.int || function(x) { return x == null ? null : Math.trunc(Number(x)); }; + +globalThis.float = globalThis.float || function(x) { return x == null ? null : Number(x); }; + +globalThis.bool = globalThis.bool || function(x) { return Boolean(x); }; + +globalThis.string = globalThis.string || function(x) { return x == null ? null : String(x); }; + +globalThis.dayofweek = globalThis.dayofweek || __pineNS({ sunday: 1, monday: 2, tuesday: 3, wednesday: 4, thursday: 5, friday: 6, saturday: 7 }); + +globalThis.str = globalThis.str || __pineNS({}); + +pinescript.math = globalThis.math; + +pinescript.ta = globalThis.ta; + +pinescript.str = globalThis.str; + +pinescript.array = __pineNS(globalThis.array); + + pinescript.color = { hex: function(s) { @@ -1190,10 +1670,10 @@ pinescript.table = { }; -// Input parameters +// Script input parameters and their defaults -// Main script logic +// The transpiled script logic, called once per bar function main() { @@ -1212,13 +1692,13 @@ function main() { let mult = 2; let useClose = true; function f_ce_dir(_length, _mult, _useClose) { - let atr = (_mult * ta.atr(_length)); - let longStop = ((_useClose ? pinescript.highest(close, _length) : pinescript.highest(high, _length)) - atr); - let longStopPrev = pinescript.nz(pinescript.offset(longStop, 1), longStop); - longStop = ((pinescript.offset(close, 1) > longStopPrev) ? pinescript.max(longStop, longStopPrev) : longStop); - let shortStop = ((_useClose ? pinescript.lowest(close, _length) : pinescript.lowest(low, _length)) + atr); - let shortStopPrev = pinescript.nz(pinescript.offset(shortStop, 1), shortStop); - shortStop = ((pinescript.offset(close, 1) < shortStopPrev) ? pinescript.min(shortStop, shortStopPrev) : shortStop); + let atr = (_mult * pinescript.atr(_length)); + let longStop = ((_useClose ? pinescript.highest(pinescript.series(0, close), _length) : pinescript.highest(pinescript.series(1, high), _length)) - atr); + let longStopPrev = pinescript.nz(pinescript.hist(2, longStop, 1), longStop); + longStop = ((pinescript.hist(3, close, 1) > longStopPrev) ? pinescript.max(longStop, longStopPrev) : longStop); + let shortStop = ((_useClose ? pinescript.lowest(pinescript.series(4, close), _length) : pinescript.lowest(pinescript.series(5, low), _length)) + atr); + let shortStopPrev = pinescript.nz(pinescript.hist(6, shortStop, 1), shortStop); + shortStop = ((pinescript.hist(7, close, 1) < shortStopPrev) ? pinescript.min(shortStop, shortStopPrev) : shortStop); if (state.dir === undefined) state.dir = 1; state.dir = ((close > shortStopPrev) ? 1 : ((close < longStopPrev) ? -1 : state.dir)); return state.dir; @@ -1227,16 +1707,76 @@ function main() { let dir_3m = pinescript.requestSecurity(syminfo.tickerid, "3", f_ce_dir(length, mult, useClose), barmerge.gaps_off, barmerge.lookahead_off); let state_1 = 0; state_1 = (((dir_3m === 1) && (dir_1m === 1)) ? 1 : (((dir_3m === -1) && (dir_1m === -1)) ? -1 : 0)); - let buySignal = ((state_1 === 1) && (pinescript.offset(state_1, 1) !== 1)); - let sellSignal = ((state_1 === -1) && (pinescript.offset(state_1, 1) !== -1)); - let holdSignal = ((state_1 === 0) && (pinescript.offset(state_1, 1) !== 0)); + let buySignal = ((state_1 === 1) && (pinescript.hist(8, state_1, 1) !== 1)); + let sellSignal = ((state_1 === -1) && (pinescript.hist(9, state_1, 1) !== -1)); + let holdSignal = ((state_1 === 0) && (pinescript.hist(10, state_1, 1) !== 0)); pinescript.plotshape(buySignal, ({ title: "BUY", location: pinescript.location.belowbar, color: pinescript.color.green, style: pinescript.shape.labelup, text: "BUY" })); pinescript.plotshape(sellSignal, ({ title: "SELL", location: pinescript.location.abovebar, color: pinescript.color.red, style: pinescript.shape.labeldown, text: "SELL" })); pinescript.plotshape(holdSignal, ({ title: "HOLD", location: pinescript.location.bottom, color: pinescript.color.yellow, style: pinescript.shape.labelup, text: "HOLD" })); } -// Export for use -export { main +// The bar-by-bar engine: runs main() once per bar over the dataset. +function run(data, options = {}) { + data = data || {}; + const inClose = (data.close || []).map(Number); + const n = inClose.length; + const inOpen = (data.open || inClose).map(Number); + const inHigh = (data.high || inClose).map(Number); + const inLow = (data.low || inClose).map(Number); + const inVol = (data.volume || new Array(n).fill(0)).map(Number); + const inTime = (data.time || inClose.map((_, i) => i)).map(Number); + + // Reset all persistent and per-run state so repeated runs are independent. + globalThis.__pineState = {}; + globalThis.__pineRuntime = { plots: {}, plotshapes: {}, alerts: [], bars: n, inputs: options.inputs || {} }; + pinescript.__rt = {}; + pinescript.__bar = 0; + + // Growing OHLCV windows: each is a SeriesArray that gains one element per bar, + // so window built-ins like sma and highest see history up to the current bar. + const O = pinescript.asSeries([]), H = pinescript.asSeries([]), L = pinescript.asSeries([]); + const C = pinescript.asSeries([]), V = pinescript.asSeries([]), T = pinescript.asSeries([]); + const HL2 = pinescript.asSeries([]), HLC3 = pinescript.asSeries([]), OHLC4 = pinescript.asSeries([]); + globalThis.open = O; globalThis.high = H; globalThis.low = L; globalThis.close = C; + globalThis.volume = V; globalThis.time = T; + globalThis.hl2 = HL2; globalThis.hlc3 = HLC3; globalThis.ohlc4 = OHLC4; + globalThis.syminfo = globalThis.syminfo || { tickerid: 'SYNTHETIC', ticker: 'SYNTHETIC', mintick: 0.01 }; + + const rt = globalThis.__pineRuntime; + for (let i = 0; i < n; i++) { + O.push(inOpen[i]); H.push(inHigh[i]); L.push(inLow[i]); C.push(inClose[i]); V.push(inVol[i]); T.push(inTime[i]); + HL2.push((inHigh[i] + inLow[i]) / 2); + HLC3.push((inHigh[i] + inLow[i] + inClose[i]) / 3); + OHLC4.push((inOpen[i] + inHigh[i] + inLow[i] + inClose[i]) / 4); + + pinescript.__bar = i; + rt.__barIndex = i; + rt.__plotIdx = 0; + rt.__shapeIdx = 0; + globalThis.bar_index = i; + globalThis.last_bar_index = n - 1; + globalThis.barstate = { + isfirst: i === 0, islast: i === n - 1, isrealtime: false, ishistory: true, + isconfirmed: true, isnew: true, islastconfirmedhistory: i === n - 1, + }; + + main(); + } + + // Backfill skipped bars so every output series has exactly n entries. + for (const k of Object.keys(rt.plots)) { + const d = rt.plots[k].data; + for (let i = 0; i < n; i++) if (d[i] === undefined) d[i] = null; + } + for (const k of Object.keys(rt.plotshapes)) { + const d = rt.plotshapes[k].data; + for (let i = 0; i < n; i++) if (d[i] === undefined) d[i] = false; + } + return rt; +} + + +export { main, run }; diff --git a/converted/Crossing_Moving_Averages_with_ADX_Filter.js b/converted/Crossing_Moving_Averages_with_ADX_Filter.js index cecdd02..4b42adb 100644 --- a/converted/Crossing_Moving_Averages_with_ADX_Filter.js +++ b/converted/Crossing_Moving_Averages_with_ADX_Filter.js @@ -1,12 +1,63 @@ -// PineScript to JavaScript Transpiled Code +// Auto-generated JavaScript from PineScript source -// Generated automatically +// Do not edit by hand -- re-run the converter instead -// Built-in PineScript functions +// All the built-in PineScript functions that scripts depend on at runtime const pinescript = { + __scalar: function(v) { + if (v === null || v === undefined) return v; + if (typeof v === 'object' && typeof v.valueOf === 'function') { + const s = v.valueOf(); + if (typeof s === 'number' || typeof s === 'boolean' || s === null) return s; + } + return v; + }, + series: function(id, value) { + const rt = this.__rt || (this.__rt = {}); + const store = rt.series || (rt.series = {}); + let buf = store[id]; + if (!buf) buf = store[id] = []; + buf[this.__bar | 0] = this.__scalar(value); + return buf; + }, + hist: function(id, value, n) { + const buf = this.series(id, value); + const idx = (this.__bar | 0) - (n | 0); + return idx >= 0 && idx < buf.length ? buf[idx] : null; + }, + time: function(timeframe, session) { + const t = globalThis.time; + if (t && typeof t.valueOf === 'function') return t.valueOf(); + return Array.isArray(t) ? t[t.length - 1] : t; + }, + sign: function(x) { + if (x === null || x === undefined || (typeof x === 'number' && isNaN(x))) return null; + return Math.sign(Number(x)); + }, + unpack: function(value, count) { + if (Array.isArray(value)) return value; + if (value != null && typeof value[Symbol.iterator] === 'function') return Array.from(value); + return new Array(count || 0).fill(null); + }, + alertcondition: function(condition, ...rest) { + if (globalThis.__pineRuntime) { + globalThis.__pineRuntime.alerts.push({ condition, args: rest }); + } + return null; + }, + barcolor: function(color) { return null; }, + bgcolor: function(color, title, editable, showLast) { + return null; + }, + fill: function(series1, series2, color, title, editable) { + return null; + }, + hline: function(_price, _title, _opts) { + return null; + }, sma: function(series, length) { if (series === null || series === undefined) return null; if (!Array.isArray(series)) series = [series]; @@ -84,6 +135,13 @@ const pinescript = { } return weightSum > 0 ? sum / weightSum : null; }, + swma: function(series) { + if (series === null || series === undefined) return null; + if (!Array.isArray(series)) series = [series]; + if (series.length < 4) return null; + const s = series.slice(-4); + return (s[0] * 1 + s[1] * 2 + s[2] * 2 + s[3] * 1) / 6; + }, atr: function(high, low, close, length = 14) { if (!Array.isArray(high) || !Array.isArray(low) || !Array.isArray(close)) return null; if (high.length < length + 1 || low.length < length + 1 || close.length < length + 1) return null; @@ -114,6 +172,53 @@ const pinescript = { const atr = builtins.get('atr')(high, low, close, length); return { middle: sma, upper: sma + mult * atr, lower: sma - mult * atr }; }, + supertrend: function(factor, atrPeriod, high, low, close) { + if (!Array.isArray(high) || !Array.isArray(low) || !Array.isArray(close)) return [null, null]; + if (high.length < atrPeriod + 1) return [null, null]; + const atrVal = builtins.get('atr')(high, low, close, atrPeriod); + if (atrVal === null) return [null, null]; + const lastIdx = close.length - 1; + const hl2 = (high[lastIdx] + low[lastIdx]) / 2; + const upperBand = hl2 + factor * atrVal; + const lowerBand = hl2 - factor * atrVal; + // Determine direction: 1 means downtrend (price below band), -1 means uptrend + const direction = close[lastIdx] > upperBand ? -1 : close[lastIdx] < lowerBand ? 1 : -1; + const supertrendValue = direction === -1 ? lowerBand : upperBand; + return [supertrendValue, direction]; + }, + ta_supertrend: function(factor, atrPeriod) { + const H = globalThis.high; + const L = globalThis.low; + const C = globalThis.close; + return builtins.get('supertrend')(factor, atrPeriod, H, L, C); + }, + dmi: function(diLength, adxSmoothing, high, low, close) { + if (!Array.isArray(high) || !Array.isArray(low) || !Array.isArray(close)) return { plusDI: null, minusDI: null, adx: null }; + if (high.length < diLength + 1) return { plusDI: null, minusDI: null, adx: null }; + let plusDMSum = 0, minusDMSum = 0, trSum = 0; + for (let i = 1; i <= diLength; i++) { + const idx = high.length - 1 - diLength + i; + const upMove = high[idx] - high[idx - 1]; + const downMove = low[idx - 1] - low[idx]; + plusDMSum += (upMove > downMove && upMove > 0) ? upMove : 0; + minusDMSum += (downMove > upMove && downMove > 0) ? downMove : 0; + const tr = Math.max( + high[idx] - low[idx], + Math.abs(high[idx] - close[idx - 1]), + Math.abs(low[idx] - close[idx - 1]) + ); + trSum += tr; + } + const plusDI = trSum > 0 ? (plusDMSum / trSum) * 100 : 0; + const minusDI = trSum > 0 ? (minusDMSum / trSum) * 100 : 0; + const diSum = plusDI + minusDI; + const dx = diSum > 0 ? Math.abs(plusDI - minusDI) / diSum * 100 : 0; + return { plusDI, minusDI, adx: dx }; + }, + adx: function(diLength, adxSmoothing, high, low, close) { + const result = builtins.get('dmi')(diLength, adxSmoothing, high, low, close); + return result ? result.adx : null; + }, rsi: function(close, length = 14) { if (close === null || close === undefined) return null; if (!Array.isArray(close)) close = [close]; @@ -171,9 +276,7 @@ const pinescript = { return { macd: macdLine, signal: signalLine, histogram: histogram }; }, cci: function(a, b, c, d) { - // Pine signatures: - // - ta.cci(source, length) - // - ta.cci(high, low, close, length) + // Supports both ta.cci(source, length) and ta.cci(high, low, close, length) signatures let source; let length; if (Array.isArray(a) && typeof b === 'number' && c === undefined) { @@ -304,6 +407,15 @@ const pinescript = { stdev: function(source, length) { return Math.sqrt(builtins.get('variance')(source, length)); }, + median: function(source, length) { + if (!source || source.length < length) return null; + const sorted = [...source.slice(-length)].sort((a, b) => a - b); + const mid = Math.floor(sorted.length / 2); + return sorted.length % 2 !== 0 ? sorted[mid] : (sorted[mid - 1] + sorted[mid]) / 2; + }, + ta_median: function(source, length) { + return builtins.get('median')(source, length); + }, hl2: function(high, low) { return high && low ? (high[high.length - 1] + low[low.length - 1]) / 2 : null; }, @@ -420,6 +532,24 @@ const pinescript = { return (series1[i - 1] < series2[i - 1] && series1[i] >= series2[i]) || (series1[i - 1] > series2[i - 1] && series1[i] <= series2[i]); }, + crossover: function(series1, series2) { + if (series1 === null || series1 === undefined) return false; + if (series2 === null || series2 === undefined) return false; + if (!Array.isArray(series1)) series1 = [series1]; + if (!Array.isArray(series2)) series2 = [series2]; + if (series1.length < 2 || series2.length < 2) return false; + const i = series1.length - 1; + return series1[i - 1] < series2[i - 1] && series1[i] >= series2[i]; + }, + crossunder: function(series1, series2) { + if (series1 === null || series1 === undefined) return false; + if (series2 === null || series2 === undefined) return false; + if (!Array.isArray(series1)) series1 = [series1]; + if (!Array.isArray(series2)) series2 = [series2]; + if (series1.length < 2 || series2.length < 2) return false; + const i = series1.length - 1; + return series1[i - 1] > series2[i - 1] && series1[i] <= series2[i]; + }, offset: function(series, offset) { if (!Array.isArray(series)) return null; const idx = series.length - 1 - offset; @@ -500,12 +630,14 @@ const pinescript = { return builtins.get('nz')(value, replacement); }, plot: function(series, title = '', color = null, linewidth = 1) { - globalThis.__pineRuntime.plots.push({ - series, - title, - color, - linewidth - }); + const rt = globalThis.__pineRuntime; + if (!rt) return series; + const bar = rt.__barIndex | 0; + const ord = (rt.__plotIdx = (rt.__plotIdx | 0) + 1) - 1; + const key = (title && String(title)) || ('plot_' + ord); + let p = rt.plots[key]; + if (!p) p = rt.plots[key] = { title: key, color, linewidth, data: [] }; + p.data[bar] = this.__scalar(series); return series; }, lineNew: function(x1, y1, x2, y2, opts = {}) { @@ -514,20 +646,72 @@ const pinescript = { lineDelete: function(l) { return null; }, - labelNew: function(...args) { - if (globalThis.__pineRuntime) { - globalThis.__pineRuntime.labels = globalThis.__pineRuntime.labels || []; - globalThis.__pineRuntime.labels.push({ args }); - } - return { args }; + lineSetXY: function(line, point, x, y) { + if (!line) return null; + if (point === 0 || point === 'x1') { line.x1 = x; line.y1 = y; } + else { line.x2 = x; line.y2 = y; } + return line; + }, + lineGetX: function(line, point) { + if (!line) return null; + return point === 0 || point === 'x1' ? line.x1 : line.x2; + }, + lineGetY: function(line, point) { + if (!line) return null; + return point === 0 || point === 'y1' ? line.y1 : line.y2; + }, + labelNew: function(x, y, text = '', opts = {}) { + return { x, y, text, opts, _type: 'label' }; }, labelDelete: function(l) { return null; }, + labelSetText: function(label, text) { + if (!label) return null; + label.text = text; + return label; + }, + labelGetText: function(label) { + if (!label) return ''; + return label.text || ''; + }, + boxNew: function(left, top, right, bottom, opts = {}) { + return { left, top, right, bottom, opts, _type: 'box' }; + }, + boxDelete: function(box) { + return null; + }, + boxSetLeftTop: function(box, left, top) { + if (!box) return null; + box.left = left; + box.top = top; + return box; + }, + boxSetRightBottom: function(box, right, bottom) { + if (!box) return null; + box.right = right; + box.bottom = bottom; + return box; + }, + polylineNew: function(points, opts = {}) { + return { points: points || [], opts, _type: 'polyline' }; + }, + polylineDelete: function(poly) { + return null; + }, plotshape: function(condition, ...rest) { - if (globalThis.__pineRuntime) { - globalThis.__pineRuntime.plotshapes.push({ condition, args: rest }); + const rt = globalThis.__pineRuntime; + if (!rt) return condition; + const bar = rt.__barIndex | 0; + const ord = (rt.__shapeIdx = (rt.__shapeIdx | 0) + 1) - 1; + let key = 'shape_' + ord; + for (const r of rest) { + if (typeof r === 'string') { key = r; break; } + if (r && typeof r === 'object' && r.title) { key = String(r.title); break; } } + let s = rt.plotshapes[key]; + if (!s) s = rt.plotshapes[key] = { title: key, data: [] }; + s.data[bar] = !!this.__scalar(condition); return condition; }, indicator: function(title, shorttitle, overlay, opts) { @@ -542,27 +726,12 @@ const pinescript = { } return null; }, - alertcondition: function(condition, ...rest) { - if (globalThis.__pineRuntime) { - globalThis.__pineRuntime.alerts.push({ condition, args: rest }); - } - return null; - }, plotbar: function(open, high, low, close, title, color, editable, showLast) { return { open, high, low, close }; }, plotcandle: function(open, high, low, close, title, color, wickColor, borderColor, editable, showLast) { return { open, high, low, close }; }, - hline: function(_price, _title, _opts) { - return null; - }, - bgcolor: function(color, title, editable, showLast) { - return null; - }, - fill: function(series1, series2, color, title, editable) { - return null; - }, alert: function(condition, message, frequency) { console.log('Alert:', condition ? message : 'Condition not met'); return condition; @@ -828,6 +997,9 @@ const pinescript = { arrayIndexOf: function(arr, value) { return arr ? arr.indexOf(value) : -1; }, + arrayLastIndexOf: function(arr, value) { + return arr ? arr.lastIndexOf(value) : -1; + }, arraySum: function(arr) { return arr ? arr.reduce((a, b) => a + b, 0) : 0; }, @@ -840,6 +1012,42 @@ const pinescript = { arrayMax: function(arr) { return arr && arr.length > 0 ? Math.max(...arr) : null; }, + arrayStdev: function(arr) { + if (!arr || arr.length === 0) return null; + const mean = arr.reduce((a, b) => a + b, 0) / arr.length; + const variance = arr.reduce((sum, val) => sum + Math.pow(val - mean, 2), 0) / arr.length; + return Math.sqrt(variance); + }, + arrayVariance: function(arr) { + if (!arr || arr.length === 0) return null; + const mean = arr.reduce((a, b) => a + b, 0) / arr.length; + return arr.reduce((sum, val) => sum + Math.pow(val - mean, 2), 0) / arr.length; + }, + arrayCovariance: function(arr1, arr2) { + if (!arr1 || !arr2 || arr1.length === 0 || arr1.length !== arr2.length) return null; + const mean1 = arr1.reduce((a, b) => a + b, 0) / arr1.length; + const mean2 = arr2.reduce((a, b) => a + b, 0) / arr2.length; + let cov = 0; + for (let i = 0; i < arr1.length; i++) { + cov += (arr1[i] - mean1) * (arr2[i] - mean2); + } + return cov / arr1.length; + }, + mode: function(arr) { + if (!arr || arr.length === 0) return null; + const counts = new Map(); + let maxCount = 0; + let modeVal = arr[0]; + for (const val of arr) { + const c = (counts.get(val) || 0) + 1; + counts.set(val, c); + if (c > maxCount) { + maxCount = c; + modeVal = val; + } + } + return modeVal; + }, strLength: function(str) { return str ? str.length : 0; }, @@ -895,6 +1103,84 @@ const pinescript = { strReverse: function(str) { return str ? str.split('').reverse().join('') : ''; }, + strFormat: function(formatStr, ...args) { + if (!formatStr) return ''; + return formatStr.replace(/\{(\d+)\}/g, (match, idx) => { + const i = parseInt(idx, 10); + return i < args.length ? String(args[i]) : match; + }); + }, + input: function(defval, title, tooltip, inline, group) { + return defval; + }, + inputInt: function(defval = 0, title, minval, maxval, step, tooltip, inline, group) { + return defval; + }, + inputFloat: function(defval = 0.0, title, minval, maxval, step, tooltip, inline, group) { + return defval; + }, + inputBool: function(defval = false, title, tooltip, inline, group) { + return defval; + }, + inputString: function(defval = '', title, options, tooltip, inline, group) { + return defval; + }, + inputSource: function(defval = null, title, tooltip, inline, group) { + return defval; + }, + inputColor: function(defval = '#000000', title, tooltip, inline, group) { + return defval; + }, + inputTime: function(defval = 0, title, tooltip, inline, group) { + return defval; + }, + mathSign: function(value) { + return Math.sign(value); + }, + mathAvg: function(...args) { + const valid = args.filter(v => v !== null && v !== undefined && !isNaN(v)); + return valid.length > 0 ? valid.reduce((a, b) => a + b, 0) / valid.length : null; + }, + mathSum: function(series, length) { + if (!series || series.length < length) return null; + return series.slice(-length).reduce((a, b) => a + b, 0); + }, + mathRandom: function(min = 0, max = 1) { + return min + Math.random() * (max - min); + }, + mathTodegrees: function(radians) { + return radians * (180 / Math.PI); + }, + mathToradians: function(degrees) { + return degrees * (Math.PI / 180); + }, + colorT: function(color, transparency) { + if (transparency !== undefined) { + return { color, transparency }; + } + if (color && typeof color === 'object' && 'transparency' in color) { + return color.transparency; + } + return 0; + }, + tableCellSetText: function(table, column, row, text) { + return null; + }, + tableCellSetBgcolor: function(table, column, row, bgcolor) { + return null; + }, + tableMergeCells: function(table, startColumn, startRow, endColumn, endRow) { + return null; + }, + tableDelete: function(table) { + return null; + }, + tableClear: function(table, startColumn, startRow, endColumn, endRow) { + return null; + }, + maxBarsBack: function(series, length) { + return null; + }, pi: 3.141592653589793, e: 2.718281828459045, true: true, @@ -905,8 +1191,7 @@ const pinescript = { const info = { ticker: 'AAPL', tickerid: 'NASDAQ:AAPL', prefix: 'NASDAQ', root: 'AAPL', suffix: '' }; return info[type] || ''; }, - time: 1771178051221, - timenow: 1771178051221, + timenow: 1781574526863, barstate: "LAST", dividends: {}, splits: {}, @@ -923,7 +1208,7 @@ const pinescript = { return null; }, tr: function(high, low, close) { - // Pine signature: ta.tr(true) + // Handles both the boolean shorthand ta.tr(true) and explicit high/low/close arguments if (typeof high === 'boolean') { const H = globalThis.high; const L = globalThis.low; @@ -934,7 +1219,7 @@ const pinescript = { return Math.max(H[i] - L[i], Math.abs(H[i] - prevClose), Math.abs(L[i] - prevClose)); } - // Fallback: treat inputs as scalars for current bar + // When arrays are passed, compute true range from the last bar using the previous close if (Array.isArray(close)) { const prevClose = close[close.length - 2] || close[0]; const currHigh = Array.isArray(high) ? high[high.length - 1] : high; @@ -945,6 +1230,92 @@ const pinescript = { return Math.max(high - low, Math.abs(high - close), Math.abs(low - close)); }, + arrayFirst: function(arr) { + if (!arr || arr.length === 0) return null; + return arr[0]; + }, + arrayLast: function(arr) { + if (!arr || arr.length === 0) return null; + return arr[arr.length - 1]; + }, + arrayJoin: function(arr, separator = ',') { + if (!arr) return ''; + return arr.join(separator); + }, + arrayConcat: function(arr1, arr2) { + if (!arr1) return arr2 || []; + if (!arr2) return arr1; + return arr1.concat(arr2); + }, + arrayCopy: function(arr) { + if (!arr) return []; + return [...arr]; + }, + arrayBinarySearch: function(arr, value) { + if (!arr || arr.length === 0) return -1; + let lo = 0, hi = arr.length - 1; + while (lo <= hi) { + const mid = (lo + hi) >>> 1; + if (arr[mid] === value) return mid; + if (arr[mid] < value) lo = mid + 1; + else hi = mid - 1; + } + return -1; + }, + arrayRange: function(arr) { + if (!arr || arr.length === 0) return 0; + return Math.max(...arr) - Math.min(...arr); + }, + arrayMedian: function(arr) { + if (!arr || arr.length === 0) return null; + const sorted = [...arr].sort((a, b) => a - b); + const mid = Math.floor(sorted.length / 2); + return sorted.length % 2 !== 0 ? sorted[mid] : (sorted[mid - 1] + sorted[mid]) / 2; + }, + arrayMode: function(arr) { + if (!arr || arr.length === 0) return null; + const freq = {}; + let maxCount = 0; + let mode = arr[0]; + for (const v of arr) { + freq[v] = (freq[v] || 0) + 1; + if (freq[v] > maxCount) { maxCount = freq[v]; mode = v; } + } + return mode; + }, + arrayPercentile: function(arr, percentile) { + if (!arr || arr.length === 0) return null; + const sorted = [...arr].sort((a, b) => a - b); + const idx = (percentile / 100) * (sorted.length - 1); + const lo = Math.floor(idx); + const hi = Math.ceil(idx); + if (lo === hi) return sorted[lo]; + return sorted[lo] + (sorted[hi] - sorted[lo]) * (idx - lo); + }, + arrayPercentileLinearInterpolation: function(arr, percentile) { + return builtins.get('arrayPercentile')(arr, percentile); + }, + arrayPercentileNearestRank: function(arr, percentile) { + if (!arr || arr.length === 0) return null; + const sorted = [...arr].sort((a, b) => a - b); + const idx = Math.ceil((percentile / 100) * sorted.length) - 1; + return sorted[Math.max(0, idx)]; + }, + arrayAbs: function(arr) { + if (!arr) return []; + return arr.map(v => Math.abs(v)); + }, + arrayEvery: function(arr, callback) { + if (!arr) return false; + return arr.every(callback); + }, + arraySome: function(arr, callback) { + if (!arr) return false; + return arr.some(callback); + }, + requestFinancial: function() { + return null; + }, }; @@ -1101,6 +1472,115 @@ globalThis.barmerge = globalThis.barmerge || { }; +function __pineNS(real) { + + const base = real || {}; + + return new Proxy(base, { + + get(target, key) { + + if (key in target) return target[key]; + + if (typeof key === "symbol") return target[key]; + + // Unknown member: usable as a value (null), a constant, or a no-op function. + + const noop = function() { return null; }; + + noop.toString = () => ""; + + return noop; + + } + + }); + +} + +globalThis.ta = globalThis.ta || __pineNS({}); + +globalThis.math = globalThis.math || __pineNS({ pi: Math.PI, e: Math.E, phi: 1.618033988749895, rphi: 0.618033988749895 }); + +globalThis.chart = globalThis.chart || __pineNS({ point: __pineNS({}), bg_color: null, fg_color: null }); + +globalThis.line = globalThis.line || __pineNS({ style_solid: "solid", style_dashed: "dashed", style_dotted: "dotted" }); + +globalThis.box = globalThis.box || __pineNS({}); + +globalThis.label = globalThis.label || __pineNS({ style_label_down: "label_down", style_label_up: "label_up", style_none: "none" }); + +globalThis.polyline = globalThis.polyline || __pineNS({}); + +globalThis.linefill = globalThis.linefill || __pineNS({}); + +globalThis.matrix = globalThis.matrix || __pineNS({}); + +globalThis.map = globalThis.map || __pineNS({}); + +globalThis.session = globalThis.session || __pineNS({ regular: "regular", extended: "extended" }); + +globalThis.dayofweek = globalThis.dayofweek || __pineNS({ sunday: 1, monday: 2, tuesday: 3, wednesday: 4, thursday: 5, friday: 6, saturday: 7 }); + +globalThis.timeframe = __pineNS(Object.assign(globalThis.timeframe || {}, { period: (globalThis.timeframe && globalThis.timeframe.period) || "D", isintraday: false, isdaily: true, multiplier: 1 })); + +globalThis.request = __pineNS(globalThis.request || {}); + +globalThis.input = __pineNS(globalThis.input || {}); + +globalThis.adjustment = globalThis.adjustment || __pineNS({}); + +globalThis.earnings = globalThis.earnings || __pineNS({}); + +globalThis.dividends = globalThis.dividends || __pineNS({}); + +globalThis.splits = globalThis.splits || __pineNS({}); + +globalThis.currency = globalThis.currency || __pineNS({}); + +globalThis.display = globalThis.display || __pineNS({ none: "none", all: "all", pane: "pane", data_window: "data_window", status_line: "status_line", price_scale: "price_scale" }); + +globalThis.format = globalThis.format || __pineNS({ price: "price", volume: "volume", percent: "percent", mintick: "mintick", inherit: "inherit" }); + +globalThis.scale = globalThis.scale || __pineNS({ left: "left", right: "right", none: "none" }); + +globalThis.font = globalThis.font || __pineNS({ family_default: "default", family_monospace: "monospace" }); + +globalThis.xloc = globalThis.xloc || __pineNS({ bar_index: "bar_index", bar_time: "bar_time" }); + +globalThis.yloc = globalThis.yloc || __pineNS({ price: "price", abovebar: "abovebar", belowbar: "belowbar" }); + +globalThis.extend = globalThis.extend || __pineNS({ none: "none", left: "left", right: "right", both: "both" }); + +globalThis.order = globalThis.order || __pineNS({ ascending: "ascending", descending: "descending" }); + +globalThis.alert = globalThis.alert || Object.assign(function() { return null; }, { freq_once_per_bar: "once_per_bar", freq_once_per_bar_close: "once_per_bar_close", freq_all: "all" }); + +globalThis.dayofmonth = globalThis.dayofmonth || function(t) { return new Date(t || 0).getUTCDate(); }; + +globalThis.plot = globalThis.plot || __pineNS({ style_line: "line", style_linebr: "linebr", style_stepline: "stepline", style_histogram: "histogram", style_cross: "cross", style_area: "area", style_areabr: "areabr", style_columns: "columns", style_circles: "circles" }); + +globalThis.int = globalThis.int || function(x) { return x == null ? null : Math.trunc(Number(x)); }; + +globalThis.float = globalThis.float || function(x) { return x == null ? null : Number(x); }; + +globalThis.bool = globalThis.bool || function(x) { return Boolean(x); }; + +globalThis.string = globalThis.string || function(x) { return x == null ? null : String(x); }; + +globalThis.dayofweek = globalThis.dayofweek || __pineNS({ sunday: 1, monday: 2, tuesday: 3, wednesday: 4, thursday: 5, friday: 6, saturday: 7 }); + +globalThis.str = globalThis.str || __pineNS({}); + +pinescript.math = globalThis.math; + +pinescript.ta = globalThis.ta; + +pinescript.str = globalThis.str; + +pinescript.array = __pineNS(globalThis.array); + + pinescript.color = { hex: function(s) { @@ -1190,10 +1670,10 @@ pinescript.table = { }; -// Input parameters +// Script input parameters and their defaults -// Main script logic +// The transpiled script logic, called once per bar function main() { @@ -1208,12 +1688,12 @@ function main() { null; // Strategy: Crossing Moving Averages with ADX Filter) // Options: {"overlay":true,"pyramiding":0} - let fastLen = input.int(20, ({ title: "Fast MA Length" })); - let slowLen = input.int(120, ({ title: "Slow MA Length" })); - let maType = input.string("SMA", ({ title: "Moving Average Type", options: ["SMA", "EMA", "WMA", "LMA"] })); - let useADX = input.bool(true, ({ title: "Enable ADX Filter" })); - let adxLen = input.int(14, ({ title: "ADX Length" })); - let adxThreshold = input.float(20, ({ title: "ADX Threshold" })); + let fastLen = pinescript.inputInt(20, ({ title: "Fast MA Length" })); + let slowLen = pinescript.inputInt(120, ({ title: "Slow MA Length" })); + let maType = pinescript.inputString("SMA", ({ title: "Moving Average Type", options: ["SMA", "EMA", "WMA", "LMA"] })); + let useADX = pinescript.inputBool(true, ({ title: "Enable ADX Filter" })); + let adxLen = pinescript.inputInt(14, ({ title: "ADX Length" })); + let adxThreshold = pinescript.inputFloat(20, ({ title: "ADX Threshold" })); function ma(src, len) { let out = null; if ((maType === "SMA")) { @@ -1235,11 +1715,11 @@ function main() { let slowMA = ma(close, slowLen); let longBias = (fastMA > slowMA); let shortBias = (fastMA < slowMA); - let upMove = (high - pinescript.offset(high, 1)); - let downMove = (pinescript.offset(low, 1) - low); + let upMove = (high - pinescript.hist(0, high, 1)); + let downMove = (pinescript.hist(1, low, 1) - low); let plusDM = (((upMove > downMove) && (upMove > 0)) ? upMove : 0); let minusDM = (((downMove > upMove) && (downMove > 0)) ? downMove : 0); - let trur = pinescript.rma(ta.tr(true), adxLen); + let trur = pinescript.rma(pinescript.tr(true), adxLen); let plusDI = ((100 * pinescript.rma(plusDM, adxLen)) / trur); let minusDI = ((100 * pinescript.rma(minusDM, adxLen)) / trur); let dx = ((100 * pinescript.abs((plusDI - minusDI))) / (plusDI + minusDI)); @@ -1258,7 +1738,67 @@ function main() { } -// Export for use -export { main +// The bar-by-bar engine: runs main() once per bar over the dataset. +function run(data, options = {}) { + data = data || {}; + const inClose = (data.close || []).map(Number); + const n = inClose.length; + const inOpen = (data.open || inClose).map(Number); + const inHigh = (data.high || inClose).map(Number); + const inLow = (data.low || inClose).map(Number); + const inVol = (data.volume || new Array(n).fill(0)).map(Number); + const inTime = (data.time || inClose.map((_, i) => i)).map(Number); + + // Reset all persistent and per-run state so repeated runs are independent. + globalThis.__pineState = {}; + globalThis.__pineRuntime = { plots: {}, plotshapes: {}, alerts: [], bars: n, inputs: options.inputs || {} }; + pinescript.__rt = {}; + pinescript.__bar = 0; + + // Growing OHLCV windows: each is a SeriesArray that gains one element per bar, + // so window built-ins like sma and highest see history up to the current bar. + const O = pinescript.asSeries([]), H = pinescript.asSeries([]), L = pinescript.asSeries([]); + const C = pinescript.asSeries([]), V = pinescript.asSeries([]), T = pinescript.asSeries([]); + const HL2 = pinescript.asSeries([]), HLC3 = pinescript.asSeries([]), OHLC4 = pinescript.asSeries([]); + globalThis.open = O; globalThis.high = H; globalThis.low = L; globalThis.close = C; + globalThis.volume = V; globalThis.time = T; + globalThis.hl2 = HL2; globalThis.hlc3 = HLC3; globalThis.ohlc4 = OHLC4; + globalThis.syminfo = globalThis.syminfo || { tickerid: 'SYNTHETIC', ticker: 'SYNTHETIC', mintick: 0.01 }; + + const rt = globalThis.__pineRuntime; + for (let i = 0; i < n; i++) { + O.push(inOpen[i]); H.push(inHigh[i]); L.push(inLow[i]); C.push(inClose[i]); V.push(inVol[i]); T.push(inTime[i]); + HL2.push((inHigh[i] + inLow[i]) / 2); + HLC3.push((inHigh[i] + inLow[i] + inClose[i]) / 3); + OHLC4.push((inOpen[i] + inHigh[i] + inLow[i] + inClose[i]) / 4); + + pinescript.__bar = i; + rt.__barIndex = i; + rt.__plotIdx = 0; + rt.__shapeIdx = 0; + globalThis.bar_index = i; + globalThis.last_bar_index = n - 1; + globalThis.barstate = { + isfirst: i === 0, islast: i === n - 1, isrealtime: false, ishistory: true, + isconfirmed: true, isnew: true, islastconfirmedhistory: i === n - 1, + }; + + main(); + } + + // Backfill skipped bars so every output series has exactly n entries. + for (const k of Object.keys(rt.plots)) { + const d = rt.plots[k].data; + for (let i = 0; i < n; i++) if (d[i] === undefined) d[i] = null; + } + for (const k of Object.keys(rt.plotshapes)) { + const d = rt.plotshapes[k].data; + for (let i = 0; i < n; i++) if (d[i] === undefined) d[i] = false; + } + return rt; +} + + +export { main, run }; diff --git a/converted/Folded_RSI.js b/converted/Folded_RSI.js index c7be421..300ea19 100644 --- a/converted/Folded_RSI.js +++ b/converted/Folded_RSI.js @@ -1,12 +1,63 @@ -// PineScript to JavaScript Transpiled Code +// Auto-generated JavaScript from PineScript source -// Generated automatically +// Do not edit by hand -- re-run the converter instead -// Built-in PineScript functions +// All the built-in PineScript functions that scripts depend on at runtime const pinescript = { + __scalar: function(v) { + if (v === null || v === undefined) return v; + if (typeof v === 'object' && typeof v.valueOf === 'function') { + const s = v.valueOf(); + if (typeof s === 'number' || typeof s === 'boolean' || s === null) return s; + } + return v; + }, + series: function(id, value) { + const rt = this.__rt || (this.__rt = {}); + const store = rt.series || (rt.series = {}); + let buf = store[id]; + if (!buf) buf = store[id] = []; + buf[this.__bar | 0] = this.__scalar(value); + return buf; + }, + hist: function(id, value, n) { + const buf = this.series(id, value); + const idx = (this.__bar | 0) - (n | 0); + return idx >= 0 && idx < buf.length ? buf[idx] : null; + }, + time: function(timeframe, session) { + const t = globalThis.time; + if (t && typeof t.valueOf === 'function') return t.valueOf(); + return Array.isArray(t) ? t[t.length - 1] : t; + }, + sign: function(x) { + if (x === null || x === undefined || (typeof x === 'number' && isNaN(x))) return null; + return Math.sign(Number(x)); + }, + unpack: function(value, count) { + if (Array.isArray(value)) return value; + if (value != null && typeof value[Symbol.iterator] === 'function') return Array.from(value); + return new Array(count || 0).fill(null); + }, + alertcondition: function(condition, ...rest) { + if (globalThis.__pineRuntime) { + globalThis.__pineRuntime.alerts.push({ condition, args: rest }); + } + return null; + }, + barcolor: function(color) { return null; }, + bgcolor: function(color, title, editable, showLast) { + return null; + }, + fill: function(series1, series2, color, title, editable) { + return null; + }, + hline: function(_price, _title, _opts) { + return null; + }, sma: function(series, length) { if (series === null || series === undefined) return null; if (!Array.isArray(series)) series = [series]; @@ -84,6 +135,13 @@ const pinescript = { } return weightSum > 0 ? sum / weightSum : null; }, + swma: function(series) { + if (series === null || series === undefined) return null; + if (!Array.isArray(series)) series = [series]; + if (series.length < 4) return null; + const s = series.slice(-4); + return (s[0] * 1 + s[1] * 2 + s[2] * 2 + s[3] * 1) / 6; + }, atr: function(high, low, close, length = 14) { if (!Array.isArray(high) || !Array.isArray(low) || !Array.isArray(close)) return null; if (high.length < length + 1 || low.length < length + 1 || close.length < length + 1) return null; @@ -114,6 +172,53 @@ const pinescript = { const atr = builtins.get('atr')(high, low, close, length); return { middle: sma, upper: sma + mult * atr, lower: sma - mult * atr }; }, + supertrend: function(factor, atrPeriod, high, low, close) { + if (!Array.isArray(high) || !Array.isArray(low) || !Array.isArray(close)) return [null, null]; + if (high.length < atrPeriod + 1) return [null, null]; + const atrVal = builtins.get('atr')(high, low, close, atrPeriod); + if (atrVal === null) return [null, null]; + const lastIdx = close.length - 1; + const hl2 = (high[lastIdx] + low[lastIdx]) / 2; + const upperBand = hl2 + factor * atrVal; + const lowerBand = hl2 - factor * atrVal; + // Determine direction: 1 means downtrend (price below band), -1 means uptrend + const direction = close[lastIdx] > upperBand ? -1 : close[lastIdx] < lowerBand ? 1 : -1; + const supertrendValue = direction === -1 ? lowerBand : upperBand; + return [supertrendValue, direction]; + }, + ta_supertrend: function(factor, atrPeriod) { + const H = globalThis.high; + const L = globalThis.low; + const C = globalThis.close; + return builtins.get('supertrend')(factor, atrPeriod, H, L, C); + }, + dmi: function(diLength, adxSmoothing, high, low, close) { + if (!Array.isArray(high) || !Array.isArray(low) || !Array.isArray(close)) return { plusDI: null, minusDI: null, adx: null }; + if (high.length < diLength + 1) return { plusDI: null, minusDI: null, adx: null }; + let plusDMSum = 0, minusDMSum = 0, trSum = 0; + for (let i = 1; i <= diLength; i++) { + const idx = high.length - 1 - diLength + i; + const upMove = high[idx] - high[idx - 1]; + const downMove = low[idx - 1] - low[idx]; + plusDMSum += (upMove > downMove && upMove > 0) ? upMove : 0; + minusDMSum += (downMove > upMove && downMove > 0) ? downMove : 0; + const tr = Math.max( + high[idx] - low[idx], + Math.abs(high[idx] - close[idx - 1]), + Math.abs(low[idx] - close[idx - 1]) + ); + trSum += tr; + } + const plusDI = trSum > 0 ? (plusDMSum / trSum) * 100 : 0; + const minusDI = trSum > 0 ? (minusDMSum / trSum) * 100 : 0; + const diSum = plusDI + minusDI; + const dx = diSum > 0 ? Math.abs(plusDI - minusDI) / diSum * 100 : 0; + return { plusDI, minusDI, adx: dx }; + }, + adx: function(diLength, adxSmoothing, high, low, close) { + const result = builtins.get('dmi')(diLength, adxSmoothing, high, low, close); + return result ? result.adx : null; + }, rsi: function(close, length = 14) { if (close === null || close === undefined) return null; if (!Array.isArray(close)) close = [close]; @@ -171,9 +276,7 @@ const pinescript = { return { macd: macdLine, signal: signalLine, histogram: histogram }; }, cci: function(a, b, c, d) { - // Pine signatures: - // - ta.cci(source, length) - // - ta.cci(high, low, close, length) + // Supports both ta.cci(source, length) and ta.cci(high, low, close, length) signatures let source; let length; if (Array.isArray(a) && typeof b === 'number' && c === undefined) { @@ -304,6 +407,15 @@ const pinescript = { stdev: function(source, length) { return Math.sqrt(builtins.get('variance')(source, length)); }, + median: function(source, length) { + if (!source || source.length < length) return null; + const sorted = [...source.slice(-length)].sort((a, b) => a - b); + const mid = Math.floor(sorted.length / 2); + return sorted.length % 2 !== 0 ? sorted[mid] : (sorted[mid - 1] + sorted[mid]) / 2; + }, + ta_median: function(source, length) { + return builtins.get('median')(source, length); + }, hl2: function(high, low) { return high && low ? (high[high.length - 1] + low[low.length - 1]) / 2 : null; }, @@ -420,6 +532,24 @@ const pinescript = { return (series1[i - 1] < series2[i - 1] && series1[i] >= series2[i]) || (series1[i - 1] > series2[i - 1] && series1[i] <= series2[i]); }, + crossover: function(series1, series2) { + if (series1 === null || series1 === undefined) return false; + if (series2 === null || series2 === undefined) return false; + if (!Array.isArray(series1)) series1 = [series1]; + if (!Array.isArray(series2)) series2 = [series2]; + if (series1.length < 2 || series2.length < 2) return false; + const i = series1.length - 1; + return series1[i - 1] < series2[i - 1] && series1[i] >= series2[i]; + }, + crossunder: function(series1, series2) { + if (series1 === null || series1 === undefined) return false; + if (series2 === null || series2 === undefined) return false; + if (!Array.isArray(series1)) series1 = [series1]; + if (!Array.isArray(series2)) series2 = [series2]; + if (series1.length < 2 || series2.length < 2) return false; + const i = series1.length - 1; + return series1[i - 1] > series2[i - 1] && series1[i] <= series2[i]; + }, offset: function(series, offset) { if (!Array.isArray(series)) return null; const idx = series.length - 1 - offset; @@ -500,12 +630,14 @@ const pinescript = { return builtins.get('nz')(value, replacement); }, plot: function(series, title = '', color = null, linewidth = 1) { - globalThis.__pineRuntime.plots.push({ - series, - title, - color, - linewidth - }); + const rt = globalThis.__pineRuntime; + if (!rt) return series; + const bar = rt.__barIndex | 0; + const ord = (rt.__plotIdx = (rt.__plotIdx | 0) + 1) - 1; + const key = (title && String(title)) || ('plot_' + ord); + let p = rt.plots[key]; + if (!p) p = rt.plots[key] = { title: key, color, linewidth, data: [] }; + p.data[bar] = this.__scalar(series); return series; }, lineNew: function(x1, y1, x2, y2, opts = {}) { @@ -514,20 +646,72 @@ const pinescript = { lineDelete: function(l) { return null; }, - labelNew: function(...args) { - if (globalThis.__pineRuntime) { - globalThis.__pineRuntime.labels = globalThis.__pineRuntime.labels || []; - globalThis.__pineRuntime.labels.push({ args }); - } - return { args }; + lineSetXY: function(line, point, x, y) { + if (!line) return null; + if (point === 0 || point === 'x1') { line.x1 = x; line.y1 = y; } + else { line.x2 = x; line.y2 = y; } + return line; + }, + lineGetX: function(line, point) { + if (!line) return null; + return point === 0 || point === 'x1' ? line.x1 : line.x2; + }, + lineGetY: function(line, point) { + if (!line) return null; + return point === 0 || point === 'y1' ? line.y1 : line.y2; + }, + labelNew: function(x, y, text = '', opts = {}) { + return { x, y, text, opts, _type: 'label' }; }, labelDelete: function(l) { return null; }, + labelSetText: function(label, text) { + if (!label) return null; + label.text = text; + return label; + }, + labelGetText: function(label) { + if (!label) return ''; + return label.text || ''; + }, + boxNew: function(left, top, right, bottom, opts = {}) { + return { left, top, right, bottom, opts, _type: 'box' }; + }, + boxDelete: function(box) { + return null; + }, + boxSetLeftTop: function(box, left, top) { + if (!box) return null; + box.left = left; + box.top = top; + return box; + }, + boxSetRightBottom: function(box, right, bottom) { + if (!box) return null; + box.right = right; + box.bottom = bottom; + return box; + }, + polylineNew: function(points, opts = {}) { + return { points: points || [], opts, _type: 'polyline' }; + }, + polylineDelete: function(poly) { + return null; + }, plotshape: function(condition, ...rest) { - if (globalThis.__pineRuntime) { - globalThis.__pineRuntime.plotshapes.push({ condition, args: rest }); + const rt = globalThis.__pineRuntime; + if (!rt) return condition; + const bar = rt.__barIndex | 0; + const ord = (rt.__shapeIdx = (rt.__shapeIdx | 0) + 1) - 1; + let key = 'shape_' + ord; + for (const r of rest) { + if (typeof r === 'string') { key = r; break; } + if (r && typeof r === 'object' && r.title) { key = String(r.title); break; } } + let s = rt.plotshapes[key]; + if (!s) s = rt.plotshapes[key] = { title: key, data: [] }; + s.data[bar] = !!this.__scalar(condition); return condition; }, indicator: function(title, shorttitle, overlay, opts) { @@ -542,27 +726,12 @@ const pinescript = { } return null; }, - alertcondition: function(condition, ...rest) { - if (globalThis.__pineRuntime) { - globalThis.__pineRuntime.alerts.push({ condition, args: rest }); - } - return null; - }, plotbar: function(open, high, low, close, title, color, editable, showLast) { return { open, high, low, close }; }, plotcandle: function(open, high, low, close, title, color, wickColor, borderColor, editable, showLast) { return { open, high, low, close }; }, - hline: function(_price, _title, _opts) { - return null; - }, - bgcolor: function(color, title, editable, showLast) { - return null; - }, - fill: function(series1, series2, color, title, editable) { - return null; - }, alert: function(condition, message, frequency) { console.log('Alert:', condition ? message : 'Condition not met'); return condition; @@ -828,6 +997,9 @@ const pinescript = { arrayIndexOf: function(arr, value) { return arr ? arr.indexOf(value) : -1; }, + arrayLastIndexOf: function(arr, value) { + return arr ? arr.lastIndexOf(value) : -1; + }, arraySum: function(arr) { return arr ? arr.reduce((a, b) => a + b, 0) : 0; }, @@ -840,6 +1012,42 @@ const pinescript = { arrayMax: function(arr) { return arr && arr.length > 0 ? Math.max(...arr) : null; }, + arrayStdev: function(arr) { + if (!arr || arr.length === 0) return null; + const mean = arr.reduce((a, b) => a + b, 0) / arr.length; + const variance = arr.reduce((sum, val) => sum + Math.pow(val - mean, 2), 0) / arr.length; + return Math.sqrt(variance); + }, + arrayVariance: function(arr) { + if (!arr || arr.length === 0) return null; + const mean = arr.reduce((a, b) => a + b, 0) / arr.length; + return arr.reduce((sum, val) => sum + Math.pow(val - mean, 2), 0) / arr.length; + }, + arrayCovariance: function(arr1, arr2) { + if (!arr1 || !arr2 || arr1.length === 0 || arr1.length !== arr2.length) return null; + const mean1 = arr1.reduce((a, b) => a + b, 0) / arr1.length; + const mean2 = arr2.reduce((a, b) => a + b, 0) / arr2.length; + let cov = 0; + for (let i = 0; i < arr1.length; i++) { + cov += (arr1[i] - mean1) * (arr2[i] - mean2); + } + return cov / arr1.length; + }, + mode: function(arr) { + if (!arr || arr.length === 0) return null; + const counts = new Map(); + let maxCount = 0; + let modeVal = arr[0]; + for (const val of arr) { + const c = (counts.get(val) || 0) + 1; + counts.set(val, c); + if (c > maxCount) { + maxCount = c; + modeVal = val; + } + } + return modeVal; + }, strLength: function(str) { return str ? str.length : 0; }, @@ -895,6 +1103,84 @@ const pinescript = { strReverse: function(str) { return str ? str.split('').reverse().join('') : ''; }, + strFormat: function(formatStr, ...args) { + if (!formatStr) return ''; + return formatStr.replace(/\{(\d+)\}/g, (match, idx) => { + const i = parseInt(idx, 10); + return i < args.length ? String(args[i]) : match; + }); + }, + input: function(defval, title, tooltip, inline, group) { + return defval; + }, + inputInt: function(defval = 0, title, minval, maxval, step, tooltip, inline, group) { + return defval; + }, + inputFloat: function(defval = 0.0, title, minval, maxval, step, tooltip, inline, group) { + return defval; + }, + inputBool: function(defval = false, title, tooltip, inline, group) { + return defval; + }, + inputString: function(defval = '', title, options, tooltip, inline, group) { + return defval; + }, + inputSource: function(defval = null, title, tooltip, inline, group) { + return defval; + }, + inputColor: function(defval = '#000000', title, tooltip, inline, group) { + return defval; + }, + inputTime: function(defval = 0, title, tooltip, inline, group) { + return defval; + }, + mathSign: function(value) { + return Math.sign(value); + }, + mathAvg: function(...args) { + const valid = args.filter(v => v !== null && v !== undefined && !isNaN(v)); + return valid.length > 0 ? valid.reduce((a, b) => a + b, 0) / valid.length : null; + }, + mathSum: function(series, length) { + if (!series || series.length < length) return null; + return series.slice(-length).reduce((a, b) => a + b, 0); + }, + mathRandom: function(min = 0, max = 1) { + return min + Math.random() * (max - min); + }, + mathTodegrees: function(radians) { + return radians * (180 / Math.PI); + }, + mathToradians: function(degrees) { + return degrees * (Math.PI / 180); + }, + colorT: function(color, transparency) { + if (transparency !== undefined) { + return { color, transparency }; + } + if (color && typeof color === 'object' && 'transparency' in color) { + return color.transparency; + } + return 0; + }, + tableCellSetText: function(table, column, row, text) { + return null; + }, + tableCellSetBgcolor: function(table, column, row, bgcolor) { + return null; + }, + tableMergeCells: function(table, startColumn, startRow, endColumn, endRow) { + return null; + }, + tableDelete: function(table) { + return null; + }, + tableClear: function(table, startColumn, startRow, endColumn, endRow) { + return null; + }, + maxBarsBack: function(series, length) { + return null; + }, pi: 3.141592653589793, e: 2.718281828459045, true: true, @@ -905,8 +1191,7 @@ const pinescript = { const info = { ticker: 'AAPL', tickerid: 'NASDAQ:AAPL', prefix: 'NASDAQ', root: 'AAPL', suffix: '' }; return info[type] || ''; }, - time: 1771178051305, - timenow: 1771178051305, + timenow: 1781574526959, barstate: "LAST", dividends: {}, splits: {}, @@ -923,7 +1208,7 @@ const pinescript = { return null; }, tr: function(high, low, close) { - // Pine signature: ta.tr(true) + // Handles both the boolean shorthand ta.tr(true) and explicit high/low/close arguments if (typeof high === 'boolean') { const H = globalThis.high; const L = globalThis.low; @@ -934,7 +1219,7 @@ const pinescript = { return Math.max(H[i] - L[i], Math.abs(H[i] - prevClose), Math.abs(L[i] - prevClose)); } - // Fallback: treat inputs as scalars for current bar + // When arrays are passed, compute true range from the last bar using the previous close if (Array.isArray(close)) { const prevClose = close[close.length - 2] || close[0]; const currHigh = Array.isArray(high) ? high[high.length - 1] : high; @@ -945,6 +1230,92 @@ const pinescript = { return Math.max(high - low, Math.abs(high - close), Math.abs(low - close)); }, + arrayFirst: function(arr) { + if (!arr || arr.length === 0) return null; + return arr[0]; + }, + arrayLast: function(arr) { + if (!arr || arr.length === 0) return null; + return arr[arr.length - 1]; + }, + arrayJoin: function(arr, separator = ',') { + if (!arr) return ''; + return arr.join(separator); + }, + arrayConcat: function(arr1, arr2) { + if (!arr1) return arr2 || []; + if (!arr2) return arr1; + return arr1.concat(arr2); + }, + arrayCopy: function(arr) { + if (!arr) return []; + return [...arr]; + }, + arrayBinarySearch: function(arr, value) { + if (!arr || arr.length === 0) return -1; + let lo = 0, hi = arr.length - 1; + while (lo <= hi) { + const mid = (lo + hi) >>> 1; + if (arr[mid] === value) return mid; + if (arr[mid] < value) lo = mid + 1; + else hi = mid - 1; + } + return -1; + }, + arrayRange: function(arr) { + if (!arr || arr.length === 0) return 0; + return Math.max(...arr) - Math.min(...arr); + }, + arrayMedian: function(arr) { + if (!arr || arr.length === 0) return null; + const sorted = [...arr].sort((a, b) => a - b); + const mid = Math.floor(sorted.length / 2); + return sorted.length % 2 !== 0 ? sorted[mid] : (sorted[mid - 1] + sorted[mid]) / 2; + }, + arrayMode: function(arr) { + if (!arr || arr.length === 0) return null; + const freq = {}; + let maxCount = 0; + let mode = arr[0]; + for (const v of arr) { + freq[v] = (freq[v] || 0) + 1; + if (freq[v] > maxCount) { maxCount = freq[v]; mode = v; } + } + return mode; + }, + arrayPercentile: function(arr, percentile) { + if (!arr || arr.length === 0) return null; + const sorted = [...arr].sort((a, b) => a - b); + const idx = (percentile / 100) * (sorted.length - 1); + const lo = Math.floor(idx); + const hi = Math.ceil(idx); + if (lo === hi) return sorted[lo]; + return sorted[lo] + (sorted[hi] - sorted[lo]) * (idx - lo); + }, + arrayPercentileLinearInterpolation: function(arr, percentile) { + return builtins.get('arrayPercentile')(arr, percentile); + }, + arrayPercentileNearestRank: function(arr, percentile) { + if (!arr || arr.length === 0) return null; + const sorted = [...arr].sort((a, b) => a - b); + const idx = Math.ceil((percentile / 100) * sorted.length) - 1; + return sorted[Math.max(0, idx)]; + }, + arrayAbs: function(arr) { + if (!arr) return []; + return arr.map(v => Math.abs(v)); + }, + arrayEvery: function(arr, callback) { + if (!arr) return false; + return arr.every(callback); + }, + arraySome: function(arr, callback) { + if (!arr) return false; + return arr.some(callback); + }, + requestFinancial: function() { + return null; + }, }; @@ -1101,6 +1472,115 @@ globalThis.barmerge = globalThis.barmerge || { }; +function __pineNS(real) { + + const base = real || {}; + + return new Proxy(base, { + + get(target, key) { + + if (key in target) return target[key]; + + if (typeof key === "symbol") return target[key]; + + // Unknown member: usable as a value (null), a constant, or a no-op function. + + const noop = function() { return null; }; + + noop.toString = () => ""; + + return noop; + + } + + }); + +} + +globalThis.ta = globalThis.ta || __pineNS({}); + +globalThis.math = globalThis.math || __pineNS({ pi: Math.PI, e: Math.E, phi: 1.618033988749895, rphi: 0.618033988749895 }); + +globalThis.chart = globalThis.chart || __pineNS({ point: __pineNS({}), bg_color: null, fg_color: null }); + +globalThis.line = globalThis.line || __pineNS({ style_solid: "solid", style_dashed: "dashed", style_dotted: "dotted" }); + +globalThis.box = globalThis.box || __pineNS({}); + +globalThis.label = globalThis.label || __pineNS({ style_label_down: "label_down", style_label_up: "label_up", style_none: "none" }); + +globalThis.polyline = globalThis.polyline || __pineNS({}); + +globalThis.linefill = globalThis.linefill || __pineNS({}); + +globalThis.matrix = globalThis.matrix || __pineNS({}); + +globalThis.map = globalThis.map || __pineNS({}); + +globalThis.session = globalThis.session || __pineNS({ regular: "regular", extended: "extended" }); + +globalThis.dayofweek = globalThis.dayofweek || __pineNS({ sunday: 1, monday: 2, tuesday: 3, wednesday: 4, thursday: 5, friday: 6, saturday: 7 }); + +globalThis.timeframe = __pineNS(Object.assign(globalThis.timeframe || {}, { period: (globalThis.timeframe && globalThis.timeframe.period) || "D", isintraday: false, isdaily: true, multiplier: 1 })); + +globalThis.request = __pineNS(globalThis.request || {}); + +globalThis.input = __pineNS(globalThis.input || {}); + +globalThis.adjustment = globalThis.adjustment || __pineNS({}); + +globalThis.earnings = globalThis.earnings || __pineNS({}); + +globalThis.dividends = globalThis.dividends || __pineNS({}); + +globalThis.splits = globalThis.splits || __pineNS({}); + +globalThis.currency = globalThis.currency || __pineNS({}); + +globalThis.display = globalThis.display || __pineNS({ none: "none", all: "all", pane: "pane", data_window: "data_window", status_line: "status_line", price_scale: "price_scale" }); + +globalThis.format = globalThis.format || __pineNS({ price: "price", volume: "volume", percent: "percent", mintick: "mintick", inherit: "inherit" }); + +globalThis.scale = globalThis.scale || __pineNS({ left: "left", right: "right", none: "none" }); + +globalThis.font = globalThis.font || __pineNS({ family_default: "default", family_monospace: "monospace" }); + +globalThis.xloc = globalThis.xloc || __pineNS({ bar_index: "bar_index", bar_time: "bar_time" }); + +globalThis.yloc = globalThis.yloc || __pineNS({ price: "price", abovebar: "abovebar", belowbar: "belowbar" }); + +globalThis.extend = globalThis.extend || __pineNS({ none: "none", left: "left", right: "right", both: "both" }); + +globalThis.order = globalThis.order || __pineNS({ ascending: "ascending", descending: "descending" }); + +globalThis.alert = globalThis.alert || Object.assign(function() { return null; }, { freq_once_per_bar: "once_per_bar", freq_once_per_bar_close: "once_per_bar_close", freq_all: "all" }); + +globalThis.dayofmonth = globalThis.dayofmonth || function(t) { return new Date(t || 0).getUTCDate(); }; + +globalThis.plot = globalThis.plot || __pineNS({ style_line: "line", style_linebr: "linebr", style_stepline: "stepline", style_histogram: "histogram", style_cross: "cross", style_area: "area", style_areabr: "areabr", style_columns: "columns", style_circles: "circles" }); + +globalThis.int = globalThis.int || function(x) { return x == null ? null : Math.trunc(Number(x)); }; + +globalThis.float = globalThis.float || function(x) { return x == null ? null : Number(x); }; + +globalThis.bool = globalThis.bool || function(x) { return Boolean(x); }; + +globalThis.string = globalThis.string || function(x) { return x == null ? null : String(x); }; + +globalThis.dayofweek = globalThis.dayofweek || __pineNS({ sunday: 1, monday: 2, tuesday: 3, wednesday: 4, thursday: 5, friday: 6, saturday: 7 }); + +globalThis.str = globalThis.str || __pineNS({}); + +pinescript.math = globalThis.math; + +pinescript.ta = globalThis.ta; + +pinescript.str = globalThis.str; + +pinescript.array = __pineNS(globalThis.array); + + pinescript.color = { hex: function(s) { @@ -1190,10 +1670,10 @@ pinescript.table = { }; -// Input parameters +// Script input parameters and their defaults -// Main script logic +// The transpiled script logic, called once per bar function main() { @@ -1209,17 +1689,17 @@ function main() { // Study: Folded RSI (custom RSI, phase-invariant) // Options: {"shorttitle":"Folded RSI","overlay":false,"max_lines_count":500,"max_labels_count":500} let grp_cycle = "Cycle / Correlation"; - let cycle_len = input.int(40, "Cycle Window (corr length)", ({ group: grp_cycle, minval: 10, maxval: 600, tooltip: "Bars used to estimate correlation with a pure sine/cos basis." })); - let target_period = input.int(20, "Target Sine Period (bars)", ({ group: grp_cycle, minval: 6, maxval: 300, tooltip: "Assumed dominant cycle length to correlate against." })); - let use_harmonic2 = input.bool(true, "Also test 2× harmonic and keep the stronger", ({ group: grp_cycle })); + let cycle_len = pinescript.inputInt(40, "Cycle Window (corr length)", ({ group: grp_cycle, minval: 10, maxval: 600, tooltip: "Bars used to estimate correlation with a pure sine/cos basis." })); + let target_period = pinescript.inputInt(20, "Target Sine Period (bars)", ({ group: grp_cycle, minval: 6, maxval: 300, tooltip: "Assumed dominant cycle length to correlate against." })); + let use_harmonic2 = pinescript.inputBool(true, "Also test 2× harmonic and keep the stronger", ({ group: grp_cycle })); let grp_map = "Correlation → RSI Length Mapping"; - let len_min = input.int(5, "RSI Length Min", ({ group: grp_map, minval: 2, maxval: 300 })); - let len_max = input.int(33, "RSI Length Max", ({ group: grp_map, minval: 5, maxval: 600 })); - let map_power = input.float(1, "Nonlinearity (power)", ({ group: grp_map, minval: 0.25, maxval: 4, tooltip: ">1 emphasizes strong correlations; <1 flattens the response." })); - let invert_map = input.bool(false, "Invert mapping (strong cycle → longer length)", ({ group: grp_map })); + let len_min = pinescript.inputInt(5, "RSI Length Min", ({ group: grp_map, minval: 2, maxval: 300 })); + let len_max = pinescript.inputInt(33, "RSI Length Max", ({ group: grp_map, minval: 5, maxval: 600 })); + let map_power = pinescript.inputFloat(1, "Nonlinearity (power)", ({ group: grp_map, minval: 0.25, maxval: 4, tooltip: ">1 emphasizes strong correlations; <1 flattens the response." })); + let invert_map = pinescript.inputBool(false, "Invert mapping (strong cycle → longer length)", ({ group: grp_map })); let grp_src = "Source / Smoothing"; - let src = input.source(close, "Price Source", ({ group: grp_src })); - let smooth_rsi = input.int(3, "Post Smoothing (EMA on RSI)", ({ group: grp_src, minval: 1, maxval: 50 })); + let src = pinescript.inputSource(close, "Price Source", ({ group: grp_src })); + let smooth_rsi = pinescript.inputInt(3, "Post Smoothing (EMA on RSI)", ({ group: grp_src, minval: 1, maxval: 50 })); function f_clamp_float(x, lo, hi) { return ((x < lo) ? lo : ((x > hi) ? hi : x)); } @@ -1237,10 +1717,10 @@ function main() { let _win = pinescript.max(win, 2); let _per = pinescript.max(period, 2); for (let i = 0; i <= (_win - 1); i++) { - let x = pinescript.offset(_src, i); + let x = pinescript.hist(0, _src, i); let t = (bar_index - i); let ang = (((2 * pinescript.math.pi) * (t % _per)) / _per); - let y = (cosBasis ? math.cos(ang) : math.sin(ang)); + let y = (cosBasis ? pinescript.cos(ang) : pinescript.sin(ang)); state.sumx = x; state.sumy = y; state.sumx2 = (x * x); @@ -1268,15 +1748,15 @@ function main() { return f_clamp_float(L, lenLo, lenHi); } function f_rsi_custom(_src, len_t) { - let chg = (_src - pinescript.offset(_src, 1)); + let chg = (_src - pinescript.hist(1, _src, 1)); let up = pinescript.max(chg, 0); let dn = pinescript.max(-chg, 0); L = pinescript.max(len_t, 1); let a = (1 / L); if (state.avgU === undefined) state.avgU = null; if (state.avgD === undefined) state.avgD = null; - state.avgU = (pinescript.na(pinescript.offset(state.avgU, 1)) ? up : (((1 - a) * pinescript.offset(state.avgU, 1)) + (a * up))); - state.avgD = (pinescript.na(pinescript.offset(state.avgD, 1)) ? dn : (((1 - a) * pinescript.offset(state.avgD, 1)) + (a * dn))); + state.avgU = (pinescript.na(pinescript.hist(2, state.avgU, 1)) ? up : (((1 - a) * pinescript.hist(3, state.avgU, 1)) + (a * up))); + state.avgD = (pinescript.na(pinescript.hist(4, state.avgD, 1)) ? dn : (((1 - a) * pinescript.hist(5, state.avgD, 1)) + (a * dn))); let rs = ((state.avgD <= 1e-10) ? 1000000 : (state.avgU / state.avgD)); let rsi = (100 - (100 / (1 + rs))); return f_clamp_float(rsi, 0, 100); @@ -1285,10 +1765,10 @@ function main() { L = pinescript.max(len, 1); a = (2 / (L + 1)); if (state.s === undefined) state.s = null; - state.s = (pinescript.na(pinescript.offset(state.s, 1)) ? x : (((1 - a) * pinescript.offset(state.s, 1)) + (a * x))); + state.s = (pinescript.na(pinescript.hist(6, state.s, 1)) ? x : (((1 - a) * pinescript.hist(7, state.s, 1)) + (a * x))); return state.s; } - let ret = (math.log(src) - math.log(pinescript.offset(src, 1))); + let ret = (pinescript.log(src) - pinescript.log(pinescript.hist(8, src, 1))); ret = (pinescript.na(ret) ? 0 : ret); let mag1 = f_corr_mag_phase_free(ret, cycle_len, target_period); let mag2 = (use_harmonic2 ? f_corr_mag_phase_free(ret, cycle_len, (target_period * 2)) : 0); @@ -1343,7 +1823,67 @@ function main() { } -// Export for use -export { main +// The bar-by-bar engine: runs main() once per bar over the dataset. +function run(data, options = {}) { + data = data || {}; + const inClose = (data.close || []).map(Number); + const n = inClose.length; + const inOpen = (data.open || inClose).map(Number); + const inHigh = (data.high || inClose).map(Number); + const inLow = (data.low || inClose).map(Number); + const inVol = (data.volume || new Array(n).fill(0)).map(Number); + const inTime = (data.time || inClose.map((_, i) => i)).map(Number); + + // Reset all persistent and per-run state so repeated runs are independent. + globalThis.__pineState = {}; + globalThis.__pineRuntime = { plots: {}, plotshapes: {}, alerts: [], bars: n, inputs: options.inputs || {} }; + pinescript.__rt = {}; + pinescript.__bar = 0; + + // Growing OHLCV windows: each is a SeriesArray that gains one element per bar, + // so window built-ins like sma and highest see history up to the current bar. + const O = pinescript.asSeries([]), H = pinescript.asSeries([]), L = pinescript.asSeries([]); + const C = pinescript.asSeries([]), V = pinescript.asSeries([]), T = pinescript.asSeries([]); + const HL2 = pinescript.asSeries([]), HLC3 = pinescript.asSeries([]), OHLC4 = pinescript.asSeries([]); + globalThis.open = O; globalThis.high = H; globalThis.low = L; globalThis.close = C; + globalThis.volume = V; globalThis.time = T; + globalThis.hl2 = HL2; globalThis.hlc3 = HLC3; globalThis.ohlc4 = OHLC4; + globalThis.syminfo = globalThis.syminfo || { tickerid: 'SYNTHETIC', ticker: 'SYNTHETIC', mintick: 0.01 }; + + const rt = globalThis.__pineRuntime; + for (let i = 0; i < n; i++) { + O.push(inOpen[i]); H.push(inHigh[i]); L.push(inLow[i]); C.push(inClose[i]); V.push(inVol[i]); T.push(inTime[i]); + HL2.push((inHigh[i] + inLow[i]) / 2); + HLC3.push((inHigh[i] + inLow[i] + inClose[i]) / 3); + OHLC4.push((inOpen[i] + inHigh[i] + inLow[i] + inClose[i]) / 4); + + pinescript.__bar = i; + rt.__barIndex = i; + rt.__plotIdx = 0; + rt.__shapeIdx = 0; + globalThis.bar_index = i; + globalThis.last_bar_index = n - 1; + globalThis.barstate = { + isfirst: i === 0, islast: i === n - 1, isrealtime: false, ishistory: true, + isconfirmed: true, isnew: true, islastconfirmedhistory: i === n - 1, + }; + + main(); + } + + // Backfill skipped bars so every output series has exactly n entries. + for (const k of Object.keys(rt.plots)) { + const d = rt.plots[k].data; + for (let i = 0; i < n; i++) if (d[i] === undefined) d[i] = null; + } + for (const k of Object.keys(rt.plotshapes)) { + const d = rt.plotshapes[k].data; + for (let i = 0; i < n; i++) if (d[i] === undefined) d[i] = false; + } + return rt; +} + + +export { main, run }; diff --git a/converted/Golden_Pocket_Zones.js b/converted/Golden_Pocket_Zones.js index c372524..e2acaba 100644 --- a/converted/Golden_Pocket_Zones.js +++ b/converted/Golden_Pocket_Zones.js @@ -1,12 +1,63 @@ -// PineScript to JavaScript Transpiled Code +// Auto-generated JavaScript from PineScript source -// Generated automatically +// Do not edit by hand -- re-run the converter instead -// Built-in PineScript functions +// All the built-in PineScript functions that scripts depend on at runtime const pinescript = { + __scalar: function(v) { + if (v === null || v === undefined) return v; + if (typeof v === 'object' && typeof v.valueOf === 'function') { + const s = v.valueOf(); + if (typeof s === 'number' || typeof s === 'boolean' || s === null) return s; + } + return v; + }, + series: function(id, value) { + const rt = this.__rt || (this.__rt = {}); + const store = rt.series || (rt.series = {}); + let buf = store[id]; + if (!buf) buf = store[id] = []; + buf[this.__bar | 0] = this.__scalar(value); + return buf; + }, + hist: function(id, value, n) { + const buf = this.series(id, value); + const idx = (this.__bar | 0) - (n | 0); + return idx >= 0 && idx < buf.length ? buf[idx] : null; + }, + time: function(timeframe, session) { + const t = globalThis.time; + if (t && typeof t.valueOf === 'function') return t.valueOf(); + return Array.isArray(t) ? t[t.length - 1] : t; + }, + sign: function(x) { + if (x === null || x === undefined || (typeof x === 'number' && isNaN(x))) return null; + return Math.sign(Number(x)); + }, + unpack: function(value, count) { + if (Array.isArray(value)) return value; + if (value != null && typeof value[Symbol.iterator] === 'function') return Array.from(value); + return new Array(count || 0).fill(null); + }, + alertcondition: function(condition, ...rest) { + if (globalThis.__pineRuntime) { + globalThis.__pineRuntime.alerts.push({ condition, args: rest }); + } + return null; + }, + barcolor: function(color) { return null; }, + bgcolor: function(color, title, editable, showLast) { + return null; + }, + fill: function(series1, series2, color, title, editable) { + return null; + }, + hline: function(_price, _title, _opts) { + return null; + }, sma: function(series, length) { if (series === null || series === undefined) return null; if (!Array.isArray(series)) series = [series]; @@ -84,6 +135,13 @@ const pinescript = { } return weightSum > 0 ? sum / weightSum : null; }, + swma: function(series) { + if (series === null || series === undefined) return null; + if (!Array.isArray(series)) series = [series]; + if (series.length < 4) return null; + const s = series.slice(-4); + return (s[0] * 1 + s[1] * 2 + s[2] * 2 + s[3] * 1) / 6; + }, atr: function(high, low, close, length = 14) { if (!Array.isArray(high) || !Array.isArray(low) || !Array.isArray(close)) return null; if (high.length < length + 1 || low.length < length + 1 || close.length < length + 1) return null; @@ -114,6 +172,53 @@ const pinescript = { const atr = builtins.get('atr')(high, low, close, length); return { middle: sma, upper: sma + mult * atr, lower: sma - mult * atr }; }, + supertrend: function(factor, atrPeriod, high, low, close) { + if (!Array.isArray(high) || !Array.isArray(low) || !Array.isArray(close)) return [null, null]; + if (high.length < atrPeriod + 1) return [null, null]; + const atrVal = builtins.get('atr')(high, low, close, atrPeriod); + if (atrVal === null) return [null, null]; + const lastIdx = close.length - 1; + const hl2 = (high[lastIdx] + low[lastIdx]) / 2; + const upperBand = hl2 + factor * atrVal; + const lowerBand = hl2 - factor * atrVal; + // Determine direction: 1 means downtrend (price below band), -1 means uptrend + const direction = close[lastIdx] > upperBand ? -1 : close[lastIdx] < lowerBand ? 1 : -1; + const supertrendValue = direction === -1 ? lowerBand : upperBand; + return [supertrendValue, direction]; + }, + ta_supertrend: function(factor, atrPeriod) { + const H = globalThis.high; + const L = globalThis.low; + const C = globalThis.close; + return builtins.get('supertrend')(factor, atrPeriod, H, L, C); + }, + dmi: function(diLength, adxSmoothing, high, low, close) { + if (!Array.isArray(high) || !Array.isArray(low) || !Array.isArray(close)) return { plusDI: null, minusDI: null, adx: null }; + if (high.length < diLength + 1) return { plusDI: null, minusDI: null, adx: null }; + let plusDMSum = 0, minusDMSum = 0, trSum = 0; + for (let i = 1; i <= diLength; i++) { + const idx = high.length - 1 - diLength + i; + const upMove = high[idx] - high[idx - 1]; + const downMove = low[idx - 1] - low[idx]; + plusDMSum += (upMove > downMove && upMove > 0) ? upMove : 0; + minusDMSum += (downMove > upMove && downMove > 0) ? downMove : 0; + const tr = Math.max( + high[idx] - low[idx], + Math.abs(high[idx] - close[idx - 1]), + Math.abs(low[idx] - close[idx - 1]) + ); + trSum += tr; + } + const plusDI = trSum > 0 ? (plusDMSum / trSum) * 100 : 0; + const minusDI = trSum > 0 ? (minusDMSum / trSum) * 100 : 0; + const diSum = plusDI + minusDI; + const dx = diSum > 0 ? Math.abs(plusDI - minusDI) / diSum * 100 : 0; + return { plusDI, minusDI, adx: dx }; + }, + adx: function(diLength, adxSmoothing, high, low, close) { + const result = builtins.get('dmi')(diLength, adxSmoothing, high, low, close); + return result ? result.adx : null; + }, rsi: function(close, length = 14) { if (close === null || close === undefined) return null; if (!Array.isArray(close)) close = [close]; @@ -171,9 +276,7 @@ const pinescript = { return { macd: macdLine, signal: signalLine, histogram: histogram }; }, cci: function(a, b, c, d) { - // Pine signatures: - // - ta.cci(source, length) - // - ta.cci(high, low, close, length) + // Supports both ta.cci(source, length) and ta.cci(high, low, close, length) signatures let source; let length; if (Array.isArray(a) && typeof b === 'number' && c === undefined) { @@ -304,6 +407,15 @@ const pinescript = { stdev: function(source, length) { return Math.sqrt(builtins.get('variance')(source, length)); }, + median: function(source, length) { + if (!source || source.length < length) return null; + const sorted = [...source.slice(-length)].sort((a, b) => a - b); + const mid = Math.floor(sorted.length / 2); + return sorted.length % 2 !== 0 ? sorted[mid] : (sorted[mid - 1] + sorted[mid]) / 2; + }, + ta_median: function(source, length) { + return builtins.get('median')(source, length); + }, hl2: function(high, low) { return high && low ? (high[high.length - 1] + low[low.length - 1]) / 2 : null; }, @@ -420,6 +532,24 @@ const pinescript = { return (series1[i - 1] < series2[i - 1] && series1[i] >= series2[i]) || (series1[i - 1] > series2[i - 1] && series1[i] <= series2[i]); }, + crossover: function(series1, series2) { + if (series1 === null || series1 === undefined) return false; + if (series2 === null || series2 === undefined) return false; + if (!Array.isArray(series1)) series1 = [series1]; + if (!Array.isArray(series2)) series2 = [series2]; + if (series1.length < 2 || series2.length < 2) return false; + const i = series1.length - 1; + return series1[i - 1] < series2[i - 1] && series1[i] >= series2[i]; + }, + crossunder: function(series1, series2) { + if (series1 === null || series1 === undefined) return false; + if (series2 === null || series2 === undefined) return false; + if (!Array.isArray(series1)) series1 = [series1]; + if (!Array.isArray(series2)) series2 = [series2]; + if (series1.length < 2 || series2.length < 2) return false; + const i = series1.length - 1; + return series1[i - 1] > series2[i - 1] && series1[i] <= series2[i]; + }, offset: function(series, offset) { if (!Array.isArray(series)) return null; const idx = series.length - 1 - offset; @@ -500,12 +630,14 @@ const pinescript = { return builtins.get('nz')(value, replacement); }, plot: function(series, title = '', color = null, linewidth = 1) { - globalThis.__pineRuntime.plots.push({ - series, - title, - color, - linewidth - }); + const rt = globalThis.__pineRuntime; + if (!rt) return series; + const bar = rt.__barIndex | 0; + const ord = (rt.__plotIdx = (rt.__plotIdx | 0) + 1) - 1; + const key = (title && String(title)) || ('plot_' + ord); + let p = rt.plots[key]; + if (!p) p = rt.plots[key] = { title: key, color, linewidth, data: [] }; + p.data[bar] = this.__scalar(series); return series; }, lineNew: function(x1, y1, x2, y2, opts = {}) { @@ -514,20 +646,72 @@ const pinescript = { lineDelete: function(l) { return null; }, - labelNew: function(...args) { - if (globalThis.__pineRuntime) { - globalThis.__pineRuntime.labels = globalThis.__pineRuntime.labels || []; - globalThis.__pineRuntime.labels.push({ args }); - } - return { args }; + lineSetXY: function(line, point, x, y) { + if (!line) return null; + if (point === 0 || point === 'x1') { line.x1 = x; line.y1 = y; } + else { line.x2 = x; line.y2 = y; } + return line; + }, + lineGetX: function(line, point) { + if (!line) return null; + return point === 0 || point === 'x1' ? line.x1 : line.x2; + }, + lineGetY: function(line, point) { + if (!line) return null; + return point === 0 || point === 'y1' ? line.y1 : line.y2; + }, + labelNew: function(x, y, text = '', opts = {}) { + return { x, y, text, opts, _type: 'label' }; }, labelDelete: function(l) { return null; }, + labelSetText: function(label, text) { + if (!label) return null; + label.text = text; + return label; + }, + labelGetText: function(label) { + if (!label) return ''; + return label.text || ''; + }, + boxNew: function(left, top, right, bottom, opts = {}) { + return { left, top, right, bottom, opts, _type: 'box' }; + }, + boxDelete: function(box) { + return null; + }, + boxSetLeftTop: function(box, left, top) { + if (!box) return null; + box.left = left; + box.top = top; + return box; + }, + boxSetRightBottom: function(box, right, bottom) { + if (!box) return null; + box.right = right; + box.bottom = bottom; + return box; + }, + polylineNew: function(points, opts = {}) { + return { points: points || [], opts, _type: 'polyline' }; + }, + polylineDelete: function(poly) { + return null; + }, plotshape: function(condition, ...rest) { - if (globalThis.__pineRuntime) { - globalThis.__pineRuntime.plotshapes.push({ condition, args: rest }); + const rt = globalThis.__pineRuntime; + if (!rt) return condition; + const bar = rt.__barIndex | 0; + const ord = (rt.__shapeIdx = (rt.__shapeIdx | 0) + 1) - 1; + let key = 'shape_' + ord; + for (const r of rest) { + if (typeof r === 'string') { key = r; break; } + if (r && typeof r === 'object' && r.title) { key = String(r.title); break; } } + let s = rt.plotshapes[key]; + if (!s) s = rt.plotshapes[key] = { title: key, data: [] }; + s.data[bar] = !!this.__scalar(condition); return condition; }, indicator: function(title, shorttitle, overlay, opts) { @@ -542,27 +726,12 @@ const pinescript = { } return null; }, - alertcondition: function(condition, ...rest) { - if (globalThis.__pineRuntime) { - globalThis.__pineRuntime.alerts.push({ condition, args: rest }); - } - return null; - }, plotbar: function(open, high, low, close, title, color, editable, showLast) { return { open, high, low, close }; }, plotcandle: function(open, high, low, close, title, color, wickColor, borderColor, editable, showLast) { return { open, high, low, close }; }, - hline: function(_price, _title, _opts) { - return null; - }, - bgcolor: function(color, title, editable, showLast) { - return null; - }, - fill: function(series1, series2, color, title, editable) { - return null; - }, alert: function(condition, message, frequency) { console.log('Alert:', condition ? message : 'Condition not met'); return condition; @@ -828,6 +997,9 @@ const pinescript = { arrayIndexOf: function(arr, value) { return arr ? arr.indexOf(value) : -1; }, + arrayLastIndexOf: function(arr, value) { + return arr ? arr.lastIndexOf(value) : -1; + }, arraySum: function(arr) { return arr ? arr.reduce((a, b) => a + b, 0) : 0; }, @@ -840,6 +1012,42 @@ const pinescript = { arrayMax: function(arr) { return arr && arr.length > 0 ? Math.max(...arr) : null; }, + arrayStdev: function(arr) { + if (!arr || arr.length === 0) return null; + const mean = arr.reduce((a, b) => a + b, 0) / arr.length; + const variance = arr.reduce((sum, val) => sum + Math.pow(val - mean, 2), 0) / arr.length; + return Math.sqrt(variance); + }, + arrayVariance: function(arr) { + if (!arr || arr.length === 0) return null; + const mean = arr.reduce((a, b) => a + b, 0) / arr.length; + return arr.reduce((sum, val) => sum + Math.pow(val - mean, 2), 0) / arr.length; + }, + arrayCovariance: function(arr1, arr2) { + if (!arr1 || !arr2 || arr1.length === 0 || arr1.length !== arr2.length) return null; + const mean1 = arr1.reduce((a, b) => a + b, 0) / arr1.length; + const mean2 = arr2.reduce((a, b) => a + b, 0) / arr2.length; + let cov = 0; + for (let i = 0; i < arr1.length; i++) { + cov += (arr1[i] - mean1) * (arr2[i] - mean2); + } + return cov / arr1.length; + }, + mode: function(arr) { + if (!arr || arr.length === 0) return null; + const counts = new Map(); + let maxCount = 0; + let modeVal = arr[0]; + for (const val of arr) { + const c = (counts.get(val) || 0) + 1; + counts.set(val, c); + if (c > maxCount) { + maxCount = c; + modeVal = val; + } + } + return modeVal; + }, strLength: function(str) { return str ? str.length : 0; }, @@ -895,6 +1103,84 @@ const pinescript = { strReverse: function(str) { return str ? str.split('').reverse().join('') : ''; }, + strFormat: function(formatStr, ...args) { + if (!formatStr) return ''; + return formatStr.replace(/\{(\d+)\}/g, (match, idx) => { + const i = parseInt(idx, 10); + return i < args.length ? String(args[i]) : match; + }); + }, + input: function(defval, title, tooltip, inline, group) { + return defval; + }, + inputInt: function(defval = 0, title, minval, maxval, step, tooltip, inline, group) { + return defval; + }, + inputFloat: function(defval = 0.0, title, minval, maxval, step, tooltip, inline, group) { + return defval; + }, + inputBool: function(defval = false, title, tooltip, inline, group) { + return defval; + }, + inputString: function(defval = '', title, options, tooltip, inline, group) { + return defval; + }, + inputSource: function(defval = null, title, tooltip, inline, group) { + return defval; + }, + inputColor: function(defval = '#000000', title, tooltip, inline, group) { + return defval; + }, + inputTime: function(defval = 0, title, tooltip, inline, group) { + return defval; + }, + mathSign: function(value) { + return Math.sign(value); + }, + mathAvg: function(...args) { + const valid = args.filter(v => v !== null && v !== undefined && !isNaN(v)); + return valid.length > 0 ? valid.reduce((a, b) => a + b, 0) / valid.length : null; + }, + mathSum: function(series, length) { + if (!series || series.length < length) return null; + return series.slice(-length).reduce((a, b) => a + b, 0); + }, + mathRandom: function(min = 0, max = 1) { + return min + Math.random() * (max - min); + }, + mathTodegrees: function(radians) { + return radians * (180 / Math.PI); + }, + mathToradians: function(degrees) { + return degrees * (Math.PI / 180); + }, + colorT: function(color, transparency) { + if (transparency !== undefined) { + return { color, transparency }; + } + if (color && typeof color === 'object' && 'transparency' in color) { + return color.transparency; + } + return 0; + }, + tableCellSetText: function(table, column, row, text) { + return null; + }, + tableCellSetBgcolor: function(table, column, row, bgcolor) { + return null; + }, + tableMergeCells: function(table, startColumn, startRow, endColumn, endRow) { + return null; + }, + tableDelete: function(table) { + return null; + }, + tableClear: function(table, startColumn, startRow, endColumn, endRow) { + return null; + }, + maxBarsBack: function(series, length) { + return null; + }, pi: 3.141592653589793, e: 2.718281828459045, true: true, @@ -905,8 +1191,7 @@ const pinescript = { const info = { ticker: 'AAPL', tickerid: 'NASDAQ:AAPL', prefix: 'NASDAQ', root: 'AAPL', suffix: '' }; return info[type] || ''; }, - time: 1771178051405, - timenow: 1771178051405, + timenow: 1781574527061, barstate: "LAST", dividends: {}, splits: {}, @@ -923,7 +1208,7 @@ const pinescript = { return null; }, tr: function(high, low, close) { - // Pine signature: ta.tr(true) + // Handles both the boolean shorthand ta.tr(true) and explicit high/low/close arguments if (typeof high === 'boolean') { const H = globalThis.high; const L = globalThis.low; @@ -934,7 +1219,7 @@ const pinescript = { return Math.max(H[i] - L[i], Math.abs(H[i] - prevClose), Math.abs(L[i] - prevClose)); } - // Fallback: treat inputs as scalars for current bar + // When arrays are passed, compute true range from the last bar using the previous close if (Array.isArray(close)) { const prevClose = close[close.length - 2] || close[0]; const currHigh = Array.isArray(high) ? high[high.length - 1] : high; @@ -945,6 +1230,92 @@ const pinescript = { return Math.max(high - low, Math.abs(high - close), Math.abs(low - close)); }, + arrayFirst: function(arr) { + if (!arr || arr.length === 0) return null; + return arr[0]; + }, + arrayLast: function(arr) { + if (!arr || arr.length === 0) return null; + return arr[arr.length - 1]; + }, + arrayJoin: function(arr, separator = ',') { + if (!arr) return ''; + return arr.join(separator); + }, + arrayConcat: function(arr1, arr2) { + if (!arr1) return arr2 || []; + if (!arr2) return arr1; + return arr1.concat(arr2); + }, + arrayCopy: function(arr) { + if (!arr) return []; + return [...arr]; + }, + arrayBinarySearch: function(arr, value) { + if (!arr || arr.length === 0) return -1; + let lo = 0, hi = arr.length - 1; + while (lo <= hi) { + const mid = (lo + hi) >>> 1; + if (arr[mid] === value) return mid; + if (arr[mid] < value) lo = mid + 1; + else hi = mid - 1; + } + return -1; + }, + arrayRange: function(arr) { + if (!arr || arr.length === 0) return 0; + return Math.max(...arr) - Math.min(...arr); + }, + arrayMedian: function(arr) { + if (!arr || arr.length === 0) return null; + const sorted = [...arr].sort((a, b) => a - b); + const mid = Math.floor(sorted.length / 2); + return sorted.length % 2 !== 0 ? sorted[mid] : (sorted[mid - 1] + sorted[mid]) / 2; + }, + arrayMode: function(arr) { + if (!arr || arr.length === 0) return null; + const freq = {}; + let maxCount = 0; + let mode = arr[0]; + for (const v of arr) { + freq[v] = (freq[v] || 0) + 1; + if (freq[v] > maxCount) { maxCount = freq[v]; mode = v; } + } + return mode; + }, + arrayPercentile: function(arr, percentile) { + if (!arr || arr.length === 0) return null; + const sorted = [...arr].sort((a, b) => a - b); + const idx = (percentile / 100) * (sorted.length - 1); + const lo = Math.floor(idx); + const hi = Math.ceil(idx); + if (lo === hi) return sorted[lo]; + return sorted[lo] + (sorted[hi] - sorted[lo]) * (idx - lo); + }, + arrayPercentileLinearInterpolation: function(arr, percentile) { + return builtins.get('arrayPercentile')(arr, percentile); + }, + arrayPercentileNearestRank: function(arr, percentile) { + if (!arr || arr.length === 0) return null; + const sorted = [...arr].sort((a, b) => a - b); + const idx = Math.ceil((percentile / 100) * sorted.length) - 1; + return sorted[Math.max(0, idx)]; + }, + arrayAbs: function(arr) { + if (!arr) return []; + return arr.map(v => Math.abs(v)); + }, + arrayEvery: function(arr, callback) { + if (!arr) return false; + return arr.every(callback); + }, + arraySome: function(arr, callback) { + if (!arr) return false; + return arr.some(callback); + }, + requestFinancial: function() { + return null; + }, }; @@ -1101,6 +1472,115 @@ globalThis.barmerge = globalThis.barmerge || { }; +function __pineNS(real) { + + const base = real || {}; + + return new Proxy(base, { + + get(target, key) { + + if (key in target) return target[key]; + + if (typeof key === "symbol") return target[key]; + + // Unknown member: usable as a value (null), a constant, or a no-op function. + + const noop = function() { return null; }; + + noop.toString = () => ""; + + return noop; + + } + + }); + +} + +globalThis.ta = globalThis.ta || __pineNS({}); + +globalThis.math = globalThis.math || __pineNS({ pi: Math.PI, e: Math.E, phi: 1.618033988749895, rphi: 0.618033988749895 }); + +globalThis.chart = globalThis.chart || __pineNS({ point: __pineNS({}), bg_color: null, fg_color: null }); + +globalThis.line = globalThis.line || __pineNS({ style_solid: "solid", style_dashed: "dashed", style_dotted: "dotted" }); + +globalThis.box = globalThis.box || __pineNS({}); + +globalThis.label = globalThis.label || __pineNS({ style_label_down: "label_down", style_label_up: "label_up", style_none: "none" }); + +globalThis.polyline = globalThis.polyline || __pineNS({}); + +globalThis.linefill = globalThis.linefill || __pineNS({}); + +globalThis.matrix = globalThis.matrix || __pineNS({}); + +globalThis.map = globalThis.map || __pineNS({}); + +globalThis.session = globalThis.session || __pineNS({ regular: "regular", extended: "extended" }); + +globalThis.dayofweek = globalThis.dayofweek || __pineNS({ sunday: 1, monday: 2, tuesday: 3, wednesday: 4, thursday: 5, friday: 6, saturday: 7 }); + +globalThis.timeframe = __pineNS(Object.assign(globalThis.timeframe || {}, { period: (globalThis.timeframe && globalThis.timeframe.period) || "D", isintraday: false, isdaily: true, multiplier: 1 })); + +globalThis.request = __pineNS(globalThis.request || {}); + +globalThis.input = __pineNS(globalThis.input || {}); + +globalThis.adjustment = globalThis.adjustment || __pineNS({}); + +globalThis.earnings = globalThis.earnings || __pineNS({}); + +globalThis.dividends = globalThis.dividends || __pineNS({}); + +globalThis.splits = globalThis.splits || __pineNS({}); + +globalThis.currency = globalThis.currency || __pineNS({}); + +globalThis.display = globalThis.display || __pineNS({ none: "none", all: "all", pane: "pane", data_window: "data_window", status_line: "status_line", price_scale: "price_scale" }); + +globalThis.format = globalThis.format || __pineNS({ price: "price", volume: "volume", percent: "percent", mintick: "mintick", inherit: "inherit" }); + +globalThis.scale = globalThis.scale || __pineNS({ left: "left", right: "right", none: "none" }); + +globalThis.font = globalThis.font || __pineNS({ family_default: "default", family_monospace: "monospace" }); + +globalThis.xloc = globalThis.xloc || __pineNS({ bar_index: "bar_index", bar_time: "bar_time" }); + +globalThis.yloc = globalThis.yloc || __pineNS({ price: "price", abovebar: "abovebar", belowbar: "belowbar" }); + +globalThis.extend = globalThis.extend || __pineNS({ none: "none", left: "left", right: "right", both: "both" }); + +globalThis.order = globalThis.order || __pineNS({ ascending: "ascending", descending: "descending" }); + +globalThis.alert = globalThis.alert || Object.assign(function() { return null; }, { freq_once_per_bar: "once_per_bar", freq_once_per_bar_close: "once_per_bar_close", freq_all: "all" }); + +globalThis.dayofmonth = globalThis.dayofmonth || function(t) { return new Date(t || 0).getUTCDate(); }; + +globalThis.plot = globalThis.plot || __pineNS({ style_line: "line", style_linebr: "linebr", style_stepline: "stepline", style_histogram: "histogram", style_cross: "cross", style_area: "area", style_areabr: "areabr", style_columns: "columns", style_circles: "circles" }); + +globalThis.int = globalThis.int || function(x) { return x == null ? null : Math.trunc(Number(x)); }; + +globalThis.float = globalThis.float || function(x) { return x == null ? null : Number(x); }; + +globalThis.bool = globalThis.bool || function(x) { return Boolean(x); }; + +globalThis.string = globalThis.string || function(x) { return x == null ? null : String(x); }; + +globalThis.dayofweek = globalThis.dayofweek || __pineNS({ sunday: 1, monday: 2, tuesday: 3, wednesday: 4, thursday: 5, friday: 6, saturday: 7 }); + +globalThis.str = globalThis.str || __pineNS({}); + +pinescript.math = globalThis.math; + +pinescript.ta = globalThis.ta; + +pinescript.str = globalThis.str; + +pinescript.array = __pineNS(globalThis.array); + + pinescript.color = { hex: function(s) { @@ -1190,11 +1670,11 @@ pinescript.table = { }; -// Input parameters +// Script input parameters and their defaults const ZoneData = { new: function(upper, lower, startBar, age, strength) { return { upper: upper, lower: lower, startBar: startBar, age: age, strength: strength }; } }; -// Main script logic +// The transpiled script logic, called once per bar function main() { @@ -1209,13 +1689,13 @@ function main() { null; // Study: Golden Pocket Zones // Options: {"overlay":true,"max_boxes_count":500} - let lookbackDays = input.int(20, "Lookback Days", ({ minval: 1, maxval: 100, tooltip: "Number of daily candles to analyze for golden pocket zones" })); - let zoneHeightPct = input.float(3, "Zone Height (% of ADR10)", ({ minval: 0.1, maxval: 50, step: 0.1, tooltip: "Height of golden pocket zone as percentage of ADR10 - SAME for all zones" })); - let minCandleSizePct = input.float(60, "Min Candle Size (% of ADR10)", ({ minval: 0, maxval: 200, step: 5, tooltip: "Only show zones for candles larger than this % of ADR10. Set to 0 to show all zones." })); - let showLabels = input.bool(true, "Show Labels", ({ tooltip: "Display age and strength labels inside zones" })); - let zoneColor = input.color(pinescript.color.new(pinescript.color.hex("#FFB84D"), 20), "Zone Color", ({ tooltip: "Color of the golden pocket zones" })); - let textColor = input.color(pinescript.color.new(pinescript.color.hex("#000000"), 0), "Text Color", ({ tooltip: "Color of the text labels" })); - let [dailyHigh, dailyLow, dailyTime] = pinescript.requestSecurity(syminfo.tickerid, "D", [high, low, time], ({ lookahead: barmerge.lookahead_off })); + let lookbackDays = pinescript.inputInt(20, "Lookback Days", ({ minval: 1, maxval: 100, tooltip: "Number of daily candles to analyze for golden pocket zones" })); + let zoneHeightPct = pinescript.inputFloat(3, "Zone Height (% of ADR10)", ({ minval: 0.1, maxval: 50, step: 0.1, tooltip: "Height of golden pocket zone as percentage of ADR10 - SAME for all zones" })); + let minCandleSizePct = pinescript.inputFloat(60, "Min Candle Size (% of ADR10)", ({ minval: 0, maxval: 200, step: 5, tooltip: "Only show zones for candles larger than this % of ADR10. Set to 0 to show all zones." })); + let showLabels = pinescript.inputBool(true, "Show Labels", ({ tooltip: "Display age and strength labels inside zones" })); + let zoneColor = pinescript.inputColor(pinescript.color.new(pinescript.color.hex("#FFB84D"), 20), "Zone Color", ({ tooltip: "Color of the golden pocket zones" })); + let textColor = pinescript.inputColor(pinescript.color.new(pinescript.color.hex("#000000"), 0), "Text Color", ({ tooltip: "Color of the text labels" })); + let [dailyHigh, dailyLow, dailyTime] = pinescript.unpack(pinescript.requestSecurity(syminfo.tickerid, "D", [high, low, time], ({ lookahead: barmerge.lookahead_off })), 3); let adr10 = pinescript.requestSecurity(syminfo.tickerid, "D", pinescript.sma((high - low), 10), ({ lookahead: barmerge.lookahead_off })); if (state.lastDailyTime === undefined) state.lastDailyTime = 0; if (state.lastDailyStartBar === undefined) state.lastDailyStartBar = null; @@ -1305,7 +1785,7 @@ function main() { let candleSize = (dayHigh - dayLow); let candleSizePct = ((candleSize / dayADR) * 100); if ((candleSizePct >= minCandleSizePct)) { - let [upper, lower, range] = calcGoldenPocket(dayHigh, dayLow, fixedZoneHeight); + let [upper, lower, range] = pinescript.unpack(calcGoldenPocket(dayHigh, dayLow, fixedZoneHeight), 3); strength = calcStrength(candleSize, dayADR); let newZone = ZoneData.new(upper, lower, dayBar, i, strength); pinescript.arrayPush(tempZones, newZone); @@ -1326,7 +1806,7 @@ function main() { if (!pinescript.arrayGet(mergedIndices, j)) { let zone2 = pinescript.arrayGet(tempZones, j); if (zonesOverlap(zone1.upper, zone1.lower, zone2.upper, zone2.lower)) { - let [mergedUpper, mergedLower] = mergeZones(zone1.upper, zone1.lower, zone2.upper, zone2.lower); + let [mergedUpper, mergedLower] = pinescript.unpack(mergeZones(zone1.upper, zone1.lower, zone2.upper, zone2.lower), 2); zone1.upper = mergedUpper; zone1.lower = mergedLower; zone1.age = pinescript.max(zone1.age, zone2.age); @@ -1346,7 +1826,7 @@ function main() { if ((barstate.islast && (pinescript.arraySize(state.zones) > 0))) { if ((pinescript.arraySize(box.all) > 0)) { for (const existingBox of box.all) { - box.delete(existingBox); + pinescript.boxDelete(existingBox); } } if ((pinescript.arraySize(label.all) > 0)) { @@ -1401,7 +1881,7 @@ function main() { let heatmapColor = pinescript.color.new(pinescript.color.rgb(r, g, b), 20); let maxLookback = pinescript.min(2000, bar_index); let safeStartBar = pinescript.max(zone.startBar, (bar_index - maxLookback)); - let boxId = box.new(({ left: safeStartBar, top: zone.upper, right: (bar_index + 50), bottom: zone.lower, border_color: pinescript.color.new(heatmapColor, 70), border_width: 1, bgcolor: heatmapColor, extend: extend.right })); + let boxId = pinescript.boxNew(({ left: safeStartBar, top: zone.upper, right: (bar_index + 50), bottom: zone.lower, border_color: pinescript.color.new(heatmapColor, 70), border_width: 1, bgcolor: heatmapColor, extend: extend.right })); if (showLabels) { let ageText = ((zone.age === 0) ? "TODAY" : (pinescript.strToString(zone.age) + " DAYS")); let strengthText = (pinescript.strToString(zone.strength, "#") + "/10"); @@ -1430,7 +1910,67 @@ function main() { } -// Export for use -export { main +// The bar-by-bar engine: runs main() once per bar over the dataset. +function run(data, options = {}) { + data = data || {}; + const inClose = (data.close || []).map(Number); + const n = inClose.length; + const inOpen = (data.open || inClose).map(Number); + const inHigh = (data.high || inClose).map(Number); + const inLow = (data.low || inClose).map(Number); + const inVol = (data.volume || new Array(n).fill(0)).map(Number); + const inTime = (data.time || inClose.map((_, i) => i)).map(Number); + + // Reset all persistent and per-run state so repeated runs are independent. + globalThis.__pineState = {}; + globalThis.__pineRuntime = { plots: {}, plotshapes: {}, alerts: [], bars: n, inputs: options.inputs || {} }; + pinescript.__rt = {}; + pinescript.__bar = 0; + + // Growing OHLCV windows: each is a SeriesArray that gains one element per bar, + // so window built-ins like sma and highest see history up to the current bar. + const O = pinescript.asSeries([]), H = pinescript.asSeries([]), L = pinescript.asSeries([]); + const C = pinescript.asSeries([]), V = pinescript.asSeries([]), T = pinescript.asSeries([]); + const HL2 = pinescript.asSeries([]), HLC3 = pinescript.asSeries([]), OHLC4 = pinescript.asSeries([]); + globalThis.open = O; globalThis.high = H; globalThis.low = L; globalThis.close = C; + globalThis.volume = V; globalThis.time = T; + globalThis.hl2 = HL2; globalThis.hlc3 = HLC3; globalThis.ohlc4 = OHLC4; + globalThis.syminfo = globalThis.syminfo || { tickerid: 'SYNTHETIC', ticker: 'SYNTHETIC', mintick: 0.01 }; + + const rt = globalThis.__pineRuntime; + for (let i = 0; i < n; i++) { + O.push(inOpen[i]); H.push(inHigh[i]); L.push(inLow[i]); C.push(inClose[i]); V.push(inVol[i]); T.push(inTime[i]); + HL2.push((inHigh[i] + inLow[i]) / 2); + HLC3.push((inHigh[i] + inLow[i] + inClose[i]) / 3); + OHLC4.push((inOpen[i] + inHigh[i] + inLow[i] + inClose[i]) / 4); + + pinescript.__bar = i; + rt.__barIndex = i; + rt.__plotIdx = 0; + rt.__shapeIdx = 0; + globalThis.bar_index = i; + globalThis.last_bar_index = n - 1; + globalThis.barstate = { + isfirst: i === 0, islast: i === n - 1, isrealtime: false, ishistory: true, + isconfirmed: true, isnew: true, islastconfirmedhistory: i === n - 1, + }; + + main(); + } + + // Backfill skipped bars so every output series has exactly n entries. + for (const k of Object.keys(rt.plots)) { + const d = rt.plots[k].data; + for (let i = 0; i < n; i++) if (d[i] === undefined) d[i] = null; + } + for (const k of Object.keys(rt.plotshapes)) { + const d = rt.plotshapes[k].data; + for (let i = 0; i < n; i++) if (d[i] === undefined) d[i] = false; + } + return rt; +} + + +export { main, run }; diff --git a/converted/Hyperfork_Matrix.js b/converted/Hyperfork_Matrix.js index 4cd9af8..2f8e282 100644 --- a/converted/Hyperfork_Matrix.js +++ b/converted/Hyperfork_Matrix.js @@ -1,12 +1,63 @@ -// PineScript to JavaScript Transpiled Code +// Auto-generated JavaScript from PineScript source -// Generated automatically +// Do not edit by hand -- re-run the converter instead -// Built-in PineScript functions +// All the built-in PineScript functions that scripts depend on at runtime const pinescript = { + __scalar: function(v) { + if (v === null || v === undefined) return v; + if (typeof v === 'object' && typeof v.valueOf === 'function') { + const s = v.valueOf(); + if (typeof s === 'number' || typeof s === 'boolean' || s === null) return s; + } + return v; + }, + series: function(id, value) { + const rt = this.__rt || (this.__rt = {}); + const store = rt.series || (rt.series = {}); + let buf = store[id]; + if (!buf) buf = store[id] = []; + buf[this.__bar | 0] = this.__scalar(value); + return buf; + }, + hist: function(id, value, n) { + const buf = this.series(id, value); + const idx = (this.__bar | 0) - (n | 0); + return idx >= 0 && idx < buf.length ? buf[idx] : null; + }, + time: function(timeframe, session) { + const t = globalThis.time; + if (t && typeof t.valueOf === 'function') return t.valueOf(); + return Array.isArray(t) ? t[t.length - 1] : t; + }, + sign: function(x) { + if (x === null || x === undefined || (typeof x === 'number' && isNaN(x))) return null; + return Math.sign(Number(x)); + }, + unpack: function(value, count) { + if (Array.isArray(value)) return value; + if (value != null && typeof value[Symbol.iterator] === 'function') return Array.from(value); + return new Array(count || 0).fill(null); + }, + alertcondition: function(condition, ...rest) { + if (globalThis.__pineRuntime) { + globalThis.__pineRuntime.alerts.push({ condition, args: rest }); + } + return null; + }, + barcolor: function(color) { return null; }, + bgcolor: function(color, title, editable, showLast) { + return null; + }, + fill: function(series1, series2, color, title, editable) { + return null; + }, + hline: function(_price, _title, _opts) { + return null; + }, sma: function(series, length) { if (series === null || series === undefined) return null; if (!Array.isArray(series)) series = [series]; @@ -84,6 +135,13 @@ const pinescript = { } return weightSum > 0 ? sum / weightSum : null; }, + swma: function(series) { + if (series === null || series === undefined) return null; + if (!Array.isArray(series)) series = [series]; + if (series.length < 4) return null; + const s = series.slice(-4); + return (s[0] * 1 + s[1] * 2 + s[2] * 2 + s[3] * 1) / 6; + }, atr: function(high, low, close, length = 14) { if (!Array.isArray(high) || !Array.isArray(low) || !Array.isArray(close)) return null; if (high.length < length + 1 || low.length < length + 1 || close.length < length + 1) return null; @@ -114,6 +172,53 @@ const pinescript = { const atr = builtins.get('atr')(high, low, close, length); return { middle: sma, upper: sma + mult * atr, lower: sma - mult * atr }; }, + supertrend: function(factor, atrPeriod, high, low, close) { + if (!Array.isArray(high) || !Array.isArray(low) || !Array.isArray(close)) return [null, null]; + if (high.length < atrPeriod + 1) return [null, null]; + const atrVal = builtins.get('atr')(high, low, close, atrPeriod); + if (atrVal === null) return [null, null]; + const lastIdx = close.length - 1; + const hl2 = (high[lastIdx] + low[lastIdx]) / 2; + const upperBand = hl2 + factor * atrVal; + const lowerBand = hl2 - factor * atrVal; + // Determine direction: 1 means downtrend (price below band), -1 means uptrend + const direction = close[lastIdx] > upperBand ? -1 : close[lastIdx] < lowerBand ? 1 : -1; + const supertrendValue = direction === -1 ? lowerBand : upperBand; + return [supertrendValue, direction]; + }, + ta_supertrend: function(factor, atrPeriod) { + const H = globalThis.high; + const L = globalThis.low; + const C = globalThis.close; + return builtins.get('supertrend')(factor, atrPeriod, H, L, C); + }, + dmi: function(diLength, adxSmoothing, high, low, close) { + if (!Array.isArray(high) || !Array.isArray(low) || !Array.isArray(close)) return { plusDI: null, minusDI: null, adx: null }; + if (high.length < diLength + 1) return { plusDI: null, minusDI: null, adx: null }; + let plusDMSum = 0, minusDMSum = 0, trSum = 0; + for (let i = 1; i <= diLength; i++) { + const idx = high.length - 1 - diLength + i; + const upMove = high[idx] - high[idx - 1]; + const downMove = low[idx - 1] - low[idx]; + plusDMSum += (upMove > downMove && upMove > 0) ? upMove : 0; + minusDMSum += (downMove > upMove && downMove > 0) ? downMove : 0; + const tr = Math.max( + high[idx] - low[idx], + Math.abs(high[idx] - close[idx - 1]), + Math.abs(low[idx] - close[idx - 1]) + ); + trSum += tr; + } + const plusDI = trSum > 0 ? (plusDMSum / trSum) * 100 : 0; + const minusDI = trSum > 0 ? (minusDMSum / trSum) * 100 : 0; + const diSum = plusDI + minusDI; + const dx = diSum > 0 ? Math.abs(plusDI - minusDI) / diSum * 100 : 0; + return { plusDI, minusDI, adx: dx }; + }, + adx: function(diLength, adxSmoothing, high, low, close) { + const result = builtins.get('dmi')(diLength, adxSmoothing, high, low, close); + return result ? result.adx : null; + }, rsi: function(close, length = 14) { if (close === null || close === undefined) return null; if (!Array.isArray(close)) close = [close]; @@ -171,9 +276,7 @@ const pinescript = { return { macd: macdLine, signal: signalLine, histogram: histogram }; }, cci: function(a, b, c, d) { - // Pine signatures: - // - ta.cci(source, length) - // - ta.cci(high, low, close, length) + // Supports both ta.cci(source, length) and ta.cci(high, low, close, length) signatures let source; let length; if (Array.isArray(a) && typeof b === 'number' && c === undefined) { @@ -304,6 +407,15 @@ const pinescript = { stdev: function(source, length) { return Math.sqrt(builtins.get('variance')(source, length)); }, + median: function(source, length) { + if (!source || source.length < length) return null; + const sorted = [...source.slice(-length)].sort((a, b) => a - b); + const mid = Math.floor(sorted.length / 2); + return sorted.length % 2 !== 0 ? sorted[mid] : (sorted[mid - 1] + sorted[mid]) / 2; + }, + ta_median: function(source, length) { + return builtins.get('median')(source, length); + }, hl2: function(high, low) { return high && low ? (high[high.length - 1] + low[low.length - 1]) / 2 : null; }, @@ -420,6 +532,24 @@ const pinescript = { return (series1[i - 1] < series2[i - 1] && series1[i] >= series2[i]) || (series1[i - 1] > series2[i - 1] && series1[i] <= series2[i]); }, + crossover: function(series1, series2) { + if (series1 === null || series1 === undefined) return false; + if (series2 === null || series2 === undefined) return false; + if (!Array.isArray(series1)) series1 = [series1]; + if (!Array.isArray(series2)) series2 = [series2]; + if (series1.length < 2 || series2.length < 2) return false; + const i = series1.length - 1; + return series1[i - 1] < series2[i - 1] && series1[i] >= series2[i]; + }, + crossunder: function(series1, series2) { + if (series1 === null || series1 === undefined) return false; + if (series2 === null || series2 === undefined) return false; + if (!Array.isArray(series1)) series1 = [series1]; + if (!Array.isArray(series2)) series2 = [series2]; + if (series1.length < 2 || series2.length < 2) return false; + const i = series1.length - 1; + return series1[i - 1] > series2[i - 1] && series1[i] <= series2[i]; + }, offset: function(series, offset) { if (!Array.isArray(series)) return null; const idx = series.length - 1 - offset; @@ -500,12 +630,14 @@ const pinescript = { return builtins.get('nz')(value, replacement); }, plot: function(series, title = '', color = null, linewidth = 1) { - globalThis.__pineRuntime.plots.push({ - series, - title, - color, - linewidth - }); + const rt = globalThis.__pineRuntime; + if (!rt) return series; + const bar = rt.__barIndex | 0; + const ord = (rt.__plotIdx = (rt.__plotIdx | 0) + 1) - 1; + const key = (title && String(title)) || ('plot_' + ord); + let p = rt.plots[key]; + if (!p) p = rt.plots[key] = { title: key, color, linewidth, data: [] }; + p.data[bar] = this.__scalar(series); return series; }, lineNew: function(x1, y1, x2, y2, opts = {}) { @@ -514,20 +646,72 @@ const pinescript = { lineDelete: function(l) { return null; }, - labelNew: function(...args) { - if (globalThis.__pineRuntime) { - globalThis.__pineRuntime.labels = globalThis.__pineRuntime.labels || []; - globalThis.__pineRuntime.labels.push({ args }); - } - return { args }; + lineSetXY: function(line, point, x, y) { + if (!line) return null; + if (point === 0 || point === 'x1') { line.x1 = x; line.y1 = y; } + else { line.x2 = x; line.y2 = y; } + return line; + }, + lineGetX: function(line, point) { + if (!line) return null; + return point === 0 || point === 'x1' ? line.x1 : line.x2; + }, + lineGetY: function(line, point) { + if (!line) return null; + return point === 0 || point === 'y1' ? line.y1 : line.y2; + }, + labelNew: function(x, y, text = '', opts = {}) { + return { x, y, text, opts, _type: 'label' }; }, labelDelete: function(l) { return null; }, + labelSetText: function(label, text) { + if (!label) return null; + label.text = text; + return label; + }, + labelGetText: function(label) { + if (!label) return ''; + return label.text || ''; + }, + boxNew: function(left, top, right, bottom, opts = {}) { + return { left, top, right, bottom, opts, _type: 'box' }; + }, + boxDelete: function(box) { + return null; + }, + boxSetLeftTop: function(box, left, top) { + if (!box) return null; + box.left = left; + box.top = top; + return box; + }, + boxSetRightBottom: function(box, right, bottom) { + if (!box) return null; + box.right = right; + box.bottom = bottom; + return box; + }, + polylineNew: function(points, opts = {}) { + return { points: points || [], opts, _type: 'polyline' }; + }, + polylineDelete: function(poly) { + return null; + }, plotshape: function(condition, ...rest) { - if (globalThis.__pineRuntime) { - globalThis.__pineRuntime.plotshapes.push({ condition, args: rest }); + const rt = globalThis.__pineRuntime; + if (!rt) return condition; + const bar = rt.__barIndex | 0; + const ord = (rt.__shapeIdx = (rt.__shapeIdx | 0) + 1) - 1; + let key = 'shape_' + ord; + for (const r of rest) { + if (typeof r === 'string') { key = r; break; } + if (r && typeof r === 'object' && r.title) { key = String(r.title); break; } } + let s = rt.plotshapes[key]; + if (!s) s = rt.plotshapes[key] = { title: key, data: [] }; + s.data[bar] = !!this.__scalar(condition); return condition; }, indicator: function(title, shorttitle, overlay, opts) { @@ -542,27 +726,12 @@ const pinescript = { } return null; }, - alertcondition: function(condition, ...rest) { - if (globalThis.__pineRuntime) { - globalThis.__pineRuntime.alerts.push({ condition, args: rest }); - } - return null; - }, plotbar: function(open, high, low, close, title, color, editable, showLast) { return { open, high, low, close }; }, plotcandle: function(open, high, low, close, title, color, wickColor, borderColor, editable, showLast) { return { open, high, low, close }; }, - hline: function(_price, _title, _opts) { - return null; - }, - bgcolor: function(color, title, editable, showLast) { - return null; - }, - fill: function(series1, series2, color, title, editable) { - return null; - }, alert: function(condition, message, frequency) { console.log('Alert:', condition ? message : 'Condition not met'); return condition; @@ -828,6 +997,9 @@ const pinescript = { arrayIndexOf: function(arr, value) { return arr ? arr.indexOf(value) : -1; }, + arrayLastIndexOf: function(arr, value) { + return arr ? arr.lastIndexOf(value) : -1; + }, arraySum: function(arr) { return arr ? arr.reduce((a, b) => a + b, 0) : 0; }, @@ -840,6 +1012,42 @@ const pinescript = { arrayMax: function(arr) { return arr && arr.length > 0 ? Math.max(...arr) : null; }, + arrayStdev: function(arr) { + if (!arr || arr.length === 0) return null; + const mean = arr.reduce((a, b) => a + b, 0) / arr.length; + const variance = arr.reduce((sum, val) => sum + Math.pow(val - mean, 2), 0) / arr.length; + return Math.sqrt(variance); + }, + arrayVariance: function(arr) { + if (!arr || arr.length === 0) return null; + const mean = arr.reduce((a, b) => a + b, 0) / arr.length; + return arr.reduce((sum, val) => sum + Math.pow(val - mean, 2), 0) / arr.length; + }, + arrayCovariance: function(arr1, arr2) { + if (!arr1 || !arr2 || arr1.length === 0 || arr1.length !== arr2.length) return null; + const mean1 = arr1.reduce((a, b) => a + b, 0) / arr1.length; + const mean2 = arr2.reduce((a, b) => a + b, 0) / arr2.length; + let cov = 0; + for (let i = 0; i < arr1.length; i++) { + cov += (arr1[i] - mean1) * (arr2[i] - mean2); + } + return cov / arr1.length; + }, + mode: function(arr) { + if (!arr || arr.length === 0) return null; + const counts = new Map(); + let maxCount = 0; + let modeVal = arr[0]; + for (const val of arr) { + const c = (counts.get(val) || 0) + 1; + counts.set(val, c); + if (c > maxCount) { + maxCount = c; + modeVal = val; + } + } + return modeVal; + }, strLength: function(str) { return str ? str.length : 0; }, @@ -895,6 +1103,84 @@ const pinescript = { strReverse: function(str) { return str ? str.split('').reverse().join('') : ''; }, + strFormat: function(formatStr, ...args) { + if (!formatStr) return ''; + return formatStr.replace(/\{(\d+)\}/g, (match, idx) => { + const i = parseInt(idx, 10); + return i < args.length ? String(args[i]) : match; + }); + }, + input: function(defval, title, tooltip, inline, group) { + return defval; + }, + inputInt: function(defval = 0, title, minval, maxval, step, tooltip, inline, group) { + return defval; + }, + inputFloat: function(defval = 0.0, title, minval, maxval, step, tooltip, inline, group) { + return defval; + }, + inputBool: function(defval = false, title, tooltip, inline, group) { + return defval; + }, + inputString: function(defval = '', title, options, tooltip, inline, group) { + return defval; + }, + inputSource: function(defval = null, title, tooltip, inline, group) { + return defval; + }, + inputColor: function(defval = '#000000', title, tooltip, inline, group) { + return defval; + }, + inputTime: function(defval = 0, title, tooltip, inline, group) { + return defval; + }, + mathSign: function(value) { + return Math.sign(value); + }, + mathAvg: function(...args) { + const valid = args.filter(v => v !== null && v !== undefined && !isNaN(v)); + return valid.length > 0 ? valid.reduce((a, b) => a + b, 0) / valid.length : null; + }, + mathSum: function(series, length) { + if (!series || series.length < length) return null; + return series.slice(-length).reduce((a, b) => a + b, 0); + }, + mathRandom: function(min = 0, max = 1) { + return min + Math.random() * (max - min); + }, + mathTodegrees: function(radians) { + return radians * (180 / Math.PI); + }, + mathToradians: function(degrees) { + return degrees * (Math.PI / 180); + }, + colorT: function(color, transparency) { + if (transparency !== undefined) { + return { color, transparency }; + } + if (color && typeof color === 'object' && 'transparency' in color) { + return color.transparency; + } + return 0; + }, + tableCellSetText: function(table, column, row, text) { + return null; + }, + tableCellSetBgcolor: function(table, column, row, bgcolor) { + return null; + }, + tableMergeCells: function(table, startColumn, startRow, endColumn, endRow) { + return null; + }, + tableDelete: function(table) { + return null; + }, + tableClear: function(table, startColumn, startRow, endColumn, endRow) { + return null; + }, + maxBarsBack: function(series, length) { + return null; + }, pi: 3.141592653589793, e: 2.718281828459045, true: true, @@ -905,8 +1191,7 @@ const pinescript = { const info = { ticker: 'AAPL', tickerid: 'NASDAQ:AAPL', prefix: 'NASDAQ', root: 'AAPL', suffix: '' }; return info[type] || ''; }, - time: 1771178051501, - timenow: 1771178051501, + timenow: 1781574527160, barstate: "LAST", dividends: {}, splits: {}, @@ -923,7 +1208,7 @@ const pinescript = { return null; }, tr: function(high, low, close) { - // Pine signature: ta.tr(true) + // Handles both the boolean shorthand ta.tr(true) and explicit high/low/close arguments if (typeof high === 'boolean') { const H = globalThis.high; const L = globalThis.low; @@ -934,7 +1219,7 @@ const pinescript = { return Math.max(H[i] - L[i], Math.abs(H[i] - prevClose), Math.abs(L[i] - prevClose)); } - // Fallback: treat inputs as scalars for current bar + // When arrays are passed, compute true range from the last bar using the previous close if (Array.isArray(close)) { const prevClose = close[close.length - 2] || close[0]; const currHigh = Array.isArray(high) ? high[high.length - 1] : high; @@ -945,6 +1230,92 @@ const pinescript = { return Math.max(high - low, Math.abs(high - close), Math.abs(low - close)); }, + arrayFirst: function(arr) { + if (!arr || arr.length === 0) return null; + return arr[0]; + }, + arrayLast: function(arr) { + if (!arr || arr.length === 0) return null; + return arr[arr.length - 1]; + }, + arrayJoin: function(arr, separator = ',') { + if (!arr) return ''; + return arr.join(separator); + }, + arrayConcat: function(arr1, arr2) { + if (!arr1) return arr2 || []; + if (!arr2) return arr1; + return arr1.concat(arr2); + }, + arrayCopy: function(arr) { + if (!arr) return []; + return [...arr]; + }, + arrayBinarySearch: function(arr, value) { + if (!arr || arr.length === 0) return -1; + let lo = 0, hi = arr.length - 1; + while (lo <= hi) { + const mid = (lo + hi) >>> 1; + if (arr[mid] === value) return mid; + if (arr[mid] < value) lo = mid + 1; + else hi = mid - 1; + } + return -1; + }, + arrayRange: function(arr) { + if (!arr || arr.length === 0) return 0; + return Math.max(...arr) - Math.min(...arr); + }, + arrayMedian: function(arr) { + if (!arr || arr.length === 0) return null; + const sorted = [...arr].sort((a, b) => a - b); + const mid = Math.floor(sorted.length / 2); + return sorted.length % 2 !== 0 ? sorted[mid] : (sorted[mid - 1] + sorted[mid]) / 2; + }, + arrayMode: function(arr) { + if (!arr || arr.length === 0) return null; + const freq = {}; + let maxCount = 0; + let mode = arr[0]; + for (const v of arr) { + freq[v] = (freq[v] || 0) + 1; + if (freq[v] > maxCount) { maxCount = freq[v]; mode = v; } + } + return mode; + }, + arrayPercentile: function(arr, percentile) { + if (!arr || arr.length === 0) return null; + const sorted = [...arr].sort((a, b) => a - b); + const idx = (percentile / 100) * (sorted.length - 1); + const lo = Math.floor(idx); + const hi = Math.ceil(idx); + if (lo === hi) return sorted[lo]; + return sorted[lo] + (sorted[hi] - sorted[lo]) * (idx - lo); + }, + arrayPercentileLinearInterpolation: function(arr, percentile) { + return builtins.get('arrayPercentile')(arr, percentile); + }, + arrayPercentileNearestRank: function(arr, percentile) { + if (!arr || arr.length === 0) return null; + const sorted = [...arr].sort((a, b) => a - b); + const idx = Math.ceil((percentile / 100) * sorted.length) - 1; + return sorted[Math.max(0, idx)]; + }, + arrayAbs: function(arr) { + if (!arr) return []; + return arr.map(v => Math.abs(v)); + }, + arrayEvery: function(arr, callback) { + if (!arr) return false; + return arr.every(callback); + }, + arraySome: function(arr, callback) { + if (!arr) return false; + return arr.some(callback); + }, + requestFinancial: function() { + return null; + }, }; @@ -1101,6 +1472,115 @@ globalThis.barmerge = globalThis.barmerge || { }; +function __pineNS(real) { + + const base = real || {}; + + return new Proxy(base, { + + get(target, key) { + + if (key in target) return target[key]; + + if (typeof key === "symbol") return target[key]; + + // Unknown member: usable as a value (null), a constant, or a no-op function. + + const noop = function() { return null; }; + + noop.toString = () => ""; + + return noop; + + } + + }); + +} + +globalThis.ta = globalThis.ta || __pineNS({}); + +globalThis.math = globalThis.math || __pineNS({ pi: Math.PI, e: Math.E, phi: 1.618033988749895, rphi: 0.618033988749895 }); + +globalThis.chart = globalThis.chart || __pineNS({ point: __pineNS({}), bg_color: null, fg_color: null }); + +globalThis.line = globalThis.line || __pineNS({ style_solid: "solid", style_dashed: "dashed", style_dotted: "dotted" }); + +globalThis.box = globalThis.box || __pineNS({}); + +globalThis.label = globalThis.label || __pineNS({ style_label_down: "label_down", style_label_up: "label_up", style_none: "none" }); + +globalThis.polyline = globalThis.polyline || __pineNS({}); + +globalThis.linefill = globalThis.linefill || __pineNS({}); + +globalThis.matrix = globalThis.matrix || __pineNS({}); + +globalThis.map = globalThis.map || __pineNS({}); + +globalThis.session = globalThis.session || __pineNS({ regular: "regular", extended: "extended" }); + +globalThis.dayofweek = globalThis.dayofweek || __pineNS({ sunday: 1, monday: 2, tuesday: 3, wednesday: 4, thursday: 5, friday: 6, saturday: 7 }); + +globalThis.timeframe = __pineNS(Object.assign(globalThis.timeframe || {}, { period: (globalThis.timeframe && globalThis.timeframe.period) || "D", isintraday: false, isdaily: true, multiplier: 1 })); + +globalThis.request = __pineNS(globalThis.request || {}); + +globalThis.input = __pineNS(globalThis.input || {}); + +globalThis.adjustment = globalThis.adjustment || __pineNS({}); + +globalThis.earnings = globalThis.earnings || __pineNS({}); + +globalThis.dividends = globalThis.dividends || __pineNS({}); + +globalThis.splits = globalThis.splits || __pineNS({}); + +globalThis.currency = globalThis.currency || __pineNS({}); + +globalThis.display = globalThis.display || __pineNS({ none: "none", all: "all", pane: "pane", data_window: "data_window", status_line: "status_line", price_scale: "price_scale" }); + +globalThis.format = globalThis.format || __pineNS({ price: "price", volume: "volume", percent: "percent", mintick: "mintick", inherit: "inherit" }); + +globalThis.scale = globalThis.scale || __pineNS({ left: "left", right: "right", none: "none" }); + +globalThis.font = globalThis.font || __pineNS({ family_default: "default", family_monospace: "monospace" }); + +globalThis.xloc = globalThis.xloc || __pineNS({ bar_index: "bar_index", bar_time: "bar_time" }); + +globalThis.yloc = globalThis.yloc || __pineNS({ price: "price", abovebar: "abovebar", belowbar: "belowbar" }); + +globalThis.extend = globalThis.extend || __pineNS({ none: "none", left: "left", right: "right", both: "both" }); + +globalThis.order = globalThis.order || __pineNS({ ascending: "ascending", descending: "descending" }); + +globalThis.alert = globalThis.alert || Object.assign(function() { return null; }, { freq_once_per_bar: "once_per_bar", freq_once_per_bar_close: "once_per_bar_close", freq_all: "all" }); + +globalThis.dayofmonth = globalThis.dayofmonth || function(t) { return new Date(t || 0).getUTCDate(); }; + +globalThis.plot = globalThis.plot || __pineNS({ style_line: "line", style_linebr: "linebr", style_stepline: "stepline", style_histogram: "histogram", style_cross: "cross", style_area: "area", style_areabr: "areabr", style_columns: "columns", style_circles: "circles" }); + +globalThis.int = globalThis.int || function(x) { return x == null ? null : Math.trunc(Number(x)); }; + +globalThis.float = globalThis.float || function(x) { return x == null ? null : Number(x); }; + +globalThis.bool = globalThis.bool || function(x) { return Boolean(x); }; + +globalThis.string = globalThis.string || function(x) { return x == null ? null : String(x); }; + +globalThis.dayofweek = globalThis.dayofweek || __pineNS({ sunday: 1, monday: 2, tuesday: 3, wednesday: 4, thursday: 5, friday: 6, saturday: 7 }); + +globalThis.str = globalThis.str || __pineNS({}); + +pinescript.math = globalThis.math; + +pinescript.ta = globalThis.ta; + +pinescript.str = globalThis.str; + +pinescript.array = __pineNS(globalThis.array); + + pinescript.color = { hex: function(s) { @@ -1190,10 +1670,10 @@ pinescript.table = { }; -// Input parameters +// Script input parameters and their defaults -// Main script logic +// The transpiled script logic, called once per bar function main() { @@ -1210,42 +1690,42 @@ function main() { // Options: {"overlay":true,"max_lines_count":500,"max_labels_count":500} let MAX_BAR_EXTENSION = 500; let group_pivots = "1. Pivot Points"; - let start_pivot_type = input.string("Auto", ({ title: "Starting Pivot Type", options: ["Auto", "Low", "High"], group: group_pivots, tooltip: "Auto: detect pattern from prices | Low: force Low-High-Low | High: force High-Low-High" })); - let pivotA_time = input.time(({ title: "Pivot A Time", defval: 0, group: group_pivots, tooltip: "Select the timestamp for the starting pivot (A, the anchor point)", confirm: true })); - let pivotB_time = input.time(({ title: "Pivot B Time", defval: 0, group: group_pivots, tooltip: "Select the timestamp for Pivot B (the first swing point after A)", confirm: true })); - let pivotC_time = input.time(({ title: "Pivot C Time", defval: 0, group: group_pivots, tooltip: "Select the timestamp for Pivot C (the second swing point after B)", confirm: true })); - let show_pivot_labels = input.bool(true, ({ title: "Show Pivot Labels", group: group_pivots, tooltip: "Show A, B, C labels at pivot points" })); - let labelColorH = input.color(pinescript.color.lime, "High Pivot Color", ({ group: group_pivots, inline: "pivot_colors" })); - let labelColorL = input.color(pinescript.color.red, "Low Pivot Color", ({ group: group_pivots, inline: "pivot_colors" })); - let pivot_label_size = input.string("Normal", ({ title: "Size", options: ["Tiny", "Small", "Normal", "Large"], group: group_pivots, tooltip: "Text size for pivot labels" })); + let start_pivot_type = pinescript.inputString("Auto", ({ title: "Starting Pivot Type", options: ["Auto", "Low", "High"], group: group_pivots, tooltip: "Auto: detect pattern from prices | Low: force Low-High-Low | High: force High-Low-High" })); + let pivotA_time = pinescript.inputTime(({ title: "Pivot A Time", defval: 0, group: group_pivots, tooltip: "Select the timestamp for the starting pivot (A, the anchor point)", confirm: true })); + let pivotB_time = pinescript.inputTime(({ title: "Pivot B Time", defval: 0, group: group_pivots, tooltip: "Select the timestamp for Pivot B (the first swing point after A)", confirm: true })); + let pivotC_time = pinescript.inputTime(({ title: "Pivot C Time", defval: 0, group: group_pivots, tooltip: "Select the timestamp for Pivot C (the second swing point after B)", confirm: true })); + let show_pivot_labels = pinescript.inputBool(true, ({ title: "Show Pivot Labels", group: group_pivots, tooltip: "Show A, B, C labels at pivot points" })); + let labelColorH = pinescript.inputColor(pinescript.color.lime, "High Pivot Color", ({ group: group_pivots, inline: "pivot_colors" })); + let labelColorL = pinescript.inputColor(pinescript.color.red, "Low Pivot Color", ({ group: group_pivots, inline: "pivot_colors" })); + let pivot_label_size = pinescript.inputString("Normal", ({ title: "Size", options: ["Tiny", "Small", "Normal", "Large"], group: group_pivots, tooltip: "Text size for pivot labels" })); let group_pitchfork = "2. Pitchfork Settings"; - let use_log = input.bool(false, ({ title: "Logarithmic Scale", group: group_pitchfork, tooltip: "Enable to support logarithmic chart scaling. Calculations will be performed in log space for proper line rendering on log charts." })); - let pitchfork_type = input.string(({ title: "Pitchfork Type", defval: "Original", options: ["Original", "Schiff", "Modified Schiff"], group: group_pitchfork, tooltip: "Original: anchor at A | Schiff: anchor shifted 50% Y toward B | Modified Schiff: anchor shifted 50% X+Y toward B" })); - let extra_levels = input.int(({ title: "Extra Parallel Levels", defval: 0, minval: 0, group: group_pitchfork, tooltip: "Number of additional parallel lines outside the main pitchfork" })); - let line_color = input.color(pinescript.color.lime, ({ title: "Pitchfork Color", group: group_pitchfork, inline: "pitchfork", tooltip: "Color for the pitchfork lines (median and parallels)" })); - let line_width = input.int(1, ({ title: "Width", minval: 1, group: group_pitchfork, inline: "pitchfork", tooltip: "Width for the pitchfork lines" })); - let line_style = input.string("Solid", ({ title: "Style", options: ["Solid", "Dashed", "Dotted"], group: group_pitchfork, inline: "pitchfork", tooltip: "Style for the pitchfork lines" })); - let extend_dir = input.string(({ title: "Extend Direction", defval: "Right", options: ["Right", "Both"], group: group_pitchfork, tooltip: "Direction to extend the pitchfork lines: Right, or Both" })); - let enable_price_range = input.bool(true, ({ title: "Enable Price Range Filter", group: group_pitchfork, tooltip: "When enabled, extra parallels outside the price range are hidden." })); - let price_range_min = input.float(0, ({ title: "Price Range Min", minval: 0, group: group_pitchfork, tooltip: "Minimum price for pitchfork elements. Extra parallels below this will be removed." })); - let price_range_max = input.float(1000000, ({ title: "Price Range Max", minval: 0, group: group_pitchfork, tooltip: "Maximum price for pitchfork elements. Extra parallels above this will be removed." })); + let use_log = pinescript.inputBool(false, ({ title: "Logarithmic Scale", group: group_pitchfork, tooltip: "Enable to support logarithmic chart scaling. Calculations will be performed in log space for proper line rendering on log charts." })); + let pitchfork_type = pinescript.inputString(({ title: "Pitchfork Type", defval: "Original", options: ["Original", "Schiff", "Modified Schiff"], group: group_pitchfork, tooltip: "Original: anchor at A | Schiff: anchor shifted 50% Y toward B | Modified Schiff: anchor shifted 50% X+Y toward B" })); + let extra_levels = pinescript.inputInt(({ title: "Extra Parallel Levels", defval: 0, minval: 0, group: group_pitchfork, tooltip: "Number of additional parallel lines outside the main pitchfork" })); + let line_color = pinescript.inputColor(pinescript.color.lime, ({ title: "Pitchfork Color", group: group_pitchfork, inline: "pitchfork", tooltip: "Color for the pitchfork lines (median and parallels)" })); + let line_width = pinescript.inputInt(1, ({ title: "Width", minval: 1, group: group_pitchfork, inline: "pitchfork", tooltip: "Width for the pitchfork lines" })); + let line_style = pinescript.inputString("Solid", ({ title: "Style", options: ["Solid", "Dashed", "Dotted"], group: group_pitchfork, inline: "pitchfork", tooltip: "Style for the pitchfork lines" })); + let extend_dir = pinescript.inputString(({ title: "Extend Direction", defval: "Right", options: ["Right", "Both"], group: group_pitchfork, tooltip: "Direction to extend the pitchfork lines: Right, or Both" })); + let enable_price_range = pinescript.inputBool(true, ({ title: "Enable Price Range Filter", group: group_pitchfork, tooltip: "When enabled, extra parallels outside the price range are hidden." })); + let price_range_min = pinescript.inputFloat(0, ({ title: "Price Range Min", minval: 0, group: group_pitchfork, tooltip: "Minimum price for pitchfork elements. Extra parallels below this will be removed." })); + let price_range_max = pinescript.inputFloat(1000000, ({ title: "Price Range Max", minval: 0, group: group_pitchfork, tooltip: "Maximum price for pitchfork elements. Extra parallels above this will be removed." })); let group_action_reaction = "3. Action / Reaction Lines"; - let draw_type = input.string("Both", ({ title: "Draw Action/Reaction Lines", options: ["None", "Action Only", "Reaction Only", "Both"], group: group_action_reaction, tooltip: "Choose which lines to draw: None, Action Only, Reaction Only, or Both" })); - let forward_count = input.int(({ title: "Forward Lines Offset", defval: 0, group: group_action_reaction, tooltip: "Offset from auto-extend. 0 = to current bar. Positive adds more, negative removes." })); - let backward_count = input.int(({ title: "Backward Lines Count", defval: 0, minval: 0, group: group_action_reaction, tooltip: "Number of lines backward from the median" })); - let action_color = input.color(pinescript.color.aqua, ({ title: "Action Lines Color", group: group_action_reaction, inline: "action", tooltip: "Color for the action transversal lines" })); - let action_width = input.int(1, ({ title: "Width", minval: 1, group: group_action_reaction, inline: "action", tooltip: "Width for the action transversal lines" })); - let action_style = input.string("Dotted", ({ title: "Style", options: ["Solid", "Dashed", "Dotted"], group: group_action_reaction, inline: "action", tooltip: "Style for the action transversal lines" })); - let reaction_color = input.color(pinescript.color.hex("#ff8c69"), ({ title: "Reaction Lines Color", group: group_action_reaction, inline: "reaction", tooltip: "Color for the reaction transversal lines" })); - let reaction_width = input.int(1, ({ title: "Width", minval: 1, group: group_action_reaction, inline: "reaction", tooltip: "Width for the reaction transversal lines" })); - let reaction_style = input.string("Dotted", ({ title: "Style", options: ["Solid", "Dashed", "Dotted"], group: group_action_reaction, inline: "reaction", tooltip: "Style for the reaction transversal lines" })); + let draw_type = pinescript.inputString("Both", ({ title: "Draw Action/Reaction Lines", options: ["None", "Action Only", "Reaction Only", "Both"], group: group_action_reaction, tooltip: "Choose which lines to draw: None, Action Only, Reaction Only, or Both" })); + let forward_count = pinescript.inputInt(({ title: "Forward Lines Offset", defval: 0, group: group_action_reaction, tooltip: "Offset from auto-extend. 0 = to current bar. Positive adds more, negative removes." })); + let backward_count = pinescript.inputInt(({ title: "Backward Lines Count", defval: 0, minval: 0, group: group_action_reaction, tooltip: "Number of lines backward from the median" })); + let action_color = pinescript.inputColor(pinescript.color.aqua, ({ title: "Action Lines Color", group: group_action_reaction, inline: "action", tooltip: "Color for the action transversal lines" })); + let action_width = pinescript.inputInt(1, ({ title: "Width", minval: 1, group: group_action_reaction, inline: "action", tooltip: "Width for the action transversal lines" })); + let action_style = pinescript.inputString("Dotted", ({ title: "Style", options: ["Solid", "Dashed", "Dotted"], group: group_action_reaction, inline: "action", tooltip: "Style for the action transversal lines" })); + let reaction_color = pinescript.inputColor(pinescript.color.hex("#ff8c69"), ({ title: "Reaction Lines Color", group: group_action_reaction, inline: "reaction", tooltip: "Color for the reaction transversal lines" })); + let reaction_width = pinescript.inputInt(1, ({ title: "Width", minval: 1, group: group_action_reaction, inline: "reaction", tooltip: "Width for the reaction transversal lines" })); + let reaction_style = pinescript.inputString("Dotted", ({ title: "Style", options: ["Solid", "Dashed", "Dotted"], group: group_action_reaction, inline: "reaction", tooltip: "Style for the reaction transversal lines" })); let group_lattice = "4. Lattice"; - let draw_lattice = input.bool(false, ({ title: "Draw Lattice", group: group_lattice, tooltip: "Enable drawing of horizontal and vertical lattice lines based on pivots and intersections" })); - let lattice_pivot = input.string("A", ({ title: "Select Pivot for Horizontal", options: ["A", "B", "C"], group: group_lattice, tooltip: "Choose which pivot's horizontal line to plot" })); - let lattice_source = input.string("Pitchfork & Parallels", ({ title: "Intersection Source", options: ["Pitchfork & Parallels", "Action Lines", "Reaction Lines", "Action & Reaction"], group: group_lattice, tooltip: "Choose the source for intersections: Pitchfork & parallels, Action lines, Reaction lines, or both Action & Reaction" })); - let lattice_color = input.color(pinescript.color.white, ({ title: "Lattice Color", group: group_lattice, inline: "lattice", tooltip: "Color for the lattice lines (horizontals and verticals)" })); - let lattice_width = input.int(1, ({ title: "Width", minval: 1, group: group_lattice, inline: "lattice", tooltip: "Width for the lattice lines" })); - let lattice_style = input.string("Dotted", ({ title: "Style", options: ["Solid", "Dashed", "Dotted"], group: group_lattice, inline: "lattice", tooltip: "Style for the lattice lines" })); + let draw_lattice = pinescript.inputBool(false, ({ title: "Draw Lattice", group: group_lattice, tooltip: "Enable drawing of horizontal and vertical lattice lines based on pivots and intersections" })); + let lattice_pivot = pinescript.inputString("A", ({ title: "Select Pivot for Horizontal", options: ["A", "B", "C"], group: group_lattice, tooltip: "Choose which pivot's horizontal line to plot" })); + let lattice_source = pinescript.inputString("Pitchfork & Parallels", ({ title: "Intersection Source", options: ["Pitchfork & Parallels", "Action Lines", "Reaction Lines", "Action & Reaction"], group: group_lattice, tooltip: "Choose the source for intersections: Pitchfork & parallels, Action lines, Reaction lines, or both Action & Reaction" })); + let lattice_color = pinescript.inputColor(pinescript.color.white, ({ title: "Lattice Color", group: group_lattice, inline: "lattice", tooltip: "Color for the lattice lines (horizontals and verticals)" })); + let lattice_width = pinescript.inputInt(1, ({ title: "Width", minval: 1, group: group_lattice, inline: "lattice", tooltip: "Width for the lattice lines" })); + let lattice_style = pinescript.inputString("Dotted", ({ title: "Style", options: ["Solid", "Dashed", "Dotted"], group: group_lattice, inline: "lattice", tooltip: "Style for the lattice lines" })); line.style_dotted; line.style_dashed; line.style_solid; @@ -1267,15 +1747,15 @@ function main() { pinescript.size.small; pinescript.size.tiny; let label_size = ((pivot_label_size === "Tiny") ? undefined : ((pivot_label_size === "Small") ? undefined : ((pivot_label_size === "Normal") ? undefined : ((pivot_label_size === "Large") ? undefined : null)))); - let pivotA_x = ta.valuewhen((time === pivotA_time), bar_index, 0); - let pivotB_x = ta.valuewhen((time === pivotB_time), bar_index, 0); - let pivotC_x = ta.valuewhen((time === pivotC_time), bar_index, 0); - let rawA_high = ta.valuewhen((time === pivotA_time), high, 0); - let rawA_low = ta.valuewhen((time === pivotA_time), low, 0); - let rawB_high = ta.valuewhen((time === pivotB_time), high, 0); - let rawB_low = ta.valuewhen((time === pivotB_time), low, 0); - let rawC_high = ta.valuewhen((time === pivotC_time), high, 0); - let rawC_low = ta.valuewhen((time === pivotC_time), low, 0); + let pivotA_x = pinescript.valuewhen(pinescript.series(0, (time === pivotA_time)), pinescript.series(1, bar_index), 0); + let pivotB_x = pinescript.valuewhen(pinescript.series(2, (time === pivotB_time)), pinescript.series(3, bar_index), 0); + let pivotC_x = pinescript.valuewhen(pinescript.series(4, (time === pivotC_time)), pinescript.series(5, bar_index), 0); + let rawA_high = pinescript.valuewhen(pinescript.series(6, (time === pivotA_time)), pinescript.series(7, high), 0); + let rawA_low = pinescript.valuewhen(pinescript.series(8, (time === pivotA_time)), pinescript.series(9, low), 0); + let rawB_high = pinescript.valuewhen(pinescript.series(10, (time === pivotB_time)), pinescript.series(11, high), 0); + let rawB_low = pinescript.valuewhen(pinescript.series(12, (time === pivotB_time)), pinescript.series(13, low), 0); + let rawC_high = pinescript.valuewhen(pinescript.series(14, (time === pivotC_time)), pinescript.series(15, high), 0); + let rawC_low = pinescript.valuewhen(pinescript.series(16, (time === pivotC_time)), pinescript.series(17, low), 0); let pivotA_type = null; let pivotB_type = null; let pivotC_type = null; @@ -1299,9 +1779,9 @@ function main() { let pivotA_raw = ((pivotA_type === "High") ? rawA_high : rawA_low); let pivotB_raw = ((pivotB_type === "High") ? rawB_high : rawB_low); let pivotC_raw = ((pivotC_type === "High") ? rawC_high : rawC_low); - let pivotA_y = (use_log ? math.log(pivotA_raw) : pivotA_raw); - let pivotB_y = (use_log ? math.log(pivotB_raw) : pivotB_raw); - let pivotC_y = (use_log ? math.log(pivotC_raw) : pivotC_raw); + let pivotA_y = (use_log ? pinescript.log(pivotA_raw) : pivotA_raw); + let pivotB_y = (use_log ? pinescript.log(pivotB_raw) : pivotB_raw); + let pivotC_y = (use_log ? pinescript.log(pivotC_raw) : pivotC_raw); let mid_x = ((pivotB_x + pivotC_x) / 2); let mid_y = ((pivotB_y + pivotC_y) / 2); let anchor_x = null; @@ -1360,8 +1840,8 @@ function main() { y1 = start_y; x2 = max_x; y2 = (start_y + (slope_ml * (x2 - x1))); - let draw_y1 = (use_log ? math.exp(y1) : y1); - let draw_y2 = (use_log ? math.exp(y2) : y2); + let draw_y1 = (use_log ? pinescript.exp(y1) : y1); + let draw_y2 = (use_log ? pinescript.exp(y2) : y2); pinescript.lineNew(int(pinescript.round(x1)), draw_y1, int(pinescript.round(x2)), draw_y2, ({ extend: extend.right, color: col, width: wid, style: st })); break; } @@ -1371,8 +1851,8 @@ function main() { y1 = (start_y + (slope_ml * (x1 - start_x))); x2 = max_x; y2 = (start_y + (slope_ml * (x2 - start_x))); - let draw_y1 = (use_log ? math.exp(y1) : y1); - let draw_y2 = (use_log ? math.exp(y2) : y2); + let draw_y1 = (use_log ? pinescript.exp(y1) : y1); + let draw_y2 = (use_log ? pinescript.exp(y2) : y2); pinescript.lineNew(int(pinescript.round(x1)), draw_y1, int(pinescript.round(x2)), draw_y2, ({ extend: extend.both, color: col, width: wid, style: st })); break; } @@ -1407,9 +1887,9 @@ function main() { let color_A = ((pivotA_type === "High") ? labelColorH : labelColorL); let color_B = ((pivotB_type === "High") ? labelColorH : labelColorL); let color_C = ((pivotC_type === "High") ? labelColorH : labelColorL); - let y_A = (use_log ? math.exp(pivotA_y) : pivotA_y); - let y_B = (use_log ? math.exp(pivotB_y) : pivotB_y); - let y_C = (use_log ? math.exp(pivotC_y) : pivotC_y); + let y_A = (use_log ? pinescript.exp(pivotA_y) : pivotA_y); + let y_B = (use_log ? pinescript.exp(pivotB_y) : pivotB_y); + let y_C = (use_log ? pinescript.exp(pivotC_y) : pivotC_y); pinescript.labelNew(int(pivotA_x), y_A, "A", ({ style: style_A, color: pinescript.color.new(color_A, 100), textcolor: color_A, size: label_size, text_font_family: font.family_monospace })); pinescript.labelNew(int(pivotB_x), y_B, "B", ({ style: style_B, color: pinescript.color.new(color_B, 100), textcolor: color_B, size: label_size, text_font_family: font.family_monospace })); pinescript.labelNew(int(pivotC_x), y_C, "C", ({ style: style_C, color: pinescript.color.new(color_C, 100), textcolor: color_C, size: label_size, text_font_family: font.family_monospace })); @@ -1420,25 +1900,25 @@ function main() { let median_y_lower = (anchor_y + (slope_ml * (lower_x - anchor_x))); let offset_lo = (lower_y - median_y_lower); if (enable_price_range) { - let range_min_y = (use_log ? math.log(pinescript.max(price_range_min, 0.0001)) : price_range_min); - let range_max_y = (use_log ? math.log(price_range_max) : price_range_max); + let range_min_y = (use_log ? pinescript.log(pinescript.max(price_range_min, 0.0001)) : price_range_min); + let range_max_y = (use_log ? pinescript.log(price_range_max) : price_range_max); state.effective_extra_upper = extra_levels; if (((offset_up !== 0) && (extra_levels > 0))) { if ((offset_up > 0)) { - let max_levels = int(math.floor(((range_max_y - upper_y) / offset_up))); + let max_levels = int(pinescript.floor(((range_max_y - upper_y) / offset_up))); state.effective_extra_upper = pinescript.max(0, pinescript.min(extra_levels, max_levels)); } else { - let max_levels = int(math.floor(((upper_y - range_min_y) / pinescript.abs(offset_up)))); + let max_levels = int(pinescript.floor(((upper_y - range_min_y) / pinescript.abs(offset_up)))); state.effective_extra_upper = pinescript.max(0, pinescript.min(extra_levels, max_levels)); } } state.effective_extra_lower = extra_levels; if (((offset_lo !== 0) && (extra_levels > 0))) { if ((offset_lo < 0)) { - let max_levels = int(math.floor(((lower_y - range_min_y) / pinescript.abs(offset_lo)))); + let max_levels = int(pinescript.floor(((lower_y - range_min_y) / pinescript.abs(offset_lo)))); state.effective_extra_lower = pinescript.max(0, pinescript.min(extra_levels, max_levels)); } else { - let max_levels = int(math.floor(((range_max_y - lower_y) / offset_lo))); + let max_levels = int(pinescript.floor(((range_max_y - lower_y) / offset_lo))); state.effective_extra_lower = pinescript.max(0, pinescript.min(extra_levels, max_levels)); } } @@ -1508,7 +1988,7 @@ function main() { let auto_forward_count = 0; if ((handle_dx !== 0)) { let bars_to_current = (last_bar_index - anchor_x); - auto_forward_count = int(math.ceil(pinescript.abs((bars_to_current / handle_dx)))); + auto_forward_count = int(pinescript.ceil(pinescript.abs((bars_to_current / handle_dx)))); auto_forward_count = pinescript.max(1, auto_forward_count); } let effective_forward_count = pinescript.max(1, (auto_forward_count + forward_count)); @@ -1554,8 +2034,8 @@ function main() { lower_iy = ((slope_trans * (lower_ix - p_x)) + p_y); } if (((!pinescript.na(upper_ix) && !pinescript.na(lower_ix)) && (upper_ix !== lower_ix))) { - let draw_upper_iy = (use_log ? math.exp(upper_iy) : upper_iy); - let draw_lower_iy = (use_log ? math.exp(lower_iy) : lower_iy); + let draw_upper_iy = (use_log ? pinescript.exp(upper_iy) : upper_iy); + let draw_lower_iy = (use_log ? pinescript.exp(lower_iy) : lower_iy); pinescript.lineNew(int(pinescript.round(upper_ix)), draw_upper_iy, int(pinescript.round(lower_ix)), draw_lower_iy, ({ extend: extend.none, color: reaction_color, width: reaction_width, style: reaction_line_style })); } } @@ -1573,8 +2053,8 @@ function main() { lower_iy = ((slope_action * (lower_ix - p_x)) + p_y); } if (((!pinescript.na(upper_ix) && !pinescript.na(lower_ix)) && (upper_ix !== lower_ix))) { - let draw_upper_iy = (use_log ? math.exp(upper_iy) : upper_iy); - let draw_lower_iy = (use_log ? math.exp(lower_iy) : lower_iy); + let draw_upper_iy = (use_log ? pinescript.exp(upper_iy) : upper_iy); + let draw_lower_iy = (use_log ? pinescript.exp(lower_iy) : lower_iy); pinescript.lineNew(int(pinescript.round(upper_ix)), draw_upper_iy, int(pinescript.round(lower_ix)), draw_lower_iy, ({ extend: extend.none, color: action_color, width: action_width, style: action_line_style })); } } @@ -1681,7 +2161,7 @@ function main() { } let horiz_start_x = pivot_x; if (((int(pinescript.round(draw_max_x)) <= (last_bar_index + MAX_BAR_EXTENSION)) && (int(pinescript.round(horiz_start_x)) <= (last_bar_index + MAX_BAR_EXTENSION)))) { - let draw_horiz_y = (use_log ? math.exp(horiz_y) : horiz_y); + let draw_horiz_y = (use_log ? pinescript.exp(horiz_y) : horiz_y); pinescript.lineNew(int(pinescript.round(horiz_start_x)), draw_horiz_y, int(pinescript.round(draw_max_x)), draw_horiz_y, ({ color: lattice_color, width: lattice_width, style: lattice_line_style })); } } @@ -1710,8 +2190,8 @@ function main() { let y_min = (outer_lower_start_y + (slope_ml * (v_x - outer_lower_start_x))); let top = pinescript.max(y_min, y_max); let bot = pinescript.min(y_min, y_max); - let draw_top = (use_log ? math.exp(top) : top); - let draw_bot = (use_log ? math.exp(bot) : bot); + let draw_top = (use_log ? pinescript.exp(top) : top); + let draw_bot = (use_log ? pinescript.exp(bot) : bot); pinescript.lineNew(int(pinescript.round(v_x)), draw_bot, int(pinescript.round(v_x)), draw_top, ({ color: lattice_color, width: lattice_width, style: lattice_line_style })); } } @@ -1719,8 +2199,8 @@ function main() { } } } - let price_y = (use_log ? math.log(close) : close); - let prev_price_y = (use_log ? math.log(pinescript.offset(close, 1)) : pinescript.offset(close, 1)); + let price_y = (use_log ? pinescript.log(close) : close); + let prev_price_y = (use_log ? pinescript.log(pinescript.hist(18, close, 1)) : pinescript.hist(19, close, 1)); let median_y_at_upper = (anchor_y + (slope_ml * (upper_x - anchor_x))); let alert_offset_up = (upper_y - median_y_at_upper); let median_y_at_lower = (anchor_y + (slope_ml * (lower_x - anchor_x))); @@ -1771,7 +2251,7 @@ function main() { let auto_alert_forward = 0; if ((alert_handle_dx !== 0)) { let alert_bars_to_current = (last_bar_index - anchor_x); - auto_alert_forward = int(math.ceil(pinescript.abs((alert_bars_to_current / alert_handle_dx)))); + auto_alert_forward = int(pinescript.ceil(pinescript.abs((alert_bars_to_current / alert_handle_dx)))); auto_alert_forward = pinescript.max(1, auto_alert_forward); } let alert_effective_forward = pinescript.max(1, (auto_alert_forward + forward_count)); @@ -1865,15 +2345,75 @@ function main() { } } } - alertcondition(crossed_pitchfork, ({ title: "Pitchfork Line Crossing", message: "Price crossed a pitchfork line (median or parallel)" })); - alertcondition(crossed_action, ({ title: "Action Line Crossing", message: "Price crossed an action line" })); - alertcondition(crossed_reaction, ({ title: "Reaction Line Crossing", message: "Price crossed a reaction line" })); - alertcondition(crossed_lattice_h, ({ title: "Lattice Horizontal Crossing", message: "Price crossed lattice horizontal" })); - alertcondition((((crossed_pitchfork || crossed_action) || crossed_reaction) || crossed_lattice_h), ({ title: "Any Line Crossing", message: "Price crossed a pitchfork, action, reaction, or lattice line" })); + pinescript.alertcondition(crossed_pitchfork, ({ title: "Pitchfork Line Crossing", message: "Price crossed a pitchfork line (median or parallel)" })); + pinescript.alertcondition(crossed_action, ({ title: "Action Line Crossing", message: "Price crossed an action line" })); + pinescript.alertcondition(crossed_reaction, ({ title: "Reaction Line Crossing", message: "Price crossed a reaction line" })); + pinescript.alertcondition(crossed_lattice_h, ({ title: "Lattice Horizontal Crossing", message: "Price crossed lattice horizontal" })); + pinescript.alertcondition((((crossed_pitchfork || crossed_action) || crossed_reaction) || crossed_lattice_h), ({ title: "Any Line Crossing", message: "Price crossed a pitchfork, action, reaction, or lattice line" })); } -// Export for use -export { main +// The bar-by-bar engine: runs main() once per bar over the dataset. +function run(data, options = {}) { + data = data || {}; + const inClose = (data.close || []).map(Number); + const n = inClose.length; + const inOpen = (data.open || inClose).map(Number); + const inHigh = (data.high || inClose).map(Number); + const inLow = (data.low || inClose).map(Number); + const inVol = (data.volume || new Array(n).fill(0)).map(Number); + const inTime = (data.time || inClose.map((_, i) => i)).map(Number); + + // Reset all persistent and per-run state so repeated runs are independent. + globalThis.__pineState = {}; + globalThis.__pineRuntime = { plots: {}, plotshapes: {}, alerts: [], bars: n, inputs: options.inputs || {} }; + pinescript.__rt = {}; + pinescript.__bar = 0; + + // Growing OHLCV windows: each is a SeriesArray that gains one element per bar, + // so window built-ins like sma and highest see history up to the current bar. + const O = pinescript.asSeries([]), H = pinescript.asSeries([]), L = pinescript.asSeries([]); + const C = pinescript.asSeries([]), V = pinescript.asSeries([]), T = pinescript.asSeries([]); + const HL2 = pinescript.asSeries([]), HLC3 = pinescript.asSeries([]), OHLC4 = pinescript.asSeries([]); + globalThis.open = O; globalThis.high = H; globalThis.low = L; globalThis.close = C; + globalThis.volume = V; globalThis.time = T; + globalThis.hl2 = HL2; globalThis.hlc3 = HLC3; globalThis.ohlc4 = OHLC4; + globalThis.syminfo = globalThis.syminfo || { tickerid: 'SYNTHETIC', ticker: 'SYNTHETIC', mintick: 0.01 }; + + const rt = globalThis.__pineRuntime; + for (let i = 0; i < n; i++) { + O.push(inOpen[i]); H.push(inHigh[i]); L.push(inLow[i]); C.push(inClose[i]); V.push(inVol[i]); T.push(inTime[i]); + HL2.push((inHigh[i] + inLow[i]) / 2); + HLC3.push((inHigh[i] + inLow[i] + inClose[i]) / 3); + OHLC4.push((inOpen[i] + inHigh[i] + inLow[i] + inClose[i]) / 4); + + pinescript.__bar = i; + rt.__barIndex = i; + rt.__plotIdx = 0; + rt.__shapeIdx = 0; + globalThis.bar_index = i; + globalThis.last_bar_index = n - 1; + globalThis.barstate = { + isfirst: i === 0, islast: i === n - 1, isrealtime: false, ishistory: true, + isconfirmed: true, isnew: true, islastconfirmedhistory: i === n - 1, + }; + + main(); + } + + // Backfill skipped bars so every output series has exactly n entries. + for (const k of Object.keys(rt.plots)) { + const d = rt.plots[k].data; + for (let i = 0; i < n; i++) if (d[i] === undefined) d[i] = null; + } + for (const k of Object.keys(rt.plotshapes)) { + const d = rt.plotshapes[k].data; + for (let i = 0; i < n; i++) if (d[i] === undefined) d[i] = false; + } + return rt; +} + + +export { main, run }; diff --git a/converted/Impulse_Trend_Arrows.js b/converted/Impulse_Trend_Arrows.js index 3aca685..d00cdcf 100644 --- a/converted/Impulse_Trend_Arrows.js +++ b/converted/Impulse_Trend_Arrows.js @@ -1,12 +1,63 @@ -// PineScript to JavaScript Transpiled Code +// Auto-generated JavaScript from PineScript source -// Generated automatically +// Do not edit by hand -- re-run the converter instead -// Built-in PineScript functions +// All the built-in PineScript functions that scripts depend on at runtime const pinescript = { + __scalar: function(v) { + if (v === null || v === undefined) return v; + if (typeof v === 'object' && typeof v.valueOf === 'function') { + const s = v.valueOf(); + if (typeof s === 'number' || typeof s === 'boolean' || s === null) return s; + } + return v; + }, + series: function(id, value) { + const rt = this.__rt || (this.__rt = {}); + const store = rt.series || (rt.series = {}); + let buf = store[id]; + if (!buf) buf = store[id] = []; + buf[this.__bar | 0] = this.__scalar(value); + return buf; + }, + hist: function(id, value, n) { + const buf = this.series(id, value); + const idx = (this.__bar | 0) - (n | 0); + return idx >= 0 && idx < buf.length ? buf[idx] : null; + }, + time: function(timeframe, session) { + const t = globalThis.time; + if (t && typeof t.valueOf === 'function') return t.valueOf(); + return Array.isArray(t) ? t[t.length - 1] : t; + }, + sign: function(x) { + if (x === null || x === undefined || (typeof x === 'number' && isNaN(x))) return null; + return Math.sign(Number(x)); + }, + unpack: function(value, count) { + if (Array.isArray(value)) return value; + if (value != null && typeof value[Symbol.iterator] === 'function') return Array.from(value); + return new Array(count || 0).fill(null); + }, + alertcondition: function(condition, ...rest) { + if (globalThis.__pineRuntime) { + globalThis.__pineRuntime.alerts.push({ condition, args: rest }); + } + return null; + }, + barcolor: function(color) { return null; }, + bgcolor: function(color, title, editable, showLast) { + return null; + }, + fill: function(series1, series2, color, title, editable) { + return null; + }, + hline: function(_price, _title, _opts) { + return null; + }, sma: function(series, length) { if (series === null || series === undefined) return null; if (!Array.isArray(series)) series = [series]; @@ -84,6 +135,13 @@ const pinescript = { } return weightSum > 0 ? sum / weightSum : null; }, + swma: function(series) { + if (series === null || series === undefined) return null; + if (!Array.isArray(series)) series = [series]; + if (series.length < 4) return null; + const s = series.slice(-4); + return (s[0] * 1 + s[1] * 2 + s[2] * 2 + s[3] * 1) / 6; + }, atr: function(high, low, close, length = 14) { if (!Array.isArray(high) || !Array.isArray(low) || !Array.isArray(close)) return null; if (high.length < length + 1 || low.length < length + 1 || close.length < length + 1) return null; @@ -114,6 +172,53 @@ const pinescript = { const atr = builtins.get('atr')(high, low, close, length); return { middle: sma, upper: sma + mult * atr, lower: sma - mult * atr }; }, + supertrend: function(factor, atrPeriod, high, low, close) { + if (!Array.isArray(high) || !Array.isArray(low) || !Array.isArray(close)) return [null, null]; + if (high.length < atrPeriod + 1) return [null, null]; + const atrVal = builtins.get('atr')(high, low, close, atrPeriod); + if (atrVal === null) return [null, null]; + const lastIdx = close.length - 1; + const hl2 = (high[lastIdx] + low[lastIdx]) / 2; + const upperBand = hl2 + factor * atrVal; + const lowerBand = hl2 - factor * atrVal; + // Determine direction: 1 means downtrend (price below band), -1 means uptrend + const direction = close[lastIdx] > upperBand ? -1 : close[lastIdx] < lowerBand ? 1 : -1; + const supertrendValue = direction === -1 ? lowerBand : upperBand; + return [supertrendValue, direction]; + }, + ta_supertrend: function(factor, atrPeriod) { + const H = globalThis.high; + const L = globalThis.low; + const C = globalThis.close; + return builtins.get('supertrend')(factor, atrPeriod, H, L, C); + }, + dmi: function(diLength, adxSmoothing, high, low, close) { + if (!Array.isArray(high) || !Array.isArray(low) || !Array.isArray(close)) return { plusDI: null, minusDI: null, adx: null }; + if (high.length < diLength + 1) return { plusDI: null, minusDI: null, adx: null }; + let plusDMSum = 0, minusDMSum = 0, trSum = 0; + for (let i = 1; i <= diLength; i++) { + const idx = high.length - 1 - diLength + i; + const upMove = high[idx] - high[idx - 1]; + const downMove = low[idx - 1] - low[idx]; + plusDMSum += (upMove > downMove && upMove > 0) ? upMove : 0; + minusDMSum += (downMove > upMove && downMove > 0) ? downMove : 0; + const tr = Math.max( + high[idx] - low[idx], + Math.abs(high[idx] - close[idx - 1]), + Math.abs(low[idx] - close[idx - 1]) + ); + trSum += tr; + } + const plusDI = trSum > 0 ? (plusDMSum / trSum) * 100 : 0; + const minusDI = trSum > 0 ? (minusDMSum / trSum) * 100 : 0; + const diSum = plusDI + minusDI; + const dx = diSum > 0 ? Math.abs(plusDI - minusDI) / diSum * 100 : 0; + return { plusDI, minusDI, adx: dx }; + }, + adx: function(diLength, adxSmoothing, high, low, close) { + const result = builtins.get('dmi')(diLength, adxSmoothing, high, low, close); + return result ? result.adx : null; + }, rsi: function(close, length = 14) { if (close === null || close === undefined) return null; if (!Array.isArray(close)) close = [close]; @@ -171,9 +276,7 @@ const pinescript = { return { macd: macdLine, signal: signalLine, histogram: histogram }; }, cci: function(a, b, c, d) { - // Pine signatures: - // - ta.cci(source, length) - // - ta.cci(high, low, close, length) + // Supports both ta.cci(source, length) and ta.cci(high, low, close, length) signatures let source; let length; if (Array.isArray(a) && typeof b === 'number' && c === undefined) { @@ -304,6 +407,15 @@ const pinescript = { stdev: function(source, length) { return Math.sqrt(builtins.get('variance')(source, length)); }, + median: function(source, length) { + if (!source || source.length < length) return null; + const sorted = [...source.slice(-length)].sort((a, b) => a - b); + const mid = Math.floor(sorted.length / 2); + return sorted.length % 2 !== 0 ? sorted[mid] : (sorted[mid - 1] + sorted[mid]) / 2; + }, + ta_median: function(source, length) { + return builtins.get('median')(source, length); + }, hl2: function(high, low) { return high && low ? (high[high.length - 1] + low[low.length - 1]) / 2 : null; }, @@ -420,6 +532,24 @@ const pinescript = { return (series1[i - 1] < series2[i - 1] && series1[i] >= series2[i]) || (series1[i - 1] > series2[i - 1] && series1[i] <= series2[i]); }, + crossover: function(series1, series2) { + if (series1 === null || series1 === undefined) return false; + if (series2 === null || series2 === undefined) return false; + if (!Array.isArray(series1)) series1 = [series1]; + if (!Array.isArray(series2)) series2 = [series2]; + if (series1.length < 2 || series2.length < 2) return false; + const i = series1.length - 1; + return series1[i - 1] < series2[i - 1] && series1[i] >= series2[i]; + }, + crossunder: function(series1, series2) { + if (series1 === null || series1 === undefined) return false; + if (series2 === null || series2 === undefined) return false; + if (!Array.isArray(series1)) series1 = [series1]; + if (!Array.isArray(series2)) series2 = [series2]; + if (series1.length < 2 || series2.length < 2) return false; + const i = series1.length - 1; + return series1[i - 1] > series2[i - 1] && series1[i] <= series2[i]; + }, offset: function(series, offset) { if (!Array.isArray(series)) return null; const idx = series.length - 1 - offset; @@ -500,12 +630,14 @@ const pinescript = { return builtins.get('nz')(value, replacement); }, plot: function(series, title = '', color = null, linewidth = 1) { - globalThis.__pineRuntime.plots.push({ - series, - title, - color, - linewidth - }); + const rt = globalThis.__pineRuntime; + if (!rt) return series; + const bar = rt.__barIndex | 0; + const ord = (rt.__plotIdx = (rt.__plotIdx | 0) + 1) - 1; + const key = (title && String(title)) || ('plot_' + ord); + let p = rt.plots[key]; + if (!p) p = rt.plots[key] = { title: key, color, linewidth, data: [] }; + p.data[bar] = this.__scalar(series); return series; }, lineNew: function(x1, y1, x2, y2, opts = {}) { @@ -514,20 +646,72 @@ const pinescript = { lineDelete: function(l) { return null; }, - labelNew: function(...args) { - if (globalThis.__pineRuntime) { - globalThis.__pineRuntime.labels = globalThis.__pineRuntime.labels || []; - globalThis.__pineRuntime.labels.push({ args }); - } - return { args }; + lineSetXY: function(line, point, x, y) { + if (!line) return null; + if (point === 0 || point === 'x1') { line.x1 = x; line.y1 = y; } + else { line.x2 = x; line.y2 = y; } + return line; + }, + lineGetX: function(line, point) { + if (!line) return null; + return point === 0 || point === 'x1' ? line.x1 : line.x2; + }, + lineGetY: function(line, point) { + if (!line) return null; + return point === 0 || point === 'y1' ? line.y1 : line.y2; + }, + labelNew: function(x, y, text = '', opts = {}) { + return { x, y, text, opts, _type: 'label' }; }, labelDelete: function(l) { return null; }, + labelSetText: function(label, text) { + if (!label) return null; + label.text = text; + return label; + }, + labelGetText: function(label) { + if (!label) return ''; + return label.text || ''; + }, + boxNew: function(left, top, right, bottom, opts = {}) { + return { left, top, right, bottom, opts, _type: 'box' }; + }, + boxDelete: function(box) { + return null; + }, + boxSetLeftTop: function(box, left, top) { + if (!box) return null; + box.left = left; + box.top = top; + return box; + }, + boxSetRightBottom: function(box, right, bottom) { + if (!box) return null; + box.right = right; + box.bottom = bottom; + return box; + }, + polylineNew: function(points, opts = {}) { + return { points: points || [], opts, _type: 'polyline' }; + }, + polylineDelete: function(poly) { + return null; + }, plotshape: function(condition, ...rest) { - if (globalThis.__pineRuntime) { - globalThis.__pineRuntime.plotshapes.push({ condition, args: rest }); + const rt = globalThis.__pineRuntime; + if (!rt) return condition; + const bar = rt.__barIndex | 0; + const ord = (rt.__shapeIdx = (rt.__shapeIdx | 0) + 1) - 1; + let key = 'shape_' + ord; + for (const r of rest) { + if (typeof r === 'string') { key = r; break; } + if (r && typeof r === 'object' && r.title) { key = String(r.title); break; } } + let s = rt.plotshapes[key]; + if (!s) s = rt.plotshapes[key] = { title: key, data: [] }; + s.data[bar] = !!this.__scalar(condition); return condition; }, indicator: function(title, shorttitle, overlay, opts) { @@ -542,27 +726,12 @@ const pinescript = { } return null; }, - alertcondition: function(condition, ...rest) { - if (globalThis.__pineRuntime) { - globalThis.__pineRuntime.alerts.push({ condition, args: rest }); - } - return null; - }, plotbar: function(open, high, low, close, title, color, editable, showLast) { return { open, high, low, close }; }, plotcandle: function(open, high, low, close, title, color, wickColor, borderColor, editable, showLast) { return { open, high, low, close }; }, - hline: function(_price, _title, _opts) { - return null; - }, - bgcolor: function(color, title, editable, showLast) { - return null; - }, - fill: function(series1, series2, color, title, editable) { - return null; - }, alert: function(condition, message, frequency) { console.log('Alert:', condition ? message : 'Condition not met'); return condition; @@ -828,6 +997,9 @@ const pinescript = { arrayIndexOf: function(arr, value) { return arr ? arr.indexOf(value) : -1; }, + arrayLastIndexOf: function(arr, value) { + return arr ? arr.lastIndexOf(value) : -1; + }, arraySum: function(arr) { return arr ? arr.reduce((a, b) => a + b, 0) : 0; }, @@ -840,6 +1012,42 @@ const pinescript = { arrayMax: function(arr) { return arr && arr.length > 0 ? Math.max(...arr) : null; }, + arrayStdev: function(arr) { + if (!arr || arr.length === 0) return null; + const mean = arr.reduce((a, b) => a + b, 0) / arr.length; + const variance = arr.reduce((sum, val) => sum + Math.pow(val - mean, 2), 0) / arr.length; + return Math.sqrt(variance); + }, + arrayVariance: function(arr) { + if (!arr || arr.length === 0) return null; + const mean = arr.reduce((a, b) => a + b, 0) / arr.length; + return arr.reduce((sum, val) => sum + Math.pow(val - mean, 2), 0) / arr.length; + }, + arrayCovariance: function(arr1, arr2) { + if (!arr1 || !arr2 || arr1.length === 0 || arr1.length !== arr2.length) return null; + const mean1 = arr1.reduce((a, b) => a + b, 0) / arr1.length; + const mean2 = arr2.reduce((a, b) => a + b, 0) / arr2.length; + let cov = 0; + for (let i = 0; i < arr1.length; i++) { + cov += (arr1[i] - mean1) * (arr2[i] - mean2); + } + return cov / arr1.length; + }, + mode: function(arr) { + if (!arr || arr.length === 0) return null; + const counts = new Map(); + let maxCount = 0; + let modeVal = arr[0]; + for (const val of arr) { + const c = (counts.get(val) || 0) + 1; + counts.set(val, c); + if (c > maxCount) { + maxCount = c; + modeVal = val; + } + } + return modeVal; + }, strLength: function(str) { return str ? str.length : 0; }, @@ -895,6 +1103,84 @@ const pinescript = { strReverse: function(str) { return str ? str.split('').reverse().join('') : ''; }, + strFormat: function(formatStr, ...args) { + if (!formatStr) return ''; + return formatStr.replace(/\{(\d+)\}/g, (match, idx) => { + const i = parseInt(idx, 10); + return i < args.length ? String(args[i]) : match; + }); + }, + input: function(defval, title, tooltip, inline, group) { + return defval; + }, + inputInt: function(defval = 0, title, minval, maxval, step, tooltip, inline, group) { + return defval; + }, + inputFloat: function(defval = 0.0, title, minval, maxval, step, tooltip, inline, group) { + return defval; + }, + inputBool: function(defval = false, title, tooltip, inline, group) { + return defval; + }, + inputString: function(defval = '', title, options, tooltip, inline, group) { + return defval; + }, + inputSource: function(defval = null, title, tooltip, inline, group) { + return defval; + }, + inputColor: function(defval = '#000000', title, tooltip, inline, group) { + return defval; + }, + inputTime: function(defval = 0, title, tooltip, inline, group) { + return defval; + }, + mathSign: function(value) { + return Math.sign(value); + }, + mathAvg: function(...args) { + const valid = args.filter(v => v !== null && v !== undefined && !isNaN(v)); + return valid.length > 0 ? valid.reduce((a, b) => a + b, 0) / valid.length : null; + }, + mathSum: function(series, length) { + if (!series || series.length < length) return null; + return series.slice(-length).reduce((a, b) => a + b, 0); + }, + mathRandom: function(min = 0, max = 1) { + return min + Math.random() * (max - min); + }, + mathTodegrees: function(radians) { + return radians * (180 / Math.PI); + }, + mathToradians: function(degrees) { + return degrees * (Math.PI / 180); + }, + colorT: function(color, transparency) { + if (transparency !== undefined) { + return { color, transparency }; + } + if (color && typeof color === 'object' && 'transparency' in color) { + return color.transparency; + } + return 0; + }, + tableCellSetText: function(table, column, row, text) { + return null; + }, + tableCellSetBgcolor: function(table, column, row, bgcolor) { + return null; + }, + tableMergeCells: function(table, startColumn, startRow, endColumn, endRow) { + return null; + }, + tableDelete: function(table) { + return null; + }, + tableClear: function(table, startColumn, startRow, endColumn, endRow) { + return null; + }, + maxBarsBack: function(series, length) { + return null; + }, pi: 3.141592653589793, e: 2.718281828459045, true: true, @@ -905,8 +1191,7 @@ const pinescript = { const info = { ticker: 'AAPL', tickerid: 'NASDAQ:AAPL', prefix: 'NASDAQ', root: 'AAPL', suffix: '' }; return info[type] || ''; }, - time: 1771178051604, - timenow: 1771178051604, + timenow: 1781574527280, barstate: "LAST", dividends: {}, splits: {}, @@ -923,7 +1208,7 @@ const pinescript = { return null; }, tr: function(high, low, close) { - // Pine signature: ta.tr(true) + // Handles both the boolean shorthand ta.tr(true) and explicit high/low/close arguments if (typeof high === 'boolean') { const H = globalThis.high; const L = globalThis.low; @@ -934,7 +1219,7 @@ const pinescript = { return Math.max(H[i] - L[i], Math.abs(H[i] - prevClose), Math.abs(L[i] - prevClose)); } - // Fallback: treat inputs as scalars for current bar + // When arrays are passed, compute true range from the last bar using the previous close if (Array.isArray(close)) { const prevClose = close[close.length - 2] || close[0]; const currHigh = Array.isArray(high) ? high[high.length - 1] : high; @@ -945,6 +1230,92 @@ const pinescript = { return Math.max(high - low, Math.abs(high - close), Math.abs(low - close)); }, + arrayFirst: function(arr) { + if (!arr || arr.length === 0) return null; + return arr[0]; + }, + arrayLast: function(arr) { + if (!arr || arr.length === 0) return null; + return arr[arr.length - 1]; + }, + arrayJoin: function(arr, separator = ',') { + if (!arr) return ''; + return arr.join(separator); + }, + arrayConcat: function(arr1, arr2) { + if (!arr1) return arr2 || []; + if (!arr2) return arr1; + return arr1.concat(arr2); + }, + arrayCopy: function(arr) { + if (!arr) return []; + return [...arr]; + }, + arrayBinarySearch: function(arr, value) { + if (!arr || arr.length === 0) return -1; + let lo = 0, hi = arr.length - 1; + while (lo <= hi) { + const mid = (lo + hi) >>> 1; + if (arr[mid] === value) return mid; + if (arr[mid] < value) lo = mid + 1; + else hi = mid - 1; + } + return -1; + }, + arrayRange: function(arr) { + if (!arr || arr.length === 0) return 0; + return Math.max(...arr) - Math.min(...arr); + }, + arrayMedian: function(arr) { + if (!arr || arr.length === 0) return null; + const sorted = [...arr].sort((a, b) => a - b); + const mid = Math.floor(sorted.length / 2); + return sorted.length % 2 !== 0 ? sorted[mid] : (sorted[mid - 1] + sorted[mid]) / 2; + }, + arrayMode: function(arr) { + if (!arr || arr.length === 0) return null; + const freq = {}; + let maxCount = 0; + let mode = arr[0]; + for (const v of arr) { + freq[v] = (freq[v] || 0) + 1; + if (freq[v] > maxCount) { maxCount = freq[v]; mode = v; } + } + return mode; + }, + arrayPercentile: function(arr, percentile) { + if (!arr || arr.length === 0) return null; + const sorted = [...arr].sort((a, b) => a - b); + const idx = (percentile / 100) * (sorted.length - 1); + const lo = Math.floor(idx); + const hi = Math.ceil(idx); + if (lo === hi) return sorted[lo]; + return sorted[lo] + (sorted[hi] - sorted[lo]) * (idx - lo); + }, + arrayPercentileLinearInterpolation: function(arr, percentile) { + return builtins.get('arrayPercentile')(arr, percentile); + }, + arrayPercentileNearestRank: function(arr, percentile) { + if (!arr || arr.length === 0) return null; + const sorted = [...arr].sort((a, b) => a - b); + const idx = Math.ceil((percentile / 100) * sorted.length) - 1; + return sorted[Math.max(0, idx)]; + }, + arrayAbs: function(arr) { + if (!arr) return []; + return arr.map(v => Math.abs(v)); + }, + arrayEvery: function(arr, callback) { + if (!arr) return false; + return arr.every(callback); + }, + arraySome: function(arr, callback) { + if (!arr) return false; + return arr.some(callback); + }, + requestFinancial: function() { + return null; + }, }; @@ -1101,6 +1472,115 @@ globalThis.barmerge = globalThis.barmerge || { }; +function __pineNS(real) { + + const base = real || {}; + + return new Proxy(base, { + + get(target, key) { + + if (key in target) return target[key]; + + if (typeof key === "symbol") return target[key]; + + // Unknown member: usable as a value (null), a constant, or a no-op function. + + const noop = function() { return null; }; + + noop.toString = () => ""; + + return noop; + + } + + }); + +} + +globalThis.ta = globalThis.ta || __pineNS({}); + +globalThis.math = globalThis.math || __pineNS({ pi: Math.PI, e: Math.E, phi: 1.618033988749895, rphi: 0.618033988749895 }); + +globalThis.chart = globalThis.chart || __pineNS({ point: __pineNS({}), bg_color: null, fg_color: null }); + +globalThis.line = globalThis.line || __pineNS({ style_solid: "solid", style_dashed: "dashed", style_dotted: "dotted" }); + +globalThis.box = globalThis.box || __pineNS({}); + +globalThis.label = globalThis.label || __pineNS({ style_label_down: "label_down", style_label_up: "label_up", style_none: "none" }); + +globalThis.polyline = globalThis.polyline || __pineNS({}); + +globalThis.linefill = globalThis.linefill || __pineNS({}); + +globalThis.matrix = globalThis.matrix || __pineNS({}); + +globalThis.map = globalThis.map || __pineNS({}); + +globalThis.session = globalThis.session || __pineNS({ regular: "regular", extended: "extended" }); + +globalThis.dayofweek = globalThis.dayofweek || __pineNS({ sunday: 1, monday: 2, tuesday: 3, wednesday: 4, thursday: 5, friday: 6, saturday: 7 }); + +globalThis.timeframe = __pineNS(Object.assign(globalThis.timeframe || {}, { period: (globalThis.timeframe && globalThis.timeframe.period) || "D", isintraday: false, isdaily: true, multiplier: 1 })); + +globalThis.request = __pineNS(globalThis.request || {}); + +globalThis.input = __pineNS(globalThis.input || {}); + +globalThis.adjustment = globalThis.adjustment || __pineNS({}); + +globalThis.earnings = globalThis.earnings || __pineNS({}); + +globalThis.dividends = globalThis.dividends || __pineNS({}); + +globalThis.splits = globalThis.splits || __pineNS({}); + +globalThis.currency = globalThis.currency || __pineNS({}); + +globalThis.display = globalThis.display || __pineNS({ none: "none", all: "all", pane: "pane", data_window: "data_window", status_line: "status_line", price_scale: "price_scale" }); + +globalThis.format = globalThis.format || __pineNS({ price: "price", volume: "volume", percent: "percent", mintick: "mintick", inherit: "inherit" }); + +globalThis.scale = globalThis.scale || __pineNS({ left: "left", right: "right", none: "none" }); + +globalThis.font = globalThis.font || __pineNS({ family_default: "default", family_monospace: "monospace" }); + +globalThis.xloc = globalThis.xloc || __pineNS({ bar_index: "bar_index", bar_time: "bar_time" }); + +globalThis.yloc = globalThis.yloc || __pineNS({ price: "price", abovebar: "abovebar", belowbar: "belowbar" }); + +globalThis.extend = globalThis.extend || __pineNS({ none: "none", left: "left", right: "right", both: "both" }); + +globalThis.order = globalThis.order || __pineNS({ ascending: "ascending", descending: "descending" }); + +globalThis.alert = globalThis.alert || Object.assign(function() { return null; }, { freq_once_per_bar: "once_per_bar", freq_once_per_bar_close: "once_per_bar_close", freq_all: "all" }); + +globalThis.dayofmonth = globalThis.dayofmonth || function(t) { return new Date(t || 0).getUTCDate(); }; + +globalThis.plot = globalThis.plot || __pineNS({ style_line: "line", style_linebr: "linebr", style_stepline: "stepline", style_histogram: "histogram", style_cross: "cross", style_area: "area", style_areabr: "areabr", style_columns: "columns", style_circles: "circles" }); + +globalThis.int = globalThis.int || function(x) { return x == null ? null : Math.trunc(Number(x)); }; + +globalThis.float = globalThis.float || function(x) { return x == null ? null : Number(x); }; + +globalThis.bool = globalThis.bool || function(x) { return Boolean(x); }; + +globalThis.string = globalThis.string || function(x) { return x == null ? null : String(x); }; + +globalThis.dayofweek = globalThis.dayofweek || __pineNS({ sunday: 1, monday: 2, tuesday: 3, wednesday: 4, thursday: 5, friday: 6, saturday: 7 }); + +globalThis.str = globalThis.str || __pineNS({}); + +pinescript.math = globalThis.math; + +pinescript.ta = globalThis.ta; + +pinescript.str = globalThis.str; + +pinescript.array = __pineNS(globalThis.array); + + pinescript.color = { hex: function(s) { @@ -1190,10 +1670,10 @@ pinescript.table = { }; -// Input parameters +// Script input parameters and their defaults -// Main script logic +// The transpiled script logic, called once per bar function main() { @@ -1208,15 +1688,15 @@ function main() { null; // Study: Impulse Trend Arrows // Options: {"shorttitle":"Impulse Trend Arrows","overlay":true} - let basisLen = input.int(50, "Adaptive Baseline Length"); - let showBands = input.bool(true, "Show ATR Bands"); - let showBg = input.bool(true, "Background Shading"); - let showArrows = input.bool(true, "Show Entry Arrows"); - let smoothLen = input.int(8, "Channel Smoothing", ({ minval: 1 })); - let baseColorIn = input.color(pinescript.color.new(pinescript.color.hex("#5f5f5f"), 0), "Baseline Color"); - let upColorIn = input.color(pinescript.color.new(pinescript.color.hex("#000000"), 0), "Upper Band Color"); - let dnColorIn = input.color(pinescript.color.new(pinescript.color.hex("#000000"), 0), "Lower Band Color"); - let fillColorIn = input.color(pinescript.color.new(pinescript.color.blue, 75), "Channel Fill Color"); + let basisLen = pinescript.inputInt(50, "Adaptive Baseline Length"); + let showBands = pinescript.inputBool(true, "Show ATR Bands"); + let showBg = pinescript.inputBool(true, "Background Shading"); + let showArrows = pinescript.inputBool(true, "Show Entry Arrows"); + let smoothLen = pinescript.inputInt(8, "Channel Smoothing", ({ minval: 1 })); + let baseColorIn = pinescript.inputColor(pinescript.color.new(pinescript.color.hex("#5f5f5f"), 0), "Baseline Color"); + let upColorIn = pinescript.inputColor(pinescript.color.new(pinescript.color.hex("#000000"), 0), "Upper Band Color"); + let dnColorIn = pinescript.inputColor(pinescript.color.new(pinescript.color.hex("#000000"), 0), "Lower Band Color"); + let fillColorIn = pinescript.inputColor(pinescript.color.new(pinescript.color.blue, 75), "Channel Fill Color"); let atrLen = 14; let atrMult = 2; let rsiLen = 14; @@ -1224,12 +1704,12 @@ function main() { let macdSlow = 26; let macdSig = 9; let basis = pinescript.ema(close, basisLen); - let atr = ta.atr(atrLen); + let atr = pinescript.atr(atrLen); let upBand = (basis + (atrMult * atr)); let dnBand = (basis - (atrMult * atr)); let impulseUp = ((close > basis) && (close > upBand)); let impulseDn = ((close < basis) && (close < dnBand)); - let rsi = ta.rsi(close, rsiLen); + let rsi = pinescript.rsi(close, rsiLen); let macdLine = (pinescript.ema(close, macdFast) - pinescript.ema(close, macdSlow)); let macdSigLn = pinescript.ema(macdLine, macdSig); let macdHist = (macdLine - macdSigLn); @@ -1283,7 +1763,67 @@ function main() { } -// Export for use -export { main +// The bar-by-bar engine: runs main() once per bar over the dataset. +function run(data, options = {}) { + data = data || {}; + const inClose = (data.close || []).map(Number); + const n = inClose.length; + const inOpen = (data.open || inClose).map(Number); + const inHigh = (data.high || inClose).map(Number); + const inLow = (data.low || inClose).map(Number); + const inVol = (data.volume || new Array(n).fill(0)).map(Number); + const inTime = (data.time || inClose.map((_, i) => i)).map(Number); + + // Reset all persistent and per-run state so repeated runs are independent. + globalThis.__pineState = {}; + globalThis.__pineRuntime = { plots: {}, plotshapes: {}, alerts: [], bars: n, inputs: options.inputs || {} }; + pinescript.__rt = {}; + pinescript.__bar = 0; + + // Growing OHLCV windows: each is a SeriesArray that gains one element per bar, + // so window built-ins like sma and highest see history up to the current bar. + const O = pinescript.asSeries([]), H = pinescript.asSeries([]), L = pinescript.asSeries([]); + const C = pinescript.asSeries([]), V = pinescript.asSeries([]), T = pinescript.asSeries([]); + const HL2 = pinescript.asSeries([]), HLC3 = pinescript.asSeries([]), OHLC4 = pinescript.asSeries([]); + globalThis.open = O; globalThis.high = H; globalThis.low = L; globalThis.close = C; + globalThis.volume = V; globalThis.time = T; + globalThis.hl2 = HL2; globalThis.hlc3 = HLC3; globalThis.ohlc4 = OHLC4; + globalThis.syminfo = globalThis.syminfo || { tickerid: 'SYNTHETIC', ticker: 'SYNTHETIC', mintick: 0.01 }; + + const rt = globalThis.__pineRuntime; + for (let i = 0; i < n; i++) { + O.push(inOpen[i]); H.push(inHigh[i]); L.push(inLow[i]); C.push(inClose[i]); V.push(inVol[i]); T.push(inTime[i]); + HL2.push((inHigh[i] + inLow[i]) / 2); + HLC3.push((inHigh[i] + inLow[i] + inClose[i]) / 3); + OHLC4.push((inOpen[i] + inHigh[i] + inLow[i] + inClose[i]) / 4); + + pinescript.__bar = i; + rt.__barIndex = i; + rt.__plotIdx = 0; + rt.__shapeIdx = 0; + globalThis.bar_index = i; + globalThis.last_bar_index = n - 1; + globalThis.barstate = { + isfirst: i === 0, islast: i === n - 1, isrealtime: false, ishistory: true, + isconfirmed: true, isnew: true, islastconfirmedhistory: i === n - 1, + }; + + main(); + } + + // Backfill skipped bars so every output series has exactly n entries. + for (const k of Object.keys(rt.plots)) { + const d = rt.plots[k].data; + for (let i = 0; i < n; i++) if (d[i] === undefined) d[i] = null; + } + for (const k of Object.keys(rt.plotshapes)) { + const d = rt.plotshapes[k].data; + for (let i = 0; i < n; i++) if (d[i] === undefined) d[i] = false; + } + return rt; +} + + +export { main, run }; diff --git a/converted/Institutional_Order_Flow_Strength_Classifier_LuxAlgo.js b/converted/Institutional_Order_Flow_Strength_Classifier_LuxAlgo.js index 16d22ff..e8adfa6 100644 --- a/converted/Institutional_Order_Flow_Strength_Classifier_LuxAlgo.js +++ b/converted/Institutional_Order_Flow_Strength_Classifier_LuxAlgo.js @@ -1,12 +1,63 @@ -// PineScript to JavaScript Transpiled Code +// Auto-generated JavaScript from PineScript source -// Generated automatically +// Do not edit by hand -- re-run the converter instead -// Built-in PineScript functions +// All the built-in PineScript functions that scripts depend on at runtime const pinescript = { + __scalar: function(v) { + if (v === null || v === undefined) return v; + if (typeof v === 'object' && typeof v.valueOf === 'function') { + const s = v.valueOf(); + if (typeof s === 'number' || typeof s === 'boolean' || s === null) return s; + } + return v; + }, + series: function(id, value) { + const rt = this.__rt || (this.__rt = {}); + const store = rt.series || (rt.series = {}); + let buf = store[id]; + if (!buf) buf = store[id] = []; + buf[this.__bar | 0] = this.__scalar(value); + return buf; + }, + hist: function(id, value, n) { + const buf = this.series(id, value); + const idx = (this.__bar | 0) - (n | 0); + return idx >= 0 && idx < buf.length ? buf[idx] : null; + }, + time: function(timeframe, session) { + const t = globalThis.time; + if (t && typeof t.valueOf === 'function') return t.valueOf(); + return Array.isArray(t) ? t[t.length - 1] : t; + }, + sign: function(x) { + if (x === null || x === undefined || (typeof x === 'number' && isNaN(x))) return null; + return Math.sign(Number(x)); + }, + unpack: function(value, count) { + if (Array.isArray(value)) return value; + if (value != null && typeof value[Symbol.iterator] === 'function') return Array.from(value); + return new Array(count || 0).fill(null); + }, + alertcondition: function(condition, ...rest) { + if (globalThis.__pineRuntime) { + globalThis.__pineRuntime.alerts.push({ condition, args: rest }); + } + return null; + }, + barcolor: function(color) { return null; }, + bgcolor: function(color, title, editable, showLast) { + return null; + }, + fill: function(series1, series2, color, title, editable) { + return null; + }, + hline: function(_price, _title, _opts) { + return null; + }, sma: function(series, length) { if (series === null || series === undefined) return null; if (!Array.isArray(series)) series = [series]; @@ -84,6 +135,13 @@ const pinescript = { } return weightSum > 0 ? sum / weightSum : null; }, + swma: function(series) { + if (series === null || series === undefined) return null; + if (!Array.isArray(series)) series = [series]; + if (series.length < 4) return null; + const s = series.slice(-4); + return (s[0] * 1 + s[1] * 2 + s[2] * 2 + s[3] * 1) / 6; + }, atr: function(high, low, close, length = 14) { if (!Array.isArray(high) || !Array.isArray(low) || !Array.isArray(close)) return null; if (high.length < length + 1 || low.length < length + 1 || close.length < length + 1) return null; @@ -114,6 +172,53 @@ const pinescript = { const atr = builtins.get('atr')(high, low, close, length); return { middle: sma, upper: sma + mult * atr, lower: sma - mult * atr }; }, + supertrend: function(factor, atrPeriod, high, low, close) { + if (!Array.isArray(high) || !Array.isArray(low) || !Array.isArray(close)) return [null, null]; + if (high.length < atrPeriod + 1) return [null, null]; + const atrVal = builtins.get('atr')(high, low, close, atrPeriod); + if (atrVal === null) return [null, null]; + const lastIdx = close.length - 1; + const hl2 = (high[lastIdx] + low[lastIdx]) / 2; + const upperBand = hl2 + factor * atrVal; + const lowerBand = hl2 - factor * atrVal; + // Determine direction: 1 means downtrend (price below band), -1 means uptrend + const direction = close[lastIdx] > upperBand ? -1 : close[lastIdx] < lowerBand ? 1 : -1; + const supertrendValue = direction === -1 ? lowerBand : upperBand; + return [supertrendValue, direction]; + }, + ta_supertrend: function(factor, atrPeriod) { + const H = globalThis.high; + const L = globalThis.low; + const C = globalThis.close; + return builtins.get('supertrend')(factor, atrPeriod, H, L, C); + }, + dmi: function(diLength, adxSmoothing, high, low, close) { + if (!Array.isArray(high) || !Array.isArray(low) || !Array.isArray(close)) return { plusDI: null, minusDI: null, adx: null }; + if (high.length < diLength + 1) return { plusDI: null, minusDI: null, adx: null }; + let plusDMSum = 0, minusDMSum = 0, trSum = 0; + for (let i = 1; i <= diLength; i++) { + const idx = high.length - 1 - diLength + i; + const upMove = high[idx] - high[idx - 1]; + const downMove = low[idx - 1] - low[idx]; + plusDMSum += (upMove > downMove && upMove > 0) ? upMove : 0; + minusDMSum += (downMove > upMove && downMove > 0) ? downMove : 0; + const tr = Math.max( + high[idx] - low[idx], + Math.abs(high[idx] - close[idx - 1]), + Math.abs(low[idx] - close[idx - 1]) + ); + trSum += tr; + } + const plusDI = trSum > 0 ? (plusDMSum / trSum) * 100 : 0; + const minusDI = trSum > 0 ? (minusDMSum / trSum) * 100 : 0; + const diSum = plusDI + minusDI; + const dx = diSum > 0 ? Math.abs(plusDI - minusDI) / diSum * 100 : 0; + return { plusDI, minusDI, adx: dx }; + }, + adx: function(diLength, adxSmoothing, high, low, close) { + const result = builtins.get('dmi')(diLength, adxSmoothing, high, low, close); + return result ? result.adx : null; + }, rsi: function(close, length = 14) { if (close === null || close === undefined) return null; if (!Array.isArray(close)) close = [close]; @@ -171,9 +276,7 @@ const pinescript = { return { macd: macdLine, signal: signalLine, histogram: histogram }; }, cci: function(a, b, c, d) { - // Pine signatures: - // - ta.cci(source, length) - // - ta.cci(high, low, close, length) + // Supports both ta.cci(source, length) and ta.cci(high, low, close, length) signatures let source; let length; if (Array.isArray(a) && typeof b === 'number' && c === undefined) { @@ -304,6 +407,15 @@ const pinescript = { stdev: function(source, length) { return Math.sqrt(builtins.get('variance')(source, length)); }, + median: function(source, length) { + if (!source || source.length < length) return null; + const sorted = [...source.slice(-length)].sort((a, b) => a - b); + const mid = Math.floor(sorted.length / 2); + return sorted.length % 2 !== 0 ? sorted[mid] : (sorted[mid - 1] + sorted[mid]) / 2; + }, + ta_median: function(source, length) { + return builtins.get('median')(source, length); + }, hl2: function(high, low) { return high && low ? (high[high.length - 1] + low[low.length - 1]) / 2 : null; }, @@ -420,6 +532,24 @@ const pinescript = { return (series1[i - 1] < series2[i - 1] && series1[i] >= series2[i]) || (series1[i - 1] > series2[i - 1] && series1[i] <= series2[i]); }, + crossover: function(series1, series2) { + if (series1 === null || series1 === undefined) return false; + if (series2 === null || series2 === undefined) return false; + if (!Array.isArray(series1)) series1 = [series1]; + if (!Array.isArray(series2)) series2 = [series2]; + if (series1.length < 2 || series2.length < 2) return false; + const i = series1.length - 1; + return series1[i - 1] < series2[i - 1] && series1[i] >= series2[i]; + }, + crossunder: function(series1, series2) { + if (series1 === null || series1 === undefined) return false; + if (series2 === null || series2 === undefined) return false; + if (!Array.isArray(series1)) series1 = [series1]; + if (!Array.isArray(series2)) series2 = [series2]; + if (series1.length < 2 || series2.length < 2) return false; + const i = series1.length - 1; + return series1[i - 1] > series2[i - 1] && series1[i] <= series2[i]; + }, offset: function(series, offset) { if (!Array.isArray(series)) return null; const idx = series.length - 1 - offset; @@ -500,12 +630,14 @@ const pinescript = { return builtins.get('nz')(value, replacement); }, plot: function(series, title = '', color = null, linewidth = 1) { - globalThis.__pineRuntime.plots.push({ - series, - title, - color, - linewidth - }); + const rt = globalThis.__pineRuntime; + if (!rt) return series; + const bar = rt.__barIndex | 0; + const ord = (rt.__plotIdx = (rt.__plotIdx | 0) + 1) - 1; + const key = (title && String(title)) || ('plot_' + ord); + let p = rt.plots[key]; + if (!p) p = rt.plots[key] = { title: key, color, linewidth, data: [] }; + p.data[bar] = this.__scalar(series); return series; }, lineNew: function(x1, y1, x2, y2, opts = {}) { @@ -514,20 +646,72 @@ const pinescript = { lineDelete: function(l) { return null; }, - labelNew: function(...args) { - if (globalThis.__pineRuntime) { - globalThis.__pineRuntime.labels = globalThis.__pineRuntime.labels || []; - globalThis.__pineRuntime.labels.push({ args }); - } - return { args }; + lineSetXY: function(line, point, x, y) { + if (!line) return null; + if (point === 0 || point === 'x1') { line.x1 = x; line.y1 = y; } + else { line.x2 = x; line.y2 = y; } + return line; + }, + lineGetX: function(line, point) { + if (!line) return null; + return point === 0 || point === 'x1' ? line.x1 : line.x2; + }, + lineGetY: function(line, point) { + if (!line) return null; + return point === 0 || point === 'y1' ? line.y1 : line.y2; + }, + labelNew: function(x, y, text = '', opts = {}) { + return { x, y, text, opts, _type: 'label' }; }, labelDelete: function(l) { return null; }, + labelSetText: function(label, text) { + if (!label) return null; + label.text = text; + return label; + }, + labelGetText: function(label) { + if (!label) return ''; + return label.text || ''; + }, + boxNew: function(left, top, right, bottom, opts = {}) { + return { left, top, right, bottom, opts, _type: 'box' }; + }, + boxDelete: function(box) { + return null; + }, + boxSetLeftTop: function(box, left, top) { + if (!box) return null; + box.left = left; + box.top = top; + return box; + }, + boxSetRightBottom: function(box, right, bottom) { + if (!box) return null; + box.right = right; + box.bottom = bottom; + return box; + }, + polylineNew: function(points, opts = {}) { + return { points: points || [], opts, _type: 'polyline' }; + }, + polylineDelete: function(poly) { + return null; + }, plotshape: function(condition, ...rest) { - if (globalThis.__pineRuntime) { - globalThis.__pineRuntime.plotshapes.push({ condition, args: rest }); + const rt = globalThis.__pineRuntime; + if (!rt) return condition; + const bar = rt.__barIndex | 0; + const ord = (rt.__shapeIdx = (rt.__shapeIdx | 0) + 1) - 1; + let key = 'shape_' + ord; + for (const r of rest) { + if (typeof r === 'string') { key = r; break; } + if (r && typeof r === 'object' && r.title) { key = String(r.title); break; } } + let s = rt.plotshapes[key]; + if (!s) s = rt.plotshapes[key] = { title: key, data: [] }; + s.data[bar] = !!this.__scalar(condition); return condition; }, indicator: function(title, shorttitle, overlay, opts) { @@ -542,27 +726,12 @@ const pinescript = { } return null; }, - alertcondition: function(condition, ...rest) { - if (globalThis.__pineRuntime) { - globalThis.__pineRuntime.alerts.push({ condition, args: rest }); - } - return null; - }, plotbar: function(open, high, low, close, title, color, editable, showLast) { return { open, high, low, close }; }, plotcandle: function(open, high, low, close, title, color, wickColor, borderColor, editable, showLast) { return { open, high, low, close }; }, - hline: function(_price, _title, _opts) { - return null; - }, - bgcolor: function(color, title, editable, showLast) { - return null; - }, - fill: function(series1, series2, color, title, editable) { - return null; - }, alert: function(condition, message, frequency) { console.log('Alert:', condition ? message : 'Condition not met'); return condition; @@ -828,6 +997,9 @@ const pinescript = { arrayIndexOf: function(arr, value) { return arr ? arr.indexOf(value) : -1; }, + arrayLastIndexOf: function(arr, value) { + return arr ? arr.lastIndexOf(value) : -1; + }, arraySum: function(arr) { return arr ? arr.reduce((a, b) => a + b, 0) : 0; }, @@ -840,6 +1012,42 @@ const pinescript = { arrayMax: function(arr) { return arr && arr.length > 0 ? Math.max(...arr) : null; }, + arrayStdev: function(arr) { + if (!arr || arr.length === 0) return null; + const mean = arr.reduce((a, b) => a + b, 0) / arr.length; + const variance = arr.reduce((sum, val) => sum + Math.pow(val - mean, 2), 0) / arr.length; + return Math.sqrt(variance); + }, + arrayVariance: function(arr) { + if (!arr || arr.length === 0) return null; + const mean = arr.reduce((a, b) => a + b, 0) / arr.length; + return arr.reduce((sum, val) => sum + Math.pow(val - mean, 2), 0) / arr.length; + }, + arrayCovariance: function(arr1, arr2) { + if (!arr1 || !arr2 || arr1.length === 0 || arr1.length !== arr2.length) return null; + const mean1 = arr1.reduce((a, b) => a + b, 0) / arr1.length; + const mean2 = arr2.reduce((a, b) => a + b, 0) / arr2.length; + let cov = 0; + for (let i = 0; i < arr1.length; i++) { + cov += (arr1[i] - mean1) * (arr2[i] - mean2); + } + return cov / arr1.length; + }, + mode: function(arr) { + if (!arr || arr.length === 0) return null; + const counts = new Map(); + let maxCount = 0; + let modeVal = arr[0]; + for (const val of arr) { + const c = (counts.get(val) || 0) + 1; + counts.set(val, c); + if (c > maxCount) { + maxCount = c; + modeVal = val; + } + } + return modeVal; + }, strLength: function(str) { return str ? str.length : 0; }, @@ -895,6 +1103,84 @@ const pinescript = { strReverse: function(str) { return str ? str.split('').reverse().join('') : ''; }, + strFormat: function(formatStr, ...args) { + if (!formatStr) return ''; + return formatStr.replace(/\{(\d+)\}/g, (match, idx) => { + const i = parseInt(idx, 10); + return i < args.length ? String(args[i]) : match; + }); + }, + input: function(defval, title, tooltip, inline, group) { + return defval; + }, + inputInt: function(defval = 0, title, minval, maxval, step, tooltip, inline, group) { + return defval; + }, + inputFloat: function(defval = 0.0, title, minval, maxval, step, tooltip, inline, group) { + return defval; + }, + inputBool: function(defval = false, title, tooltip, inline, group) { + return defval; + }, + inputString: function(defval = '', title, options, tooltip, inline, group) { + return defval; + }, + inputSource: function(defval = null, title, tooltip, inline, group) { + return defval; + }, + inputColor: function(defval = '#000000', title, tooltip, inline, group) { + return defval; + }, + inputTime: function(defval = 0, title, tooltip, inline, group) { + return defval; + }, + mathSign: function(value) { + return Math.sign(value); + }, + mathAvg: function(...args) { + const valid = args.filter(v => v !== null && v !== undefined && !isNaN(v)); + return valid.length > 0 ? valid.reduce((a, b) => a + b, 0) / valid.length : null; + }, + mathSum: function(series, length) { + if (!series || series.length < length) return null; + return series.slice(-length).reduce((a, b) => a + b, 0); + }, + mathRandom: function(min = 0, max = 1) { + return min + Math.random() * (max - min); + }, + mathTodegrees: function(radians) { + return radians * (180 / Math.PI); + }, + mathToradians: function(degrees) { + return degrees * (Math.PI / 180); + }, + colorT: function(color, transparency) { + if (transparency !== undefined) { + return { color, transparency }; + } + if (color && typeof color === 'object' && 'transparency' in color) { + return color.transparency; + } + return 0; + }, + tableCellSetText: function(table, column, row, text) { + return null; + }, + tableCellSetBgcolor: function(table, column, row, bgcolor) { + return null; + }, + tableMergeCells: function(table, startColumn, startRow, endColumn, endRow) { + return null; + }, + tableDelete: function(table) { + return null; + }, + tableClear: function(table, startColumn, startRow, endColumn, endRow) { + return null; + }, + maxBarsBack: function(series, length) { + return null; + }, pi: 3.141592653589793, e: 2.718281828459045, true: true, @@ -905,8 +1191,7 @@ const pinescript = { const info = { ticker: 'AAPL', tickerid: 'NASDAQ:AAPL', prefix: 'NASDAQ', root: 'AAPL', suffix: '' }; return info[type] || ''; }, - time: 1771178051687, - timenow: 1771178051687, + timenow: 1781574527375, barstate: "LAST", dividends: {}, splits: {}, @@ -923,7 +1208,7 @@ const pinescript = { return null; }, tr: function(high, low, close) { - // Pine signature: ta.tr(true) + // Handles both the boolean shorthand ta.tr(true) and explicit high/low/close arguments if (typeof high === 'boolean') { const H = globalThis.high; const L = globalThis.low; @@ -934,7 +1219,7 @@ const pinescript = { return Math.max(H[i] - L[i], Math.abs(H[i] - prevClose), Math.abs(L[i] - prevClose)); } - // Fallback: treat inputs as scalars for current bar + // When arrays are passed, compute true range from the last bar using the previous close if (Array.isArray(close)) { const prevClose = close[close.length - 2] || close[0]; const currHigh = Array.isArray(high) ? high[high.length - 1] : high; @@ -945,6 +1230,92 @@ const pinescript = { return Math.max(high - low, Math.abs(high - close), Math.abs(low - close)); }, + arrayFirst: function(arr) { + if (!arr || arr.length === 0) return null; + return arr[0]; + }, + arrayLast: function(arr) { + if (!arr || arr.length === 0) return null; + return arr[arr.length - 1]; + }, + arrayJoin: function(arr, separator = ',') { + if (!arr) return ''; + return arr.join(separator); + }, + arrayConcat: function(arr1, arr2) { + if (!arr1) return arr2 || []; + if (!arr2) return arr1; + return arr1.concat(arr2); + }, + arrayCopy: function(arr) { + if (!arr) return []; + return [...arr]; + }, + arrayBinarySearch: function(arr, value) { + if (!arr || arr.length === 0) return -1; + let lo = 0, hi = arr.length - 1; + while (lo <= hi) { + const mid = (lo + hi) >>> 1; + if (arr[mid] === value) return mid; + if (arr[mid] < value) lo = mid + 1; + else hi = mid - 1; + } + return -1; + }, + arrayRange: function(arr) { + if (!arr || arr.length === 0) return 0; + return Math.max(...arr) - Math.min(...arr); + }, + arrayMedian: function(arr) { + if (!arr || arr.length === 0) return null; + const sorted = [...arr].sort((a, b) => a - b); + const mid = Math.floor(sorted.length / 2); + return sorted.length % 2 !== 0 ? sorted[mid] : (sorted[mid - 1] + sorted[mid]) / 2; + }, + arrayMode: function(arr) { + if (!arr || arr.length === 0) return null; + const freq = {}; + let maxCount = 0; + let mode = arr[0]; + for (const v of arr) { + freq[v] = (freq[v] || 0) + 1; + if (freq[v] > maxCount) { maxCount = freq[v]; mode = v; } + } + return mode; + }, + arrayPercentile: function(arr, percentile) { + if (!arr || arr.length === 0) return null; + const sorted = [...arr].sort((a, b) => a - b); + const idx = (percentile / 100) * (sorted.length - 1); + const lo = Math.floor(idx); + const hi = Math.ceil(idx); + if (lo === hi) return sorted[lo]; + return sorted[lo] + (sorted[hi] - sorted[lo]) * (idx - lo); + }, + arrayPercentileLinearInterpolation: function(arr, percentile) { + return builtins.get('arrayPercentile')(arr, percentile); + }, + arrayPercentileNearestRank: function(arr, percentile) { + if (!arr || arr.length === 0) return null; + const sorted = [...arr].sort((a, b) => a - b); + const idx = Math.ceil((percentile / 100) * sorted.length) - 1; + return sorted[Math.max(0, idx)]; + }, + arrayAbs: function(arr) { + if (!arr) return []; + return arr.map(v => Math.abs(v)); + }, + arrayEvery: function(arr, callback) { + if (!arr) return false; + return arr.every(callback); + }, + arraySome: function(arr, callback) { + if (!arr) return false; + return arr.some(callback); + }, + requestFinancial: function() { + return null; + }, }; @@ -1101,6 +1472,115 @@ globalThis.barmerge = globalThis.barmerge || { }; +function __pineNS(real) { + + const base = real || {}; + + return new Proxy(base, { + + get(target, key) { + + if (key in target) return target[key]; + + if (typeof key === "symbol") return target[key]; + + // Unknown member: usable as a value (null), a constant, or a no-op function. + + const noop = function() { return null; }; + + noop.toString = () => ""; + + return noop; + + } + + }); + +} + +globalThis.ta = globalThis.ta || __pineNS({}); + +globalThis.math = globalThis.math || __pineNS({ pi: Math.PI, e: Math.E, phi: 1.618033988749895, rphi: 0.618033988749895 }); + +globalThis.chart = globalThis.chart || __pineNS({ point: __pineNS({}), bg_color: null, fg_color: null }); + +globalThis.line = globalThis.line || __pineNS({ style_solid: "solid", style_dashed: "dashed", style_dotted: "dotted" }); + +globalThis.box = globalThis.box || __pineNS({}); + +globalThis.label = globalThis.label || __pineNS({ style_label_down: "label_down", style_label_up: "label_up", style_none: "none" }); + +globalThis.polyline = globalThis.polyline || __pineNS({}); + +globalThis.linefill = globalThis.linefill || __pineNS({}); + +globalThis.matrix = globalThis.matrix || __pineNS({}); + +globalThis.map = globalThis.map || __pineNS({}); + +globalThis.session = globalThis.session || __pineNS({ regular: "regular", extended: "extended" }); + +globalThis.dayofweek = globalThis.dayofweek || __pineNS({ sunday: 1, monday: 2, tuesday: 3, wednesday: 4, thursday: 5, friday: 6, saturday: 7 }); + +globalThis.timeframe = __pineNS(Object.assign(globalThis.timeframe || {}, { period: (globalThis.timeframe && globalThis.timeframe.period) || "D", isintraday: false, isdaily: true, multiplier: 1 })); + +globalThis.request = __pineNS(globalThis.request || {}); + +globalThis.input = __pineNS(globalThis.input || {}); + +globalThis.adjustment = globalThis.adjustment || __pineNS({}); + +globalThis.earnings = globalThis.earnings || __pineNS({}); + +globalThis.dividends = globalThis.dividends || __pineNS({}); + +globalThis.splits = globalThis.splits || __pineNS({}); + +globalThis.currency = globalThis.currency || __pineNS({}); + +globalThis.display = globalThis.display || __pineNS({ none: "none", all: "all", pane: "pane", data_window: "data_window", status_line: "status_line", price_scale: "price_scale" }); + +globalThis.format = globalThis.format || __pineNS({ price: "price", volume: "volume", percent: "percent", mintick: "mintick", inherit: "inherit" }); + +globalThis.scale = globalThis.scale || __pineNS({ left: "left", right: "right", none: "none" }); + +globalThis.font = globalThis.font || __pineNS({ family_default: "default", family_monospace: "monospace" }); + +globalThis.xloc = globalThis.xloc || __pineNS({ bar_index: "bar_index", bar_time: "bar_time" }); + +globalThis.yloc = globalThis.yloc || __pineNS({ price: "price", abovebar: "abovebar", belowbar: "belowbar" }); + +globalThis.extend = globalThis.extend || __pineNS({ none: "none", left: "left", right: "right", both: "both" }); + +globalThis.order = globalThis.order || __pineNS({ ascending: "ascending", descending: "descending" }); + +globalThis.alert = globalThis.alert || Object.assign(function() { return null; }, { freq_once_per_bar: "once_per_bar", freq_once_per_bar_close: "once_per_bar_close", freq_all: "all" }); + +globalThis.dayofmonth = globalThis.dayofmonth || function(t) { return new Date(t || 0).getUTCDate(); }; + +globalThis.plot = globalThis.plot || __pineNS({ style_line: "line", style_linebr: "linebr", style_stepline: "stepline", style_histogram: "histogram", style_cross: "cross", style_area: "area", style_areabr: "areabr", style_columns: "columns", style_circles: "circles" }); + +globalThis.int = globalThis.int || function(x) { return x == null ? null : Math.trunc(Number(x)); }; + +globalThis.float = globalThis.float || function(x) { return x == null ? null : Number(x); }; + +globalThis.bool = globalThis.bool || function(x) { return Boolean(x); }; + +globalThis.string = globalThis.string || function(x) { return x == null ? null : String(x); }; + +globalThis.dayofweek = globalThis.dayofweek || __pineNS({ sunday: 1, monday: 2, tuesday: 3, wednesday: 4, thursday: 5, friday: 6, saturday: 7 }); + +globalThis.str = globalThis.str || __pineNS({}); + +pinescript.math = globalThis.math; + +pinescript.ta = globalThis.ta; + +pinescript.str = globalThis.str; + +pinescript.array = __pineNS(globalThis.array); + + pinescript.color = { hex: function(s) { @@ -1190,11 +1670,11 @@ pinescript.table = { }; -// Input parameters +// Script input parameters and their defaults const OrderBlock = { new: function(top, bottom, startTime, strength, isBullish, boxId, labelId, mitigated) { return { top: top, bottom: bottom, startTime: startTime, strength: strength, isBullish: isBullish, boxId: boxId, labelId: labelId, mitigated: mitigated }; } }; -// Main script logic +// The transpiled script logic, called once per bar function main() { @@ -1213,15 +1693,15 @@ function main() { let BEAR_COLOR = pinescript.color.hex("#f23645"); let TEXT_COLOR = chart.fg_color; let OB_GROUP = "Order Block Settings"; - let pivotLenInput = input.int(5, "Pivot Lookback", ({ minval: 2, group: OB_GROUP })); - let maxOBsInput = input.int(5, "Max Unmitigated OBs", ({ minval: 1, maxval: 50, group: OB_GROUP })); + let pivotLenInput = pinescript.inputInt(5, "Pivot Lookback", ({ minval: 2, group: OB_GROUP })); + let maxOBsInput = pinescript.inputInt(5, "Max Unmitigated OBs", ({ minval: 1, maxval: 50, group: OB_GROUP })); let VIS_GROUP = "Visualization"; - let bullColorInput = input.color(pinescript.color.new(BULL_COLOR, 80), "Bullish OB Color", ({ group: VIS_GROUP })); - let bearColorInput = input.color(pinescript.color.new(BEAR_COLOR, 80), "Bearish OB Color", ({ group: VIS_GROUP })); - let hideOverlappedInput = input.bool(true, "Hide Overlapped Zones", ({ group: VIS_GROUP })); - let showLabelsInput = input.bool(true, "Show Strength Labels", ({ group: VIS_GROUP })); - let showStrongestInput = input.bool(true, "Show Strongest OB Plot", ({ group: VIS_GROUP })); - let bufferSizeInput = input.int(5, "Strongest OB Buffer Size", ({ minval: 1, maxval: 100, group: VIS_GROUP })); + let bullColorInput = pinescript.inputColor(pinescript.color.new(BULL_COLOR, 80), "Bullish OB Color", ({ group: VIS_GROUP })); + let bearColorInput = pinescript.inputColor(pinescript.color.new(BEAR_COLOR, 80), "Bearish OB Color", ({ group: VIS_GROUP })); + let hideOverlappedInput = pinescript.inputBool(true, "Hide Overlapped Zones", ({ group: VIS_GROUP })); + let showLabelsInput = pinescript.inputBool(true, "Show Strength Labels", ({ group: VIS_GROUP })); + let showStrongestInput = pinescript.inputBool(true, "Show Strongest OB Plot", ({ group: VIS_GROUP })); + let bufferSizeInput = pinescript.inputInt(5, "Strongest OB Buffer Size", ({ minval: 1, maxval: 100, group: VIS_GROUP })); function calculateStrength(obRange, breakoutDist, relVol) { let dispFactor = pinescript.min((breakoutDist / (obRange * 5)), 1); let volFactor = pinescript.min((relVol / 2), 1); @@ -1244,8 +1724,8 @@ function main() { state.lastLo = lo; state.lastLoIdx = (bar_index - pivotLenInput); } - let bullBOS = pinescript.cross(close, state.lastHi); - let bearBOS = pinescript.cross(close, state.lastLo); + let bullBOS = pinescript.crossover(pinescript.series(0, close), pinescript.series(1, state.lastHi)); + let bearBOS = pinescript.crossunder(pinescript.series(2, close), pinescript.series(3, state.lastLo)); if ((bullBOS || bearBOS)) { let searchLen = (bar_index - (bullBOS ? state.lastHiIdx : state.lastLoIdx)); let obTop = null; @@ -1255,13 +1735,13 @@ function main() { if (bullBOS) { let minLow = null; for (let i = 1; i <= searchLen; i++) { - if ((pinescript.offset(close, i) < pinescript.offset(open, i))) { - if ((pinescript.na(minLow) || (pinescript.offset(low, i) < minLow))) { - minLow = pinescript.offset(low, i); - obTop = pinescript.offset(high, i); - obBtm = pinescript.offset(low, i); + if ((pinescript.hist(4, close, i) < pinescript.hist(5, open, i))) { + if ((pinescript.na(minLow) || (pinescript.hist(6, low, i) < minLow))) { + minLow = pinescript.hist(7, low, i); + obTop = pinescript.hist(8, high, i); + obBtm = pinescript.hist(9, low, i); obIdx = i; - obVol = pinescript.offset(volume, i); + obVol = pinescript.hist(10, volume, i); } } } @@ -1269,13 +1749,13 @@ function main() { } else { let maxHigh = null; for (let i = 1; i <= searchLen; i++) { - if ((pinescript.offset(close, i) > pinescript.offset(open, i))) { - if ((pinescript.na(maxHigh) || (pinescript.offset(high, i) > maxHigh))) { - maxHigh = pinescript.offset(high, i); - obTop = pinescript.offset(high, i); - obBtm = pinescript.offset(low, i); + if ((pinescript.hist(11, close, i) > pinescript.hist(12, open, i))) { + if ((pinescript.na(maxHigh) || (pinescript.hist(13, high, i) > maxHigh))) { + maxHigh = pinescript.hist(14, high, i); + obTop = pinescript.hist(15, high, i); + obBtm = pinescript.hist(16, low, i); obIdx = i; - obVol = pinescript.offset(volume, i); + obVol = pinescript.hist(17, volume, i); } } } @@ -1284,9 +1764,9 @@ function main() { if (!pinescript.na(obIdx)) { let obHeight = (obTop - obBtm); let breakoutDist = (bullBOS ? (close - obTop) : (obBtm - close)); - let avgVol = pinescript.offset(volSMA, obIdx); + let avgVol = pinescript.hist(18, volSMA, obIdx); let strength = calculateStrength(obHeight, breakoutDist, (obVol / avgVol)); - let newOB = OrderBlock.new(obTop, obBtm, pinescript.offset(time, obIdx), strength, bullBOS, null, null, false); + let newOB = OrderBlock.new(obTop, obBtm, pinescript.hist(19, time, obIdx), strength, bullBOS, null, null, false); pinescript.arrayUnshift(state.obArray, newOB); } } @@ -1326,7 +1806,7 @@ function main() { } } let plotCol = (pinescript.na(strongestAvgPrice) ? null : (strongestIsBullish ? bullColorInput : bearColorInput)); - let hasChanged = (strongestAvgPrice !== pinescript.offset(strongestAvgPrice, 1)); + let hasChanged = (strongestAvgPrice !== pinescript.hist(20, strongestAvgPrice, 1)); let plotTop = pinescript.plot((showStrongestInput ? strongestTop : null), "Strongest OB Top", ({ color: (hasChanged ? null : pinescript.color.new(plotCol, 70)) })); let plotBtm = pinescript.plot((showStrongestInput ? strongestBottom : null), "Strongest OB Bottom", ({ color: (hasChanged ? null : pinescript.color.new(plotCol, 70)) })); pinescript.fill(plotTop, plotBtm, (hasChanged ? null : pinescript.color.new(plotCol, 90)), "Strongest OB Zone Fill"); @@ -1358,16 +1838,16 @@ function main() { let obCol = (ob.isBullish ? bullColorInput : bearColorInput); let labelCol = (ob.isBullish ? BULL_COLOR : BEAR_COLOR); if (pinescript.na(ob.boxId)) { - ob.boxId = box.new(ob.startTime, ob.top, time, ob.bottom, ({ bgcolor: obCol, border_color: obCol, border_style: line.style_dashed, xloc: xloc.bar_time })); + ob.boxId = pinescript.boxNew(ob.startTime, ob.top, time, ob.bottom, ({ bgcolor: obCol, border_color: obCol, border_style: line.style_dashed, xloc: xloc.bar_time })); } if (showLabelsInput) { if (pinescript.na(ob.labelId)) { - ob.labelId = pinescript.labelNew(time, ((ob.top + ob.bottom) / 2), ({ text: str.format("{0}%", ob.strength), style: label.style_label_left, textcolor: labelCol, color: pinescript.color.hex("#00000000"), size: pinescript.size.small, textalign: pinescript.text.align_center, xloc: xloc.bar_time })); + ob.labelId = pinescript.labelNew(time, ((ob.top + ob.bottom) / 2), ({ text: pinescript.strFormat("{0}%", ob.strength), style: label.style_label_left, textcolor: labelCol, color: pinescript.color.hex("#00000000"), size: pinescript.size.small, textalign: pinescript.text.align_center, xloc: xloc.bar_time })); } } } else { if (!pinescript.na(ob.boxId)) { - box.delete(ob.boxId); + pinescript.boxDelete(ob.boxId); ob.boxId = null; } if (!pinescript.na(ob.labelId)) { @@ -1393,7 +1873,67 @@ function main() { } -// Export for use -export { main +// The bar-by-bar engine: runs main() once per bar over the dataset. +function run(data, options = {}) { + data = data || {}; + const inClose = (data.close || []).map(Number); + const n = inClose.length; + const inOpen = (data.open || inClose).map(Number); + const inHigh = (data.high || inClose).map(Number); + const inLow = (data.low || inClose).map(Number); + const inVol = (data.volume || new Array(n).fill(0)).map(Number); + const inTime = (data.time || inClose.map((_, i) => i)).map(Number); + + // Reset all persistent and per-run state so repeated runs are independent. + globalThis.__pineState = {}; + globalThis.__pineRuntime = { plots: {}, plotshapes: {}, alerts: [], bars: n, inputs: options.inputs || {} }; + pinescript.__rt = {}; + pinescript.__bar = 0; + + // Growing OHLCV windows: each is a SeriesArray that gains one element per bar, + // so window built-ins like sma and highest see history up to the current bar. + const O = pinescript.asSeries([]), H = pinescript.asSeries([]), L = pinescript.asSeries([]); + const C = pinescript.asSeries([]), V = pinescript.asSeries([]), T = pinescript.asSeries([]); + const HL2 = pinescript.asSeries([]), HLC3 = pinescript.asSeries([]), OHLC4 = pinescript.asSeries([]); + globalThis.open = O; globalThis.high = H; globalThis.low = L; globalThis.close = C; + globalThis.volume = V; globalThis.time = T; + globalThis.hl2 = HL2; globalThis.hlc3 = HLC3; globalThis.ohlc4 = OHLC4; + globalThis.syminfo = globalThis.syminfo || { tickerid: 'SYNTHETIC', ticker: 'SYNTHETIC', mintick: 0.01 }; + + const rt = globalThis.__pineRuntime; + for (let i = 0; i < n; i++) { + O.push(inOpen[i]); H.push(inHigh[i]); L.push(inLow[i]); C.push(inClose[i]); V.push(inVol[i]); T.push(inTime[i]); + HL2.push((inHigh[i] + inLow[i]) / 2); + HLC3.push((inHigh[i] + inLow[i] + inClose[i]) / 3); + OHLC4.push((inOpen[i] + inHigh[i] + inLow[i] + inClose[i]) / 4); + + pinescript.__bar = i; + rt.__barIndex = i; + rt.__plotIdx = 0; + rt.__shapeIdx = 0; + globalThis.bar_index = i; + globalThis.last_bar_index = n - 1; + globalThis.barstate = { + isfirst: i === 0, islast: i === n - 1, isrealtime: false, ishistory: true, + isconfirmed: true, isnew: true, islastconfirmedhistory: i === n - 1, + }; + + main(); + } + + // Backfill skipped bars so every output series has exactly n entries. + for (const k of Object.keys(rt.plots)) { + const d = rt.plots[k].data; + for (let i = 0; i < n; i++) if (d[i] === undefined) d[i] = null; + } + for (const k of Object.keys(rt.plotshapes)) { + const d = rt.plotshapes[k].data; + for (let i = 0; i < n; i++) if (d[i] === undefined) d[i] = false; + } + return rt; +} + + +export { main, run }; diff --git a/converted/Liquidity_Sweep_Detector.js b/converted/Liquidity_Sweep_Detector.js index 56a2245..7ed56bd 100644 --- a/converted/Liquidity_Sweep_Detector.js +++ b/converted/Liquidity_Sweep_Detector.js @@ -1,12 +1,63 @@ -// PineScript to JavaScript Transpiled Code +// Auto-generated JavaScript from PineScript source -// Generated automatically +// Do not edit by hand -- re-run the converter instead -// Built-in PineScript functions +// All the built-in PineScript functions that scripts depend on at runtime const pinescript = { + __scalar: function(v) { + if (v === null || v === undefined) return v; + if (typeof v === 'object' && typeof v.valueOf === 'function') { + const s = v.valueOf(); + if (typeof s === 'number' || typeof s === 'boolean' || s === null) return s; + } + return v; + }, + series: function(id, value) { + const rt = this.__rt || (this.__rt = {}); + const store = rt.series || (rt.series = {}); + let buf = store[id]; + if (!buf) buf = store[id] = []; + buf[this.__bar | 0] = this.__scalar(value); + return buf; + }, + hist: function(id, value, n) { + const buf = this.series(id, value); + const idx = (this.__bar | 0) - (n | 0); + return idx >= 0 && idx < buf.length ? buf[idx] : null; + }, + time: function(timeframe, session) { + const t = globalThis.time; + if (t && typeof t.valueOf === 'function') return t.valueOf(); + return Array.isArray(t) ? t[t.length - 1] : t; + }, + sign: function(x) { + if (x === null || x === undefined || (typeof x === 'number' && isNaN(x))) return null; + return Math.sign(Number(x)); + }, + unpack: function(value, count) { + if (Array.isArray(value)) return value; + if (value != null && typeof value[Symbol.iterator] === 'function') return Array.from(value); + return new Array(count || 0).fill(null); + }, + alertcondition: function(condition, ...rest) { + if (globalThis.__pineRuntime) { + globalThis.__pineRuntime.alerts.push({ condition, args: rest }); + } + return null; + }, + barcolor: function(color) { return null; }, + bgcolor: function(color, title, editable, showLast) { + return null; + }, + fill: function(series1, series2, color, title, editable) { + return null; + }, + hline: function(_price, _title, _opts) { + return null; + }, sma: function(series, length) { if (series === null || series === undefined) return null; if (!Array.isArray(series)) series = [series]; @@ -84,6 +135,13 @@ const pinescript = { } return weightSum > 0 ? sum / weightSum : null; }, + swma: function(series) { + if (series === null || series === undefined) return null; + if (!Array.isArray(series)) series = [series]; + if (series.length < 4) return null; + const s = series.slice(-4); + return (s[0] * 1 + s[1] * 2 + s[2] * 2 + s[3] * 1) / 6; + }, atr: function(high, low, close, length = 14) { if (!Array.isArray(high) || !Array.isArray(low) || !Array.isArray(close)) return null; if (high.length < length + 1 || low.length < length + 1 || close.length < length + 1) return null; @@ -114,6 +172,53 @@ const pinescript = { const atr = builtins.get('atr')(high, low, close, length); return { middle: sma, upper: sma + mult * atr, lower: sma - mult * atr }; }, + supertrend: function(factor, atrPeriod, high, low, close) { + if (!Array.isArray(high) || !Array.isArray(low) || !Array.isArray(close)) return [null, null]; + if (high.length < atrPeriod + 1) return [null, null]; + const atrVal = builtins.get('atr')(high, low, close, atrPeriod); + if (atrVal === null) return [null, null]; + const lastIdx = close.length - 1; + const hl2 = (high[lastIdx] + low[lastIdx]) / 2; + const upperBand = hl2 + factor * atrVal; + const lowerBand = hl2 - factor * atrVal; + // Determine direction: 1 means downtrend (price below band), -1 means uptrend + const direction = close[lastIdx] > upperBand ? -1 : close[lastIdx] < lowerBand ? 1 : -1; + const supertrendValue = direction === -1 ? lowerBand : upperBand; + return [supertrendValue, direction]; + }, + ta_supertrend: function(factor, atrPeriod) { + const H = globalThis.high; + const L = globalThis.low; + const C = globalThis.close; + return builtins.get('supertrend')(factor, atrPeriod, H, L, C); + }, + dmi: function(diLength, adxSmoothing, high, low, close) { + if (!Array.isArray(high) || !Array.isArray(low) || !Array.isArray(close)) return { plusDI: null, minusDI: null, adx: null }; + if (high.length < diLength + 1) return { plusDI: null, minusDI: null, adx: null }; + let plusDMSum = 0, minusDMSum = 0, trSum = 0; + for (let i = 1; i <= diLength; i++) { + const idx = high.length - 1 - diLength + i; + const upMove = high[idx] - high[idx - 1]; + const downMove = low[idx - 1] - low[idx]; + plusDMSum += (upMove > downMove && upMove > 0) ? upMove : 0; + minusDMSum += (downMove > upMove && downMove > 0) ? downMove : 0; + const tr = Math.max( + high[idx] - low[idx], + Math.abs(high[idx] - close[idx - 1]), + Math.abs(low[idx] - close[idx - 1]) + ); + trSum += tr; + } + const plusDI = trSum > 0 ? (plusDMSum / trSum) * 100 : 0; + const minusDI = trSum > 0 ? (minusDMSum / trSum) * 100 : 0; + const diSum = plusDI + minusDI; + const dx = diSum > 0 ? Math.abs(plusDI - minusDI) / diSum * 100 : 0; + return { plusDI, minusDI, adx: dx }; + }, + adx: function(diLength, adxSmoothing, high, low, close) { + const result = builtins.get('dmi')(diLength, adxSmoothing, high, low, close); + return result ? result.adx : null; + }, rsi: function(close, length = 14) { if (close === null || close === undefined) return null; if (!Array.isArray(close)) close = [close]; @@ -171,9 +276,7 @@ const pinescript = { return { macd: macdLine, signal: signalLine, histogram: histogram }; }, cci: function(a, b, c, d) { - // Pine signatures: - // - ta.cci(source, length) - // - ta.cci(high, low, close, length) + // Supports both ta.cci(source, length) and ta.cci(high, low, close, length) signatures let source; let length; if (Array.isArray(a) && typeof b === 'number' && c === undefined) { @@ -304,6 +407,15 @@ const pinescript = { stdev: function(source, length) { return Math.sqrt(builtins.get('variance')(source, length)); }, + median: function(source, length) { + if (!source || source.length < length) return null; + const sorted = [...source.slice(-length)].sort((a, b) => a - b); + const mid = Math.floor(sorted.length / 2); + return sorted.length % 2 !== 0 ? sorted[mid] : (sorted[mid - 1] + sorted[mid]) / 2; + }, + ta_median: function(source, length) { + return builtins.get('median')(source, length); + }, hl2: function(high, low) { return high && low ? (high[high.length - 1] + low[low.length - 1]) / 2 : null; }, @@ -420,6 +532,24 @@ const pinescript = { return (series1[i - 1] < series2[i - 1] && series1[i] >= series2[i]) || (series1[i - 1] > series2[i - 1] && series1[i] <= series2[i]); }, + crossover: function(series1, series2) { + if (series1 === null || series1 === undefined) return false; + if (series2 === null || series2 === undefined) return false; + if (!Array.isArray(series1)) series1 = [series1]; + if (!Array.isArray(series2)) series2 = [series2]; + if (series1.length < 2 || series2.length < 2) return false; + const i = series1.length - 1; + return series1[i - 1] < series2[i - 1] && series1[i] >= series2[i]; + }, + crossunder: function(series1, series2) { + if (series1 === null || series1 === undefined) return false; + if (series2 === null || series2 === undefined) return false; + if (!Array.isArray(series1)) series1 = [series1]; + if (!Array.isArray(series2)) series2 = [series2]; + if (series1.length < 2 || series2.length < 2) return false; + const i = series1.length - 1; + return series1[i - 1] > series2[i - 1] && series1[i] <= series2[i]; + }, offset: function(series, offset) { if (!Array.isArray(series)) return null; const idx = series.length - 1 - offset; @@ -500,12 +630,14 @@ const pinescript = { return builtins.get('nz')(value, replacement); }, plot: function(series, title = '', color = null, linewidth = 1) { - globalThis.__pineRuntime.plots.push({ - series, - title, - color, - linewidth - }); + const rt = globalThis.__pineRuntime; + if (!rt) return series; + const bar = rt.__barIndex | 0; + const ord = (rt.__plotIdx = (rt.__plotIdx | 0) + 1) - 1; + const key = (title && String(title)) || ('plot_' + ord); + let p = rt.plots[key]; + if (!p) p = rt.plots[key] = { title: key, color, linewidth, data: [] }; + p.data[bar] = this.__scalar(series); return series; }, lineNew: function(x1, y1, x2, y2, opts = {}) { @@ -514,20 +646,72 @@ const pinescript = { lineDelete: function(l) { return null; }, - labelNew: function(...args) { - if (globalThis.__pineRuntime) { - globalThis.__pineRuntime.labels = globalThis.__pineRuntime.labels || []; - globalThis.__pineRuntime.labels.push({ args }); - } - return { args }; + lineSetXY: function(line, point, x, y) { + if (!line) return null; + if (point === 0 || point === 'x1') { line.x1 = x; line.y1 = y; } + else { line.x2 = x; line.y2 = y; } + return line; + }, + lineGetX: function(line, point) { + if (!line) return null; + return point === 0 || point === 'x1' ? line.x1 : line.x2; + }, + lineGetY: function(line, point) { + if (!line) return null; + return point === 0 || point === 'y1' ? line.y1 : line.y2; + }, + labelNew: function(x, y, text = '', opts = {}) { + return { x, y, text, opts, _type: 'label' }; }, labelDelete: function(l) { return null; }, + labelSetText: function(label, text) { + if (!label) return null; + label.text = text; + return label; + }, + labelGetText: function(label) { + if (!label) return ''; + return label.text || ''; + }, + boxNew: function(left, top, right, bottom, opts = {}) { + return { left, top, right, bottom, opts, _type: 'box' }; + }, + boxDelete: function(box) { + return null; + }, + boxSetLeftTop: function(box, left, top) { + if (!box) return null; + box.left = left; + box.top = top; + return box; + }, + boxSetRightBottom: function(box, right, bottom) { + if (!box) return null; + box.right = right; + box.bottom = bottom; + return box; + }, + polylineNew: function(points, opts = {}) { + return { points: points || [], opts, _type: 'polyline' }; + }, + polylineDelete: function(poly) { + return null; + }, plotshape: function(condition, ...rest) { - if (globalThis.__pineRuntime) { - globalThis.__pineRuntime.plotshapes.push({ condition, args: rest }); + const rt = globalThis.__pineRuntime; + if (!rt) return condition; + const bar = rt.__barIndex | 0; + const ord = (rt.__shapeIdx = (rt.__shapeIdx | 0) + 1) - 1; + let key = 'shape_' + ord; + for (const r of rest) { + if (typeof r === 'string') { key = r; break; } + if (r && typeof r === 'object' && r.title) { key = String(r.title); break; } } + let s = rt.plotshapes[key]; + if (!s) s = rt.plotshapes[key] = { title: key, data: [] }; + s.data[bar] = !!this.__scalar(condition); return condition; }, indicator: function(title, shorttitle, overlay, opts) { @@ -542,27 +726,12 @@ const pinescript = { } return null; }, - alertcondition: function(condition, ...rest) { - if (globalThis.__pineRuntime) { - globalThis.__pineRuntime.alerts.push({ condition, args: rest }); - } - return null; - }, plotbar: function(open, high, low, close, title, color, editable, showLast) { return { open, high, low, close }; }, plotcandle: function(open, high, low, close, title, color, wickColor, borderColor, editable, showLast) { return { open, high, low, close }; }, - hline: function(_price, _title, _opts) { - return null; - }, - bgcolor: function(color, title, editable, showLast) { - return null; - }, - fill: function(series1, series2, color, title, editable) { - return null; - }, alert: function(condition, message, frequency) { console.log('Alert:', condition ? message : 'Condition not met'); return condition; @@ -828,6 +997,9 @@ const pinescript = { arrayIndexOf: function(arr, value) { return arr ? arr.indexOf(value) : -1; }, + arrayLastIndexOf: function(arr, value) { + return arr ? arr.lastIndexOf(value) : -1; + }, arraySum: function(arr) { return arr ? arr.reduce((a, b) => a + b, 0) : 0; }, @@ -840,6 +1012,42 @@ const pinescript = { arrayMax: function(arr) { return arr && arr.length > 0 ? Math.max(...arr) : null; }, + arrayStdev: function(arr) { + if (!arr || arr.length === 0) return null; + const mean = arr.reduce((a, b) => a + b, 0) / arr.length; + const variance = arr.reduce((sum, val) => sum + Math.pow(val - mean, 2), 0) / arr.length; + return Math.sqrt(variance); + }, + arrayVariance: function(arr) { + if (!arr || arr.length === 0) return null; + const mean = arr.reduce((a, b) => a + b, 0) / arr.length; + return arr.reduce((sum, val) => sum + Math.pow(val - mean, 2), 0) / arr.length; + }, + arrayCovariance: function(arr1, arr2) { + if (!arr1 || !arr2 || arr1.length === 0 || arr1.length !== arr2.length) return null; + const mean1 = arr1.reduce((a, b) => a + b, 0) / arr1.length; + const mean2 = arr2.reduce((a, b) => a + b, 0) / arr2.length; + let cov = 0; + for (let i = 0; i < arr1.length; i++) { + cov += (arr1[i] - mean1) * (arr2[i] - mean2); + } + return cov / arr1.length; + }, + mode: function(arr) { + if (!arr || arr.length === 0) return null; + const counts = new Map(); + let maxCount = 0; + let modeVal = arr[0]; + for (const val of arr) { + const c = (counts.get(val) || 0) + 1; + counts.set(val, c); + if (c > maxCount) { + maxCount = c; + modeVal = val; + } + } + return modeVal; + }, strLength: function(str) { return str ? str.length : 0; }, @@ -895,6 +1103,84 @@ const pinescript = { strReverse: function(str) { return str ? str.split('').reverse().join('') : ''; }, + strFormat: function(formatStr, ...args) { + if (!formatStr) return ''; + return formatStr.replace(/\{(\d+)\}/g, (match, idx) => { + const i = parseInt(idx, 10); + return i < args.length ? String(args[i]) : match; + }); + }, + input: function(defval, title, tooltip, inline, group) { + return defval; + }, + inputInt: function(defval = 0, title, minval, maxval, step, tooltip, inline, group) { + return defval; + }, + inputFloat: function(defval = 0.0, title, minval, maxval, step, tooltip, inline, group) { + return defval; + }, + inputBool: function(defval = false, title, tooltip, inline, group) { + return defval; + }, + inputString: function(defval = '', title, options, tooltip, inline, group) { + return defval; + }, + inputSource: function(defval = null, title, tooltip, inline, group) { + return defval; + }, + inputColor: function(defval = '#000000', title, tooltip, inline, group) { + return defval; + }, + inputTime: function(defval = 0, title, tooltip, inline, group) { + return defval; + }, + mathSign: function(value) { + return Math.sign(value); + }, + mathAvg: function(...args) { + const valid = args.filter(v => v !== null && v !== undefined && !isNaN(v)); + return valid.length > 0 ? valid.reduce((a, b) => a + b, 0) / valid.length : null; + }, + mathSum: function(series, length) { + if (!series || series.length < length) return null; + return series.slice(-length).reduce((a, b) => a + b, 0); + }, + mathRandom: function(min = 0, max = 1) { + return min + Math.random() * (max - min); + }, + mathTodegrees: function(radians) { + return radians * (180 / Math.PI); + }, + mathToradians: function(degrees) { + return degrees * (Math.PI / 180); + }, + colorT: function(color, transparency) { + if (transparency !== undefined) { + return { color, transparency }; + } + if (color && typeof color === 'object' && 'transparency' in color) { + return color.transparency; + } + return 0; + }, + tableCellSetText: function(table, column, row, text) { + return null; + }, + tableCellSetBgcolor: function(table, column, row, bgcolor) { + return null; + }, + tableMergeCells: function(table, startColumn, startRow, endColumn, endRow) { + return null; + }, + tableDelete: function(table) { + return null; + }, + tableClear: function(table, startColumn, startRow, endColumn, endRow) { + return null; + }, + maxBarsBack: function(series, length) { + return null; + }, pi: 3.141592653589793, e: 2.718281828459045, true: true, @@ -905,8 +1191,7 @@ const pinescript = { const info = { ticker: 'AAPL', tickerid: 'NASDAQ:AAPL', prefix: 'NASDAQ', root: 'AAPL', suffix: '' }; return info[type] || ''; }, - time: 1771178051769, - timenow: 1771178051769, + timenow: 1781574527470, barstate: "LAST", dividends: {}, splits: {}, @@ -923,7 +1208,7 @@ const pinescript = { return null; }, tr: function(high, low, close) { - // Pine signature: ta.tr(true) + // Handles both the boolean shorthand ta.tr(true) and explicit high/low/close arguments if (typeof high === 'boolean') { const H = globalThis.high; const L = globalThis.low; @@ -934,7 +1219,7 @@ const pinescript = { return Math.max(H[i] - L[i], Math.abs(H[i] - prevClose), Math.abs(L[i] - prevClose)); } - // Fallback: treat inputs as scalars for current bar + // When arrays are passed, compute true range from the last bar using the previous close if (Array.isArray(close)) { const prevClose = close[close.length - 2] || close[0]; const currHigh = Array.isArray(high) ? high[high.length - 1] : high; @@ -945,6 +1230,92 @@ const pinescript = { return Math.max(high - low, Math.abs(high - close), Math.abs(low - close)); }, + arrayFirst: function(arr) { + if (!arr || arr.length === 0) return null; + return arr[0]; + }, + arrayLast: function(arr) { + if (!arr || arr.length === 0) return null; + return arr[arr.length - 1]; + }, + arrayJoin: function(arr, separator = ',') { + if (!arr) return ''; + return arr.join(separator); + }, + arrayConcat: function(arr1, arr2) { + if (!arr1) return arr2 || []; + if (!arr2) return arr1; + return arr1.concat(arr2); + }, + arrayCopy: function(arr) { + if (!arr) return []; + return [...arr]; + }, + arrayBinarySearch: function(arr, value) { + if (!arr || arr.length === 0) return -1; + let lo = 0, hi = arr.length - 1; + while (lo <= hi) { + const mid = (lo + hi) >>> 1; + if (arr[mid] === value) return mid; + if (arr[mid] < value) lo = mid + 1; + else hi = mid - 1; + } + return -1; + }, + arrayRange: function(arr) { + if (!arr || arr.length === 0) return 0; + return Math.max(...arr) - Math.min(...arr); + }, + arrayMedian: function(arr) { + if (!arr || arr.length === 0) return null; + const sorted = [...arr].sort((a, b) => a - b); + const mid = Math.floor(sorted.length / 2); + return sorted.length % 2 !== 0 ? sorted[mid] : (sorted[mid - 1] + sorted[mid]) / 2; + }, + arrayMode: function(arr) { + if (!arr || arr.length === 0) return null; + const freq = {}; + let maxCount = 0; + let mode = arr[0]; + for (const v of arr) { + freq[v] = (freq[v] || 0) + 1; + if (freq[v] > maxCount) { maxCount = freq[v]; mode = v; } + } + return mode; + }, + arrayPercentile: function(arr, percentile) { + if (!arr || arr.length === 0) return null; + const sorted = [...arr].sort((a, b) => a - b); + const idx = (percentile / 100) * (sorted.length - 1); + const lo = Math.floor(idx); + const hi = Math.ceil(idx); + if (lo === hi) return sorted[lo]; + return sorted[lo] + (sorted[hi] - sorted[lo]) * (idx - lo); + }, + arrayPercentileLinearInterpolation: function(arr, percentile) { + return builtins.get('arrayPercentile')(arr, percentile); + }, + arrayPercentileNearestRank: function(arr, percentile) { + if (!arr || arr.length === 0) return null; + const sorted = [...arr].sort((a, b) => a - b); + const idx = Math.ceil((percentile / 100) * sorted.length) - 1; + return sorted[Math.max(0, idx)]; + }, + arrayAbs: function(arr) { + if (!arr) return []; + return arr.map(v => Math.abs(v)); + }, + arrayEvery: function(arr, callback) { + if (!arr) return false; + return arr.every(callback); + }, + arraySome: function(arr, callback) { + if (!arr) return false; + return arr.some(callback); + }, + requestFinancial: function() { + return null; + }, }; @@ -1101,6 +1472,115 @@ globalThis.barmerge = globalThis.barmerge || { }; +function __pineNS(real) { + + const base = real || {}; + + return new Proxy(base, { + + get(target, key) { + + if (key in target) return target[key]; + + if (typeof key === "symbol") return target[key]; + + // Unknown member: usable as a value (null), a constant, or a no-op function. + + const noop = function() { return null; }; + + noop.toString = () => ""; + + return noop; + + } + + }); + +} + +globalThis.ta = globalThis.ta || __pineNS({}); + +globalThis.math = globalThis.math || __pineNS({ pi: Math.PI, e: Math.E, phi: 1.618033988749895, rphi: 0.618033988749895 }); + +globalThis.chart = globalThis.chart || __pineNS({ point: __pineNS({}), bg_color: null, fg_color: null }); + +globalThis.line = globalThis.line || __pineNS({ style_solid: "solid", style_dashed: "dashed", style_dotted: "dotted" }); + +globalThis.box = globalThis.box || __pineNS({}); + +globalThis.label = globalThis.label || __pineNS({ style_label_down: "label_down", style_label_up: "label_up", style_none: "none" }); + +globalThis.polyline = globalThis.polyline || __pineNS({}); + +globalThis.linefill = globalThis.linefill || __pineNS({}); + +globalThis.matrix = globalThis.matrix || __pineNS({}); + +globalThis.map = globalThis.map || __pineNS({}); + +globalThis.session = globalThis.session || __pineNS({ regular: "regular", extended: "extended" }); + +globalThis.dayofweek = globalThis.dayofweek || __pineNS({ sunday: 1, monday: 2, tuesday: 3, wednesday: 4, thursday: 5, friday: 6, saturday: 7 }); + +globalThis.timeframe = __pineNS(Object.assign(globalThis.timeframe || {}, { period: (globalThis.timeframe && globalThis.timeframe.period) || "D", isintraday: false, isdaily: true, multiplier: 1 })); + +globalThis.request = __pineNS(globalThis.request || {}); + +globalThis.input = __pineNS(globalThis.input || {}); + +globalThis.adjustment = globalThis.adjustment || __pineNS({}); + +globalThis.earnings = globalThis.earnings || __pineNS({}); + +globalThis.dividends = globalThis.dividends || __pineNS({}); + +globalThis.splits = globalThis.splits || __pineNS({}); + +globalThis.currency = globalThis.currency || __pineNS({}); + +globalThis.display = globalThis.display || __pineNS({ none: "none", all: "all", pane: "pane", data_window: "data_window", status_line: "status_line", price_scale: "price_scale" }); + +globalThis.format = globalThis.format || __pineNS({ price: "price", volume: "volume", percent: "percent", mintick: "mintick", inherit: "inherit" }); + +globalThis.scale = globalThis.scale || __pineNS({ left: "left", right: "right", none: "none" }); + +globalThis.font = globalThis.font || __pineNS({ family_default: "default", family_monospace: "monospace" }); + +globalThis.xloc = globalThis.xloc || __pineNS({ bar_index: "bar_index", bar_time: "bar_time" }); + +globalThis.yloc = globalThis.yloc || __pineNS({ price: "price", abovebar: "abovebar", belowbar: "belowbar" }); + +globalThis.extend = globalThis.extend || __pineNS({ none: "none", left: "left", right: "right", both: "both" }); + +globalThis.order = globalThis.order || __pineNS({ ascending: "ascending", descending: "descending" }); + +globalThis.alert = globalThis.alert || Object.assign(function() { return null; }, { freq_once_per_bar: "once_per_bar", freq_once_per_bar_close: "once_per_bar_close", freq_all: "all" }); + +globalThis.dayofmonth = globalThis.dayofmonth || function(t) { return new Date(t || 0).getUTCDate(); }; + +globalThis.plot = globalThis.plot || __pineNS({ style_line: "line", style_linebr: "linebr", style_stepline: "stepline", style_histogram: "histogram", style_cross: "cross", style_area: "area", style_areabr: "areabr", style_columns: "columns", style_circles: "circles" }); + +globalThis.int = globalThis.int || function(x) { return x == null ? null : Math.trunc(Number(x)); }; + +globalThis.float = globalThis.float || function(x) { return x == null ? null : Number(x); }; + +globalThis.bool = globalThis.bool || function(x) { return Boolean(x); }; + +globalThis.string = globalThis.string || function(x) { return x == null ? null : String(x); }; + +globalThis.dayofweek = globalThis.dayofweek || __pineNS({ sunday: 1, monday: 2, tuesday: 3, wednesday: 4, thursday: 5, friday: 6, saturday: 7 }); + +globalThis.str = globalThis.str || __pineNS({}); + +pinescript.math = globalThis.math; + +pinescript.ta = globalThis.ta; + +pinescript.str = globalThis.str; + +pinescript.array = __pineNS(globalThis.array); + + pinescript.color = { hex: function(s) { @@ -1190,10 +1670,10 @@ pinescript.table = { }; -// Input parameters +// Script input parameters and their defaults -// Main script logic +// The transpiled script logic, called once per bar function main() { @@ -1209,28 +1689,28 @@ function main() { // Study: Liquidity Sweep Detector [DefinedEdge] // Options: {"shorttitle":"Sweep Detector","overlay":true,"max_boxes_count":500,"max_labels_count":500,"max_lines_count":500} let zoneGroup = "⚙️ Zone Detection"; - let length = input.int(8, "Pivot Lookback", ({ minval: 3, maxval: 50, group: zoneGroup, tooltip: "Higher = fewer but more significant swing points. Lower = more zones and more sweep signals." })); - let maxZones = input.int(8, "Max Active Zones", ({ minval: 2, maxval: 15, group: zoneGroup, tooltip: "How many unbroken zones to track per side" })); - let zoneRange = input.string("Wick Rejection", "Zone Range", ({ options: ["Wick Rejection", "Full Bar"], group: zoneGroup })); - let showInvalidated = input.bool(false, "Show Invalidated Zones (faded)", ({ group: zoneGroup })); + let length = pinescript.inputInt(8, "Pivot Lookback", ({ minval: 3, maxval: 50, group: zoneGroup, tooltip: "Higher = fewer but more significant swing points. Lower = more zones and more sweep signals." })); + let maxZones = pinescript.inputInt(8, "Max Active Zones", ({ minval: 2, maxval: 15, group: zoneGroup, tooltip: "How many unbroken zones to track per side" })); + let zoneRange = pinescript.inputString("Wick Rejection", "Zone Range", ({ options: ["Wick Rejection", "Full Bar"], group: zoneGroup })); + let showInvalidated = pinescript.inputBool(false, "Show Invalidated Zones (faded)", ({ group: zoneGroup })); let sweepGroup = "🎯 Sweep Detection"; - let showSweeps = input.bool(true, "Show Sweep Signals", ({ group: sweepGroup })); - let showSweepBG = input.bool(true, "Highlight Sweep Bars", ({ group: sweepGroup })); + let showSweeps = pinescript.inputBool(true, "Show Sweep Signals", ({ group: sweepGroup })); + let showSweepBG = pinescript.inputBool(true, "Highlight Sweep Bars", ({ group: sweepGroup })); let filterGroup = "🔬 Signal Quality Filters"; - let useTrendFilter = input.bool(false, "Trend Filter (EMA)", ({ group: filterGroup, tooltip: "Only show bull sweeps above EMA, bear sweeps below EMA. With-trend sweeps = institutional reloading." })); - let trendEmaLen = input.int(50, "Trend EMA Length", ({ minval: 10, maxval: 200, group: filterGroup })); - let useVolFilter = input.bool(false, "Volume Spike Filter", ({ group: filterGroup, tooltip: "Sweep bar must have higher-than-average volume. Real liquidity grabs leave a volume footprint." })); - let volMultiplier = input.float(1.2, "Volume Multiplier", ({ minval: 1, maxval: 5, step: 0.1, group: filterGroup })); - let volAvgLen = input.int(20, "Volume Avg Length", ({ minval: 5, maxval: 100, group: filterGroup })); - let useWickFilter = input.bool(false, "Wick Rejection Filter", ({ group: filterGroup, tooltip: "Sweep bar must show strong rejection — big wick, small body relative to total range." })); - let wickMinPct = input.float(40, "Min Wick %", ({ minval: 20, maxval: 90, step: 5, group: filterGroup, tooltip: "Minimum percentage of bar range that must be wick (rejection strength)" })); + let useTrendFilter = pinescript.inputBool(false, "Trend Filter (EMA)", ({ group: filterGroup, tooltip: "Only show bull sweeps above EMA, bear sweeps below EMA. With-trend sweeps = institutional reloading." })); + let trendEmaLen = pinescript.inputInt(50, "Trend EMA Length", ({ minval: 10, maxval: 200, group: filterGroup })); + let useVolFilter = pinescript.inputBool(false, "Volume Spike Filter", ({ group: filterGroup, tooltip: "Sweep bar must have higher-than-average volume. Real liquidity grabs leave a volume footprint." })); + let volMultiplier = pinescript.inputFloat(1.2, "Volume Multiplier", ({ minval: 1, maxval: 5, step: 0.1, group: filterGroup })); + let volAvgLen = pinescript.inputInt(20, "Volume Avg Length", ({ minval: 5, maxval: 100, group: filterGroup })); + let useWickFilter = pinescript.inputBool(false, "Wick Rejection Filter", ({ group: filterGroup, tooltip: "Sweep bar must show strong rejection — big wick, small body relative to total range." })); + let wickMinPct = pinescript.inputFloat(40, "Min Wick %", ({ minval: 20, maxval: 90, step: 5, group: filterGroup, tooltip: "Minimum percentage of bar range that must be wick (rejection strength)" })); let vizGroup = "🎨 Visual Settings"; - let showDash = input.bool(true, "Show Dashboard", ({ group: vizGroup })); - let dashPos = input.string("Top Right", "Dashboard Position", ({ options: ["Top Right", "Top Left", "Bottom Right", "Bottom Left"], group: vizGroup })); - let dashSize = input.int(2, "Dashboard Size (1-3)", ({ minval: 1, maxval: 3, group: vizGroup })); - let bullCol = input.color(pinescript.color.hex("#00b8d4"), "Support Zones", ({ group: vizGroup })); - let bearCol = input.color(pinescript.color.hex("#ff6d00"), "Resistance Zones", ({ group: vizGroup })); - let sweepCol = input.color(pinescript.color.hex("#e040fb"), "Sweep Signal", ({ group: vizGroup })); + let showDash = pinescript.inputBool(true, "Show Dashboard", ({ group: vizGroup })); + let dashPos = pinescript.inputString("Top Right", "Dashboard Position", ({ options: ["Top Right", "Top Left", "Bottom Right", "Bottom Left"], group: vizGroup })); + let dashSize = pinescript.inputInt(2, "Dashboard Size (1-3)", ({ minval: 1, maxval: 3, group: vizGroup })); + let bullCol = pinescript.inputColor(pinescript.color.hex("#00b8d4"), "Support Zones", ({ group: vizGroup })); + let bearCol = pinescript.inputColor(pinescript.color.hex("#ff6d00"), "Resistance Zones", ({ group: vizGroup })); + let sweepCol = pinescript.inputColor(pinescript.color.hex("#e040fb"), "Sweep Signal", ({ group: vizGroup })); if (state.rz_top === undefined) state.rz_top = pinescript.arrayNew(); if (state.rz_btm === undefined) state.rz_btm = pinescript.arrayNew(); if (state.rz_x1 === undefined) state.rz_x1 = pinescript.arrayNew(); @@ -1257,7 +1737,7 @@ function main() { pinescript.arrayShift(state.rz_broken); pinescript.arrayShift(state.rz_swept); pinescript.arrayShift(state.rz_tests); - box.delete(pinescript.arrayShift(state.rz_box)); + pinescript.boxDelete(pinescript.arrayShift(state.rz_box)); pinescript.lineDelete(pinescript.arrayShift(state.rz_line)); } pinescript.arrayPush(state.rz_top, top); @@ -1266,7 +1746,7 @@ function main() { pinescript.arrayPush(state.rz_broken, false); pinescript.arrayPush(state.rz_swept, false); pinescript.arrayPush(state.rz_tests, 0); - pinescript.arrayPush(state.rz_box, box.new(x, top, x, btm, ({ border_color: pinescript.color.new(bearCol, 60), border_width: 1, bgcolor: pinescript.color.new(bearCol, 88), border_style: line.style_solid }))); + pinescript.arrayPush(state.rz_box, pinescript.boxNew(x, top, x, btm, ({ border_color: pinescript.color.new(bearCol, 60), border_width: 1, bgcolor: pinescript.color.new(bearCol, 88), border_style: line.style_solid }))); return pinescript.arrayPush(state.rz_line, pinescript.lineNew(x, top, x, top, ({ color: bearCol, width: 1, style: line.style_dotted }))); } function addSupportZone(top, btm, x) { @@ -1277,7 +1757,7 @@ function main() { pinescript.arrayShift(state.sz_broken); pinescript.arrayShift(state.sz_swept); pinescript.arrayShift(state.sz_tests); - box.delete(pinescript.arrayShift(state.sz_box)); + pinescript.boxDelete(pinescript.arrayShift(state.sz_box)); pinescript.lineDelete(pinescript.arrayShift(state.sz_line)); } pinescript.arrayPush(state.sz_top, top); @@ -1286,19 +1766,19 @@ function main() { pinescript.arrayPush(state.sz_broken, false); pinescript.arrayPush(state.sz_swept, false); pinescript.arrayPush(state.sz_tests, 0); - pinescript.arrayPush(state.sz_box, box.new(x, top, x, btm, ({ border_color: pinescript.color.new(bullCol, 60), border_width: 1, bgcolor: pinescript.color.new(bullCol, 88), border_style: line.style_solid }))); + pinescript.arrayPush(state.sz_box, pinescript.boxNew(x, top, x, btm, ({ border_color: pinescript.color.new(bullCol, 60), border_width: 1, bgcolor: pinescript.color.new(bullCol, 88), border_style: line.style_solid }))); return pinescript.arrayPush(state.sz_line, pinescript.lineNew(x, btm, x, btm, ({ color: bullCol, width: 1, style: line.style_dotted }))); } let ph = ta.pivothigh(length, length); let pl = ta.pivotlow(length, length); if (ph) { - let _top = pinescript.offset(high, length); - let _btm = ((zoneRange === "Wick Rejection") ? pinescript.max(pinescript.offset(open, length), pinescript.offset(close, length)) : pinescript.offset(low, length)); + let _top = pinescript.hist(0, high, length); + let _btm = ((zoneRange === "Wick Rejection") ? pinescript.max(pinescript.hist(1, open, length), pinescript.hist(2, close, length)) : pinescript.hist(3, low, length)); addResistanceZone(_top, _btm, (bar_index - length)); } if (pl) { - _top = ((zoneRange === "Wick Rejection") ? pinescript.min(pinescript.offset(open, length), pinescript.offset(close, length)) : pinescript.offset(high, length)); - _btm = pinescript.offset(low, length); + _top = ((zoneRange === "Wick Rejection") ? pinescript.min(pinescript.hist(4, open, length), pinescript.hist(5, close, length)) : pinescript.hist(6, high, length)); + _btm = pinescript.hist(7, low, length); addSupportZone(_top, _btm, (bar_index - length)); } if (state.bullSweepNow === undefined) state.bullSweepNow = false; @@ -1342,7 +1822,7 @@ function main() { box.set_border_width(_bx, 2); box.set_bgcolor(_bx, pinescript.color.new(sweepCol, 80)); } - if (((close > _top) && (pinescript.offset(close, 1) > _top))) { + if (((close > _top) && (pinescript.hist(8, close, 1) > _top))) { pinescript.arraySet(state.rz_broken, i, true); if (showInvalidated) { box.set_bgcolor(_bx, pinescript.color.new(bearCol, 95)); @@ -1390,7 +1870,7 @@ function main() { box.set_border_width(_bx, 2); box.set_bgcolor(_bx, pinescript.color.new(sweepCol, 80)); } - if (((close < _btm) && (pinescript.offset(close, 1) < _btm))) { + if (((close < _btm) && (pinescript.hist(9, close, 1) < _btm))) { pinescript.arraySet(state.sz_broken, i, true); if (showInvalidated) { box.set_bgcolor(_bx, pinescript.color.new(bullCol, 95)); @@ -1490,7 +1970,7 @@ function main() { if (state.dash === undefined) state.dash = pinescript.table.new(dashPosTV, 3, 9, ({ bgcolor: pinescript.color.new(pinescript.color.hex("#0a0e17"), 5), border_color: pinescript.color.new(pinescript.color.hex("#1e2a3a"), 0), border_width: 2, frame_color: pinescript.color.new(pinescript.color.hex("#1e2a3a"), 0), frame_width: 3 })); if ((showDash && barstate.islast)) { pinescript.table.cell(state.dash, 0, 0, "🎯 SWEEP DETECTOR", ({ text_color: pinescript.color.white, text_size: textTitle, bgcolor: pinescript.color.new(sweepCol, 40), text_halign: pinescript.text.align_center })); - table.merge_cells(state.dash, 0, 0, 2, 0); + pinescript.tableMergeCells(state.dash, 0, 0, 2, 0); pinescript.table.cell(state.dash, 0, 1, "ZONES", ({ text_color: pinescript.color.new(pinescript.color.white, 40), text_size: textLabel, bgcolor: pinescript.color.new(pinescript.color.hex("#0d1117"), 0) })); pinescript.table.cell(state.dash, 1, 1, (pinescript.strToString(activeResist) + " 🔴"), ({ text_color: bearCol, text_size: textValue, bgcolor: pinescript.color.new(pinescript.color.hex("#0d1117"), 0), text_halign: pinescript.text.align_center })); pinescript.table.cell(state.dash, 2, 1, (pinescript.strToString(activeSup) + " 🟢"), ({ text_color: bullCol, text_size: textValue, bgcolor: pinescript.color.new(pinescript.color.hex("#0d1117"), 0), text_halign: pinescript.text.align_center })); @@ -1498,19 +1978,19 @@ function main() { let nrCol = (!pinescript.na(nearResistPct) ? ((nearResistPct < 1) ? bearCol : ((nearResistPct < 3) ? pinescript.color.new(bearCol, 40) : pinescript.color.new(pinescript.color.white, 40))) : pinescript.color.new(pinescript.color.white, 60)); pinescript.table.cell(state.dash, 0, 2, "RESIST", ({ text_color: pinescript.color.new(pinescript.color.white, 40), text_size: textLabel, bgcolor: pinescript.color.new(pinescript.color.hex("#0d1117"), 0) })); pinescript.table.cell(state.dash, 1, 2, nrText, ({ text_color: nrCol, text_size: textValue, bgcolor: pinescript.color.new(pinescript.color.hex("#0d1117"), 0), text_halign: pinescript.text.align_center })); - table.merge_cells(state.dash, 1, 2, 2, 2); + pinescript.tableMergeCells(state.dash, 1, 2, 2, 2); let nsText = (!pinescript.na(nearSupPct) ? (pinescript.strToString(nearSupPct, "#.##") + "% away") : "— none —"); let nsCol = (!pinescript.na(nearSupPct) ? ((nearSupPct < 1) ? bullCol : ((nearSupPct < 3) ? pinescript.color.new(bullCol, 40) : pinescript.color.new(pinescript.color.white, 40))) : pinescript.color.new(pinescript.color.white, 60)); pinescript.table.cell(state.dash, 0, 3, "SUPPORT", ({ text_color: pinescript.color.new(pinescript.color.white, 40), text_size: textLabel, bgcolor: pinescript.color.new(pinescript.color.hex("#0d1117"), 0) })); pinescript.table.cell(state.dash, 1, 3, nsText, ({ text_color: nsCol, text_size: textValue, bgcolor: pinescript.color.new(pinescript.color.hex("#0d1117"), 0), text_halign: pinescript.text.align_center })); - table.merge_cells(state.dash, 1, 3, 2, 3); + pinescript.tableMergeCells(state.dash, 1, 3, 2, 3); pinescript.table.cell(state.dash, 0, 4, "SWEEPS", ({ text_color: pinescript.color.new(pinescript.color.white, 40), text_size: textLabel, bgcolor: pinescript.color.new(pinescript.color.hex("#0d1117"), 0) })); pinescript.table.cell(state.dash, 1, 4, ("▼ " + pinescript.strToString(state.totalBearSweeps)), ({ text_color: bearCol, text_size: textValue, bgcolor: pinescript.color.new(pinescript.color.hex("#0d1117"), 0), text_halign: pinescript.text.align_center })); pinescript.table.cell(state.dash, 2, 4, ("▲ " + pinescript.strToString(state.totalBullSweeps)), ({ text_color: bullCol, text_size: textValue, bgcolor: pinescript.color.new(pinescript.color.hex("#0d1117"), 0), text_halign: pinescript.text.align_center })); let biasText = ((state.totalBullSweeps > state.totalBearSweeps) ? "🟢 BULLISH BIAS" : ((state.totalBearSweeps > state.totalBullSweeps) ? "🔴 BEARISH BIAS" : "⚪ NEUTRAL")); let biasBG = ((state.totalBullSweeps > state.totalBearSweeps) ? pinescript.color.new(bullCol, 80) : ((state.totalBearSweeps > state.totalBullSweeps) ? pinescript.color.new(bearCol, 80) : pinescript.color.new(pinescript.color.gray, 80))); pinescript.table.cell(state.dash, 0, 5, biasText, ({ text_color: pinescript.color.white, text_size: textValue, bgcolor: biasBG, text_halign: pinescript.text.align_center })); - table.merge_cells(state.dash, 0, 5, 2, 5); + pinescript.tableMergeCells(state.dash, 0, 5, 2, 5); let tF = (useTrendFilter ? "✅ Trend" : "❌ Trend"); let vF = (useVolFilter ? "✅ Vol" : "❌ Vol"); let wF = (useWickFilter ? "✅ Wick" : "❌ Wick"); @@ -1518,19 +1998,79 @@ function main() { let filterBG = ((filtersOn === 3) ? pinescript.color.new(pinescript.color.hex("#00e676"), 85) : ((filtersOn >= 1) ? pinescript.color.new(pinescript.color.yellow, 85) : pinescript.color.new(pinescript.color.hex("#ff1744"), 85))); pinescript.table.cell(state.dash, 0, 6, "FILTERS", ({ text_color: pinescript.color.new(pinescript.color.white, 40), text_size: textLabel, bgcolor: pinescript.color.new(pinescript.color.hex("#0d1117"), 0) })); pinescript.table.cell(state.dash, 1, 6, ((((tF + " ") + vF) + " ") + wF), ({ text_color: pinescript.color.new(pinescript.color.white, 30), text_size: textLabel, bgcolor: filterBG, text_halign: pinescript.text.align_center })); - table.merge_cells(state.dash, 1, 6, 2, 6); + pinescript.tableMergeCells(state.dash, 1, 6, 2, 6); pinescript.table.cell(state.dash, 0, 7, (("Based on " + pinescript.strToString((state.totalBullSweeps + state.totalBearSweeps))) + " sweeps detected"), ({ text_color: pinescript.color.new(pinescript.color.white, 65), text_size: pinescript.size.tiny, bgcolor: pinescript.color.new(pinescript.color.hex("#0d1117"), 0), text_halign: pinescript.text.align_center })); - table.merge_cells(state.dash, 0, 7, 2, 7); + pinescript.tableMergeCells(state.dash, 0, 7, 2, 7); pinescript.table.cell(state.dash, 0, 8, ((("⚡ DefinedEdge — Sweep Detector • " + syminfo.ticker) + " • ") + timeframe.period), ({ text_color: pinescript.color.new(pinescript.color.white, 60), text_size: pinescript.size.small, bgcolor: pinescript.color.new(pinescript.color.hex("#0d1117"), 0), text_halign: pinescript.text.align_center })); - table.merge_cells(state.dash, 0, 8, 2, 8); + pinescript.tableMergeCells(state.dash, 0, 8, 2, 8); } - alertcondition(state.bullSweepNow, "🟢 Bullish Sweep", "Sweep Detector: Bullish liquidity sweep detected! Smart money grabbed stops below support."); - alertcondition(state.bearSweepNow, "🔴 Bearish Sweep", "Sweep Detector: Bearish liquidity sweep detected! Smart money grabbed stops above resistance."); - alertcondition((state.bullSweepNow || state.bearSweepNow), "Any Sweep", "Sweep Detector: Liquidity sweep detected! Check chart for direction."); + pinescript.alertcondition(state.bullSweepNow, "🟢 Bullish Sweep", "Sweep Detector: Bullish liquidity sweep detected! Smart money grabbed stops below support."); + pinescript.alertcondition(state.bearSweepNow, "🔴 Bearish Sweep", "Sweep Detector: Bearish liquidity sweep detected! Smart money grabbed stops above resistance."); + pinescript.alertcondition((state.bullSweepNow || state.bearSweepNow), "Any Sweep", "Sweep Detector: Liquidity sweep detected! Check chart for direction."); } -// Export for use -export { main +// The bar-by-bar engine: runs main() once per bar over the dataset. +function run(data, options = {}) { + data = data || {}; + const inClose = (data.close || []).map(Number); + const n = inClose.length; + const inOpen = (data.open || inClose).map(Number); + const inHigh = (data.high || inClose).map(Number); + const inLow = (data.low || inClose).map(Number); + const inVol = (data.volume || new Array(n).fill(0)).map(Number); + const inTime = (data.time || inClose.map((_, i) => i)).map(Number); + + // Reset all persistent and per-run state so repeated runs are independent. + globalThis.__pineState = {}; + globalThis.__pineRuntime = { plots: {}, plotshapes: {}, alerts: [], bars: n, inputs: options.inputs || {} }; + pinescript.__rt = {}; + pinescript.__bar = 0; + + // Growing OHLCV windows: each is a SeriesArray that gains one element per bar, + // so window built-ins like sma and highest see history up to the current bar. + const O = pinescript.asSeries([]), H = pinescript.asSeries([]), L = pinescript.asSeries([]); + const C = pinescript.asSeries([]), V = pinescript.asSeries([]), T = pinescript.asSeries([]); + const HL2 = pinescript.asSeries([]), HLC3 = pinescript.asSeries([]), OHLC4 = pinescript.asSeries([]); + globalThis.open = O; globalThis.high = H; globalThis.low = L; globalThis.close = C; + globalThis.volume = V; globalThis.time = T; + globalThis.hl2 = HL2; globalThis.hlc3 = HLC3; globalThis.ohlc4 = OHLC4; + globalThis.syminfo = globalThis.syminfo || { tickerid: 'SYNTHETIC', ticker: 'SYNTHETIC', mintick: 0.01 }; + + const rt = globalThis.__pineRuntime; + for (let i = 0; i < n; i++) { + O.push(inOpen[i]); H.push(inHigh[i]); L.push(inLow[i]); C.push(inClose[i]); V.push(inVol[i]); T.push(inTime[i]); + HL2.push((inHigh[i] + inLow[i]) / 2); + HLC3.push((inHigh[i] + inLow[i] + inClose[i]) / 3); + OHLC4.push((inOpen[i] + inHigh[i] + inLow[i] + inClose[i]) / 4); + + pinescript.__bar = i; + rt.__barIndex = i; + rt.__plotIdx = 0; + rt.__shapeIdx = 0; + globalThis.bar_index = i; + globalThis.last_bar_index = n - 1; + globalThis.barstate = { + isfirst: i === 0, islast: i === n - 1, isrealtime: false, ishistory: true, + isconfirmed: true, isnew: true, islastconfirmedhistory: i === n - 1, + }; + + main(); + } + + // Backfill skipped bars so every output series has exactly n entries. + for (const k of Object.keys(rt.plots)) { + const d = rt.plots[k].data; + for (let i = 0; i < n; i++) if (d[i] === undefined) d[i] = null; + } + for (const k of Object.keys(rt.plotshapes)) { + const d = rt.plotshapes[k].data; + for (let i = 0; i < n; i++) if (d[i] === undefined) d[i] = false; + } + return rt; +} + + +export { main, run }; diff --git a/converted/Manias_Panics_and_Crashes.js b/converted/Manias_Panics_and_Crashes.js index 53bd921..951649d 100644 --- a/converted/Manias_Panics_and_Crashes.js +++ b/converted/Manias_Panics_and_Crashes.js @@ -1,12 +1,63 @@ -// PineScript to JavaScript Transpiled Code +// Auto-generated JavaScript from PineScript source -// Generated automatically +// Do not edit by hand -- re-run the converter instead -// Built-in PineScript functions +// All the built-in PineScript functions that scripts depend on at runtime const pinescript = { + __scalar: function(v) { + if (v === null || v === undefined) return v; + if (typeof v === 'object' && typeof v.valueOf === 'function') { + const s = v.valueOf(); + if (typeof s === 'number' || typeof s === 'boolean' || s === null) return s; + } + return v; + }, + series: function(id, value) { + const rt = this.__rt || (this.__rt = {}); + const store = rt.series || (rt.series = {}); + let buf = store[id]; + if (!buf) buf = store[id] = []; + buf[this.__bar | 0] = this.__scalar(value); + return buf; + }, + hist: function(id, value, n) { + const buf = this.series(id, value); + const idx = (this.__bar | 0) - (n | 0); + return idx >= 0 && idx < buf.length ? buf[idx] : null; + }, + time: function(timeframe, session) { + const t = globalThis.time; + if (t && typeof t.valueOf === 'function') return t.valueOf(); + return Array.isArray(t) ? t[t.length - 1] : t; + }, + sign: function(x) { + if (x === null || x === undefined || (typeof x === 'number' && isNaN(x))) return null; + return Math.sign(Number(x)); + }, + unpack: function(value, count) { + if (Array.isArray(value)) return value; + if (value != null && typeof value[Symbol.iterator] === 'function') return Array.from(value); + return new Array(count || 0).fill(null); + }, + alertcondition: function(condition, ...rest) { + if (globalThis.__pineRuntime) { + globalThis.__pineRuntime.alerts.push({ condition, args: rest }); + } + return null; + }, + barcolor: function(color) { return null; }, + bgcolor: function(color, title, editable, showLast) { + return null; + }, + fill: function(series1, series2, color, title, editable) { + return null; + }, + hline: function(_price, _title, _opts) { + return null; + }, sma: function(series, length) { if (series === null || series === undefined) return null; if (!Array.isArray(series)) series = [series]; @@ -84,6 +135,13 @@ const pinescript = { } return weightSum > 0 ? sum / weightSum : null; }, + swma: function(series) { + if (series === null || series === undefined) return null; + if (!Array.isArray(series)) series = [series]; + if (series.length < 4) return null; + const s = series.slice(-4); + return (s[0] * 1 + s[1] * 2 + s[2] * 2 + s[3] * 1) / 6; + }, atr: function(high, low, close, length = 14) { if (!Array.isArray(high) || !Array.isArray(low) || !Array.isArray(close)) return null; if (high.length < length + 1 || low.length < length + 1 || close.length < length + 1) return null; @@ -114,6 +172,53 @@ const pinescript = { const atr = builtins.get('atr')(high, low, close, length); return { middle: sma, upper: sma + mult * atr, lower: sma - mult * atr }; }, + supertrend: function(factor, atrPeriod, high, low, close) { + if (!Array.isArray(high) || !Array.isArray(low) || !Array.isArray(close)) return [null, null]; + if (high.length < atrPeriod + 1) return [null, null]; + const atrVal = builtins.get('atr')(high, low, close, atrPeriod); + if (atrVal === null) return [null, null]; + const lastIdx = close.length - 1; + const hl2 = (high[lastIdx] + low[lastIdx]) / 2; + const upperBand = hl2 + factor * atrVal; + const lowerBand = hl2 - factor * atrVal; + // Determine direction: 1 means downtrend (price below band), -1 means uptrend + const direction = close[lastIdx] > upperBand ? -1 : close[lastIdx] < lowerBand ? 1 : -1; + const supertrendValue = direction === -1 ? lowerBand : upperBand; + return [supertrendValue, direction]; + }, + ta_supertrend: function(factor, atrPeriod) { + const H = globalThis.high; + const L = globalThis.low; + const C = globalThis.close; + return builtins.get('supertrend')(factor, atrPeriod, H, L, C); + }, + dmi: function(diLength, adxSmoothing, high, low, close) { + if (!Array.isArray(high) || !Array.isArray(low) || !Array.isArray(close)) return { plusDI: null, minusDI: null, adx: null }; + if (high.length < diLength + 1) return { plusDI: null, minusDI: null, adx: null }; + let plusDMSum = 0, minusDMSum = 0, trSum = 0; + for (let i = 1; i <= diLength; i++) { + const idx = high.length - 1 - diLength + i; + const upMove = high[idx] - high[idx - 1]; + const downMove = low[idx - 1] - low[idx]; + plusDMSum += (upMove > downMove && upMove > 0) ? upMove : 0; + minusDMSum += (downMove > upMove && downMove > 0) ? downMove : 0; + const tr = Math.max( + high[idx] - low[idx], + Math.abs(high[idx] - close[idx - 1]), + Math.abs(low[idx] - close[idx - 1]) + ); + trSum += tr; + } + const plusDI = trSum > 0 ? (plusDMSum / trSum) * 100 : 0; + const minusDI = trSum > 0 ? (minusDMSum / trSum) * 100 : 0; + const diSum = plusDI + minusDI; + const dx = diSum > 0 ? Math.abs(plusDI - minusDI) / diSum * 100 : 0; + return { plusDI, minusDI, adx: dx }; + }, + adx: function(diLength, adxSmoothing, high, low, close) { + const result = builtins.get('dmi')(diLength, adxSmoothing, high, low, close); + return result ? result.adx : null; + }, rsi: function(close, length = 14) { if (close === null || close === undefined) return null; if (!Array.isArray(close)) close = [close]; @@ -171,9 +276,7 @@ const pinescript = { return { macd: macdLine, signal: signalLine, histogram: histogram }; }, cci: function(a, b, c, d) { - // Pine signatures: - // - ta.cci(source, length) - // - ta.cci(high, low, close, length) + // Supports both ta.cci(source, length) and ta.cci(high, low, close, length) signatures let source; let length; if (Array.isArray(a) && typeof b === 'number' && c === undefined) { @@ -304,6 +407,15 @@ const pinescript = { stdev: function(source, length) { return Math.sqrt(builtins.get('variance')(source, length)); }, + median: function(source, length) { + if (!source || source.length < length) return null; + const sorted = [...source.slice(-length)].sort((a, b) => a - b); + const mid = Math.floor(sorted.length / 2); + return sorted.length % 2 !== 0 ? sorted[mid] : (sorted[mid - 1] + sorted[mid]) / 2; + }, + ta_median: function(source, length) { + return builtins.get('median')(source, length); + }, hl2: function(high, low) { return high && low ? (high[high.length - 1] + low[low.length - 1]) / 2 : null; }, @@ -420,6 +532,24 @@ const pinescript = { return (series1[i - 1] < series2[i - 1] && series1[i] >= series2[i]) || (series1[i - 1] > series2[i - 1] && series1[i] <= series2[i]); }, + crossover: function(series1, series2) { + if (series1 === null || series1 === undefined) return false; + if (series2 === null || series2 === undefined) return false; + if (!Array.isArray(series1)) series1 = [series1]; + if (!Array.isArray(series2)) series2 = [series2]; + if (series1.length < 2 || series2.length < 2) return false; + const i = series1.length - 1; + return series1[i - 1] < series2[i - 1] && series1[i] >= series2[i]; + }, + crossunder: function(series1, series2) { + if (series1 === null || series1 === undefined) return false; + if (series2 === null || series2 === undefined) return false; + if (!Array.isArray(series1)) series1 = [series1]; + if (!Array.isArray(series2)) series2 = [series2]; + if (series1.length < 2 || series2.length < 2) return false; + const i = series1.length - 1; + return series1[i - 1] > series2[i - 1] && series1[i] <= series2[i]; + }, offset: function(series, offset) { if (!Array.isArray(series)) return null; const idx = series.length - 1 - offset; @@ -500,12 +630,14 @@ const pinescript = { return builtins.get('nz')(value, replacement); }, plot: function(series, title = '', color = null, linewidth = 1) { - globalThis.__pineRuntime.plots.push({ - series, - title, - color, - linewidth - }); + const rt = globalThis.__pineRuntime; + if (!rt) return series; + const bar = rt.__barIndex | 0; + const ord = (rt.__plotIdx = (rt.__plotIdx | 0) + 1) - 1; + const key = (title && String(title)) || ('plot_' + ord); + let p = rt.plots[key]; + if (!p) p = rt.plots[key] = { title: key, color, linewidth, data: [] }; + p.data[bar] = this.__scalar(series); return series; }, lineNew: function(x1, y1, x2, y2, opts = {}) { @@ -514,20 +646,72 @@ const pinescript = { lineDelete: function(l) { return null; }, - labelNew: function(...args) { - if (globalThis.__pineRuntime) { - globalThis.__pineRuntime.labels = globalThis.__pineRuntime.labels || []; - globalThis.__pineRuntime.labels.push({ args }); - } - return { args }; + lineSetXY: function(line, point, x, y) { + if (!line) return null; + if (point === 0 || point === 'x1') { line.x1 = x; line.y1 = y; } + else { line.x2 = x; line.y2 = y; } + return line; + }, + lineGetX: function(line, point) { + if (!line) return null; + return point === 0 || point === 'x1' ? line.x1 : line.x2; + }, + lineGetY: function(line, point) { + if (!line) return null; + return point === 0 || point === 'y1' ? line.y1 : line.y2; + }, + labelNew: function(x, y, text = '', opts = {}) { + return { x, y, text, opts, _type: 'label' }; }, labelDelete: function(l) { return null; }, + labelSetText: function(label, text) { + if (!label) return null; + label.text = text; + return label; + }, + labelGetText: function(label) { + if (!label) return ''; + return label.text || ''; + }, + boxNew: function(left, top, right, bottom, opts = {}) { + return { left, top, right, bottom, opts, _type: 'box' }; + }, + boxDelete: function(box) { + return null; + }, + boxSetLeftTop: function(box, left, top) { + if (!box) return null; + box.left = left; + box.top = top; + return box; + }, + boxSetRightBottom: function(box, right, bottom) { + if (!box) return null; + box.right = right; + box.bottom = bottom; + return box; + }, + polylineNew: function(points, opts = {}) { + return { points: points || [], opts, _type: 'polyline' }; + }, + polylineDelete: function(poly) { + return null; + }, plotshape: function(condition, ...rest) { - if (globalThis.__pineRuntime) { - globalThis.__pineRuntime.plotshapes.push({ condition, args: rest }); + const rt = globalThis.__pineRuntime; + if (!rt) return condition; + const bar = rt.__barIndex | 0; + const ord = (rt.__shapeIdx = (rt.__shapeIdx | 0) + 1) - 1; + let key = 'shape_' + ord; + for (const r of rest) { + if (typeof r === 'string') { key = r; break; } + if (r && typeof r === 'object' && r.title) { key = String(r.title); break; } } + let s = rt.plotshapes[key]; + if (!s) s = rt.plotshapes[key] = { title: key, data: [] }; + s.data[bar] = !!this.__scalar(condition); return condition; }, indicator: function(title, shorttitle, overlay, opts) { @@ -542,27 +726,12 @@ const pinescript = { } return null; }, - alertcondition: function(condition, ...rest) { - if (globalThis.__pineRuntime) { - globalThis.__pineRuntime.alerts.push({ condition, args: rest }); - } - return null; - }, plotbar: function(open, high, low, close, title, color, editable, showLast) { return { open, high, low, close }; }, plotcandle: function(open, high, low, close, title, color, wickColor, borderColor, editable, showLast) { return { open, high, low, close }; }, - hline: function(_price, _title, _opts) { - return null; - }, - bgcolor: function(color, title, editable, showLast) { - return null; - }, - fill: function(series1, series2, color, title, editable) { - return null; - }, alert: function(condition, message, frequency) { console.log('Alert:', condition ? message : 'Condition not met'); return condition; @@ -828,6 +997,9 @@ const pinescript = { arrayIndexOf: function(arr, value) { return arr ? arr.indexOf(value) : -1; }, + arrayLastIndexOf: function(arr, value) { + return arr ? arr.lastIndexOf(value) : -1; + }, arraySum: function(arr) { return arr ? arr.reduce((a, b) => a + b, 0) : 0; }, @@ -840,6 +1012,42 @@ const pinescript = { arrayMax: function(arr) { return arr && arr.length > 0 ? Math.max(...arr) : null; }, + arrayStdev: function(arr) { + if (!arr || arr.length === 0) return null; + const mean = arr.reduce((a, b) => a + b, 0) / arr.length; + const variance = arr.reduce((sum, val) => sum + Math.pow(val - mean, 2), 0) / arr.length; + return Math.sqrt(variance); + }, + arrayVariance: function(arr) { + if (!arr || arr.length === 0) return null; + const mean = arr.reduce((a, b) => a + b, 0) / arr.length; + return arr.reduce((sum, val) => sum + Math.pow(val - mean, 2), 0) / arr.length; + }, + arrayCovariance: function(arr1, arr2) { + if (!arr1 || !arr2 || arr1.length === 0 || arr1.length !== arr2.length) return null; + const mean1 = arr1.reduce((a, b) => a + b, 0) / arr1.length; + const mean2 = arr2.reduce((a, b) => a + b, 0) / arr2.length; + let cov = 0; + for (let i = 0; i < arr1.length; i++) { + cov += (arr1[i] - mean1) * (arr2[i] - mean2); + } + return cov / arr1.length; + }, + mode: function(arr) { + if (!arr || arr.length === 0) return null; + const counts = new Map(); + let maxCount = 0; + let modeVal = arr[0]; + for (const val of arr) { + const c = (counts.get(val) || 0) + 1; + counts.set(val, c); + if (c > maxCount) { + maxCount = c; + modeVal = val; + } + } + return modeVal; + }, strLength: function(str) { return str ? str.length : 0; }, @@ -895,6 +1103,84 @@ const pinescript = { strReverse: function(str) { return str ? str.split('').reverse().join('') : ''; }, + strFormat: function(formatStr, ...args) { + if (!formatStr) return ''; + return formatStr.replace(/\{(\d+)\}/g, (match, idx) => { + const i = parseInt(idx, 10); + return i < args.length ? String(args[i]) : match; + }); + }, + input: function(defval, title, tooltip, inline, group) { + return defval; + }, + inputInt: function(defval = 0, title, minval, maxval, step, tooltip, inline, group) { + return defval; + }, + inputFloat: function(defval = 0.0, title, minval, maxval, step, tooltip, inline, group) { + return defval; + }, + inputBool: function(defval = false, title, tooltip, inline, group) { + return defval; + }, + inputString: function(defval = '', title, options, tooltip, inline, group) { + return defval; + }, + inputSource: function(defval = null, title, tooltip, inline, group) { + return defval; + }, + inputColor: function(defval = '#000000', title, tooltip, inline, group) { + return defval; + }, + inputTime: function(defval = 0, title, tooltip, inline, group) { + return defval; + }, + mathSign: function(value) { + return Math.sign(value); + }, + mathAvg: function(...args) { + const valid = args.filter(v => v !== null && v !== undefined && !isNaN(v)); + return valid.length > 0 ? valid.reduce((a, b) => a + b, 0) / valid.length : null; + }, + mathSum: function(series, length) { + if (!series || series.length < length) return null; + return series.slice(-length).reduce((a, b) => a + b, 0); + }, + mathRandom: function(min = 0, max = 1) { + return min + Math.random() * (max - min); + }, + mathTodegrees: function(radians) { + return radians * (180 / Math.PI); + }, + mathToradians: function(degrees) { + return degrees * (Math.PI / 180); + }, + colorT: function(color, transparency) { + if (transparency !== undefined) { + return { color, transparency }; + } + if (color && typeof color === 'object' && 'transparency' in color) { + return color.transparency; + } + return 0; + }, + tableCellSetText: function(table, column, row, text) { + return null; + }, + tableCellSetBgcolor: function(table, column, row, bgcolor) { + return null; + }, + tableMergeCells: function(table, startColumn, startRow, endColumn, endRow) { + return null; + }, + tableDelete: function(table) { + return null; + }, + tableClear: function(table, startColumn, startRow, endColumn, endRow) { + return null; + }, + maxBarsBack: function(series, length) { + return null; + }, pi: 3.141592653589793, e: 2.718281828459045, true: true, @@ -905,8 +1191,7 @@ const pinescript = { const info = { ticker: 'AAPL', tickerid: 'NASDAQ:AAPL', prefix: 'NASDAQ', root: 'AAPL', suffix: '' }; return info[type] || ''; }, - time: 1771178051865, - timenow: 1771178051865, + timenow: 1781574527577, barstate: "LAST", dividends: {}, splits: {}, @@ -923,7 +1208,7 @@ const pinescript = { return null; }, tr: function(high, low, close) { - // Pine signature: ta.tr(true) + // Handles both the boolean shorthand ta.tr(true) and explicit high/low/close arguments if (typeof high === 'boolean') { const H = globalThis.high; const L = globalThis.low; @@ -934,7 +1219,7 @@ const pinescript = { return Math.max(H[i] - L[i], Math.abs(H[i] - prevClose), Math.abs(L[i] - prevClose)); } - // Fallback: treat inputs as scalars for current bar + // When arrays are passed, compute true range from the last bar using the previous close if (Array.isArray(close)) { const prevClose = close[close.length - 2] || close[0]; const currHigh = Array.isArray(high) ? high[high.length - 1] : high; @@ -945,6 +1230,92 @@ const pinescript = { return Math.max(high - low, Math.abs(high - close), Math.abs(low - close)); }, + arrayFirst: function(arr) { + if (!arr || arr.length === 0) return null; + return arr[0]; + }, + arrayLast: function(arr) { + if (!arr || arr.length === 0) return null; + return arr[arr.length - 1]; + }, + arrayJoin: function(arr, separator = ',') { + if (!arr) return ''; + return arr.join(separator); + }, + arrayConcat: function(arr1, arr2) { + if (!arr1) return arr2 || []; + if (!arr2) return arr1; + return arr1.concat(arr2); + }, + arrayCopy: function(arr) { + if (!arr) return []; + return [...arr]; + }, + arrayBinarySearch: function(arr, value) { + if (!arr || arr.length === 0) return -1; + let lo = 0, hi = arr.length - 1; + while (lo <= hi) { + const mid = (lo + hi) >>> 1; + if (arr[mid] === value) return mid; + if (arr[mid] < value) lo = mid + 1; + else hi = mid - 1; + } + return -1; + }, + arrayRange: function(arr) { + if (!arr || arr.length === 0) return 0; + return Math.max(...arr) - Math.min(...arr); + }, + arrayMedian: function(arr) { + if (!arr || arr.length === 0) return null; + const sorted = [...arr].sort((a, b) => a - b); + const mid = Math.floor(sorted.length / 2); + return sorted.length % 2 !== 0 ? sorted[mid] : (sorted[mid - 1] + sorted[mid]) / 2; + }, + arrayMode: function(arr) { + if (!arr || arr.length === 0) return null; + const freq = {}; + let maxCount = 0; + let mode = arr[0]; + for (const v of arr) { + freq[v] = (freq[v] || 0) + 1; + if (freq[v] > maxCount) { maxCount = freq[v]; mode = v; } + } + return mode; + }, + arrayPercentile: function(arr, percentile) { + if (!arr || arr.length === 0) return null; + const sorted = [...arr].sort((a, b) => a - b); + const idx = (percentile / 100) * (sorted.length - 1); + const lo = Math.floor(idx); + const hi = Math.ceil(idx); + if (lo === hi) return sorted[lo]; + return sorted[lo] + (sorted[hi] - sorted[lo]) * (idx - lo); + }, + arrayPercentileLinearInterpolation: function(arr, percentile) { + return builtins.get('arrayPercentile')(arr, percentile); + }, + arrayPercentileNearestRank: function(arr, percentile) { + if (!arr || arr.length === 0) return null; + const sorted = [...arr].sort((a, b) => a - b); + const idx = Math.ceil((percentile / 100) * sorted.length) - 1; + return sorted[Math.max(0, idx)]; + }, + arrayAbs: function(arr) { + if (!arr) return []; + return arr.map(v => Math.abs(v)); + }, + arrayEvery: function(arr, callback) { + if (!arr) return false; + return arr.every(callback); + }, + arraySome: function(arr, callback) { + if (!arr) return false; + return arr.some(callback); + }, + requestFinancial: function() { + return null; + }, }; @@ -1101,6 +1472,115 @@ globalThis.barmerge = globalThis.barmerge || { }; +function __pineNS(real) { + + const base = real || {}; + + return new Proxy(base, { + + get(target, key) { + + if (key in target) return target[key]; + + if (typeof key === "symbol") return target[key]; + + // Unknown member: usable as a value (null), a constant, or a no-op function. + + const noop = function() { return null; }; + + noop.toString = () => ""; + + return noop; + + } + + }); + +} + +globalThis.ta = globalThis.ta || __pineNS({}); + +globalThis.math = globalThis.math || __pineNS({ pi: Math.PI, e: Math.E, phi: 1.618033988749895, rphi: 0.618033988749895 }); + +globalThis.chart = globalThis.chart || __pineNS({ point: __pineNS({}), bg_color: null, fg_color: null }); + +globalThis.line = globalThis.line || __pineNS({ style_solid: "solid", style_dashed: "dashed", style_dotted: "dotted" }); + +globalThis.box = globalThis.box || __pineNS({}); + +globalThis.label = globalThis.label || __pineNS({ style_label_down: "label_down", style_label_up: "label_up", style_none: "none" }); + +globalThis.polyline = globalThis.polyline || __pineNS({}); + +globalThis.linefill = globalThis.linefill || __pineNS({}); + +globalThis.matrix = globalThis.matrix || __pineNS({}); + +globalThis.map = globalThis.map || __pineNS({}); + +globalThis.session = globalThis.session || __pineNS({ regular: "regular", extended: "extended" }); + +globalThis.dayofweek = globalThis.dayofweek || __pineNS({ sunday: 1, monday: 2, tuesday: 3, wednesday: 4, thursday: 5, friday: 6, saturday: 7 }); + +globalThis.timeframe = __pineNS(Object.assign(globalThis.timeframe || {}, { period: (globalThis.timeframe && globalThis.timeframe.period) || "D", isintraday: false, isdaily: true, multiplier: 1 })); + +globalThis.request = __pineNS(globalThis.request || {}); + +globalThis.input = __pineNS(globalThis.input || {}); + +globalThis.adjustment = globalThis.adjustment || __pineNS({}); + +globalThis.earnings = globalThis.earnings || __pineNS({}); + +globalThis.dividends = globalThis.dividends || __pineNS({}); + +globalThis.splits = globalThis.splits || __pineNS({}); + +globalThis.currency = globalThis.currency || __pineNS({}); + +globalThis.display = globalThis.display || __pineNS({ none: "none", all: "all", pane: "pane", data_window: "data_window", status_line: "status_line", price_scale: "price_scale" }); + +globalThis.format = globalThis.format || __pineNS({ price: "price", volume: "volume", percent: "percent", mintick: "mintick", inherit: "inherit" }); + +globalThis.scale = globalThis.scale || __pineNS({ left: "left", right: "right", none: "none" }); + +globalThis.font = globalThis.font || __pineNS({ family_default: "default", family_monospace: "monospace" }); + +globalThis.xloc = globalThis.xloc || __pineNS({ bar_index: "bar_index", bar_time: "bar_time" }); + +globalThis.yloc = globalThis.yloc || __pineNS({ price: "price", abovebar: "abovebar", belowbar: "belowbar" }); + +globalThis.extend = globalThis.extend || __pineNS({ none: "none", left: "left", right: "right", both: "both" }); + +globalThis.order = globalThis.order || __pineNS({ ascending: "ascending", descending: "descending" }); + +globalThis.alert = globalThis.alert || Object.assign(function() { return null; }, { freq_once_per_bar: "once_per_bar", freq_once_per_bar_close: "once_per_bar_close", freq_all: "all" }); + +globalThis.dayofmonth = globalThis.dayofmonth || function(t) { return new Date(t || 0).getUTCDate(); }; + +globalThis.plot = globalThis.plot || __pineNS({ style_line: "line", style_linebr: "linebr", style_stepline: "stepline", style_histogram: "histogram", style_cross: "cross", style_area: "area", style_areabr: "areabr", style_columns: "columns", style_circles: "circles" }); + +globalThis.int = globalThis.int || function(x) { return x == null ? null : Math.trunc(Number(x)); }; + +globalThis.float = globalThis.float || function(x) { return x == null ? null : Number(x); }; + +globalThis.bool = globalThis.bool || function(x) { return Boolean(x); }; + +globalThis.string = globalThis.string || function(x) { return x == null ? null : String(x); }; + +globalThis.dayofweek = globalThis.dayofweek || __pineNS({ sunday: 1, monday: 2, tuesday: 3, wednesday: 4, thursday: 5, friday: 6, saturday: 7 }); + +globalThis.str = globalThis.str || __pineNS({}); + +pinescript.math = globalThis.math; + +pinescript.ta = globalThis.ta; + +pinescript.str = globalThis.str; + +pinescript.array = __pineNS(globalThis.array); + + pinescript.color = { hex: function(s) { @@ -1190,10 +1670,10 @@ pinescript.table = { }; -// Input parameters +// Script input parameters and their defaults -// Main script logic +// The transpiled script logic, called once per bar function main() { @@ -1216,26 +1696,26 @@ function main() { let color_purple_composite = pinescript.color.hex("#ab47bc"); let color_gray_neutral = pinescript.color.hex("#78909c"); let color_dark_bg = pinescript.color.hex("#1e1e1e"); - let display_mode = input.string("Smoothed All Lines", "Display Mode", ({ options: ["Smoothed All Lines", "Single Dominant Line", "Both"], group: "Display" })); - let smooth_period = input.int(3, "Smoothing Period", ({ minval: 1, maxval: 20, group: "Display" })); - let show_zones = input.bool(true, "Show Background Phase Zones", ({ group: "Display" })); - let show_phase_labels = input.bool(true, "Show Phase Change Labels", ({ group: "Display" })); - let show_historical_context = input.bool(true, "Show Historical Context", ({ group: "Display" })); - let dashboard_position = input.string("Top Right", "Dashboard Position", ({ options: ["Top Left", "Top Center", "Top Right", "Middle Left", "Middle Right", "Bottom Left", "Bottom Right"], group: "Dashboard" })); - let dashboard_size = input.string("Normal", "Dashboard Size", ({ options: ["Small", "Normal", "Large", "Extra Large"], group: "Dashboard" })); - let show_components = input.bool(true, "Show Component Health Panel", ({ group: "Dashboard" })); - let show_action_hints = input.bool(true, "Show Action Guidance", ({ group: "Dashboard" })); - let mania_threshold = input.int(75, "Mania Alert Threshold", ({ minval: 0, maxval: 100, group: "Alert Thresholds" })); - let panic_threshold = input.int(60, "Panic Alert Threshold", ({ minval: 0, maxval: 100, group: "Alert Thresholds" })); - let crash_threshold = input.int(70, "Crash Alert Threshold", ({ minval: 0, maxval: 100, group: "Alert Thresholds" })); - let ma_period = input.int(200, "Long-term MA Period", ({ minval: 50, maxval: 300, group: "Technical Parameters" })); - let rsi_period = input.int(14, "RSI Period", ({ minval: 5, maxval: 30, group: "Technical Parameters" })); - let vol_period = input.int(20, "Volume Average Period", ({ minval: 10, maxval: 50, group: "Technical Parameters" })); - let show_credit = input.bool(true, "Include Credit Risk Indicators", ({ group: "Components" })); - let show_breadth = input.bool(true, "Include Market Breadth", ({ group: "Components" })); - let show_vix = input.bool(true, "Include VIX Analysis", ({ group: "Components" })); + let display_mode = pinescript.inputString("Smoothed All Lines", "Display Mode", ({ options: ["Smoothed All Lines", "Single Dominant Line", "Both"], group: "Display" })); + let smooth_period = pinescript.inputInt(3, "Smoothing Period", ({ minval: 1, maxval: 20, group: "Display" })); + let show_zones = pinescript.inputBool(true, "Show Background Phase Zones", ({ group: "Display" })); + let show_phase_labels = pinescript.inputBool(true, "Show Phase Change Labels", ({ group: "Display" })); + let show_historical_context = pinescript.inputBool(true, "Show Historical Context", ({ group: "Display" })); + let dashboard_position = pinescript.inputString("Top Right", "Dashboard Position", ({ options: ["Top Left", "Top Center", "Top Right", "Middle Left", "Middle Right", "Bottom Left", "Bottom Right"], group: "Dashboard" })); + let dashboard_size = pinescript.inputString("Normal", "Dashboard Size", ({ options: ["Small", "Normal", "Large", "Extra Large"], group: "Dashboard" })); + let show_components = pinescript.inputBool(true, "Show Component Health Panel", ({ group: "Dashboard" })); + let show_action_hints = pinescript.inputBool(true, "Show Action Guidance", ({ group: "Dashboard" })); + let mania_threshold = pinescript.inputInt(75, "Mania Alert Threshold", ({ minval: 0, maxval: 100, group: "Alert Thresholds" })); + let panic_threshold = pinescript.inputInt(60, "Panic Alert Threshold", ({ minval: 0, maxval: 100, group: "Alert Thresholds" })); + let crash_threshold = pinescript.inputInt(70, "Crash Alert Threshold", ({ minval: 0, maxval: 100, group: "Alert Thresholds" })); + let ma_period = pinescript.inputInt(200, "Long-term MA Period", ({ minval: 50, maxval: 300, group: "Technical Parameters" })); + let rsi_period = pinescript.inputInt(14, "RSI Period", ({ minval: 5, maxval: 30, group: "Technical Parameters" })); + let vol_period = pinescript.inputInt(20, "Volume Average Period", ({ minval: 10, maxval: 50, group: "Technical Parameters" })); + let show_credit = pinescript.inputBool(true, "Include Credit Risk Indicators", ({ group: "Components" })); + let show_breadth = pinescript.inputBool(true, "Include Market Breadth", ({ group: "Components" })); + let show_vix = pinescript.inputBool(true, "Include VIX Analysis", ({ group: "Components" })); let vix = (show_vix ? pinescript.requestSecurity("TVC:VIX", timeframe.period, close) : null); - let vix_5 = (show_vix ? pinescript.requestSecurity("TVC:VIX", timeframe.period, pinescript.offset(close, 5)) : null); + let vix_5 = (show_vix ? pinescript.requestSecurity("TVC:VIX", timeframe.period, pinescript.hist(0, close, 5)) : null); let hyg = (show_credit ? pinescript.requestSecurity("AMEX:HYG", timeframe.period, close) : null); let lqd = (show_credit ? pinescript.requestSecurity("AMEX:LQD", timeframe.period, close) : null); let tlt = (show_credit ? pinescript.requestSecurity("NASDAQ:TLT", timeframe.period, close) : null); @@ -1256,22 +1736,22 @@ function main() { let credit_stress = pinescript.max(0, pinescript.min(100, (credit_stress_raw * 5))); let ma_long = pinescript.sma(close, ma_period); let price_deviation = (((close - ma_long) / ma_long) * 100); - let rsi = ta.rsi(close, rsi_period); - let rsi_overbought_days = ta.barssince((rsi < 70)); + let rsi = pinescript.rsi(close, rsi_period); + let rsi_overbought_days = pinescript.barssince(pinescript.series(1, (rsi < 70))); let rsi_oversold = (rsi < 30); - let roc = ta.roc(close, 20); + let roc = pinescript.roc(close, 20); let roc_ma = pinescript.sma(roc, 20); let roc_accelerating = (roc > roc_ma); let vol_avg = pinescript.sma(volume, vol_period); let vol_spike = (volume / vol_avg); let high_vol_down = ((vol_spike > 1.5) && (close < open)); - let [macd_line, signal_line, ] = ta.macd(close, 12, 26, 9); - let macd_bearish = pinescript.cross(macd_line, signal_line); + let [macd_line, signal_line, ] = pinescript.unpack(pinescript.macd(close, 12, 26, 9), 3); + let macd_bearish = pinescript.crossunder(pinescript.series(2, macd_line), pinescript.series(3, signal_line)); let vix_spike = (((show_vix && !pinescript.na(vix)) && !pinescript.na(vix_5)) ? ((vix > (vix_5 * 1.3)) || (vix > 25)) : false); let vix_extreme = ((show_vix && !pinescript.na(vix)) ? (vix > 40) : false); let vix_low = ((show_vix && !pinescript.na(vix)) ? (vix < 20) : false); - let peak_20 = pinescript.highest(close, 20); - let peak_60 = pinescript.highest(close, 60); + let peak_20 = pinescript.highest(pinescript.series(4, close), 20); + let peak_60 = pinescript.highest(pinescript.series(5, close), 60); let decline_20 = (((close - peak_20) / peak_20) * 100); let decline_60 = (((close - peak_60) / peak_60) * 100); let price_above_50ma = (close > pinescript.sma(close, 50)); @@ -1508,18 +1988,18 @@ function main() { pinescript.table.cell(state.dashboard, 1, row, momentum_status, ({ text_color: pinescript.color.white, text_size: state.text_size_normal, bgcolor: pinescript.color.new(color_dark_bg, 65) })); } } - let mania_alert = ((state.phase === "MANIA") && (pinescript.offset(state.phase, 1) !== "MANIA")); - let panic_alert = ((state.phase === "PANIC") && (pinescript.offset(state.phase, 1) !== "PANIC")); - let crash_alert = ((state.phase === "CRASH") && (pinescript.offset(state.phase, 1) !== "CRASH")); - let breadth_collapse_alert = ((breadth_proxy < 30) && (pinescript.offset(breadth_proxy, 1) >= 30)); - let vix_spike_alert = ((((show_vix && !pinescript.na(vix)) && !pinescript.na(vix_5)) && (vix > (vix_5 * 1.3))) && (pinescript.offset(vix, 1) <= (pinescript.offset(vix_5, 1) * 1.3))); - let credit_freeze_alert = ((credit_stress > 70) && (pinescript.offset(credit_stress, 1) <= 70)); - alertcondition(mania_alert, ({ title: "🟡 Mania Phase Detected", message: "🟡 MANIA PHASE: Elevated crash risk detected. Consider reducing exposure and taking profits." })); - alertcondition(panic_alert, ({ title: "🟠 Panic Phase Detected", message: "🟠 PANIC PHASE: Severe market stress detected. Consider raising cash and avoiding buying dips." })); - alertcondition(crash_alert, ({ title: "🔴 CRASH DETECTED", message: "🔴 CRASH PHASE: Capitulation event detected. Extreme risk - prepare to buy quality after capitulation." })); - alertcondition(breadth_collapse_alert, ({ title: "⚠️ Breadth Collapse", message: "⚠️ Market breadth collapsed below 30% - participation deteriorating rapidly." })); - alertcondition(vix_spike_alert, ({ title: "⚠️ VIX Spike", message: "⚠️ VIX spiked >30% in 5 days - fear increasing rapidly." })); - alertcondition(credit_freeze_alert, ({ title: "⚠️ Credit Freeze", message: "⚠️ Credit stress extreme - potential liquidity crisis developing." })); + let mania_alert = ((state.phase === "MANIA") && (pinescript.hist(6, state.phase, 1) !== "MANIA")); + let panic_alert = ((state.phase === "PANIC") && (pinescript.hist(7, state.phase, 1) !== "PANIC")); + let crash_alert = ((state.phase === "CRASH") && (pinescript.hist(8, state.phase, 1) !== "CRASH")); + let breadth_collapse_alert = ((breadth_proxy < 30) && (pinescript.hist(9, breadth_proxy, 1) >= 30)); + let vix_spike_alert = ((((show_vix && !pinescript.na(vix)) && !pinescript.na(vix_5)) && (vix > (vix_5 * 1.3))) && (pinescript.hist(10, vix, 1) <= (pinescript.hist(11, vix_5, 1) * 1.3))); + let credit_freeze_alert = ((credit_stress > 70) && (pinescript.hist(12, credit_stress, 1) <= 70)); + pinescript.alertcondition(mania_alert, ({ title: "🟡 Mania Phase Detected", message: "🟡 MANIA PHASE: Elevated crash risk detected. Consider reducing exposure and taking profits." })); + pinescript.alertcondition(panic_alert, ({ title: "🟠 Panic Phase Detected", message: "🟠 PANIC PHASE: Severe market stress detected. Consider raising cash and avoiding buying dips." })); + pinescript.alertcondition(crash_alert, ({ title: "🔴 CRASH DETECTED", message: "🔴 CRASH PHASE: Capitulation event detected. Extreme risk - prepare to buy quality after capitulation." })); + pinescript.alertcondition(breadth_collapse_alert, ({ title: "⚠️ Breadth Collapse", message: "⚠️ Market breadth collapsed below 30% - participation deteriorating rapidly." })); + pinescript.alertcondition(vix_spike_alert, ({ title: "⚠️ VIX Spike", message: "⚠️ VIX spiked >30% in 5 days - fear increasing rapidly." })); + pinescript.alertcondition(credit_freeze_alert, ({ title: "⚠️ Credit Freeze", message: "⚠️ Credit stress extreme - potential liquidity crisis developing." })); plotchar(composite_risk, ({ title: "Composite Risk", char: "", location: pinescript.location.top, display: display.data_window })); plotchar(mania_score, ({ title: "Mania Score", char: "", location: pinescript.location.top, display: display.data_window })); plotchar(panic_score, ({ title: "Panic Score", char: "", location: pinescript.location.top, display: display.data_window })); @@ -1530,7 +2010,67 @@ function main() { } -// Export for use -export { main +// The bar-by-bar engine: runs main() once per bar over the dataset. +function run(data, options = {}) { + data = data || {}; + const inClose = (data.close || []).map(Number); + const n = inClose.length; + const inOpen = (data.open || inClose).map(Number); + const inHigh = (data.high || inClose).map(Number); + const inLow = (data.low || inClose).map(Number); + const inVol = (data.volume || new Array(n).fill(0)).map(Number); + const inTime = (data.time || inClose.map((_, i) => i)).map(Number); + + // Reset all persistent and per-run state so repeated runs are independent. + globalThis.__pineState = {}; + globalThis.__pineRuntime = { plots: {}, plotshapes: {}, alerts: [], bars: n, inputs: options.inputs || {} }; + pinescript.__rt = {}; + pinescript.__bar = 0; + + // Growing OHLCV windows: each is a SeriesArray that gains one element per bar, + // so window built-ins like sma and highest see history up to the current bar. + const O = pinescript.asSeries([]), H = pinescript.asSeries([]), L = pinescript.asSeries([]); + const C = pinescript.asSeries([]), V = pinescript.asSeries([]), T = pinescript.asSeries([]); + const HL2 = pinescript.asSeries([]), HLC3 = pinescript.asSeries([]), OHLC4 = pinescript.asSeries([]); + globalThis.open = O; globalThis.high = H; globalThis.low = L; globalThis.close = C; + globalThis.volume = V; globalThis.time = T; + globalThis.hl2 = HL2; globalThis.hlc3 = HLC3; globalThis.ohlc4 = OHLC4; + globalThis.syminfo = globalThis.syminfo || { tickerid: 'SYNTHETIC', ticker: 'SYNTHETIC', mintick: 0.01 }; + + const rt = globalThis.__pineRuntime; + for (let i = 0; i < n; i++) { + O.push(inOpen[i]); H.push(inHigh[i]); L.push(inLow[i]); C.push(inClose[i]); V.push(inVol[i]); T.push(inTime[i]); + HL2.push((inHigh[i] + inLow[i]) / 2); + HLC3.push((inHigh[i] + inLow[i] + inClose[i]) / 3); + OHLC4.push((inOpen[i] + inHigh[i] + inLow[i] + inClose[i]) / 4); + + pinescript.__bar = i; + rt.__barIndex = i; + rt.__plotIdx = 0; + rt.__shapeIdx = 0; + globalThis.bar_index = i; + globalThis.last_bar_index = n - 1; + globalThis.barstate = { + isfirst: i === 0, islast: i === n - 1, isrealtime: false, ishistory: true, + isconfirmed: true, isnew: true, islastconfirmedhistory: i === n - 1, + }; + + main(); + } + + // Backfill skipped bars so every output series has exactly n entries. + for (const k of Object.keys(rt.plots)) { + const d = rt.plots[k].data; + for (let i = 0; i < n; i++) if (d[i] === undefined) d[i] = null; + } + for (const k of Object.keys(rt.plotshapes)) { + const d = rt.plotshapes[k].data; + for (let i = 0; i < n; i++) if (d[i] === undefined) d[i] = false; + } + return rt; +} + + +export { main, run }; diff --git a/converted/NY_5m_15m_Orb_-_Statistics_LTF_Candle_structure.js b/converted/NY_5m_15m_Orb_-_Statistics_LTF_Candle_structure.js index 69a5f25..45c5041 100644 --- a/converted/NY_5m_15m_Orb_-_Statistics_LTF_Candle_structure.js +++ b/converted/NY_5m_15m_Orb_-_Statistics_LTF_Candle_structure.js @@ -1,12 +1,63 @@ -// PineScript to JavaScript Transpiled Code +// Auto-generated JavaScript from PineScript source -// Generated automatically +// Do not edit by hand -- re-run the converter instead -// Built-in PineScript functions +// All the built-in PineScript functions that scripts depend on at runtime const pinescript = { + __scalar: function(v) { + if (v === null || v === undefined) return v; + if (typeof v === 'object' && typeof v.valueOf === 'function') { + const s = v.valueOf(); + if (typeof s === 'number' || typeof s === 'boolean' || s === null) return s; + } + return v; + }, + series: function(id, value) { + const rt = this.__rt || (this.__rt = {}); + const store = rt.series || (rt.series = {}); + let buf = store[id]; + if (!buf) buf = store[id] = []; + buf[this.__bar | 0] = this.__scalar(value); + return buf; + }, + hist: function(id, value, n) { + const buf = this.series(id, value); + const idx = (this.__bar | 0) - (n | 0); + return idx >= 0 && idx < buf.length ? buf[idx] : null; + }, + time: function(timeframe, session) { + const t = globalThis.time; + if (t && typeof t.valueOf === 'function') return t.valueOf(); + return Array.isArray(t) ? t[t.length - 1] : t; + }, + sign: function(x) { + if (x === null || x === undefined || (typeof x === 'number' && isNaN(x))) return null; + return Math.sign(Number(x)); + }, + unpack: function(value, count) { + if (Array.isArray(value)) return value; + if (value != null && typeof value[Symbol.iterator] === 'function') return Array.from(value); + return new Array(count || 0).fill(null); + }, + alertcondition: function(condition, ...rest) { + if (globalThis.__pineRuntime) { + globalThis.__pineRuntime.alerts.push({ condition, args: rest }); + } + return null; + }, + barcolor: function(color) { return null; }, + bgcolor: function(color, title, editable, showLast) { + return null; + }, + fill: function(series1, series2, color, title, editable) { + return null; + }, + hline: function(_price, _title, _opts) { + return null; + }, sma: function(series, length) { if (series === null || series === undefined) return null; if (!Array.isArray(series)) series = [series]; @@ -84,6 +135,13 @@ const pinescript = { } return weightSum > 0 ? sum / weightSum : null; }, + swma: function(series) { + if (series === null || series === undefined) return null; + if (!Array.isArray(series)) series = [series]; + if (series.length < 4) return null; + const s = series.slice(-4); + return (s[0] * 1 + s[1] * 2 + s[2] * 2 + s[3] * 1) / 6; + }, atr: function(high, low, close, length = 14) { if (!Array.isArray(high) || !Array.isArray(low) || !Array.isArray(close)) return null; if (high.length < length + 1 || low.length < length + 1 || close.length < length + 1) return null; @@ -114,6 +172,53 @@ const pinescript = { const atr = builtins.get('atr')(high, low, close, length); return { middle: sma, upper: sma + mult * atr, lower: sma - mult * atr }; }, + supertrend: function(factor, atrPeriod, high, low, close) { + if (!Array.isArray(high) || !Array.isArray(low) || !Array.isArray(close)) return [null, null]; + if (high.length < atrPeriod + 1) return [null, null]; + const atrVal = builtins.get('atr')(high, low, close, atrPeriod); + if (atrVal === null) return [null, null]; + const lastIdx = close.length - 1; + const hl2 = (high[lastIdx] + low[lastIdx]) / 2; + const upperBand = hl2 + factor * atrVal; + const lowerBand = hl2 - factor * atrVal; + // Determine direction: 1 means downtrend (price below band), -1 means uptrend + const direction = close[lastIdx] > upperBand ? -1 : close[lastIdx] < lowerBand ? 1 : -1; + const supertrendValue = direction === -1 ? lowerBand : upperBand; + return [supertrendValue, direction]; + }, + ta_supertrend: function(factor, atrPeriod) { + const H = globalThis.high; + const L = globalThis.low; + const C = globalThis.close; + return builtins.get('supertrend')(factor, atrPeriod, H, L, C); + }, + dmi: function(diLength, adxSmoothing, high, low, close) { + if (!Array.isArray(high) || !Array.isArray(low) || !Array.isArray(close)) return { plusDI: null, minusDI: null, adx: null }; + if (high.length < diLength + 1) return { plusDI: null, minusDI: null, adx: null }; + let plusDMSum = 0, minusDMSum = 0, trSum = 0; + for (let i = 1; i <= diLength; i++) { + const idx = high.length - 1 - diLength + i; + const upMove = high[idx] - high[idx - 1]; + const downMove = low[idx - 1] - low[idx]; + plusDMSum += (upMove > downMove && upMove > 0) ? upMove : 0; + minusDMSum += (downMove > upMove && downMove > 0) ? downMove : 0; + const tr = Math.max( + high[idx] - low[idx], + Math.abs(high[idx] - close[idx - 1]), + Math.abs(low[idx] - close[idx - 1]) + ); + trSum += tr; + } + const plusDI = trSum > 0 ? (plusDMSum / trSum) * 100 : 0; + const minusDI = trSum > 0 ? (minusDMSum / trSum) * 100 : 0; + const diSum = plusDI + minusDI; + const dx = diSum > 0 ? Math.abs(plusDI - minusDI) / diSum * 100 : 0; + return { plusDI, minusDI, adx: dx }; + }, + adx: function(diLength, adxSmoothing, high, low, close) { + const result = builtins.get('dmi')(diLength, adxSmoothing, high, low, close); + return result ? result.adx : null; + }, rsi: function(close, length = 14) { if (close === null || close === undefined) return null; if (!Array.isArray(close)) close = [close]; @@ -171,9 +276,7 @@ const pinescript = { return { macd: macdLine, signal: signalLine, histogram: histogram }; }, cci: function(a, b, c, d) { - // Pine signatures: - // - ta.cci(source, length) - // - ta.cci(high, low, close, length) + // Supports both ta.cci(source, length) and ta.cci(high, low, close, length) signatures let source; let length; if (Array.isArray(a) && typeof b === 'number' && c === undefined) { @@ -304,6 +407,15 @@ const pinescript = { stdev: function(source, length) { return Math.sqrt(builtins.get('variance')(source, length)); }, + median: function(source, length) { + if (!source || source.length < length) return null; + const sorted = [...source.slice(-length)].sort((a, b) => a - b); + const mid = Math.floor(sorted.length / 2); + return sorted.length % 2 !== 0 ? sorted[mid] : (sorted[mid - 1] + sorted[mid]) / 2; + }, + ta_median: function(source, length) { + return builtins.get('median')(source, length); + }, hl2: function(high, low) { return high && low ? (high[high.length - 1] + low[low.length - 1]) / 2 : null; }, @@ -420,6 +532,24 @@ const pinescript = { return (series1[i - 1] < series2[i - 1] && series1[i] >= series2[i]) || (series1[i - 1] > series2[i - 1] && series1[i] <= series2[i]); }, + crossover: function(series1, series2) { + if (series1 === null || series1 === undefined) return false; + if (series2 === null || series2 === undefined) return false; + if (!Array.isArray(series1)) series1 = [series1]; + if (!Array.isArray(series2)) series2 = [series2]; + if (series1.length < 2 || series2.length < 2) return false; + const i = series1.length - 1; + return series1[i - 1] < series2[i - 1] && series1[i] >= series2[i]; + }, + crossunder: function(series1, series2) { + if (series1 === null || series1 === undefined) return false; + if (series2 === null || series2 === undefined) return false; + if (!Array.isArray(series1)) series1 = [series1]; + if (!Array.isArray(series2)) series2 = [series2]; + if (series1.length < 2 || series2.length < 2) return false; + const i = series1.length - 1; + return series1[i - 1] > series2[i - 1] && series1[i] <= series2[i]; + }, offset: function(series, offset) { if (!Array.isArray(series)) return null; const idx = series.length - 1 - offset; @@ -500,12 +630,14 @@ const pinescript = { return builtins.get('nz')(value, replacement); }, plot: function(series, title = '', color = null, linewidth = 1) { - globalThis.__pineRuntime.plots.push({ - series, - title, - color, - linewidth - }); + const rt = globalThis.__pineRuntime; + if (!rt) return series; + const bar = rt.__barIndex | 0; + const ord = (rt.__plotIdx = (rt.__plotIdx | 0) + 1) - 1; + const key = (title && String(title)) || ('plot_' + ord); + let p = rt.plots[key]; + if (!p) p = rt.plots[key] = { title: key, color, linewidth, data: [] }; + p.data[bar] = this.__scalar(series); return series; }, lineNew: function(x1, y1, x2, y2, opts = {}) { @@ -514,20 +646,72 @@ const pinescript = { lineDelete: function(l) { return null; }, - labelNew: function(...args) { - if (globalThis.__pineRuntime) { - globalThis.__pineRuntime.labels = globalThis.__pineRuntime.labels || []; - globalThis.__pineRuntime.labels.push({ args }); - } - return { args }; + lineSetXY: function(line, point, x, y) { + if (!line) return null; + if (point === 0 || point === 'x1') { line.x1 = x; line.y1 = y; } + else { line.x2 = x; line.y2 = y; } + return line; + }, + lineGetX: function(line, point) { + if (!line) return null; + return point === 0 || point === 'x1' ? line.x1 : line.x2; + }, + lineGetY: function(line, point) { + if (!line) return null; + return point === 0 || point === 'y1' ? line.y1 : line.y2; + }, + labelNew: function(x, y, text = '', opts = {}) { + return { x, y, text, opts, _type: 'label' }; }, labelDelete: function(l) { return null; }, + labelSetText: function(label, text) { + if (!label) return null; + label.text = text; + return label; + }, + labelGetText: function(label) { + if (!label) return ''; + return label.text || ''; + }, + boxNew: function(left, top, right, bottom, opts = {}) { + return { left, top, right, bottom, opts, _type: 'box' }; + }, + boxDelete: function(box) { + return null; + }, + boxSetLeftTop: function(box, left, top) { + if (!box) return null; + box.left = left; + box.top = top; + return box; + }, + boxSetRightBottom: function(box, right, bottom) { + if (!box) return null; + box.right = right; + box.bottom = bottom; + return box; + }, + polylineNew: function(points, opts = {}) { + return { points: points || [], opts, _type: 'polyline' }; + }, + polylineDelete: function(poly) { + return null; + }, plotshape: function(condition, ...rest) { - if (globalThis.__pineRuntime) { - globalThis.__pineRuntime.plotshapes.push({ condition, args: rest }); + const rt = globalThis.__pineRuntime; + if (!rt) return condition; + const bar = rt.__barIndex | 0; + const ord = (rt.__shapeIdx = (rt.__shapeIdx | 0) + 1) - 1; + let key = 'shape_' + ord; + for (const r of rest) { + if (typeof r === 'string') { key = r; break; } + if (r && typeof r === 'object' && r.title) { key = String(r.title); break; } } + let s = rt.plotshapes[key]; + if (!s) s = rt.plotshapes[key] = { title: key, data: [] }; + s.data[bar] = !!this.__scalar(condition); return condition; }, indicator: function(title, shorttitle, overlay, opts) { @@ -542,27 +726,12 @@ const pinescript = { } return null; }, - alertcondition: function(condition, ...rest) { - if (globalThis.__pineRuntime) { - globalThis.__pineRuntime.alerts.push({ condition, args: rest }); - } - return null; - }, plotbar: function(open, high, low, close, title, color, editable, showLast) { return { open, high, low, close }; }, plotcandle: function(open, high, low, close, title, color, wickColor, borderColor, editable, showLast) { return { open, high, low, close }; }, - hline: function(_price, _title, _opts) { - return null; - }, - bgcolor: function(color, title, editable, showLast) { - return null; - }, - fill: function(series1, series2, color, title, editable) { - return null; - }, alert: function(condition, message, frequency) { console.log('Alert:', condition ? message : 'Condition not met'); return condition; @@ -828,6 +997,9 @@ const pinescript = { arrayIndexOf: function(arr, value) { return arr ? arr.indexOf(value) : -1; }, + arrayLastIndexOf: function(arr, value) { + return arr ? arr.lastIndexOf(value) : -1; + }, arraySum: function(arr) { return arr ? arr.reduce((a, b) => a + b, 0) : 0; }, @@ -840,6 +1012,42 @@ const pinescript = { arrayMax: function(arr) { return arr && arr.length > 0 ? Math.max(...arr) : null; }, + arrayStdev: function(arr) { + if (!arr || arr.length === 0) return null; + const mean = arr.reduce((a, b) => a + b, 0) / arr.length; + const variance = arr.reduce((sum, val) => sum + Math.pow(val - mean, 2), 0) / arr.length; + return Math.sqrt(variance); + }, + arrayVariance: function(arr) { + if (!arr || arr.length === 0) return null; + const mean = arr.reduce((a, b) => a + b, 0) / arr.length; + return arr.reduce((sum, val) => sum + Math.pow(val - mean, 2), 0) / arr.length; + }, + arrayCovariance: function(arr1, arr2) { + if (!arr1 || !arr2 || arr1.length === 0 || arr1.length !== arr2.length) return null; + const mean1 = arr1.reduce((a, b) => a + b, 0) / arr1.length; + const mean2 = arr2.reduce((a, b) => a + b, 0) / arr2.length; + let cov = 0; + for (let i = 0; i < arr1.length; i++) { + cov += (arr1[i] - mean1) * (arr2[i] - mean2); + } + return cov / arr1.length; + }, + mode: function(arr) { + if (!arr || arr.length === 0) return null; + const counts = new Map(); + let maxCount = 0; + let modeVal = arr[0]; + for (const val of arr) { + const c = (counts.get(val) || 0) + 1; + counts.set(val, c); + if (c > maxCount) { + maxCount = c; + modeVal = val; + } + } + return modeVal; + }, strLength: function(str) { return str ? str.length : 0; }, @@ -895,6 +1103,84 @@ const pinescript = { strReverse: function(str) { return str ? str.split('').reverse().join('') : ''; }, + strFormat: function(formatStr, ...args) { + if (!formatStr) return ''; + return formatStr.replace(/\{(\d+)\}/g, (match, idx) => { + const i = parseInt(idx, 10); + return i < args.length ? String(args[i]) : match; + }); + }, + input: function(defval, title, tooltip, inline, group) { + return defval; + }, + inputInt: function(defval = 0, title, minval, maxval, step, tooltip, inline, group) { + return defval; + }, + inputFloat: function(defval = 0.0, title, minval, maxval, step, tooltip, inline, group) { + return defval; + }, + inputBool: function(defval = false, title, tooltip, inline, group) { + return defval; + }, + inputString: function(defval = '', title, options, tooltip, inline, group) { + return defval; + }, + inputSource: function(defval = null, title, tooltip, inline, group) { + return defval; + }, + inputColor: function(defval = '#000000', title, tooltip, inline, group) { + return defval; + }, + inputTime: function(defval = 0, title, tooltip, inline, group) { + return defval; + }, + mathSign: function(value) { + return Math.sign(value); + }, + mathAvg: function(...args) { + const valid = args.filter(v => v !== null && v !== undefined && !isNaN(v)); + return valid.length > 0 ? valid.reduce((a, b) => a + b, 0) / valid.length : null; + }, + mathSum: function(series, length) { + if (!series || series.length < length) return null; + return series.slice(-length).reduce((a, b) => a + b, 0); + }, + mathRandom: function(min = 0, max = 1) { + return min + Math.random() * (max - min); + }, + mathTodegrees: function(radians) { + return radians * (180 / Math.PI); + }, + mathToradians: function(degrees) { + return degrees * (Math.PI / 180); + }, + colorT: function(color, transparency) { + if (transparency !== undefined) { + return { color, transparency }; + } + if (color && typeof color === 'object' && 'transparency' in color) { + return color.transparency; + } + return 0; + }, + tableCellSetText: function(table, column, row, text) { + return null; + }, + tableCellSetBgcolor: function(table, column, row, bgcolor) { + return null; + }, + tableMergeCells: function(table, startColumn, startRow, endColumn, endRow) { + return null; + }, + tableDelete: function(table) { + return null; + }, + tableClear: function(table, startColumn, startRow, endColumn, endRow) { + return null; + }, + maxBarsBack: function(series, length) { + return null; + }, pi: 3.141592653589793, e: 2.718281828459045, true: true, @@ -905,8 +1191,7 @@ const pinescript = { const info = { ticker: 'AAPL', tickerid: 'NASDAQ:AAPL', prefix: 'NASDAQ', root: 'AAPL', suffix: '' }; return info[type] || ''; }, - time: 1771178052040, - timenow: 1771178052040, + timenow: 1781574527774, barstate: "LAST", dividends: {}, splits: {}, @@ -923,7 +1208,7 @@ const pinescript = { return null; }, tr: function(high, low, close) { - // Pine signature: ta.tr(true) + // Handles both the boolean shorthand ta.tr(true) and explicit high/low/close arguments if (typeof high === 'boolean') { const H = globalThis.high; const L = globalThis.low; @@ -934,7 +1219,7 @@ const pinescript = { return Math.max(H[i] - L[i], Math.abs(H[i] - prevClose), Math.abs(L[i] - prevClose)); } - // Fallback: treat inputs as scalars for current bar + // When arrays are passed, compute true range from the last bar using the previous close if (Array.isArray(close)) { const prevClose = close[close.length - 2] || close[0]; const currHigh = Array.isArray(high) ? high[high.length - 1] : high; @@ -945,6 +1230,92 @@ const pinescript = { return Math.max(high - low, Math.abs(high - close), Math.abs(low - close)); }, + arrayFirst: function(arr) { + if (!arr || arr.length === 0) return null; + return arr[0]; + }, + arrayLast: function(arr) { + if (!arr || arr.length === 0) return null; + return arr[arr.length - 1]; + }, + arrayJoin: function(arr, separator = ',') { + if (!arr) return ''; + return arr.join(separator); + }, + arrayConcat: function(arr1, arr2) { + if (!arr1) return arr2 || []; + if (!arr2) return arr1; + return arr1.concat(arr2); + }, + arrayCopy: function(arr) { + if (!arr) return []; + return [...arr]; + }, + arrayBinarySearch: function(arr, value) { + if (!arr || arr.length === 0) return -1; + let lo = 0, hi = arr.length - 1; + while (lo <= hi) { + const mid = (lo + hi) >>> 1; + if (arr[mid] === value) return mid; + if (arr[mid] < value) lo = mid + 1; + else hi = mid - 1; + } + return -1; + }, + arrayRange: function(arr) { + if (!arr || arr.length === 0) return 0; + return Math.max(...arr) - Math.min(...arr); + }, + arrayMedian: function(arr) { + if (!arr || arr.length === 0) return null; + const sorted = [...arr].sort((a, b) => a - b); + const mid = Math.floor(sorted.length / 2); + return sorted.length % 2 !== 0 ? sorted[mid] : (sorted[mid - 1] + sorted[mid]) / 2; + }, + arrayMode: function(arr) { + if (!arr || arr.length === 0) return null; + const freq = {}; + let maxCount = 0; + let mode = arr[0]; + for (const v of arr) { + freq[v] = (freq[v] || 0) + 1; + if (freq[v] > maxCount) { maxCount = freq[v]; mode = v; } + } + return mode; + }, + arrayPercentile: function(arr, percentile) { + if (!arr || arr.length === 0) return null; + const sorted = [...arr].sort((a, b) => a - b); + const idx = (percentile / 100) * (sorted.length - 1); + const lo = Math.floor(idx); + const hi = Math.ceil(idx); + if (lo === hi) return sorted[lo]; + return sorted[lo] + (sorted[hi] - sorted[lo]) * (idx - lo); + }, + arrayPercentileLinearInterpolation: function(arr, percentile) { + return builtins.get('arrayPercentile')(arr, percentile); + }, + arrayPercentileNearestRank: function(arr, percentile) { + if (!arr || arr.length === 0) return null; + const sorted = [...arr].sort((a, b) => a - b); + const idx = Math.ceil((percentile / 100) * sorted.length) - 1; + return sorted[Math.max(0, idx)]; + }, + arrayAbs: function(arr) { + if (!arr) return []; + return arr.map(v => Math.abs(v)); + }, + arrayEvery: function(arr, callback) { + if (!arr) return false; + return arr.every(callback); + }, + arraySome: function(arr, callback) { + if (!arr) return false; + return arr.some(callback); + }, + requestFinancial: function() { + return null; + }, }; @@ -1101,6 +1472,115 @@ globalThis.barmerge = globalThis.barmerge || { }; +function __pineNS(real) { + + const base = real || {}; + + return new Proxy(base, { + + get(target, key) { + + if (key in target) return target[key]; + + if (typeof key === "symbol") return target[key]; + + // Unknown member: usable as a value (null), a constant, or a no-op function. + + const noop = function() { return null; }; + + noop.toString = () => ""; + + return noop; + + } + + }); + +} + +globalThis.ta = globalThis.ta || __pineNS({}); + +globalThis.math = globalThis.math || __pineNS({ pi: Math.PI, e: Math.E, phi: 1.618033988749895, rphi: 0.618033988749895 }); + +globalThis.chart = globalThis.chart || __pineNS({ point: __pineNS({}), bg_color: null, fg_color: null }); + +globalThis.line = globalThis.line || __pineNS({ style_solid: "solid", style_dashed: "dashed", style_dotted: "dotted" }); + +globalThis.box = globalThis.box || __pineNS({}); + +globalThis.label = globalThis.label || __pineNS({ style_label_down: "label_down", style_label_up: "label_up", style_none: "none" }); + +globalThis.polyline = globalThis.polyline || __pineNS({}); + +globalThis.linefill = globalThis.linefill || __pineNS({}); + +globalThis.matrix = globalThis.matrix || __pineNS({}); + +globalThis.map = globalThis.map || __pineNS({}); + +globalThis.session = globalThis.session || __pineNS({ regular: "regular", extended: "extended" }); + +globalThis.dayofweek = globalThis.dayofweek || __pineNS({ sunday: 1, monday: 2, tuesday: 3, wednesday: 4, thursday: 5, friday: 6, saturday: 7 }); + +globalThis.timeframe = __pineNS(Object.assign(globalThis.timeframe || {}, { period: (globalThis.timeframe && globalThis.timeframe.period) || "D", isintraday: false, isdaily: true, multiplier: 1 })); + +globalThis.request = __pineNS(globalThis.request || {}); + +globalThis.input = __pineNS(globalThis.input || {}); + +globalThis.adjustment = globalThis.adjustment || __pineNS({}); + +globalThis.earnings = globalThis.earnings || __pineNS({}); + +globalThis.dividends = globalThis.dividends || __pineNS({}); + +globalThis.splits = globalThis.splits || __pineNS({}); + +globalThis.currency = globalThis.currency || __pineNS({}); + +globalThis.display = globalThis.display || __pineNS({ none: "none", all: "all", pane: "pane", data_window: "data_window", status_line: "status_line", price_scale: "price_scale" }); + +globalThis.format = globalThis.format || __pineNS({ price: "price", volume: "volume", percent: "percent", mintick: "mintick", inherit: "inherit" }); + +globalThis.scale = globalThis.scale || __pineNS({ left: "left", right: "right", none: "none" }); + +globalThis.font = globalThis.font || __pineNS({ family_default: "default", family_monospace: "monospace" }); + +globalThis.xloc = globalThis.xloc || __pineNS({ bar_index: "bar_index", bar_time: "bar_time" }); + +globalThis.yloc = globalThis.yloc || __pineNS({ price: "price", abovebar: "abovebar", belowbar: "belowbar" }); + +globalThis.extend = globalThis.extend || __pineNS({ none: "none", left: "left", right: "right", both: "both" }); + +globalThis.order = globalThis.order || __pineNS({ ascending: "ascending", descending: "descending" }); + +globalThis.alert = globalThis.alert || Object.assign(function() { return null; }, { freq_once_per_bar: "once_per_bar", freq_once_per_bar_close: "once_per_bar_close", freq_all: "all" }); + +globalThis.dayofmonth = globalThis.dayofmonth || function(t) { return new Date(t || 0).getUTCDate(); }; + +globalThis.plot = globalThis.plot || __pineNS({ style_line: "line", style_linebr: "linebr", style_stepline: "stepline", style_histogram: "histogram", style_cross: "cross", style_area: "area", style_areabr: "areabr", style_columns: "columns", style_circles: "circles" }); + +globalThis.int = globalThis.int || function(x) { return x == null ? null : Math.trunc(Number(x)); }; + +globalThis.float = globalThis.float || function(x) { return x == null ? null : Number(x); }; + +globalThis.bool = globalThis.bool || function(x) { return Boolean(x); }; + +globalThis.string = globalThis.string || function(x) { return x == null ? null : String(x); }; + +globalThis.dayofweek = globalThis.dayofweek || __pineNS({ sunday: 1, monday: 2, tuesday: 3, wednesday: 4, thursday: 5, friday: 6, saturday: 7 }); + +globalThis.str = globalThis.str || __pineNS({}); + +pinescript.math = globalThis.math; + +pinescript.ta = globalThis.ta; + +pinescript.str = globalThis.str; + +pinescript.array = __pineNS(globalThis.array); + + pinescript.color = { hex: function(s) { @@ -1190,11 +1670,11 @@ pinescript.table = { }; -// Input parameters +// Script input parameters and their defaults const LTFCandles = { new: function(bodies, wicks_high, wicks_low, opens, highs, lows, closes) { return { bodies: bodies, wicks_high: wicks_high, wicks_low: wicks_low, opens: opens, highs: highs, lows: lows, closes: closes }; } }; -// Main script logic +// The transpiled script logic, called once per bar function main() { @@ -1210,65 +1690,65 @@ function main() { // Study: // Options: {"title":"NY Opening Range (Enhanced with LTF)","shorttitle":"NY ORB","overlay":true,"max_lines_count":500,"max_labels_count":500,"max_boxes_count":500,"max_bars_back":5000} if (state.g_5MIN === undefined) state.g_5MIN = "━━━━━━━━━ 5-MINUTE ORB ━━━━━━━━━"; - let show_5min = input.bool(true, "Show 5-Minute ORB", ({ group: state.g_5MIN })); - let bullish_color_5m = input.color(pinescript.color.rgb(0, 255, 149), "Bullish Color", ({ inline: "5mc", group: state.g_5MIN })); - let bearish_color_5m = input.color(pinescript.color.rgb(195, 0, 255), "Bearish Color", ({ inline: "5mc", group: state.g_5MIN })); - let bullish_fill_5m = input.color(pinescript.color.rgb(0, 255, 149, 85), "Bullish Fill", ({ inline: "5mf", group: state.g_5MIN })); - let bearish_fill_5m = input.color(pinescript.color.rgb(195, 0, 255, 85), "Bearish Fill", ({ inline: "5mf", group: state.g_5MIN })); - let boxStyle_5m = input.string("Solid", "Range Style", ({ options: ["Solid", "Dashed", "Dotted"], inline: "5box", group: state.g_5MIN })); - let borderWidth_5m = input.int(1, "Width", 1, 10, ({ inline: "5box", group: state.g_5MIN })); - let show_midLine_5m = input.bool(true, "Show Midline", ({ inline: "5mid", group: state.g_5MIN })); - let lineStyle_5m = input.string("Dashed", "Style", ({ options: ["Solid", "Dashed", "Dotted"], inline: "5mid", group: state.g_5MIN })); - let lineWidth_5m = input.int(1, "Width", 1, 10, ({ inline: "5mid", group: state.g_5MIN })); - let show_extensions_5m = input.bool(true, "Show Median Extensions", ({ inline: "5ext", group: state.g_5MIN })); - let ext_line_style_5m = input.string("Dotted", "Style", ({ options: ["Solid", "Dashed", "Dotted"], inline: "5ext", group: state.g_5MIN })); - let ext_line_width_5m = input.int(1, "Width", ({ minval: 1, maxval: 5, inline: "5ext", group: state.g_5MIN })); - let show_ext_labels_5m = input.bool(true, "Show Extension Labels", ({ group: state.g_5MIN })); - let show_ltf_5m = input.bool(true, "Show LTF Candles", ({ group: state.g_5MIN })); - let ltf_5m_count = input.int(5, " └─ Number of Candles", ({ minval: 1, maxval: 20, group: state.g_5MIN })); - let ltf_offset_5m = input.int(5, " └─ Offset (bars from right)", ({ minval: 1, maxval: 100, group: state.g_5MIN })); - let ltf_spacing_5m = input.int(3, " └─ Candle Spacing", ({ minval: 1, maxval: 5, group: state.g_5MIN })); - let ltf_width_5m = input.int(2, " └─ Candle Width", ({ minval: 1, maxval: 5, group: state.g_5MIN })); - let ltf_bull_color_5m = input.color(pinescript.color.rgb(0, 255, 149, 60), " └─ LTF Bullish Color", ({ group: state.g_5MIN })); - let ltf_bear_color_5m = input.color(pinescript.color.rgb(195, 0, 255, 60), " └─ LTF Bearish Color", ({ group: state.g_5MIN })); - let show_ltf_range_5m = input.bool(true, " └─ Show LTF High/Low Lines", ({ group: state.g_5MIN })); - let ltf_range_style_5m = input.string("Dotted", " └─ LTF Range Style", ({ options: ["Solid", "Dashed", "Dotted"], group: state.g_5MIN })); - let ltf_range_width_5m = input.int(1, " └─ LTF Range Width", ({ minval: 1, maxval: 5, group: state.g_5MIN })); - let ltf_range_color_5m = input.color(pinescript.color.rgb(0, 255, 149, 50), " └─ LTF Range Color", ({ group: state.g_5MIN })); + let show_5min = pinescript.inputBool(true, "Show 5-Minute ORB", ({ group: state.g_5MIN })); + let bullish_color_5m = pinescript.inputColor(pinescript.color.rgb(0, 255, 149), "Bullish Color", ({ inline: "5mc", group: state.g_5MIN })); + let bearish_color_5m = pinescript.inputColor(pinescript.color.rgb(195, 0, 255), "Bearish Color", ({ inline: "5mc", group: state.g_5MIN })); + let bullish_fill_5m = pinescript.inputColor(pinescript.color.rgb(0, 255, 149, 85), "Bullish Fill", ({ inline: "5mf", group: state.g_5MIN })); + let bearish_fill_5m = pinescript.inputColor(pinescript.color.rgb(195, 0, 255, 85), "Bearish Fill", ({ inline: "5mf", group: state.g_5MIN })); + let boxStyle_5m = pinescript.inputString("Solid", "Range Style", ({ options: ["Solid", "Dashed", "Dotted"], inline: "5box", group: state.g_5MIN })); + let borderWidth_5m = pinescript.inputInt(1, "Width", 1, 10, ({ inline: "5box", group: state.g_5MIN })); + let show_midLine_5m = pinescript.inputBool(true, "Show Midline", ({ inline: "5mid", group: state.g_5MIN })); + let lineStyle_5m = pinescript.inputString("Dashed", "Style", ({ options: ["Solid", "Dashed", "Dotted"], inline: "5mid", group: state.g_5MIN })); + let lineWidth_5m = pinescript.inputInt(1, "Width", 1, 10, ({ inline: "5mid", group: state.g_5MIN })); + let show_extensions_5m = pinescript.inputBool(true, "Show Median Extensions", ({ inline: "5ext", group: state.g_5MIN })); + let ext_line_style_5m = pinescript.inputString("Dotted", "Style", ({ options: ["Solid", "Dashed", "Dotted"], inline: "5ext", group: state.g_5MIN })); + let ext_line_width_5m = pinescript.inputInt(1, "Width", ({ minval: 1, maxval: 5, inline: "5ext", group: state.g_5MIN })); + let show_ext_labels_5m = pinescript.inputBool(true, "Show Extension Labels", ({ group: state.g_5MIN })); + let show_ltf_5m = pinescript.inputBool(true, "Show LTF Candles", ({ group: state.g_5MIN })); + let ltf_5m_count = pinescript.inputInt(5, " └─ Number of Candles", ({ minval: 1, maxval: 20, group: state.g_5MIN })); + let ltf_offset_5m = pinescript.inputInt(5, " └─ Offset (bars from right)", ({ minval: 1, maxval: 100, group: state.g_5MIN })); + let ltf_spacing_5m = pinescript.inputInt(3, " └─ Candle Spacing", ({ minval: 1, maxval: 5, group: state.g_5MIN })); + let ltf_width_5m = pinescript.inputInt(2, " └─ Candle Width", ({ minval: 1, maxval: 5, group: state.g_5MIN })); + let ltf_bull_color_5m = pinescript.inputColor(pinescript.color.rgb(0, 255, 149, 60), " └─ LTF Bullish Color", ({ group: state.g_5MIN })); + let ltf_bear_color_5m = pinescript.inputColor(pinescript.color.rgb(195, 0, 255, 60), " └─ LTF Bearish Color", ({ group: state.g_5MIN })); + let show_ltf_range_5m = pinescript.inputBool(true, " └─ Show LTF High/Low Lines", ({ group: state.g_5MIN })); + let ltf_range_style_5m = pinescript.inputString("Dotted", " └─ LTF Range Style", ({ options: ["Solid", "Dashed", "Dotted"], group: state.g_5MIN })); + let ltf_range_width_5m = pinescript.inputInt(1, " └─ LTF Range Width", ({ minval: 1, maxval: 5, group: state.g_5MIN })); + let ltf_range_color_5m = pinescript.inputColor(pinescript.color.rgb(0, 255, 149, 50), " └─ LTF Range Color", ({ group: state.g_5MIN })); if (state.g_15MIN === undefined) state.g_15MIN = "━━━━━━━━━ 15-MINUTE ORB ━━━━━━━━━"; - let show_15min = input.bool(true, "Show 15-Minute ORB", ({ group: state.g_15MIN })); - let bullish_color_15m = input.color(pinescript.color.rgb(33, 150, 243), "Bullish Color", ({ inline: "15mc", group: state.g_15MIN })); - let bearish_color_15m = input.color(pinescript.color.rgb(255, 152, 0), "Bearish Color", ({ inline: "15mc", group: state.g_15MIN })); - let bullish_fill_15m = input.color(pinescript.color.rgb(33, 150, 243, 85), "Bullish Fill", ({ inline: "15mf", group: state.g_15MIN })); - let bearish_fill_15m = input.color(pinescript.color.rgb(255, 152, 0, 85), "Bearish Fill", ({ inline: "15mf", group: state.g_15MIN })); - let boxStyle_15m = input.string("Solid", "Range Style", ({ options: ["Solid", "Dashed", "Dotted"], inline: "15box", group: state.g_15MIN })); - let borderWidth_15m = input.int(1, "Width", 1, 10, ({ inline: "15box", group: state.g_15MIN })); - let show_midLine_15m = input.bool(true, "Show Midline", ({ inline: "15mid", group: state.g_15MIN })); - let lineStyle_15m = input.string("Dashed", "Style", ({ options: ["Solid", "Dashed", "Dotted"], inline: "15mid", group: state.g_15MIN })); - let lineWidth_15m = input.int(1, "Width", 1, 10, ({ inline: "15mid", group: state.g_15MIN })); - let show_extensions_15m = input.bool(true, "Show Median Extensions", ({ inline: "15ext", group: state.g_15MIN })); - let ext_line_style_15m = input.string("Dotted", "Style", ({ options: ["Solid", "Dashed", "Dotted"], inline: "15ext", group: state.g_15MIN })); - let ext_line_width_15m = input.int(1, "Width", ({ minval: 1, maxval: 5, inline: "15ext", group: state.g_15MIN })); - let show_ext_labels_15m = input.bool(true, "Show Extension Labels", ({ group: state.g_15MIN })); - let show_ltf_15m = input.bool(true, "Show LTF Candles", ({ group: state.g_15MIN })); - let ltf_15m_count = input.int(15, " └─ Number of Candles", ({ minval: 1, maxval: 20, group: state.g_15MIN })); - let ltf_offset_15m = input.int(35, " └─ Offset (bars from right)", ({ minval: 1, maxval: 100, group: state.g_15MIN })); - let ltf_spacing_15m = input.int(3, " └─ Candle Spacing", ({ minval: 1, maxval: 5, group: state.g_15MIN })); - let ltf_width_15m = input.int(2, " └─ Candle Width", ({ minval: 1, maxval: 5, group: state.g_15MIN })); - let ltf_bull_color_15m = input.color(pinescript.color.rgb(0, 255, 149, 60), " └─ LTF Bullish Color", ({ group: state.g_15MIN })); - let ltf_bear_color_15m = input.color(pinescript.color.rgb(195, 0, 255, 60), " └─ LTF Bearish Color", ({ group: state.g_15MIN })); - let show_ltf_range_15m = input.bool(true, " └─ Show LTF High/Low Lines", ({ group: state.g_15MIN })); - let ltf_range_style_15m = input.string("Dotted", " └─ LTF Range Style", ({ options: ["Solid", "Dashed", "Dotted"], group: state.g_15MIN })); - let ltf_range_width_15m = input.int(1, " └─ LTF Range Width", ({ minval: 1, maxval: 5, group: state.g_15MIN })); - let ltf_range_color_15m = input.color(pinescript.color.rgb(33, 150, 243, 50), " └─ LTF Range Color", ({ group: state.g_15MIN })); + let show_15min = pinescript.inputBool(true, "Show 15-Minute ORB", ({ group: state.g_15MIN })); + let bullish_color_15m = pinescript.inputColor(pinescript.color.rgb(33, 150, 243), "Bullish Color", ({ inline: "15mc", group: state.g_15MIN })); + let bearish_color_15m = pinescript.inputColor(pinescript.color.rgb(255, 152, 0), "Bearish Color", ({ inline: "15mc", group: state.g_15MIN })); + let bullish_fill_15m = pinescript.inputColor(pinescript.color.rgb(33, 150, 243, 85), "Bullish Fill", ({ inline: "15mf", group: state.g_15MIN })); + let bearish_fill_15m = pinescript.inputColor(pinescript.color.rgb(255, 152, 0, 85), "Bearish Fill", ({ inline: "15mf", group: state.g_15MIN })); + let boxStyle_15m = pinescript.inputString("Solid", "Range Style", ({ options: ["Solid", "Dashed", "Dotted"], inline: "15box", group: state.g_15MIN })); + let borderWidth_15m = pinescript.inputInt(1, "Width", 1, 10, ({ inline: "15box", group: state.g_15MIN })); + let show_midLine_15m = pinescript.inputBool(true, "Show Midline", ({ inline: "15mid", group: state.g_15MIN })); + let lineStyle_15m = pinescript.inputString("Dashed", "Style", ({ options: ["Solid", "Dashed", "Dotted"], inline: "15mid", group: state.g_15MIN })); + let lineWidth_15m = pinescript.inputInt(1, "Width", 1, 10, ({ inline: "15mid", group: state.g_15MIN })); + let show_extensions_15m = pinescript.inputBool(true, "Show Median Extensions", ({ inline: "15ext", group: state.g_15MIN })); + let ext_line_style_15m = pinescript.inputString("Dotted", "Style", ({ options: ["Solid", "Dashed", "Dotted"], inline: "15ext", group: state.g_15MIN })); + let ext_line_width_15m = pinescript.inputInt(1, "Width", ({ minval: 1, maxval: 5, inline: "15ext", group: state.g_15MIN })); + let show_ext_labels_15m = pinescript.inputBool(true, "Show Extension Labels", ({ group: state.g_15MIN })); + let show_ltf_15m = pinescript.inputBool(true, "Show LTF Candles", ({ group: state.g_15MIN })); + let ltf_15m_count = pinescript.inputInt(15, " └─ Number of Candles", ({ minval: 1, maxval: 20, group: state.g_15MIN })); + let ltf_offset_15m = pinescript.inputInt(35, " └─ Offset (bars from right)", ({ minval: 1, maxval: 100, group: state.g_15MIN })); + let ltf_spacing_15m = pinescript.inputInt(3, " └─ Candle Spacing", ({ minval: 1, maxval: 5, group: state.g_15MIN })); + let ltf_width_15m = pinescript.inputInt(2, " └─ Candle Width", ({ minval: 1, maxval: 5, group: state.g_15MIN })); + let ltf_bull_color_15m = pinescript.inputColor(pinescript.color.rgb(0, 255, 149, 60), " └─ LTF Bullish Color", ({ group: state.g_15MIN })); + let ltf_bear_color_15m = pinescript.inputColor(pinescript.color.rgb(195, 0, 255, 60), " └─ LTF Bearish Color", ({ group: state.g_15MIN })); + let show_ltf_range_15m = pinescript.inputBool(true, " └─ Show LTF High/Low Lines", ({ group: state.g_15MIN })); + let ltf_range_style_15m = pinescript.inputString("Dotted", " └─ LTF Range Style", ({ options: ["Solid", "Dashed", "Dotted"], group: state.g_15MIN })); + let ltf_range_width_15m = pinescript.inputInt(1, " └─ LTF Range Width", ({ minval: 1, maxval: 5, group: state.g_15MIN })); + let ltf_range_color_15m = pinescript.inputColor(pinescript.color.rgb(33, 150, 243, 50), " └─ LTF Range Color", ({ group: state.g_15MIN })); if (state.g_LABELS === undefined) state.g_LABELS = "Labels & Display"; - let showL = input.bool(true, "Show High/Low/Mid Labels", ({ inline: "Labels", group: state.g_LABELS })); - let showP = input.bool(false, "Show Prices", ({ inline: "Labels", group: state.g_LABELS })); - let pos = input.string("Left", "Position", ({ options: ["Left", "Right"], group: state.g_LABELS })); + let showL = pinescript.inputBool(true, "Show High/Low/Mid Labels", ({ inline: "Labels", group: state.g_LABELS })); + let showP = pinescript.inputBool(false, "Show Prices", ({ inline: "Labels", group: state.g_LABELS })); + let pos = pinescript.inputString("Left", "Position", ({ options: ["Left", "Right"], group: state.g_LABELS })); if (state.g_TABLE === undefined) state.g_TABLE = "Statistics Table"; - let show_stats_table = input.bool(true, "Show Statistics Table", ({ group: state.g_TABLE })); - let stats_table_pos = input.string("Top Right", "Table Position", ({ options: ["Bottom Center", "Bottom Left", "Bottom Right", "Middle Center", "Middle Left", "Middle Right", "Top Center", "Top Left", "Top Right"], group: state.g_TABLE })); - let stats_table_size = input.string("Small", "Table Size", ({ options: ["Auto", "Tiny", "Small", "Normal", "Large", "Huge"], group: state.g_TABLE })); + let show_stats_table = pinescript.inputBool(true, "Show Statistics Table", ({ group: state.g_TABLE })); + let stats_table_pos = pinescript.inputString("Top Right", "Table Position", ({ options: ["Bottom Center", "Bottom Left", "Bottom Right", "Middle Center", "Middle Left", "Middle Right", "Top Center", "Top Left", "Top Right"], group: state.g_TABLE })); + let stats_table_size = pinescript.inputString("Small", "Table Size", ({ options: ["Auto", "Tiny", "Small", "Normal", "Large", "Huge"], group: state.g_TABLE })); function get_style(style) { switch (style) { case "Solid": @@ -1478,20 +1958,20 @@ function main() { if (state.ib_captured === undefined) state.ib_captured = false; if (state.ib_is_bullish === undefined) state.ib_is_bullish = false; let is_5m_session = !pinescript.na(pinescript.time(timeframe.period, "0930-0935", tz)); - let is_5m_start = (is_5m_session && !pinescript.offset(is_5m_session, 1)); - let is_5m_end = (pinescript.offset(is_5m_session, 1) && !is_5m_session); + let is_5m_start = (is_5m_session && !pinescript.hist(0, is_5m_session, 1)); + let is_5m_end = (pinescript.hist(1, is_5m_session, 1) && !is_5m_session); let is_15m_session = !pinescript.na(pinescript.time(timeframe.period, "0930-0945", tz)); - let is_15m_start = (is_15m_session && !pinescript.offset(is_15m_session, 1)); - let is_15m_end = (pinescript.offset(is_15m_session, 1) && !is_15m_session); + let is_15m_start = (is_15m_session && !pinescript.hist(2, is_15m_session, 1)); + let is_15m_end = (pinescript.hist(3, is_15m_session, 1) && !is_15m_session); let is_ib_session = !pinescript.na(pinescript.time(timeframe.period, "0930-1030", tz)); - let is_ib_start = (is_ib_session && !pinescript.offset(is_ib_session, 1)); - let is_ib_end = (pinescript.offset(is_ib_session, 1) && !is_ib_session); + let is_ib_start = (is_ib_session && !pinescript.hist(4, is_ib_session, 1)); + let is_ib_end = (pinescript.hist(5, is_ib_session, 1) && !is_ib_session); let is_ny_session = !pinescript.na(pinescript.time(timeframe.period, "0930-1600", tz)); - let is_ny_session_end = (pinescript.offset(is_ny_session, 1) && !is_ny_session); - let [H5, L5, M5, O5, C5] = pinescript.requestSecurity(ticker.standard(syminfo.tickerid), "5", [high, low, hl2, open, close], ({ lookahead: barmerge.lookahead_on })); - let [H15, L15, M15, O15, C15] = pinescript.requestSecurity(ticker.standard(syminfo.tickerid), "15", [high, low, hl2, open, close], ({ lookahead: barmerge.lookahead_on })); - let [H60, L60, O60, C60] = pinescript.requestSecurity(ticker.standard(syminfo.tickerid), "60", [high, low, open, close], ({ lookahead: barmerge.lookahead_on })); - let [high_1m, low_1m, open_1m, close_1m] = request.security_lower_tf(ticker.standard(syminfo.tickerid), "1", [high, low, open, close]); + let is_ny_session_end = (pinescript.hist(6, is_ny_session, 1) && !is_ny_session); + let [H5, L5, M5, O5, C5] = pinescript.unpack(pinescript.requestSecurity(ticker.standard(syminfo.tickerid), "5", [high, low, hl2, open, close], ({ lookahead: barmerge.lookahead_on })), 5); + let [H15, L15, M15, O15, C15] = pinescript.unpack(pinescript.requestSecurity(ticker.standard(syminfo.tickerid), "15", [high, low, hl2, open, close], ({ lookahead: barmerge.lookahead_on })), 5); + let [H60, L60, O60, C60] = pinescript.unpack(pinescript.requestSecurity(ticker.standard(syminfo.tickerid), "60", [high, low, open, close], ({ lookahead: barmerge.lookahead_on })), 4); + let [high_1m, low_1m, open_1m, close_1m] = pinescript.unpack(request.security_lower_tf(ticker.standard(syminfo.tickerid), "1", [high, low, open, close]), 4); let _style = ((pos === "Left") ? label.style_label_right : label.style_label_left); if (((is_ib_start && !pinescript.na(H60)) && !pinescript.na(L60))) { state.ib_high = H60; @@ -1507,7 +1987,7 @@ function main() { function draw_ltf_candles(ltf, o_array, h_array, l_array, c_array, bull_color, bear_color, offset_from_right, max_candles, spacing, width) { if ((pinescript.arraySize(o_array) > 0)) { while ((pinescript.arraySize(ltf.bodies) > 0)) { - box.delete(ltf.bodies.shift()); + pinescript.boxDelete(ltf.bodies.shift()); } while ((pinescript.arraySize(ltf.wicks_high) > 0)) { pinescript.lineDelete(ltf.wicks_high.shift()); @@ -1532,7 +2012,7 @@ function main() { let x_mid = pinescript.round((x_start + (width / 2))); let body_top = pinescript.max(o, c); let body_bottom = pinescript.min(o, c); - ltf.bodies.push(box.new(x_start, body_top, x_end, body_bottom, ({ border_color: candle_color, bgcolor: candle_color, border_width: 1 }))); + ltf.bodies.push(pinescript.boxNew(x_start, body_top, x_end, body_bottom, ({ border_color: candle_color, bgcolor: candle_color, border_width: 1 }))); ltf.wicks_high.push(pinescript.lineNew(x_mid, body_top, x_mid, h, ({ color: candle_color, width: 1 }))); ltf.wicks_low.push(pinescript.lineNew(x_mid, body_bottom, x_mid, l, ({ color: candle_color, width: 1 }))); ltf.highs.push(h); @@ -1621,7 +2101,7 @@ function main() { } } if ((state.or5_captured && !state.or5_mid_retested)) { - if (((((high >= state.or5_mid) && (low <= state.or5_mid)) || ((pinescript.offset(close, 1) < state.or5_mid) && (close >= state.or5_mid))) || ((pinescript.offset(close, 1) > state.or5_mid) && (close <= state.or5_mid)))) { + if (((((high >= state.or5_mid) && (low <= state.or5_mid)) || ((pinescript.hist(7, close, 1) < state.or5_mid) && (close >= state.or5_mid))) || ((pinescript.hist(8, close, 1) > state.or5_mid) && (close <= state.or5_mid)))) { state.or5_mid_retested = true; } } @@ -1657,7 +2137,7 @@ function main() { } } if (((((state.or5_captured && show_ltf_5m) && state.or5_ltf_frozen) && (pinescript.arraySize(state.or5_ltf_opens_frozen) > 0)) && is_ny_session)) { - [ltf_start_x, ltf_end_x] = draw_ltf_candles(state.ltf5, state.or5_ltf_opens_frozen, state.or5_ltf_highs_frozen, state.or5_ltf_lows_frozen, state.or5_ltf_closes_frozen, ltf_bull_color_5m, ltf_bear_color_5m, ltf_offset_5m, ltf_5m_count, ltf_spacing_5m, ltf_width_5m); + [ltf_start_x, ltf_end_x] = pinescript.unpack(draw_ltf_candles(state.ltf5, state.or5_ltf_opens_frozen, state.or5_ltf_highs_frozen, state.or5_ltf_lows_frozen, state.or5_ltf_closes_frozen, ltf_bull_color_5m, ltf_bear_color_5m, ltf_offset_5m, ltf_5m_count, ltf_spacing_5m, ltf_width_5m), 2); if ((show_ltf_range_5m && (pinescript.arraySize(state.ltf5.highs) > 0))) { pinescript.lineDelete(state.or5_ltf_high_line); pinescript.lineDelete(state.or5_ltf_low_line); @@ -1801,7 +2281,7 @@ function main() { } } if ((state.or15_captured && !state.or15_mid_retested)) { - if (((((high >= state.or15_mid) && (low <= state.or15_mid)) || ((pinescript.offset(close, 1) < state.or15_mid) && (close >= state.or15_mid))) || ((pinescript.offset(close, 1) > state.or15_mid) && (close <= state.or15_mid)))) { + if (((((high >= state.or15_mid) && (low <= state.or15_mid)) || ((pinescript.hist(9, close, 1) < state.or15_mid) && (close >= state.or15_mid))) || ((pinescript.hist(10, close, 1) > state.or15_mid) && (close <= state.or15_mid)))) { state.or15_mid_retested = true; } } @@ -1837,7 +2317,7 @@ function main() { } } if (((((state.or15_captured && show_ltf_15m) && state.or15_ltf_frozen) && (pinescript.arraySize(state.or15_ltf_opens_frozen) > 0)) && is_ny_session)) { - [ltf_start_x, ltf_end_x] = draw_ltf_candles(state.ltf15, state.or15_ltf_opens_frozen, state.or15_ltf_highs_frozen, state.or15_ltf_lows_frozen, state.or15_ltf_closes_frozen, ltf_bull_color_15m, ltf_bear_color_15m, ltf_offset_15m, ltf_15m_count, ltf_spacing_15m, ltf_width_15m); + [ltf_start_x, ltf_end_x] = pinescript.unpack(draw_ltf_candles(state.ltf15, state.or15_ltf_opens_frozen, state.or15_ltf_highs_frozen, state.or15_ltf_lows_frozen, state.or15_ltf_closes_frozen, ltf_bull_color_15m, ltf_bear_color_15m, ltf_offset_15m, ltf_15m_count, ltf_spacing_15m, ltf_width_15m), 2); if ((show_ltf_range_15m && (pinescript.arraySize(state.ltf15.highs) > 0))) { pinescript.lineDelete(state.or15_ltf_high_line); pinescript.lineDelete(state.or15_ltf_low_line); @@ -1905,7 +2385,7 @@ function main() { } if ((show_stats_table && (state.or5_captured || state.or15_captured))) { if (state.stats_table === undefined) state.stats_table = null; - table.delete(pinescript.offset(state.stats_table, 1)); + pinescript.tableDelete(pinescript.hist(11, state.stats_table, 1)); let table_pos = get_table_position(stats_table_pos); let table_size = get_table_text_size(stats_table_size); let header_bg = pinescript.color.rgb(64, 64, 64); @@ -1920,13 +2400,13 @@ function main() { let session_ended = (is_ny_session_end || !is_ny_session); let row = 0; pinescript.table.cell(state.stats_table, 0, row, "━━━ ORB STATISTICS ━━━", ({ bgcolor: header_bg, text_color: header_text, text_size: table_size, text_font_family: font.family_monospace })); - table.merge_cells(state.stats_table, 0, row, 2, row); + pinescript.tableMergeCells(state.stats_table, 0, row, 2, row); row = (row + 1); if ((show_5min && state.or5_captured)) { let is_bull_5m = (state.or5_close >= state.or5_open); let extreme_5m = state.or5_extreme_first; pinescript.table.cell(state.stats_table, 0, row, "━━━ 5-MIN ORB ━━━", ({ bgcolor: header_bg, text_color: header_text, text_size: table_size, text_font_family: font.family_monospace })); - table.merge_cells(state.stats_table, 0, row, 2, row); + pinescript.tableMergeCells(state.stats_table, 0, row, 2, row); row = (row + 1); pinescript.table.cell(state.stats_table, 0, row, "Direction", ({ bgcolor: cell_bg, text_color: cell_text, text_size: table_size, text_font_family: font.family_monospace })); let dir_text = (is_bull_5m ? "Bullish" : "Bearish"); @@ -2076,14 +2556,14 @@ function main() { row = (row + 1); } pinescript.table.cell(state.stats_table, 0, row, "", ({ bgcolor: cell_bg, text_color: cell_text, text_size: table_size })); - table.merge_cells(state.stats_table, 0, row, 2, row); + pinescript.tableMergeCells(state.stats_table, 0, row, 2, row); row = (row + 1); } if ((show_15min && state.or15_captured)) { let is_bull_15m = (state.or15_close >= state.or15_open); let extreme_15m = state.or15_extreme_first; pinescript.table.cell(state.stats_table, 0, row, "━━━ 15-MIN ORB ━━━", ({ bgcolor: header_bg, text_color: header_text, text_size: table_size, text_font_family: font.family_monospace })); - table.merge_cells(state.stats_table, 0, row, 2, row); + pinescript.tableMergeCells(state.stats_table, 0, row, 2, row); row = (row + 1); pinescript.table.cell(state.stats_table, 0, row, "Direction", ({ bgcolor: cell_bg, text_color: cell_text, text_size: table_size, text_font_family: font.family_monospace })); dir_text = (is_bull_15m ? "Bullish" : "Bearish"); @@ -2237,7 +2717,67 @@ function main() { } -// Export for use -export { main +// The bar-by-bar engine: runs main() once per bar over the dataset. +function run(data, options = {}) { + data = data || {}; + const inClose = (data.close || []).map(Number); + const n = inClose.length; + const inOpen = (data.open || inClose).map(Number); + const inHigh = (data.high || inClose).map(Number); + const inLow = (data.low || inClose).map(Number); + const inVol = (data.volume || new Array(n).fill(0)).map(Number); + const inTime = (data.time || inClose.map((_, i) => i)).map(Number); + + // Reset all persistent and per-run state so repeated runs are independent. + globalThis.__pineState = {}; + globalThis.__pineRuntime = { plots: {}, plotshapes: {}, alerts: [], bars: n, inputs: options.inputs || {} }; + pinescript.__rt = {}; + pinescript.__bar = 0; + + // Growing OHLCV windows: each is a SeriesArray that gains one element per bar, + // so window built-ins like sma and highest see history up to the current bar. + const O = pinescript.asSeries([]), H = pinescript.asSeries([]), L = pinescript.asSeries([]); + const C = pinescript.asSeries([]), V = pinescript.asSeries([]), T = pinescript.asSeries([]); + const HL2 = pinescript.asSeries([]), HLC3 = pinescript.asSeries([]), OHLC4 = pinescript.asSeries([]); + globalThis.open = O; globalThis.high = H; globalThis.low = L; globalThis.close = C; + globalThis.volume = V; globalThis.time = T; + globalThis.hl2 = HL2; globalThis.hlc3 = HLC3; globalThis.ohlc4 = OHLC4; + globalThis.syminfo = globalThis.syminfo || { tickerid: 'SYNTHETIC', ticker: 'SYNTHETIC', mintick: 0.01 }; + + const rt = globalThis.__pineRuntime; + for (let i = 0; i < n; i++) { + O.push(inOpen[i]); H.push(inHigh[i]); L.push(inLow[i]); C.push(inClose[i]); V.push(inVol[i]); T.push(inTime[i]); + HL2.push((inHigh[i] + inLow[i]) / 2); + HLC3.push((inHigh[i] + inLow[i] + inClose[i]) / 3); + OHLC4.push((inOpen[i] + inHigh[i] + inLow[i] + inClose[i]) / 4); + + pinescript.__bar = i; + rt.__barIndex = i; + rt.__plotIdx = 0; + rt.__shapeIdx = 0; + globalThis.bar_index = i; + globalThis.last_bar_index = n - 1; + globalThis.barstate = { + isfirst: i === 0, islast: i === n - 1, isrealtime: false, ishistory: true, + isconfirmed: true, isnew: true, islastconfirmedhistory: i === n - 1, + }; + + main(); + } + + // Backfill skipped bars so every output series has exactly n entries. + for (const k of Object.keys(rt.plots)) { + const d = rt.plots[k].data; + for (let i = 0; i < n; i++) if (d[i] === undefined) d[i] = null; + } + for (const k of Object.keys(rt.plotshapes)) { + const d = rt.plotshapes[k].data; + for (let i = 0; i < n; i++) if (d[i] === undefined) d[i] = false; + } + return rt; +} + + +export { main, run }; diff --git a/converted/Net_Buy_Volume_theUltimator5.js b/converted/Net_Buy_Volume_theUltimator5.js index 25af0ea..3b480a2 100644 --- a/converted/Net_Buy_Volume_theUltimator5.js +++ b/converted/Net_Buy_Volume_theUltimator5.js @@ -1,12 +1,63 @@ -// PineScript to JavaScript Transpiled Code +// Auto-generated JavaScript from PineScript source -// Generated automatically +// Do not edit by hand -- re-run the converter instead -// Built-in PineScript functions +// All the built-in PineScript functions that scripts depend on at runtime const pinescript = { + __scalar: function(v) { + if (v === null || v === undefined) return v; + if (typeof v === 'object' && typeof v.valueOf === 'function') { + const s = v.valueOf(); + if (typeof s === 'number' || typeof s === 'boolean' || s === null) return s; + } + return v; + }, + series: function(id, value) { + const rt = this.__rt || (this.__rt = {}); + const store = rt.series || (rt.series = {}); + let buf = store[id]; + if (!buf) buf = store[id] = []; + buf[this.__bar | 0] = this.__scalar(value); + return buf; + }, + hist: function(id, value, n) { + const buf = this.series(id, value); + const idx = (this.__bar | 0) - (n | 0); + return idx >= 0 && idx < buf.length ? buf[idx] : null; + }, + time: function(timeframe, session) { + const t = globalThis.time; + if (t && typeof t.valueOf === 'function') return t.valueOf(); + return Array.isArray(t) ? t[t.length - 1] : t; + }, + sign: function(x) { + if (x === null || x === undefined || (typeof x === 'number' && isNaN(x))) return null; + return Math.sign(Number(x)); + }, + unpack: function(value, count) { + if (Array.isArray(value)) return value; + if (value != null && typeof value[Symbol.iterator] === 'function') return Array.from(value); + return new Array(count || 0).fill(null); + }, + alertcondition: function(condition, ...rest) { + if (globalThis.__pineRuntime) { + globalThis.__pineRuntime.alerts.push({ condition, args: rest }); + } + return null; + }, + barcolor: function(color) { return null; }, + bgcolor: function(color, title, editable, showLast) { + return null; + }, + fill: function(series1, series2, color, title, editable) { + return null; + }, + hline: function(_price, _title, _opts) { + return null; + }, sma: function(series, length) { if (series === null || series === undefined) return null; if (!Array.isArray(series)) series = [series]; @@ -84,6 +135,13 @@ const pinescript = { } return weightSum > 0 ? sum / weightSum : null; }, + swma: function(series) { + if (series === null || series === undefined) return null; + if (!Array.isArray(series)) series = [series]; + if (series.length < 4) return null; + const s = series.slice(-4); + return (s[0] * 1 + s[1] * 2 + s[2] * 2 + s[3] * 1) / 6; + }, atr: function(high, low, close, length = 14) { if (!Array.isArray(high) || !Array.isArray(low) || !Array.isArray(close)) return null; if (high.length < length + 1 || low.length < length + 1 || close.length < length + 1) return null; @@ -114,6 +172,53 @@ const pinescript = { const atr = builtins.get('atr')(high, low, close, length); return { middle: sma, upper: sma + mult * atr, lower: sma - mult * atr }; }, + supertrend: function(factor, atrPeriod, high, low, close) { + if (!Array.isArray(high) || !Array.isArray(low) || !Array.isArray(close)) return [null, null]; + if (high.length < atrPeriod + 1) return [null, null]; + const atrVal = builtins.get('atr')(high, low, close, atrPeriod); + if (atrVal === null) return [null, null]; + const lastIdx = close.length - 1; + const hl2 = (high[lastIdx] + low[lastIdx]) / 2; + const upperBand = hl2 + factor * atrVal; + const lowerBand = hl2 - factor * atrVal; + // Determine direction: 1 means downtrend (price below band), -1 means uptrend + const direction = close[lastIdx] > upperBand ? -1 : close[lastIdx] < lowerBand ? 1 : -1; + const supertrendValue = direction === -1 ? lowerBand : upperBand; + return [supertrendValue, direction]; + }, + ta_supertrend: function(factor, atrPeriod) { + const H = globalThis.high; + const L = globalThis.low; + const C = globalThis.close; + return builtins.get('supertrend')(factor, atrPeriod, H, L, C); + }, + dmi: function(diLength, adxSmoothing, high, low, close) { + if (!Array.isArray(high) || !Array.isArray(low) || !Array.isArray(close)) return { plusDI: null, minusDI: null, adx: null }; + if (high.length < diLength + 1) return { plusDI: null, minusDI: null, adx: null }; + let plusDMSum = 0, minusDMSum = 0, trSum = 0; + for (let i = 1; i <= diLength; i++) { + const idx = high.length - 1 - diLength + i; + const upMove = high[idx] - high[idx - 1]; + const downMove = low[idx - 1] - low[idx]; + plusDMSum += (upMove > downMove && upMove > 0) ? upMove : 0; + minusDMSum += (downMove > upMove && downMove > 0) ? downMove : 0; + const tr = Math.max( + high[idx] - low[idx], + Math.abs(high[idx] - close[idx - 1]), + Math.abs(low[idx] - close[idx - 1]) + ); + trSum += tr; + } + const plusDI = trSum > 0 ? (plusDMSum / trSum) * 100 : 0; + const minusDI = trSum > 0 ? (minusDMSum / trSum) * 100 : 0; + const diSum = plusDI + minusDI; + const dx = diSum > 0 ? Math.abs(plusDI - minusDI) / diSum * 100 : 0; + return { plusDI, minusDI, adx: dx }; + }, + adx: function(diLength, adxSmoothing, high, low, close) { + const result = builtins.get('dmi')(diLength, adxSmoothing, high, low, close); + return result ? result.adx : null; + }, rsi: function(close, length = 14) { if (close === null || close === undefined) return null; if (!Array.isArray(close)) close = [close]; @@ -171,9 +276,7 @@ const pinescript = { return { macd: macdLine, signal: signalLine, histogram: histogram }; }, cci: function(a, b, c, d) { - // Pine signatures: - // - ta.cci(source, length) - // - ta.cci(high, low, close, length) + // Supports both ta.cci(source, length) and ta.cci(high, low, close, length) signatures let source; let length; if (Array.isArray(a) && typeof b === 'number' && c === undefined) { @@ -304,6 +407,15 @@ const pinescript = { stdev: function(source, length) { return Math.sqrt(builtins.get('variance')(source, length)); }, + median: function(source, length) { + if (!source || source.length < length) return null; + const sorted = [...source.slice(-length)].sort((a, b) => a - b); + const mid = Math.floor(sorted.length / 2); + return sorted.length % 2 !== 0 ? sorted[mid] : (sorted[mid - 1] + sorted[mid]) / 2; + }, + ta_median: function(source, length) { + return builtins.get('median')(source, length); + }, hl2: function(high, low) { return high && low ? (high[high.length - 1] + low[low.length - 1]) / 2 : null; }, @@ -420,6 +532,24 @@ const pinescript = { return (series1[i - 1] < series2[i - 1] && series1[i] >= series2[i]) || (series1[i - 1] > series2[i - 1] && series1[i] <= series2[i]); }, + crossover: function(series1, series2) { + if (series1 === null || series1 === undefined) return false; + if (series2 === null || series2 === undefined) return false; + if (!Array.isArray(series1)) series1 = [series1]; + if (!Array.isArray(series2)) series2 = [series2]; + if (series1.length < 2 || series2.length < 2) return false; + const i = series1.length - 1; + return series1[i - 1] < series2[i - 1] && series1[i] >= series2[i]; + }, + crossunder: function(series1, series2) { + if (series1 === null || series1 === undefined) return false; + if (series2 === null || series2 === undefined) return false; + if (!Array.isArray(series1)) series1 = [series1]; + if (!Array.isArray(series2)) series2 = [series2]; + if (series1.length < 2 || series2.length < 2) return false; + const i = series1.length - 1; + return series1[i - 1] > series2[i - 1] && series1[i] <= series2[i]; + }, offset: function(series, offset) { if (!Array.isArray(series)) return null; const idx = series.length - 1 - offset; @@ -500,12 +630,14 @@ const pinescript = { return builtins.get('nz')(value, replacement); }, plot: function(series, title = '', color = null, linewidth = 1) { - globalThis.__pineRuntime.plots.push({ - series, - title, - color, - linewidth - }); + const rt = globalThis.__pineRuntime; + if (!rt) return series; + const bar = rt.__barIndex | 0; + const ord = (rt.__plotIdx = (rt.__plotIdx | 0) + 1) - 1; + const key = (title && String(title)) || ('plot_' + ord); + let p = rt.plots[key]; + if (!p) p = rt.plots[key] = { title: key, color, linewidth, data: [] }; + p.data[bar] = this.__scalar(series); return series; }, lineNew: function(x1, y1, x2, y2, opts = {}) { @@ -514,20 +646,72 @@ const pinescript = { lineDelete: function(l) { return null; }, - labelNew: function(...args) { - if (globalThis.__pineRuntime) { - globalThis.__pineRuntime.labels = globalThis.__pineRuntime.labels || []; - globalThis.__pineRuntime.labels.push({ args }); - } - return { args }; + lineSetXY: function(line, point, x, y) { + if (!line) return null; + if (point === 0 || point === 'x1') { line.x1 = x; line.y1 = y; } + else { line.x2 = x; line.y2 = y; } + return line; + }, + lineGetX: function(line, point) { + if (!line) return null; + return point === 0 || point === 'x1' ? line.x1 : line.x2; + }, + lineGetY: function(line, point) { + if (!line) return null; + return point === 0 || point === 'y1' ? line.y1 : line.y2; + }, + labelNew: function(x, y, text = '', opts = {}) { + return { x, y, text, opts, _type: 'label' }; }, labelDelete: function(l) { return null; }, + labelSetText: function(label, text) { + if (!label) return null; + label.text = text; + return label; + }, + labelGetText: function(label) { + if (!label) return ''; + return label.text || ''; + }, + boxNew: function(left, top, right, bottom, opts = {}) { + return { left, top, right, bottom, opts, _type: 'box' }; + }, + boxDelete: function(box) { + return null; + }, + boxSetLeftTop: function(box, left, top) { + if (!box) return null; + box.left = left; + box.top = top; + return box; + }, + boxSetRightBottom: function(box, right, bottom) { + if (!box) return null; + box.right = right; + box.bottom = bottom; + return box; + }, + polylineNew: function(points, opts = {}) { + return { points: points || [], opts, _type: 'polyline' }; + }, + polylineDelete: function(poly) { + return null; + }, plotshape: function(condition, ...rest) { - if (globalThis.__pineRuntime) { - globalThis.__pineRuntime.plotshapes.push({ condition, args: rest }); + const rt = globalThis.__pineRuntime; + if (!rt) return condition; + const bar = rt.__barIndex | 0; + const ord = (rt.__shapeIdx = (rt.__shapeIdx | 0) + 1) - 1; + let key = 'shape_' + ord; + for (const r of rest) { + if (typeof r === 'string') { key = r; break; } + if (r && typeof r === 'object' && r.title) { key = String(r.title); break; } } + let s = rt.plotshapes[key]; + if (!s) s = rt.plotshapes[key] = { title: key, data: [] }; + s.data[bar] = !!this.__scalar(condition); return condition; }, indicator: function(title, shorttitle, overlay, opts) { @@ -542,27 +726,12 @@ const pinescript = { } return null; }, - alertcondition: function(condition, ...rest) { - if (globalThis.__pineRuntime) { - globalThis.__pineRuntime.alerts.push({ condition, args: rest }); - } - return null; - }, plotbar: function(open, high, low, close, title, color, editable, showLast) { return { open, high, low, close }; }, plotcandle: function(open, high, low, close, title, color, wickColor, borderColor, editable, showLast) { return { open, high, low, close }; }, - hline: function(_price, _title, _opts) { - return null; - }, - bgcolor: function(color, title, editable, showLast) { - return null; - }, - fill: function(series1, series2, color, title, editable) { - return null; - }, alert: function(condition, message, frequency) { console.log('Alert:', condition ? message : 'Condition not met'); return condition; @@ -828,6 +997,9 @@ const pinescript = { arrayIndexOf: function(arr, value) { return arr ? arr.indexOf(value) : -1; }, + arrayLastIndexOf: function(arr, value) { + return arr ? arr.lastIndexOf(value) : -1; + }, arraySum: function(arr) { return arr ? arr.reduce((a, b) => a + b, 0) : 0; }, @@ -840,6 +1012,42 @@ const pinescript = { arrayMax: function(arr) { return arr && arr.length > 0 ? Math.max(...arr) : null; }, + arrayStdev: function(arr) { + if (!arr || arr.length === 0) return null; + const mean = arr.reduce((a, b) => a + b, 0) / arr.length; + const variance = arr.reduce((sum, val) => sum + Math.pow(val - mean, 2), 0) / arr.length; + return Math.sqrt(variance); + }, + arrayVariance: function(arr) { + if (!arr || arr.length === 0) return null; + const mean = arr.reduce((a, b) => a + b, 0) / arr.length; + return arr.reduce((sum, val) => sum + Math.pow(val - mean, 2), 0) / arr.length; + }, + arrayCovariance: function(arr1, arr2) { + if (!arr1 || !arr2 || arr1.length === 0 || arr1.length !== arr2.length) return null; + const mean1 = arr1.reduce((a, b) => a + b, 0) / arr1.length; + const mean2 = arr2.reduce((a, b) => a + b, 0) / arr2.length; + let cov = 0; + for (let i = 0; i < arr1.length; i++) { + cov += (arr1[i] - mean1) * (arr2[i] - mean2); + } + return cov / arr1.length; + }, + mode: function(arr) { + if (!arr || arr.length === 0) return null; + const counts = new Map(); + let maxCount = 0; + let modeVal = arr[0]; + for (const val of arr) { + const c = (counts.get(val) || 0) + 1; + counts.set(val, c); + if (c > maxCount) { + maxCount = c; + modeVal = val; + } + } + return modeVal; + }, strLength: function(str) { return str ? str.length : 0; }, @@ -895,6 +1103,84 @@ const pinescript = { strReverse: function(str) { return str ? str.split('').reverse().join('') : ''; }, + strFormat: function(formatStr, ...args) { + if (!formatStr) return ''; + return formatStr.replace(/\{(\d+)\}/g, (match, idx) => { + const i = parseInt(idx, 10); + return i < args.length ? String(args[i]) : match; + }); + }, + input: function(defval, title, tooltip, inline, group) { + return defval; + }, + inputInt: function(defval = 0, title, minval, maxval, step, tooltip, inline, group) { + return defval; + }, + inputFloat: function(defval = 0.0, title, minval, maxval, step, tooltip, inline, group) { + return defval; + }, + inputBool: function(defval = false, title, tooltip, inline, group) { + return defval; + }, + inputString: function(defval = '', title, options, tooltip, inline, group) { + return defval; + }, + inputSource: function(defval = null, title, tooltip, inline, group) { + return defval; + }, + inputColor: function(defval = '#000000', title, tooltip, inline, group) { + return defval; + }, + inputTime: function(defval = 0, title, tooltip, inline, group) { + return defval; + }, + mathSign: function(value) { + return Math.sign(value); + }, + mathAvg: function(...args) { + const valid = args.filter(v => v !== null && v !== undefined && !isNaN(v)); + return valid.length > 0 ? valid.reduce((a, b) => a + b, 0) / valid.length : null; + }, + mathSum: function(series, length) { + if (!series || series.length < length) return null; + return series.slice(-length).reduce((a, b) => a + b, 0); + }, + mathRandom: function(min = 0, max = 1) { + return min + Math.random() * (max - min); + }, + mathTodegrees: function(radians) { + return radians * (180 / Math.PI); + }, + mathToradians: function(degrees) { + return degrees * (Math.PI / 180); + }, + colorT: function(color, transparency) { + if (transparency !== undefined) { + return { color, transparency }; + } + if (color && typeof color === 'object' && 'transparency' in color) { + return color.transparency; + } + return 0; + }, + tableCellSetText: function(table, column, row, text) { + return null; + }, + tableCellSetBgcolor: function(table, column, row, bgcolor) { + return null; + }, + tableMergeCells: function(table, startColumn, startRow, endColumn, endRow) { + return null; + }, + tableDelete: function(table) { + return null; + }, + tableClear: function(table, startColumn, startRow, endColumn, endRow) { + return null; + }, + maxBarsBack: function(series, length) { + return null; + }, pi: 3.141592653589793, e: 2.718281828459045, true: true, @@ -905,8 +1191,7 @@ const pinescript = { const info = { ticker: 'AAPL', tickerid: 'NASDAQ:AAPL', prefix: 'NASDAQ', root: 'AAPL', suffix: '' }; return info[type] || ''; }, - time: 1771178051964, - timenow: 1771178051964, + timenow: 1781574527681, barstate: "LAST", dividends: {}, splits: {}, @@ -923,7 +1208,7 @@ const pinescript = { return null; }, tr: function(high, low, close) { - // Pine signature: ta.tr(true) + // Handles both the boolean shorthand ta.tr(true) and explicit high/low/close arguments if (typeof high === 'boolean') { const H = globalThis.high; const L = globalThis.low; @@ -934,7 +1219,7 @@ const pinescript = { return Math.max(H[i] - L[i], Math.abs(H[i] - prevClose), Math.abs(L[i] - prevClose)); } - // Fallback: treat inputs as scalars for current bar + // When arrays are passed, compute true range from the last bar using the previous close if (Array.isArray(close)) { const prevClose = close[close.length - 2] || close[0]; const currHigh = Array.isArray(high) ? high[high.length - 1] : high; @@ -945,6 +1230,92 @@ const pinescript = { return Math.max(high - low, Math.abs(high - close), Math.abs(low - close)); }, + arrayFirst: function(arr) { + if (!arr || arr.length === 0) return null; + return arr[0]; + }, + arrayLast: function(arr) { + if (!arr || arr.length === 0) return null; + return arr[arr.length - 1]; + }, + arrayJoin: function(arr, separator = ',') { + if (!arr) return ''; + return arr.join(separator); + }, + arrayConcat: function(arr1, arr2) { + if (!arr1) return arr2 || []; + if (!arr2) return arr1; + return arr1.concat(arr2); + }, + arrayCopy: function(arr) { + if (!arr) return []; + return [...arr]; + }, + arrayBinarySearch: function(arr, value) { + if (!arr || arr.length === 0) return -1; + let lo = 0, hi = arr.length - 1; + while (lo <= hi) { + const mid = (lo + hi) >>> 1; + if (arr[mid] === value) return mid; + if (arr[mid] < value) lo = mid + 1; + else hi = mid - 1; + } + return -1; + }, + arrayRange: function(arr) { + if (!arr || arr.length === 0) return 0; + return Math.max(...arr) - Math.min(...arr); + }, + arrayMedian: function(arr) { + if (!arr || arr.length === 0) return null; + const sorted = [...arr].sort((a, b) => a - b); + const mid = Math.floor(sorted.length / 2); + return sorted.length % 2 !== 0 ? sorted[mid] : (sorted[mid - 1] + sorted[mid]) / 2; + }, + arrayMode: function(arr) { + if (!arr || arr.length === 0) return null; + const freq = {}; + let maxCount = 0; + let mode = arr[0]; + for (const v of arr) { + freq[v] = (freq[v] || 0) + 1; + if (freq[v] > maxCount) { maxCount = freq[v]; mode = v; } + } + return mode; + }, + arrayPercentile: function(arr, percentile) { + if (!arr || arr.length === 0) return null; + const sorted = [...arr].sort((a, b) => a - b); + const idx = (percentile / 100) * (sorted.length - 1); + const lo = Math.floor(idx); + const hi = Math.ceil(idx); + if (lo === hi) return sorted[lo]; + return sorted[lo] + (sorted[hi] - sorted[lo]) * (idx - lo); + }, + arrayPercentileLinearInterpolation: function(arr, percentile) { + return builtins.get('arrayPercentile')(arr, percentile); + }, + arrayPercentileNearestRank: function(arr, percentile) { + if (!arr || arr.length === 0) return null; + const sorted = [...arr].sort((a, b) => a - b); + const idx = Math.ceil((percentile / 100) * sorted.length) - 1; + return sorted[Math.max(0, idx)]; + }, + arrayAbs: function(arr) { + if (!arr) return []; + return arr.map(v => Math.abs(v)); + }, + arrayEvery: function(arr, callback) { + if (!arr) return false; + return arr.every(callback); + }, + arraySome: function(arr, callback) { + if (!arr) return false; + return arr.some(callback); + }, + requestFinancial: function() { + return null; + }, }; @@ -1101,6 +1472,115 @@ globalThis.barmerge = globalThis.barmerge || { }; +function __pineNS(real) { + + const base = real || {}; + + return new Proxy(base, { + + get(target, key) { + + if (key in target) return target[key]; + + if (typeof key === "symbol") return target[key]; + + // Unknown member: usable as a value (null), a constant, or a no-op function. + + const noop = function() { return null; }; + + noop.toString = () => ""; + + return noop; + + } + + }); + +} + +globalThis.ta = globalThis.ta || __pineNS({}); + +globalThis.math = globalThis.math || __pineNS({ pi: Math.PI, e: Math.E, phi: 1.618033988749895, rphi: 0.618033988749895 }); + +globalThis.chart = globalThis.chart || __pineNS({ point: __pineNS({}), bg_color: null, fg_color: null }); + +globalThis.line = globalThis.line || __pineNS({ style_solid: "solid", style_dashed: "dashed", style_dotted: "dotted" }); + +globalThis.box = globalThis.box || __pineNS({}); + +globalThis.label = globalThis.label || __pineNS({ style_label_down: "label_down", style_label_up: "label_up", style_none: "none" }); + +globalThis.polyline = globalThis.polyline || __pineNS({}); + +globalThis.linefill = globalThis.linefill || __pineNS({}); + +globalThis.matrix = globalThis.matrix || __pineNS({}); + +globalThis.map = globalThis.map || __pineNS({}); + +globalThis.session = globalThis.session || __pineNS({ regular: "regular", extended: "extended" }); + +globalThis.dayofweek = globalThis.dayofweek || __pineNS({ sunday: 1, monday: 2, tuesday: 3, wednesday: 4, thursday: 5, friday: 6, saturday: 7 }); + +globalThis.timeframe = __pineNS(Object.assign(globalThis.timeframe || {}, { period: (globalThis.timeframe && globalThis.timeframe.period) || "D", isintraday: false, isdaily: true, multiplier: 1 })); + +globalThis.request = __pineNS(globalThis.request || {}); + +globalThis.input = __pineNS(globalThis.input || {}); + +globalThis.adjustment = globalThis.adjustment || __pineNS({}); + +globalThis.earnings = globalThis.earnings || __pineNS({}); + +globalThis.dividends = globalThis.dividends || __pineNS({}); + +globalThis.splits = globalThis.splits || __pineNS({}); + +globalThis.currency = globalThis.currency || __pineNS({}); + +globalThis.display = globalThis.display || __pineNS({ none: "none", all: "all", pane: "pane", data_window: "data_window", status_line: "status_line", price_scale: "price_scale" }); + +globalThis.format = globalThis.format || __pineNS({ price: "price", volume: "volume", percent: "percent", mintick: "mintick", inherit: "inherit" }); + +globalThis.scale = globalThis.scale || __pineNS({ left: "left", right: "right", none: "none" }); + +globalThis.font = globalThis.font || __pineNS({ family_default: "default", family_monospace: "monospace" }); + +globalThis.xloc = globalThis.xloc || __pineNS({ bar_index: "bar_index", bar_time: "bar_time" }); + +globalThis.yloc = globalThis.yloc || __pineNS({ price: "price", abovebar: "abovebar", belowbar: "belowbar" }); + +globalThis.extend = globalThis.extend || __pineNS({ none: "none", left: "left", right: "right", both: "both" }); + +globalThis.order = globalThis.order || __pineNS({ ascending: "ascending", descending: "descending" }); + +globalThis.alert = globalThis.alert || Object.assign(function() { return null; }, { freq_once_per_bar: "once_per_bar", freq_once_per_bar_close: "once_per_bar_close", freq_all: "all" }); + +globalThis.dayofmonth = globalThis.dayofmonth || function(t) { return new Date(t || 0).getUTCDate(); }; + +globalThis.plot = globalThis.plot || __pineNS({ style_line: "line", style_linebr: "linebr", style_stepline: "stepline", style_histogram: "histogram", style_cross: "cross", style_area: "area", style_areabr: "areabr", style_columns: "columns", style_circles: "circles" }); + +globalThis.int = globalThis.int || function(x) { return x == null ? null : Math.trunc(Number(x)); }; + +globalThis.float = globalThis.float || function(x) { return x == null ? null : Number(x); }; + +globalThis.bool = globalThis.bool || function(x) { return Boolean(x); }; + +globalThis.string = globalThis.string || function(x) { return x == null ? null : String(x); }; + +globalThis.dayofweek = globalThis.dayofweek || __pineNS({ sunday: 1, monday: 2, tuesday: 3, wednesday: 4, thursday: 5, friday: 6, saturday: 7 }); + +globalThis.str = globalThis.str || __pineNS({}); + +pinescript.math = globalThis.math; + +pinescript.ta = globalThis.ta; + +pinescript.str = globalThis.str; + +pinescript.array = __pineNS(globalThis.array); + + pinescript.color = { hex: function(s) { @@ -1190,10 +1670,10 @@ pinescript.table = { }; -// Input parameters +// Script input parameters and their defaults -// Main script logic +// The transpiled script logic, called once per bar function main() { @@ -1208,8 +1688,8 @@ function main() { null; // Study: Net Buy Volume [theUltimator5] // Options: {"overlay":false,"format":null} - let dataType = input.string("Volume", ({ title: "Data Type", options: ["Volume", "Dollars", "Delta"] })); - let priceCalc = input.string("Close", ({ title: "Price Calculation (for Dollars)", options: ["Close", "Open", "(Open + Close) / 2", "(High + Low + Open + Close) / 4", "(High + Low) / 2"] })); + let dataType = pinescript.inputString("Volume", ({ title: "Data Type", options: ["Volume", "Dollars", "Delta"] })); + let priceCalc = pinescript.inputString("Close", ({ title: "Price Calculation (for Dollars)", options: ["Close", "Open", "(Open + Close) / 2", "(High + Low + Open + Close) / 4", "(High + Low) / 2"] })); let fp = request.footprint(10000, 100); let buy = null; let sell = null; @@ -1232,8 +1712,8 @@ function main() { net = 0; netDollars = 0; } else { - net = (pinescript.offset(net, 1) + fpChange); - netDollars = (pinescript.offset(netDollars, 1) + (fpChange * price)); + net = (pinescript.hist(0, net, 1) + fpChange); + netDollars = (pinescript.hist(1, netDollars, 1) + (fpChange * price)); } let deltaColor = ((fpChange >= 0) ? pinescript.color.green : pinescript.color.red); pinescript.plot(((dataType === "Volume") ? net : null), "Net Vol", ({ color: pinescript.color.aqua, style: plot.style_stepline, linewidth: 2 })); @@ -1243,7 +1723,67 @@ function main() { } -// Export for use -export { main +// The bar-by-bar engine: runs main() once per bar over the dataset. +function run(data, options = {}) { + data = data || {}; + const inClose = (data.close || []).map(Number); + const n = inClose.length; + const inOpen = (data.open || inClose).map(Number); + const inHigh = (data.high || inClose).map(Number); + const inLow = (data.low || inClose).map(Number); + const inVol = (data.volume || new Array(n).fill(0)).map(Number); + const inTime = (data.time || inClose.map((_, i) => i)).map(Number); + + // Reset all persistent and per-run state so repeated runs are independent. + globalThis.__pineState = {}; + globalThis.__pineRuntime = { plots: {}, plotshapes: {}, alerts: [], bars: n, inputs: options.inputs || {} }; + pinescript.__rt = {}; + pinescript.__bar = 0; + + // Growing OHLCV windows: each is a SeriesArray that gains one element per bar, + // so window built-ins like sma and highest see history up to the current bar. + const O = pinescript.asSeries([]), H = pinescript.asSeries([]), L = pinescript.asSeries([]); + const C = pinescript.asSeries([]), V = pinescript.asSeries([]), T = pinescript.asSeries([]); + const HL2 = pinescript.asSeries([]), HLC3 = pinescript.asSeries([]), OHLC4 = pinescript.asSeries([]); + globalThis.open = O; globalThis.high = H; globalThis.low = L; globalThis.close = C; + globalThis.volume = V; globalThis.time = T; + globalThis.hl2 = HL2; globalThis.hlc3 = HLC3; globalThis.ohlc4 = OHLC4; + globalThis.syminfo = globalThis.syminfo || { tickerid: 'SYNTHETIC', ticker: 'SYNTHETIC', mintick: 0.01 }; + + const rt = globalThis.__pineRuntime; + for (let i = 0; i < n; i++) { + O.push(inOpen[i]); H.push(inHigh[i]); L.push(inLow[i]); C.push(inClose[i]); V.push(inVol[i]); T.push(inTime[i]); + HL2.push((inHigh[i] + inLow[i]) / 2); + HLC3.push((inHigh[i] + inLow[i] + inClose[i]) / 3); + OHLC4.push((inOpen[i] + inHigh[i] + inLow[i] + inClose[i]) / 4); + + pinescript.__bar = i; + rt.__barIndex = i; + rt.__plotIdx = 0; + rt.__shapeIdx = 0; + globalThis.bar_index = i; + globalThis.last_bar_index = n - 1; + globalThis.barstate = { + isfirst: i === 0, islast: i === n - 1, isrealtime: false, ishistory: true, + isconfirmed: true, isnew: true, islastconfirmedhistory: i === n - 1, + }; + + main(); + } + + // Backfill skipped bars so every output series has exactly n entries. + for (const k of Object.keys(rt.plots)) { + const d = rt.plots[k].data; + for (let i = 0; i < n; i++) if (d[i] === undefined) d[i] = null; + } + for (const k of Object.keys(rt.plotshapes)) { + const d = rt.plotshapes[k].data; + for (let i = 0; i < n; i++) if (d[i] === undefined) d[i] = false; + } + return rt; +} + + +export { main, run }; diff --git a/converted/Peak_Trading_Activity_Graphs_LuxAlgo.js b/converted/Peak_Trading_Activity_Graphs_LuxAlgo.js index b3f64a8..bbef0e9 100644 --- a/converted/Peak_Trading_Activity_Graphs_LuxAlgo.js +++ b/converted/Peak_Trading_Activity_Graphs_LuxAlgo.js @@ -1,12 +1,63 @@ -// PineScript to JavaScript Transpiled Code +// Auto-generated JavaScript from PineScript source -// Generated automatically +// Do not edit by hand -- re-run the converter instead -// Built-in PineScript functions +// All the built-in PineScript functions that scripts depend on at runtime const pinescript = { + __scalar: function(v) { + if (v === null || v === undefined) return v; + if (typeof v === 'object' && typeof v.valueOf === 'function') { + const s = v.valueOf(); + if (typeof s === 'number' || typeof s === 'boolean' || s === null) return s; + } + return v; + }, + series: function(id, value) { + const rt = this.__rt || (this.__rt = {}); + const store = rt.series || (rt.series = {}); + let buf = store[id]; + if (!buf) buf = store[id] = []; + buf[this.__bar | 0] = this.__scalar(value); + return buf; + }, + hist: function(id, value, n) { + const buf = this.series(id, value); + const idx = (this.__bar | 0) - (n | 0); + return idx >= 0 && idx < buf.length ? buf[idx] : null; + }, + time: function(timeframe, session) { + const t = globalThis.time; + if (t && typeof t.valueOf === 'function') return t.valueOf(); + return Array.isArray(t) ? t[t.length - 1] : t; + }, + sign: function(x) { + if (x === null || x === undefined || (typeof x === 'number' && isNaN(x))) return null; + return Math.sign(Number(x)); + }, + unpack: function(value, count) { + if (Array.isArray(value)) return value; + if (value != null && typeof value[Symbol.iterator] === 'function') return Array.from(value); + return new Array(count || 0).fill(null); + }, + alertcondition: function(condition, ...rest) { + if (globalThis.__pineRuntime) { + globalThis.__pineRuntime.alerts.push({ condition, args: rest }); + } + return null; + }, + barcolor: function(color) { return null; }, + bgcolor: function(color, title, editable, showLast) { + return null; + }, + fill: function(series1, series2, color, title, editable) { + return null; + }, + hline: function(_price, _title, _opts) { + return null; + }, sma: function(series, length) { if (series === null || series === undefined) return null; if (!Array.isArray(series)) series = [series]; @@ -84,6 +135,13 @@ const pinescript = { } return weightSum > 0 ? sum / weightSum : null; }, + swma: function(series) { + if (series === null || series === undefined) return null; + if (!Array.isArray(series)) series = [series]; + if (series.length < 4) return null; + const s = series.slice(-4); + return (s[0] * 1 + s[1] * 2 + s[2] * 2 + s[3] * 1) / 6; + }, atr: function(high, low, close, length = 14) { if (!Array.isArray(high) || !Array.isArray(low) || !Array.isArray(close)) return null; if (high.length < length + 1 || low.length < length + 1 || close.length < length + 1) return null; @@ -114,6 +172,53 @@ const pinescript = { const atr = builtins.get('atr')(high, low, close, length); return { middle: sma, upper: sma + mult * atr, lower: sma - mult * atr }; }, + supertrend: function(factor, atrPeriod, high, low, close) { + if (!Array.isArray(high) || !Array.isArray(low) || !Array.isArray(close)) return [null, null]; + if (high.length < atrPeriod + 1) return [null, null]; + const atrVal = builtins.get('atr')(high, low, close, atrPeriod); + if (atrVal === null) return [null, null]; + const lastIdx = close.length - 1; + const hl2 = (high[lastIdx] + low[lastIdx]) / 2; + const upperBand = hl2 + factor * atrVal; + const lowerBand = hl2 - factor * atrVal; + // Determine direction: 1 means downtrend (price below band), -1 means uptrend + const direction = close[lastIdx] > upperBand ? -1 : close[lastIdx] < lowerBand ? 1 : -1; + const supertrendValue = direction === -1 ? lowerBand : upperBand; + return [supertrendValue, direction]; + }, + ta_supertrend: function(factor, atrPeriod) { + const H = globalThis.high; + const L = globalThis.low; + const C = globalThis.close; + return builtins.get('supertrend')(factor, atrPeriod, H, L, C); + }, + dmi: function(diLength, adxSmoothing, high, low, close) { + if (!Array.isArray(high) || !Array.isArray(low) || !Array.isArray(close)) return { plusDI: null, minusDI: null, adx: null }; + if (high.length < diLength + 1) return { plusDI: null, minusDI: null, adx: null }; + let plusDMSum = 0, minusDMSum = 0, trSum = 0; + for (let i = 1; i <= diLength; i++) { + const idx = high.length - 1 - diLength + i; + const upMove = high[idx] - high[idx - 1]; + const downMove = low[idx - 1] - low[idx]; + plusDMSum += (upMove > downMove && upMove > 0) ? upMove : 0; + minusDMSum += (downMove > upMove && downMove > 0) ? downMove : 0; + const tr = Math.max( + high[idx] - low[idx], + Math.abs(high[idx] - close[idx - 1]), + Math.abs(low[idx] - close[idx - 1]) + ); + trSum += tr; + } + const plusDI = trSum > 0 ? (plusDMSum / trSum) * 100 : 0; + const minusDI = trSum > 0 ? (minusDMSum / trSum) * 100 : 0; + const diSum = plusDI + minusDI; + const dx = diSum > 0 ? Math.abs(plusDI - minusDI) / diSum * 100 : 0; + return { plusDI, minusDI, adx: dx }; + }, + adx: function(diLength, adxSmoothing, high, low, close) { + const result = builtins.get('dmi')(diLength, adxSmoothing, high, low, close); + return result ? result.adx : null; + }, rsi: function(close, length = 14) { if (close === null || close === undefined) return null; if (!Array.isArray(close)) close = [close]; @@ -171,9 +276,7 @@ const pinescript = { return { macd: macdLine, signal: signalLine, histogram: histogram }; }, cci: function(a, b, c, d) { - // Pine signatures: - // - ta.cci(source, length) - // - ta.cci(high, low, close, length) + // Supports both ta.cci(source, length) and ta.cci(high, low, close, length) signatures let source; let length; if (Array.isArray(a) && typeof b === 'number' && c === undefined) { @@ -304,6 +407,15 @@ const pinescript = { stdev: function(source, length) { return Math.sqrt(builtins.get('variance')(source, length)); }, + median: function(source, length) { + if (!source || source.length < length) return null; + const sorted = [...source.slice(-length)].sort((a, b) => a - b); + const mid = Math.floor(sorted.length / 2); + return sorted.length % 2 !== 0 ? sorted[mid] : (sorted[mid - 1] + sorted[mid]) / 2; + }, + ta_median: function(source, length) { + return builtins.get('median')(source, length); + }, hl2: function(high, low) { return high && low ? (high[high.length - 1] + low[low.length - 1]) / 2 : null; }, @@ -420,6 +532,24 @@ const pinescript = { return (series1[i - 1] < series2[i - 1] && series1[i] >= series2[i]) || (series1[i - 1] > series2[i - 1] && series1[i] <= series2[i]); }, + crossover: function(series1, series2) { + if (series1 === null || series1 === undefined) return false; + if (series2 === null || series2 === undefined) return false; + if (!Array.isArray(series1)) series1 = [series1]; + if (!Array.isArray(series2)) series2 = [series2]; + if (series1.length < 2 || series2.length < 2) return false; + const i = series1.length - 1; + return series1[i - 1] < series2[i - 1] && series1[i] >= series2[i]; + }, + crossunder: function(series1, series2) { + if (series1 === null || series1 === undefined) return false; + if (series2 === null || series2 === undefined) return false; + if (!Array.isArray(series1)) series1 = [series1]; + if (!Array.isArray(series2)) series2 = [series2]; + if (series1.length < 2 || series2.length < 2) return false; + const i = series1.length - 1; + return series1[i - 1] > series2[i - 1] && series1[i] <= series2[i]; + }, offset: function(series, offset) { if (!Array.isArray(series)) return null; const idx = series.length - 1 - offset; @@ -500,12 +630,14 @@ const pinescript = { return builtins.get('nz')(value, replacement); }, plot: function(series, title = '', color = null, linewidth = 1) { - globalThis.__pineRuntime.plots.push({ - series, - title, - color, - linewidth - }); + const rt = globalThis.__pineRuntime; + if (!rt) return series; + const bar = rt.__barIndex | 0; + const ord = (rt.__plotIdx = (rt.__plotIdx | 0) + 1) - 1; + const key = (title && String(title)) || ('plot_' + ord); + let p = rt.plots[key]; + if (!p) p = rt.plots[key] = { title: key, color, linewidth, data: [] }; + p.data[bar] = this.__scalar(series); return series; }, lineNew: function(x1, y1, x2, y2, opts = {}) { @@ -514,20 +646,72 @@ const pinescript = { lineDelete: function(l) { return null; }, - labelNew: function(...args) { - if (globalThis.__pineRuntime) { - globalThis.__pineRuntime.labels = globalThis.__pineRuntime.labels || []; - globalThis.__pineRuntime.labels.push({ args }); - } - return { args }; + lineSetXY: function(line, point, x, y) { + if (!line) return null; + if (point === 0 || point === 'x1') { line.x1 = x; line.y1 = y; } + else { line.x2 = x; line.y2 = y; } + return line; + }, + lineGetX: function(line, point) { + if (!line) return null; + return point === 0 || point === 'x1' ? line.x1 : line.x2; + }, + lineGetY: function(line, point) { + if (!line) return null; + return point === 0 || point === 'y1' ? line.y1 : line.y2; + }, + labelNew: function(x, y, text = '', opts = {}) { + return { x, y, text, opts, _type: 'label' }; }, labelDelete: function(l) { return null; }, + labelSetText: function(label, text) { + if (!label) return null; + label.text = text; + return label; + }, + labelGetText: function(label) { + if (!label) return ''; + return label.text || ''; + }, + boxNew: function(left, top, right, bottom, opts = {}) { + return { left, top, right, bottom, opts, _type: 'box' }; + }, + boxDelete: function(box) { + return null; + }, + boxSetLeftTop: function(box, left, top) { + if (!box) return null; + box.left = left; + box.top = top; + return box; + }, + boxSetRightBottom: function(box, right, bottom) { + if (!box) return null; + box.right = right; + box.bottom = bottom; + return box; + }, + polylineNew: function(points, opts = {}) { + return { points: points || [], opts, _type: 'polyline' }; + }, + polylineDelete: function(poly) { + return null; + }, plotshape: function(condition, ...rest) { - if (globalThis.__pineRuntime) { - globalThis.__pineRuntime.plotshapes.push({ condition, args: rest }); + const rt = globalThis.__pineRuntime; + if (!rt) return condition; + const bar = rt.__barIndex | 0; + const ord = (rt.__shapeIdx = (rt.__shapeIdx | 0) + 1) - 1; + let key = 'shape_' + ord; + for (const r of rest) { + if (typeof r === 'string') { key = r; break; } + if (r && typeof r === 'object' && r.title) { key = String(r.title); break; } } + let s = rt.plotshapes[key]; + if (!s) s = rt.plotshapes[key] = { title: key, data: [] }; + s.data[bar] = !!this.__scalar(condition); return condition; }, indicator: function(title, shorttitle, overlay, opts) { @@ -542,27 +726,12 @@ const pinescript = { } return null; }, - alertcondition: function(condition, ...rest) { - if (globalThis.__pineRuntime) { - globalThis.__pineRuntime.alerts.push({ condition, args: rest }); - } - return null; - }, plotbar: function(open, high, low, close, title, color, editable, showLast) { return { open, high, low, close }; }, plotcandle: function(open, high, low, close, title, color, wickColor, borderColor, editable, showLast) { return { open, high, low, close }; }, - hline: function(_price, _title, _opts) { - return null; - }, - bgcolor: function(color, title, editable, showLast) { - return null; - }, - fill: function(series1, series2, color, title, editable) { - return null; - }, alert: function(condition, message, frequency) { console.log('Alert:', condition ? message : 'Condition not met'); return condition; @@ -828,6 +997,9 @@ const pinescript = { arrayIndexOf: function(arr, value) { return arr ? arr.indexOf(value) : -1; }, + arrayLastIndexOf: function(arr, value) { + return arr ? arr.lastIndexOf(value) : -1; + }, arraySum: function(arr) { return arr ? arr.reduce((a, b) => a + b, 0) : 0; }, @@ -840,6 +1012,42 @@ const pinescript = { arrayMax: function(arr) { return arr && arr.length > 0 ? Math.max(...arr) : null; }, + arrayStdev: function(arr) { + if (!arr || arr.length === 0) return null; + const mean = arr.reduce((a, b) => a + b, 0) / arr.length; + const variance = arr.reduce((sum, val) => sum + Math.pow(val - mean, 2), 0) / arr.length; + return Math.sqrt(variance); + }, + arrayVariance: function(arr) { + if (!arr || arr.length === 0) return null; + const mean = arr.reduce((a, b) => a + b, 0) / arr.length; + return arr.reduce((sum, val) => sum + Math.pow(val - mean, 2), 0) / arr.length; + }, + arrayCovariance: function(arr1, arr2) { + if (!arr1 || !arr2 || arr1.length === 0 || arr1.length !== arr2.length) return null; + const mean1 = arr1.reduce((a, b) => a + b, 0) / arr1.length; + const mean2 = arr2.reduce((a, b) => a + b, 0) / arr2.length; + let cov = 0; + for (let i = 0; i < arr1.length; i++) { + cov += (arr1[i] - mean1) * (arr2[i] - mean2); + } + return cov / arr1.length; + }, + mode: function(arr) { + if (!arr || arr.length === 0) return null; + const counts = new Map(); + let maxCount = 0; + let modeVal = arr[0]; + for (const val of arr) { + const c = (counts.get(val) || 0) + 1; + counts.set(val, c); + if (c > maxCount) { + maxCount = c; + modeVal = val; + } + } + return modeVal; + }, strLength: function(str) { return str ? str.length : 0; }, @@ -895,6 +1103,84 @@ const pinescript = { strReverse: function(str) { return str ? str.split('').reverse().join('') : ''; }, + strFormat: function(formatStr, ...args) { + if (!formatStr) return ''; + return formatStr.replace(/\{(\d+)\}/g, (match, idx) => { + const i = parseInt(idx, 10); + return i < args.length ? String(args[i]) : match; + }); + }, + input: function(defval, title, tooltip, inline, group) { + return defval; + }, + inputInt: function(defval = 0, title, minval, maxval, step, tooltip, inline, group) { + return defval; + }, + inputFloat: function(defval = 0.0, title, minval, maxval, step, tooltip, inline, group) { + return defval; + }, + inputBool: function(defval = false, title, tooltip, inline, group) { + return defval; + }, + inputString: function(defval = '', title, options, tooltip, inline, group) { + return defval; + }, + inputSource: function(defval = null, title, tooltip, inline, group) { + return defval; + }, + inputColor: function(defval = '#000000', title, tooltip, inline, group) { + return defval; + }, + inputTime: function(defval = 0, title, tooltip, inline, group) { + return defval; + }, + mathSign: function(value) { + return Math.sign(value); + }, + mathAvg: function(...args) { + const valid = args.filter(v => v !== null && v !== undefined && !isNaN(v)); + return valid.length > 0 ? valid.reduce((a, b) => a + b, 0) / valid.length : null; + }, + mathSum: function(series, length) { + if (!series || series.length < length) return null; + return series.slice(-length).reduce((a, b) => a + b, 0); + }, + mathRandom: function(min = 0, max = 1) { + return min + Math.random() * (max - min); + }, + mathTodegrees: function(radians) { + return radians * (180 / Math.PI); + }, + mathToradians: function(degrees) { + return degrees * (Math.PI / 180); + }, + colorT: function(color, transparency) { + if (transparency !== undefined) { + return { color, transparency }; + } + if (color && typeof color === 'object' && 'transparency' in color) { + return color.transparency; + } + return 0; + }, + tableCellSetText: function(table, column, row, text) { + return null; + }, + tableCellSetBgcolor: function(table, column, row, bgcolor) { + return null; + }, + tableMergeCells: function(table, startColumn, startRow, endColumn, endRow) { + return null; + }, + tableDelete: function(table) { + return null; + }, + tableClear: function(table, startColumn, startRow, endColumn, endRow) { + return null; + }, + maxBarsBack: function(series, length) { + return null; + }, pi: 3.141592653589793, e: 2.718281828459045, true: true, @@ -905,8 +1191,7 @@ const pinescript = { const info = { ticker: 'AAPL', tickerid: 'NASDAQ:AAPL', prefix: 'NASDAQ', root: 'AAPL', suffix: '' }; return info[type] || ''; }, - time: 1771178052143, - timenow: 1771178052143, + timenow: 1781574527898, barstate: "LAST", dividends: {}, splits: {}, @@ -923,7 +1208,7 @@ const pinescript = { return null; }, tr: function(high, low, close) { - // Pine signature: ta.tr(true) + // Handles both the boolean shorthand ta.tr(true) and explicit high/low/close arguments if (typeof high === 'boolean') { const H = globalThis.high; const L = globalThis.low; @@ -934,7 +1219,7 @@ const pinescript = { return Math.max(H[i] - L[i], Math.abs(H[i] - prevClose), Math.abs(L[i] - prevClose)); } - // Fallback: treat inputs as scalars for current bar + // When arrays are passed, compute true range from the last bar using the previous close if (Array.isArray(close)) { const prevClose = close[close.length - 2] || close[0]; const currHigh = Array.isArray(high) ? high[high.length - 1] : high; @@ -945,6 +1230,92 @@ const pinescript = { return Math.max(high - low, Math.abs(high - close), Math.abs(low - close)); }, + arrayFirst: function(arr) { + if (!arr || arr.length === 0) return null; + return arr[0]; + }, + arrayLast: function(arr) { + if (!arr || arr.length === 0) return null; + return arr[arr.length - 1]; + }, + arrayJoin: function(arr, separator = ',') { + if (!arr) return ''; + return arr.join(separator); + }, + arrayConcat: function(arr1, arr2) { + if (!arr1) return arr2 || []; + if (!arr2) return arr1; + return arr1.concat(arr2); + }, + arrayCopy: function(arr) { + if (!arr) return []; + return [...arr]; + }, + arrayBinarySearch: function(arr, value) { + if (!arr || arr.length === 0) return -1; + let lo = 0, hi = arr.length - 1; + while (lo <= hi) { + const mid = (lo + hi) >>> 1; + if (arr[mid] === value) return mid; + if (arr[mid] < value) lo = mid + 1; + else hi = mid - 1; + } + return -1; + }, + arrayRange: function(arr) { + if (!arr || arr.length === 0) return 0; + return Math.max(...arr) - Math.min(...arr); + }, + arrayMedian: function(arr) { + if (!arr || arr.length === 0) return null; + const sorted = [...arr].sort((a, b) => a - b); + const mid = Math.floor(sorted.length / 2); + return sorted.length % 2 !== 0 ? sorted[mid] : (sorted[mid - 1] + sorted[mid]) / 2; + }, + arrayMode: function(arr) { + if (!arr || arr.length === 0) return null; + const freq = {}; + let maxCount = 0; + let mode = arr[0]; + for (const v of arr) { + freq[v] = (freq[v] || 0) + 1; + if (freq[v] > maxCount) { maxCount = freq[v]; mode = v; } + } + return mode; + }, + arrayPercentile: function(arr, percentile) { + if (!arr || arr.length === 0) return null; + const sorted = [...arr].sort((a, b) => a - b); + const idx = (percentile / 100) * (sorted.length - 1); + const lo = Math.floor(idx); + const hi = Math.ceil(idx); + if (lo === hi) return sorted[lo]; + return sorted[lo] + (sorted[hi] - sorted[lo]) * (idx - lo); + }, + arrayPercentileLinearInterpolation: function(arr, percentile) { + return builtins.get('arrayPercentile')(arr, percentile); + }, + arrayPercentileNearestRank: function(arr, percentile) { + if (!arr || arr.length === 0) return null; + const sorted = [...arr].sort((a, b) => a - b); + const idx = Math.ceil((percentile / 100) * sorted.length) - 1; + return sorted[Math.max(0, idx)]; + }, + arrayAbs: function(arr) { + if (!arr) return []; + return arr.map(v => Math.abs(v)); + }, + arrayEvery: function(arr, callback) { + if (!arr) return false; + return arr.every(callback); + }, + arraySome: function(arr, callback) { + if (!arr) return false; + return arr.some(callback); + }, + requestFinancial: function() { + return null; + }, }; @@ -1101,6 +1472,115 @@ globalThis.barmerge = globalThis.barmerge || { }; +function __pineNS(real) { + + const base = real || {}; + + return new Proxy(base, { + + get(target, key) { + + if (key in target) return target[key]; + + if (typeof key === "symbol") return target[key]; + + // Unknown member: usable as a value (null), a constant, or a no-op function. + + const noop = function() { return null; }; + + noop.toString = () => ""; + + return noop; + + } + + }); + +} + +globalThis.ta = globalThis.ta || __pineNS({}); + +globalThis.math = globalThis.math || __pineNS({ pi: Math.PI, e: Math.E, phi: 1.618033988749895, rphi: 0.618033988749895 }); + +globalThis.chart = globalThis.chart || __pineNS({ point: __pineNS({}), bg_color: null, fg_color: null }); + +globalThis.line = globalThis.line || __pineNS({ style_solid: "solid", style_dashed: "dashed", style_dotted: "dotted" }); + +globalThis.box = globalThis.box || __pineNS({}); + +globalThis.label = globalThis.label || __pineNS({ style_label_down: "label_down", style_label_up: "label_up", style_none: "none" }); + +globalThis.polyline = globalThis.polyline || __pineNS({}); + +globalThis.linefill = globalThis.linefill || __pineNS({}); + +globalThis.matrix = globalThis.matrix || __pineNS({}); + +globalThis.map = globalThis.map || __pineNS({}); + +globalThis.session = globalThis.session || __pineNS({ regular: "regular", extended: "extended" }); + +globalThis.dayofweek = globalThis.dayofweek || __pineNS({ sunday: 1, monday: 2, tuesday: 3, wednesday: 4, thursday: 5, friday: 6, saturday: 7 }); + +globalThis.timeframe = __pineNS(Object.assign(globalThis.timeframe || {}, { period: (globalThis.timeframe && globalThis.timeframe.period) || "D", isintraday: false, isdaily: true, multiplier: 1 })); + +globalThis.request = __pineNS(globalThis.request || {}); + +globalThis.input = __pineNS(globalThis.input || {}); + +globalThis.adjustment = globalThis.adjustment || __pineNS({}); + +globalThis.earnings = globalThis.earnings || __pineNS({}); + +globalThis.dividends = globalThis.dividends || __pineNS({}); + +globalThis.splits = globalThis.splits || __pineNS({}); + +globalThis.currency = globalThis.currency || __pineNS({}); + +globalThis.display = globalThis.display || __pineNS({ none: "none", all: "all", pane: "pane", data_window: "data_window", status_line: "status_line", price_scale: "price_scale" }); + +globalThis.format = globalThis.format || __pineNS({ price: "price", volume: "volume", percent: "percent", mintick: "mintick", inherit: "inherit" }); + +globalThis.scale = globalThis.scale || __pineNS({ left: "left", right: "right", none: "none" }); + +globalThis.font = globalThis.font || __pineNS({ family_default: "default", family_monospace: "monospace" }); + +globalThis.xloc = globalThis.xloc || __pineNS({ bar_index: "bar_index", bar_time: "bar_time" }); + +globalThis.yloc = globalThis.yloc || __pineNS({ price: "price", abovebar: "abovebar", belowbar: "belowbar" }); + +globalThis.extend = globalThis.extend || __pineNS({ none: "none", left: "left", right: "right", both: "both" }); + +globalThis.order = globalThis.order || __pineNS({ ascending: "ascending", descending: "descending" }); + +globalThis.alert = globalThis.alert || Object.assign(function() { return null; }, { freq_once_per_bar: "once_per_bar", freq_once_per_bar_close: "once_per_bar_close", freq_all: "all" }); + +globalThis.dayofmonth = globalThis.dayofmonth || function(t) { return new Date(t || 0).getUTCDate(); }; + +globalThis.plot = globalThis.plot || __pineNS({ style_line: "line", style_linebr: "linebr", style_stepline: "stepline", style_histogram: "histogram", style_cross: "cross", style_area: "area", style_areabr: "areabr", style_columns: "columns", style_circles: "circles" }); + +globalThis.int = globalThis.int || function(x) { return x == null ? null : Math.trunc(Number(x)); }; + +globalThis.float = globalThis.float || function(x) { return x == null ? null : Number(x); }; + +globalThis.bool = globalThis.bool || function(x) { return Boolean(x); }; + +globalThis.string = globalThis.string || function(x) { return x == null ? null : String(x); }; + +globalThis.dayofweek = globalThis.dayofweek || __pineNS({ sunday: 1, monday: 2, tuesday: 3, wednesday: 4, thursday: 5, friday: 6, saturday: 7 }); + +globalThis.str = globalThis.str || __pineNS({}); + +pinescript.math = globalThis.math; + +pinescript.ta = globalThis.ta; + +pinescript.str = globalThis.str; + +pinescript.array = __pineNS(globalThis.array); + + pinescript.color = { hex: function(s) { @@ -1190,11 +1670,11 @@ pinescript.table = { }; -// Input parameters +// Script input parameters and their defaults const data = { new: function(values, tag) { return { values: values, tag: tag }; } }; -// Main script logic +// The transpiled script logic, called once per bar function main() { @@ -1251,17 +1731,17 @@ function main() { let dashboardSizeTooltip = "Select the graph text size."; let dashboardWidthTooltip = "Select the graph width."; let dashboardHeightTooltip = "Select the graph height."; - let baseDataInput = input.string(VOLUME, "Data", ({ options: [VOLATILITY, VOLUME] })); - let periodInput = input.string(DAYOFMONTH, "Period", ({ options: [MONTH, DAYOFMONTH, DAYOFWEEK, HOURS] })); - let deltaInput = input.bool(false, "Display Delta Between Medians", ({ tooltip: deltaTooltip })); - let dashboardPositionInput = input.string(TOP_RIGHT, "Position", ({ group: DASHBOARD_GROUP, tooltip: dashboardPositionTooltip, options: [TOP_RIGHT, BOTTOM_RIGHT, BOTTOM_LEFT] })); - let dashboardSizeInput = input.string(SMALL, "Size", ({ group: DASHBOARD_GROUP, tooltip: dashboardSizeTooltip, options: [TINY, SMALL, NORMAL, LARGE, HUGE] })); - let dashboardWidthInput = input.float(1, "Width", ({ group: DASHBOARD_GROUP, tooltip: dashboardWidthTooltip, minval: 1, step: 0.25 })); - let dashboardHeightInput = input.float(1, "Height", ({ group: DASHBOARD_GROUP, tooltip: dashboardHeightTooltip, minval: 1, step: 0.25 })); - let colormapInput = input.string(VIRIDIS, "Colors", ({ group: STYLE_GROUP, options: [VIRIDIS, PLASMA, MAGMA, CUSTOM] })); - let coldColorInput = input.color(RED, "Custom Cold", ({ group: STYLE_GROUP })); - let lukewarmColorInput = input.color(YELLOW, "Custom Lukewarm", ({ group: STYLE_GROUP })); - let hotColorInput = input.color(GREEN, "Custom Hot", ({ group: STYLE_GROUP })); + let baseDataInput = pinescript.inputString(VOLUME, "Data", ({ options: [VOLATILITY, VOLUME] })); + let periodInput = pinescript.inputString(DAYOFMONTH, "Period", ({ options: [MONTH, DAYOFMONTH, DAYOFWEEK, HOURS] })); + let deltaInput = pinescript.inputBool(false, "Display Delta Between Medians", ({ tooltip: deltaTooltip })); + let dashboardPositionInput = pinescript.inputString(TOP_RIGHT, "Position", ({ group: DASHBOARD_GROUP, tooltip: dashboardPositionTooltip, options: [TOP_RIGHT, BOTTOM_RIGHT, BOTTOM_LEFT] })); + let dashboardSizeInput = pinescript.inputString(SMALL, "Size", ({ group: DASHBOARD_GROUP, tooltip: dashboardSizeTooltip, options: [TINY, SMALL, NORMAL, LARGE, HUGE] })); + let dashboardWidthInput = pinescript.inputFloat(1, "Width", ({ group: DASHBOARD_GROUP, tooltip: dashboardWidthTooltip, minval: 1, step: 0.25 })); + let dashboardHeightInput = pinescript.inputFloat(1, "Height", ({ group: DASHBOARD_GROUP, tooltip: dashboardHeightTooltip, minval: 1, step: 0.25 })); + let colormapInput = pinescript.inputString(VIRIDIS, "Colors", ({ group: STYLE_GROUP, options: [VIRIDIS, PLASMA, MAGMA, CUSTOM] })); + let coldColorInput = pinescript.inputColor(RED, "Custom Cold", ({ group: STYLE_GROUP })); + let lukewarmColorInput = pinescript.inputColor(YELLOW, "Custom Lukewarm", ({ group: STYLE_GROUP })); + let hotColorInput = pinescript.inputColor(GREEN, "Custom Hot", ({ group: STYLE_GROUP })); if (state.fullMonth === undefined) state.fullMonth = array.from(data.new(pinescript.arrayNew()), data.new(pinescript.arrayNew()), data.new(pinescript.arrayNew()), data.new(pinescript.arrayNew()), data.new(pinescript.arrayNew()), data.new(pinescript.arrayNew()), data.new(pinescript.arrayNew()), data.new(pinescript.arrayNew()), data.new(pinescript.arrayNew()), data.new(pinescript.arrayNew()), data.new(pinescript.arrayNew()), data.new(pinescript.arrayNew()), data.new(pinescript.arrayNew()), data.new(pinescript.arrayNew()), data.new(pinescript.arrayNew()), data.new(pinescript.arrayNew()), data.new(pinescript.arrayNew()), data.new(pinescript.arrayNew()), data.new(pinescript.arrayNew()), data.new(pinescript.arrayNew()), data.new(pinescript.arrayNew()), data.new(pinescript.arrayNew()), data.new(pinescript.arrayNew()), data.new(pinescript.arrayNew()), data.new(pinescript.arrayNew()), data.new(pinescript.arrayNew()), data.new(pinescript.arrayNew()), data.new(pinescript.arrayNew()), data.new(pinescript.arrayNew()), data.new(pinescript.arrayNew()), data.new(pinescript.arrayNew())); if (state.fullWeek === undefined) state.fullWeek = ((syminfo.type === "crypto") ? array.from(data.new(pinescript.arrayNew(), "SUN"), data.new(pinescript.arrayNew(), "MON"), data.new(pinescript.arrayNew(), "TUE"), data.new(pinescript.arrayNew(), "WED"), data.new(pinescript.arrayNew(), "THU"), data.new(pinescript.arrayNew(), "FRI"), data.new(pinescript.arrayNew(), "SAT")) : array.from(data.new(pinescript.arrayNew(), "MON"), data.new(pinescript.arrayNew(), "TUE"), data.new(pinescript.arrayNew(), "WED"), data.new(pinescript.arrayNew(), "THU"), data.new(pinescript.arrayNew(), "FRI"))); if (state.fullYear === undefined) state.fullYear = array.from(data.new(pinescript.arrayNew(), "JAN"), data.new(pinescript.arrayNew(), "FEB"), data.new(pinescript.arrayNew(), "MAR"), data.new(pinescript.arrayNew(), "APR"), data.new(pinescript.arrayNew(), "MAY"), data.new(pinescript.arrayNew(), "JUN"), data.new(pinescript.arrayNew(), "JUL"), data.new(pinescript.arrayNew(), "AUG"), data.new(pinescript.arrayNew(), "SEP"), data.new(pinescript.arrayNew(), "OCT"), data.new(pinescript.arrayNew(), "NOV"), data.new(pinescript.arrayNew(), "DEC")); @@ -1288,34 +1768,34 @@ function main() { [COLD_MAGMA, LUKEWARM_MAGMA, HOT_MAGMA]; [COLD_PLASMA, LUKEWARM_PLASMA, HOT_VIRIDIS]; [COLD_VIRIDIS, LUKEWARM_VIRIDIS, HOT_VIRIDIS]; - let [cold, lukewarm, hot] = ((colormapInput === VIRIDIS) ? undefined : ((colormapInput === PLASMA) ? undefined : ((colormapInput === MAGMA) ? undefined : ((colormapInput === CUSTOM) ? undefined : null)))); + let [cold, lukewarm, hot] = pinescript.unpack(((colormapInput === VIRIDIS) ? undefined : ((colormapInput === PLASMA) ? undefined : ((colormapInput === MAGMA) ? undefined : ((colormapInput === CUSTOM) ? undefined : null)))), 3); function error() { if (state.t_able === undefined) state.t_able = pinescript.table.new(pinescript.position.top_center, 1, 1, ({ bgcolor: ORANGE_LIGHT, border_color: ORANGE, border_width: 1, frame_color: ORANGE, frame_width: 1 })); return t_able.cell(0, 0, "⚠ Warning: The tool will not display the dashboard on higher timeframes.nPlease select a chart timeframe that is equal to or lower than the period parameter in the settings panel.", ({ text_color: ORANGE, text_size: 0 })); } function gatherDaysOfWeek(output) { - let [dailyData, currentDay] = pinescript.requestSecurity(syminfo.tickerid, "1D", [output, parsedDayofweek]); + let [dailyData, currentDay] = pinescript.unpack(pinescript.requestSecurity(syminfo.tickerid, "1D", [output, parsedDayofweek]), 2); let current = fullWeek.get((currentDay - 1)); if (!pinescript.na(current)) { current.values.push(pinescript.nz(dailyData)); } } function gatherMonths(output) { - let [montlhyData, currentMonth] = pinescript.requestSecurity(syminfo.tickerid, "1M", [output, parsedMonth]); + let [montlhyData, currentMonth] = pinescript.unpack(pinescript.requestSecurity(syminfo.tickerid, "1M", [output, parsedMonth]), 2); let current = fullYear.get((currentMonth - 1)); if (!pinescript.na(current)) { current.values.push(pinescript.nz(montlhyData)); } } function gatherDayOfMonth(output) { - let [dailyData, dayOfMonth] = pinescript.requestSecurity(syminfo.tickerid, "1D", [output, parsedDayofmonth]); + let [dailyData, dayOfMonth] = pinescript.unpack(pinescript.requestSecurity(syminfo.tickerid, "1D", [output, parsedDayofmonth]), 2); let current = fullMonth.get((dayOfMonth - 1)); if (!pinescript.na(current)) { current.values.push(pinescript.nz(dailyData)); } } function gatherHours(output) { - let [hourlyData, currentHour] = pinescript.requestSecurity(syminfo.tickerid, "60", [output, hour]); + let [hourlyData, currentHour] = pinescript.unpack(pinescript.requestSecurity(syminfo.tickerid, "60", [output, hour]), 2); let current = fullDay.get(currentHour); if (!pinescript.na(current)) { current.values.push(pinescript.nz(hourlyData)); @@ -1369,7 +1849,7 @@ function main() { "Monthly"; let periodTag = ((periodInput === MONTH) ? undefined : ((periodInput === HOURS) ? undefined : undefined)); let header = (deltaInput ? "(Delta)" : ""); - cell(state.t_able, 0, 0, str.format("{0} Median {1} {2}nMin: {3} Max: {4}", periodTag, baseDataInput, header, min, max), ({ align: pinescript.text.align_center, color: DATA })); + cell(state.t_able, 0, 0, pinescript.strFormat("{0} Median {1} {2}nMin: {3} Max: {4}", periodTag, baseDataInput, header, min, max), ({ align: pinescript.text.align_center, color: DATA })); let column = 0; for (const [index, eachDataPoint] of state.parsedData) { if ((index > 0)) { @@ -1378,7 +1858,7 @@ function main() { t_able.merge_cells(column, 2, column, 103); t_able.cell_set_height(column, 2, (0.2 * dashboardHeightInput)); let scale = (deltaInput ? ((eachDataPoint - parsedData.min()) / parsedData.range()) : (eachDataPoint / parsedData.max())); - let value = pinescript.max(1, math.floor((100 * scale))); + let value = pinescript.max(1, pinescript.floor((100 * scale))); let customColor = ((value >= 50) ? pinescript.color.from_gradient(value, 50, 100, pinescript.color.new(lukewarm, 0), pinescript.color.new(hot, 0)) : pinescript.color.from_gradient(value, 1, 50, pinescript.color.new(cold, 0), pinescript.color.new(lukewarm, 0))); let topRow = (101 - value); for (let row = 1; row <= 100; row++) { @@ -1408,7 +1888,7 @@ function main() { "Monthly"; let periodTag = ((periodInput === MONTH) ? undefined : ((periodInput === HOURS) ? undefined : undefined)); let header = (deltaInput ? "(Delta)" : ""); - cell(state.t_able, 0, 0, str.format("{0} Median {1} {2}nMax: {3}nMin: {4}", periodTag, baseDataInput, header, max, min), ({ align: pinescript.text.align_center, color: DATA })); + cell(state.t_able, 0, 0, pinescript.strFormat("{0} Median {1} {2}nMax: {3}nMin: {4}", periodTag, baseDataInput, header, max, min), ({ align: pinescript.text.align_center, color: DATA })); divider(state.t_able, 1, 6); let row = 2; let column0 = array.from(1, 8, 15, 22, 29); @@ -1436,7 +1916,7 @@ function main() { nextRow = true; } let scale = (deltaInput ? ((eachDataPoint - parsedData.min()) / parsedData.range()) : (eachDataPoint / parsedData.max())); - let value = pinescript.max(1, math.floor((100 * scale))); + let value = pinescript.max(1, pinescript.floor((100 * scale))); let customColor = ((value >= 50) ? pinescript.color.from_gradient(value, 50, 100, pinescript.color.new(lukewarm, 0), pinescript.color.new(hot, 0)) : pinescript.color.from_gradient(value, 1, 50, pinescript.color.new(cold, 0), pinescript.color.new(lukewarm, 0))); cell(state.t_able, column, row, pinescript.strToString((index + 1)), ({ background: customColor })); } @@ -1476,7 +1956,67 @@ function main() { } -// Export for use -export { main +// The bar-by-bar engine: runs main() once per bar over the dataset. +function run(data, options = {}) { + data = data || {}; + const inClose = (data.close || []).map(Number); + const n = inClose.length; + const inOpen = (data.open || inClose).map(Number); + const inHigh = (data.high || inClose).map(Number); + const inLow = (data.low || inClose).map(Number); + const inVol = (data.volume || new Array(n).fill(0)).map(Number); + const inTime = (data.time || inClose.map((_, i) => i)).map(Number); + + // Reset all persistent and per-run state so repeated runs are independent. + globalThis.__pineState = {}; + globalThis.__pineRuntime = { plots: {}, plotshapes: {}, alerts: [], bars: n, inputs: options.inputs || {} }; + pinescript.__rt = {}; + pinescript.__bar = 0; + + // Growing OHLCV windows: each is a SeriesArray that gains one element per bar, + // so window built-ins like sma and highest see history up to the current bar. + const O = pinescript.asSeries([]), H = pinescript.asSeries([]), L = pinescript.asSeries([]); + const C = pinescript.asSeries([]), V = pinescript.asSeries([]), T = pinescript.asSeries([]); + const HL2 = pinescript.asSeries([]), HLC3 = pinescript.asSeries([]), OHLC4 = pinescript.asSeries([]); + globalThis.open = O; globalThis.high = H; globalThis.low = L; globalThis.close = C; + globalThis.volume = V; globalThis.time = T; + globalThis.hl2 = HL2; globalThis.hlc3 = HLC3; globalThis.ohlc4 = OHLC4; + globalThis.syminfo = globalThis.syminfo || { tickerid: 'SYNTHETIC', ticker: 'SYNTHETIC', mintick: 0.01 }; + + const rt = globalThis.__pineRuntime; + for (let i = 0; i < n; i++) { + O.push(inOpen[i]); H.push(inHigh[i]); L.push(inLow[i]); C.push(inClose[i]); V.push(inVol[i]); T.push(inTime[i]); + HL2.push((inHigh[i] + inLow[i]) / 2); + HLC3.push((inHigh[i] + inLow[i] + inClose[i]) / 3); + OHLC4.push((inOpen[i] + inHigh[i] + inLow[i] + inClose[i]) / 4); + + pinescript.__bar = i; + rt.__barIndex = i; + rt.__plotIdx = 0; + rt.__shapeIdx = 0; + globalThis.bar_index = i; + globalThis.last_bar_index = n - 1; + globalThis.barstate = { + isfirst: i === 0, islast: i === n - 1, isrealtime: false, ishistory: true, + isconfirmed: true, isnew: true, islastconfirmedhistory: i === n - 1, + }; + + main(); + } + + // Backfill skipped bars so every output series has exactly n entries. + for (const k of Object.keys(rt.plots)) { + const d = rt.plots[k].data; + for (let i = 0; i < n; i++) if (d[i] === undefined) d[i] = null; + } + for (const k of Object.keys(rt.plotshapes)) { + const d = rt.plotshapes[k].data; + for (let i = 0; i < n; i++) if (d[i] === undefined) d[i] = false; + } + return rt; +} + + +export { main, run }; diff --git a/converted/Pro_Pattern_Long-Short_High_Winrate.js b/converted/Pro_Pattern_Long-Short_High_Winrate.js index 8fb586a..8a8e03e 100644 --- a/converted/Pro_Pattern_Long-Short_High_Winrate.js +++ b/converted/Pro_Pattern_Long-Short_High_Winrate.js @@ -1,12 +1,63 @@ -// PineScript to JavaScript Transpiled Code +// Auto-generated JavaScript from PineScript source -// Generated automatically +// Do not edit by hand -- re-run the converter instead -// Built-in PineScript functions +// All the built-in PineScript functions that scripts depend on at runtime const pinescript = { + __scalar: function(v) { + if (v === null || v === undefined) return v; + if (typeof v === 'object' && typeof v.valueOf === 'function') { + const s = v.valueOf(); + if (typeof s === 'number' || typeof s === 'boolean' || s === null) return s; + } + return v; + }, + series: function(id, value) { + const rt = this.__rt || (this.__rt = {}); + const store = rt.series || (rt.series = {}); + let buf = store[id]; + if (!buf) buf = store[id] = []; + buf[this.__bar | 0] = this.__scalar(value); + return buf; + }, + hist: function(id, value, n) { + const buf = this.series(id, value); + const idx = (this.__bar | 0) - (n | 0); + return idx >= 0 && idx < buf.length ? buf[idx] : null; + }, + time: function(timeframe, session) { + const t = globalThis.time; + if (t && typeof t.valueOf === 'function') return t.valueOf(); + return Array.isArray(t) ? t[t.length - 1] : t; + }, + sign: function(x) { + if (x === null || x === undefined || (typeof x === 'number' && isNaN(x))) return null; + return Math.sign(Number(x)); + }, + unpack: function(value, count) { + if (Array.isArray(value)) return value; + if (value != null && typeof value[Symbol.iterator] === 'function') return Array.from(value); + return new Array(count || 0).fill(null); + }, + alertcondition: function(condition, ...rest) { + if (globalThis.__pineRuntime) { + globalThis.__pineRuntime.alerts.push({ condition, args: rest }); + } + return null; + }, + barcolor: function(color) { return null; }, + bgcolor: function(color, title, editable, showLast) { + return null; + }, + fill: function(series1, series2, color, title, editable) { + return null; + }, + hline: function(_price, _title, _opts) { + return null; + }, sma: function(series, length) { if (series === null || series === undefined) return null; if (!Array.isArray(series)) series = [series]; @@ -84,6 +135,13 @@ const pinescript = { } return weightSum > 0 ? sum / weightSum : null; }, + swma: function(series) { + if (series === null || series === undefined) return null; + if (!Array.isArray(series)) series = [series]; + if (series.length < 4) return null; + const s = series.slice(-4); + return (s[0] * 1 + s[1] * 2 + s[2] * 2 + s[3] * 1) / 6; + }, atr: function(high, low, close, length = 14) { if (!Array.isArray(high) || !Array.isArray(low) || !Array.isArray(close)) return null; if (high.length < length + 1 || low.length < length + 1 || close.length < length + 1) return null; @@ -114,6 +172,53 @@ const pinescript = { const atr = builtins.get('atr')(high, low, close, length); return { middle: sma, upper: sma + mult * atr, lower: sma - mult * atr }; }, + supertrend: function(factor, atrPeriod, high, low, close) { + if (!Array.isArray(high) || !Array.isArray(low) || !Array.isArray(close)) return [null, null]; + if (high.length < atrPeriod + 1) return [null, null]; + const atrVal = builtins.get('atr')(high, low, close, atrPeriod); + if (atrVal === null) return [null, null]; + const lastIdx = close.length - 1; + const hl2 = (high[lastIdx] + low[lastIdx]) / 2; + const upperBand = hl2 + factor * atrVal; + const lowerBand = hl2 - factor * atrVal; + // Determine direction: 1 means downtrend (price below band), -1 means uptrend + const direction = close[lastIdx] > upperBand ? -1 : close[lastIdx] < lowerBand ? 1 : -1; + const supertrendValue = direction === -1 ? lowerBand : upperBand; + return [supertrendValue, direction]; + }, + ta_supertrend: function(factor, atrPeriod) { + const H = globalThis.high; + const L = globalThis.low; + const C = globalThis.close; + return builtins.get('supertrend')(factor, atrPeriod, H, L, C); + }, + dmi: function(diLength, adxSmoothing, high, low, close) { + if (!Array.isArray(high) || !Array.isArray(low) || !Array.isArray(close)) return { plusDI: null, minusDI: null, adx: null }; + if (high.length < diLength + 1) return { plusDI: null, minusDI: null, adx: null }; + let plusDMSum = 0, minusDMSum = 0, trSum = 0; + for (let i = 1; i <= diLength; i++) { + const idx = high.length - 1 - diLength + i; + const upMove = high[idx] - high[idx - 1]; + const downMove = low[idx - 1] - low[idx]; + plusDMSum += (upMove > downMove && upMove > 0) ? upMove : 0; + minusDMSum += (downMove > upMove && downMove > 0) ? downMove : 0; + const tr = Math.max( + high[idx] - low[idx], + Math.abs(high[idx] - close[idx - 1]), + Math.abs(low[idx] - close[idx - 1]) + ); + trSum += tr; + } + const plusDI = trSum > 0 ? (plusDMSum / trSum) * 100 : 0; + const minusDI = trSum > 0 ? (minusDMSum / trSum) * 100 : 0; + const diSum = plusDI + minusDI; + const dx = diSum > 0 ? Math.abs(plusDI - minusDI) / diSum * 100 : 0; + return { plusDI, minusDI, adx: dx }; + }, + adx: function(diLength, adxSmoothing, high, low, close) { + const result = builtins.get('dmi')(diLength, adxSmoothing, high, low, close); + return result ? result.adx : null; + }, rsi: function(close, length = 14) { if (close === null || close === undefined) return null; if (!Array.isArray(close)) close = [close]; @@ -171,9 +276,7 @@ const pinescript = { return { macd: macdLine, signal: signalLine, histogram: histogram }; }, cci: function(a, b, c, d) { - // Pine signatures: - // - ta.cci(source, length) - // - ta.cci(high, low, close, length) + // Supports both ta.cci(source, length) and ta.cci(high, low, close, length) signatures let source; let length; if (Array.isArray(a) && typeof b === 'number' && c === undefined) { @@ -304,6 +407,15 @@ const pinescript = { stdev: function(source, length) { return Math.sqrt(builtins.get('variance')(source, length)); }, + median: function(source, length) { + if (!source || source.length < length) return null; + const sorted = [...source.slice(-length)].sort((a, b) => a - b); + const mid = Math.floor(sorted.length / 2); + return sorted.length % 2 !== 0 ? sorted[mid] : (sorted[mid - 1] + sorted[mid]) / 2; + }, + ta_median: function(source, length) { + return builtins.get('median')(source, length); + }, hl2: function(high, low) { return high && low ? (high[high.length - 1] + low[low.length - 1]) / 2 : null; }, @@ -420,6 +532,24 @@ const pinescript = { return (series1[i - 1] < series2[i - 1] && series1[i] >= series2[i]) || (series1[i - 1] > series2[i - 1] && series1[i] <= series2[i]); }, + crossover: function(series1, series2) { + if (series1 === null || series1 === undefined) return false; + if (series2 === null || series2 === undefined) return false; + if (!Array.isArray(series1)) series1 = [series1]; + if (!Array.isArray(series2)) series2 = [series2]; + if (series1.length < 2 || series2.length < 2) return false; + const i = series1.length - 1; + return series1[i - 1] < series2[i - 1] && series1[i] >= series2[i]; + }, + crossunder: function(series1, series2) { + if (series1 === null || series1 === undefined) return false; + if (series2 === null || series2 === undefined) return false; + if (!Array.isArray(series1)) series1 = [series1]; + if (!Array.isArray(series2)) series2 = [series2]; + if (series1.length < 2 || series2.length < 2) return false; + const i = series1.length - 1; + return series1[i - 1] > series2[i - 1] && series1[i] <= series2[i]; + }, offset: function(series, offset) { if (!Array.isArray(series)) return null; const idx = series.length - 1 - offset; @@ -500,12 +630,14 @@ const pinescript = { return builtins.get('nz')(value, replacement); }, plot: function(series, title = '', color = null, linewidth = 1) { - globalThis.__pineRuntime.plots.push({ - series, - title, - color, - linewidth - }); + const rt = globalThis.__pineRuntime; + if (!rt) return series; + const bar = rt.__barIndex | 0; + const ord = (rt.__plotIdx = (rt.__plotIdx | 0) + 1) - 1; + const key = (title && String(title)) || ('plot_' + ord); + let p = rt.plots[key]; + if (!p) p = rt.plots[key] = { title: key, color, linewidth, data: [] }; + p.data[bar] = this.__scalar(series); return series; }, lineNew: function(x1, y1, x2, y2, opts = {}) { @@ -514,20 +646,72 @@ const pinescript = { lineDelete: function(l) { return null; }, - labelNew: function(...args) { - if (globalThis.__pineRuntime) { - globalThis.__pineRuntime.labels = globalThis.__pineRuntime.labels || []; - globalThis.__pineRuntime.labels.push({ args }); - } - return { args }; + lineSetXY: function(line, point, x, y) { + if (!line) return null; + if (point === 0 || point === 'x1') { line.x1 = x; line.y1 = y; } + else { line.x2 = x; line.y2 = y; } + return line; + }, + lineGetX: function(line, point) { + if (!line) return null; + return point === 0 || point === 'x1' ? line.x1 : line.x2; + }, + lineGetY: function(line, point) { + if (!line) return null; + return point === 0 || point === 'y1' ? line.y1 : line.y2; + }, + labelNew: function(x, y, text = '', opts = {}) { + return { x, y, text, opts, _type: 'label' }; }, labelDelete: function(l) { return null; }, + labelSetText: function(label, text) { + if (!label) return null; + label.text = text; + return label; + }, + labelGetText: function(label) { + if (!label) return ''; + return label.text || ''; + }, + boxNew: function(left, top, right, bottom, opts = {}) { + return { left, top, right, bottom, opts, _type: 'box' }; + }, + boxDelete: function(box) { + return null; + }, + boxSetLeftTop: function(box, left, top) { + if (!box) return null; + box.left = left; + box.top = top; + return box; + }, + boxSetRightBottom: function(box, right, bottom) { + if (!box) return null; + box.right = right; + box.bottom = bottom; + return box; + }, + polylineNew: function(points, opts = {}) { + return { points: points || [], opts, _type: 'polyline' }; + }, + polylineDelete: function(poly) { + return null; + }, plotshape: function(condition, ...rest) { - if (globalThis.__pineRuntime) { - globalThis.__pineRuntime.plotshapes.push({ condition, args: rest }); + const rt = globalThis.__pineRuntime; + if (!rt) return condition; + const bar = rt.__barIndex | 0; + const ord = (rt.__shapeIdx = (rt.__shapeIdx | 0) + 1) - 1; + let key = 'shape_' + ord; + for (const r of rest) { + if (typeof r === 'string') { key = r; break; } + if (r && typeof r === 'object' && r.title) { key = String(r.title); break; } } + let s = rt.plotshapes[key]; + if (!s) s = rt.plotshapes[key] = { title: key, data: [] }; + s.data[bar] = !!this.__scalar(condition); return condition; }, indicator: function(title, shorttitle, overlay, opts) { @@ -542,27 +726,12 @@ const pinescript = { } return null; }, - alertcondition: function(condition, ...rest) { - if (globalThis.__pineRuntime) { - globalThis.__pineRuntime.alerts.push({ condition, args: rest }); - } - return null; - }, plotbar: function(open, high, low, close, title, color, editable, showLast) { return { open, high, low, close }; }, plotcandle: function(open, high, low, close, title, color, wickColor, borderColor, editable, showLast) { return { open, high, low, close }; }, - hline: function(_price, _title, _opts) { - return null; - }, - bgcolor: function(color, title, editable, showLast) { - return null; - }, - fill: function(series1, series2, color, title, editable) { - return null; - }, alert: function(condition, message, frequency) { console.log('Alert:', condition ? message : 'Condition not met'); return condition; @@ -828,6 +997,9 @@ const pinescript = { arrayIndexOf: function(arr, value) { return arr ? arr.indexOf(value) : -1; }, + arrayLastIndexOf: function(arr, value) { + return arr ? arr.lastIndexOf(value) : -1; + }, arraySum: function(arr) { return arr ? arr.reduce((a, b) => a + b, 0) : 0; }, @@ -840,6 +1012,42 @@ const pinescript = { arrayMax: function(arr) { return arr && arr.length > 0 ? Math.max(...arr) : null; }, + arrayStdev: function(arr) { + if (!arr || arr.length === 0) return null; + const mean = arr.reduce((a, b) => a + b, 0) / arr.length; + const variance = arr.reduce((sum, val) => sum + Math.pow(val - mean, 2), 0) / arr.length; + return Math.sqrt(variance); + }, + arrayVariance: function(arr) { + if (!arr || arr.length === 0) return null; + const mean = arr.reduce((a, b) => a + b, 0) / arr.length; + return arr.reduce((sum, val) => sum + Math.pow(val - mean, 2), 0) / arr.length; + }, + arrayCovariance: function(arr1, arr2) { + if (!arr1 || !arr2 || arr1.length === 0 || arr1.length !== arr2.length) return null; + const mean1 = arr1.reduce((a, b) => a + b, 0) / arr1.length; + const mean2 = arr2.reduce((a, b) => a + b, 0) / arr2.length; + let cov = 0; + for (let i = 0; i < arr1.length; i++) { + cov += (arr1[i] - mean1) * (arr2[i] - mean2); + } + return cov / arr1.length; + }, + mode: function(arr) { + if (!arr || arr.length === 0) return null; + const counts = new Map(); + let maxCount = 0; + let modeVal = arr[0]; + for (const val of arr) { + const c = (counts.get(val) || 0) + 1; + counts.set(val, c); + if (c > maxCount) { + maxCount = c; + modeVal = val; + } + } + return modeVal; + }, strLength: function(str) { return str ? str.length : 0; }, @@ -895,6 +1103,84 @@ const pinescript = { strReverse: function(str) { return str ? str.split('').reverse().join('') : ''; }, + strFormat: function(formatStr, ...args) { + if (!formatStr) return ''; + return formatStr.replace(/\{(\d+)\}/g, (match, idx) => { + const i = parseInt(idx, 10); + return i < args.length ? String(args[i]) : match; + }); + }, + input: function(defval, title, tooltip, inline, group) { + return defval; + }, + inputInt: function(defval = 0, title, minval, maxval, step, tooltip, inline, group) { + return defval; + }, + inputFloat: function(defval = 0.0, title, minval, maxval, step, tooltip, inline, group) { + return defval; + }, + inputBool: function(defval = false, title, tooltip, inline, group) { + return defval; + }, + inputString: function(defval = '', title, options, tooltip, inline, group) { + return defval; + }, + inputSource: function(defval = null, title, tooltip, inline, group) { + return defval; + }, + inputColor: function(defval = '#000000', title, tooltip, inline, group) { + return defval; + }, + inputTime: function(defval = 0, title, tooltip, inline, group) { + return defval; + }, + mathSign: function(value) { + return Math.sign(value); + }, + mathAvg: function(...args) { + const valid = args.filter(v => v !== null && v !== undefined && !isNaN(v)); + return valid.length > 0 ? valid.reduce((a, b) => a + b, 0) / valid.length : null; + }, + mathSum: function(series, length) { + if (!series || series.length < length) return null; + return series.slice(-length).reduce((a, b) => a + b, 0); + }, + mathRandom: function(min = 0, max = 1) { + return min + Math.random() * (max - min); + }, + mathTodegrees: function(radians) { + return radians * (180 / Math.PI); + }, + mathToradians: function(degrees) { + return degrees * (Math.PI / 180); + }, + colorT: function(color, transparency) { + if (transparency !== undefined) { + return { color, transparency }; + } + if (color && typeof color === 'object' && 'transparency' in color) { + return color.transparency; + } + return 0; + }, + tableCellSetText: function(table, column, row, text) { + return null; + }, + tableCellSetBgcolor: function(table, column, row, bgcolor) { + return null; + }, + tableMergeCells: function(table, startColumn, startRow, endColumn, endRow) { + return null; + }, + tableDelete: function(table) { + return null; + }, + tableClear: function(table, startColumn, startRow, endColumn, endRow) { + return null; + }, + maxBarsBack: function(series, length) { + return null; + }, pi: 3.141592653589793, e: 2.718281828459045, true: true, @@ -905,8 +1191,7 @@ const pinescript = { const info = { ticker: 'AAPL', tickerid: 'NASDAQ:AAPL', prefix: 'NASDAQ', root: 'AAPL', suffix: '' }; return info[type] || ''; }, - time: 1771178052327, - timenow: 1771178052327, + timenow: 1781574528093, barstate: "LAST", dividends: {}, splits: {}, @@ -923,7 +1208,7 @@ const pinescript = { return null; }, tr: function(high, low, close) { - // Pine signature: ta.tr(true) + // Handles both the boolean shorthand ta.tr(true) and explicit high/low/close arguments if (typeof high === 'boolean') { const H = globalThis.high; const L = globalThis.low; @@ -934,7 +1219,7 @@ const pinescript = { return Math.max(H[i] - L[i], Math.abs(H[i] - prevClose), Math.abs(L[i] - prevClose)); } - // Fallback: treat inputs as scalars for current bar + // When arrays are passed, compute true range from the last bar using the previous close if (Array.isArray(close)) { const prevClose = close[close.length - 2] || close[0]; const currHigh = Array.isArray(high) ? high[high.length - 1] : high; @@ -945,6 +1230,92 @@ const pinescript = { return Math.max(high - low, Math.abs(high - close), Math.abs(low - close)); }, + arrayFirst: function(arr) { + if (!arr || arr.length === 0) return null; + return arr[0]; + }, + arrayLast: function(arr) { + if (!arr || arr.length === 0) return null; + return arr[arr.length - 1]; + }, + arrayJoin: function(arr, separator = ',') { + if (!arr) return ''; + return arr.join(separator); + }, + arrayConcat: function(arr1, arr2) { + if (!arr1) return arr2 || []; + if (!arr2) return arr1; + return arr1.concat(arr2); + }, + arrayCopy: function(arr) { + if (!arr) return []; + return [...arr]; + }, + arrayBinarySearch: function(arr, value) { + if (!arr || arr.length === 0) return -1; + let lo = 0, hi = arr.length - 1; + while (lo <= hi) { + const mid = (lo + hi) >>> 1; + if (arr[mid] === value) return mid; + if (arr[mid] < value) lo = mid + 1; + else hi = mid - 1; + } + return -1; + }, + arrayRange: function(arr) { + if (!arr || arr.length === 0) return 0; + return Math.max(...arr) - Math.min(...arr); + }, + arrayMedian: function(arr) { + if (!arr || arr.length === 0) return null; + const sorted = [...arr].sort((a, b) => a - b); + const mid = Math.floor(sorted.length / 2); + return sorted.length % 2 !== 0 ? sorted[mid] : (sorted[mid - 1] + sorted[mid]) / 2; + }, + arrayMode: function(arr) { + if (!arr || arr.length === 0) return null; + const freq = {}; + let maxCount = 0; + let mode = arr[0]; + for (const v of arr) { + freq[v] = (freq[v] || 0) + 1; + if (freq[v] > maxCount) { maxCount = freq[v]; mode = v; } + } + return mode; + }, + arrayPercentile: function(arr, percentile) { + if (!arr || arr.length === 0) return null; + const sorted = [...arr].sort((a, b) => a - b); + const idx = (percentile / 100) * (sorted.length - 1); + const lo = Math.floor(idx); + const hi = Math.ceil(idx); + if (lo === hi) return sorted[lo]; + return sorted[lo] + (sorted[hi] - sorted[lo]) * (idx - lo); + }, + arrayPercentileLinearInterpolation: function(arr, percentile) { + return builtins.get('arrayPercentile')(arr, percentile); + }, + arrayPercentileNearestRank: function(arr, percentile) { + if (!arr || arr.length === 0) return null; + const sorted = [...arr].sort((a, b) => a - b); + const idx = Math.ceil((percentile / 100) * sorted.length) - 1; + return sorted[Math.max(0, idx)]; + }, + arrayAbs: function(arr) { + if (!arr) return []; + return arr.map(v => Math.abs(v)); + }, + arrayEvery: function(arr, callback) { + if (!arr) return false; + return arr.every(callback); + }, + arraySome: function(arr, callback) { + if (!arr) return false; + return arr.some(callback); + }, + requestFinancial: function() { + return null; + }, }; @@ -1101,6 +1472,115 @@ globalThis.barmerge = globalThis.barmerge || { }; +function __pineNS(real) { + + const base = real || {}; + + return new Proxy(base, { + + get(target, key) { + + if (key in target) return target[key]; + + if (typeof key === "symbol") return target[key]; + + // Unknown member: usable as a value (null), a constant, or a no-op function. + + const noop = function() { return null; }; + + noop.toString = () => ""; + + return noop; + + } + + }); + +} + +globalThis.ta = globalThis.ta || __pineNS({}); + +globalThis.math = globalThis.math || __pineNS({ pi: Math.PI, e: Math.E, phi: 1.618033988749895, rphi: 0.618033988749895 }); + +globalThis.chart = globalThis.chart || __pineNS({ point: __pineNS({}), bg_color: null, fg_color: null }); + +globalThis.line = globalThis.line || __pineNS({ style_solid: "solid", style_dashed: "dashed", style_dotted: "dotted" }); + +globalThis.box = globalThis.box || __pineNS({}); + +globalThis.label = globalThis.label || __pineNS({ style_label_down: "label_down", style_label_up: "label_up", style_none: "none" }); + +globalThis.polyline = globalThis.polyline || __pineNS({}); + +globalThis.linefill = globalThis.linefill || __pineNS({}); + +globalThis.matrix = globalThis.matrix || __pineNS({}); + +globalThis.map = globalThis.map || __pineNS({}); + +globalThis.session = globalThis.session || __pineNS({ regular: "regular", extended: "extended" }); + +globalThis.dayofweek = globalThis.dayofweek || __pineNS({ sunday: 1, monday: 2, tuesday: 3, wednesday: 4, thursday: 5, friday: 6, saturday: 7 }); + +globalThis.timeframe = __pineNS(Object.assign(globalThis.timeframe || {}, { period: (globalThis.timeframe && globalThis.timeframe.period) || "D", isintraday: false, isdaily: true, multiplier: 1 })); + +globalThis.request = __pineNS(globalThis.request || {}); + +globalThis.input = __pineNS(globalThis.input || {}); + +globalThis.adjustment = globalThis.adjustment || __pineNS({}); + +globalThis.earnings = globalThis.earnings || __pineNS({}); + +globalThis.dividends = globalThis.dividends || __pineNS({}); + +globalThis.splits = globalThis.splits || __pineNS({}); + +globalThis.currency = globalThis.currency || __pineNS({}); + +globalThis.display = globalThis.display || __pineNS({ none: "none", all: "all", pane: "pane", data_window: "data_window", status_line: "status_line", price_scale: "price_scale" }); + +globalThis.format = globalThis.format || __pineNS({ price: "price", volume: "volume", percent: "percent", mintick: "mintick", inherit: "inherit" }); + +globalThis.scale = globalThis.scale || __pineNS({ left: "left", right: "right", none: "none" }); + +globalThis.font = globalThis.font || __pineNS({ family_default: "default", family_monospace: "monospace" }); + +globalThis.xloc = globalThis.xloc || __pineNS({ bar_index: "bar_index", bar_time: "bar_time" }); + +globalThis.yloc = globalThis.yloc || __pineNS({ price: "price", abovebar: "abovebar", belowbar: "belowbar" }); + +globalThis.extend = globalThis.extend || __pineNS({ none: "none", left: "left", right: "right", both: "both" }); + +globalThis.order = globalThis.order || __pineNS({ ascending: "ascending", descending: "descending" }); + +globalThis.alert = globalThis.alert || Object.assign(function() { return null; }, { freq_once_per_bar: "once_per_bar", freq_once_per_bar_close: "once_per_bar_close", freq_all: "all" }); + +globalThis.dayofmonth = globalThis.dayofmonth || function(t) { return new Date(t || 0).getUTCDate(); }; + +globalThis.plot = globalThis.plot || __pineNS({ style_line: "line", style_linebr: "linebr", style_stepline: "stepline", style_histogram: "histogram", style_cross: "cross", style_area: "area", style_areabr: "areabr", style_columns: "columns", style_circles: "circles" }); + +globalThis.int = globalThis.int || function(x) { return x == null ? null : Math.trunc(Number(x)); }; + +globalThis.float = globalThis.float || function(x) { return x == null ? null : Number(x); }; + +globalThis.bool = globalThis.bool || function(x) { return Boolean(x); }; + +globalThis.string = globalThis.string || function(x) { return x == null ? null : String(x); }; + +globalThis.dayofweek = globalThis.dayofweek || __pineNS({ sunday: 1, monday: 2, tuesday: 3, wednesday: 4, thursday: 5, friday: 6, saturday: 7 }); + +globalThis.str = globalThis.str || __pineNS({}); + +pinescript.math = globalThis.math; + +pinescript.ta = globalThis.ta; + +pinescript.str = globalThis.str; + +pinescript.array = __pineNS(globalThis.array); + + pinescript.color = { hex: function(s) { @@ -1190,10 +1670,10 @@ pinescript.table = { }; -// Input parameters +// Script input parameters and their defaults -// Main script logic +// The transpiled script logic, called once per bar function main() { @@ -1208,50 +1688,50 @@ function main() { null; // Study: Pro Pattern Al-Sat (High Winrate) // Options: {"overlay":true,"max_labels_count":500,"max_lines_count":500,"max_boxes_count":500} - let showBullishPatterns = input.bool(true, "Yükseliş Formasyonlarını Göster", ({ group: "Pattern Ayarları" })); - let showBearishPatterns = input.bool(true, "Düşüş Formasyonlarını Göster", ({ group: "Pattern Ayarları" })); - let riskRewardRatio = input.float(2, "Risk/Ödül Oranı", ({ minval: 0.5, maxval: 5, step: 0.1, group: "Risk Yönetimi" })); - let stopLossATR = input.float(1.5, "Stop Loss (ATR Çarpanı)", ({ minval: 0.5, maxval: 5, step: 0.1, group: "Risk Yönetimi" })); - let showTable = input.bool(true, "Sinyal Tablosunu Göster", ({ group: "Görünüm" })); - let tablePosition = input.string("top_right", "Tablo Pozisyonu", ({ options: ["top_right", "top_left", "bottom_right", "bottom_left"], group: "Görünüm" })); - let showTradeResults = input.bool(true, "İşlem Sonuçlarını Göster", ({ group: "Görünüm" })); - let showExtendedLines = input.bool(true, "Uzatılmış Çizgileri Göster", ({ group: "Görünüm" })); - let useTrendFilter = input.bool(true, "Trend Filtresi Kullan", ({ group: "🎯 Filtreler" })); - let useVolumeFilter = input.bool(true, "Volume Filtresi Kullan", ({ group: "🎯 Filtreler" })); - let useRSIFilter = input.bool(true, "RSI Filtresi Kullan", ({ group: "🎯 Filtreler" })); - let useSupportResistance = input.bool(true, "Destek/Direnç Filtresi", ({ group: "🎯 Filtreler" })); - let useTimeFilter = input.bool(true, "Zaman Filtresi Kullan", ({ group: "🎯 Filtreler" })); - let emaFastLength = input.int(50, "Hızlı EMA", ({ minval: 10, group: "📈 Trend Filtresi" })); - let emaSlowLength = input.int(200, "Yavaş EMA", ({ minval: 50, group: "📈 Trend Filtresi" })); - let supertrendMultiplier = input.float(3, "Supertrend Çarpanı", ({ minval: 1, maxval: 10, step: 0.5, group: "📈 Trend Filtresi" })); - let supertrendPeriod = input.int(10, "Supertrend Periyodu", ({ minval: 5, group: "📈 Trend Filtresi" })); - let adxThreshold = input.int(25, "ADX Minimum", ({ minval: 10, maxval: 50, group: "📈 Trend Filtresi" })); - let volumeMultiplier = input.float(1.5, "Volume Çarpanı", ({ minval: 1, maxval: 3, step: 0.1, group: "📊 Volume Filtresi" })); - let volumePeriod = input.int(20, "Volume SMA Periyodu", ({ minval: 10, group: "📊 Volume Filtresi" })); - let rsiPeriod = input.int(14, "RSI Periyodu", ({ minval: 5, group: "📉 RSI Filtresi" })); - let rsiOverbought = input.int(70, "Aşırı Alım Seviyesi", ({ minval: 60, maxval: 90, group: "📉 RSI Filtresi" })); - let rsiOversold = input.int(30, "Aşırı Satım Seviyesi", ({ minval: 10, maxval: 40, group: "📉 RSI Filtresi" })); - let pivotLookback = input.int(10, "Pivot Lookback", ({ minval: 5, maxval: 50, group: "🎚️ Destek/Direnç" })); - let srProximity = input.float(0.5, "Seviye Yakınlığı (%)", ({ minval: 0.1, maxval: 2, step: 0.1, group: "🎚️ Destek/Direnç" })); - let avoidFirstHour = input.bool(true, "İlk 1 Saati Atla", ({ group: "⏰ Zaman Filtresi" })); - let avoidLastHour = input.bool(true, "Son 1 Saati Atla", ({ group: "⏰ Zaman Filtresi" })); - let tradingStartHour = input.int(9, "İşlem Başlangıç Saati", ({ minval: 0, maxval: 23, group: "⏰ Zaman Filtresi" })); - let tradingEndHour = input.int(17, "İşlem Bitiş Saati", ({ minval: 0, maxval: 23, group: "⏰ Zaman Filtresi" })); - let atrValue = ta.atr(14); + let showBullishPatterns = pinescript.inputBool(true, "Yükseliş Formasyonlarını Göster", ({ group: "Pattern Ayarları" })); + let showBearishPatterns = pinescript.inputBool(true, "Düşüş Formasyonlarını Göster", ({ group: "Pattern Ayarları" })); + let riskRewardRatio = pinescript.inputFloat(2, "Risk/Ödül Oranı", ({ minval: 0.5, maxval: 5, step: 0.1, group: "Risk Yönetimi" })); + let stopLossATR = pinescript.inputFloat(1.5, "Stop Loss (ATR Çarpanı)", ({ minval: 0.5, maxval: 5, step: 0.1, group: "Risk Yönetimi" })); + let showTable = pinescript.inputBool(true, "Sinyal Tablosunu Göster", ({ group: "Görünüm" })); + let tablePosition = pinescript.inputString("top_right", "Tablo Pozisyonu", ({ options: ["top_right", "top_left", "bottom_right", "bottom_left"], group: "Görünüm" })); + let showTradeResults = pinescript.inputBool(true, "İşlem Sonuçlarını Göster", ({ group: "Görünüm" })); + let showExtendedLines = pinescript.inputBool(true, "Uzatılmış Çizgileri Göster", ({ group: "Görünüm" })); + let useTrendFilter = pinescript.inputBool(true, "Trend Filtresi Kullan", ({ group: "🎯 Filtreler" })); + let useVolumeFilter = pinescript.inputBool(true, "Volume Filtresi Kullan", ({ group: "🎯 Filtreler" })); + let useRSIFilter = pinescript.inputBool(true, "RSI Filtresi Kullan", ({ group: "🎯 Filtreler" })); + let useSupportResistance = pinescript.inputBool(true, "Destek/Direnç Filtresi", ({ group: "🎯 Filtreler" })); + let useTimeFilter = pinescript.inputBool(true, "Zaman Filtresi Kullan", ({ group: "🎯 Filtreler" })); + let emaFastLength = pinescript.inputInt(50, "Hızlı EMA", ({ minval: 10, group: "📈 Trend Filtresi" })); + let emaSlowLength = pinescript.inputInt(200, "Yavaş EMA", ({ minval: 50, group: "📈 Trend Filtresi" })); + let supertrendMultiplier = pinescript.inputFloat(3, "Supertrend Çarpanı", ({ minval: 1, maxval: 10, step: 0.5, group: "📈 Trend Filtresi" })); + let supertrendPeriod = pinescript.inputInt(10, "Supertrend Periyodu", ({ minval: 5, group: "📈 Trend Filtresi" })); + let adxThreshold = pinescript.inputInt(25, "ADX Minimum", ({ minval: 10, maxval: 50, group: "📈 Trend Filtresi" })); + let volumeMultiplier = pinescript.inputFloat(1.5, "Volume Çarpanı", ({ minval: 1, maxval: 3, step: 0.1, group: "📊 Volume Filtresi" })); + let volumePeriod = pinescript.inputInt(20, "Volume SMA Periyodu", ({ minval: 10, group: "📊 Volume Filtresi" })); + let rsiPeriod = pinescript.inputInt(14, "RSI Periyodu", ({ minval: 5, group: "📉 RSI Filtresi" })); + let rsiOverbought = pinescript.inputInt(70, "Aşırı Alım Seviyesi", ({ minval: 60, maxval: 90, group: "📉 RSI Filtresi" })); + let rsiOversold = pinescript.inputInt(30, "Aşırı Satım Seviyesi", ({ minval: 10, maxval: 40, group: "📉 RSI Filtresi" })); + let pivotLookback = pinescript.inputInt(10, "Pivot Lookback", ({ minval: 5, maxval: 50, group: "🎚️ Destek/Direnç" })); + let srProximity = pinescript.inputFloat(0.5, "Seviye Yakınlığı (%)", ({ minval: 0.1, maxval: 2, step: 0.1, group: "🎚️ Destek/Direnç" })); + let avoidFirstHour = pinescript.inputBool(true, "İlk 1 Saati Atla", ({ group: "⏰ Zaman Filtresi" })); + let avoidLastHour = pinescript.inputBool(true, "Son 1 Saati Atla", ({ group: "⏰ Zaman Filtresi" })); + let tradingStartHour = pinescript.inputInt(9, "İşlem Başlangıç Saati", ({ minval: 0, maxval: 23, group: "⏰ Zaman Filtresi" })); + let tradingEndHour = pinescript.inputInt(17, "İşlem Bitiş Saati", ({ minval: 0, maxval: 23, group: "⏰ Zaman Filtresi" })); + let atrValue = pinescript.atr(14); let emaFast = pinescript.ema(close, emaFastLength); let emaSlow = pinescript.ema(close, emaSlowLength); let uptrend = (emaFast > emaSlow); let downtrend = (emaFast < emaSlow); - let [supertrendValue, supertrendDirection] = ta.supertrend(supertrendMultiplier, supertrendPeriod); + let [supertrendValue, supertrendDirection] = pinescript.unpack(pinescript.supertrend(supertrendMultiplier, supertrendPeriod), 2); let supertrendBullish = (supertrendDirection < 0); let supertrendBearish = (supertrendDirection > 0); - let [diPlus, diMinus, adx] = ta.dmi(14, 14); + let [diPlus, diMinus, adx] = pinescript.unpack(pinescript.dmi(14, 14), 3); let strongTrend = (adx > adxThreshold); let trendFilterBullish = ((uptrend && supertrendBullish) && strongTrend); let trendFilterBearish = ((downtrend && supertrendBearish) && strongTrend); let volumeSMA = pinescript.sma(volume, volumePeriod); let volumeFilter = (volume > (volumeSMA * volumeMultiplier)); - let rsi = ta.rsi(close, rsiPeriod); + let rsi = pinescript.rsi(close, rsiPeriod); let rsiFilterBullish = (rsi < rsiOverbought); let rsiFilterBearish = (rsi > rsiOversold); let pivotHigh = ta.pivothigh(high, pivotLookback, pivotLookback); @@ -1259,10 +1739,10 @@ function main() { if (state.lastResistance === undefined) state.lastResistance = null; if (state.lastSupport === undefined) state.lastSupport = null; if (!pinescript.na(pivotHigh)) { - state.lastResistance = pinescript.offset(high, pivotLookback); + state.lastResistance = pinescript.hist(0, high, pivotLookback); } if (!pinescript.na(pivotLow)) { - state.lastSupport = pinescript.offset(low, pivotLookback); + state.lastSupport = pinescript.hist(1, low, pivotLookback); } let proximityPercent = (srProximity / 100); let nearSupport = (!pinescript.na(state.lastSupport) && ((pinescript.abs((close - state.lastSupport)) / state.lastSupport) < proximityPercent)); @@ -1275,25 +1755,25 @@ function main() { let lastHourAvoid = (avoidLastHour && (currentHour === tradingEndHour)); let inTradingHours = ((currentHour >= tradingStartHour) && (currentHour <= tradingEndHour)); let timeFilter = ((inTradingHours && !firstHourAvoid) && !lastHourAvoid); - let bullishEngulfing = ((((pinescript.offset(close, 1) < pinescript.offset(open, 1)) && (close > open)) && (open <= pinescript.offset(close, 1))) && (close >= pinescript.offset(open, 1))); - let bearishEngulfing = ((((pinescript.offset(close, 1) > pinescript.offset(open, 1)) && (close < open)) && (open >= pinescript.offset(close, 1))) && (close <= pinescript.offset(open, 1))); + let bullishEngulfing = ((((pinescript.hist(2, close, 1) < pinescript.hist(3, open, 1)) && (close > open)) && (open <= pinescript.hist(4, close, 1))) && (close >= pinescript.hist(5, open, 1))); + let bearishEngulfing = ((((pinescript.hist(6, close, 1) > pinescript.hist(7, open, 1)) && (close < open)) && (open >= pinescript.hist(8, close, 1))) && (close <= pinescript.hist(9, open, 1))); let bodySize = pinescript.abs((close - open)); let upperWick = (high - pinescript.max(close, open)); let lowerWick = (pinescript.min(close, open) - low); - let lowestLow = pinescript.lowest(low, 10); - let highestHigh = pinescript.highest(high, 10); + let lowestLow = pinescript.lowest(pinescript.series(10, low), 10); + let highestHigh = pinescript.highest(pinescript.series(11, high), 10); let isHammer = ((((lowerWick > (bodySize * 2)) && (upperWick < bodySize)) && (close > open)) && (low === lowestLow)); let isShootingStar = ((((upperWick > (bodySize * 2)) && (lowerWick < bodySize)) && (close < open)) && (high === highestHigh)); - let avgBody2 = pinescript.abs((pinescript.offset(close, 2) - pinescript.offset(open, 2))); - let isMorningStar = (pinescript.offset(close, 2)((pinescript.offset(open, 2) + pinescript.offset(close, 2))) / 2); - let isEveningStar = ((((pinescript.offset(close, 2) > pinescript.offset(open, 2)) && (pinescript.abs((pinescript.offset(close, 1) - pinescript.offset(open, 1))) < (avgBody2 * 0.3))) && (close < open)) && (close < ((pinescript.offset(open, 2) + pinescript.offset(close, 2)) / 2))); + let avgBody2 = pinescript.abs((pinescript.hist(12, close, 2) - pinescript.hist(13, open, 2))); + let isMorningStar = (pinescript.hist(14, close, 2)((pinescript.hist(15, open, 2) + pinescript.hist(16, close, 2))) / 2); + let isEveningStar = ((((pinescript.hist(17, close, 2) > pinescript.hist(18, open, 2)) && (pinescript.abs((pinescript.hist(19, close, 1) - pinescript.hist(20, open, 1))) < (avgBody2 * 0.3))) && (close < open)) && (close < ((pinescript.hist(21, open, 2) + pinescript.hist(22, close, 2)) / 2))); let candleRange = (high - low); let avgRange = pinescript.sma((high - low), 14); let isDoji = ((pinescript.abs((close - open)) < (candleRange * 0.1)) && (candleRange > avgRange)); - let threeWhiteSoldiers = (((((((((pinescript.offset(close, 2) > pinescript.offset(open, 2)) && (pinescript.offset(close, 1) > pinescript.offset(open, 1))) && (close > open)) && (pinescript.offset(close, 1) > pinescript.offset(close, 2))) && (close > pinescript.offset(close, 1))) && (pinescript.offset(open, 1) > pinescript.offset(open, 2))) && (open > pinescript.offset(open, 1))) && (pinescript.offset(open, 1) < pinescript.offset(close, 2))) && (open < pinescript.offset(close, 1))); - let threeBlackCrows = (((((((((pinescript.offset(close, 2) < pinescript.offset(open, 2)) && (pinescript.offset(close, 1) < pinescript.offset(open, 1))) && (close < open)) && (pinescript.offset(close, 1) < pinescript.offset(close, 2))) && (close < pinescript.offset(close, 1))) && (pinescript.offset(open, 1) < pinescript.offset(open, 2))) && (open < pinescript.offset(open, 1))) && (pinescript.offset(open, 1) > pinescript.offset(close, 2))) && (open > pinescript.offset(close, 1))); - let piercingLine = ((((pinescript.offset(close, 1) < pinescript.offset(open, 1)) && (close > open)) && (pinescript.open((pinescript.offset(open, 1) + pinescript.offset(close, 1))) / 2)) && (close < pinescript.offset(open, 1))); - let darkCloudCover = (((((pinescript.offset(close, 1) > pinescript.offset(open, 1)) && (close < open)) && (open > pinescript.offset(close, 1))) && (close < ((pinescript.offset(open, 1) + pinescript.offset(close, 1)) / 2))) && (close > pinescript.offset(open, 1))); + let threeWhiteSoldiers = (((((((((pinescript.hist(23, close, 2) > pinescript.hist(24, open, 2)) && (pinescript.hist(25, close, 1) > pinescript.hist(26, open, 1))) && (close > open)) && (pinescript.hist(27, close, 1) > pinescript.hist(28, close, 2))) && (close > pinescript.hist(29, close, 1))) && (pinescript.hist(30, open, 1) > pinescript.hist(31, open, 2))) && (open > pinescript.hist(32, open, 1))) && (pinescript.hist(33, open, 1) < pinescript.hist(34, close, 2))) && (open < pinescript.hist(35, close, 1))); + let threeBlackCrows = (((((((((pinescript.hist(36, close, 2) < pinescript.hist(37, open, 2)) && (pinescript.hist(38, close, 1) < pinescript.hist(39, open, 1))) && (close < open)) && (pinescript.hist(40, close, 1) < pinescript.hist(41, close, 2))) && (close < pinescript.hist(42, close, 1))) && (pinescript.hist(43, open, 1) < pinescript.hist(44, open, 2))) && (open < pinescript.hist(45, open, 1))) && (pinescript.hist(46, open, 1) > pinescript.hist(47, close, 2))) && (open > pinescript.hist(48, close, 1))); + let piercingLine = ((((pinescript.hist(49, close, 1) < pinescript.hist(50, open, 1)) && (close > open)) && (pinescript.open((pinescript.hist(51, open, 1) + pinescript.hist(52, close, 1))) / 2)) && (close < pinescript.hist(53, open, 1))); + let darkCloudCover = (((((pinescript.hist(54, close, 1) > pinescript.hist(55, open, 1)) && (close < open)) && (open > pinescript.hist(56, close, 1))) && (close < ((pinescript.hist(57, open, 1) + pinescript.hist(58, close, 1)) / 2))) && (close > pinescript.hist(59, open, 1))); let bullishPattern = (((((bullishEngulfing || isHammer) || isMorningStar) || threeWhiteSoldiers) || piercingLine) && showBullishPatterns); let bearishPattern = (((((bearishEngulfing || isShootingStar) || isEveningStar) || threeBlackCrows) || darkCloudCover) && showBearishPatterns); let bullishSignal = (((((bullishPattern && (!useTrendFilter || trendFilterBullish)) && (!useVolumeFilter || volumeFilter)) && (!useRSIFilter || rsiFilterBullish)) && (!useSupportResistance || srFilterBullish)) && (!useTimeFilter || timeFilter)); @@ -1443,9 +1923,9 @@ function main() { } if (showTable) { if (state.t === undefined) state.t = pinescript.table.new(((tablePosition === "top_right") ? pinescript.position.top_right : ((tablePosition === "top_left") ? pinescript.position.top_left : ((tablePosition === "bottom_right") ? pinescript.position.bottom_right : pinescript.position.bottom_left))), ({ columns: 3, rows: 16, bgcolor: pinescript.color.new(pinescript.color.black, 85), border_width: 1, border_color: pinescript.color.new(pinescript.color.blue, 60) })); - table.clear(state.t, 0, 0, 2, 15); + pinescript.tableClear(state.t, 0, 0, 2, 15); pinescript.table.cell(state.t, 0, 0, "PRO SİSTEM - HIGH WINRATE", ({ text_color: pinescript.color.white, bgcolor: pinescript.color.new(pinescript.color.blue, 40), text_size: pinescript.size.normal })); - table.merge_cells(state.t, 0, 0, 2, 0); + pinescript.tableMergeCells(state.t, 0, 0, 2, 0); pinescript.table.cell(state.t, 0, 1, "Parametre", ({ text_color: pinescript.color.yellow, bgcolor: pinescript.color.new(pinescript.color.gray, 70) })); pinescript.table.cell(state.t, 1, 1, "Değer", ({ text_color: pinescript.color.yellow, bgcolor: pinescript.color.new(pinescript.color.gray, 70) })); pinescript.table.cell(state.t, 2, 1, "Durum", ({ text_color: pinescript.color.yellow, bgcolor: pinescript.color.new(pinescript.color.gray, 70) })); @@ -1468,7 +1948,7 @@ function main() { pinescript.table.cell(state.t, 1, 7, ("1:" + pinescript.strToString(riskRewardRatio, "#.#")), ({ text_color: pinescript.color.yellow })); pinescript.table.cell(state.t, 2, 7, ""); pinescript.table.cell(state.t, 0, 8, "━━━━━ PERFORMANS ━━━━━", ({ text_color: pinescript.color.aqua })); - table.merge_cells(state.t, 0, 8, 2, 8); + pinescript.tableMergeCells(state.t, 0, 8, 2, 8); pinescript.table.cell(state.t, 0, 9, "Toplam İşlem", ({ text_color: pinescript.color.white })); pinescript.table.cell(state.t, 1, 9, pinescript.strToString(state.totalTrades), ({ text_color: pinescript.color.aqua })); pinescript.table.cell(state.t, 2, 9, "📊", ({ text_color: pinescript.color.white })); @@ -1481,7 +1961,7 @@ function main() { pinescript.table.cell(state.t, 1, 11, pinescript.strToString(profitLossRatio, "#.##"), ({ text_color: plRatioColor })); pinescript.table.cell(state.t, 2, 11, ((profitLossRatio >= 1) ? "✓" : "✗"), ({ text_color: plRatioColor })); pinescript.table.cell(state.t, 0, 12, "━━━━━ FİLTRELER ━━━━━", ({ text_color: pinescript.color.purple })); - table.merge_cells(state.t, 0, 12, 2, 12); + pinescript.tableMergeCells(state.t, 0, 12, 2, 12); pinescript.table.cell(state.t, 0, 13, "Ham Sinyal", ({ text_color: pinescript.color.white })); pinescript.table.cell(state.t, 1, 13, pinescript.strToString(state.totalSignalsBeforeFilter), ({ text_color: pinescript.color.gray })); pinescript.table.cell(state.t, 2, 13, "📡", ({ text_color: pinescript.color.gray })); @@ -1493,12 +1973,72 @@ function main() { pinescript.table.cell(state.t, 1, 15, (pinescript.strToString(filterEfficiency, "#.#") + "%"), ({ text_color: filterColor })); pinescript.table.cell(state.t, 2, 15, ((filterEfficiency >= 50) ? "🔥" : "⚡"), ({ text_color: filterColor })); } - alertcondition(bullishSignal, ({ title: "🟢 Filtrelenmiş Alış", message: "ALIŞ {{ticker}} - {{plot(\"Pattern\")}} | Giriş {{close}}" })); - alertcondition(bearishSignal, ({ title: "🔴 Filtrelenmiş Satış", message: "SATIŞ {{ticker}} - {{plot(\"Pattern\")}} | Giriş {{close}}" })); + pinescript.alertcondition(bullishSignal, ({ title: "🟢 Filtrelenmiş Alış", message: "ALIŞ {{ticker}} - {{plot(\"Pattern\")}} | Giriş {{close}}" })); + pinescript.alertcondition(bearishSignal, ({ title: "🔴 Filtrelenmiş Satış", message: "SATIŞ {{ticker}} - {{plot(\"Pattern\")}} | Giriş {{close}}" })); } -// Export for use -export { main +// The bar-by-bar engine: runs main() once per bar over the dataset. +function run(data, options = {}) { + data = data || {}; + const inClose = (data.close || []).map(Number); + const n = inClose.length; + const inOpen = (data.open || inClose).map(Number); + const inHigh = (data.high || inClose).map(Number); + const inLow = (data.low || inClose).map(Number); + const inVol = (data.volume || new Array(n).fill(0)).map(Number); + const inTime = (data.time || inClose.map((_, i) => i)).map(Number); + + // Reset all persistent and per-run state so repeated runs are independent. + globalThis.__pineState = {}; + globalThis.__pineRuntime = { plots: {}, plotshapes: {}, alerts: [], bars: n, inputs: options.inputs || {} }; + pinescript.__rt = {}; + pinescript.__bar = 0; + + // Growing OHLCV windows: each is a SeriesArray that gains one element per bar, + // so window built-ins like sma and highest see history up to the current bar. + const O = pinescript.asSeries([]), H = pinescript.asSeries([]), L = pinescript.asSeries([]); + const C = pinescript.asSeries([]), V = pinescript.asSeries([]), T = pinescript.asSeries([]); + const HL2 = pinescript.asSeries([]), HLC3 = pinescript.asSeries([]), OHLC4 = pinescript.asSeries([]); + globalThis.open = O; globalThis.high = H; globalThis.low = L; globalThis.close = C; + globalThis.volume = V; globalThis.time = T; + globalThis.hl2 = HL2; globalThis.hlc3 = HLC3; globalThis.ohlc4 = OHLC4; + globalThis.syminfo = globalThis.syminfo || { tickerid: 'SYNTHETIC', ticker: 'SYNTHETIC', mintick: 0.01 }; + + const rt = globalThis.__pineRuntime; + for (let i = 0; i < n; i++) { + O.push(inOpen[i]); H.push(inHigh[i]); L.push(inLow[i]); C.push(inClose[i]); V.push(inVol[i]); T.push(inTime[i]); + HL2.push((inHigh[i] + inLow[i]) / 2); + HLC3.push((inHigh[i] + inLow[i] + inClose[i]) / 3); + OHLC4.push((inOpen[i] + inHigh[i] + inLow[i] + inClose[i]) / 4); + + pinescript.__bar = i; + rt.__barIndex = i; + rt.__plotIdx = 0; + rt.__shapeIdx = 0; + globalThis.bar_index = i; + globalThis.last_bar_index = n - 1; + globalThis.barstate = { + isfirst: i === 0, islast: i === n - 1, isrealtime: false, ishistory: true, + isconfirmed: true, isnew: true, islastconfirmedhistory: i === n - 1, + }; + + main(); + } + + // Backfill skipped bars so every output series has exactly n entries. + for (const k of Object.keys(rt.plots)) { + const d = rt.plots[k].data; + for (let i = 0; i < n; i++) if (d[i] === undefined) d[i] = null; + } + for (const k of Object.keys(rt.plotshapes)) { + const d = rt.plotshapes[k].data; + for (let i = 0; i < n; i++) if (d[i] === undefined) d[i] = false; + } + return rt; +} + + +export { main, run }; diff --git a/converted/Proximal_Range_Filter_LuxAlgo.js b/converted/Proximal_Range_Filter_LuxAlgo.js index b40a413..8d7b327 100644 --- a/converted/Proximal_Range_Filter_LuxAlgo.js +++ b/converted/Proximal_Range_Filter_LuxAlgo.js @@ -1,12 +1,63 @@ -// PineScript to JavaScript Transpiled Code +// Auto-generated JavaScript from PineScript source -// Generated automatically +// Do not edit by hand -- re-run the converter instead -// Built-in PineScript functions +// All the built-in PineScript functions that scripts depend on at runtime const pinescript = { + __scalar: function(v) { + if (v === null || v === undefined) return v; + if (typeof v === 'object' && typeof v.valueOf === 'function') { + const s = v.valueOf(); + if (typeof s === 'number' || typeof s === 'boolean' || s === null) return s; + } + return v; + }, + series: function(id, value) { + const rt = this.__rt || (this.__rt = {}); + const store = rt.series || (rt.series = {}); + let buf = store[id]; + if (!buf) buf = store[id] = []; + buf[this.__bar | 0] = this.__scalar(value); + return buf; + }, + hist: function(id, value, n) { + const buf = this.series(id, value); + const idx = (this.__bar | 0) - (n | 0); + return idx >= 0 && idx < buf.length ? buf[idx] : null; + }, + time: function(timeframe, session) { + const t = globalThis.time; + if (t && typeof t.valueOf === 'function') return t.valueOf(); + return Array.isArray(t) ? t[t.length - 1] : t; + }, + sign: function(x) { + if (x === null || x === undefined || (typeof x === 'number' && isNaN(x))) return null; + return Math.sign(Number(x)); + }, + unpack: function(value, count) { + if (Array.isArray(value)) return value; + if (value != null && typeof value[Symbol.iterator] === 'function') return Array.from(value); + return new Array(count || 0).fill(null); + }, + alertcondition: function(condition, ...rest) { + if (globalThis.__pineRuntime) { + globalThis.__pineRuntime.alerts.push({ condition, args: rest }); + } + return null; + }, + barcolor: function(color) { return null; }, + bgcolor: function(color, title, editable, showLast) { + return null; + }, + fill: function(series1, series2, color, title, editable) { + return null; + }, + hline: function(_price, _title, _opts) { + return null; + }, sma: function(series, length) { if (series === null || series === undefined) return null; if (!Array.isArray(series)) series = [series]; @@ -84,6 +135,13 @@ const pinescript = { } return weightSum > 0 ? sum / weightSum : null; }, + swma: function(series) { + if (series === null || series === undefined) return null; + if (!Array.isArray(series)) series = [series]; + if (series.length < 4) return null; + const s = series.slice(-4); + return (s[0] * 1 + s[1] * 2 + s[2] * 2 + s[3] * 1) / 6; + }, atr: function(high, low, close, length = 14) { if (!Array.isArray(high) || !Array.isArray(low) || !Array.isArray(close)) return null; if (high.length < length + 1 || low.length < length + 1 || close.length < length + 1) return null; @@ -114,6 +172,53 @@ const pinescript = { const atr = builtins.get('atr')(high, low, close, length); return { middle: sma, upper: sma + mult * atr, lower: sma - mult * atr }; }, + supertrend: function(factor, atrPeriod, high, low, close) { + if (!Array.isArray(high) || !Array.isArray(low) || !Array.isArray(close)) return [null, null]; + if (high.length < atrPeriod + 1) return [null, null]; + const atrVal = builtins.get('atr')(high, low, close, atrPeriod); + if (atrVal === null) return [null, null]; + const lastIdx = close.length - 1; + const hl2 = (high[lastIdx] + low[lastIdx]) / 2; + const upperBand = hl2 + factor * atrVal; + const lowerBand = hl2 - factor * atrVal; + // Determine direction: 1 means downtrend (price below band), -1 means uptrend + const direction = close[lastIdx] > upperBand ? -1 : close[lastIdx] < lowerBand ? 1 : -1; + const supertrendValue = direction === -1 ? lowerBand : upperBand; + return [supertrendValue, direction]; + }, + ta_supertrend: function(factor, atrPeriod) { + const H = globalThis.high; + const L = globalThis.low; + const C = globalThis.close; + return builtins.get('supertrend')(factor, atrPeriod, H, L, C); + }, + dmi: function(diLength, adxSmoothing, high, low, close) { + if (!Array.isArray(high) || !Array.isArray(low) || !Array.isArray(close)) return { plusDI: null, minusDI: null, adx: null }; + if (high.length < diLength + 1) return { plusDI: null, minusDI: null, adx: null }; + let plusDMSum = 0, minusDMSum = 0, trSum = 0; + for (let i = 1; i <= diLength; i++) { + const idx = high.length - 1 - diLength + i; + const upMove = high[idx] - high[idx - 1]; + const downMove = low[idx - 1] - low[idx]; + plusDMSum += (upMove > downMove && upMove > 0) ? upMove : 0; + minusDMSum += (downMove > upMove && downMove > 0) ? downMove : 0; + const tr = Math.max( + high[idx] - low[idx], + Math.abs(high[idx] - close[idx - 1]), + Math.abs(low[idx] - close[idx - 1]) + ); + trSum += tr; + } + const plusDI = trSum > 0 ? (plusDMSum / trSum) * 100 : 0; + const minusDI = trSum > 0 ? (minusDMSum / trSum) * 100 : 0; + const diSum = plusDI + minusDI; + const dx = diSum > 0 ? Math.abs(plusDI - minusDI) / diSum * 100 : 0; + return { plusDI, minusDI, adx: dx }; + }, + adx: function(diLength, adxSmoothing, high, low, close) { + const result = builtins.get('dmi')(diLength, adxSmoothing, high, low, close); + return result ? result.adx : null; + }, rsi: function(close, length = 14) { if (close === null || close === undefined) return null; if (!Array.isArray(close)) close = [close]; @@ -171,9 +276,7 @@ const pinescript = { return { macd: macdLine, signal: signalLine, histogram: histogram }; }, cci: function(a, b, c, d) { - // Pine signatures: - // - ta.cci(source, length) - // - ta.cci(high, low, close, length) + // Supports both ta.cci(source, length) and ta.cci(high, low, close, length) signatures let source; let length; if (Array.isArray(a) && typeof b === 'number' && c === undefined) { @@ -304,6 +407,15 @@ const pinescript = { stdev: function(source, length) { return Math.sqrt(builtins.get('variance')(source, length)); }, + median: function(source, length) { + if (!source || source.length < length) return null; + const sorted = [...source.slice(-length)].sort((a, b) => a - b); + const mid = Math.floor(sorted.length / 2); + return sorted.length % 2 !== 0 ? sorted[mid] : (sorted[mid - 1] + sorted[mid]) / 2; + }, + ta_median: function(source, length) { + return builtins.get('median')(source, length); + }, hl2: function(high, low) { return high && low ? (high[high.length - 1] + low[low.length - 1]) / 2 : null; }, @@ -420,6 +532,24 @@ const pinescript = { return (series1[i - 1] < series2[i - 1] && series1[i] >= series2[i]) || (series1[i - 1] > series2[i - 1] && series1[i] <= series2[i]); }, + crossover: function(series1, series2) { + if (series1 === null || series1 === undefined) return false; + if (series2 === null || series2 === undefined) return false; + if (!Array.isArray(series1)) series1 = [series1]; + if (!Array.isArray(series2)) series2 = [series2]; + if (series1.length < 2 || series2.length < 2) return false; + const i = series1.length - 1; + return series1[i - 1] < series2[i - 1] && series1[i] >= series2[i]; + }, + crossunder: function(series1, series2) { + if (series1 === null || series1 === undefined) return false; + if (series2 === null || series2 === undefined) return false; + if (!Array.isArray(series1)) series1 = [series1]; + if (!Array.isArray(series2)) series2 = [series2]; + if (series1.length < 2 || series2.length < 2) return false; + const i = series1.length - 1; + return series1[i - 1] > series2[i - 1] && series1[i] <= series2[i]; + }, offset: function(series, offset) { if (!Array.isArray(series)) return null; const idx = series.length - 1 - offset; @@ -500,12 +630,14 @@ const pinescript = { return builtins.get('nz')(value, replacement); }, plot: function(series, title = '', color = null, linewidth = 1) { - globalThis.__pineRuntime.plots.push({ - series, - title, - color, - linewidth - }); + const rt = globalThis.__pineRuntime; + if (!rt) return series; + const bar = rt.__barIndex | 0; + const ord = (rt.__plotIdx = (rt.__plotIdx | 0) + 1) - 1; + const key = (title && String(title)) || ('plot_' + ord); + let p = rt.plots[key]; + if (!p) p = rt.plots[key] = { title: key, color, linewidth, data: [] }; + p.data[bar] = this.__scalar(series); return series; }, lineNew: function(x1, y1, x2, y2, opts = {}) { @@ -514,20 +646,72 @@ const pinescript = { lineDelete: function(l) { return null; }, - labelNew: function(...args) { - if (globalThis.__pineRuntime) { - globalThis.__pineRuntime.labels = globalThis.__pineRuntime.labels || []; - globalThis.__pineRuntime.labels.push({ args }); - } - return { args }; + lineSetXY: function(line, point, x, y) { + if (!line) return null; + if (point === 0 || point === 'x1') { line.x1 = x; line.y1 = y; } + else { line.x2 = x; line.y2 = y; } + return line; + }, + lineGetX: function(line, point) { + if (!line) return null; + return point === 0 || point === 'x1' ? line.x1 : line.x2; + }, + lineGetY: function(line, point) { + if (!line) return null; + return point === 0 || point === 'y1' ? line.y1 : line.y2; + }, + labelNew: function(x, y, text = '', opts = {}) { + return { x, y, text, opts, _type: 'label' }; }, labelDelete: function(l) { return null; }, + labelSetText: function(label, text) { + if (!label) return null; + label.text = text; + return label; + }, + labelGetText: function(label) { + if (!label) return ''; + return label.text || ''; + }, + boxNew: function(left, top, right, bottom, opts = {}) { + return { left, top, right, bottom, opts, _type: 'box' }; + }, + boxDelete: function(box) { + return null; + }, + boxSetLeftTop: function(box, left, top) { + if (!box) return null; + box.left = left; + box.top = top; + return box; + }, + boxSetRightBottom: function(box, right, bottom) { + if (!box) return null; + box.right = right; + box.bottom = bottom; + return box; + }, + polylineNew: function(points, opts = {}) { + return { points: points || [], opts, _type: 'polyline' }; + }, + polylineDelete: function(poly) { + return null; + }, plotshape: function(condition, ...rest) { - if (globalThis.__pineRuntime) { - globalThis.__pineRuntime.plotshapes.push({ condition, args: rest }); + const rt = globalThis.__pineRuntime; + if (!rt) return condition; + const bar = rt.__barIndex | 0; + const ord = (rt.__shapeIdx = (rt.__shapeIdx | 0) + 1) - 1; + let key = 'shape_' + ord; + for (const r of rest) { + if (typeof r === 'string') { key = r; break; } + if (r && typeof r === 'object' && r.title) { key = String(r.title); break; } } + let s = rt.plotshapes[key]; + if (!s) s = rt.plotshapes[key] = { title: key, data: [] }; + s.data[bar] = !!this.__scalar(condition); return condition; }, indicator: function(title, shorttitle, overlay, opts) { @@ -542,27 +726,12 @@ const pinescript = { } return null; }, - alertcondition: function(condition, ...rest) { - if (globalThis.__pineRuntime) { - globalThis.__pineRuntime.alerts.push({ condition, args: rest }); - } - return null; - }, plotbar: function(open, high, low, close, title, color, editable, showLast) { return { open, high, low, close }; }, plotcandle: function(open, high, low, close, title, color, wickColor, borderColor, editable, showLast) { return { open, high, low, close }; }, - hline: function(_price, _title, _opts) { - return null; - }, - bgcolor: function(color, title, editable, showLast) { - return null; - }, - fill: function(series1, series2, color, title, editable) { - return null; - }, alert: function(condition, message, frequency) { console.log('Alert:', condition ? message : 'Condition not met'); return condition; @@ -828,6 +997,9 @@ const pinescript = { arrayIndexOf: function(arr, value) { return arr ? arr.indexOf(value) : -1; }, + arrayLastIndexOf: function(arr, value) { + return arr ? arr.lastIndexOf(value) : -1; + }, arraySum: function(arr) { return arr ? arr.reduce((a, b) => a + b, 0) : 0; }, @@ -840,6 +1012,42 @@ const pinescript = { arrayMax: function(arr) { return arr && arr.length > 0 ? Math.max(...arr) : null; }, + arrayStdev: function(arr) { + if (!arr || arr.length === 0) return null; + const mean = arr.reduce((a, b) => a + b, 0) / arr.length; + const variance = arr.reduce((sum, val) => sum + Math.pow(val - mean, 2), 0) / arr.length; + return Math.sqrt(variance); + }, + arrayVariance: function(arr) { + if (!arr || arr.length === 0) return null; + const mean = arr.reduce((a, b) => a + b, 0) / arr.length; + return arr.reduce((sum, val) => sum + Math.pow(val - mean, 2), 0) / arr.length; + }, + arrayCovariance: function(arr1, arr2) { + if (!arr1 || !arr2 || arr1.length === 0 || arr1.length !== arr2.length) return null; + const mean1 = arr1.reduce((a, b) => a + b, 0) / arr1.length; + const mean2 = arr2.reduce((a, b) => a + b, 0) / arr2.length; + let cov = 0; + for (let i = 0; i < arr1.length; i++) { + cov += (arr1[i] - mean1) * (arr2[i] - mean2); + } + return cov / arr1.length; + }, + mode: function(arr) { + if (!arr || arr.length === 0) return null; + const counts = new Map(); + let maxCount = 0; + let modeVal = arr[0]; + for (const val of arr) { + const c = (counts.get(val) || 0) + 1; + counts.set(val, c); + if (c > maxCount) { + maxCount = c; + modeVal = val; + } + } + return modeVal; + }, strLength: function(str) { return str ? str.length : 0; }, @@ -895,6 +1103,84 @@ const pinescript = { strReverse: function(str) { return str ? str.split('').reverse().join('') : ''; }, + strFormat: function(formatStr, ...args) { + if (!formatStr) return ''; + return formatStr.replace(/\{(\d+)\}/g, (match, idx) => { + const i = parseInt(idx, 10); + return i < args.length ? String(args[i]) : match; + }); + }, + input: function(defval, title, tooltip, inline, group) { + return defval; + }, + inputInt: function(defval = 0, title, minval, maxval, step, tooltip, inline, group) { + return defval; + }, + inputFloat: function(defval = 0.0, title, minval, maxval, step, tooltip, inline, group) { + return defval; + }, + inputBool: function(defval = false, title, tooltip, inline, group) { + return defval; + }, + inputString: function(defval = '', title, options, tooltip, inline, group) { + return defval; + }, + inputSource: function(defval = null, title, tooltip, inline, group) { + return defval; + }, + inputColor: function(defval = '#000000', title, tooltip, inline, group) { + return defval; + }, + inputTime: function(defval = 0, title, tooltip, inline, group) { + return defval; + }, + mathSign: function(value) { + return Math.sign(value); + }, + mathAvg: function(...args) { + const valid = args.filter(v => v !== null && v !== undefined && !isNaN(v)); + return valid.length > 0 ? valid.reduce((a, b) => a + b, 0) / valid.length : null; + }, + mathSum: function(series, length) { + if (!series || series.length < length) return null; + return series.slice(-length).reduce((a, b) => a + b, 0); + }, + mathRandom: function(min = 0, max = 1) { + return min + Math.random() * (max - min); + }, + mathTodegrees: function(radians) { + return radians * (180 / Math.PI); + }, + mathToradians: function(degrees) { + return degrees * (Math.PI / 180); + }, + colorT: function(color, transparency) { + if (transparency !== undefined) { + return { color, transparency }; + } + if (color && typeof color === 'object' && 'transparency' in color) { + return color.transparency; + } + return 0; + }, + tableCellSetText: function(table, column, row, text) { + return null; + }, + tableCellSetBgcolor: function(table, column, row, bgcolor) { + return null; + }, + tableMergeCells: function(table, startColumn, startRow, endColumn, endRow) { + return null; + }, + tableDelete: function(table) { + return null; + }, + tableClear: function(table, startColumn, startRow, endColumn, endRow) { + return null; + }, + maxBarsBack: function(series, length) { + return null; + }, pi: 3.141592653589793, e: 2.718281828459045, true: true, @@ -905,8 +1191,7 @@ const pinescript = { const info = { ticker: 'AAPL', tickerid: 'NASDAQ:AAPL', prefix: 'NASDAQ', root: 'AAPL', suffix: '' }; return info[type] || ''; }, - time: 1771178052232, - timenow: 1771178052232, + timenow: 1781574528000, barstate: "LAST", dividends: {}, splits: {}, @@ -923,7 +1208,7 @@ const pinescript = { return null; }, tr: function(high, low, close) { - // Pine signature: ta.tr(true) + // Handles both the boolean shorthand ta.tr(true) and explicit high/low/close arguments if (typeof high === 'boolean') { const H = globalThis.high; const L = globalThis.low; @@ -934,7 +1219,7 @@ const pinescript = { return Math.max(H[i] - L[i], Math.abs(H[i] - prevClose), Math.abs(L[i] - prevClose)); } - // Fallback: treat inputs as scalars for current bar + // When arrays are passed, compute true range from the last bar using the previous close if (Array.isArray(close)) { const prevClose = close[close.length - 2] || close[0]; const currHigh = Array.isArray(high) ? high[high.length - 1] : high; @@ -945,6 +1230,92 @@ const pinescript = { return Math.max(high - low, Math.abs(high - close), Math.abs(low - close)); }, + arrayFirst: function(arr) { + if (!arr || arr.length === 0) return null; + return arr[0]; + }, + arrayLast: function(arr) { + if (!arr || arr.length === 0) return null; + return arr[arr.length - 1]; + }, + arrayJoin: function(arr, separator = ',') { + if (!arr) return ''; + return arr.join(separator); + }, + arrayConcat: function(arr1, arr2) { + if (!arr1) return arr2 || []; + if (!arr2) return arr1; + return arr1.concat(arr2); + }, + arrayCopy: function(arr) { + if (!arr) return []; + return [...arr]; + }, + arrayBinarySearch: function(arr, value) { + if (!arr || arr.length === 0) return -1; + let lo = 0, hi = arr.length - 1; + while (lo <= hi) { + const mid = (lo + hi) >>> 1; + if (arr[mid] === value) return mid; + if (arr[mid] < value) lo = mid + 1; + else hi = mid - 1; + } + return -1; + }, + arrayRange: function(arr) { + if (!arr || arr.length === 0) return 0; + return Math.max(...arr) - Math.min(...arr); + }, + arrayMedian: function(arr) { + if (!arr || arr.length === 0) return null; + const sorted = [...arr].sort((a, b) => a - b); + const mid = Math.floor(sorted.length / 2); + return sorted.length % 2 !== 0 ? sorted[mid] : (sorted[mid - 1] + sorted[mid]) / 2; + }, + arrayMode: function(arr) { + if (!arr || arr.length === 0) return null; + const freq = {}; + let maxCount = 0; + let mode = arr[0]; + for (const v of arr) { + freq[v] = (freq[v] || 0) + 1; + if (freq[v] > maxCount) { maxCount = freq[v]; mode = v; } + } + return mode; + }, + arrayPercentile: function(arr, percentile) { + if (!arr || arr.length === 0) return null; + const sorted = [...arr].sort((a, b) => a - b); + const idx = (percentile / 100) * (sorted.length - 1); + const lo = Math.floor(idx); + const hi = Math.ceil(idx); + if (lo === hi) return sorted[lo]; + return sorted[lo] + (sorted[hi] - sorted[lo]) * (idx - lo); + }, + arrayPercentileLinearInterpolation: function(arr, percentile) { + return builtins.get('arrayPercentile')(arr, percentile); + }, + arrayPercentileNearestRank: function(arr, percentile) { + if (!arr || arr.length === 0) return null; + const sorted = [...arr].sort((a, b) => a - b); + const idx = Math.ceil((percentile / 100) * sorted.length) - 1; + return sorted[Math.max(0, idx)]; + }, + arrayAbs: function(arr) { + if (!arr) return []; + return arr.map(v => Math.abs(v)); + }, + arrayEvery: function(arr, callback) { + if (!arr) return false; + return arr.every(callback); + }, + arraySome: function(arr, callback) { + if (!arr) return false; + return arr.some(callback); + }, + requestFinancial: function() { + return null; + }, }; @@ -1101,6 +1472,115 @@ globalThis.barmerge = globalThis.barmerge || { }; +function __pineNS(real) { + + const base = real || {}; + + return new Proxy(base, { + + get(target, key) { + + if (key in target) return target[key]; + + if (typeof key === "symbol") return target[key]; + + // Unknown member: usable as a value (null), a constant, or a no-op function. + + const noop = function() { return null; }; + + noop.toString = () => ""; + + return noop; + + } + + }); + +} + +globalThis.ta = globalThis.ta || __pineNS({}); + +globalThis.math = globalThis.math || __pineNS({ pi: Math.PI, e: Math.E, phi: 1.618033988749895, rphi: 0.618033988749895 }); + +globalThis.chart = globalThis.chart || __pineNS({ point: __pineNS({}), bg_color: null, fg_color: null }); + +globalThis.line = globalThis.line || __pineNS({ style_solid: "solid", style_dashed: "dashed", style_dotted: "dotted" }); + +globalThis.box = globalThis.box || __pineNS({}); + +globalThis.label = globalThis.label || __pineNS({ style_label_down: "label_down", style_label_up: "label_up", style_none: "none" }); + +globalThis.polyline = globalThis.polyline || __pineNS({}); + +globalThis.linefill = globalThis.linefill || __pineNS({}); + +globalThis.matrix = globalThis.matrix || __pineNS({}); + +globalThis.map = globalThis.map || __pineNS({}); + +globalThis.session = globalThis.session || __pineNS({ regular: "regular", extended: "extended" }); + +globalThis.dayofweek = globalThis.dayofweek || __pineNS({ sunday: 1, monday: 2, tuesday: 3, wednesday: 4, thursday: 5, friday: 6, saturday: 7 }); + +globalThis.timeframe = __pineNS(Object.assign(globalThis.timeframe || {}, { period: (globalThis.timeframe && globalThis.timeframe.period) || "D", isintraday: false, isdaily: true, multiplier: 1 })); + +globalThis.request = __pineNS(globalThis.request || {}); + +globalThis.input = __pineNS(globalThis.input || {}); + +globalThis.adjustment = globalThis.adjustment || __pineNS({}); + +globalThis.earnings = globalThis.earnings || __pineNS({}); + +globalThis.dividends = globalThis.dividends || __pineNS({}); + +globalThis.splits = globalThis.splits || __pineNS({}); + +globalThis.currency = globalThis.currency || __pineNS({}); + +globalThis.display = globalThis.display || __pineNS({ none: "none", all: "all", pane: "pane", data_window: "data_window", status_line: "status_line", price_scale: "price_scale" }); + +globalThis.format = globalThis.format || __pineNS({ price: "price", volume: "volume", percent: "percent", mintick: "mintick", inherit: "inherit" }); + +globalThis.scale = globalThis.scale || __pineNS({ left: "left", right: "right", none: "none" }); + +globalThis.font = globalThis.font || __pineNS({ family_default: "default", family_monospace: "monospace" }); + +globalThis.xloc = globalThis.xloc || __pineNS({ bar_index: "bar_index", bar_time: "bar_time" }); + +globalThis.yloc = globalThis.yloc || __pineNS({ price: "price", abovebar: "abovebar", belowbar: "belowbar" }); + +globalThis.extend = globalThis.extend || __pineNS({ none: "none", left: "left", right: "right", both: "both" }); + +globalThis.order = globalThis.order || __pineNS({ ascending: "ascending", descending: "descending" }); + +globalThis.alert = globalThis.alert || Object.assign(function() { return null; }, { freq_once_per_bar: "once_per_bar", freq_once_per_bar_close: "once_per_bar_close", freq_all: "all" }); + +globalThis.dayofmonth = globalThis.dayofmonth || function(t) { return new Date(t || 0).getUTCDate(); }; + +globalThis.plot = globalThis.plot || __pineNS({ style_line: "line", style_linebr: "linebr", style_stepline: "stepline", style_histogram: "histogram", style_cross: "cross", style_area: "area", style_areabr: "areabr", style_columns: "columns", style_circles: "circles" }); + +globalThis.int = globalThis.int || function(x) { return x == null ? null : Math.trunc(Number(x)); }; + +globalThis.float = globalThis.float || function(x) { return x == null ? null : Number(x); }; + +globalThis.bool = globalThis.bool || function(x) { return Boolean(x); }; + +globalThis.string = globalThis.string || function(x) { return x == null ? null : String(x); }; + +globalThis.dayofweek = globalThis.dayofweek || __pineNS({ sunday: 1, monday: 2, tuesday: 3, wednesday: 4, thursday: 5, friday: 6, saturday: 7 }); + +globalThis.str = globalThis.str || __pineNS({}); + +pinescript.math = globalThis.math; + +pinescript.ta = globalThis.ta; + +pinescript.str = globalThis.str; + +pinescript.array = __pineNS(globalThis.array); + + pinescript.color = { hex: function(s) { @@ -1190,10 +1670,10 @@ pinescript.table = { }; -// Input parameters +// Script input parameters and their defaults -// Main script logic +// The transpiled script logic, called once per bar function main() { @@ -1210,37 +1690,37 @@ function main() { // Options: {"overlay":true} let BULL_COLOR = pinescript.color.hex("#089981"); let BEAR_COLOR = pinescript.color.hex("#f23645"); - let srcInput = input.source(close, "Source"); - let atrMultInput = input.float(1.5, "ATR Multiplier", ({ minval: 0, step: 0.1, tooltip: "Defines the noise threshold by multiplying the 200-period ATR." })); - let muInput = input.float(0.6, "Adaptation Rate (μ)", ({ minval: 0, maxval: 1, step: 0.1, tooltip: "Controls how fast the filter adapts to price changes." })); - let threshold = (ta.atr(200) * atrMultInput); + let srcInput = pinescript.inputSource(close, "Source"); + let atrMultInput = pinescript.inputFloat(1.5, "ATR Multiplier", ({ minval: 0, step: 0.1, tooltip: "Defines the noise threshold by multiplying the 200-period ATR." })); + let muInput = pinescript.inputFloat(0.6, "Adaptation Rate (μ)", ({ minval: 0, maxval: 1, step: 0.1, tooltip: "Controls how fast the filter adapts to price changes." })); + let threshold = (pinescript.atr(200) * atrMultInput); if (state.z === undefined) state.z = null; if (state.v === undefined) state.v = 0; if ((bar_index === 0)) { state.z = srcInput; } else { - let zPrev = pinescript.offset(state.z, 1); - let vPrev = pinescript.offset(state.v, 1); + let zPrev = pinescript.hist(0, state.z, 1); + let vPrev = pinescript.hist(1, state.v, 1); let zPred = (zPrev + vPrev); let zTemp = (zPred + (muInput * (srcInput - zPred))); let diff = (zTemp - zPrev); if ((pinescript.abs(diff) > threshold)) { - state.v = (math.sign(diff) * (pinescript.abs(diff) - threshold)); + state.v = (pinescript.sign(diff) * (pinescript.abs(diff) - threshold)); } else { state.v = 0; } state.z = (zPrev + state.v); } if (state.trend === undefined) state.trend = 0; - if ((state.z > pinescript.offset(state.z, 1))) { + if ((state.z > pinescript.hist(2, state.z, 1))) { state.trend = 1; } else { - if ((state.z < pinescript.offset(state.z, 1))) { + if ((state.z < pinescript.hist(3, state.z, 1))) { state.trend = -1; } } - let upChange = ((state.trend === 1) && (pinescript.offset(state.trend, 1) === -1)); - let downChange = ((state.trend === -1) && (pinescript.offset(state.trend, 1) === 1)); + let upChange = ((state.trend === 1) && (pinescript.hist(4, state.trend, 1) === -1)); + let downChange = ((state.trend === -1) && (pinescript.hist(5, state.trend, 1) === 1)); let zColor = ((state.trend === 1) ? BULL_COLOR : BEAR_COLOR); let zPlot = pinescript.plot(state.z, "L1 Proximal Filter", ({ color: zColor, linewidth: 3 })); let srcPlot = pinescript.plot(srcInput, "Source Plot", ({ color: null })); @@ -1248,13 +1728,73 @@ function main() { let fillTopColor = (showFill ? pinescript.color.new(zColor, 50) : null); let fillBottomColor = (showFill ? pinescript.color.new(zColor, 100) : null); pinescript.fill(srcPlot, zPlot, srcInput, state.z, fillTopColor, fillBottomColor, "Trend Fill"); - let switchVal = ((upChange || downChange) ? pinescript.offset(state.z, 1) : null); + let switchVal = ((upChange || downChange) ? pinescript.hist(6, state.z, 1) : null); let switchColor = (upChange ? BULL_COLOR : BEAR_COLOR); pinescript.plot(switchVal, "Trend Switch Dot", ({ color: switchColor, style: plot.style_circles, linewidth: 4, offset: -1 })); } -// Export for use -export { main +// The bar-by-bar engine: runs main() once per bar over the dataset. +function run(data, options = {}) { + data = data || {}; + const inClose = (data.close || []).map(Number); + const n = inClose.length; + const inOpen = (data.open || inClose).map(Number); + const inHigh = (data.high || inClose).map(Number); + const inLow = (data.low || inClose).map(Number); + const inVol = (data.volume || new Array(n).fill(0)).map(Number); + const inTime = (data.time || inClose.map((_, i) => i)).map(Number); + + // Reset all persistent and per-run state so repeated runs are independent. + globalThis.__pineState = {}; + globalThis.__pineRuntime = { plots: {}, plotshapes: {}, alerts: [], bars: n, inputs: options.inputs || {} }; + pinescript.__rt = {}; + pinescript.__bar = 0; + + // Growing OHLCV windows: each is a SeriesArray that gains one element per bar, + // so window built-ins like sma and highest see history up to the current bar. + const O = pinescript.asSeries([]), H = pinescript.asSeries([]), L = pinescript.asSeries([]); + const C = pinescript.asSeries([]), V = pinescript.asSeries([]), T = pinescript.asSeries([]); + const HL2 = pinescript.asSeries([]), HLC3 = pinescript.asSeries([]), OHLC4 = pinescript.asSeries([]); + globalThis.open = O; globalThis.high = H; globalThis.low = L; globalThis.close = C; + globalThis.volume = V; globalThis.time = T; + globalThis.hl2 = HL2; globalThis.hlc3 = HLC3; globalThis.ohlc4 = OHLC4; + globalThis.syminfo = globalThis.syminfo || { tickerid: 'SYNTHETIC', ticker: 'SYNTHETIC', mintick: 0.01 }; + + const rt = globalThis.__pineRuntime; + for (let i = 0; i < n; i++) { + O.push(inOpen[i]); H.push(inHigh[i]); L.push(inLow[i]); C.push(inClose[i]); V.push(inVol[i]); T.push(inTime[i]); + HL2.push((inHigh[i] + inLow[i]) / 2); + HLC3.push((inHigh[i] + inLow[i] + inClose[i]) / 3); + OHLC4.push((inOpen[i] + inHigh[i] + inLow[i] + inClose[i]) / 4); + + pinescript.__bar = i; + rt.__barIndex = i; + rt.__plotIdx = 0; + rt.__shapeIdx = 0; + globalThis.bar_index = i; + globalThis.last_bar_index = n - 1; + globalThis.barstate = { + isfirst: i === 0, islast: i === n - 1, isrealtime: false, ishistory: true, + isconfirmed: true, isnew: true, islastconfirmedhistory: i === n - 1, + }; + + main(); + } + + // Backfill skipped bars so every output series has exactly n entries. + for (const k of Object.keys(rt.plots)) { + const d = rt.plots[k].data; + for (let i = 0; i < n; i++) if (d[i] === undefined) d[i] = null; + } + for (const k of Object.keys(rt.plotshapes)) { + const d = rt.plotshapes[k].data; + for (let i = 0; i < n; i++) if (d[i] === undefined) d[i] = false; + } + return rt; +} + + +export { main, run }; diff --git a/converted/Quantum_Structure_jjart.err.txt b/converted/Quantum_Structure_jjart.err.txt index aa3aabd..73e233d 100644 --- a/converted/Quantum_Structure_jjart.err.txt +++ b/converted/Quantum_Structure_jjart.err.txt @@ -1 +1,4 @@ -Error: Unexpected token: ) at line 489 +Error: Unexpected token ")" at line 489, column 143 while parsing if statement + +Command failed: node src/cli.js "examples\Quantum_Structure_jjart.pine" "converted\Quantum_Structure_jjart.js" +Error: Unexpected token ")" at line 489, column 143 while parsing if statement diff --git a/converted/RBT_strategy.js b/converted/RBT_strategy.js index 83f444d..3468aca 100644 --- a/converted/RBT_strategy.js +++ b/converted/RBT_strategy.js @@ -1,12 +1,63 @@ -// PineScript to JavaScript Transpiled Code +// Auto-generated JavaScript from PineScript source -// Generated automatically +// Do not edit by hand -- re-run the converter instead -// Built-in PineScript functions +// All the built-in PineScript functions that scripts depend on at runtime const pinescript = { + __scalar: function(v) { + if (v === null || v === undefined) return v; + if (typeof v === 'object' && typeof v.valueOf === 'function') { + const s = v.valueOf(); + if (typeof s === 'number' || typeof s === 'boolean' || s === null) return s; + } + return v; + }, + series: function(id, value) { + const rt = this.__rt || (this.__rt = {}); + const store = rt.series || (rt.series = {}); + let buf = store[id]; + if (!buf) buf = store[id] = []; + buf[this.__bar | 0] = this.__scalar(value); + return buf; + }, + hist: function(id, value, n) { + const buf = this.series(id, value); + const idx = (this.__bar | 0) - (n | 0); + return idx >= 0 && idx < buf.length ? buf[idx] : null; + }, + time: function(timeframe, session) { + const t = globalThis.time; + if (t && typeof t.valueOf === 'function') return t.valueOf(); + return Array.isArray(t) ? t[t.length - 1] : t; + }, + sign: function(x) { + if (x === null || x === undefined || (typeof x === 'number' && isNaN(x))) return null; + return Math.sign(Number(x)); + }, + unpack: function(value, count) { + if (Array.isArray(value)) return value; + if (value != null && typeof value[Symbol.iterator] === 'function') return Array.from(value); + return new Array(count || 0).fill(null); + }, + alertcondition: function(condition, ...rest) { + if (globalThis.__pineRuntime) { + globalThis.__pineRuntime.alerts.push({ condition, args: rest }); + } + return null; + }, + barcolor: function(color) { return null; }, + bgcolor: function(color, title, editable, showLast) { + return null; + }, + fill: function(series1, series2, color, title, editable) { + return null; + }, + hline: function(_price, _title, _opts) { + return null; + }, sma: function(series, length) { if (series === null || series === undefined) return null; if (!Array.isArray(series)) series = [series]; @@ -84,6 +135,13 @@ const pinescript = { } return weightSum > 0 ? sum / weightSum : null; }, + swma: function(series) { + if (series === null || series === undefined) return null; + if (!Array.isArray(series)) series = [series]; + if (series.length < 4) return null; + const s = series.slice(-4); + return (s[0] * 1 + s[1] * 2 + s[2] * 2 + s[3] * 1) / 6; + }, atr: function(high, low, close, length = 14) { if (!Array.isArray(high) || !Array.isArray(low) || !Array.isArray(close)) return null; if (high.length < length + 1 || low.length < length + 1 || close.length < length + 1) return null; @@ -114,6 +172,53 @@ const pinescript = { const atr = builtins.get('atr')(high, low, close, length); return { middle: sma, upper: sma + mult * atr, lower: sma - mult * atr }; }, + supertrend: function(factor, atrPeriod, high, low, close) { + if (!Array.isArray(high) || !Array.isArray(low) || !Array.isArray(close)) return [null, null]; + if (high.length < atrPeriod + 1) return [null, null]; + const atrVal = builtins.get('atr')(high, low, close, atrPeriod); + if (atrVal === null) return [null, null]; + const lastIdx = close.length - 1; + const hl2 = (high[lastIdx] + low[lastIdx]) / 2; + const upperBand = hl2 + factor * atrVal; + const lowerBand = hl2 - factor * atrVal; + // Determine direction: 1 means downtrend (price below band), -1 means uptrend + const direction = close[lastIdx] > upperBand ? -1 : close[lastIdx] < lowerBand ? 1 : -1; + const supertrendValue = direction === -1 ? lowerBand : upperBand; + return [supertrendValue, direction]; + }, + ta_supertrend: function(factor, atrPeriod) { + const H = globalThis.high; + const L = globalThis.low; + const C = globalThis.close; + return builtins.get('supertrend')(factor, atrPeriod, H, L, C); + }, + dmi: function(diLength, adxSmoothing, high, low, close) { + if (!Array.isArray(high) || !Array.isArray(low) || !Array.isArray(close)) return { plusDI: null, minusDI: null, adx: null }; + if (high.length < diLength + 1) return { plusDI: null, minusDI: null, adx: null }; + let plusDMSum = 0, minusDMSum = 0, trSum = 0; + for (let i = 1; i <= diLength; i++) { + const idx = high.length - 1 - diLength + i; + const upMove = high[idx] - high[idx - 1]; + const downMove = low[idx - 1] - low[idx]; + plusDMSum += (upMove > downMove && upMove > 0) ? upMove : 0; + minusDMSum += (downMove > upMove && downMove > 0) ? downMove : 0; + const tr = Math.max( + high[idx] - low[idx], + Math.abs(high[idx] - close[idx - 1]), + Math.abs(low[idx] - close[idx - 1]) + ); + trSum += tr; + } + const plusDI = trSum > 0 ? (plusDMSum / trSum) * 100 : 0; + const minusDI = trSum > 0 ? (minusDMSum / trSum) * 100 : 0; + const diSum = plusDI + minusDI; + const dx = diSum > 0 ? Math.abs(plusDI - minusDI) / diSum * 100 : 0; + return { plusDI, minusDI, adx: dx }; + }, + adx: function(diLength, adxSmoothing, high, low, close) { + const result = builtins.get('dmi')(diLength, adxSmoothing, high, low, close); + return result ? result.adx : null; + }, rsi: function(close, length = 14) { if (close === null || close === undefined) return null; if (!Array.isArray(close)) close = [close]; @@ -171,9 +276,7 @@ const pinescript = { return { macd: macdLine, signal: signalLine, histogram: histogram }; }, cci: function(a, b, c, d) { - // Pine signatures: - // - ta.cci(source, length) - // - ta.cci(high, low, close, length) + // Supports both ta.cci(source, length) and ta.cci(high, low, close, length) signatures let source; let length; if (Array.isArray(a) && typeof b === 'number' && c === undefined) { @@ -304,6 +407,15 @@ const pinescript = { stdev: function(source, length) { return Math.sqrt(builtins.get('variance')(source, length)); }, + median: function(source, length) { + if (!source || source.length < length) return null; + const sorted = [...source.slice(-length)].sort((a, b) => a - b); + const mid = Math.floor(sorted.length / 2); + return sorted.length % 2 !== 0 ? sorted[mid] : (sorted[mid - 1] + sorted[mid]) / 2; + }, + ta_median: function(source, length) { + return builtins.get('median')(source, length); + }, hl2: function(high, low) { return high && low ? (high[high.length - 1] + low[low.length - 1]) / 2 : null; }, @@ -420,6 +532,24 @@ const pinescript = { return (series1[i - 1] < series2[i - 1] && series1[i] >= series2[i]) || (series1[i - 1] > series2[i - 1] && series1[i] <= series2[i]); }, + crossover: function(series1, series2) { + if (series1 === null || series1 === undefined) return false; + if (series2 === null || series2 === undefined) return false; + if (!Array.isArray(series1)) series1 = [series1]; + if (!Array.isArray(series2)) series2 = [series2]; + if (series1.length < 2 || series2.length < 2) return false; + const i = series1.length - 1; + return series1[i - 1] < series2[i - 1] && series1[i] >= series2[i]; + }, + crossunder: function(series1, series2) { + if (series1 === null || series1 === undefined) return false; + if (series2 === null || series2 === undefined) return false; + if (!Array.isArray(series1)) series1 = [series1]; + if (!Array.isArray(series2)) series2 = [series2]; + if (series1.length < 2 || series2.length < 2) return false; + const i = series1.length - 1; + return series1[i - 1] > series2[i - 1] && series1[i] <= series2[i]; + }, offset: function(series, offset) { if (!Array.isArray(series)) return null; const idx = series.length - 1 - offset; @@ -500,12 +630,14 @@ const pinescript = { return builtins.get('nz')(value, replacement); }, plot: function(series, title = '', color = null, linewidth = 1) { - globalThis.__pineRuntime.plots.push({ - series, - title, - color, - linewidth - }); + const rt = globalThis.__pineRuntime; + if (!rt) return series; + const bar = rt.__barIndex | 0; + const ord = (rt.__plotIdx = (rt.__plotIdx | 0) + 1) - 1; + const key = (title && String(title)) || ('plot_' + ord); + let p = rt.plots[key]; + if (!p) p = rt.plots[key] = { title: key, color, linewidth, data: [] }; + p.data[bar] = this.__scalar(series); return series; }, lineNew: function(x1, y1, x2, y2, opts = {}) { @@ -514,20 +646,72 @@ const pinescript = { lineDelete: function(l) { return null; }, - labelNew: function(...args) { - if (globalThis.__pineRuntime) { - globalThis.__pineRuntime.labels = globalThis.__pineRuntime.labels || []; - globalThis.__pineRuntime.labels.push({ args }); - } - return { args }; + lineSetXY: function(line, point, x, y) { + if (!line) return null; + if (point === 0 || point === 'x1') { line.x1 = x; line.y1 = y; } + else { line.x2 = x; line.y2 = y; } + return line; + }, + lineGetX: function(line, point) { + if (!line) return null; + return point === 0 || point === 'x1' ? line.x1 : line.x2; + }, + lineGetY: function(line, point) { + if (!line) return null; + return point === 0 || point === 'y1' ? line.y1 : line.y2; + }, + labelNew: function(x, y, text = '', opts = {}) { + return { x, y, text, opts, _type: 'label' }; }, labelDelete: function(l) { return null; }, + labelSetText: function(label, text) { + if (!label) return null; + label.text = text; + return label; + }, + labelGetText: function(label) { + if (!label) return ''; + return label.text || ''; + }, + boxNew: function(left, top, right, bottom, opts = {}) { + return { left, top, right, bottom, opts, _type: 'box' }; + }, + boxDelete: function(box) { + return null; + }, + boxSetLeftTop: function(box, left, top) { + if (!box) return null; + box.left = left; + box.top = top; + return box; + }, + boxSetRightBottom: function(box, right, bottom) { + if (!box) return null; + box.right = right; + box.bottom = bottom; + return box; + }, + polylineNew: function(points, opts = {}) { + return { points: points || [], opts, _type: 'polyline' }; + }, + polylineDelete: function(poly) { + return null; + }, plotshape: function(condition, ...rest) { - if (globalThis.__pineRuntime) { - globalThis.__pineRuntime.plotshapes.push({ condition, args: rest }); + const rt = globalThis.__pineRuntime; + if (!rt) return condition; + const bar = rt.__barIndex | 0; + const ord = (rt.__shapeIdx = (rt.__shapeIdx | 0) + 1) - 1; + let key = 'shape_' + ord; + for (const r of rest) { + if (typeof r === 'string') { key = r; break; } + if (r && typeof r === 'object' && r.title) { key = String(r.title); break; } } + let s = rt.plotshapes[key]; + if (!s) s = rt.plotshapes[key] = { title: key, data: [] }; + s.data[bar] = !!this.__scalar(condition); return condition; }, indicator: function(title, shorttitle, overlay, opts) { @@ -542,27 +726,12 @@ const pinescript = { } return null; }, - alertcondition: function(condition, ...rest) { - if (globalThis.__pineRuntime) { - globalThis.__pineRuntime.alerts.push({ condition, args: rest }); - } - return null; - }, plotbar: function(open, high, low, close, title, color, editable, showLast) { return { open, high, low, close }; }, plotcandle: function(open, high, low, close, title, color, wickColor, borderColor, editable, showLast) { return { open, high, low, close }; }, - hline: function(_price, _title, _opts) { - return null; - }, - bgcolor: function(color, title, editable, showLast) { - return null; - }, - fill: function(series1, series2, color, title, editable) { - return null; - }, alert: function(condition, message, frequency) { console.log('Alert:', condition ? message : 'Condition not met'); return condition; @@ -828,6 +997,9 @@ const pinescript = { arrayIndexOf: function(arr, value) { return arr ? arr.indexOf(value) : -1; }, + arrayLastIndexOf: function(arr, value) { + return arr ? arr.lastIndexOf(value) : -1; + }, arraySum: function(arr) { return arr ? arr.reduce((a, b) => a + b, 0) : 0; }, @@ -840,6 +1012,42 @@ const pinescript = { arrayMax: function(arr) { return arr && arr.length > 0 ? Math.max(...arr) : null; }, + arrayStdev: function(arr) { + if (!arr || arr.length === 0) return null; + const mean = arr.reduce((a, b) => a + b, 0) / arr.length; + const variance = arr.reduce((sum, val) => sum + Math.pow(val - mean, 2), 0) / arr.length; + return Math.sqrt(variance); + }, + arrayVariance: function(arr) { + if (!arr || arr.length === 0) return null; + const mean = arr.reduce((a, b) => a + b, 0) / arr.length; + return arr.reduce((sum, val) => sum + Math.pow(val - mean, 2), 0) / arr.length; + }, + arrayCovariance: function(arr1, arr2) { + if (!arr1 || !arr2 || arr1.length === 0 || arr1.length !== arr2.length) return null; + const mean1 = arr1.reduce((a, b) => a + b, 0) / arr1.length; + const mean2 = arr2.reduce((a, b) => a + b, 0) / arr2.length; + let cov = 0; + for (let i = 0; i < arr1.length; i++) { + cov += (arr1[i] - mean1) * (arr2[i] - mean2); + } + return cov / arr1.length; + }, + mode: function(arr) { + if (!arr || arr.length === 0) return null; + const counts = new Map(); + let maxCount = 0; + let modeVal = arr[0]; + for (const val of arr) { + const c = (counts.get(val) || 0) + 1; + counts.set(val, c); + if (c > maxCount) { + maxCount = c; + modeVal = val; + } + } + return modeVal; + }, strLength: function(str) { return str ? str.length : 0; }, @@ -895,6 +1103,84 @@ const pinescript = { strReverse: function(str) { return str ? str.split('').reverse().join('') : ''; }, + strFormat: function(formatStr, ...args) { + if (!formatStr) return ''; + return formatStr.replace(/\{(\d+)\}/g, (match, idx) => { + const i = parseInt(idx, 10); + return i < args.length ? String(args[i]) : match; + }); + }, + input: function(defval, title, tooltip, inline, group) { + return defval; + }, + inputInt: function(defval = 0, title, minval, maxval, step, tooltip, inline, group) { + return defval; + }, + inputFloat: function(defval = 0.0, title, minval, maxval, step, tooltip, inline, group) { + return defval; + }, + inputBool: function(defval = false, title, tooltip, inline, group) { + return defval; + }, + inputString: function(defval = '', title, options, tooltip, inline, group) { + return defval; + }, + inputSource: function(defval = null, title, tooltip, inline, group) { + return defval; + }, + inputColor: function(defval = '#000000', title, tooltip, inline, group) { + return defval; + }, + inputTime: function(defval = 0, title, tooltip, inline, group) { + return defval; + }, + mathSign: function(value) { + return Math.sign(value); + }, + mathAvg: function(...args) { + const valid = args.filter(v => v !== null && v !== undefined && !isNaN(v)); + return valid.length > 0 ? valid.reduce((a, b) => a + b, 0) / valid.length : null; + }, + mathSum: function(series, length) { + if (!series || series.length < length) return null; + return series.slice(-length).reduce((a, b) => a + b, 0); + }, + mathRandom: function(min = 0, max = 1) { + return min + Math.random() * (max - min); + }, + mathTodegrees: function(radians) { + return radians * (180 / Math.PI); + }, + mathToradians: function(degrees) { + return degrees * (Math.PI / 180); + }, + colorT: function(color, transparency) { + if (transparency !== undefined) { + return { color, transparency }; + } + if (color && typeof color === 'object' && 'transparency' in color) { + return color.transparency; + } + return 0; + }, + tableCellSetText: function(table, column, row, text) { + return null; + }, + tableCellSetBgcolor: function(table, column, row, bgcolor) { + return null; + }, + tableMergeCells: function(table, startColumn, startRow, endColumn, endRow) { + return null; + }, + tableDelete: function(table) { + return null; + }, + tableClear: function(table, startColumn, startRow, endColumn, endRow) { + return null; + }, + maxBarsBack: function(series, length) { + return null; + }, pi: 3.141592653589793, e: 2.718281828459045, true: true, @@ -905,8 +1191,7 @@ const pinescript = { const info = { ticker: 'AAPL', tickerid: 'NASDAQ:AAPL', prefix: 'NASDAQ', root: 'AAPL', suffix: '' }; return info[type] || ''; }, - time: 1771178052615, - timenow: 1771178052615, + timenow: 1781574528290, barstate: "LAST", dividends: {}, splits: {}, @@ -923,7 +1208,7 @@ const pinescript = { return null; }, tr: function(high, low, close) { - // Pine signature: ta.tr(true) + // Handles both the boolean shorthand ta.tr(true) and explicit high/low/close arguments if (typeof high === 'boolean') { const H = globalThis.high; const L = globalThis.low; @@ -934,7 +1219,7 @@ const pinescript = { return Math.max(H[i] - L[i], Math.abs(H[i] - prevClose), Math.abs(L[i] - prevClose)); } - // Fallback: treat inputs as scalars for current bar + // When arrays are passed, compute true range from the last bar using the previous close if (Array.isArray(close)) { const prevClose = close[close.length - 2] || close[0]; const currHigh = Array.isArray(high) ? high[high.length - 1] : high; @@ -945,6 +1230,92 @@ const pinescript = { return Math.max(high - low, Math.abs(high - close), Math.abs(low - close)); }, + arrayFirst: function(arr) { + if (!arr || arr.length === 0) return null; + return arr[0]; + }, + arrayLast: function(arr) { + if (!arr || arr.length === 0) return null; + return arr[arr.length - 1]; + }, + arrayJoin: function(arr, separator = ',') { + if (!arr) return ''; + return arr.join(separator); + }, + arrayConcat: function(arr1, arr2) { + if (!arr1) return arr2 || []; + if (!arr2) return arr1; + return arr1.concat(arr2); + }, + arrayCopy: function(arr) { + if (!arr) return []; + return [...arr]; + }, + arrayBinarySearch: function(arr, value) { + if (!arr || arr.length === 0) return -1; + let lo = 0, hi = arr.length - 1; + while (lo <= hi) { + const mid = (lo + hi) >>> 1; + if (arr[mid] === value) return mid; + if (arr[mid] < value) lo = mid + 1; + else hi = mid - 1; + } + return -1; + }, + arrayRange: function(arr) { + if (!arr || arr.length === 0) return 0; + return Math.max(...arr) - Math.min(...arr); + }, + arrayMedian: function(arr) { + if (!arr || arr.length === 0) return null; + const sorted = [...arr].sort((a, b) => a - b); + const mid = Math.floor(sorted.length / 2); + return sorted.length % 2 !== 0 ? sorted[mid] : (sorted[mid - 1] + sorted[mid]) / 2; + }, + arrayMode: function(arr) { + if (!arr || arr.length === 0) return null; + const freq = {}; + let maxCount = 0; + let mode = arr[0]; + for (const v of arr) { + freq[v] = (freq[v] || 0) + 1; + if (freq[v] > maxCount) { maxCount = freq[v]; mode = v; } + } + return mode; + }, + arrayPercentile: function(arr, percentile) { + if (!arr || arr.length === 0) return null; + const sorted = [...arr].sort((a, b) => a - b); + const idx = (percentile / 100) * (sorted.length - 1); + const lo = Math.floor(idx); + const hi = Math.ceil(idx); + if (lo === hi) return sorted[lo]; + return sorted[lo] + (sorted[hi] - sorted[lo]) * (idx - lo); + }, + arrayPercentileLinearInterpolation: function(arr, percentile) { + return builtins.get('arrayPercentile')(arr, percentile); + }, + arrayPercentileNearestRank: function(arr, percentile) { + if (!arr || arr.length === 0) return null; + const sorted = [...arr].sort((a, b) => a - b); + const idx = Math.ceil((percentile / 100) * sorted.length) - 1; + return sorted[Math.max(0, idx)]; + }, + arrayAbs: function(arr) { + if (!arr) return []; + return arr.map(v => Math.abs(v)); + }, + arrayEvery: function(arr, callback) { + if (!arr) return false; + return arr.every(callback); + }, + arraySome: function(arr, callback) { + if (!arr) return false; + return arr.some(callback); + }, + requestFinancial: function() { + return null; + }, }; @@ -1101,6 +1472,115 @@ globalThis.barmerge = globalThis.barmerge || { }; +function __pineNS(real) { + + const base = real || {}; + + return new Proxy(base, { + + get(target, key) { + + if (key in target) return target[key]; + + if (typeof key === "symbol") return target[key]; + + // Unknown member: usable as a value (null), a constant, or a no-op function. + + const noop = function() { return null; }; + + noop.toString = () => ""; + + return noop; + + } + + }); + +} + +globalThis.ta = globalThis.ta || __pineNS({}); + +globalThis.math = globalThis.math || __pineNS({ pi: Math.PI, e: Math.E, phi: 1.618033988749895, rphi: 0.618033988749895 }); + +globalThis.chart = globalThis.chart || __pineNS({ point: __pineNS({}), bg_color: null, fg_color: null }); + +globalThis.line = globalThis.line || __pineNS({ style_solid: "solid", style_dashed: "dashed", style_dotted: "dotted" }); + +globalThis.box = globalThis.box || __pineNS({}); + +globalThis.label = globalThis.label || __pineNS({ style_label_down: "label_down", style_label_up: "label_up", style_none: "none" }); + +globalThis.polyline = globalThis.polyline || __pineNS({}); + +globalThis.linefill = globalThis.linefill || __pineNS({}); + +globalThis.matrix = globalThis.matrix || __pineNS({}); + +globalThis.map = globalThis.map || __pineNS({}); + +globalThis.session = globalThis.session || __pineNS({ regular: "regular", extended: "extended" }); + +globalThis.dayofweek = globalThis.dayofweek || __pineNS({ sunday: 1, monday: 2, tuesday: 3, wednesday: 4, thursday: 5, friday: 6, saturday: 7 }); + +globalThis.timeframe = __pineNS(Object.assign(globalThis.timeframe || {}, { period: (globalThis.timeframe && globalThis.timeframe.period) || "D", isintraday: false, isdaily: true, multiplier: 1 })); + +globalThis.request = __pineNS(globalThis.request || {}); + +globalThis.input = __pineNS(globalThis.input || {}); + +globalThis.adjustment = globalThis.adjustment || __pineNS({}); + +globalThis.earnings = globalThis.earnings || __pineNS({}); + +globalThis.dividends = globalThis.dividends || __pineNS({}); + +globalThis.splits = globalThis.splits || __pineNS({}); + +globalThis.currency = globalThis.currency || __pineNS({}); + +globalThis.display = globalThis.display || __pineNS({ none: "none", all: "all", pane: "pane", data_window: "data_window", status_line: "status_line", price_scale: "price_scale" }); + +globalThis.format = globalThis.format || __pineNS({ price: "price", volume: "volume", percent: "percent", mintick: "mintick", inherit: "inherit" }); + +globalThis.scale = globalThis.scale || __pineNS({ left: "left", right: "right", none: "none" }); + +globalThis.font = globalThis.font || __pineNS({ family_default: "default", family_monospace: "monospace" }); + +globalThis.xloc = globalThis.xloc || __pineNS({ bar_index: "bar_index", bar_time: "bar_time" }); + +globalThis.yloc = globalThis.yloc || __pineNS({ price: "price", abovebar: "abovebar", belowbar: "belowbar" }); + +globalThis.extend = globalThis.extend || __pineNS({ none: "none", left: "left", right: "right", both: "both" }); + +globalThis.order = globalThis.order || __pineNS({ ascending: "ascending", descending: "descending" }); + +globalThis.alert = globalThis.alert || Object.assign(function() { return null; }, { freq_once_per_bar: "once_per_bar", freq_once_per_bar_close: "once_per_bar_close", freq_all: "all" }); + +globalThis.dayofmonth = globalThis.dayofmonth || function(t) { return new Date(t || 0).getUTCDate(); }; + +globalThis.plot = globalThis.plot || __pineNS({ style_line: "line", style_linebr: "linebr", style_stepline: "stepline", style_histogram: "histogram", style_cross: "cross", style_area: "area", style_areabr: "areabr", style_columns: "columns", style_circles: "circles" }); + +globalThis.int = globalThis.int || function(x) { return x == null ? null : Math.trunc(Number(x)); }; + +globalThis.float = globalThis.float || function(x) { return x == null ? null : Number(x); }; + +globalThis.bool = globalThis.bool || function(x) { return Boolean(x); }; + +globalThis.string = globalThis.string || function(x) { return x == null ? null : String(x); }; + +globalThis.dayofweek = globalThis.dayofweek || __pineNS({ sunday: 1, monday: 2, tuesday: 3, wednesday: 4, thursday: 5, friday: 6, saturday: 7 }); + +globalThis.str = globalThis.str || __pineNS({}); + +pinescript.math = globalThis.math; + +pinescript.ta = globalThis.ta; + +pinescript.str = globalThis.str; + +pinescript.array = __pineNS(globalThis.array); + + pinescript.color = { hex: function(s) { @@ -1190,10 +1670,10 @@ pinescript.table = { }; -// Input parameters +// Script input parameters and their defaults -// Main script logic +// The transpiled script logic, called once per bar function main() { @@ -1208,20 +1688,20 @@ function main() { null; // Study: LuckyFX RBtrader strategy // Options: {"overlay":true} - let enable_prev_candles = input.bool(true, "Check: Prev Candles Same Dir?"); - let same_prev_candles = input.int(2, "Prev Candles Same (0–4)", ({ minval: 0, maxval: 4 })); - let enable_body_percent = input.bool(true, "Check: Cur > % Prev Body?"); - let body_percent_min = input.float(0.3, "Body % Min (0.3)", ({ minval: 0.3, maxval: 2, step: 0.05 })); - let body_percent_max = input.float(3, "Body % Max (3.0)", ({ minval: 0.3, maxval: 3, step: 0.05 })); - let ignore_doji = input.bool(true, "Ignore if Prev is Doji"); - let enable_mean_range = input.bool(true, "Check: Cur >= Mean Full Candle?"); - let mean_length = input.int(5, "Mean Candle Range (2-50)", ({ minval: 2, maxval: 50 })); - let ignore_engulf = input.bool(true, "Ignore if Engulfing (Body Size)"); - let enable_wick_compare = input.bool(false, "Check: Cur wick > Prev wick (on/off)"); - let prev_open = pinescript.offset(open, 1); - let prev_close = pinescript.offset(close, 1); - let prev_high = pinescript.offset(high, 1); - let prev_low = pinescript.offset(low, 1); + let enable_prev_candles = pinescript.inputBool(true, "Check: Prev Candles Same Dir?"); + let same_prev_candles = pinescript.inputInt(2, "Prev Candles Same (0–4)", ({ minval: 0, maxval: 4 })); + let enable_body_percent = pinescript.inputBool(true, "Check: Cur > % Prev Body?"); + let body_percent_min = pinescript.inputFloat(0.3, "Body % Min (0.3)", ({ minval: 0.3, maxval: 2, step: 0.05 })); + let body_percent_max = pinescript.inputFloat(3, "Body % Max (3.0)", ({ minval: 0.3, maxval: 3, step: 0.05 })); + let ignore_doji = pinescript.inputBool(true, "Ignore if Prev is Doji"); + let enable_mean_range = pinescript.inputBool(true, "Check: Cur >= Mean Full Candle?"); + let mean_length = pinescript.inputInt(5, "Mean Candle Range (2-50)", ({ minval: 2, maxval: 50 })); + let ignore_engulf = pinescript.inputBool(true, "Ignore if Engulfing (Body Size)"); + let enable_wick_compare = pinescript.inputBool(false, "Check: Cur wick > Prev wick (on/off)"); + let prev_open = pinescript.hist(0, open, 1); + let prev_close = pinescript.hist(1, close, 1); + let prev_high = pinescript.hist(2, high, 1); + let prev_low = pinescript.hist(3, low, 1); let prev_body = pinescript.abs((prev_close - prev_open)); let prev_upper_wick = (prev_high - pinescript.max(prev_open, prev_close)); let prev_lower_wick = (pinescript.min(prev_open, prev_close) - prev_low); @@ -1238,7 +1718,7 @@ function main() { let cur_lower_wick = (pinescript.min(open, close) - low); let sum_range = 0; for (let i = 1; i <= mean_length; i++) { - sum_range = (sum_range + (pinescript.offset(high, i) - pinescript.offset(low, i))); + sum_range = (sum_range + (pinescript.hist(4, high, i) - pinescript.hist(5, low, i))); } let mean_prev_range = (sum_range / mean_length); let is_cur_range_big_enough = (!enable_mean_range || (cur_range >= mean_prev_range)); @@ -1246,7 +1726,7 @@ function main() { let match = true; if ((same_prev_candles > 0)) { for (let i = 2; i <= (same_prev_candles + 1); i++) { - match = (match && (((pinescript.offset(close, i) > pinescript.offset(open, i)) ? 1 : ((pinescript.offset(close, i) < pinescript.offset(open, i)) ? -1 : 0)) === prev_dir)); + match = (match && (((pinescript.hist(6, close, i) > pinescript.hist(7, open, i)) ? 1 : ((pinescript.hist(8, close, i) < pinescript.hist(9, open, i)) ? -1 : 0)) === prev_dir)); } } let last_n_same_as_prev = (!enable_prev_candles || ((prev_dir !== 0) && match)); @@ -1256,12 +1736,72 @@ function main() { let bear_bigger_than_bull = (((((((is_bear && prev_is_bull) && body_percent_ok) && (!ignore_doji || !prev_is_doji)) && (!ignore_engulf || !is_bearish_engulf)) && last_n_same_as_prev) && is_cur_range_big_enough) && wick_condition); pinescript.plotshape(bull_bigger_than_bear, ({ style: pinescript.shape.triangleup, color: pinescript.color.rgb(38, 129, 11), size: pinescript.size.small, location: pinescript.location.belowbar, title: "Bullish Signal" })); pinescript.plotshape(bear_bigger_than_bull, ({ style: pinescript.shape.triangledown, color: pinescript.color.rgb(226, 10, 10), size: pinescript.size.small, location: pinescript.location.abovebar, title: "Bearish Signal" })); - alertcondition(bull_bigger_than_bear, ({ title: "Bullish Signal", message: "Bullish > Bearish Setup" })); - alertcondition(bear_bigger_than_bull, ({ title: "Bearish Signal", message: "Bearish > Bullish Setup" })); + pinescript.alertcondition(bull_bigger_than_bear, ({ title: "Bullish Signal", message: "Bullish > Bearish Setup" })); + pinescript.alertcondition(bear_bigger_than_bull, ({ title: "Bearish Signal", message: "Bearish > Bullish Setup" })); } -// Export for use -export { main +// The bar-by-bar engine: runs main() once per bar over the dataset. +function run(data, options = {}) { + data = data || {}; + const inClose = (data.close || []).map(Number); + const n = inClose.length; + const inOpen = (data.open || inClose).map(Number); + const inHigh = (data.high || inClose).map(Number); + const inLow = (data.low || inClose).map(Number); + const inVol = (data.volume || new Array(n).fill(0)).map(Number); + const inTime = (data.time || inClose.map((_, i) => i)).map(Number); + + // Reset all persistent and per-run state so repeated runs are independent. + globalThis.__pineState = {}; + globalThis.__pineRuntime = { plots: {}, plotshapes: {}, alerts: [], bars: n, inputs: options.inputs || {} }; + pinescript.__rt = {}; + pinescript.__bar = 0; + + // Growing OHLCV windows: each is a SeriesArray that gains one element per bar, + // so window built-ins like sma and highest see history up to the current bar. + const O = pinescript.asSeries([]), H = pinescript.asSeries([]), L = pinescript.asSeries([]); + const C = pinescript.asSeries([]), V = pinescript.asSeries([]), T = pinescript.asSeries([]); + const HL2 = pinescript.asSeries([]), HLC3 = pinescript.asSeries([]), OHLC4 = pinescript.asSeries([]); + globalThis.open = O; globalThis.high = H; globalThis.low = L; globalThis.close = C; + globalThis.volume = V; globalThis.time = T; + globalThis.hl2 = HL2; globalThis.hlc3 = HLC3; globalThis.ohlc4 = OHLC4; + globalThis.syminfo = globalThis.syminfo || { tickerid: 'SYNTHETIC', ticker: 'SYNTHETIC', mintick: 0.01 }; + + const rt = globalThis.__pineRuntime; + for (let i = 0; i < n; i++) { + O.push(inOpen[i]); H.push(inHigh[i]); L.push(inLow[i]); C.push(inClose[i]); V.push(inVol[i]); T.push(inTime[i]); + HL2.push((inHigh[i] + inLow[i]) / 2); + HLC3.push((inHigh[i] + inLow[i] + inClose[i]) / 3); + OHLC4.push((inOpen[i] + inHigh[i] + inLow[i] + inClose[i]) / 4); + + pinescript.__bar = i; + rt.__barIndex = i; + rt.__plotIdx = 0; + rt.__shapeIdx = 0; + globalThis.bar_index = i; + globalThis.last_bar_index = n - 1; + globalThis.barstate = { + isfirst: i === 0, islast: i === n - 1, isrealtime: false, ishistory: true, + isconfirmed: true, isnew: true, islastconfirmedhistory: i === n - 1, + }; + + main(); + } + + // Backfill skipped bars so every output series has exactly n entries. + for (const k of Object.keys(rt.plots)) { + const d = rt.plots[k].data; + for (let i = 0; i < n; i++) if (d[i] === undefined) d[i] = null; + } + for (const k of Object.keys(rt.plotshapes)) { + const d = rt.plotshapes[k].data; + for (let i = 0; i < n; i++) if (d[i] === undefined) d[i] = false; + } + return rt; +} + + +export { main, run }; diff --git a/converted/SMC_Trade_with_Dhruv.js b/converted/SMC_Trade_with_Dhruv.js index aa09159..38648e4 100644 --- a/converted/SMC_Trade_with_Dhruv.js +++ b/converted/SMC_Trade_with_Dhruv.js @@ -1,12 +1,63 @@ -// PineScript to JavaScript Transpiled Code +// Auto-generated JavaScript from PineScript source -// Generated automatically +// Do not edit by hand -- re-run the converter instead -// Built-in PineScript functions +// All the built-in PineScript functions that scripts depend on at runtime const pinescript = { + __scalar: function(v) { + if (v === null || v === undefined) return v; + if (typeof v === 'object' && typeof v.valueOf === 'function') { + const s = v.valueOf(); + if (typeof s === 'number' || typeof s === 'boolean' || s === null) return s; + } + return v; + }, + series: function(id, value) { + const rt = this.__rt || (this.__rt = {}); + const store = rt.series || (rt.series = {}); + let buf = store[id]; + if (!buf) buf = store[id] = []; + buf[this.__bar | 0] = this.__scalar(value); + return buf; + }, + hist: function(id, value, n) { + const buf = this.series(id, value); + const idx = (this.__bar | 0) - (n | 0); + return idx >= 0 && idx < buf.length ? buf[idx] : null; + }, + time: function(timeframe, session) { + const t = globalThis.time; + if (t && typeof t.valueOf === 'function') return t.valueOf(); + return Array.isArray(t) ? t[t.length - 1] : t; + }, + sign: function(x) { + if (x === null || x === undefined || (typeof x === 'number' && isNaN(x))) return null; + return Math.sign(Number(x)); + }, + unpack: function(value, count) { + if (Array.isArray(value)) return value; + if (value != null && typeof value[Symbol.iterator] === 'function') return Array.from(value); + return new Array(count || 0).fill(null); + }, + alertcondition: function(condition, ...rest) { + if (globalThis.__pineRuntime) { + globalThis.__pineRuntime.alerts.push({ condition, args: rest }); + } + return null; + }, + barcolor: function(color) { return null; }, + bgcolor: function(color, title, editable, showLast) { + return null; + }, + fill: function(series1, series2, color, title, editable) { + return null; + }, + hline: function(_price, _title, _opts) { + return null; + }, sma: function(series, length) { if (series === null || series === undefined) return null; if (!Array.isArray(series)) series = [series]; @@ -84,6 +135,13 @@ const pinescript = { } return weightSum > 0 ? sum / weightSum : null; }, + swma: function(series) { + if (series === null || series === undefined) return null; + if (!Array.isArray(series)) series = [series]; + if (series.length < 4) return null; + const s = series.slice(-4); + return (s[0] * 1 + s[1] * 2 + s[2] * 2 + s[3] * 1) / 6; + }, atr: function(high, low, close, length = 14) { if (!Array.isArray(high) || !Array.isArray(low) || !Array.isArray(close)) return null; if (high.length < length + 1 || low.length < length + 1 || close.length < length + 1) return null; @@ -114,6 +172,53 @@ const pinescript = { const atr = builtins.get('atr')(high, low, close, length); return { middle: sma, upper: sma + mult * atr, lower: sma - mult * atr }; }, + supertrend: function(factor, atrPeriod, high, low, close) { + if (!Array.isArray(high) || !Array.isArray(low) || !Array.isArray(close)) return [null, null]; + if (high.length < atrPeriod + 1) return [null, null]; + const atrVal = builtins.get('atr')(high, low, close, atrPeriod); + if (atrVal === null) return [null, null]; + const lastIdx = close.length - 1; + const hl2 = (high[lastIdx] + low[lastIdx]) / 2; + const upperBand = hl2 + factor * atrVal; + const lowerBand = hl2 - factor * atrVal; + // Determine direction: 1 means downtrend (price below band), -1 means uptrend + const direction = close[lastIdx] > upperBand ? -1 : close[lastIdx] < lowerBand ? 1 : -1; + const supertrendValue = direction === -1 ? lowerBand : upperBand; + return [supertrendValue, direction]; + }, + ta_supertrend: function(factor, atrPeriod) { + const H = globalThis.high; + const L = globalThis.low; + const C = globalThis.close; + return builtins.get('supertrend')(factor, atrPeriod, H, L, C); + }, + dmi: function(diLength, adxSmoothing, high, low, close) { + if (!Array.isArray(high) || !Array.isArray(low) || !Array.isArray(close)) return { plusDI: null, minusDI: null, adx: null }; + if (high.length < diLength + 1) return { plusDI: null, minusDI: null, adx: null }; + let plusDMSum = 0, minusDMSum = 0, trSum = 0; + for (let i = 1; i <= diLength; i++) { + const idx = high.length - 1 - diLength + i; + const upMove = high[idx] - high[idx - 1]; + const downMove = low[idx - 1] - low[idx]; + plusDMSum += (upMove > downMove && upMove > 0) ? upMove : 0; + minusDMSum += (downMove > upMove && downMove > 0) ? downMove : 0; + const tr = Math.max( + high[idx] - low[idx], + Math.abs(high[idx] - close[idx - 1]), + Math.abs(low[idx] - close[idx - 1]) + ); + trSum += tr; + } + const plusDI = trSum > 0 ? (plusDMSum / trSum) * 100 : 0; + const minusDI = trSum > 0 ? (minusDMSum / trSum) * 100 : 0; + const diSum = plusDI + minusDI; + const dx = diSum > 0 ? Math.abs(plusDI - minusDI) / diSum * 100 : 0; + return { plusDI, minusDI, adx: dx }; + }, + adx: function(diLength, adxSmoothing, high, low, close) { + const result = builtins.get('dmi')(diLength, adxSmoothing, high, low, close); + return result ? result.adx : null; + }, rsi: function(close, length = 14) { if (close === null || close === undefined) return null; if (!Array.isArray(close)) close = [close]; @@ -171,9 +276,7 @@ const pinescript = { return { macd: macdLine, signal: signalLine, histogram: histogram }; }, cci: function(a, b, c, d) { - // Pine signatures: - // - ta.cci(source, length) - // - ta.cci(high, low, close, length) + // Supports both ta.cci(source, length) and ta.cci(high, low, close, length) signatures let source; let length; if (Array.isArray(a) && typeof b === 'number' && c === undefined) { @@ -304,6 +407,15 @@ const pinescript = { stdev: function(source, length) { return Math.sqrt(builtins.get('variance')(source, length)); }, + median: function(source, length) { + if (!source || source.length < length) return null; + const sorted = [...source.slice(-length)].sort((a, b) => a - b); + const mid = Math.floor(sorted.length / 2); + return sorted.length % 2 !== 0 ? sorted[mid] : (sorted[mid - 1] + sorted[mid]) / 2; + }, + ta_median: function(source, length) { + return builtins.get('median')(source, length); + }, hl2: function(high, low) { return high && low ? (high[high.length - 1] + low[low.length - 1]) / 2 : null; }, @@ -420,6 +532,24 @@ const pinescript = { return (series1[i - 1] < series2[i - 1] && series1[i] >= series2[i]) || (series1[i - 1] > series2[i - 1] && series1[i] <= series2[i]); }, + crossover: function(series1, series2) { + if (series1 === null || series1 === undefined) return false; + if (series2 === null || series2 === undefined) return false; + if (!Array.isArray(series1)) series1 = [series1]; + if (!Array.isArray(series2)) series2 = [series2]; + if (series1.length < 2 || series2.length < 2) return false; + const i = series1.length - 1; + return series1[i - 1] < series2[i - 1] && series1[i] >= series2[i]; + }, + crossunder: function(series1, series2) { + if (series1 === null || series1 === undefined) return false; + if (series2 === null || series2 === undefined) return false; + if (!Array.isArray(series1)) series1 = [series1]; + if (!Array.isArray(series2)) series2 = [series2]; + if (series1.length < 2 || series2.length < 2) return false; + const i = series1.length - 1; + return series1[i - 1] > series2[i - 1] && series1[i] <= series2[i]; + }, offset: function(series, offset) { if (!Array.isArray(series)) return null; const idx = series.length - 1 - offset; @@ -500,12 +630,14 @@ const pinescript = { return builtins.get('nz')(value, replacement); }, plot: function(series, title = '', color = null, linewidth = 1) { - globalThis.__pineRuntime.plots.push({ - series, - title, - color, - linewidth - }); + const rt = globalThis.__pineRuntime; + if (!rt) return series; + const bar = rt.__barIndex | 0; + const ord = (rt.__plotIdx = (rt.__plotIdx | 0) + 1) - 1; + const key = (title && String(title)) || ('plot_' + ord); + let p = rt.plots[key]; + if (!p) p = rt.plots[key] = { title: key, color, linewidth, data: [] }; + p.data[bar] = this.__scalar(series); return series; }, lineNew: function(x1, y1, x2, y2, opts = {}) { @@ -514,20 +646,72 @@ const pinescript = { lineDelete: function(l) { return null; }, - labelNew: function(...args) { - if (globalThis.__pineRuntime) { - globalThis.__pineRuntime.labels = globalThis.__pineRuntime.labels || []; - globalThis.__pineRuntime.labels.push({ args }); - } - return { args }; + lineSetXY: function(line, point, x, y) { + if (!line) return null; + if (point === 0 || point === 'x1') { line.x1 = x; line.y1 = y; } + else { line.x2 = x; line.y2 = y; } + return line; + }, + lineGetX: function(line, point) { + if (!line) return null; + return point === 0 || point === 'x1' ? line.x1 : line.x2; + }, + lineGetY: function(line, point) { + if (!line) return null; + return point === 0 || point === 'y1' ? line.y1 : line.y2; + }, + labelNew: function(x, y, text = '', opts = {}) { + return { x, y, text, opts, _type: 'label' }; }, labelDelete: function(l) { return null; }, + labelSetText: function(label, text) { + if (!label) return null; + label.text = text; + return label; + }, + labelGetText: function(label) { + if (!label) return ''; + return label.text || ''; + }, + boxNew: function(left, top, right, bottom, opts = {}) { + return { left, top, right, bottom, opts, _type: 'box' }; + }, + boxDelete: function(box) { + return null; + }, + boxSetLeftTop: function(box, left, top) { + if (!box) return null; + box.left = left; + box.top = top; + return box; + }, + boxSetRightBottom: function(box, right, bottom) { + if (!box) return null; + box.right = right; + box.bottom = bottom; + return box; + }, + polylineNew: function(points, opts = {}) { + return { points: points || [], opts, _type: 'polyline' }; + }, + polylineDelete: function(poly) { + return null; + }, plotshape: function(condition, ...rest) { - if (globalThis.__pineRuntime) { - globalThis.__pineRuntime.plotshapes.push({ condition, args: rest }); + const rt = globalThis.__pineRuntime; + if (!rt) return condition; + const bar = rt.__barIndex | 0; + const ord = (rt.__shapeIdx = (rt.__shapeIdx | 0) + 1) - 1; + let key = 'shape_' + ord; + for (const r of rest) { + if (typeof r === 'string') { key = r; break; } + if (r && typeof r === 'object' && r.title) { key = String(r.title); break; } } + let s = rt.plotshapes[key]; + if (!s) s = rt.plotshapes[key] = { title: key, data: [] }; + s.data[bar] = !!this.__scalar(condition); return condition; }, indicator: function(title, shorttitle, overlay, opts) { @@ -542,27 +726,12 @@ const pinescript = { } return null; }, - alertcondition: function(condition, ...rest) { - if (globalThis.__pineRuntime) { - globalThis.__pineRuntime.alerts.push({ condition, args: rest }); - } - return null; - }, plotbar: function(open, high, low, close, title, color, editable, showLast) { return { open, high, low, close }; }, plotcandle: function(open, high, low, close, title, color, wickColor, borderColor, editable, showLast) { return { open, high, low, close }; }, - hline: function(_price, _title, _opts) { - return null; - }, - bgcolor: function(color, title, editable, showLast) { - return null; - }, - fill: function(series1, series2, color, title, editable) { - return null; - }, alert: function(condition, message, frequency) { console.log('Alert:', condition ? message : 'Condition not met'); return condition; @@ -828,6 +997,9 @@ const pinescript = { arrayIndexOf: function(arr, value) { return arr ? arr.indexOf(value) : -1; }, + arrayLastIndexOf: function(arr, value) { + return arr ? arr.lastIndexOf(value) : -1; + }, arraySum: function(arr) { return arr ? arr.reduce((a, b) => a + b, 0) : 0; }, @@ -840,6 +1012,42 @@ const pinescript = { arrayMax: function(arr) { return arr && arr.length > 0 ? Math.max(...arr) : null; }, + arrayStdev: function(arr) { + if (!arr || arr.length === 0) return null; + const mean = arr.reduce((a, b) => a + b, 0) / arr.length; + const variance = arr.reduce((sum, val) => sum + Math.pow(val - mean, 2), 0) / arr.length; + return Math.sqrt(variance); + }, + arrayVariance: function(arr) { + if (!arr || arr.length === 0) return null; + const mean = arr.reduce((a, b) => a + b, 0) / arr.length; + return arr.reduce((sum, val) => sum + Math.pow(val - mean, 2), 0) / arr.length; + }, + arrayCovariance: function(arr1, arr2) { + if (!arr1 || !arr2 || arr1.length === 0 || arr1.length !== arr2.length) return null; + const mean1 = arr1.reduce((a, b) => a + b, 0) / arr1.length; + const mean2 = arr2.reduce((a, b) => a + b, 0) / arr2.length; + let cov = 0; + for (let i = 0; i < arr1.length; i++) { + cov += (arr1[i] - mean1) * (arr2[i] - mean2); + } + return cov / arr1.length; + }, + mode: function(arr) { + if (!arr || arr.length === 0) return null; + const counts = new Map(); + let maxCount = 0; + let modeVal = arr[0]; + for (const val of arr) { + const c = (counts.get(val) || 0) + 1; + counts.set(val, c); + if (c > maxCount) { + maxCount = c; + modeVal = val; + } + } + return modeVal; + }, strLength: function(str) { return str ? str.length : 0; }, @@ -895,6 +1103,84 @@ const pinescript = { strReverse: function(str) { return str ? str.split('').reverse().join('') : ''; }, + strFormat: function(formatStr, ...args) { + if (!formatStr) return ''; + return formatStr.replace(/\{(\d+)\}/g, (match, idx) => { + const i = parseInt(idx, 10); + return i < args.length ? String(args[i]) : match; + }); + }, + input: function(defval, title, tooltip, inline, group) { + return defval; + }, + inputInt: function(defval = 0, title, minval, maxval, step, tooltip, inline, group) { + return defval; + }, + inputFloat: function(defval = 0.0, title, minval, maxval, step, tooltip, inline, group) { + return defval; + }, + inputBool: function(defval = false, title, tooltip, inline, group) { + return defval; + }, + inputString: function(defval = '', title, options, tooltip, inline, group) { + return defval; + }, + inputSource: function(defval = null, title, tooltip, inline, group) { + return defval; + }, + inputColor: function(defval = '#000000', title, tooltip, inline, group) { + return defval; + }, + inputTime: function(defval = 0, title, tooltip, inline, group) { + return defval; + }, + mathSign: function(value) { + return Math.sign(value); + }, + mathAvg: function(...args) { + const valid = args.filter(v => v !== null && v !== undefined && !isNaN(v)); + return valid.length > 0 ? valid.reduce((a, b) => a + b, 0) / valid.length : null; + }, + mathSum: function(series, length) { + if (!series || series.length < length) return null; + return series.slice(-length).reduce((a, b) => a + b, 0); + }, + mathRandom: function(min = 0, max = 1) { + return min + Math.random() * (max - min); + }, + mathTodegrees: function(radians) { + return radians * (180 / Math.PI); + }, + mathToradians: function(degrees) { + return degrees * (Math.PI / 180); + }, + colorT: function(color, transparency) { + if (transparency !== undefined) { + return { color, transparency }; + } + if (color && typeof color === 'object' && 'transparency' in color) { + return color.transparency; + } + return 0; + }, + tableCellSetText: function(table, column, row, text) { + return null; + }, + tableCellSetBgcolor: function(table, column, row, bgcolor) { + return null; + }, + tableMergeCells: function(table, startColumn, startRow, endColumn, endRow) { + return null; + }, + tableDelete: function(table) { + return null; + }, + tableClear: function(table, startColumn, startRow, endColumn, endRow) { + return null; + }, + maxBarsBack: function(series, length) { + return null; + }, pi: 3.141592653589793, e: 2.718281828459045, true: true, @@ -905,8 +1191,7 @@ const pinescript = { const info = { ticker: 'AAPL', tickerid: 'NASDAQ:AAPL', prefix: 'NASDAQ', root: 'AAPL', suffix: '' }; return info[type] || ''; }, - time: 1771178053104, - timenow: 1771178053104, + timenow: 1781574528763, barstate: "LAST", dividends: {}, splits: {}, @@ -923,7 +1208,7 @@ const pinescript = { return null; }, tr: function(high, low, close) { - // Pine signature: ta.tr(true) + // Handles both the boolean shorthand ta.tr(true) and explicit high/low/close arguments if (typeof high === 'boolean') { const H = globalThis.high; const L = globalThis.low; @@ -934,7 +1219,7 @@ const pinescript = { return Math.max(H[i] - L[i], Math.abs(H[i] - prevClose), Math.abs(L[i] - prevClose)); } - // Fallback: treat inputs as scalars for current bar + // When arrays are passed, compute true range from the last bar using the previous close if (Array.isArray(close)) { const prevClose = close[close.length - 2] || close[0]; const currHigh = Array.isArray(high) ? high[high.length - 1] : high; @@ -945,6 +1230,92 @@ const pinescript = { return Math.max(high - low, Math.abs(high - close), Math.abs(low - close)); }, + arrayFirst: function(arr) { + if (!arr || arr.length === 0) return null; + return arr[0]; + }, + arrayLast: function(arr) { + if (!arr || arr.length === 0) return null; + return arr[arr.length - 1]; + }, + arrayJoin: function(arr, separator = ',') { + if (!arr) return ''; + return arr.join(separator); + }, + arrayConcat: function(arr1, arr2) { + if (!arr1) return arr2 || []; + if (!arr2) return arr1; + return arr1.concat(arr2); + }, + arrayCopy: function(arr) { + if (!arr) return []; + return [...arr]; + }, + arrayBinarySearch: function(arr, value) { + if (!arr || arr.length === 0) return -1; + let lo = 0, hi = arr.length - 1; + while (lo <= hi) { + const mid = (lo + hi) >>> 1; + if (arr[mid] === value) return mid; + if (arr[mid] < value) lo = mid + 1; + else hi = mid - 1; + } + return -1; + }, + arrayRange: function(arr) { + if (!arr || arr.length === 0) return 0; + return Math.max(...arr) - Math.min(...arr); + }, + arrayMedian: function(arr) { + if (!arr || arr.length === 0) return null; + const sorted = [...arr].sort((a, b) => a - b); + const mid = Math.floor(sorted.length / 2); + return sorted.length % 2 !== 0 ? sorted[mid] : (sorted[mid - 1] + sorted[mid]) / 2; + }, + arrayMode: function(arr) { + if (!arr || arr.length === 0) return null; + const freq = {}; + let maxCount = 0; + let mode = arr[0]; + for (const v of arr) { + freq[v] = (freq[v] || 0) + 1; + if (freq[v] > maxCount) { maxCount = freq[v]; mode = v; } + } + return mode; + }, + arrayPercentile: function(arr, percentile) { + if (!arr || arr.length === 0) return null; + const sorted = [...arr].sort((a, b) => a - b); + const idx = (percentile / 100) * (sorted.length - 1); + const lo = Math.floor(idx); + const hi = Math.ceil(idx); + if (lo === hi) return sorted[lo]; + return sorted[lo] + (sorted[hi] - sorted[lo]) * (idx - lo); + }, + arrayPercentileLinearInterpolation: function(arr, percentile) { + return builtins.get('arrayPercentile')(arr, percentile); + }, + arrayPercentileNearestRank: function(arr, percentile) { + if (!arr || arr.length === 0) return null; + const sorted = [...arr].sort((a, b) => a - b); + const idx = Math.ceil((percentile / 100) * sorted.length) - 1; + return sorted[Math.max(0, idx)]; + }, + arrayAbs: function(arr) { + if (!arr) return []; + return arr.map(v => Math.abs(v)); + }, + arrayEvery: function(arr, callback) { + if (!arr) return false; + return arr.every(callback); + }, + arraySome: function(arr, callback) { + if (!arr) return false; + return arr.some(callback); + }, + requestFinancial: function() { + return null; + }, }; @@ -1101,6 +1472,115 @@ globalThis.barmerge = globalThis.barmerge || { }; +function __pineNS(real) { + + const base = real || {}; + + return new Proxy(base, { + + get(target, key) { + + if (key in target) return target[key]; + + if (typeof key === "symbol") return target[key]; + + // Unknown member: usable as a value (null), a constant, or a no-op function. + + const noop = function() { return null; }; + + noop.toString = () => ""; + + return noop; + + } + + }); + +} + +globalThis.ta = globalThis.ta || __pineNS({}); + +globalThis.math = globalThis.math || __pineNS({ pi: Math.PI, e: Math.E, phi: 1.618033988749895, rphi: 0.618033988749895 }); + +globalThis.chart = globalThis.chart || __pineNS({ point: __pineNS({}), bg_color: null, fg_color: null }); + +globalThis.line = globalThis.line || __pineNS({ style_solid: "solid", style_dashed: "dashed", style_dotted: "dotted" }); + +globalThis.box = globalThis.box || __pineNS({}); + +globalThis.label = globalThis.label || __pineNS({ style_label_down: "label_down", style_label_up: "label_up", style_none: "none" }); + +globalThis.polyline = globalThis.polyline || __pineNS({}); + +globalThis.linefill = globalThis.linefill || __pineNS({}); + +globalThis.matrix = globalThis.matrix || __pineNS({}); + +globalThis.map = globalThis.map || __pineNS({}); + +globalThis.session = globalThis.session || __pineNS({ regular: "regular", extended: "extended" }); + +globalThis.dayofweek = globalThis.dayofweek || __pineNS({ sunday: 1, monday: 2, tuesday: 3, wednesday: 4, thursday: 5, friday: 6, saturday: 7 }); + +globalThis.timeframe = __pineNS(Object.assign(globalThis.timeframe || {}, { period: (globalThis.timeframe && globalThis.timeframe.period) || "D", isintraday: false, isdaily: true, multiplier: 1 })); + +globalThis.request = __pineNS(globalThis.request || {}); + +globalThis.input = __pineNS(globalThis.input || {}); + +globalThis.adjustment = globalThis.adjustment || __pineNS({}); + +globalThis.earnings = globalThis.earnings || __pineNS({}); + +globalThis.dividends = globalThis.dividends || __pineNS({}); + +globalThis.splits = globalThis.splits || __pineNS({}); + +globalThis.currency = globalThis.currency || __pineNS({}); + +globalThis.display = globalThis.display || __pineNS({ none: "none", all: "all", pane: "pane", data_window: "data_window", status_line: "status_line", price_scale: "price_scale" }); + +globalThis.format = globalThis.format || __pineNS({ price: "price", volume: "volume", percent: "percent", mintick: "mintick", inherit: "inherit" }); + +globalThis.scale = globalThis.scale || __pineNS({ left: "left", right: "right", none: "none" }); + +globalThis.font = globalThis.font || __pineNS({ family_default: "default", family_monospace: "monospace" }); + +globalThis.xloc = globalThis.xloc || __pineNS({ bar_index: "bar_index", bar_time: "bar_time" }); + +globalThis.yloc = globalThis.yloc || __pineNS({ price: "price", abovebar: "abovebar", belowbar: "belowbar" }); + +globalThis.extend = globalThis.extend || __pineNS({ none: "none", left: "left", right: "right", both: "both" }); + +globalThis.order = globalThis.order || __pineNS({ ascending: "ascending", descending: "descending" }); + +globalThis.alert = globalThis.alert || Object.assign(function() { return null; }, { freq_once_per_bar: "once_per_bar", freq_once_per_bar_close: "once_per_bar_close", freq_all: "all" }); + +globalThis.dayofmonth = globalThis.dayofmonth || function(t) { return new Date(t || 0).getUTCDate(); }; + +globalThis.plot = globalThis.plot || __pineNS({ style_line: "line", style_linebr: "linebr", style_stepline: "stepline", style_histogram: "histogram", style_cross: "cross", style_area: "area", style_areabr: "areabr", style_columns: "columns", style_circles: "circles" }); + +globalThis.int = globalThis.int || function(x) { return x == null ? null : Math.trunc(Number(x)); }; + +globalThis.float = globalThis.float || function(x) { return x == null ? null : Number(x); }; + +globalThis.bool = globalThis.bool || function(x) { return Boolean(x); }; + +globalThis.string = globalThis.string || function(x) { return x == null ? null : String(x); }; + +globalThis.dayofweek = globalThis.dayofweek || __pineNS({ sunday: 1, monday: 2, tuesday: 3, wednesday: 4, thursday: 5, friday: 6, saturday: 7 }); + +globalThis.str = globalThis.str || __pineNS({}); + +pinescript.math = globalThis.math; + +pinescript.ta = globalThis.ta; + +pinescript.str = globalThis.str; + +pinescript.array = __pineNS(globalThis.array); + + pinescript.color = { hex: function(s) { @@ -1190,7 +1670,7 @@ pinescript.table = { }; -// Input parameters +// Script input parameters and their defaults const alerts = { new: function(internalBullishBOS, internalBearishBOS, internalBullishCHoCH, internalBearishCHoCH, swingBullishBOS, swingBearishBOS, swingBullishCHoCH, swingBearishCHoCH, internalBullishOrderBlock, internalBearishOrderBlock, swingBullishOrderBlock, swingBearishOrderBlock, equalHighs, equalLows, bullishFairValueGap, bearishFairValueGap) { return { internalBullishBOS: internalBullishBOS, internalBearishBOS: internalBearishBOS, internalBullishCHoCH: internalBullishCHoCH, internalBearishCHoCH: internalBearishCHoCH, swingBullishBOS: swingBullishBOS, swingBearishBOS: swingBearishBOS, swingBullishCHoCH: swingBullishCHoCH, swingBearishCHoCH: swingBearishCHoCH, internalBullishOrderBlock: internalBullishOrderBlock, internalBearishOrderBlock: internalBearishOrderBlock, swingBullishOrderBlock: swingBullishOrderBlock, swingBearishOrderBlock: swingBearishOrderBlock, equalHighs: equalHighs, equalLows: equalLows, bullishFairValueGap: bullishFairValueGap, bearishFairValueGap: bearishFairValueGap }; } }; const trailingExtremes = { new: function(top, bottom, barTime, barIndex, lastTopTime, lastBottomTime) { return { top: top, bottom: bottom, barTime: barTime, barIndex: barIndex, lastTopTime: lastTopTime, lastBottomTime: lastBottomTime }; } }; @@ -1200,7 +1680,7 @@ const equalDisplay = { new: function(l_ine, l_abel) { return { l_ine: l_ine, l_a const pivot = { new: function(currentLevel, lastLevel, crossed, barTime, barIndex) { return { currentLevel: currentLevel, lastLevel: lastLevel, crossed: crossed, barTime: barTime, barIndex: barIndex }; } }; const orderBlock = { new: function(barHigh, barLow, barTime, bias) { return { barHigh: barHigh, barLow: barLow, barTime: barTime, bias: bias }; } }; -// Main script logic +// The transpiled script logic, called once per bar function main() { @@ -1270,58 +1750,58 @@ function main() { let fairValueGapsTimeframeTooltip = "Fair value gaps timeframe"; let fairValueGapsExtendTooltip = "Determine how many bars to extend the Fair Value Gap boxes on chart"; let showPremiumDiscountZonesTooltip = "Display premium, discount, and equilibrium zones on chart"; - let modeInput = input.string(HISTORICAL, "Mode", ({ group: SMART_GROUP, tooltip: modeTooltip, options: [HISTORICAL, PRESENT] })); - let styleInput = input.string(COLORED, "Style", ({ group: SMART_GROUP, tooltip: styleTooltip, options: [COLORED, MONOCHROME] })); - let showTrendInput = input(false, "Color Candles", ({ group: SMART_GROUP, tooltip: showTrendTooltip })); - let showInternalsInput = input(true, "Show Internal Structure", ({ group: INTERNAL_GROUP, tooltip: showInternalsTooltip })); - let showInternalBullInput = input.string(ALL, "Bullish Structure", ({ group: INTERNAL_GROUP, inline: "ibull", options: [ALL, BOS, CHOCH] })); - let internalBullColorInput = input(GREEN, "", ({ group: INTERNAL_GROUP, inline: "ibull" })); - let showInternalBearInput = input.string(ALL, "Bearish Structure", ({ group: INTERNAL_GROUP, inline: "ibear", options: [ALL, BOS, CHOCH] })); - let internalBearColorInput = input(RED, "", ({ group: INTERNAL_GROUP, inline: "ibear" })); - let internalFilterConfluenceInput = input(false, "Confluence Filter", ({ group: INTERNAL_GROUP, tooltip: internalFilterConfluenceTooltip })); - let internalStructureSize = input.string(TINY, "Internal Label Size", ({ group: INTERNAL_GROUP, options: [TINY, SMALL, NORMAL] })); - let showStructureInput = input(true, "Show Swing Structure", ({ group: SWING_GROUP, tooltip: showStructureTooltip })); - let showSwingBullInput = input.string(ALL, "Bullish Structure", ({ group: SWING_GROUP, inline: "bull", options: [ALL, BOS, CHOCH] })); - let swingBullColorInput = input(GREEN, "", ({ group: SWING_GROUP, inline: "bull" })); - let showSwingBearInput = input.string(ALL, "Bearish Structure", ({ group: SWING_GROUP, inline: "bear", options: [ALL, BOS, CHOCH] })); - let swingBearColorInput = input(RED, "", ({ group: SWING_GROUP, inline: "bear" })); - let swingStructureSize = input.string(SMALL, "Swing Label Size", ({ group: SWING_GROUP, options: [TINY, SMALL, NORMAL] })); - let showSwingsInput = input(false, "Show Swings Points", ({ group: SWING_GROUP, tooltip: showSwingsTooltip, inline: "swings" })); - let swingsLengthInput = input.int(50, "", ({ group: SWING_GROUP, minval: 10, inline: "swings" })); - let showHighLowSwingsInput = input(true, "Show Strong/Weak High/Low", ({ group: SWING_GROUP, tooltip: showHighLowSwingsTooltip })); - let showInternalOrderBlocksInput = input(true, "Internal Order Blocks", ({ group: BLOCKS_GROUP, tooltip: showInternalOrderBlocksTooltip, inline: "iob" })); - let internalOrderBlocksSizeInput = input.int(5, "", ({ group: BLOCKS_GROUP, minval: 1, maxval: 20, inline: "iob" })); - let showSwingOrderBlocksInput = input(false, "Swing Order Blocks", ({ group: BLOCKS_GROUP, tooltip: showSwingOrderBlocksTooltip, inline: "ob" })); - let swingOrderBlocksSizeInput = input.int(5, "", ({ group: BLOCKS_GROUP, minval: 1, maxval: 20, inline: "ob" })); - let orderBlockFilterInput = input.string("Atr", "Order Block Filter", ({ group: BLOCKS_GROUP, tooltip: orderBlockFilterTooltip, options: [ATR, RANGE] })); - let orderBlockMitigationInput = input.string(HIGHLOW, "Order Block Mitigation", ({ group: BLOCKS_GROUP, tooltip: orderBlockMitigationTooltip, options: [CLOSE, HIGHLOW] })); - let internalBullishOrderBlockColor = input.color(pinescript.color.rgb(49, 245, 186, 80), "Internal Bullish OB", ({ group: BLOCKS_GROUP })); - let internalBearishOrderBlockColor = input.color(pinescript.color.new(pinescript.color.hex("#f77c80"), 80), "Internal Bearish OB", ({ group: BLOCKS_GROUP })); - let swingBullishOrderBlockColor = input.color(pinescript.color.new(pinescript.color.hex("#1848cc"), 80), "Bullish OB", ({ group: BLOCKS_GROUP })); - let swingBearishOrderBlockColor = input.color(pinescript.color.new(pinescript.color.hex("#b22833"), 80), "Bearish OB", ({ group: BLOCKS_GROUP })); - let showEqualHighsLowsInput = input(true, "Equal High/Low", ({ group: EQUAL_GROUP, tooltip: showEqualHighsLowsTooltip })); - let equalHighsLowsLengthInput = input.int(3, "Bars Confirmation", ({ group: EQUAL_GROUP, tooltip: equalHighsLowsLengthTooltip, minval: 1 })); - let equalHighsLowsThresholdInput = input.float(0.1, "Threshold", ({ group: EQUAL_GROUP, tooltip: equalHighsLowsThresholdTooltip, minval: 0, maxval: 0.5, step: 0.1 })); - let equalHighsLowsSizeInput = input.string(TINY, "Label Size", ({ group: EQUAL_GROUP, options: [TINY, SMALL, NORMAL] })); - let showFairValueGapsInput = input(false, "Fair Value Gaps", ({ group: GAPS_GROUP, tooltip: showFairValueGapsTooltip })); - let fairValueGapsThresholdInput = input(true, "Auto Threshold", ({ group: GAPS_GROUP, tooltip: fairValueGapsThresholdTooltip })); + let modeInput = pinescript.inputString(HISTORICAL, "Mode", ({ group: SMART_GROUP, tooltip: modeTooltip, options: [HISTORICAL, PRESENT] })); + let styleInput = pinescript.inputString(COLORED, "Style", ({ group: SMART_GROUP, tooltip: styleTooltip, options: [COLORED, MONOCHROME] })); + let showTrendInput = pinescript.input(false, "Color Candles", ({ group: SMART_GROUP, tooltip: showTrendTooltip })); + let showInternalsInput = pinescript.input(true, "Show Internal Structure", ({ group: INTERNAL_GROUP, tooltip: showInternalsTooltip })); + let showInternalBullInput = pinescript.inputString(ALL, "Bullish Structure", ({ group: INTERNAL_GROUP, inline: "ibull", options: [ALL, BOS, CHOCH] })); + let internalBullColorInput = pinescript.input(GREEN, "", ({ group: INTERNAL_GROUP, inline: "ibull" })); + let showInternalBearInput = pinescript.inputString(ALL, "Bearish Structure", ({ group: INTERNAL_GROUP, inline: "ibear", options: [ALL, BOS, CHOCH] })); + let internalBearColorInput = pinescript.input(RED, "", ({ group: INTERNAL_GROUP, inline: "ibear" })); + let internalFilterConfluenceInput = pinescript.input(false, "Confluence Filter", ({ group: INTERNAL_GROUP, tooltip: internalFilterConfluenceTooltip })); + let internalStructureSize = pinescript.inputString(TINY, "Internal Label Size", ({ group: INTERNAL_GROUP, options: [TINY, SMALL, NORMAL] })); + let showStructureInput = pinescript.input(true, "Show Swing Structure", ({ group: SWING_GROUP, tooltip: showStructureTooltip })); + let showSwingBullInput = pinescript.inputString(ALL, "Bullish Structure", ({ group: SWING_GROUP, inline: "bull", options: [ALL, BOS, CHOCH] })); + let swingBullColorInput = pinescript.input(GREEN, "", ({ group: SWING_GROUP, inline: "bull" })); + let showSwingBearInput = pinescript.inputString(ALL, "Bearish Structure", ({ group: SWING_GROUP, inline: "bear", options: [ALL, BOS, CHOCH] })); + let swingBearColorInput = pinescript.input(RED, "", ({ group: SWING_GROUP, inline: "bear" })); + let swingStructureSize = pinescript.inputString(SMALL, "Swing Label Size", ({ group: SWING_GROUP, options: [TINY, SMALL, NORMAL] })); + let showSwingsInput = pinescript.input(false, "Show Swings Points", ({ group: SWING_GROUP, tooltip: showSwingsTooltip, inline: "swings" })); + let swingsLengthInput = pinescript.inputInt(50, "", ({ group: SWING_GROUP, minval: 10, inline: "swings" })); + let showHighLowSwingsInput = pinescript.input(true, "Show Strong/Weak High/Low", ({ group: SWING_GROUP, tooltip: showHighLowSwingsTooltip })); + let showInternalOrderBlocksInput = pinescript.input(true, "Internal Order Blocks", ({ group: BLOCKS_GROUP, tooltip: showInternalOrderBlocksTooltip, inline: "iob" })); + let internalOrderBlocksSizeInput = pinescript.inputInt(5, "", ({ group: BLOCKS_GROUP, minval: 1, maxval: 20, inline: "iob" })); + let showSwingOrderBlocksInput = pinescript.input(false, "Swing Order Blocks", ({ group: BLOCKS_GROUP, tooltip: showSwingOrderBlocksTooltip, inline: "ob" })); + let swingOrderBlocksSizeInput = pinescript.inputInt(5, "", ({ group: BLOCKS_GROUP, minval: 1, maxval: 20, inline: "ob" })); + let orderBlockFilterInput = pinescript.inputString("Atr", "Order Block Filter", ({ group: BLOCKS_GROUP, tooltip: orderBlockFilterTooltip, options: [ATR, RANGE] })); + let orderBlockMitigationInput = pinescript.inputString(HIGHLOW, "Order Block Mitigation", ({ group: BLOCKS_GROUP, tooltip: orderBlockMitigationTooltip, options: [CLOSE, HIGHLOW] })); + let internalBullishOrderBlockColor = pinescript.inputColor(pinescript.color.rgb(49, 245, 186, 80), "Internal Bullish OB", ({ group: BLOCKS_GROUP })); + let internalBearishOrderBlockColor = pinescript.inputColor(pinescript.color.new(pinescript.color.hex("#f77c80"), 80), "Internal Bearish OB", ({ group: BLOCKS_GROUP })); + let swingBullishOrderBlockColor = pinescript.inputColor(pinescript.color.new(pinescript.color.hex("#1848cc"), 80), "Bullish OB", ({ group: BLOCKS_GROUP })); + let swingBearishOrderBlockColor = pinescript.inputColor(pinescript.color.new(pinescript.color.hex("#b22833"), 80), "Bearish OB", ({ group: BLOCKS_GROUP })); + let showEqualHighsLowsInput = pinescript.input(true, "Equal High/Low", ({ group: EQUAL_GROUP, tooltip: showEqualHighsLowsTooltip })); + let equalHighsLowsLengthInput = pinescript.inputInt(3, "Bars Confirmation", ({ group: EQUAL_GROUP, tooltip: equalHighsLowsLengthTooltip, minval: 1 })); + let equalHighsLowsThresholdInput = pinescript.inputFloat(0.1, "Threshold", ({ group: EQUAL_GROUP, tooltip: equalHighsLowsThresholdTooltip, minval: 0, maxval: 0.5, step: 0.1 })); + let equalHighsLowsSizeInput = pinescript.inputString(TINY, "Label Size", ({ group: EQUAL_GROUP, options: [TINY, SMALL, NORMAL] })); + let showFairValueGapsInput = pinescript.input(false, "Fair Value Gaps", ({ group: GAPS_GROUP, tooltip: showFairValueGapsTooltip })); + let fairValueGapsThresholdInput = pinescript.input(true, "Auto Threshold", ({ group: GAPS_GROUP, tooltip: fairValueGapsThresholdTooltip })); let fairValueGapsTimeframeInput = input.timeframe("", "Timeframe", ({ group: GAPS_GROUP, tooltip: fairValueGapsTimeframeTooltip })); - let fairValueGapsBullColorInput = input.color(pinescript.color.new(pinescript.color.hex("#00ff68"), 70), "Bullish FVG", ({ group: GAPS_GROUP })); - let fairValueGapsBearColorInput = input.color(pinescript.color.new(pinescript.color.hex("#ff0008"), 70), "Bearish FVG", ({ group: GAPS_GROUP })); - let fairValueGapsExtendInput = input.int(1, "Extend FVG", ({ group: GAPS_GROUP, tooltip: fairValueGapsExtendTooltip, minval: 0 })); - let showDailyLevelsInput = input(false, "Daily", ({ group: LEVELS_GROUP, inline: "daily" })); - let dailyLevelsStyleInput = input.string(SOLID, "", ({ group: LEVELS_GROUP, inline: "daily", options: [SOLID, DASHED, DOTTED] })); - let dailyLevelsColorInput = input(BLUE, "", ({ group: LEVELS_GROUP, inline: "daily" })); - let showWeeklyLevelsInput = input(false, "Weekly", ({ group: LEVELS_GROUP, inline: "weekly" })); - let weeklyLevelsStyleInput = input.string(SOLID, "", ({ group: LEVELS_GROUP, inline: "weekly", options: [SOLID, DASHED, DOTTED] })); - let weeklyLevelsColorInput = input(BLUE, "", ({ group: LEVELS_GROUP, inline: "weekly" })); - let showMonthlyLevelsInput = input(false, "Monthly", ({ group: LEVELS_GROUP, inline: "monthly" })); - let monthlyLevelsStyleInput = input.string(SOLID, "", ({ group: LEVELS_GROUP, inline: "monthly", options: [SOLID, DASHED, DOTTED] })); - let monthlyLevelsColorInput = input(BLUE, "", ({ group: LEVELS_GROUP, inline: "monthly" })); - let showPremiumDiscountZonesInput = input(false, "Premium/Discount Zones", ({ group: ZONES_GROUP, tooltip: showPremiumDiscountZonesTooltip })); - let premiumZoneColorInput = input.color(RED, "Premium Zone", ({ group: ZONES_GROUP })); - let equilibriumZoneColorInput = input.color(GRAY, "Equilibrium Zone", ({ group: ZONES_GROUP })); - let discountZoneColorInput = input.color(GREEN, "Discount Zone", ({ group: ZONES_GROUP })); + let fairValueGapsBullColorInput = pinescript.inputColor(pinescript.color.new(pinescript.color.hex("#00ff68"), 70), "Bullish FVG", ({ group: GAPS_GROUP })); + let fairValueGapsBearColorInput = pinescript.inputColor(pinescript.color.new(pinescript.color.hex("#ff0008"), 70), "Bearish FVG", ({ group: GAPS_GROUP })); + let fairValueGapsExtendInput = pinescript.inputInt(1, "Extend FVG", ({ group: GAPS_GROUP, tooltip: fairValueGapsExtendTooltip, minval: 0 })); + let showDailyLevelsInput = pinescript.input(false, "Daily", ({ group: LEVELS_GROUP, inline: "daily" })); + let dailyLevelsStyleInput = pinescript.inputString(SOLID, "", ({ group: LEVELS_GROUP, inline: "daily", options: [SOLID, DASHED, DOTTED] })); + let dailyLevelsColorInput = pinescript.input(BLUE, "", ({ group: LEVELS_GROUP, inline: "daily" })); + let showWeeklyLevelsInput = pinescript.input(false, "Weekly", ({ group: LEVELS_GROUP, inline: "weekly" })); + let weeklyLevelsStyleInput = pinescript.inputString(SOLID, "", ({ group: LEVELS_GROUP, inline: "weekly", options: [SOLID, DASHED, DOTTED] })); + let weeklyLevelsColorInput = pinescript.input(BLUE, "", ({ group: LEVELS_GROUP, inline: "weekly" })); + let showMonthlyLevelsInput = pinescript.input(false, "Monthly", ({ group: LEVELS_GROUP, inline: "monthly" })); + let monthlyLevelsStyleInput = pinescript.inputString(SOLID, "", ({ group: LEVELS_GROUP, inline: "monthly", options: [SOLID, DASHED, DOTTED] })); + let monthlyLevelsColorInput = pinescript.input(BLUE, "", ({ group: LEVELS_GROUP, inline: "monthly" })); + let showPremiumDiscountZonesInput = pinescript.input(false, "Premium/Discount Zones", ({ group: ZONES_GROUP, tooltip: showPremiumDiscountZonesTooltip })); + let premiumZoneColorInput = pinescript.inputColor(RED, "Premium Zone", ({ group: ZONES_GROUP })); + let equilibriumZoneColorInput = pinescript.inputColor(GRAY, "Equilibrium Zone", ({ group: ZONES_GROUP })); + let discountZoneColorInput = pinescript.inputColor(GREEN, "Discount Zone", ({ group: ZONES_GROUP })); if (state.swingHigh === undefined) state.swingHigh = pivot.new(null, null, false); if (state.swingLow === undefined) state.swingLow = pivot.new(null, null, false); if (state.internalHigh === undefined) state.internalHigh = pivot.new(null, null, false); @@ -1356,19 +1836,19 @@ function main() { if (barstate.isfirst) { if (showSwingOrderBlocksInput) { for (let index = 1; index <= swingOrderBlocksSizeInput; index++) { - swingOrderBlocksBoxes.push(box.new(null, null, null, null, ({ xloc: xloc.bar_time, extend: extend.right }))); + swingOrderBlocksBoxes.push(pinescript.boxNew(null, null, null, null, ({ xloc: xloc.bar_time, extend: extend.right }))); } } if (showInternalOrderBlocksInput) { for (let index = 1; index <= internalOrderBlocksSizeInput; index++) { - internalOrderBlocksBoxes.push(box.new(null, null, null, null, ({ xloc: xloc.bar_time, extend: extend.right }))); + internalOrderBlocksBoxes.push(pinescript.boxNew(null, null, null, null, ({ xloc: xloc.bar_time, extend: extend.right }))); } } } let bearishOrderBlockMitigationSource = ((orderBlockMitigationInput === CLOSE) ? close : high); let bullishOrderBlockMitigationSource = ((orderBlockMitigationInput === CLOSE) ? close : low); - let atrMeasure = ta.atr(200); - let volatilityMeasure = ((orderBlockFilterInput === ATR) ? atrMeasure : (ta.cum(pinescript.ta.tr) / bar_index)); + let atrMeasure = pinescript.atr(200); + let volatilityMeasure = ((orderBlockFilterInput === ATR) ? atrMeasure : (pinescript.cum(pinescript.series(0, pinescript.ta.tr)) / bar_index)); let highVolatilityBar = ((high - low) >= (2 * volatilityMeasure)); let parsedHigh = (highVolatilityBar ? low : high); let parsedLow = (highVolatilityBar ? high : low); @@ -1379,8 +1859,8 @@ function main() { times.push(time); function leg(size) { if (state.leg === undefined) state.leg = 0; - let newLegHigh = (pinescript.offset(high, pinescript.size) > pinescript.highest(pinescript.size)); - let newLegLow = (pinescript.offset(low, pinescript.size) < pinescript.lowest(pinescript.size)); + let newLegHigh = (pinescript.hist(1, high, pinescript.size) > pinescript.highest(pinescript.series(2, pinescript.size))); + let newLegLow = (pinescript.hist(3, low, pinescript.size) < pinescript.lowest(pinescript.series(4, pinescript.size))); if (newLegHigh) { state.leg = BEARISH_LEG; } else { @@ -1391,13 +1871,13 @@ function main() { return state.leg; } function startOfNewLeg(leg) { - return (ta.change(state.leg) !== 0); + return (pinescript.change(pinescript.series(5, state.leg)) !== 0); } function startOfBearishLeg(leg) { - return (ta.change(state.leg) === -1); + return (pinescript.change(pinescript.series(6, state.leg)) === -1); } function startOfBullishLeg(leg) { - return (ta.change(state.leg) === 1); + return (pinescript.change(pinescript.series(7, state.leg)) === 1); } function drawLabel(labelTime, labelPrice, tag, labelColor, labelStyle) { if (state.l_abel === undefined) state.l_abel = null; @@ -1420,7 +1900,7 @@ function main() { pinescript.lineDelete(e_qualDisplay.l_ine); pinescript.labelDelete(e_qualDisplay.l_abel); } - e_qualDisplay.l_ine = pinescript.lineNew(pinescript.chartPointNew(p_ivot.barTime, null, p_ivot.currentLevel), pinescript.chartPointNew(pinescript.offset(time, pinescript.size), null, level), ({ xloc: xloc.bar_time, color: equalColor, style: line.style_dotted })); + e_qualDisplay.l_ine = pinescript.lineNew(pinescript.chartPointNew(p_ivot.barTime, null, p_ivot.currentLevel), pinescript.chartPointNew(pinescript.hist(8, time, pinescript.size), null, level), ({ xloc: xloc.bar_time, color: equalColor, style: line.style_dotted })); let labelPosition = pinescript.round((0.5 * ((p_ivot.barIndex + bar_index) - pinescript.size))); e_qualDisplay.l_abel = pinescript.labelNew(pinescript.chartPointNew(null, labelPosition, level), tag, xloc.bar_index, ({ color: color(null), textcolor: equalColor, style: labelStyle, size: equalHighsLowsSizeInput })); } @@ -1432,14 +1912,14 @@ function main() { if (newPivot) { if (pivotLow) { let p_ivot = (equalHighLow ? state.equalLow : (internal ? state.internalLow : state.swingLow)); - if ((equalHighLow && (pinescript.abs((p_ivot.currentLevel - pinescript.offset(low, pinescript.size))) < (equalHighsLowsThresholdInput * atrMeasure)))) { - drawEqualHighLow(p_ivot, pinescript.offset(low, pinescript.size), pinescript.size, false); + if ((equalHighLow && (pinescript.abs((p_ivot.currentLevel - pinescript.hist(9, low, pinescript.size))) < (equalHighsLowsThresholdInput * atrMeasure)))) { + drawEqualHighLow(p_ivot, pinescript.hist(10, low, pinescript.size), pinescript.size, false); } p_ivot.lastLevel = p_ivot.currentLevel; - p_ivot.currentLevel = pinescript.offset(low, pinescript.size); + p_ivot.currentLevel = pinescript.hist(11, low, pinescript.size); p_ivot.crossed = false; - p_ivot.barTime = pinescript.offset(time, pinescript.size); - p_ivot.barIndex = pinescript.offset(bar_index, pinescript.size); + p_ivot.barTime = pinescript.hist(12, time, pinescript.size); + p_ivot.barIndex = pinescript.hist(13, bar_index, pinescript.size); if ((!equalHighLow && !internal)) { state.trailing.bottom = p_ivot.currentLevel; state.trailing.barTime = p_ivot.barTime; @@ -1447,18 +1927,18 @@ function main() { state.trailing.lastBottomTime = p_ivot.barTime; } if (((showSwingsInput && !internal) && !equalHighLow)) { - drawLabel(pinescript.offset(time, pinescript.size), p_ivot.currentLevel, ((p_ivot.currentLevel < p_ivot.lastLevel) ? "LL" : "HL"), state.swingBullishColor, label.style_label_up); + drawLabel(pinescript.hist(14, time, pinescript.size), p_ivot.currentLevel, ((p_ivot.currentLevel < p_ivot.lastLevel) ? "LL" : "HL"), state.swingBullishColor, label.style_label_up); } } else { let p_ivot = (equalHighLow ? state.equalHigh : (internal ? state.internalHigh : state.swingHigh)); - if ((equalHighLow && (pinescript.abs((p_ivot.currentLevel - pinescript.offset(high, pinescript.size))) < (equalHighsLowsThresholdInput * atrMeasure)))) { - drawEqualHighLow(p_ivot, pinescript.offset(high, pinescript.size), pinescript.size, true); + if ((equalHighLow && (pinescript.abs((p_ivot.currentLevel - pinescript.hist(15, high, pinescript.size))) < (equalHighsLowsThresholdInput * atrMeasure)))) { + drawEqualHighLow(p_ivot, pinescript.hist(16, high, pinescript.size), pinescript.size, true); } p_ivot.lastLevel = p_ivot.currentLevel; - p_ivot.currentLevel = pinescript.offset(high, pinescript.size); + p_ivot.currentLevel = pinescript.hist(17, high, pinescript.size); p_ivot.crossed = false; - p_ivot.barTime = pinescript.offset(time, pinescript.size); - p_ivot.barIndex = pinescript.offset(bar_index, pinescript.size); + p_ivot.barTime = pinescript.hist(18, time, pinescript.size); + p_ivot.barIndex = pinescript.hist(19, bar_index, pinescript.size); if ((!equalHighLow && !internal)) { state.trailing.top = p_ivot.currentLevel; state.trailing.barTime = p_ivot.barTime; @@ -1466,7 +1946,7 @@ function main() { state.trailing.lastTopTime = p_ivot.barTime; } if (((showSwingsInput && !internal) && !equalHighLow)) { - drawLabel(pinescript.offset(time, pinescript.size), p_ivot.currentLevel, ((p_ivot.currentLevel > p_ivot.lastLevel) ? "HH" : "LH"), state.swingBearishColor, label.style_label_down); + drawLabel(pinescript.hist(20, time, pinescript.size), p_ivot.currentLevel, ((p_ivot.currentLevel > p_ivot.lastLevel) ? "HH" : "LH"), state.swingBearishColor, label.style_label_down); } } } @@ -1556,7 +2036,7 @@ function main() { let labelSize = (internal ? internalStructureSize : swingStructureSize); let extraCondition = (internal ? ((state.internalHigh.currentLevel !== state.swingHigh.currentLevel) && state.bullishBar) : true); let bullishColor = ((styleInput === MONOCHROME) ? MONO_BULLISH : (internal ? internalBullColorInput : swingBullColorInput)); - if (((pinescript.cross(close, p_ivot.currentLevel) && !p_ivot.crossed) && extraCondition)) { + if (((pinescript.crossover(pinescript.series(21, close), pinescript.series(22, p_ivot.currentLevel)) && !p_ivot.crossed) && extraCondition)) { let tag = ((t_rend.bias === BEARISH) ? CHOCH : BOS); if (internal) { currentAlerts.internalBullishCHoCH = (tag === CHOCH); @@ -1578,7 +2058,7 @@ function main() { p_ivot = (internal ? state.internalLow : state.swingLow); extraCondition = (internal ? ((state.internalLow.currentLevel !== state.swingLow.currentLevel) && state.bearishBar) : true); let bearishColor = ((styleInput === MONOCHROME) ? MONO_BEARISH : (internal ? internalBearColorInput : swingBearColorInput)); - if (((pinescript.cross(close, p_ivot.currentLevel) && !p_ivot.crossed) && extraCondition)) { + if (((pinescript.crossunder(pinescript.series(23, close), pinescript.series(24, p_ivot.currentLevel)) && !p_ivot.crossed) && extraCondition)) { let tag = ((t_rend.bias === BULLISH) ? CHOCH : BOS); if (internal) { currentAlerts.internalBearishCHoCH = (tag === CHOCH); @@ -1599,7 +2079,7 @@ function main() { } } function fairValueGapBox(leftTime, rightTime, topPrice, bottomPrice, boxColor) { - return box.new(pinescript.chartPointNew(leftTime, null, topPrice), pinescript.chartPointNew((rightTime + (fairValueGapsExtendInput * (time - pinescript.offset(time, 1)))), null, bottomPrice), ({ xloc: xloc.bar_time, border_color: boxColor, bgcolor: boxColor })); + return pinescript.boxNew(pinescript.chartPointNew(leftTime, null, topPrice), pinescript.chartPointNew((rightTime + (fairValueGapsExtendInput * (time - pinescript.hist(25, time, 1)))), null, bottomPrice), ({ xloc: xloc.bar_time, border_color: boxColor, bgcolor: boxColor })); } function deleteFairValueGaps() { for (const [index, eachFairValueGap] of state.fairValueGaps) { @@ -1611,19 +2091,19 @@ function main() { } } function drawFairValueGaps() { - let [lastClose, lastOpen, lastTime, currentHigh, currentLow, currentTime, last2High, last2Low] = pinescript.requestSecurity(syminfo.tickerid, fairValueGapsTimeframeInput, [pinescript.offset(close, 1), pinescript.offset(open, 1), pinescript.offset(time, 1), pinescript.offset(high, 0), pinescript.offset(low, 0), pinescript.offset(time, 0), pinescript.offset(high, 2), pinescript.offset(low, 2)], ({ lookahead: barmerge.lookahead_on })); + let [lastClose, lastOpen, lastTime, currentHigh, currentLow, currentTime, last2High, last2Low] = pinescript.unpack(pinescript.requestSecurity(syminfo.tickerid, fairValueGapsTimeframeInput, [pinescript.hist(26, close, 1), pinescript.hist(27, open, 1), pinescript.hist(28, time, 1), pinescript.hist(29, high, 0), pinescript.hist(30, low, 0), pinescript.hist(31, time, 0), pinescript.hist(32, high, 2), pinescript.hist(33, low, 2)], ({ lookahead: barmerge.lookahead_on })), 8); let barDeltaPercent = ((lastClose - lastOpen) / (lastOpen * 100)); let newTimeframe = timeframe.change(fairValueGapsTimeframeInput); - let threshold = (fairValueGapsThresholdInput ? ((ta.cum(pinescript.abs((newTimeframe ? barDeltaPercent : 0))) / bar_index) * 2) : 0); + let threshold = (fairValueGapsThresholdInput ? ((pinescript.cum(pinescript.series(34, pinescript.abs((newTimeframe ? barDeltaPercent : 0)))) / bar_index) * 2) : 0); let bullishFairValueGap = ((((currentLow > last2High) && (lastClose > last2High)) && (barDeltaPercent > threshold)) && newTimeframe); let bearishFairValueGap = ((((currentHigh < last2Low) && (lastClose < last2Low)) && (-barDeltaPercent > threshold)) && newTimeframe); if (bullishFairValueGap) { currentAlerts.bullishFairValueGap = true; - fairValueGaps.unshift(fairValueGap.new(currentLow, last2High, BULLISH, fairValueGapBox(lastTime, currentTime, currentLow, math.avg(currentLow, last2High), state.fairValueGapBullishColor), fairValueGapBox(lastTime, currentTime, math.avg(currentLow, last2High), last2High, state.fairValueGapBullishColor))); + fairValueGaps.unshift(fairValueGap.new(currentLow, last2High, BULLISH, fairValueGapBox(lastTime, currentTime, currentLow, pinescript.avg(currentLow, last2High), state.fairValueGapBullishColor), fairValueGapBox(lastTime, currentTime, pinescript.avg(currentLow, last2High), last2High, state.fairValueGapBullishColor))); } if (bearishFairValueGap) { currentAlerts.bearishFairValueGap = true; - fairValueGaps.unshift(fairValueGap.new(currentHigh, last2Low, BEARISH, fairValueGapBox(lastTime, currentTime, currentHigh, math.avg(currentHigh, last2Low), state.fairValueGapBearishColor), fairValueGapBox(lastTime, currentTime, math.avg(currentHigh, last2Low), last2Low, state.fairValueGapBearishColor))); + fairValueGaps.unshift(fairValueGap.new(currentHigh, last2Low, BEARISH, fairValueGapBox(lastTime, currentTime, currentHigh, pinescript.avg(currentHigh, last2Low), state.fairValueGapBearishColor), fairValueGapBox(lastTime, currentTime, pinescript.avg(currentHigh, last2Low), last2Low, state.fairValueGapBearishColor))); } } function getStyle(style) { @@ -1646,7 +2126,7 @@ function main() { } } function drawLevels(timeframe, sameTimeframe, style, levelColor) { - let [topLevel, bottomLevel, leftTime, rightTime] = pinescript.requestSecurity(syminfo.tickerid, timeframe, [pinescript.offset(high, 1), pinescript.offset(low, 1), pinescript.offset(time, 1), time], ({ lookahead: barmerge.lookahead_on })); + let [topLevel, bottomLevel, leftTime, rightTime] = pinescript.unpack(pinescript.requestSecurity(syminfo.tickerid, timeframe, [pinescript.hist(35, high, 1), pinescript.hist(36, low, 1), pinescript.hist(37, time, 1), time], ({ lookahead: barmerge.lookahead_on })), 4); let parsedTop = (sameTimeframe ? high : topLevel); let parsedBottom = (sameTimeframe ? low : bottomLevel); let parsedLeftTime = (sameTimeframe ? time : leftTime); @@ -1664,14 +2144,14 @@ function main() { } if (state.topLine === undefined) state.topLine = pinescript.lineNew(null, null, null, null, ({ xloc: xloc.bar_time, color: levelColor, style: getStyle(style) })); if (state.bottomLine === undefined) state.bottomLine = pinescript.lineNew(null, null, null, null, ({ xloc: xloc.bar_time, color: levelColor, style: getStyle(style) })); - if (state.topLabel === undefined) state.topLabel = pinescript.labelNew(null, null, ({ xloc: xloc.bar_time, text: str.format("P{0}H", timeframe), color: color(null), textcolor: levelColor, size: pinescript.size.small, style: label.style_label_left })); - if (state.bottomLabel === undefined) state.bottomLabel = pinescript.labelNew(null, null, ({ xloc: xloc.bar_time, text: str.format("P{0}L", timeframe), color: color(null), textcolor: levelColor, size: pinescript.size.small, style: label.style_label_left })); + if (state.topLabel === undefined) state.topLabel = pinescript.labelNew(null, null, ({ xloc: xloc.bar_time, text: pinescript.strFormat("P{0}H", timeframe), color: color(null), textcolor: levelColor, size: pinescript.size.small, style: label.style_label_left })); + if (state.bottomLabel === undefined) state.bottomLabel = pinescript.labelNew(null, null, ({ xloc: xloc.bar_time, text: pinescript.strFormat("P{0}L", timeframe), color: color(null), textcolor: levelColor, size: pinescript.size.small, style: label.style_label_left })); topLine.set_first_point(pinescript.chartPointNew(parsedTopTime, null, parsedTop)); - topLine.set_second_point(pinescript.chartPointNew((last_bar_time + (20 * (time - pinescript.offset(time, 1)))), null, parsedTop)); - topLabel.set_point(pinescript.chartPointNew((last_bar_time + (20 * (time - pinescript.offset(time, 1)))), null, parsedTop)); + topLine.set_second_point(pinescript.chartPointNew((last_bar_time + (20 * (time - pinescript.hist(38, time, 1)))), null, parsedTop)); + topLabel.set_point(pinescript.chartPointNew((last_bar_time + (20 * (time - pinescript.hist(39, time, 1)))), null, parsedTop)); bottomLine.set_first_point(pinescript.chartPointNew(parsedBottomTime, null, parsedBottom)); - bottomLine.set_second_point(pinescript.chartPointNew((last_bar_time + (20 * (time - pinescript.offset(time, 1)))), null, parsedBottom)); - return bottomLabel.set_point(pinescript.chartPointNew((last_bar_time + (20 * (time - pinescript.offset(time, 1)))), null, parsedBottom)); + bottomLine.set_second_point(pinescript.chartPointNew((last_bar_time + (20 * (time - pinescript.hist(40, time, 1)))), null, parsedBottom)); + return bottomLabel.set_point(pinescript.chartPointNew((last_bar_time + (20 * (time - pinescript.hist(41, time, 1)))), null, parsedBottom)); } function higherTimeframe(timeframe) { return (timeframe.in_seconds() > timeframe.in_seconds(timeframe)); @@ -1687,7 +2167,7 @@ function main() { if (state.bottomLine === undefined) state.bottomLine = pinescript.lineNew(null, null, null, null, ({ color: state.swingBullishColor, xloc: xloc.bar_time })); if (state.topLabel === undefined) state.topLabel = pinescript.labelNew(null, null, ({ color: color(null), textcolor: state.swingBearishColor, xloc: xloc.bar_time, style: label.style_label_down, size: pinescript.size.tiny })); if (state.bottomLabel === undefined) state.bottomLabel = pinescript.labelNew(null, null, ({ color: color(null), textcolor: state.swingBullishColor, xloc: xloc.bar_time, style: label.style_label_up, size: pinescript.size.tiny })); - let rightTimeBar = (last_bar_time + (20 * (time - pinescript.offset(time, 1)))); + let rightTimeBar = (last_bar_time + (20 * (time - pinescript.hist(42, time, 1)))); topLine.set_first_point(pinescript.chartPointNew(state.trailing.lastTopTime, null, state.trailing.top)); topLine.set_second_point(pinescript.chartPointNew(rightTimeBar, null, state.trailing.top)); topLabel.set_point(pinescript.chartPointNew(rightTimeBar, null, state.trailing.top)); @@ -1699,14 +2179,14 @@ function main() { } function drawZone(labelLevel, labelIndex, top, bottom, tag, zoneColor, style) { if (state.l_abel === undefined) state.l_abel = pinescript.labelNew(null, null, ({ text: tag, color: color(null), textcolor: zoneColor, style: style, size: pinescript.size.small })); - if (state.b_ox === undefined) state.b_ox = box.new(null, null, null, null, ({ bgcolor: pinescript.color.new(zoneColor, 80), border_color: color(null), xloc: xloc.bar_time })); + if (state.b_ox === undefined) state.b_ox = pinescript.boxNew(null, null, null, null, ({ bgcolor: pinescript.color.new(zoneColor, 80), border_color: color(null), xloc: xloc.bar_time })); b_ox.set_top_left_point(pinescript.chartPointNew(state.trailing.barTime, null, top)); b_ox.set_bottom_right_point(pinescript.chartPointNew(last_bar_time, null, bottom)); return l_abel.set_point(pinescript.chartPointNew(null, labelIndex, labelLevel)); } function drawPremiumDiscountZones() { drawZone(state.trailing.top, pinescript.round((0.5 * (state.trailing.barIndex + last_bar_index))), state.trailing.top, ((0.95 * state.trailing.top) + (0.05 * state.trailing.bottom)), "Premium", state.premiumZoneColor, label.style_label_down); - let equilibriumLevel = math.avg(state.trailing.top, state.trailing.bottom); + let equilibriumLevel = pinescript.avg(state.trailing.top, state.trailing.bottom); drawZone(equilibriumLevel, last_bar_index, ((0.525 * state.trailing.top) + (0.475 * state.trailing.bottom)), ((0.525 * state.trailing.bottom) + (0.475 * state.trailing.top)), "Equilibrium", equilibriumZoneColorInput, label.style_label_left); return drawZone(state.trailing.bottom, pinescript.round((0.5 * (state.trailing.barIndex + last_bar_index))), ((0.95 * state.trailing.bottom) + (0.05 * state.trailing.top)), state.trailing.bottom, "Discount", state.discountZoneColor, label.style_label_up); } @@ -1767,26 +2247,86 @@ function main() { drawLevels("M", timeframe.ismonthly, monthlyLevelsStyleInput, monthlyLevelsColorInput); } } - alertcondition(currentAlerts.internalBullishBOS, "Internal Bullish BOS", "Internal Bullish BOS formed"); - alertcondition(currentAlerts.internalBullishCHoCH, "Internal Bullish CHoCH", "Internal Bullish CHoCH formed"); - alertcondition(currentAlerts.internalBearishBOS, "Internal Bearish BOS", "Internal Bearish BOS formed"); - alertcondition(currentAlerts.internalBearishCHoCH, "Internal Bearish CHoCH", "Internal Bearish CHoCH formed"); - alertcondition(currentAlerts.swingBullishBOS, "Bullish BOS", "Internal Bullish BOS formed"); - alertcondition(currentAlerts.swingBullishCHoCH, "Bullish CHoCH", "Internal Bullish CHoCH formed"); - alertcondition(currentAlerts.swingBearishBOS, "Bearish BOS", "Bearish BOS formed"); - alertcondition(currentAlerts.swingBearishCHoCH, "Bearish CHoCH", "Bearish CHoCH formed"); - alertcondition(currentAlerts.internalBullishOrderBlock, "Bullish Internal OB Breakout", "Price broke bullish internal OB"); - alertcondition(currentAlerts.internalBearishOrderBlock, "Bearish Internal OB Breakout", "Price broke bearish internal OB"); - alertcondition(currentAlerts.swingBullishOrderBlock, "Bullish Swing OB Breakout", "Price broke bullish swing OB"); - alertcondition(currentAlerts.swingBearishOrderBlock, "Bearish Swing OB Breakout", "Price broke bearish swing OB"); - alertcondition(currentAlerts.equalHighs, "Equal Highs", "Equal highs detected"); - alertcondition(currentAlerts.equalLows, "Equal Lows", "Equal lows detected"); - alertcondition(currentAlerts.bullishFairValueGap, "Bullish FVG", "Bullish FVG formed"); - alertcondition(currentAlerts.bearishFairValueGap, "Bearish FVG", "Bearish FVG formed"); + pinescript.alertcondition(currentAlerts.internalBullishBOS, "Internal Bullish BOS", "Internal Bullish BOS formed"); + pinescript.alertcondition(currentAlerts.internalBullishCHoCH, "Internal Bullish CHoCH", "Internal Bullish CHoCH formed"); + pinescript.alertcondition(currentAlerts.internalBearishBOS, "Internal Bearish BOS", "Internal Bearish BOS formed"); + pinescript.alertcondition(currentAlerts.internalBearishCHoCH, "Internal Bearish CHoCH", "Internal Bearish CHoCH formed"); + pinescript.alertcondition(currentAlerts.swingBullishBOS, "Bullish BOS", "Internal Bullish BOS formed"); + pinescript.alertcondition(currentAlerts.swingBullishCHoCH, "Bullish CHoCH", "Internal Bullish CHoCH formed"); + pinescript.alertcondition(currentAlerts.swingBearishBOS, "Bearish BOS", "Bearish BOS formed"); + pinescript.alertcondition(currentAlerts.swingBearishCHoCH, "Bearish CHoCH", "Bearish CHoCH formed"); + pinescript.alertcondition(currentAlerts.internalBullishOrderBlock, "Bullish Internal OB Breakout", "Price broke bullish internal OB"); + pinescript.alertcondition(currentAlerts.internalBearishOrderBlock, "Bearish Internal OB Breakout", "Price broke bearish internal OB"); + pinescript.alertcondition(currentAlerts.swingBullishOrderBlock, "Bullish Swing OB Breakout", "Price broke bullish swing OB"); + pinescript.alertcondition(currentAlerts.swingBearishOrderBlock, "Bearish Swing OB Breakout", "Price broke bearish swing OB"); + pinescript.alertcondition(currentAlerts.equalHighs, "Equal Highs", "Equal highs detected"); + pinescript.alertcondition(currentAlerts.equalLows, "Equal Lows", "Equal lows detected"); + pinescript.alertcondition(currentAlerts.bullishFairValueGap, "Bullish FVG", "Bullish FVG formed"); + pinescript.alertcondition(currentAlerts.bearishFairValueGap, "Bearish FVG", "Bearish FVG formed"); } -// Export for use -export { main +// The bar-by-bar engine: runs main() once per bar over the dataset. +function run(data, options = {}) { + data = data || {}; + const inClose = (data.close || []).map(Number); + const n = inClose.length; + const inOpen = (data.open || inClose).map(Number); + const inHigh = (data.high || inClose).map(Number); + const inLow = (data.low || inClose).map(Number); + const inVol = (data.volume || new Array(n).fill(0)).map(Number); + const inTime = (data.time || inClose.map((_, i) => i)).map(Number); + + // Reset all persistent and per-run state so repeated runs are independent. + globalThis.__pineState = {}; + globalThis.__pineRuntime = { plots: {}, plotshapes: {}, alerts: [], bars: n, inputs: options.inputs || {} }; + pinescript.__rt = {}; + pinescript.__bar = 0; + + // Growing OHLCV windows: each is a SeriesArray that gains one element per bar, + // so window built-ins like sma and highest see history up to the current bar. + const O = pinescript.asSeries([]), H = pinescript.asSeries([]), L = pinescript.asSeries([]); + const C = pinescript.asSeries([]), V = pinescript.asSeries([]), T = pinescript.asSeries([]); + const HL2 = pinescript.asSeries([]), HLC3 = pinescript.asSeries([]), OHLC4 = pinescript.asSeries([]); + globalThis.open = O; globalThis.high = H; globalThis.low = L; globalThis.close = C; + globalThis.volume = V; globalThis.time = T; + globalThis.hl2 = HL2; globalThis.hlc3 = HLC3; globalThis.ohlc4 = OHLC4; + globalThis.syminfo = globalThis.syminfo || { tickerid: 'SYNTHETIC', ticker: 'SYNTHETIC', mintick: 0.01 }; + + const rt = globalThis.__pineRuntime; + for (let i = 0; i < n; i++) { + O.push(inOpen[i]); H.push(inHigh[i]); L.push(inLow[i]); C.push(inClose[i]); V.push(inVol[i]); T.push(inTime[i]); + HL2.push((inHigh[i] + inLow[i]) / 2); + HLC3.push((inHigh[i] + inLow[i] + inClose[i]) / 3); + OHLC4.push((inOpen[i] + inHigh[i] + inLow[i] + inClose[i]) / 4); + + pinescript.__bar = i; + rt.__barIndex = i; + rt.__plotIdx = 0; + rt.__shapeIdx = 0; + globalThis.bar_index = i; + globalThis.last_bar_index = n - 1; + globalThis.barstate = { + isfirst: i === 0, islast: i === n - 1, isrealtime: false, ishistory: true, + isconfirmed: true, isnew: true, islastconfirmedhistory: i === n - 1, + }; + + main(); + } + + // Backfill skipped bars so every output series has exactly n entries. + for (const k of Object.keys(rt.plots)) { + const d = rt.plots[k].data; + for (let i = 0; i < n; i++) if (d[i] === undefined) d[i] = null; + } + for (const k of Object.keys(rt.plotshapes)) { + const d = rt.plotshapes[k].data; + for (let i = 0; i < n; i++) if (d[i] === undefined) d[i] = false; + } + return rt; +} + + +export { main, run }; diff --git a/converted/Scalping_Strategy_3_minutes.js b/converted/Scalping_Strategy_3_minutes.js index 314e0b7..37aeac5 100644 --- a/converted/Scalping_Strategy_3_minutes.js +++ b/converted/Scalping_Strategy_3_minutes.js @@ -1,12 +1,63 @@ -// PineScript to JavaScript Transpiled Code +// Auto-generated JavaScript from PineScript source -// Generated automatically +// Do not edit by hand -- re-run the converter instead -// Built-in PineScript functions +// All the built-in PineScript functions that scripts depend on at runtime const pinescript = { + __scalar: function(v) { + if (v === null || v === undefined) return v; + if (typeof v === 'object' && typeof v.valueOf === 'function') { + const s = v.valueOf(); + if (typeof s === 'number' || typeof s === 'boolean' || s === null) return s; + } + return v; + }, + series: function(id, value) { + const rt = this.__rt || (this.__rt = {}); + const store = rt.series || (rt.series = {}); + let buf = store[id]; + if (!buf) buf = store[id] = []; + buf[this.__bar | 0] = this.__scalar(value); + return buf; + }, + hist: function(id, value, n) { + const buf = this.series(id, value); + const idx = (this.__bar | 0) - (n | 0); + return idx >= 0 && idx < buf.length ? buf[idx] : null; + }, + time: function(timeframe, session) { + const t = globalThis.time; + if (t && typeof t.valueOf === 'function') return t.valueOf(); + return Array.isArray(t) ? t[t.length - 1] : t; + }, + sign: function(x) { + if (x === null || x === undefined || (typeof x === 'number' && isNaN(x))) return null; + return Math.sign(Number(x)); + }, + unpack: function(value, count) { + if (Array.isArray(value)) return value; + if (value != null && typeof value[Symbol.iterator] === 'function') return Array.from(value); + return new Array(count || 0).fill(null); + }, + alertcondition: function(condition, ...rest) { + if (globalThis.__pineRuntime) { + globalThis.__pineRuntime.alerts.push({ condition, args: rest }); + } + return null; + }, + barcolor: function(color) { return null; }, + bgcolor: function(color, title, editable, showLast) { + return null; + }, + fill: function(series1, series2, color, title, editable) { + return null; + }, + hline: function(_price, _title, _opts) { + return null; + }, sma: function(series, length) { if (series === null || series === undefined) return null; if (!Array.isArray(series)) series = [series]; @@ -84,6 +135,13 @@ const pinescript = { } return weightSum > 0 ? sum / weightSum : null; }, + swma: function(series) { + if (series === null || series === undefined) return null; + if (!Array.isArray(series)) series = [series]; + if (series.length < 4) return null; + const s = series.slice(-4); + return (s[0] * 1 + s[1] * 2 + s[2] * 2 + s[3] * 1) / 6; + }, atr: function(high, low, close, length = 14) { if (!Array.isArray(high) || !Array.isArray(low) || !Array.isArray(close)) return null; if (high.length < length + 1 || low.length < length + 1 || close.length < length + 1) return null; @@ -114,6 +172,53 @@ const pinescript = { const atr = builtins.get('atr')(high, low, close, length); return { middle: sma, upper: sma + mult * atr, lower: sma - mult * atr }; }, + supertrend: function(factor, atrPeriod, high, low, close) { + if (!Array.isArray(high) || !Array.isArray(low) || !Array.isArray(close)) return [null, null]; + if (high.length < atrPeriod + 1) return [null, null]; + const atrVal = builtins.get('atr')(high, low, close, atrPeriod); + if (atrVal === null) return [null, null]; + const lastIdx = close.length - 1; + const hl2 = (high[lastIdx] + low[lastIdx]) / 2; + const upperBand = hl2 + factor * atrVal; + const lowerBand = hl2 - factor * atrVal; + // Determine direction: 1 means downtrend (price below band), -1 means uptrend + const direction = close[lastIdx] > upperBand ? -1 : close[lastIdx] < lowerBand ? 1 : -1; + const supertrendValue = direction === -1 ? lowerBand : upperBand; + return [supertrendValue, direction]; + }, + ta_supertrend: function(factor, atrPeriod) { + const H = globalThis.high; + const L = globalThis.low; + const C = globalThis.close; + return builtins.get('supertrend')(factor, atrPeriod, H, L, C); + }, + dmi: function(diLength, adxSmoothing, high, low, close) { + if (!Array.isArray(high) || !Array.isArray(low) || !Array.isArray(close)) return { plusDI: null, minusDI: null, adx: null }; + if (high.length < diLength + 1) return { plusDI: null, minusDI: null, adx: null }; + let plusDMSum = 0, minusDMSum = 0, trSum = 0; + for (let i = 1; i <= diLength; i++) { + const idx = high.length - 1 - diLength + i; + const upMove = high[idx] - high[idx - 1]; + const downMove = low[idx - 1] - low[idx]; + plusDMSum += (upMove > downMove && upMove > 0) ? upMove : 0; + minusDMSum += (downMove > upMove && downMove > 0) ? downMove : 0; + const tr = Math.max( + high[idx] - low[idx], + Math.abs(high[idx] - close[idx - 1]), + Math.abs(low[idx] - close[idx - 1]) + ); + trSum += tr; + } + const plusDI = trSum > 0 ? (plusDMSum / trSum) * 100 : 0; + const minusDI = trSum > 0 ? (minusDMSum / trSum) * 100 : 0; + const diSum = plusDI + minusDI; + const dx = diSum > 0 ? Math.abs(plusDI - minusDI) / diSum * 100 : 0; + return { plusDI, minusDI, adx: dx }; + }, + adx: function(diLength, adxSmoothing, high, low, close) { + const result = builtins.get('dmi')(diLength, adxSmoothing, high, low, close); + return result ? result.adx : null; + }, rsi: function(close, length = 14) { if (close === null || close === undefined) return null; if (!Array.isArray(close)) close = [close]; @@ -171,9 +276,7 @@ const pinescript = { return { macd: macdLine, signal: signalLine, histogram: histogram }; }, cci: function(a, b, c, d) { - // Pine signatures: - // - ta.cci(source, length) - // - ta.cci(high, low, close, length) + // Supports both ta.cci(source, length) and ta.cci(high, low, close, length) signatures let source; let length; if (Array.isArray(a) && typeof b === 'number' && c === undefined) { @@ -304,6 +407,15 @@ const pinescript = { stdev: function(source, length) { return Math.sqrt(builtins.get('variance')(source, length)); }, + median: function(source, length) { + if (!source || source.length < length) return null; + const sorted = [...source.slice(-length)].sort((a, b) => a - b); + const mid = Math.floor(sorted.length / 2); + return sorted.length % 2 !== 0 ? sorted[mid] : (sorted[mid - 1] + sorted[mid]) / 2; + }, + ta_median: function(source, length) { + return builtins.get('median')(source, length); + }, hl2: function(high, low) { return high && low ? (high[high.length - 1] + low[low.length - 1]) / 2 : null; }, @@ -420,6 +532,24 @@ const pinescript = { return (series1[i - 1] < series2[i - 1] && series1[i] >= series2[i]) || (series1[i - 1] > series2[i - 1] && series1[i] <= series2[i]); }, + crossover: function(series1, series2) { + if (series1 === null || series1 === undefined) return false; + if (series2 === null || series2 === undefined) return false; + if (!Array.isArray(series1)) series1 = [series1]; + if (!Array.isArray(series2)) series2 = [series2]; + if (series1.length < 2 || series2.length < 2) return false; + const i = series1.length - 1; + return series1[i - 1] < series2[i - 1] && series1[i] >= series2[i]; + }, + crossunder: function(series1, series2) { + if (series1 === null || series1 === undefined) return false; + if (series2 === null || series2 === undefined) return false; + if (!Array.isArray(series1)) series1 = [series1]; + if (!Array.isArray(series2)) series2 = [series2]; + if (series1.length < 2 || series2.length < 2) return false; + const i = series1.length - 1; + return series1[i - 1] > series2[i - 1] && series1[i] <= series2[i]; + }, offset: function(series, offset) { if (!Array.isArray(series)) return null; const idx = series.length - 1 - offset; @@ -500,12 +630,14 @@ const pinescript = { return builtins.get('nz')(value, replacement); }, plot: function(series, title = '', color = null, linewidth = 1) { - globalThis.__pineRuntime.plots.push({ - series, - title, - color, - linewidth - }); + const rt = globalThis.__pineRuntime; + if (!rt) return series; + const bar = rt.__barIndex | 0; + const ord = (rt.__plotIdx = (rt.__plotIdx | 0) + 1) - 1; + const key = (title && String(title)) || ('plot_' + ord); + let p = rt.plots[key]; + if (!p) p = rt.plots[key] = { title: key, color, linewidth, data: [] }; + p.data[bar] = this.__scalar(series); return series; }, lineNew: function(x1, y1, x2, y2, opts = {}) { @@ -514,20 +646,72 @@ const pinescript = { lineDelete: function(l) { return null; }, - labelNew: function(...args) { - if (globalThis.__pineRuntime) { - globalThis.__pineRuntime.labels = globalThis.__pineRuntime.labels || []; - globalThis.__pineRuntime.labels.push({ args }); - } - return { args }; + lineSetXY: function(line, point, x, y) { + if (!line) return null; + if (point === 0 || point === 'x1') { line.x1 = x; line.y1 = y; } + else { line.x2 = x; line.y2 = y; } + return line; + }, + lineGetX: function(line, point) { + if (!line) return null; + return point === 0 || point === 'x1' ? line.x1 : line.x2; + }, + lineGetY: function(line, point) { + if (!line) return null; + return point === 0 || point === 'y1' ? line.y1 : line.y2; + }, + labelNew: function(x, y, text = '', opts = {}) { + return { x, y, text, opts, _type: 'label' }; }, labelDelete: function(l) { return null; }, + labelSetText: function(label, text) { + if (!label) return null; + label.text = text; + return label; + }, + labelGetText: function(label) { + if (!label) return ''; + return label.text || ''; + }, + boxNew: function(left, top, right, bottom, opts = {}) { + return { left, top, right, bottom, opts, _type: 'box' }; + }, + boxDelete: function(box) { + return null; + }, + boxSetLeftTop: function(box, left, top) { + if (!box) return null; + box.left = left; + box.top = top; + return box; + }, + boxSetRightBottom: function(box, right, bottom) { + if (!box) return null; + box.right = right; + box.bottom = bottom; + return box; + }, + polylineNew: function(points, opts = {}) { + return { points: points || [], opts, _type: 'polyline' }; + }, + polylineDelete: function(poly) { + return null; + }, plotshape: function(condition, ...rest) { - if (globalThis.__pineRuntime) { - globalThis.__pineRuntime.plotshapes.push({ condition, args: rest }); + const rt = globalThis.__pineRuntime; + if (!rt) return condition; + const bar = rt.__barIndex | 0; + const ord = (rt.__shapeIdx = (rt.__shapeIdx | 0) + 1) - 1; + let key = 'shape_' + ord; + for (const r of rest) { + if (typeof r === 'string') { key = r; break; } + if (r && typeof r === 'object' && r.title) { key = String(r.title); break; } } + let s = rt.plotshapes[key]; + if (!s) s = rt.plotshapes[key] = { title: key, data: [] }; + s.data[bar] = !!this.__scalar(condition); return condition; }, indicator: function(title, shorttitle, overlay, opts) { @@ -542,27 +726,12 @@ const pinescript = { } return null; }, - alertcondition: function(condition, ...rest) { - if (globalThis.__pineRuntime) { - globalThis.__pineRuntime.alerts.push({ condition, args: rest }); - } - return null; - }, plotbar: function(open, high, low, close, title, color, editable, showLast) { return { open, high, low, close }; }, plotcandle: function(open, high, low, close, title, color, wickColor, borderColor, editable, showLast) { return { open, high, low, close }; }, - hline: function(_price, _title, _opts) { - return null; - }, - bgcolor: function(color, title, editable, showLast) { - return null; - }, - fill: function(series1, series2, color, title, editable) { - return null; - }, alert: function(condition, message, frequency) { console.log('Alert:', condition ? message : 'Condition not met'); return condition; @@ -828,6 +997,9 @@ const pinescript = { arrayIndexOf: function(arr, value) { return arr ? arr.indexOf(value) : -1; }, + arrayLastIndexOf: function(arr, value) { + return arr ? arr.lastIndexOf(value) : -1; + }, arraySum: function(arr) { return arr ? arr.reduce((a, b) => a + b, 0) : 0; }, @@ -840,6 +1012,42 @@ const pinescript = { arrayMax: function(arr) { return arr && arr.length > 0 ? Math.max(...arr) : null; }, + arrayStdev: function(arr) { + if (!arr || arr.length === 0) return null; + const mean = arr.reduce((a, b) => a + b, 0) / arr.length; + const variance = arr.reduce((sum, val) => sum + Math.pow(val - mean, 2), 0) / arr.length; + return Math.sqrt(variance); + }, + arrayVariance: function(arr) { + if (!arr || arr.length === 0) return null; + const mean = arr.reduce((a, b) => a + b, 0) / arr.length; + return arr.reduce((sum, val) => sum + Math.pow(val - mean, 2), 0) / arr.length; + }, + arrayCovariance: function(arr1, arr2) { + if (!arr1 || !arr2 || arr1.length === 0 || arr1.length !== arr2.length) return null; + const mean1 = arr1.reduce((a, b) => a + b, 0) / arr1.length; + const mean2 = arr2.reduce((a, b) => a + b, 0) / arr2.length; + let cov = 0; + for (let i = 0; i < arr1.length; i++) { + cov += (arr1[i] - mean1) * (arr2[i] - mean2); + } + return cov / arr1.length; + }, + mode: function(arr) { + if (!arr || arr.length === 0) return null; + const counts = new Map(); + let maxCount = 0; + let modeVal = arr[0]; + for (const val of arr) { + const c = (counts.get(val) || 0) + 1; + counts.set(val, c); + if (c > maxCount) { + maxCount = c; + modeVal = val; + } + } + return modeVal; + }, strLength: function(str) { return str ? str.length : 0; }, @@ -895,6 +1103,84 @@ const pinescript = { strReverse: function(str) { return str ? str.split('').reverse().join('') : ''; }, + strFormat: function(formatStr, ...args) { + if (!formatStr) return ''; + return formatStr.replace(/\{(\d+)\}/g, (match, idx) => { + const i = parseInt(idx, 10); + return i < args.length ? String(args[i]) : match; + }); + }, + input: function(defval, title, tooltip, inline, group) { + return defval; + }, + inputInt: function(defval = 0, title, minval, maxval, step, tooltip, inline, group) { + return defval; + }, + inputFloat: function(defval = 0.0, title, minval, maxval, step, tooltip, inline, group) { + return defval; + }, + inputBool: function(defval = false, title, tooltip, inline, group) { + return defval; + }, + inputString: function(defval = '', title, options, tooltip, inline, group) { + return defval; + }, + inputSource: function(defval = null, title, tooltip, inline, group) { + return defval; + }, + inputColor: function(defval = '#000000', title, tooltip, inline, group) { + return defval; + }, + inputTime: function(defval = 0, title, tooltip, inline, group) { + return defval; + }, + mathSign: function(value) { + return Math.sign(value); + }, + mathAvg: function(...args) { + const valid = args.filter(v => v !== null && v !== undefined && !isNaN(v)); + return valid.length > 0 ? valid.reduce((a, b) => a + b, 0) / valid.length : null; + }, + mathSum: function(series, length) { + if (!series || series.length < length) return null; + return series.slice(-length).reduce((a, b) => a + b, 0); + }, + mathRandom: function(min = 0, max = 1) { + return min + Math.random() * (max - min); + }, + mathTodegrees: function(radians) { + return radians * (180 / Math.PI); + }, + mathToradians: function(degrees) { + return degrees * (Math.PI / 180); + }, + colorT: function(color, transparency) { + if (transparency !== undefined) { + return { color, transparency }; + } + if (color && typeof color === 'object' && 'transparency' in color) { + return color.transparency; + } + return 0; + }, + tableCellSetText: function(table, column, row, text) { + return null; + }, + tableCellSetBgcolor: function(table, column, row, bgcolor) { + return null; + }, + tableMergeCells: function(table, startColumn, startRow, endColumn, endRow) { + return null; + }, + tableDelete: function(table) { + return null; + }, + tableClear: function(table, startColumn, startRow, endColumn, endRow) { + return null; + }, + maxBarsBack: function(series, length) { + return null; + }, pi: 3.141592653589793, e: 2.718281828459045, true: true, @@ -905,8 +1191,7 @@ const pinescript = { const info = { ticker: 'AAPL', tickerid: 'NASDAQ:AAPL', prefix: 'NASDAQ', root: 'AAPL', suffix: '' }; return info[type] || ''; }, - time: 1771178052740, - timenow: 1771178052740, + timenow: 1781574528384, barstate: "LAST", dividends: {}, splits: {}, @@ -923,7 +1208,7 @@ const pinescript = { return null; }, tr: function(high, low, close) { - // Pine signature: ta.tr(true) + // Handles both the boolean shorthand ta.tr(true) and explicit high/low/close arguments if (typeof high === 'boolean') { const H = globalThis.high; const L = globalThis.low; @@ -934,7 +1219,7 @@ const pinescript = { return Math.max(H[i] - L[i], Math.abs(H[i] - prevClose), Math.abs(L[i] - prevClose)); } - // Fallback: treat inputs as scalars for current bar + // When arrays are passed, compute true range from the last bar using the previous close if (Array.isArray(close)) { const prevClose = close[close.length - 2] || close[0]; const currHigh = Array.isArray(high) ? high[high.length - 1] : high; @@ -945,6 +1230,92 @@ const pinescript = { return Math.max(high - low, Math.abs(high - close), Math.abs(low - close)); }, + arrayFirst: function(arr) { + if (!arr || arr.length === 0) return null; + return arr[0]; + }, + arrayLast: function(arr) { + if (!arr || arr.length === 0) return null; + return arr[arr.length - 1]; + }, + arrayJoin: function(arr, separator = ',') { + if (!arr) return ''; + return arr.join(separator); + }, + arrayConcat: function(arr1, arr2) { + if (!arr1) return arr2 || []; + if (!arr2) return arr1; + return arr1.concat(arr2); + }, + arrayCopy: function(arr) { + if (!arr) return []; + return [...arr]; + }, + arrayBinarySearch: function(arr, value) { + if (!arr || arr.length === 0) return -1; + let lo = 0, hi = arr.length - 1; + while (lo <= hi) { + const mid = (lo + hi) >>> 1; + if (arr[mid] === value) return mid; + if (arr[mid] < value) lo = mid + 1; + else hi = mid - 1; + } + return -1; + }, + arrayRange: function(arr) { + if (!arr || arr.length === 0) return 0; + return Math.max(...arr) - Math.min(...arr); + }, + arrayMedian: function(arr) { + if (!arr || arr.length === 0) return null; + const sorted = [...arr].sort((a, b) => a - b); + const mid = Math.floor(sorted.length / 2); + return sorted.length % 2 !== 0 ? sorted[mid] : (sorted[mid - 1] + sorted[mid]) / 2; + }, + arrayMode: function(arr) { + if (!arr || arr.length === 0) return null; + const freq = {}; + let maxCount = 0; + let mode = arr[0]; + for (const v of arr) { + freq[v] = (freq[v] || 0) + 1; + if (freq[v] > maxCount) { maxCount = freq[v]; mode = v; } + } + return mode; + }, + arrayPercentile: function(arr, percentile) { + if (!arr || arr.length === 0) return null; + const sorted = [...arr].sort((a, b) => a - b); + const idx = (percentile / 100) * (sorted.length - 1); + const lo = Math.floor(idx); + const hi = Math.ceil(idx); + if (lo === hi) return sorted[lo]; + return sorted[lo] + (sorted[hi] - sorted[lo]) * (idx - lo); + }, + arrayPercentileLinearInterpolation: function(arr, percentile) { + return builtins.get('arrayPercentile')(arr, percentile); + }, + arrayPercentileNearestRank: function(arr, percentile) { + if (!arr || arr.length === 0) return null; + const sorted = [...arr].sort((a, b) => a - b); + const idx = Math.ceil((percentile / 100) * sorted.length) - 1; + return sorted[Math.max(0, idx)]; + }, + arrayAbs: function(arr) { + if (!arr) return []; + return arr.map(v => Math.abs(v)); + }, + arrayEvery: function(arr, callback) { + if (!arr) return false; + return arr.every(callback); + }, + arraySome: function(arr, callback) { + if (!arr) return false; + return arr.some(callback); + }, + requestFinancial: function() { + return null; + }, }; @@ -1101,6 +1472,115 @@ globalThis.barmerge = globalThis.barmerge || { }; +function __pineNS(real) { + + const base = real || {}; + + return new Proxy(base, { + + get(target, key) { + + if (key in target) return target[key]; + + if (typeof key === "symbol") return target[key]; + + // Unknown member: usable as a value (null), a constant, or a no-op function. + + const noop = function() { return null; }; + + noop.toString = () => ""; + + return noop; + + } + + }); + +} + +globalThis.ta = globalThis.ta || __pineNS({}); + +globalThis.math = globalThis.math || __pineNS({ pi: Math.PI, e: Math.E, phi: 1.618033988749895, rphi: 0.618033988749895 }); + +globalThis.chart = globalThis.chart || __pineNS({ point: __pineNS({}), bg_color: null, fg_color: null }); + +globalThis.line = globalThis.line || __pineNS({ style_solid: "solid", style_dashed: "dashed", style_dotted: "dotted" }); + +globalThis.box = globalThis.box || __pineNS({}); + +globalThis.label = globalThis.label || __pineNS({ style_label_down: "label_down", style_label_up: "label_up", style_none: "none" }); + +globalThis.polyline = globalThis.polyline || __pineNS({}); + +globalThis.linefill = globalThis.linefill || __pineNS({}); + +globalThis.matrix = globalThis.matrix || __pineNS({}); + +globalThis.map = globalThis.map || __pineNS({}); + +globalThis.session = globalThis.session || __pineNS({ regular: "regular", extended: "extended" }); + +globalThis.dayofweek = globalThis.dayofweek || __pineNS({ sunday: 1, monday: 2, tuesday: 3, wednesday: 4, thursday: 5, friday: 6, saturday: 7 }); + +globalThis.timeframe = __pineNS(Object.assign(globalThis.timeframe || {}, { period: (globalThis.timeframe && globalThis.timeframe.period) || "D", isintraday: false, isdaily: true, multiplier: 1 })); + +globalThis.request = __pineNS(globalThis.request || {}); + +globalThis.input = __pineNS(globalThis.input || {}); + +globalThis.adjustment = globalThis.adjustment || __pineNS({}); + +globalThis.earnings = globalThis.earnings || __pineNS({}); + +globalThis.dividends = globalThis.dividends || __pineNS({}); + +globalThis.splits = globalThis.splits || __pineNS({}); + +globalThis.currency = globalThis.currency || __pineNS({}); + +globalThis.display = globalThis.display || __pineNS({ none: "none", all: "all", pane: "pane", data_window: "data_window", status_line: "status_line", price_scale: "price_scale" }); + +globalThis.format = globalThis.format || __pineNS({ price: "price", volume: "volume", percent: "percent", mintick: "mintick", inherit: "inherit" }); + +globalThis.scale = globalThis.scale || __pineNS({ left: "left", right: "right", none: "none" }); + +globalThis.font = globalThis.font || __pineNS({ family_default: "default", family_monospace: "monospace" }); + +globalThis.xloc = globalThis.xloc || __pineNS({ bar_index: "bar_index", bar_time: "bar_time" }); + +globalThis.yloc = globalThis.yloc || __pineNS({ price: "price", abovebar: "abovebar", belowbar: "belowbar" }); + +globalThis.extend = globalThis.extend || __pineNS({ none: "none", left: "left", right: "right", both: "both" }); + +globalThis.order = globalThis.order || __pineNS({ ascending: "ascending", descending: "descending" }); + +globalThis.alert = globalThis.alert || Object.assign(function() { return null; }, { freq_once_per_bar: "once_per_bar", freq_once_per_bar_close: "once_per_bar_close", freq_all: "all" }); + +globalThis.dayofmonth = globalThis.dayofmonth || function(t) { return new Date(t || 0).getUTCDate(); }; + +globalThis.plot = globalThis.plot || __pineNS({ style_line: "line", style_linebr: "linebr", style_stepline: "stepline", style_histogram: "histogram", style_cross: "cross", style_area: "area", style_areabr: "areabr", style_columns: "columns", style_circles: "circles" }); + +globalThis.int = globalThis.int || function(x) { return x == null ? null : Math.trunc(Number(x)); }; + +globalThis.float = globalThis.float || function(x) { return x == null ? null : Number(x); }; + +globalThis.bool = globalThis.bool || function(x) { return Boolean(x); }; + +globalThis.string = globalThis.string || function(x) { return x == null ? null : String(x); }; + +globalThis.dayofweek = globalThis.dayofweek || __pineNS({ sunday: 1, monday: 2, tuesday: 3, wednesday: 4, thursday: 5, friday: 6, saturday: 7 }); + +globalThis.str = globalThis.str || __pineNS({}); + +pinescript.math = globalThis.math; + +pinescript.ta = globalThis.ta; + +pinescript.str = globalThis.str; + +pinescript.array = __pineNS(globalThis.array); + + pinescript.color = { hex: function(s) { @@ -1190,10 +1670,10 @@ pinescript.table = { }; -// Input parameters +// Script input parameters and their defaults -// Main script logic +// The transpiled script logic, called once per bar function main() { @@ -1212,30 +1692,30 @@ function main() { // Options: {"shorttitle":"XXX ","overlay":true,"explicit_plot_zorder":true,"pyramiding":0,"default_qty_type":null,"default_qty_value":10,"calc_on_every_tick":false,"process_orders_on_close":true} let G_SCRIPT01 = ("■ " + "SAIYAN OCC"); let res = input.timeframe("15", "TIMEFRAME", ({ group: "NON REPAINT" })); - let useRes = input(true, "Use Alternate Signals"); - let intRes = input(8, "Multiplier for Alernate Signals"); - let basisType = input.string("ALMA", "MA Type: ", ({ options: ["TEMA", "HullMA", "ALMA"] })); - let basisLen = input.int(2, "MA Period", ({ minval: 1 })); - let offsetSigma = input.int(5, "Offset for LSMA / Sigma for ALMA", ({ minval: 0 })); - let offsetALMA = input.float(0.85, "Offset for ALMA", ({ minval: 0, step: 0.01 })); - let scolor = input(false, "Show coloured Bars to indicate Trend?"); - let delayOffset = input.int(0, "Delay Open/Close MA", ({ minval: 0, step: 1, tooltip: "Forces Non-Repainting" })); - let tradeType = input.string("BOTH", "What trades should be taken : ", ({ options: ["LONG", "SHORT", "BOTH", "NONE"] })); - let h = input(false, "Signals for Heikin Ashi Candles"); - let swing_length = input.int(10, "Swing High/Low Length", ({ group: "Settings", minval: 1, maxval: 50 })); - let history_of_demand_to_keep = input.int(20, "History To Keep", ({ minval: 5, maxval: 50 })); - let box_width = input.float(2.5, "Supply/Demand Box Width", ({ group: "Settings", minval: 1, maxval: 10, step: 0.5 })); - let show_zigzag = input.bool(false, "Show Zig Zag", ({ group: "Visual Settings", inline: "1" })); - let show_price_action_labels = input.bool(false, "Show Price Action Labels", ({ group: "Visual Settings", inline: "2" })); - let supply_color = input.color(pinescript.color.hex("#00000000"), "Supply", ({ group: "Visual Settings", inline: "3" })); - let supply_outline_color = input.color(pinescript.color.hex("#00000000"), "Outline", ({ group: "Visual Settings", inline: "3" })); - let demand_color = input.color(pinescript.color.hex("#00000000"), "Demand", ({ group: "Visual Settings", inline: "4" })); - let demand_outline_color = input.color(pinescript.color.hex("#00000000"), "Outline", ({ group: "Visual Settings", inline: "4" })); - let bos_label_color = input.color(pinescript.color.hex("#00000000"), "BOS Label", ({ group: "Visual Settings", inline: "5" })); - let poi_label_color = input.color(pinescript.color.hex("#00000000"), "POI Label", ({ group: "Visual Settings", inline: "7" })); - let poi_border_color = input.color(pinescript.color.hex("#00000000"), "POI border", ({ group: "Visual Settings", inline: "7" })); - let swing_type_color = input.color(pinescript.color.hex("#00000000"), "Price Action Label", ({ group: "Visual Settings", inline: "8" })); - let zigzag_color = input.color(pinescript.color.hex("#00000000"), "Zig Zag", ({ group: "Visual Settings", inline: "9" })); + let useRes = pinescript.input(true, "Use Alternate Signals"); + let intRes = pinescript.input(8, "Multiplier for Alernate Signals"); + let basisType = pinescript.inputString("ALMA", "MA Type: ", ({ options: ["TEMA", "HullMA", "ALMA"] })); + let basisLen = pinescript.inputInt(2, "MA Period", ({ minval: 1 })); + let offsetSigma = pinescript.inputInt(5, "Offset for LSMA / Sigma for ALMA", ({ minval: 0 })); + let offsetALMA = pinescript.inputFloat(0.85, "Offset for ALMA", ({ minval: 0, step: 0.01 })); + let scolor = pinescript.input(false, "Show coloured Bars to indicate Trend?"); + let delayOffset = pinescript.inputInt(0, "Delay Open/Close MA", ({ minval: 0, step: 1, tooltip: "Forces Non-Repainting" })); + let tradeType = pinescript.inputString("BOTH", "What trades should be taken : ", ({ options: ["LONG", "SHORT", "BOTH", "NONE"] })); + let h = pinescript.input(false, "Signals for Heikin Ashi Candles"); + let swing_length = pinescript.inputInt(10, "Swing High/Low Length", ({ group: "Settings", minval: 1, maxval: 50 })); + let history_of_demand_to_keep = pinescript.inputInt(20, "History To Keep", ({ minval: 5, maxval: 50 })); + let box_width = pinescript.inputFloat(2.5, "Supply/Demand Box Width", ({ group: "Settings", minval: 1, maxval: 10, step: 0.5 })); + let show_zigzag = pinescript.inputBool(false, "Show Zig Zag", ({ group: "Visual Settings", inline: "1" })); + let show_price_action_labels = pinescript.inputBool(false, "Show Price Action Labels", ({ group: "Visual Settings", inline: "2" })); + let supply_color = pinescript.inputColor(pinescript.color.hex("#00000000"), "Supply", ({ group: "Visual Settings", inline: "3" })); + let supply_outline_color = pinescript.inputColor(pinescript.color.hex("#00000000"), "Outline", ({ group: "Visual Settings", inline: "3" })); + let demand_color = pinescript.inputColor(pinescript.color.hex("#00000000"), "Demand", ({ group: "Visual Settings", inline: "4" })); + let demand_outline_color = pinescript.inputColor(pinescript.color.hex("#00000000"), "Outline", ({ group: "Visual Settings", inline: "4" })); + let bos_label_color = pinescript.inputColor(pinescript.color.hex("#00000000"), "BOS Label", ({ group: "Visual Settings", inline: "5" })); + let poi_label_color = pinescript.inputColor(pinescript.color.hex("#00000000"), "POI Label", ({ group: "Visual Settings", inline: "7" })); + let poi_border_color = pinescript.inputColor(pinescript.color.hex("#00000000"), "POI border", ({ group: "Visual Settings", inline: "7" })); + let swing_type_color = pinescript.inputColor(pinescript.color.hex("#00000000"), "Price Action Label", ({ group: "Visual Settings", inline: "8" })); + let zigzag_color = pinescript.inputColor(pinescript.color.hex("#00000000"), "Zig Zag", ({ group: "Visual Settings", inline: "9" })); function f_array_add_pop(array, new_value_to_add) { pinescript.arrayUnshift(pinescript.array, new_value_to_add); return pinescript.arrayPop(pinescript.array); @@ -1298,16 +1778,16 @@ function main() { } okay_to_draw = f_check_overlapping(state.poi, box_array, atrValue); if (((box_type === 1) && okay_to_draw)) { - box.delete(pinescript.arrayGet(box_array, (pinescript.arraySize(box_array) - 1))); - f_array_add_pop(box_array, box.new(({ left: box_left, top: state.box_top, right: box_right, bottom: state.box_bottom, border_color: supply_outline_color, bgcolor: supply_color, extend: extend.right, text: "SUPPLY", text_halign: pinescript.text.align_center, text_valign: pinescript.text.align_center, text_color: poi_label_color, text_size: pinescript.size.small, xloc: xloc.bar_index }))); - box.delete(pinescript.arrayGet(label_array, (pinescript.arraySize(label_array) - 1))); - f_array_add_pop(label_array, box.new(({ left: box_left, top: state.poi, right: box_right, bottom: state.poi, border_color: poi_border_color, bgcolor: poi_border_color, extend: extend.right, text: "POI", text_halign: pinescript.text.align_left, text_valign: pinescript.text.align_center, text_color: poi_label_color, text_size: pinescript.size.small, xloc: xloc.bar_index }))); + pinescript.boxDelete(pinescript.arrayGet(box_array, (pinescript.arraySize(box_array) - 1))); + f_array_add_pop(box_array, pinescript.boxNew(({ left: box_left, top: state.box_top, right: box_right, bottom: state.box_bottom, border_color: supply_outline_color, bgcolor: supply_color, extend: extend.right, text: "SUPPLY", text_halign: pinescript.text.align_center, text_valign: pinescript.text.align_center, text_color: poi_label_color, text_size: pinescript.size.small, xloc: xloc.bar_index }))); + pinescript.boxDelete(pinescript.arrayGet(label_array, (pinescript.arraySize(label_array) - 1))); + f_array_add_pop(label_array, pinescript.boxNew(({ left: box_left, top: state.poi, right: box_right, bottom: state.poi, border_color: poi_border_color, bgcolor: poi_border_color, extend: extend.right, text: "POI", text_halign: pinescript.text.align_left, text_valign: pinescript.text.align_center, text_color: poi_label_color, text_size: pinescript.size.small, xloc: xloc.bar_index }))); } else { if (((box_type === -1) && okay_to_draw)) { - box.delete(pinescript.arrayGet(box_array, (pinescript.arraySize(box_array) - 1))); - f_array_add_pop(box_array, box.new(({ left: box_left, top: state.box_top, right: box_right, bottom: state.box_bottom, border_color: demand_outline_color, bgcolor: demand_color, extend: extend.right, text: "DEMAND", text_halign: pinescript.text.align_center, text_valign: pinescript.text.align_center, text_color: poi_label_color, text_size: pinescript.size.small, xloc: xloc.bar_index }))); - box.delete(pinescript.arrayGet(label_array, (pinescript.arraySize(label_array) - 1))); - f_array_add_pop(label_array, box.new(({ left: box_left, top: state.poi, right: box_right, bottom: state.poi, border_color: poi_border_color, bgcolor: poi_border_color, extend: extend.right, text: "POI", text_halign: pinescript.text.align_left, text_valign: pinescript.text.align_center, text_color: poi_label_color, text_size: pinescript.size.small, xloc: xloc.bar_index }))); + pinescript.boxDelete(pinescript.arrayGet(box_array, (pinescript.arraySize(box_array) - 1))); + f_array_add_pop(box_array, pinescript.boxNew(({ left: box_left, top: state.box_top, right: box_right, bottom: state.box_bottom, border_color: demand_outline_color, bgcolor: demand_color, extend: extend.right, text: "DEMAND", text_halign: pinescript.text.align_center, text_valign: pinescript.text.align_center, text_color: poi_label_color, text_size: pinescript.size.small, xloc: xloc.bar_index }))); + pinescript.boxDelete(pinescript.arrayGet(label_array, (pinescript.arraySize(label_array) - 1))); + f_array_add_pop(label_array, pinescript.boxNew(({ left: box_left, top: state.poi, right: box_right, bottom: state.poi, border_color: poi_border_color, bgcolor: poi_border_color, extend: extend.right, text: "POI", text_halign: pinescript.text.align_left, text_valign: pinescript.text.align_center, text_color: poi_label_color, text_size: pinescript.size.small, xloc: xloc.bar_index }))); } } } @@ -1328,8 +1808,8 @@ function main() { box.set_text_size(pinescript.arrayGet(bos_array, 0), pinescript.size.small); box.set_text_halign(pinescript.arrayGet(bos_array, 0), pinescript.text.align_center); box.set_text_valign(pinescript.arrayGet(bos_array, 0), pinescript.text.align_center); - box.delete(pinescript.arrayGet(box_array, i)); - box.delete(pinescript.arrayGet(label_array, i)); + pinescript.boxDelete(pinescript.arrayGet(box_array, i)); + pinescript.boxDelete(pinescript.arrayGet(label_array, i)); } } } @@ -1349,8 +1829,8 @@ function main() { box.set_text_size(pinescript.arrayGet(bos_array, 0), pinescript.size.small); box.set_text_halign(pinescript.arrayGet(bos_array, 0), pinescript.text.align_center); box.set_text_valign(pinescript.arrayGet(bos_array, 0), pinescript.text.align_center); - box.delete(pinescript.arrayGet(box_array, i)); - box.delete(pinescript.arrayGet(label_array, i)); + pinescript.boxDelete(pinescript.arrayGet(box_array, i)); + pinescript.boxDelete(pinescript.arrayGet(label_array, i)); } } } @@ -1362,7 +1842,7 @@ function main() { } let stratRes = (timeframe.ismonthly ? pinescript.strToString((timeframe.multiplier * intRes), "###M") : (timeframe.isweekly ? pinescript.strToString((timeframe.multiplier * intRes), "###W") : (timeframe.isdaily ? pinescript.strToString((timeframe.multiplier * intRes), "###D") : (timeframe.isintraday ? pinescript.strToString((timeframe.multiplier * intRes), "####") : "60")))); let src = (h ? pinescript.requestSecurity(ticker.heikinashi(syminfo.tickerid), timeframe.period, close, ({ lookahead: barmerge.lookahead_off })) : close); - let atrValue = ta.atr(50); + let atrValue = pinescript.atr(50); let swing_high = ta.pivothigh(high, swing_length, swing_length); let swing_low = ta.pivotlow(low, swing_length, swing_length); if (state.swing_high_values === undefined) state.swing_high_values = pinescript.arrayNew(5, 0); @@ -1377,7 +1857,7 @@ function main() { if (state.demand_bos === undefined) state.demand_bos = pinescript.arrayNew(5, null); if (!pinescript.na(swing_high)) { f_array_add_pop(state.swing_high_values, swing_high); - f_array_add_pop(state.swing_high_bns, pinescript.offset(bar_index, swing_length)); + f_array_add_pop(state.swing_high_bns, pinescript.hist(0, bar_index, swing_length)); if (show_price_action_labels) { f_sh_sl_labels(state.swing_high_values, 1); } @@ -1385,7 +1865,7 @@ function main() { } else { if (!pinescript.na(swing_low)) { f_array_add_pop(state.swing_low_values, swing_low); - f_array_add_pop(state.swing_low_bns, pinescript.offset(bar_index, swing_length)); + f_array_add_pop(state.swing_low_bns, pinescript.hist(1, bar_index, swing_length)); if (show_price_action_labels) { f_sh_sl_labels(state.swing_low_values, -1); } @@ -1396,14 +1876,14 @@ function main() { f_sd_to_bos(state.current_demand_box, state.demand_bos, state.current_demand_poi, -1); f_extend_box_endpoint(state.current_supply_box); f_extend_box_endpoint(state.current_demand_box); - let channelBal = input.bool(false, "Channel Balance", ({ group: "CHART" })); + let channelBal = pinescript.inputBool(false, "Channel Balance", ({ group: "CHART" })); function lr_slope(_src, _len) { let x = 0; let y = 0; let x2 = 0; let xy = 0; for (let i = 0; i <= (_len - 1); i++) { - let val = pinescript.offset(_src, i); + let val = pinescript.hist(2, _src, i); let per = (i + 1); x += per; y += val; @@ -1420,35 +1900,35 @@ function main() { let dnDev = 0; val = _int; for (let j = 0; j <= (_len - 1); j++) { - let price = (pinescript.offset(high, j) - val); + let price = (pinescript.hist(3, high, j) - val); if ((price > upDev)) { upDev = price; } - price = (val - pinescript.offset(low, j)); + price = (val - pinescript.hist(4, low, j)); if ((price > dnDev)) { dnDev = price; } - price = pinescript.offset(_src, j); + price = pinescript.hist(5, _src, j); val += _slp; } return [upDev, dnDev]; } - let [, upperKC1, lowerKC1] = ta.kc(close, 80, 10.5); - let [, upperKC2, lowerKC2] = ta.kc(close, 80, 9.5); - let [, upperKC3, lowerKC3] = ta.kc(close, 80, 8); - let [, upperKC4, lowerKC4] = ta.kc(close, 80, 3); + let [, upperKC1, lowerKC1] = pinescript.unpack(pinescript.kc(close, 80, 10.5), 3); + let [, upperKC2, lowerKC2] = pinescript.unpack(pinescript.kc(close, 80, 9.5), 3); + let [, upperKC3, lowerKC3] = pinescript.unpack(pinescript.kc(close, 80, 8), 3); + let [, upperKC4, lowerKC4] = pinescript.unpack(pinescript.kc(close, 80, 3), 3); let barsL = 10; let barsR = 10; - let pivotHigh = pinescript.fixnan(pinescript.offset(ta.pivothigh(barsL, barsR), 1)); - let pivotLow = pinescript.fixnan(pinescript.offset(ta.pivotlow(barsL, barsR), 1)); + let pivotHigh = pinescript.fixnan(pinescript.hist(6, ta.pivothigh(barsL, barsR), 1)); + let pivotLow = pinescript.fixnan(pinescript.hist(7, ta.pivotlow(barsL, barsR), 1)); let source = close; let period = 150; - let [s, a, i] = lr_slope(source, period); - [upDev, dnDev] = lr_dev(source, period, s, a, i); - let y1 = (low - (ta.atr(30) * 2)); - let y1B = (low - ta.atr(30)); - let y2 = (high + (ta.atr(30) * 2)); - let y2B = (high + ta.atr(30)); + let [s, a, i] = pinescript.unpack(lr_slope(source, period), 3); + [upDev, dnDev] = pinescript.unpack(lr_dev(source, period, s, a, i), 2); + let y1 = (low - (pinescript.atr(30) * 2)); + let y1B = (low - pinescript.atr(30)); + let y2 = (high + (pinescript.atr(30) * 2)); + let y2B = (high + pinescript.atr(30)); let x1 = ((bar_index - period) + 1); let _y1 = (i + (s * (period - 1))); x2 = bar_index; @@ -1466,7 +1946,7 @@ function main() { if (state.ob_left === undefined) state.ob_left = pinescript.arrayNew(0); let ob = null; if (condition) { - let avg = math.avg(top, btm); + let avg = pinescript.avg(top, btm); pinescript.arrayUnshift(state.ob_top, top); pinescript.arrayUnshift(state.ob_btm, btm); pinescript.arrayUnshift(state.ob_avg, avg); @@ -1497,21 +1977,21 @@ function main() { if (state.target_bull === undefined) state.target_bull = 0; if (state.target_bear === undefined) state.target_bear = 0; function rp_security(_symbol, _res, _src) { - return pinescript.requestSecurity(_symbol, _res, pinescript.offset(_src, (barstate.isrealtime ? 1 : 0))); + return pinescript.requestSecurity(_symbol, _res, pinescript.hist(8, _src, (barstate.isrealtime ? 1 : 0))); } let htfHigh = rp_security(syminfo.tickerid, res, high); let htfLow = rp_security(syminfo.tickerid, res, low); function smoothrng(x, t, m) { let wper = ((t * 2) - 1); - let avrng = pinescript.ema(pinescript.abs((x - pinescript.offset(x, 1))), t); + let avrng = pinescript.ema(pinescript.abs((x - pinescript.hist(9, x, 1))), t); let smoothrng = (pinescript.ema(avrng, wper) * m); } function rngfilt(x, r) { let rngfilt = x; - rngfilt = ((x > pinescript.nz(pinescript.offset(rngfilt, 1))) ? (((x - r) < pinescript.nz(pinescript.offset(rngfilt, 1))) ? pinescript.nz(pinescript.offset(rngfilt, 1)) : (x - r)) : (((x + r) > pinescript.nz(pinescript.offset(rngfilt, 1))) ? pinescript.nz(pinescript.offset(rngfilt, 1)) : (x + r))); + rngfilt = ((x > pinescript.nz(pinescript.hist(10, rngfilt, 1))) ? (((x - r) < pinescript.nz(pinescript.hist(11, rngfilt, 1))) ? pinescript.nz(pinescript.hist(12, rngfilt, 1)) : (x - r)) : (((x + r) > pinescript.nz(pinescript.hist(13, rngfilt, 1))) ? pinescript.nz(pinescript.hist(14, rngfilt, 1)) : (x + r))); } function percWidth(len, perc) { - return (((pinescript.highest(len) - pinescript.lowest(len)) * perc) / 100); + return (((pinescript.highest(pinescript.series(15, len)) - pinescript.lowest(pinescript.series(16, len))) * perc) / 100); } function securityNoRep(sym, res, src) { return pinescript.requestSecurity(sym, res, src, barmerge.gaps_off, barmerge.lookahead_on); @@ -1519,8 +1999,8 @@ function main() { function swingPoints(prd) { let pivHi = ta.pivothigh(prd, prd); let pivLo = ta.pivotlow(prd, prd); - let last_pivHi = ta.valuewhen(pivHi, pivHi, 1); - let last_pivLo = ta.valuewhen(pivLo, pivLo, 1); + let last_pivHi = pinescript.valuewhen(pinescript.series(17, pivHi), pinescript.series(18, pivHi), 1); + let last_pivLo = pinescript.valuewhen(pinescript.series(19, pivLo), pinescript.series(20, pivLo), 1); let hh = ((pivHi && (pivHi > last_pivHi)) ? pivHi : null); let lh = ((pivHi && (pivHi < last_pivHi)) ? pivHi : null); let hl = ((pivLo && (pivLo > last_pivLo)) ? pivLo : null); @@ -1532,7 +2012,7 @@ function main() { } function f_kc(src, len, sensitivity) { let basis = pinescript.sma(src, len); - let span = ta.atr(len); + let span = pinescript.atr(len); return [(basis + (span * sensitivity)), (basis - (span * sensitivity))]; } function wavetrend(src, chlLen, avgLen) { @@ -1544,10 +2024,10 @@ function main() { return [wt1, wt2]; } function f_top_fractal(_src) { - return ((((pinescript.offset(_src, 4) < pinescript.offset(_src, 2)) && (pinescript.offset(_src, 3) < pinescript.offset(_src, 2))) && (pinescript.offset(_src, 2) > pinescript.offset(_src, 1))) && (pinescript.offset(_src, 2) > pinescript.offset(_src, 0))); + return ((((pinescript.hist(21, _src, 4) < pinescript.hist(22, _src, 2)) && (pinescript.hist(23, _src, 3) < pinescript.hist(24, _src, 2))) && (pinescript.hist(25, _src, 2) > pinescript.hist(26, _src, 1))) && (pinescript.hist(27, _src, 2) > pinescript.hist(28, _src, 0))); } function f_bot_fractal(_src) { - return ((((pinescript.offset(_src, 4) > pinescript.offset(_src, 2)) && (pinescript.offset(_src, 3) > pinescript.offset(_src, 2))) && (pinescript.offset(_src, 2) < pinescript.offset(_src, 1))) && (pinescript.offset(_src, 2) < pinescript.offset(_src, 0))); + return ((((pinescript.hist(29, _src, 4) > pinescript.hist(30, _src, 2)) && (pinescript.hist(31, _src, 3) > pinescript.hist(32, _src, 2))) && (pinescript.hist(33, _src, 2) < pinescript.hist(34, _src, 1))) && (pinescript.hist(35, _src, 2) < pinescript.hist(36, _src, 0))); } let top_fractal = f_top_fractal(src); let bot_fractal = f_bot_fractal(src); @@ -1555,26 +2035,26 @@ function main() { return (top_fractal ? 1 : (bot_fractal ? -1 : 0)); } function f_findDivs(src, topLimit, botLimit) { - let fractalTop = (((f_fractalize(src) > 0) && (pinescript.offset(src, 2) >= topLimit)) ? pinescript.offset(src, 2) : null); - let fractalBot = (((f_fractalize(src) < 0) && (pinescript.offset(src, 2) <= botLimit)) ? pinescript.offset(src, 2) : null); - let highPrev = pinescript.offset(ta.valuewhen(fractalTop, pinescript.offset(src, 2), 0), 2); - let highPrice = pinescript.offset(ta.valuewhen(fractalTop, pinescript.offset(high, 2), 0), 2); - let lowPrev = pinescript.offset(ta.valuewhen(fractalBot, pinescript.offset(src, 2), 0), 2); - let lowPrice = pinescript.offset(ta.valuewhen(fractalBot, pinescript.offset(low, 2), 0), 2); - let bearSignal = ((fractalTop && (pinescript.offset(high, 1) > highPrice)) && (pinescript.offset(src, 1) < highPrev)); - let bullSignal = ((fractalBot && (pinescript.offset(low, 1) < lowPrice)) && (pinescript.offset(src, 1) > lowPrev)); + let fractalTop = (((f_fractalize(src) > 0) && (pinescript.hist(37, src, 2) >= topLimit)) ? pinescript.hist(38, src, 2) : null); + let fractalBot = (((f_fractalize(src) < 0) && (pinescript.hist(39, src, 2) <= botLimit)) ? pinescript.hist(40, src, 2) : null); + let highPrev = pinescript.hist(41, pinescript.valuewhen(pinescript.series(43, fractalTop), pinescript.series(44, pinescript.hist(42, src, 2)), 0), 2); + let highPrice = pinescript.hist(45, pinescript.valuewhen(pinescript.series(47, fractalTop), pinescript.series(48, pinescript.hist(46, high, 2)), 0), 2); + let lowPrev = pinescript.hist(49, pinescript.valuewhen(pinescript.series(51, fractalBot), pinescript.series(52, pinescript.hist(50, src, 2)), 0), 2); + let lowPrice = pinescript.hist(53, pinescript.valuewhen(pinescript.series(55, fractalBot), pinescript.series(56, pinescript.hist(54, low, 2)), 0), 2); + let bearSignal = ((fractalTop && (pinescript.hist(57, high, 1) > highPrice)) && (pinescript.hist(58, src, 1) < highPrev)); + let bullSignal = ((fractalBot && (pinescript.hist(59, low, 1) < lowPrice)) && (pinescript.hist(60, src, 1) > lowPrev)); return [bearSignal, bullSignal]; } - let enableSR = input(false, "SR On/Off", ({ group: "SR" })); - let colorSup = input(pinescript.color.hex("#00000000"), "Support Color", ({ group: "SR" })); - let colorRes = input(pinescript.color.hex("#00000000"), "Resistance Color", ({ group: "SR" })); - let strengthSR = input.int(2, "S/R Strength", 1, ({ group: "SR" })); - let lineStyle = input.string("Dotted", "Line Style", ["Solid", "Dotted", "Dashed"], ({ group: "SR" })); - let lineWidth = input.int(2, "S/R Line Width", 1, ({ group: "SR" })); - let useZones = input(true, "Zones On/Off", ({ group: "SR" })); - let useHLZones = input(true, "High Low Zones On/Off", ({ group: "SR" })); - let zoneWidth = input.int(2, "Zone Width %", 0, ({ tooltip: "it's calculated using % of the distance between highest/lowest in last 300 bars", group: "SR" })); - let expandSR = input(true, "Expand SR"); + let enableSR = pinescript.input(false, "SR On/Off", ({ group: "SR" })); + let colorSup = pinescript.input(pinescript.color.hex("#00000000"), "Support Color", ({ group: "SR" })); + let colorRes = pinescript.input(pinescript.color.hex("#00000000"), "Resistance Color", ({ group: "SR" })); + let strengthSR = pinescript.inputInt(2, "S/R Strength", 1, ({ group: "SR" })); + let lineStyle = pinescript.inputString("Dotted", "Line Style", ["Solid", "Dotted", "Dashed"], ({ group: "SR" })); + let lineWidth = pinescript.inputInt(2, "S/R Line Width", 1, ({ group: "SR" })); + let useZones = pinescript.input(true, "Zones On/Off", ({ group: "SR" })); + let useHLZones = pinescript.input(true, "High Low Zones On/Off", ({ group: "SR" })); + let zoneWidth = pinescript.inputInt(2, "Zone Width %", 0, ({ tooltip: "it's calculated using % of the distance between highest/lowest in last 300 bars", group: "SR" })); + let expandSR = pinescript.input(true, "Expand SR"); let rb = 10; let prd = 284; let ChannelW = 10; @@ -1583,24 +2063,24 @@ function main() { let ph = ta.pivothigh(rb, rb); let pl = ta.pivotlow(rb, rb); let sr_levels = pinescript.arrayNew(21, null); - let prdhighest = pinescript.highest(prd); - let prdlowest = pinescript.lowest(prd); + let prdhighest = pinescript.highest(pinescript.series(61, prd)); + let prdlowest = pinescript.lowest(pinescript.series(62, prd)); let cwidth = percWidth(prd, ChannelW); let zonePerc = percWidth(300, zoneWidth); let aas = pinescript.arrayNew(41, true); let u1 = 0; - u1 = pinescript.nz(pinescript.offset(u1, 1)); + u1 = pinescript.nz(pinescript.hist(63, u1, 1)); let d1 = 0; - d1 = pinescript.nz(pinescript.offset(d1, 1)); + d1 = pinescript.nz(pinescript.hist(64, d1, 1)); let highestph = 0; - highestph = pinescript.offset(highestph, 1); + highestph = pinescript.hist(65, highestph, 1); let lowestpl = 0; - lowestpl = pinescript.offset(lowestpl, 1); + lowestpl = pinescript.hist(66, lowestpl, 1); if (state.sr_levs === undefined) state.sr_levs = pinescript.arrayNew(21, null); let hlabel = null; - pinescript.labelDelete(pinescript.offset(hlabel, 1)); + pinescript.labelDelete(pinescript.hist(67, hlabel, 1)); let llabel = null; - pinescript.labelDelete(pinescript.offset(llabel, 1)); + pinescript.labelDelete(pinescript.hist(68, llabel, 1)); if (state.sr_lines === undefined) state.sr_lines = pinescript.arrayNew(21, null); if (state.sr_linesH === undefined) state.sr_linesH = pinescript.arrayNew(21, null); if (state.sr_linesL === undefined) state.sr_linesL = pinescript.arrayNew(21, null); @@ -1614,43 +2094,43 @@ function main() { lowestpl = prdhighest; let countpp = 0; for (let x = 0; x <= prd; x++) { - if (pinescript.na(pinescript.offset(close, x))) { + if (pinescript.na(pinescript.hist(69, close, x))) { break; } - if ((!pinescript.na(pinescript.offset(ph, x)) || !pinescript.na(pinescript.offset(pl, x)))) { - highestph = pinescript.max(highestph, pinescript.nz(pinescript.offset(ph, x), prdlowest), pinescript.nz(pinescript.offset(pl, x), prdlowest)); - lowestpl = pinescript.min(lowestpl, pinescript.nz(pinescript.offset(ph, x), prdhighest), pinescript.nz(pinescript.offset(pl, x), prdhighest)); + if ((!pinescript.na(pinescript.hist(70, ph, x)) || !pinescript.na(pinescript.hist(71, pl, x)))) { + highestph = pinescript.max(highestph, pinescript.nz(pinescript.hist(72, ph, x), prdlowest), pinescript.nz(pinescript.hist(73, pl, x), prdlowest)); + lowestpl = pinescript.min(lowestpl, pinescript.nz(pinescript.hist(74, ph, x), prdhighest), pinescript.nz(pinescript.hist(75, pl, x), prdhighest)); countpp += 1; if ((countpp > 40)) { break; } if (pinescript.arrayGet(aas, countpp)) { - let upl = ((pinescript.offset(ph, x) ? pinescript.offset(high, (x + rb)) : pinescript.offset(low, (x + rb))) + cwidth); - let dnl = ((pinescript.offset(ph, x) ? pinescript.offset(high, (x + rb)) : pinescript.offset(low, (x + rb))) - cwidth); + let upl = ((pinescript.hist(76, ph, x) ? pinescript.hist(77, high, (x + rb)) : pinescript.hist(78, low, (x + rb))) + cwidth); + let dnl = ((pinescript.hist(79, ph, x) ? pinescript.hist(80, high, (x + rb)) : pinescript.hist(81, low, (x + rb))) - cwidth); u1 = ((countpp === 1) ? upl : u1); d1 = ((countpp === 1) ? dnl : d1); let tmp = pinescript.arrayNew(41, true); let cnt = 0; let tpoint = 0; for (let xx = 0; xx <= prd; xx++) { - if (pinescript.na(pinescript.offset(close, xx))) { + if (pinescript.na(pinescript.hist(82, close, xx))) { break; } - if ((!pinescript.na(pinescript.offset(ph, xx)) || !pinescript.na(pinescript.offset(pl, xx)))) { + if ((!pinescript.na(pinescript.hist(83, ph, xx)) || !pinescript.na(pinescript.hist(84, pl, xx)))) { let chg = false; cnt += 1; if ((cnt > 40)) { break; } if (pinescript.arrayGet(aas, cnt)) { - if (!pinescript.na(pinescript.offset(ph, xx))) { - if (((pinescript.offset(high, (xx + rb)) <= upl) && (pinescript.offset(high, (xx + rb)) >= dnl))) { + if (!pinescript.na(pinescript.hist(85, ph, xx))) { + if (((pinescript.hist(86, high, (xx + rb)) <= upl) && (pinescript.hist(87, high, (xx + rb)) >= dnl))) { tpoint += 1; chg = true; } } - if (!pinescript.na(pinescript.offset(pl, xx))) { - if (((pinescript.offset(low, (xx + rb)) <= upl) && (pinescript.offset(low, (xx + rb)) >= dnl))) { + if (!pinescript.na(pinescript.hist(88, pl, xx))) { + if (((pinescript.hist(89, low, (xx + rb)) <= upl) && (pinescript.hist(90, low, (xx + rb)) >= dnl))) { tpoint += 1; chg = true; } @@ -1667,11 +2147,11 @@ function main() { pinescript.arraySet(aas, g, false); } } - if ((pinescript.offset(ph, x) && (countpp < 21))) { - pinescript.arraySet(sr_levels, countpp, pinescript.offset(high, (x + rb))); + if ((pinescript.hist(91, ph, x) && (countpp < 21))) { + pinescript.arraySet(sr_levels, countpp, pinescript.hist(92, high, (x + rb))); } - if ((pinescript.offset(pl, x) && (countpp < 21))) { - pinescript.arraySet(sr_levels, countpp, pinescript.offset(low, (x + rb))); + if ((pinescript.hist(93, pl, x) && (countpp < 21))) { + pinescript.arraySet(sr_levels, countpp, pinescript.hist(94, low, (x + rb))); } } } @@ -1732,14 +2212,14 @@ function main() { pinescript.arraySet(state.sr_labels, x, pinescript.labelNew((bar_index + label_loc), pinescript.arrayGet(state.sr_levs, x), pinescript.strToString(math.round_to_mintick(pinescript.arrayGet(state.sr_levs, x))), ({ color: lab_col, textcolor: pinescript.color.hex("#000000"), style: lab_loc }))); } } - hlabel = (enableSR ? pinescript.labelNew(((bar_index + label_loc) + (pinescript.round(math.sign(label_loc)) * 20)), highestph, ("High Level : " + pinescript.strToString(highestph)), ({ color: hi_col, textcolor: pinescript.color.hex("#000000"), style: label.style_label_down })) : null); - llabel = (enableSR ? pinescript.labelNew(((bar_index + label_loc) + (pinescript.round(math.sign(label_loc)) * 20)), lowestpl, ("Low Level : " + pinescript.strToString(lowestpl)), ({ color: lo_col, textcolor: pinescript.color.hex("#000000"), style: label.style_label_up })) : null); - let rsi = ta.rsi(close, 28); + hlabel = (enableSR ? pinescript.labelNew(((bar_index + label_loc) + (pinescript.round(pinescript.sign(label_loc)) * 20)), highestph, ("High Level : " + pinescript.strToString(highestph)), ({ color: hi_col, textcolor: pinescript.color.hex("#000000"), style: label.style_label_down })) : null); + llabel = (enableSR ? pinescript.labelNew(((bar_index + label_loc) + (pinescript.round(pinescript.sign(label_loc)) * 20)), lowestpl, ("Low Level : " + pinescript.strToString(lowestpl)), ({ color: lo_col, textcolor: pinescript.color.hex("#000000"), style: label.style_label_up })) : null); + let rsi = pinescript.rsi(close, 28); let rsiOb = ((rsi > 65) && (rsi > pinescript.ema(rsi, 10))); let rsiOs = ((rsi < 35) && (rsi < pinescript.ema(rsi, 10))); - let dHigh = securityNoRep(syminfo.tickerid, "D", pinescript.offset(high, 1)); - let dLow = securityNoRep(syminfo.tickerid, "D", pinescript.offset(low, 1)); - let dClose = securityNoRep(syminfo.tickerid, "D", pinescript.offset(close, 1)); + let dHigh = securityNoRep(syminfo.tickerid, "D", pinescript.hist(95, high, 1)); + let dLow = securityNoRep(syminfo.tickerid, "D", pinescript.hist(96, low, 1)); + let dClose = securityNoRep(syminfo.tickerid, "D", pinescript.hist(97, close, 1)); let ema = pinescript.ema(close, 144); let emaBull = (close > ema); function equal_tf(res) { @@ -1771,32 +2251,32 @@ function main() { let v6 = pinescript.vwma(src, len); let v7 = 0; let sma_1 = pinescript.sma(src, len); - v7 = (pinescript.na(pinescript.offset(v7, 1)) ? sma_1 : (((pinescript.offset(v7, 1) * (len - 1)) + src) / len)); + v7 = (pinescript.na(pinescript.hist(98, v7, 1)) ? sma_1 : (((pinescript.hist(99, v7, 1) * (len - 1)) + src) / len)); let v8 = pinescript.wma(((2 * pinescript.wma(src, (len / 2))) - pinescript.wma(src, len)), pinescript.round(pinescript.sqrt(len))); let v9 = pinescript.linreg(src, len, offSig); let v10 = pinescript.alma(src, len, offALMA, offSig); let v11 = pinescript.sma(v1, len); - let a1 = math.exp(((-1.414 * 3.14159) / len)); - let b1 = ((2 * a1) * math.cos(((1.414 * 3.14159) / len))); + let a1 = pinescript.exp(((-1.414 * 3.14159) / len)); + let b1 = ((2 * a1) * pinescript.cos(((1.414 * 3.14159) / len))); let c2 = b1; let c3 = (-a1 * a1); let c1 = ((1 - c2) - c3); let v12 = 0; - v12 = ((((c1 * (src + pinescript.nz(pinescript.offset(src, 1)))) / 2) + (c2 * pinescript.nz(pinescript.offset(v12, 1)))) + (c3 * pinescript.nz(pinescript.offset(v12, 2)))); + v12 = ((((c1 * (src + pinescript.nz(pinescript.hist(100, src, 1)))) / 2) + (c2 * pinescript.nz(pinescript.hist(101, v12, 1)))) + (c3 * pinescript.nz(pinescript.hist(102, v12, 2)))); return ((type === "EMA") ? v2 : ((type === "DEMA") ? v3 : ((type === "TEMA") ? v4 : ((type === "WMA") ? v5 : ((type === "VWMA") ? v6 : ((type === "SMMA") ? v7 : ((type === "HullMA") ? v8 : ((type === "LSMA") ? v9 : ((type === "ALMA") ? v10 : ((type === "TMA") ? v11 : ((type === "SSMA") ? v12 : v1))))))))))); } function reso(exp, use, res) { let security_1 = pinescript.requestSecurity(syminfo.tickerid, res, exp, ({ gaps: barmerge.gaps_off, lookahead: barmerge.lookahead_on })); return (use ? security_1 : exp); } - let closeSeries = variant(basisType, pinescript.offset(close, delayOffset), basisLen, offsetSigma, offsetALMA); - let openSeries = variant(basisType, pinescript.offset(open, delayOffset), basisLen, offsetSigma, offsetALMA); + let closeSeries = variant(basisType, pinescript.hist(103, close, delayOffset), basisLen, offsetSigma, offsetALMA); + let openSeries = variant(basisType, pinescript.hist(104, open, delayOffset), basisLen, offsetSigma, offsetALMA); let closeSeriesAlt = reso(closeSeries, useRes, stratRes); let openSeriesAlt = reso(openSeries, useRes, stratRes); let lxTrigger = false; let sxTrigger = false; - let leTrigger = pinescript.cross(closeSeriesAlt, openSeriesAlt); - let seTrigger = pinescript.cross(closeSeriesAlt, openSeriesAlt); + let leTrigger = pinescript.crossover(pinescript.series(105, closeSeriesAlt), pinescript.series(106, openSeriesAlt)); + let seTrigger = pinescript.crossunder(pinescript.series(107, closeSeriesAlt), pinescript.series(108, openSeriesAlt)); let G_RISK = ("■ " + "Risk Management"); let T_LVL = "(%) Exit Level"; let T_QTY = "(%) Adjust trade exit volume"; @@ -1813,13 +2293,13 @@ function main() { let O_SXMSGB = "Short TP2"; let O_SXMSGC = "Short TP3"; let O_SXMSGX = "Short Exit"; - let i_lxLvlTP1 = input.float(1, "Level TP1", ({ group: G_RISK, tooltip: T_LVL })); - let i_lxQtyTP1 = input.float(50, "Qty TP1", ({ group: G_RISK, tooltip: T_QTY })); - let i_lxLvlTP2 = input.float(1.5, "Level TP2", ({ group: G_RISK, tooltip: T_LVL })); - let i_lxQtyTP2 = input.float(30, "Qty TP2", ({ group: G_RISK, tooltip: T_QTY })); - let i_lxLvlTP3 = input.float(2, "Level TP3", ({ group: G_RISK, tooltip: T_LVL })); - let i_lxQtyTP3 = input.float(20, "Qty TP3", ({ group: G_RISK, tooltip: T_QTY })); - let i_lxLvlSL = input.float(0.5, "Stop Loss", ({ group: G_RISK, tooltip: T_LVL })); + let i_lxLvlTP1 = pinescript.inputFloat(1, "Level TP1", ({ group: G_RISK, tooltip: T_LVL })); + let i_lxQtyTP1 = pinescript.inputFloat(50, "Qty TP1", ({ group: G_RISK, tooltip: T_QTY })); + let i_lxLvlTP2 = pinescript.inputFloat(1.5, "Level TP2", ({ group: G_RISK, tooltip: T_LVL })); + let i_lxQtyTP2 = pinescript.inputFloat(30, "Qty TP2", ({ group: G_RISK, tooltip: T_QTY })); + let i_lxLvlTP3 = pinescript.inputFloat(2, "Level TP3", ({ group: G_RISK, tooltip: T_LVL })); + let i_lxQtyTP3 = pinescript.inputFloat(20, "Qty TP3", ({ group: G_RISK, tooltip: T_QTY })); + let i_lxLvlSL = pinescript.inputFloat(0.5, "Stop Loss", ({ group: G_RISK, tooltip: T_LVL })); let i_sxLvlTP1 = i_lxLvlTP1; let i_sxQtyTP1 = i_lxQtyTP1; let i_sxLvlTP2 = i_lxLvlTP2; @@ -1828,98 +2308,98 @@ function main() { let i_sxQtyTP3 = i_lxQtyTP3; let i_sxLvlSL = i_lxLvlSL; let G_MSG = ("■ " + "Webhook Message"); - let i_leMsg = input.string(O_LEMSG, "Long Entry", ({ group: G_MSG, tooltip: T_MSG })); - let i_lxMsgSL = input.string(O_LXMSGSL, "Long SL", ({ group: G_MSG, tooltip: T_MSG })); - let i_lxMsgTP1 = input.string(O_LXMSGTP1, "Long TP1", ({ group: G_MSG, tooltip: T_MSG })); - let i_lxMsgTP2 = input.string(O_LXMSGTP2, "Long TP2", ({ group: G_MSG, tooltip: T_MSG })); - let i_lxMsgTP3 = input.string(O_LXMSGTP3, "Long TP3", ({ group: G_MSG, tooltip: T_MSG })); - let i_lxMsg = input.string(O_LXMSG, "Long Exit", ({ group: G_MSG, tooltip: T_MSG })); - let i_seMsg = input.string(O_SEMSG, "Short Entry", ({ group: G_MSG, tooltip: T_MSG })); - let i_sxMsgSL = input.string(O_SXMSGSL, "Short SL", ({ group: G_MSG, tooltip: T_MSG })); - let i_sxMsgTP1 = input.string(O_SXMSGA, "Short TP1", ({ group: G_MSG, tooltip: T_MSG })); - let i_sxMsgTP2 = input.string(O_SXMSGB, "Short TP2", ({ group: G_MSG, tooltip: T_MSG })); - let i_sxMsgTP3 = input.string(O_SXMSGC, "Short TP3", ({ group: G_MSG, tooltip: T_MSG })); - let i_sxMsg = input.string(O_SXMSGX, "Short Exit", ({ group: G_MSG, tooltip: T_MSG })); + let i_leMsg = pinescript.inputString(O_LEMSG, "Long Entry", ({ group: G_MSG, tooltip: T_MSG })); + let i_lxMsgSL = pinescript.inputString(O_LXMSGSL, "Long SL", ({ group: G_MSG, tooltip: T_MSG })); + let i_lxMsgTP1 = pinescript.inputString(O_LXMSGTP1, "Long TP1", ({ group: G_MSG, tooltip: T_MSG })); + let i_lxMsgTP2 = pinescript.inputString(O_LXMSGTP2, "Long TP2", ({ group: G_MSG, tooltip: T_MSG })); + let i_lxMsgTP3 = pinescript.inputString(O_LXMSGTP3, "Long TP3", ({ group: G_MSG, tooltip: T_MSG })); + let i_lxMsg = pinescript.inputString(O_LXMSG, "Long Exit", ({ group: G_MSG, tooltip: T_MSG })); + let i_seMsg = pinescript.inputString(O_SEMSG, "Short Entry", ({ group: G_MSG, tooltip: T_MSG })); + let i_sxMsgSL = pinescript.inputString(O_SXMSGSL, "Short SL", ({ group: G_MSG, tooltip: T_MSG })); + let i_sxMsgTP1 = pinescript.inputString(O_SXMSGA, "Short TP1", ({ group: G_MSG, tooltip: T_MSG })); + let i_sxMsgTP2 = pinescript.inputString(O_SXMSGB, "Short TP2", ({ group: G_MSG, tooltip: T_MSG })); + let i_sxMsgTP3 = pinescript.inputString(O_SXMSGC, "Short TP3", ({ group: G_MSG, tooltip: T_MSG })); + let i_sxMsg = pinescript.inputString(O_SXMSGX, "Short Exit", ({ group: G_MSG, tooltip: T_MSG })); let i_src = close; let G_DISPLAY = "Display"; - let i_alertOn = input.bool(true, "Alert Labels On/Off", ({ group: G_DISPLAY })); - let i_barColOn = input.bool(true, "Bar Color On/Off", ({ group: G_DISPLAY })); + let i_alertOn = pinescript.inputBool(true, "Alert Labels On/Off", ({ group: G_DISPLAY })); + let i_barColOn = pinescript.inputBool(true, "Bar Color On/Off", ({ group: G_DISPLAY })); function f_tp(_condition, _conditionValue, _leTrigger, _seTrigger, _src, _lxLvlTP, _sxLvlTP) { if (state._tpLine === undefined) state._tpLine = 0; let _topLvl = (_src + (_src * (_lxLvlTP / 100))); let _botLvl = (_src - (_src * (_sxLvlTP / 100))); - state._tpLine = (((pinescript.offset(_condition, 1) !== _conditionValue) && _leTrigger) ? _topLvl : (((pinescript.offset(_condition, 1) !== -_conditionValue) && _seTrigger) ? _botLvl : pinescript.nz(pinescript.offset(state._tpLine, 1)))); + state._tpLine = (((pinescript.hist(109, _condition, 1) !== _conditionValue) && _leTrigger) ? _topLvl : (((pinescript.hist(110, _condition, 1) !== -_conditionValue) && _seTrigger) ? _botLvl : pinescript.nz(pinescript.hist(111, state._tpLine, 1)))); return [state._tpLine]; } function f_cross(_scr1, _scr2, _over) { - let _cross = (_over ? ((_scr1 > _scr2) && (pinescript.offset(_scr1, 1) < pinescript.offset(_scr2, 1))) : ((_scr1 < _scr2) && (pinescript.offset(_scr1, 1) > pinescript.offset(_scr2, 1)))); + let _cross = (_over ? ((_scr1 > _scr2) && (pinescript.hist(112, _scr1, 1) < pinescript.hist(113, _scr2, 1))) : ((_scr1 < _scr2) && (pinescript.hist(114, _scr1, 1) > pinescript.hist(115, _scr2, 1)))); } if (state.condition === undefined) state.condition = 0; if (state.slLine === undefined) state.slLine = 0; if (state.entryLine === undefined) state.entryLine = 0; - state.entryLine = ((leTrigger && (pinescript.offset(state.condition, 1) <= 0)) ? close : ((seTrigger && (pinescript.offset(state.condition, 1) >= 0)) ? close : pinescript.nz(pinescript.offset(state.entryLine, 1)))); + state.entryLine = ((leTrigger && (pinescript.hist(116, state.condition, 1) <= 0)) ? close : ((seTrigger && (pinescript.hist(117, state.condition, 1) >= 0)) ? close : pinescript.nz(pinescript.hist(118, state.entryLine, 1)))); let slTopLvl = (i_src + (i_src * (i_lxLvlSL / 100))); let slBotLvl = (i_src - (i_src * (i_sxLvlSL / 100))); - state.slLine = (((pinescript.offset(state.condition, 1) <= 0) && leTrigger) ? slBotLvl : (((pinescript.offset(state.condition, 1) >= 0) && seTrigger) ? slTopLvl : pinescript.nz(pinescript.offset(state.slLine, 1)))); + state.slLine = (((pinescript.hist(119, state.condition, 1) <= 0) && leTrigger) ? slBotLvl : (((pinescript.hist(120, state.condition, 1) >= 0) && seTrigger) ? slTopLvl : pinescript.nz(pinescript.hist(121, state.slLine, 1)))); let slLong = f_cross(low, state.slLine, false); let slShort = f_cross(high, state.slLine, true); - let [tp3Line] = f_tp(state.condition, 1.2, leTrigger, seTrigger, i_src, i_lxLvlTP3, i_sxLvlTP3); - let [tp2Line] = f_tp(state.condition, 1.1, leTrigger, seTrigger, i_src, i_lxLvlTP2, i_sxLvlTP2); - let [tp1Line] = f_tp(state.condition, 1, leTrigger, seTrigger, i_src, i_lxLvlTP1, i_sxLvlTP1); + let [tp3Line] = pinescript.unpack(f_tp(state.condition, 1.2, leTrigger, seTrigger, i_src, i_lxLvlTP3, i_sxLvlTP3), 1); + let [tp2Line] = pinescript.unpack(f_tp(state.condition, 1.1, leTrigger, seTrigger, i_src, i_lxLvlTP2, i_sxLvlTP2), 1); + let [tp1Line] = pinescript.unpack(f_tp(state.condition, 1, leTrigger, seTrigger, i_src, i_lxLvlTP1, i_sxLvlTP1), 1); let tp3Long = f_cross(high, tp3Line, true); let tp3Short = f_cross(low, tp3Line, false); let tp2Long = f_cross(high, tp2Line, true); let tp2Short = f_cross(low, tp2Line, false); let tp1Long = f_cross(high, tp1Line, true); let tp1Short = f_cross(low, tp1Line, false); - if ((leTrigger && (pinescript.offset(state.condition, 1) <= 0))) { + if ((leTrigger && (pinescript.hist(122, state.condition, 1) <= 0))) { state.condition = 1; } - else if ((seTrigger && (pinescript.offset(state.condition, 1) >= 0))) { + else if ((seTrigger && (pinescript.hist(123, state.condition, 1) >= 0))) { state.condition = -1; } - else if ((tp3Long && (pinescript.offset(state.condition, 1) === 1.2))) { + else if ((tp3Long && (pinescript.hist(124, state.condition, 1) === 1.2))) { state.condition = 1.3; } - else if ((tp3Short && (pinescript.offset(state.condition, 1) === -1.2))) { + else if ((tp3Short && (pinescript.hist(125, state.condition, 1) === -1.2))) { state.condition = -1.3; } - else if ((tp2Long && (pinescript.offset(state.condition, 1) === 1.1))) { + else if ((tp2Long && (pinescript.hist(126, state.condition, 1) === 1.1))) { state.condition = 1.2; } - else if ((tp2Short && (pinescript.offset(state.condition, 1) === -1.1))) { + else if ((tp2Short && (pinescript.hist(127, state.condition, 1) === -1.1))) { state.condition = -1.2; } - else if ((tp1Long && (pinescript.offset(state.condition, 1) === 1))) { + else if ((tp1Long && (pinescript.hist(128, state.condition, 1) === 1))) { state.condition = 1.1; } - else if ((tp1Short && (pinescript.offset(state.condition, 1) === -1))) { + else if ((tp1Short && (pinescript.hist(129, state.condition, 1) === -1))) { state.condition = -1.1; } - else if ((slLong && (pinescript.offset(state.condition, 1) >= 1))) { + else if ((slLong && (pinescript.hist(130, state.condition, 1) >= 1))) { state.condition = 0; } - else if ((slShort && (pinescript.offset(state.condition, 1) <= -1))) { + else if ((slShort && (pinescript.hist(131, state.condition, 1) <= -1))) { state.condition = 0; } - else if ((lxTrigger && (pinescript.offset(state.condition, 1) >= 1))) { + else if ((lxTrigger && (pinescript.hist(132, state.condition, 1) >= 1))) { state.condition = 0; } - else if ((sxTrigger && (pinescript.offset(state.condition, 1) <= -1))) { + else if ((sxTrigger && (pinescript.hist(133, state.condition, 1) <= -1))) { state.condition = 0; } - let longE = ((leTrigger && (pinescript.offset(state.condition, 1) <= 0)) && (state.condition === 1)); - let shortE = ((seTrigger && (pinescript.offset(state.condition, 1) >= 0)) && (state.condition === -1)); - let longX = ((lxTrigger && (pinescript.offset(state.condition, 1) >= 1)) && (state.condition === 0)); - let shortX = ((sxTrigger && (pinescript.offset(state.condition, 1) <= -1)) && (state.condition === 0)); - let longSL = ((slLong && (pinescript.offset(state.condition, 1) >= 1)) && (state.condition === 0)); - let shortSL = ((slShort && (pinescript.offset(state.condition, 1) <= -1)) && (state.condition === 0)); - let longTP3 = ((tp3Long && (pinescript.offset(state.condition, 1) === 1.2)) && (state.condition === 1.3)); - let shortTP3 = ((tp3Short && (pinescript.offset(state.condition, 1) === -1.2)) && (state.condition === -1.3)); - let longTP2 = ((tp2Long && (pinescript.offset(state.condition, 1) === 1.1)) && (state.condition === 1.2)); - let shortTP2 = ((tp2Short && (pinescript.offset(state.condition, 1) === -1.1)) && (state.condition === -1.2)); - let longTP1 = ((tp1Long && (pinescript.offset(state.condition, 1) === 1)) && (state.condition === 1.1)); - let shortTP1 = ((tp1Short && (pinescript.offset(state.condition, 1) === -1)) && (state.condition === -1.1)); + let longE = ((leTrigger && (pinescript.hist(134, state.condition, 1) <= 0)) && (state.condition === 1)); + let shortE = ((seTrigger && (pinescript.hist(135, state.condition, 1) >= 0)) && (state.condition === -1)); + let longX = ((lxTrigger && (pinescript.hist(136, state.condition, 1) >= 1)) && (state.condition === 0)); + let shortX = ((sxTrigger && (pinescript.hist(137, state.condition, 1) <= -1)) && (state.condition === 0)); + let longSL = ((slLong && (pinescript.hist(138, state.condition, 1) >= 1)) && (state.condition === 0)); + let shortSL = ((slShort && (pinescript.hist(139, state.condition, 1) <= -1)) && (state.condition === 0)); + let longTP3 = ((tp3Long && (pinescript.hist(140, state.condition, 1) === 1.2)) && (state.condition === 1.3)); + let shortTP3 = ((tp3Short && (pinescript.hist(141, state.condition, 1) === -1.2)) && (state.condition === -1.3)); + let longTP2 = ((tp2Long && (pinescript.hist(142, state.condition, 1) === 1.1)) && (state.condition === 1.2)); + let shortTP2 = ((tp2Short && (pinescript.hist(143, state.condition, 1) === -1.1)) && (state.condition === -1.2)); + let longTP1 = ((tp1Long && (pinescript.hist(144, state.condition, 1) === 1)) && (state.condition === 1.1)); + let shortTP1 = ((tp1Short && (pinescript.hist(145, state.condition, 1) === -1)) && (state.condition === -1.1)); if ((((pinescript.strategy.position_size <= 0) && longE) && barstate.isconfirmed)) { pinescript.strategyEntry("Long", pinescript.strategy.long, ({ alert_message: i_leMsg, comment: "LE" })); } @@ -1988,7 +2468,7 @@ function main() { if (state.a_entryLabel === undefined) state.a_entryLabel = pinescript.arrayNew(1); let newEntry = (longE || shortE); let entryIndex = 1; - entryIndex = (newEntry ? bar_index : pinescript.nz(pinescript.offset(entryIndex, 1))); + entryIndex = (newEntry ? bar_index : pinescript.nz(pinescript.hist(146, entryIndex, 1))); let lasTrade = (bar_index >= entryIndex); let l_right = 10; pinescript.lineDelete(f_qDq(state.a_slLine, pinescript.lineNew(entryIndex, state.slLine, (last_bar_index + l_right), state.slLine, ({ style: line.style_solid, color: c_sl })))); @@ -2002,7 +2482,7 @@ function main() { pinescript.labelDelete(f_qDq(state.a_tp2label, pinescript.labelNew((last_bar_index + l_right), tp2Line, ("TP2: " + pinescript.strToString(tp2Line, "##.###")), ({ style: label.style_label_left, textcolor: pinescript.color.white, color: c_tp })))); pinescript.labelDelete(f_qDq(state.a_tp1label, pinescript.labelNew((last_bar_index + l_right), tp1Line, ("TP1: " + pinescript.strToString(tp1Line, "##.###")), ({ style: label.style_label_left, textcolor: pinescript.color.white, color: c_tp })))); let c_barCol = ((close > open) ? pinescript.color.rgb(120, 9, 139) : pinescript.color.rgb(69, 155, 225)); - barcolor((i_barColOn ? c_barCol : null)); + pinescript.barcolor((i_barColOn ? c_barCol : null)); if ((((longE || shortE) || longX) || shortX)) { pinescript.alert(({ message: "Any Alert", freq: alert.freq_once_per_bar_close })); } @@ -2021,7 +2501,67 @@ function main() { } -// Export for use -export { main +// The bar-by-bar engine: runs main() once per bar over the dataset. +function run(data, options = {}) { + data = data || {}; + const inClose = (data.close || []).map(Number); + const n = inClose.length; + const inOpen = (data.open || inClose).map(Number); + const inHigh = (data.high || inClose).map(Number); + const inLow = (data.low || inClose).map(Number); + const inVol = (data.volume || new Array(n).fill(0)).map(Number); + const inTime = (data.time || inClose.map((_, i) => i)).map(Number); + + // Reset all persistent and per-run state so repeated runs are independent. + globalThis.__pineState = {}; + globalThis.__pineRuntime = { plots: {}, plotshapes: {}, alerts: [], bars: n, inputs: options.inputs || {} }; + pinescript.__rt = {}; + pinescript.__bar = 0; + + // Growing OHLCV windows: each is a SeriesArray that gains one element per bar, + // so window built-ins like sma and highest see history up to the current bar. + const O = pinescript.asSeries([]), H = pinescript.asSeries([]), L = pinescript.asSeries([]); + const C = pinescript.asSeries([]), V = pinescript.asSeries([]), T = pinescript.asSeries([]); + const HL2 = pinescript.asSeries([]), HLC3 = pinescript.asSeries([]), OHLC4 = pinescript.asSeries([]); + globalThis.open = O; globalThis.high = H; globalThis.low = L; globalThis.close = C; + globalThis.volume = V; globalThis.time = T; + globalThis.hl2 = HL2; globalThis.hlc3 = HLC3; globalThis.ohlc4 = OHLC4; + globalThis.syminfo = globalThis.syminfo || { tickerid: 'SYNTHETIC', ticker: 'SYNTHETIC', mintick: 0.01 }; + + const rt = globalThis.__pineRuntime; + for (let i = 0; i < n; i++) { + O.push(inOpen[i]); H.push(inHigh[i]); L.push(inLow[i]); C.push(inClose[i]); V.push(inVol[i]); T.push(inTime[i]); + HL2.push((inHigh[i] + inLow[i]) / 2); + HLC3.push((inHigh[i] + inLow[i] + inClose[i]) / 3); + OHLC4.push((inOpen[i] + inHigh[i] + inLow[i] + inClose[i]) / 4); + + pinescript.__bar = i; + rt.__barIndex = i; + rt.__plotIdx = 0; + rt.__shapeIdx = 0; + globalThis.bar_index = i; + globalThis.last_bar_index = n - 1; + globalThis.barstate = { + isfirst: i === 0, islast: i === n - 1, isrealtime: false, ishistory: true, + isconfirmed: true, isnew: true, islastconfirmedhistory: i === n - 1, + }; + + main(); + } + + // Backfill skipped bars so every output series has exactly n entries. + for (const k of Object.keys(rt.plots)) { + const d = rt.plots[k].data; + for (let i = 0; i < n; i++) if (d[i] === undefined) d[i] = null; + } + for (const k of Object.keys(rt.plotshapes)) { + const d = rt.plotshapes[k].data; + for (let i = 0; i < n; i++) if (d[i] === undefined) d[i] = false; + } + return rt; +} + + +export { main, run }; diff --git a/converted/Singular_Spectrum_Decomposition_LuxAlgo.js b/converted/Singular_Spectrum_Decomposition_LuxAlgo.js index ca77a54..a03f67e 100644 --- a/converted/Singular_Spectrum_Decomposition_LuxAlgo.js +++ b/converted/Singular_Spectrum_Decomposition_LuxAlgo.js @@ -1,12 +1,63 @@ -// PineScript to JavaScript Transpiled Code +// Auto-generated JavaScript from PineScript source -// Generated automatically +// Do not edit by hand -- re-run the converter instead -// Built-in PineScript functions +// All the built-in PineScript functions that scripts depend on at runtime const pinescript = { + __scalar: function(v) { + if (v === null || v === undefined) return v; + if (typeof v === 'object' && typeof v.valueOf === 'function') { + const s = v.valueOf(); + if (typeof s === 'number' || typeof s === 'boolean' || s === null) return s; + } + return v; + }, + series: function(id, value) { + const rt = this.__rt || (this.__rt = {}); + const store = rt.series || (rt.series = {}); + let buf = store[id]; + if (!buf) buf = store[id] = []; + buf[this.__bar | 0] = this.__scalar(value); + return buf; + }, + hist: function(id, value, n) { + const buf = this.series(id, value); + const idx = (this.__bar | 0) - (n | 0); + return idx >= 0 && idx < buf.length ? buf[idx] : null; + }, + time: function(timeframe, session) { + const t = globalThis.time; + if (t && typeof t.valueOf === 'function') return t.valueOf(); + return Array.isArray(t) ? t[t.length - 1] : t; + }, + sign: function(x) { + if (x === null || x === undefined || (typeof x === 'number' && isNaN(x))) return null; + return Math.sign(Number(x)); + }, + unpack: function(value, count) { + if (Array.isArray(value)) return value; + if (value != null && typeof value[Symbol.iterator] === 'function') return Array.from(value); + return new Array(count || 0).fill(null); + }, + alertcondition: function(condition, ...rest) { + if (globalThis.__pineRuntime) { + globalThis.__pineRuntime.alerts.push({ condition, args: rest }); + } + return null; + }, + barcolor: function(color) { return null; }, + bgcolor: function(color, title, editable, showLast) { + return null; + }, + fill: function(series1, series2, color, title, editable) { + return null; + }, + hline: function(_price, _title, _opts) { + return null; + }, sma: function(series, length) { if (series === null || series === undefined) return null; if (!Array.isArray(series)) series = [series]; @@ -84,6 +135,13 @@ const pinescript = { } return weightSum > 0 ? sum / weightSum : null; }, + swma: function(series) { + if (series === null || series === undefined) return null; + if (!Array.isArray(series)) series = [series]; + if (series.length < 4) return null; + const s = series.slice(-4); + return (s[0] * 1 + s[1] * 2 + s[2] * 2 + s[3] * 1) / 6; + }, atr: function(high, low, close, length = 14) { if (!Array.isArray(high) || !Array.isArray(low) || !Array.isArray(close)) return null; if (high.length < length + 1 || low.length < length + 1 || close.length < length + 1) return null; @@ -114,6 +172,53 @@ const pinescript = { const atr = builtins.get('atr')(high, low, close, length); return { middle: sma, upper: sma + mult * atr, lower: sma - mult * atr }; }, + supertrend: function(factor, atrPeriod, high, low, close) { + if (!Array.isArray(high) || !Array.isArray(low) || !Array.isArray(close)) return [null, null]; + if (high.length < atrPeriod + 1) return [null, null]; + const atrVal = builtins.get('atr')(high, low, close, atrPeriod); + if (atrVal === null) return [null, null]; + const lastIdx = close.length - 1; + const hl2 = (high[lastIdx] + low[lastIdx]) / 2; + const upperBand = hl2 + factor * atrVal; + const lowerBand = hl2 - factor * atrVal; + // Determine direction: 1 means downtrend (price below band), -1 means uptrend + const direction = close[lastIdx] > upperBand ? -1 : close[lastIdx] < lowerBand ? 1 : -1; + const supertrendValue = direction === -1 ? lowerBand : upperBand; + return [supertrendValue, direction]; + }, + ta_supertrend: function(factor, atrPeriod) { + const H = globalThis.high; + const L = globalThis.low; + const C = globalThis.close; + return builtins.get('supertrend')(factor, atrPeriod, H, L, C); + }, + dmi: function(diLength, adxSmoothing, high, low, close) { + if (!Array.isArray(high) || !Array.isArray(low) || !Array.isArray(close)) return { plusDI: null, minusDI: null, adx: null }; + if (high.length < diLength + 1) return { plusDI: null, minusDI: null, adx: null }; + let plusDMSum = 0, minusDMSum = 0, trSum = 0; + for (let i = 1; i <= diLength; i++) { + const idx = high.length - 1 - diLength + i; + const upMove = high[idx] - high[idx - 1]; + const downMove = low[idx - 1] - low[idx]; + plusDMSum += (upMove > downMove && upMove > 0) ? upMove : 0; + minusDMSum += (downMove > upMove && downMove > 0) ? downMove : 0; + const tr = Math.max( + high[idx] - low[idx], + Math.abs(high[idx] - close[idx - 1]), + Math.abs(low[idx] - close[idx - 1]) + ); + trSum += tr; + } + const plusDI = trSum > 0 ? (plusDMSum / trSum) * 100 : 0; + const minusDI = trSum > 0 ? (minusDMSum / trSum) * 100 : 0; + const diSum = plusDI + minusDI; + const dx = diSum > 0 ? Math.abs(plusDI - minusDI) / diSum * 100 : 0; + return { plusDI, minusDI, adx: dx }; + }, + adx: function(diLength, adxSmoothing, high, low, close) { + const result = builtins.get('dmi')(diLength, adxSmoothing, high, low, close); + return result ? result.adx : null; + }, rsi: function(close, length = 14) { if (close === null || close === undefined) return null; if (!Array.isArray(close)) close = [close]; @@ -171,9 +276,7 @@ const pinescript = { return { macd: macdLine, signal: signalLine, histogram: histogram }; }, cci: function(a, b, c, d) { - // Pine signatures: - // - ta.cci(source, length) - // - ta.cci(high, low, close, length) + // Supports both ta.cci(source, length) and ta.cci(high, low, close, length) signatures let source; let length; if (Array.isArray(a) && typeof b === 'number' && c === undefined) { @@ -304,6 +407,15 @@ const pinescript = { stdev: function(source, length) { return Math.sqrt(builtins.get('variance')(source, length)); }, + median: function(source, length) { + if (!source || source.length < length) return null; + const sorted = [...source.slice(-length)].sort((a, b) => a - b); + const mid = Math.floor(sorted.length / 2); + return sorted.length % 2 !== 0 ? sorted[mid] : (sorted[mid - 1] + sorted[mid]) / 2; + }, + ta_median: function(source, length) { + return builtins.get('median')(source, length); + }, hl2: function(high, low) { return high && low ? (high[high.length - 1] + low[low.length - 1]) / 2 : null; }, @@ -420,6 +532,24 @@ const pinescript = { return (series1[i - 1] < series2[i - 1] && series1[i] >= series2[i]) || (series1[i - 1] > series2[i - 1] && series1[i] <= series2[i]); }, + crossover: function(series1, series2) { + if (series1 === null || series1 === undefined) return false; + if (series2 === null || series2 === undefined) return false; + if (!Array.isArray(series1)) series1 = [series1]; + if (!Array.isArray(series2)) series2 = [series2]; + if (series1.length < 2 || series2.length < 2) return false; + const i = series1.length - 1; + return series1[i - 1] < series2[i - 1] && series1[i] >= series2[i]; + }, + crossunder: function(series1, series2) { + if (series1 === null || series1 === undefined) return false; + if (series2 === null || series2 === undefined) return false; + if (!Array.isArray(series1)) series1 = [series1]; + if (!Array.isArray(series2)) series2 = [series2]; + if (series1.length < 2 || series2.length < 2) return false; + const i = series1.length - 1; + return series1[i - 1] > series2[i - 1] && series1[i] <= series2[i]; + }, offset: function(series, offset) { if (!Array.isArray(series)) return null; const idx = series.length - 1 - offset; @@ -500,12 +630,14 @@ const pinescript = { return builtins.get('nz')(value, replacement); }, plot: function(series, title = '', color = null, linewidth = 1) { - globalThis.__pineRuntime.plots.push({ - series, - title, - color, - linewidth - }); + const rt = globalThis.__pineRuntime; + if (!rt) return series; + const bar = rt.__barIndex | 0; + const ord = (rt.__plotIdx = (rt.__plotIdx | 0) + 1) - 1; + const key = (title && String(title)) || ('plot_' + ord); + let p = rt.plots[key]; + if (!p) p = rt.plots[key] = { title: key, color, linewidth, data: [] }; + p.data[bar] = this.__scalar(series); return series; }, lineNew: function(x1, y1, x2, y2, opts = {}) { @@ -514,20 +646,72 @@ const pinescript = { lineDelete: function(l) { return null; }, - labelNew: function(...args) { - if (globalThis.__pineRuntime) { - globalThis.__pineRuntime.labels = globalThis.__pineRuntime.labels || []; - globalThis.__pineRuntime.labels.push({ args }); - } - return { args }; + lineSetXY: function(line, point, x, y) { + if (!line) return null; + if (point === 0 || point === 'x1') { line.x1 = x; line.y1 = y; } + else { line.x2 = x; line.y2 = y; } + return line; + }, + lineGetX: function(line, point) { + if (!line) return null; + return point === 0 || point === 'x1' ? line.x1 : line.x2; + }, + lineGetY: function(line, point) { + if (!line) return null; + return point === 0 || point === 'y1' ? line.y1 : line.y2; + }, + labelNew: function(x, y, text = '', opts = {}) { + return { x, y, text, opts, _type: 'label' }; }, labelDelete: function(l) { return null; }, + labelSetText: function(label, text) { + if (!label) return null; + label.text = text; + return label; + }, + labelGetText: function(label) { + if (!label) return ''; + return label.text || ''; + }, + boxNew: function(left, top, right, bottom, opts = {}) { + return { left, top, right, bottom, opts, _type: 'box' }; + }, + boxDelete: function(box) { + return null; + }, + boxSetLeftTop: function(box, left, top) { + if (!box) return null; + box.left = left; + box.top = top; + return box; + }, + boxSetRightBottom: function(box, right, bottom) { + if (!box) return null; + box.right = right; + box.bottom = bottom; + return box; + }, + polylineNew: function(points, opts = {}) { + return { points: points || [], opts, _type: 'polyline' }; + }, + polylineDelete: function(poly) { + return null; + }, plotshape: function(condition, ...rest) { - if (globalThis.__pineRuntime) { - globalThis.__pineRuntime.plotshapes.push({ condition, args: rest }); + const rt = globalThis.__pineRuntime; + if (!rt) return condition; + const bar = rt.__barIndex | 0; + const ord = (rt.__shapeIdx = (rt.__shapeIdx | 0) + 1) - 1; + let key = 'shape_' + ord; + for (const r of rest) { + if (typeof r === 'string') { key = r; break; } + if (r && typeof r === 'object' && r.title) { key = String(r.title); break; } } + let s = rt.plotshapes[key]; + if (!s) s = rt.plotshapes[key] = { title: key, data: [] }; + s.data[bar] = !!this.__scalar(condition); return condition; }, indicator: function(title, shorttitle, overlay, opts) { @@ -542,27 +726,12 @@ const pinescript = { } return null; }, - alertcondition: function(condition, ...rest) { - if (globalThis.__pineRuntime) { - globalThis.__pineRuntime.alerts.push({ condition, args: rest }); - } - return null; - }, plotbar: function(open, high, low, close, title, color, editable, showLast) { return { open, high, low, close }; }, plotcandle: function(open, high, low, close, title, color, wickColor, borderColor, editable, showLast) { return { open, high, low, close }; }, - hline: function(_price, _title, _opts) { - return null; - }, - bgcolor: function(color, title, editable, showLast) { - return null; - }, - fill: function(series1, series2, color, title, editable) { - return null; - }, alert: function(condition, message, frequency) { console.log('Alert:', condition ? message : 'Condition not met'); return condition; @@ -828,6 +997,9 @@ const pinescript = { arrayIndexOf: function(arr, value) { return arr ? arr.indexOf(value) : -1; }, + arrayLastIndexOf: function(arr, value) { + return arr ? arr.lastIndexOf(value) : -1; + }, arraySum: function(arr) { return arr ? arr.reduce((a, b) => a + b, 0) : 0; }, @@ -840,6 +1012,42 @@ const pinescript = { arrayMax: function(arr) { return arr && arr.length > 0 ? Math.max(...arr) : null; }, + arrayStdev: function(arr) { + if (!arr || arr.length === 0) return null; + const mean = arr.reduce((a, b) => a + b, 0) / arr.length; + const variance = arr.reduce((sum, val) => sum + Math.pow(val - mean, 2), 0) / arr.length; + return Math.sqrt(variance); + }, + arrayVariance: function(arr) { + if (!arr || arr.length === 0) return null; + const mean = arr.reduce((a, b) => a + b, 0) / arr.length; + return arr.reduce((sum, val) => sum + Math.pow(val - mean, 2), 0) / arr.length; + }, + arrayCovariance: function(arr1, arr2) { + if (!arr1 || !arr2 || arr1.length === 0 || arr1.length !== arr2.length) return null; + const mean1 = arr1.reduce((a, b) => a + b, 0) / arr1.length; + const mean2 = arr2.reduce((a, b) => a + b, 0) / arr2.length; + let cov = 0; + for (let i = 0; i < arr1.length; i++) { + cov += (arr1[i] - mean1) * (arr2[i] - mean2); + } + return cov / arr1.length; + }, + mode: function(arr) { + if (!arr || arr.length === 0) return null; + const counts = new Map(); + let maxCount = 0; + let modeVal = arr[0]; + for (const val of arr) { + const c = (counts.get(val) || 0) + 1; + counts.set(val, c); + if (c > maxCount) { + maxCount = c; + modeVal = val; + } + } + return modeVal; + }, strLength: function(str) { return str ? str.length : 0; }, @@ -895,6 +1103,84 @@ const pinescript = { strReverse: function(str) { return str ? str.split('').reverse().join('') : ''; }, + strFormat: function(formatStr, ...args) { + if (!formatStr) return ''; + return formatStr.replace(/\{(\d+)\}/g, (match, idx) => { + const i = parseInt(idx, 10); + return i < args.length ? String(args[i]) : match; + }); + }, + input: function(defval, title, tooltip, inline, group) { + return defval; + }, + inputInt: function(defval = 0, title, minval, maxval, step, tooltip, inline, group) { + return defval; + }, + inputFloat: function(defval = 0.0, title, minval, maxval, step, tooltip, inline, group) { + return defval; + }, + inputBool: function(defval = false, title, tooltip, inline, group) { + return defval; + }, + inputString: function(defval = '', title, options, tooltip, inline, group) { + return defval; + }, + inputSource: function(defval = null, title, tooltip, inline, group) { + return defval; + }, + inputColor: function(defval = '#000000', title, tooltip, inline, group) { + return defval; + }, + inputTime: function(defval = 0, title, tooltip, inline, group) { + return defval; + }, + mathSign: function(value) { + return Math.sign(value); + }, + mathAvg: function(...args) { + const valid = args.filter(v => v !== null && v !== undefined && !isNaN(v)); + return valid.length > 0 ? valid.reduce((a, b) => a + b, 0) / valid.length : null; + }, + mathSum: function(series, length) { + if (!series || series.length < length) return null; + return series.slice(-length).reduce((a, b) => a + b, 0); + }, + mathRandom: function(min = 0, max = 1) { + return min + Math.random() * (max - min); + }, + mathTodegrees: function(radians) { + return radians * (180 / Math.PI); + }, + mathToradians: function(degrees) { + return degrees * (Math.PI / 180); + }, + colorT: function(color, transparency) { + if (transparency !== undefined) { + return { color, transparency }; + } + if (color && typeof color === 'object' && 'transparency' in color) { + return color.transparency; + } + return 0; + }, + tableCellSetText: function(table, column, row, text) { + return null; + }, + tableCellSetBgcolor: function(table, column, row, bgcolor) { + return null; + }, + tableMergeCells: function(table, startColumn, startRow, endColumn, endRow) { + return null; + }, + tableDelete: function(table) { + return null; + }, + tableClear: function(table, startColumn, startRow, endColumn, endRow) { + return null; + }, + maxBarsBack: function(series, length) { + return null; + }, pi: 3.141592653589793, e: 2.718281828459045, true: true, @@ -905,8 +1191,7 @@ const pinescript = { const info = { ticker: 'AAPL', tickerid: 'NASDAQ:AAPL', prefix: 'NASDAQ', root: 'AAPL', suffix: '' }; return info[type] || ''; }, - time: 1771178052861, - timenow: 1771178052861, + timenow: 1781574528509, barstate: "LAST", dividends: {}, splits: {}, @@ -923,7 +1208,7 @@ const pinescript = { return null; }, tr: function(high, low, close) { - // Pine signature: ta.tr(true) + // Handles both the boolean shorthand ta.tr(true) and explicit high/low/close arguments if (typeof high === 'boolean') { const H = globalThis.high; const L = globalThis.low; @@ -934,7 +1219,7 @@ const pinescript = { return Math.max(H[i] - L[i], Math.abs(H[i] - prevClose), Math.abs(L[i] - prevClose)); } - // Fallback: treat inputs as scalars for current bar + // When arrays are passed, compute true range from the last bar using the previous close if (Array.isArray(close)) { const prevClose = close[close.length - 2] || close[0]; const currHigh = Array.isArray(high) ? high[high.length - 1] : high; @@ -945,6 +1230,92 @@ const pinescript = { return Math.max(high - low, Math.abs(high - close), Math.abs(low - close)); }, + arrayFirst: function(arr) { + if (!arr || arr.length === 0) return null; + return arr[0]; + }, + arrayLast: function(arr) { + if (!arr || arr.length === 0) return null; + return arr[arr.length - 1]; + }, + arrayJoin: function(arr, separator = ',') { + if (!arr) return ''; + return arr.join(separator); + }, + arrayConcat: function(arr1, arr2) { + if (!arr1) return arr2 || []; + if (!arr2) return arr1; + return arr1.concat(arr2); + }, + arrayCopy: function(arr) { + if (!arr) return []; + return [...arr]; + }, + arrayBinarySearch: function(arr, value) { + if (!arr || arr.length === 0) return -1; + let lo = 0, hi = arr.length - 1; + while (lo <= hi) { + const mid = (lo + hi) >>> 1; + if (arr[mid] === value) return mid; + if (arr[mid] < value) lo = mid + 1; + else hi = mid - 1; + } + return -1; + }, + arrayRange: function(arr) { + if (!arr || arr.length === 0) return 0; + return Math.max(...arr) - Math.min(...arr); + }, + arrayMedian: function(arr) { + if (!arr || arr.length === 0) return null; + const sorted = [...arr].sort((a, b) => a - b); + const mid = Math.floor(sorted.length / 2); + return sorted.length % 2 !== 0 ? sorted[mid] : (sorted[mid - 1] + sorted[mid]) / 2; + }, + arrayMode: function(arr) { + if (!arr || arr.length === 0) return null; + const freq = {}; + let maxCount = 0; + let mode = arr[0]; + for (const v of arr) { + freq[v] = (freq[v] || 0) + 1; + if (freq[v] > maxCount) { maxCount = freq[v]; mode = v; } + } + return mode; + }, + arrayPercentile: function(arr, percentile) { + if (!arr || arr.length === 0) return null; + const sorted = [...arr].sort((a, b) => a - b); + const idx = (percentile / 100) * (sorted.length - 1); + const lo = Math.floor(idx); + const hi = Math.ceil(idx); + if (lo === hi) return sorted[lo]; + return sorted[lo] + (sorted[hi] - sorted[lo]) * (idx - lo); + }, + arrayPercentileLinearInterpolation: function(arr, percentile) { + return builtins.get('arrayPercentile')(arr, percentile); + }, + arrayPercentileNearestRank: function(arr, percentile) { + if (!arr || arr.length === 0) return null; + const sorted = [...arr].sort((a, b) => a - b); + const idx = Math.ceil((percentile / 100) * sorted.length) - 1; + return sorted[Math.max(0, idx)]; + }, + arrayAbs: function(arr) { + if (!arr) return []; + return arr.map(v => Math.abs(v)); + }, + arrayEvery: function(arr, callback) { + if (!arr) return false; + return arr.every(callback); + }, + arraySome: function(arr, callback) { + if (!arr) return false; + return arr.some(callback); + }, + requestFinancial: function() { + return null; + }, }; @@ -1101,6 +1472,115 @@ globalThis.barmerge = globalThis.barmerge || { }; +function __pineNS(real) { + + const base = real || {}; + + return new Proxy(base, { + + get(target, key) { + + if (key in target) return target[key]; + + if (typeof key === "symbol") return target[key]; + + // Unknown member: usable as a value (null), a constant, or a no-op function. + + const noop = function() { return null; }; + + noop.toString = () => ""; + + return noop; + + } + + }); + +} + +globalThis.ta = globalThis.ta || __pineNS({}); + +globalThis.math = globalThis.math || __pineNS({ pi: Math.PI, e: Math.E, phi: 1.618033988749895, rphi: 0.618033988749895 }); + +globalThis.chart = globalThis.chart || __pineNS({ point: __pineNS({}), bg_color: null, fg_color: null }); + +globalThis.line = globalThis.line || __pineNS({ style_solid: "solid", style_dashed: "dashed", style_dotted: "dotted" }); + +globalThis.box = globalThis.box || __pineNS({}); + +globalThis.label = globalThis.label || __pineNS({ style_label_down: "label_down", style_label_up: "label_up", style_none: "none" }); + +globalThis.polyline = globalThis.polyline || __pineNS({}); + +globalThis.linefill = globalThis.linefill || __pineNS({}); + +globalThis.matrix = globalThis.matrix || __pineNS({}); + +globalThis.map = globalThis.map || __pineNS({}); + +globalThis.session = globalThis.session || __pineNS({ regular: "regular", extended: "extended" }); + +globalThis.dayofweek = globalThis.dayofweek || __pineNS({ sunday: 1, monday: 2, tuesday: 3, wednesday: 4, thursday: 5, friday: 6, saturday: 7 }); + +globalThis.timeframe = __pineNS(Object.assign(globalThis.timeframe || {}, { period: (globalThis.timeframe && globalThis.timeframe.period) || "D", isintraday: false, isdaily: true, multiplier: 1 })); + +globalThis.request = __pineNS(globalThis.request || {}); + +globalThis.input = __pineNS(globalThis.input || {}); + +globalThis.adjustment = globalThis.adjustment || __pineNS({}); + +globalThis.earnings = globalThis.earnings || __pineNS({}); + +globalThis.dividends = globalThis.dividends || __pineNS({}); + +globalThis.splits = globalThis.splits || __pineNS({}); + +globalThis.currency = globalThis.currency || __pineNS({}); + +globalThis.display = globalThis.display || __pineNS({ none: "none", all: "all", pane: "pane", data_window: "data_window", status_line: "status_line", price_scale: "price_scale" }); + +globalThis.format = globalThis.format || __pineNS({ price: "price", volume: "volume", percent: "percent", mintick: "mintick", inherit: "inherit" }); + +globalThis.scale = globalThis.scale || __pineNS({ left: "left", right: "right", none: "none" }); + +globalThis.font = globalThis.font || __pineNS({ family_default: "default", family_monospace: "monospace" }); + +globalThis.xloc = globalThis.xloc || __pineNS({ bar_index: "bar_index", bar_time: "bar_time" }); + +globalThis.yloc = globalThis.yloc || __pineNS({ price: "price", abovebar: "abovebar", belowbar: "belowbar" }); + +globalThis.extend = globalThis.extend || __pineNS({ none: "none", left: "left", right: "right", both: "both" }); + +globalThis.order = globalThis.order || __pineNS({ ascending: "ascending", descending: "descending" }); + +globalThis.alert = globalThis.alert || Object.assign(function() { return null; }, { freq_once_per_bar: "once_per_bar", freq_once_per_bar_close: "once_per_bar_close", freq_all: "all" }); + +globalThis.dayofmonth = globalThis.dayofmonth || function(t) { return new Date(t || 0).getUTCDate(); }; + +globalThis.plot = globalThis.plot || __pineNS({ style_line: "line", style_linebr: "linebr", style_stepline: "stepline", style_histogram: "histogram", style_cross: "cross", style_area: "area", style_areabr: "areabr", style_columns: "columns", style_circles: "circles" }); + +globalThis.int = globalThis.int || function(x) { return x == null ? null : Math.trunc(Number(x)); }; + +globalThis.float = globalThis.float || function(x) { return x == null ? null : Number(x); }; + +globalThis.bool = globalThis.bool || function(x) { return Boolean(x); }; + +globalThis.string = globalThis.string || function(x) { return x == null ? null : String(x); }; + +globalThis.dayofweek = globalThis.dayofweek || __pineNS({ sunday: 1, monday: 2, tuesday: 3, wednesday: 4, thursday: 5, friday: 6, saturday: 7 }); + +globalThis.str = globalThis.str || __pineNS({}); + +pinescript.math = globalThis.math; + +pinescript.ta = globalThis.ta; + +pinescript.str = globalThis.str; + +pinescript.array = __pineNS(globalThis.array); + + pinescript.color = { hex: function(s) { @@ -1190,10 +1670,10 @@ pinescript.table = { }; -// Input parameters +// Script input parameters and their defaults -// Main script logic +// The transpiled script logic, called once per bar function main() { @@ -1208,19 +1688,19 @@ function main() { null; // Study: Singular Spectrum Decomposition [LuxAlgo] // Options: {"overlay":false} - let lagInput = input.int(20, "Window Length (L)", ({ minval: 2, tooltip: "The embedding window length (L). Larger values capture longer cycles." })); - let trainingInput = input.int(100, "Buffer Length (N)", ({ minval: 50, tooltip: "The number of recent bars (N) used for the static decomposition." })); - let forecastInput = input.int(20, "Forecast Length", ({ minval: 1, tooltip: "Number of bars to extrapolate into the future." })); + let lagInput = pinescript.inputInt(20, "Window Length (L)", ({ minval: 2, tooltip: "The embedding window length (L). Larger values capture longer cycles." })); + let trainingInput = pinescript.inputInt(100, "Buffer Length (N)", ({ minval: 50, tooltip: "The number of recent bars (N) used for the static decomposition." })); + let forecastInput = pinescript.inputInt(20, "Forecast Length", ({ minval: 1, tooltip: "Number of bars to extrapolate into the future." })); let GROUP_STYLE = "Style"; - let showTrend = input.bool(true, "Trend on Price", ({ inline: "Trend", group: GROUP_STYLE })); - let trendColorInput = input.color(pinescript.color.hex("#ff5d00"), "", ({ inline: "Trend", group: GROUP_STYLE })); - let showP1 = input.bool(true, "Long Term Periodic", ({ inline: "P1", group: GROUP_STYLE })); - let p1ColorInput = input.color(pinescript.color.hex("#2962ff"), "", ({ inline: "P1", group: GROUP_STYLE })); - let showP2 = input.bool(true, "Short Term Periodic", ({ inline: "P2", group: GROUP_STYLE })); - let p2ColorInput = input.color(pinescript.color.hex("#f06292"), "", ({ inline: "P2", group: GROUP_STYLE })); - let showNoise = input.bool(true, "Noise", ({ inline: "Noise", group: GROUP_STYLE })); - let noiseColorInput = input.color(pinescript.color.new(pinescript.color.hex("#9c9c9c"), 50), "", ({ inline: "Noise", group: GROUP_STYLE })); - let showExtrapolation = input.bool(true, "Show Extrapolation", ({ group: GROUP_STYLE, tooltip: "Show the forecast as dashed lines." })); + let showTrend = pinescript.inputBool(true, "Trend on Price", ({ inline: "Trend", group: GROUP_STYLE })); + let trendColorInput = pinescript.inputColor(pinescript.color.hex("#ff5d00"), "", ({ inline: "Trend", group: GROUP_STYLE })); + let showP1 = pinescript.inputBool(true, "Long Term Periodic", ({ inline: "P1", group: GROUP_STYLE })); + let p1ColorInput = pinescript.inputColor(pinescript.color.hex("#2962ff"), "", ({ inline: "P1", group: GROUP_STYLE })); + let showP2 = pinescript.inputBool(true, "Short Term Periodic", ({ inline: "P2", group: GROUP_STYLE })); + let p2ColorInput = pinescript.inputColor(pinescript.color.hex("#f06292"), "", ({ inline: "P2", group: GROUP_STYLE })); + let showNoise = pinescript.inputBool(true, "Noise", ({ inline: "Noise", group: GROUP_STYLE })); + let noiseColorInput = pinescript.inputColor(pinescript.color.new(pinescript.color.hex("#9c9c9c"), 50), "", ({ inline: "Noise", group: GROUP_STYLE })); + let showExtrapolation = pinescript.inputBool(true, "Show Extrapolation", ({ group: GROUP_STYLE, tooltip: "Show the forecast as dashed lines." })); let DATA = pinescript.color.hex("#DBDBDB"); let HEADERS = pinescript.color.hex("#808080"); let BACKGROUND = pinescript.color.hex("#161616"); @@ -1237,9 +1717,9 @@ function main() { let dashboardTooltip = "Enable or disable the dashboard."; let dashboardPositionTooltip = "Select the dashboard location."; let dashboardSizeTooltip = "Select the dashboard size."; - let dashboardInput = input.bool(true, "Dashboard", ({ group: DASHBOARD_GROUP, tooltip: dashboardTooltip })); - let dashboardPositionInput = input.string(TOP_RIGHT, "Position", ({ group: DASHBOARD_GROUP, tooltip: dashboardPositionTooltip, options: [TOP_RIGHT, BOTTOM_RIGHT, BOTTOM_LEFT] })); - let dashboardSizeInput = input.string(SMALL, "Size", ({ group: DASHBOARD_GROUP, tooltip: dashboardSizeTooltip, options: [TINY, SMALL, NORMAL, LARGE, HUGE] })); + let dashboardInput = pinescript.inputBool(true, "Dashboard", ({ group: DASHBOARD_GROUP, tooltip: dashboardTooltip })); + let dashboardPositionInput = pinescript.inputString(TOP_RIGHT, "Position", ({ group: DASHBOARD_GROUP, tooltip: dashboardPositionTooltip, options: [TOP_RIGHT, BOTTOM_RIGHT, BOTTOM_LEFT] })); + let dashboardSizeInput = pinescript.inputString(SMALL, "Size", ({ group: DASHBOARD_GROUP, tooltip: dashboardSizeTooltip, options: [TINY, SMALL, NORMAL, LARGE, HUGE] })); pinescript.position.bottom_left; pinescript.position.bottom_right; pinescript.position.top_right; @@ -1317,7 +1797,7 @@ function main() { } function extrapolate(src, lrr, nForecast) { let L = (lrr.size() + 1); - let res = array.copy(src); + let res = pinescript.arrayCopy(src); let lastRecon = src.get((src.size() - 1)); let f0 = 0; for (let j = 0; j <= (L - 2); j++) { @@ -1345,14 +1825,14 @@ function main() { for (let i = 0; i <= (values.size() - 1); i++) { pinescript.arrayPush(points, pinescript.chartPointFromIndex((startBar + i), pinescript.arrayGet(values, i))); } - return polyline.new(points, ({ line_color: c, line_width: width, line_style: style })); + return pinescript.polylineNew(points, ({ line_color: c, line_width: width, line_style: style })); } function createOverlayPolyline(values, startBar, c, width = 2, style = line.style_solid) { let points = pinescript.arrayNew(0); for (let i = 0; i <= (values.size() - 1); i++) { pinescript.arrayPush(points, pinescript.chartPointFromIndex((startBar + i), pinescript.arrayGet(values, i))); } - return polyline.new(points, ({ line_color: c, line_width: width, line_style: style, force_overlay: true })); + return pinescript.polylineNew(points, ({ line_color: c, line_width: width, line_style: style, force_overlay: true })); } function getPeriod(src) { let n = src.size(); @@ -1393,7 +1873,7 @@ function main() { let foreIdx = bar_index; let y = pinescript.arrayNew(0); for (let i = 0; i <= (trainingInput - 1); i++) { - pinescript.arrayPush(y, pinescript.offset(close, ((trainingInput - 1) - i))); + pinescript.arrayPush(y, pinescript.hist(0, close, ((trainingInput - 1) - i))); } let S = pinescript.matrixNew(lagInput, lagInput, 0); let nRowsX = ((trainingInput - lagInput) + 1); @@ -1433,11 +1913,11 @@ function main() { } } } - polyline.delete(state.trendLine); - polyline.delete(state.trendFore); - polyline.delete(state.p1Line); - polyline.delete(state.p2Line); - polyline.delete(state.noiseLine); + pinescript.polylineDelete(state.trendLine); + pinescript.polylineDelete(state.trendFore); + pinescript.polylineDelete(state.p1Line); + pinescript.polylineDelete(state.p2Line); + pinescript.polylineDelete(state.noiseLine); if (showTrend) { state.trendLine = createOverlayPolyline(trendValues, startIdx, trendColorInput, 3); if (showExtrapolation) { @@ -1473,7 +1953,67 @@ function main() { } -// Export for use -export { main +// The bar-by-bar engine: runs main() once per bar over the dataset. +function run(data, options = {}) { + data = data || {}; + const inClose = (data.close || []).map(Number); + const n = inClose.length; + const inOpen = (data.open || inClose).map(Number); + const inHigh = (data.high || inClose).map(Number); + const inLow = (data.low || inClose).map(Number); + const inVol = (data.volume || new Array(n).fill(0)).map(Number); + const inTime = (data.time || inClose.map((_, i) => i)).map(Number); + + // Reset all persistent and per-run state so repeated runs are independent. + globalThis.__pineState = {}; + globalThis.__pineRuntime = { plots: {}, plotshapes: {}, alerts: [], bars: n, inputs: options.inputs || {} }; + pinescript.__rt = {}; + pinescript.__bar = 0; + + // Growing OHLCV windows: each is a SeriesArray that gains one element per bar, + // so window built-ins like sma and highest see history up to the current bar. + const O = pinescript.asSeries([]), H = pinescript.asSeries([]), L = pinescript.asSeries([]); + const C = pinescript.asSeries([]), V = pinescript.asSeries([]), T = pinescript.asSeries([]); + const HL2 = pinescript.asSeries([]), HLC3 = pinescript.asSeries([]), OHLC4 = pinescript.asSeries([]); + globalThis.open = O; globalThis.high = H; globalThis.low = L; globalThis.close = C; + globalThis.volume = V; globalThis.time = T; + globalThis.hl2 = HL2; globalThis.hlc3 = HLC3; globalThis.ohlc4 = OHLC4; + globalThis.syminfo = globalThis.syminfo || { tickerid: 'SYNTHETIC', ticker: 'SYNTHETIC', mintick: 0.01 }; + + const rt = globalThis.__pineRuntime; + for (let i = 0; i < n; i++) { + O.push(inOpen[i]); H.push(inHigh[i]); L.push(inLow[i]); C.push(inClose[i]); V.push(inVol[i]); T.push(inTime[i]); + HL2.push((inHigh[i] + inLow[i]) / 2); + HLC3.push((inHigh[i] + inLow[i] + inClose[i]) / 3); + OHLC4.push((inOpen[i] + inHigh[i] + inLow[i] + inClose[i]) / 4); + + pinescript.__bar = i; + rt.__barIndex = i; + rt.__plotIdx = 0; + rt.__shapeIdx = 0; + globalThis.bar_index = i; + globalThis.last_bar_index = n - 1; + globalThis.barstate = { + isfirst: i === 0, islast: i === n - 1, isrealtime: false, ishistory: true, + isconfirmed: true, isnew: true, islastconfirmedhistory: i === n - 1, + }; + + main(); + } + + // Backfill skipped bars so every output series has exactly n entries. + for (const k of Object.keys(rt.plots)) { + const d = rt.plots[k].data; + for (let i = 0; i < n; i++) if (d[i] === undefined) d[i] = null; + } + for (const k of Object.keys(rt.plotshapes)) { + const d = rt.plotshapes[k].data; + for (let i = 0; i < n; i++) if (d[i] === undefined) d[i] = false; + } + return rt; +} + + +export { main, run }; diff --git a/converted/Smart_Trader_Episode_03_by_Ata_Sabanci_Candles_and_Tradelines.js b/converted/Smart_Trader_Episode_03_by_Ata_Sabanci_Candles_and_Tradelines.js index 068fae1..20311b4 100644 --- a/converted/Smart_Trader_Episode_03_by_Ata_Sabanci_Candles_and_Tradelines.js +++ b/converted/Smart_Trader_Episode_03_by_Ata_Sabanci_Candles_and_Tradelines.js @@ -1,12 +1,63 @@ -// PineScript to JavaScript Transpiled Code +// Auto-generated JavaScript from PineScript source -// Generated automatically +// Do not edit by hand -- re-run the converter instead -// Built-in PineScript functions +// All the built-in PineScript functions that scripts depend on at runtime const pinescript = { + __scalar: function(v) { + if (v === null || v === undefined) return v; + if (typeof v === 'object' && typeof v.valueOf === 'function') { + const s = v.valueOf(); + if (typeof s === 'number' || typeof s === 'boolean' || s === null) return s; + } + return v; + }, + series: function(id, value) { + const rt = this.__rt || (this.__rt = {}); + const store = rt.series || (rt.series = {}); + let buf = store[id]; + if (!buf) buf = store[id] = []; + buf[this.__bar | 0] = this.__scalar(value); + return buf; + }, + hist: function(id, value, n) { + const buf = this.series(id, value); + const idx = (this.__bar | 0) - (n | 0); + return idx >= 0 && idx < buf.length ? buf[idx] : null; + }, + time: function(timeframe, session) { + const t = globalThis.time; + if (t && typeof t.valueOf === 'function') return t.valueOf(); + return Array.isArray(t) ? t[t.length - 1] : t; + }, + sign: function(x) { + if (x === null || x === undefined || (typeof x === 'number' && isNaN(x))) return null; + return Math.sign(Number(x)); + }, + unpack: function(value, count) { + if (Array.isArray(value)) return value; + if (value != null && typeof value[Symbol.iterator] === 'function') return Array.from(value); + return new Array(count || 0).fill(null); + }, + alertcondition: function(condition, ...rest) { + if (globalThis.__pineRuntime) { + globalThis.__pineRuntime.alerts.push({ condition, args: rest }); + } + return null; + }, + barcolor: function(color) { return null; }, + bgcolor: function(color, title, editable, showLast) { + return null; + }, + fill: function(series1, series2, color, title, editable) { + return null; + }, + hline: function(_price, _title, _opts) { + return null; + }, sma: function(series, length) { if (series === null || series === undefined) return null; if (!Array.isArray(series)) series = [series]; @@ -84,6 +135,13 @@ const pinescript = { } return weightSum > 0 ? sum / weightSum : null; }, + swma: function(series) { + if (series === null || series === undefined) return null; + if (!Array.isArray(series)) series = [series]; + if (series.length < 4) return null; + const s = series.slice(-4); + return (s[0] * 1 + s[1] * 2 + s[2] * 2 + s[3] * 1) / 6; + }, atr: function(high, low, close, length = 14) { if (!Array.isArray(high) || !Array.isArray(low) || !Array.isArray(close)) return null; if (high.length < length + 1 || low.length < length + 1 || close.length < length + 1) return null; @@ -114,6 +172,53 @@ const pinescript = { const atr = builtins.get('atr')(high, low, close, length); return { middle: sma, upper: sma + mult * atr, lower: sma - mult * atr }; }, + supertrend: function(factor, atrPeriod, high, low, close) { + if (!Array.isArray(high) || !Array.isArray(low) || !Array.isArray(close)) return [null, null]; + if (high.length < atrPeriod + 1) return [null, null]; + const atrVal = builtins.get('atr')(high, low, close, atrPeriod); + if (atrVal === null) return [null, null]; + const lastIdx = close.length - 1; + const hl2 = (high[lastIdx] + low[lastIdx]) / 2; + const upperBand = hl2 + factor * atrVal; + const lowerBand = hl2 - factor * atrVal; + // Determine direction: 1 means downtrend (price below band), -1 means uptrend + const direction = close[lastIdx] > upperBand ? -1 : close[lastIdx] < lowerBand ? 1 : -1; + const supertrendValue = direction === -1 ? lowerBand : upperBand; + return [supertrendValue, direction]; + }, + ta_supertrend: function(factor, atrPeriod) { + const H = globalThis.high; + const L = globalThis.low; + const C = globalThis.close; + return builtins.get('supertrend')(factor, atrPeriod, H, L, C); + }, + dmi: function(diLength, adxSmoothing, high, low, close) { + if (!Array.isArray(high) || !Array.isArray(low) || !Array.isArray(close)) return { plusDI: null, minusDI: null, adx: null }; + if (high.length < diLength + 1) return { plusDI: null, minusDI: null, adx: null }; + let plusDMSum = 0, minusDMSum = 0, trSum = 0; + for (let i = 1; i <= diLength; i++) { + const idx = high.length - 1 - diLength + i; + const upMove = high[idx] - high[idx - 1]; + const downMove = low[idx - 1] - low[idx]; + plusDMSum += (upMove > downMove && upMove > 0) ? upMove : 0; + minusDMSum += (downMove > upMove && downMove > 0) ? downMove : 0; + const tr = Math.max( + high[idx] - low[idx], + Math.abs(high[idx] - close[idx - 1]), + Math.abs(low[idx] - close[idx - 1]) + ); + trSum += tr; + } + const plusDI = trSum > 0 ? (plusDMSum / trSum) * 100 : 0; + const minusDI = trSum > 0 ? (minusDMSum / trSum) * 100 : 0; + const diSum = plusDI + minusDI; + const dx = diSum > 0 ? Math.abs(plusDI - minusDI) / diSum * 100 : 0; + return { plusDI, minusDI, adx: dx }; + }, + adx: function(diLength, adxSmoothing, high, low, close) { + const result = builtins.get('dmi')(diLength, adxSmoothing, high, low, close); + return result ? result.adx : null; + }, rsi: function(close, length = 14) { if (close === null || close === undefined) return null; if (!Array.isArray(close)) close = [close]; @@ -171,9 +276,7 @@ const pinescript = { return { macd: macdLine, signal: signalLine, histogram: histogram }; }, cci: function(a, b, c, d) { - // Pine signatures: - // - ta.cci(source, length) - // - ta.cci(high, low, close, length) + // Supports both ta.cci(source, length) and ta.cci(high, low, close, length) signatures let source; let length; if (Array.isArray(a) && typeof b === 'number' && c === undefined) { @@ -304,6 +407,15 @@ const pinescript = { stdev: function(source, length) { return Math.sqrt(builtins.get('variance')(source, length)); }, + median: function(source, length) { + if (!source || source.length < length) return null; + const sorted = [...source.slice(-length)].sort((a, b) => a - b); + const mid = Math.floor(sorted.length / 2); + return sorted.length % 2 !== 0 ? sorted[mid] : (sorted[mid - 1] + sorted[mid]) / 2; + }, + ta_median: function(source, length) { + return builtins.get('median')(source, length); + }, hl2: function(high, low) { return high && low ? (high[high.length - 1] + low[low.length - 1]) / 2 : null; }, @@ -420,6 +532,24 @@ const pinescript = { return (series1[i - 1] < series2[i - 1] && series1[i] >= series2[i]) || (series1[i - 1] > series2[i - 1] && series1[i] <= series2[i]); }, + crossover: function(series1, series2) { + if (series1 === null || series1 === undefined) return false; + if (series2 === null || series2 === undefined) return false; + if (!Array.isArray(series1)) series1 = [series1]; + if (!Array.isArray(series2)) series2 = [series2]; + if (series1.length < 2 || series2.length < 2) return false; + const i = series1.length - 1; + return series1[i - 1] < series2[i - 1] && series1[i] >= series2[i]; + }, + crossunder: function(series1, series2) { + if (series1 === null || series1 === undefined) return false; + if (series2 === null || series2 === undefined) return false; + if (!Array.isArray(series1)) series1 = [series1]; + if (!Array.isArray(series2)) series2 = [series2]; + if (series1.length < 2 || series2.length < 2) return false; + const i = series1.length - 1; + return series1[i - 1] > series2[i - 1] && series1[i] <= series2[i]; + }, offset: function(series, offset) { if (!Array.isArray(series)) return null; const idx = series.length - 1 - offset; @@ -500,12 +630,14 @@ const pinescript = { return builtins.get('nz')(value, replacement); }, plot: function(series, title = '', color = null, linewidth = 1) { - globalThis.__pineRuntime.plots.push({ - series, - title, - color, - linewidth - }); + const rt = globalThis.__pineRuntime; + if (!rt) return series; + const bar = rt.__barIndex | 0; + const ord = (rt.__plotIdx = (rt.__plotIdx | 0) + 1) - 1; + const key = (title && String(title)) || ('plot_' + ord); + let p = rt.plots[key]; + if (!p) p = rt.plots[key] = { title: key, color, linewidth, data: [] }; + p.data[bar] = this.__scalar(series); return series; }, lineNew: function(x1, y1, x2, y2, opts = {}) { @@ -514,20 +646,72 @@ const pinescript = { lineDelete: function(l) { return null; }, - labelNew: function(...args) { - if (globalThis.__pineRuntime) { - globalThis.__pineRuntime.labels = globalThis.__pineRuntime.labels || []; - globalThis.__pineRuntime.labels.push({ args }); - } - return { args }; + lineSetXY: function(line, point, x, y) { + if (!line) return null; + if (point === 0 || point === 'x1') { line.x1 = x; line.y1 = y; } + else { line.x2 = x; line.y2 = y; } + return line; + }, + lineGetX: function(line, point) { + if (!line) return null; + return point === 0 || point === 'x1' ? line.x1 : line.x2; + }, + lineGetY: function(line, point) { + if (!line) return null; + return point === 0 || point === 'y1' ? line.y1 : line.y2; + }, + labelNew: function(x, y, text = '', opts = {}) { + return { x, y, text, opts, _type: 'label' }; }, labelDelete: function(l) { return null; }, + labelSetText: function(label, text) { + if (!label) return null; + label.text = text; + return label; + }, + labelGetText: function(label) { + if (!label) return ''; + return label.text || ''; + }, + boxNew: function(left, top, right, bottom, opts = {}) { + return { left, top, right, bottom, opts, _type: 'box' }; + }, + boxDelete: function(box) { + return null; + }, + boxSetLeftTop: function(box, left, top) { + if (!box) return null; + box.left = left; + box.top = top; + return box; + }, + boxSetRightBottom: function(box, right, bottom) { + if (!box) return null; + box.right = right; + box.bottom = bottom; + return box; + }, + polylineNew: function(points, opts = {}) { + return { points: points || [], opts, _type: 'polyline' }; + }, + polylineDelete: function(poly) { + return null; + }, plotshape: function(condition, ...rest) { - if (globalThis.__pineRuntime) { - globalThis.__pineRuntime.plotshapes.push({ condition, args: rest }); + const rt = globalThis.__pineRuntime; + if (!rt) return condition; + const bar = rt.__barIndex | 0; + const ord = (rt.__shapeIdx = (rt.__shapeIdx | 0) + 1) - 1; + let key = 'shape_' + ord; + for (const r of rest) { + if (typeof r === 'string') { key = r; break; } + if (r && typeof r === 'object' && r.title) { key = String(r.title); break; } } + let s = rt.plotshapes[key]; + if (!s) s = rt.plotshapes[key] = { title: key, data: [] }; + s.data[bar] = !!this.__scalar(condition); return condition; }, indicator: function(title, shorttitle, overlay, opts) { @@ -542,27 +726,12 @@ const pinescript = { } return null; }, - alertcondition: function(condition, ...rest) { - if (globalThis.__pineRuntime) { - globalThis.__pineRuntime.alerts.push({ condition, args: rest }); - } - return null; - }, plotbar: function(open, high, low, close, title, color, editable, showLast) { return { open, high, low, close }; }, plotcandle: function(open, high, low, close, title, color, wickColor, borderColor, editable, showLast) { return { open, high, low, close }; }, - hline: function(_price, _title, _opts) { - return null; - }, - bgcolor: function(color, title, editable, showLast) { - return null; - }, - fill: function(series1, series2, color, title, editable) { - return null; - }, alert: function(condition, message, frequency) { console.log('Alert:', condition ? message : 'Condition not met'); return condition; @@ -828,6 +997,9 @@ const pinescript = { arrayIndexOf: function(arr, value) { return arr ? arr.indexOf(value) : -1; }, + arrayLastIndexOf: function(arr, value) { + return arr ? arr.lastIndexOf(value) : -1; + }, arraySum: function(arr) { return arr ? arr.reduce((a, b) => a + b, 0) : 0; }, @@ -840,6 +1012,42 @@ const pinescript = { arrayMax: function(arr) { return arr && arr.length > 0 ? Math.max(...arr) : null; }, + arrayStdev: function(arr) { + if (!arr || arr.length === 0) return null; + const mean = arr.reduce((a, b) => a + b, 0) / arr.length; + const variance = arr.reduce((sum, val) => sum + Math.pow(val - mean, 2), 0) / arr.length; + return Math.sqrt(variance); + }, + arrayVariance: function(arr) { + if (!arr || arr.length === 0) return null; + const mean = arr.reduce((a, b) => a + b, 0) / arr.length; + return arr.reduce((sum, val) => sum + Math.pow(val - mean, 2), 0) / arr.length; + }, + arrayCovariance: function(arr1, arr2) { + if (!arr1 || !arr2 || arr1.length === 0 || arr1.length !== arr2.length) return null; + const mean1 = arr1.reduce((a, b) => a + b, 0) / arr1.length; + const mean2 = arr2.reduce((a, b) => a + b, 0) / arr2.length; + let cov = 0; + for (let i = 0; i < arr1.length; i++) { + cov += (arr1[i] - mean1) * (arr2[i] - mean2); + } + return cov / arr1.length; + }, + mode: function(arr) { + if (!arr || arr.length === 0) return null; + const counts = new Map(); + let maxCount = 0; + let modeVal = arr[0]; + for (const val of arr) { + const c = (counts.get(val) || 0) + 1; + counts.set(val, c); + if (c > maxCount) { + maxCount = c; + modeVal = val; + } + } + return modeVal; + }, strLength: function(str) { return str ? str.length : 0; }, @@ -895,6 +1103,84 @@ const pinescript = { strReverse: function(str) { return str ? str.split('').reverse().join('') : ''; }, + strFormat: function(formatStr, ...args) { + if (!formatStr) return ''; + return formatStr.replace(/\{(\d+)\}/g, (match, idx) => { + const i = parseInt(idx, 10); + return i < args.length ? String(args[i]) : match; + }); + }, + input: function(defval, title, tooltip, inline, group) { + return defval; + }, + inputInt: function(defval = 0, title, minval, maxval, step, tooltip, inline, group) { + return defval; + }, + inputFloat: function(defval = 0.0, title, minval, maxval, step, tooltip, inline, group) { + return defval; + }, + inputBool: function(defval = false, title, tooltip, inline, group) { + return defval; + }, + inputString: function(defval = '', title, options, tooltip, inline, group) { + return defval; + }, + inputSource: function(defval = null, title, tooltip, inline, group) { + return defval; + }, + inputColor: function(defval = '#000000', title, tooltip, inline, group) { + return defval; + }, + inputTime: function(defval = 0, title, tooltip, inline, group) { + return defval; + }, + mathSign: function(value) { + return Math.sign(value); + }, + mathAvg: function(...args) { + const valid = args.filter(v => v !== null && v !== undefined && !isNaN(v)); + return valid.length > 0 ? valid.reduce((a, b) => a + b, 0) / valid.length : null; + }, + mathSum: function(series, length) { + if (!series || series.length < length) return null; + return series.slice(-length).reduce((a, b) => a + b, 0); + }, + mathRandom: function(min = 0, max = 1) { + return min + Math.random() * (max - min); + }, + mathTodegrees: function(radians) { + return radians * (180 / Math.PI); + }, + mathToradians: function(degrees) { + return degrees * (Math.PI / 180); + }, + colorT: function(color, transparency) { + if (transparency !== undefined) { + return { color, transparency }; + } + if (color && typeof color === 'object' && 'transparency' in color) { + return color.transparency; + } + return 0; + }, + tableCellSetText: function(table, column, row, text) { + return null; + }, + tableCellSetBgcolor: function(table, column, row, bgcolor) { + return null; + }, + tableMergeCells: function(table, startColumn, startRow, endColumn, endRow) { + return null; + }, + tableDelete: function(table) { + return null; + }, + tableClear: function(table, startColumn, startRow, endColumn, endRow) { + return null; + }, + maxBarsBack: function(series, length) { + return null; + }, pi: 3.141592653589793, e: 2.718281828459045, true: true, @@ -905,8 +1191,7 @@ const pinescript = { const info = { ticker: 'AAPL', tickerid: 'NASDAQ:AAPL', prefix: 'NASDAQ', root: 'AAPL', suffix: '' }; return info[type] || ''; }, - time: 1771178052949, - timenow: 1771178052949, + timenow: 1781574528607, barstate: "LAST", dividends: {}, splits: {}, @@ -923,7 +1208,7 @@ const pinescript = { return null; }, tr: function(high, low, close) { - // Pine signature: ta.tr(true) + // Handles both the boolean shorthand ta.tr(true) and explicit high/low/close arguments if (typeof high === 'boolean') { const H = globalThis.high; const L = globalThis.low; @@ -934,7 +1219,7 @@ const pinescript = { return Math.max(H[i] - L[i], Math.abs(H[i] - prevClose), Math.abs(L[i] - prevClose)); } - // Fallback: treat inputs as scalars for current bar + // When arrays are passed, compute true range from the last bar using the previous close if (Array.isArray(close)) { const prevClose = close[close.length - 2] || close[0]; const currHigh = Array.isArray(high) ? high[high.length - 1] : high; @@ -945,6 +1230,92 @@ const pinescript = { return Math.max(high - low, Math.abs(high - close), Math.abs(low - close)); }, + arrayFirst: function(arr) { + if (!arr || arr.length === 0) return null; + return arr[0]; + }, + arrayLast: function(arr) { + if (!arr || arr.length === 0) return null; + return arr[arr.length - 1]; + }, + arrayJoin: function(arr, separator = ',') { + if (!arr) return ''; + return arr.join(separator); + }, + arrayConcat: function(arr1, arr2) { + if (!arr1) return arr2 || []; + if (!arr2) return arr1; + return arr1.concat(arr2); + }, + arrayCopy: function(arr) { + if (!arr) return []; + return [...arr]; + }, + arrayBinarySearch: function(arr, value) { + if (!arr || arr.length === 0) return -1; + let lo = 0, hi = arr.length - 1; + while (lo <= hi) { + const mid = (lo + hi) >>> 1; + if (arr[mid] === value) return mid; + if (arr[mid] < value) lo = mid + 1; + else hi = mid - 1; + } + return -1; + }, + arrayRange: function(arr) { + if (!arr || arr.length === 0) return 0; + return Math.max(...arr) - Math.min(...arr); + }, + arrayMedian: function(arr) { + if (!arr || arr.length === 0) return null; + const sorted = [...arr].sort((a, b) => a - b); + const mid = Math.floor(sorted.length / 2); + return sorted.length % 2 !== 0 ? sorted[mid] : (sorted[mid - 1] + sorted[mid]) / 2; + }, + arrayMode: function(arr) { + if (!arr || arr.length === 0) return null; + const freq = {}; + let maxCount = 0; + let mode = arr[0]; + for (const v of arr) { + freq[v] = (freq[v] || 0) + 1; + if (freq[v] > maxCount) { maxCount = freq[v]; mode = v; } + } + return mode; + }, + arrayPercentile: function(arr, percentile) { + if (!arr || arr.length === 0) return null; + const sorted = [...arr].sort((a, b) => a - b); + const idx = (percentile / 100) * (sorted.length - 1); + const lo = Math.floor(idx); + const hi = Math.ceil(idx); + if (lo === hi) return sorted[lo]; + return sorted[lo] + (sorted[hi] - sorted[lo]) * (idx - lo); + }, + arrayPercentileLinearInterpolation: function(arr, percentile) { + return builtins.get('arrayPercentile')(arr, percentile); + }, + arrayPercentileNearestRank: function(arr, percentile) { + if (!arr || arr.length === 0) return null; + const sorted = [...arr].sort((a, b) => a - b); + const idx = Math.ceil((percentile / 100) * sorted.length) - 1; + return sorted[Math.max(0, idx)]; + }, + arrayAbs: function(arr) { + if (!arr) return []; + return arr.map(v => Math.abs(v)); + }, + arrayEvery: function(arr, callback) { + if (!arr) return false; + return arr.every(callback); + }, + arraySome: function(arr, callback) { + if (!arr) return false; + return arr.some(callback); + }, + requestFinancial: function() { + return null; + }, }; @@ -1101,6 +1472,115 @@ globalThis.barmerge = globalThis.barmerge || { }; +function __pineNS(real) { + + const base = real || {}; + + return new Proxy(base, { + + get(target, key) { + + if (key in target) return target[key]; + + if (typeof key === "symbol") return target[key]; + + // Unknown member: usable as a value (null), a constant, or a no-op function. + + const noop = function() { return null; }; + + noop.toString = () => ""; + + return noop; + + } + + }); + +} + +globalThis.ta = globalThis.ta || __pineNS({}); + +globalThis.math = globalThis.math || __pineNS({ pi: Math.PI, e: Math.E, phi: 1.618033988749895, rphi: 0.618033988749895 }); + +globalThis.chart = globalThis.chart || __pineNS({ point: __pineNS({}), bg_color: null, fg_color: null }); + +globalThis.line = globalThis.line || __pineNS({ style_solid: "solid", style_dashed: "dashed", style_dotted: "dotted" }); + +globalThis.box = globalThis.box || __pineNS({}); + +globalThis.label = globalThis.label || __pineNS({ style_label_down: "label_down", style_label_up: "label_up", style_none: "none" }); + +globalThis.polyline = globalThis.polyline || __pineNS({}); + +globalThis.linefill = globalThis.linefill || __pineNS({}); + +globalThis.matrix = globalThis.matrix || __pineNS({}); + +globalThis.map = globalThis.map || __pineNS({}); + +globalThis.session = globalThis.session || __pineNS({ regular: "regular", extended: "extended" }); + +globalThis.dayofweek = globalThis.dayofweek || __pineNS({ sunday: 1, monday: 2, tuesday: 3, wednesday: 4, thursday: 5, friday: 6, saturday: 7 }); + +globalThis.timeframe = __pineNS(Object.assign(globalThis.timeframe || {}, { period: (globalThis.timeframe && globalThis.timeframe.period) || "D", isintraday: false, isdaily: true, multiplier: 1 })); + +globalThis.request = __pineNS(globalThis.request || {}); + +globalThis.input = __pineNS(globalThis.input || {}); + +globalThis.adjustment = globalThis.adjustment || __pineNS({}); + +globalThis.earnings = globalThis.earnings || __pineNS({}); + +globalThis.dividends = globalThis.dividends || __pineNS({}); + +globalThis.splits = globalThis.splits || __pineNS({}); + +globalThis.currency = globalThis.currency || __pineNS({}); + +globalThis.display = globalThis.display || __pineNS({ none: "none", all: "all", pane: "pane", data_window: "data_window", status_line: "status_line", price_scale: "price_scale" }); + +globalThis.format = globalThis.format || __pineNS({ price: "price", volume: "volume", percent: "percent", mintick: "mintick", inherit: "inherit" }); + +globalThis.scale = globalThis.scale || __pineNS({ left: "left", right: "right", none: "none" }); + +globalThis.font = globalThis.font || __pineNS({ family_default: "default", family_monospace: "monospace" }); + +globalThis.xloc = globalThis.xloc || __pineNS({ bar_index: "bar_index", bar_time: "bar_time" }); + +globalThis.yloc = globalThis.yloc || __pineNS({ price: "price", abovebar: "abovebar", belowbar: "belowbar" }); + +globalThis.extend = globalThis.extend || __pineNS({ none: "none", left: "left", right: "right", both: "both" }); + +globalThis.order = globalThis.order || __pineNS({ ascending: "ascending", descending: "descending" }); + +globalThis.alert = globalThis.alert || Object.assign(function() { return null; }, { freq_once_per_bar: "once_per_bar", freq_once_per_bar_close: "once_per_bar_close", freq_all: "all" }); + +globalThis.dayofmonth = globalThis.dayofmonth || function(t) { return new Date(t || 0).getUTCDate(); }; + +globalThis.plot = globalThis.plot || __pineNS({ style_line: "line", style_linebr: "linebr", style_stepline: "stepline", style_histogram: "histogram", style_cross: "cross", style_area: "area", style_areabr: "areabr", style_columns: "columns", style_circles: "circles" }); + +globalThis.int = globalThis.int || function(x) { return x == null ? null : Math.trunc(Number(x)); }; + +globalThis.float = globalThis.float || function(x) { return x == null ? null : Number(x); }; + +globalThis.bool = globalThis.bool || function(x) { return Boolean(x); }; + +globalThis.string = globalThis.string || function(x) { return x == null ? null : String(x); }; + +globalThis.dayofweek = globalThis.dayofweek || __pineNS({ sunday: 1, monday: 2, tuesday: 3, wednesday: 4, thursday: 5, friday: 6, saturday: 7 }); + +globalThis.str = globalThis.str || __pineNS({}); + +pinescript.math = globalThis.math; + +pinescript.ta = globalThis.ta; + +pinescript.str = globalThis.str; + +pinescript.array = __pineNS(globalThis.array); + + pinescript.color = { hex: function(s) { @@ -1190,7 +1670,7 @@ pinescript.table = { }; -// Input parameters +// Script input parameters and their defaults const BlockAnalytics = { new: function(valid, totalBuy, totalSell, delta, blockHeight, blockArea, avgUpperPin, avgLowerPin, avgBody, newOpen, newHigh, newLow, newClose, startIdx, endIdx, centerIdx, boxBottom, trendType, trendLocked) { return { valid: valid, totalBuy: totalBuy, totalSell: totalSell, delta: delta, blockHeight: blockHeight, blockArea: blockArea, avgUpperPin: avgUpperPin, avgLowerPin: avgLowerPin, avgBody: avgBody, newOpen: newOpen, newHigh: newHigh, newLow: newLow, newClose: newClose, startIdx: startIdx, endIdx: endIdx, centerIdx: centerIdx, boxBottom: boxBottom, trendType: trendType, trendLocked: trendLocked }; } }; const BoxCore = { new: function(valid, sumBuy, sumSell, delta, pocPrice, pocOff, pocVol, maxBuyVal, maxBuyOff, maxBuyHigh, maxSellVal, maxSellOff, maxSellLow, minBuyVal, minBuyOff, minBuyHigh, minSellVal, minSellOff, minSellLow) { return { valid: valid, sumBuy: sumBuy, sumSell: sumSell, delta: delta, pocPrice: pocPrice, pocOff: pocOff, pocVol: pocVol, maxBuyVal: maxBuyVal, maxBuyOff: maxBuyOff, maxBuyHigh: maxBuyHigh, maxSellVal: maxSellVal, maxSellOff: maxSellOff, maxSellLow: maxSellLow, minBuyVal: minBuyVal, minBuyOff: minBuyOff, minBuyHigh: minBuyHigh, minSellVal: minSellVal, minSellOff: minSellOff, minSellLow: minSellLow }; } }; @@ -1201,7 +1681,7 @@ const TrendContext = { new: function(currentType, currentBlocks, currentLabel, p const NarrativeData = { new: function(trendType, strengthLabel, qualityScore, blockRange, transitionType, pricePattern, channelStatus, upperAngle, lowerAngle, blockPatternDesc, hasDivergence, dominance, trendDeltaPct, volumeMomentum, barPattern, barPatternBias, barRangeStatus, barVolumeStatus, barDelta, barPressure, bodyTrend, upperPinChgPct, lowerPinChgPct, pinSignal, compositeForm, channelPositionPct, positionZone, resistanceLevel1, resistanceSource1, resistanceLevel2, resistanceSource2, supportLevel1, supportSource1, supportLevel2, supportSource2, trendAssessment, contradictionCount, warnings) { return { trendType: trendType, strengthLabel: strengthLabel, qualityScore: qualityScore, blockRange: blockRange, transitionType: transitionType, pricePattern: pricePattern, channelStatus: channelStatus, upperAngle: upperAngle, lowerAngle: lowerAngle, blockPatternDesc: blockPatternDesc, hasDivergence: hasDivergence, dominance: dominance, trendDeltaPct: trendDeltaPct, volumeMomentum: volumeMomentum, barPattern: barPattern, barPatternBias: barPatternBias, barRangeStatus: barRangeStatus, barVolumeStatus: barVolumeStatus, barDelta: barDelta, barPressure: barPressure, bodyTrend: bodyTrend, upperPinChgPct: upperPinChgPct, lowerPinChgPct: lowerPinChgPct, pinSignal: pinSignal, compositeForm: compositeForm, channelPositionPct: channelPositionPct, positionZone: positionZone, resistanceLevel1: resistanceLevel1, resistanceSource1: resistanceSource1, resistanceLevel2: resistanceLevel2, resistanceSource2: resistanceSource2, supportLevel1: supportLevel1, supportSource1: supportSource1, supportLevel2: supportLevel2, supportSource2: supportSource2, trendAssessment: trendAssessment, contradictionCount: contradictionCount, warnings: warnings }; } }; const DisplayedLevels = { new: function(hasPOCUp, pocUp, pocUpBox, hasPOCDn, pocDn, pocDnBox, hasCoreUp, coreUp, coreUpType, coreUpBox, hasCoreDn, coreDn, coreDnType, coreDnBox) { return { hasPOCUp: hasPOCUp, pocUp: pocUp, pocUpBox: pocUpBox, hasPOCDn: hasPOCDn, pocDn: pocDn, pocDnBox: pocDnBox, hasCoreUp: hasCoreUp, coreUp: coreUp, coreUpType: coreUpType, coreUpBox: coreUpBox, hasCoreDn: hasCoreDn, coreDn: coreDn, coreDnType: coreDnType, coreDnBox: coreDnBox }; } }; -// Main script logic +// The transpiled script logic, called once per bar function main() { @@ -1217,28 +1697,28 @@ function main() { // Study: Smart Trader, Episode 03, by Ata Sabancı | Candles and Tradelines // Options: {"shorttitle":"ST-EP03","overlay":true,"max_bars_back":5000,"max_labels_count":500,"max_boxes_count":500,"max_lines_count":500} let G_MAIN = "═══ Main Settings ═══"; - let i_window = input.int(100, "Window Bars", ({ minval: 1, group: G_MAIN })); - let i_groups = input.int(20, "Group Count", ({ minval: 1, group: G_MAIN })); - let i_calcBasis = input.string("Closed (bar[1])", "Calculation Basis", ({ options: ["Closed (bar[1])", "Current (bar[0])"], group: G_MAIN })); + let i_window = pinescript.inputInt(100, "Window Bars", ({ minval: 1, group: G_MAIN })); + let i_groups = pinescript.inputInt(20, "Group Count", ({ minval: 1, group: G_MAIN })); + let i_calcBasis = pinescript.inputString("Closed (bar[1])", "Calculation Basis", ({ options: ["Closed (bar[1])", "Current (bar[0])"], group: G_MAIN })); let baseOff = ((i_calcBasis === "Current (bar[0])") ? 0 : 1); - let i_showGroupNums = input.bool(true, "Show Group Numbers", ({ group: G_MAIN })); - let i_showGroupBoxes = input.bool(true, "Show Group Boxes", ({ group: G_MAIN })); - let i_boxColor = input.color(pinescript.color.gray, "Box Color", ({ group: G_MAIN })); - let i_boxTransp = input.int(85, "Box Transparency", ({ minval: 0, maxval: 100, group: G_MAIN })); - let i_globalTextSizeStr = input.string("Small", "Global Text Size", ({ options: ["Tiny", "Small", "Normal", "Large"], group: G_MAIN, tooltip: "Applies to ALL text elements on chart: labels, dashboard, trend lines, etc." })); + let i_showGroupNums = pinescript.inputBool(true, "Show Group Numbers", ({ group: G_MAIN })); + let i_showGroupBoxes = pinescript.inputBool(true, "Show Group Boxes", ({ group: G_MAIN })); + let i_boxColor = pinescript.inputColor(pinescript.color.gray, "Box Color", ({ group: G_MAIN })); + let i_boxTransp = pinescript.inputInt(85, "Box Transparency", ({ minval: 0, maxval: 100, group: G_MAIN })); + let i_globalTextSizeStr = pinescript.inputString("Small", "Global Text Size", ({ options: ["Tiny", "Small", "Normal", "Large"], group: G_MAIN, tooltip: "Applies to ALL text elements on chart: labels, dashboard, trend lines, etc." })); let GLOBAL_TEXT_SIZE = ((i_globalTextSizeStr === "Tiny") ? pinescript.size.tiny : ((i_globalTextSizeStr === "Normal") ? pinescript.size.normal : ((i_globalTextSizeStr === "Large") ? pinescript.size.large : pinescript.size.small))); let G_ANALYTICS = "═══ Block Analytics ═══"; - let i_showCompositeCandle = input.bool(false, "Show Composite Candle", ({ group: G_ANALYTICS, tooltip: "Displays a ghost-like composite candle at the center of each block representing aggregated OHLC." })); - let i_compositeCandleTransp = input.int(50, "Composite Candle Transparency", ({ minval: 50, maxval: 95, group: G_ANALYTICS, tooltip: "Transparency level for the ghost-like composite candle (higher = more transparent)." })); - let i_dimOriginalCandles = input.bool(true, "Dim Original Candles", ({ group: G_ANALYTICS, tooltip: "When composite candles are shown, dims the original chart candles to make composites stand out." })); - let i_originalCandleDimLevel = input.int(95, "Original Candle Dim Level", ({ minval: 50, maxval: 95, group: G_ANALYTICS, tooltip: "Transparency level for dimmed original candles (higher = more transparent/faded)." })); + let i_showCompositeCandle = pinescript.inputBool(false, "Show Composite Candle", ({ group: G_ANALYTICS, tooltip: "Displays a ghost-like composite candle at the center of each block representing aggregated OHLC." })); + let i_compositeCandleTransp = pinescript.inputInt(50, "Composite Candle Transparency", ({ minval: 50, maxval: 95, group: G_ANALYTICS, tooltip: "Transparency level for the ghost-like composite candle (higher = more transparent)." })); + let i_dimOriginalCandles = pinescript.inputBool(true, "Dim Original Candles", ({ group: G_ANALYTICS, tooltip: "When composite candles are shown, dims the original chart candles to make composites stand out." })); + let i_originalCandleDimLevel = pinescript.inputInt(95, "Original Candle Dim Level", ({ minval: 50, maxval: 95, group: G_ANALYTICS, tooltip: "Transparency level for dimmed original candles (higher = more transparent/faded)." })); let groupSize = pinescript.max(1, int(pinescript.round((float(i_window) / i_groups)))); let effWindow = (groupSize * i_groups); let _dimBullish = pinescript.color.new(pinescript.color.rgb(34, 197, 94), i_originalCandleDimLevel); let _dimBearish = pinescript.color.new(pinescript.color.rgb(239, 68, 68), i_originalCandleDimLevel); let _dimNeutral = pinescript.color.new(pinescript.color.rgb(120, 120, 120), i_originalCandleDimLevel); let _originalCandleColor = ((close > open) ? _dimBullish : ((close < open) ? _dimBearish : _dimNeutral)); - barcolor(((i_showCompositeCandle && i_dimOriginalCandles) ? _originalCandleColor : null)); + pinescript.barcolor(((i_showCompositeCandle && i_dimOriginalCandles) ? _originalCandleColor : null)); function clear(_this) { if (!pinescript.na(_this.upperLine)) { pinescript.lineDelete(_this.upperLine); @@ -1325,10 +1805,10 @@ function main() { } function f_ensureBoxCount(n) { while ((pinescript.arraySize(state.grpBoxes) < n)) { - pinescript.arrayPush(state.grpBoxes, box.new(bar_index, high, bar_index, low, ({ bgcolor: pinescript.color.new(i_boxColor, i_boxTransp), border_color: pinescript.color.new(pinescript.color.black, 100), border_width: 0 }))); + pinescript.arrayPush(state.grpBoxes, pinescript.boxNew(bar_index, high, bar_index, low, ({ bgcolor: pinescript.color.new(i_boxColor, i_boxTransp), border_color: pinescript.color.new(pinescript.color.black, 100), border_width: 0 }))); } while ((pinescript.arraySize(state.grpBoxes) > n)) { - box.delete(pinescript.arrayPop(state.grpBoxes)); + pinescript.boxDelete(pinescript.arrayPop(state.grpBoxes)); } } function f_analyticsEmpty() { @@ -1344,10 +1824,10 @@ function main() { } function f_ensureCompositeBodyCount(n) { while ((pinescript.arraySize(state.compositeBodies) < n)) { - pinescript.arrayPush(state.compositeBodies, box.new(bar_index, high, bar_index, low, ({ bgcolor: pinescript.color.new(pinescript.color.gray, 75), border_color: pinescript.color.new(pinescript.color.gray, 60), border_width: 1 }))); + pinescript.arrayPush(state.compositeBodies, pinescript.boxNew(bar_index, high, bar_index, low, ({ bgcolor: pinescript.color.new(pinescript.color.gray, 75), border_color: pinescript.color.new(pinescript.color.gray, 60), border_width: 1 }))); } while ((pinescript.arraySize(state.compositeBodies) > n)) { - box.delete(pinescript.arrayPop(state.compositeBodies)); + pinescript.boxDelete(pinescript.arrayPop(state.compositeBodies)); } } function f_ensureCompositeUpperWickCount(n) { @@ -1381,9 +1861,9 @@ function main() { let DIR_DOWN = -1; let DIR_RANGE = 0; let G_NARRATIVE = "═══ Market Narrative ═══"; - let i_narrativeEnable = input.bool(true, "Enable Market Narrative", ({ group: G_NARRATIVE, tooltip: "Displays -generated market analysis based on calculated data. Educational purposes only." })); - let i_narrativeLang = input.string("English", "Narrative Language", ({ options: ["English", "Türkçe", "हिन्दी", "العربية"], group: G_NARRATIVE, tooltip: "Select language for market narrative output." })); - let i_narrativeShowDisclaimer = input.bool(true, "Show Educational Disclaimer", ({ group: G_NARRATIVE, tooltip: "Display legal disclaimer: Not investment advice, educational purposes only." })); + let i_narrativeEnable = pinescript.inputBool(true, "Enable Market Narrative", ({ group: G_NARRATIVE, tooltip: "Displays -generated market analysis based on calculated data. Educational purposes only." })); + let i_narrativeLang = pinescript.inputString("English", "Narrative Language", ({ options: ["English", "Türkçe", "हिन्दी", "العربية"], group: G_NARRATIVE, tooltip: "Select language for market narrative output." })); + let i_narrativeShowDisclaimer = pinescript.inputBool(true, "Show Educational Disclaimer", ({ group: G_NARRATIVE, tooltip: "Display legal disclaimer: Not investment advice, educational purposes only." })); function f_L(en, tr, hi, ar) { return ((i_narrativeLang === "Türkçe") ? tr : ((i_narrativeLang === "हिन्दी") ? hi : ((i_narrativeLang === "العربية") ? ar : en))); } @@ -1663,16 +2143,16 @@ function main() { let sumRS = 0; let n = pinescript.max(2, period); for (let i = 0; i <= (n - 1); i++) { - let ov = math.log((pinescript.offset(open, i) / pinescript.offset(close, (i + 1)))); + let ov = pinescript.log((pinescript.hist(0, open, i) / pinescript.hist(1, close, (i + 1)))); sumOv += ov; sumOvSq += (ov * ov); - let cl = math.log((pinescript.offset(close, i) / pinescript.offset(open, i))); + let cl = pinescript.log((pinescript.hist(2, close, i) / pinescript.hist(3, open, i))); sumCl += cl; sumClSq += (cl * cl); - let logHO = math.log((pinescript.offset(high, i) / pinescript.offset(open, i))); - let logHC = math.log((pinescript.offset(high, i) / pinescript.offset(close, i))); - let logLO = math.log((pinescript.offset(low, i) / pinescript.offset(open, i))); - let logLC = math.log((pinescript.offset(low, i) / pinescript.offset(close, i))); + let logHO = pinescript.log((pinescript.hist(4, high, i) / pinescript.hist(5, open, i))); + let logHC = pinescript.log((pinescript.hist(6, high, i) / pinescript.hist(7, close, i))); + let logLO = pinescript.log((pinescript.hist(8, low, i) / pinescript.hist(9, open, i))); + let logLC = pinescript.log((pinescript.hist(10, low, i) / pinescript.hist(11, close, i))); sumRS += ((logHO * logHC) + (logLO * logLC)); } let nm1 = float((n - 1)); @@ -1690,7 +2170,7 @@ function main() { } else { let expectedMove = (yzVolPrice * pinescript.sqrt(float(barSpan))); let normalizedChange = ((expectedMove > 0) ? (priceChange / expectedMove) : 0); - math.todegrees(math.atan(normalizedChange)); + pinescript.todegrees(pinescript.atan(normalizedChange)); } } function f_clearAllChannels() { @@ -1788,7 +2268,7 @@ function main() { } } f_lockBlockTrend(segEnd, dir); - let [highestHigh, lowestHigh, highestLow, lowestLow, hhIdx, lhIdx, hlIdx, llIdx] = f_findSegmentExtremes(segStart, segEnd); + let [highestHigh, lowestHigh, highestLow, lowestLow, hhIdx, lhIdx, hlIdx, llIdx] = pinescript.unpack(f_findSegmentExtremes(segStart, segEnd), 8); if ((((!pinescript.na(highestHigh) && !pinescript.na(lowestHigh)) && !pinescript.na(highestLow)) && !pinescript.na(lowestLow))) { let blkOldest = pinescript.arrayGet(state.analyticsData, segEnd); let blkNewest = pinescript.arrayGet(state.analyticsData, segStart); @@ -1878,35 +2358,35 @@ function main() { } let G_VOL = "═══ Volume Engine ═══"; let G_DASH = "═══ Smart Dashboard ═══"; - let i_calcMethod = input.string("Geometry (Approx)", "Calculation Method", ({ options: ["Geometry (Approx)", "Intrabar (Precise)"], group: G_VOL })); - let i_useCustomLTF = input.bool(true, "Use custom lower timeframe", ({ group: G_VOL })); + let i_calcMethod = pinescript.inputString("Geometry (Approx)", "Calculation Method", ({ options: ["Geometry (Approx)", "Intrabar (Precise)"], group: G_VOL })); + let i_useCustomLTF = pinescript.inputBool(true, "Use custom lower timeframe", ({ group: G_VOL })); let i_customLTF = input.timeframe("15S", "Lower timeframe (LTF)", ({ group: G_VOL })); - let i_globalPeriodBoxes = input.int(5, "Global Period (Boxes)", ({ minval: 1, maxval: 200, group: G_VOL })); - let i_showDash = input.bool(true, "Show Dashboard", ({ group: G_DASH })); - let i_dashPos = input.string(pinescript.position.top_right, "Position", ({ options: [pinescript.position.top_right, pinescript.position.bottom_right, pinescript.position.bottom_left], group: G_DASH })); + let i_globalPeriodBoxes = pinescript.inputInt(5, "Global Period (Boxes)", ({ minval: 1, maxval: 200, group: G_VOL })); + let i_showDash = pinescript.inputBool(true, "Show Dashboard", ({ group: G_DASH })); + let i_dashPos = pinescript.inputString(pinescript.position.top_right, "Position", ({ options: [pinescript.position.top_right, pinescript.position.bottom_right, pinescript.position.bottom_left], group: G_DASH })); let G_TREND = "═══ Multi-Segment Trend ═══"; - let i_trendEnable = input.bool(true, "Enable Trend Detection", ({ group: G_TREND, tooltip: "Multi-segment trend system using ghost candle HH/HL/LH/LL pattern detection." })); - let i_rangeAngleThreshold = input.float(10, "Range Angle Threshold (°)", ({ minval: 1, maxval: 45, step: 1, group: G_TREND, tooltip: "Lines with angle ≤ this value are classified as RANGE." })); - let i_trendMinBlocks = input.int(2, "Minimum Blocks per Segment", ({ minval: 2, maxval: 10, group: G_TREND, tooltip: "Minimum blocks required to draw a segment line." })); - let i_showRangeSegments = input.bool(true, "Show Range Segments", ({ group: G_TREND, tooltip: "Draw lines for range/sideways segments." })); - let i_trendUpColor = input.color(pinescript.color.rgb(34, 197, 94), "Uptrend Line Color", ({ group: G_TREND, tooltip: "Color for uptrend support lines (green)." })); - let i_trendDnColor = input.color(pinescript.color.rgb(239, 68, 68), "Downtrend Line Color", ({ group: G_TREND, tooltip: "Color for downtrend resistance lines (red)." })); - let i_trendRangeColor = input.color(pinescript.color.rgb(148, 163, 184), "Range Line Color", ({ group: G_TREND, tooltip: "Color for range/sideways lines (gray)." })); - let i_trendLineWidth = input.int(2, "Trend Line Width", ({ minval: 1, maxval: 5, group: G_TREND })); - let i_trendLineStyle = input.string("solid", "Trend Line Style", ({ options: ["solid", "dashed", "dotted"], group: G_TREND })); - let i_trendProject = input.bool(true, "Project to Future", ({ group: G_TREND, tooltip: "Extend trend lines into the future by one block width." })); + let i_trendEnable = pinescript.inputBool(true, "Enable Trend Detection", ({ group: G_TREND, tooltip: "Multi-segment trend system using ghost candle HH/HL/LH/LL pattern detection." })); + let i_rangeAngleThreshold = pinescript.inputFloat(10, "Range Angle Threshold (°)", ({ minval: 1, maxval: 45, step: 1, group: G_TREND, tooltip: "Lines with angle ≤ this value are classified as RANGE." })); + let i_trendMinBlocks = pinescript.inputInt(2, "Minimum Blocks per Segment", ({ minval: 2, maxval: 10, group: G_TREND, tooltip: "Minimum blocks required to draw a segment line." })); + let i_showRangeSegments = pinescript.inputBool(true, "Show Range Segments", ({ group: G_TREND, tooltip: "Draw lines for range/sideways segments." })); + let i_trendUpColor = pinescript.inputColor(pinescript.color.rgb(34, 197, 94), "Uptrend Line Color", ({ group: G_TREND, tooltip: "Color for uptrend support lines (green)." })); + let i_trendDnColor = pinescript.inputColor(pinescript.color.rgb(239, 68, 68), "Downtrend Line Color", ({ group: G_TREND, tooltip: "Color for downtrend resistance lines (red)." })); + let i_trendRangeColor = pinescript.inputColor(pinescript.color.rgb(148, 163, 184), "Range Line Color", ({ group: G_TREND, tooltip: "Color for range/sideways lines (gray)." })); + let i_trendLineWidth = pinescript.inputInt(2, "Trend Line Width", ({ minval: 1, maxval: 5, group: G_TREND })); + let i_trendLineStyle = pinescript.inputString("solid", "Trend Line Style", ({ options: ["solid", "dashed", "dotted"], group: G_TREND })); + let i_trendProject = pinescript.inputBool(true, "Project to Future", ({ group: G_TREND, tooltip: "Extend trend lines into the future by one block width." })); let G_CORE = "═══ Core Calculation ═══"; - let i_coreEnable = input.bool(true, "Enable Core Calculation", ({ group: G_CORE })); - let i_coreDrawLines = input.bool(true, "Draw Core Lines", ({ group: G_CORE })); - let i_coreProjectFwd = input.bool(true, "Offset / Projection (to Future)", ({ group: G_CORE })); - let i_coreNearestOnly = input.bool(true, "Nearest Only (1 Up / 1 Down)", ({ group: G_CORE })); - let i_coreShowMax = input.bool(true, "Include MAX Buy/Sell Levels", ({ group: G_CORE })); - let i_coreShowMin = input.bool(true, "Include MIN Buy/Sell Levels", ({ group: G_CORE })); - let i_pocEnable = input.bool(true, "Enable POC", ({ group: G_CORE })); - let i_pocShowNearest = input.bool(true, "Show POC Nearest Up/Down", ({ group: G_CORE })); - let i_pocBinProfile = input.bool(true, "POC: Volume Profile Bins (All Plans)", ({ group: G_CORE, tooltip: "True Volume Profile POC — available on ALL TradingView plans (Free, Plus, Premium, Ultimate).nnHow it works: Divides each block's price range into equal bins, distributes each bar's volume proportionally across the bins its range overlaps, and selects the bin with the highest accumulated volume as the Point of Control.nnThis is significantly more accurate than the default single-bar method (which simply uses the average price of the highest-volume bar). When ON, POC reflects the actual high-volume price zone across all bars in the block.nnWhen OFF, the original single-bar method is used as fallback.nnNote: A future update will add native TradingView Footprint POC via request.footprint(), which provides tick-level precision but requires a Premium or Ultimate plan." })); - let i_pocBinCount = input.int(25, "POC Bin Resolution", ({ minval: 10, maxval: 100, group: G_CORE, tooltip: "Number of equal price bins used to build the Volume Profile within each block.nnHigher values = finer price resolution = more precise POC, but heavier computation.nLower values = coarser resolution = faster, but POC may be less precise.nnRecommended ranges:n• 10–15: Fast, suitable for high-timeframe or low-volatility instruments.n• 20–30: Good balance for most instruments and timeframes (default: 25).n• 50–100: High precision for scalping or tick-level analysis on lower timeframes.nnThis setting has no effect when 'POC: Volume Profile Bins' is OFF." })); - let i_coreLineWidth = input.int(1, "Line Width", ({ minval: 1, maxval: 4, group: G_CORE })); + let i_coreEnable = pinescript.inputBool(true, "Enable Core Calculation", ({ group: G_CORE })); + let i_coreDrawLines = pinescript.inputBool(true, "Draw Core Lines", ({ group: G_CORE })); + let i_coreProjectFwd = pinescript.inputBool(true, "Offset / Projection (to Future)", ({ group: G_CORE })); + let i_coreNearestOnly = pinescript.inputBool(true, "Nearest Only (1 Up / 1 Down)", ({ group: G_CORE })); + let i_coreShowMax = pinescript.inputBool(true, "Include MAX Buy/Sell Levels", ({ group: G_CORE })); + let i_coreShowMin = pinescript.inputBool(true, "Include MIN Buy/Sell Levels", ({ group: G_CORE })); + let i_pocEnable = pinescript.inputBool(true, "Enable POC", ({ group: G_CORE })); + let i_pocShowNearest = pinescript.inputBool(true, "Show POC Nearest Up/Down", ({ group: G_CORE })); + let i_pocBinProfile = pinescript.inputBool(true, "POC: Volume Profile Bins (All Plans)", ({ group: G_CORE, tooltip: "True Volume Profile POC — available on ALL TradingView plans (Free, Plus, Premium, Ultimate).nnHow it works: Divides each block's price range into equal bins, distributes each bar's volume proportionally across the bins its range overlaps, and selects the bin with the highest accumulated volume as the Point of Control.nnThis is significantly more accurate than the default single-bar method (which simply uses the average price of the highest-volume bar). When ON, POC reflects the actual high-volume price zone across all bars in the block.nnWhen OFF, the original single-bar method is used as fallback.nnNote: A future update will add native TradingView Footprint POC via request.footprint(), which provides tick-level precision but requires a Premium or Ultimate plan." })); + let i_pocBinCount = pinescript.inputInt(25, "POC Bin Resolution", ({ minval: 10, maxval: 100, group: G_CORE, tooltip: "Number of equal price bins used to build the Volume Profile within each block.nnHigher values = finer price resolution = more precise POC, but heavier computation.nLower values = coarser resolution = faster, but POC may be less precise.nnRecommended ranges:n• 10–15: Fast, suitable for high-timeframe or low-volatility instruments.n• 20–30: Good balance for most instruments and timeframes (default: 25).n• 50–100: High precision for scalping or tick-level analysis on lower timeframes.nnThis setting has no effect when 'POC: Volume Profile Bins' is OFF." })); + let i_coreLineWidth = pinescript.inputInt(1, "Line Width", ({ minval: 1, maxval: 4, group: G_CORE })); let _coreBuyCol = pinescript.color.rgb(59, 130, 246); let _coreSellCol = pinescript.color.rgb(244, 63, 94); let _pocCol = pinescript.color.rgb(234, 179, 8); @@ -1971,15 +2451,15 @@ function main() { } function f_hideLine(l, x, y) { if (!pinescript.na(l)) { - line.set_xy1(l, x, y); - line.set_xy2(l, x, y); + pinescript.lineSetXY(l, x, y); + pinescript.lineSetXY(l, x, y); line.set_color(l, pinescript.color.new(pinescript.color.gray, 100)); } } function f_setHLine(l, x1, x2, y, col) { if (!pinescript.na(l)) { - line.set_xy1(l, x1, y); - line.set_xy2(l, x2, y); + pinescript.lineSetXY(l, x1, y); + pinescript.lineSetXY(l, x2, y); line.set_color(l, col); line.set_width(l, i_coreLineWidth); } @@ -1998,7 +2478,7 @@ function main() { } function f_hideLabel(lb) { if (!pinescript.na(lb)) { - label.set_text(lb, ""); + pinescript.labelSetText(lb, ""); label.set_xy(lb, bar_index, close); label.set_textcolor(lb, pinescript.color.new(pinescript.color.gray, 100)); } @@ -2006,7 +2486,7 @@ function main() { function f_setLineLabel(lb, x, y, txt, col) { if (!pinescript.na(lb)) { label.set_xy(lb, x, y); - label.set_text(lb, txt); + pinescript.labelSetText(lb, txt); label.set_textcolor(lb, col); label.set_style(lb, label.style_label_left); label.set_size(lb, GLOBAL_TEXT_SIZE); @@ -2231,7 +2711,7 @@ function main() { let sumVol = 0; let count = 0; for (let i = offset; i <= ((offset + period) - 1); i++) { - let v = pinescript.offset(volume, i); + let v = pinescript.hist(12, volume, i); if (!pinescript.na(v)) { sumVol += v; count += 1; @@ -2252,11 +2732,11 @@ function main() { } } function f_analyzeCurrentBar(offset, atr, buyV, sellV) { - let h = pinescript.offset(high, offset); - let l = pinescript.offset(low, offset); - let o = pinescript.offset(open, offset); - let c = pinescript.offset(close, offset); - let v = pinescript.offset(volume, offset); + let h = pinescript.hist(13, high, offset); + let l = pinescript.hist(14, low, offset); + let o = pinescript.hist(15, open, offset); + let c = pinescript.hist(16, close, offset); + let v = pinescript.hist(17, volume, offset); let barRange = (h - l); let bodySize = pinescript.abs((c - o)); let upperWick = (h - pinescript.max(o, c)); @@ -2265,9 +2745,9 @@ function main() { let upperR = ((barRange > 0) ? ((upperWick / barRange) * 100) : 0); let lowerR = ((barRange > 0) ? ((lowerWick / barRange) * 100) : 0); let isBull = (c > o); - let [pattern, patternBias] = f_classifyCandlePattern(bodyR, upperR, lowerR, isBull); - let [rangeRatio, rangeStatus] = f_calcRangeStatus(barRange, atr); - let [volRatio, volStatus] = f_calcVolumeStatus(v, 20, offset); + let [pattern, patternBias] = pinescript.unpack(f_classifyCandlePattern(bodyR, upperR, lowerR, isBull), 2); + let [rangeRatio, rangeStatus] = pinescript.unpack(f_calcRangeStatus(barRange, atr), 2); + let [volRatio, volStatus] = pinescript.unpack(f_calcVolumeStatus(v, 20, offset), 2); let delta = (buyV - sellV); let pressure = f_calcPressureStatus(buyV, sellV); return CurrentBarAnalysis.new(barRange, bodySize, upperWick, lowerWick, bodyR, upperR, lowerR, pattern, patternBias, isBull, rangeRatio, rangeStatus, volRatio, volStatus, buyV, sellV, delta, pressure); @@ -2283,7 +2763,7 @@ function main() { let cUpperR = ((cRange > 0) ? ((cUpper / cRange) * 100) : 0); let cLowerR = ((cRange > 0) ? ((cLower / cRange) * 100) : 0); let isBull = (blk1.newClose > blk1.newOpen); - [pattern, bias] = f_classifyCandlePattern(cBodyR, cUpperR, cLowerR, isBull); + [pattern, bias] = pinescript.unpack(f_classifyCandlePattern(cBodyR, cUpperR, cLowerR, isBull), 2); result.compRange = cRange; result.compBodySize = cBody; result.compUpperWick = cUpper; @@ -2428,7 +2908,7 @@ function main() { } function f_buildProfessionalNarrativeParts(cBar, comp, ctx, ch, trendBuy, trendSell, trendDelta, trendDeltaPct, bodyStatus, upperPinChg, lowerPinChg, volMom, channelPos, posStatus, resistPrice, supportPrice, lvls, contradictions, hasDivergence) { let narrative = ""; - let currentPrice = pinescript.offset(close, baseOff); + let currentPrice = pinescript.hist(18, close, baseOff); narrative += (((f_connect("CURRENT_CANDLE") + " ") + f_patternDesc(cBar.pattern)) + " "); narrative += (((f_connect("WITH_RANGE") + " ") + f_rangeStatusStr(cBar.rangeStatus)) + ". "); if ((cBar.pressureStatus === "BUYING")) { @@ -2535,12 +3015,12 @@ function main() { let pocDnLevel = (lvls.hasPOCDn ? lvls.pocDn : null); let coreUpLevel = (lvls.hasCoreUp ? lvls.coreUp : null); let coreDnLevel = (lvls.hasCoreDn ? lvls.coreDn : null); - let [nearResist, resistPct] = f_checkProximity(currentPrice, resistPrice, 2); - let [nearSupport, supportPct] = f_checkProximity(currentPrice, supportPrice, 2); - let [nearPOCUp, pocUpPct] = f_checkProximity(currentPrice, pocUpLevel, 1.5); - let [nearPOCDn, pocDnPct] = f_checkProximity(currentPrice, pocDnLevel, 1.5); - let [nearCoreUp, coreUpPct] = f_checkProximity(currentPrice, coreUpLevel, 1); - let [nearCoreDn, coreDnPct] = f_checkProximity(currentPrice, coreDnLevel, 1); + let [nearResist, resistPct] = pinescript.unpack(f_checkProximity(currentPrice, resistPrice, 2), 2); + let [nearSupport, supportPct] = pinescript.unpack(f_checkProximity(currentPrice, supportPrice, 2), 2); + let [nearPOCUp, pocUpPct] = pinescript.unpack(f_checkProximity(currentPrice, pocUpLevel, 1.5), 2); + let [nearPOCDn, pocDnPct] = pinescript.unpack(f_checkProximity(currentPrice, pocDnLevel, 1.5), 2); + let [nearCoreUp, coreUpPct] = pinescript.unpack(f_checkProximity(currentPrice, coreUpLevel, 1), 2); + let [nearCoreDn, coreDnPct] = pinescript.unpack(f_checkProximity(currentPrice, coreDnLevel, 1), 2); let hasKeyEvent = (((((nearResist || nearSupport) || nearPOCUp) || nearPOCDn) || nearCoreUp) || nearCoreDn); if (hasKeyEvent) { narrative += (f_connect("KEY_NOTE") + ": "); @@ -2572,14 +3052,14 @@ function main() { let dUp = (!pinescript.na(pocUpLevel) ? pinescript.abs((currentPrice - pocUpLevel)) : null); let dDn = (!pinescript.na(pocDnLevel) ? pinescript.abs((currentPrice - pocDnLevel)) : null); let nearestPOC = (pinescript.na(dUp) ? pocDnLevel : (pinescript.na(dDn) ? pocUpLevel : ((dUp <= dDn) ? pocUpLevel : pocDnLevel))); - let [, pocPct] = f_checkProximity(currentPrice, nearestPOC, 100); + let [, pocPct] = pinescript.unpack(f_checkProximity(currentPrice, nearestPOC, 100), 2); narrative += ((((f_L(" Nearest POC: ", " En yakın POC: ", " निकटतम POC: ", " أقرب POC: ") + pinescript.strToString(nearestPOC, format.mintick)) + " (") + pinescript.strToString(pocPct, "#.##")) + "%). "); } if ((!pinescript.na(coreUpLevel) || !pinescript.na(coreDnLevel))) { let cdUp = (!pinescript.na(coreUpLevel) ? pinescript.abs((currentPrice - coreUpLevel)) : null); let cdDn = (!pinescript.na(coreDnLevel) ? pinescript.abs((currentPrice - coreDnLevel)) : null); let nearestCore = (pinescript.na(cdUp) ? coreDnLevel : (pinescript.na(cdDn) ? coreUpLevel : ((cdUp <= cdDn) ? coreUpLevel : coreDnLevel))); - let [, corePct] = f_checkProximity(currentPrice, nearestCore, 100); + let [, corePct] = pinescript.unpack(f_checkProximity(currentPrice, nearestCore, 100), 2); narrative += ((((f_L(" Nearest core level: ", " En yakın çekirdek seviye: ", " निकटतम कोर स्तर: ", " أقرب مستوى جوهري: ") + pinescript.strToString(nearestCore, format.mintick)) + " (") + pinescript.strToString(corePct, "#.##")) + "%). "); } } @@ -2623,7 +3103,7 @@ function main() { return (useCustom ? customTF : (timeframe.isseconds ? "1S" : (timeframe.isintraday ? "1" : (timeframe.isdaily ? "5" : "60")))); } function f_ltfVol(ltf) { - let [u, d, dl] = tvta.requestUpAndDownVolume(ltf); + let [u, d, dl] = pinescript.unpack(tvta.requestUpAndDownVolume(ltf), 3); return [pinescript.abs(u), pinescript.abs(d), dl]; } function f_geoVol() { @@ -2649,12 +3129,12 @@ function main() { if (state.vDel === undefined) state.vDel = null; let ltf = f_resolveLTF(i_useCustomLTF, i_customLTF); if ((i_calcMethod === "Intrabar (Precise)")) { - let [b1, s1, d1] = f_ltfVol(ltf); + let [b1, s1, d1] = pinescript.unpack(f_ltfVol(ltf), 3); state.vBuy = b1; state.vSell = s1; state.vDel = d1; } else { - let [b2, s2, d2] = f_geoVol(); + let [b2, s2, d2] = pinescript.unpack(f_geoVol(), 3); state.vBuy = b2; state.vSell = s2; state.vDel = d2; @@ -2663,8 +3143,8 @@ function main() { let blockHigh = null; let blockLow = null; for (let k = startOff; k <= endOff; k++) { - let h = pinescript.offset(high, k); - let l = pinescript.offset(low, k); + let h = pinescript.hist(19, high, k); + let l = pinescript.hist(20, low, k); if ((pinescript.na(blockHigh) || (h > blockHigh))) { blockHigh = h; } @@ -2679,12 +3159,12 @@ function main() { let binSize = ((blockHigh - blockLow) / numBins); let binVol = pinescript.arrayNew(numBins, 0); for (let k = startOff; k <= endOff; k++) { - let barH = pinescript.offset(high, k); - let barL = pinescript.offset(low, k); - let barTotalVol = (pinescript.nz(pinescript.offset(state.vBuy, k), 0) + pinescript.nz(pinescript.offset(state.vSell, k), 0)); + let barH = pinescript.hist(21, high, k); + let barL = pinescript.hist(22, low, k); + let barTotalVol = (pinescript.nz(pinescript.hist(23, state.vBuy, k), 0) + pinescript.nz(pinescript.hist(24, state.vSell, k), 0)); if (((barTotalVol > 0) && (barH > barL))) { - let firstBin = pinescript.max(0, int(math.floor(((barL - blockLow) / binSize)))); - let lastBin = pinescript.min((numBins - 1), int(math.floor((((barH - blockLow) - (syminfo.mintick * 0.01)) / binSize)))); + let firstBin = pinescript.max(0, int(pinescript.floor(((barL - blockLow) / binSize)))); + let lastBin = pinescript.min((numBins - 1), int(pinescript.floor((((barH - blockLow) - (syminfo.mintick * 0.01)) / binSize)))); lastBin = pinescript.max(firstBin, lastBin); let barRange = (barH - barL); for (let b = firstBin; b <= lastBin; b++) { @@ -2709,7 +3189,7 @@ function main() { resultVol = maxVol; let minDist = null; for (let k = startOff; k <= endOff; k++) { - let dist = pinescript.abs((pinescript.offset(hlc3, k) - resultPrice)); + let dist = pinescript.abs((pinescript.hist(25, hlc3, k) - resultPrice)); if ((pinescript.na(minDist) || (dist < minDist))) { minDist = dist; resultOff = k; @@ -2718,7 +3198,7 @@ function main() { } return [resultPrice, resultOff, resultVol]; } - let globalAvgRange = ta.atr(20); + let globalAvgRange = pinescript.atr(20); let globalYZVol = f_yangZhangVolatility(20); let histBarsDash = pinescript.max(0, ((bar_index - baseOff) + 1)); histBarsDash = pinescript.min(histBarsDash, 5000); @@ -2726,7 +3206,7 @@ function main() { if ((i_calcMethod === "Intrabar (Precise)")) { let probe = pinescript.min(histBarsDash, effWindow); for (let off = baseOff; off <= ((baseOff + probe) - 1); off++) { - let ok = ((!pinescript.na(pinescript.offset(state.vBuy, off)) && !pinescript.na(pinescript.offset(state.vSell, off))) && !pinescript.na(pinescript.offset(state.vDel, off))); + let ok = ((!pinescript.na(pinescript.hist(26, state.vBuy, off)) && !pinescript.na(pinescript.hist(27, state.vSell, off))) && !pinescript.na(pinescript.hist(28, state.vDel, off))); if (ok) { scannableBars += 1; } else { @@ -2754,8 +3234,8 @@ function main() { for (let gi = 0; gi <= (i_groups - 1); gi++) { let endOff = ((baseOff + ((gi + 1) * groupSize)) - 1); let lb = pinescript.arrayGet(state.lblNums, gi); - label.set_xy(lb, (bar_index - endOff), pinescript.offset(high, endOff)); - label.set_text(lb, pinescript.strToString((gi + 1))); + label.set_xy(lb, (bar_index - endOff), pinescript.hist(29, high, endOff)); + pinescript.labelSetText(lb, pinescript.strToString((gi + 1))); } } else { f_ensureLabelCount(0); @@ -2782,8 +3262,8 @@ function main() { let xRight = (bar_index - startOff); let xLeft = (bar_index - endOff); let bx = pinescript.arrayGet(state.grpBoxes, gi); - box.set_lefttop(bx, xLeft, pinescript.offset(high, endOff)); - box.set_rightbottom(bx, xRight, pinescript.offset(low, startOff)); + box.set_lefttop(bx, xLeft, pinescript.hist(30, high, endOff)); + box.set_rightbottom(bx, xRight, pinescript.hist(31, low, startOff)); continue; } let startOff = (baseOff + (gi * groupSize)); @@ -2800,18 +3280,18 @@ function main() { let compLow = null; let compClose = null; for (let k = startOff; k <= endOff; k++) { - let h = pinescript.offset(high, k); - let l = pinescript.offset(low, k); - let o = pinescript.offset(open, k); - let c = pinescript.offset(close, k); + let h = pinescript.hist(32, high, k); + let l = pinescript.hist(33, low, k); + let o = pinescript.hist(34, open, k); + let c = pinescript.hist(35, close, k); if ((pinescript.na(top) || (h > top))) { top = h; } if ((pinescript.na(bot) || (l < bot))) { bot = l; } - let bVol = pinescript.offset(state.vBuy, k); - let sVol = pinescript.offset(state.vSell, k); + let bVol = pinescript.hist(36, state.vBuy, k); + let sVol = pinescript.hist(37, state.vSell, k); if ((!pinescript.na(bVol) && !pinescript.na(sVol))) { sumBuyBlock += bVol; sumSellBlock += sVol; @@ -2863,13 +3343,13 @@ function main() { box.set_border_color(bodyBox, borderCol); box.set_border_width(bodyBox, 1); let upperWick = pinescript.arrayGet(state.compositeUpperWicks, gi); - line.set_xy1(upperWick, xCenter, bodyTop); - line.set_xy2(upperWick, xCenter, compHigh); + pinescript.lineSetXY(upperWick, xCenter, bodyTop); + pinescript.lineSetXY(upperWick, xCenter, compHigh); line.set_color(upperWick, wickCol); line.set_width(upperWick, 1); let lowerWick = pinescript.arrayGet(state.compositeLowerWicks, gi); - line.set_xy1(lowerWick, xCenter, bodyBot); - line.set_xy2(lowerWick, xCenter, compLow); + pinescript.lineSetXY(lowerWick, xCenter, bodyBot); + pinescript.lineSetXY(lowerWick, xCenter, compLow); line.set_color(lowerWick, wickCol); line.set_width(lowerWick, 1); } @@ -2898,8 +3378,8 @@ function main() { let _blockHigh = null; let _blockLow = null; for (let k = _sOff; k <= _eOff; k++) { - let _h = pinescript.offset(high, k); - let _l = pinescript.offset(low, k); + let _h = pinescript.hist(38, high, k); + let _l = pinescript.hist(39, low, k); if ((pinescript.na(_blockHigh) || (_h > _blockHigh))) { _blockHigh = _h; } @@ -2914,12 +3394,12 @@ function main() { let _binSize = ((_blockHigh - _blockLow) / i_pocBinCount); let _binVol = pinescript.arrayNew(i_pocBinCount, 0); for (let k = _sOff; k <= _eOff; k++) { - let _barH = pinescript.offset(high, k); - let _barL = pinescript.offset(low, k); - let _barTotalVol = (pinescript.nz(pinescript.offset(state.vBuy, k), 0) + pinescript.nz(pinescript.offset(state.vSell, k), 0)); + let _barH = pinescript.hist(40, high, k); + let _barL = pinescript.hist(41, low, k); + let _barTotalVol = (pinescript.nz(pinescript.hist(42, state.vBuy, k), 0) + pinescript.nz(pinescript.hist(43, state.vSell, k), 0)); if (((_barTotalVol > 0) && (_barH > _barL))) { - let _firstBin = pinescript.max(0, int(math.floor(((_barL - _blockLow) / _binSize)))); - let _lastBin = pinescript.min((i_pocBinCount - 1), int(math.floor((((_barH - _blockLow) - (syminfo.mintick * 0.01)) / _binSize)))); + let _firstBin = pinescript.max(0, int(pinescript.floor(((_barL - _blockLow) / _binSize)))); + let _lastBin = pinescript.min((i_pocBinCount - 1), int(pinescript.floor((((_barH - _blockLow) - (syminfo.mintick * 0.01)) / _binSize)))); _lastBin = pinescript.max(_firstBin, _lastBin); let _barRange = (_barH - _barL); for (let b = _firstBin; b <= _lastBin; b++) { @@ -2944,7 +3424,7 @@ function main() { _bv = _maxVol; let _minDist = null; for (let k = _sOff; k <= _eOff; k++) { - let _dist = pinescript.abs((pinescript.offset(hlc3, k) - _bp)); + let _dist = pinescript.abs((pinescript.hist(44, hlc3, k) - _bp)); if ((pinescript.na(_minDist) || (_dist < _minDist))) { _minDist = _dist; _bo = k; @@ -2961,9 +3441,9 @@ function main() { let refX = (bar_index - baseOff); let projLen = (i_coreProjectFwd ? groupSize : 0); let x2 = (refX + projLen); - let refPrice = pinescript.offset(close, baseOff); - let hiBasis = pinescript.offset(high, baseOff); - let loBasis = pinescript.offset(low, baseOff); + let refPrice = pinescript.hist(45, close, baseOff); + let hiBasis = pinescript.hist(46, high, baseOff); + let loBasis = pinescript.hist(47, low, baseOff); let minKeepSrc = (bar_index - ((effWindow + groupSize) + 25)); f_pruneConsumed(minKeepSrc); state.lnCoreUp = f_ensureLine(state.lnCoreUp, _coreBuyCol, line.style_solid); @@ -2982,7 +3462,7 @@ function main() { } else { f_ensureCoreCount(i_groups); let validBarsCore = ((i_calcMethod === "Intrabar (Precise)") ? scannableBars : pinescript.min(histBarsDash, effWindow)); - let validGroupsCore = ((groupSize > 0) ? int(math.floor((float(validBarsCore) / groupSize))) : 0); + let validGroupsCore = ((groupSize > 0) ? int(pinescript.floor((float(validBarsCore) / groupSize))) : 0); validGroupsCore = pinescript.min(validGroupsCore, i_groups); if (!i_coreEnable) { for (let gi = 0; gi <= (i_groups - 1); gi++) { @@ -3013,8 +3493,8 @@ function main() { let pocV = -1; let pocOff = null; for (let k = startOff; k <= endOff; k++) { - let b = pinescript.offset(state.vBuy, k); - let s = pinescript.offset(state.vSell, k); + let b = pinescript.hist(48, state.vBuy, k); + let s = pinescript.hist(49, state.vSell, k); if ((pinescript.na(b) || pinescript.na(s))) { gValid = false; } else { @@ -3046,7 +3526,7 @@ function main() { if ((((((!gValid || pinescript.na(maxBOff)) || pinescript.na(maxSOff)) || pinescript.na(minBOff)) || pinescript.na(minSOff)) || pinescript.na(pocOff))) { pinescript.arraySet(state.coreBoxes, gi, f_coreEmpty()); } else { - let finalPocPrice = pinescript.offset(hlc3, pocOff); + let finalPocPrice = pinescript.hist(50, hlc3, pocOff); let finalPocOff = pocOff; let finalPocVol = pocV; if ((i_pocBinProfile && !pinescript.na(_bpP))) { @@ -3054,7 +3534,7 @@ function main() { finalPocOff = pinescript.nz(_bpOff, pocOff); finalPocVol = _bpV; } - let bc = BoxCore.new(true, sumB, sumS, (sumB - sumS), finalPocPrice, finalPocOff, finalPocVol, maxB, maxBOff, pinescript.offset(high, maxBOff), maxS, maxSOff, pinescript.offset(low, maxSOff), minB, minBOff, pinescript.offset(high, minBOff), minS, minSOff, pinescript.offset(low, minSOff)); + let bc = BoxCore.new(true, sumB, sumS, (sumB - sumS), finalPocPrice, finalPocOff, finalPocVol, maxB, maxBOff, pinescript.hist(51, high, maxBOff), maxS, maxSOff, pinescript.hist(52, low, maxSOff), minB, minBOff, pinescript.hist(53, high, minBOff), minS, minSOff, pinescript.hist(54, low, minSOff)); pinescript.arraySet(state.coreBoxes, gi, bc); } } @@ -3070,7 +3550,7 @@ function main() { let extDnTyp = null; let extDnBox = null; if ((showCoreNearest && i_coreEnable)) { - let [_eUpP, _eUpSrc, _eUpTyp, _eUpBox, _eDnP, _eDnSrc, _eDnTyp, _eDnBox] = f_pickNearestExt(refPrice); + let [_eUpP, _eUpSrc, _eUpTyp, _eUpBox, _eDnP, _eDnSrc, _eDnTyp, _eDnBox] = pinescript.unpack(f_pickNearestExt(refPrice), 8); extUpP = _eUpP; extUpSrc = _eUpSrc; extUpTyp = _eUpTyp; @@ -3088,7 +3568,7 @@ function main() { let pocDnSrc = null; let pocDnBox = null; if (showPOCNearest) { - let [_pUpP, _pUpSrc, _pUpBox, _pDnP, _pDnSrc, _pDnBox] = f_pickNearestPOC(refPrice); + let [_pUpP, _pUpSrc, _pUpBox, _pDnP, _pDnSrc, _pDnBox] = pinescript.unpack(f_pickNearestPOC(refPrice), 6); pocUpP = _pUpP; pocUpSrc = _pUpSrc; pocUpBox = _pUpBox; @@ -3123,7 +3603,7 @@ function main() { } if (changed) { if (showCoreNearest) { - let [_extUpP, _extUpSrc, _extUpTyp, _extUpBox, _extDnP, _extDnSrc, _extDnTyp, _extDnBox] = f_pickNearestExt(refPrice); + let [_extUpP, _extUpSrc, _extUpTyp, _extUpBox, _extDnP, _extDnSrc, _extDnTyp, _extDnBox] = pinescript.unpack(f_pickNearestExt(refPrice), 8); extUpP = _extUpP; extUpSrc = _extUpSrc; extUpTyp = _extUpTyp; @@ -3134,7 +3614,7 @@ function main() { extDnBox = _extDnBox; } if (showPOCNearest) { - let [_pocUpP, _pocUpSrc, _pocUpBox, _pocDnP, _pocDnSrc, _pocDnBox] = f_pickNearestPOC(refPrice); + let [_pocUpP, _pocUpSrc, _pocUpBox, _pocDnP, _pocDnSrc, _pocDnBox] = pinescript.unpack(f_pickNearestPOC(refPrice), 6); pocUpP = _pocUpP; pocUpSrc = _pocUpSrc; pocUpBox = _pocUpBox; @@ -3202,9 +3682,9 @@ function main() { let lowerX2Proj = (latestCh.lowerX2 + projLen); let upperY2Proj = (latestCh.upperY2 + (upperSlope * projLen)); let lowerY2Proj = (latestCh.lowerY2 + (lowerSlope * projLen)); - let upperAngleDeg = (((upperBarSpan > 0) && (globalYZVol > 0)) ? math.todegrees(math.atan(((upperSlope * pinescript.sqrt(float(upperBarSpan))) / globalYZVol))) : 0); - let lowerAngleDeg = (((lowerBarSpan > 0) && (globalYZVol > 0)) ? math.todegrees(math.atan(((lowerSlope * pinescript.sqrt(float(lowerBarSpan))) / globalYZVol))) : 0); - let currentPrice = pinescript.offset(close, baseOff); + let upperAngleDeg = (((upperBarSpan > 0) && (globalYZVol > 0)) ? pinescript.todegrees(pinescript.atan(((upperSlope * pinescript.sqrt(float(upperBarSpan))) / globalYZVol))) : 0); + let lowerAngleDeg = (((lowerBarSpan > 0) && (globalYZVol > 0)) ? pinescript.todegrees(pinescript.atan(((lowerSlope * pinescript.sqrt(float(lowerBarSpan))) / globalYZVol))) : 0); + let currentPrice = pinescript.hist(55, close, baseOff); let resistDiff = (upperY2Proj - currentPrice); let supportDiff = (currentPrice - lowerY2Proj); let resistPct = ((currentPrice !== 0) ? ((resistDiff / currentPrice) * 100) : 0); @@ -3217,7 +3697,7 @@ function main() { state.lblTrendResist = pinescript.labelNew(upperX2Proj, upperY2Proj, upperText, ({ style: label.style_label_left, size: GLOBAL_TEXT_SIZE, textcolor: chCol, color: pinescript.color.new(pinescript.color.black, 85), tooltip: "Upper channel line: Block range | Price | Distance | Angle" })); } else { label.set_xy(state.lblTrendResist, upperX2Proj, upperY2Proj); - label.set_text(state.lblTrendResist, upperText); + pinescript.labelSetText(state.lblTrendResist, upperText); label.set_textcolor(state.lblTrendResist, chCol); label.set_size(state.lblTrendResist, GLOBAL_TEXT_SIZE); label.set_tooltip(state.lblTrendResist, "Upper channel line: Block range | Price | Distance | Angle"); @@ -3226,7 +3706,7 @@ function main() { state.lblTrendSupport = pinescript.labelNew(lowerX2Proj, lowerY2Proj, lowerText, ({ style: label.style_label_left, size: GLOBAL_TEXT_SIZE, textcolor: chCol, color: pinescript.color.new(pinescript.color.black, 85), tooltip: "Lower channel line: Block range | Price | Distance | Angle" })); } else { label.set_xy(state.lblTrendSupport, lowerX2Proj, lowerY2Proj); - label.set_text(state.lblTrendSupport, lowerText); + pinescript.labelSetText(state.lblTrendSupport, lowerText); label.set_textcolor(state.lblTrendSupport, chCol); label.set_size(state.lblTrendSupport, GLOBAL_TEXT_SIZE); label.set_tooltip(state.lblTrendSupport, "Lower channel line: Block range | Price | Distance | Angle"); @@ -3234,20 +3714,20 @@ function main() { } else { if (!pinescript.na(state.lblTrendResist)) { label.set_xy(state.lblTrendResist, bar_index, close); - label.set_text(state.lblTrendResist, ""); + pinescript.labelSetText(state.lblTrendResist, ""); label.set_textcolor(state.lblTrendResist, pinescript.color.new(pinescript.color.gray, 100)); } if (!pinescript.na(state.lblTrendSupport)) { label.set_xy(state.lblTrendSupport, bar_index, close); - label.set_text(state.lblTrendSupport, ""); + pinescript.labelSetText(state.lblTrendSupport, ""); label.set_textcolor(state.lblTrendSupport, pinescript.color.new(pinescript.color.gray, 100)); } } } } } - let maxBoxesByHist = ((groupSize > 0) ? int(math.floor((float(pinescript.min(histBarsDash, effWindow)) / groupSize))) : 0); - let maxBoxesByScan = ((groupSize > 0) ? int(math.floor((float(scannableBars) / groupSize))) : 0); + let maxBoxesByHist = ((groupSize > 0) ? int(pinescript.floor((float(pinescript.min(histBarsDash, effWindow)) / groupSize))) : 0); + let maxBoxesByScan = ((groupSize > 0) ? int(pinescript.floor((float(scannableBars) / groupSize))) : 0); let maxBoxesAllowedDash = pinescript.max(0, pinescript.min(i_groups, maxBoxesByHist)); if ((i_calcMethod === "Intrabar (Precise)")) { maxBoxesAllowedDash = pinescript.min(maxBoxesAllowedDash, maxBoxesByScan); @@ -3336,7 +3816,7 @@ function main() { let deltaDir = ((!pinescript.na(firstBlockDelta) && !pinescript.na(lastBlockDelta)) ? ((lastBlockDelta > firstBlockDelta) ? "POSITIVE ▲" : "NEGATIVE ▼") : "-"); let pinRatio = (((!pinescript.na(avgUpperPin) && !pinescript.na(avgLowerPin)) && (avgLowerPin !== 0)) ? (avgUpperPin / avgLowerPin) : 1); let control = ((trendDelta > 0) ? "BUYERS" : ((trendDelta < 0) ? "SELLERS" : "BALANCED")); - let currentPrice = pinescript.offset(close, baseOff); + let currentPrice = pinescript.hist(56, close, baseOff); let projLenCh = ((trendValid && i_trendProject) ? (groupSize + 2) : 0); let upperChPrice = null; let lowerChPrice = null; @@ -3373,7 +3853,7 @@ function main() { let d2 = pinescript.arrayGet(state.analyticsData, (i + 1)).delta; if ((!pinescript.na(d1) && !pinescript.na(d2))) { totalPairs += 1; - if ((math.sign(d1) === math.sign(d2))) { + if ((pinescript.sign(d1) === pinescript.sign(d2))) { consistentPairs += 1; } } @@ -3431,21 +3911,21 @@ function main() { let posCol = pinescript.color.rgb(34, 197, 94); let negCol = pinescript.color.rgb(239, 68, 68); let accentCol = pinescript.color.rgb(59, 130, 246); - let cBuy = pinescript.offset(state.vBuy, baseOff); - let cSell = pinescript.offset(state.vSell, baseOff); + let cBuy = pinescript.hist(57, state.vBuy, baseOff); + let cSell = pinescript.hist(58, state.vSell, baseOff); let cTot = (cBuy + cSell); let cDel = (cBuy - cSell); let delCol = (pinescript.na(cDel) ? txtVal : ((cDel >= 0) ? posCol : negCol)); let _alignL = ((i_narrativeLang === "العربية") ? pinescript.text.align_right : pinescript.text.align_left); let basisTitle = ((baseOff === 0) ? f_L("CURRENT", "MEVCUT", "वर्तमान", "الحالية") : f_L("CLOSED", "KAPALI", "बंद", "المغلقة")); let engTxt = ((i_calcMethod === "Intrabar (Precise)") ? (((f_dashIntrabar() + " ") + ltf) + ")") : f_dashGeometric()); - table.merge_cells(state.dashT, 0, 0, 3, 0); + pinescript.tableMergeCells(state.dashT, 0, 0, 3, 0); pinescript.table.cell(state.dashT, 0, 0, f_dashVolumeEngine(), ({ text_color: txtHdr, bgcolor: hdrBg, text_size: GLOBAL_TEXT_SIZE, text_halign: pinescript.text.align_center, tooltip: f_dashTooltipEngine() })); pinescript.table.cell(state.dashT, 0, 1, f_dashEngine(), ({ text_color: txtLbl, bgcolor: rowBg1, text_size: GLOBAL_TEXT_SIZE, text_halign: pinescript.text.align_right, tooltip: f_L("Volume calculation method", "Hacim hesaplama yöntemi", "वॉल्यूम गणना विधि", "طريقة حساب الحجم") })); pinescript.table.cell(state.dashT, 1, 1, engTxt, ({ text_color: accentCol, bgcolor: rowBg1, text_size: GLOBAL_TEXT_SIZE, text_halign: _alignL, tooltip: f_dashTooltipMethod() })); pinescript.table.cell(state.dashT, 2, 1, f_dashData(), ({ text_color: txtLbl, bgcolor: rowBg1, text_size: GLOBAL_TEXT_SIZE, text_halign: pinescript.text.align_right, tooltip: f_dashTooltipData() })); pinescript.table.cell(state.dashT, 3, 1, ((pinescript.strToString(scannableBars) + " ") + f_dashBar()), ({ text_color: txtVal, bgcolor: rowBg1, text_size: GLOBAL_TEXT_SIZE, text_halign: _alignL, tooltip: f_dashTooltipScannable() })); - table.merge_cells(state.dashT, 0, 2, 3, 2); + pinescript.tableMergeCells(state.dashT, 0, 2, 3, 2); let candleHeader = ((baseOff === 0) ? f_dashCurrentCandle() : f_dashClosedCandle()); let candleTooltip = ((f_L("Most recent ", "En son ", "सबसे हाल का ", "أحدث ") + ((baseOff === 0) ? f_L("live", "canlı", "लाइव", "مباشرة") : f_L("closed", "kapalı", "बंद", "مغلقة"))) + f_L(" candle analysis", " mum analizi", " कैंडल विश्लेषण", " تحليل الشمعة")); pinescript.table.cell(state.dashT, 0, 2, candleHeader, ({ text_color: txtHdr, bgcolor: hdrBg, text_size: GLOBAL_TEXT_SIZE, text_halign: pinescript.text.align_center, tooltip: candleTooltip })); @@ -3459,7 +3939,7 @@ function main() { let deltaSign = (pinescript.na(cDel) ? "" : ((cDel > 0) ? "+" : "")); pinescript.table.cell(state.dashT, 2, 4, ((deltaIcon + " ") + f_dashDelta()), ({ text_color: delCol, bgcolor: rowBg2, text_size: GLOBAL_TEXT_SIZE, text_halign: pinescript.text.align_right, tooltip: f_dashTooltipDelta() })); pinescript.table.cell(state.dashT, 3, 4, (deltaSign + f_fmtVol(cDel)), ({ text_color: delCol, bgcolor: rowBg2, text_size: GLOBAL_TEXT_SIZE, text_halign: _alignL })); - table.merge_cells(state.dashT, 0, 5, 3, 5); + pinescript.tableMergeCells(state.dashT, 0, 5, 3, 5); let trendTitle = (trendValid ? ((((((f_dashTrendVolumetrics() + " • ") + f_L("Blk", "Blk", "ब्लॉक", "كتلة")) + " ") + pinescript.strToString(latestTrend.startBlock)) + "-") + pinescript.strToString(latestTrend.endBlock)) : (f_dashTrendVolumetrics() + " • N/A")); pinescript.table.cell(state.dashT, 0, 5, trendTitle, ({ text_color: txtHdr, bgcolor: hdrBg, text_size: GLOBAL_TEXT_SIZE, text_halign: pinescript.text.align_center, tooltip: f_dashTooltipTrendVolume() })); pinescript.table.cell(state.dashT, 0, 6, f_dashBuy(), ({ text_color: posCol, bgcolor: rowBg1, text_size: GLOBAL_TEXT_SIZE, text_halign: pinescript.text.align_center, tooltip: f_dashTooltipTrendBuy() })); @@ -3472,7 +3952,7 @@ function main() { pinescript.table.cell(state.dashT, 2, 7, trendDeltaStr, ({ text_color: ((trendDelta >= 0) ? posCol : negCol), bgcolor: rowBg2, text_size: GLOBAL_TEXT_SIZE, text_halign: pinescript.text.align_center })); let trendTypeName = (trendValid ? ((latestTrend.channelType === DIR_UP) ? f_dashUptrend() : ((latestTrend.channelType === DIR_DOWN) ? f_dashDowntrend() : f_dashRange())) : "-"); pinescript.table.cell(state.dashT, 3, 7, trendTypeName, ({ text_color: trendPrimaryCol, bgcolor: rowBg2, text_size: GLOBAL_TEXT_SIZE, text_halign: pinescript.text.align_center })); - table.merge_cells(state.dashT, 0, 8, 3, 8); + pinescript.tableMergeCells(state.dashT, 0, 8, 3, 8); pinescript.table.cell(state.dashT, 0, 8, f_dashPressureMomentum(), ({ text_color: txtHdr, bgcolor: hdrBg, text_size: GLOBAL_TEXT_SIZE, text_halign: pinescript.text.align_center, tooltip: f_dashTooltipPressure() })); let upperPinChgStr = (pinescript.strToString(upperPinChg, "#.##") + "%"); let lowerPinChgStr = (pinescript.strToString(lowerPinChg, "#.##") + "%"); @@ -3486,15 +3966,15 @@ function main() { let bodyStatusLoc = ((bodyStatus === "EXPANDING") ? f_dashExpanding() : ((bodyStatus === "CONTRACTING") ? f_dashContracting() : f_dashStable())); let bodyStatusCol = ((bodyStatus === "EXPANDING") ? posCol : ((bodyStatus === "CONTRACTING") ? negCol : txtLbl)); pinescript.table.cell(state.dashT, 3, 10, bodyStatusLoc, ({ text_color: bodyStatusCol, bgcolor: rowBg2, text_size: GLOBAL_TEXT_SIZE, text_halign: pinescript.text.align_center })); - table.merge_cells(state.dashT, 0, 11, 3, 11); + pinescript.tableMergeCells(state.dashT, 0, 11, 3, 11); pinescript.table.cell(state.dashT, 0, 11, f_dashTrendChannelBoundaries(), ({ text_color: txtHdr, bgcolor: hdrBg, text_size: GLOBAL_TEXT_SIZE, text_halign: pinescript.text.align_center, tooltip: f_dashTooltipChannelBounds() })); let upDiffSign = ((!pinescript.na(resistDiff) && (resistDiff > 0)) ? "+" : ""); let upPctSign = ((!pinescript.na(resistPct) && (resistPct > 0)) ? "+" : ""); let upperStr = (trendValid ? (((((((((f_dashUpperChannel() + " ") + f_fmtPrice(resistPrice)) + " (Δ ") + upDiffSign) + pinescript.strToString(resistDiff, "#.##")) + " | ") + upPctSign) + pinescript.strToString(resistPct, "#.##")) + "%)") : "-"); let lowerStr = (trendValid ? (((((((f_dashLowerChannel() + " ") + f_fmtPrice(supportPrice)) + " (Δ -") + pinescript.strToString(supportDiff, "#.##")) + " | -") + pinescript.strToString(supportPct, "#.##")) + "%)") : "-"); - table.merge_cells(state.dashT, 0, 12, 1, 12); + pinescript.tableMergeCells(state.dashT, 0, 12, 1, 12); pinescript.table.cell(state.dashT, 0, 12, upperStr, ({ text_color: txtVal, bgcolor: rowBg1, text_size: GLOBAL_TEXT_SIZE, text_halign: pinescript.text.align_center, tooltip: f_dashTooltipUpper() })); - table.merge_cells(state.dashT, 2, 12, 3, 12); + pinescript.tableMergeCells(state.dashT, 2, 12, 3, 12); pinescript.table.cell(state.dashT, 2, 12, lowerStr, ({ text_color: txtVal, bgcolor: rowBg1, text_size: GLOBAL_TEXT_SIZE, text_halign: pinescript.text.align_center, tooltip: f_dashTooltipLower() })); let widthStr = (trendValid ? f_fmtPrice(channelWidth) : "-"); let posStr = (!pinescript.na(channelPos) ? (pinescript.strToString(channelPos, "#.##") + "%") : "-"); @@ -3505,7 +3985,7 @@ function main() { pinescript.table.cell(state.dashT, 1, 13, posStr, ({ text_color: txtVal, bgcolor: rowBg2, text_size: GLOBAL_TEXT_SIZE, text_halign: pinescript.text.align_center, tooltip: f_dashTooltipPosition() })); pinescript.table.cell(state.dashT, 2, 13, rrStr, ({ text_color: txtVal, bgcolor: rowBg2, text_size: GLOBAL_TEXT_SIZE, text_halign: pinescript.text.align_center, tooltip: f_dashTooltipRR() })); pinescript.table.cell(state.dashT, 3, 13, posStatusLoc, ({ text_color: statusCol, bgcolor: rowBg2, text_size: GLOBAL_TEXT_SIZE, text_halign: pinescript.text.align_center, tooltip: f_dashTooltipStatus() })); - table.merge_cells(state.dashT, 0, 14, 3, 14); + pinescript.tableMergeCells(state.dashT, 0, 14, 3, 14); pinescript.table.cell(state.dashT, 0, 14, f_dashTrendIntelligence(), ({ text_color: txtHdr, bgcolor: hdrBg, text_size: GLOBAL_TEXT_SIZE, text_halign: pinescript.text.align_center, tooltip: f_dashTooltipQuality(quality, contradictions) })); let qualityStr = (pinescript.strToString(quality, "#") + "/100"); let confidenceLoc = ((confidenceLevel === "HIGH") ? f_dashHigh() : ((confidenceLevel === "MEDIUM") ? f_dashMedium() : f_dashLow())); @@ -3530,7 +4010,7 @@ function main() { let narrTxtSub = pinescript.color.rgb(148, 163, 184); let narrHeader = f_L("Market Analysis", "Piyasa Analizi", "बाजार विश्लेषण", "تحليل السوق"); let textAlign = _alignL; - table.merge_cells(state.dashT, 0, 16, 3, 16); + pinescript.tableMergeCells(state.dashT, 0, 16, 3, 16); pinescript.table.cell(state.dashT, 0, 16, narrHeader, ({ text_color: narrTxtHdr, bgcolor: narrHdrBg, text_size: GLOBAL_TEXT_SIZE, text_halign: pinescript.text.align_center })); let wordsPerLine = 18; let maxCharsPerLine = 90; @@ -3553,7 +4033,7 @@ function main() { if ((narrativeRowsNeeded > 0)) { for (let r = 0; r <= (narrativeRowsNeeded - 1); r++) { let row = (firstNarrRow + r); - table.merge_cells(state.dashT, 0, row, 3, row); + pinescript.tableMergeCells(state.dashT, 0, row, 3, row); let txt = pinescript.arrayGet(narrLines, r); if (((r === (narrativeRowsNeeded - 1)) && (totalLines > narrativeRowsNeeded))) { txt += " …"; @@ -3568,7 +4048,7 @@ function main() { let disclaimerLine3 = f_L("and consult with a qualified financial advisor before making investment decisions.", "ve yatırım kararları vermeden önce kalifiye bir finansal danışmana danışın.", "और निवेश निर्णय लेने से पहले योग्य वित्तीय सलाहकार से परामर्श करें।", "واستشر مستشاراً مالياً مؤهلاً قبل اتخاذ قرارات الاستثمار."); for (let disclaimerLineNum = 0; disclaimerLineNum <= 2; disclaimerLineNum++) { let row = (disclaimerStartRow + disclaimerLineNum); - table.merge_cells(state.dashT, 0, row, 3, row); + pinescript.tableMergeCells(state.dashT, 0, row, 3, row); let lineText = ((disclaimerLineNum === 0) ? disclaimerLine1 : ((disclaimerLineNum === 1) ? disclaimerLine2 : disclaimerLine3)); pinescript.table.cell(state.dashT, 0, row, lineText, ({ text_color: narrTxtSub, bgcolor: pinescript.color.new(pinescript.color.rgb(30, 30, 30), 10), text_size: GLOBAL_TEXT_SIZE, text_halign: pinescript.text.align_center })); } @@ -3578,7 +4058,67 @@ function main() { } -// Export for use -export { main +// The bar-by-bar engine: runs main() once per bar over the dataset. +function run(data, options = {}) { + data = data || {}; + const inClose = (data.close || []).map(Number); + const n = inClose.length; + const inOpen = (data.open || inClose).map(Number); + const inHigh = (data.high || inClose).map(Number); + const inLow = (data.low || inClose).map(Number); + const inVol = (data.volume || new Array(n).fill(0)).map(Number); + const inTime = (data.time || inClose.map((_, i) => i)).map(Number); + + // Reset all persistent and per-run state so repeated runs are independent. + globalThis.__pineState = {}; + globalThis.__pineRuntime = { plots: {}, plotshapes: {}, alerts: [], bars: n, inputs: options.inputs || {} }; + pinescript.__rt = {}; + pinescript.__bar = 0; + + // Growing OHLCV windows: each is a SeriesArray that gains one element per bar, + // so window built-ins like sma and highest see history up to the current bar. + const O = pinescript.asSeries([]), H = pinescript.asSeries([]), L = pinescript.asSeries([]); + const C = pinescript.asSeries([]), V = pinescript.asSeries([]), T = pinescript.asSeries([]); + const HL2 = pinescript.asSeries([]), HLC3 = pinescript.asSeries([]), OHLC4 = pinescript.asSeries([]); + globalThis.open = O; globalThis.high = H; globalThis.low = L; globalThis.close = C; + globalThis.volume = V; globalThis.time = T; + globalThis.hl2 = HL2; globalThis.hlc3 = HLC3; globalThis.ohlc4 = OHLC4; + globalThis.syminfo = globalThis.syminfo || { tickerid: 'SYNTHETIC', ticker: 'SYNTHETIC', mintick: 0.01 }; + + const rt = globalThis.__pineRuntime; + for (let i = 0; i < n; i++) { + O.push(inOpen[i]); H.push(inHigh[i]); L.push(inLow[i]); C.push(inClose[i]); V.push(inVol[i]); T.push(inTime[i]); + HL2.push((inHigh[i] + inLow[i]) / 2); + HLC3.push((inHigh[i] + inLow[i] + inClose[i]) / 3); + OHLC4.push((inOpen[i] + inHigh[i] + inLow[i] + inClose[i]) / 4); + + pinescript.__bar = i; + rt.__barIndex = i; + rt.__plotIdx = 0; + rt.__shapeIdx = 0; + globalThis.bar_index = i; + globalThis.last_bar_index = n - 1; + globalThis.barstate = { + isfirst: i === 0, islast: i === n - 1, isrealtime: false, ishistory: true, + isconfirmed: true, isnew: true, islastconfirmedhistory: i === n - 1, + }; + + main(); + } + + // Backfill skipped bars so every output series has exactly n entries. + for (const k of Object.keys(rt.plots)) { + const d = rt.plots[k].data; + for (let i = 0; i < n; i++) if (d[i] === undefined) d[i] = null; + } + for (const k of Object.keys(rt.plotshapes)) { + const d = rt.plotshapes[k].data; + for (let i = 0; i < n; i++) if (d[i] === undefined) d[i] = false; + } + return rt; +} + + +export { main, run }; diff --git a/converted/TehThomas_-_Aligned_Timeframe_Fair_Value_Gaps.js b/converted/TehThomas_-_Aligned_Timeframe_Fair_Value_Gaps.js index bdd3d6c..7152546 100644 --- a/converted/TehThomas_-_Aligned_Timeframe_Fair_Value_Gaps.js +++ b/converted/TehThomas_-_Aligned_Timeframe_Fair_Value_Gaps.js @@ -1,12 +1,63 @@ -// PineScript to JavaScript Transpiled Code +// Auto-generated JavaScript from PineScript source -// Generated automatically +// Do not edit by hand -- re-run the converter instead -// Built-in PineScript functions +// All the built-in PineScript functions that scripts depend on at runtime const pinescript = { + __scalar: function(v) { + if (v === null || v === undefined) return v; + if (typeof v === 'object' && typeof v.valueOf === 'function') { + const s = v.valueOf(); + if (typeof s === 'number' || typeof s === 'boolean' || s === null) return s; + } + return v; + }, + series: function(id, value) { + const rt = this.__rt || (this.__rt = {}); + const store = rt.series || (rt.series = {}); + let buf = store[id]; + if (!buf) buf = store[id] = []; + buf[this.__bar | 0] = this.__scalar(value); + return buf; + }, + hist: function(id, value, n) { + const buf = this.series(id, value); + const idx = (this.__bar | 0) - (n | 0); + return idx >= 0 && idx < buf.length ? buf[idx] : null; + }, + time: function(timeframe, session) { + const t = globalThis.time; + if (t && typeof t.valueOf === 'function') return t.valueOf(); + return Array.isArray(t) ? t[t.length - 1] : t; + }, + sign: function(x) { + if (x === null || x === undefined || (typeof x === 'number' && isNaN(x))) return null; + return Math.sign(Number(x)); + }, + unpack: function(value, count) { + if (Array.isArray(value)) return value; + if (value != null && typeof value[Symbol.iterator] === 'function') return Array.from(value); + return new Array(count || 0).fill(null); + }, + alertcondition: function(condition, ...rest) { + if (globalThis.__pineRuntime) { + globalThis.__pineRuntime.alerts.push({ condition, args: rest }); + } + return null; + }, + barcolor: function(color) { return null; }, + bgcolor: function(color, title, editable, showLast) { + return null; + }, + fill: function(series1, series2, color, title, editable) { + return null; + }, + hline: function(_price, _title, _opts) { + return null; + }, sma: function(series, length) { if (series === null || series === undefined) return null; if (!Array.isArray(series)) series = [series]; @@ -84,6 +135,13 @@ const pinescript = { } return weightSum > 0 ? sum / weightSum : null; }, + swma: function(series) { + if (series === null || series === undefined) return null; + if (!Array.isArray(series)) series = [series]; + if (series.length < 4) return null; + const s = series.slice(-4); + return (s[0] * 1 + s[1] * 2 + s[2] * 2 + s[3] * 1) / 6; + }, atr: function(high, low, close, length = 14) { if (!Array.isArray(high) || !Array.isArray(low) || !Array.isArray(close)) return null; if (high.length < length + 1 || low.length < length + 1 || close.length < length + 1) return null; @@ -114,6 +172,53 @@ const pinescript = { const atr = builtins.get('atr')(high, low, close, length); return { middle: sma, upper: sma + mult * atr, lower: sma - mult * atr }; }, + supertrend: function(factor, atrPeriod, high, low, close) { + if (!Array.isArray(high) || !Array.isArray(low) || !Array.isArray(close)) return [null, null]; + if (high.length < atrPeriod + 1) return [null, null]; + const atrVal = builtins.get('atr')(high, low, close, atrPeriod); + if (atrVal === null) return [null, null]; + const lastIdx = close.length - 1; + const hl2 = (high[lastIdx] + low[lastIdx]) / 2; + const upperBand = hl2 + factor * atrVal; + const lowerBand = hl2 - factor * atrVal; + // Determine direction: 1 means downtrend (price below band), -1 means uptrend + const direction = close[lastIdx] > upperBand ? -1 : close[lastIdx] < lowerBand ? 1 : -1; + const supertrendValue = direction === -1 ? lowerBand : upperBand; + return [supertrendValue, direction]; + }, + ta_supertrend: function(factor, atrPeriod) { + const H = globalThis.high; + const L = globalThis.low; + const C = globalThis.close; + return builtins.get('supertrend')(factor, atrPeriod, H, L, C); + }, + dmi: function(diLength, adxSmoothing, high, low, close) { + if (!Array.isArray(high) || !Array.isArray(low) || !Array.isArray(close)) return { plusDI: null, minusDI: null, adx: null }; + if (high.length < diLength + 1) return { plusDI: null, minusDI: null, adx: null }; + let plusDMSum = 0, minusDMSum = 0, trSum = 0; + for (let i = 1; i <= diLength; i++) { + const idx = high.length - 1 - diLength + i; + const upMove = high[idx] - high[idx - 1]; + const downMove = low[idx - 1] - low[idx]; + plusDMSum += (upMove > downMove && upMove > 0) ? upMove : 0; + minusDMSum += (downMove > upMove && downMove > 0) ? downMove : 0; + const tr = Math.max( + high[idx] - low[idx], + Math.abs(high[idx] - close[idx - 1]), + Math.abs(low[idx] - close[idx - 1]) + ); + trSum += tr; + } + const plusDI = trSum > 0 ? (plusDMSum / trSum) * 100 : 0; + const minusDI = trSum > 0 ? (minusDMSum / trSum) * 100 : 0; + const diSum = plusDI + minusDI; + const dx = diSum > 0 ? Math.abs(plusDI - minusDI) / diSum * 100 : 0; + return { plusDI, minusDI, adx: dx }; + }, + adx: function(diLength, adxSmoothing, high, low, close) { + const result = builtins.get('dmi')(diLength, adxSmoothing, high, low, close); + return result ? result.adx : null; + }, rsi: function(close, length = 14) { if (close === null || close === undefined) return null; if (!Array.isArray(close)) close = [close]; @@ -171,9 +276,7 @@ const pinescript = { return { macd: macdLine, signal: signalLine, histogram: histogram }; }, cci: function(a, b, c, d) { - // Pine signatures: - // - ta.cci(source, length) - // - ta.cci(high, low, close, length) + // Supports both ta.cci(source, length) and ta.cci(high, low, close, length) signatures let source; let length; if (Array.isArray(a) && typeof b === 'number' && c === undefined) { @@ -304,6 +407,15 @@ const pinescript = { stdev: function(source, length) { return Math.sqrt(builtins.get('variance')(source, length)); }, + median: function(source, length) { + if (!source || source.length < length) return null; + const sorted = [...source.slice(-length)].sort((a, b) => a - b); + const mid = Math.floor(sorted.length / 2); + return sorted.length % 2 !== 0 ? sorted[mid] : (sorted[mid - 1] + sorted[mid]) / 2; + }, + ta_median: function(source, length) { + return builtins.get('median')(source, length); + }, hl2: function(high, low) { return high && low ? (high[high.length - 1] + low[low.length - 1]) / 2 : null; }, @@ -420,6 +532,24 @@ const pinescript = { return (series1[i - 1] < series2[i - 1] && series1[i] >= series2[i]) || (series1[i - 1] > series2[i - 1] && series1[i] <= series2[i]); }, + crossover: function(series1, series2) { + if (series1 === null || series1 === undefined) return false; + if (series2 === null || series2 === undefined) return false; + if (!Array.isArray(series1)) series1 = [series1]; + if (!Array.isArray(series2)) series2 = [series2]; + if (series1.length < 2 || series2.length < 2) return false; + const i = series1.length - 1; + return series1[i - 1] < series2[i - 1] && series1[i] >= series2[i]; + }, + crossunder: function(series1, series2) { + if (series1 === null || series1 === undefined) return false; + if (series2 === null || series2 === undefined) return false; + if (!Array.isArray(series1)) series1 = [series1]; + if (!Array.isArray(series2)) series2 = [series2]; + if (series1.length < 2 || series2.length < 2) return false; + const i = series1.length - 1; + return series1[i - 1] > series2[i - 1] && series1[i] <= series2[i]; + }, offset: function(series, offset) { if (!Array.isArray(series)) return null; const idx = series.length - 1 - offset; @@ -500,12 +630,14 @@ const pinescript = { return builtins.get('nz')(value, replacement); }, plot: function(series, title = '', color = null, linewidth = 1) { - globalThis.__pineRuntime.plots.push({ - series, - title, - color, - linewidth - }); + const rt = globalThis.__pineRuntime; + if (!rt) return series; + const bar = rt.__barIndex | 0; + const ord = (rt.__plotIdx = (rt.__plotIdx | 0) + 1) - 1; + const key = (title && String(title)) || ('plot_' + ord); + let p = rt.plots[key]; + if (!p) p = rt.plots[key] = { title: key, color, linewidth, data: [] }; + p.data[bar] = this.__scalar(series); return series; }, lineNew: function(x1, y1, x2, y2, opts = {}) { @@ -514,20 +646,72 @@ const pinescript = { lineDelete: function(l) { return null; }, - labelNew: function(...args) { - if (globalThis.__pineRuntime) { - globalThis.__pineRuntime.labels = globalThis.__pineRuntime.labels || []; - globalThis.__pineRuntime.labels.push({ args }); - } - return { args }; + lineSetXY: function(line, point, x, y) { + if (!line) return null; + if (point === 0 || point === 'x1') { line.x1 = x; line.y1 = y; } + else { line.x2 = x; line.y2 = y; } + return line; + }, + lineGetX: function(line, point) { + if (!line) return null; + return point === 0 || point === 'x1' ? line.x1 : line.x2; + }, + lineGetY: function(line, point) { + if (!line) return null; + return point === 0 || point === 'y1' ? line.y1 : line.y2; + }, + labelNew: function(x, y, text = '', opts = {}) { + return { x, y, text, opts, _type: 'label' }; }, labelDelete: function(l) { return null; }, + labelSetText: function(label, text) { + if (!label) return null; + label.text = text; + return label; + }, + labelGetText: function(label) { + if (!label) return ''; + return label.text || ''; + }, + boxNew: function(left, top, right, bottom, opts = {}) { + return { left, top, right, bottom, opts, _type: 'box' }; + }, + boxDelete: function(box) { + return null; + }, + boxSetLeftTop: function(box, left, top) { + if (!box) return null; + box.left = left; + box.top = top; + return box; + }, + boxSetRightBottom: function(box, right, bottom) { + if (!box) return null; + box.right = right; + box.bottom = bottom; + return box; + }, + polylineNew: function(points, opts = {}) { + return { points: points || [], opts, _type: 'polyline' }; + }, + polylineDelete: function(poly) { + return null; + }, plotshape: function(condition, ...rest) { - if (globalThis.__pineRuntime) { - globalThis.__pineRuntime.plotshapes.push({ condition, args: rest }); + const rt = globalThis.__pineRuntime; + if (!rt) return condition; + const bar = rt.__barIndex | 0; + const ord = (rt.__shapeIdx = (rt.__shapeIdx | 0) + 1) - 1; + let key = 'shape_' + ord; + for (const r of rest) { + if (typeof r === 'string') { key = r; break; } + if (r && typeof r === 'object' && r.title) { key = String(r.title); break; } } + let s = rt.plotshapes[key]; + if (!s) s = rt.plotshapes[key] = { title: key, data: [] }; + s.data[bar] = !!this.__scalar(condition); return condition; }, indicator: function(title, shorttitle, overlay, opts) { @@ -542,27 +726,12 @@ const pinescript = { } return null; }, - alertcondition: function(condition, ...rest) { - if (globalThis.__pineRuntime) { - globalThis.__pineRuntime.alerts.push({ condition, args: rest }); - } - return null; - }, plotbar: function(open, high, low, close, title, color, editable, showLast) { return { open, high, low, close }; }, plotcandle: function(open, high, low, close, title, color, wickColor, borderColor, editable, showLast) { return { open, high, low, close }; }, - hline: function(_price, _title, _opts) { - return null; - }, - bgcolor: function(color, title, editable, showLast) { - return null; - }, - fill: function(series1, series2, color, title, editable) { - return null; - }, alert: function(condition, message, frequency) { console.log('Alert:', condition ? message : 'Condition not met'); return condition; @@ -828,6 +997,9 @@ const pinescript = { arrayIndexOf: function(arr, value) { return arr ? arr.indexOf(value) : -1; }, + arrayLastIndexOf: function(arr, value) { + return arr ? arr.lastIndexOf(value) : -1; + }, arraySum: function(arr) { return arr ? arr.reduce((a, b) => a + b, 0) : 0; }, @@ -840,6 +1012,42 @@ const pinescript = { arrayMax: function(arr) { return arr && arr.length > 0 ? Math.max(...arr) : null; }, + arrayStdev: function(arr) { + if (!arr || arr.length === 0) return null; + const mean = arr.reduce((a, b) => a + b, 0) / arr.length; + const variance = arr.reduce((sum, val) => sum + Math.pow(val - mean, 2), 0) / arr.length; + return Math.sqrt(variance); + }, + arrayVariance: function(arr) { + if (!arr || arr.length === 0) return null; + const mean = arr.reduce((a, b) => a + b, 0) / arr.length; + return arr.reduce((sum, val) => sum + Math.pow(val - mean, 2), 0) / arr.length; + }, + arrayCovariance: function(arr1, arr2) { + if (!arr1 || !arr2 || arr1.length === 0 || arr1.length !== arr2.length) return null; + const mean1 = arr1.reduce((a, b) => a + b, 0) / arr1.length; + const mean2 = arr2.reduce((a, b) => a + b, 0) / arr2.length; + let cov = 0; + for (let i = 0; i < arr1.length; i++) { + cov += (arr1[i] - mean1) * (arr2[i] - mean2); + } + return cov / arr1.length; + }, + mode: function(arr) { + if (!arr || arr.length === 0) return null; + const counts = new Map(); + let maxCount = 0; + let modeVal = arr[0]; + for (const val of arr) { + const c = (counts.get(val) || 0) + 1; + counts.set(val, c); + if (c > maxCount) { + maxCount = c; + modeVal = val; + } + } + return modeVal; + }, strLength: function(str) { return str ? str.length : 0; }, @@ -895,6 +1103,84 @@ const pinescript = { strReverse: function(str) { return str ? str.split('').reverse().join('') : ''; }, + strFormat: function(formatStr, ...args) { + if (!formatStr) return ''; + return formatStr.replace(/\{(\d+)\}/g, (match, idx) => { + const i = parseInt(idx, 10); + return i < args.length ? String(args[i]) : match; + }); + }, + input: function(defval, title, tooltip, inline, group) { + return defval; + }, + inputInt: function(defval = 0, title, minval, maxval, step, tooltip, inline, group) { + return defval; + }, + inputFloat: function(defval = 0.0, title, minval, maxval, step, tooltip, inline, group) { + return defval; + }, + inputBool: function(defval = false, title, tooltip, inline, group) { + return defval; + }, + inputString: function(defval = '', title, options, tooltip, inline, group) { + return defval; + }, + inputSource: function(defval = null, title, tooltip, inline, group) { + return defval; + }, + inputColor: function(defval = '#000000', title, tooltip, inline, group) { + return defval; + }, + inputTime: function(defval = 0, title, tooltip, inline, group) { + return defval; + }, + mathSign: function(value) { + return Math.sign(value); + }, + mathAvg: function(...args) { + const valid = args.filter(v => v !== null && v !== undefined && !isNaN(v)); + return valid.length > 0 ? valid.reduce((a, b) => a + b, 0) / valid.length : null; + }, + mathSum: function(series, length) { + if (!series || series.length < length) return null; + return series.slice(-length).reduce((a, b) => a + b, 0); + }, + mathRandom: function(min = 0, max = 1) { + return min + Math.random() * (max - min); + }, + mathTodegrees: function(radians) { + return radians * (180 / Math.PI); + }, + mathToradians: function(degrees) { + return degrees * (Math.PI / 180); + }, + colorT: function(color, transparency) { + if (transparency !== undefined) { + return { color, transparency }; + } + if (color && typeof color === 'object' && 'transparency' in color) { + return color.transparency; + } + return 0; + }, + tableCellSetText: function(table, column, row, text) { + return null; + }, + tableCellSetBgcolor: function(table, column, row, bgcolor) { + return null; + }, + tableMergeCells: function(table, startColumn, startRow, endColumn, endRow) { + return null; + }, + tableDelete: function(table) { + return null; + }, + tableClear: function(table, startColumn, startRow, endColumn, endRow) { + return null; + }, + maxBarsBack: function(series, length) { + return null; + }, pi: 3.141592653589793, e: 2.718281828459045, true: true, @@ -905,8 +1191,7 @@ const pinescript = { const info = { ticker: 'AAPL', tickerid: 'NASDAQ:AAPL', prefix: 'NASDAQ', root: 'AAPL', suffix: '' }; return info[type] || ''; }, - time: 1771178053204, - timenow: 1771178053204, + timenow: 1781574528878, barstate: "LAST", dividends: {}, splits: {}, @@ -923,7 +1208,7 @@ const pinescript = { return null; }, tr: function(high, low, close) { - // Pine signature: ta.tr(true) + // Handles both the boolean shorthand ta.tr(true) and explicit high/low/close arguments if (typeof high === 'boolean') { const H = globalThis.high; const L = globalThis.low; @@ -934,7 +1219,7 @@ const pinescript = { return Math.max(H[i] - L[i], Math.abs(H[i] - prevClose), Math.abs(L[i] - prevClose)); } - // Fallback: treat inputs as scalars for current bar + // When arrays are passed, compute true range from the last bar using the previous close if (Array.isArray(close)) { const prevClose = close[close.length - 2] || close[0]; const currHigh = Array.isArray(high) ? high[high.length - 1] : high; @@ -945,6 +1230,92 @@ const pinescript = { return Math.max(high - low, Math.abs(high - close), Math.abs(low - close)); }, + arrayFirst: function(arr) { + if (!arr || arr.length === 0) return null; + return arr[0]; + }, + arrayLast: function(arr) { + if (!arr || arr.length === 0) return null; + return arr[arr.length - 1]; + }, + arrayJoin: function(arr, separator = ',') { + if (!arr) return ''; + return arr.join(separator); + }, + arrayConcat: function(arr1, arr2) { + if (!arr1) return arr2 || []; + if (!arr2) return arr1; + return arr1.concat(arr2); + }, + arrayCopy: function(arr) { + if (!arr) return []; + return [...arr]; + }, + arrayBinarySearch: function(arr, value) { + if (!arr || arr.length === 0) return -1; + let lo = 0, hi = arr.length - 1; + while (lo <= hi) { + const mid = (lo + hi) >>> 1; + if (arr[mid] === value) return mid; + if (arr[mid] < value) lo = mid + 1; + else hi = mid - 1; + } + return -1; + }, + arrayRange: function(arr) { + if (!arr || arr.length === 0) return 0; + return Math.max(...arr) - Math.min(...arr); + }, + arrayMedian: function(arr) { + if (!arr || arr.length === 0) return null; + const sorted = [...arr].sort((a, b) => a - b); + const mid = Math.floor(sorted.length / 2); + return sorted.length % 2 !== 0 ? sorted[mid] : (sorted[mid - 1] + sorted[mid]) / 2; + }, + arrayMode: function(arr) { + if (!arr || arr.length === 0) return null; + const freq = {}; + let maxCount = 0; + let mode = arr[0]; + for (const v of arr) { + freq[v] = (freq[v] || 0) + 1; + if (freq[v] > maxCount) { maxCount = freq[v]; mode = v; } + } + return mode; + }, + arrayPercentile: function(arr, percentile) { + if (!arr || arr.length === 0) return null; + const sorted = [...arr].sort((a, b) => a - b); + const idx = (percentile / 100) * (sorted.length - 1); + const lo = Math.floor(idx); + const hi = Math.ceil(idx); + if (lo === hi) return sorted[lo]; + return sorted[lo] + (sorted[hi] - sorted[lo]) * (idx - lo); + }, + arrayPercentileLinearInterpolation: function(arr, percentile) { + return builtins.get('arrayPercentile')(arr, percentile); + }, + arrayPercentileNearestRank: function(arr, percentile) { + if (!arr || arr.length === 0) return null; + const sorted = [...arr].sort((a, b) => a - b); + const idx = Math.ceil((percentile / 100) * sorted.length) - 1; + return sorted[Math.max(0, idx)]; + }, + arrayAbs: function(arr) { + if (!arr) return []; + return arr.map(v => Math.abs(v)); + }, + arrayEvery: function(arr, callback) { + if (!arr) return false; + return arr.every(callback); + }, + arraySome: function(arr, callback) { + if (!arr) return false; + return arr.some(callback); + }, + requestFinancial: function() { + return null; + }, }; @@ -1101,6 +1472,115 @@ globalThis.barmerge = globalThis.barmerge || { }; +function __pineNS(real) { + + const base = real || {}; + + return new Proxy(base, { + + get(target, key) { + + if (key in target) return target[key]; + + if (typeof key === "symbol") return target[key]; + + // Unknown member: usable as a value (null), a constant, or a no-op function. + + const noop = function() { return null; }; + + noop.toString = () => ""; + + return noop; + + } + + }); + +} + +globalThis.ta = globalThis.ta || __pineNS({}); + +globalThis.math = globalThis.math || __pineNS({ pi: Math.PI, e: Math.E, phi: 1.618033988749895, rphi: 0.618033988749895 }); + +globalThis.chart = globalThis.chart || __pineNS({ point: __pineNS({}), bg_color: null, fg_color: null }); + +globalThis.line = globalThis.line || __pineNS({ style_solid: "solid", style_dashed: "dashed", style_dotted: "dotted" }); + +globalThis.box = globalThis.box || __pineNS({}); + +globalThis.label = globalThis.label || __pineNS({ style_label_down: "label_down", style_label_up: "label_up", style_none: "none" }); + +globalThis.polyline = globalThis.polyline || __pineNS({}); + +globalThis.linefill = globalThis.linefill || __pineNS({}); + +globalThis.matrix = globalThis.matrix || __pineNS({}); + +globalThis.map = globalThis.map || __pineNS({}); + +globalThis.session = globalThis.session || __pineNS({ regular: "regular", extended: "extended" }); + +globalThis.dayofweek = globalThis.dayofweek || __pineNS({ sunday: 1, monday: 2, tuesday: 3, wednesday: 4, thursday: 5, friday: 6, saturday: 7 }); + +globalThis.timeframe = __pineNS(Object.assign(globalThis.timeframe || {}, { period: (globalThis.timeframe && globalThis.timeframe.period) || "D", isintraday: false, isdaily: true, multiplier: 1 })); + +globalThis.request = __pineNS(globalThis.request || {}); + +globalThis.input = __pineNS(globalThis.input || {}); + +globalThis.adjustment = globalThis.adjustment || __pineNS({}); + +globalThis.earnings = globalThis.earnings || __pineNS({}); + +globalThis.dividends = globalThis.dividends || __pineNS({}); + +globalThis.splits = globalThis.splits || __pineNS({}); + +globalThis.currency = globalThis.currency || __pineNS({}); + +globalThis.display = globalThis.display || __pineNS({ none: "none", all: "all", pane: "pane", data_window: "data_window", status_line: "status_line", price_scale: "price_scale" }); + +globalThis.format = globalThis.format || __pineNS({ price: "price", volume: "volume", percent: "percent", mintick: "mintick", inherit: "inherit" }); + +globalThis.scale = globalThis.scale || __pineNS({ left: "left", right: "right", none: "none" }); + +globalThis.font = globalThis.font || __pineNS({ family_default: "default", family_monospace: "monospace" }); + +globalThis.xloc = globalThis.xloc || __pineNS({ bar_index: "bar_index", bar_time: "bar_time" }); + +globalThis.yloc = globalThis.yloc || __pineNS({ price: "price", abovebar: "abovebar", belowbar: "belowbar" }); + +globalThis.extend = globalThis.extend || __pineNS({ none: "none", left: "left", right: "right", both: "both" }); + +globalThis.order = globalThis.order || __pineNS({ ascending: "ascending", descending: "descending" }); + +globalThis.alert = globalThis.alert || Object.assign(function() { return null; }, { freq_once_per_bar: "once_per_bar", freq_once_per_bar_close: "once_per_bar_close", freq_all: "all" }); + +globalThis.dayofmonth = globalThis.dayofmonth || function(t) { return new Date(t || 0).getUTCDate(); }; + +globalThis.plot = globalThis.plot || __pineNS({ style_line: "line", style_linebr: "linebr", style_stepline: "stepline", style_histogram: "histogram", style_cross: "cross", style_area: "area", style_areabr: "areabr", style_columns: "columns", style_circles: "circles" }); + +globalThis.int = globalThis.int || function(x) { return x == null ? null : Math.trunc(Number(x)); }; + +globalThis.float = globalThis.float || function(x) { return x == null ? null : Number(x); }; + +globalThis.bool = globalThis.bool || function(x) { return Boolean(x); }; + +globalThis.string = globalThis.string || function(x) { return x == null ? null : String(x); }; + +globalThis.dayofweek = globalThis.dayofweek || __pineNS({ sunday: 1, monday: 2, tuesday: 3, wednesday: 4, thursday: 5, friday: 6, saturday: 7 }); + +globalThis.str = globalThis.str || __pineNS({}); + +pinescript.math = globalThis.math; + +pinescript.ta = globalThis.ta; + +pinescript.str = globalThis.str; + +pinescript.array = __pineNS(globalThis.array); + + pinescript.color = { hex: function(s) { @@ -1190,11 +1670,11 @@ pinescript.table = { }; -// Input parameters +// Script input parameters and their defaults const FVG = { new: function(top, bot, Bullish, created_bar, start_bar, triggered, fvgBox, midLine, isFilled, isVisible) { return { top: top, bot: bot, Bullish: Bullish, created_bar: created_bar, start_bar: start_bar, triggered: triggered, fvgBox: fvgBox, midLine: midLine, isFilled: isFilled, isVisible: isVisible }; } }; -// Main script logic +// The transpiled script logic, called once per bar function main() { @@ -1273,25 +1753,25 @@ function main() { } let alignedTF = getAlignedTimeframe(); let tfDisplay = getTimeframeDisplay(alignedTF); - let extendBars = input.int(20, "Extend boxes", ({ minval: 0, group: "Boxes" })); - let Gapsize = input.float(0, "Min Gap Size (%)", ({ minval: 0, step: 0.1, group: "Boxes", tooltip: "Set the percentage size of how big the fvg should be" })); - let dynamicGaps = input.bool(false, "Dynamic (shrinking) gaps", ({ group: "Boxes" })); - let maxGaps = input.int(10, "Max Unfilled Gaps to Display", ({ minval: 1, group: "Boxes" })); - let maxHistory = input.int(50, "Max FVGs in History", ({ minval: 50, maxval: 200, group: "Boxes", tooltip: "Higher = more unfilled gaps available, but slower performance" })); - let bullColor = input.color(pinescript.color.new(pinescript.color.hex("#47f7414d"), 85), "Bullish Gap Color", ({ group: "Colors" })); - let bearColor = input.color(pinescript.color.hex("#fa7b7b26"), "Bearish Gap Color", ({ group: "Colors" })); - let gapBorderColor = input.color(pinescript.color.black, "Gap Border Color", ({ group: "Colors" })); - let showMidline = input.bool(true, "Show Midline", ({ group: "Midline" })); - let midlineColor = input.color(pinescript.color.new(pinescript.color.black, 0), "Midline Color", ({ group: "Midline" })); - let midlineStyleOpt = input.string("Dotted", "Midline Style", ({ options: ["Solid", "Dashed", "Dotted"], group: "Midline" })); - let showLabel = input.bool(true, "Show FVG Label", ({ group: "Label" })); - let labelColor = input.color(pinescript.color.new(pinescript.color.black, 0), "Label Text Color", ({ group: "Label" })); - let labelSize = input.string("Small", "Label Size", ({ options: ["Tiny", "Small", "Normal", "Large"], group: "Label" })); + let extendBars = pinescript.inputInt(20, "Extend boxes", ({ minval: 0, group: "Boxes" })); + let Gapsize = pinescript.inputFloat(0, "Min Gap Size (%)", ({ minval: 0, step: 0.1, group: "Boxes", tooltip: "Set the percentage size of how big the fvg should be" })); + let dynamicGaps = pinescript.inputBool(false, "Dynamic (shrinking) gaps", ({ group: "Boxes" })); + let maxGaps = pinescript.inputInt(10, "Max Unfilled Gaps to Display", ({ minval: 1, group: "Boxes" })); + let maxHistory = pinescript.inputInt(50, "Max FVGs in History", ({ minval: 50, maxval: 200, group: "Boxes", tooltip: "Higher = more unfilled gaps available, but slower performance" })); + let bullColor = pinescript.inputColor(pinescript.color.new(pinescript.color.hex("#47f7414d"), 85), "Bullish Gap Color", ({ group: "Colors" })); + let bearColor = pinescript.inputColor(pinescript.color.hex("#fa7b7b26"), "Bearish Gap Color", ({ group: "Colors" })); + let gapBorderColor = pinescript.inputColor(pinescript.color.black, "Gap Border Color", ({ group: "Colors" })); + let showMidline = pinescript.inputBool(true, "Show Midline", ({ group: "Midline" })); + let midlineColor = pinescript.inputColor(pinescript.color.new(pinescript.color.black, 0), "Midline Color", ({ group: "Midline" })); + let midlineStyleOpt = pinescript.inputString("Dotted", "Midline Style", ({ options: ["Solid", "Dashed", "Dotted"], group: "Midline" })); + let showLabel = pinescript.inputBool(true, "Show FVG Label", ({ group: "Label" })); + let labelColor = pinescript.inputColor(pinescript.color.new(pinescript.color.black, 0), "Label Text Color", ({ group: "Label" })); + let labelSize = pinescript.inputString("Small", "Label Size", ({ options: ["Tiny", "Small", "Normal", "Large"], group: "Label" })); let midlineStyle = ((midlineStyleOpt === "Solid") ? line.style_solid : ((midlineStyleOpt === "Dashed") ? line.style_dashed : line.style_dotted)); let textSize = ((labelSize === "Tiny") ? pinescript.size.tiny : ((labelSize === "Small") ? pinescript.size.small : ((labelSize === "Normal") ? pinescript.size.normal : pinescript.size.large))); if (state.fvg_records === undefined) state.fvg_records = pinescript.arrayNew(); - let [htfLow, htfHigh, htfLow2, htfHigh2, htfTime] = pinescript.requestSecurity(syminfo.tickerid, alignedTF, [pinescript.offset(low, 1), pinescript.offset(high, 1), pinescript.offset(low, 3), pinescript.offset(high, 3), pinescript.offset(time, 1)], ({ lookahead: barmerge.lookahead_on })); - let htfChanged = (ta.change(htfTime) !== 0); + let [htfLow, htfHigh, htfLow2, htfHigh2, htfTime] = pinescript.unpack(pinescript.requestSecurity(syminfo.tickerid, alignedTF, [pinescript.hist(0, low, 1), pinescript.hist(1, high, 1), pinescript.hist(2, low, 3), pinescript.hist(3, high, 3), pinescript.hist(4, time, 1)], ({ lookahead: barmerge.lookahead_on })), 5); + let htfChanged = (pinescript.change(pinescript.series(5, htfTime)) !== 0); if (state.htfBarStart === undefined) state.htfBarStart = null; if (state.htfBarStart1 === undefined) state.htfBarStart1 = null; if (state.htfBarStart2 === undefined) state.htfBarStart2 = null; @@ -1359,7 +1839,7 @@ function main() { } if (shouldMarkFilled) { if (get.isVisible) { - box.delete(get.fvgBox); + pinescript.boxDelete(get.fvgBox); if ((showMidline && !pinescript.na(get.midLine))) { pinescript.lineDelete(get.midLine); } @@ -1390,7 +1870,7 @@ function main() { i += 1; } function addFVG(isBull, top, bot, color, startBar) { - let fvgBox = box.new(({ left: startBar, top: top, right: (bar_index + extendBars), bottom: bot, bgcolor: pinescript.color, border_color: gapBorderColor })); + let fvgBox = pinescript.boxNew(({ left: startBar, top: top, right: (bar_index + extendBars), bottom: bot, bgcolor: pinescript.color, border_color: gapBorderColor })); if (showLabel) { box.set_text(fvgBox, (tfDisplay + " FVG")); box.set_text_color(fvgBox, labelColor); @@ -1405,7 +1885,7 @@ function main() { if ((pinescript.arraySize(state.fvg_records) > maxHistory)) { let oldFvg = pinescript.arrayPop(state.fvg_records); if ((!oldFvg.isFilled && oldFvg.isVisible)) { - box.delete(oldFvg.fvgBox); + pinescript.boxDelete(oldFvg.fvgBox); if ((showMidline && !pinescript.na(oldFvg.midLine))) { pinescript.lineDelete(oldFvg.midLine); } @@ -1427,7 +1907,7 @@ function main() { if ((unfilledCount <= maxGaps)) { if (!get2.isVisible) { let color_1 = (get2.Bullish ? bullColor : bearColor); - let newBox = box.new(({ left: get2.start_bar, top: get2.top, right: (bar_index + extendBars), bottom: get2.bot, bgcolor: color_1, border_color: gapBorderColor })); + let newBox = pinescript.boxNew(({ left: get2.start_bar, top: get2.top, right: (bar_index + extendBars), bottom: get2.bot, bgcolor: color_1, border_color: gapBorderColor })); if (showLabel) { box.set_text(newBox, (tfDisplay + " FVG")); box.set_text_color(newBox, labelColor); @@ -1442,7 +1922,7 @@ function main() { } } else { if (get2.isVisible) { - box.delete(get2.fvgBox); + pinescript.boxDelete(get2.fvgBox); if ((showMidline && !pinescript.na(get2.midLine))) { pinescript.lineDelete(get2.midLine); } @@ -1456,7 +1936,67 @@ function main() { } -// Export for use -export { main +// The bar-by-bar engine: runs main() once per bar over the dataset. +function run(data, options = {}) { + data = data || {}; + const inClose = (data.close || []).map(Number); + const n = inClose.length; + const inOpen = (data.open || inClose).map(Number); + const inHigh = (data.high || inClose).map(Number); + const inLow = (data.low || inClose).map(Number); + const inVol = (data.volume || new Array(n).fill(0)).map(Number); + const inTime = (data.time || inClose.map((_, i) => i)).map(Number); + + // Reset all persistent and per-run state so repeated runs are independent. + globalThis.__pineState = {}; + globalThis.__pineRuntime = { plots: {}, plotshapes: {}, alerts: [], bars: n, inputs: options.inputs || {} }; + pinescript.__rt = {}; + pinescript.__bar = 0; + + // Growing OHLCV windows: each is a SeriesArray that gains one element per bar, + // so window built-ins like sma and highest see history up to the current bar. + const O = pinescript.asSeries([]), H = pinescript.asSeries([]), L = pinescript.asSeries([]); + const C = pinescript.asSeries([]), V = pinescript.asSeries([]), T = pinescript.asSeries([]); + const HL2 = pinescript.asSeries([]), HLC3 = pinescript.asSeries([]), OHLC4 = pinescript.asSeries([]); + globalThis.open = O; globalThis.high = H; globalThis.low = L; globalThis.close = C; + globalThis.volume = V; globalThis.time = T; + globalThis.hl2 = HL2; globalThis.hlc3 = HLC3; globalThis.ohlc4 = OHLC4; + globalThis.syminfo = globalThis.syminfo || { tickerid: 'SYNTHETIC', ticker: 'SYNTHETIC', mintick: 0.01 }; + + const rt = globalThis.__pineRuntime; + for (let i = 0; i < n; i++) { + O.push(inOpen[i]); H.push(inHigh[i]); L.push(inLow[i]); C.push(inClose[i]); V.push(inVol[i]); T.push(inTime[i]); + HL2.push((inHigh[i] + inLow[i]) / 2); + HLC3.push((inHigh[i] + inLow[i] + inClose[i]) / 3); + OHLC4.push((inOpen[i] + inHigh[i] + inLow[i] + inClose[i]) / 4); + + pinescript.__bar = i; + rt.__barIndex = i; + rt.__plotIdx = 0; + rt.__shapeIdx = 0; + globalThis.bar_index = i; + globalThis.last_bar_index = n - 1; + globalThis.barstate = { + isfirst: i === 0, islast: i === n - 1, isrealtime: false, ishistory: true, + isconfirmed: true, isnew: true, islastconfirmedhistory: i === n - 1, + }; + + main(); + } + + // Backfill skipped bars so every output series has exactly n entries. + for (const k of Object.keys(rt.plots)) { + const d = rt.plots[k].data; + for (let i = 0; i < n; i++) if (d[i] === undefined) d[i] = null; + } + for (const k of Object.keys(rt.plotshapes)) { + const d = rt.plotshapes[k].data; + for (let i = 0; i < n; i++) if (d[i] === undefined) d[i] = false; + } + return rt; +} + + +export { main, run }; diff --git a/converted/VSA_Pro_Trend_and_Rejection.js b/converted/VSA_Pro_Trend_and_Rejection.js index 4c87515..2e44146 100644 --- a/converted/VSA_Pro_Trend_and_Rejection.js +++ b/converted/VSA_Pro_Trend_and_Rejection.js @@ -1,12 +1,63 @@ -// PineScript to JavaScript Transpiled Code +// Auto-generated JavaScript from PineScript source -// Generated automatically +// Do not edit by hand -- re-run the converter instead -// Built-in PineScript functions +// All the built-in PineScript functions that scripts depend on at runtime const pinescript = { + __scalar: function(v) { + if (v === null || v === undefined) return v; + if (typeof v === 'object' && typeof v.valueOf === 'function') { + const s = v.valueOf(); + if (typeof s === 'number' || typeof s === 'boolean' || s === null) return s; + } + return v; + }, + series: function(id, value) { + const rt = this.__rt || (this.__rt = {}); + const store = rt.series || (rt.series = {}); + let buf = store[id]; + if (!buf) buf = store[id] = []; + buf[this.__bar | 0] = this.__scalar(value); + return buf; + }, + hist: function(id, value, n) { + const buf = this.series(id, value); + const idx = (this.__bar | 0) - (n | 0); + return idx >= 0 && idx < buf.length ? buf[idx] : null; + }, + time: function(timeframe, session) { + const t = globalThis.time; + if (t && typeof t.valueOf === 'function') return t.valueOf(); + return Array.isArray(t) ? t[t.length - 1] : t; + }, + sign: function(x) { + if (x === null || x === undefined || (typeof x === 'number' && isNaN(x))) return null; + return Math.sign(Number(x)); + }, + unpack: function(value, count) { + if (Array.isArray(value)) return value; + if (value != null && typeof value[Symbol.iterator] === 'function') return Array.from(value); + return new Array(count || 0).fill(null); + }, + alertcondition: function(condition, ...rest) { + if (globalThis.__pineRuntime) { + globalThis.__pineRuntime.alerts.push({ condition, args: rest }); + } + return null; + }, + barcolor: function(color) { return null; }, + bgcolor: function(color, title, editable, showLast) { + return null; + }, + fill: function(series1, series2, color, title, editable) { + return null; + }, + hline: function(_price, _title, _opts) { + return null; + }, sma: function(series, length) { if (series === null || series === undefined) return null; if (!Array.isArray(series)) series = [series]; @@ -84,6 +135,13 @@ const pinescript = { } return weightSum > 0 ? sum / weightSum : null; }, + swma: function(series) { + if (series === null || series === undefined) return null; + if (!Array.isArray(series)) series = [series]; + if (series.length < 4) return null; + const s = series.slice(-4); + return (s[0] * 1 + s[1] * 2 + s[2] * 2 + s[3] * 1) / 6; + }, atr: function(high, low, close, length = 14) { if (!Array.isArray(high) || !Array.isArray(low) || !Array.isArray(close)) return null; if (high.length < length + 1 || low.length < length + 1 || close.length < length + 1) return null; @@ -114,6 +172,53 @@ const pinescript = { const atr = builtins.get('atr')(high, low, close, length); return { middle: sma, upper: sma + mult * atr, lower: sma - mult * atr }; }, + supertrend: function(factor, atrPeriod, high, low, close) { + if (!Array.isArray(high) || !Array.isArray(low) || !Array.isArray(close)) return [null, null]; + if (high.length < atrPeriod + 1) return [null, null]; + const atrVal = builtins.get('atr')(high, low, close, atrPeriod); + if (atrVal === null) return [null, null]; + const lastIdx = close.length - 1; + const hl2 = (high[lastIdx] + low[lastIdx]) / 2; + const upperBand = hl2 + factor * atrVal; + const lowerBand = hl2 - factor * atrVal; + // Determine direction: 1 means downtrend (price below band), -1 means uptrend + const direction = close[lastIdx] > upperBand ? -1 : close[lastIdx] < lowerBand ? 1 : -1; + const supertrendValue = direction === -1 ? lowerBand : upperBand; + return [supertrendValue, direction]; + }, + ta_supertrend: function(factor, atrPeriod) { + const H = globalThis.high; + const L = globalThis.low; + const C = globalThis.close; + return builtins.get('supertrend')(factor, atrPeriod, H, L, C); + }, + dmi: function(diLength, adxSmoothing, high, low, close) { + if (!Array.isArray(high) || !Array.isArray(low) || !Array.isArray(close)) return { plusDI: null, minusDI: null, adx: null }; + if (high.length < diLength + 1) return { plusDI: null, minusDI: null, adx: null }; + let plusDMSum = 0, minusDMSum = 0, trSum = 0; + for (let i = 1; i <= diLength; i++) { + const idx = high.length - 1 - diLength + i; + const upMove = high[idx] - high[idx - 1]; + const downMove = low[idx - 1] - low[idx]; + plusDMSum += (upMove > downMove && upMove > 0) ? upMove : 0; + minusDMSum += (downMove > upMove && downMove > 0) ? downMove : 0; + const tr = Math.max( + high[idx] - low[idx], + Math.abs(high[idx] - close[idx - 1]), + Math.abs(low[idx] - close[idx - 1]) + ); + trSum += tr; + } + const plusDI = trSum > 0 ? (plusDMSum / trSum) * 100 : 0; + const minusDI = trSum > 0 ? (minusDMSum / trSum) * 100 : 0; + const diSum = plusDI + minusDI; + const dx = diSum > 0 ? Math.abs(plusDI - minusDI) / diSum * 100 : 0; + return { plusDI, minusDI, adx: dx }; + }, + adx: function(diLength, adxSmoothing, high, low, close) { + const result = builtins.get('dmi')(diLength, adxSmoothing, high, low, close); + return result ? result.adx : null; + }, rsi: function(close, length = 14) { if (close === null || close === undefined) return null; if (!Array.isArray(close)) close = [close]; @@ -171,9 +276,7 @@ const pinescript = { return { macd: macdLine, signal: signalLine, histogram: histogram }; }, cci: function(a, b, c, d) { - // Pine signatures: - // - ta.cci(source, length) - // - ta.cci(high, low, close, length) + // Supports both ta.cci(source, length) and ta.cci(high, low, close, length) signatures let source; let length; if (Array.isArray(a) && typeof b === 'number' && c === undefined) { @@ -304,6 +407,15 @@ const pinescript = { stdev: function(source, length) { return Math.sqrt(builtins.get('variance')(source, length)); }, + median: function(source, length) { + if (!source || source.length < length) return null; + const sorted = [...source.slice(-length)].sort((a, b) => a - b); + const mid = Math.floor(sorted.length / 2); + return sorted.length % 2 !== 0 ? sorted[mid] : (sorted[mid - 1] + sorted[mid]) / 2; + }, + ta_median: function(source, length) { + return builtins.get('median')(source, length); + }, hl2: function(high, low) { return high && low ? (high[high.length - 1] + low[low.length - 1]) / 2 : null; }, @@ -420,6 +532,24 @@ const pinescript = { return (series1[i - 1] < series2[i - 1] && series1[i] >= series2[i]) || (series1[i - 1] > series2[i - 1] && series1[i] <= series2[i]); }, + crossover: function(series1, series2) { + if (series1 === null || series1 === undefined) return false; + if (series2 === null || series2 === undefined) return false; + if (!Array.isArray(series1)) series1 = [series1]; + if (!Array.isArray(series2)) series2 = [series2]; + if (series1.length < 2 || series2.length < 2) return false; + const i = series1.length - 1; + return series1[i - 1] < series2[i - 1] && series1[i] >= series2[i]; + }, + crossunder: function(series1, series2) { + if (series1 === null || series1 === undefined) return false; + if (series2 === null || series2 === undefined) return false; + if (!Array.isArray(series1)) series1 = [series1]; + if (!Array.isArray(series2)) series2 = [series2]; + if (series1.length < 2 || series2.length < 2) return false; + const i = series1.length - 1; + return series1[i - 1] > series2[i - 1] && series1[i] <= series2[i]; + }, offset: function(series, offset) { if (!Array.isArray(series)) return null; const idx = series.length - 1 - offset; @@ -500,12 +630,14 @@ const pinescript = { return builtins.get('nz')(value, replacement); }, plot: function(series, title = '', color = null, linewidth = 1) { - globalThis.__pineRuntime.plots.push({ - series, - title, - color, - linewidth - }); + const rt = globalThis.__pineRuntime; + if (!rt) return series; + const bar = rt.__barIndex | 0; + const ord = (rt.__plotIdx = (rt.__plotIdx | 0) + 1) - 1; + const key = (title && String(title)) || ('plot_' + ord); + let p = rt.plots[key]; + if (!p) p = rt.plots[key] = { title: key, color, linewidth, data: [] }; + p.data[bar] = this.__scalar(series); return series; }, lineNew: function(x1, y1, x2, y2, opts = {}) { @@ -514,20 +646,72 @@ const pinescript = { lineDelete: function(l) { return null; }, - labelNew: function(...args) { - if (globalThis.__pineRuntime) { - globalThis.__pineRuntime.labels = globalThis.__pineRuntime.labels || []; - globalThis.__pineRuntime.labels.push({ args }); - } - return { args }; + lineSetXY: function(line, point, x, y) { + if (!line) return null; + if (point === 0 || point === 'x1') { line.x1 = x; line.y1 = y; } + else { line.x2 = x; line.y2 = y; } + return line; + }, + lineGetX: function(line, point) { + if (!line) return null; + return point === 0 || point === 'x1' ? line.x1 : line.x2; + }, + lineGetY: function(line, point) { + if (!line) return null; + return point === 0 || point === 'y1' ? line.y1 : line.y2; + }, + labelNew: function(x, y, text = '', opts = {}) { + return { x, y, text, opts, _type: 'label' }; }, labelDelete: function(l) { return null; }, + labelSetText: function(label, text) { + if (!label) return null; + label.text = text; + return label; + }, + labelGetText: function(label) { + if (!label) return ''; + return label.text || ''; + }, + boxNew: function(left, top, right, bottom, opts = {}) { + return { left, top, right, bottom, opts, _type: 'box' }; + }, + boxDelete: function(box) { + return null; + }, + boxSetLeftTop: function(box, left, top) { + if (!box) return null; + box.left = left; + box.top = top; + return box; + }, + boxSetRightBottom: function(box, right, bottom) { + if (!box) return null; + box.right = right; + box.bottom = bottom; + return box; + }, + polylineNew: function(points, opts = {}) { + return { points: points || [], opts, _type: 'polyline' }; + }, + polylineDelete: function(poly) { + return null; + }, plotshape: function(condition, ...rest) { - if (globalThis.__pineRuntime) { - globalThis.__pineRuntime.plotshapes.push({ condition, args: rest }); + const rt = globalThis.__pineRuntime; + if (!rt) return condition; + const bar = rt.__barIndex | 0; + const ord = (rt.__shapeIdx = (rt.__shapeIdx | 0) + 1) - 1; + let key = 'shape_' + ord; + for (const r of rest) { + if (typeof r === 'string') { key = r; break; } + if (r && typeof r === 'object' && r.title) { key = String(r.title); break; } } + let s = rt.plotshapes[key]; + if (!s) s = rt.plotshapes[key] = { title: key, data: [] }; + s.data[bar] = !!this.__scalar(condition); return condition; }, indicator: function(title, shorttitle, overlay, opts) { @@ -542,27 +726,12 @@ const pinescript = { } return null; }, - alertcondition: function(condition, ...rest) { - if (globalThis.__pineRuntime) { - globalThis.__pineRuntime.alerts.push({ condition, args: rest }); - } - return null; - }, plotbar: function(open, high, low, close, title, color, editable, showLast) { return { open, high, low, close }; }, plotcandle: function(open, high, low, close, title, color, wickColor, borderColor, editable, showLast) { return { open, high, low, close }; }, - hline: function(_price, _title, _opts) { - return null; - }, - bgcolor: function(color, title, editable, showLast) { - return null; - }, - fill: function(series1, series2, color, title, editable) { - return null; - }, alert: function(condition, message, frequency) { console.log('Alert:', condition ? message : 'Condition not met'); return condition; @@ -828,6 +997,9 @@ const pinescript = { arrayIndexOf: function(arr, value) { return arr ? arr.indexOf(value) : -1; }, + arrayLastIndexOf: function(arr, value) { + return arr ? arr.lastIndexOf(value) : -1; + }, arraySum: function(arr) { return arr ? arr.reduce((a, b) => a + b, 0) : 0; }, @@ -840,6 +1012,42 @@ const pinescript = { arrayMax: function(arr) { return arr && arr.length > 0 ? Math.max(...arr) : null; }, + arrayStdev: function(arr) { + if (!arr || arr.length === 0) return null; + const mean = arr.reduce((a, b) => a + b, 0) / arr.length; + const variance = arr.reduce((sum, val) => sum + Math.pow(val - mean, 2), 0) / arr.length; + return Math.sqrt(variance); + }, + arrayVariance: function(arr) { + if (!arr || arr.length === 0) return null; + const mean = arr.reduce((a, b) => a + b, 0) / arr.length; + return arr.reduce((sum, val) => sum + Math.pow(val - mean, 2), 0) / arr.length; + }, + arrayCovariance: function(arr1, arr2) { + if (!arr1 || !arr2 || arr1.length === 0 || arr1.length !== arr2.length) return null; + const mean1 = arr1.reduce((a, b) => a + b, 0) / arr1.length; + const mean2 = arr2.reduce((a, b) => a + b, 0) / arr2.length; + let cov = 0; + for (let i = 0; i < arr1.length; i++) { + cov += (arr1[i] - mean1) * (arr2[i] - mean2); + } + return cov / arr1.length; + }, + mode: function(arr) { + if (!arr || arr.length === 0) return null; + const counts = new Map(); + let maxCount = 0; + let modeVal = arr[0]; + for (const val of arr) { + const c = (counts.get(val) || 0) + 1; + counts.set(val, c); + if (c > maxCount) { + maxCount = c; + modeVal = val; + } + } + return modeVal; + }, strLength: function(str) { return str ? str.length : 0; }, @@ -895,6 +1103,84 @@ const pinescript = { strReverse: function(str) { return str ? str.split('').reverse().join('') : ''; }, + strFormat: function(formatStr, ...args) { + if (!formatStr) return ''; + return formatStr.replace(/\{(\d+)\}/g, (match, idx) => { + const i = parseInt(idx, 10); + return i < args.length ? String(args[i]) : match; + }); + }, + input: function(defval, title, tooltip, inline, group) { + return defval; + }, + inputInt: function(defval = 0, title, minval, maxval, step, tooltip, inline, group) { + return defval; + }, + inputFloat: function(defval = 0.0, title, minval, maxval, step, tooltip, inline, group) { + return defval; + }, + inputBool: function(defval = false, title, tooltip, inline, group) { + return defval; + }, + inputString: function(defval = '', title, options, tooltip, inline, group) { + return defval; + }, + inputSource: function(defval = null, title, tooltip, inline, group) { + return defval; + }, + inputColor: function(defval = '#000000', title, tooltip, inline, group) { + return defval; + }, + inputTime: function(defval = 0, title, tooltip, inline, group) { + return defval; + }, + mathSign: function(value) { + return Math.sign(value); + }, + mathAvg: function(...args) { + const valid = args.filter(v => v !== null && v !== undefined && !isNaN(v)); + return valid.length > 0 ? valid.reduce((a, b) => a + b, 0) / valid.length : null; + }, + mathSum: function(series, length) { + if (!series || series.length < length) return null; + return series.slice(-length).reduce((a, b) => a + b, 0); + }, + mathRandom: function(min = 0, max = 1) { + return min + Math.random() * (max - min); + }, + mathTodegrees: function(radians) { + return radians * (180 / Math.PI); + }, + mathToradians: function(degrees) { + return degrees * (Math.PI / 180); + }, + colorT: function(color, transparency) { + if (transparency !== undefined) { + return { color, transparency }; + } + if (color && typeof color === 'object' && 'transparency' in color) { + return color.transparency; + } + return 0; + }, + tableCellSetText: function(table, column, row, text) { + return null; + }, + tableCellSetBgcolor: function(table, column, row, bgcolor) { + return null; + }, + tableMergeCells: function(table, startColumn, startRow, endColumn, endRow) { + return null; + }, + tableDelete: function(table) { + return null; + }, + tableClear: function(table, startColumn, startRow, endColumn, endRow) { + return null; + }, + maxBarsBack: function(series, length) { + return null; + }, pi: 3.141592653589793, e: 2.718281828459045, true: true, @@ -905,8 +1191,7 @@ const pinescript = { const info = { ticker: 'AAPL', tickerid: 'NASDAQ:AAPL', prefix: 'NASDAQ', root: 'AAPL', suffix: '' }; return info[type] || ''; }, - time: 1771178053528, - timenow: 1771178053528, + timenow: 1781574529258, barstate: "LAST", dividends: {}, splits: {}, @@ -923,7 +1208,7 @@ const pinescript = { return null; }, tr: function(high, low, close) { - // Pine signature: ta.tr(true) + // Handles both the boolean shorthand ta.tr(true) and explicit high/low/close arguments if (typeof high === 'boolean') { const H = globalThis.high; const L = globalThis.low; @@ -934,7 +1219,7 @@ const pinescript = { return Math.max(H[i] - L[i], Math.abs(H[i] - prevClose), Math.abs(L[i] - prevClose)); } - // Fallback: treat inputs as scalars for current bar + // When arrays are passed, compute true range from the last bar using the previous close if (Array.isArray(close)) { const prevClose = close[close.length - 2] || close[0]; const currHigh = Array.isArray(high) ? high[high.length - 1] : high; @@ -945,6 +1230,92 @@ const pinescript = { return Math.max(high - low, Math.abs(high - close), Math.abs(low - close)); }, + arrayFirst: function(arr) { + if (!arr || arr.length === 0) return null; + return arr[0]; + }, + arrayLast: function(arr) { + if (!arr || arr.length === 0) return null; + return arr[arr.length - 1]; + }, + arrayJoin: function(arr, separator = ',') { + if (!arr) return ''; + return arr.join(separator); + }, + arrayConcat: function(arr1, arr2) { + if (!arr1) return arr2 || []; + if (!arr2) return arr1; + return arr1.concat(arr2); + }, + arrayCopy: function(arr) { + if (!arr) return []; + return [...arr]; + }, + arrayBinarySearch: function(arr, value) { + if (!arr || arr.length === 0) return -1; + let lo = 0, hi = arr.length - 1; + while (lo <= hi) { + const mid = (lo + hi) >>> 1; + if (arr[mid] === value) return mid; + if (arr[mid] < value) lo = mid + 1; + else hi = mid - 1; + } + return -1; + }, + arrayRange: function(arr) { + if (!arr || arr.length === 0) return 0; + return Math.max(...arr) - Math.min(...arr); + }, + arrayMedian: function(arr) { + if (!arr || arr.length === 0) return null; + const sorted = [...arr].sort((a, b) => a - b); + const mid = Math.floor(sorted.length / 2); + return sorted.length % 2 !== 0 ? sorted[mid] : (sorted[mid - 1] + sorted[mid]) / 2; + }, + arrayMode: function(arr) { + if (!arr || arr.length === 0) return null; + const freq = {}; + let maxCount = 0; + let mode = arr[0]; + for (const v of arr) { + freq[v] = (freq[v] || 0) + 1; + if (freq[v] > maxCount) { maxCount = freq[v]; mode = v; } + } + return mode; + }, + arrayPercentile: function(arr, percentile) { + if (!arr || arr.length === 0) return null; + const sorted = [...arr].sort((a, b) => a - b); + const idx = (percentile / 100) * (sorted.length - 1); + const lo = Math.floor(idx); + const hi = Math.ceil(idx); + if (lo === hi) return sorted[lo]; + return sorted[lo] + (sorted[hi] - sorted[lo]) * (idx - lo); + }, + arrayPercentileLinearInterpolation: function(arr, percentile) { + return builtins.get('arrayPercentile')(arr, percentile); + }, + arrayPercentileNearestRank: function(arr, percentile) { + if (!arr || arr.length === 0) return null; + const sorted = [...arr].sort((a, b) => a - b); + const idx = Math.ceil((percentile / 100) * sorted.length) - 1; + return sorted[Math.max(0, idx)]; + }, + arrayAbs: function(arr) { + if (!arr) return []; + return arr.map(v => Math.abs(v)); + }, + arrayEvery: function(arr, callback) { + if (!arr) return false; + return arr.every(callback); + }, + arraySome: function(arr, callback) { + if (!arr) return false; + return arr.some(callback); + }, + requestFinancial: function() { + return null; + }, }; @@ -1101,6 +1472,115 @@ globalThis.barmerge = globalThis.barmerge || { }; +function __pineNS(real) { + + const base = real || {}; + + return new Proxy(base, { + + get(target, key) { + + if (key in target) return target[key]; + + if (typeof key === "symbol") return target[key]; + + // Unknown member: usable as a value (null), a constant, or a no-op function. + + const noop = function() { return null; }; + + noop.toString = () => ""; + + return noop; + + } + + }); + +} + +globalThis.ta = globalThis.ta || __pineNS({}); + +globalThis.math = globalThis.math || __pineNS({ pi: Math.PI, e: Math.E, phi: 1.618033988749895, rphi: 0.618033988749895 }); + +globalThis.chart = globalThis.chart || __pineNS({ point: __pineNS({}), bg_color: null, fg_color: null }); + +globalThis.line = globalThis.line || __pineNS({ style_solid: "solid", style_dashed: "dashed", style_dotted: "dotted" }); + +globalThis.box = globalThis.box || __pineNS({}); + +globalThis.label = globalThis.label || __pineNS({ style_label_down: "label_down", style_label_up: "label_up", style_none: "none" }); + +globalThis.polyline = globalThis.polyline || __pineNS({}); + +globalThis.linefill = globalThis.linefill || __pineNS({}); + +globalThis.matrix = globalThis.matrix || __pineNS({}); + +globalThis.map = globalThis.map || __pineNS({}); + +globalThis.session = globalThis.session || __pineNS({ regular: "regular", extended: "extended" }); + +globalThis.dayofweek = globalThis.dayofweek || __pineNS({ sunday: 1, monday: 2, tuesday: 3, wednesday: 4, thursday: 5, friday: 6, saturday: 7 }); + +globalThis.timeframe = __pineNS(Object.assign(globalThis.timeframe || {}, { period: (globalThis.timeframe && globalThis.timeframe.period) || "D", isintraday: false, isdaily: true, multiplier: 1 })); + +globalThis.request = __pineNS(globalThis.request || {}); + +globalThis.input = __pineNS(globalThis.input || {}); + +globalThis.adjustment = globalThis.adjustment || __pineNS({}); + +globalThis.earnings = globalThis.earnings || __pineNS({}); + +globalThis.dividends = globalThis.dividends || __pineNS({}); + +globalThis.splits = globalThis.splits || __pineNS({}); + +globalThis.currency = globalThis.currency || __pineNS({}); + +globalThis.display = globalThis.display || __pineNS({ none: "none", all: "all", pane: "pane", data_window: "data_window", status_line: "status_line", price_scale: "price_scale" }); + +globalThis.format = globalThis.format || __pineNS({ price: "price", volume: "volume", percent: "percent", mintick: "mintick", inherit: "inherit" }); + +globalThis.scale = globalThis.scale || __pineNS({ left: "left", right: "right", none: "none" }); + +globalThis.font = globalThis.font || __pineNS({ family_default: "default", family_monospace: "monospace" }); + +globalThis.xloc = globalThis.xloc || __pineNS({ bar_index: "bar_index", bar_time: "bar_time" }); + +globalThis.yloc = globalThis.yloc || __pineNS({ price: "price", abovebar: "abovebar", belowbar: "belowbar" }); + +globalThis.extend = globalThis.extend || __pineNS({ none: "none", left: "left", right: "right", both: "both" }); + +globalThis.order = globalThis.order || __pineNS({ ascending: "ascending", descending: "descending" }); + +globalThis.alert = globalThis.alert || Object.assign(function() { return null; }, { freq_once_per_bar: "once_per_bar", freq_once_per_bar_close: "once_per_bar_close", freq_all: "all" }); + +globalThis.dayofmonth = globalThis.dayofmonth || function(t) { return new Date(t || 0).getUTCDate(); }; + +globalThis.plot = globalThis.plot || __pineNS({ style_line: "line", style_linebr: "linebr", style_stepline: "stepline", style_histogram: "histogram", style_cross: "cross", style_area: "area", style_areabr: "areabr", style_columns: "columns", style_circles: "circles" }); + +globalThis.int = globalThis.int || function(x) { return x == null ? null : Math.trunc(Number(x)); }; + +globalThis.float = globalThis.float || function(x) { return x == null ? null : Number(x); }; + +globalThis.bool = globalThis.bool || function(x) { return Boolean(x); }; + +globalThis.string = globalThis.string || function(x) { return x == null ? null : String(x); }; + +globalThis.dayofweek = globalThis.dayofweek || __pineNS({ sunday: 1, monday: 2, tuesday: 3, wednesday: 4, thursday: 5, friday: 6, saturday: 7 }); + +globalThis.str = globalThis.str || __pineNS({}); + +pinescript.math = globalThis.math; + +pinescript.ta = globalThis.ta; + +pinescript.str = globalThis.str; + +pinescript.array = __pineNS(globalThis.array); + + pinescript.color = { hex: function(s) { @@ -1190,10 +1670,10 @@ pinescript.table = { }; -// Input parameters +// Script input parameters and their defaults -// Main script logic +// The transpiled script logic, called once per bar function main() { @@ -1209,22 +1689,22 @@ function main() { // Strategy: VSA Pro: Trend and Rejection 🚀 // Options: {"overlay":true,"initial_capital":100000,"default_qty_type":null,"default_qty_value":100,"currency":null,"process_orders_on_close":true} let group_trend = "Trend Ayarları (SuperTrend)"; - let st_period = input.int(10, "ST Periyodu", ({ group: group_trend })); - let st_multiplier = input.float(3, "ST Çarpanı", ({ step: 0.1, group: group_trend })); + let st_period = pinescript.inputInt(10, "ST Periyodu", ({ group: group_trend })); + let st_multiplier = pinescript.inputFloat(3, "ST Çarpanı", ({ step: 0.1, group: group_trend })); let group_volatility = "Hacim & Volatilite (VSA)"; - let atr_length = input.int(14, "ATR Uzunluğu", ({ group: group_volatility })); - let min_vol_mult = input.float(1, "Min. Hacim Eşiği (Ortalama)", ({ group: group_volatility })); - let effort_mult = input.float(2, "Yüksek Hacim (Görsel Ekleme)", ({ step: 0.1, group: group_volatility })); - let result_mult = input.float(1.5, "Gövde Boyu (Görsel Momentum)", ({ step: 0.1, group: group_volatility })); + let atr_length = pinescript.inputInt(14, "ATR Uzunluğu", ({ group: group_volatility })); + let min_vol_mult = pinescript.inputFloat(1, "Min. Hacim Eşiği (Ortalama)", ({ group: group_volatility })); + let effort_mult = pinescript.inputFloat(2, "Yüksek Hacim (Görsel Ekleme)", ({ step: 0.1, group: group_volatility })); + let result_mult = pinescript.inputFloat(1.5, "Gövde Boyu (Görsel Momentum)", ({ step: 0.1, group: group_volatility })); let group_labels = "Etiket İsimleri"; - let txt_start_long = input.string("AL", "Trend Başlangıcı (Long)", ({ group: group_labels })); - let txt_start_short = input.string("NAKİT", "Trend Bitişi (Sat)", ({ group: group_labels })); - let [superTrendVal, superTrendDir] = ta.supertrend(st_multiplier, st_period); - let isTrendNewGreen = ((superTrendDir === -1) && (pinescript.offset(superTrendDir, 1) === 1)); - let isTrendNewRed = ((superTrendDir === 1) && (pinescript.offset(superTrendDir, 1) === -1)); + let txt_start_long = pinescript.inputString("AL", "Trend Başlangıcı (Long)", ({ group: group_labels })); + let txt_start_short = pinescript.inputString("NAKİT", "Trend Bitişi (Sat)", ({ group: group_labels })); + let [superTrendVal, superTrendDir] = pinescript.unpack(pinescript.supertrend(st_multiplier, st_period), 2); + let isTrendNewGreen = ((superTrendDir === -1) && (pinescript.hist(0, superTrendDir, 1) === 1)); + let isTrendNewRed = ((superTrendDir === 1) && (pinescript.hist(1, superTrendDir, 1) === -1)); let isGreenZone = (superTrendDir === -1); let isRedZone = (superTrendDir === 1); - let atr = ta.atr(atr_length); + let atr = pinescript.atr(atr_length); let avgVol = pinescript.sma(volume, 50); let bodySize = pinescript.abs((close - open)); let isGreenCandle = (close > open); @@ -1261,16 +1741,76 @@ function main() { if (state.legend === undefined) state.legend = pinescript.table.new(pinescript.position.bottom_right, 2, 2, ({ bgcolor: pinescript.color.new(pinescript.color.black, 60), frame_color: pinescript.color.gray, border_width: 1, force_overlay: true })); if (barstate.islast) { pinescript.table.cell(state.legend, 0, 0, "STRATEJİ DURUMU", ({ text_color: pinescript.color.yellow })); - table.merge_cells(state.legend, 0, 0, 1, 0); + pinescript.tableMergeCells(state.legend, 0, 0, 1, 0); let statusText = (isGreenZone ? "ALIMDA (Yükseliş)" : "NAKİTTE (Düşüş)"); let statusColor = (isGreenZone ? pinescript.color.green : pinescript.color.red); pinescript.table.cell(state.legend, 0, 1, statusText, ({ text_color: statusColor, text_size: pinescript.size.normal })); - table.merge_cells(state.legend, 0, 1, 1, 1); + pinescript.tableMergeCells(state.legend, 0, 1, 1, 1); } } -// Export for use -export { main +// The bar-by-bar engine: runs main() once per bar over the dataset. +function run(data, options = {}) { + data = data || {}; + const inClose = (data.close || []).map(Number); + const n = inClose.length; + const inOpen = (data.open || inClose).map(Number); + const inHigh = (data.high || inClose).map(Number); + const inLow = (data.low || inClose).map(Number); + const inVol = (data.volume || new Array(n).fill(0)).map(Number); + const inTime = (data.time || inClose.map((_, i) => i)).map(Number); + + // Reset all persistent and per-run state so repeated runs are independent. + globalThis.__pineState = {}; + globalThis.__pineRuntime = { plots: {}, plotshapes: {}, alerts: [], bars: n, inputs: options.inputs || {} }; + pinescript.__rt = {}; + pinescript.__bar = 0; + + // Growing OHLCV windows: each is a SeriesArray that gains one element per bar, + // so window built-ins like sma and highest see history up to the current bar. + const O = pinescript.asSeries([]), H = pinescript.asSeries([]), L = pinescript.asSeries([]); + const C = pinescript.asSeries([]), V = pinescript.asSeries([]), T = pinescript.asSeries([]); + const HL2 = pinescript.asSeries([]), HLC3 = pinescript.asSeries([]), OHLC4 = pinescript.asSeries([]); + globalThis.open = O; globalThis.high = H; globalThis.low = L; globalThis.close = C; + globalThis.volume = V; globalThis.time = T; + globalThis.hl2 = HL2; globalThis.hlc3 = HLC3; globalThis.ohlc4 = OHLC4; + globalThis.syminfo = globalThis.syminfo || { tickerid: 'SYNTHETIC', ticker: 'SYNTHETIC', mintick: 0.01 }; + + const rt = globalThis.__pineRuntime; + for (let i = 0; i < n; i++) { + O.push(inOpen[i]); H.push(inHigh[i]); L.push(inLow[i]); C.push(inClose[i]); V.push(inVol[i]); T.push(inTime[i]); + HL2.push((inHigh[i] + inLow[i]) / 2); + HLC3.push((inHigh[i] + inLow[i] + inClose[i]) / 3); + OHLC4.push((inOpen[i] + inHigh[i] + inLow[i] + inClose[i]) / 4); + + pinescript.__bar = i; + rt.__barIndex = i; + rt.__plotIdx = 0; + rt.__shapeIdx = 0; + globalThis.bar_index = i; + globalThis.last_bar_index = n - 1; + globalThis.barstate = { + isfirst: i === 0, islast: i === n - 1, isrealtime: false, ishistory: true, + isconfirmed: true, isnew: true, islastconfirmedhistory: i === n - 1, + }; + + main(); + } + + // Backfill skipped bars so every output series has exactly n entries. + for (const k of Object.keys(rt.plots)) { + const d = rt.plots[k].data; + for (let i = 0; i < n; i++) if (d[i] === undefined) d[i] = null; + } + for (const k of Object.keys(rt.plotshapes)) { + const d = rt.plotshapes[k].data; + for (let i = 0; i < n; i++) if (d[i] === undefined) d[i] = false; + } + return rt; +} + + +export { main, run }; diff --git a/converted/Volatility_Contraction_Score.js b/converted/Volatility_Contraction_Score.js index 417b655..2e6cad8 100644 --- a/converted/Volatility_Contraction_Score.js +++ b/converted/Volatility_Contraction_Score.js @@ -1,12 +1,63 @@ -// PineScript to JavaScript Transpiled Code +// Auto-generated JavaScript from PineScript source -// Generated automatically +// Do not edit by hand -- re-run the converter instead -// Built-in PineScript functions +// All the built-in PineScript functions that scripts depend on at runtime const pinescript = { + __scalar: function(v) { + if (v === null || v === undefined) return v; + if (typeof v === 'object' && typeof v.valueOf === 'function') { + const s = v.valueOf(); + if (typeof s === 'number' || typeof s === 'boolean' || s === null) return s; + } + return v; + }, + series: function(id, value) { + const rt = this.__rt || (this.__rt = {}); + const store = rt.series || (rt.series = {}); + let buf = store[id]; + if (!buf) buf = store[id] = []; + buf[this.__bar | 0] = this.__scalar(value); + return buf; + }, + hist: function(id, value, n) { + const buf = this.series(id, value); + const idx = (this.__bar | 0) - (n | 0); + return idx >= 0 && idx < buf.length ? buf[idx] : null; + }, + time: function(timeframe, session) { + const t = globalThis.time; + if (t && typeof t.valueOf === 'function') return t.valueOf(); + return Array.isArray(t) ? t[t.length - 1] : t; + }, + sign: function(x) { + if (x === null || x === undefined || (typeof x === 'number' && isNaN(x))) return null; + return Math.sign(Number(x)); + }, + unpack: function(value, count) { + if (Array.isArray(value)) return value; + if (value != null && typeof value[Symbol.iterator] === 'function') return Array.from(value); + return new Array(count || 0).fill(null); + }, + alertcondition: function(condition, ...rest) { + if (globalThis.__pineRuntime) { + globalThis.__pineRuntime.alerts.push({ condition, args: rest }); + } + return null; + }, + barcolor: function(color) { return null; }, + bgcolor: function(color, title, editable, showLast) { + return null; + }, + fill: function(series1, series2, color, title, editable) { + return null; + }, + hline: function(_price, _title, _opts) { + return null; + }, sma: function(series, length) { if (series === null || series === undefined) return null; if (!Array.isArray(series)) series = [series]; @@ -84,6 +135,13 @@ const pinescript = { } return weightSum > 0 ? sum / weightSum : null; }, + swma: function(series) { + if (series === null || series === undefined) return null; + if (!Array.isArray(series)) series = [series]; + if (series.length < 4) return null; + const s = series.slice(-4); + return (s[0] * 1 + s[1] * 2 + s[2] * 2 + s[3] * 1) / 6; + }, atr: function(high, low, close, length = 14) { if (!Array.isArray(high) || !Array.isArray(low) || !Array.isArray(close)) return null; if (high.length < length + 1 || low.length < length + 1 || close.length < length + 1) return null; @@ -114,6 +172,53 @@ const pinescript = { const atr = builtins.get('atr')(high, low, close, length); return { middle: sma, upper: sma + mult * atr, lower: sma - mult * atr }; }, + supertrend: function(factor, atrPeriod, high, low, close) { + if (!Array.isArray(high) || !Array.isArray(low) || !Array.isArray(close)) return [null, null]; + if (high.length < atrPeriod + 1) return [null, null]; + const atrVal = builtins.get('atr')(high, low, close, atrPeriod); + if (atrVal === null) return [null, null]; + const lastIdx = close.length - 1; + const hl2 = (high[lastIdx] + low[lastIdx]) / 2; + const upperBand = hl2 + factor * atrVal; + const lowerBand = hl2 - factor * atrVal; + // Determine direction: 1 means downtrend (price below band), -1 means uptrend + const direction = close[lastIdx] > upperBand ? -1 : close[lastIdx] < lowerBand ? 1 : -1; + const supertrendValue = direction === -1 ? lowerBand : upperBand; + return [supertrendValue, direction]; + }, + ta_supertrend: function(factor, atrPeriod) { + const H = globalThis.high; + const L = globalThis.low; + const C = globalThis.close; + return builtins.get('supertrend')(factor, atrPeriod, H, L, C); + }, + dmi: function(diLength, adxSmoothing, high, low, close) { + if (!Array.isArray(high) || !Array.isArray(low) || !Array.isArray(close)) return { plusDI: null, minusDI: null, adx: null }; + if (high.length < diLength + 1) return { plusDI: null, minusDI: null, adx: null }; + let plusDMSum = 0, minusDMSum = 0, trSum = 0; + for (let i = 1; i <= diLength; i++) { + const idx = high.length - 1 - diLength + i; + const upMove = high[idx] - high[idx - 1]; + const downMove = low[idx - 1] - low[idx]; + plusDMSum += (upMove > downMove && upMove > 0) ? upMove : 0; + minusDMSum += (downMove > upMove && downMove > 0) ? downMove : 0; + const tr = Math.max( + high[idx] - low[idx], + Math.abs(high[idx] - close[idx - 1]), + Math.abs(low[idx] - close[idx - 1]) + ); + trSum += tr; + } + const plusDI = trSum > 0 ? (plusDMSum / trSum) * 100 : 0; + const minusDI = trSum > 0 ? (minusDMSum / trSum) * 100 : 0; + const diSum = plusDI + minusDI; + const dx = diSum > 0 ? Math.abs(plusDI - minusDI) / diSum * 100 : 0; + return { plusDI, minusDI, adx: dx }; + }, + adx: function(diLength, adxSmoothing, high, low, close) { + const result = builtins.get('dmi')(diLength, adxSmoothing, high, low, close); + return result ? result.adx : null; + }, rsi: function(close, length = 14) { if (close === null || close === undefined) return null; if (!Array.isArray(close)) close = [close]; @@ -171,9 +276,7 @@ const pinescript = { return { macd: macdLine, signal: signalLine, histogram: histogram }; }, cci: function(a, b, c, d) { - // Pine signatures: - // - ta.cci(source, length) - // - ta.cci(high, low, close, length) + // Supports both ta.cci(source, length) and ta.cci(high, low, close, length) signatures let source; let length; if (Array.isArray(a) && typeof b === 'number' && c === undefined) { @@ -304,6 +407,15 @@ const pinescript = { stdev: function(source, length) { return Math.sqrt(builtins.get('variance')(source, length)); }, + median: function(source, length) { + if (!source || source.length < length) return null; + const sorted = [...source.slice(-length)].sort((a, b) => a - b); + const mid = Math.floor(sorted.length / 2); + return sorted.length % 2 !== 0 ? sorted[mid] : (sorted[mid - 1] + sorted[mid]) / 2; + }, + ta_median: function(source, length) { + return builtins.get('median')(source, length); + }, hl2: function(high, low) { return high && low ? (high[high.length - 1] + low[low.length - 1]) / 2 : null; }, @@ -420,6 +532,24 @@ const pinescript = { return (series1[i - 1] < series2[i - 1] && series1[i] >= series2[i]) || (series1[i - 1] > series2[i - 1] && series1[i] <= series2[i]); }, + crossover: function(series1, series2) { + if (series1 === null || series1 === undefined) return false; + if (series2 === null || series2 === undefined) return false; + if (!Array.isArray(series1)) series1 = [series1]; + if (!Array.isArray(series2)) series2 = [series2]; + if (series1.length < 2 || series2.length < 2) return false; + const i = series1.length - 1; + return series1[i - 1] < series2[i - 1] && series1[i] >= series2[i]; + }, + crossunder: function(series1, series2) { + if (series1 === null || series1 === undefined) return false; + if (series2 === null || series2 === undefined) return false; + if (!Array.isArray(series1)) series1 = [series1]; + if (!Array.isArray(series2)) series2 = [series2]; + if (series1.length < 2 || series2.length < 2) return false; + const i = series1.length - 1; + return series1[i - 1] > series2[i - 1] && series1[i] <= series2[i]; + }, offset: function(series, offset) { if (!Array.isArray(series)) return null; const idx = series.length - 1 - offset; @@ -500,12 +630,14 @@ const pinescript = { return builtins.get('nz')(value, replacement); }, plot: function(series, title = '', color = null, linewidth = 1) { - globalThis.__pineRuntime.plots.push({ - series, - title, - color, - linewidth - }); + const rt = globalThis.__pineRuntime; + if (!rt) return series; + const bar = rt.__barIndex | 0; + const ord = (rt.__plotIdx = (rt.__plotIdx | 0) + 1) - 1; + const key = (title && String(title)) || ('plot_' + ord); + let p = rt.plots[key]; + if (!p) p = rt.plots[key] = { title: key, color, linewidth, data: [] }; + p.data[bar] = this.__scalar(series); return series; }, lineNew: function(x1, y1, x2, y2, opts = {}) { @@ -514,20 +646,72 @@ const pinescript = { lineDelete: function(l) { return null; }, - labelNew: function(...args) { - if (globalThis.__pineRuntime) { - globalThis.__pineRuntime.labels = globalThis.__pineRuntime.labels || []; - globalThis.__pineRuntime.labels.push({ args }); - } - return { args }; + lineSetXY: function(line, point, x, y) { + if (!line) return null; + if (point === 0 || point === 'x1') { line.x1 = x; line.y1 = y; } + else { line.x2 = x; line.y2 = y; } + return line; + }, + lineGetX: function(line, point) { + if (!line) return null; + return point === 0 || point === 'x1' ? line.x1 : line.x2; + }, + lineGetY: function(line, point) { + if (!line) return null; + return point === 0 || point === 'y1' ? line.y1 : line.y2; + }, + labelNew: function(x, y, text = '', opts = {}) { + return { x, y, text, opts, _type: 'label' }; }, labelDelete: function(l) { return null; }, + labelSetText: function(label, text) { + if (!label) return null; + label.text = text; + return label; + }, + labelGetText: function(label) { + if (!label) return ''; + return label.text || ''; + }, + boxNew: function(left, top, right, bottom, opts = {}) { + return { left, top, right, bottom, opts, _type: 'box' }; + }, + boxDelete: function(box) { + return null; + }, + boxSetLeftTop: function(box, left, top) { + if (!box) return null; + box.left = left; + box.top = top; + return box; + }, + boxSetRightBottom: function(box, right, bottom) { + if (!box) return null; + box.right = right; + box.bottom = bottom; + return box; + }, + polylineNew: function(points, opts = {}) { + return { points: points || [], opts, _type: 'polyline' }; + }, + polylineDelete: function(poly) { + return null; + }, plotshape: function(condition, ...rest) { - if (globalThis.__pineRuntime) { - globalThis.__pineRuntime.plotshapes.push({ condition, args: rest }); + const rt = globalThis.__pineRuntime; + if (!rt) return condition; + const bar = rt.__barIndex | 0; + const ord = (rt.__shapeIdx = (rt.__shapeIdx | 0) + 1) - 1; + let key = 'shape_' + ord; + for (const r of rest) { + if (typeof r === 'string') { key = r; break; } + if (r && typeof r === 'object' && r.title) { key = String(r.title); break; } } + let s = rt.plotshapes[key]; + if (!s) s = rt.plotshapes[key] = { title: key, data: [] }; + s.data[bar] = !!this.__scalar(condition); return condition; }, indicator: function(title, shorttitle, overlay, opts) { @@ -542,27 +726,12 @@ const pinescript = { } return null; }, - alertcondition: function(condition, ...rest) { - if (globalThis.__pineRuntime) { - globalThis.__pineRuntime.alerts.push({ condition, args: rest }); - } - return null; - }, plotbar: function(open, high, low, close, title, color, editable, showLast) { return { open, high, low, close }; }, plotcandle: function(open, high, low, close, title, color, wickColor, borderColor, editable, showLast) { return { open, high, low, close }; }, - hline: function(_price, _title, _opts) { - return null; - }, - bgcolor: function(color, title, editable, showLast) { - return null; - }, - fill: function(series1, series2, color, title, editable) { - return null; - }, alert: function(condition, message, frequency) { console.log('Alert:', condition ? message : 'Condition not met'); return condition; @@ -828,6 +997,9 @@ const pinescript = { arrayIndexOf: function(arr, value) { return arr ? arr.indexOf(value) : -1; }, + arrayLastIndexOf: function(arr, value) { + return arr ? arr.lastIndexOf(value) : -1; + }, arraySum: function(arr) { return arr ? arr.reduce((a, b) => a + b, 0) : 0; }, @@ -840,6 +1012,42 @@ const pinescript = { arrayMax: function(arr) { return arr && arr.length > 0 ? Math.max(...arr) : null; }, + arrayStdev: function(arr) { + if (!arr || arr.length === 0) return null; + const mean = arr.reduce((a, b) => a + b, 0) / arr.length; + const variance = arr.reduce((sum, val) => sum + Math.pow(val - mean, 2), 0) / arr.length; + return Math.sqrt(variance); + }, + arrayVariance: function(arr) { + if (!arr || arr.length === 0) return null; + const mean = arr.reduce((a, b) => a + b, 0) / arr.length; + return arr.reduce((sum, val) => sum + Math.pow(val - mean, 2), 0) / arr.length; + }, + arrayCovariance: function(arr1, arr2) { + if (!arr1 || !arr2 || arr1.length === 0 || arr1.length !== arr2.length) return null; + const mean1 = arr1.reduce((a, b) => a + b, 0) / arr1.length; + const mean2 = arr2.reduce((a, b) => a + b, 0) / arr2.length; + let cov = 0; + for (let i = 0; i < arr1.length; i++) { + cov += (arr1[i] - mean1) * (arr2[i] - mean2); + } + return cov / arr1.length; + }, + mode: function(arr) { + if (!arr || arr.length === 0) return null; + const counts = new Map(); + let maxCount = 0; + let modeVal = arr[0]; + for (const val of arr) { + const c = (counts.get(val) || 0) + 1; + counts.set(val, c); + if (c > maxCount) { + maxCount = c; + modeVal = val; + } + } + return modeVal; + }, strLength: function(str) { return str ? str.length : 0; }, @@ -895,6 +1103,84 @@ const pinescript = { strReverse: function(str) { return str ? str.split('').reverse().join('') : ''; }, + strFormat: function(formatStr, ...args) { + if (!formatStr) return ''; + return formatStr.replace(/\{(\d+)\}/g, (match, idx) => { + const i = parseInt(idx, 10); + return i < args.length ? String(args[i]) : match; + }); + }, + input: function(defval, title, tooltip, inline, group) { + return defval; + }, + inputInt: function(defval = 0, title, minval, maxval, step, tooltip, inline, group) { + return defval; + }, + inputFloat: function(defval = 0.0, title, minval, maxval, step, tooltip, inline, group) { + return defval; + }, + inputBool: function(defval = false, title, tooltip, inline, group) { + return defval; + }, + inputString: function(defval = '', title, options, tooltip, inline, group) { + return defval; + }, + inputSource: function(defval = null, title, tooltip, inline, group) { + return defval; + }, + inputColor: function(defval = '#000000', title, tooltip, inline, group) { + return defval; + }, + inputTime: function(defval = 0, title, tooltip, inline, group) { + return defval; + }, + mathSign: function(value) { + return Math.sign(value); + }, + mathAvg: function(...args) { + const valid = args.filter(v => v !== null && v !== undefined && !isNaN(v)); + return valid.length > 0 ? valid.reduce((a, b) => a + b, 0) / valid.length : null; + }, + mathSum: function(series, length) { + if (!series || series.length < length) return null; + return series.slice(-length).reduce((a, b) => a + b, 0); + }, + mathRandom: function(min = 0, max = 1) { + return min + Math.random() * (max - min); + }, + mathTodegrees: function(radians) { + return radians * (180 / Math.PI); + }, + mathToradians: function(degrees) { + return degrees * (Math.PI / 180); + }, + colorT: function(color, transparency) { + if (transparency !== undefined) { + return { color, transparency }; + } + if (color && typeof color === 'object' && 'transparency' in color) { + return color.transparency; + } + return 0; + }, + tableCellSetText: function(table, column, row, text) { + return null; + }, + tableCellSetBgcolor: function(table, column, row, bgcolor) { + return null; + }, + tableMergeCells: function(table, startColumn, startRow, endColumn, endRow) { + return null; + }, + tableDelete: function(table) { + return null; + }, + tableClear: function(table, startColumn, startRow, endColumn, endRow) { + return null; + }, + maxBarsBack: function(series, length) { + return null; + }, pi: 3.141592653589793, e: 2.718281828459045, true: true, @@ -905,8 +1191,7 @@ const pinescript = { const info = { ticker: 'AAPL', tickerid: 'NASDAQ:AAPL', prefix: 'NASDAQ', root: 'AAPL', suffix: '' }; return info[type] || ''; }, - time: 1771178053290, - timenow: 1771178053290, + timenow: 1781574528980, barstate: "LAST", dividends: {}, splits: {}, @@ -923,7 +1208,7 @@ const pinescript = { return null; }, tr: function(high, low, close) { - // Pine signature: ta.tr(true) + // Handles both the boolean shorthand ta.tr(true) and explicit high/low/close arguments if (typeof high === 'boolean') { const H = globalThis.high; const L = globalThis.low; @@ -934,7 +1219,7 @@ const pinescript = { return Math.max(H[i] - L[i], Math.abs(H[i] - prevClose), Math.abs(L[i] - prevClose)); } - // Fallback: treat inputs as scalars for current bar + // When arrays are passed, compute true range from the last bar using the previous close if (Array.isArray(close)) { const prevClose = close[close.length - 2] || close[0]; const currHigh = Array.isArray(high) ? high[high.length - 1] : high; @@ -945,6 +1230,92 @@ const pinescript = { return Math.max(high - low, Math.abs(high - close), Math.abs(low - close)); }, + arrayFirst: function(arr) { + if (!arr || arr.length === 0) return null; + return arr[0]; + }, + arrayLast: function(arr) { + if (!arr || arr.length === 0) return null; + return arr[arr.length - 1]; + }, + arrayJoin: function(arr, separator = ',') { + if (!arr) return ''; + return arr.join(separator); + }, + arrayConcat: function(arr1, arr2) { + if (!arr1) return arr2 || []; + if (!arr2) return arr1; + return arr1.concat(arr2); + }, + arrayCopy: function(arr) { + if (!arr) return []; + return [...arr]; + }, + arrayBinarySearch: function(arr, value) { + if (!arr || arr.length === 0) return -1; + let lo = 0, hi = arr.length - 1; + while (lo <= hi) { + const mid = (lo + hi) >>> 1; + if (arr[mid] === value) return mid; + if (arr[mid] < value) lo = mid + 1; + else hi = mid - 1; + } + return -1; + }, + arrayRange: function(arr) { + if (!arr || arr.length === 0) return 0; + return Math.max(...arr) - Math.min(...arr); + }, + arrayMedian: function(arr) { + if (!arr || arr.length === 0) return null; + const sorted = [...arr].sort((a, b) => a - b); + const mid = Math.floor(sorted.length / 2); + return sorted.length % 2 !== 0 ? sorted[mid] : (sorted[mid - 1] + sorted[mid]) / 2; + }, + arrayMode: function(arr) { + if (!arr || arr.length === 0) return null; + const freq = {}; + let maxCount = 0; + let mode = arr[0]; + for (const v of arr) { + freq[v] = (freq[v] || 0) + 1; + if (freq[v] > maxCount) { maxCount = freq[v]; mode = v; } + } + return mode; + }, + arrayPercentile: function(arr, percentile) { + if (!arr || arr.length === 0) return null; + const sorted = [...arr].sort((a, b) => a - b); + const idx = (percentile / 100) * (sorted.length - 1); + const lo = Math.floor(idx); + const hi = Math.ceil(idx); + if (lo === hi) return sorted[lo]; + return sorted[lo] + (sorted[hi] - sorted[lo]) * (idx - lo); + }, + arrayPercentileLinearInterpolation: function(arr, percentile) { + return builtins.get('arrayPercentile')(arr, percentile); + }, + arrayPercentileNearestRank: function(arr, percentile) { + if (!arr || arr.length === 0) return null; + const sorted = [...arr].sort((a, b) => a - b); + const idx = Math.ceil((percentile / 100) * sorted.length) - 1; + return sorted[Math.max(0, idx)]; + }, + arrayAbs: function(arr) { + if (!arr) return []; + return arr.map(v => Math.abs(v)); + }, + arrayEvery: function(arr, callback) { + if (!arr) return false; + return arr.every(callback); + }, + arraySome: function(arr, callback) { + if (!arr) return false; + return arr.some(callback); + }, + requestFinancial: function() { + return null; + }, }; @@ -1101,6 +1472,115 @@ globalThis.barmerge = globalThis.barmerge || { }; +function __pineNS(real) { + + const base = real || {}; + + return new Proxy(base, { + + get(target, key) { + + if (key in target) return target[key]; + + if (typeof key === "symbol") return target[key]; + + // Unknown member: usable as a value (null), a constant, or a no-op function. + + const noop = function() { return null; }; + + noop.toString = () => ""; + + return noop; + + } + + }); + +} + +globalThis.ta = globalThis.ta || __pineNS({}); + +globalThis.math = globalThis.math || __pineNS({ pi: Math.PI, e: Math.E, phi: 1.618033988749895, rphi: 0.618033988749895 }); + +globalThis.chart = globalThis.chart || __pineNS({ point: __pineNS({}), bg_color: null, fg_color: null }); + +globalThis.line = globalThis.line || __pineNS({ style_solid: "solid", style_dashed: "dashed", style_dotted: "dotted" }); + +globalThis.box = globalThis.box || __pineNS({}); + +globalThis.label = globalThis.label || __pineNS({ style_label_down: "label_down", style_label_up: "label_up", style_none: "none" }); + +globalThis.polyline = globalThis.polyline || __pineNS({}); + +globalThis.linefill = globalThis.linefill || __pineNS({}); + +globalThis.matrix = globalThis.matrix || __pineNS({}); + +globalThis.map = globalThis.map || __pineNS({}); + +globalThis.session = globalThis.session || __pineNS({ regular: "regular", extended: "extended" }); + +globalThis.dayofweek = globalThis.dayofweek || __pineNS({ sunday: 1, monday: 2, tuesday: 3, wednesday: 4, thursday: 5, friday: 6, saturday: 7 }); + +globalThis.timeframe = __pineNS(Object.assign(globalThis.timeframe || {}, { period: (globalThis.timeframe && globalThis.timeframe.period) || "D", isintraday: false, isdaily: true, multiplier: 1 })); + +globalThis.request = __pineNS(globalThis.request || {}); + +globalThis.input = __pineNS(globalThis.input || {}); + +globalThis.adjustment = globalThis.adjustment || __pineNS({}); + +globalThis.earnings = globalThis.earnings || __pineNS({}); + +globalThis.dividends = globalThis.dividends || __pineNS({}); + +globalThis.splits = globalThis.splits || __pineNS({}); + +globalThis.currency = globalThis.currency || __pineNS({}); + +globalThis.display = globalThis.display || __pineNS({ none: "none", all: "all", pane: "pane", data_window: "data_window", status_line: "status_line", price_scale: "price_scale" }); + +globalThis.format = globalThis.format || __pineNS({ price: "price", volume: "volume", percent: "percent", mintick: "mintick", inherit: "inherit" }); + +globalThis.scale = globalThis.scale || __pineNS({ left: "left", right: "right", none: "none" }); + +globalThis.font = globalThis.font || __pineNS({ family_default: "default", family_monospace: "monospace" }); + +globalThis.xloc = globalThis.xloc || __pineNS({ bar_index: "bar_index", bar_time: "bar_time" }); + +globalThis.yloc = globalThis.yloc || __pineNS({ price: "price", abovebar: "abovebar", belowbar: "belowbar" }); + +globalThis.extend = globalThis.extend || __pineNS({ none: "none", left: "left", right: "right", both: "both" }); + +globalThis.order = globalThis.order || __pineNS({ ascending: "ascending", descending: "descending" }); + +globalThis.alert = globalThis.alert || Object.assign(function() { return null; }, { freq_once_per_bar: "once_per_bar", freq_once_per_bar_close: "once_per_bar_close", freq_all: "all" }); + +globalThis.dayofmonth = globalThis.dayofmonth || function(t) { return new Date(t || 0).getUTCDate(); }; + +globalThis.plot = globalThis.plot || __pineNS({ style_line: "line", style_linebr: "linebr", style_stepline: "stepline", style_histogram: "histogram", style_cross: "cross", style_area: "area", style_areabr: "areabr", style_columns: "columns", style_circles: "circles" }); + +globalThis.int = globalThis.int || function(x) { return x == null ? null : Math.trunc(Number(x)); }; + +globalThis.float = globalThis.float || function(x) { return x == null ? null : Number(x); }; + +globalThis.bool = globalThis.bool || function(x) { return Boolean(x); }; + +globalThis.string = globalThis.string || function(x) { return x == null ? null : String(x); }; + +globalThis.dayofweek = globalThis.dayofweek || __pineNS({ sunday: 1, monday: 2, tuesday: 3, wednesday: 4, thursday: 5, friday: 6, saturday: 7 }); + +globalThis.str = globalThis.str || __pineNS({}); + +pinescript.math = globalThis.math; + +pinescript.ta = globalThis.ta; + +pinescript.str = globalThis.str; + +pinescript.array = __pineNS(globalThis.array); + + pinescript.color = { hex: function(s) { @@ -1190,10 +1670,10 @@ pinescript.table = { }; -// Input parameters +// Script input parameters and their defaults -// Main script logic +// The transpiled script logic, called once per bar function main() { @@ -1210,32 +1690,32 @@ function main() { // Options: {"shorttitle":"VCS","overlay":false} let grpCore = "VCP Logic Settings"; let grpScreen = "Screener Data for Pine Screener"; - let lenShortInput = input.int(13, "Short Term", ({ minval: 3, group: grpCore })); - let lenLongInput = input.int(63, "Long Term", ({ minval: 20, group: grpCore })); - let lenVolInput = input.int(50, "Volume Average (Logic)", ({ minval: 10, group: grpCore })); + let lenShortInput = pinescript.inputInt(13, "Short Term", ({ minval: 3, group: grpCore })); + let lenLongInput = pinescript.inputInt(63, "Long Term", ({ minval: 20, group: grpCore })); + let lenVolInput = pinescript.inputInt(50, "Volume Average (Logic)", ({ minval: 10, group: grpCore })); let sensitivity = 2; let grpTrend = "Trend Filter"; - let trendPenaltyWeight = input.float(1, "Trend Penalty Strength", ({ minval: 0, maxval: 5, step: 0.5, group: grpTrend })); + let trendPenaltyWeight = pinescript.inputFloat(1, "Trend Penalty Strength", ({ minval: 0, maxval: 5, step: 0.5, group: grpTrend })); let grpStruct = "Structure Settings"; - let hlLookbackInput = input.int(63, "Structure Lookback", ({ minval: 20, group: grpStruct })); - let penaltyFactor = input.float(0.75, "Penalty for Lower Low", ({ minval: 0, maxval: 1, step: 0.05, group: grpStruct })); + let hlLookbackInput = pinescript.inputInt(63, "Structure Lookback", ({ minval: 20, group: grpStruct })); + let penaltyFactor = pinescript.inputFloat(0.75, "Penalty for Lower Low", ({ minval: 0, maxval: 1, step: 0.05, group: grpStruct })); let grpBonus = "Consistency Weight"; - let bonusMax = input.int(15, "Max Consistency Points", ({ minval: 5, maxval: 30, group: grpBonus })); - let scr_vol_len = input.int(50, "Avg Volume Length", ({ minval: 10, group: grpScreen })); + let bonusMax = pinescript.inputInt(15, "Max Consistency Points", ({ minval: 5, maxval: 30, group: grpBonus })); + let scr_vol_len = pinescript.inputInt(50, "Avg Volume Length", ({ minval: 10, group: grpScreen })); let rs_benchmark = input.symbol("SPY", "RS Benchmark", ({ group: grpScreen })); - let rs_len = input.int(21, "RS Period", ({ group: grpScreen })); - let adr_len = input.int(20, "ADR Period", ({ group: grpScreen })); + let rs_len = pinescript.inputInt(21, "RS Period", ({ group: grpScreen })); + let adr_len = pinescript.inputInt(20, "ADR Period", ({ group: grpScreen })); let grpColor = "Color & Thresholds"; - let thHigh = input.int(80, "Level 2 (High)", ({ minval: 60, maxval: 100, group: grpColor })); - let thLow = input.int(60, "Level 1 (Base)", ({ minval: 0, maxval: 80, group: grpColor })); - let cHigh = input.color(pinescript.color.hex("#00E676"), "Level 2 Color", ({ group: grpColor })); - let cLow = input.color(pinescript.color.hex("#2962FF"), "Level 1 Color", ({ group: grpColor })); - let cBase = input.color(pinescript.color.hex("#787B86"), "Base Color (<60)", ({ group: grpColor })); + let thHigh = pinescript.inputInt(80, "Level 2 (High)", ({ minval: 60, maxval: 100, group: grpColor })); + let thLow = pinescript.inputInt(60, "Level 1 (Base)", ({ minval: 0, maxval: 80, group: grpColor })); + let cHigh = pinescript.inputColor(pinescript.color.hex("#00E676"), "Level 2 Color", ({ group: grpColor })); + let cLow = pinescript.inputColor(pinescript.color.hex("#2962FF"), "Level 1 Color", ({ group: grpColor })); + let cBase = pinescript.inputColor(pinescript.color.hex("#787B86"), "Base Color (<60)", ({ group: grpColor })); let barCount = (bar_index + 1); let lenLong = pinescript.min(barCount, lenLongInput); let lenShort = pinescript.min(barCount, lenShortInput); let lenVol = pinescript.min(barCount, lenVolInput); - let trVal = ta.tr(true); + let trVal = pinescript.tr(true); let trShort = pinescript.sma(trVal, lenShort); let trLongAvg = pinescript.sma(trVal, lenLong); let ratioATR = (trShort / pinescript.max(trLongAvg, 0.000001)); @@ -1245,17 +1725,17 @@ function main() { let volAvg = pinescript.sma(volume, lenVol); let volShortAvg = pinescript.sma(volume, 5); let volRatio = (volShortAvg / pinescript.max(volAvg, 1)); - let netChange = pinescript.abs((close - pinescript.offset(close, lenShort))); - let totalTravel = math.sum(ta.tr(true), lenShort); + let netChange = pinescript.abs((close - pinescript.hist(0, close, lenShort))); + let totalTravel = pinescript.sum(pinescript.series(1, pinescript.tr(true)), lenShort); let efficiency = (netChange / pinescript.max(totalTravel, 0.000001)); let trendFactor = pinescript.max(0, (1 - (efficiency * trendPenaltyWeight))); - let lowRecent = pinescript.lowest(low, lenShort); + let lowRecent = pinescript.lowest(pinescript.series(2, low), lenShort); let barsBeforeShort = (barCount - lenShortInput); let hasHistory = (barsBeforeShort > 0); let lowBase = 0; let isHigherLow = true; if (hasHistory) { - lowBase = pinescript.offset(pinescript.lowest(low, hlLookbackInput), lenShortInput); + lowBase = pinescript.hist(3, pinescript.lowest(pinescript.series(4, low), hlLookbackInput), lenShortInput); isHigherLow = (lowRecent >= lowBase); } let s_atr = (pinescript.max(0, (1 - pinescript.nz(ratioATR, 1))) * sensitivity); @@ -1279,19 +1759,79 @@ function main() { pinescript.hline(thLow, "Threshold", ({ color: pinescript.color.gray, linestyle: hline.style_dotted })); let scr_vol_val = pinescript.sma(volume, scr_vol_len); pinescript.plot(scr_vol_val, "Avg Vol", ({ display: display.data_window })); - let mCapBasic = request.financial(syminfo.tickerid, "MARKET_CAP_BASIC", "D", ({ ignore_invalid_symbol: true })); + let mCapBasic = pinescript.requestFinancial(syminfo.tickerid, "MARKET_CAP_BASIC", "D", ({ ignore_invalid_symbol: true })); let mCapFinal = (pinescript.na(mCapBasic) ? (pinescript.nz(syminfo.shares_outstanding_total) * close) : mCapBasic); pinescript.plot(mCapFinal, "Market Cap", ({ display: display.data_window })); let adr_val = (100 * (pinescript.sma((high / low), adr_len) - 1)); pinescript.plot(adr_val, "ADR %", ({ display: display.data_window })); let benchmark_close = pinescript.requestSecurity(rs_benchmark, timeframe.period, close, ({ ignore_invalid_symbol: true })); let rs_ratio = (close / benchmark_close); - let rs_rank = ta.percentrank(rs_ratio, rs_len); + let rs_rank = pinescript.percentrank(rs_ratio, rs_len); pinescript.plot(rs_rank, "Relative Strength", ({ display: display.data_window })); } -// Export for use -export { main +// The bar-by-bar engine: runs main() once per bar over the dataset. +function run(data, options = {}) { + data = data || {}; + const inClose = (data.close || []).map(Number); + const n = inClose.length; + const inOpen = (data.open || inClose).map(Number); + const inHigh = (data.high || inClose).map(Number); + const inLow = (data.low || inClose).map(Number); + const inVol = (data.volume || new Array(n).fill(0)).map(Number); + const inTime = (data.time || inClose.map((_, i) => i)).map(Number); + + // Reset all persistent and per-run state so repeated runs are independent. + globalThis.__pineState = {}; + globalThis.__pineRuntime = { plots: {}, plotshapes: {}, alerts: [], bars: n, inputs: options.inputs || {} }; + pinescript.__rt = {}; + pinescript.__bar = 0; + + // Growing OHLCV windows: each is a SeriesArray that gains one element per bar, + // so window built-ins like sma and highest see history up to the current bar. + const O = pinescript.asSeries([]), H = pinescript.asSeries([]), L = pinescript.asSeries([]); + const C = pinescript.asSeries([]), V = pinescript.asSeries([]), T = pinescript.asSeries([]); + const HL2 = pinescript.asSeries([]), HLC3 = pinescript.asSeries([]), OHLC4 = pinescript.asSeries([]); + globalThis.open = O; globalThis.high = H; globalThis.low = L; globalThis.close = C; + globalThis.volume = V; globalThis.time = T; + globalThis.hl2 = HL2; globalThis.hlc3 = HLC3; globalThis.ohlc4 = OHLC4; + globalThis.syminfo = globalThis.syminfo || { tickerid: 'SYNTHETIC', ticker: 'SYNTHETIC', mintick: 0.01 }; + + const rt = globalThis.__pineRuntime; + for (let i = 0; i < n; i++) { + O.push(inOpen[i]); H.push(inHigh[i]); L.push(inLow[i]); C.push(inClose[i]); V.push(inVol[i]); T.push(inTime[i]); + HL2.push((inHigh[i] + inLow[i]) / 2); + HLC3.push((inHigh[i] + inLow[i] + inClose[i]) / 3); + OHLC4.push((inOpen[i] + inHigh[i] + inLow[i] + inClose[i]) / 4); + + pinescript.__bar = i; + rt.__barIndex = i; + rt.__plotIdx = 0; + rt.__shapeIdx = 0; + globalThis.bar_index = i; + globalThis.last_bar_index = n - 1; + globalThis.barstate = { + isfirst: i === 0, islast: i === n - 1, isrealtime: false, ishistory: true, + isconfirmed: true, isnew: true, islastconfirmedhistory: i === n - 1, + }; + + main(); + } + + // Backfill skipped bars so every output series has exactly n entries. + for (const k of Object.keys(rt.plots)) { + const d = rt.plots[k].data; + for (let i = 0; i < n; i++) if (d[i] === undefined) d[i] = null; + } + for (const k of Object.keys(rt.plotshapes)) { + const d = rt.plotshapes[k].data; + for (let i = 0; i < n; i++) if (d[i] === undefined) d[i] = false; + } + return rt; +} + + +export { main, run }; diff --git a/converted/Volume_Surprise_LuxAlgo.js b/converted/Volume_Surprise_LuxAlgo.js index 21af4c0..ffa2591 100644 --- a/converted/Volume_Surprise_LuxAlgo.js +++ b/converted/Volume_Surprise_LuxAlgo.js @@ -1,12 +1,63 @@ -// PineScript to JavaScript Transpiled Code +// Auto-generated JavaScript from PineScript source -// Generated automatically +// Do not edit by hand -- re-run the converter instead -// Built-in PineScript functions +// All the built-in PineScript functions that scripts depend on at runtime const pinescript = { + __scalar: function(v) { + if (v === null || v === undefined) return v; + if (typeof v === 'object' && typeof v.valueOf === 'function') { + const s = v.valueOf(); + if (typeof s === 'number' || typeof s === 'boolean' || s === null) return s; + } + return v; + }, + series: function(id, value) { + const rt = this.__rt || (this.__rt = {}); + const store = rt.series || (rt.series = {}); + let buf = store[id]; + if (!buf) buf = store[id] = []; + buf[this.__bar | 0] = this.__scalar(value); + return buf; + }, + hist: function(id, value, n) { + const buf = this.series(id, value); + const idx = (this.__bar | 0) - (n | 0); + return idx >= 0 && idx < buf.length ? buf[idx] : null; + }, + time: function(timeframe, session) { + const t = globalThis.time; + if (t && typeof t.valueOf === 'function') return t.valueOf(); + return Array.isArray(t) ? t[t.length - 1] : t; + }, + sign: function(x) { + if (x === null || x === undefined || (typeof x === 'number' && isNaN(x))) return null; + return Math.sign(Number(x)); + }, + unpack: function(value, count) { + if (Array.isArray(value)) return value; + if (value != null && typeof value[Symbol.iterator] === 'function') return Array.from(value); + return new Array(count || 0).fill(null); + }, + alertcondition: function(condition, ...rest) { + if (globalThis.__pineRuntime) { + globalThis.__pineRuntime.alerts.push({ condition, args: rest }); + } + return null; + }, + barcolor: function(color) { return null; }, + bgcolor: function(color, title, editable, showLast) { + return null; + }, + fill: function(series1, series2, color, title, editable) { + return null; + }, + hline: function(_price, _title, _opts) { + return null; + }, sma: function(series, length) { if (series === null || series === undefined) return null; if (!Array.isArray(series)) series = [series]; @@ -84,6 +135,13 @@ const pinescript = { } return weightSum > 0 ? sum / weightSum : null; }, + swma: function(series) { + if (series === null || series === undefined) return null; + if (!Array.isArray(series)) series = [series]; + if (series.length < 4) return null; + const s = series.slice(-4); + return (s[0] * 1 + s[1] * 2 + s[2] * 2 + s[3] * 1) / 6; + }, atr: function(high, low, close, length = 14) { if (!Array.isArray(high) || !Array.isArray(low) || !Array.isArray(close)) return null; if (high.length < length + 1 || low.length < length + 1 || close.length < length + 1) return null; @@ -114,6 +172,53 @@ const pinescript = { const atr = builtins.get('atr')(high, low, close, length); return { middle: sma, upper: sma + mult * atr, lower: sma - mult * atr }; }, + supertrend: function(factor, atrPeriod, high, low, close) { + if (!Array.isArray(high) || !Array.isArray(low) || !Array.isArray(close)) return [null, null]; + if (high.length < atrPeriod + 1) return [null, null]; + const atrVal = builtins.get('atr')(high, low, close, atrPeriod); + if (atrVal === null) return [null, null]; + const lastIdx = close.length - 1; + const hl2 = (high[lastIdx] + low[lastIdx]) / 2; + const upperBand = hl2 + factor * atrVal; + const lowerBand = hl2 - factor * atrVal; + // Determine direction: 1 means downtrend (price below band), -1 means uptrend + const direction = close[lastIdx] > upperBand ? -1 : close[lastIdx] < lowerBand ? 1 : -1; + const supertrendValue = direction === -1 ? lowerBand : upperBand; + return [supertrendValue, direction]; + }, + ta_supertrend: function(factor, atrPeriod) { + const H = globalThis.high; + const L = globalThis.low; + const C = globalThis.close; + return builtins.get('supertrend')(factor, atrPeriod, H, L, C); + }, + dmi: function(diLength, adxSmoothing, high, low, close) { + if (!Array.isArray(high) || !Array.isArray(low) || !Array.isArray(close)) return { plusDI: null, minusDI: null, adx: null }; + if (high.length < diLength + 1) return { plusDI: null, minusDI: null, adx: null }; + let plusDMSum = 0, minusDMSum = 0, trSum = 0; + for (let i = 1; i <= diLength; i++) { + const idx = high.length - 1 - diLength + i; + const upMove = high[idx] - high[idx - 1]; + const downMove = low[idx - 1] - low[idx]; + plusDMSum += (upMove > downMove && upMove > 0) ? upMove : 0; + minusDMSum += (downMove > upMove && downMove > 0) ? downMove : 0; + const tr = Math.max( + high[idx] - low[idx], + Math.abs(high[idx] - close[idx - 1]), + Math.abs(low[idx] - close[idx - 1]) + ); + trSum += tr; + } + const plusDI = trSum > 0 ? (plusDMSum / trSum) * 100 : 0; + const minusDI = trSum > 0 ? (minusDMSum / trSum) * 100 : 0; + const diSum = plusDI + minusDI; + const dx = diSum > 0 ? Math.abs(plusDI - minusDI) / diSum * 100 : 0; + return { plusDI, minusDI, adx: dx }; + }, + adx: function(diLength, adxSmoothing, high, low, close) { + const result = builtins.get('dmi')(diLength, adxSmoothing, high, low, close); + return result ? result.adx : null; + }, rsi: function(close, length = 14) { if (close === null || close === undefined) return null; if (!Array.isArray(close)) close = [close]; @@ -171,9 +276,7 @@ const pinescript = { return { macd: macdLine, signal: signalLine, histogram: histogram }; }, cci: function(a, b, c, d) { - // Pine signatures: - // - ta.cci(source, length) - // - ta.cci(high, low, close, length) + // Supports both ta.cci(source, length) and ta.cci(high, low, close, length) signatures let source; let length; if (Array.isArray(a) && typeof b === 'number' && c === undefined) { @@ -304,6 +407,15 @@ const pinescript = { stdev: function(source, length) { return Math.sqrt(builtins.get('variance')(source, length)); }, + median: function(source, length) { + if (!source || source.length < length) return null; + const sorted = [...source.slice(-length)].sort((a, b) => a - b); + const mid = Math.floor(sorted.length / 2); + return sorted.length % 2 !== 0 ? sorted[mid] : (sorted[mid - 1] + sorted[mid]) / 2; + }, + ta_median: function(source, length) { + return builtins.get('median')(source, length); + }, hl2: function(high, low) { return high && low ? (high[high.length - 1] + low[low.length - 1]) / 2 : null; }, @@ -420,6 +532,24 @@ const pinescript = { return (series1[i - 1] < series2[i - 1] && series1[i] >= series2[i]) || (series1[i - 1] > series2[i - 1] && series1[i] <= series2[i]); }, + crossover: function(series1, series2) { + if (series1 === null || series1 === undefined) return false; + if (series2 === null || series2 === undefined) return false; + if (!Array.isArray(series1)) series1 = [series1]; + if (!Array.isArray(series2)) series2 = [series2]; + if (series1.length < 2 || series2.length < 2) return false; + const i = series1.length - 1; + return series1[i - 1] < series2[i - 1] && series1[i] >= series2[i]; + }, + crossunder: function(series1, series2) { + if (series1 === null || series1 === undefined) return false; + if (series2 === null || series2 === undefined) return false; + if (!Array.isArray(series1)) series1 = [series1]; + if (!Array.isArray(series2)) series2 = [series2]; + if (series1.length < 2 || series2.length < 2) return false; + const i = series1.length - 1; + return series1[i - 1] > series2[i - 1] && series1[i] <= series2[i]; + }, offset: function(series, offset) { if (!Array.isArray(series)) return null; const idx = series.length - 1 - offset; @@ -500,12 +630,14 @@ const pinescript = { return builtins.get('nz')(value, replacement); }, plot: function(series, title = '', color = null, linewidth = 1) { - globalThis.__pineRuntime.plots.push({ - series, - title, - color, - linewidth - }); + const rt = globalThis.__pineRuntime; + if (!rt) return series; + const bar = rt.__barIndex | 0; + const ord = (rt.__plotIdx = (rt.__plotIdx | 0) + 1) - 1; + const key = (title && String(title)) || ('plot_' + ord); + let p = rt.plots[key]; + if (!p) p = rt.plots[key] = { title: key, color, linewidth, data: [] }; + p.data[bar] = this.__scalar(series); return series; }, lineNew: function(x1, y1, x2, y2, opts = {}) { @@ -514,20 +646,72 @@ const pinescript = { lineDelete: function(l) { return null; }, - labelNew: function(...args) { - if (globalThis.__pineRuntime) { - globalThis.__pineRuntime.labels = globalThis.__pineRuntime.labels || []; - globalThis.__pineRuntime.labels.push({ args }); - } - return { args }; + lineSetXY: function(line, point, x, y) { + if (!line) return null; + if (point === 0 || point === 'x1') { line.x1 = x; line.y1 = y; } + else { line.x2 = x; line.y2 = y; } + return line; + }, + lineGetX: function(line, point) { + if (!line) return null; + return point === 0 || point === 'x1' ? line.x1 : line.x2; + }, + lineGetY: function(line, point) { + if (!line) return null; + return point === 0 || point === 'y1' ? line.y1 : line.y2; + }, + labelNew: function(x, y, text = '', opts = {}) { + return { x, y, text, opts, _type: 'label' }; }, labelDelete: function(l) { return null; }, + labelSetText: function(label, text) { + if (!label) return null; + label.text = text; + return label; + }, + labelGetText: function(label) { + if (!label) return ''; + return label.text || ''; + }, + boxNew: function(left, top, right, bottom, opts = {}) { + return { left, top, right, bottom, opts, _type: 'box' }; + }, + boxDelete: function(box) { + return null; + }, + boxSetLeftTop: function(box, left, top) { + if (!box) return null; + box.left = left; + box.top = top; + return box; + }, + boxSetRightBottom: function(box, right, bottom) { + if (!box) return null; + box.right = right; + box.bottom = bottom; + return box; + }, + polylineNew: function(points, opts = {}) { + return { points: points || [], opts, _type: 'polyline' }; + }, + polylineDelete: function(poly) { + return null; + }, plotshape: function(condition, ...rest) { - if (globalThis.__pineRuntime) { - globalThis.__pineRuntime.plotshapes.push({ condition, args: rest }); + const rt = globalThis.__pineRuntime; + if (!rt) return condition; + const bar = rt.__barIndex | 0; + const ord = (rt.__shapeIdx = (rt.__shapeIdx | 0) + 1) - 1; + let key = 'shape_' + ord; + for (const r of rest) { + if (typeof r === 'string') { key = r; break; } + if (r && typeof r === 'object' && r.title) { key = String(r.title); break; } } + let s = rt.plotshapes[key]; + if (!s) s = rt.plotshapes[key] = { title: key, data: [] }; + s.data[bar] = !!this.__scalar(condition); return condition; }, indicator: function(title, shorttitle, overlay, opts) { @@ -542,27 +726,12 @@ const pinescript = { } return null; }, - alertcondition: function(condition, ...rest) { - if (globalThis.__pineRuntime) { - globalThis.__pineRuntime.alerts.push({ condition, args: rest }); - } - return null; - }, plotbar: function(open, high, low, close, title, color, editable, showLast) { return { open, high, low, close }; }, plotcandle: function(open, high, low, close, title, color, wickColor, borderColor, editable, showLast) { return { open, high, low, close }; }, - hline: function(_price, _title, _opts) { - return null; - }, - bgcolor: function(color, title, editable, showLast) { - return null; - }, - fill: function(series1, series2, color, title, editable) { - return null; - }, alert: function(condition, message, frequency) { console.log('Alert:', condition ? message : 'Condition not met'); return condition; @@ -828,6 +997,9 @@ const pinescript = { arrayIndexOf: function(arr, value) { return arr ? arr.indexOf(value) : -1; }, + arrayLastIndexOf: function(arr, value) { + return arr ? arr.lastIndexOf(value) : -1; + }, arraySum: function(arr) { return arr ? arr.reduce((a, b) => a + b, 0) : 0; }, @@ -840,6 +1012,42 @@ const pinescript = { arrayMax: function(arr) { return arr && arr.length > 0 ? Math.max(...arr) : null; }, + arrayStdev: function(arr) { + if (!arr || arr.length === 0) return null; + const mean = arr.reduce((a, b) => a + b, 0) / arr.length; + const variance = arr.reduce((sum, val) => sum + Math.pow(val - mean, 2), 0) / arr.length; + return Math.sqrt(variance); + }, + arrayVariance: function(arr) { + if (!arr || arr.length === 0) return null; + const mean = arr.reduce((a, b) => a + b, 0) / arr.length; + return arr.reduce((sum, val) => sum + Math.pow(val - mean, 2), 0) / arr.length; + }, + arrayCovariance: function(arr1, arr2) { + if (!arr1 || !arr2 || arr1.length === 0 || arr1.length !== arr2.length) return null; + const mean1 = arr1.reduce((a, b) => a + b, 0) / arr1.length; + const mean2 = arr2.reduce((a, b) => a + b, 0) / arr2.length; + let cov = 0; + for (let i = 0; i < arr1.length; i++) { + cov += (arr1[i] - mean1) * (arr2[i] - mean2); + } + return cov / arr1.length; + }, + mode: function(arr) { + if (!arr || arr.length === 0) return null; + const counts = new Map(); + let maxCount = 0; + let modeVal = arr[0]; + for (const val of arr) { + const c = (counts.get(val) || 0) + 1; + counts.set(val, c); + if (c > maxCount) { + maxCount = c; + modeVal = val; + } + } + return modeVal; + }, strLength: function(str) { return str ? str.length : 0; }, @@ -895,6 +1103,84 @@ const pinescript = { strReverse: function(str) { return str ? str.split('').reverse().join('') : ''; }, + strFormat: function(formatStr, ...args) { + if (!formatStr) return ''; + return formatStr.replace(/\{(\d+)\}/g, (match, idx) => { + const i = parseInt(idx, 10); + return i < args.length ? String(args[i]) : match; + }); + }, + input: function(defval, title, tooltip, inline, group) { + return defval; + }, + inputInt: function(defval = 0, title, minval, maxval, step, tooltip, inline, group) { + return defval; + }, + inputFloat: function(defval = 0.0, title, minval, maxval, step, tooltip, inline, group) { + return defval; + }, + inputBool: function(defval = false, title, tooltip, inline, group) { + return defval; + }, + inputString: function(defval = '', title, options, tooltip, inline, group) { + return defval; + }, + inputSource: function(defval = null, title, tooltip, inline, group) { + return defval; + }, + inputColor: function(defval = '#000000', title, tooltip, inline, group) { + return defval; + }, + inputTime: function(defval = 0, title, tooltip, inline, group) { + return defval; + }, + mathSign: function(value) { + return Math.sign(value); + }, + mathAvg: function(...args) { + const valid = args.filter(v => v !== null && v !== undefined && !isNaN(v)); + return valid.length > 0 ? valid.reduce((a, b) => a + b, 0) / valid.length : null; + }, + mathSum: function(series, length) { + if (!series || series.length < length) return null; + return series.slice(-length).reduce((a, b) => a + b, 0); + }, + mathRandom: function(min = 0, max = 1) { + return min + Math.random() * (max - min); + }, + mathTodegrees: function(radians) { + return radians * (180 / Math.PI); + }, + mathToradians: function(degrees) { + return degrees * (Math.PI / 180); + }, + colorT: function(color, transparency) { + if (transparency !== undefined) { + return { color, transparency }; + } + if (color && typeof color === 'object' && 'transparency' in color) { + return color.transparency; + } + return 0; + }, + tableCellSetText: function(table, column, row, text) { + return null; + }, + tableCellSetBgcolor: function(table, column, row, bgcolor) { + return null; + }, + tableMergeCells: function(table, startColumn, startRow, endColumn, endRow) { + return null; + }, + tableDelete: function(table) { + return null; + }, + tableClear: function(table, startColumn, startRow, endColumn, endRow) { + return null; + }, + maxBarsBack: function(series, length) { + return null; + }, pi: 3.141592653589793, e: 2.718281828459045, true: true, @@ -905,8 +1191,7 @@ const pinescript = { const info = { ticker: 'AAPL', tickerid: 'NASDAQ:AAPL', prefix: 'NASDAQ', root: 'AAPL', suffix: '' }; return info[type] || ''; }, - time: 1771178053368, - timenow: 1771178053368, + timenow: 1781574529071, barstate: "LAST", dividends: {}, splits: {}, @@ -923,7 +1208,7 @@ const pinescript = { return null; }, tr: function(high, low, close) { - // Pine signature: ta.tr(true) + // Handles both the boolean shorthand ta.tr(true) and explicit high/low/close arguments if (typeof high === 'boolean') { const H = globalThis.high; const L = globalThis.low; @@ -934,7 +1219,7 @@ const pinescript = { return Math.max(H[i] - L[i], Math.abs(H[i] - prevClose), Math.abs(L[i] - prevClose)); } - // Fallback: treat inputs as scalars for current bar + // When arrays are passed, compute true range from the last bar using the previous close if (Array.isArray(close)) { const prevClose = close[close.length - 2] || close[0]; const currHigh = Array.isArray(high) ? high[high.length - 1] : high; @@ -945,6 +1230,92 @@ const pinescript = { return Math.max(high - low, Math.abs(high - close), Math.abs(low - close)); }, + arrayFirst: function(arr) { + if (!arr || arr.length === 0) return null; + return arr[0]; + }, + arrayLast: function(arr) { + if (!arr || arr.length === 0) return null; + return arr[arr.length - 1]; + }, + arrayJoin: function(arr, separator = ',') { + if (!arr) return ''; + return arr.join(separator); + }, + arrayConcat: function(arr1, arr2) { + if (!arr1) return arr2 || []; + if (!arr2) return arr1; + return arr1.concat(arr2); + }, + arrayCopy: function(arr) { + if (!arr) return []; + return [...arr]; + }, + arrayBinarySearch: function(arr, value) { + if (!arr || arr.length === 0) return -1; + let lo = 0, hi = arr.length - 1; + while (lo <= hi) { + const mid = (lo + hi) >>> 1; + if (arr[mid] === value) return mid; + if (arr[mid] < value) lo = mid + 1; + else hi = mid - 1; + } + return -1; + }, + arrayRange: function(arr) { + if (!arr || arr.length === 0) return 0; + return Math.max(...arr) - Math.min(...arr); + }, + arrayMedian: function(arr) { + if (!arr || arr.length === 0) return null; + const sorted = [...arr].sort((a, b) => a - b); + const mid = Math.floor(sorted.length / 2); + return sorted.length % 2 !== 0 ? sorted[mid] : (sorted[mid - 1] + sorted[mid]) / 2; + }, + arrayMode: function(arr) { + if (!arr || arr.length === 0) return null; + const freq = {}; + let maxCount = 0; + let mode = arr[0]; + for (const v of arr) { + freq[v] = (freq[v] || 0) + 1; + if (freq[v] > maxCount) { maxCount = freq[v]; mode = v; } + } + return mode; + }, + arrayPercentile: function(arr, percentile) { + if (!arr || arr.length === 0) return null; + const sorted = [...arr].sort((a, b) => a - b); + const idx = (percentile / 100) * (sorted.length - 1); + const lo = Math.floor(idx); + const hi = Math.ceil(idx); + if (lo === hi) return sorted[lo]; + return sorted[lo] + (sorted[hi] - sorted[lo]) * (idx - lo); + }, + arrayPercentileLinearInterpolation: function(arr, percentile) { + return builtins.get('arrayPercentile')(arr, percentile); + }, + arrayPercentileNearestRank: function(arr, percentile) { + if (!arr || arr.length === 0) return null; + const sorted = [...arr].sort((a, b) => a - b); + const idx = Math.ceil((percentile / 100) * sorted.length) - 1; + return sorted[Math.max(0, idx)]; + }, + arrayAbs: function(arr) { + if (!arr) return []; + return arr.map(v => Math.abs(v)); + }, + arrayEvery: function(arr, callback) { + if (!arr) return false; + return arr.every(callback); + }, + arraySome: function(arr, callback) { + if (!arr) return false; + return arr.some(callback); + }, + requestFinancial: function() { + return null; + }, }; @@ -1101,6 +1472,115 @@ globalThis.barmerge = globalThis.barmerge || { }; +function __pineNS(real) { + + const base = real || {}; + + return new Proxy(base, { + + get(target, key) { + + if (key in target) return target[key]; + + if (typeof key === "symbol") return target[key]; + + // Unknown member: usable as a value (null), a constant, or a no-op function. + + const noop = function() { return null; }; + + noop.toString = () => ""; + + return noop; + + } + + }); + +} + +globalThis.ta = globalThis.ta || __pineNS({}); + +globalThis.math = globalThis.math || __pineNS({ pi: Math.PI, e: Math.E, phi: 1.618033988749895, rphi: 0.618033988749895 }); + +globalThis.chart = globalThis.chart || __pineNS({ point: __pineNS({}), bg_color: null, fg_color: null }); + +globalThis.line = globalThis.line || __pineNS({ style_solid: "solid", style_dashed: "dashed", style_dotted: "dotted" }); + +globalThis.box = globalThis.box || __pineNS({}); + +globalThis.label = globalThis.label || __pineNS({ style_label_down: "label_down", style_label_up: "label_up", style_none: "none" }); + +globalThis.polyline = globalThis.polyline || __pineNS({}); + +globalThis.linefill = globalThis.linefill || __pineNS({}); + +globalThis.matrix = globalThis.matrix || __pineNS({}); + +globalThis.map = globalThis.map || __pineNS({}); + +globalThis.session = globalThis.session || __pineNS({ regular: "regular", extended: "extended" }); + +globalThis.dayofweek = globalThis.dayofweek || __pineNS({ sunday: 1, monday: 2, tuesday: 3, wednesday: 4, thursday: 5, friday: 6, saturday: 7 }); + +globalThis.timeframe = __pineNS(Object.assign(globalThis.timeframe || {}, { period: (globalThis.timeframe && globalThis.timeframe.period) || "D", isintraday: false, isdaily: true, multiplier: 1 })); + +globalThis.request = __pineNS(globalThis.request || {}); + +globalThis.input = __pineNS(globalThis.input || {}); + +globalThis.adjustment = globalThis.adjustment || __pineNS({}); + +globalThis.earnings = globalThis.earnings || __pineNS({}); + +globalThis.dividends = globalThis.dividends || __pineNS({}); + +globalThis.splits = globalThis.splits || __pineNS({}); + +globalThis.currency = globalThis.currency || __pineNS({}); + +globalThis.display = globalThis.display || __pineNS({ none: "none", all: "all", pane: "pane", data_window: "data_window", status_line: "status_line", price_scale: "price_scale" }); + +globalThis.format = globalThis.format || __pineNS({ price: "price", volume: "volume", percent: "percent", mintick: "mintick", inherit: "inherit" }); + +globalThis.scale = globalThis.scale || __pineNS({ left: "left", right: "right", none: "none" }); + +globalThis.font = globalThis.font || __pineNS({ family_default: "default", family_monospace: "monospace" }); + +globalThis.xloc = globalThis.xloc || __pineNS({ bar_index: "bar_index", bar_time: "bar_time" }); + +globalThis.yloc = globalThis.yloc || __pineNS({ price: "price", abovebar: "abovebar", belowbar: "belowbar" }); + +globalThis.extend = globalThis.extend || __pineNS({ none: "none", left: "left", right: "right", both: "both" }); + +globalThis.order = globalThis.order || __pineNS({ ascending: "ascending", descending: "descending" }); + +globalThis.alert = globalThis.alert || Object.assign(function() { return null; }, { freq_once_per_bar: "once_per_bar", freq_once_per_bar_close: "once_per_bar_close", freq_all: "all" }); + +globalThis.dayofmonth = globalThis.dayofmonth || function(t) { return new Date(t || 0).getUTCDate(); }; + +globalThis.plot = globalThis.plot || __pineNS({ style_line: "line", style_linebr: "linebr", style_stepline: "stepline", style_histogram: "histogram", style_cross: "cross", style_area: "area", style_areabr: "areabr", style_columns: "columns", style_circles: "circles" }); + +globalThis.int = globalThis.int || function(x) { return x == null ? null : Math.trunc(Number(x)); }; + +globalThis.float = globalThis.float || function(x) { return x == null ? null : Number(x); }; + +globalThis.bool = globalThis.bool || function(x) { return Boolean(x); }; + +globalThis.string = globalThis.string || function(x) { return x == null ? null : String(x); }; + +globalThis.dayofweek = globalThis.dayofweek || __pineNS({ sunday: 1, monday: 2, tuesday: 3, wednesday: 4, thursday: 5, friday: 6, saturday: 7 }); + +globalThis.str = globalThis.str || __pineNS({}); + +pinescript.math = globalThis.math; + +pinescript.ta = globalThis.ta; + +pinescript.str = globalThis.str; + +pinescript.array = __pineNS(globalThis.array); + + pinescript.color = { hex: function(s) { @@ -1190,11 +1670,11 @@ pinescript.table = { }; -// Input parameters +// Script input parameters and their defaults const vector = { new: function(v) { return { v: v }; } }; -// Main script logic +// The transpiled script logic, called once per bar function main() { @@ -1209,21 +1689,21 @@ function main() { null; // Study: Volume Surprise [LuxAlgo] // Options: {"overlay":false,"format":null} - let length = input.int(50, ({ minval: 1 })); - let smooth = input.int(1, ({ minval: 1 })); - let autoFormat = input(true, "Auto Selection", ({ group: "Periods", tooltip: "Automatically choose a practical combination of periods based on the chart timeframe" })); - let considerMinutes = input(true, "Minutes", ({ group: "Periods" })); - let considerHours = input(true, "Hours", ({ group: "Periods" })); - let considerDays = input(false, "Days", ({ inline: "day", group: "Periods" })); - let daysOptions = input.string("Day of Week", "", ({ options: ["Day of Week", "Day of Month", "Day of Year"], inline: "day", group: "Periods" })); - let considerMonths = input(false, "Months", ({ group: "Periods" })); - let considerQuarters = input(false, "Quarters", ({ group: "Periods" })); - let summaryMethod = input.string("Mean", "Method", ({ options: ["Mean", "Percentile"], group: "Summary" })); - let percentile = input(50, ({ active: (summaryMethod === "Percentile"), group: "Summary" })); - let showForecast = input(true, "Show Forecast", ({ group: "Forecast" })); - let forecastWindow = input.int(50, "Forecast Window", ({ minval: 1, maxval: 499, group: "Forecast" })); - let forecastStyleColor = input(pinescript.color.hex("#2962ff"), "Style", ({ inline: "forecast_style", group: "Forecast" })); - let forecastStyleLine = input.string("Dotted", "", ({ options: ["Solid", "Dashed", "Dotted"], inline: "forecast_style", group: "Forecast" })); + let length = pinescript.inputInt(50, ({ minval: 1 })); + let smooth = pinescript.inputInt(1, ({ minval: 1 })); + let autoFormat = pinescript.input(true, "Auto Selection", ({ group: "Periods", tooltip: "Automatically choose a practical combination of periods based on the chart timeframe" })); + let considerMinutes = pinescript.input(true, "Minutes", ({ group: "Periods" })); + let considerHours = pinescript.input(true, "Hours", ({ group: "Periods" })); + let considerDays = pinescript.input(false, "Days", ({ inline: "day", group: "Periods" })); + let daysOptions = pinescript.inputString("Day of Week", "", ({ options: ["Day of Week", "Day of Month", "Day of Year"], inline: "day", group: "Periods" })); + let considerMonths = pinescript.input(false, "Months", ({ group: "Periods" })); + let considerQuarters = pinescript.input(false, "Quarters", ({ group: "Periods" })); + let summaryMethod = pinescript.inputString("Mean", "Method", ({ options: ["Mean", "Percentile"], group: "Summary" })); + let percentile = pinescript.input(50, ({ active: (summaryMethod === "Percentile"), group: "Summary" })); + let showForecast = pinescript.input(true, "Show Forecast", ({ group: "Forecast" })); + let forecastWindow = pinescript.inputInt(50, "Forecast Window", ({ minval: 1, maxval: 499, group: "Forecast" })); + let forecastStyleColor = pinescript.input(pinescript.color.hex("#2962ff"), "Style", ({ inline: "forecast_style", group: "Forecast" })); + let forecastStyleLine = pinescript.inputString("Dotted", "", ({ options: ["Solid", "Dashed", "Dotted"], inline: "forecast_style", group: "Forecast" })); function getSummary(id, key) { id.get(key).v.percentile_linear_interpolation(percentile); id.get(key).v.avg(); @@ -1260,7 +1740,7 @@ function main() { } } } - if (state.format === undefined) state.format = str.format("{0}{1}{2}{3}{4}ss", (considerQuarters ? pinescript.strToString(math.ceil((month / 3))) : ""), (considerMonths ? "MM:" : ""), (considerDays ? ((daysOptions === "Day of Week") ? "EEE:" : ((daysOptions === "Day of Month") ? "dd:" : "DDD:")) : ""), (considerHours ? "hh:" : ""), (considerMinutes ? "mm:" : "")); + if (state.format === undefined) state.format = pinescript.strFormat("{0}{1}{2}{3}{4}ss", (considerQuarters ? pinescript.strToString(pinescript.ceil((month / 3))) : ""), (considerMonths ? "MM:" : ""), (considerDays ? ((daysOptions === "Day of Week") ? "EEE:" : ((daysOptions === "Day of Month") ? "dd:" : "DDD:")) : ""), (considerHours ? "hh:" : ""), (considerMinutes ? "mm:" : "")); if (state.data === undefined) state.data = pinescript.mapNew(); let vol = pinescript.sma(volume, smooth); let key = str.format_time(time, state.format); @@ -1289,7 +1769,7 @@ function main() { forecastCoordinates.push(chart.point.from_time(t, null)); } } - polyline.delete(pinescript.offset(polyline.new(forecastCoordinates, ({ xloc: xloc.bar_time, line_style: state.forecastStyle, line_color: forecastStyleColor })), 1)); + pinescript.polylineDelete(pinescript.hist(0, pinescript.polylineNew(forecastCoordinates, ({ xloc: xloc.bar_time, line_style: state.forecastStyle, line_color: forecastStyleColor })), 1)); } let css = ((vol > summary) ? pinescript.color.new(pinescript.color.hex("#089981"), 50) : pinescript.color.new(pinescript.color.hex("#f23645"), 50)); let plotVolume = pinescript.plot(vol, "Volume", ({ color: pinescript.color.gray })); @@ -1298,7 +1778,67 @@ function main() { } -// Export for use -export { main +// The bar-by-bar engine: runs main() once per bar over the dataset. +function run(data, options = {}) { + data = data || {}; + const inClose = (data.close || []).map(Number); + const n = inClose.length; + const inOpen = (data.open || inClose).map(Number); + const inHigh = (data.high || inClose).map(Number); + const inLow = (data.low || inClose).map(Number); + const inVol = (data.volume || new Array(n).fill(0)).map(Number); + const inTime = (data.time || inClose.map((_, i) => i)).map(Number); + + // Reset all persistent and per-run state so repeated runs are independent. + globalThis.__pineState = {}; + globalThis.__pineRuntime = { plots: {}, plotshapes: {}, alerts: [], bars: n, inputs: options.inputs || {} }; + pinescript.__rt = {}; + pinescript.__bar = 0; + + // Growing OHLCV windows: each is a SeriesArray that gains one element per bar, + // so window built-ins like sma and highest see history up to the current bar. + const O = pinescript.asSeries([]), H = pinescript.asSeries([]), L = pinescript.asSeries([]); + const C = pinescript.asSeries([]), V = pinescript.asSeries([]), T = pinescript.asSeries([]); + const HL2 = pinescript.asSeries([]), HLC3 = pinescript.asSeries([]), OHLC4 = pinescript.asSeries([]); + globalThis.open = O; globalThis.high = H; globalThis.low = L; globalThis.close = C; + globalThis.volume = V; globalThis.time = T; + globalThis.hl2 = HL2; globalThis.hlc3 = HLC3; globalThis.ohlc4 = OHLC4; + globalThis.syminfo = globalThis.syminfo || { tickerid: 'SYNTHETIC', ticker: 'SYNTHETIC', mintick: 0.01 }; + + const rt = globalThis.__pineRuntime; + for (let i = 0; i < n; i++) { + O.push(inOpen[i]); H.push(inHigh[i]); L.push(inLow[i]); C.push(inClose[i]); V.push(inVol[i]); T.push(inTime[i]); + HL2.push((inHigh[i] + inLow[i]) / 2); + HLC3.push((inHigh[i] + inLow[i] + inClose[i]) / 3); + OHLC4.push((inOpen[i] + inHigh[i] + inLow[i] + inClose[i]) / 4); + + pinescript.__bar = i; + rt.__barIndex = i; + rt.__plotIdx = 0; + rt.__shapeIdx = 0; + globalThis.bar_index = i; + globalThis.last_bar_index = n - 1; + globalThis.barstate = { + isfirst: i === 0, islast: i === n - 1, isrealtime: false, ishistory: true, + isconfirmed: true, isnew: true, islastconfirmedhistory: i === n - 1, + }; + + main(); + } + + // Backfill skipped bars so every output series has exactly n entries. + for (const k of Object.keys(rt.plots)) { + const d = rt.plots[k].data; + for (let i = 0; i < n; i++) if (d[i] === undefined) d[i] = null; + } + for (const k of Object.keys(rt.plotshapes)) { + const d = rt.plotshapes[k].data; + for (let i = 0; i < n; i++) if (d[i] === undefined) d[i] = false; + } + return rt; +} + + +export { main, run }; diff --git a/converted/Volume_Weighted_Trend_QuantAlgo.js b/converted/Volume_Weighted_Trend_QuantAlgo.js index f95bcc3..179bfab 100644 --- a/converted/Volume_Weighted_Trend_QuantAlgo.js +++ b/converted/Volume_Weighted_Trend_QuantAlgo.js @@ -1,12 +1,63 @@ -// PineScript to JavaScript Transpiled Code +// Auto-generated JavaScript from PineScript source -// Generated automatically +// Do not edit by hand -- re-run the converter instead -// Built-in PineScript functions +// All the built-in PineScript functions that scripts depend on at runtime const pinescript = { + __scalar: function(v) { + if (v === null || v === undefined) return v; + if (typeof v === 'object' && typeof v.valueOf === 'function') { + const s = v.valueOf(); + if (typeof s === 'number' || typeof s === 'boolean' || s === null) return s; + } + return v; + }, + series: function(id, value) { + const rt = this.__rt || (this.__rt = {}); + const store = rt.series || (rt.series = {}); + let buf = store[id]; + if (!buf) buf = store[id] = []; + buf[this.__bar | 0] = this.__scalar(value); + return buf; + }, + hist: function(id, value, n) { + const buf = this.series(id, value); + const idx = (this.__bar | 0) - (n | 0); + return idx >= 0 && idx < buf.length ? buf[idx] : null; + }, + time: function(timeframe, session) { + const t = globalThis.time; + if (t && typeof t.valueOf === 'function') return t.valueOf(); + return Array.isArray(t) ? t[t.length - 1] : t; + }, + sign: function(x) { + if (x === null || x === undefined || (typeof x === 'number' && isNaN(x))) return null; + return Math.sign(Number(x)); + }, + unpack: function(value, count) { + if (Array.isArray(value)) return value; + if (value != null && typeof value[Symbol.iterator] === 'function') return Array.from(value); + return new Array(count || 0).fill(null); + }, + alertcondition: function(condition, ...rest) { + if (globalThis.__pineRuntime) { + globalThis.__pineRuntime.alerts.push({ condition, args: rest }); + } + return null; + }, + barcolor: function(color) { return null; }, + bgcolor: function(color, title, editable, showLast) { + return null; + }, + fill: function(series1, series2, color, title, editable) { + return null; + }, + hline: function(_price, _title, _opts) { + return null; + }, sma: function(series, length) { if (series === null || series === undefined) return null; if (!Array.isArray(series)) series = [series]; @@ -84,6 +135,13 @@ const pinescript = { } return weightSum > 0 ? sum / weightSum : null; }, + swma: function(series) { + if (series === null || series === undefined) return null; + if (!Array.isArray(series)) series = [series]; + if (series.length < 4) return null; + const s = series.slice(-4); + return (s[0] * 1 + s[1] * 2 + s[2] * 2 + s[3] * 1) / 6; + }, atr: function(high, low, close, length = 14) { if (!Array.isArray(high) || !Array.isArray(low) || !Array.isArray(close)) return null; if (high.length < length + 1 || low.length < length + 1 || close.length < length + 1) return null; @@ -114,6 +172,53 @@ const pinescript = { const atr = builtins.get('atr')(high, low, close, length); return { middle: sma, upper: sma + mult * atr, lower: sma - mult * atr }; }, + supertrend: function(factor, atrPeriod, high, low, close) { + if (!Array.isArray(high) || !Array.isArray(low) || !Array.isArray(close)) return [null, null]; + if (high.length < atrPeriod + 1) return [null, null]; + const atrVal = builtins.get('atr')(high, low, close, atrPeriod); + if (atrVal === null) return [null, null]; + const lastIdx = close.length - 1; + const hl2 = (high[lastIdx] + low[lastIdx]) / 2; + const upperBand = hl2 + factor * atrVal; + const lowerBand = hl2 - factor * atrVal; + // Determine direction: 1 means downtrend (price below band), -1 means uptrend + const direction = close[lastIdx] > upperBand ? -1 : close[lastIdx] < lowerBand ? 1 : -1; + const supertrendValue = direction === -1 ? lowerBand : upperBand; + return [supertrendValue, direction]; + }, + ta_supertrend: function(factor, atrPeriod) { + const H = globalThis.high; + const L = globalThis.low; + const C = globalThis.close; + return builtins.get('supertrend')(factor, atrPeriod, H, L, C); + }, + dmi: function(diLength, adxSmoothing, high, low, close) { + if (!Array.isArray(high) || !Array.isArray(low) || !Array.isArray(close)) return { plusDI: null, minusDI: null, adx: null }; + if (high.length < diLength + 1) return { plusDI: null, minusDI: null, adx: null }; + let plusDMSum = 0, minusDMSum = 0, trSum = 0; + for (let i = 1; i <= diLength; i++) { + const idx = high.length - 1 - diLength + i; + const upMove = high[idx] - high[idx - 1]; + const downMove = low[idx - 1] - low[idx]; + plusDMSum += (upMove > downMove && upMove > 0) ? upMove : 0; + minusDMSum += (downMove > upMove && downMove > 0) ? downMove : 0; + const tr = Math.max( + high[idx] - low[idx], + Math.abs(high[idx] - close[idx - 1]), + Math.abs(low[idx] - close[idx - 1]) + ); + trSum += tr; + } + const plusDI = trSum > 0 ? (plusDMSum / trSum) * 100 : 0; + const minusDI = trSum > 0 ? (minusDMSum / trSum) * 100 : 0; + const diSum = plusDI + minusDI; + const dx = diSum > 0 ? Math.abs(plusDI - minusDI) / diSum * 100 : 0; + return { plusDI, minusDI, adx: dx }; + }, + adx: function(diLength, adxSmoothing, high, low, close) { + const result = builtins.get('dmi')(diLength, adxSmoothing, high, low, close); + return result ? result.adx : null; + }, rsi: function(close, length = 14) { if (close === null || close === undefined) return null; if (!Array.isArray(close)) close = [close]; @@ -171,9 +276,7 @@ const pinescript = { return { macd: macdLine, signal: signalLine, histogram: histogram }; }, cci: function(a, b, c, d) { - // Pine signatures: - // - ta.cci(source, length) - // - ta.cci(high, low, close, length) + // Supports both ta.cci(source, length) and ta.cci(high, low, close, length) signatures let source; let length; if (Array.isArray(a) && typeof b === 'number' && c === undefined) { @@ -304,6 +407,15 @@ const pinescript = { stdev: function(source, length) { return Math.sqrt(builtins.get('variance')(source, length)); }, + median: function(source, length) { + if (!source || source.length < length) return null; + const sorted = [...source.slice(-length)].sort((a, b) => a - b); + const mid = Math.floor(sorted.length / 2); + return sorted.length % 2 !== 0 ? sorted[mid] : (sorted[mid - 1] + sorted[mid]) / 2; + }, + ta_median: function(source, length) { + return builtins.get('median')(source, length); + }, hl2: function(high, low) { return high && low ? (high[high.length - 1] + low[low.length - 1]) / 2 : null; }, @@ -420,6 +532,24 @@ const pinescript = { return (series1[i - 1] < series2[i - 1] && series1[i] >= series2[i]) || (series1[i - 1] > series2[i - 1] && series1[i] <= series2[i]); }, + crossover: function(series1, series2) { + if (series1 === null || series1 === undefined) return false; + if (series2 === null || series2 === undefined) return false; + if (!Array.isArray(series1)) series1 = [series1]; + if (!Array.isArray(series2)) series2 = [series2]; + if (series1.length < 2 || series2.length < 2) return false; + const i = series1.length - 1; + return series1[i - 1] < series2[i - 1] && series1[i] >= series2[i]; + }, + crossunder: function(series1, series2) { + if (series1 === null || series1 === undefined) return false; + if (series2 === null || series2 === undefined) return false; + if (!Array.isArray(series1)) series1 = [series1]; + if (!Array.isArray(series2)) series2 = [series2]; + if (series1.length < 2 || series2.length < 2) return false; + const i = series1.length - 1; + return series1[i - 1] > series2[i - 1] && series1[i] <= series2[i]; + }, offset: function(series, offset) { if (!Array.isArray(series)) return null; const idx = series.length - 1 - offset; @@ -500,12 +630,14 @@ const pinescript = { return builtins.get('nz')(value, replacement); }, plot: function(series, title = '', color = null, linewidth = 1) { - globalThis.__pineRuntime.plots.push({ - series, - title, - color, - linewidth - }); + const rt = globalThis.__pineRuntime; + if (!rt) return series; + const bar = rt.__barIndex | 0; + const ord = (rt.__plotIdx = (rt.__plotIdx | 0) + 1) - 1; + const key = (title && String(title)) || ('plot_' + ord); + let p = rt.plots[key]; + if (!p) p = rt.plots[key] = { title: key, color, linewidth, data: [] }; + p.data[bar] = this.__scalar(series); return series; }, lineNew: function(x1, y1, x2, y2, opts = {}) { @@ -514,20 +646,72 @@ const pinescript = { lineDelete: function(l) { return null; }, - labelNew: function(...args) { - if (globalThis.__pineRuntime) { - globalThis.__pineRuntime.labels = globalThis.__pineRuntime.labels || []; - globalThis.__pineRuntime.labels.push({ args }); - } - return { args }; + lineSetXY: function(line, point, x, y) { + if (!line) return null; + if (point === 0 || point === 'x1') { line.x1 = x; line.y1 = y; } + else { line.x2 = x; line.y2 = y; } + return line; + }, + lineGetX: function(line, point) { + if (!line) return null; + return point === 0 || point === 'x1' ? line.x1 : line.x2; + }, + lineGetY: function(line, point) { + if (!line) return null; + return point === 0 || point === 'y1' ? line.y1 : line.y2; + }, + labelNew: function(x, y, text = '', opts = {}) { + return { x, y, text, opts, _type: 'label' }; }, labelDelete: function(l) { return null; }, + labelSetText: function(label, text) { + if (!label) return null; + label.text = text; + return label; + }, + labelGetText: function(label) { + if (!label) return ''; + return label.text || ''; + }, + boxNew: function(left, top, right, bottom, opts = {}) { + return { left, top, right, bottom, opts, _type: 'box' }; + }, + boxDelete: function(box) { + return null; + }, + boxSetLeftTop: function(box, left, top) { + if (!box) return null; + box.left = left; + box.top = top; + return box; + }, + boxSetRightBottom: function(box, right, bottom) { + if (!box) return null; + box.right = right; + box.bottom = bottom; + return box; + }, + polylineNew: function(points, opts = {}) { + return { points: points || [], opts, _type: 'polyline' }; + }, + polylineDelete: function(poly) { + return null; + }, plotshape: function(condition, ...rest) { - if (globalThis.__pineRuntime) { - globalThis.__pineRuntime.plotshapes.push({ condition, args: rest }); + const rt = globalThis.__pineRuntime; + if (!rt) return condition; + const bar = rt.__barIndex | 0; + const ord = (rt.__shapeIdx = (rt.__shapeIdx | 0) + 1) - 1; + let key = 'shape_' + ord; + for (const r of rest) { + if (typeof r === 'string') { key = r; break; } + if (r && typeof r === 'object' && r.title) { key = String(r.title); break; } } + let s = rt.plotshapes[key]; + if (!s) s = rt.plotshapes[key] = { title: key, data: [] }; + s.data[bar] = !!this.__scalar(condition); return condition; }, indicator: function(title, shorttitle, overlay, opts) { @@ -542,27 +726,12 @@ const pinescript = { } return null; }, - alertcondition: function(condition, ...rest) { - if (globalThis.__pineRuntime) { - globalThis.__pineRuntime.alerts.push({ condition, args: rest }); - } - return null; - }, plotbar: function(open, high, low, close, title, color, editable, showLast) { return { open, high, low, close }; }, plotcandle: function(open, high, low, close, title, color, wickColor, borderColor, editable, showLast) { return { open, high, low, close }; }, - hline: function(_price, _title, _opts) { - return null; - }, - bgcolor: function(color, title, editable, showLast) { - return null; - }, - fill: function(series1, series2, color, title, editable) { - return null; - }, alert: function(condition, message, frequency) { console.log('Alert:', condition ? message : 'Condition not met'); return condition; @@ -828,6 +997,9 @@ const pinescript = { arrayIndexOf: function(arr, value) { return arr ? arr.indexOf(value) : -1; }, + arrayLastIndexOf: function(arr, value) { + return arr ? arr.lastIndexOf(value) : -1; + }, arraySum: function(arr) { return arr ? arr.reduce((a, b) => a + b, 0) : 0; }, @@ -840,6 +1012,42 @@ const pinescript = { arrayMax: function(arr) { return arr && arr.length > 0 ? Math.max(...arr) : null; }, + arrayStdev: function(arr) { + if (!arr || arr.length === 0) return null; + const mean = arr.reduce((a, b) => a + b, 0) / arr.length; + const variance = arr.reduce((sum, val) => sum + Math.pow(val - mean, 2), 0) / arr.length; + return Math.sqrt(variance); + }, + arrayVariance: function(arr) { + if (!arr || arr.length === 0) return null; + const mean = arr.reduce((a, b) => a + b, 0) / arr.length; + return arr.reduce((sum, val) => sum + Math.pow(val - mean, 2), 0) / arr.length; + }, + arrayCovariance: function(arr1, arr2) { + if (!arr1 || !arr2 || arr1.length === 0 || arr1.length !== arr2.length) return null; + const mean1 = arr1.reduce((a, b) => a + b, 0) / arr1.length; + const mean2 = arr2.reduce((a, b) => a + b, 0) / arr2.length; + let cov = 0; + for (let i = 0; i < arr1.length; i++) { + cov += (arr1[i] - mean1) * (arr2[i] - mean2); + } + return cov / arr1.length; + }, + mode: function(arr) { + if (!arr || arr.length === 0) return null; + const counts = new Map(); + let maxCount = 0; + let modeVal = arr[0]; + for (const val of arr) { + const c = (counts.get(val) || 0) + 1; + counts.set(val, c); + if (c > maxCount) { + maxCount = c; + modeVal = val; + } + } + return modeVal; + }, strLength: function(str) { return str ? str.length : 0; }, @@ -895,6 +1103,84 @@ const pinescript = { strReverse: function(str) { return str ? str.split('').reverse().join('') : ''; }, + strFormat: function(formatStr, ...args) { + if (!formatStr) return ''; + return formatStr.replace(/\{(\d+)\}/g, (match, idx) => { + const i = parseInt(idx, 10); + return i < args.length ? String(args[i]) : match; + }); + }, + input: function(defval, title, tooltip, inline, group) { + return defval; + }, + inputInt: function(defval = 0, title, minval, maxval, step, tooltip, inline, group) { + return defval; + }, + inputFloat: function(defval = 0.0, title, minval, maxval, step, tooltip, inline, group) { + return defval; + }, + inputBool: function(defval = false, title, tooltip, inline, group) { + return defval; + }, + inputString: function(defval = '', title, options, tooltip, inline, group) { + return defval; + }, + inputSource: function(defval = null, title, tooltip, inline, group) { + return defval; + }, + inputColor: function(defval = '#000000', title, tooltip, inline, group) { + return defval; + }, + inputTime: function(defval = 0, title, tooltip, inline, group) { + return defval; + }, + mathSign: function(value) { + return Math.sign(value); + }, + mathAvg: function(...args) { + const valid = args.filter(v => v !== null && v !== undefined && !isNaN(v)); + return valid.length > 0 ? valid.reduce((a, b) => a + b, 0) / valid.length : null; + }, + mathSum: function(series, length) { + if (!series || series.length < length) return null; + return series.slice(-length).reduce((a, b) => a + b, 0); + }, + mathRandom: function(min = 0, max = 1) { + return min + Math.random() * (max - min); + }, + mathTodegrees: function(radians) { + return radians * (180 / Math.PI); + }, + mathToradians: function(degrees) { + return degrees * (Math.PI / 180); + }, + colorT: function(color, transparency) { + if (transparency !== undefined) { + return { color, transparency }; + } + if (color && typeof color === 'object' && 'transparency' in color) { + return color.transparency; + } + return 0; + }, + tableCellSetText: function(table, column, row, text) { + return null; + }, + tableCellSetBgcolor: function(table, column, row, bgcolor) { + return null; + }, + tableMergeCells: function(table, startColumn, startRow, endColumn, endRow) { + return null; + }, + tableDelete: function(table) { + return null; + }, + tableClear: function(table, startColumn, startRow, endColumn, endRow) { + return null; + }, + maxBarsBack: function(series, length) { + return null; + }, pi: 3.141592653589793, e: 2.718281828459045, true: true, @@ -905,8 +1191,7 @@ const pinescript = { const info = { ticker: 'AAPL', tickerid: 'NASDAQ:AAPL', prefix: 'NASDAQ', root: 'AAPL', suffix: '' }; return info[type] || ''; }, - time: 1771178053447, - timenow: 1771178053447, + timenow: 1781574529162, barstate: "LAST", dividends: {}, splits: {}, @@ -923,7 +1208,7 @@ const pinescript = { return null; }, tr: function(high, low, close) { - // Pine signature: ta.tr(true) + // Handles both the boolean shorthand ta.tr(true) and explicit high/low/close arguments if (typeof high === 'boolean') { const H = globalThis.high; const L = globalThis.low; @@ -934,7 +1219,7 @@ const pinescript = { return Math.max(H[i] - L[i], Math.abs(H[i] - prevClose), Math.abs(L[i] - prevClose)); } - // Fallback: treat inputs as scalars for current bar + // When arrays are passed, compute true range from the last bar using the previous close if (Array.isArray(close)) { const prevClose = close[close.length - 2] || close[0]; const currHigh = Array.isArray(high) ? high[high.length - 1] : high; @@ -945,6 +1230,92 @@ const pinescript = { return Math.max(high - low, Math.abs(high - close), Math.abs(low - close)); }, + arrayFirst: function(arr) { + if (!arr || arr.length === 0) return null; + return arr[0]; + }, + arrayLast: function(arr) { + if (!arr || arr.length === 0) return null; + return arr[arr.length - 1]; + }, + arrayJoin: function(arr, separator = ',') { + if (!arr) return ''; + return arr.join(separator); + }, + arrayConcat: function(arr1, arr2) { + if (!arr1) return arr2 || []; + if (!arr2) return arr1; + return arr1.concat(arr2); + }, + arrayCopy: function(arr) { + if (!arr) return []; + return [...arr]; + }, + arrayBinarySearch: function(arr, value) { + if (!arr || arr.length === 0) return -1; + let lo = 0, hi = arr.length - 1; + while (lo <= hi) { + const mid = (lo + hi) >>> 1; + if (arr[mid] === value) return mid; + if (arr[mid] < value) lo = mid + 1; + else hi = mid - 1; + } + return -1; + }, + arrayRange: function(arr) { + if (!arr || arr.length === 0) return 0; + return Math.max(...arr) - Math.min(...arr); + }, + arrayMedian: function(arr) { + if (!arr || arr.length === 0) return null; + const sorted = [...arr].sort((a, b) => a - b); + const mid = Math.floor(sorted.length / 2); + return sorted.length % 2 !== 0 ? sorted[mid] : (sorted[mid - 1] + sorted[mid]) / 2; + }, + arrayMode: function(arr) { + if (!arr || arr.length === 0) return null; + const freq = {}; + let maxCount = 0; + let mode = arr[0]; + for (const v of arr) { + freq[v] = (freq[v] || 0) + 1; + if (freq[v] > maxCount) { maxCount = freq[v]; mode = v; } + } + return mode; + }, + arrayPercentile: function(arr, percentile) { + if (!arr || arr.length === 0) return null; + const sorted = [...arr].sort((a, b) => a - b); + const idx = (percentile / 100) * (sorted.length - 1); + const lo = Math.floor(idx); + const hi = Math.ceil(idx); + if (lo === hi) return sorted[lo]; + return sorted[lo] + (sorted[hi] - sorted[lo]) * (idx - lo); + }, + arrayPercentileLinearInterpolation: function(arr, percentile) { + return builtins.get('arrayPercentile')(arr, percentile); + }, + arrayPercentileNearestRank: function(arr, percentile) { + if (!arr || arr.length === 0) return null; + const sorted = [...arr].sort((a, b) => a - b); + const idx = Math.ceil((percentile / 100) * sorted.length) - 1; + return sorted[Math.max(0, idx)]; + }, + arrayAbs: function(arr) { + if (!arr) return []; + return arr.map(v => Math.abs(v)); + }, + arrayEvery: function(arr, callback) { + if (!arr) return false; + return arr.every(callback); + }, + arraySome: function(arr, callback) { + if (!arr) return false; + return arr.some(callback); + }, + requestFinancial: function() { + return null; + }, }; @@ -1101,6 +1472,115 @@ globalThis.barmerge = globalThis.barmerge || { }; +function __pineNS(real) { + + const base = real || {}; + + return new Proxy(base, { + + get(target, key) { + + if (key in target) return target[key]; + + if (typeof key === "symbol") return target[key]; + + // Unknown member: usable as a value (null), a constant, or a no-op function. + + const noop = function() { return null; }; + + noop.toString = () => ""; + + return noop; + + } + + }); + +} + +globalThis.ta = globalThis.ta || __pineNS({}); + +globalThis.math = globalThis.math || __pineNS({ pi: Math.PI, e: Math.E, phi: 1.618033988749895, rphi: 0.618033988749895 }); + +globalThis.chart = globalThis.chart || __pineNS({ point: __pineNS({}), bg_color: null, fg_color: null }); + +globalThis.line = globalThis.line || __pineNS({ style_solid: "solid", style_dashed: "dashed", style_dotted: "dotted" }); + +globalThis.box = globalThis.box || __pineNS({}); + +globalThis.label = globalThis.label || __pineNS({ style_label_down: "label_down", style_label_up: "label_up", style_none: "none" }); + +globalThis.polyline = globalThis.polyline || __pineNS({}); + +globalThis.linefill = globalThis.linefill || __pineNS({}); + +globalThis.matrix = globalThis.matrix || __pineNS({}); + +globalThis.map = globalThis.map || __pineNS({}); + +globalThis.session = globalThis.session || __pineNS({ regular: "regular", extended: "extended" }); + +globalThis.dayofweek = globalThis.dayofweek || __pineNS({ sunday: 1, monday: 2, tuesday: 3, wednesday: 4, thursday: 5, friday: 6, saturday: 7 }); + +globalThis.timeframe = __pineNS(Object.assign(globalThis.timeframe || {}, { period: (globalThis.timeframe && globalThis.timeframe.period) || "D", isintraday: false, isdaily: true, multiplier: 1 })); + +globalThis.request = __pineNS(globalThis.request || {}); + +globalThis.input = __pineNS(globalThis.input || {}); + +globalThis.adjustment = globalThis.adjustment || __pineNS({}); + +globalThis.earnings = globalThis.earnings || __pineNS({}); + +globalThis.dividends = globalThis.dividends || __pineNS({}); + +globalThis.splits = globalThis.splits || __pineNS({}); + +globalThis.currency = globalThis.currency || __pineNS({}); + +globalThis.display = globalThis.display || __pineNS({ none: "none", all: "all", pane: "pane", data_window: "data_window", status_line: "status_line", price_scale: "price_scale" }); + +globalThis.format = globalThis.format || __pineNS({ price: "price", volume: "volume", percent: "percent", mintick: "mintick", inherit: "inherit" }); + +globalThis.scale = globalThis.scale || __pineNS({ left: "left", right: "right", none: "none" }); + +globalThis.font = globalThis.font || __pineNS({ family_default: "default", family_monospace: "monospace" }); + +globalThis.xloc = globalThis.xloc || __pineNS({ bar_index: "bar_index", bar_time: "bar_time" }); + +globalThis.yloc = globalThis.yloc || __pineNS({ price: "price", abovebar: "abovebar", belowbar: "belowbar" }); + +globalThis.extend = globalThis.extend || __pineNS({ none: "none", left: "left", right: "right", both: "both" }); + +globalThis.order = globalThis.order || __pineNS({ ascending: "ascending", descending: "descending" }); + +globalThis.alert = globalThis.alert || Object.assign(function() { return null; }, { freq_once_per_bar: "once_per_bar", freq_once_per_bar_close: "once_per_bar_close", freq_all: "all" }); + +globalThis.dayofmonth = globalThis.dayofmonth || function(t) { return new Date(t || 0).getUTCDate(); }; + +globalThis.plot = globalThis.plot || __pineNS({ style_line: "line", style_linebr: "linebr", style_stepline: "stepline", style_histogram: "histogram", style_cross: "cross", style_area: "area", style_areabr: "areabr", style_columns: "columns", style_circles: "circles" }); + +globalThis.int = globalThis.int || function(x) { return x == null ? null : Math.trunc(Number(x)); }; + +globalThis.float = globalThis.float || function(x) { return x == null ? null : Number(x); }; + +globalThis.bool = globalThis.bool || function(x) { return Boolean(x); }; + +globalThis.string = globalThis.string || function(x) { return x == null ? null : String(x); }; + +globalThis.dayofweek = globalThis.dayofweek || __pineNS({ sunday: 1, monday: 2, tuesday: 3, wednesday: 4, thursday: 5, friday: 6, saturday: 7 }); + +globalThis.str = globalThis.str || __pineNS({}); + +pinescript.math = globalThis.math; + +pinescript.ta = globalThis.ta; + +pinescript.str = globalThis.str; + +pinescript.array = __pineNS(globalThis.array); + + pinescript.color = { hex: function(s) { @@ -1190,10 +1670,10 @@ pinescript.table = { }; -// Input parameters +// Script input parameters and their defaults -// Main script logic +// The transpiled script logic, called once per bar function main() { @@ -1221,16 +1701,16 @@ function main() { let tooltip_enable_ribbons = "Enable/disable gradient ribbon fills between the VWMA and volatility bands. When enabled, creates semi-transparent colored zones showing the buffer range around the trend line, providing visual context for price position relative to trend boundaries. The ribbons help identify when price is approaching breakout thresholds. Disable to reduce chart clutter or focus solely on the central VWMA line and band boundaries."; let tooltip_band_transparency = "Transparency level for the upper and lower volatility band lines. Higher values (80-95) create subtle band outlines that provide boundary context without visual clutter - useful when running multiple indicators or preferring minimalist charts. Lower values (50-70) create more prominent band lines for stronger emphasis on breakout thresholds - useful for traders who want clear visual boundaries for trend trigger zones. Standard value is 90 for subtle guidance lines that don't dominate the chart."; let tooltip_enable_barcolor = "Enable/disable bar coloring based on current trend direction. When enabled, price bars are tinted with bullish color during uptrends and bearish color during downtrends, providing instant visual confirmation of trend state without checking the indicator. This helps quickly identify trend alignment across multiple timeframes. Disable if you prefer default bar colors or use other bar coloring indicators."; - let vwma_length = input.int(34, "VWMA Length", ({ minval: 5, group: state.calculation_settings, tooltip: tooltip_vwma })); - let atr_multiplier = input.float(1.5, "ATR Multiplier", ({ minval: 0.5, step: 0.1, group: state.calculation_settings, tooltip: tooltip_atr })); - let preset_config = input.string("Default", "Preset Configuration", ({ options: ["Default", "Fast Response", "Smooth Trend"], group: state.calculation_settings, tooltip: ((tooltip_preset + "nn") + tooltip_preset_details) })); - let color_preset = input.string("Custom", "Color Preset", ({ options: ["Classic", "Aqua", "Cosmic", "Cyber", "Neon", "Custom"], group: state.visual_settings, tooltip: tooltip_color_preset })); - let bullish_input = input.color(pinescript.color.hex("#00ffaa"), "Bullish Trend Color", ({ group: state.visual_settings, tooltip: tooltip_bullish })); - let bearish_input = input.color(pinescript.color.hex("#ff0000"), "Bearish Trend Color", ({ group: state.visual_settings, tooltip: tooltip_bearish })); - let enable_glow = input.bool(true, "Enable Neon Glow Effect", ({ group: state.visual_settings, tooltip: tooltip_enable_glow })); - let enable_ribbons = input.bool(true, "Enable Volatility Ribbons", ({ group: state.visual_settings, tooltip: tooltip_enable_ribbons })); - let band_transparency = input.int(90, "Band Transparency", ({ minval: 0, maxval: 100, group: state.visual_settings, tooltip: tooltip_band_transparency })); - let enable_barcolor = input.bool(true, "Enable Bar Coloring", ({ group: state.visual_settings, tooltip: tooltip_enable_barcolor })); + let vwma_length = pinescript.inputInt(34, "VWMA Length", ({ minval: 5, group: state.calculation_settings, tooltip: tooltip_vwma })); + let atr_multiplier = pinescript.inputFloat(1.5, "ATR Multiplier", ({ minval: 0.5, step: 0.1, group: state.calculation_settings, tooltip: tooltip_atr })); + let preset_config = pinescript.inputString("Default", "Preset Configuration", ({ options: ["Default", "Fast Response", "Smooth Trend"], group: state.calculation_settings, tooltip: ((tooltip_preset + "nn") + tooltip_preset_details) })); + let color_preset = pinescript.inputString("Custom", "Color Preset", ({ options: ["Classic", "Aqua", "Cosmic", "Cyber", "Neon", "Custom"], group: state.visual_settings, tooltip: tooltip_color_preset })); + let bullish_input = pinescript.inputColor(pinescript.color.hex("#00ffaa"), "Bullish Trend Color", ({ group: state.visual_settings, tooltip: tooltip_bullish })); + let bearish_input = pinescript.inputColor(pinescript.color.hex("#ff0000"), "Bearish Trend Color", ({ group: state.visual_settings, tooltip: tooltip_bearish })); + let enable_glow = pinescript.inputBool(true, "Enable Neon Glow Effect", ({ group: state.visual_settings, tooltip: tooltip_enable_glow })); + let enable_ribbons = pinescript.inputBool(true, "Enable Volatility Ribbons", ({ group: state.visual_settings, tooltip: tooltip_enable_ribbons })); + let band_transparency = pinescript.inputInt(90, "Band Transparency", ({ minval: 0, maxval: 100, group: state.visual_settings, tooltip: tooltip_band_transparency })); + let enable_barcolor = pinescript.inputBool(true, "Enable Bar Coloring", ({ group: state.visual_settings, tooltip: tooltip_enable_barcolor })); if ((preset_config === "Fast Response")) { vwma_length = 21; atr_multiplier = 1.2; @@ -1246,9 +1726,9 @@ function main() { [pinescript.color.hex("#49ffce"), pinescript.color.hex("#9932cc")]; [pinescript.color.hex("#00d4ff"), pinescript.color.hex("#ff8c00")]; [pinescript.color.hex("#00ff00"), pinescript.color.hex("#ff0000")]; - let [bullish_color, bearish_color] = ((color_preset === "Classic") ? undefined : ((color_preset === "Aqua") ? undefined : ((color_preset === "Cosmic") ? undefined : ((color_preset === "Cyber") ? undefined : ((color_preset === "Neon") ? undefined : ((color_preset === "Custom") ? undefined : null)))))); + let [bullish_color, bearish_color] = pinescript.unpack(((color_preset === "Classic") ? undefined : ((color_preset === "Aqua") ? undefined : ((color_preset === "Cosmic") ? undefined : ((color_preset === "Cyber") ? undefined : ((color_preset === "Neon") ? undefined : ((color_preset === "Custom") ? undefined : null)))))), 2); let vwma_basis = pinescript.vwma(close, vwma_length); - let atr_value = ta.atr(vwma_length); + let atr_value = pinescript.atr(vwma_length); let upper_band = (vwma_basis + (atr_value * atr_multiplier)); let lower_band = (vwma_basis - (atr_value * atr_multiplier)); if (state.trend_direction === undefined) state.trend_direction = 0; @@ -1259,29 +1739,89 @@ function main() { state.trend_direction = -1; } } - let trend_turned_bullish = ((state.trend_direction === 1) && (pinescript.offset(state.trend_direction, 1) !== 1)); - let trend_turned_bearish = ((state.trend_direction === -1) && (pinescript.offset(state.trend_direction, 1) !== -1)); + let trend_turned_bullish = ((state.trend_direction === 1) && (pinescript.hist(0, state.trend_direction, 1) !== 1)); + let trend_turned_bearish = ((state.trend_direction === -1) && (pinescript.hist(1, state.trend_direction, 1) !== -1)); let trend_changed = (trend_turned_bullish || trend_turned_bearish); let trend_color = ((state.trend_direction === 1) ? bullish_color : bearish_color); pinescript.plot((enable_glow ? vwma_basis : null), "VWMA Glow Outer", pinescript.color.new(trend_color, 80), 8); pinescript.plot((enable_glow ? vwma_basis : null), "VWMA Glow Inner", pinescript.color.new(trend_color, 50), 4); let vwma_plot = pinescript.plot(vwma_basis, "VWMA Line", pinescript.color.new(trend_color, 0), 2); let upper_band_plot = pinescript.plot(upper_band, "Upper Band", ({ color: pinescript.color.new(trend_color, band_transparency) })); - let upper_mid_plot = pinescript.plot(math.avg(upper_band, vwma_basis), "Upper Mid", ({ color: pinescript.color.new(trend_color, 95) })); + let upper_mid_plot = pinescript.plot(pinescript.avg(upper_band, vwma_basis), "Upper Mid", ({ color: pinescript.color.new(trend_color, 95) })); let lower_band_plot = pinescript.plot(lower_band, "Lower Band", ({ color: pinescript.color.new(trend_color, band_transparency) })); - let lower_mid_plot = pinescript.plot(math.avg(lower_band, vwma_basis), "Lower Mid", ({ color: pinescript.color.new(trend_color, 95) })); + let lower_mid_plot = pinescript.plot(pinescript.avg(lower_band, vwma_basis), "Lower Mid", ({ color: pinescript.color.new(trend_color, 95) })); pinescript.fill(upper_band_plot, upper_mid_plot, (enable_ribbons ? pinescript.color.new(trend_color, 85) : null), "Upper Outer Ribbon"); pinescript.fill(upper_mid_plot, vwma_plot, (enable_ribbons ? pinescript.color.new(trend_color, 70) : null), "Upper Inner Ribbon"); pinescript.fill(lower_band_plot, lower_mid_plot, (enable_ribbons ? pinescript.color.new(trend_color, 85) : null), "Lower Outer Ribbon"); pinescript.fill(lower_mid_plot, vwma_plot, (enable_ribbons ? pinescript.color.new(trend_color, 70) : null), "Lower Inner Ribbon"); - barcolor((enable_barcolor ? ((state.trend_direction === 1) ? pinescript.color.new(bullish_color, 20) : pinescript.color.new(bearish_color, 20)) : null), ({ title: "Trend Bar Color" })); - alertcondition(trend_turned_bullish, ({ title: "Bullish Trend Signal", message: "Volume Weighted Trend: BULLISH trend confirmed on {{exchange}}:{{ticker}} - {{interval}}" })); - alertcondition(trend_turned_bearish, ({ title: "Bearish Trend Signal", message: "Volume Weighted Trend: BEARISH trend confirmed on {{exchange}}:{{ticker}} - {{interval}}" })); - alertcondition(trend_changed, ({ title: "Trend Direction Changed", message: "Volume Weighted Trend: Trend direction changed on {{exchange}}:{{ticker}} - {{interval}}" })); + pinescript.barcolor((enable_barcolor ? ((state.trend_direction === 1) ? pinescript.color.new(bullish_color, 20) : pinescript.color.new(bearish_color, 20)) : null), ({ title: "Trend Bar Color" })); + pinescript.alertcondition(trend_turned_bullish, ({ title: "Bullish Trend Signal", message: "Volume Weighted Trend: BULLISH trend confirmed on {{exchange}}:{{ticker}} - {{interval}}" })); + pinescript.alertcondition(trend_turned_bearish, ({ title: "Bearish Trend Signal", message: "Volume Weighted Trend: BEARISH trend confirmed on {{exchange}}:{{ticker}} - {{interval}}" })); + pinescript.alertcondition(trend_changed, ({ title: "Trend Direction Changed", message: "Volume Weighted Trend: Trend direction changed on {{exchange}}:{{ticker}} - {{interval}}" })); } -// Export for use -export { main +// The bar-by-bar engine: runs main() once per bar over the dataset. +function run(data, options = {}) { + data = data || {}; + const inClose = (data.close || []).map(Number); + const n = inClose.length; + const inOpen = (data.open || inClose).map(Number); + const inHigh = (data.high || inClose).map(Number); + const inLow = (data.low || inClose).map(Number); + const inVol = (data.volume || new Array(n).fill(0)).map(Number); + const inTime = (data.time || inClose.map((_, i) => i)).map(Number); + + // Reset all persistent and per-run state so repeated runs are independent. + globalThis.__pineState = {}; + globalThis.__pineRuntime = { plots: {}, plotshapes: {}, alerts: [], bars: n, inputs: options.inputs || {} }; + pinescript.__rt = {}; + pinescript.__bar = 0; + + // Growing OHLCV windows: each is a SeriesArray that gains one element per bar, + // so window built-ins like sma and highest see history up to the current bar. + const O = pinescript.asSeries([]), H = pinescript.asSeries([]), L = pinescript.asSeries([]); + const C = pinescript.asSeries([]), V = pinescript.asSeries([]), T = pinescript.asSeries([]); + const HL2 = pinescript.asSeries([]), HLC3 = pinescript.asSeries([]), OHLC4 = pinescript.asSeries([]); + globalThis.open = O; globalThis.high = H; globalThis.low = L; globalThis.close = C; + globalThis.volume = V; globalThis.time = T; + globalThis.hl2 = HL2; globalThis.hlc3 = HLC3; globalThis.ohlc4 = OHLC4; + globalThis.syminfo = globalThis.syminfo || { tickerid: 'SYNTHETIC', ticker: 'SYNTHETIC', mintick: 0.01 }; + + const rt = globalThis.__pineRuntime; + for (let i = 0; i < n; i++) { + O.push(inOpen[i]); H.push(inHigh[i]); L.push(inLow[i]); C.push(inClose[i]); V.push(inVol[i]); T.push(inTime[i]); + HL2.push((inHigh[i] + inLow[i]) / 2); + HLC3.push((inHigh[i] + inLow[i] + inClose[i]) / 3); + OHLC4.push((inOpen[i] + inHigh[i] + inLow[i] + inClose[i]) / 4); + + pinescript.__bar = i; + rt.__barIndex = i; + rt.__plotIdx = 0; + rt.__shapeIdx = 0; + globalThis.bar_index = i; + globalThis.last_bar_index = n - 1; + globalThis.barstate = { + isfirst: i === 0, islast: i === n - 1, isrealtime: false, ishistory: true, + isconfirmed: true, isnew: true, islastconfirmedhistory: i === n - 1, + }; + + main(); + } + + // Backfill skipped bars so every output series has exactly n entries. + for (const k of Object.keys(rt.plots)) { + const d = rt.plots[k].data; + for (let i = 0; i < n; i++) if (d[i] === undefined) d[i] = null; + } + for (const k of Object.keys(rt.plotshapes)) { + const d = rt.plotshapes[k].data; + for (let i = 0; i < n; i++) if (d[i] === undefined) d[i] = false; + } + return rt; +} + + +export { main, run }; diff --git a/converted/status.json b/converted/status.json index 1cf2811..ab8ad7f 100644 --- a/converted/status.json +++ b/converted/status.json @@ -1,180 +1,240 @@ { - "timestamp": "2026-02-15T17:54:13.554Z", + "timestamp": "2026-06-16T01:48:49.291Z", "total": 29, "passed": 28, "failed": 1, + "totalTimeMs": 2995, + "averageConfidenceScore": 67, "files": [ { "name": "3D_MACD_Bar_Plot_LuxAlgo.pine", "status": "converted", + "confidenceScore": 65, + "elapsedMs": 97, "jsExists": true, "errorExists": false }, { "name": "Arbitrage_Matrix_LuxAlgo.pine", "status": "converted", + "confidenceScore": 65, + "elapsedMs": 103, "jsExists": true, "errorExists": false }, { "name": "Asset_Drift_Model.pine", "status": "converted", + "confidenceScore": 70, + "elapsedMs": 105, "jsExists": true, "errorExists": false }, { "name": "Automatic_Trendline_Metrify.pine", "status": "converted", + "confidenceScore": 70, + "elapsedMs": 103, "jsExists": true, "errorExists": false }, { "name": "CE_1M_3M_Buy_Sell_Hold.pine", "status": "converted", + "confidenceScore": 70, + "elapsedMs": 93, "jsExists": true, "errorExists": false }, { "name": "Crossing_Moving_Averages_with_ADX_Filter.pine", "status": "converted", + "confidenceScore": 70, + "elapsedMs": 100, "jsExists": true, "errorExists": false }, { "name": "Folded_RSI.pine", "status": "converted", + "confidenceScore": 70, + "elapsedMs": 102, "jsExists": true, "errorExists": false }, { "name": "Golden_Pocket_Zones.pine", "status": "converted", + "confidenceScore": 70, + "elapsedMs": 102, "jsExists": true, "errorExists": false }, { "name": "Hyperfork_Matrix.pine", "status": "converted", + "confidenceScore": 70, + "elapsedMs": 117, "jsExists": true, "errorExists": false }, { "name": "Impulse_Trend_Arrows.pine", "status": "converted", + "confidenceScore": 70, + "elapsedMs": 97, "jsExists": true, "errorExists": false }, { "name": "Institutional_Order_Flow_Strength_Classifier_LuxAlgo.pine", "status": "converted", + "confidenceScore": 70, + "elapsedMs": 98, "jsExists": true, "errorExists": false }, { "name": "Liquidity_Sweep_Detector.pine", "status": "converted", + "confidenceScore": 70, + "elapsedMs": 108, "jsExists": true, "errorExists": false }, { "name": "Manias_Panics_and_Crashes.pine", "status": "converted", + "confidenceScore": 70, + "elapsedMs": 103, "jsExists": true, "errorExists": false }, { "name": "Net_Buy_Volume_theUltimator5.pine", "status": "converted", + "confidenceScore": 70, + "elapsedMs": 89, "jsExists": true, "errorExists": false }, { "name": "NY_5m_15m_Orb_-_Statistics_LTF_Candle_structure.pine", "status": "converted", + "confidenceScore": 70, + "elapsedMs": 126, "jsExists": true, "errorExists": false }, { "name": "Peak_Trading_Activity_Graphs_LuxAlgo.pine", "status": "converted", + "confidenceScore": 70, + "elapsedMs": 104, "jsExists": true, "errorExists": false }, { "name": "Proximal_Range_Filter_LuxAlgo.pine", "status": "converted", + "confidenceScore": 70, + "elapsedMs": 90, "jsExists": true, "errorExists": false }, { "name": "Pro_Pattern_Long-Short_High_Winrate.pine", "status": "converted", + "confidenceScore": 70, + "elapsedMs": 107, "jsExists": true, "errorExists": false }, { "name": "Quantum_Structure_jjart.pine", "status": "failed", + "confidenceScore": null, + "elapsedMs": 92, "jsExists": false, "errorExists": true }, { "name": "RBT_strategy.pine", "status": "converted", + "confidenceScore": 70, + "elapsedMs": 92, "jsExists": true, "errorExists": false }, { "name": "Scalping_Strategy_3_minutes.pine", "status": "converted", + "confidenceScore": 45, + "elapsedMs": 123, "jsExists": true, "errorExists": false }, { "name": "Singular_Spectrum_Decomposition_LuxAlgo.pine", "status": "converted", + "confidenceScore": 70, + "elapsedMs": 102, "jsExists": true, "errorExists": false }, { "name": "Smart_Trader_Episode_03_by_Ata_Sabanci_Candles_and_Tradelines.pine", "status": "converted", + "confidenceScore": 50, + "elapsedMs": 153, "jsExists": true, "errorExists": false }, { "name": "SMC_Trade_with_Dhruv.pine", "status": "converted", + "confidenceScore": 40, + "elapsedMs": 118, "jsExists": true, "errorExists": false }, { "name": "TehThomas_-_Aligned_Timeframe_Fair_Value_Gaps.pine", "status": "converted", + "confidenceScore": 70, + "elapsedMs": 96, "jsExists": true, "errorExists": false }, { "name": "Volatility_Contraction_Score.pine", "status": "converted", + "confidenceScore": 70, + "elapsedMs": 96, "jsExists": true, "errorExists": false }, { "name": "Volume_Surprise_LuxAlgo.pine", "status": "converted", + "confidenceScore": 70, + "elapsedMs": 92, "jsExists": true, "errorExists": false }, { "name": "Volume_Weighted_Trend_QuantAlgo.pine", "status": "converted", + "confidenceScore": 70, + "elapsedMs": 93, "jsExists": true, "errorExists": false }, { "name": "VSA_Pro_Trend_and_Rejection.pine", "status": "converted", + "confidenceScore": 70, + "elapsedMs": 94, "jsExists": true, "errorExists": false } diff --git a/package.json b/package.json index 2433b7b..af46311 100644 --- a/package.json +++ b/package.json @@ -6,8 +6,10 @@ "type": "module", "scripts": { "transpile": "node src/transpiler.js", - "test": "node test/test.js", + "test": "node test/test.js && node test/indicator_tests.js && node test/engine_tests.js", + "test:unit": "node test/test.js", "test:indicators": "node test/indicator_tests.js", + "test:engine": "node test/engine_tests.js", "convert:all": "node tools/batch_convert.js", "convert:status": "node tools/conversion_status.js" }, diff --git a/src/builtins.js b/src/builtins.js index 2f6b6cd..938c026 100644 --- a/src/builtins.js +++ b/src/builtins.js @@ -5,6 +5,90 @@ */ export const builtins = new Map([ + // ===== Bar-by-bar execution engine ===== + // PineScript runs the whole script once per bar, building up time series as it + // goes. The generated run() driver calls main() once per bar with OHLCV grown + // up to the current bar. These helpers give every history-dependent construct + // (x[n], crossover, change, ...) real per-bar history, keyed by a stable + // call-site id that the code generator injects, stored on pinescript.__rt and + // indexed by the current bar pinescript.__bar. Without this, values computed on + // the current bar (like ta.sma(close, 20)) would be lone scalars with no past, + // and crossover/[1]/var logic could never work. + + // Collapse a SeriesArray (or any object with valueOf) down to its current-bar + // scalar so we record a plain number/boolean in history buffers. + ['__scalar', function(v) { + if (v === null || v === undefined) return v; + if (typeof v === 'object' && typeof v.valueOf === 'function') { + const s = v.valueOf(); + if (typeof s === 'number' || typeof s === 'boolean' || s === null) return s; + } + return v; + }], + + // Record the current-bar value of an expression at call-site `id` and return + // its full per-bar history as an array, so the array-based built-ins below + // (crossover, change, highest, ...) see genuine history instead of a scalar. + ['series', function(id, value) { + const rt = this.__rt || (this.__rt = {}); + const store = rt.series || (rt.series = {}); + let buf = store[id]; + if (!buf) buf = store[id] = []; + buf[this.__bar | 0] = this.__scalar(value); + return buf; + }], + + // Pine's `x[n]` historical lookback. Records the current value at call-site + // `id`, then returns the value from n bars ago (or null if out of range). + ['hist', function(id, value, n) { + const buf = this.series(id, value); + const idx = (this.__bar | 0) - (n | 0); + return idx >= 0 && idx < buf.length ? buf[idx] : null; + }], + + // Pine's time(timeframe, session) built-in. We don't model session calendars, + // so this returns the current bar's timestamp (the common `time` usage). + ['time', function(timeframe, session) { + const t = globalThis.time; + if (t && typeof t.valueOf === 'function') return t.valueOf(); + return Array.isArray(t) ? t[t.length - 1] : t; + }], + + ['sign', function(x) { + if (x === null || x === undefined || (typeof x === 'number' && isNaN(x))) return null; + return Math.sign(Number(x)); + }], + + // Safe tuple unpacking for `[a, b] = expr`. Many Pine functions return na (null) + // before they have enough data; destructuring null throws in JS, so we coerce a + // non-iterable into an array of nulls of the expected length instead. + ['unpack', function(value, count) { + if (Array.isArray(value)) return value; + if (value != null && typeof value[Symbol.iterator] === 'function') return Array.from(value); + return new Array(count || 0).fill(null); + }], + + // Visual-output built-ins. We don't render charts, so these record the call and + // return safely instead of throwing, letting indicators that use them still run. + ['alertcondition', function(condition, title, message) { + const rt = globalThis.__pineRuntime; + if (rt) { + rt.alerts = rt.alerts || []; + if (this.__scalar(condition)) rt.alerts.push({ bar: rt.__barIndex | 0, title, message }); + } + return null; + }], + + ['barcolor', function(color) { return null; }], + + ['bgcolor', function(color, opts) { return null; }], + + ['fill', function(plot1, plot2, color, opts) { return null; }], + + ['hline', function(price, title, opts) { + return { price: this.__scalar(price), title: title || '', _type: 'hline' }; + }], + // Moving average calculations used for trend smoothing and noise reduction ['sma', function(series, length) { if (series === null || series === undefined) return null; @@ -676,14 +760,19 @@ export const builtins = new Map([ return builtins.get('nz')(value, replacement); }], - // Plotting and visual output stubs that record drawing instructions for the runtime + // Plotting output. Under the bar-by-bar engine this is called once per bar, so + // we append the current-bar value into a named series (rt.plots[name].data[bar]), + // building up a full output series instead of a single final value. The plot + // ordinal is reset each bar by the engine so untitled plots get stable names. ['plot', function(series, title = '', color = null, linewidth = 1) { - globalThis.__pineRuntime.plots.push({ - series, - title, - color, - linewidth - }); + const rt = globalThis.__pineRuntime; + if (!rt) return series; + const bar = rt.__barIndex | 0; + const ord = (rt.__plotIdx = (rt.__plotIdx | 0) + 1) - 1; + const key = (title && String(title)) || ('plot_' + ord); + let p = rt.plots[key]; + if (!p) p = rt.plots[key] = { title: key, color, linewidth, data: [] }; + p.data[bar] = this.__scalar(series); return series; }], @@ -766,9 +855,18 @@ export const builtins = new Map([ }], ['plotshape', function(condition, ...rest) { - if (globalThis.__pineRuntime) { - globalThis.__pineRuntime.plotshapes.push({ condition, args: rest }); + const rt = globalThis.__pineRuntime; + if (!rt) return condition; + const bar = rt.__barIndex | 0; + const ord = (rt.__shapeIdx = (rt.__shapeIdx | 0) + 1) - 1; + let key = 'shape_' + ord; + for (const r of rest) { + if (typeof r === 'string') { key = r; break; } + if (r && typeof r === 'object' && r.title) { key = String(r.title); break; } } + let s = rt.plotshapes[key]; + if (!s) s = rt.plotshapes[key] = { title: key, data: [] }; + s.data[bar] = !!this.__scalar(condition); return condition; }], @@ -1395,7 +1493,9 @@ export const builtins = new Map([ const info = { ticker: 'AAPL', tickerid: 'NASDAQ:AAPL', prefix: 'NASDAQ', root: 'AAPL', suffix: '' }; return info[type] || ''; }], - ['time', Date.now()], + // NOTE: `time` is defined as a function near the top of this map (Pine's + // time(timeframe, session) built-in). Don't redefine it here as a scalar, or + // the Map would overwrite the function and `time(...)` calls would break. ['timenow', Date.now()], ['barstate', 'LAST'], ['dividends', {}], diff --git a/src/generator.js b/src/generator.js index bb1d7f5..78df63a 100644 --- a/src/generator.js +++ b/src/generator.js @@ -2,6 +2,30 @@ import { builtins } from './builtins.js'; +// Built-ins whose listed positional arguments are *series* that must be seen with +// full per-bar history (not just the current-bar scalar) for the result to be +// correct. The generator wraps those arguments in pinescript.series(id, expr) so +// the runtime accumulates their history across bars. This is what makes +// crossover/change/highest/lowest/etc. actually work on computed values. +const SERIES_ARG_FUNCS = { + 'pinescript.crossover': [0, 1], + 'pinescript.crossunder': [0, 1], + 'pinescript.cross': [0, 1], + 'pinescript.change': [0], + 'pinescript.rising': [0], + 'pinescript.falling': [0], + 'pinescript.cum': [0], + 'pinescript.cumsum': [0], + 'pinescript.barssince': [0], + 'pinescript.valuewhen': [0, 1], + 'pinescript.highest': [0], + 'pinescript.lowest': [0], + 'pinescript.highestbars': [0], + 'pinescript.lowestbars': [0], + 'pinescript.sum': [0], + 'pinescript.offset': [0], +}; + class CodeGenerator { constructor() { this.output = []; @@ -22,6 +46,11 @@ class CodeGenerator { this.renameMap = new Map(); + // Monotonic id handed to history-dependent runtime calls (x[n], crossover, + // change, ...). Each call-site in the source gets a unique, stable id so the + // engine can keep a separate per-bar history buffer for it. + this.siteCounter = 0; + this.reservedNamespaces = new Set([ 'ta', 'math', 'array', 'str', 'color', 'table', 'position', 'location', 'shape', 'size', 'text', @@ -30,6 +59,11 @@ class CodeGenerator { ]); } + // Hands out the next stable call-site id for history-dependent runtime calls. + nextSite() { + return this.siteCounter++; + } + // Renames user-declared variables that collide with reserved Pine namespaces // (like "ta" or "strategy") so they don't shadow the runtime objects. getSafeName(name) { @@ -119,16 +153,20 @@ class CodeGenerator { const targets = (node.targets || []).filter(t => t !== '_'); const lhs = `[${(node.targets || []).map(t => t === '_' ? '' : t).join(', ')}]`; + // Wrap the right-hand side so unpacking a na/null tuple yields nulls instead + // of throwing "is not iterable" (Pine functions often return na early on). + const rhs = `pinescript.unpack(${this.generate(node.value)}, ${(node.targets || []).length})`; + const needsDeclaration = targets.some(t => !this.context.variables.has(t) && !this.reservedNamespaces.has(t)); if (needsDeclaration) { for (const t of targets) { if (!this.reservedNamespaces.has(t)) this.context.variables.add(t); } - this.writeln(`let ${lhs} = ${this.generate(node.value)};`); + this.writeln(`let ${lhs} = ${rhs};`); return; } - this.writeln(`${lhs} = ${this.generate(node.value)};`); + this.writeln(`${lhs} = ${rhs};`); } // Pine's switch-as-expression becomes a chain of ternary operators in JavaScript, @@ -261,6 +299,76 @@ class CodeGenerator { this.write(' lookahead_on: true,\n'); this.write('};\n\n'); + // Graceful-degradation namespace shims. PineScript has a huge standard library + // and not every function/namespace is implemented yet. Rather than crashing on + // an unmapped reference like `chart.point.now()` or `timeframe.in_seconds()`, + // these Proxy-backed namespaces return a safe no-op (null) for anything we don't + // implement, so a script still runs end-to-end and produces whatever output it + // can. The post-conversion reviewer separately reports which builtins are missing. + this.write('function __pineNS(real) {\n'); + this.write(' const base = real || {};\n'); + this.write(' return new Proxy(base, {\n'); + this.write(' get(target, key) {\n'); + this.write(' if (key in target) return target[key];\n'); + this.write(' if (typeof key === "symbol") return target[key];\n'); + this.write(' // Unknown member: usable as a value (null), a constant, or a no-op function.\n'); + this.write(' const noop = function() { return null; };\n'); + this.write(' noop.toString = () => "";\n'); + this.write(' return noop;\n'); + this.write(' }\n'); + this.write(' });\n'); + this.write('}\n'); + // Bare namespace objects that scripts reference directly. ta./math./array./str. + // calls are normally rewritten to pinescript.*, so these mainly catch the long + // tail of unimplemented members and keep them from throwing. + this.write('globalThis.ta = globalThis.ta || __pineNS({});\n'); + this.write('globalThis.math = globalThis.math || __pineNS({ pi: Math.PI, e: Math.E, phi: 1.618033988749895, rphi: 0.618033988749895 });\n'); + this.write('globalThis.chart = globalThis.chart || __pineNS({ point: __pineNS({}), bg_color: null, fg_color: null });\n'); + this.write('globalThis.line = globalThis.line || __pineNS({ style_solid: "solid", style_dashed: "dashed", style_dotted: "dotted" });\n'); + this.write('globalThis.box = globalThis.box || __pineNS({});\n'); + this.write('globalThis.label = globalThis.label || __pineNS({ style_label_down: "label_down", style_label_up: "label_up", style_none: "none" });\n'); + this.write('globalThis.polyline = globalThis.polyline || __pineNS({});\n'); + this.write('globalThis.linefill = globalThis.linefill || __pineNS({});\n'); + this.write('globalThis.matrix = globalThis.matrix || __pineNS({});\n'); + this.write('globalThis.map = globalThis.map || __pineNS({});\n'); + this.write('globalThis.session = globalThis.session || __pineNS({ regular: "regular", extended: "extended" });\n'); + this.write('globalThis.dayofweek = globalThis.dayofweek || __pineNS({ sunday: 1, monday: 2, tuesday: 3, wednesday: 4, thursday: 5, friday: 6, saturday: 7 });\n'); + this.write('globalThis.timeframe = __pineNS(Object.assign(globalThis.timeframe || {}, { period: (globalThis.timeframe && globalThis.timeframe.period) || "D", isintraday: false, isdaily: true, multiplier: 1 }));\n'); + this.write('globalThis.request = __pineNS(globalThis.request || {});\n'); + this.write('globalThis.input = __pineNS(globalThis.input || {});\n'); + this.write('globalThis.adjustment = globalThis.adjustment || __pineNS({});\n'); + this.write('globalThis.earnings = globalThis.earnings || __pineNS({});\n'); + this.write('globalThis.dividends = globalThis.dividends || __pineNS({});\n'); + this.write('globalThis.splits = globalThis.splits || __pineNS({});\n'); + this.write('globalThis.currency = globalThis.currency || __pineNS({});\n'); + this.write('globalThis.display = globalThis.display || __pineNS({ none: "none", all: "all", pane: "pane", data_window: "data_window", status_line: "status_line", price_scale: "price_scale" });\n'); + this.write('globalThis.format = globalThis.format || __pineNS({ price: "price", volume: "volume", percent: "percent", mintick: "mintick", inherit: "inherit" });\n'); + this.write('globalThis.scale = globalThis.scale || __pineNS({ left: "left", right: "right", none: "none" });\n'); + this.write('globalThis.font = globalThis.font || __pineNS({ family_default: "default", family_monospace: "monospace" });\n'); + this.write('globalThis.xloc = globalThis.xloc || __pineNS({ bar_index: "bar_index", bar_time: "bar_time" });\n'); + this.write('globalThis.yloc = globalThis.yloc || __pineNS({ price: "price", abovebar: "abovebar", belowbar: "belowbar" });\n'); + this.write('globalThis.extend = globalThis.extend || __pineNS({ none: "none", left: "left", right: "right", both: "both" });\n'); + this.write('globalThis.order = globalThis.order || __pineNS({ ascending: "ascending", descending: "descending" });\n'); + this.write('globalThis.alert = globalThis.alert || Object.assign(function() { return null; }, { freq_once_per_bar: "once_per_bar", freq_once_per_bar_close: "once_per_bar_close", freq_all: "all" });\n'); + this.write('globalThis.dayofmonth = globalThis.dayofmonth || function(t) { return new Date(t || 0).getUTCDate(); };\n'); + // `plot` is rewritten to pinescript.plot when called, so a bare `plot` is only + // ever a style-constant lookup like plot.style_histogram. + this.write('globalThis.plot = globalThis.plot || __pineNS({ style_line: "line", style_linebr: "linebr", style_stepline: "stepline", style_histogram: "histogram", style_cross: "cross", style_area: "area", style_areabr: "areabr", style_columns: "columns", style_circles: "circles" });\n'); + // PineScript type-cast helpers used as functions, e.g. int(x), float(x). + this.write('globalThis.int = globalThis.int || function(x) { return x == null ? null : Math.trunc(Number(x)); };\n'); + this.write('globalThis.float = globalThis.float || function(x) { return x == null ? null : Number(x); };\n'); + this.write('globalThis.bool = globalThis.bool || function(x) { return Boolean(x); };\n'); + this.write('globalThis.string = globalThis.string || function(x) { return x == null ? null : String(x); };\n'); + this.write('globalThis.dayofweek = globalThis.dayofweek || __pineNS({ sunday: 1, monday: 2, tuesday: 3, wednesday: 4, thursday: 5, friday: 6, saturday: 7 });\n'); + this.write('globalThis.str = globalThis.str || __pineNS({});\n'); + // The reserved namespaces (ta, math, str, array) are emitted as pinescript. + // when a script uses them as a value (e.g. `math.pi`), so mirror the global + // shims onto pinescript too. + this.write('pinescript.math = globalThis.math;\n'); + this.write('pinescript.ta = globalThis.ta;\n'); + this.write('pinescript.str = globalThis.str;\n'); + this.write('pinescript.array = __pineNS(globalThis.array);\n\n'); + // Minimal color namespace -- provides hex parsing, gradient interpolation, // rgb construction, and a handful of named color constants. this.write('pinescript.color = {\n'); @@ -358,9 +466,15 @@ class CodeGenerator { this.popIndent(); this.write('}\n\n'); - // Export the main entry point along with any input parameters - // so the host environment can inspect and override them. - this.write('export { main'); + // Emit the bar-by-bar engine driver. PineScript executes the whole script + // once per bar; run() reproduces that by growing the OHLCV series one bar at + // a time and calling main() for each bar, collecting plotted values into + // full output series. This is the core of correct PineScript semantics. + this.write(this.buildRunDriver()); + + // Export the main entry point, the bar-by-bar runner, and any input + // parameters so the host environment can inspect and override them. + this.write('export { main, run'); for (const input of node.inputs || []) { this.write(`, ${input.name}`); } @@ -369,6 +483,75 @@ class CodeGenerator { return this.output.join('\n'); } + // Produces the run(data) driver source that executes the script bar by bar. + // `data` is an object of OHLCV arrays: { open, high, low, close, volume, time }. + // Returns the populated globalThis.__pineRuntime, whose .plots / .plotshapes are + // named series with one value per bar. + buildRunDriver() { + return ` +// The bar-by-bar engine: runs main() once per bar over the dataset. +function run(data, options = {}) { + data = data || {}; + const inClose = (data.close || []).map(Number); + const n = inClose.length; + const inOpen = (data.open || inClose).map(Number); + const inHigh = (data.high || inClose).map(Number); + const inLow = (data.low || inClose).map(Number); + const inVol = (data.volume || new Array(n).fill(0)).map(Number); + const inTime = (data.time || inClose.map((_, i) => i)).map(Number); + + // Reset all persistent and per-run state so repeated runs are independent. + globalThis.__pineState = {}; + globalThis.__pineRuntime = { plots: {}, plotshapes: {}, alerts: [], bars: n, inputs: options.inputs || {} }; + pinescript.__rt = {}; + pinescript.__bar = 0; + + // Growing OHLCV windows: each is a SeriesArray that gains one element per bar, + // so window built-ins like sma and highest see history up to the current bar. + const O = pinescript.asSeries([]), H = pinescript.asSeries([]), L = pinescript.asSeries([]); + const C = pinescript.asSeries([]), V = pinescript.asSeries([]), T = pinescript.asSeries([]); + const HL2 = pinescript.asSeries([]), HLC3 = pinescript.asSeries([]), OHLC4 = pinescript.asSeries([]); + globalThis.open = O; globalThis.high = H; globalThis.low = L; globalThis.close = C; + globalThis.volume = V; globalThis.time = T; + globalThis.hl2 = HL2; globalThis.hlc3 = HLC3; globalThis.ohlc4 = OHLC4; + globalThis.syminfo = globalThis.syminfo || { tickerid: 'SYNTHETIC', ticker: 'SYNTHETIC', mintick: 0.01 }; + + const rt = globalThis.__pineRuntime; + for (let i = 0; i < n; i++) { + O.push(inOpen[i]); H.push(inHigh[i]); L.push(inLow[i]); C.push(inClose[i]); V.push(inVol[i]); T.push(inTime[i]); + HL2.push((inHigh[i] + inLow[i]) / 2); + HLC3.push((inHigh[i] + inLow[i] + inClose[i]) / 3); + OHLC4.push((inOpen[i] + inHigh[i] + inLow[i] + inClose[i]) / 4); + + pinescript.__bar = i; + rt.__barIndex = i; + rt.__plotIdx = 0; + rt.__shapeIdx = 0; + globalThis.bar_index = i; + globalThis.last_bar_index = n - 1; + globalThis.barstate = { + isfirst: i === 0, islast: i === n - 1, isrealtime: false, ishistory: true, + isconfirmed: true, isnew: true, islastconfirmedhistory: i === n - 1, + }; + + main(); + } + + // Backfill skipped bars so every output series has exactly n entries. + for (const k of Object.keys(rt.plots)) { + const d = rt.plots[k].data; + for (let i = 0; i < n; i++) if (d[i] === undefined) d[i] = null; + } + for (const k of Object.keys(rt.plotshapes)) { + const d = rt.plotshapes[k].data; + for (let i = 0; i < n; i++) if (d[i] === undefined) d[i] = false; + } + return rt; +} + +`; + } + generateInputDeclaration(node) { this.context.inputs.push(node); this.writeln(`// Input: ${node.name} (${node.inputType})`); @@ -715,6 +898,18 @@ class CodeGenerator { } const args = positionalArgs.map(arg => this.generate(arg)); + + // For history-dependent built-ins, wrap their series arguments so the runtime + // tracks each one's per-bar history (see SERIES_ARG_FUNCS). + const wrapPositions = SERIES_ARG_FUNCS[calleeName]; + if (wrapPositions) { + for (const pos of wrapPositions) { + if (pos < args.length) { + args[pos] = `pinescript.series(${this.nextSite()}, ${args[pos]})`; + } + } + } + if (namedArgs.length > 0) { const opts = namedArgs .map(na => `${na.name}: ${this.generate(na.value)}`) @@ -725,10 +920,11 @@ class CodeGenerator { return `${calleeName}(${args.join(', ')})`; } - // Pine's `x[n]` is historical series look-back, not array indexing. - // We translate it to a runtime helper that handles the offset safely. + // Pine's `x[n]` is historical series look-back, not array indexing. We record + // the current-bar value of `x` at a unique call-site id and read back n bars, + // so lookback works even when `x` is a value computed on the current bar. generateArrayAccess(node) { - return `pinescript.offset(${this.generate(node.array)}, ${this.generate(node.index)})`; + return `pinescript.hist(${this.nextSite()}, ${this.generate(node.array)}, ${this.generate(node.index)})`; } generatePropertyAccess(node) { @@ -1012,6 +1208,8 @@ class CodeGenerator { 'bgcolor': 'pinescript.bgcolor', 'fill': 'pinescript.fill', 'alert': 'pinescript.alert', + 'alertcondition': 'pinescript.alertcondition', + 'barcolor': 'pinescript.barcolor', // Strategy order functions 'strategy.entry': 'pinescript.strategyEntry', diff --git a/src/reviewer.js b/src/reviewer.js index e4f46d2..4e19c31 100644 --- a/src/reviewer.js +++ b/src/reviewer.js @@ -421,7 +421,9 @@ export async function reviewGeneratedCode(code, opts = {}) { const used = extractPinescriptMembers(code); const builtinKeys = new Set(Array.from(runtimeBuiltins.keys())); const allowed = allowedMembers(); - const missing = used.filter((m) => !builtinKeys.has(m) && !allowed.has(m)); + // Names starting with "__" are internal engine state (pinescript.__rt, + // pinescript.__bar), not built-in functions, so don't flag them as missing. + const missing = used.filter((m) => !m.startsWith('__') && !builtinKeys.has(m) && !allowed.has(m)); if (missing.length) { report.warnings.push( `Possibly missing runtime builtins/mappings: ${missing.slice(0, 30).join(', ')}${missing.length > 30 ? ' ...' : ''}` diff --git a/test/engine_tests.js b/test/engine_tests.js new file mode 100644 index 0000000..23ddb15 --- /dev/null +++ b/test/engine_tests.js @@ -0,0 +1,178 @@ +// Numeric correctness tests for the bar-by-bar execution engine. +// +// Unlike the other suites (which only check that transpilation *succeeds* and the +// output *contains* expected strings), these tests actually RUN the generated +// code over known OHLCV data and assert the full per-bar output series equals +// hand-computed expected values. This is the test that proves PineScript +// semantics are implemented correctly, not just that code was produced. + +import { transpile } from '../src/transpiler.js'; + +let passed = 0; +let failed = 0; + +// Transpile Pine source, import the generated module, and run it bar-by-bar. +async function runPine(pine, data) { + const result = transpile(pine.trim()); + if (!result.success) throw new Error(`Transpile failed: ${result.error}`); + const url = 'data:text/javascript,' + encodeURIComponent(result.code); + const mod = await import(url); + if (typeof mod.run !== 'function') throw new Error('generated module has no run() export'); + return mod.run(data); +} + +// Compare two series of numbers/null with a small tolerance for floats. +function seriesEqual(actual, expected, tol = 1e-9) { + if (!Array.isArray(actual) || actual.length !== expected.length) return false; + for (let i = 0; i < expected.length; i++) { + const a = actual[i]; + const e = expected[i]; + if (e === null || e === undefined) { + if (a !== null && a !== undefined && !(typeof a === 'number' && Number.isNaN(a))) return false; + } else if (typeof e === 'boolean') { + if (Boolean(a) !== e) return false; + } else { + if (a === null || a === undefined || Math.abs(a - e) > tol) return false; + } + } + return true; +} + +const fmt = (s) => s.map((x) => (x == null ? 'na' : typeof x === 'number' ? Math.round(x * 1000) / 1000 : x)).join(' '); + +async function test(name, fn) { + try { + await fn(); + console.log(`PASS: ${name}`); + passed++; + } catch (e) { + console.log(`FAIL: ${name}`); + console.log(` ${e.message}`); + failed++; + } +} + +function assertSeries(actual, expected, label) { + if (!seriesEqual(actual, expected)) { + throw new Error(`${label}\n expected: ${fmt(expected)}\n actual: ${fmt(actual)}`); + } +} + +// Shared dataset: a simple ramp so expected values are easy to verify by hand. +const ramp = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; +const rampData = { close: ramp, open: ramp, high: ramp, low: ramp, volume: ramp.map(() => 1) }; + +console.log('Running Bar-by-Bar Engine Numeric Tests\n'); +console.log('='.repeat(60)); + +await test('ta.sma produces a full per-bar series', async () => { + const rt = await runPine( + `//@version=5 +indicator("t") +plot(ta.sma(close, 3), "s")`, + rampData, + ); + // SMA(3) of 1..10: first two bars are na, then mean of the trailing 3. + assertSeries(rt.plots['s'].data, [null, null, 2, 3, 4, 5, 6, 7, 8, 9], 'SMA(3) mismatch'); +}); + +await test('x[1] historical lookback returns the previous bar', async () => { + const rt = await runPine( + `//@version=5 +indicator("t") +plot(close[1], "p")`, + rampData, + ); + assertSeries(rt.plots['p'].data, [null, 1, 2, 3, 4, 5, 6, 7, 8, 9], 'close[1] mismatch'); +}); + +await test('ta.change returns bar-over-bar difference', async () => { + const rt = await runPine( + `//@version=5 +indicator("t") +plot(ta.change(close), "c")`, + rampData, + ); + assertSeries(rt.plots['c'].data, [null, 1, 1, 1, 1, 1, 1, 1, 1, 1], 'change mismatch'); +}); + +await test('ta.highest tracks the rolling window max', async () => { + const rt = await runPine( + `//@version=5 +indicator("t") +plot(ta.highest(high, 3), "h")`, + rampData, + ); + assertSeries(rt.plots['h'].data, [null, null, 3, 4, 5, 6, 7, 8, 9, 10], 'highest mismatch'); +}); + +await test('var state accumulates across bars', async () => { + const rt = await runPine( + `//@version=5 +indicator("t") +var float total = 0.0 +total := total + close +plot(total, "t")`, + rampData, + ); + // Running cumulative sum of 1..10. + assertSeries(rt.plots['t'].data, [1, 3, 6, 10, 15, 21, 28, 36, 45, 55], 'var accumulation mismatch'); +}); + +await test('ta.crossover fires on the exact bar of the cross', async () => { + // fast SMA(2) crosses above slow SMA(4) once the series turns up. + const wave = [5, 4, 3, 2, 3, 4, 5, 6, 7, 8]; + const rt = await runPine( + `//@version=5 +indicator("t") +fast = ta.sma(close, 2) +slow = ta.sma(close, 4) +plotshape(ta.crossover(fast, slow), "x")`, + { close: wave, open: wave, high: wave, low: wave, volume: wave.map(() => 1) }, + ); + const fired = rt.plotshapes['x'].data.map((v, i) => (v ? i : null)).filter((v) => v != null); + // Compute the expected cross bar directly from SMA definitions. + const sma = (a, len, i) => (i + 1 < len ? null : a.slice(i - len + 1, i + 1).reduce((x, y) => x + y, 0) / len); + const expected = []; + for (let i = 1; i < wave.length; i++) { + const f0 = sma(wave, 2, i - 1), s0 = sma(wave, 4, i - 1); + const f1 = sma(wave, 2, i), s1 = sma(wave, 4, i); + if (f0 != null && s0 != null && f0 < s0 && f1 >= s1) expected.push(i); + } + if (JSON.stringify(fired) !== JSON.stringify(expected)) { + throw new Error(`crossover bars mismatch: expected ${JSON.stringify(expected)}, got ${JSON.stringify(fired)}`); + } + if (expected.length === 0) throw new Error('test data never crosses -- test is meaningless'); +}); + +await test('bar_index counts bars from zero', async () => { + const rt = await runPine( + `//@version=5 +indicator("t") +plot(bar_index, "bi")`, + rampData, + ); + assertSeries(rt.plots['bi'].data, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], 'bar_index mismatch'); +}); + +await test('ta.ema matches a reference exponential moving average', async () => { + const rt = await runPine( + `//@version=5 +indicator("t") +plot(ta.ema(close, 3), "e")`, + rampData, + ); + // Reference EMA seeded with the first value, alpha = 2/(len+1), over all bars. + const alpha = 2 / (3 + 1); + const expected = []; + let prev = ramp[0]; + for (let i = 0; i < ramp.length; i++) { + prev = i === 0 ? ramp[0] : alpha * ramp[i] + (1 - alpha) * prev; + expected.push(prev); + } + assertSeries(rt.plots['e'].data, expected, 'ema mismatch'); +}); + +console.log('='.repeat(60)); +console.log(`Results: ${passed} passed, ${failed} failed out of ${passed + failed} tests`); +if (failed > 0) process.exit(1); diff --git a/tools/batch_convert.js b/tools/batch_convert.js index 7d29954..fb509a4 100644 --- a/tools/batch_convert.js +++ b/tools/batch_convert.js @@ -52,14 +52,14 @@ for (const file of pineFiles) { confidenceScore = parseFloat(confidenceMatch[1]); } - if (output.includes('Review: PASS')) { + if (output.includes('Review Result: PASS')) { const scoreLabel = confidenceScore !== null ? ` (confidence: ${confidenceScore}%)` : ''; console.log(`PASS${scoreLabel}`); status = 'converted'; results.passed.push(file); // Clean up any leftover error file from a previous run if (fs.existsSync(errorPath)) fs.unlinkSync(errorPath); - } else if (output.includes('Review: FAIL')) { + } else if (output.includes('Review Result: FAIL')) { const scoreLabel = confidenceScore !== null ? ` (confidence: ${confidenceScore}%)` : ''; console.log(`FAIL${scoreLabel}`); status = 'failed'; From c1035284e792bae6294a5e451489bc0c6a0c3c01 Mon Sep 17 00:00:00 2001 From: MeridianAlgo Date: Mon, 29 Jun 2026 20:41:47 -0500 Subject: [PATCH 2/2] Remove AI reviewer, add browser demo, fix converter bugs - Remove optional local AI reviewer (Python deps, spawn-based stage, --review-ai flag) and all references - Add browser-based converter at web/index.html (client-side, no backend) with GitHub Pages + CI workflows under .github/workflows - Fix multi-line ternary parse failure: line continuation now also looks forward when the next line begins with a leading operator (?, :, +, .) - Implement missing math.todegrees / math.toradians runtime builtins - Reviewer accuracy: skip member calls, strip comments/strings before scanning, recognize Pine global builtins, ignore [0] series offsets Result: 29/29 examples convert, avg confidence 68.4% -> 98.8%, all tests pass Co-Authored-By: Claude Opus 4.8 --- .github/workflows/ci.yml | 16 + .github/workflows/pages.yml | 42 + README.md | 40 +- ai_reviewer/requirements.txt | 5 - ai_reviewer/review.py | 135 - converted/3D_MACD_Bar_Plot_LuxAlgo.js | 319 +- converted/Arbitrage_Matrix_LuxAlgo.js | 499 ++- converted/Asset_Drift_Model.js | 469 ++- converted/Automatic_Trendline_Metrify.js | 397 ++- converted/CE_1M_3M_Buy_Sell_Hold.js | 227 +- ...rossing_Moving_Averages_with_ADX_Filter.js | 253 +- converted/Folded_RSI.js | 347 +- converted/Golden_Pocket_Zones.js | 305 +- converted/Hyperfork_Matrix.js | 725 ++-- converted/Impulse_Trend_Arrows.js | 295 +- ..._Order_Flow_Strength_Classifier_LuxAlgo.js | 331 +- converted/Liquidity_Sweep_Detector.js | 339 +- converted/Manias_Panics_and_Crashes.js | 469 ++- ...m_Orb_-_Statistics_LTF_Candle_structure.js | 509 ++- converted/Net_Buy_Volume_theUltimator5.js | 219 +- .../Peak_Trading_Activity_Graphs_LuxAlgo.js | 449 ++- .../Pro_Pattern_Long-Short_High_Winrate.js | 393 +- converted/Proximal_Range_Filter_LuxAlgo.js | 241 +- converted/Quantum_Structure_jjart.err.txt | 4 - converted/Quantum_Structure_jjart.js | 3165 +++++++++++++++++ converted/RBT_strategy.js | 276 +- converted/SMC_Trade_with_Dhruv.js | 641 ++-- converted/Scalping_Strategy_3_minutes.js | 873 +++-- ...Singular_Spectrum_Decomposition_LuxAlgo.js | 383 +- ...3_by_Ata_Sabanci_Candles_and_Tradelines.js | 1531 ++++---- ...mas_-_Aligned_Timeframe_Fair_Value_Gaps.js | 303 +- converted/VSA_Pro_Trend_and_Rejection.js | 268 +- converted/Volatility_Contraction_Score.js | 335 +- converted/Volume_Surprise_LuxAlgo.js | 265 +- converted/Volume_Weighted_Trend_QuantAlgo.js | 269 +- converted/status.json | 132 +- src/builtins.js | 210 +- src/cli.js | 21 +- src/generator.js | 183 +- src/lexer.js | 38 +- src/parser.js | 10 + src/reviewer.js | 145 +- test/engine_tests.js | 11 + test/test.js | 3 +- web/index.html | 274 ++ 45 files changed, 12308 insertions(+), 4056 deletions(-) create mode 100644 .github/workflows/ci.yml create mode 100644 .github/workflows/pages.yml delete mode 100644 ai_reviewer/requirements.txt delete mode 100644 ai_reviewer/review.py delete mode 100644 converted/Quantum_Structure_jjart.err.txt create mode 100644 converted/Quantum_Structure_jjart.js create mode 100644 web/index.html diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..fef8fbe --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,16 @@ +name: Tests + +on: + push: + branches: [master, main] + pull_request: + +jobs: + test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: 20 + - run: npm test diff --git a/.github/workflows/pages.yml b/.github/workflows/pages.yml new file mode 100644 index 0000000..a933f09 --- /dev/null +++ b/.github/workflows/pages.yml @@ -0,0 +1,42 @@ +name: Deploy converter demo to GitHub Pages + +on: + push: + branches: [master, main] + workflow_dispatch: + +permissions: + contents: read + pages: write + id-token: write + +concurrency: + group: pages + cancel-in-progress: true + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Assemble site + run: | + mkdir -p _site/src _site/examples + cp web/index.html _site/index.html + cp src/*.js _site/src/ + cp examples/*.pine _site/examples/ + # manifest the page fetches to populate the examples dropdown + ( cd examples && ls *.pine | sort | sed 's/.*/"&"/' | paste -sd, - | sed 's/^/[/;s/$/]/' ) > _site/examples/manifest.json + - uses: actions/upload-pages-artifact@v3 + with: + path: _site + + deploy: + needs: build + runs-on: ubuntu-latest + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + steps: + - id: deployment + uses: actions/deploy-pages@v4 diff --git a/README.md b/README.md index fe2c941..1153777 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,14 @@ A production-grade transpiler that converts TradingView PineScript (v5/v6) into This is not a toy converter. It handles real-world PineScript with 240+ function mappings, full series semantics, state persistence across bars, multi-timeframe support, and a post-conversion reviewer that catches problems before they hit you at runtime. +## Try It Online + +Paste your PineScript and convert it right in your browser — nothing is uploaded, the transpiler runs client-side: + +**https://meridianalgo.github.io/Pine-A-Script/** + +(Published automatically from `web/index.html` by the GitHub Pages workflow. Enable it once under repo Settings → Pages → Source: GitHub Actions.) + ## What It Does - Converts PineScript v5/v6 into standard JavaScript (ESM modules) @@ -12,7 +20,6 @@ This is not a toy converter. It handles real-world PineScript with 240+ function - Persists `var` / `varip` state across bar executions, just like TradingView does - Supports multi-timeframe data via `request.security()` with timeframe resampling - Runs a multi-stage post-conversion reviewer that catches syntax errors, missing functions, and conversion artifacts -- Optional local AI review using a lightweight code model (no server or API key needed) ## Project Layout @@ -26,7 +33,7 @@ src/ cli.js Command-line interface for converting files reviewer.js Post-conversion quality checks and validation -ai_reviewer/ Optional local AI review (Python, runs on your machine) +web/ Browser-based converter demo (deployed to GitHub Pages) test/ Test suite including bar-by-bar runtime smoke tests tools/ Batch conversion utilities and status tracking examples/ Sample PineScript files for testing @@ -100,31 +107,6 @@ Environment variables for fine-grained control: |----------|--------| | `PINE_REVIEW=0` | Disables the reviewer completely | | `PINE_REVIEW_IMPORT=0` | Skips the import smoke test | -| `PINE_REVIEW_AI=1` | Enables the optional AI review | -| `PINE_REVIEWER_MODEL` | Sets the HuggingFace model for AI review | -| `PINE_REVIEWER_PYTHON` | Path to your Python interpreter | -| `PINE_REVIEWER_USE_CUDA=1` | Enables GPU acceleration for AI review | - -### Optional AI review - -The repo includes a fully local AI reviewer that runs a small code model on your machine. No server, no API key, no data leaves your laptop. - -Default model: `Qwen/Qwen2.5-Coder-0.5B-Instruct` (fast, low RAM) -Larger model: `Qwen/Qwen2.5-Coder-1.5B-Instruct` (better results, slower) - -Setup: - -```bash -pip install -r ai_reviewer/requirements.txt -``` - -Run with AI review enabled: - -```bash -PINE_REVIEW_AI=1 node src/cli.js script.pine output.js --review-ai -``` - -Note: Small models can produce repetitive or low-quality output. The reviewer sanitizes bad responses and will suggest switching models if needed. ## Supported PineScript Features @@ -355,7 +337,6 @@ Options: --tokens Output the token stream instead of JavaScript --no-review Skip post-conversion review --no-review-import Skip the runtime import test only - --review-ai Enable local AI review (needs Python deps) ``` ### Example workflow @@ -369,9 +350,6 @@ node src/cli.js my_indicator.pine --ast # Convert without any review (fastest) node src/cli.js my_indicator.pine my_indicator.js --no-review - -# Convert with AI review for deeper analysis -PINE_REVIEW_AI=1 node src/cli.js my_indicator.pine my_indicator.js --review-ai ``` ## Running Tests diff --git a/ai_reviewer/requirements.txt b/ai_reviewer/requirements.txt deleted file mode 100644 index e39d4dd..0000000 --- a/ai_reviewer/requirements.txt +++ /dev/null @@ -1,5 +0,0 @@ -transformers>=4.36.0 -torch -sentencepiece -safetensors -accelerate diff --git a/ai_reviewer/review.py b/ai_reviewer/review.py deleted file mode 100644 index cfd1ceb..0000000 --- a/ai_reviewer/review.py +++ /dev/null @@ -1,135 +0,0 @@ -import json -import os -import sys -import re -import warnings - -# Reduce noise from optional TF/JAX backends and other warnings. -os.environ.setdefault("TRANSFORMERS_NO_TF", "1") -os.environ.setdefault("TRANSFORMERS_NO_FLAX", "1") -os.environ.setdefault("TF_CPP_MIN_LOG_LEVEL", "3") -os.environ.setdefault("TOKENIZERS_PARALLELISM", "false") -os.environ.setdefault("HF_HUB_DISABLE_SYMLINKS_WARNING", "1") -os.environ.setdefault("TRANSFORMERS_VERBOSITY", "error") - -warnings.filterwarnings("ignore") - -def main(): - def log(msg: str): - print(msg, file=sys.stderr, flush=True) - - raw = sys.stdin.read() - try: - req = json.loads(raw) if raw.strip() else {} - except Exception: - sys.stdout.write(json.dumps({"ok": False, "errors": ["Invalid JSON on stdin"], "warnings": [], "notes": []})) - return - - code = req.get("code", "") - if not isinstance(code, str) or not code.strip(): - sys.stdout.write(json.dumps({"ok": False, "errors": ["Missing 'code'"], "warnings": [], "notes": []})) - return - - model_id = os.environ.get("PINE_REVIEWER_MODEL", "Qwen/Qwen2.5-Coder-0.5B-Instruct") - max_new = int(os.environ.get("PINE_REVIEWER_MAX_NEW_TOKENS", "256")) - # Default to a small non-zero temperature so the model doesn't immediately emit EOS on some prompts. - temperature = float(os.environ.get("PINE_REVIEWER_TEMPERATURE", "0.2")) - - errors = [] - warnings = [] - notes = [] - - if "undefined" in code: - warnings.append("Generated code contains the string 'undefined' (may indicate missing values).") - - try: - from transformers import AutoTokenizer, AutoModelForCausalLM - import torch - - log(f"[ai-review] Loading tokenizer: {model_id}") - tok = AutoTokenizer.from_pretrained(model_id) - - log(f"[ai-review] Loading model: {model_id} (first run may take a while)") - model = AutoModelForCausalLM.from_pretrained( - model_id, - device_map="auto" if os.environ.get("PINE_REVIEWER_USE_CUDA", "0") == "1" else None, - torch_dtype=torch.float16 if os.environ.get("PINE_REVIEWER_USE_CUDA", "0") == "1" else None, - ) - - device = "cpu" - if torch.cuda.is_available() and os.environ.get("PINE_REVIEWER_USE_CUDA", "0") == "1": - device = "cuda" - model.to(device) - - prompt_text = ( - "You are a strict code reviewer for JavaScript generated from PineScript. " - "Give concise bullets. Prefer phrasing like 'possible issue' or 'check that...'. " - "Do not invent identifiers that are not in the code.\n\n" - "Return 3 sections:\n" - "- Possible runtime issues\n" - "- Possible missing mappings/runtime builtins\n" - "- Suggestions\n\n" - "CODE START\n" - + code[:12000] - + "\nCODE END\n" - ) - - if hasattr(tok, "apply_chat_template"): - messages = [ - {"role": "system", "content": "You are a code reviewer."}, - {"role": "user", "content": prompt_text}, - ] - prompt = tok.apply_chat_template(messages, tokenize=False, add_generation_prompt=True) - else: - prompt = prompt_text + "\n\nAnswer:\n" - - inputs = tok(prompt, return_tensors="pt", truncation=True, max_length=2048) - if device != "cpu": - inputs = {k: v.to(device) for k, v in inputs.items()} - - log("[ai-review] Generating review...") - out = model.generate( - **inputs, - max_new_tokens=max_new, - min_new_tokens=int(os.environ.get("PINE_REVIEWER_MIN_NEW_TOKENS", "48")), - do_sample=True, - temperature=temperature, - top_p=0.9, - pad_token_id=tok.eos_token_id, - eos_token_id=tok.eos_token_id, - ) - - # Decode only the newly generated tokens (exclude the prompt tokens) - prompt_len = inputs["input_ids"].shape[1] - gen_tokens = out[0][prompt_len:] - text = tok.decode(gen_tokens, skip_special_tokens=True) - text = (text or "").strip() - if text.startswith(":"): - text = text[1:].lstrip() - - # If the code does not mention pinescript at all, drop any model lines that do. - if "pinescript" not in code.lower(): - text = "\n".join([ln for ln in text.splitlines() if "pinescript" not in ln.lower()]).strip() - - if not text: - text = "(model returned empty output)" - - if len(text) >= 120: - grams = set(text[i : i + 3] for i in range(0, min(len(text) - 2, 600))) - if len(grams) <= 8: - text = "(model output was highly repetitive/unusable; try a different PINE_REVIEWER_MODEL)" - - if len(text) > 800: - text = text[:800] + " ..." - - notes.append(f"Model({model_id}) review: {text if text else '(empty)'}") - log("[ai-review] Done") - - except Exception as e: - notes.append(f"Model review unavailable: {e}") - - sys.stdout.write(json.dumps({"ok": len(errors) == 0, "errors": errors, "warnings": warnings, "notes": notes})) - - -if __name__ == "__main__": - main() diff --git a/converted/3D_MACD_Bar_Plot_LuxAlgo.js b/converted/3D_MACD_Bar_Plot_LuxAlgo.js index 75f6720..b6e2606 100644 --- a/converted/3D_MACD_Bar_Plot_LuxAlgo.js +++ b/converted/3D_MACD_Bar_Plot_LuxAlgo.js @@ -42,6 +42,80 @@ const pinescript = { if (value != null && typeof value[Symbol.iterator] === 'function') return Array.from(value); return new Array(count || 0).fill(null); }, + __decArr: function(arr) { + if (!Array.isArray(arr) || arr.__pineDecorated) return arr; + const self = this; + const def = (name, fn) => Object.defineProperty(arr, name, { value: fn, writable: true, configurable: true }); + Object.defineProperty(arr, '__pineDecorated', { value: true, configurable: true }); + def('get', (i) => (arr[i] === undefined ? null : arr[i])); + def('set', (i, v) => { arr[i] = v; return v; }); + def('size', () => arr.length); + def('clear', () => { arr.length = 0; }); + def('insert', (i, v) => { arr.splice(i, 0, v); }); + def('remove', (i) => arr.splice(i, 1)[0]); + def('contains', (v) => arr.includes(v)); + def('indexof', (v) => arr.indexOf(v)); + def('lastindexof', (v) => arr.lastIndexOf(v)); + def('first', () => (arr.length ? arr[0] : null)); + def('last', () => (arr.length ? arr[arr.length - 1] : null)); + def('sum', () => arr.reduce((a, b) => a + b, 0)); + def('avg', () => (arr.length ? arr.reduce((a, b) => a + b, 0) / arr.length : 0)); + def('min', () => (arr.length ? Math.min(...arr) : null)); + def('max', () => (arr.length ? Math.max(...arr) : null)); + def('range', () => (arr.length ? Math.max(...arr) - Math.min(...arr) : null)); + // Statistical methods that route to the array.* built-ins (these names are not + // native to JS arrays, so attaching them here is safe from recursion). + def('median', () => self.arrayMedian(arr)); + def('mode', () => self.arrayMode(arr)); + def('stdev', () => self.arrayStdev(arr)); + def('variance', () => self.arrayVariance(arr)); + def('covariance', (other) => self.arrayCovariance(arr, other)); + def('percentile_linear_interpolation', (p) => self.arrayPercentileLinearInterpolation(arr, p)); + def('percentile_nearest_rank', (p) => self.arrayPercentileNearestRank(arr, p)); + def('abs', () => self.__decArr(self.arrayAbs(arr))); + def('binary_search', (v) => self.arrayBinarySearch(arr, v)); + // Pine's array.sort takes an order string, not a comparator. Use the native + // sort via .call so we don't recurse through this overridden method. + def('sort', (order) => { Array.prototype.sort.call(arr, (a, b) => (order === 'descending' ? b - a : a - b)); return arr; }); + def('sort_indices', (order) => self.__decArr(arr.map((_, i) => i).sort((a, b) => (order === 'descending' ? arr[b] - arr[a] : arr[a] - arr[b])))); + // join/slice/reverse/concat/includes/fill already exist natively on Array with + // compatible semantics, so we deliberately leave them to the native methods. + return arr; + }, + __decMap: function(m) { + if (!(m instanceof Map) || m.__pineDecorated) return m; + const def = (name, fn) => Object.defineProperty(m, name, { value: fn, writable: true, configurable: true }); + Object.defineProperty(m, '__pineDecorated', { value: true, configurable: true }); + def('put', (k, v) => { m.set(k, v); return v; }); + def('contains', (k) => m.has(k)); + def('remove', (k) => m.delete(k)); + def('keys', () => Array.from(Map.prototype.keys.call(m))); + def('values', () => Array.from(Map.prototype.values.call(m))); + def('size_', () => m.size); + return m; + }, + __decMatrix: function(m) { + if (!m || typeof m !== 'object' || m.__pineDecorated) return m; + const self = this; + const def = (name, fn) => Object.defineProperty(m, name, { value: fn, writable: true, configurable: true }); + Object.defineProperty(m, '__pineDecorated', { value: true, configurable: true }); + def('get', (r, c) => self.matrixGet(m, r, c)); + def('set', (r, c, v) => self.matrixSet(m, r, c, v)); + def('rows_', () => m.rows); + def('columns', () => m.cols); + def('fill', (v) => self.matrixFill(m, v)); + return m; + }, + __decDraw: function(obj) { + if (!obj || typeof obj !== 'object') return obj; + const p = new Proxy(obj, { + get(t, k) { + if (k in t || typeof k === 'symbol') return t[k]; + return function() { return p; }; + }, + }); + return p; + }, alertcondition: function(condition, ...rest) { if (globalThis.__pineRuntime) { globalThis.__pineRuntime.alerts.push({ condition, args: rest }); @@ -49,6 +123,32 @@ const pinescript = { return null; }, barcolor: function(color) { return null; }, + plotchar: function(series, ...rest) { + const rt = globalThis.__pineRuntime; + if (!rt) return series; + const bar = rt.__barIndex | 0; + const ord = (rt.__shapeIdx = (rt.__shapeIdx | 0) + 1) - 1; + let key = 'char_' + ord; + for (const r of rest) { + if (typeof r === 'string') { key = r; break; } + if (r && typeof r === 'object' && r.title) { key = String(r.title); break; } + } + let s = rt.plotshapes[key]; + if (!s) s = rt.plotshapes[key] = { title: key, data: [] }; + s.data[bar] = this.__scalar(series); + return series; + }, + plotarrow: function(series, ...rest) { + const rt = globalThis.__pineRuntime; + if (!rt) return series; + const bar = rt.__barIndex | 0; + const ord = (rt.__plotIdx = (rt.__plotIdx | 0) + 1) - 1; + const key = 'arrow_' + ord; + let p = rt.plots[key]; + if (!p) p = rt.plots[key] = { title: key, data: [] }; + p.data[bar] = this.__scalar(series); + return series; + }, bgcolor: function(color, title, editable, showLast) { return null; }, @@ -470,6 +570,12 @@ const pinescript = { atan: function(value) { return Math.atan(value); }, + todegrees: function(radians) { + return radians * (180 / Math.PI); + }, + toradians: function(degrees) { + return degrees * (Math.PI / 180); + }, floor: function(value) { return Math.floor(value); }, @@ -641,7 +747,7 @@ const pinescript = { return series; }, lineNew: function(x1, y1, x2, y2, opts = {}) { - return { x1, y1, x2, y2, opts, _type: 'line' }; + return this.__decDraw({ x1, y1, x2, y2, opts, _type: 'line' }); }, lineDelete: function(l) { return null; @@ -661,7 +767,7 @@ const pinescript = { return point === 0 || point === 'y1' ? line.y1 : line.y2; }, labelNew: function(x, y, text = '', opts = {}) { - return { x, y, text, opts, _type: 'label' }; + return this.__decDraw({ x, y, text, opts, _type: 'label' }); }, labelDelete: function(l) { return null; @@ -676,7 +782,7 @@ const pinescript = { return label.text || ''; }, boxNew: function(left, top, right, bottom, opts = {}) { - return { left, top, right, bottom, opts, _type: 'box' }; + return this.__decDraw({ left, top, right, bottom, opts, _type: 'box' }); }, boxDelete: function(box) { return null; @@ -694,7 +800,7 @@ const pinescript = { return box; }, polylineNew: function(points, opts = {}) { - return { points: points || [], opts, _type: 'polyline' }; + return this.__decDraw({ points: points || [], opts, _type: 'polyline' }); }, polylineDelete: function(poly) { return null; @@ -765,7 +871,7 @@ const pinescript = { return { time: _time ?? null, price: _price ?? null }; }, mapNew: function() { - return new Map(); + return this.__decMap(new Map()); }, mapSize: function(m) { return m instanceof Map ? m.size : 0; @@ -800,7 +906,7 @@ const pinescript = { const r = Math.max(0, rows ?? 0); const c = Math.max(0, cols ?? 0); const data = Array.from({ length: r }, () => Array.from({ length: c }, () => initialValue)); - return { rows: r, cols: c, data }; + return this.__decMatrix({ rows: r, cols: c, data }); }, matrixRows: function(m) { return m?.rows ?? 0; @@ -923,11 +1029,62 @@ const pinescript = { } return null; }, + __jacobiEigen: function(m) { + if (!m || !Array.isArray(m.data)) return null; + const n = m.rows ?? 0; + if (n === 0 || n !== (m.cols ?? 0)) return null; + // Work on a copy so the input matrix is untouched. + const a = m.data.map(row => row.slice()); + const v = Array.from({ length: n }, (_, i) => Array.from({ length: n }, (_, j) => (i === j ? 1 : 0))); + for (let sweep = 0; sweep < 100; sweep++) { + let off = 0; + for (let p = 0; p < n; p++) for (let q = p + 1; q < n; q++) off += a[p][q] * a[p][q]; + if (off < 1e-20) break; + for (let p = 0; p < n; p++) { + for (let q = p + 1; q < n; q++) { + if (Math.abs(a[p][q]) < 1e-18) continue; + const theta = (a[q][q] - a[p][p]) / (2 * a[p][q]); + const t = Math.sign(theta || 1) / (Math.abs(theta) + Math.sqrt(theta * theta + 1)); + const cos = 1 / Math.sqrt(t * t + 1); + const sin = t * cos; + for (let k = 0; k < n; k++) { + const akp = a[k][p], akq = a[k][q]; + a[k][p] = cos * akp - sin * akq; + a[k][q] = sin * akp + cos * akq; + } + for (let k = 0; k < n; k++) { + const apk = a[p][k], aqk = a[q][k]; + a[p][k] = cos * apk - sin * aqk; + a[q][k] = sin * apk + cos * aqk; + } + for (let k = 0; k < n; k++) { + const vkp = v[k][p], vkq = v[k][q]; + v[k][p] = cos * vkp - sin * vkq; + v[k][q] = sin * vkp + cos * vkq; + } + } + } + } + // Sort eigenpairs by eigenvalue, descending. + const order = Array.from({ length: n }, (_, i) => i).sort((i, j) => a[j][j] - a[i][i]); + const values = order.map(i => a[i][i]); + const vectors = Array.from({ length: n }, (_, r) => order.map(c => v[r][c])); + return { values, vectors }; + }, + matrixEigenvalues: function(m) { + const e = this.__jacobiEigen(m); + return this.__decArr(e ? e.values : []); + }, + matrixEigenvectors: function(m) { + const e = this.__jacobiEigen(m); + if (!e) return this.__decMatrix({ rows: 0, cols: 0, data: [] }); + return this.__decMatrix({ rows: e.vectors.length, cols: e.vectors.length, data: e.vectors }); + }, requestSecurity: function(symbol, timeframe, expression) { return expression; }, arrayNew: function(initialSize = 0, initialValue = 0) { - return Array(initialSize).fill(initialValue); + return this.__decArr(Array(initialSize).fill(initialValue)); }, arraySize: function(arr) { return arr ? arr.length : 0; @@ -978,10 +1135,10 @@ const pinescript = { return arr; }, arraySlice: function(arr, startIndex = 0, endIndex = null) { - if (!arr) return []; + if (!arr) return this.__decArr([]); const start = Number(startIndex) || 0; const end = endIndex === null || endIndex === undefined ? arr.length : Number(endIndex) || 0; - return arr.slice(start, end); + return this.__decArr(arr.slice(start, end)); }, arraySort: function(arr, order = 'ascending') { if (arr) arr.sort((a, b) => order === 'ascending' ? a - b : b - a); @@ -1191,7 +1348,7 @@ const pinescript = { const info = { ticker: 'AAPL', tickerid: 'NASDAQ:AAPL', prefix: 'NASDAQ', root: 'AAPL', suffix: '' }; return info[type] || ''; }, - timenow: 1781574526356, + timenow: 1782783575681, barstate: "LAST", dividends: {}, splits: {}, @@ -1245,11 +1402,11 @@ const pinescript = { arrayConcat: function(arr1, arr2) { if (!arr1) return arr2 || []; if (!arr2) return arr1; - return arr1.concat(arr2); + return this.__decArr(arr1.concat(arr2)); }, arrayCopy: function(arr) { - if (!arr) return []; - return [...arr]; + if (!arr) return this.__decArr([]); + return this.__decArr([...arr]); }, arrayBinarySearch: function(arr, value) { if (!arr || arr.length === 0) return -1; @@ -1339,7 +1496,7 @@ globalThis.input.timeframe = globalThis.input.timeframe || ((defval) => defval); globalThis.array = globalThis.array || { - from: (...items) => items, + from: (...items) => pinescript.__decArr(items), size: (arr) => pinescript.arraySize(arr), @@ -1508,6 +1665,8 @@ globalThis.line = globalThis.line || __pineNS({ style_solid: "solid", style_dash globalThis.box = globalThis.box || __pineNS({}); +globalThis.color = globalThis.color || __pineNS(Object.assign(function(c) { return c; }, { new: function(c, t) { return c; }, rgb: function(r, g, b, t) { return "#rgb(" + [r, g, b].join(",") + ")"; }, from_gradient: function(v, lo, hi, c1, c2) { return c1; }, r: function() { return 0; }, g: function() { return 0; }, b: function() { return 0; }, t: function() { return 0; }, aqua: "#00BCD4", black: "#363A45", blue: "#2962FF", fuchsia: "#E040FB", gray: "#787B86", green: "#4CAF50", lime: "#00E676", maroon: "#880E4F", navy: "#311B92", olive: "#808000", orange: "#FF9800", purple: "#9C27B0", red: "#FF5252", silver: "#B2B5BE", teal: "#00897B", white: "#FFFFFF", yellow: "#FFEB3B" })); + globalThis.label = globalThis.label || __pineNS({ style_label_down: "label_down", style_label_up: "label_up", style_none: "none" }); globalThis.polyline = globalThis.polyline || __pineNS({}); @@ -1520,7 +1679,9 @@ globalThis.map = globalThis.map || __pineNS({}); globalThis.session = globalThis.session || __pineNS({ regular: "regular", extended: "extended" }); -globalThis.dayofweek = globalThis.dayofweek || __pineNS({ sunday: 1, monday: 2, tuesday: 3, wednesday: 4, thursday: 5, friday: 6, saturday: 7 }); +globalThis.ticker = globalThis.ticker || __pineNS({}); + +globalThis.dayofweek = globalThis.dayofweek || __pineNS(Object.assign(function(t) { return new Date(t != null ? t : (globalThis.time || 0)).getUTCDay() + 1; }, { sunday: 1, monday: 2, tuesday: 3, wednesday: 4, thursday: 5, friday: 6, saturday: 7 })); globalThis.timeframe = __pineNS(Object.assign(globalThis.timeframe || {}, { period: (globalThis.timeframe && globalThis.timeframe.period) || "D", isintraday: false, isdaily: true, multiplier: 1 })); @@ -1568,8 +1729,6 @@ globalThis.bool = globalThis.bool || function(x) { return Boolean(x); }; globalThis.string = globalThis.string || function(x) { return x == null ? null : String(x); }; -globalThis.dayofweek = globalThis.dayofweek || __pineNS({ sunday: 1, monday: 2, tuesday: 3, wednesday: 4, thursday: 5, friday: 6, saturday: 7 }); - globalThis.str = globalThis.str || __pineNS({}); pinescript.math = globalThis.math; @@ -1655,7 +1814,7 @@ pinescript.text = { align_center: "center" }; pinescript.table = { - new: function(position, columns, rows, opts) { return { position, columns, rows, opts: opts || {}, cells: [] }; }, + new: function(position, columns, rows, opts) { return pinescript.__decDraw({ position, columns, rows, opts: opts || {}, cells: [] }); }, cell: function(table, column, row, text, opts) { @@ -1688,46 +1847,46 @@ function main() { null; // Study: 3D MACD Bar Plot [LuxAlgo] // Options: {"overlay":false,"max_lines_count":500,"max_labels_count":500,"max_polylines_count":100} - let GRID_GRP = "Grid Configuration"; - let fastMinInput = pinescript.inputInt(5, "Fast Min Length", ({ minval: 2, group: GRID_GRP })); - let fastMaxInput = pinescript.inputInt(25, "Fast Max Length", ({ minval: 2, group: GRID_GRP })); - let fastStepInput = pinescript.inputInt(5, "Fast Step", ({ minval: 1, group: GRID_GRP })); - let slowMinInput = pinescript.inputInt(10, "Slow Min Length", ({ minval: 2, group: GRID_GRP })); - let slowMaxInput = pinescript.inputInt(50, "Slow Max Length", ({ minval: 2, group: GRID_GRP })); - let slowStepInput = pinescript.inputInt(10, "Slow Step", ({ minval: 1, group: GRID_GRP })); - let PROJ_GRP = "3D Projection Settings"; - let yOffsetInput = pinescript.inputFloat(0, "Y Baseline Price", ({ group: PROJ_GRP })); - let scaleXInput = pinescript.inputFloat(3, "X Spacing (bars)", ({ group: PROJ_GRP })); - let scaleZInput = pinescript.inputFloat(5, "Z Height (Relative to Y Spacing)", ({ group: PROJ_GRP })); - let STYLE_GRP = "Style"; - let bullColorInput = pinescript.inputColor(pinescript.color.hex("#089981"), "Bullish Color", ({ group: STYLE_GRP })); - let bearColorInput = pinescript.inputColor(pinescript.color.hex("#f23645"), "Bearish Color", ({ group: STYLE_GRP })); - let gridColorInput = pinescript.inputColor(pinescript.color.hex("#808080"), "Grid Color", ({ group: STYLE_GRP })); + var GRID_GRP = "Grid Configuration"; + var fastMinInput = pinescript.inputInt(5, "Fast Min Length", ({ minval: 2, group: GRID_GRP })); + var fastMaxInput = pinescript.inputInt(25, "Fast Max Length", ({ minval: 2, group: GRID_GRP })); + var fastStepInput = pinescript.inputInt(5, "Fast Step", ({ minval: 1, group: GRID_GRP })); + var slowMinInput = pinescript.inputInt(10, "Slow Min Length", ({ minval: 2, group: GRID_GRP })); + var slowMaxInput = pinescript.inputInt(50, "Slow Max Length", ({ minval: 2, group: GRID_GRP })); + var slowStepInput = pinescript.inputInt(10, "Slow Step", ({ minval: 1, group: GRID_GRP })); + var PROJ_GRP = "3D Projection Settings"; + var yOffsetInput = pinescript.inputFloat(0, "Y Baseline Price", ({ group: PROJ_GRP })); + var scaleXInput = pinescript.inputFloat(3, "X Spacing (bars)", ({ group: PROJ_GRP })); + var scaleZInput = pinescript.inputFloat(5, "Z Height (Relative to Y Spacing)", ({ group: PROJ_GRP })); + var STYLE_GRP = "Style"; + var bullColorInput = pinescript.inputColor(pinescript.color.hex("#089981"), "Bullish Color", ({ group: STYLE_GRP })); + var bearColorInput = pinescript.inputColor(pinescript.color.hex("#f23645"), "Bearish Color", ({ group: STYLE_GRP })); + var gridColorInput = pinescript.inputColor(pinescript.color.hex("#808080"), "Grid Color", ({ group: STYLE_GRP })); if (state.emaStates === undefined) state.emaStates = pinescript.mapNew(); if (state.uniqueLengths === undefined) state.uniqueLengths = pinescript.arrayNew(); if ((bar_index === 0)) { for (let f = fastMinInput; f <= fastMaxInput; f++) { - if (!uniqueLengths.includes(f)) { - uniqueLengths.push(f); + if (!state.uniqueLengths.includes(f)) { + state.uniqueLengths.push(f); } } for (let s = slowMinInput; s <= slowMaxInput; s++) { - if (!uniqueLengths.includes(s)) { - uniqueLengths.push(s); + if (!state.uniqueLengths.includes(s)) { + state.uniqueLengths.push(s); } } } - for (const length of state.uniqueLengths) { - let alpha = (2 / (length + 1)); - let prevEma = emaStates.get(length); + for (const length of (state.uniqueLengths ?? [])) { + var alpha = (2 / (length + 1)); + var prevEma = state.emaStates.get(length); if (pinescript.na(prevEma)) { - emaStates.put(length, close); + state.emaStates.put(length, close); } else { - emaStates.put(length, ((close * alpha) + (prevEma * (1 - alpha)))); + state.emaStates.put(length, ((close * alpha) + (prevEma * (1 - alpha)))); } } function generateGridPath(pts, rows, cols) { - let path = pinescript.arrayNew(); + var path = pinescript.arrayNew(); for (let r = 0; r <= (rows - 1); r++) { if (((r % 2) === 0)) { for (let c = 0; c <= (cols - 1); c++) { @@ -1739,10 +1898,10 @@ function main() { } } } - let currC = ((((rows - 1) % 2) === 0) ? (cols - 1) : 0); + var currC = ((((rows - 1) % 2) === 0) ? (cols - 1) : 0); if ((currC === (cols - 1))) { for (let c = (cols - 1); c <= 0; c++) { - let colIdx = ((cols - 1) - c); + var colIdx = ((cols - 1) - c); if (((colIdx % 2) === 0)) { for (let r = (rows - 1); r <= 0; r++) { path.push(pts.get(r, c)); @@ -1781,68 +1940,68 @@ function main() { while ((pinescript.arraySize(state.polylinesArray) > 0)) { pinescript.polylineDelete(pinescript.arrayPop(state.polylinesArray)); } - let basePrice = yOffsetInput; - let baseBar = bar_index; - let yStep = 1; - let macdMax = 0; + var basePrice = yOffsetInput; + var baseBar = bar_index; + var yStep = 1; + var macdMax = 0; for (let f = fastMinInput; f <= fastMaxInput; f++) { for (let s = slowMinInput; s <= slowMaxInput; s++) { - let val = pinescript.abs((emaStates.get(f) - emaStates.get(s))); + var val = pinescript.abs((state.emaStates.get(f) - state.emaStates.get(s))); if ((val > macdMax)) { macdMax = val; } } } - let zMultiplier = ((macdMax !== 0) ? ((scaleZInput * yStep) / macdMax) : 1); - let fCount = 0; + var zMultiplier = ((macdMax !== 0) ? ((scaleZInput * yStep) / macdMax) : 1); + var fCount = 0; for (let f = fastMinInput; f <= fastMaxInput; f++) { fCount += 1; } - let sCount = 0; + var sCount = 0; for (let s = slowMinInput; s <= slowMaxInput; s++) { sCount += 1; } - let maxDepth = pinescript.max(1, ((fCount - 1) + (sCount - 1))); - let topPoints = pinescript.matrixNew(fCount, sCount); - let basePoints = pinescript.matrixNew(fCount, sCount); - let i = 0; + var maxDepth = pinescript.max(1, ((fCount - 1) + (sCount - 1))); + var topPoints = pinescript.matrixNew(fCount, sCount); + var basePoints = pinescript.matrixNew(fCount, sCount); + var i = 0; for (let f = fastMinInput; f <= fastMaxInput; f++) { - let j = 0; + var j = 0; for (let s = slowMinInput; s <= slowMaxInput; s++) { - let fEma = emaStates.get(f); - let sEma = emaStates.get(s); - let macd = (fEma - sEma); - let depthFactor = ((i + j) / maxDepth); - let barTransp = (10 + (depthFactor * 70)); - let barWidth = int((6 - (5 * depthFactor))); - let screenX = (baseBar + int(((i - j) * scaleXInput))); - let screenYB = (basePrice + ((i + j) * yStep)); - let screenYT = (screenYB + (macd * zMultiplier)); + var fEma = state.emaStates.get(f); + var sEma = state.emaStates.get(s); + var macd = (fEma - sEma); + var depthFactor = ((i + j) / maxDepth); + var barTransp = (10 + (depthFactor * 70)); + var barWidth = int((6 - (5 * depthFactor))); + var screenX = (baseBar + int(((i - j) * scaleXInput))); + var screenYB = (basePrice + ((i + j) * yStep)); + var screenYT = (screenYB + (macd * zMultiplier)); chart.point; - let pBase = pinescript.chartPointFromIndex(screenX, screenYB); + var pBase = pinescript.chartPointFromIndex(screenX, screenYB); chart.point; - let pTop = pinescript.chartPointFromIndex(screenX, screenYT); + var pTop = pinescript.chartPointFromIndex(screenX, screenYT); topPoints.set(i, j, pTop); basePoints.set(i, j, pBase); - let barCol = ((macd >= 0) ? bullColorInput : bearColorInput); - linesArray.push(pinescript.lineNew(pBase, pTop, ({ color: pinescript.color.new(barCol, barTransp), width: barWidth }))); + var barCol = ((macd >= 0) ? bullColorInput : bearColorInput); + state.linesArray.push(pinescript.lineNew(pBase, pTop, ({ color: pinescript.color.new(barCol, barTransp), width: barWidth }))); if (((i === 0) && (j === 0))) { - labelsArray.push(pinescript.labelNew(pBase, (((("Origin (F" + pinescript.strToString(f)) + ", S") + pinescript.strToString(s)) + ")"), ({ style: label.style_label_up, textcolor: chart.fg_color, color: pinescript.color.hex("#00000000"), size: pinescript.size.small }))); + state.labelsArray.push(pinescript.labelNew(pBase, (((("Origin (F" + pinescript.strToString(f)) + ", S") + pinescript.strToString(s)) + ")"), ({ style: label.style_label_up, textcolor: chart.fg_color, color: pinescript.color.hex("#00000000"), size: pinescript.size.small }))); } if (((j === 0) && (i === (fCount - 1)))) { - labelsArray.push(pinescript.labelNew(pBase, (("Fast Axis (F" + pinescript.strToString(f)) + ")"), ({ style: label.style_label_left, textcolor: chart.fg_color, color: pinescript.color.hex("#00000000"), size: pinescript.size.small }))); + state.labelsArray.push(pinescript.labelNew(pBase, (("Fast Axis (F" + pinescript.strToString(f)) + ")"), ({ style: label.style_label_left, textcolor: chart.fg_color, color: pinescript.color.hex("#00000000"), size: pinescript.size.small }))); } if (((i === 0) && (j === (sCount - 1)))) { - labelsArray.push(pinescript.labelNew(pBase, (("Slow Axis (S" + pinescript.strToString(s)) + ")"), ({ style: label.style_label_right, textcolor: chart.fg_color, color: pinescript.color.hex("#00000000"), size: pinescript.size.small }))); + state.labelsArray.push(pinescript.labelNew(pBase, (("Slow Axis (S" + pinescript.strToString(s)) + ")"), ({ style: label.style_label_right, textcolor: chart.fg_color, color: pinescript.color.hex("#00000000"), size: pinescript.size.small }))); } j += 1; } i += 1; } - let surfacePath = generateGridPath(topPoints, fCount, sCount); - let gridPath = generateGridPath(basePoints, fCount, sCount); - polylinesArray.push(pinescript.polylineNew(surfacePath, ({ line_color: pinescript.color.new(gridColorInput, 40) }))); - polylinesArray.push(pinescript.polylineNew(gridPath, ({ line_color: pinescript.color.new(gridColorInput, 70) }))); + var surfacePath = generateGridPath(topPoints, fCount, sCount); + var gridPath = generateGridPath(basePoints, fCount, sCount); + state.polylinesArray.push(pinescript.polylineNew(surfacePath, ({ line_color: pinescript.color.new(gridColorInput, 40) }))); + state.polylinesArray.push(pinescript.polylineNew(gridPath, ({ line_color: pinescript.color.new(gridColorInput, 70) }))); } } @@ -1888,6 +2047,10 @@ function run(data, options = {}) { rt.__shapeIdx = 0; globalThis.bar_index = i; globalThis.last_bar_index = n - 1; + globalThis.time_tradingday = inTime[i]; + globalThis.time_close = inTime[i]; + globalThis.last_bar_time = inTime[n - 1]; + globalThis.timenow = inTime[n - 1]; globalThis.barstate = { isfirst: i === 0, islast: i === n - 1, isrealtime: false, ishistory: true, isconfirmed: true, isnew: true, islastconfirmedhistory: i === n - 1, diff --git a/converted/Arbitrage_Matrix_LuxAlgo.js b/converted/Arbitrage_Matrix_LuxAlgo.js index 9049458..57363af 100644 --- a/converted/Arbitrage_Matrix_LuxAlgo.js +++ b/converted/Arbitrage_Matrix_LuxAlgo.js @@ -42,6 +42,80 @@ const pinescript = { if (value != null && typeof value[Symbol.iterator] === 'function') return Array.from(value); return new Array(count || 0).fill(null); }, + __decArr: function(arr) { + if (!Array.isArray(arr) || arr.__pineDecorated) return arr; + const self = this; + const def = (name, fn) => Object.defineProperty(arr, name, { value: fn, writable: true, configurable: true }); + Object.defineProperty(arr, '__pineDecorated', { value: true, configurable: true }); + def('get', (i) => (arr[i] === undefined ? null : arr[i])); + def('set', (i, v) => { arr[i] = v; return v; }); + def('size', () => arr.length); + def('clear', () => { arr.length = 0; }); + def('insert', (i, v) => { arr.splice(i, 0, v); }); + def('remove', (i) => arr.splice(i, 1)[0]); + def('contains', (v) => arr.includes(v)); + def('indexof', (v) => arr.indexOf(v)); + def('lastindexof', (v) => arr.lastIndexOf(v)); + def('first', () => (arr.length ? arr[0] : null)); + def('last', () => (arr.length ? arr[arr.length - 1] : null)); + def('sum', () => arr.reduce((a, b) => a + b, 0)); + def('avg', () => (arr.length ? arr.reduce((a, b) => a + b, 0) / arr.length : 0)); + def('min', () => (arr.length ? Math.min(...arr) : null)); + def('max', () => (arr.length ? Math.max(...arr) : null)); + def('range', () => (arr.length ? Math.max(...arr) - Math.min(...arr) : null)); + // Statistical methods that route to the array.* built-ins (these names are not + // native to JS arrays, so attaching them here is safe from recursion). + def('median', () => self.arrayMedian(arr)); + def('mode', () => self.arrayMode(arr)); + def('stdev', () => self.arrayStdev(arr)); + def('variance', () => self.arrayVariance(arr)); + def('covariance', (other) => self.arrayCovariance(arr, other)); + def('percentile_linear_interpolation', (p) => self.arrayPercentileLinearInterpolation(arr, p)); + def('percentile_nearest_rank', (p) => self.arrayPercentileNearestRank(arr, p)); + def('abs', () => self.__decArr(self.arrayAbs(arr))); + def('binary_search', (v) => self.arrayBinarySearch(arr, v)); + // Pine's array.sort takes an order string, not a comparator. Use the native + // sort via .call so we don't recurse through this overridden method. + def('sort', (order) => { Array.prototype.sort.call(arr, (a, b) => (order === 'descending' ? b - a : a - b)); return arr; }); + def('sort_indices', (order) => self.__decArr(arr.map((_, i) => i).sort((a, b) => (order === 'descending' ? arr[b] - arr[a] : arr[a] - arr[b])))); + // join/slice/reverse/concat/includes/fill already exist natively on Array with + // compatible semantics, so we deliberately leave them to the native methods. + return arr; + }, + __decMap: function(m) { + if (!(m instanceof Map) || m.__pineDecorated) return m; + const def = (name, fn) => Object.defineProperty(m, name, { value: fn, writable: true, configurable: true }); + Object.defineProperty(m, '__pineDecorated', { value: true, configurable: true }); + def('put', (k, v) => { m.set(k, v); return v; }); + def('contains', (k) => m.has(k)); + def('remove', (k) => m.delete(k)); + def('keys', () => Array.from(Map.prototype.keys.call(m))); + def('values', () => Array.from(Map.prototype.values.call(m))); + def('size_', () => m.size); + return m; + }, + __decMatrix: function(m) { + if (!m || typeof m !== 'object' || m.__pineDecorated) return m; + const self = this; + const def = (name, fn) => Object.defineProperty(m, name, { value: fn, writable: true, configurable: true }); + Object.defineProperty(m, '__pineDecorated', { value: true, configurable: true }); + def('get', (r, c) => self.matrixGet(m, r, c)); + def('set', (r, c, v) => self.matrixSet(m, r, c, v)); + def('rows_', () => m.rows); + def('columns', () => m.cols); + def('fill', (v) => self.matrixFill(m, v)); + return m; + }, + __decDraw: function(obj) { + if (!obj || typeof obj !== 'object') return obj; + const p = new Proxy(obj, { + get(t, k) { + if (k in t || typeof k === 'symbol') return t[k]; + return function() { return p; }; + }, + }); + return p; + }, alertcondition: function(condition, ...rest) { if (globalThis.__pineRuntime) { globalThis.__pineRuntime.alerts.push({ condition, args: rest }); @@ -49,6 +123,32 @@ const pinescript = { return null; }, barcolor: function(color) { return null; }, + plotchar: function(series, ...rest) { + const rt = globalThis.__pineRuntime; + if (!rt) return series; + const bar = rt.__barIndex | 0; + const ord = (rt.__shapeIdx = (rt.__shapeIdx | 0) + 1) - 1; + let key = 'char_' + ord; + for (const r of rest) { + if (typeof r === 'string') { key = r; break; } + if (r && typeof r === 'object' && r.title) { key = String(r.title); break; } + } + let s = rt.plotshapes[key]; + if (!s) s = rt.plotshapes[key] = { title: key, data: [] }; + s.data[bar] = this.__scalar(series); + return series; + }, + plotarrow: function(series, ...rest) { + const rt = globalThis.__pineRuntime; + if (!rt) return series; + const bar = rt.__barIndex | 0; + const ord = (rt.__plotIdx = (rt.__plotIdx | 0) + 1) - 1; + const key = 'arrow_' + ord; + let p = rt.plots[key]; + if (!p) p = rt.plots[key] = { title: key, data: [] }; + p.data[bar] = this.__scalar(series); + return series; + }, bgcolor: function(color, title, editable, showLast) { return null; }, @@ -470,6 +570,12 @@ const pinescript = { atan: function(value) { return Math.atan(value); }, + todegrees: function(radians) { + return radians * (180 / Math.PI); + }, + toradians: function(degrees) { + return degrees * (Math.PI / 180); + }, floor: function(value) { return Math.floor(value); }, @@ -641,7 +747,7 @@ const pinescript = { return series; }, lineNew: function(x1, y1, x2, y2, opts = {}) { - return { x1, y1, x2, y2, opts, _type: 'line' }; + return this.__decDraw({ x1, y1, x2, y2, opts, _type: 'line' }); }, lineDelete: function(l) { return null; @@ -661,7 +767,7 @@ const pinescript = { return point === 0 || point === 'y1' ? line.y1 : line.y2; }, labelNew: function(x, y, text = '', opts = {}) { - return { x, y, text, opts, _type: 'label' }; + return this.__decDraw({ x, y, text, opts, _type: 'label' }); }, labelDelete: function(l) { return null; @@ -676,7 +782,7 @@ const pinescript = { return label.text || ''; }, boxNew: function(left, top, right, bottom, opts = {}) { - return { left, top, right, bottom, opts, _type: 'box' }; + return this.__decDraw({ left, top, right, bottom, opts, _type: 'box' }); }, boxDelete: function(box) { return null; @@ -694,7 +800,7 @@ const pinescript = { return box; }, polylineNew: function(points, opts = {}) { - return { points: points || [], opts, _type: 'polyline' }; + return this.__decDraw({ points: points || [], opts, _type: 'polyline' }); }, polylineDelete: function(poly) { return null; @@ -765,7 +871,7 @@ const pinescript = { return { time: _time ?? null, price: _price ?? null }; }, mapNew: function() { - return new Map(); + return this.__decMap(new Map()); }, mapSize: function(m) { return m instanceof Map ? m.size : 0; @@ -800,7 +906,7 @@ const pinescript = { const r = Math.max(0, rows ?? 0); const c = Math.max(0, cols ?? 0); const data = Array.from({ length: r }, () => Array.from({ length: c }, () => initialValue)); - return { rows: r, cols: c, data }; + return this.__decMatrix({ rows: r, cols: c, data }); }, matrixRows: function(m) { return m?.rows ?? 0; @@ -923,11 +1029,62 @@ const pinescript = { } return null; }, + __jacobiEigen: function(m) { + if (!m || !Array.isArray(m.data)) return null; + const n = m.rows ?? 0; + if (n === 0 || n !== (m.cols ?? 0)) return null; + // Work on a copy so the input matrix is untouched. + const a = m.data.map(row => row.slice()); + const v = Array.from({ length: n }, (_, i) => Array.from({ length: n }, (_, j) => (i === j ? 1 : 0))); + for (let sweep = 0; sweep < 100; sweep++) { + let off = 0; + for (let p = 0; p < n; p++) for (let q = p + 1; q < n; q++) off += a[p][q] * a[p][q]; + if (off < 1e-20) break; + for (let p = 0; p < n; p++) { + for (let q = p + 1; q < n; q++) { + if (Math.abs(a[p][q]) < 1e-18) continue; + const theta = (a[q][q] - a[p][p]) / (2 * a[p][q]); + const t = Math.sign(theta || 1) / (Math.abs(theta) + Math.sqrt(theta * theta + 1)); + const cos = 1 / Math.sqrt(t * t + 1); + const sin = t * cos; + for (let k = 0; k < n; k++) { + const akp = a[k][p], akq = a[k][q]; + a[k][p] = cos * akp - sin * akq; + a[k][q] = sin * akp + cos * akq; + } + for (let k = 0; k < n; k++) { + const apk = a[p][k], aqk = a[q][k]; + a[p][k] = cos * apk - sin * aqk; + a[q][k] = sin * apk + cos * aqk; + } + for (let k = 0; k < n; k++) { + const vkp = v[k][p], vkq = v[k][q]; + v[k][p] = cos * vkp - sin * vkq; + v[k][q] = sin * vkp + cos * vkq; + } + } + } + } + // Sort eigenpairs by eigenvalue, descending. + const order = Array.from({ length: n }, (_, i) => i).sort((i, j) => a[j][j] - a[i][i]); + const values = order.map(i => a[i][i]); + const vectors = Array.from({ length: n }, (_, r) => order.map(c => v[r][c])); + return { values, vectors }; + }, + matrixEigenvalues: function(m) { + const e = this.__jacobiEigen(m); + return this.__decArr(e ? e.values : []); + }, + matrixEigenvectors: function(m) { + const e = this.__jacobiEigen(m); + if (!e) return this.__decMatrix({ rows: 0, cols: 0, data: [] }); + return this.__decMatrix({ rows: e.vectors.length, cols: e.vectors.length, data: e.vectors }); + }, requestSecurity: function(symbol, timeframe, expression) { return expression; }, arrayNew: function(initialSize = 0, initialValue = 0) { - return Array(initialSize).fill(initialValue); + return this.__decArr(Array(initialSize).fill(initialValue)); }, arraySize: function(arr) { return arr ? arr.length : 0; @@ -978,10 +1135,10 @@ const pinescript = { return arr; }, arraySlice: function(arr, startIndex = 0, endIndex = null) { - if (!arr) return []; + if (!arr) return this.__decArr([]); const start = Number(startIndex) || 0; const end = endIndex === null || endIndex === undefined ? arr.length : Number(endIndex) || 0; - return arr.slice(start, end); + return this.__decArr(arr.slice(start, end)); }, arraySort: function(arr, order = 'ascending') { if (arr) arr.sort((a, b) => order === 'ascending' ? a - b : b - a); @@ -1191,7 +1348,7 @@ const pinescript = { const info = { ticker: 'AAPL', tickerid: 'NASDAQ:AAPL', prefix: 'NASDAQ', root: 'AAPL', suffix: '' }; return info[type] || ''; }, - timenow: 1781574526454, + timenow: 1782783575749, barstate: "LAST", dividends: {}, splits: {}, @@ -1245,11 +1402,11 @@ const pinescript = { arrayConcat: function(arr1, arr2) { if (!arr1) return arr2 || []; if (!arr2) return arr1; - return arr1.concat(arr2); + return this.__decArr(arr1.concat(arr2)); }, arrayCopy: function(arr) { - if (!arr) return []; - return [...arr]; + if (!arr) return this.__decArr([]); + return this.__decArr([...arr]); }, arrayBinarySearch: function(arr, value) { if (!arr || arr.length === 0) return -1; @@ -1339,7 +1496,7 @@ globalThis.input.timeframe = globalThis.input.timeframe || ((defval) => defval); globalThis.array = globalThis.array || { - from: (...items) => items, + from: (...items) => pinescript.__decArr(items), size: (arr) => pinescript.arraySize(arr), @@ -1508,6 +1665,8 @@ globalThis.line = globalThis.line || __pineNS({ style_solid: "solid", style_dash globalThis.box = globalThis.box || __pineNS({}); +globalThis.color = globalThis.color || __pineNS(Object.assign(function(c) { return c; }, { new: function(c, t) { return c; }, rgb: function(r, g, b, t) { return "#rgb(" + [r, g, b].join(",") + ")"; }, from_gradient: function(v, lo, hi, c1, c2) { return c1; }, r: function() { return 0; }, g: function() { return 0; }, b: function() { return 0; }, t: function() { return 0; }, aqua: "#00BCD4", black: "#363A45", blue: "#2962FF", fuchsia: "#E040FB", gray: "#787B86", green: "#4CAF50", lime: "#00E676", maroon: "#880E4F", navy: "#311B92", olive: "#808000", orange: "#FF9800", purple: "#9C27B0", red: "#FF5252", silver: "#B2B5BE", teal: "#00897B", white: "#FFFFFF", yellow: "#FFEB3B" })); + globalThis.label = globalThis.label || __pineNS({ style_label_down: "label_down", style_label_up: "label_up", style_none: "none" }); globalThis.polyline = globalThis.polyline || __pineNS({}); @@ -1520,7 +1679,9 @@ globalThis.map = globalThis.map || __pineNS({}); globalThis.session = globalThis.session || __pineNS({ regular: "regular", extended: "extended" }); -globalThis.dayofweek = globalThis.dayofweek || __pineNS({ sunday: 1, monday: 2, tuesday: 3, wednesday: 4, thursday: 5, friday: 6, saturday: 7 }); +globalThis.ticker = globalThis.ticker || __pineNS({}); + +globalThis.dayofweek = globalThis.dayofweek || __pineNS(Object.assign(function(t) { return new Date(t != null ? t : (globalThis.time || 0)).getUTCDay() + 1; }, { sunday: 1, monday: 2, tuesday: 3, wednesday: 4, thursday: 5, friday: 6, saturday: 7 })); globalThis.timeframe = __pineNS(Object.assign(globalThis.timeframe || {}, { period: (globalThis.timeframe && globalThis.timeframe.period) || "D", isintraday: false, isdaily: true, multiplier: 1 })); @@ -1568,8 +1729,6 @@ globalThis.bool = globalThis.bool || function(x) { return Boolean(x); }; globalThis.string = globalThis.string || function(x) { return x == null ? null : String(x); }; -globalThis.dayofweek = globalThis.dayofweek || __pineNS({ sunday: 1, monday: 2, tuesday: 3, wednesday: 4, thursday: 5, friday: 6, saturday: 7 }); - globalThis.str = globalThis.str || __pineNS({}); pinescript.math = globalThis.math; @@ -1655,7 +1814,7 @@ pinescript.text = { align_center: "center" }; pinescript.table = { - new: function(position, columns, rows, opts) { return { position, columns, rows, opts: opts || {}, cells: [] }; }, + new: function(position, columns, rows, opts) { return pinescript.__decDraw({ position, columns, rows, opts: opts || {}, cells: [] }); }, cell: function(table, column, row, text, opts) { @@ -1690,188 +1849,188 @@ function main() { null; // Study: Arbitrage Matrix [LuxAlgo] // Options: {"overlay":true} - let RED = pinescript.color.hex("#F23645"); - let GREEN = pinescript.color.hex("#089981"); - let AUTO = "Auto"; - let CRYPTO_EXCHANGES = "Crypto Exchanges"; - let FX_BROKERS = "Forex Brokers"; - let TOP_RIGHT = "Top Right"; - let BOTTOM_RIGHT = "Bottom Right"; - let BOTTOM_LEFT = "Bottom Left"; - let TINY = "Tiny"; - let SMALL = "Small"; - let NORMAL = "Normal"; - let LARGE = "Large"; - let HUGE = "Huge"; - let PRICE_LAST = "Last Price"; - let VOLUME_LAST = "Last Volume"; - let PRICE_AVG = "Avg Price"; - let VOLUME_AVG = "Avg Volume"; - let DASHBOARD_GROUP = "Dashboard"; - let STYLE_GROUP = "Style"; - let EXCHANGES_GROUP = "Crypto Exchanges"; - let BROKERS_GROUP = "Forex Brokers"; - let sourceTooltip = "Choose between crypto exchanges, forex brokers, or automatic selection based on the asset in the chart."; - let averageLengthTooltip = "Select the length for the price and volume averages."; - let dashboardDataTooltip = "Select the data to display."; - let dashboardPositionTooltip = "Select the dashboard location."; - let dashboardSizeTooltip = "Select the dashboard size."; - let bullishColorTooltip = "Select bullish color."; - let bearishColorTooltip = "Select bearish color."; - let backgroundTooltip = "Enable background gradient color."; - let EM_SPACE = " "; - let EN_SPACE = " "; - let FOUR_PER_EM_SPACE = " "; - let SIX_PER_EM_SPACE = " "; - let HAIR_SPACE = " "; - let MS = EM_SPACE; - let NS = EN_SPACE; - let FS = FOUR_PER_EM_SPACE; - let SS = SIX_PER_EM_SPACE; - let HS = HAIR_SPACE; - let sourceInput = pinescript.inputString(AUTO, "Sources", ({ tooltip: sourceTooltip, options: [AUTO, CRYPTO_EXCHANGES, FX_BROKERS] })); - let averageLengthInput = pinescript.inputInt(20, "Average Length", ({ tooltip: averageLengthTooltip })); - let enableCRYPTOCOMInput = pinescript.inputBool(true, "CRYPTOCOM", ({ group: EXCHANGES_GROUP, inline: "row1" })); - let enableBINANCEInput = pinescript.inputBool(true, "BINANCE", ({ group: EXCHANGES_GROUP, inline: "row1" })); - let enableBYBITInput = pinescript.inputBool(true, "BYBIT", ({ group: EXCHANGES_GROUP, inline: "row1" })); - let enableWEBULLPAYInput = pinescript.inputBool(true, (("WEBULLPAY" + FS) + HS), ({ group: EXCHANGES_GROUP, inline: "row2" })); - let enableGEMINIInput = pinescript.inputBool(true, (("GEMINI" + NS) + FS), ({ group: EXCHANGES_GROUP, inline: "row2" })); - let enableCRYPTOInput = pinescript.inputBool(true, "CRYPTO", ({ group: EXCHANGES_GROUP, inline: "row2" })); - let enableBINANCEUSInput = pinescript.inputBool(true, ((("BINANCEUS" + FS) + SS) + HS), ({ group: EXCHANGES_GROUP, inline: "row3" })); - let enableKRAKENInput = pinescript.inputBool(true, (("KRAKEN" + FS) + HS), ({ group: EXCHANGES_GROUP, inline: "row3" })); - let enableBTSEInput = pinescript.inputBool(true, "BTSE", ({ group: EXCHANGES_GROUP, inline: "row3" })); - let enableBITSTAMPInput = pinescript.inputBool(true, (("BITSTAMP" + MS) + SS), ({ group: EXCHANGES_GROUP, inline: "row4" })); - let enableKUCOINInput = pinescript.inputBool(true, ("KUCOIN" + NS), ({ group: EXCHANGES_GROUP, inline: "row4" })); - let enableHTXInput = pinescript.inputBool(true, "HTX", ({ group: EXCHANGES_GROUP, inline: "row4" })); - let enableCOINBASEInput = pinescript.inputBool(true, (("COINBASE" + MS) + HS), ({ group: EXCHANGES_GROUP, inline: "row5" })); - let enableBITGETInput = pinescript.inputBool(true, ((("BITGET" + NS) + HS) + HS), ({ group: EXCHANGES_GROUP, inline: "row5" })); - let enableGATEInput = pinescript.inputBool(true, "GATE", ({ group: EXCHANGES_GROUP, inline: "row5" })); - let enableWHITEBITInput = pinescript.inputBool(true, ((("WHITEBIT" + MS) + HS) + HS), ({ group: EXCHANGES_GROUP, inline: "row6" })); - let enableCOINEXInput = pinescript.inputBool(true, (("COINEX" + NS) + HS), ({ group: EXCHANGES_GROUP, inline: "row6" })); - let enableMEXCInput = pinescript.inputBool(true, "MEXC", ({ group: EXCHANGES_GROUP, inline: "row6" })); - let enableOKXInput = pinescript.inputBool(true, "OKX", ({ group: EXCHANGES_GROUP, inline: "row7" })); - let enablePEPPERSTONEInput = pinescript.inputBool(true, "PEPPERSTONE", ({ group: BROKERS_GROUP, inline: "row8" })); - let enableEIGHTCAPInput = pinescript.inputBool(true, (("EIGHTCAP" + FS) + SS), ({ group: BROKERS_GROUP, inline: "row8" })); - let enableOANDAInput = pinescript.inputBool(true, "OANDA", ({ group: BROKERS_GROUP, inline: "row8" })); - let enableCMCMARKETSInput = pinescript.inputBool(true, ("CMCMARKETS" + FS), ({ group: BROKERS_GROUP, inline: "row9" })); - let enableTICKMILLInput = pinescript.inputBool(true, (((("TICKMILL" + NS) + FS) + HS) + HS), ({ group: BROKERS_GROUP, inline: "row9" })); - let enableSAXOInput = pinescript.inputBool(true, "SAXO", ({ group: BROKERS_GROUP, inline: "row9" })); - let enableCAPITALCOMInput = pinescript.inputBool(true, ((("CAPITALCOM" + NS) + HS) + HS), ({ group: BROKERS_GROUP, inline: "row10" })); - let enableFOREXCOMInput = pinescript.inputBool(true, "FOREXCOM", ({ group: BROKERS_GROUP, inline: "row10" })); - let enableIBKRInput = pinescript.inputBool(true, "IBKR", ({ group: BROKERS_GROUP, inline: "row10" })); - let enableICMARKETSInput = pinescript.inputBool(true, (("ICMARKETS" + MS) + FS), ({ group: BROKERS_GROUP, inline: "row11" })); - let enableVANTAGEInput = pinescript.inputBool(true, (((("VANTAGE" + NS) + SS) + SS) + HS), ({ group: BROKERS_GROUP, inline: "row11" })); - let enableFXInput = pinescript.inputBool(true, "FX", ({ group: BROKERS_GROUP, inline: "row11" })); - let enableIGInput = pinescript.inputBool(true, "IG", ({ group: BROKERS_GROUP, inline: "row12" })); - let dashboardDataInput = pinescript.inputString(PRICE_LAST, "Data", ({ group: DASHBOARD_GROUP, tooltip: dashboardDataTooltip, options: [PRICE_LAST, PRICE_AVG, VOLUME_LAST, VOLUME_AVG] })); - let dashboardPositionInput = pinescript.inputString(TOP_RIGHT, "Position", ({ group: DASHBOARD_GROUP, tooltip: dashboardPositionTooltip, options: [TOP_RIGHT, BOTTOM_RIGHT, BOTTOM_LEFT] })); - let dashboardSizeInput = pinescript.inputString(SMALL, "Size", ({ group: DASHBOARD_GROUP, tooltip: dashboardSizeTooltip, options: [TINY, SMALL, NORMAL, LARGE, HUGE] })); - let bullishColorInput = pinescript.inputColor(GREEN, "Bullish", ({ group: STYLE_GROUP, tooltip: bullishColorTooltip })); - let bearishColorInput = pinescript.inputColor(RED, "Bearish", ({ group: STYLE_GROUP, tooltip: bearishColorTooltip })); - let backgroundInput = pinescript.inputBool(true, "Background Gradient", ({ group: STYLE_GROUP, tooltip: backgroundTooltip })); + var RED = pinescript.color.hex("#F23645"); + var GREEN = pinescript.color.hex("#089981"); + var AUTO = "Auto"; + var CRYPTO_EXCHANGES = "Crypto Exchanges"; + var FX_BROKERS = "Forex Brokers"; + var TOP_RIGHT = "Top Right"; + var BOTTOM_RIGHT = "Bottom Right"; + var BOTTOM_LEFT = "Bottom Left"; + var TINY = "Tiny"; + var SMALL = "Small"; + var NORMAL = "Normal"; + var LARGE = "Large"; + var HUGE = "Huge"; + var PRICE_LAST = "Last Price"; + var VOLUME_LAST = "Last Volume"; + var PRICE_AVG = "Avg Price"; + var VOLUME_AVG = "Avg Volume"; + var DASHBOARD_GROUP = "Dashboard"; + var STYLE_GROUP = "Style"; + var EXCHANGES_GROUP = "Crypto Exchanges"; + var BROKERS_GROUP = "Forex Brokers"; + var sourceTooltip = "Choose between crypto exchanges, forex brokers, or automatic selection based on the asset in the chart."; + var averageLengthTooltip = "Select the length for the price and volume averages."; + var dashboardDataTooltip = "Select the data to display."; + var dashboardPositionTooltip = "Select the dashboard location."; + var dashboardSizeTooltip = "Select the dashboard size."; + var bullishColorTooltip = "Select bullish color."; + var bearishColorTooltip = "Select bearish color."; + var backgroundTooltip = "Enable background gradient color."; + var EM_SPACE = " "; + var EN_SPACE = " "; + var FOUR_PER_EM_SPACE = " "; + var SIX_PER_EM_SPACE = " "; + var HAIR_SPACE = " "; + var MS = EM_SPACE; + var NS = EN_SPACE; + var FS = FOUR_PER_EM_SPACE; + var SS = SIX_PER_EM_SPACE; + var HS = HAIR_SPACE; + var sourceInput = pinescript.inputString(AUTO, "Sources", ({ tooltip: sourceTooltip, options: [AUTO, CRYPTO_EXCHANGES, FX_BROKERS] })); + var averageLengthInput = pinescript.inputInt(20, "Average Length", ({ tooltip: averageLengthTooltip })); + var enableCRYPTOCOMInput = pinescript.inputBool(true, "CRYPTOCOM", ({ group: EXCHANGES_GROUP, inline: "row1" })); + var enableBINANCEInput = pinescript.inputBool(true, "BINANCE", ({ group: EXCHANGES_GROUP, inline: "row1" })); + var enableBYBITInput = pinescript.inputBool(true, "BYBIT", ({ group: EXCHANGES_GROUP, inline: "row1" })); + var enableWEBULLPAYInput = pinescript.inputBool(true, (("WEBULLPAY" + FS) + HS), ({ group: EXCHANGES_GROUP, inline: "row2" })); + var enableGEMINIInput = pinescript.inputBool(true, (("GEMINI" + NS) + FS), ({ group: EXCHANGES_GROUP, inline: "row2" })); + var enableCRYPTOInput = pinescript.inputBool(true, "CRYPTO", ({ group: EXCHANGES_GROUP, inline: "row2" })); + var enableBINANCEUSInput = pinescript.inputBool(true, ((("BINANCEUS" + FS) + SS) + HS), ({ group: EXCHANGES_GROUP, inline: "row3" })); + var enableKRAKENInput = pinescript.inputBool(true, (("KRAKEN" + FS) + HS), ({ group: EXCHANGES_GROUP, inline: "row3" })); + var enableBTSEInput = pinescript.inputBool(true, "BTSE", ({ group: EXCHANGES_GROUP, inline: "row3" })); + var enableBITSTAMPInput = pinescript.inputBool(true, (("BITSTAMP" + MS) + SS), ({ group: EXCHANGES_GROUP, inline: "row4" })); + var enableKUCOINInput = pinescript.inputBool(true, ("KUCOIN" + NS), ({ group: EXCHANGES_GROUP, inline: "row4" })); + var enableHTXInput = pinescript.inputBool(true, "HTX", ({ group: EXCHANGES_GROUP, inline: "row4" })); + var enableCOINBASEInput = pinescript.inputBool(true, (("COINBASE" + MS) + HS), ({ group: EXCHANGES_GROUP, inline: "row5" })); + var enableBITGETInput = pinescript.inputBool(true, ((("BITGET" + NS) + HS) + HS), ({ group: EXCHANGES_GROUP, inline: "row5" })); + var enableGATEInput = pinescript.inputBool(true, "GATE", ({ group: EXCHANGES_GROUP, inline: "row5" })); + var enableWHITEBITInput = pinescript.inputBool(true, ((("WHITEBIT" + MS) + HS) + HS), ({ group: EXCHANGES_GROUP, inline: "row6" })); + var enableCOINEXInput = pinescript.inputBool(true, (("COINEX" + NS) + HS), ({ group: EXCHANGES_GROUP, inline: "row6" })); + var enableMEXCInput = pinescript.inputBool(true, "MEXC", ({ group: EXCHANGES_GROUP, inline: "row6" })); + var enableOKXInput = pinescript.inputBool(true, "OKX", ({ group: EXCHANGES_GROUP, inline: "row7" })); + var enablePEPPERSTONEInput = pinescript.inputBool(true, "PEPPERSTONE", ({ group: BROKERS_GROUP, inline: "row8" })); + var enableEIGHTCAPInput = pinescript.inputBool(true, (("EIGHTCAP" + FS) + SS), ({ group: BROKERS_GROUP, inline: "row8" })); + var enableOANDAInput = pinescript.inputBool(true, "OANDA", ({ group: BROKERS_GROUP, inline: "row8" })); + var enableCMCMARKETSInput = pinescript.inputBool(true, ("CMCMARKETS" + FS), ({ group: BROKERS_GROUP, inline: "row9" })); + var enableTICKMILLInput = pinescript.inputBool(true, (((("TICKMILL" + NS) + FS) + HS) + HS), ({ group: BROKERS_GROUP, inline: "row9" })); + var enableSAXOInput = pinescript.inputBool(true, "SAXO", ({ group: BROKERS_GROUP, inline: "row9" })); + var enableCAPITALCOMInput = pinescript.inputBool(true, ((("CAPITALCOM" + NS) + HS) + HS), ({ group: BROKERS_GROUP, inline: "row10" })); + var enableFOREXCOMInput = pinescript.inputBool(true, "FOREXCOM", ({ group: BROKERS_GROUP, inline: "row10" })); + var enableIBKRInput = pinescript.inputBool(true, "IBKR", ({ group: BROKERS_GROUP, inline: "row10" })); + var enableICMARKETSInput = pinescript.inputBool(true, (("ICMARKETS" + MS) + FS), ({ group: BROKERS_GROUP, inline: "row11" })); + var enableVANTAGEInput = pinescript.inputBool(true, (((("VANTAGE" + NS) + SS) + SS) + HS), ({ group: BROKERS_GROUP, inline: "row11" })); + var enableFXInput = pinescript.inputBool(true, "FX", ({ group: BROKERS_GROUP, inline: "row11" })); + var enableIGInput = pinescript.inputBool(true, "IG", ({ group: BROKERS_GROUP, inline: "row12" })); + var dashboardDataInput = pinescript.inputString(PRICE_LAST, "Data", ({ group: DASHBOARD_GROUP, tooltip: dashboardDataTooltip, options: [PRICE_LAST, PRICE_AVG, VOLUME_LAST, VOLUME_AVG] })); + var dashboardPositionInput = pinescript.inputString(TOP_RIGHT, "Position", ({ group: DASHBOARD_GROUP, tooltip: dashboardPositionTooltip, options: [TOP_RIGHT, BOTTOM_RIGHT, BOTTOM_LEFT] })); + var dashboardSizeInput = pinescript.inputString(SMALL, "Size", ({ group: DASHBOARD_GROUP, tooltip: dashboardSizeTooltip, options: [TINY, SMALL, NORMAL, LARGE, HUGE] })); + var bullishColorInput = pinescript.inputColor(GREEN, "Bullish", ({ group: STYLE_GROUP, tooltip: bullishColorTooltip })); + var bearishColorInput = pinescript.inputColor(RED, "Bearish", ({ group: STYLE_GROUP, tooltip: bearishColorTooltip })); + var backgroundInput = pinescript.inputBool(true, "Background Gradient", ({ group: STYLE_GROUP, tooltip: backgroundTooltip })); if (state.crypto === undefined) state.crypto = pinescript.arrayNew(); if (state.forex === undefined) state.forex = pinescript.arrayNew(); if (barstate.isfirst) { if (enableBITSTAMPInput) { - crypto.push("BITSTAMP"); + state.crypto.push("BITSTAMP"); } if (enableCOINBASEInput) { - crypto.push("COINBASE"); + state.crypto.push("COINBASE"); } if (enableCRYPTOInput) { - crypto.push("CRYPTO"); + state.crypto.push("CRYPTO"); } if (enableBINANCEInput) { - crypto.push("BINANCE"); + state.crypto.push("BINANCE"); } if (enableKRAKENInput) { - crypto.push("KRAKEN"); + state.crypto.push("KRAKEN"); } if (enableOKXInput) { - crypto.push("OKX"); + state.crypto.push("OKX"); } if (enableGEMINIInput) { - crypto.push("GEMINI"); + state.crypto.push("GEMINI"); } if (enableCRYPTOCOMInput) { - crypto.push("CRYPTOCOM"); + state.crypto.push("CRYPTOCOM"); } if (enableWEBULLPAYInput) { - crypto.push("WEBULLPAY"); + state.crypto.push("WEBULLPAY"); } if (enableBINANCEUSInput) { - crypto.push("BINANCEUS"); + state.crypto.push("BINANCEUS"); } if (enableBTSEInput) { - crypto.push("BTSE"); + state.crypto.push("BTSE"); } if (enableWHITEBITInput) { - crypto.push("WHITEBIT"); + state.crypto.push("WHITEBIT"); } if (enableBYBITInput) { - crypto.push("BYBIT"); + state.crypto.push("BYBIT"); } if (enableKUCOINInput) { - crypto.push("KUCOIN"); + state.crypto.push("KUCOIN"); } if (enableMEXCInput) { - crypto.push("MEXC"); + state.crypto.push("MEXC"); } if (enableBITGETInput) { - crypto.push("BITGET"); + state.crypto.push("BITGET"); } if (enableCOINEXInput) { - crypto.push("COINEX"); + state.crypto.push("COINEX"); } if (enableHTXInput) { - crypto.push("HTX"); + state.crypto.push("HTX"); } if (enableGATEInput) { - crypto.push("GATE"); + state.crypto.push("GATE"); } if (enableTICKMILLInput) { - forex.push("TICKMILL"); + state.forex.push("TICKMILL"); } if (enableFXInput) { - forex.push("FX"); + state.forex.push("FX"); } if (enableOANDAInput) { - forex.push("OANDA"); + state.forex.push("OANDA"); } if (enableFOREXCOMInput) { - forex.push("FOREXCOM"); + state.forex.push("FOREXCOM"); } if (enablePEPPERSTONEInput) { - forex.push("PEPPERSTONE"); + state.forex.push("PEPPERSTONE"); } if (enableCMCMARKETSInput) { - forex.push("CMCMARKETS"); + state.forex.push("CMCMARKETS"); } if (enableICMARKETSInput) { - forex.push("ICMARKETS"); + state.forex.push("ICMARKETS"); } if (enableIBKRInput) { - forex.push("IBKR"); + state.forex.push("IBKR"); } if (enableIGInput) { - forex.push("IG"); + state.forex.push("IG"); } if (enableEIGHTCAPInput) { - forex.push("EIGHTCAP"); + state.forex.push("EIGHTCAP"); } if (enableSAXOInput) { - forex.push("SAXO"); + state.forex.push("SAXO"); } if (enableCAPITALCOMInput) { - forex.push("CAPITALCOM"); + state.forex.push("CAPITALCOM"); } if (enableVANTAGEInput) { - forex.push("VANTAGE"); + state.forex.push("VANTAGE"); } } if (state.exchanges === undefined) state.exchanges = ((sourceInput === AUTO) ? ((syminfo.type === "crypto") ? state.crypto : state.forex) : ((sourceInput === CRYPTO_EXCHANGES) ? state.crypto : state.forex)); if (state.currentData === undefined) state.currentData = data.new(pinescript.arrayNew(), pinescript.arrayNew(), pinescript.arrayNew(), pinescript.arrayNew()); - if (state.assets === undefined) state.assets = array.from(asset.new(pinescript.arrayNew(), pinescript.arrayNew()), asset.new(pinescript.arrayNew(), pinescript.arrayNew()), asset.new(pinescript.arrayNew(), pinescript.arrayNew()), asset.new(pinescript.arrayNew(), pinescript.arrayNew()), asset.new(pinescript.arrayNew(), pinescript.arrayNew()), asset.new(pinescript.arrayNew(), pinescript.arrayNew()), asset.new(pinescript.arrayNew(), pinescript.arrayNew()), asset.new(pinescript.arrayNew(), pinescript.arrayNew()), asset.new(pinescript.arrayNew(), pinescript.arrayNew()), asset.new(pinescript.arrayNew(), pinescript.arrayNew()), asset.new(pinescript.arrayNew(), pinescript.arrayNew()), asset.new(pinescript.arrayNew(), pinescript.arrayNew()), asset.new(pinescript.arrayNew(), pinescript.arrayNew()), asset.new(pinescript.arrayNew(), pinescript.arrayNew()), asset.new(pinescript.arrayNew(), pinescript.arrayNew()), asset.new(pinescript.arrayNew(), pinescript.arrayNew()), asset.new(pinescript.arrayNew(), pinescript.arrayNew()), asset.new(pinescript.arrayNew(), pinescript.arrayNew()), asset.new(pinescript.arrayNew(), pinescript.arrayNew())); + if (state.assets === undefined) state.assets = pinescript.array.from(asset.new(pinescript.arrayNew(), pinescript.arrayNew()), asset.new(pinescript.arrayNew(), pinescript.arrayNew()), asset.new(pinescript.arrayNew(), pinescript.arrayNew()), asset.new(pinescript.arrayNew(), pinescript.arrayNew()), asset.new(pinescript.arrayNew(), pinescript.arrayNew()), asset.new(pinescript.arrayNew(), pinescript.arrayNew()), asset.new(pinescript.arrayNew(), pinescript.arrayNew()), asset.new(pinescript.arrayNew(), pinescript.arrayNew()), asset.new(pinescript.arrayNew(), pinescript.arrayNew()), asset.new(pinescript.arrayNew(), pinescript.arrayNew()), asset.new(pinescript.arrayNew(), pinescript.arrayNew()), asset.new(pinescript.arrayNew(), pinescript.arrayNew()), asset.new(pinescript.arrayNew(), pinescript.arrayNew()), asset.new(pinescript.arrayNew(), pinescript.arrayNew()), asset.new(pinescript.arrayNew(), pinescript.arrayNew()), asset.new(pinescript.arrayNew(), pinescript.arrayNew()), asset.new(pinescript.arrayNew(), pinescript.arrayNew()), asset.new(pinescript.arrayNew(), pinescript.arrayNew()), asset.new(pinescript.arrayNew(), pinescript.arrayNew())); pinescript.position.bottom_left; pinescript.position.bottom_right; pinescript.position.top_right; @@ -1883,40 +2042,40 @@ function main() { pinescript.size.tiny; if (state.parsedDashboardSize === undefined) state.parsedDashboardSize = ((dashboardSizeInput === TINY) ? undefined : ((dashboardSizeInput === SMALL) ? undefined : ((dashboardSizeInput === NORMAL) ? undefined : ((dashboardSizeInput === LARGE) ? undefined : ((dashboardSizeInput === HUGE) ? undefined : null))))); function clearData() { - currentData.lastPrices.clear(); - currentData.lastVolumes.clear(); - currentData.avgPrices.clear(); - return currentData.avgVolumes.clear(); + state.currentData.lastPrices.clear(); + state.currentData.lastVolumes.clear(); + state.currentData.avgPrices.clear(); + return state.currentData.avgVolumes.clear(); } function gatherData() { - let ticker = syminfo.ticker; - for (const [index, eachExchange] of state.exchanges) { - let tickerid = ((eachExchange + ":") + ticker); - let [assetPrice, assetVolume] = pinescript.unpack(pinescript.requestSecurity(tickerid, "", [close, volume], ({ ignore_invalid_symbol: true, calc_bars_count: averageLengthInput })), 2); + var ticker = syminfo.ticker; + for (const [index, eachExchange] of (state.exchanges ?? []).entries()) { + var tickerid = ((eachExchange + ":") + ticker); + var [assetPrice, assetVolume] = pinescript.unpack(pinescript.requestSecurity(tickerid, "", [close, volume], ({ ignore_invalid_symbol: true, calc_bars_count: averageLengthInput })), 2); if (!pinescript.na(assetPrice)) { - assets.get(index).prices.push(pinescript.nz(assetPrice)); - if ((assets.get(index).prices.size() > averageLengthInput)) { - assets.get(index).prices.shift(); + state.assets.get(index).prices.push(pinescript.nz(assetPrice)); + if ((state.assets.get(index).prices.size() > averageLengthInput)) { + state.assets.get(index).prices.shift(); } } if (!pinescript.na(assetVolume)) { - assets.get(index).volumes.push(pinescript.nz(assetVolume)); - if ((assets.get(index).prices.size() > averageLengthInput)) { - assets.get(index).volumes.shift(); + state.assets.get(index).volumes.push(pinescript.nz(assetVolume)); + if ((state.assets.get(index).prices.size() > averageLengthInput)) { + state.assets.get(index).volumes.shift(); } } } } function updateData() { clearData(); - for (const eachAsset of state.assets) { + for (const eachAsset of (state.assets ?? [])) { if ((eachAsset.prices.size() !== 0)) { - currentData.lastPrices.push(eachAsset.prices.last()); - currentData.avgPrices.push(eachAsset.prices.avg()); + state.currentData.lastPrices.push(eachAsset.prices.last()); + state.currentData.avgPrices.push(eachAsset.prices.avg()); } if ((eachAsset.volumes.size() !== 0)) { - currentData.lastVolumes.push(eachAsset.volumes.last()); - currentData.avgVolumes.push(eachAsset.volumes.avg()); + state.currentData.lastVolumes.push(eachAsset.volumes.last()); + state.currentData.avgVolumes.push(eachAsset.volumes.avg()); } } } @@ -1924,28 +2083,28 @@ function main() { return t_able.cell(column, row, data, ({ text_color: pinescript.color, text_size: state.parsedDashboardSize, text_halign: align, bgcolor: background, tooltip: tooltip })); } function checkData(index) { - return (((dashboardDataInput === PRICE_LAST) || (dashboardDataInput === PRICE_AVG)) ? ((assets.get(index).prices.size() !== 0) && (assets.get(index).prices.avg() !== 0)) : ((assets.get(index).volumes.size() !== 0) && (assets.get(index).volumes.avg() !== 0))); + return (((dashboardDataInput === PRICE_LAST) || (dashboardDataInput === PRICE_AVG)) ? ((state.assets.get(index).prices.size() !== 0) && (state.assets.get(index).prices.avg() !== 0)) : ((state.assets.get(index).volumes.size() !== 0) && (state.assets.get(index).volumes.avg() !== 0))); } function dashboardData(column, row) { switch (dashboardDataInput) { case PRICE_LAST: { - (assets.get(row).prices.last() - assets.get(column).prices.last()); + (state.assets.get(row).prices.last() - state.assets.get(column).prices.last()); break; } case VOLUME_LAST: { - (assets.get(row).volumes.last() - assets.get(column).volumes.last()); + (state.assets.get(row).volumes.last() - state.assets.get(column).volumes.last()); break; } case PRICE_AVG: { - (assets.get(row).prices.avg() - assets.get(column).prices.avg()); + (state.assets.get(row).prices.avg() - state.assets.get(column).prices.avg()); break; } case VOLUME_AVG: { - (assets.get(row).volumes.avg() - assets.get(column).volumes.avg()); + (state.assets.get(row).volumes.avg() - state.assets.get(column).volumes.avg()); break; } } @@ -1957,22 +2116,22 @@ function main() { switch (dashboardDataInput) { case PRICE_LAST: { - currentData.lastPrices.range(); + state.currentData.lastPrices.range(); break; } case VOLUME_LAST: { - currentData.lastVolumes.range(); + state.currentData.lastVolumes.range(); break; } case PRICE_AVG: { - currentData.avgPrices.range(); + state.currentData.avgPrices.range(); break; } case VOLUME_AVG: { - currentData.avgVolumes.range(); + state.currentData.avgVolumes.range(); break; } } @@ -1980,22 +2139,22 @@ function main() { function drawDashboard() { if (state.t_able === undefined) state.t_able = pinescript.table.new(state.parsedDashboardPosition, 21, 21, ({ bgcolor: pinescript.color.hex("#1e222d"), border_color: pinescript.color.hex("#373a46"), border_width: 1, frame_color: pinescript.color.hex("#373a46"), frame_width: 1, force_overlay: true })); cell(state.t_able, 0, 0, syminfo.ticker, pinescript.color.new(pinescript.color.white, 20), ({ align: pinescript.text.align_center })); - t_able.cell_set_text_formatting(0, 0, pinescript.text.format_bold); - for (const [row, rowExchange] of state.exchanges) { + state.t_able.cell_set_text_formatting(0, 0, pinescript.text.format_bold); + for (const [row, rowExchange] of (state.exchanges ?? []).entries()) { if (checkData(row)) { cell(state.t_able, 0, (row + 1), rowExchange, ({ align: pinescript.text.align_left })); - for (const [column, columnExchange] of state.exchanges) { + for (const [column, columnExchange] of (state.exchanges ?? []).entries()) { if (checkData(column)) { - let delta = dashboardData(column, row); - let maxDelta = extremeDataValue(); - let backgroundColor = (backgroundInput ? pinescript.color.from_gradient(pinescript.abs(delta), 0, maxDelta, pinescript.color.new(((delta > 0) ? bullishColorInput : bearishColorInput), 100), pinescript.color.new(((delta > 0) ? bullishColorInput : bearishColorInput), 70)) : null); - let textColor = pinescript.color.from_gradient(pinescript.abs(delta), 0, maxDelta, pinescript.color.new(((delta > 0) ? bullishColorInput : bearishColorInput), 50), pinescript.color.new(((delta > 0) ? bullishColorInput : bearishColorInput), 0)); + var delta = dashboardData(column, row); + var maxDelta = extremeDataValue(); + var backgroundColor = (backgroundInput ? pinescript.color.from_gradient(pinescript.abs(delta), 0, maxDelta, pinescript.color.new(((delta > 0) ? bullishColorInput : bearishColorInput), 100), pinescript.color.new(((delta > 0) ? bullishColorInput : bearishColorInput), 70)) : null); + var textColor = pinescript.color.from_gradient(pinescript.abs(delta), 0, maxDelta, pinescript.color.new(((delta > 0) ? bullishColorInput : bearishColorInput), 50), pinescript.color.new(((delta > 0) ? bullishColorInput : bearishColorInput), 0)); cell(state.t_able, (column + 1), 0, columnExchange, ({ align: pinescript.text.align_center })); cell(state.t_able, (column + 1), (row + 1), ((delta !== 0) ? pinescript.strToString(delta, dashboardFormat()) : ""), ({ align: pinescript.text.align_center, color: textColor, background: backgroundColor, tooltip: ((rowExchange + " vs ") + columnExchange) })); if ((pinescript.abs(delta) === maxDelta)) { - t_able.cell_set_bgcolor((column + 1), (row + 1), ((delta > 0) ? bullishColorInput : bearishColorInput)); - t_able.cell_set_text_formatting((column + 1), (row + 1), pinescript.text.format_bold); - t_able.cell_set_text_color((column + 1), (row + 1), pinescript.color.white); + state.t_able.cell_set_bgcolor((column + 1), (row + 1), ((delta > 0) ? bullishColorInput : bearishColorInput)); + state.t_able.cell_set_text_formatting((column + 1), (row + 1), pinescript.text.format_bold); + state.t_able.cell_set_text_color((column + 1), (row + 1), pinescript.color.white); } } } @@ -2055,6 +2214,10 @@ function run(data, options = {}) { rt.__shapeIdx = 0; globalThis.bar_index = i; globalThis.last_bar_index = n - 1; + globalThis.time_tradingday = inTime[i]; + globalThis.time_close = inTime[i]; + globalThis.last_bar_time = inTime[n - 1]; + globalThis.timenow = inTime[n - 1]; globalThis.barstate = { isfirst: i === 0, islast: i === n - 1, isrealtime: false, ishistory: true, isconfirmed: true, isnew: true, islastconfirmedhistory: i === n - 1, diff --git a/converted/Asset_Drift_Model.js b/converted/Asset_Drift_Model.js index 60b5c58..2f4f8f4 100644 --- a/converted/Asset_Drift_Model.js +++ b/converted/Asset_Drift_Model.js @@ -42,6 +42,80 @@ const pinescript = { if (value != null && typeof value[Symbol.iterator] === 'function') return Array.from(value); return new Array(count || 0).fill(null); }, + __decArr: function(arr) { + if (!Array.isArray(arr) || arr.__pineDecorated) return arr; + const self = this; + const def = (name, fn) => Object.defineProperty(arr, name, { value: fn, writable: true, configurable: true }); + Object.defineProperty(arr, '__pineDecorated', { value: true, configurable: true }); + def('get', (i) => (arr[i] === undefined ? null : arr[i])); + def('set', (i, v) => { arr[i] = v; return v; }); + def('size', () => arr.length); + def('clear', () => { arr.length = 0; }); + def('insert', (i, v) => { arr.splice(i, 0, v); }); + def('remove', (i) => arr.splice(i, 1)[0]); + def('contains', (v) => arr.includes(v)); + def('indexof', (v) => arr.indexOf(v)); + def('lastindexof', (v) => arr.lastIndexOf(v)); + def('first', () => (arr.length ? arr[0] : null)); + def('last', () => (arr.length ? arr[arr.length - 1] : null)); + def('sum', () => arr.reduce((a, b) => a + b, 0)); + def('avg', () => (arr.length ? arr.reduce((a, b) => a + b, 0) / arr.length : 0)); + def('min', () => (arr.length ? Math.min(...arr) : null)); + def('max', () => (arr.length ? Math.max(...arr) : null)); + def('range', () => (arr.length ? Math.max(...arr) - Math.min(...arr) : null)); + // Statistical methods that route to the array.* built-ins (these names are not + // native to JS arrays, so attaching them here is safe from recursion). + def('median', () => self.arrayMedian(arr)); + def('mode', () => self.arrayMode(arr)); + def('stdev', () => self.arrayStdev(arr)); + def('variance', () => self.arrayVariance(arr)); + def('covariance', (other) => self.arrayCovariance(arr, other)); + def('percentile_linear_interpolation', (p) => self.arrayPercentileLinearInterpolation(arr, p)); + def('percentile_nearest_rank', (p) => self.arrayPercentileNearestRank(arr, p)); + def('abs', () => self.__decArr(self.arrayAbs(arr))); + def('binary_search', (v) => self.arrayBinarySearch(arr, v)); + // Pine's array.sort takes an order string, not a comparator. Use the native + // sort via .call so we don't recurse through this overridden method. + def('sort', (order) => { Array.prototype.sort.call(arr, (a, b) => (order === 'descending' ? b - a : a - b)); return arr; }); + def('sort_indices', (order) => self.__decArr(arr.map((_, i) => i).sort((a, b) => (order === 'descending' ? arr[b] - arr[a] : arr[a] - arr[b])))); + // join/slice/reverse/concat/includes/fill already exist natively on Array with + // compatible semantics, so we deliberately leave them to the native methods. + return arr; + }, + __decMap: function(m) { + if (!(m instanceof Map) || m.__pineDecorated) return m; + const def = (name, fn) => Object.defineProperty(m, name, { value: fn, writable: true, configurable: true }); + Object.defineProperty(m, '__pineDecorated', { value: true, configurable: true }); + def('put', (k, v) => { m.set(k, v); return v; }); + def('contains', (k) => m.has(k)); + def('remove', (k) => m.delete(k)); + def('keys', () => Array.from(Map.prototype.keys.call(m))); + def('values', () => Array.from(Map.prototype.values.call(m))); + def('size_', () => m.size); + return m; + }, + __decMatrix: function(m) { + if (!m || typeof m !== 'object' || m.__pineDecorated) return m; + const self = this; + const def = (name, fn) => Object.defineProperty(m, name, { value: fn, writable: true, configurable: true }); + Object.defineProperty(m, '__pineDecorated', { value: true, configurable: true }); + def('get', (r, c) => self.matrixGet(m, r, c)); + def('set', (r, c, v) => self.matrixSet(m, r, c, v)); + def('rows_', () => m.rows); + def('columns', () => m.cols); + def('fill', (v) => self.matrixFill(m, v)); + return m; + }, + __decDraw: function(obj) { + if (!obj || typeof obj !== 'object') return obj; + const p = new Proxy(obj, { + get(t, k) { + if (k in t || typeof k === 'symbol') return t[k]; + return function() { return p; }; + }, + }); + return p; + }, alertcondition: function(condition, ...rest) { if (globalThis.__pineRuntime) { globalThis.__pineRuntime.alerts.push({ condition, args: rest }); @@ -49,6 +123,32 @@ const pinescript = { return null; }, barcolor: function(color) { return null; }, + plotchar: function(series, ...rest) { + const rt = globalThis.__pineRuntime; + if (!rt) return series; + const bar = rt.__barIndex | 0; + const ord = (rt.__shapeIdx = (rt.__shapeIdx | 0) + 1) - 1; + let key = 'char_' + ord; + for (const r of rest) { + if (typeof r === 'string') { key = r; break; } + if (r && typeof r === 'object' && r.title) { key = String(r.title); break; } + } + let s = rt.plotshapes[key]; + if (!s) s = rt.plotshapes[key] = { title: key, data: [] }; + s.data[bar] = this.__scalar(series); + return series; + }, + plotarrow: function(series, ...rest) { + const rt = globalThis.__pineRuntime; + if (!rt) return series; + const bar = rt.__barIndex | 0; + const ord = (rt.__plotIdx = (rt.__plotIdx | 0) + 1) - 1; + const key = 'arrow_' + ord; + let p = rt.plots[key]; + if (!p) p = rt.plots[key] = { title: key, data: [] }; + p.data[bar] = this.__scalar(series); + return series; + }, bgcolor: function(color, title, editable, showLast) { return null; }, @@ -470,6 +570,12 @@ const pinescript = { atan: function(value) { return Math.atan(value); }, + todegrees: function(radians) { + return radians * (180 / Math.PI); + }, + toradians: function(degrees) { + return degrees * (Math.PI / 180); + }, floor: function(value) { return Math.floor(value); }, @@ -641,7 +747,7 @@ const pinescript = { return series; }, lineNew: function(x1, y1, x2, y2, opts = {}) { - return { x1, y1, x2, y2, opts, _type: 'line' }; + return this.__decDraw({ x1, y1, x2, y2, opts, _type: 'line' }); }, lineDelete: function(l) { return null; @@ -661,7 +767,7 @@ const pinescript = { return point === 0 || point === 'y1' ? line.y1 : line.y2; }, labelNew: function(x, y, text = '', opts = {}) { - return { x, y, text, opts, _type: 'label' }; + return this.__decDraw({ x, y, text, opts, _type: 'label' }); }, labelDelete: function(l) { return null; @@ -676,7 +782,7 @@ const pinescript = { return label.text || ''; }, boxNew: function(left, top, right, bottom, opts = {}) { - return { left, top, right, bottom, opts, _type: 'box' }; + return this.__decDraw({ left, top, right, bottom, opts, _type: 'box' }); }, boxDelete: function(box) { return null; @@ -694,7 +800,7 @@ const pinescript = { return box; }, polylineNew: function(points, opts = {}) { - return { points: points || [], opts, _type: 'polyline' }; + return this.__decDraw({ points: points || [], opts, _type: 'polyline' }); }, polylineDelete: function(poly) { return null; @@ -765,7 +871,7 @@ const pinescript = { return { time: _time ?? null, price: _price ?? null }; }, mapNew: function() { - return new Map(); + return this.__decMap(new Map()); }, mapSize: function(m) { return m instanceof Map ? m.size : 0; @@ -800,7 +906,7 @@ const pinescript = { const r = Math.max(0, rows ?? 0); const c = Math.max(0, cols ?? 0); const data = Array.from({ length: r }, () => Array.from({ length: c }, () => initialValue)); - return { rows: r, cols: c, data }; + return this.__decMatrix({ rows: r, cols: c, data }); }, matrixRows: function(m) { return m?.rows ?? 0; @@ -923,11 +1029,62 @@ const pinescript = { } return null; }, + __jacobiEigen: function(m) { + if (!m || !Array.isArray(m.data)) return null; + const n = m.rows ?? 0; + if (n === 0 || n !== (m.cols ?? 0)) return null; + // Work on a copy so the input matrix is untouched. + const a = m.data.map(row => row.slice()); + const v = Array.from({ length: n }, (_, i) => Array.from({ length: n }, (_, j) => (i === j ? 1 : 0))); + for (let sweep = 0; sweep < 100; sweep++) { + let off = 0; + for (let p = 0; p < n; p++) for (let q = p + 1; q < n; q++) off += a[p][q] * a[p][q]; + if (off < 1e-20) break; + for (let p = 0; p < n; p++) { + for (let q = p + 1; q < n; q++) { + if (Math.abs(a[p][q]) < 1e-18) continue; + const theta = (a[q][q] - a[p][p]) / (2 * a[p][q]); + const t = Math.sign(theta || 1) / (Math.abs(theta) + Math.sqrt(theta * theta + 1)); + const cos = 1 / Math.sqrt(t * t + 1); + const sin = t * cos; + for (let k = 0; k < n; k++) { + const akp = a[k][p], akq = a[k][q]; + a[k][p] = cos * akp - sin * akq; + a[k][q] = sin * akp + cos * akq; + } + for (let k = 0; k < n; k++) { + const apk = a[p][k], aqk = a[q][k]; + a[p][k] = cos * apk - sin * aqk; + a[q][k] = sin * apk + cos * aqk; + } + for (let k = 0; k < n; k++) { + const vkp = v[k][p], vkq = v[k][q]; + v[k][p] = cos * vkp - sin * vkq; + v[k][q] = sin * vkp + cos * vkq; + } + } + } + } + // Sort eigenpairs by eigenvalue, descending. + const order = Array.from({ length: n }, (_, i) => i).sort((i, j) => a[j][j] - a[i][i]); + const values = order.map(i => a[i][i]); + const vectors = Array.from({ length: n }, (_, r) => order.map(c => v[r][c])); + return { values, vectors }; + }, + matrixEigenvalues: function(m) { + const e = this.__jacobiEigen(m); + return this.__decArr(e ? e.values : []); + }, + matrixEigenvectors: function(m) { + const e = this.__jacobiEigen(m); + if (!e) return this.__decMatrix({ rows: 0, cols: 0, data: [] }); + return this.__decMatrix({ rows: e.vectors.length, cols: e.vectors.length, data: e.vectors }); + }, requestSecurity: function(symbol, timeframe, expression) { return expression; }, arrayNew: function(initialSize = 0, initialValue = 0) { - return Array(initialSize).fill(initialValue); + return this.__decArr(Array(initialSize).fill(initialValue)); }, arraySize: function(arr) { return arr ? arr.length : 0; @@ -978,10 +1135,10 @@ const pinescript = { return arr; }, arraySlice: function(arr, startIndex = 0, endIndex = null) { - if (!arr) return []; + if (!arr) return this.__decArr([]); const start = Number(startIndex) || 0; const end = endIndex === null || endIndex === undefined ? arr.length : Number(endIndex) || 0; - return arr.slice(start, end); + return this.__decArr(arr.slice(start, end)); }, arraySort: function(arr, order = 'ascending') { if (arr) arr.sort((a, b) => order === 'ascending' ? a - b : b - a); @@ -1191,7 +1348,7 @@ const pinescript = { const info = { ticker: 'AAPL', tickerid: 'NASDAQ:AAPL', prefix: 'NASDAQ', root: 'AAPL', suffix: '' }; return info[type] || ''; }, - timenow: 1781574526555, + timenow: 1782783575854, barstate: "LAST", dividends: {}, splits: {}, @@ -1245,11 +1402,11 @@ const pinescript = { arrayConcat: function(arr1, arr2) { if (!arr1) return arr2 || []; if (!arr2) return arr1; - return arr1.concat(arr2); + return this.__decArr(arr1.concat(arr2)); }, arrayCopy: function(arr) { - if (!arr) return []; - return [...arr]; + if (!arr) return this.__decArr([]); + return this.__decArr([...arr]); }, arrayBinarySearch: function(arr, value) { if (!arr || arr.length === 0) return -1; @@ -1339,7 +1496,7 @@ globalThis.input.timeframe = globalThis.input.timeframe || ((defval) => defval); globalThis.array = globalThis.array || { - from: (...items) => items, + from: (...items) => pinescript.__decArr(items), size: (arr) => pinescript.arraySize(arr), @@ -1508,6 +1665,8 @@ globalThis.line = globalThis.line || __pineNS({ style_solid: "solid", style_dash globalThis.box = globalThis.box || __pineNS({}); +globalThis.color = globalThis.color || __pineNS(Object.assign(function(c) { return c; }, { new: function(c, t) { return c; }, rgb: function(r, g, b, t) { return "#rgb(" + [r, g, b].join(",") + ")"; }, from_gradient: function(v, lo, hi, c1, c2) { return c1; }, r: function() { return 0; }, g: function() { return 0; }, b: function() { return 0; }, t: function() { return 0; }, aqua: "#00BCD4", black: "#363A45", blue: "#2962FF", fuchsia: "#E040FB", gray: "#787B86", green: "#4CAF50", lime: "#00E676", maroon: "#880E4F", navy: "#311B92", olive: "#808000", orange: "#FF9800", purple: "#9C27B0", red: "#FF5252", silver: "#B2B5BE", teal: "#00897B", white: "#FFFFFF", yellow: "#FFEB3B" })); + globalThis.label = globalThis.label || __pineNS({ style_label_down: "label_down", style_label_up: "label_up", style_none: "none" }); globalThis.polyline = globalThis.polyline || __pineNS({}); @@ -1520,7 +1679,9 @@ globalThis.map = globalThis.map || __pineNS({}); globalThis.session = globalThis.session || __pineNS({ regular: "regular", extended: "extended" }); -globalThis.dayofweek = globalThis.dayofweek || __pineNS({ sunday: 1, monday: 2, tuesday: 3, wednesday: 4, thursday: 5, friday: 6, saturday: 7 }); +globalThis.ticker = globalThis.ticker || __pineNS({}); + +globalThis.dayofweek = globalThis.dayofweek || __pineNS(Object.assign(function(t) { return new Date(t != null ? t : (globalThis.time || 0)).getUTCDay() + 1; }, { sunday: 1, monday: 2, tuesday: 3, wednesday: 4, thursday: 5, friday: 6, saturday: 7 })); globalThis.timeframe = __pineNS(Object.assign(globalThis.timeframe || {}, { period: (globalThis.timeframe && globalThis.timeframe.period) || "D", isintraday: false, isdaily: true, multiplier: 1 })); @@ -1568,8 +1729,6 @@ globalThis.bool = globalThis.bool || function(x) { return Boolean(x); }; globalThis.string = globalThis.string || function(x) { return x == null ? null : String(x); }; -globalThis.dayofweek = globalThis.dayofweek || __pineNS({ sunday: 1, monday: 2, tuesday: 3, wednesday: 4, thursday: 5, friday: 6, saturday: 7 }); - globalThis.str = globalThis.str || __pineNS({}); pinescript.math = globalThis.math; @@ -1655,7 +1814,7 @@ pinescript.text = { align_center: "center" }; pinescript.table = { - new: function(position, columns, rows, opts) { return { position, columns, rows, opts: opts || {}, cells: [] }; }, + new: function(position, columns, rows, opts) { return pinescript.__decDraw({ position, columns, rows, opts: opts || {}, cells: [] }); }, cell: function(table, column, row, text, opts) { @@ -1688,68 +1847,68 @@ function main() { null; // Study: Asset Drift Model // Options: {"shorttitle":"ADM","overlay":false,"max_bars_back":800} - let HORIZON = 60; - let SAMPLE_TOTAL = 756; - let MIN_OBS_STRICT = 10; - let MIN_OBS_ROBUST = 20; - let T_CRIT_95 = 2; - let T_CRIT_90 = 1.65; - let MIN_DRIFT_ECON = 0.03; - let MAX_DEV_SD = 1.5; - let SIGN_Z_CRIT = 1.65; - let ANN_FACTOR = (252 / HORIZON); - let i_bgMode = pinescript.inputString("On", "Background", ({ options: ["Off", "On"], group: "Display", tooltip: "Background color based on drift classification" })); - let i_bgInt = pinescript.inputInt(75, "BG Intensity", ({ minval: 70, maxval: 95, group: "Display", tooltip: "Transparency of background color" })); - let i_barColor = pinescript.inputBool(true, "Bar Coloring", ({ group: "Display", tooltip: "Color bars based on drift direction" })); - let i_showDash = pinescript.inputBool(true, "Dashboard", ({ group: "Dashboard", tooltip: "Show statistical details panel" })); - let i_dashPos = pinescript.inputString("Top Right", "Position", ({ options: ["Top Right", "Top Left", "Bottom Right", "Bottom Left"], group: "Dashboard" })); - let i_dashSize = pinescript.inputString("Small", "Size", ({ options: ["Tiny", "Small", "Normal"], group: "Dashboard", tooltip: "Text size of dashboard" })); - let i_showWM = pinescript.inputBool(true, "Watermark", ({ group: "Watermark", tooltip: "Show ticker and classification summary" })); - let i_wmPos = pinescript.inputString("Bottom Right", "Position", ({ options: ["Top Right", "Top Left", "Bottom Right", "Bottom Left"], group: "Watermark" })); - let i_wmSize = pinescript.inputString("Normal", "Size", ({ options: ["Small", "Normal", "Large"], group: "Watermark", tooltip: "Text size of watermark" })); - let i_dark = pinescript.inputBool(true, "Dark Mode", ({ group: "Style", tooltip: "Optimize colors for dark charts" })); - let i_alerts = pinescript.inputBool(false, "Alerts", ({ group: "Alerts", tooltip: "Trigger alerts on classification changes" })); - let cBull = (i_dark ? pinescript.color.hex("#22c55e") : pinescript.color.hex("#16a34a")); - let cBear = (i_dark ? pinescript.color.hex("#ef4444") : pinescript.color.hex("#dc2626")); - let cWeak = (i_dark ? pinescript.color.hex("#f59e0b") : pinescript.color.hex("#d97706")); - let cNeut = (i_dark ? pinescript.color.hex("#737373") : pinescript.color.hex("#525252")); - let cPrim = (i_dark ? pinescript.color.hex("#3b82f6") : pinescript.color.hex("#2563eb")); - let cText = (i_dark ? pinescript.color.hex("#fafafa") : pinescript.color.hex("#0a0a0a")); - let cTblBg = (i_dark ? pinescript.color.hex("#171717") : pinescript.color.hex("#f5f5f5")); - let cHdrBg = (i_dark ? pinescript.color.hex("#262626") : pinescript.color.hex("#e5e5e5")); + var HORIZON = 60; + var SAMPLE_TOTAL = 756; + var MIN_OBS_STRICT = 10; + var MIN_OBS_ROBUST = 20; + var T_CRIT_95 = 2; + var T_CRIT_90 = 1.65; + var MIN_DRIFT_ECON = 0.03; + var MAX_DEV_SD = 1.5; + var SIGN_Z_CRIT = 1.65; + var ANN_FACTOR = (252 / HORIZON); + var i_bgMode = pinescript.inputString("On", "Background", ({ options: ["Off", "On"], group: "Display", tooltip: "Background color based on drift classification" })); + var i_bgInt = pinescript.inputInt(75, "BG Intensity", ({ minval: 70, maxval: 95, group: "Display", tooltip: "Transparency of background color" })); + var i_barColor = pinescript.inputBool(true, "Bar Coloring", ({ group: "Display", tooltip: "Color bars based on drift direction" })); + var i_showDash = pinescript.inputBool(true, "Dashboard", ({ group: "Dashboard", tooltip: "Show statistical details panel" })); + var i_dashPos = pinescript.inputString("Top Right", "Position", ({ options: ["Top Right", "Top Left", "Bottom Right", "Bottom Left"], group: "Dashboard" })); + var i_dashSize = pinescript.inputString("Small", "Size", ({ options: ["Tiny", "Small", "Normal"], group: "Dashboard", tooltip: "Text size of dashboard" })); + var i_showWM = pinescript.inputBool(true, "Watermark", ({ group: "Watermark", tooltip: "Show ticker and classification summary" })); + var i_wmPos = pinescript.inputString("Bottom Right", "Position", ({ options: ["Top Right", "Top Left", "Bottom Right", "Bottom Left"], group: "Watermark" })); + var i_wmSize = pinescript.inputString("Normal", "Size", ({ options: ["Small", "Normal", "Large"], group: "Watermark", tooltip: "Text size of watermark" })); + var i_dark = pinescript.inputBool(true, "Dark Mode", ({ group: "Style", tooltip: "Optimize colors for dark charts" })); + var i_alerts = pinescript.inputBool(false, "Alerts", ({ group: "Alerts", tooltip: "Trigger alerts on classification changes" })); + var cBull = (i_dark ? pinescript.color.hex("#22c55e") : pinescript.color.hex("#16a34a")); + var cBear = (i_dark ? pinescript.color.hex("#ef4444") : pinescript.color.hex("#dc2626")); + var cWeak = (i_dark ? pinescript.color.hex("#f59e0b") : pinescript.color.hex("#d97706")); + var cNeut = (i_dark ? pinescript.color.hex("#737373") : pinescript.color.hex("#525252")); + var cPrim = (i_dark ? pinescript.color.hex("#3b82f6") : pinescript.color.hex("#2563eb")); + var cText = (i_dark ? pinescript.color.hex("#fafafa") : pinescript.color.hex("#0a0a0a")); + var cTblBg = (i_dark ? pinescript.color.hex("#171717") : pinescript.color.hex("#f5f5f5")); + var cHdrBg = (i_dark ? pinescript.color.hex("#262626") : pinescript.color.hex("#e5e5e5")); function f_pos(p) { return ((p === "Top Right") ? pinescript.position.top_right : ((p === "Top Left") ? pinescript.position.top_left : ((p === "Bottom Right") ? pinescript.position.bottom_right : pinescript.position.bottom_left))); } function f_size(s) { return ((s === "Tiny") ? pinescript.size.tiny : ((s === "Small") ? pinescript.size.small : ((s === "Normal") ? pinescript.size.normal : ((s === "Large") ? pinescript.size.large : pinescript.size.small)))); } - let dashSizeH = f_size(((i_dashSize === "Tiny") ? "Small" : i_dashSize)); - let dashSizeD = f_size(i_dashSize); - let dashSizeF = f_size(((i_dashSize === "Normal") ? "Small" : "Tiny")); - let wmSizeT = f_size(((i_wmSize === "Small") ? "Normal" : ((i_wmSize === "Normal") ? "Large" : "Large"))); - let wmSizeM = f_size(i_wmSize); - let wmSizeS = f_size(((i_wmSize === "Large") ? "Small" : "Tiny")); + var dashSizeH = f_size(((i_dashSize === "Tiny") ? "Small" : i_dashSize)); + var dashSizeD = f_size(i_dashSize); + var dashSizeF = f_size(((i_dashSize === "Normal") ? "Small" : "Tiny")); + var wmSizeT = f_size(((i_wmSize === "Small") ? "Normal" : ((i_wmSize === "Normal") ? "Large" : "Large"))); + var wmSizeM = f_size(i_wmSize); + var wmSizeS = f_size(((i_wmSize === "Large") ? "Small" : "Tiny")); function f_median(arr) { - let n = pinescript.arraySize(arr); + var n = pinescript.arraySize(arr); if ((n === 0)) { - 0; + return 0; } else { - let s = pinescript.arrayCopy(arr); + var s = pinescript.arrayCopy(arr); pinescript.arraySort(s); - let m = (n / 2); - (((n % 2) === 0) ? ((pinescript.arrayGet(s, (m - 1)) + pinescript.arrayGet(s, m)) / 2) : pinescript.arrayGet(s, m)); + var m = (n / 2); + return (((n % 2) === 0) ? ((pinescript.arrayGet(s, (m - 1)) + pinescript.arrayGet(s, m)) / 2) : pinescript.arrayGet(s, m)); } } function f_collectAllReturns(src, horizon, maxBars) { if (state.arr === undefined) state.arr = pinescript.arrayNew(0); pinescript.arrayClear(state.arr); - let nPeriods = pinescript.floor((maxBars / horizon)); + var nPeriods = pinescript.floor((maxBars / horizon)); for (let i = 0; i <= (nPeriods - 1); i++) { - let recentBar = (i * horizon); - let pastBar = ((i + 1) * horizon); + var recentBar = (i * horizon); + var pastBar = ((i + 1) * horizon); if ((pastBar <= maxBars)) { - let pRecent = pinescript.nz(pinescript.hist(0, src, recentBar), src); - let pPast = pinescript.nz(pinescript.hist(1, src, pastBar), src); + var pRecent = pinescript.nz(pinescript.hist(0, src, recentBar), src); + var pPast = pinescript.nz(pinescript.hist(1, src, pastBar), src); if (((pPast > 0) && (pRecent > 0))) { pinescript.arrayPush(state.arr, pinescript.log((pRecent / pPast))); } @@ -1758,55 +1917,55 @@ function main() { return state.arr; } function f_arrayStats(arr) { - n = pinescript.arraySize(state.arr); + var n = pinescript.arraySize(state.arr); if ((n < 3)) { - [0, 0, 0, 0, 0]; + return [0, 0, 0, 0, 0]; } else { - let mean = pinescript.arrayAvg(state.arr); - let med = f_median(state.arr); - let sumSq = 0; - let posCount = 0; + var mean = pinescript.arrayAvg(state.arr); + var med = f_median(state.arr); + var sumSq = 0; + var posCount = 0; for (let i = 0; i <= (n - 1); i++) { - let v = pinescript.arrayGet(state.arr, i); + var v = pinescript.arrayGet(state.arr, i); sumSq += ((v - mean) * (v - mean)); if ((v > 0)) { posCount += 1; } } - let sd = pinescript.sqrt((sumSq / (n - 1))); - [mean, med, sd, n, posCount]; + var sd = pinescript.sqrt((sumSq / (n - 1))); + return [mean, med, sd, n, posCount]; } } function f_hacSE(arr, mean) { - n = pinescript.arraySize(state.arr); + var n = pinescript.arraySize(state.arr); if ((n < 3)) { - 0; + return 0; } else { - let g0 = 0; + var g0 = 0; for (let i = 0; i <= (n - 1); i++) { - let d = (pinescript.arrayGet(state.arr, i) - mean); + var d = (pinescript.arrayGet(state.arr, i) - mean); g0 += (d * d); } g0 = (g0 / n); - let g1 = 0; + var g1 = 0; for (let i = 1; i <= (n - 1); i++) { - let di = (pinescript.arrayGet(state.arr, i) - mean); - let dj = (pinescript.arrayGet(state.arr, (i - 1)) - mean); + var di = (pinescript.arrayGet(state.arr, i) - mean); + var dj = (pinescript.arrayGet(state.arr, (i - 1)) - mean); g1 += (di * dj); } g1 = (g1 / n); - let hV = (g0 + g1); - pinescript.sqrt(pinescript.max((hV / n), 1e-14)); + var hV = (g0 + g1); + return pinescript.sqrt(pinescript.max((hV / n), 1e-14)); } } function f_splitArray(arr, isFirstHalf) { - n = pinescript.arraySize(state.arr); + var n = pinescript.arraySize(state.arr); if (state.result === undefined) state.result = pinescript.arrayNew(0); pinescript.arrayClear(state.result); if ((n < 2)) { - state.result; + return state.result; } else { - let halfN = pinescript.floor((n / 2)); + var halfN = pinescript.floor((n / 2)); if (isFirstHalf) { for (let i = 0; i <= (halfN - 1); i++) { pinescript.arrayPush(state.result, pinescript.arrayGet(state.arr, i)); @@ -1816,24 +1975,24 @@ function main() { pinescript.arrayPush(state.result, pinescript.arrayGet(state.arr, i)); } } - state.result; + return state.result; } } function f_vrTest(src, horizon, k, sampleBars) { - let arr1 = f_collectAllReturns(src, horizon, sampleBars); - let arrK = f_collectAllReturns(src, (horizon * k), sampleBars); - let [m1, md1, sd1, n1, pc1] = pinescript.unpack(f_arrayStats(arr1), 5); - let [mK, mdK, sdK, nK, pcK] = pinescript.unpack(f_arrayStats(arrK), 5); + var arr1 = f_collectAllReturns(src, horizon, sampleBars); + var arrK = f_collectAllReturns(src, (horizon * k), sampleBars); + var [m1, md1, sd1, n1, pc1] = pinescript.unpack(f_arrayStats(arr1), 5); + var [mK, mdK, sdK, nK, pcK] = pinescript.unpack(f_arrayStats(arrK), 5); if ((((sd1 < 1e-10) || (n1 < MIN_OBS_STRICT)) || (nK < 3))) { - [1, 0, false]; + return [1, 0, false]; } else { - let var1 = (sd1 * sd1); - let varK = (sdK * sdK); - let vr = (varK / (k * var1)); - let vrSE = pinescript.sqrt((((2 * ((2 * k) - 1)) * (k - 1)) / ((3 * k) * nK))); - let zVR = ((vrSE > 1e-10) ? (pinescript.abs((vr - 1)) / vrSE) : 0); - let vrSig = (zVR > T_CRIT_90); - [vr, zVR, vrSig]; + var var1 = (sd1 * sd1); + var varK = (sdK * sdK); + var vr = (varK / (k * var1)); + var vrSE = pinescript.sqrt((((2 * ((2 * k) - 1)) * (k - 1)) / ((3 * k) * nK))); + var zVR = ((vrSE > 1e-10) ? (pinescript.abs((vr - 1)) / vrSE) : 0); + var vrSig = (zVR > T_CRIT_90); + return [vr, zVR, vrSig]; } } function f_mde(sd, n, tCrit) { @@ -1842,42 +2001,42 @@ function main() { function f_signTestZ(posCount, n) { return ((n < MIN_OBS_STRICT) ? 0 : ((float(posCount) - (float(n) / 2)) / pinescript.sqrt((float(n) / 4)))); } - let allReturns = f_collectAllReturns(close, HORIZON, SAMPLE_TOTAL); - let [mF, mdF, sdF, nF, pcF] = pinescript.unpack(f_arrayStats(allReturns), 5); - let seF = f_hacSE(allReturns, mF); - let tF = ((seF > 1e-10) ? (mF / seF) : 0); - arr1 = f_splitArray(allReturns, true); - let arr2 = f_splitArray(allReturns, false); - [m1, md1, sd1, n1, pc1] = pinescript.unpack(f_arrayStats(arr1), 5); - let se1 = f_hacSE(arr1, m1); - let t1 = ((se1 > 1e-10) ? (m1 / se1) : 0); - let [m2, md2, sd2, n2, pc2] = pinescript.unpack(f_arrayStats(arr2), 5); - let se2 = f_hacSE(arr2, m2); - let t2 = ((se2 > 1e-10) ? (m2 / se2) : 0); - [vr, zVR, vrSig] = pinescript.unpack(f_vrTest(close, HORIZON, 5, SAMPLE_TOTAL), 3); - let mde95 = f_mde(sdF, nF, T_CRIT_95); - let annDrift = (mF * ANN_FACTOR); - let zSign = f_signTestZ(pcF, nF); - let hitRate = ((nF > 0) ? (float(pcF) / float(nF)) : 0.5); - let inferenceAllowed = (((nF >= MIN_OBS_STRICT) && (n1 >= pinescript.floor((MIN_OBS_STRICT / 2)))) && (n2 >= pinescript.floor((MIN_OBS_STRICT / 2)))); - let isRobust = (nF >= MIN_OBS_ROBUST); - let statSig = (inferenceAllowed && (pinescript.abs(tF) > T_CRIT_95)); - let econSig = (inferenceAllowed && (pinescript.abs(annDrift) >= MIN_DRIFT_ECON)); - let powerOK = (inferenceAllowed && (pinescript.na(mde95) ? false : (pinescript.abs(annDrift) >= mde95))); - let medianSig = (inferenceAllowed && (pinescript.abs(zSign) > SIGN_Z_CRIT)); - let medianDir = ((zSign > 0) ? 1 : ((zSign < 0) ? -1 : 0)); - let meanDir = ((mF > 0) ? 1 : ((mF < 0) ? -1 : 0)); - let signTestOK = (inferenceAllowed && (!medianSig || (medianDir === meanDir))); - let meanMedOK = (inferenceAllowed && ((((mF > 0) && (mdF > 0)) || ((mF < 0) && (mdF < 0))) || (pinescript.abs(mF) < 1e-10))); - let sameSignMean = (((m1 > 0) && (m2 > 0)) || ((m1 < 0) && (m2 < 0))); - let pooledSD = pinescript.sqrt((((sd1 * sd1) + (sd2 * sd2)) / 2)); - let levelDevSD = ((pooledSD > 1e-10) ? (pinescript.abs((m1 - m2)) / pooledSD) : 0); - let levelOK = (levelDevSD <= MAX_DEV_SD); - let sameSignT = ((((t1 > 0) && (t2 > 0)) && (tF > 0)) || (((t1 < 0) && (t2 < 0)) && (tF < 0))); - let structOK = (((inferenceAllowed && sameSignMean) && levelOK) && sameSignT); - let isMeanRev = ((vr < 1) && vrSig); - let vrOK = (inferenceAllowed && !isMeanRev); - let direction = ((mF > 0) ? 1 : ((mF < 0) ? -1 : 0)); + var allReturns = f_collectAllReturns(close, HORIZON, SAMPLE_TOTAL); + var [mF, mdF, sdF, nF, pcF] = pinescript.unpack(f_arrayStats(allReturns), 5); + var seF = f_hacSE(allReturns, mF); + var tF = ((seF > 1e-10) ? (mF / seF) : 0); + var arr1 = f_splitArray(allReturns, true); + var arr2 = f_splitArray(allReturns, false); + var [m1, md1, sd1, n1, pc1] = pinescript.unpack(f_arrayStats(arr1), 5); + var se1 = f_hacSE(arr1, m1); + var t1 = ((se1 > 1e-10) ? (m1 / se1) : 0); + var [m2, md2, sd2, n2, pc2] = pinescript.unpack(f_arrayStats(arr2), 5); + var se2 = f_hacSE(arr2, m2); + var t2 = ((se2 > 1e-10) ? (m2 / se2) : 0); + var [vr, zVR, vrSig] = pinescript.unpack(f_vrTest(close, HORIZON, 5, SAMPLE_TOTAL), 3); + var mde95 = f_mde(sdF, nF, T_CRIT_95); + var annDrift = (mF * ANN_FACTOR); + var zSign = f_signTestZ(pcF, nF); + var hitRate = ((nF > 0) ? (float(pcF) / float(nF)) : 0.5); + var inferenceAllowed = (((nF >= MIN_OBS_STRICT) && (n1 >= pinescript.floor((MIN_OBS_STRICT / 2)))) && (n2 >= pinescript.floor((MIN_OBS_STRICT / 2)))); + var isRobust = (nF >= MIN_OBS_ROBUST); + var statSig = (inferenceAllowed && (pinescript.abs(tF) > T_CRIT_95)); + var econSig = (inferenceAllowed && (pinescript.abs(annDrift) >= MIN_DRIFT_ECON)); + var powerOK = (inferenceAllowed && (pinescript.na(mde95) ? false : (pinescript.abs(annDrift) >= mde95))); + var medianSig = (inferenceAllowed && (pinescript.abs(zSign) > SIGN_Z_CRIT)); + var medianDir = ((zSign > 0) ? 1 : ((zSign < 0) ? -1 : 0)); + var meanDir = ((mF > 0) ? 1 : ((mF < 0) ? -1 : 0)); + var signTestOK = (inferenceAllowed && (!medianSig || (medianDir === meanDir))); + var meanMedOK = (inferenceAllowed && ((((mF > 0) && (mdF > 0)) || ((mF < 0) && (mdF < 0))) || (pinescript.abs(mF) < 1e-10))); + var sameSignMean = (((m1 > 0) && (m2 > 0)) || ((m1 < 0) && (m2 < 0))); + var pooledSD = pinescript.sqrt((((sd1 * sd1) + (sd2 * sd2)) / 2)); + var levelDevSD = ((pooledSD > 1e-10) ? (pinescript.abs((m1 - m2)) / pooledSD) : 0); + var levelOK = (levelDevSD <= MAX_DEV_SD); + var sameSignT = ((((t1 > 0) && (t2 > 0)) && (tF > 0)) || (((t1 < 0) && (t2 < 0)) && (tF < 0))); + var structOK = (((inferenceAllowed && sameSignMean) && levelOK) && sameSignT); + var isMeanRev = ((vr < 1) && vrSig); + var vrOK = (inferenceAllowed && !isMeanRev); + var direction = ((mF > 0) ? 1 : ((mF < 0) ? -1 : 0)); if (state.classReason === undefined) state.classReason = ""; if (state.classCode === undefined) state.classCode = 0; if (!inferenceAllowed) { @@ -1922,29 +2081,29 @@ function main() { } } } - let strongEvidence = (state.classCode === 2); - let weakEvidence = (state.classCode === 1); - let classification = ((state.classCode === 2) ? (direction * 2) : ((state.classCode === 1) ? direction : 0)); - let evidenceStr = (strongEvidence ? "STRONG" : (weakEvidence ? "WEAK" : "NONE")); - let biasStr = ((classification === 2) ? "Positive Drift" : ((classification === -2) ? "Negative Drift" : ((classification === 1) ? "Positive (weak)" : ((classification === -1) ? "Negative (weak)" : "No Drift")))); - let regimeStr = (isMeanRev ? "Mean-Rev" : "Neutral"); - let bgClr = null; + var strongEvidence = (state.classCode === 2); + var weakEvidence = (state.classCode === 1); + var classification = ((state.classCode === 2) ? (direction * 2) : ((state.classCode === 1) ? direction : 0)); + var evidenceStr = (strongEvidence ? "STRONG" : (weakEvidence ? "WEAK" : "NONE")); + var biasStr = ((classification === 2) ? "Positive Drift" : ((classification === -2) ? "Negative Drift" : ((classification === 1) ? "Positive (weak)" : ((classification === -1) ? "Negative (weak)" : "No Drift")))); + var regimeStr = (isMeanRev ? "Mean-Rev" : "Neutral"); + var bgClr = null; if ((i_bgMode === "On")) { bgClr = ((classification >= 2) ? cBull : ((classification <= -2) ? cBear : ((pinescript.abs(classification) === 1) ? cWeak : null))); } pinescript.bgcolor(((i_bgMode === "On") ? pinescript.color.new(bgClr, i_bgInt) : null)); - let cBarLong = pinescript.color.hex("#22c55e"); - let cBarShort = pinescript.color.hex("#ef4444"); - let cBarNeut = pinescript.color.hex("#a3a3a3"); - let barClr = null; + var cBarLong = pinescript.color.hex("#22c55e"); + var cBarShort = pinescript.color.hex("#ef4444"); + var cBarNeut = pinescript.color.hex("#a3a3a3"); + var barClr = null; if (i_barColor) { barClr = ((classification >= 2) ? cBarLong : ((classification <= -2) ? cBarShort : ((pinescript.abs(classification) === 1) ? ((direction > 0) ? cBarLong : cBarShort) : cBarNeut))); } pinescript.barcolor(barClr); if (state.wm === undefined) state.wm = pinescript.table.new(f_pos(i_wmPos), 1, 5, ({ bgcolor: pinescript.color.new(pinescript.color.black, 100) })); if ((i_showWM && barstate.islast)) { - let evClr = (!inferenceAllowed ? cNeut : (strongEvidence ? cBull : (weakEvidence ? cWeak : cNeut))); - let wmClr = pinescript.color.new((i_dark ? pinescript.color.white : pinescript.color.black), 30); + var evClr = (!inferenceAllowed ? cNeut : (strongEvidence ? cBull : (weakEvidence ? cWeak : cNeut))); + var wmClr = pinescript.color.new((i_dark ? pinescript.color.white : pinescript.color.black), 30); pinescript.table.cell(state.wm, 0, 0, syminfo.ticker, ({ text_color: wmClr, text_size: wmSizeT, text_halign: pinescript.text.align_right })); pinescript.table.cell(state.wm, 0, 1, (((inferenceAllowed ? ("t=" + pinescript.strToString(tF, "#.##")) : ("n<" + pinescript.strToString(MIN_OBS_STRICT))) + " | n=") + pinescript.strToString(nF)), ({ text_color: wmClr, text_size: wmSizeS, text_halign: pinescript.text.align_right })); pinescript.table.cell(state.wm, 0, 2, (evidenceStr + ((state.classReason !== "") ? ((" (" + state.classReason) + ")") : "")), ({ text_color: evClr, text_size: wmSizeM, text_halign: pinescript.text.align_right })); @@ -1953,7 +2112,7 @@ function main() { } if ((i_showDash && barstate.islast)) { if (state.d === undefined) state.d = pinescript.table.new(f_pos(i_dashPos), 2, 18, ({ border_width: 1, bgcolor: pinescript.color.new(cTblBg, 80) })); - let hB = pinescript.color.new(cHdrBg, 20); + var hB = pinescript.color.new(cHdrBg, 20); evClr = (!inferenceAllowed ? cNeut : (strongEvidence ? cBull : (weakEvidence ? cWeak : cNeut))); pinescript.table.cell(state.d, 0, 0, "ADM", ({ text_color: cText, bgcolor: hB, text_size: dashSizeH })); pinescript.table.cell(state.d, 1, 0, "Retrospective", ({ text_color: pinescript.color.new(cText, 50), bgcolor: hB, text_size: dashSizeF })); @@ -1961,16 +2120,16 @@ function main() { pinescript.table.cell(state.d, 1, 1, biasStr, ({ text_color: evClr, bgcolor: pinescript.color.new(evClr, 80), text_size: dashSizeH })); pinescript.table.cell(state.d, 0, 2, "Evidence", ({ text_color: cText, bgcolor: pinescript.color.new(evClr, 90), text_size: dashSizeD })); pinescript.table.cell(state.d, 1, 2, (evidenceStr + ((state.classReason !== "") ? ((" (" + state.classReason) + ")") : "")), ({ text_color: evClr, bgcolor: pinescript.color.new(evClr, 90), text_size: dashSizeD })); - let infClr = (inferenceAllowed ? cBull : cBear); + var infClr = (inferenceAllowed ? cBull : cBear); pinescript.table.cell(state.d, 0, 3, "Inference", ({ text_color: cText, bgcolor: pinescript.color.new(infClr, 90), text_size: dashSizeD })); pinescript.table.cell(state.d, 1, 3, (inferenceAllowed ? (isRobust ? "Allowed" : "Heuristic") : "Blocked"), ({ text_color: infClr, bgcolor: pinescript.color.new(infClr, 90), text_size: dashSizeD })); - let tClr = (statSig ? cBull : cNeut); + var tClr = (statSig ? cBull : cNeut); pinescript.table.cell(state.d, 0, 4, ("t(full) n=" + pinescript.strToString(nF)), ({ text_color: cText, bgcolor: pinescript.color.new(tClr, 90), text_size: dashSizeD })); pinescript.table.cell(state.d, 1, 4, (pinescript.strToString(tF, "#.##") + (statSig ? "**" : "")), ({ text_color: tClr, bgcolor: pinescript.color.new(tClr, 90), text_size: dashSizeD })); - let t1Clr = ((t1 > 0) ? cBull : cBear); + var t1Clr = ((t1 > 0) ? cBull : cBear); pinescript.table.cell(state.d, 0, 5, ("t(H1) n=" + pinescript.strToString(n1)), ({ text_color: cText, bgcolor: pinescript.color.new(t1Clr, 90), text_size: dashSizeD })); pinescript.table.cell(state.d, 1, 5, pinescript.strToString(t1, "#.##"), ({ text_color: t1Clr, bgcolor: pinescript.color.new(t1Clr, 90), text_size: dashSizeD })); - let t2Clr = ((t2 > 0) ? cBull : cBear); + var t2Clr = ((t2 > 0) ? cBull : cBear); pinescript.table.cell(state.d, 0, 6, ("t(H2) n=" + pinescript.strToString(n2)), ({ text_color: cText, bgcolor: pinescript.color.new(t2Clr, 90), text_size: dashSizeD })); pinescript.table.cell(state.d, 1, 6, pinescript.strToString(t2, "#.##"), ({ text_color: t2Clr, bgcolor: pinescript.color.new(t2Clr, 90), text_size: dashSizeD })); pinescript.table.cell(state.d, 0, 7, "Power", ({ text_color: cText, bgcolor: pinescript.color.new((powerOK ? cBull : cBear), 90), text_size: dashSizeD })); @@ -1987,7 +2146,7 @@ function main() { pinescript.table.cell(state.d, 1, 12, (((pinescript.strToString(vr, "#.##") + " z=") + pinescript.strToString(zVR, "#.#")) + (vrSig ? "*" : "")), ({ text_color: (vrOK ? cBull : cBear), bgcolor: pinescript.color.new((vrOK ? cBull : cBear), 90), text_size: dashSizeD })); pinescript.table.cell(state.d, 0, 13, "Econ. Sig.", ({ text_color: cText, bgcolor: pinescript.color.new((econSig ? cBull : cWeak), 90), text_size: dashSizeD })); pinescript.table.cell(state.d, 1, 13, (econSig ? "Yes" : (("<" + pinescript.strToString((MIN_DRIFT_ECON * 100), "#")) + "%")), ({ text_color: (econSig ? cBull : cWeak), bgcolor: pinescript.color.new((econSig ? cBull : cWeak), 90), text_size: dashSizeD })); - let dClr = ((annDrift > 0) ? cBull : cBear); + var dClr = ((annDrift > 0) ? cBull : cBear); pinescript.table.cell(state.d, 0, 14, "Drift (ann.)", ({ text_color: cText, bgcolor: pinescript.color.new(dClr, 90), text_size: dashSizeD })); pinescript.table.cell(state.d, 1, 14, (pinescript.strToString((annDrift * 100), "#.#") + "%"), ({ text_color: dClr, bgcolor: pinescript.color.new(dClr, 90), text_size: dashSizeD })); pinescript.table.cell(state.d, 0, 15, "Regime", ({ text_color: cText, bgcolor: pinescript.color.new(cPrim, 90), text_size: dashSizeD })); @@ -1997,7 +2156,7 @@ function main() { pinescript.table.cell(state.d, 0, 17, "**p<.05 *p<.10", ({ text_color: pinescript.color.new(cText, 50), bgcolor: pinescript.color.new(cNeut, 95), text_size: dashSizeF })); pinescript.table.cell(state.d, 1, 17, (isRobust ? "" : "Heuristic"), ({ text_color: pinescript.color.new(cWeak, 30), bgcolor: pinescript.color.new(cNeut, 95), text_size: dashSizeF })); } - let changed = (classification !== pinescript.hist(2, classification, 1)); + var changed = (classification !== pinescript.hist(2, classification, 1)); if ((changed && i_alerts)) { pinescript.alert((((("ADM: " + biasStr) + " (") + evidenceStr) + ")"), alert.freq_once_per_bar); } @@ -2049,6 +2208,10 @@ function run(data, options = {}) { rt.__shapeIdx = 0; globalThis.bar_index = i; globalThis.last_bar_index = n - 1; + globalThis.time_tradingday = inTime[i]; + globalThis.time_close = inTime[i]; + globalThis.last_bar_time = inTime[n - 1]; + globalThis.timenow = inTime[n - 1]; globalThis.barstate = { isfirst: i === 0, islast: i === n - 1, isrealtime: false, ishistory: true, isconfirmed: true, isnew: true, islastconfirmedhistory: i === n - 1, diff --git a/converted/Automatic_Trendline_Metrify.js b/converted/Automatic_Trendline_Metrify.js index 9346205..029af0b 100644 --- a/converted/Automatic_Trendline_Metrify.js +++ b/converted/Automatic_Trendline_Metrify.js @@ -42,6 +42,80 @@ const pinescript = { if (value != null && typeof value[Symbol.iterator] === 'function') return Array.from(value); return new Array(count || 0).fill(null); }, + __decArr: function(arr) { + if (!Array.isArray(arr) || arr.__pineDecorated) return arr; + const self = this; + const def = (name, fn) => Object.defineProperty(arr, name, { value: fn, writable: true, configurable: true }); + Object.defineProperty(arr, '__pineDecorated', { value: true, configurable: true }); + def('get', (i) => (arr[i] === undefined ? null : arr[i])); + def('set', (i, v) => { arr[i] = v; return v; }); + def('size', () => arr.length); + def('clear', () => { arr.length = 0; }); + def('insert', (i, v) => { arr.splice(i, 0, v); }); + def('remove', (i) => arr.splice(i, 1)[0]); + def('contains', (v) => arr.includes(v)); + def('indexof', (v) => arr.indexOf(v)); + def('lastindexof', (v) => arr.lastIndexOf(v)); + def('first', () => (arr.length ? arr[0] : null)); + def('last', () => (arr.length ? arr[arr.length - 1] : null)); + def('sum', () => arr.reduce((a, b) => a + b, 0)); + def('avg', () => (arr.length ? arr.reduce((a, b) => a + b, 0) / arr.length : 0)); + def('min', () => (arr.length ? Math.min(...arr) : null)); + def('max', () => (arr.length ? Math.max(...arr) : null)); + def('range', () => (arr.length ? Math.max(...arr) - Math.min(...arr) : null)); + // Statistical methods that route to the array.* built-ins (these names are not + // native to JS arrays, so attaching them here is safe from recursion). + def('median', () => self.arrayMedian(arr)); + def('mode', () => self.arrayMode(arr)); + def('stdev', () => self.arrayStdev(arr)); + def('variance', () => self.arrayVariance(arr)); + def('covariance', (other) => self.arrayCovariance(arr, other)); + def('percentile_linear_interpolation', (p) => self.arrayPercentileLinearInterpolation(arr, p)); + def('percentile_nearest_rank', (p) => self.arrayPercentileNearestRank(arr, p)); + def('abs', () => self.__decArr(self.arrayAbs(arr))); + def('binary_search', (v) => self.arrayBinarySearch(arr, v)); + // Pine's array.sort takes an order string, not a comparator. Use the native + // sort via .call so we don't recurse through this overridden method. + def('sort', (order) => { Array.prototype.sort.call(arr, (a, b) => (order === 'descending' ? b - a : a - b)); return arr; }); + def('sort_indices', (order) => self.__decArr(arr.map((_, i) => i).sort((a, b) => (order === 'descending' ? arr[b] - arr[a] : arr[a] - arr[b])))); + // join/slice/reverse/concat/includes/fill already exist natively on Array with + // compatible semantics, so we deliberately leave them to the native methods. + return arr; + }, + __decMap: function(m) { + if (!(m instanceof Map) || m.__pineDecorated) return m; + const def = (name, fn) => Object.defineProperty(m, name, { value: fn, writable: true, configurable: true }); + Object.defineProperty(m, '__pineDecorated', { value: true, configurable: true }); + def('put', (k, v) => { m.set(k, v); return v; }); + def('contains', (k) => m.has(k)); + def('remove', (k) => m.delete(k)); + def('keys', () => Array.from(Map.prototype.keys.call(m))); + def('values', () => Array.from(Map.prototype.values.call(m))); + def('size_', () => m.size); + return m; + }, + __decMatrix: function(m) { + if (!m || typeof m !== 'object' || m.__pineDecorated) return m; + const self = this; + const def = (name, fn) => Object.defineProperty(m, name, { value: fn, writable: true, configurable: true }); + Object.defineProperty(m, '__pineDecorated', { value: true, configurable: true }); + def('get', (r, c) => self.matrixGet(m, r, c)); + def('set', (r, c, v) => self.matrixSet(m, r, c, v)); + def('rows_', () => m.rows); + def('columns', () => m.cols); + def('fill', (v) => self.matrixFill(m, v)); + return m; + }, + __decDraw: function(obj) { + if (!obj || typeof obj !== 'object') return obj; + const p = new Proxy(obj, { + get(t, k) { + if (k in t || typeof k === 'symbol') return t[k]; + return function() { return p; }; + }, + }); + return p; + }, alertcondition: function(condition, ...rest) { if (globalThis.__pineRuntime) { globalThis.__pineRuntime.alerts.push({ condition, args: rest }); @@ -49,6 +123,32 @@ const pinescript = { return null; }, barcolor: function(color) { return null; }, + plotchar: function(series, ...rest) { + const rt = globalThis.__pineRuntime; + if (!rt) return series; + const bar = rt.__barIndex | 0; + const ord = (rt.__shapeIdx = (rt.__shapeIdx | 0) + 1) - 1; + let key = 'char_' + ord; + for (const r of rest) { + if (typeof r === 'string') { key = r; break; } + if (r && typeof r === 'object' && r.title) { key = String(r.title); break; } + } + let s = rt.plotshapes[key]; + if (!s) s = rt.plotshapes[key] = { title: key, data: [] }; + s.data[bar] = this.__scalar(series); + return series; + }, + plotarrow: function(series, ...rest) { + const rt = globalThis.__pineRuntime; + if (!rt) return series; + const bar = rt.__barIndex | 0; + const ord = (rt.__plotIdx = (rt.__plotIdx | 0) + 1) - 1; + const key = 'arrow_' + ord; + let p = rt.plots[key]; + if (!p) p = rt.plots[key] = { title: key, data: [] }; + p.data[bar] = this.__scalar(series); + return series; + }, bgcolor: function(color, title, editable, showLast) { return null; }, @@ -470,6 +570,12 @@ const pinescript = { atan: function(value) { return Math.atan(value); }, + todegrees: function(radians) { + return radians * (180 / Math.PI); + }, + toradians: function(degrees) { + return degrees * (Math.PI / 180); + }, floor: function(value) { return Math.floor(value); }, @@ -641,7 +747,7 @@ const pinescript = { return series; }, lineNew: function(x1, y1, x2, y2, opts = {}) { - return { x1, y1, x2, y2, opts, _type: 'line' }; + return this.__decDraw({ x1, y1, x2, y2, opts, _type: 'line' }); }, lineDelete: function(l) { return null; @@ -661,7 +767,7 @@ const pinescript = { return point === 0 || point === 'y1' ? line.y1 : line.y2; }, labelNew: function(x, y, text = '', opts = {}) { - return { x, y, text, opts, _type: 'label' }; + return this.__decDraw({ x, y, text, opts, _type: 'label' }); }, labelDelete: function(l) { return null; @@ -676,7 +782,7 @@ const pinescript = { return label.text || ''; }, boxNew: function(left, top, right, bottom, opts = {}) { - return { left, top, right, bottom, opts, _type: 'box' }; + return this.__decDraw({ left, top, right, bottom, opts, _type: 'box' }); }, boxDelete: function(box) { return null; @@ -694,7 +800,7 @@ const pinescript = { return box; }, polylineNew: function(points, opts = {}) { - return { points: points || [], opts, _type: 'polyline' }; + return this.__decDraw({ points: points || [], opts, _type: 'polyline' }); }, polylineDelete: function(poly) { return null; @@ -765,7 +871,7 @@ const pinescript = { return { time: _time ?? null, price: _price ?? null }; }, mapNew: function() { - return new Map(); + return this.__decMap(new Map()); }, mapSize: function(m) { return m instanceof Map ? m.size : 0; @@ -800,7 +906,7 @@ const pinescript = { const r = Math.max(0, rows ?? 0); const c = Math.max(0, cols ?? 0); const data = Array.from({ length: r }, () => Array.from({ length: c }, () => initialValue)); - return { rows: r, cols: c, data }; + return this.__decMatrix({ rows: r, cols: c, data }); }, matrixRows: function(m) { return m?.rows ?? 0; @@ -923,11 +1029,62 @@ const pinescript = { } return null; }, + __jacobiEigen: function(m) { + if (!m || !Array.isArray(m.data)) return null; + const n = m.rows ?? 0; + if (n === 0 || n !== (m.cols ?? 0)) return null; + // Work on a copy so the input matrix is untouched. + const a = m.data.map(row => row.slice()); + const v = Array.from({ length: n }, (_, i) => Array.from({ length: n }, (_, j) => (i === j ? 1 : 0))); + for (let sweep = 0; sweep < 100; sweep++) { + let off = 0; + for (let p = 0; p < n; p++) for (let q = p + 1; q < n; q++) off += a[p][q] * a[p][q]; + if (off < 1e-20) break; + for (let p = 0; p < n; p++) { + for (let q = p + 1; q < n; q++) { + if (Math.abs(a[p][q]) < 1e-18) continue; + const theta = (a[q][q] - a[p][p]) / (2 * a[p][q]); + const t = Math.sign(theta || 1) / (Math.abs(theta) + Math.sqrt(theta * theta + 1)); + const cos = 1 / Math.sqrt(t * t + 1); + const sin = t * cos; + for (let k = 0; k < n; k++) { + const akp = a[k][p], akq = a[k][q]; + a[k][p] = cos * akp - sin * akq; + a[k][q] = sin * akp + cos * akq; + } + for (let k = 0; k < n; k++) { + const apk = a[p][k], aqk = a[q][k]; + a[p][k] = cos * apk - sin * aqk; + a[q][k] = sin * apk + cos * aqk; + } + for (let k = 0; k < n; k++) { + const vkp = v[k][p], vkq = v[k][q]; + v[k][p] = cos * vkp - sin * vkq; + v[k][q] = sin * vkp + cos * vkq; + } + } + } + } + // Sort eigenpairs by eigenvalue, descending. + const order = Array.from({ length: n }, (_, i) => i).sort((i, j) => a[j][j] - a[i][i]); + const values = order.map(i => a[i][i]); + const vectors = Array.from({ length: n }, (_, r) => order.map(c => v[r][c])); + return { values, vectors }; + }, + matrixEigenvalues: function(m) { + const e = this.__jacobiEigen(m); + return this.__decArr(e ? e.values : []); + }, + matrixEigenvectors: function(m) { + const e = this.__jacobiEigen(m); + if (!e) return this.__decMatrix({ rows: 0, cols: 0, data: [] }); + return this.__decMatrix({ rows: e.vectors.length, cols: e.vectors.length, data: e.vectors }); + }, requestSecurity: function(symbol, timeframe, expression) { return expression; }, arrayNew: function(initialSize = 0, initialValue = 0) { - return Array(initialSize).fill(initialValue); + return this.__decArr(Array(initialSize).fill(initialValue)); }, arraySize: function(arr) { return arr ? arr.length : 0; @@ -978,10 +1135,10 @@ const pinescript = { return arr; }, arraySlice: function(arr, startIndex = 0, endIndex = null) { - if (!arr) return []; + if (!arr) return this.__decArr([]); const start = Number(startIndex) || 0; const end = endIndex === null || endIndex === undefined ? arr.length : Number(endIndex) || 0; - return arr.slice(start, end); + return this.__decArr(arr.slice(start, end)); }, arraySort: function(arr, order = 'ascending') { if (arr) arr.sort((a, b) => order === 'ascending' ? a - b : b - a); @@ -1191,7 +1348,7 @@ const pinescript = { const info = { ticker: 'AAPL', tickerid: 'NASDAQ:AAPL', prefix: 'NASDAQ', root: 'AAPL', suffix: '' }; return info[type] || ''; }, - timenow: 1781574526661, + timenow: 1782783575948, barstate: "LAST", dividends: {}, splits: {}, @@ -1245,11 +1402,11 @@ const pinescript = { arrayConcat: function(arr1, arr2) { if (!arr1) return arr2 || []; if (!arr2) return arr1; - return arr1.concat(arr2); + return this.__decArr(arr1.concat(arr2)); }, arrayCopy: function(arr) { - if (!arr) return []; - return [...arr]; + if (!arr) return this.__decArr([]); + return this.__decArr([...arr]); }, arrayBinarySearch: function(arr, value) { if (!arr || arr.length === 0) return -1; @@ -1339,7 +1496,7 @@ globalThis.input.timeframe = globalThis.input.timeframe || ((defval) => defval); globalThis.array = globalThis.array || { - from: (...items) => items, + from: (...items) => pinescript.__decArr(items), size: (arr) => pinescript.arraySize(arr), @@ -1508,6 +1665,8 @@ globalThis.line = globalThis.line || __pineNS({ style_solid: "solid", style_dash globalThis.box = globalThis.box || __pineNS({}); +globalThis.color = globalThis.color || __pineNS(Object.assign(function(c) { return c; }, { new: function(c, t) { return c; }, rgb: function(r, g, b, t) { return "#rgb(" + [r, g, b].join(",") + ")"; }, from_gradient: function(v, lo, hi, c1, c2) { return c1; }, r: function() { return 0; }, g: function() { return 0; }, b: function() { return 0; }, t: function() { return 0; }, aqua: "#00BCD4", black: "#363A45", blue: "#2962FF", fuchsia: "#E040FB", gray: "#787B86", green: "#4CAF50", lime: "#00E676", maroon: "#880E4F", navy: "#311B92", olive: "#808000", orange: "#FF9800", purple: "#9C27B0", red: "#FF5252", silver: "#B2B5BE", teal: "#00897B", white: "#FFFFFF", yellow: "#FFEB3B" })); + globalThis.label = globalThis.label || __pineNS({ style_label_down: "label_down", style_label_up: "label_up", style_none: "none" }); globalThis.polyline = globalThis.polyline || __pineNS({}); @@ -1520,7 +1679,9 @@ globalThis.map = globalThis.map || __pineNS({}); globalThis.session = globalThis.session || __pineNS({ regular: "regular", extended: "extended" }); -globalThis.dayofweek = globalThis.dayofweek || __pineNS({ sunday: 1, monday: 2, tuesday: 3, wednesday: 4, thursday: 5, friday: 6, saturday: 7 }); +globalThis.ticker = globalThis.ticker || __pineNS({}); + +globalThis.dayofweek = globalThis.dayofweek || __pineNS(Object.assign(function(t) { return new Date(t != null ? t : (globalThis.time || 0)).getUTCDay() + 1; }, { sunday: 1, monday: 2, tuesday: 3, wednesday: 4, thursday: 5, friday: 6, saturday: 7 })); globalThis.timeframe = __pineNS(Object.assign(globalThis.timeframe || {}, { period: (globalThis.timeframe && globalThis.timeframe.period) || "D", isintraday: false, isdaily: true, multiplier: 1 })); @@ -1568,8 +1729,6 @@ globalThis.bool = globalThis.bool || function(x) { return Boolean(x); }; globalThis.string = globalThis.string || function(x) { return x == null ? null : String(x); }; -globalThis.dayofweek = globalThis.dayofweek || __pineNS({ sunday: 1, monday: 2, tuesday: 3, wednesday: 4, thursday: 5, friday: 6, saturday: 7 }); - globalThis.str = globalThis.str || __pineNS({}); pinescript.math = globalThis.math; @@ -1655,7 +1814,7 @@ pinescript.text = { align_center: "center" }; pinescript.table = { - new: function(position, columns, rows, opts) { return { position, columns, rows, opts: opts || {}, cells: [] }; }, + new: function(position, columns, rows, opts) { return pinescript.__decDraw({ position, columns, rows, opts: opts || {}, cells: [] }); }, cell: function(table, column, row, text, opts) { @@ -1689,48 +1848,48 @@ function main() { null; // Study: Automatic Trendline [Metrify] // Options: {"overlay":true,"max_bars_back":5000,"max_lines_count":20} - let GROUP_ENGINE = "Engine"; - let GROUP_FILTER = "Quality Filters"; - let GROUP_VISUAL = "Visual"; - let atrLenTooltip = "Controls how ATR is calculated.nnATR measures average recent candle movement (volatility).nHigher value = smoother, slower adaptation.nLower value = faster, more reactive adaptation."; - let breakTolTooltip = "How far price must move through a trendline before the script treats it as a real break.nHigher value reduces false breaks from small wicks/noise."; - let maxRelevanceTooltip = "Keeps selected trendlines close enough to current price.nnIf a candidate line is too far from current price (in ATR units), it is ignored.nLower value = more actionable nearby lines.nHigher value = allows farther/older lines."; - let pivotLen = pinescript.inputInt(10, "Pivot Length", ({ minval: 2, maxval: 120, group: GROUP_ENGINE })); - let lookbackBars = pinescript.inputInt(1000, "Lookback Bars", ({ minval: 150, maxval: 4000, group: GROUP_ENGINE })); - let atrLen = pinescript.inputInt(100, "ATR Length", ({ minval: 10, maxval: 500, group: GROUP_ENGINE, tooltip: atrLenTooltip })); - let maxViolations = pinescript.inputInt(2, "Max Violations", ({ minval: 0, maxval: 6, group: GROUP_FILTER })); - let touchTolATR = pinescript.inputFloat(0.3, "Touch Tolerance (ATR)", ({ step: 0.05, minval: 0.05, maxval: 2, group: GROUP_FILTER })); - let maxSlopeDeg = pinescript.inputFloat(6, "Max Slope Angle", ({ step: 1, minval: 1, maxval: 30, group: GROUP_FILTER })); - let maxRelevanceATR = pinescript.inputFloat(8, "Max Relevance Distance (ATR)", ({ step: 0.5, minval: 1, maxval: 30, group: GROUP_FILTER, tooltip: maxRelevanceTooltip })); - let extendBars = pinescript.inputInt(60, "Line Extend (Bars)", ({ minval: 10, maxval: 300, group: GROUP_VISUAL })); - let lineWidth = pinescript.inputInt(2, "Line Width", ({ minval: 1, maxval: 4, group: GROUP_VISUAL })); - let showFill = pinescript.inputBool(true, "Show Fill Between Lines", ({ group: GROUP_VISUAL })); - let supportColor = pinescript.inputColor(pinescript.color.new(pinescript.color.hex("#20A56C"), 0), "Support Color", ({ group: GROUP_VISUAL })); - let resistColor = pinescript.inputColor(pinescript.color.new(pinescript.color.hex("#C24545"), 0), "Resistance Color", ({ group: GROUP_VISUAL })); - let neutralColor = pinescript.inputColor(pinescript.color.new(pinescript.color.hex("#607D8B"), 0), "Fill Base Color", ({ group: GROUP_VISUAL })); - let FAST_LOAD_BUFFER_BARS = 100; - let BAR_SCAN_WINDOW = 250; - let BAR_SCAN_STEP = 5; - let minAnchorBars = 10; - let pivotMemory = 200; - let maxBarViolRatio = 1; - let minTouches = 2; - let breakTolATR = 0.6; + var GROUP_ENGINE = "Engine"; + var GROUP_FILTER = "Quality Filters"; + var GROUP_VISUAL = "Visual"; + var atrLenTooltip = "Controls how ATR is calculated.nnATR measures average recent candle movement (volatility).nHigher value = smoother, slower adaptation.nLower value = faster, more reactive adaptation."; + var breakTolTooltip = "How far price must move through a trendline before the script treats it as a real break.nHigher value reduces false breaks from small wicks/noise."; + var maxRelevanceTooltip = "Keeps selected trendlines close enough to current price.nnIf a candidate line is too far from current price (in ATR units), it is ignored.nLower value = more actionable nearby lines.nHigher value = allows farther/older lines."; + var pivotLen = pinescript.inputInt(10, "Pivot Length", ({ minval: 2, maxval: 120, group: GROUP_ENGINE })); + var lookbackBars = pinescript.inputInt(1000, "Lookback Bars", ({ minval: 150, maxval: 4000, group: GROUP_ENGINE })); + var atrLen = pinescript.inputInt(100, "ATR Length", ({ minval: 10, maxval: 500, group: GROUP_ENGINE, tooltip: atrLenTooltip })); + var maxViolations = pinescript.inputInt(2, "Max Violations", ({ minval: 0, maxval: 6, group: GROUP_FILTER })); + var touchTolATR = pinescript.inputFloat(0.3, "Touch Tolerance (ATR)", ({ step: 0.05, minval: 0.05, maxval: 2, group: GROUP_FILTER })); + var maxSlopeDeg = pinescript.inputFloat(6, "Max Slope Angle", ({ step: 1, minval: 1, maxval: 30, group: GROUP_FILTER })); + var maxRelevanceATR = pinescript.inputFloat(8, "Max Relevance Distance (ATR)", ({ step: 0.5, minval: 1, maxval: 30, group: GROUP_FILTER, tooltip: maxRelevanceTooltip })); + var extendBars = pinescript.inputInt(60, "Line Extend (Bars)", ({ minval: 10, maxval: 300, group: GROUP_VISUAL })); + var lineWidth = pinescript.inputInt(2, "Line Width", ({ minval: 1, maxval: 4, group: GROUP_VISUAL })); + var showFill = pinescript.inputBool(true, "Show Fill Between Lines", ({ group: GROUP_VISUAL })); + var supportColor = pinescript.inputColor(pinescript.color.new(pinescript.color.hex("#20A56C"), 0), "Support Color", ({ group: GROUP_VISUAL })); + var resistColor = pinescript.inputColor(pinescript.color.new(pinescript.color.hex("#C24545"), 0), "Resistance Color", ({ group: GROUP_VISUAL })); + var neutralColor = pinescript.inputColor(pinescript.color.new(pinescript.color.hex("#607D8B"), 0), "Fill Base Color", ({ group: GROUP_VISUAL })); + var FAST_LOAD_BUFFER_BARS = 100; + var BAR_SCAN_WINDOW = 250; + var BAR_SCAN_STEP = 5; + var minAnchorBars = 10; + var pivotMemory = 200; + var maxBarViolRatio = 1; + var minTouches = 2; + var breakTolATR = 0.6; function f_line_y(x1, y1, x2, y2, x) { - let dx = (x2 - x1); + var dx = (x2 - x1); return ((dx === 0) ? y2 : (y1 + (((y2 - y1) * (x - x1)) / dx))); } function f_store_pivot(idxArr, priceArr, idx, price, isLow, mergeBars, maxCount) { - let sz = pinescript.arraySize(idxArr); + var sz = pinescript.arraySize(idxArr); if ((sz === 0)) { pinescript.arrayUnshift(idxArr, idx); pinescript.arrayUnshift(priceArr, price); } else { - let latestIdx = pinescript.arrayGet(idxArr, 0); - let latestPrice = pinescript.arrayGet(priceArr, 0); + var latestIdx = pinescript.arrayGet(idxArr, 0); + var latestPrice = pinescript.arrayGet(priceArr, 0); if ((idx > latestIdx)) { if (((idx - latestIdx) <= mergeBars)) { - let replace = (isLow ? (price < latestPrice) : (price > latestPrice)); + var replace = (isLow ? (price < latestPrice) : (price > latestPrice)); if (replace) { pinescript.arraySet(idxArr, 0, idx); pinescript.arraySet(priceArr, 0, price); @@ -1748,7 +1907,7 @@ function main() { } function f_trim_lookback(idxArr, priceArr, lbBars) { while ((pinescript.arraySize(idxArr) > 0)) { - let oldestIdx = pinescript.arrayGet(idxArr, (pinescript.arraySize(idxArr) - 1)); + var oldestIdx = pinescript.arrayGet(idxArr, (pinescript.arraySize(idxArr) - 1)); if (((bar_index - oldestIdx) > lbBars)) { pinescript.arrayPop(idxArr); pinescript.arrayPop(priceArr); @@ -1759,60 +1918,60 @@ function main() { return 0; } function f_find_best_line(idxArr, priceArr, support, atrValue, lbBars, minSpan, needTouches, allowViol, touchTolMult, breakTolMult, maxDeg, maxRelAtrMult, barWindow, sampleStep, maxBarBreakRatio) { - let bestValid = false; - let bestX1 = null; - let bestY1 = null; - let bestX2 = null; - let bestY2 = null; - let bestScore = null; - let n = pinescript.arraySize(idxArr); - let safeAtr = pinescript.max(atrValue, syminfo.mintick); - let touchTol = (safeAtr * touchTolMult); - let breakTol = (safeAtr * breakTolMult); - let maxSlopeAtrPerBar = pinescript.tan(((maxDeg * pinescript.math.pi) / 180)); - let maxRelDist = (safeAtr * maxRelAtrMult); + var bestValid = false; + var bestX1 = null; + var bestY1 = null; + var bestX2 = null; + var bestY2 = null; + var bestScore = null; + var n = pinescript.arraySize(idxArr); + var safeAtr = pinescript.max(atrValue, syminfo.mintick); + var touchTol = (safeAtr * touchTolMult); + var breakTol = (safeAtr * breakTolMult); + var maxSlopeAtrPerBar = pinescript.tan(((maxDeg * pinescript.math.pi) / 180)); + var maxRelDist = (safeAtr * maxRelAtrMult); if ((n >= needTouches)) { - let localBest = -10000000000; + var localBest = -10000000000; for (let newer = 0; newer <= (n - 2); newer++) { - let p2Idx = pinescript.arrayGet(idxArr, newer); - let p2Price = pinescript.arrayGet(priceArr, newer); + var p2Idx = pinescript.arrayGet(idxArr, newer); + var p2Price = pinescript.arrayGet(priceArr, newer); if (((bar_index - p2Idx) > lbBars)) { break; } for (let older = (newer + 1); older <= (n - 1); older++) { - let p1Idx = pinescript.arrayGet(idxArr, older); - let p1Price = pinescript.arrayGet(priceArr, older); + var p1Idx = pinescript.arrayGet(idxArr, older); + var p1Price = pinescript.arrayGet(priceArr, older); if (((bar_index - p1Idx) > lbBars)) { break; } - let span = (p2Idx - p1Idx); + var span = (p2Idx - p1Idx); if ((span >= minSpan)) { - let slope = ((p2Price - p1Price) / span); - let slopeAtrPerBar = ((safeAtr > 0) ? (pinescript.abs(slope) / safeAtr) : 0); + var slope = ((p2Price - p1Price) / span); + var slopeAtrPerBar = ((safeAtr > 0) ? (pinescript.abs(slope) / safeAtr) : 0); if ((slopeAtrPerBar <= maxSlopeAtrPerBar)) { - let yNow = (p1Price + (slope * (bar_index - p1Idx))); + var yNow = (p1Price + (slope * (bar_index - p1Idx))); if ((pinescript.abs((close - yNow)) <= maxRelDist)) { - let touches = 0; - let violations = 0; - let errorSum = 0; - let touchedPoints = 0; - let eligible = (older + 1); + var touches = 0; + var violations = 0; + var errorSum = 0; + var touchedPoints = 0; + var eligible = (older + 1); for (let k = 0; k <= (eligible - 1); k++) { - let pIdx = pinescript.arrayGet(idxArr, k); - let pPrice = pinescript.arrayGet(priceArr, k); - let y = (p1Price + (slope * (pIdx - p1Idx))); - let dev = (pPrice - y); - let isTouch = (pinescript.abs(dev) <= touchTol); + var pIdx = pinescript.arrayGet(idxArr, k); + var pPrice = pinescript.arrayGet(priceArr, k); + var y = (p1Price + (slope * (pIdx - p1Idx))); + var dev = (pPrice - y); + var isTouch = (pinescript.abs(dev) <= touchTol); if (isTouch) { touches = (touches + 1); errorSum = (errorSum + pinescript.abs(dev)); touchedPoints = (touchedPoints + 1); } - let isViolation = (support ? (dev < -breakTol) : (dev > breakTol)); + var isViolation = (support ? (dev < -breakTol) : (dev > breakTol)); if (isViolation) { violations = (violations + 1); } - let remaining = ((eligible - 1) - k); + var remaining = ((eligible - 1) - k); if (((touches + remaining) < needTouches)) { break; } @@ -1820,37 +1979,37 @@ function main() { break; } } - let brokenNow = (support ? (close < (yNow - breakTol)) : (close > (yNow + breakTol))); + var brokenNow = (support ? (close < (yNow - breakTol)) : (close > (yNow + breakTol))); if (brokenNow) { violations = (violations + 2); } if (((touches >= needTouches) && (violations <= allowViol))) { - let avgErr = ((touchedPoints > 0) ? (errorSum / touchedPoints) : touchTol); - let recency = (1 - pinescript.min(((bar_index - p2Idx) / (lbBars * 1)), 1)); - let spanScore = pinescript.min((span / (lbBars * 1)), 1); - let tightness = (1 - pinescript.min((avgErr / touchTol), 1)); - let slopePenalty = ((maxSlopeAtrPerBar > 0) ? (slopeAtrPerBar / maxSlopeAtrPerBar) : 0); - let preScore = (touches * 2.8); + var avgErr = ((touchedPoints > 0) ? (errorSum / touchedPoints) : touchTol); + var recency = (1 - pinescript.min(((bar_index - p2Idx) / (lbBars * 1)), 1)); + var spanScore = pinescript.min((span / (lbBars * 1)), 1); + var tightness = (1 - pinescript.min((avgErr / touchTol), 1)); + var slopePenalty = ((maxSlopeAtrPerBar > 0) ? (slopeAtrPerBar / maxSlopeAtrPerBar) : 0); + var preScore = (touches * 2.8); preScore = (preScore + (recency * 1.2)); preScore = (preScore + (spanScore * 1)); preScore = (preScore + (tightness * 1.3)); preScore = (preScore - (violations * 2.2)); preScore = (preScore - (slopePenalty * 0.5)); if ((preScore > localBest)) { - let scanLen = pinescript.min(barWindow, (bar_index - p1Idx)); - let barSamples = 0; - let barBreaks = 0; - let maxSamples = ((scanLen / sampleStep) + 1); - let scanRejected = false; - let off = 0; + var scanLen = pinescript.min(barWindow, (bar_index - p1Idx)); + var barSamples = 0; + var barBreaks = 0; + var maxSamples = ((scanLen / sampleStep) + 1); + var scanRejected = false; + var off = 0; while ((off <= scanLen)) { - let yBar = (p1Price + (slope * ((bar_index - off) - p1Idx))); - let barBreak = (support ? (pinescript.hist(0, low, off) < (yBar - breakTol)) : (pinescript.hist(1, high, off) > (yBar + breakTol))); + var yBar = (p1Price + (slope * ((bar_index - off) - p1Idx))); + var barBreak = (support ? (pinescript.hist(0, low, off) < (yBar - breakTol)) : (pinescript.hist(1, high, off) > (yBar + breakTol))); barSamples = (barSamples + 1); if (barBreak) { barBreaks = (barBreaks + 1); } - let minPossibleRatio = (barBreaks / (maxSamples * 1)); + var minPossibleRatio = (barBreaks / (maxSamples * 1)); if ((minPossibleRatio > maxBarBreakRatio)) { scanRejected = true; break; @@ -1858,15 +2017,15 @@ function main() { off = (off + sampleStep); } if (!scanRejected) { - let barBreakRatio = ((barSamples > 0) ? (barBreaks / (barSamples * 1)) : 1); - let finalViol = violations; + var barBreakRatio = ((barSamples > 0) ? (barBreaks / (barSamples * 1)) : 1); + var finalViol = violations; if ((barBreakRatio > maxBarBreakRatio)) { finalViol = ((finalViol + allowViol) + 1); } else { finalViol = (finalViol + int(pinescript.floor((barBreakRatio * 2)))); } if ((finalViol <= allowViol)) { - let score = preScore; + var score = preScore; score = (score - ((finalViol - violations) * 2.2)); score = (score - (barBreakRatio * 2)); if ((score > localBest)) { @@ -1897,12 +2056,12 @@ function main() { if (state.supportLine === undefined) state.supportLine = pinescript.lineNew(null, null, null, null, ({ xloc: xloc.bar_index })); if (state.resistanceLine === undefined) state.resistanceLine = pinescript.lineNew(null, null, null, null, ({ xloc: xloc.bar_index })); if (state.channelFill === undefined) state.channelFill = linefill.new(state.supportLine, state.resistanceLine, pinescript.color.new(neutralColor, 95)); - let atrValue = pinescript.atr(atrLen); - let requiredHistoryBars = (((lookbackBars + BAR_SCAN_WINDOW) + (pivotLen * 3)) + FAST_LOAD_BUFFER_BARS); - let processStartBar = pinescript.max(0, (last_bar_index - requiredHistoryBars)); - let processEngine = (bar_index >= processStartBar); - let ph = ta.pivothigh(high, pivotLen, pivotLen); - let pl = ta.pivotlow(low, pivotLen, pivotLen); + var atrValue = pinescript.atr(atrLen); + var requiredHistoryBars = (((lookbackBars + BAR_SCAN_WINDOW) + (pivotLen * 3)) + FAST_LOAD_BUFFER_BARS); + var processStartBar = pinescript.max(0, (last_bar_index - requiredHistoryBars)); + var processEngine = (bar_index >= processStartBar); + var ph = pinescript.ta.pivothigh(high, pivotLen, pivotLen); + var pl = pinescript.ta.pivotlow(low, pivotLen, pivotLen); if ((processEngine && !pinescript.na(pl))) { f_store_pivot(state.lowPivotIdxs, state.lowPivotPrices, (bar_index - pivotLen), pl, true, pivotLen, pivotMemory); } @@ -1913,13 +2072,13 @@ function main() { f_trim_lookback(state.lowPivotIdxs, state.lowPivotPrices, lookbackBars); f_trim_lookback(state.highPivotIdxs, state.highPivotPrices, lookbackBars); } - let [supValid, supX1, supY1, supX2, supY2, supScore] = pinescript.unpack(f_find_best_line(state.lowPivotIdxs, state.lowPivotPrices, true, atrValue, lookbackBars, minAnchorBars, minTouches, maxViolations, touchTolATR, breakTolATR, maxSlopeDeg, maxRelevanceATR, BAR_SCAN_WINDOW, BAR_SCAN_STEP, maxBarViolRatio), 6); - let [resValid, resX1, resY1, resX2, resY2, resScore] = pinescript.unpack(f_find_best_line(state.highPivotIdxs, state.highPivotPrices, false, atrValue, lookbackBars, minAnchorBars, minTouches, maxViolations, touchTolATR, breakTolATR, maxSlopeDeg, maxRelevanceATR, BAR_SCAN_WINDOW, BAR_SCAN_STEP, maxBarViolRatio), 6); - let supRight = null; + var [supValid, supX1, supY1, supX2, supY2, supScore] = pinescript.unpack(f_find_best_line(state.lowPivotIdxs, state.lowPivotPrices, true, atrValue, lookbackBars, minAnchorBars, minTouches, maxViolations, touchTolATR, breakTolATR, maxSlopeDeg, maxRelevanceATR, BAR_SCAN_WINDOW, BAR_SCAN_STEP, maxBarViolRatio), 6); + var [resValid, resX1, resY1, resX2, resY2, resScore] = pinescript.unpack(f_find_best_line(state.highPivotIdxs, state.highPivotPrices, false, atrValue, lookbackBars, minAnchorBars, minTouches, maxViolations, touchTolATR, breakTolATR, maxSlopeDeg, maxRelevanceATR, BAR_SCAN_WINDOW, BAR_SCAN_STEP, maxBarViolRatio), 6); + var supRight = null; if (supValid) { supRight = f_line_y(supX1, supY1, supX2, supY2, (bar_index + extendBars)); } - let resRight = null; + var resRight = null; if (resValid) { resRight = f_line_y(resX1, resY1, resX2, resY2, (bar_index + extendBars)); } @@ -1946,10 +2105,10 @@ function main() { line.set_color(state.resistanceLine, pinescript.color.new(resistColor, 100)); } if (((showFill && supValid) && resValid)) { - let supNow = f_line_y(supX1, supY1, supX2, supY2, bar_index); - let resNow = f_line_y(resX1, resY1, resX2, resY2, bar_index); - let mid = ((supNow + resNow) * 0.5); - let fillColor = ((close >= mid) ? pinescript.color.new(supportColor, 92) : pinescript.color.new(resistColor, 92)); + var supNow = f_line_y(supX1, supY1, supX2, supY2, bar_index); + var resNow = f_line_y(resX1, resY1, resX2, resY2, bar_index); + var mid = ((supNow + resNow) * 0.5); + var fillColor = ((close >= mid) ? pinescript.color.new(supportColor, 92) : pinescript.color.new(resistColor, 92)); linefill.set_color(state.channelFill, fillColor); } else { linefill.set_color(state.channelFill, pinescript.color.new(neutralColor, 100)); @@ -1998,6 +2157,10 @@ function run(data, options = {}) { rt.__shapeIdx = 0; globalThis.bar_index = i; globalThis.last_bar_index = n - 1; + globalThis.time_tradingday = inTime[i]; + globalThis.time_close = inTime[i]; + globalThis.last_bar_time = inTime[n - 1]; + globalThis.timenow = inTime[n - 1]; globalThis.barstate = { isfirst: i === 0, islast: i === n - 1, isrealtime: false, ishistory: true, isconfirmed: true, isnew: true, islastconfirmedhistory: i === n - 1, diff --git a/converted/CE_1M_3M_Buy_Sell_Hold.js b/converted/CE_1M_3M_Buy_Sell_Hold.js index adfd82a..b83e6d7 100644 --- a/converted/CE_1M_3M_Buy_Sell_Hold.js +++ b/converted/CE_1M_3M_Buy_Sell_Hold.js @@ -42,6 +42,80 @@ const pinescript = { if (value != null && typeof value[Symbol.iterator] === 'function') return Array.from(value); return new Array(count || 0).fill(null); }, + __decArr: function(arr) { + if (!Array.isArray(arr) || arr.__pineDecorated) return arr; + const self = this; + const def = (name, fn) => Object.defineProperty(arr, name, { value: fn, writable: true, configurable: true }); + Object.defineProperty(arr, '__pineDecorated', { value: true, configurable: true }); + def('get', (i) => (arr[i] === undefined ? null : arr[i])); + def('set', (i, v) => { arr[i] = v; return v; }); + def('size', () => arr.length); + def('clear', () => { arr.length = 0; }); + def('insert', (i, v) => { arr.splice(i, 0, v); }); + def('remove', (i) => arr.splice(i, 1)[0]); + def('contains', (v) => arr.includes(v)); + def('indexof', (v) => arr.indexOf(v)); + def('lastindexof', (v) => arr.lastIndexOf(v)); + def('first', () => (arr.length ? arr[0] : null)); + def('last', () => (arr.length ? arr[arr.length - 1] : null)); + def('sum', () => arr.reduce((a, b) => a + b, 0)); + def('avg', () => (arr.length ? arr.reduce((a, b) => a + b, 0) / arr.length : 0)); + def('min', () => (arr.length ? Math.min(...arr) : null)); + def('max', () => (arr.length ? Math.max(...arr) : null)); + def('range', () => (arr.length ? Math.max(...arr) - Math.min(...arr) : null)); + // Statistical methods that route to the array.* built-ins (these names are not + // native to JS arrays, so attaching them here is safe from recursion). + def('median', () => self.arrayMedian(arr)); + def('mode', () => self.arrayMode(arr)); + def('stdev', () => self.arrayStdev(arr)); + def('variance', () => self.arrayVariance(arr)); + def('covariance', (other) => self.arrayCovariance(arr, other)); + def('percentile_linear_interpolation', (p) => self.arrayPercentileLinearInterpolation(arr, p)); + def('percentile_nearest_rank', (p) => self.arrayPercentileNearestRank(arr, p)); + def('abs', () => self.__decArr(self.arrayAbs(arr))); + def('binary_search', (v) => self.arrayBinarySearch(arr, v)); + // Pine's array.sort takes an order string, not a comparator. Use the native + // sort via .call so we don't recurse through this overridden method. + def('sort', (order) => { Array.prototype.sort.call(arr, (a, b) => (order === 'descending' ? b - a : a - b)); return arr; }); + def('sort_indices', (order) => self.__decArr(arr.map((_, i) => i).sort((a, b) => (order === 'descending' ? arr[b] - arr[a] : arr[a] - arr[b])))); + // join/slice/reverse/concat/includes/fill already exist natively on Array with + // compatible semantics, so we deliberately leave them to the native methods. + return arr; + }, + __decMap: function(m) { + if (!(m instanceof Map) || m.__pineDecorated) return m; + const def = (name, fn) => Object.defineProperty(m, name, { value: fn, writable: true, configurable: true }); + Object.defineProperty(m, '__pineDecorated', { value: true, configurable: true }); + def('put', (k, v) => { m.set(k, v); return v; }); + def('contains', (k) => m.has(k)); + def('remove', (k) => m.delete(k)); + def('keys', () => Array.from(Map.prototype.keys.call(m))); + def('values', () => Array.from(Map.prototype.values.call(m))); + def('size_', () => m.size); + return m; + }, + __decMatrix: function(m) { + if (!m || typeof m !== 'object' || m.__pineDecorated) return m; + const self = this; + const def = (name, fn) => Object.defineProperty(m, name, { value: fn, writable: true, configurable: true }); + Object.defineProperty(m, '__pineDecorated', { value: true, configurable: true }); + def('get', (r, c) => self.matrixGet(m, r, c)); + def('set', (r, c, v) => self.matrixSet(m, r, c, v)); + def('rows_', () => m.rows); + def('columns', () => m.cols); + def('fill', (v) => self.matrixFill(m, v)); + return m; + }, + __decDraw: function(obj) { + if (!obj || typeof obj !== 'object') return obj; + const p = new Proxy(obj, { + get(t, k) { + if (k in t || typeof k === 'symbol') return t[k]; + return function() { return p; }; + }, + }); + return p; + }, alertcondition: function(condition, ...rest) { if (globalThis.__pineRuntime) { globalThis.__pineRuntime.alerts.push({ condition, args: rest }); @@ -49,6 +123,32 @@ const pinescript = { return null; }, barcolor: function(color) { return null; }, + plotchar: function(series, ...rest) { + const rt = globalThis.__pineRuntime; + if (!rt) return series; + const bar = rt.__barIndex | 0; + const ord = (rt.__shapeIdx = (rt.__shapeIdx | 0) + 1) - 1; + let key = 'char_' + ord; + for (const r of rest) { + if (typeof r === 'string') { key = r; break; } + if (r && typeof r === 'object' && r.title) { key = String(r.title); break; } + } + let s = rt.plotshapes[key]; + if (!s) s = rt.plotshapes[key] = { title: key, data: [] }; + s.data[bar] = this.__scalar(series); + return series; + }, + plotarrow: function(series, ...rest) { + const rt = globalThis.__pineRuntime; + if (!rt) return series; + const bar = rt.__barIndex | 0; + const ord = (rt.__plotIdx = (rt.__plotIdx | 0) + 1) - 1; + const key = 'arrow_' + ord; + let p = rt.plots[key]; + if (!p) p = rt.plots[key] = { title: key, data: [] }; + p.data[bar] = this.__scalar(series); + return series; + }, bgcolor: function(color, title, editable, showLast) { return null; }, @@ -470,6 +570,12 @@ const pinescript = { atan: function(value) { return Math.atan(value); }, + todegrees: function(radians) { + return radians * (180 / Math.PI); + }, + toradians: function(degrees) { + return degrees * (Math.PI / 180); + }, floor: function(value) { return Math.floor(value); }, @@ -641,7 +747,7 @@ const pinescript = { return series; }, lineNew: function(x1, y1, x2, y2, opts = {}) { - return { x1, y1, x2, y2, opts, _type: 'line' }; + return this.__decDraw({ x1, y1, x2, y2, opts, _type: 'line' }); }, lineDelete: function(l) { return null; @@ -661,7 +767,7 @@ const pinescript = { return point === 0 || point === 'y1' ? line.y1 : line.y2; }, labelNew: function(x, y, text = '', opts = {}) { - return { x, y, text, opts, _type: 'label' }; + return this.__decDraw({ x, y, text, opts, _type: 'label' }); }, labelDelete: function(l) { return null; @@ -676,7 +782,7 @@ const pinescript = { return label.text || ''; }, boxNew: function(left, top, right, bottom, opts = {}) { - return { left, top, right, bottom, opts, _type: 'box' }; + return this.__decDraw({ left, top, right, bottom, opts, _type: 'box' }); }, boxDelete: function(box) { return null; @@ -694,7 +800,7 @@ const pinescript = { return box; }, polylineNew: function(points, opts = {}) { - return { points: points || [], opts, _type: 'polyline' }; + return this.__decDraw({ points: points || [], opts, _type: 'polyline' }); }, polylineDelete: function(poly) { return null; @@ -765,7 +871,7 @@ const pinescript = { return { time: _time ?? null, price: _price ?? null }; }, mapNew: function() { - return new Map(); + return this.__decMap(new Map()); }, mapSize: function(m) { return m instanceof Map ? m.size : 0; @@ -800,7 +906,7 @@ const pinescript = { const r = Math.max(0, rows ?? 0); const c = Math.max(0, cols ?? 0); const data = Array.from({ length: r }, () => Array.from({ length: c }, () => initialValue)); - return { rows: r, cols: c, data }; + return this.__decMatrix({ rows: r, cols: c, data }); }, matrixRows: function(m) { return m?.rows ?? 0; @@ -923,11 +1029,62 @@ const pinescript = { } return null; }, + __jacobiEigen: function(m) { + if (!m || !Array.isArray(m.data)) return null; + const n = m.rows ?? 0; + if (n === 0 || n !== (m.cols ?? 0)) return null; + // Work on a copy so the input matrix is untouched. + const a = m.data.map(row => row.slice()); + const v = Array.from({ length: n }, (_, i) => Array.from({ length: n }, (_, j) => (i === j ? 1 : 0))); + for (let sweep = 0; sweep < 100; sweep++) { + let off = 0; + for (let p = 0; p < n; p++) for (let q = p + 1; q < n; q++) off += a[p][q] * a[p][q]; + if (off < 1e-20) break; + for (let p = 0; p < n; p++) { + for (let q = p + 1; q < n; q++) { + if (Math.abs(a[p][q]) < 1e-18) continue; + const theta = (a[q][q] - a[p][p]) / (2 * a[p][q]); + const t = Math.sign(theta || 1) / (Math.abs(theta) + Math.sqrt(theta * theta + 1)); + const cos = 1 / Math.sqrt(t * t + 1); + const sin = t * cos; + for (let k = 0; k < n; k++) { + const akp = a[k][p], akq = a[k][q]; + a[k][p] = cos * akp - sin * akq; + a[k][q] = sin * akp + cos * akq; + } + for (let k = 0; k < n; k++) { + const apk = a[p][k], aqk = a[q][k]; + a[p][k] = cos * apk - sin * aqk; + a[q][k] = sin * apk + cos * aqk; + } + for (let k = 0; k < n; k++) { + const vkp = v[k][p], vkq = v[k][q]; + v[k][p] = cos * vkp - sin * vkq; + v[k][q] = sin * vkp + cos * vkq; + } + } + } + } + // Sort eigenpairs by eigenvalue, descending. + const order = Array.from({ length: n }, (_, i) => i).sort((i, j) => a[j][j] - a[i][i]); + const values = order.map(i => a[i][i]); + const vectors = Array.from({ length: n }, (_, r) => order.map(c => v[r][c])); + return { values, vectors }; + }, + matrixEigenvalues: function(m) { + const e = this.__jacobiEigen(m); + return this.__decArr(e ? e.values : []); + }, + matrixEigenvectors: function(m) { + const e = this.__jacobiEigen(m); + if (!e) return this.__decMatrix({ rows: 0, cols: 0, data: [] }); + return this.__decMatrix({ rows: e.vectors.length, cols: e.vectors.length, data: e.vectors }); + }, requestSecurity: function(symbol, timeframe, expression) { return expression; }, arrayNew: function(initialSize = 0, initialValue = 0) { - return Array(initialSize).fill(initialValue); + return this.__decArr(Array(initialSize).fill(initialValue)); }, arraySize: function(arr) { return arr ? arr.length : 0; @@ -978,10 +1135,10 @@ const pinescript = { return arr; }, arraySlice: function(arr, startIndex = 0, endIndex = null) { - if (!arr) return []; + if (!arr) return this.__decArr([]); const start = Number(startIndex) || 0; const end = endIndex === null || endIndex === undefined ? arr.length : Number(endIndex) || 0; - return arr.slice(start, end); + return this.__decArr(arr.slice(start, end)); }, arraySort: function(arr, order = 'ascending') { if (arr) arr.sort((a, b) => order === 'ascending' ? a - b : b - a); @@ -1191,7 +1348,7 @@ const pinescript = { const info = { ticker: 'AAPL', tickerid: 'NASDAQ:AAPL', prefix: 'NASDAQ', root: 'AAPL', suffix: '' }; return info[type] || ''; }, - timenow: 1781574526762, + timenow: 1782783576023, barstate: "LAST", dividends: {}, splits: {}, @@ -1245,11 +1402,11 @@ const pinescript = { arrayConcat: function(arr1, arr2) { if (!arr1) return arr2 || []; if (!arr2) return arr1; - return arr1.concat(arr2); + return this.__decArr(arr1.concat(arr2)); }, arrayCopy: function(arr) { - if (!arr) return []; - return [...arr]; + if (!arr) return this.__decArr([]); + return this.__decArr([...arr]); }, arrayBinarySearch: function(arr, value) { if (!arr || arr.length === 0) return -1; @@ -1339,7 +1496,7 @@ globalThis.input.timeframe = globalThis.input.timeframe || ((defval) => defval); globalThis.array = globalThis.array || { - from: (...items) => items, + from: (...items) => pinescript.__decArr(items), size: (arr) => pinescript.arraySize(arr), @@ -1508,6 +1665,8 @@ globalThis.line = globalThis.line || __pineNS({ style_solid: "solid", style_dash globalThis.box = globalThis.box || __pineNS({}); +globalThis.color = globalThis.color || __pineNS(Object.assign(function(c) { return c; }, { new: function(c, t) { return c; }, rgb: function(r, g, b, t) { return "#rgb(" + [r, g, b].join(",") + ")"; }, from_gradient: function(v, lo, hi, c1, c2) { return c1; }, r: function() { return 0; }, g: function() { return 0; }, b: function() { return 0; }, t: function() { return 0; }, aqua: "#00BCD4", black: "#363A45", blue: "#2962FF", fuchsia: "#E040FB", gray: "#787B86", green: "#4CAF50", lime: "#00E676", maroon: "#880E4F", navy: "#311B92", olive: "#808000", orange: "#FF9800", purple: "#9C27B0", red: "#FF5252", silver: "#B2B5BE", teal: "#00897B", white: "#FFFFFF", yellow: "#FFEB3B" })); + globalThis.label = globalThis.label || __pineNS({ style_label_down: "label_down", style_label_up: "label_up", style_none: "none" }); globalThis.polyline = globalThis.polyline || __pineNS({}); @@ -1520,7 +1679,9 @@ globalThis.map = globalThis.map || __pineNS({}); globalThis.session = globalThis.session || __pineNS({ regular: "regular", extended: "extended" }); -globalThis.dayofweek = globalThis.dayofweek || __pineNS({ sunday: 1, monday: 2, tuesday: 3, wednesday: 4, thursday: 5, friday: 6, saturday: 7 }); +globalThis.ticker = globalThis.ticker || __pineNS({}); + +globalThis.dayofweek = globalThis.dayofweek || __pineNS(Object.assign(function(t) { return new Date(t != null ? t : (globalThis.time || 0)).getUTCDay() + 1; }, { sunday: 1, monday: 2, tuesday: 3, wednesday: 4, thursday: 5, friday: 6, saturday: 7 })); globalThis.timeframe = __pineNS(Object.assign(globalThis.timeframe || {}, { period: (globalThis.timeframe && globalThis.timeframe.period) || "D", isintraday: false, isdaily: true, multiplier: 1 })); @@ -1568,8 +1729,6 @@ globalThis.bool = globalThis.bool || function(x) { return Boolean(x); }; globalThis.string = globalThis.string || function(x) { return x == null ? null : String(x); }; -globalThis.dayofweek = globalThis.dayofweek || __pineNS({ sunday: 1, monday: 2, tuesday: 3, wednesday: 4, thursday: 5, friday: 6, saturday: 7 }); - globalThis.str = globalThis.str || __pineNS({}); pinescript.math = globalThis.math; @@ -1655,7 +1814,7 @@ pinescript.text = { align_center: "center" }; pinescript.table = { - new: function(position, columns, rows, opts) { return { position, columns, rows, opts: opts || {}, cells: [] }; }, + new: function(position, columns, rows, opts) { return pinescript.__decDraw({ position, columns, rows, opts: opts || {}, cells: [] }); }, cell: function(table, column, row, text, opts) { @@ -1688,28 +1847,28 @@ function main() { null; // Study: CE 1M + 3M Buy, Sell, Hold // Options: {"overlay":true} - let length = 7; - let mult = 2; - let useClose = true; + var length = 7; + var mult = 2; + var useClose = true; function f_ce_dir(_length, _mult, _useClose) { - let atr = (_mult * pinescript.atr(_length)); - let longStop = ((_useClose ? pinescript.highest(pinescript.series(0, close), _length) : pinescript.highest(pinescript.series(1, high), _length)) - atr); - let longStopPrev = pinescript.nz(pinescript.hist(2, longStop, 1), longStop); + var atr = (_mult * pinescript.atr(_length)); + var longStop = ((_useClose ? pinescript.highest(pinescript.series(0, close), _length) : pinescript.highest(pinescript.series(1, high), _length)) - atr); + var longStopPrev = pinescript.nz(pinescript.hist(2, longStop, 1), longStop); longStop = ((pinescript.hist(3, close, 1) > longStopPrev) ? pinescript.max(longStop, longStopPrev) : longStop); - let shortStop = ((_useClose ? pinescript.lowest(pinescript.series(4, close), _length) : pinescript.lowest(pinescript.series(5, low), _length)) + atr); - let shortStopPrev = pinescript.nz(pinescript.hist(6, shortStop, 1), shortStop); + var shortStop = ((_useClose ? pinescript.lowest(pinescript.series(4, close), _length) : pinescript.lowest(pinescript.series(5, low), _length)) + atr); + var shortStopPrev = pinescript.nz(pinescript.hist(6, shortStop, 1), shortStop); shortStop = ((pinescript.hist(7, close, 1) < shortStopPrev) ? pinescript.min(shortStop, shortStopPrev) : shortStop); if (state.dir === undefined) state.dir = 1; state.dir = ((close > shortStopPrev) ? 1 : ((close < longStopPrev) ? -1 : state.dir)); return state.dir; } - let dir_1m = f_ce_dir(length, mult, useClose); - let dir_3m = pinescript.requestSecurity(syminfo.tickerid, "3", f_ce_dir(length, mult, useClose), barmerge.gaps_off, barmerge.lookahead_off); - let state_1 = 0; + var dir_1m = f_ce_dir(length, mult, useClose); + var dir_3m = pinescript.requestSecurity(syminfo.tickerid, "3", f_ce_dir(length, mult, useClose), barmerge.gaps_off, barmerge.lookahead_off); + var state_1 = 0; state_1 = (((dir_3m === 1) && (dir_1m === 1)) ? 1 : (((dir_3m === -1) && (dir_1m === -1)) ? -1 : 0)); - let buySignal = ((state_1 === 1) && (pinescript.hist(8, state_1, 1) !== 1)); - let sellSignal = ((state_1 === -1) && (pinescript.hist(9, state_1, 1) !== -1)); - let holdSignal = ((state_1 === 0) && (pinescript.hist(10, state_1, 1) !== 0)); + var buySignal = ((state_1 === 1) && (pinescript.hist(8, state_1, 1) !== 1)); + var sellSignal = ((state_1 === -1) && (pinescript.hist(9, state_1, 1) !== -1)); + var holdSignal = ((state_1 === 0) && (pinescript.hist(10, state_1, 1) !== 0)); pinescript.plotshape(buySignal, ({ title: "BUY", location: pinescript.location.belowbar, color: pinescript.color.green, style: pinescript.shape.labelup, text: "BUY" })); pinescript.plotshape(sellSignal, ({ title: "SELL", location: pinescript.location.abovebar, color: pinescript.color.red, style: pinescript.shape.labeldown, text: "SELL" })); pinescript.plotshape(holdSignal, ({ title: "HOLD", location: pinescript.location.bottom, color: pinescript.color.yellow, style: pinescript.shape.labelup, text: "HOLD" })); @@ -1757,6 +1916,10 @@ function run(data, options = {}) { rt.__shapeIdx = 0; globalThis.bar_index = i; globalThis.last_bar_index = n - 1; + globalThis.time_tradingday = inTime[i]; + globalThis.time_close = inTime[i]; + globalThis.last_bar_time = inTime[n - 1]; + globalThis.timenow = inTime[n - 1]; globalThis.barstate = { isfirst: i === 0, islast: i === n - 1, isrealtime: false, ishistory: true, isconfirmed: true, isnew: true, islastconfirmedhistory: i === n - 1, diff --git a/converted/Crossing_Moving_Averages_with_ADX_Filter.js b/converted/Crossing_Moving_Averages_with_ADX_Filter.js index 4b42adb..6b41ac8 100644 --- a/converted/Crossing_Moving_Averages_with_ADX_Filter.js +++ b/converted/Crossing_Moving_Averages_with_ADX_Filter.js @@ -42,6 +42,80 @@ const pinescript = { if (value != null && typeof value[Symbol.iterator] === 'function') return Array.from(value); return new Array(count || 0).fill(null); }, + __decArr: function(arr) { + if (!Array.isArray(arr) || arr.__pineDecorated) return arr; + const self = this; + const def = (name, fn) => Object.defineProperty(arr, name, { value: fn, writable: true, configurable: true }); + Object.defineProperty(arr, '__pineDecorated', { value: true, configurable: true }); + def('get', (i) => (arr[i] === undefined ? null : arr[i])); + def('set', (i, v) => { arr[i] = v; return v; }); + def('size', () => arr.length); + def('clear', () => { arr.length = 0; }); + def('insert', (i, v) => { arr.splice(i, 0, v); }); + def('remove', (i) => arr.splice(i, 1)[0]); + def('contains', (v) => arr.includes(v)); + def('indexof', (v) => arr.indexOf(v)); + def('lastindexof', (v) => arr.lastIndexOf(v)); + def('first', () => (arr.length ? arr[0] : null)); + def('last', () => (arr.length ? arr[arr.length - 1] : null)); + def('sum', () => arr.reduce((a, b) => a + b, 0)); + def('avg', () => (arr.length ? arr.reduce((a, b) => a + b, 0) / arr.length : 0)); + def('min', () => (arr.length ? Math.min(...arr) : null)); + def('max', () => (arr.length ? Math.max(...arr) : null)); + def('range', () => (arr.length ? Math.max(...arr) - Math.min(...arr) : null)); + // Statistical methods that route to the array.* built-ins (these names are not + // native to JS arrays, so attaching them here is safe from recursion). + def('median', () => self.arrayMedian(arr)); + def('mode', () => self.arrayMode(arr)); + def('stdev', () => self.arrayStdev(arr)); + def('variance', () => self.arrayVariance(arr)); + def('covariance', (other) => self.arrayCovariance(arr, other)); + def('percentile_linear_interpolation', (p) => self.arrayPercentileLinearInterpolation(arr, p)); + def('percentile_nearest_rank', (p) => self.arrayPercentileNearestRank(arr, p)); + def('abs', () => self.__decArr(self.arrayAbs(arr))); + def('binary_search', (v) => self.arrayBinarySearch(arr, v)); + // Pine's array.sort takes an order string, not a comparator. Use the native + // sort via .call so we don't recurse through this overridden method. + def('sort', (order) => { Array.prototype.sort.call(arr, (a, b) => (order === 'descending' ? b - a : a - b)); return arr; }); + def('sort_indices', (order) => self.__decArr(arr.map((_, i) => i).sort((a, b) => (order === 'descending' ? arr[b] - arr[a] : arr[a] - arr[b])))); + // join/slice/reverse/concat/includes/fill already exist natively on Array with + // compatible semantics, so we deliberately leave them to the native methods. + return arr; + }, + __decMap: function(m) { + if (!(m instanceof Map) || m.__pineDecorated) return m; + const def = (name, fn) => Object.defineProperty(m, name, { value: fn, writable: true, configurable: true }); + Object.defineProperty(m, '__pineDecorated', { value: true, configurable: true }); + def('put', (k, v) => { m.set(k, v); return v; }); + def('contains', (k) => m.has(k)); + def('remove', (k) => m.delete(k)); + def('keys', () => Array.from(Map.prototype.keys.call(m))); + def('values', () => Array.from(Map.prototype.values.call(m))); + def('size_', () => m.size); + return m; + }, + __decMatrix: function(m) { + if (!m || typeof m !== 'object' || m.__pineDecorated) return m; + const self = this; + const def = (name, fn) => Object.defineProperty(m, name, { value: fn, writable: true, configurable: true }); + Object.defineProperty(m, '__pineDecorated', { value: true, configurable: true }); + def('get', (r, c) => self.matrixGet(m, r, c)); + def('set', (r, c, v) => self.matrixSet(m, r, c, v)); + def('rows_', () => m.rows); + def('columns', () => m.cols); + def('fill', (v) => self.matrixFill(m, v)); + return m; + }, + __decDraw: function(obj) { + if (!obj || typeof obj !== 'object') return obj; + const p = new Proxy(obj, { + get(t, k) { + if (k in t || typeof k === 'symbol') return t[k]; + return function() { return p; }; + }, + }); + return p; + }, alertcondition: function(condition, ...rest) { if (globalThis.__pineRuntime) { globalThis.__pineRuntime.alerts.push({ condition, args: rest }); @@ -49,6 +123,32 @@ const pinescript = { return null; }, barcolor: function(color) { return null; }, + plotchar: function(series, ...rest) { + const rt = globalThis.__pineRuntime; + if (!rt) return series; + const bar = rt.__barIndex | 0; + const ord = (rt.__shapeIdx = (rt.__shapeIdx | 0) + 1) - 1; + let key = 'char_' + ord; + for (const r of rest) { + if (typeof r === 'string') { key = r; break; } + if (r && typeof r === 'object' && r.title) { key = String(r.title); break; } + } + let s = rt.plotshapes[key]; + if (!s) s = rt.plotshapes[key] = { title: key, data: [] }; + s.data[bar] = this.__scalar(series); + return series; + }, + plotarrow: function(series, ...rest) { + const rt = globalThis.__pineRuntime; + if (!rt) return series; + const bar = rt.__barIndex | 0; + const ord = (rt.__plotIdx = (rt.__plotIdx | 0) + 1) - 1; + const key = 'arrow_' + ord; + let p = rt.plots[key]; + if (!p) p = rt.plots[key] = { title: key, data: [] }; + p.data[bar] = this.__scalar(series); + return series; + }, bgcolor: function(color, title, editable, showLast) { return null; }, @@ -470,6 +570,12 @@ const pinescript = { atan: function(value) { return Math.atan(value); }, + todegrees: function(radians) { + return radians * (180 / Math.PI); + }, + toradians: function(degrees) { + return degrees * (Math.PI / 180); + }, floor: function(value) { return Math.floor(value); }, @@ -641,7 +747,7 @@ const pinescript = { return series; }, lineNew: function(x1, y1, x2, y2, opts = {}) { - return { x1, y1, x2, y2, opts, _type: 'line' }; + return this.__decDraw({ x1, y1, x2, y2, opts, _type: 'line' }); }, lineDelete: function(l) { return null; @@ -661,7 +767,7 @@ const pinescript = { return point === 0 || point === 'y1' ? line.y1 : line.y2; }, labelNew: function(x, y, text = '', opts = {}) { - return { x, y, text, opts, _type: 'label' }; + return this.__decDraw({ x, y, text, opts, _type: 'label' }); }, labelDelete: function(l) { return null; @@ -676,7 +782,7 @@ const pinescript = { return label.text || ''; }, boxNew: function(left, top, right, bottom, opts = {}) { - return { left, top, right, bottom, opts, _type: 'box' }; + return this.__decDraw({ left, top, right, bottom, opts, _type: 'box' }); }, boxDelete: function(box) { return null; @@ -694,7 +800,7 @@ const pinescript = { return box; }, polylineNew: function(points, opts = {}) { - return { points: points || [], opts, _type: 'polyline' }; + return this.__decDraw({ points: points || [], opts, _type: 'polyline' }); }, polylineDelete: function(poly) { return null; @@ -765,7 +871,7 @@ const pinescript = { return { time: _time ?? null, price: _price ?? null }; }, mapNew: function() { - return new Map(); + return this.__decMap(new Map()); }, mapSize: function(m) { return m instanceof Map ? m.size : 0; @@ -800,7 +906,7 @@ const pinescript = { const r = Math.max(0, rows ?? 0); const c = Math.max(0, cols ?? 0); const data = Array.from({ length: r }, () => Array.from({ length: c }, () => initialValue)); - return { rows: r, cols: c, data }; + return this.__decMatrix({ rows: r, cols: c, data }); }, matrixRows: function(m) { return m?.rows ?? 0; @@ -923,11 +1029,62 @@ const pinescript = { } return null; }, + __jacobiEigen: function(m) { + if (!m || !Array.isArray(m.data)) return null; + const n = m.rows ?? 0; + if (n === 0 || n !== (m.cols ?? 0)) return null; + // Work on a copy so the input matrix is untouched. + const a = m.data.map(row => row.slice()); + const v = Array.from({ length: n }, (_, i) => Array.from({ length: n }, (_, j) => (i === j ? 1 : 0))); + for (let sweep = 0; sweep < 100; sweep++) { + let off = 0; + for (let p = 0; p < n; p++) for (let q = p + 1; q < n; q++) off += a[p][q] * a[p][q]; + if (off < 1e-20) break; + for (let p = 0; p < n; p++) { + for (let q = p + 1; q < n; q++) { + if (Math.abs(a[p][q]) < 1e-18) continue; + const theta = (a[q][q] - a[p][p]) / (2 * a[p][q]); + const t = Math.sign(theta || 1) / (Math.abs(theta) + Math.sqrt(theta * theta + 1)); + const cos = 1 / Math.sqrt(t * t + 1); + const sin = t * cos; + for (let k = 0; k < n; k++) { + const akp = a[k][p], akq = a[k][q]; + a[k][p] = cos * akp - sin * akq; + a[k][q] = sin * akp + cos * akq; + } + for (let k = 0; k < n; k++) { + const apk = a[p][k], aqk = a[q][k]; + a[p][k] = cos * apk - sin * aqk; + a[q][k] = sin * apk + cos * aqk; + } + for (let k = 0; k < n; k++) { + const vkp = v[k][p], vkq = v[k][q]; + v[k][p] = cos * vkp - sin * vkq; + v[k][q] = sin * vkp + cos * vkq; + } + } + } + } + // Sort eigenpairs by eigenvalue, descending. + const order = Array.from({ length: n }, (_, i) => i).sort((i, j) => a[j][j] - a[i][i]); + const values = order.map(i => a[i][i]); + const vectors = Array.from({ length: n }, (_, r) => order.map(c => v[r][c])); + return { values, vectors }; + }, + matrixEigenvalues: function(m) { + const e = this.__jacobiEigen(m); + return this.__decArr(e ? e.values : []); + }, + matrixEigenvectors: function(m) { + const e = this.__jacobiEigen(m); + if (!e) return this.__decMatrix({ rows: 0, cols: 0, data: [] }); + return this.__decMatrix({ rows: e.vectors.length, cols: e.vectors.length, data: e.vectors }); + }, requestSecurity: function(symbol, timeframe, expression) { return expression; }, arrayNew: function(initialSize = 0, initialValue = 0) { - return Array(initialSize).fill(initialValue); + return this.__decArr(Array(initialSize).fill(initialValue)); }, arraySize: function(arr) { return arr ? arr.length : 0; @@ -978,10 +1135,10 @@ const pinescript = { return arr; }, arraySlice: function(arr, startIndex = 0, endIndex = null) { - if (!arr) return []; + if (!arr) return this.__decArr([]); const start = Number(startIndex) || 0; const end = endIndex === null || endIndex === undefined ? arr.length : Number(endIndex) || 0; - return arr.slice(start, end); + return this.__decArr(arr.slice(start, end)); }, arraySort: function(arr, order = 'ascending') { if (arr) arr.sort((a, b) => order === 'ascending' ? a - b : b - a); @@ -1191,7 +1348,7 @@ const pinescript = { const info = { ticker: 'AAPL', tickerid: 'NASDAQ:AAPL', prefix: 'NASDAQ', root: 'AAPL', suffix: '' }; return info[type] || ''; }, - timenow: 1781574526863, + timenow: 1782783576117, barstate: "LAST", dividends: {}, splits: {}, @@ -1245,11 +1402,11 @@ const pinescript = { arrayConcat: function(arr1, arr2) { if (!arr1) return arr2 || []; if (!arr2) return arr1; - return arr1.concat(arr2); + return this.__decArr(arr1.concat(arr2)); }, arrayCopy: function(arr) { - if (!arr) return []; - return [...arr]; + if (!arr) return this.__decArr([]); + return this.__decArr([...arr]); }, arrayBinarySearch: function(arr, value) { if (!arr || arr.length === 0) return -1; @@ -1339,7 +1496,7 @@ globalThis.input.timeframe = globalThis.input.timeframe || ((defval) => defval); globalThis.array = globalThis.array || { - from: (...items) => items, + from: (...items) => pinescript.__decArr(items), size: (arr) => pinescript.arraySize(arr), @@ -1508,6 +1665,8 @@ globalThis.line = globalThis.line || __pineNS({ style_solid: "solid", style_dash globalThis.box = globalThis.box || __pineNS({}); +globalThis.color = globalThis.color || __pineNS(Object.assign(function(c) { return c; }, { new: function(c, t) { return c; }, rgb: function(r, g, b, t) { return "#rgb(" + [r, g, b].join(",") + ")"; }, from_gradient: function(v, lo, hi, c1, c2) { return c1; }, r: function() { return 0; }, g: function() { return 0; }, b: function() { return 0; }, t: function() { return 0; }, aqua: "#00BCD4", black: "#363A45", blue: "#2962FF", fuchsia: "#E040FB", gray: "#787B86", green: "#4CAF50", lime: "#00E676", maroon: "#880E4F", navy: "#311B92", olive: "#808000", orange: "#FF9800", purple: "#9C27B0", red: "#FF5252", silver: "#B2B5BE", teal: "#00897B", white: "#FFFFFF", yellow: "#FFEB3B" })); + globalThis.label = globalThis.label || __pineNS({ style_label_down: "label_down", style_label_up: "label_up", style_none: "none" }); globalThis.polyline = globalThis.polyline || __pineNS({}); @@ -1520,7 +1679,9 @@ globalThis.map = globalThis.map || __pineNS({}); globalThis.session = globalThis.session || __pineNS({ regular: "regular", extended: "extended" }); -globalThis.dayofweek = globalThis.dayofweek || __pineNS({ sunday: 1, monday: 2, tuesday: 3, wednesday: 4, thursday: 5, friday: 6, saturday: 7 }); +globalThis.ticker = globalThis.ticker || __pineNS({}); + +globalThis.dayofweek = globalThis.dayofweek || __pineNS(Object.assign(function(t) { return new Date(t != null ? t : (globalThis.time || 0)).getUTCDay() + 1; }, { sunday: 1, monday: 2, tuesday: 3, wednesday: 4, thursday: 5, friday: 6, saturday: 7 })); globalThis.timeframe = __pineNS(Object.assign(globalThis.timeframe || {}, { period: (globalThis.timeframe && globalThis.timeframe.period) || "D", isintraday: false, isdaily: true, multiplier: 1 })); @@ -1568,8 +1729,6 @@ globalThis.bool = globalThis.bool || function(x) { return Boolean(x); }; globalThis.string = globalThis.string || function(x) { return x == null ? null : String(x); }; -globalThis.dayofweek = globalThis.dayofweek || __pineNS({ sunday: 1, monday: 2, tuesday: 3, wednesday: 4, thursday: 5, friday: 6, saturday: 7 }); - globalThis.str = globalThis.str || __pineNS({}); pinescript.math = globalThis.math; @@ -1655,7 +1814,7 @@ pinescript.text = { align_center: "center" }; pinescript.table = { - new: function(position, columns, rows, opts) { return { position, columns, rows, opts: opts || {}, cells: [] }; }, + new: function(position, columns, rows, opts) { return pinescript.__decDraw({ position, columns, rows, opts: opts || {}, cells: [] }); }, cell: function(table, column, row, text, opts) { @@ -1688,45 +1847,45 @@ function main() { null; // Strategy: Crossing Moving Averages with ADX Filter) // Options: {"overlay":true,"pyramiding":0} - let fastLen = pinescript.inputInt(20, ({ title: "Fast MA Length" })); - let slowLen = pinescript.inputInt(120, ({ title: "Slow MA Length" })); - let maType = pinescript.inputString("SMA", ({ title: "Moving Average Type", options: ["SMA", "EMA", "WMA", "LMA"] })); - let useADX = pinescript.inputBool(true, ({ title: "Enable ADX Filter" })); - let adxLen = pinescript.inputInt(14, ({ title: "ADX Length" })); - let adxThreshold = pinescript.inputFloat(20, ({ title: "ADX Threshold" })); + var fastLen = pinescript.inputInt(20, ({ title: "Fast MA Length" })); + var slowLen = pinescript.inputInt(120, ({ title: "Slow MA Length" })); + var maType = pinescript.inputString("SMA", ({ title: "Moving Average Type", options: ["SMA", "EMA", "WMA", "LMA"] })); + var useADX = pinescript.inputBool(true, ({ title: "Enable ADX Filter" })); + var adxLen = pinescript.inputInt(14, ({ title: "ADX Length" })); + var adxThreshold = pinescript.inputFloat(20, ({ title: "ADX Threshold" })); function ma(src, len) { - let out = null; + var out = null; if ((maType === "SMA")) { - out = pinescript.sma(src, len); + out = pinescript.sma(pinescript.series(0, src), len); } else { if ((maType === "EMA")) { - out = pinescript.ema(src, len); + out = pinescript.ema(pinescript.series(1, src), len); } else { if ((maType === "WMA")) { - out = pinescript.wma(src, len); + out = pinescript.wma(pinescript.series(2, src), len); } else { - out = pinescript.rma(src, len); + out = pinescript.rma(pinescript.series(3, src), len); } } } return out; } - let fastMA = ma(close, fastLen); - let slowMA = ma(close, slowLen); - let longBias = (fastMA > slowMA); - let shortBias = (fastMA < slowMA); - let upMove = (high - pinescript.hist(0, high, 1)); - let downMove = (pinescript.hist(1, low, 1) - low); - let plusDM = (((upMove > downMove) && (upMove > 0)) ? upMove : 0); - let minusDM = (((downMove > upMove) && (downMove > 0)) ? downMove : 0); - let trur = pinescript.rma(pinescript.tr(true), adxLen); - let plusDI = ((100 * pinescript.rma(plusDM, adxLen)) / trur); - let minusDI = ((100 * pinescript.rma(minusDM, adxLen)) / trur); - let dx = ((100 * pinescript.abs((plusDI - minusDI))) / (plusDI + minusDI)); - let adx = pinescript.rma(dx, adxLen); - let adxOK = (!useADX || (adx >= adxThreshold)); - let enterLong = ((longBias && adxOK) && (pinescript.strategy.position_size <= 0)); - let enterShort = ((shortBias && adxOK) && (pinescript.strategy.position_size >= 0)); + var fastMA = ma(close, fastLen); + var slowMA = ma(close, slowLen); + var longBias = (fastMA > slowMA); + var shortBias = (fastMA < slowMA); + var upMove = (high - pinescript.hist(4, high, 1)); + var downMove = (pinescript.hist(5, low, 1) - low); + var plusDM = (((upMove > downMove) && (upMove > 0)) ? upMove : 0); + var minusDM = (((downMove > upMove) && (downMove > 0)) ? downMove : 0); + var trur = pinescript.rma(pinescript.series(6, pinescript.tr(true)), adxLen); + var plusDI = ((100 * pinescript.rma(pinescript.series(7, plusDM), adxLen)) / trur); + var minusDI = ((100 * pinescript.rma(pinescript.series(8, minusDM), adxLen)) / trur); + var dx = ((100 * pinescript.abs((plusDI - minusDI))) / (plusDI + minusDI)); + var adx = pinescript.rma(pinescript.series(9, dx), adxLen); + var adxOK = (!useADX || (adx >= adxThreshold)); + var enterLong = ((longBias && adxOK) && (pinescript.strategy.position_size <= 0)); + var enterShort = ((shortBias && adxOK) && (pinescript.strategy.position_size >= 0)); if (enterLong) { pinescript.strategyEntry("Long", pinescript.strategy.long); } @@ -1779,6 +1938,10 @@ function run(data, options = {}) { rt.__shapeIdx = 0; globalThis.bar_index = i; globalThis.last_bar_index = n - 1; + globalThis.time_tradingday = inTime[i]; + globalThis.time_close = inTime[i]; + globalThis.last_bar_time = inTime[n - 1]; + globalThis.timenow = inTime[n - 1]; globalThis.barstate = { isfirst: i === 0, islast: i === n - 1, isrealtime: false, ishistory: true, isconfirmed: true, isnew: true, islastconfirmedhistory: i === n - 1, diff --git a/converted/Folded_RSI.js b/converted/Folded_RSI.js index 300ea19..aad1929 100644 --- a/converted/Folded_RSI.js +++ b/converted/Folded_RSI.js @@ -42,6 +42,80 @@ const pinescript = { if (value != null && typeof value[Symbol.iterator] === 'function') return Array.from(value); return new Array(count || 0).fill(null); }, + __decArr: function(arr) { + if (!Array.isArray(arr) || arr.__pineDecorated) return arr; + const self = this; + const def = (name, fn) => Object.defineProperty(arr, name, { value: fn, writable: true, configurable: true }); + Object.defineProperty(arr, '__pineDecorated', { value: true, configurable: true }); + def('get', (i) => (arr[i] === undefined ? null : arr[i])); + def('set', (i, v) => { arr[i] = v; return v; }); + def('size', () => arr.length); + def('clear', () => { arr.length = 0; }); + def('insert', (i, v) => { arr.splice(i, 0, v); }); + def('remove', (i) => arr.splice(i, 1)[0]); + def('contains', (v) => arr.includes(v)); + def('indexof', (v) => arr.indexOf(v)); + def('lastindexof', (v) => arr.lastIndexOf(v)); + def('first', () => (arr.length ? arr[0] : null)); + def('last', () => (arr.length ? arr[arr.length - 1] : null)); + def('sum', () => arr.reduce((a, b) => a + b, 0)); + def('avg', () => (arr.length ? arr.reduce((a, b) => a + b, 0) / arr.length : 0)); + def('min', () => (arr.length ? Math.min(...arr) : null)); + def('max', () => (arr.length ? Math.max(...arr) : null)); + def('range', () => (arr.length ? Math.max(...arr) - Math.min(...arr) : null)); + // Statistical methods that route to the array.* built-ins (these names are not + // native to JS arrays, so attaching them here is safe from recursion). + def('median', () => self.arrayMedian(arr)); + def('mode', () => self.arrayMode(arr)); + def('stdev', () => self.arrayStdev(arr)); + def('variance', () => self.arrayVariance(arr)); + def('covariance', (other) => self.arrayCovariance(arr, other)); + def('percentile_linear_interpolation', (p) => self.arrayPercentileLinearInterpolation(arr, p)); + def('percentile_nearest_rank', (p) => self.arrayPercentileNearestRank(arr, p)); + def('abs', () => self.__decArr(self.arrayAbs(arr))); + def('binary_search', (v) => self.arrayBinarySearch(arr, v)); + // Pine's array.sort takes an order string, not a comparator. Use the native + // sort via .call so we don't recurse through this overridden method. + def('sort', (order) => { Array.prototype.sort.call(arr, (a, b) => (order === 'descending' ? b - a : a - b)); return arr; }); + def('sort_indices', (order) => self.__decArr(arr.map((_, i) => i).sort((a, b) => (order === 'descending' ? arr[b] - arr[a] : arr[a] - arr[b])))); + // join/slice/reverse/concat/includes/fill already exist natively on Array with + // compatible semantics, so we deliberately leave them to the native methods. + return arr; + }, + __decMap: function(m) { + if (!(m instanceof Map) || m.__pineDecorated) return m; + const def = (name, fn) => Object.defineProperty(m, name, { value: fn, writable: true, configurable: true }); + Object.defineProperty(m, '__pineDecorated', { value: true, configurable: true }); + def('put', (k, v) => { m.set(k, v); return v; }); + def('contains', (k) => m.has(k)); + def('remove', (k) => m.delete(k)); + def('keys', () => Array.from(Map.prototype.keys.call(m))); + def('values', () => Array.from(Map.prototype.values.call(m))); + def('size_', () => m.size); + return m; + }, + __decMatrix: function(m) { + if (!m || typeof m !== 'object' || m.__pineDecorated) return m; + const self = this; + const def = (name, fn) => Object.defineProperty(m, name, { value: fn, writable: true, configurable: true }); + Object.defineProperty(m, '__pineDecorated', { value: true, configurable: true }); + def('get', (r, c) => self.matrixGet(m, r, c)); + def('set', (r, c, v) => self.matrixSet(m, r, c, v)); + def('rows_', () => m.rows); + def('columns', () => m.cols); + def('fill', (v) => self.matrixFill(m, v)); + return m; + }, + __decDraw: function(obj) { + if (!obj || typeof obj !== 'object') return obj; + const p = new Proxy(obj, { + get(t, k) { + if (k in t || typeof k === 'symbol') return t[k]; + return function() { return p; }; + }, + }); + return p; + }, alertcondition: function(condition, ...rest) { if (globalThis.__pineRuntime) { globalThis.__pineRuntime.alerts.push({ condition, args: rest }); @@ -49,6 +123,32 @@ const pinescript = { return null; }, barcolor: function(color) { return null; }, + plotchar: function(series, ...rest) { + const rt = globalThis.__pineRuntime; + if (!rt) return series; + const bar = rt.__barIndex | 0; + const ord = (rt.__shapeIdx = (rt.__shapeIdx | 0) + 1) - 1; + let key = 'char_' + ord; + for (const r of rest) { + if (typeof r === 'string') { key = r; break; } + if (r && typeof r === 'object' && r.title) { key = String(r.title); break; } + } + let s = rt.plotshapes[key]; + if (!s) s = rt.plotshapes[key] = { title: key, data: [] }; + s.data[bar] = this.__scalar(series); + return series; + }, + plotarrow: function(series, ...rest) { + const rt = globalThis.__pineRuntime; + if (!rt) return series; + const bar = rt.__barIndex | 0; + const ord = (rt.__plotIdx = (rt.__plotIdx | 0) + 1) - 1; + const key = 'arrow_' + ord; + let p = rt.plots[key]; + if (!p) p = rt.plots[key] = { title: key, data: [] }; + p.data[bar] = this.__scalar(series); + return series; + }, bgcolor: function(color, title, editable, showLast) { return null; }, @@ -470,6 +570,12 @@ const pinescript = { atan: function(value) { return Math.atan(value); }, + todegrees: function(radians) { + return radians * (180 / Math.PI); + }, + toradians: function(degrees) { + return degrees * (Math.PI / 180); + }, floor: function(value) { return Math.floor(value); }, @@ -641,7 +747,7 @@ const pinescript = { return series; }, lineNew: function(x1, y1, x2, y2, opts = {}) { - return { x1, y1, x2, y2, opts, _type: 'line' }; + return this.__decDraw({ x1, y1, x2, y2, opts, _type: 'line' }); }, lineDelete: function(l) { return null; @@ -661,7 +767,7 @@ const pinescript = { return point === 0 || point === 'y1' ? line.y1 : line.y2; }, labelNew: function(x, y, text = '', opts = {}) { - return { x, y, text, opts, _type: 'label' }; + return this.__decDraw({ x, y, text, opts, _type: 'label' }); }, labelDelete: function(l) { return null; @@ -676,7 +782,7 @@ const pinescript = { return label.text || ''; }, boxNew: function(left, top, right, bottom, opts = {}) { - return { left, top, right, bottom, opts, _type: 'box' }; + return this.__decDraw({ left, top, right, bottom, opts, _type: 'box' }); }, boxDelete: function(box) { return null; @@ -694,7 +800,7 @@ const pinescript = { return box; }, polylineNew: function(points, opts = {}) { - return { points: points || [], opts, _type: 'polyline' }; + return this.__decDraw({ points: points || [], opts, _type: 'polyline' }); }, polylineDelete: function(poly) { return null; @@ -765,7 +871,7 @@ const pinescript = { return { time: _time ?? null, price: _price ?? null }; }, mapNew: function() { - return new Map(); + return this.__decMap(new Map()); }, mapSize: function(m) { return m instanceof Map ? m.size : 0; @@ -800,7 +906,7 @@ const pinescript = { const r = Math.max(0, rows ?? 0); const c = Math.max(0, cols ?? 0); const data = Array.from({ length: r }, () => Array.from({ length: c }, () => initialValue)); - return { rows: r, cols: c, data }; + return this.__decMatrix({ rows: r, cols: c, data }); }, matrixRows: function(m) { return m?.rows ?? 0; @@ -923,11 +1029,62 @@ const pinescript = { } return null; }, + __jacobiEigen: function(m) { + if (!m || !Array.isArray(m.data)) return null; + const n = m.rows ?? 0; + if (n === 0 || n !== (m.cols ?? 0)) return null; + // Work on a copy so the input matrix is untouched. + const a = m.data.map(row => row.slice()); + const v = Array.from({ length: n }, (_, i) => Array.from({ length: n }, (_, j) => (i === j ? 1 : 0))); + for (let sweep = 0; sweep < 100; sweep++) { + let off = 0; + for (let p = 0; p < n; p++) for (let q = p + 1; q < n; q++) off += a[p][q] * a[p][q]; + if (off < 1e-20) break; + for (let p = 0; p < n; p++) { + for (let q = p + 1; q < n; q++) { + if (Math.abs(a[p][q]) < 1e-18) continue; + const theta = (a[q][q] - a[p][p]) / (2 * a[p][q]); + const t = Math.sign(theta || 1) / (Math.abs(theta) + Math.sqrt(theta * theta + 1)); + const cos = 1 / Math.sqrt(t * t + 1); + const sin = t * cos; + for (let k = 0; k < n; k++) { + const akp = a[k][p], akq = a[k][q]; + a[k][p] = cos * akp - sin * akq; + a[k][q] = sin * akp + cos * akq; + } + for (let k = 0; k < n; k++) { + const apk = a[p][k], aqk = a[q][k]; + a[p][k] = cos * apk - sin * aqk; + a[q][k] = sin * apk + cos * aqk; + } + for (let k = 0; k < n; k++) { + const vkp = v[k][p], vkq = v[k][q]; + v[k][p] = cos * vkp - sin * vkq; + v[k][q] = sin * vkp + cos * vkq; + } + } + } + } + // Sort eigenpairs by eigenvalue, descending. + const order = Array.from({ length: n }, (_, i) => i).sort((i, j) => a[j][j] - a[i][i]); + const values = order.map(i => a[i][i]); + const vectors = Array.from({ length: n }, (_, r) => order.map(c => v[r][c])); + return { values, vectors }; + }, + matrixEigenvalues: function(m) { + const e = this.__jacobiEigen(m); + return this.__decArr(e ? e.values : []); + }, + matrixEigenvectors: function(m) { + const e = this.__jacobiEigen(m); + if (!e) return this.__decMatrix({ rows: 0, cols: 0, data: [] }); + return this.__decMatrix({ rows: e.vectors.length, cols: e.vectors.length, data: e.vectors }); + }, requestSecurity: function(symbol, timeframe, expression) { return expression; }, arrayNew: function(initialSize = 0, initialValue = 0) { - return Array(initialSize).fill(initialValue); + return this.__decArr(Array(initialSize).fill(initialValue)); }, arraySize: function(arr) { return arr ? arr.length : 0; @@ -978,10 +1135,10 @@ const pinescript = { return arr; }, arraySlice: function(arr, startIndex = 0, endIndex = null) { - if (!arr) return []; + if (!arr) return this.__decArr([]); const start = Number(startIndex) || 0; const end = endIndex === null || endIndex === undefined ? arr.length : Number(endIndex) || 0; - return arr.slice(start, end); + return this.__decArr(arr.slice(start, end)); }, arraySort: function(arr, order = 'ascending') { if (arr) arr.sort((a, b) => order === 'ascending' ? a - b : b - a); @@ -1191,7 +1348,7 @@ const pinescript = { const info = { ticker: 'AAPL', tickerid: 'NASDAQ:AAPL', prefix: 'NASDAQ', root: 'AAPL', suffix: '' }; return info[type] || ''; }, - timenow: 1781574526959, + timenow: 1782783576198, barstate: "LAST", dividends: {}, splits: {}, @@ -1245,11 +1402,11 @@ const pinescript = { arrayConcat: function(arr1, arr2) { if (!arr1) return arr2 || []; if (!arr2) return arr1; - return arr1.concat(arr2); + return this.__decArr(arr1.concat(arr2)); }, arrayCopy: function(arr) { - if (!arr) return []; - return [...arr]; + if (!arr) return this.__decArr([]); + return this.__decArr([...arr]); }, arrayBinarySearch: function(arr, value) { if (!arr || arr.length === 0) return -1; @@ -1339,7 +1496,7 @@ globalThis.input.timeframe = globalThis.input.timeframe || ((defval) => defval); globalThis.array = globalThis.array || { - from: (...items) => items, + from: (...items) => pinescript.__decArr(items), size: (arr) => pinescript.arraySize(arr), @@ -1508,6 +1665,8 @@ globalThis.line = globalThis.line || __pineNS({ style_solid: "solid", style_dash globalThis.box = globalThis.box || __pineNS({}); +globalThis.color = globalThis.color || __pineNS(Object.assign(function(c) { return c; }, { new: function(c, t) { return c; }, rgb: function(r, g, b, t) { return "#rgb(" + [r, g, b].join(",") + ")"; }, from_gradient: function(v, lo, hi, c1, c2) { return c1; }, r: function() { return 0; }, g: function() { return 0; }, b: function() { return 0; }, t: function() { return 0; }, aqua: "#00BCD4", black: "#363A45", blue: "#2962FF", fuchsia: "#E040FB", gray: "#787B86", green: "#4CAF50", lime: "#00E676", maroon: "#880E4F", navy: "#311B92", olive: "#808000", orange: "#FF9800", purple: "#9C27B0", red: "#FF5252", silver: "#B2B5BE", teal: "#00897B", white: "#FFFFFF", yellow: "#FFEB3B" })); + globalThis.label = globalThis.label || __pineNS({ style_label_down: "label_down", style_label_up: "label_up", style_none: "none" }); globalThis.polyline = globalThis.polyline || __pineNS({}); @@ -1520,7 +1679,9 @@ globalThis.map = globalThis.map || __pineNS({}); globalThis.session = globalThis.session || __pineNS({ regular: "regular", extended: "extended" }); -globalThis.dayofweek = globalThis.dayofweek || __pineNS({ sunday: 1, monday: 2, tuesday: 3, wednesday: 4, thursday: 5, friday: 6, saturday: 7 }); +globalThis.ticker = globalThis.ticker || __pineNS({}); + +globalThis.dayofweek = globalThis.dayofweek || __pineNS(Object.assign(function(t) { return new Date(t != null ? t : (globalThis.time || 0)).getUTCDay() + 1; }, { sunday: 1, monday: 2, tuesday: 3, wednesday: 4, thursday: 5, friday: 6, saturday: 7 })); globalThis.timeframe = __pineNS(Object.assign(globalThis.timeframe || {}, { period: (globalThis.timeframe && globalThis.timeframe.period) || "D", isintraday: false, isdaily: true, multiplier: 1 })); @@ -1568,8 +1729,6 @@ globalThis.bool = globalThis.bool || function(x) { return Boolean(x); }; globalThis.string = globalThis.string || function(x) { return x == null ? null : String(x); }; -globalThis.dayofweek = globalThis.dayofweek || __pineNS({ sunday: 1, monday: 2, tuesday: 3, wednesday: 4, thursday: 5, friday: 6, saturday: 7 }); - globalThis.str = globalThis.str || __pineNS({}); pinescript.math = globalThis.math; @@ -1655,7 +1814,7 @@ pinescript.text = { align_center: "center" }; pinescript.table = { - new: function(position, columns, rows, opts) { return { position, columns, rows, opts: opts || {}, cells: [] }; }, + new: function(position, columns, rows, opts) { return pinescript.__decDraw({ position, columns, rows, opts: opts || {}, cells: [] }); }, cell: function(table, column, row, text, opts) { @@ -1688,18 +1847,18 @@ function main() { null; // Study: Folded RSI (custom RSI, phase-invariant) // Options: {"shorttitle":"Folded RSI","overlay":false,"max_lines_count":500,"max_labels_count":500} - let grp_cycle = "Cycle / Correlation"; - let cycle_len = pinescript.inputInt(40, "Cycle Window (corr length)", ({ group: grp_cycle, minval: 10, maxval: 600, tooltip: "Bars used to estimate correlation with a pure sine/cos basis." })); - let target_period = pinescript.inputInt(20, "Target Sine Period (bars)", ({ group: grp_cycle, minval: 6, maxval: 300, tooltip: "Assumed dominant cycle length to correlate against." })); - let use_harmonic2 = pinescript.inputBool(true, "Also test 2× harmonic and keep the stronger", ({ group: grp_cycle })); - let grp_map = "Correlation → RSI Length Mapping"; - let len_min = pinescript.inputInt(5, "RSI Length Min", ({ group: grp_map, minval: 2, maxval: 300 })); - let len_max = pinescript.inputInt(33, "RSI Length Max", ({ group: grp_map, minval: 5, maxval: 600 })); - let map_power = pinescript.inputFloat(1, "Nonlinearity (power)", ({ group: grp_map, minval: 0.25, maxval: 4, tooltip: ">1 emphasizes strong correlations; <1 flattens the response." })); - let invert_map = pinescript.inputBool(false, "Invert mapping (strong cycle → longer length)", ({ group: grp_map })); - let grp_src = "Source / Smoothing"; - let src = pinescript.inputSource(close, "Price Source", ({ group: grp_src })); - let smooth_rsi = pinescript.inputInt(3, "Post Smoothing (EMA on RSI)", ({ group: grp_src, minval: 1, maxval: 50 })); + var grp_cycle = "Cycle / Correlation"; + var cycle_len = pinescript.inputInt(40, "Cycle Window (corr length)", ({ group: grp_cycle, minval: 10, maxval: 600, tooltip: "Bars used to estimate correlation with a pure sine/cos basis." })); + var target_period = pinescript.inputInt(20, "Target Sine Period (bars)", ({ group: grp_cycle, minval: 6, maxval: 300, tooltip: "Assumed dominant cycle length to correlate against." })); + var use_harmonic2 = pinescript.inputBool(true, "Also test 2× harmonic and keep the stronger", ({ group: grp_cycle })); + var grp_map = "Correlation → RSI Length Mapping"; + var len_min = pinescript.inputInt(5, "RSI Length Min", ({ group: grp_map, minval: 2, maxval: 300 })); + var len_max = pinescript.inputInt(33, "RSI Length Max", ({ group: grp_map, minval: 5, maxval: 600 })); + var map_power = pinescript.inputFloat(1, "Nonlinearity (power)", ({ group: grp_map, minval: 0.25, maxval: 4, tooltip: ">1 emphasizes strong correlations; <1 flattens the response." })); + var invert_map = pinescript.inputBool(false, "Invert mapping (strong cycle → longer length)", ({ group: grp_map })); + var grp_src = "Source / Smoothing"; + var src = pinescript.inputSource(close, "Price Source", ({ group: grp_src })); + var smooth_rsi = pinescript.inputInt(3, "Post Smoothing (EMA on RSI)", ({ group: grp_src, minval: 1, maxval: 50 })); function f_clamp_float(x, lo, hi) { return ((x < lo) ? lo : ((x > hi) ? hi : x)); } @@ -1714,68 +1873,68 @@ function main() { state.sumx2 = 0; state.sumy2 = 0; state.sumxy = 0; - let _win = pinescript.max(win, 2); - let _per = pinescript.max(period, 2); + var _win = pinescript.max(win, 2); + var _per = pinescript.max(period, 2); for (let i = 0; i <= (_win - 1); i++) { - let x = pinescript.hist(0, _src, i); - let t = (bar_index - i); - let ang = (((2 * pinescript.math.pi) * (t % _per)) / _per); - let y = (cosBasis ? pinescript.cos(ang) : pinescript.sin(ang)); + var x = pinescript.hist(0, _src, i); + var t = (bar_index - i); + var ang = (((2 * pinescript.math.pi) * (t % _per)) / _per); + var y = (cosBasis ? pinescript.cos(ang) : pinescript.sin(ang)); state.sumx = x; state.sumy = y; state.sumx2 = (x * x); state.sumy2 = (y * y); state.sumxy = (x * y); } - let n = _win; - let num = ((n * state.sumxy) - (state.sumx * state.sumy)); - let denL = ((n * state.sumx2) - (state.sumx * state.sumx)); - let denR = ((n * state.sumy2) - (state.sumy * state.sumy)); - let den = pinescript.sqrt((pinescript.max(denL, 0) * pinescript.max(denR, 0))); - let corr = ((den === 0) ? 0 : f_clamp_float((num / den), -1, 1)); + var n = _win; + var num = ((n * state.sumxy) - (state.sumx * state.sumy)); + var denL = ((n * state.sumx2) - (state.sumx * state.sumx)); + var denR = ((n * state.sumy2) - (state.sumy * state.sumy)); + var den = pinescript.sqrt((pinescript.max(denL, 0) * pinescript.max(denR, 0))); + var corr = ((den === 0) ? 0 : f_clamp_float((num / den), -1, 1)); return corr; } function f_corr_mag_phase_free(_src, win, period) { - let c_sin = f_corr_with_basis(_src, win, period, false); - let c_cos = f_corr_with_basis(_src, win, period, true); - let mag = pinescript.sqrt(((c_sin * c_sin) + (c_cos * c_cos))); + var c_sin = f_corr_with_basis(_src, win, period, false); + var c_cos = f_corr_with_basis(_src, win, period, true); + var mag = pinescript.sqrt(((c_sin * c_sin) + (c_cos * c_cos))); return f_clamp_float(mag, 0, 1); } function f_corr_to_len(mag, lenLo, lenHi, pow, invert) { - let m = pinescript.pow(mag, pow); - let w = (invert ? m : (1 - m)); - let L = (lenLo + ((lenHi - lenLo) * f_clamp_float(w, 0, 1))); + var m = pinescript.pow(mag, pow); + var w = (invert ? m : (1 - m)); + var L = (lenLo + ((lenHi - lenLo) * f_clamp_float(w, 0, 1))); return f_clamp_float(L, lenLo, lenHi); } function f_rsi_custom(_src, len_t) { - let chg = (_src - pinescript.hist(1, _src, 1)); - let up = pinescript.max(chg, 0); - let dn = pinescript.max(-chg, 0); - L = pinescript.max(len_t, 1); - let a = (1 / L); + var chg = (_src - pinescript.hist(1, _src, 1)); + var up = pinescript.max(chg, 0); + var dn = pinescript.max(-chg, 0); + var L = pinescript.max(len_t, 1); + var a = (1 / L); if (state.avgU === undefined) state.avgU = null; if (state.avgD === undefined) state.avgD = null; state.avgU = (pinescript.na(pinescript.hist(2, state.avgU, 1)) ? up : (((1 - a) * pinescript.hist(3, state.avgU, 1)) + (a * up))); state.avgD = (pinescript.na(pinescript.hist(4, state.avgD, 1)) ? dn : (((1 - a) * pinescript.hist(5, state.avgD, 1)) + (a * dn))); - let rs = ((state.avgD <= 1e-10) ? 1000000 : (state.avgU / state.avgD)); - let rsi = (100 - (100 / (1 + rs))); + var rs = ((state.avgD <= 1e-10) ? 1000000 : (state.avgU / state.avgD)); + var rsi = (100 - (100 / (1 + rs))); return f_clamp_float(rsi, 0, 100); } function f_ema_custom(x, len) { - L = pinescript.max(len, 1); - a = (2 / (L + 1)); + var L = pinescript.max(len, 1); + var a = (2 / (L + 1)); if (state.s === undefined) state.s = null; state.s = (pinescript.na(pinescript.hist(6, state.s, 1)) ? x : (((1 - a) * pinescript.hist(7, state.s, 1)) + (a * x))); return state.s; } - let ret = (pinescript.log(src) - pinescript.log(pinescript.hist(8, src, 1))); + var ret = (pinescript.log(src) - pinescript.log(pinescript.hist(8, src, 1))); ret = (pinescript.na(ret) ? 0 : ret); - let mag1 = f_corr_mag_phase_free(ret, cycle_len, target_period); - let mag2 = (use_harmonic2 ? f_corr_mag_phase_free(ret, cycle_len, (target_period * 2)) : 0); - mag = (use_harmonic2 ? pinescript.max(mag1, mag2) : mag1); - let len_now_f = f_corr_to_len(mag, len_min, len_max, map_power, invert_map); - let rsi_raw = f_rsi_custom(src, len_now_f); - let rsi_sm = ((smooth_rsi > 1) ? f_ema_custom(rsi_raw, smooth_rsi) : rsi_raw); + var mag1 = f_corr_mag_phase_free(ret, cycle_len, target_period); + var mag2 = (use_harmonic2 ? f_corr_mag_phase_free(ret, cycle_len, (target_period * 2)) : 0); + var mag = (use_harmonic2 ? pinescript.max(mag1, mag2) : mag1); + var len_now_f = f_corr_to_len(mag, len_min, len_max, map_power, invert_map); + var rsi_raw = f_rsi_custom(src, len_now_f); + var rsi_sm = ((smooth_rsi > 1) ? f_ema_custom(rsi_raw, smooth_rsi) : rsi_raw); pinescript.plot(rsi_sm, "Folded RSI", ({ linewidth: 2 })); pinescript.plot(rsi_raw, "Raw (no smooth)", ({ color: pinescript.color.new(pinescript.color.gray, 70) })); if (state.t === undefined) state.t = pinescript.table.new(({ position: pinescript.position.top_right, columns: 1, rows: 4, border_width: 1 })); @@ -1785,37 +1944,37 @@ function main() { pinescript.table.cell(state.t, 0, 2, ("Len Now: " + pinescript.strToString(len_now_f, "#.00"))); pinescript.table.cell(state.t, 0, 3, ("Best Period: " + pinescript.strToString(((use_harmonic2 && (mag2 > mag1)) ? (target_period * 2) : target_period)))); } - let ob_level = 70; - let os_level = 30; - let mid_level = 50; - let c_rsi_main = pinescript.color.new(pinescript.color.hex("#2196F3"), 0); - let c_rsi_ob = pinescript.color.new(pinescript.color.hex("#F44336"), 0); - let c_rsi_os = pinescript.color.new(pinescript.color.hex("#4CAF50"), 0); - let c_ob_line = pinescript.color.new(c_rsi_ob, 75); - let c_os_line = pinescript.color.new(c_rsi_os, 75); - let c_mid_line = pinescript.color.new(pinescript.color.gray, 85); - let c_ob_fill_s = pinescript.color.new(c_rsi_ob, 92); - let c_ob_fill_e = pinescript.color.new(c_rsi_ob, 70); - let c_os_fill_s = pinescript.color.new(c_rsi_os, 92); - let c_os_fill_e = pinescript.color.new(c_rsi_os, 70); - let hOb = pinescript.hline(ob_level, "Overbought", ({ color: c_ob_line, linestyle: hline.style_dashed, linewidth: 1 })); - let hOs = pinescript.hline(os_level, "Oversold", ({ color: c_os_line, linestyle: hline.style_dashed, linewidth: 1 })); - let h50 = pinescript.hline(mid_level, "Midline", ({ color: c_mid_line, linestyle: hline.style_dotted, linewidth: 1 })); - let rsi_val = f_clamp_float(rsi_sm, 0, 100); - let rsi_line_color = ((rsi_val > ob_level) ? pinescript.color.from_gradient(rsi_val, ob_level, 90, c_rsi_main, c_rsi_ob) : ((rsi_val < os_level) ? pinescript.color.from_gradient(rsi_val, 10, os_level, c_rsi_os, c_rsi_main) : c_rsi_main)); - let plot_rsi = pinescript.plot(rsi_sm, "Folded RSI", ({ color: rsi_line_color, linewidth: 2 })); - let ob_fill_color = pinescript.color.from_gradient(rsi_val, ob_level, 100, c_ob_fill_s, c_ob_fill_e); - let os_fill_color = pinescript.color.from_gradient(rsi_val, 0, os_level, c_os_fill_e, c_os_fill_s); - let rsiPlot = pinescript.plot(rsi_raw, "RSI", ({ color: pinescript.color.hex("#7E57C2") })); - let rsismooth = pinescript.plot(rsi_sm, " RSI Smooth", ({ color: pinescript.color.fuchsia })); - let rsiUpperBand = pinescript.hline(70, "RSI Upper Band", ({ color: pinescript.color.hex("#787B86") })); - let midline = pinescript.hline(50, "RSI Middle Band", ({ color: pinescript.color.new(pinescript.color.hex("#787B86"), 50) })); - let rsiLowerBand = pinescript.hline(30, "RSI Lower Band", ({ color: pinescript.color.hex("#787B86") })); + var ob_level = 70; + var os_level = 30; + var mid_level = 50; + var c_rsi_main = pinescript.color.new(pinescript.color.hex("#2196F3"), 0); + var c_rsi_ob = pinescript.color.new(pinescript.color.hex("#F44336"), 0); + var c_rsi_os = pinescript.color.new(pinescript.color.hex("#4CAF50"), 0); + var c_ob_line = pinescript.color.new(c_rsi_ob, 75); + var c_os_line = pinescript.color.new(c_rsi_os, 75); + var c_mid_line = pinescript.color.new(pinescript.color.gray, 85); + var c_ob_fill_s = pinescript.color.new(c_rsi_ob, 92); + var c_ob_fill_e = pinescript.color.new(c_rsi_ob, 70); + var c_os_fill_s = pinescript.color.new(c_rsi_os, 92); + var c_os_fill_e = pinescript.color.new(c_rsi_os, 70); + var hOb = pinescript.hline(ob_level, "Overbought", ({ color: c_ob_line, linestyle: hline.style_dashed, linewidth: 1 })); + var hOs = pinescript.hline(os_level, "Oversold", ({ color: c_os_line, linestyle: hline.style_dashed, linewidth: 1 })); + var h50 = pinescript.hline(mid_level, "Midline", ({ color: c_mid_line, linestyle: hline.style_dotted, linewidth: 1 })); + var rsi_val = f_clamp_float(rsi_sm, 0, 100); + var rsi_line_color = ((rsi_val > ob_level) ? pinescript.color.from_gradient(rsi_val, ob_level, 90, c_rsi_main, c_rsi_ob) : ((rsi_val < os_level) ? pinescript.color.from_gradient(rsi_val, 10, os_level, c_rsi_os, c_rsi_main) : c_rsi_main)); + var plot_rsi = pinescript.plot(rsi_sm, "Folded RSI", ({ color: rsi_line_color, linewidth: 2 })); + var ob_fill_color = pinescript.color.from_gradient(rsi_val, ob_level, 100, c_ob_fill_s, c_ob_fill_e); + var os_fill_color = pinescript.color.from_gradient(rsi_val, 0, os_level, c_os_fill_e, c_os_fill_s); + var rsiPlot = pinescript.plot(rsi_raw, "RSI", ({ color: pinescript.color.hex("#7E57C2") })); + var rsismooth = pinescript.plot(rsi_sm, " RSI Smooth", ({ color: pinescript.color.fuchsia })); + var rsiUpperBand = pinescript.hline(70, "RSI Upper Band", ({ color: pinescript.color.hex("#787B86") })); + var midline = pinescript.hline(50, "RSI Middle Band", ({ color: pinescript.color.new(pinescript.color.hex("#787B86"), 50) })); + var rsiLowerBand = pinescript.hline(30, "RSI Lower Band", ({ color: pinescript.color.hex("#787B86") })); pinescript.fill(rsiUpperBand, rsiLowerBand, ({ color: pinescript.color.rgb(126, 87, 194, 90), title: "RSI Background Fill" })); - let midLinePlot = pinescript.plot(50, ({ color: null, editable: false, display: display.none })); + var midLinePlot = pinescript.plot(50, ({ color: null, editable: false, display: display.none })); pinescript.fill(rsiPlot, midLinePlot, 100, 70, ({ top_color: pinescript.color.new(pinescript.color.green, 0), bottom_color: pinescript.color.new(pinescript.color.green, 100), title: "Overbought Gradient Fill" })); pinescript.fill(rsiPlot, midLinePlot, 30, 0, ({ top_color: pinescript.color.new(pinescript.color.red, 100), bottom_color: pinescript.color.new(pinescript.color.red, 0), title: "Oversold Gradient Fill" })); - rsi = rsi_sm; + var rsi = rsi_sm; pinescript.plot(rsi, ({ title: "RSI", linewidth: 2 })); pinescript.hline(50, "Mid", ({ color: pinescript.color.new(pinescript.color.gray, 70) })); pinescript.plotshape((rsi > 70), "OB", pinescript.shape.labeldown, pinescript.location.top, pinescript.color.red, ({ size: pinescript.size.small })); @@ -1864,6 +2023,10 @@ function run(data, options = {}) { rt.__shapeIdx = 0; globalThis.bar_index = i; globalThis.last_bar_index = n - 1; + globalThis.time_tradingday = inTime[i]; + globalThis.time_close = inTime[i]; + globalThis.last_bar_time = inTime[n - 1]; + globalThis.timenow = inTime[n - 1]; globalThis.barstate = { isfirst: i === 0, islast: i === n - 1, isrealtime: false, ishistory: true, isconfirmed: true, isnew: true, islastconfirmedhistory: i === n - 1, diff --git a/converted/Golden_Pocket_Zones.js b/converted/Golden_Pocket_Zones.js index e2acaba..9aaad04 100644 --- a/converted/Golden_Pocket_Zones.js +++ b/converted/Golden_Pocket_Zones.js @@ -42,6 +42,80 @@ const pinescript = { if (value != null && typeof value[Symbol.iterator] === 'function') return Array.from(value); return new Array(count || 0).fill(null); }, + __decArr: function(arr) { + if (!Array.isArray(arr) || arr.__pineDecorated) return arr; + const self = this; + const def = (name, fn) => Object.defineProperty(arr, name, { value: fn, writable: true, configurable: true }); + Object.defineProperty(arr, '__pineDecorated', { value: true, configurable: true }); + def('get', (i) => (arr[i] === undefined ? null : arr[i])); + def('set', (i, v) => { arr[i] = v; return v; }); + def('size', () => arr.length); + def('clear', () => { arr.length = 0; }); + def('insert', (i, v) => { arr.splice(i, 0, v); }); + def('remove', (i) => arr.splice(i, 1)[0]); + def('contains', (v) => arr.includes(v)); + def('indexof', (v) => arr.indexOf(v)); + def('lastindexof', (v) => arr.lastIndexOf(v)); + def('first', () => (arr.length ? arr[0] : null)); + def('last', () => (arr.length ? arr[arr.length - 1] : null)); + def('sum', () => arr.reduce((a, b) => a + b, 0)); + def('avg', () => (arr.length ? arr.reduce((a, b) => a + b, 0) / arr.length : 0)); + def('min', () => (arr.length ? Math.min(...arr) : null)); + def('max', () => (arr.length ? Math.max(...arr) : null)); + def('range', () => (arr.length ? Math.max(...arr) - Math.min(...arr) : null)); + // Statistical methods that route to the array.* built-ins (these names are not + // native to JS arrays, so attaching them here is safe from recursion). + def('median', () => self.arrayMedian(arr)); + def('mode', () => self.arrayMode(arr)); + def('stdev', () => self.arrayStdev(arr)); + def('variance', () => self.arrayVariance(arr)); + def('covariance', (other) => self.arrayCovariance(arr, other)); + def('percentile_linear_interpolation', (p) => self.arrayPercentileLinearInterpolation(arr, p)); + def('percentile_nearest_rank', (p) => self.arrayPercentileNearestRank(arr, p)); + def('abs', () => self.__decArr(self.arrayAbs(arr))); + def('binary_search', (v) => self.arrayBinarySearch(arr, v)); + // Pine's array.sort takes an order string, not a comparator. Use the native + // sort via .call so we don't recurse through this overridden method. + def('sort', (order) => { Array.prototype.sort.call(arr, (a, b) => (order === 'descending' ? b - a : a - b)); return arr; }); + def('sort_indices', (order) => self.__decArr(arr.map((_, i) => i).sort((a, b) => (order === 'descending' ? arr[b] - arr[a] : arr[a] - arr[b])))); + // join/slice/reverse/concat/includes/fill already exist natively on Array with + // compatible semantics, so we deliberately leave them to the native methods. + return arr; + }, + __decMap: function(m) { + if (!(m instanceof Map) || m.__pineDecorated) return m; + const def = (name, fn) => Object.defineProperty(m, name, { value: fn, writable: true, configurable: true }); + Object.defineProperty(m, '__pineDecorated', { value: true, configurable: true }); + def('put', (k, v) => { m.set(k, v); return v; }); + def('contains', (k) => m.has(k)); + def('remove', (k) => m.delete(k)); + def('keys', () => Array.from(Map.prototype.keys.call(m))); + def('values', () => Array.from(Map.prototype.values.call(m))); + def('size_', () => m.size); + return m; + }, + __decMatrix: function(m) { + if (!m || typeof m !== 'object' || m.__pineDecorated) return m; + const self = this; + const def = (name, fn) => Object.defineProperty(m, name, { value: fn, writable: true, configurable: true }); + Object.defineProperty(m, '__pineDecorated', { value: true, configurable: true }); + def('get', (r, c) => self.matrixGet(m, r, c)); + def('set', (r, c, v) => self.matrixSet(m, r, c, v)); + def('rows_', () => m.rows); + def('columns', () => m.cols); + def('fill', (v) => self.matrixFill(m, v)); + return m; + }, + __decDraw: function(obj) { + if (!obj || typeof obj !== 'object') return obj; + const p = new Proxy(obj, { + get(t, k) { + if (k in t || typeof k === 'symbol') return t[k]; + return function() { return p; }; + }, + }); + return p; + }, alertcondition: function(condition, ...rest) { if (globalThis.__pineRuntime) { globalThis.__pineRuntime.alerts.push({ condition, args: rest }); @@ -49,6 +123,32 @@ const pinescript = { return null; }, barcolor: function(color) { return null; }, + plotchar: function(series, ...rest) { + const rt = globalThis.__pineRuntime; + if (!rt) return series; + const bar = rt.__barIndex | 0; + const ord = (rt.__shapeIdx = (rt.__shapeIdx | 0) + 1) - 1; + let key = 'char_' + ord; + for (const r of rest) { + if (typeof r === 'string') { key = r; break; } + if (r && typeof r === 'object' && r.title) { key = String(r.title); break; } + } + let s = rt.plotshapes[key]; + if (!s) s = rt.plotshapes[key] = { title: key, data: [] }; + s.data[bar] = this.__scalar(series); + return series; + }, + plotarrow: function(series, ...rest) { + const rt = globalThis.__pineRuntime; + if (!rt) return series; + const bar = rt.__barIndex | 0; + const ord = (rt.__plotIdx = (rt.__plotIdx | 0) + 1) - 1; + const key = 'arrow_' + ord; + let p = rt.plots[key]; + if (!p) p = rt.plots[key] = { title: key, data: [] }; + p.data[bar] = this.__scalar(series); + return series; + }, bgcolor: function(color, title, editable, showLast) { return null; }, @@ -470,6 +570,12 @@ const pinescript = { atan: function(value) { return Math.atan(value); }, + todegrees: function(radians) { + return radians * (180 / Math.PI); + }, + toradians: function(degrees) { + return degrees * (Math.PI / 180); + }, floor: function(value) { return Math.floor(value); }, @@ -641,7 +747,7 @@ const pinescript = { return series; }, lineNew: function(x1, y1, x2, y2, opts = {}) { - return { x1, y1, x2, y2, opts, _type: 'line' }; + return this.__decDraw({ x1, y1, x2, y2, opts, _type: 'line' }); }, lineDelete: function(l) { return null; @@ -661,7 +767,7 @@ const pinescript = { return point === 0 || point === 'y1' ? line.y1 : line.y2; }, labelNew: function(x, y, text = '', opts = {}) { - return { x, y, text, opts, _type: 'label' }; + return this.__decDraw({ x, y, text, opts, _type: 'label' }); }, labelDelete: function(l) { return null; @@ -676,7 +782,7 @@ const pinescript = { return label.text || ''; }, boxNew: function(left, top, right, bottom, opts = {}) { - return { left, top, right, bottom, opts, _type: 'box' }; + return this.__decDraw({ left, top, right, bottom, opts, _type: 'box' }); }, boxDelete: function(box) { return null; @@ -694,7 +800,7 @@ const pinescript = { return box; }, polylineNew: function(points, opts = {}) { - return { points: points || [], opts, _type: 'polyline' }; + return this.__decDraw({ points: points || [], opts, _type: 'polyline' }); }, polylineDelete: function(poly) { return null; @@ -765,7 +871,7 @@ const pinescript = { return { time: _time ?? null, price: _price ?? null }; }, mapNew: function() { - return new Map(); + return this.__decMap(new Map()); }, mapSize: function(m) { return m instanceof Map ? m.size : 0; @@ -800,7 +906,7 @@ const pinescript = { const r = Math.max(0, rows ?? 0); const c = Math.max(0, cols ?? 0); const data = Array.from({ length: r }, () => Array.from({ length: c }, () => initialValue)); - return { rows: r, cols: c, data }; + return this.__decMatrix({ rows: r, cols: c, data }); }, matrixRows: function(m) { return m?.rows ?? 0; @@ -923,11 +1029,62 @@ const pinescript = { } return null; }, + __jacobiEigen: function(m) { + if (!m || !Array.isArray(m.data)) return null; + const n = m.rows ?? 0; + if (n === 0 || n !== (m.cols ?? 0)) return null; + // Work on a copy so the input matrix is untouched. + const a = m.data.map(row => row.slice()); + const v = Array.from({ length: n }, (_, i) => Array.from({ length: n }, (_, j) => (i === j ? 1 : 0))); + for (let sweep = 0; sweep < 100; sweep++) { + let off = 0; + for (let p = 0; p < n; p++) for (let q = p + 1; q < n; q++) off += a[p][q] * a[p][q]; + if (off < 1e-20) break; + for (let p = 0; p < n; p++) { + for (let q = p + 1; q < n; q++) { + if (Math.abs(a[p][q]) < 1e-18) continue; + const theta = (a[q][q] - a[p][p]) / (2 * a[p][q]); + const t = Math.sign(theta || 1) / (Math.abs(theta) + Math.sqrt(theta * theta + 1)); + const cos = 1 / Math.sqrt(t * t + 1); + const sin = t * cos; + for (let k = 0; k < n; k++) { + const akp = a[k][p], akq = a[k][q]; + a[k][p] = cos * akp - sin * akq; + a[k][q] = sin * akp + cos * akq; + } + for (let k = 0; k < n; k++) { + const apk = a[p][k], aqk = a[q][k]; + a[p][k] = cos * apk - sin * aqk; + a[q][k] = sin * apk + cos * aqk; + } + for (let k = 0; k < n; k++) { + const vkp = v[k][p], vkq = v[k][q]; + v[k][p] = cos * vkp - sin * vkq; + v[k][q] = sin * vkp + cos * vkq; + } + } + } + } + // Sort eigenpairs by eigenvalue, descending. + const order = Array.from({ length: n }, (_, i) => i).sort((i, j) => a[j][j] - a[i][i]); + const values = order.map(i => a[i][i]); + const vectors = Array.from({ length: n }, (_, r) => order.map(c => v[r][c])); + return { values, vectors }; + }, + matrixEigenvalues: function(m) { + const e = this.__jacobiEigen(m); + return this.__decArr(e ? e.values : []); + }, + matrixEigenvectors: function(m) { + const e = this.__jacobiEigen(m); + if (!e) return this.__decMatrix({ rows: 0, cols: 0, data: [] }); + return this.__decMatrix({ rows: e.vectors.length, cols: e.vectors.length, data: e.vectors }); + }, requestSecurity: function(symbol, timeframe, expression) { return expression; }, arrayNew: function(initialSize = 0, initialValue = 0) { - return Array(initialSize).fill(initialValue); + return this.__decArr(Array(initialSize).fill(initialValue)); }, arraySize: function(arr) { return arr ? arr.length : 0; @@ -978,10 +1135,10 @@ const pinescript = { return arr; }, arraySlice: function(arr, startIndex = 0, endIndex = null) { - if (!arr) return []; + if (!arr) return this.__decArr([]); const start = Number(startIndex) || 0; const end = endIndex === null || endIndex === undefined ? arr.length : Number(endIndex) || 0; - return arr.slice(start, end); + return this.__decArr(arr.slice(start, end)); }, arraySort: function(arr, order = 'ascending') { if (arr) arr.sort((a, b) => order === 'ascending' ? a - b : b - a); @@ -1191,7 +1348,7 @@ const pinescript = { const info = { ticker: 'AAPL', tickerid: 'NASDAQ:AAPL', prefix: 'NASDAQ', root: 'AAPL', suffix: '' }; return info[type] || ''; }, - timenow: 1781574527061, + timenow: 1782783576285, barstate: "LAST", dividends: {}, splits: {}, @@ -1245,11 +1402,11 @@ const pinescript = { arrayConcat: function(arr1, arr2) { if (!arr1) return arr2 || []; if (!arr2) return arr1; - return arr1.concat(arr2); + return this.__decArr(arr1.concat(arr2)); }, arrayCopy: function(arr) { - if (!arr) return []; - return [...arr]; + if (!arr) return this.__decArr([]); + return this.__decArr([...arr]); }, arrayBinarySearch: function(arr, value) { if (!arr || arr.length === 0) return -1; @@ -1339,7 +1496,7 @@ globalThis.input.timeframe = globalThis.input.timeframe || ((defval) => defval); globalThis.array = globalThis.array || { - from: (...items) => items, + from: (...items) => pinescript.__decArr(items), size: (arr) => pinescript.arraySize(arr), @@ -1508,6 +1665,8 @@ globalThis.line = globalThis.line || __pineNS({ style_solid: "solid", style_dash globalThis.box = globalThis.box || __pineNS({}); +globalThis.color = globalThis.color || __pineNS(Object.assign(function(c) { return c; }, { new: function(c, t) { return c; }, rgb: function(r, g, b, t) { return "#rgb(" + [r, g, b].join(",") + ")"; }, from_gradient: function(v, lo, hi, c1, c2) { return c1; }, r: function() { return 0; }, g: function() { return 0; }, b: function() { return 0; }, t: function() { return 0; }, aqua: "#00BCD4", black: "#363A45", blue: "#2962FF", fuchsia: "#E040FB", gray: "#787B86", green: "#4CAF50", lime: "#00E676", maroon: "#880E4F", navy: "#311B92", olive: "#808000", orange: "#FF9800", purple: "#9C27B0", red: "#FF5252", silver: "#B2B5BE", teal: "#00897B", white: "#FFFFFF", yellow: "#FFEB3B" })); + globalThis.label = globalThis.label || __pineNS({ style_label_down: "label_down", style_label_up: "label_up", style_none: "none" }); globalThis.polyline = globalThis.polyline || __pineNS({}); @@ -1520,7 +1679,9 @@ globalThis.map = globalThis.map || __pineNS({}); globalThis.session = globalThis.session || __pineNS({ regular: "regular", extended: "extended" }); -globalThis.dayofweek = globalThis.dayofweek || __pineNS({ sunday: 1, monday: 2, tuesday: 3, wednesday: 4, thursday: 5, friday: 6, saturday: 7 }); +globalThis.ticker = globalThis.ticker || __pineNS({}); + +globalThis.dayofweek = globalThis.dayofweek || __pineNS(Object.assign(function(t) { return new Date(t != null ? t : (globalThis.time || 0)).getUTCDay() + 1; }, { sunday: 1, monday: 2, tuesday: 3, wednesday: 4, thursday: 5, friday: 6, saturday: 7 })); globalThis.timeframe = __pineNS(Object.assign(globalThis.timeframe || {}, { period: (globalThis.timeframe && globalThis.timeframe.period) || "D", isintraday: false, isdaily: true, multiplier: 1 })); @@ -1568,8 +1729,6 @@ globalThis.bool = globalThis.bool || function(x) { return Boolean(x); }; globalThis.string = globalThis.string || function(x) { return x == null ? null : String(x); }; -globalThis.dayofweek = globalThis.dayofweek || __pineNS({ sunday: 1, monday: 2, tuesday: 3, wednesday: 4, thursday: 5, friday: 6, saturday: 7 }); - globalThis.str = globalThis.str || __pineNS({}); pinescript.math = globalThis.math; @@ -1655,7 +1814,7 @@ pinescript.text = { align_center: "center" }; pinescript.table = { - new: function(position, columns, rows, opts) { return { position, columns, rows, opts: opts || {}, cells: [] }; }, + new: function(position, columns, rows, opts) { return pinescript.__decDraw({ position, columns, rows, opts: opts || {}, cells: [] }); }, cell: function(table, column, row, text, opts) { @@ -1689,14 +1848,14 @@ function main() { null; // Study: Golden Pocket Zones // Options: {"overlay":true,"max_boxes_count":500} - let lookbackDays = pinescript.inputInt(20, "Lookback Days", ({ minval: 1, maxval: 100, tooltip: "Number of daily candles to analyze for golden pocket zones" })); - let zoneHeightPct = pinescript.inputFloat(3, "Zone Height (% of ADR10)", ({ minval: 0.1, maxval: 50, step: 0.1, tooltip: "Height of golden pocket zone as percentage of ADR10 - SAME for all zones" })); - let minCandleSizePct = pinescript.inputFloat(60, "Min Candle Size (% of ADR10)", ({ minval: 0, maxval: 200, step: 5, tooltip: "Only show zones for candles larger than this % of ADR10. Set to 0 to show all zones." })); - let showLabels = pinescript.inputBool(true, "Show Labels", ({ tooltip: "Display age and strength labels inside zones" })); - let zoneColor = pinescript.inputColor(pinescript.color.new(pinescript.color.hex("#FFB84D"), 20), "Zone Color", ({ tooltip: "Color of the golden pocket zones" })); - let textColor = pinescript.inputColor(pinescript.color.new(pinescript.color.hex("#000000"), 0), "Text Color", ({ tooltip: "Color of the text labels" })); - let [dailyHigh, dailyLow, dailyTime] = pinescript.unpack(pinescript.requestSecurity(syminfo.tickerid, "D", [high, low, time], ({ lookahead: barmerge.lookahead_off })), 3); - let adr10 = pinescript.requestSecurity(syminfo.tickerid, "D", pinescript.sma((high - low), 10), ({ lookahead: barmerge.lookahead_off })); + var lookbackDays = pinescript.inputInt(20, "Lookback Days", ({ minval: 1, maxval: 100, tooltip: "Number of daily candles to analyze for golden pocket zones" })); + var zoneHeightPct = pinescript.inputFloat(3, "Zone Height (% of ADR10)", ({ minval: 0.1, maxval: 50, step: 0.1, tooltip: "Height of golden pocket zone as percentage of ADR10 - SAME for all zones" })); + var minCandleSizePct = pinescript.inputFloat(60, "Min Candle Size (% of ADR10)", ({ minval: 0, maxval: 200, step: 5, tooltip: "Only show zones for candles larger than this % of ADR10. Set to 0 to show all zones." })); + var showLabels = pinescript.inputBool(true, "Show Labels", ({ tooltip: "Display age and strength labels inside zones" })); + var zoneColor = pinescript.inputColor(pinescript.color.new(pinescript.color.hex("#FFB84D"), 20), "Zone Color", ({ tooltip: "Color of the golden pocket zones" })); + var textColor = pinescript.inputColor(pinescript.color.new(pinescript.color.hex("#000000"), 0), "Text Color", ({ tooltip: "Color of the text labels" })); + var [dailyHigh, dailyLow, dailyTime] = pinescript.unpack(pinescript.requestSecurity(syminfo.tickerid, "D", [high, low, time], ({ lookahead: barmerge.lookahead_off })), 3); + var adr10 = pinescript.requestSecurity(syminfo.tickerid, "D", pinescript.sma(pinescript.series(0, (high - low)), 10), ({ lookahead: barmerge.lookahead_off })); if (state.lastDailyTime === undefined) state.lastDailyTime = 0; if (state.lastDailyStartBar === undefined) state.lastDailyStartBar = null; if (state.dailyHighs === undefined) state.dailyHighs = pinescript.arrayNew(); @@ -1712,8 +1871,8 @@ function main() { return (pinescript.abs(val) / f_getPips()); } function calcGoldenPocket(high, low, fixedZoneHeight) { - let candleRange = (high - low); - let gpMid = (low + (candleRange * 0.559)); + var candleRange = (high - low); + var gpMid = (low + (candleRange * 0.559)); return [(gpMid + fixedZoneHeight), (gpMid - fixedZoneHeight), candleRange]; } function zonesOverlap(upper1, lower1, upper2, lower2) { @@ -1723,8 +1882,8 @@ function main() { return [pinescript.max(upper1, upper2), pinescript.min(lower1, lower2)]; } function calcStrength(candleSize, adr) { - let strengthPct = ((candleSize / adr) * 100); - let strength = 0; + var strengthPct = ((candleSize / adr) * 100); + var strength = 0; if ((strengthPct >= 160)) { strength = 10; } else { @@ -1771,42 +1930,42 @@ function main() { pinescript.arrayPop(state.dailyADRs); } pinescript.arrayClear(state.zones); - let fixedZoneHeight = (!pinescript.na(state.currentADR10) ? ((state.currentADR10 * (zoneHeightPct / 100)) / 2) : 0); - let tempZones = pinescript.arrayNew(); - let arraySize = pinescript.arraySize(state.dailyHighs); - let barsArraySize = pinescript.arraySize(state.dailyBars); + var fixedZoneHeight = (!pinescript.na(state.currentADR10) ? ((state.currentADR10 * (zoneHeightPct / 100)) / 2) : 0); + var tempZones = pinescript.arrayNew(); + var arraySize = pinescript.arraySize(state.dailyHighs); + var barsArraySize = pinescript.arraySize(state.dailyBars); if (((arraySize > 0) && (fixedZoneHeight > 0))) { for (let i = 0; i <= (arraySize - 1); i++) { - let dayHigh = pinescript.arrayGet(state.dailyHighs, i); - let dayLow = pinescript.arrayGet(state.dailyLows, i); - let dayBar = ((i < barsArraySize) ? pinescript.arrayGet(state.dailyBars, i) : bar_index); - let dayADR = pinescript.arrayGet(state.dailyADRs, i); + var dayHigh = pinescript.arrayGet(state.dailyHighs, i); + var dayLow = pinescript.arrayGet(state.dailyLows, i); + var dayBar = ((i < barsArraySize) ? pinescript.arrayGet(state.dailyBars, i) : bar_index); + var dayADR = pinescript.arrayGet(state.dailyADRs, i); if ((((!pinescript.na(dayHigh) && !pinescript.na(dayLow)) && !pinescript.na(dayADR)) && (dayADR > 0))) { - let candleSize = (dayHigh - dayLow); - let candleSizePct = ((candleSize / dayADR) * 100); + var candleSize = (dayHigh - dayLow); + var candleSizePct = ((candleSize / dayADR) * 100); if ((candleSizePct >= minCandleSizePct)) { - let [upper, lower, range] = pinescript.unpack(calcGoldenPocket(dayHigh, dayLow, fixedZoneHeight), 3); - strength = calcStrength(candleSize, dayADR); - let newZone = ZoneData.new(upper, lower, dayBar, i, strength); + var [upper, lower, range] = pinescript.unpack(calcGoldenPocket(dayHigh, dayLow, fixedZoneHeight), 3); + var strength = calcStrength(candleSize, dayADR); + var newZone = ZoneData.new(upper, lower, dayBar, i, strength); pinescript.arrayPush(tempZones, newZone); } } } - let tempZonesSize = pinescript.arraySize(tempZones); + var tempZonesSize = pinescript.arraySize(tempZones); if ((tempZonesSize > 0)) { - let mergedIndices = pinescript.arrayNew(); + var mergedIndices = pinescript.arrayNew(); for (let i = 0; i <= (tempZonesSize - 1); i++) { pinescript.arrayPush(mergedIndices, false); } for (let i = 0; i <= (tempZonesSize - 1); i++) { if (!pinescript.arrayGet(mergedIndices, i)) { - let zone1 = pinescript.arrayGet(tempZones, i); + var zone1 = pinescript.arrayGet(tempZones, i); if (((i + 1) < tempZonesSize)) { for (let j = (i + 1); j <= (tempZonesSize - 1); j++) { if (!pinescript.arrayGet(mergedIndices, j)) { - let zone2 = pinescript.arrayGet(tempZones, j); + var zone2 = pinescript.arrayGet(tempZones, j); if (zonesOverlap(zone1.upper, zone1.lower, zone2.upper, zone2.lower)) { - let [mergedUpper, mergedLower] = pinescript.unpack(mergeZones(zone1.upper, zone1.lower, zone2.upper, zone2.lower), 2); + var [mergedUpper, mergedLower] = pinescript.unpack(mergeZones(zone1.upper, zone1.lower, zone2.upper, zone2.lower), 2); zone1.upper = mergedUpper; zone1.lower = mergedLower; zone1.age = pinescript.max(zone1.age, zone2.age); @@ -1825,34 +1984,34 @@ function main() { } if ((barstate.islast && (pinescript.arraySize(state.zones) > 0))) { if ((pinescript.arraySize(box.all) > 0)) { - for (const existingBox of box.all) { + for (const existingBox of (box.all ?? [])) { pinescript.boxDelete(existingBox); } } if ((pinescript.arraySize(label.all) > 0)) { - for (const existingLabel of label.all) { + for (const existingLabel of (label.all ?? [])) { pinescript.labelDelete(existingLabel); } } - let minDist = 999999; - let maxDist = 0; + var minDist = 999999; + var maxDist = 0; for (let i = 0; i <= (pinescript.arraySize(state.zones) - 1); i++) { - let zone = pinescript.arrayGet(state.zones, i); + var zone = pinescript.arrayGet(state.zones, i); if (!pinescript.na(zone)) { - let distanceInPrice = 0; + var distanceInPrice = 0; if ((close > zone.upper)) { distanceInPrice = (close - zone.upper); } else { if ((close < zone.lower)) { distanceInPrice = (zone.lower - close); } else { - let distToUpper = (zone.upper - close); - let distToLower = (close - zone.lower); + var distToUpper = (zone.upper - close); + var distToLower = (close - zone.lower); distanceInPrice = pinescript.min(distToUpper, distToLower); } } - let pipSize = ((syminfo.type === "forex") ? ((syminfo.currency === "JPY") ? 0.01 : 0.0001) : 0.01); - let distancePips = (distanceInPrice / pipSize); + var pipSize = ((syminfo.type === "forex") ? ((syminfo.currency === "JPY") ? 0.01 : 0.0001) : 0.01); + var distancePips = (distanceInPrice / pipSize); minDist = pinescript.min(minDist, distancePips); maxDist = pinescript.max(maxDist, distancePips); } @@ -1874,17 +2033,17 @@ function main() { } pipSize = ((syminfo.type === "forex") ? ((syminfo.currency === "JPY") ? 0.01 : 0.0001) : 0.01); distancePips = (distanceInPrice / pipSize); - let normalizedDist = ((maxDist > minDist) ? ((distancePips - minDist) / (maxDist - minDist)) : 0.5); - let r = 255; - let g = pinescript.round((215 - ((215 - 140) * normalizedDist))); - let b = pinescript.round(0); - let heatmapColor = pinescript.color.new(pinescript.color.rgb(r, g, b), 20); - let maxLookback = pinescript.min(2000, bar_index); - let safeStartBar = pinescript.max(zone.startBar, (bar_index - maxLookback)); - let boxId = pinescript.boxNew(({ left: safeStartBar, top: zone.upper, right: (bar_index + 50), bottom: zone.lower, border_color: pinescript.color.new(heatmapColor, 70), border_width: 1, bgcolor: heatmapColor, extend: extend.right })); + var normalizedDist = ((maxDist > minDist) ? ((distancePips - minDist) / (maxDist - minDist)) : 0.5); + var r = 255; + var g = pinescript.round((215 - ((215 - 140) * normalizedDist))); + var b = pinescript.round(0); + var heatmapColor = pinescript.color.new(pinescript.color.rgb(r, g, b), 20); + var maxLookback = pinescript.min(2000, bar_index); + var safeStartBar = pinescript.max(zone.startBar, (bar_index - maxLookback)); + var boxId = pinescript.boxNew(({ left: safeStartBar, top: zone.upper, right: (bar_index + 50), bottom: zone.lower, border_color: pinescript.color.new(heatmapColor, 70), border_width: 1, bgcolor: heatmapColor, extend: extend.right })); if (showLabels) { - let ageText = ((zone.age === 0) ? "TODAY" : (pinescript.strToString(zone.age) + " DAYS")); - let strengthText = (pinescript.strToString(zone.strength, "#") + "/10"); + var ageText = ((zone.age === 0) ? "TODAY" : (pinescript.strToString(zone.age) + " DAYS")); + var strengthText = (pinescript.strToString(zone.strength, "#") + "/10"); distanceInPrice = 0; if ((close > zone.upper)) { distanceInPrice = (close - zone.upper); @@ -1899,9 +2058,9 @@ function main() { } pipSize = ((syminfo.type === "forex") ? ((syminfo.currency === "JPY") ? 0.01 : 0.0001) : 0.01); distancePips = (distanceInPrice / pipSize); - let distanceText = (pinescript.strToString(pinescript.round(distancePips)) + " PIPS"); - let labelText = ((((("GOLDEN POCKET ||| AGE " + ageText) + " ||| STRENGTH ") + strengthText) + " ||| DISTANCE ") + distanceText); - let labelY = ((zone.upper + zone.lower) / 2); + var distanceText = (pinescript.strToString(pinescript.round(distancePips)) + " PIPS"); + var labelText = ((((("GOLDEN POCKET ||| AGE " + ageText) + " ||| STRENGTH ") + strengthText) + " ||| DISTANCE ") + distanceText); + var labelY = ((zone.upper + zone.lower) / 2); pinescript.labelNew(({ x: (bar_index + 25), y: labelY, text: labelText, color: pinescript.color.new(pinescript.color.black, 100), textcolor: textColor, size: pinescript.size.large, style: label.style_label_center, xloc: xloc.bar_index })); } } @@ -1951,6 +2110,10 @@ function run(data, options = {}) { rt.__shapeIdx = 0; globalThis.bar_index = i; globalThis.last_bar_index = n - 1; + globalThis.time_tradingday = inTime[i]; + globalThis.time_close = inTime[i]; + globalThis.last_bar_time = inTime[n - 1]; + globalThis.timenow = inTime[n - 1]; globalThis.barstate = { isfirst: i === 0, islast: i === n - 1, isrealtime: false, ishistory: true, isconfirmed: true, isnew: true, islastconfirmedhistory: i === n - 1, diff --git a/converted/Hyperfork_Matrix.js b/converted/Hyperfork_Matrix.js index 2f8e282..cc17ffb 100644 --- a/converted/Hyperfork_Matrix.js +++ b/converted/Hyperfork_Matrix.js @@ -42,6 +42,80 @@ const pinescript = { if (value != null && typeof value[Symbol.iterator] === 'function') return Array.from(value); return new Array(count || 0).fill(null); }, + __decArr: function(arr) { + if (!Array.isArray(arr) || arr.__pineDecorated) return arr; + const self = this; + const def = (name, fn) => Object.defineProperty(arr, name, { value: fn, writable: true, configurable: true }); + Object.defineProperty(arr, '__pineDecorated', { value: true, configurable: true }); + def('get', (i) => (arr[i] === undefined ? null : arr[i])); + def('set', (i, v) => { arr[i] = v; return v; }); + def('size', () => arr.length); + def('clear', () => { arr.length = 0; }); + def('insert', (i, v) => { arr.splice(i, 0, v); }); + def('remove', (i) => arr.splice(i, 1)[0]); + def('contains', (v) => arr.includes(v)); + def('indexof', (v) => arr.indexOf(v)); + def('lastindexof', (v) => arr.lastIndexOf(v)); + def('first', () => (arr.length ? arr[0] : null)); + def('last', () => (arr.length ? arr[arr.length - 1] : null)); + def('sum', () => arr.reduce((a, b) => a + b, 0)); + def('avg', () => (arr.length ? arr.reduce((a, b) => a + b, 0) / arr.length : 0)); + def('min', () => (arr.length ? Math.min(...arr) : null)); + def('max', () => (arr.length ? Math.max(...arr) : null)); + def('range', () => (arr.length ? Math.max(...arr) - Math.min(...arr) : null)); + // Statistical methods that route to the array.* built-ins (these names are not + // native to JS arrays, so attaching them here is safe from recursion). + def('median', () => self.arrayMedian(arr)); + def('mode', () => self.arrayMode(arr)); + def('stdev', () => self.arrayStdev(arr)); + def('variance', () => self.arrayVariance(arr)); + def('covariance', (other) => self.arrayCovariance(arr, other)); + def('percentile_linear_interpolation', (p) => self.arrayPercentileLinearInterpolation(arr, p)); + def('percentile_nearest_rank', (p) => self.arrayPercentileNearestRank(arr, p)); + def('abs', () => self.__decArr(self.arrayAbs(arr))); + def('binary_search', (v) => self.arrayBinarySearch(arr, v)); + // Pine's array.sort takes an order string, not a comparator. Use the native + // sort via .call so we don't recurse through this overridden method. + def('sort', (order) => { Array.prototype.sort.call(arr, (a, b) => (order === 'descending' ? b - a : a - b)); return arr; }); + def('sort_indices', (order) => self.__decArr(arr.map((_, i) => i).sort((a, b) => (order === 'descending' ? arr[b] - arr[a] : arr[a] - arr[b])))); + // join/slice/reverse/concat/includes/fill already exist natively on Array with + // compatible semantics, so we deliberately leave them to the native methods. + return arr; + }, + __decMap: function(m) { + if (!(m instanceof Map) || m.__pineDecorated) return m; + const def = (name, fn) => Object.defineProperty(m, name, { value: fn, writable: true, configurable: true }); + Object.defineProperty(m, '__pineDecorated', { value: true, configurable: true }); + def('put', (k, v) => { m.set(k, v); return v; }); + def('contains', (k) => m.has(k)); + def('remove', (k) => m.delete(k)); + def('keys', () => Array.from(Map.prototype.keys.call(m))); + def('values', () => Array.from(Map.prototype.values.call(m))); + def('size_', () => m.size); + return m; + }, + __decMatrix: function(m) { + if (!m || typeof m !== 'object' || m.__pineDecorated) return m; + const self = this; + const def = (name, fn) => Object.defineProperty(m, name, { value: fn, writable: true, configurable: true }); + Object.defineProperty(m, '__pineDecorated', { value: true, configurable: true }); + def('get', (r, c) => self.matrixGet(m, r, c)); + def('set', (r, c, v) => self.matrixSet(m, r, c, v)); + def('rows_', () => m.rows); + def('columns', () => m.cols); + def('fill', (v) => self.matrixFill(m, v)); + return m; + }, + __decDraw: function(obj) { + if (!obj || typeof obj !== 'object') return obj; + const p = new Proxy(obj, { + get(t, k) { + if (k in t || typeof k === 'symbol') return t[k]; + return function() { return p; }; + }, + }); + return p; + }, alertcondition: function(condition, ...rest) { if (globalThis.__pineRuntime) { globalThis.__pineRuntime.alerts.push({ condition, args: rest }); @@ -49,6 +123,32 @@ const pinescript = { return null; }, barcolor: function(color) { return null; }, + plotchar: function(series, ...rest) { + const rt = globalThis.__pineRuntime; + if (!rt) return series; + const bar = rt.__barIndex | 0; + const ord = (rt.__shapeIdx = (rt.__shapeIdx | 0) + 1) - 1; + let key = 'char_' + ord; + for (const r of rest) { + if (typeof r === 'string') { key = r; break; } + if (r && typeof r === 'object' && r.title) { key = String(r.title); break; } + } + let s = rt.plotshapes[key]; + if (!s) s = rt.plotshapes[key] = { title: key, data: [] }; + s.data[bar] = this.__scalar(series); + return series; + }, + plotarrow: function(series, ...rest) { + const rt = globalThis.__pineRuntime; + if (!rt) return series; + const bar = rt.__barIndex | 0; + const ord = (rt.__plotIdx = (rt.__plotIdx | 0) + 1) - 1; + const key = 'arrow_' + ord; + let p = rt.plots[key]; + if (!p) p = rt.plots[key] = { title: key, data: [] }; + p.data[bar] = this.__scalar(series); + return series; + }, bgcolor: function(color, title, editable, showLast) { return null; }, @@ -470,6 +570,12 @@ const pinescript = { atan: function(value) { return Math.atan(value); }, + todegrees: function(radians) { + return radians * (180 / Math.PI); + }, + toradians: function(degrees) { + return degrees * (Math.PI / 180); + }, floor: function(value) { return Math.floor(value); }, @@ -641,7 +747,7 @@ const pinescript = { return series; }, lineNew: function(x1, y1, x2, y2, opts = {}) { - return { x1, y1, x2, y2, opts, _type: 'line' }; + return this.__decDraw({ x1, y1, x2, y2, opts, _type: 'line' }); }, lineDelete: function(l) { return null; @@ -661,7 +767,7 @@ const pinescript = { return point === 0 || point === 'y1' ? line.y1 : line.y2; }, labelNew: function(x, y, text = '', opts = {}) { - return { x, y, text, opts, _type: 'label' }; + return this.__decDraw({ x, y, text, opts, _type: 'label' }); }, labelDelete: function(l) { return null; @@ -676,7 +782,7 @@ const pinescript = { return label.text || ''; }, boxNew: function(left, top, right, bottom, opts = {}) { - return { left, top, right, bottom, opts, _type: 'box' }; + return this.__decDraw({ left, top, right, bottom, opts, _type: 'box' }); }, boxDelete: function(box) { return null; @@ -694,7 +800,7 @@ const pinescript = { return box; }, polylineNew: function(points, opts = {}) { - return { points: points || [], opts, _type: 'polyline' }; + return this.__decDraw({ points: points || [], opts, _type: 'polyline' }); }, polylineDelete: function(poly) { return null; @@ -765,7 +871,7 @@ const pinescript = { return { time: _time ?? null, price: _price ?? null }; }, mapNew: function() { - return new Map(); + return this.__decMap(new Map()); }, mapSize: function(m) { return m instanceof Map ? m.size : 0; @@ -800,7 +906,7 @@ const pinescript = { const r = Math.max(0, rows ?? 0); const c = Math.max(0, cols ?? 0); const data = Array.from({ length: r }, () => Array.from({ length: c }, () => initialValue)); - return { rows: r, cols: c, data }; + return this.__decMatrix({ rows: r, cols: c, data }); }, matrixRows: function(m) { return m?.rows ?? 0; @@ -923,11 +1029,62 @@ const pinescript = { } return null; }, + __jacobiEigen: function(m) { + if (!m || !Array.isArray(m.data)) return null; + const n = m.rows ?? 0; + if (n === 0 || n !== (m.cols ?? 0)) return null; + // Work on a copy so the input matrix is untouched. + const a = m.data.map(row => row.slice()); + const v = Array.from({ length: n }, (_, i) => Array.from({ length: n }, (_, j) => (i === j ? 1 : 0))); + for (let sweep = 0; sweep < 100; sweep++) { + let off = 0; + for (let p = 0; p < n; p++) for (let q = p + 1; q < n; q++) off += a[p][q] * a[p][q]; + if (off < 1e-20) break; + for (let p = 0; p < n; p++) { + for (let q = p + 1; q < n; q++) { + if (Math.abs(a[p][q]) < 1e-18) continue; + const theta = (a[q][q] - a[p][p]) / (2 * a[p][q]); + const t = Math.sign(theta || 1) / (Math.abs(theta) + Math.sqrt(theta * theta + 1)); + const cos = 1 / Math.sqrt(t * t + 1); + const sin = t * cos; + for (let k = 0; k < n; k++) { + const akp = a[k][p], akq = a[k][q]; + a[k][p] = cos * akp - sin * akq; + a[k][q] = sin * akp + cos * akq; + } + for (let k = 0; k < n; k++) { + const apk = a[p][k], aqk = a[q][k]; + a[p][k] = cos * apk - sin * aqk; + a[q][k] = sin * apk + cos * aqk; + } + for (let k = 0; k < n; k++) { + const vkp = v[k][p], vkq = v[k][q]; + v[k][p] = cos * vkp - sin * vkq; + v[k][q] = sin * vkp + cos * vkq; + } + } + } + } + // Sort eigenpairs by eigenvalue, descending. + const order = Array.from({ length: n }, (_, i) => i).sort((i, j) => a[j][j] - a[i][i]); + const values = order.map(i => a[i][i]); + const vectors = Array.from({ length: n }, (_, r) => order.map(c => v[r][c])); + return { values, vectors }; + }, + matrixEigenvalues: function(m) { + const e = this.__jacobiEigen(m); + return this.__decArr(e ? e.values : []); + }, + matrixEigenvectors: function(m) { + const e = this.__jacobiEigen(m); + if (!e) return this.__decMatrix({ rows: 0, cols: 0, data: [] }); + return this.__decMatrix({ rows: e.vectors.length, cols: e.vectors.length, data: e.vectors }); + }, requestSecurity: function(symbol, timeframe, expression) { return expression; }, arrayNew: function(initialSize = 0, initialValue = 0) { - return Array(initialSize).fill(initialValue); + return this.__decArr(Array(initialSize).fill(initialValue)); }, arraySize: function(arr) { return arr ? arr.length : 0; @@ -978,10 +1135,10 @@ const pinescript = { return arr; }, arraySlice: function(arr, startIndex = 0, endIndex = null) { - if (!arr) return []; + if (!arr) return this.__decArr([]); const start = Number(startIndex) || 0; const end = endIndex === null || endIndex === undefined ? arr.length : Number(endIndex) || 0; - return arr.slice(start, end); + return this.__decArr(arr.slice(start, end)); }, arraySort: function(arr, order = 'ascending') { if (arr) arr.sort((a, b) => order === 'ascending' ? a - b : b - a); @@ -1191,7 +1348,7 @@ const pinescript = { const info = { ticker: 'AAPL', tickerid: 'NASDAQ:AAPL', prefix: 'NASDAQ', root: 'AAPL', suffix: '' }; return info[type] || ''; }, - timenow: 1781574527160, + timenow: 1782783576374, barstate: "LAST", dividends: {}, splits: {}, @@ -1245,11 +1402,11 @@ const pinescript = { arrayConcat: function(arr1, arr2) { if (!arr1) return arr2 || []; if (!arr2) return arr1; - return arr1.concat(arr2); + return this.__decArr(arr1.concat(arr2)); }, arrayCopy: function(arr) { - if (!arr) return []; - return [...arr]; + if (!arr) return this.__decArr([]); + return this.__decArr([...arr]); }, arrayBinarySearch: function(arr, value) { if (!arr || arr.length === 0) return -1; @@ -1339,7 +1496,7 @@ globalThis.input.timeframe = globalThis.input.timeframe || ((defval) => defval); globalThis.array = globalThis.array || { - from: (...items) => items, + from: (...items) => pinescript.__decArr(items), size: (arr) => pinescript.arraySize(arr), @@ -1508,6 +1665,8 @@ globalThis.line = globalThis.line || __pineNS({ style_solid: "solid", style_dash globalThis.box = globalThis.box || __pineNS({}); +globalThis.color = globalThis.color || __pineNS(Object.assign(function(c) { return c; }, { new: function(c, t) { return c; }, rgb: function(r, g, b, t) { return "#rgb(" + [r, g, b].join(",") + ")"; }, from_gradient: function(v, lo, hi, c1, c2) { return c1; }, r: function() { return 0; }, g: function() { return 0; }, b: function() { return 0; }, t: function() { return 0; }, aqua: "#00BCD4", black: "#363A45", blue: "#2962FF", fuchsia: "#E040FB", gray: "#787B86", green: "#4CAF50", lime: "#00E676", maroon: "#880E4F", navy: "#311B92", olive: "#808000", orange: "#FF9800", purple: "#9C27B0", red: "#FF5252", silver: "#B2B5BE", teal: "#00897B", white: "#FFFFFF", yellow: "#FFEB3B" })); + globalThis.label = globalThis.label || __pineNS({ style_label_down: "label_down", style_label_up: "label_up", style_none: "none" }); globalThis.polyline = globalThis.polyline || __pineNS({}); @@ -1520,7 +1679,9 @@ globalThis.map = globalThis.map || __pineNS({}); globalThis.session = globalThis.session || __pineNS({ regular: "regular", extended: "extended" }); -globalThis.dayofweek = globalThis.dayofweek || __pineNS({ sunday: 1, monday: 2, tuesday: 3, wednesday: 4, thursday: 5, friday: 6, saturday: 7 }); +globalThis.ticker = globalThis.ticker || __pineNS({}); + +globalThis.dayofweek = globalThis.dayofweek || __pineNS(Object.assign(function(t) { return new Date(t != null ? t : (globalThis.time || 0)).getUTCDay() + 1; }, { sunday: 1, monday: 2, tuesday: 3, wednesday: 4, thursday: 5, friday: 6, saturday: 7 })); globalThis.timeframe = __pineNS(Object.assign(globalThis.timeframe || {}, { period: (globalThis.timeframe && globalThis.timeframe.period) || "D", isintraday: false, isdaily: true, multiplier: 1 })); @@ -1568,8 +1729,6 @@ globalThis.bool = globalThis.bool || function(x) { return Boolean(x); }; globalThis.string = globalThis.string || function(x) { return x == null ? null : String(x); }; -globalThis.dayofweek = globalThis.dayofweek || __pineNS({ sunday: 1, monday: 2, tuesday: 3, wednesday: 4, thursday: 5, friday: 6, saturday: 7 }); - globalThis.str = globalThis.str || __pineNS({}); pinescript.math = globalThis.math; @@ -1655,7 +1814,7 @@ pinescript.text = { align_center: "center" }; pinescript.table = { - new: function(position, columns, rows, opts) { return { position, columns, rows, opts: opts || {}, cells: [] }; }, + new: function(position, columns, rows, opts) { return pinescript.__decDraw({ position, columns, rows, opts: opts || {}, cells: [] }); }, cell: function(table, column, row, text, opts) { @@ -1688,80 +1847,80 @@ function main() { null; // Study: Hyperfork Matrix // Options: {"overlay":true,"max_lines_count":500,"max_labels_count":500} - let MAX_BAR_EXTENSION = 500; - let group_pivots = "1. Pivot Points"; - let start_pivot_type = pinescript.inputString("Auto", ({ title: "Starting Pivot Type", options: ["Auto", "Low", "High"], group: group_pivots, tooltip: "Auto: detect pattern from prices | Low: force Low-High-Low | High: force High-Low-High" })); - let pivotA_time = pinescript.inputTime(({ title: "Pivot A Time", defval: 0, group: group_pivots, tooltip: "Select the timestamp for the starting pivot (A, the anchor point)", confirm: true })); - let pivotB_time = pinescript.inputTime(({ title: "Pivot B Time", defval: 0, group: group_pivots, tooltip: "Select the timestamp for Pivot B (the first swing point after A)", confirm: true })); - let pivotC_time = pinescript.inputTime(({ title: "Pivot C Time", defval: 0, group: group_pivots, tooltip: "Select the timestamp for Pivot C (the second swing point after B)", confirm: true })); - let show_pivot_labels = pinescript.inputBool(true, ({ title: "Show Pivot Labels", group: group_pivots, tooltip: "Show A, B, C labels at pivot points" })); - let labelColorH = pinescript.inputColor(pinescript.color.lime, "High Pivot Color", ({ group: group_pivots, inline: "pivot_colors" })); - let labelColorL = pinescript.inputColor(pinescript.color.red, "Low Pivot Color", ({ group: group_pivots, inline: "pivot_colors" })); - let pivot_label_size = pinescript.inputString("Normal", ({ title: "Size", options: ["Tiny", "Small", "Normal", "Large"], group: group_pivots, tooltip: "Text size for pivot labels" })); - let group_pitchfork = "2. Pitchfork Settings"; - let use_log = pinescript.inputBool(false, ({ title: "Logarithmic Scale", group: group_pitchfork, tooltip: "Enable to support logarithmic chart scaling. Calculations will be performed in log space for proper line rendering on log charts." })); - let pitchfork_type = pinescript.inputString(({ title: "Pitchfork Type", defval: "Original", options: ["Original", "Schiff", "Modified Schiff"], group: group_pitchfork, tooltip: "Original: anchor at A | Schiff: anchor shifted 50% Y toward B | Modified Schiff: anchor shifted 50% X+Y toward B" })); - let extra_levels = pinescript.inputInt(({ title: "Extra Parallel Levels", defval: 0, minval: 0, group: group_pitchfork, tooltip: "Number of additional parallel lines outside the main pitchfork" })); - let line_color = pinescript.inputColor(pinescript.color.lime, ({ title: "Pitchfork Color", group: group_pitchfork, inline: "pitchfork", tooltip: "Color for the pitchfork lines (median and parallels)" })); - let line_width = pinescript.inputInt(1, ({ title: "Width", minval: 1, group: group_pitchfork, inline: "pitchfork", tooltip: "Width for the pitchfork lines" })); - let line_style = pinescript.inputString("Solid", ({ title: "Style", options: ["Solid", "Dashed", "Dotted"], group: group_pitchfork, inline: "pitchfork", tooltip: "Style for the pitchfork lines" })); - let extend_dir = pinescript.inputString(({ title: "Extend Direction", defval: "Right", options: ["Right", "Both"], group: group_pitchfork, tooltip: "Direction to extend the pitchfork lines: Right, or Both" })); - let enable_price_range = pinescript.inputBool(true, ({ title: "Enable Price Range Filter", group: group_pitchfork, tooltip: "When enabled, extra parallels outside the price range are hidden." })); - let price_range_min = pinescript.inputFloat(0, ({ title: "Price Range Min", minval: 0, group: group_pitchfork, tooltip: "Minimum price for pitchfork elements. Extra parallels below this will be removed." })); - let price_range_max = pinescript.inputFloat(1000000, ({ title: "Price Range Max", minval: 0, group: group_pitchfork, tooltip: "Maximum price for pitchfork elements. Extra parallels above this will be removed." })); - let group_action_reaction = "3. Action / Reaction Lines"; - let draw_type = pinescript.inputString("Both", ({ title: "Draw Action/Reaction Lines", options: ["None", "Action Only", "Reaction Only", "Both"], group: group_action_reaction, tooltip: "Choose which lines to draw: None, Action Only, Reaction Only, or Both" })); - let forward_count = pinescript.inputInt(({ title: "Forward Lines Offset", defval: 0, group: group_action_reaction, tooltip: "Offset from auto-extend. 0 = to current bar. Positive adds more, negative removes." })); - let backward_count = pinescript.inputInt(({ title: "Backward Lines Count", defval: 0, minval: 0, group: group_action_reaction, tooltip: "Number of lines backward from the median" })); - let action_color = pinescript.inputColor(pinescript.color.aqua, ({ title: "Action Lines Color", group: group_action_reaction, inline: "action", tooltip: "Color for the action transversal lines" })); - let action_width = pinescript.inputInt(1, ({ title: "Width", minval: 1, group: group_action_reaction, inline: "action", tooltip: "Width for the action transversal lines" })); - let action_style = pinescript.inputString("Dotted", ({ title: "Style", options: ["Solid", "Dashed", "Dotted"], group: group_action_reaction, inline: "action", tooltip: "Style for the action transversal lines" })); - let reaction_color = pinescript.inputColor(pinescript.color.hex("#ff8c69"), ({ title: "Reaction Lines Color", group: group_action_reaction, inline: "reaction", tooltip: "Color for the reaction transversal lines" })); - let reaction_width = pinescript.inputInt(1, ({ title: "Width", minval: 1, group: group_action_reaction, inline: "reaction", tooltip: "Width for the reaction transversal lines" })); - let reaction_style = pinescript.inputString("Dotted", ({ title: "Style", options: ["Solid", "Dashed", "Dotted"], group: group_action_reaction, inline: "reaction", tooltip: "Style for the reaction transversal lines" })); - let group_lattice = "4. Lattice"; - let draw_lattice = pinescript.inputBool(false, ({ title: "Draw Lattice", group: group_lattice, tooltip: "Enable drawing of horizontal and vertical lattice lines based on pivots and intersections" })); - let lattice_pivot = pinescript.inputString("A", ({ title: "Select Pivot for Horizontal", options: ["A", "B", "C"], group: group_lattice, tooltip: "Choose which pivot's horizontal line to plot" })); - let lattice_source = pinescript.inputString("Pitchfork & Parallels", ({ title: "Intersection Source", options: ["Pitchfork & Parallels", "Action Lines", "Reaction Lines", "Action & Reaction"], group: group_lattice, tooltip: "Choose the source for intersections: Pitchfork & parallels, Action lines, Reaction lines, or both Action & Reaction" })); - let lattice_color = pinescript.inputColor(pinescript.color.white, ({ title: "Lattice Color", group: group_lattice, inline: "lattice", tooltip: "Color for the lattice lines (horizontals and verticals)" })); - let lattice_width = pinescript.inputInt(1, ({ title: "Width", minval: 1, group: group_lattice, inline: "lattice", tooltip: "Width for the lattice lines" })); - let lattice_style = pinescript.inputString("Dotted", ({ title: "Style", options: ["Solid", "Dashed", "Dotted"], group: group_lattice, inline: "lattice", tooltip: "Style for the lattice lines" })); + var MAX_BAR_EXTENSION = 500; + var group_pivots = "1. Pivot Points"; + var start_pivot_type = pinescript.inputString("Auto", ({ title: "Starting Pivot Type", options: ["Auto", "Low", "High"], group: group_pivots, tooltip: "Auto: detect pattern from prices | Low: force Low-High-Low | High: force High-Low-High" })); + var pivotA_time = pinescript.inputTime(({ title: "Pivot A Time", defval: 0, group: group_pivots, tooltip: "Select the timestamp for the starting pivot (A, the anchor point)", confirm: true })); + var pivotB_time = pinescript.inputTime(({ title: "Pivot B Time", defval: 0, group: group_pivots, tooltip: "Select the timestamp for Pivot B (the first swing point after A)", confirm: true })); + var pivotC_time = pinescript.inputTime(({ title: "Pivot C Time", defval: 0, group: group_pivots, tooltip: "Select the timestamp for Pivot C (the second swing point after B)", confirm: true })); + var show_pivot_labels = pinescript.inputBool(true, ({ title: "Show Pivot Labels", group: group_pivots, tooltip: "Show A, B, C labels at pivot points" })); + var labelColorH = pinescript.inputColor(pinescript.color.lime, "High Pivot Color", ({ group: group_pivots, inline: "pivot_colors" })); + var labelColorL = pinescript.inputColor(pinescript.color.red, "Low Pivot Color", ({ group: group_pivots, inline: "pivot_colors" })); + var pivot_label_size = pinescript.inputString("Normal", ({ title: "Size", options: ["Tiny", "Small", "Normal", "Large"], group: group_pivots, tooltip: "Text size for pivot labels" })); + var group_pitchfork = "2. Pitchfork Settings"; + var use_log = pinescript.inputBool(false, ({ title: "Logarithmic Scale", group: group_pitchfork, tooltip: "Enable to support logarithmic chart scaling. Calculations will be performed in log space for proper line rendering on log charts." })); + var pitchfork_type = pinescript.inputString(({ title: "Pitchfork Type", defval: "Original", options: ["Original", "Schiff", "Modified Schiff"], group: group_pitchfork, tooltip: "Original: anchor at A | Schiff: anchor shifted 50% Y toward B | Modified Schiff: anchor shifted 50% X+Y toward B" })); + var extra_levels = pinescript.inputInt(({ title: "Extra Parallel Levels", defval: 0, minval: 0, group: group_pitchfork, tooltip: "Number of additional parallel lines outside the main pitchfork" })); + var line_color = pinescript.inputColor(pinescript.color.lime, ({ title: "Pitchfork Color", group: group_pitchfork, inline: "pitchfork", tooltip: "Color for the pitchfork lines (median and parallels)" })); + var line_width = pinescript.inputInt(1, ({ title: "Width", minval: 1, group: group_pitchfork, inline: "pitchfork", tooltip: "Width for the pitchfork lines" })); + var line_style = pinescript.inputString("Solid", ({ title: "Style", options: ["Solid", "Dashed", "Dotted"], group: group_pitchfork, inline: "pitchfork", tooltip: "Style for the pitchfork lines" })); + var extend_dir = pinescript.inputString(({ title: "Extend Direction", defval: "Right", options: ["Right", "Both"], group: group_pitchfork, tooltip: "Direction to extend the pitchfork lines: Right, or Both" })); + var enable_price_range = pinescript.inputBool(true, ({ title: "Enable Price Range Filter", group: group_pitchfork, tooltip: "When enabled, extra parallels outside the price range are hidden." })); + var price_range_min = pinescript.inputFloat(0, ({ title: "Price Range Min", minval: 0, group: group_pitchfork, tooltip: "Minimum price for pitchfork elements. Extra parallels below this will be removed." })); + var price_range_max = pinescript.inputFloat(1000000, ({ title: "Price Range Max", minval: 0, group: group_pitchfork, tooltip: "Maximum price for pitchfork elements. Extra parallels above this will be removed." })); + var group_action_reaction = "3. Action / Reaction Lines"; + var draw_type = pinescript.inputString("Both", ({ title: "Draw Action/Reaction Lines", options: ["None", "Action Only", "Reaction Only", "Both"], group: group_action_reaction, tooltip: "Choose which lines to draw: None, Action Only, Reaction Only, or Both" })); + var forward_count = pinescript.inputInt(({ title: "Forward Lines Offset", defval: 0, group: group_action_reaction, tooltip: "Offset from auto-extend. 0 = to current bar. Positive adds more, negative removes." })); + var backward_count = pinescript.inputInt(({ title: "Backward Lines Count", defval: 0, minval: 0, group: group_action_reaction, tooltip: "Number of lines backward from the median" })); + var action_color = pinescript.inputColor(pinescript.color.aqua, ({ title: "Action Lines Color", group: group_action_reaction, inline: "action", tooltip: "Color for the action transversal lines" })); + var action_width = pinescript.inputInt(1, ({ title: "Width", minval: 1, group: group_action_reaction, inline: "action", tooltip: "Width for the action transversal lines" })); + var action_style = pinescript.inputString("Dotted", ({ title: "Style", options: ["Solid", "Dashed", "Dotted"], group: group_action_reaction, inline: "action", tooltip: "Style for the action transversal lines" })); + var reaction_color = pinescript.inputColor(pinescript.color.hex("#ff8c69"), ({ title: "Reaction Lines Color", group: group_action_reaction, inline: "reaction", tooltip: "Color for the reaction transversal lines" })); + var reaction_width = pinescript.inputInt(1, ({ title: "Width", minval: 1, group: group_action_reaction, inline: "reaction", tooltip: "Width for the reaction transversal lines" })); + var reaction_style = pinescript.inputString("Dotted", ({ title: "Style", options: ["Solid", "Dashed", "Dotted"], group: group_action_reaction, inline: "reaction", tooltip: "Style for the reaction transversal lines" })); + var group_lattice = "4. Lattice"; + var draw_lattice = pinescript.inputBool(false, ({ title: "Draw Lattice", group: group_lattice, tooltip: "Enable drawing of horizontal and vertical lattice lines based on pivots and intersections" })); + var lattice_pivot = pinescript.inputString("A", ({ title: "Select Pivot for Horizontal", options: ["A", "B", "C"], group: group_lattice, tooltip: "Choose which pivot's horizontal line to plot" })); + var lattice_source = pinescript.inputString("Pitchfork & Parallels", ({ title: "Intersection Source", options: ["Pitchfork & Parallels", "Action Lines", "Reaction Lines", "Action & Reaction"], group: group_lattice, tooltip: "Choose the source for intersections: Pitchfork & parallels, Action lines, Reaction lines, or both Action & Reaction" })); + var lattice_color = pinescript.inputColor(pinescript.color.white, ({ title: "Lattice Color", group: group_lattice, inline: "lattice", tooltip: "Color for the lattice lines (horizontals and verticals)" })); + var lattice_width = pinescript.inputInt(1, ({ title: "Width", minval: 1, group: group_lattice, inline: "lattice", tooltip: "Width for the lattice lines" })); + var lattice_style = pinescript.inputString("Dotted", ({ title: "Style", options: ["Solid", "Dashed", "Dotted"], group: group_lattice, inline: "lattice", tooltip: "Style for the lattice lines" })); line.style_dotted; line.style_dashed; line.style_solid; - let pitch_style = ((line_style === "Solid") ? undefined : ((line_style === "Dashed") ? undefined : ((line_style === "Dotted") ? undefined : null))); + var pitch_style = ((line_style === "Solid") ? undefined : ((line_style === "Dashed") ? undefined : ((line_style === "Dotted") ? undefined : null))); line.style_dotted; line.style_dashed; line.style_solid; - let reaction_line_style = ((reaction_style === "Solid") ? undefined : ((reaction_style === "Dashed") ? undefined : ((reaction_style === "Dotted") ? undefined : null))); + var reaction_line_style = ((reaction_style === "Solid") ? undefined : ((reaction_style === "Dashed") ? undefined : ((reaction_style === "Dotted") ? undefined : null))); line.style_dotted; line.style_dashed; line.style_solid; - let action_line_style = ((action_style === "Solid") ? undefined : ((action_style === "Dashed") ? undefined : ((action_style === "Dotted") ? undefined : null))); + var action_line_style = ((action_style === "Solid") ? undefined : ((action_style === "Dashed") ? undefined : ((action_style === "Dotted") ? undefined : null))); line.style_dotted; line.style_dashed; line.style_solid; - let lattice_line_style = ((lattice_style === "Solid") ? undefined : ((lattice_style === "Dashed") ? undefined : ((lattice_style === "Dotted") ? undefined : null))); + var lattice_line_style = ((lattice_style === "Solid") ? undefined : ((lattice_style === "Dashed") ? undefined : ((lattice_style === "Dotted") ? undefined : null))); pinescript.size.large; pinescript.size.normal; pinescript.size.small; pinescript.size.tiny; - let label_size = ((pivot_label_size === "Tiny") ? undefined : ((pivot_label_size === "Small") ? undefined : ((pivot_label_size === "Normal") ? undefined : ((pivot_label_size === "Large") ? undefined : null)))); - let pivotA_x = pinescript.valuewhen(pinescript.series(0, (time === pivotA_time)), pinescript.series(1, bar_index), 0); - let pivotB_x = pinescript.valuewhen(pinescript.series(2, (time === pivotB_time)), pinescript.series(3, bar_index), 0); - let pivotC_x = pinescript.valuewhen(pinescript.series(4, (time === pivotC_time)), pinescript.series(5, bar_index), 0); - let rawA_high = pinescript.valuewhen(pinescript.series(6, (time === pivotA_time)), pinescript.series(7, high), 0); - let rawA_low = pinescript.valuewhen(pinescript.series(8, (time === pivotA_time)), pinescript.series(9, low), 0); - let rawB_high = pinescript.valuewhen(pinescript.series(10, (time === pivotB_time)), pinescript.series(11, high), 0); - let rawB_low = pinescript.valuewhen(pinescript.series(12, (time === pivotB_time)), pinescript.series(13, low), 0); - let rawC_high = pinescript.valuewhen(pinescript.series(14, (time === pivotC_time)), pinescript.series(15, high), 0); - let rawC_low = pinescript.valuewhen(pinescript.series(16, (time === pivotC_time)), pinescript.series(17, low), 0); - let pivotA_type = null; - let pivotB_type = null; - let pivotC_type = null; + var label_size = ((pivot_label_size === "Tiny") ? undefined : ((pivot_label_size === "Small") ? undefined : ((pivot_label_size === "Normal") ? undefined : ((pivot_label_size === "Large") ? undefined : null)))); + var pivotA_x = pinescript.valuewhen(pinescript.series(0, (time === pivotA_time)), pinescript.series(1, bar_index), 0); + var pivotB_x = pinescript.valuewhen(pinescript.series(2, (time === pivotB_time)), pinescript.series(3, bar_index), 0); + var pivotC_x = pinescript.valuewhen(pinescript.series(4, (time === pivotC_time)), pinescript.series(5, bar_index), 0); + var rawA_high = pinescript.valuewhen(pinescript.series(6, (time === pivotA_time)), pinescript.series(7, high), 0); + var rawA_low = pinescript.valuewhen(pinescript.series(8, (time === pivotA_time)), pinescript.series(9, low), 0); + var rawB_high = pinescript.valuewhen(pinescript.series(10, (time === pivotB_time)), pinescript.series(11, high), 0); + var rawB_low = pinescript.valuewhen(pinescript.series(12, (time === pivotB_time)), pinescript.series(13, low), 0); + var rawC_high = pinescript.valuewhen(pinescript.series(14, (time === pivotC_time)), pinescript.series(15, high), 0); + var rawC_low = pinescript.valuewhen(pinescript.series(16, (time === pivotC_time)), pinescript.series(17, low), 0); + var pivotA_type = null; + var pivotB_type = null; + var pivotC_type = null; if ((start_pivot_type === "Auto")) { - let lhl_strength = ((rawB_high - rawA_low) + (rawB_high - rawC_low)); - let hlh_strength = ((rawA_high - rawB_low) + (rawC_high - rawB_low)); + var lhl_strength = ((rawB_high - rawA_low) + (rawB_high - rawC_low)); + var hlh_strength = ((rawA_high - rawB_low) + (rawC_high - rawB_low)); if ((lhl_strength >= hlh_strength)) { pivotA_type = "Low"; pivotB_type = "High"; @@ -1776,16 +1935,16 @@ function main() { pivotB_type = ((start_pivot_type === "Low") ? "High" : "Low"); pivotC_type = start_pivot_type; } - let pivotA_raw = ((pivotA_type === "High") ? rawA_high : rawA_low); - let pivotB_raw = ((pivotB_type === "High") ? rawB_high : rawB_low); - let pivotC_raw = ((pivotC_type === "High") ? rawC_high : rawC_low); - let pivotA_y = (use_log ? pinescript.log(pivotA_raw) : pivotA_raw); - let pivotB_y = (use_log ? pinescript.log(pivotB_raw) : pivotB_raw); - let pivotC_y = (use_log ? pinescript.log(pivotC_raw) : pivotC_raw); - let mid_x = ((pivotB_x + pivotC_x) / 2); - let mid_y = ((pivotB_y + pivotC_y) / 2); - let anchor_x = null; - let anchor_y = null; + var pivotA_raw = ((pivotA_type === "High") ? rawA_high : rawA_low); + var pivotB_raw = ((pivotB_type === "High") ? rawB_high : rawB_low); + var pivotC_raw = ((pivotC_type === "High") ? rawC_high : rawC_low); + var pivotA_y = (use_log ? pinescript.log(pivotA_raw) : pivotA_raw); + var pivotB_y = (use_log ? pinescript.log(pivotB_raw) : pivotB_raw); + var pivotC_y = (use_log ? pinescript.log(pivotC_raw) : pivotC_raw); + var mid_x = ((pivotB_x + pivotC_x) / 2); + var mid_y = ((pivotB_y + pivotC_y) / 2); + var anchor_x = null; + var anchor_y = null; switch (pitchfork_type) { case "Original": { @@ -1806,13 +1965,13 @@ function main() { break; } } - let slope_ml = ((mid_x !== anchor_x) ? ((mid_y - anchor_y) / (mid_x - anchor_x)) : null); - let slope_trans = ((pivotC_x !== pivotB_x) ? ((pivotC_y - pivotB_y) / (pivotC_x - pivotB_x)) : null); - let slope_action = ((pivotB_x !== pivotA_x) ? ((pivotB_y - pivotA_y) / (pivotB_x - pivotA_x)) : null); - let upper_x = pivotB_x; - let upper_y = pivotB_y; - let lower_x = pivotC_x; - let lower_y = pivotC_y; + var slope_ml = ((mid_x !== anchor_x) ? ((mid_y - anchor_y) / (mid_x - anchor_x)) : null); + var slope_trans = ((pivotC_x !== pivotB_x) ? ((pivotC_y - pivotB_y) / (pivotC_x - pivotB_x)) : null); + var slope_action = ((pivotB_x !== pivotA_x) ? ((pivotB_y - pivotA_y) / (pivotB_x - pivotA_x)) : null); + var upper_x = pivotB_x; + var upper_y = pivotB_y; + var lower_x = pivotC_x; + var lower_y = pivotC_y; if ((pivotB_y < pivotC_y)) { upper_x = pivotC_x; upper_y = pivotC_y; @@ -1829,10 +1988,10 @@ function main() { return ((((!pinescript.na(prev_price) && !pinescript.na(curr_price)) && !pinescript.na(prev_line_y)) && !pinescript.na(curr_line_y)) && (((prev_price < prev_line_y) && (curr_price >= curr_line_y)) || ((prev_price > prev_line_y) && (curr_price <= curr_line_y)))); } function f_draw_line(start_x, start_y, col, wid, st, min_x, max_x, all_min_x) { - let x1 = null; - let y1 = null; - let x2 = null; - let y2 = null; + var x1 = null; + var y1 = null; + var x2 = null; + var y2 = null; switch (extend_dir) { case "Right": { @@ -1840,8 +1999,8 @@ function main() { y1 = start_y; x2 = max_x; y2 = (start_y + (slope_ml * (x2 - x1))); - let draw_y1 = (use_log ? pinescript.exp(y1) : y1); - let draw_y2 = (use_log ? pinescript.exp(y2) : y2); + var draw_y1 = (use_log ? pinescript.exp(y1) : y1); + var draw_y2 = (use_log ? pinescript.exp(y2) : y2); pinescript.lineNew(int(pinescript.round(x1)), draw_y1, int(pinescript.round(x2)), draw_y2, ({ extend: extend.right, color: col, width: wid, style: st })); break; } @@ -1851,74 +2010,76 @@ function main() { y1 = (start_y + (slope_ml * (x1 - start_x))); x2 = max_x; y2 = (start_y + (slope_ml * (x2 - start_x))); - let draw_y1 = (use_log ? pinescript.exp(y1) : y1); - let draw_y2 = (use_log ? pinescript.exp(y2) : y2); + draw_y1 = (use_log ? pinescript.exp(y1) : y1); + draw_y2 = (use_log ? pinescript.exp(y2) : y2); pinescript.lineNew(int(pinescript.round(x1)), draw_y1, int(pinescript.round(x2)), draw_y2, ({ extend: extend.both, color: col, width: wid, style: st })); break; } } } function f_draw_tine(px, py, col, wid, st, min_x, max_x, all_min_x, slope_t) { - let int_x = f_intersect_x(slope_t, pivotB_x, pivotB_y, slope_ml, px, py); - let int_y = (pinescript.na(int_x) ? null : ((slope_t * (int_x - pivotB_x)) + pivotB_y)); + var int_x = f_intersect_x(slope_t, pivotB_x, pivotB_y, slope_ml, px, py); + var int_y = (pinescript.na(int_x) ? null : ((slope_t * (int_x - pivotB_x)) + pivotB_y)); if (!pinescript.na(int_x)) { - let draw_x = int_x; - let draw_y = int_y; + var draw_x = int_x; + var draw_y = int_y; if ((int_x < 0)) { draw_x = 0; draw_y = (py + (slope_ml * (draw_x - px))); } - f_draw_line(draw_x, draw_y, col, wid, st, min_x, max_x, all_min_x); + return f_draw_line(draw_x, draw_y, col, wid, st, min_x, max_x, all_min_x); + } else { + return null; } } if (state.drawn === undefined) state.drawn = false; - let max_x = (last_bar_index + MAX_BAR_EXTENSION); - let min_x = 0; - let all_min_x = pinescript.min(anchor_x, pinescript.min(pivotB_x, pivotC_x)); - let all_max_x = pinescript.max(anchor_x, pinescript.max(pivotB_x, pivotC_x)); + var max_x = (last_bar_index + MAX_BAR_EXTENSION); + var min_x = 0; + var all_min_x = pinescript.min(anchor_x, pinescript.min(pivotB_x, pivotC_x)); + var all_max_x = pinescript.max(anchor_x, pinescript.max(pivotB_x, pivotC_x)); if (state.effective_extra_upper === undefined) state.effective_extra_upper = 0; if (state.effective_extra_lower === undefined) state.effective_extra_lower = 0; if (((!state.drawn && !pinescript.na(anchor_x)) && !pinescript.na(slope_ml))) { state.drawn = true; if (show_pivot_labels) { - let style_A = ((pivotA_type === "Low") ? label.style_label_up : label.style_label_down); - let style_B = ((pivotB_type === "Low") ? label.style_label_up : label.style_label_down); - let style_C = ((pivotC_type === "Low") ? label.style_label_up : label.style_label_down); - let color_A = ((pivotA_type === "High") ? labelColorH : labelColorL); - let color_B = ((pivotB_type === "High") ? labelColorH : labelColorL); - let color_C = ((pivotC_type === "High") ? labelColorH : labelColorL); - let y_A = (use_log ? pinescript.exp(pivotA_y) : pivotA_y); - let y_B = (use_log ? pinescript.exp(pivotB_y) : pivotB_y); - let y_C = (use_log ? pinescript.exp(pivotC_y) : pivotC_y); + var style_A = ((pivotA_type === "Low") ? label.style_label_up : label.style_label_down); + var style_B = ((pivotB_type === "Low") ? label.style_label_up : label.style_label_down); + var style_C = ((pivotC_type === "Low") ? label.style_label_up : label.style_label_down); + var color_A = ((pivotA_type === "High") ? labelColorH : labelColorL); + var color_B = ((pivotB_type === "High") ? labelColorH : labelColorL); + var color_C = ((pivotC_type === "High") ? labelColorH : labelColorL); + var y_A = (use_log ? pinescript.exp(pivotA_y) : pivotA_y); + var y_B = (use_log ? pinescript.exp(pivotB_y) : pivotB_y); + var y_C = (use_log ? pinescript.exp(pivotC_y) : pivotC_y); pinescript.labelNew(int(pivotA_x), y_A, "A", ({ style: style_A, color: pinescript.color.new(color_A, 100), textcolor: color_A, size: label_size, text_font_family: font.family_monospace })); pinescript.labelNew(int(pivotB_x), y_B, "B", ({ style: style_B, color: pinescript.color.new(color_B, 100), textcolor: color_B, size: label_size, text_font_family: font.family_monospace })); pinescript.labelNew(int(pivotC_x), y_C, "C", ({ style: style_C, color: pinescript.color.new(color_C, 100), textcolor: color_C, size: label_size, text_font_family: font.family_monospace })); } f_draw_line(anchor_x, anchor_y, line_color, line_width, pitch_style, min_x, max_x, all_min_x); - let median_y_upper = (anchor_y + (slope_ml * (upper_x - anchor_x))); - let offset_up = (upper_y - median_y_upper); - let median_y_lower = (anchor_y + (slope_ml * (lower_x - anchor_x))); - let offset_lo = (lower_y - median_y_lower); + var median_y_upper = (anchor_y + (slope_ml * (upper_x - anchor_x))); + var offset_up = (upper_y - median_y_upper); + var median_y_lower = (anchor_y + (slope_ml * (lower_x - anchor_x))); + var offset_lo = (lower_y - median_y_lower); if (enable_price_range) { - let range_min_y = (use_log ? pinescript.log(pinescript.max(price_range_min, 0.0001)) : price_range_min); - let range_max_y = (use_log ? pinescript.log(price_range_max) : price_range_max); + var range_min_y = (use_log ? pinescript.log(pinescript.max(price_range_min, 0.0001)) : price_range_min); + var range_max_y = (use_log ? pinescript.log(price_range_max) : price_range_max); state.effective_extra_upper = extra_levels; if (((offset_up !== 0) && (extra_levels > 0))) { if ((offset_up > 0)) { - let max_levels = int(pinescript.floor(((range_max_y - upper_y) / offset_up))); + var max_levels = int(pinescript.floor(((range_max_y - upper_y) / offset_up))); state.effective_extra_upper = pinescript.max(0, pinescript.min(extra_levels, max_levels)); } else { - let max_levels = int(pinescript.floor(((upper_y - range_min_y) / pinescript.abs(offset_up)))); + max_levels = int(pinescript.floor(((upper_y - range_min_y) / pinescript.abs(offset_up)))); state.effective_extra_upper = pinescript.max(0, pinescript.min(extra_levels, max_levels)); } } state.effective_extra_lower = extra_levels; if (((offset_lo !== 0) && (extra_levels > 0))) { if ((offset_lo < 0)) { - let max_levels = int(pinescript.floor(((lower_y - range_min_y) / pinescript.abs(offset_lo)))); + max_levels = int(pinescript.floor(((lower_y - range_min_y) / pinescript.abs(offset_lo)))); state.effective_extra_lower = pinescript.max(0, pinescript.min(extra_levels, max_levels)); } else { - let max_levels = int(pinescript.floor(((range_max_y - lower_y) / offset_lo))); + max_levels = int(pinescript.floor(((range_max_y - lower_y) / offset_lo))); state.effective_extra_lower = pinescript.max(0, pinescript.min(extra_levels, max_levels)); } } @@ -1933,98 +2094,98 @@ function main() { if (!pinescript.na(slope_trans)) { if ((state.effective_extra_upper > 0)) { for (let i = 1; i <= state.effective_extra_upper; i++) { - let extra_up_y = (upper_y + (float(i) * offset_up)); + var extra_up_y = (upper_y + (float(i) * offset_up)); f_draw_tine(upper_x, extra_up_y, line_color, line_width, pitch_style, min_x, max_x, all_min_x, slope_trans); } } if ((state.effective_extra_lower > 0)) { for (let i = 1; i <= state.effective_extra_lower; i++) { - let extra_lo_y = (lower_y + (float(i) * offset_lo)); + var extra_lo_y = (lower_y + (float(i) * offset_lo)); f_draw_tine(lower_x, extra_lo_y, line_color, line_width, pitch_style, min_x, max_x, all_min_x, slope_trans); } } } - let pitch_x = pinescript.arrayNew(); - let pitch_y = pinescript.arrayNew(); + var pitch_x = pinescript.arrayNew(); + var pitch_y = pinescript.arrayNew(); pitch_x.push(anchor_x); pitch_y.push(anchor_y); - let upper_start_x = f_intersect_x(slope_trans, pivotB_x, pivotB_y, slope_ml, upper_x, upper_y); - let upper_start_y = (pinescript.na(upper_start_x) ? null : ((slope_trans * (upper_start_x - pivotB_x)) + pivotB_y)); + var upper_start_x = f_intersect_x(slope_trans, pivotB_x, pivotB_y, slope_ml, upper_x, upper_y); + var upper_start_y = (pinescript.na(upper_start_x) ? null : ((slope_trans * (upper_start_x - pivotB_x)) + pivotB_y)); pitch_x.push(upper_start_x); pitch_y.push(upper_start_y); - let lower_start_x = f_intersect_x(slope_trans, pivotB_x, pivotB_y, slope_ml, lower_x, lower_y); - let lower_start_y = (pinescript.na(lower_start_x) ? null : ((slope_trans * (lower_start_x - pivotB_x)) + pivotB_y)); + var lower_start_x = f_intersect_x(slope_trans, pivotB_x, pivotB_y, slope_ml, lower_x, lower_y); + var lower_start_y = (pinescript.na(lower_start_x) ? null : ((slope_trans * (lower_start_x - pivotB_x)) + pivotB_y)); pitch_x.push(lower_start_x); pitch_y.push(lower_start_y); if ((state.effective_extra_upper > 0)) { for (let i = 1; i <= state.effective_extra_upper; i++) { - let extra_up_y = (upper_y + (float(i) * offset_up)); - let ex_start_x = f_intersect_x(slope_trans, pivotB_x, pivotB_y, slope_ml, upper_x, extra_up_y); - let ex_start_y = (pinescript.na(ex_start_x) ? null : ((slope_trans * (ex_start_x - pivotB_x)) + pivotB_y)); + extra_up_y = (upper_y + (float(i) * offset_up)); + var ex_start_x = f_intersect_x(slope_trans, pivotB_x, pivotB_y, slope_ml, upper_x, extra_up_y); + var ex_start_y = (pinescript.na(ex_start_x) ? null : ((slope_trans * (ex_start_x - pivotB_x)) + pivotB_y)); pitch_x.push(ex_start_x); pitch_y.push(ex_start_y); } } if ((state.effective_extra_lower > 0)) { for (let i = 1; i <= state.effective_extra_lower; i++) { - let extra_lo_y = (lower_y + (float(i) * offset_lo)); - let ex_lo_start_x = f_intersect_x(slope_trans, pivotB_x, pivotB_y, slope_ml, lower_x, extra_lo_y); - let ex_lo_start_y = (pinescript.na(ex_lo_start_x) ? null : ((slope_trans * (ex_lo_start_x - pivotB_x)) + pivotB_y)); + extra_lo_y = (lower_y + (float(i) * offset_lo)); + var ex_lo_start_x = f_intersect_x(slope_trans, pivotB_x, pivotB_y, slope_ml, lower_x, extra_lo_y); + var ex_lo_start_y = (pinescript.na(ex_lo_start_x) ? null : ((slope_trans * (ex_lo_start_x - pivotB_x)) + pivotB_y)); pitch_x.push(ex_lo_start_x); pitch_y.push(ex_lo_start_y); } } - let eff_upper_y = (upper_y + (state.effective_extra_upper * offset_up)); - let outer_upper_start_x = f_intersect_x(slope_trans, pivotB_x, pivotB_y, slope_ml, upper_x, eff_upper_y); - let outer_upper_start_y = (pinescript.na(outer_upper_start_x) ? null : ((slope_trans * (outer_upper_start_x - pivotB_x)) + pivotB_y)); - let eff_lower_y = (lower_y + (state.effective_extra_lower * offset_lo)); - let outer_lower_start_x = f_intersect_x(slope_trans, pivotB_x, pivotB_y, slope_ml, lower_x, eff_lower_y); - let outer_lower_start_y = (pinescript.na(outer_lower_start_x) ? null : ((slope_trans * (outer_lower_start_x - pivotB_x)) + pivotB_y)); - let prop_x = pinescript.arrayNew(); - let prop_y = pinescript.arrayNew(); - let handle_dx = (mid_x - anchor_x); - let handle_dy = (mid_y - anchor_y); - let handle_len = pinescript.sqrt((pinescript.pow(handle_dx, 2) + pinescript.pow(handle_dy, 2))); - let auto_forward_count = 0; + var eff_upper_y = (upper_y + (state.effective_extra_upper * offset_up)); + var outer_upper_start_x = f_intersect_x(slope_trans, pivotB_x, pivotB_y, slope_ml, upper_x, eff_upper_y); + var outer_upper_start_y = (pinescript.na(outer_upper_start_x) ? null : ((slope_trans * (outer_upper_start_x - pivotB_x)) + pivotB_y)); + var eff_lower_y = (lower_y + (state.effective_extra_lower * offset_lo)); + var outer_lower_start_x = f_intersect_x(slope_trans, pivotB_x, pivotB_y, slope_ml, lower_x, eff_lower_y); + var outer_lower_start_y = (pinescript.na(outer_lower_start_x) ? null : ((slope_trans * (outer_lower_start_x - pivotB_x)) + pivotB_y)); + var prop_x = pinescript.arrayNew(); + var prop_y = pinescript.arrayNew(); + var handle_dx = (mid_x - anchor_x); + var handle_dy = (mid_y - anchor_y); + var handle_len = pinescript.sqrt((pinescript.pow(handle_dx, 2) + pinescript.pow(handle_dy, 2))); + var auto_forward_count = 0; if ((handle_dx !== 0)) { - let bars_to_current = (last_bar_index - anchor_x); + var bars_to_current = (last_bar_index - anchor_x); auto_forward_count = int(pinescript.ceil(pinescript.abs((bars_to_current / handle_dx)))); auto_forward_count = pinescript.max(1, auto_forward_count); } - let effective_forward_count = pinescript.max(1, (auto_forward_count + forward_count)); + var effective_forward_count = pinescript.max(1, (auto_forward_count + forward_count)); if ((handle_len > 0)) { - let ux = (handle_dx / handle_len); - let uy = (handle_dy / handle_len); + var ux = (handle_dx / handle_len); + var uy = (handle_dy / handle_len); if ((backward_count > 0)) { for (let i = 0; i <= backward_count; i++) { - let k = -i; - let p_dx = ((float(k) * handle_len) * ux); - let p_dy = ((float(k) * handle_len) * uy); - let p_x = (anchor_x + p_dx); - let p_y = (anchor_y + p_dy); + var k = -i; + var p_dx = ((float(k) * handle_len) * ux); + var p_dy = ((float(k) * handle_len) * uy); + var p_x = (anchor_x + p_dx); + var p_y = (anchor_y + p_dy); prop_x.push(p_x); prop_y.push(p_y); } } for (let i = 1; i <= effective_forward_count; i++) { - let k = i; - let p_dx = ((float(k) * handle_len) * ux); - let p_dy = ((float(k) * handle_len) * uy); - let p_x = (anchor_x + p_dx); - let p_y = (anchor_y + p_dy); + k = i; + p_dx = ((float(k) * handle_len) * ux); + p_dy = ((float(k) * handle_len) * uy); + p_x = (anchor_x + p_dx); + p_y = (anchor_y + p_dy); prop_x.push(p_x); prop_y.push(p_y); } } if (((draw_type !== "None") && (handle_len > 0))) { for (let i = 0; i <= (pinescript.arraySize(prop_x) - 1); i++) { - let p_x = pinescript.arrayGet(prop_x, i); - let p_y = pinescript.arrayGet(prop_y, i); + p_x = pinescript.arrayGet(prop_x, i); + p_y = pinescript.arrayGet(prop_y, i); if ((((draw_type === "Reaction Only") || (draw_type === "Both")) && !pinescript.na(slope_trans))) { - let upper_ix = f_intersect_x(slope_trans, p_x, p_y, slope_ml, upper_x, eff_upper_y); - let upper_iy = (pinescript.na(upper_ix) ? null : ((slope_trans * (upper_ix - p_x)) + p_y)); - let lower_ix = f_intersect_x(slope_trans, p_x, p_y, slope_ml, lower_x, eff_lower_y); - let lower_iy = (pinescript.na(lower_ix) ? null : ((slope_trans * (lower_ix - p_x)) + p_y)); + var upper_ix = f_intersect_x(slope_trans, p_x, p_y, slope_ml, upper_x, eff_upper_y); + var upper_iy = (pinescript.na(upper_ix) ? null : ((slope_trans * (upper_ix - p_x)) + p_y)); + var lower_ix = f_intersect_x(slope_trans, p_x, p_y, slope_ml, lower_x, eff_lower_y); + var lower_iy = (pinescript.na(lower_ix) ? null : ((slope_trans * (lower_ix - p_x)) + p_y)); if (!pinescript.na(upper_ix)) { upper_ix = pinescript.max(min_x, pinescript.min(max_x, upper_ix)); upper_iy = ((slope_trans * (upper_ix - p_x)) + p_y); @@ -2034,8 +2195,8 @@ function main() { lower_iy = ((slope_trans * (lower_ix - p_x)) + p_y); } if (((!pinescript.na(upper_ix) && !pinescript.na(lower_ix)) && (upper_ix !== lower_ix))) { - let draw_upper_iy = (use_log ? pinescript.exp(upper_iy) : upper_iy); - let draw_lower_iy = (use_log ? pinescript.exp(lower_iy) : lower_iy); + var draw_upper_iy = (use_log ? pinescript.exp(upper_iy) : upper_iy); + var draw_lower_iy = (use_log ? pinescript.exp(lower_iy) : lower_iy); pinescript.lineNew(int(pinescript.round(upper_ix)), draw_upper_iy, int(pinescript.round(lower_ix)), draw_lower_iy, ({ extend: extend.none, color: reaction_color, width: reaction_width, style: reaction_line_style })); } } @@ -2053,16 +2214,16 @@ function main() { lower_iy = ((slope_action * (lower_ix - p_x)) + p_y); } if (((!pinescript.na(upper_ix) && !pinescript.na(lower_ix)) && (upper_ix !== lower_ix))) { - let draw_upper_iy = (use_log ? pinescript.exp(upper_iy) : upper_iy); - let draw_lower_iy = (use_log ? pinescript.exp(lower_iy) : lower_iy); + draw_upper_iy = (use_log ? pinescript.exp(upper_iy) : upper_iy); + draw_lower_iy = (use_log ? pinescript.exp(lower_iy) : lower_iy); pinescript.lineNew(int(pinescript.round(upper_ix)), draw_upper_iy, int(pinescript.round(lower_ix)), draw_lower_iy, ({ extend: extend.none, color: action_color, width: action_width, style: action_line_style })); } } } } if (draw_lattice) { - let horiz_y = null; - let pivot_x = null; + var horiz_y = null; + var pivot_x = null; switch (lattice_pivot) { case "A": { @@ -2083,14 +2244,14 @@ function main() { break; } } - let all_inter_xs = pinescript.arrayNew(); - let inter_xs = pinescript.arrayNew(); + var all_inter_xs = pinescript.arrayNew(); + var inter_xs = pinescript.arrayNew(); if ((lattice_source === "Pitchfork & Parallels")) { for (let p = 0; p <= (pinescript.arraySize(pitch_x) - 1); p++) { - let sx = pinescript.arrayGet(pitch_x, p); - let sy = pinescript.arrayGet(pitch_y, p); + var sx = pinescript.arrayGet(pitch_x, p); + var sy = pinescript.arrayGet(pitch_y, p); if ((slope_ml !== 0)) { - let ix = (sx + ((horiz_y - sy) / slope_ml)); + var ix = (sx + ((horiz_y - sy) / slope_ml)); if (!pinescript.na(ix)) { pinescript.arrayPush(inter_xs, ix); } @@ -2099,10 +2260,10 @@ function main() { } if (((lattice_source === "Reaction Lines") || (lattice_source === "Action & Reaction"))) { for (let r = 0; r <= (pinescript.arraySize(prop_x) - 1); r++) { - let px = pinescript.arrayGet(prop_x, r); - let py = pinescript.arrayGet(prop_y, r); + var px = pinescript.arrayGet(prop_x, r); + var py = pinescript.arrayGet(prop_y, r); if ((slope_trans !== 0)) { - let ix = (px + ((horiz_y - py) / slope_trans)); + ix = (px + ((horiz_y - py) / slope_trans)); if (!pinescript.na(ix)) { pinescript.arrayPush(inter_xs, ix); } @@ -2111,10 +2272,10 @@ function main() { } if (((lattice_source === "Action Lines") || (lattice_source === "Action & Reaction"))) { for (let r = 0; r <= (pinescript.arraySize(prop_x) - 1); r++) { - let px = pinescript.arrayGet(prop_x, r); - let py = pinescript.arrayGet(prop_y, r); + px = pinescript.arrayGet(prop_x, r); + py = pinescript.arrayGet(prop_y, r); if ((slope_action !== 0)) { - let ix = (px + ((horiz_y - py) / slope_action)); + ix = (px + ((horiz_y - py) / slope_action)); if (!pinescript.na(ix)) { pinescript.arrayPush(inter_xs, ix); } @@ -2122,26 +2283,26 @@ function main() { } } if ((pinescript.arraySize(inter_xs) > 0)) { - let filtered_inter_xs = pinescript.arrayNew(); + var filtered_inter_xs = pinescript.arrayNew(); for (let f = 0; f <= (pinescript.arraySize(inter_xs) - 1); f++) { - let f_ix = pinescript.arrayGet(inter_xs, f); + var f_ix = pinescript.arrayGet(inter_xs, f); if (((f_ix >= 0) && (f_ix <= max_x))) { pinescript.arrayPush(filtered_inter_xs, f_ix); } } if ((pinescript.arraySize(filtered_inter_xs) > 0)) { - let max_inter = pinescript.arrayMax(filtered_inter_xs); - let min_inter = pinescript.arrayMin(filtered_inter_xs); - let lat_median_y_upper = (anchor_y + (slope_ml * (upper_x - anchor_x))); - let lat_offset_up = (upper_y - lat_median_y_upper); - let lat_median_y_lower = (anchor_y + (slope_ml * (lower_x - anchor_x))); - let lat_offset_lo = (lower_y - lat_median_y_lower); - let lat_eff_upper_y = (upper_y + (state.effective_extra_upper * lat_offset_up)); - let lat_eff_lower_y = (lower_y + (state.effective_extra_lower * lat_offset_lo)); + var max_inter = pinescript.arrayMax(filtered_inter_xs); + var min_inter = pinescript.arrayMin(filtered_inter_xs); + var lat_median_y_upper = (anchor_y + (slope_ml * (upper_x - anchor_x))); + var lat_offset_up = (upper_y - lat_median_y_upper); + var lat_median_y_lower = (anchor_y + (slope_ml * (lower_x - anchor_x))); + var lat_offset_lo = (lower_y - lat_median_y_lower); + var lat_eff_upper_y = (upper_y + (state.effective_extra_upper * lat_offset_up)); + var lat_eff_lower_y = (lower_y + (state.effective_extra_lower * lat_offset_lo)); if ((!pinescript.na(slope_ml) && (slope_ml !== 0))) { - let upper_bound_x = (upper_x + ((horiz_y - lat_eff_upper_y) / slope_ml)); - let lower_bound_x = (lower_x + ((horiz_y - lat_eff_lower_y) / slope_ml)); - let envelope_max_x = null; + var upper_bound_x = (upper_x + ((horiz_y - lat_eff_upper_y) / slope_ml)); + var lower_bound_x = (lower_x + ((horiz_y - lat_eff_lower_y) / slope_ml)); + var envelope_max_x = null; if (((upper_bound_x >= pivot_x) && (lower_bound_x >= pivot_x))) { envelope_max_x = pinescript.min(upper_bound_x, lower_bound_x); } else { @@ -2155,13 +2316,13 @@ function main() { } } } - let draw_max_x = max_inter; + var draw_max_x = max_inter; if (!pinescript.na(envelope_max_x)) { draw_max_x = pinescript.min(max_inter, envelope_max_x); } - let horiz_start_x = pivot_x; + var horiz_start_x = pivot_x; if (((int(pinescript.round(draw_max_x)) <= (last_bar_index + MAX_BAR_EXTENSION)) && (int(pinescript.round(horiz_start_x)) <= (last_bar_index + MAX_BAR_EXTENSION)))) { - let draw_horiz_y = (use_log ? pinescript.exp(horiz_y) : horiz_y); + var draw_horiz_y = (use_log ? pinescript.exp(horiz_y) : horiz_y); pinescript.lineNew(int(pinescript.round(horiz_start_x)), draw_horiz_y, int(pinescript.round(draw_max_x)), draw_horiz_y, ({ color: lattice_color, width: lattice_width, style: lattice_line_style })); } } @@ -2172,26 +2333,26 @@ function main() { } if ((pinescript.arraySize(all_inter_xs) > 0)) { pinescript.arraySort(all_inter_xs); - let unique_xs = pinescript.arrayNew(); - let last = pinescript.arrayGet(all_inter_xs, 0); + var unique_xs = pinescript.arrayNew(); + var last = pinescript.arrayGet(all_inter_xs, 0); pinescript.arrayPush(unique_xs, last); for (let uu = 1; uu <= (pinescript.arraySize(all_inter_xs) - 1); uu++) { - let curr = pinescript.arrayGet(all_inter_xs, uu); + var curr = pinescript.arrayGet(all_inter_xs, uu); if ((curr !== last)) { pinescript.arrayPush(unique_xs, curr); last = curr; } } for (let vv = 0; vv <= (pinescript.arraySize(unique_xs) - 1); vv++) { - let v_x = pinescript.arrayGet(unique_xs, vv); + var v_x = pinescript.arrayGet(unique_xs, vv); if ((((v_x >= pivot_x) && (v_x >= min_x)) && (v_x <= max_x))) { if ((!pinescript.na(outer_upper_start_x) && !pinescript.na(outer_lower_start_x))) { - let y_max = (outer_upper_start_y + (slope_ml * (v_x - outer_upper_start_x))); - let y_min = (outer_lower_start_y + (slope_ml * (v_x - outer_lower_start_x))); - let top = pinescript.max(y_min, y_max); - let bot = pinescript.min(y_min, y_max); - let draw_top = (use_log ? pinescript.exp(top) : top); - let draw_bot = (use_log ? pinescript.exp(bot) : bot); + var y_max = (outer_upper_start_y + (slope_ml * (v_x - outer_upper_start_x))); + var y_min = (outer_lower_start_y + (slope_ml * (v_x - outer_lower_start_x))); + var top = pinescript.max(y_min, y_max); + var bot = pinescript.min(y_min, y_max); + var draw_top = (use_log ? pinescript.exp(top) : top); + var draw_bot = (use_log ? pinescript.exp(bot) : bot); pinescript.lineNew(int(pinescript.round(v_x)), draw_bot, int(pinescript.round(v_x)), draw_top, ({ color: lattice_color, width: lattice_width, style: lattice_line_style })); } } @@ -2199,37 +2360,37 @@ function main() { } } } - let price_y = (use_log ? pinescript.log(close) : close); - let prev_price_y = (use_log ? pinescript.log(pinescript.hist(18, close, 1)) : pinescript.hist(19, close, 1)); - let median_y_at_upper = (anchor_y + (slope_ml * (upper_x - anchor_x))); - let alert_offset_up = (upper_y - median_y_at_upper); - let median_y_at_lower = (anchor_y + (slope_ml * (lower_x - anchor_x))); - let alert_offset_lo = (lower_y - median_y_at_lower); - let crossed_pitchfork = false; - let crossed_action = false; - let crossed_reaction = false; - let crossed_lattice_h = false; + var price_y = (use_log ? pinescript.log(close) : close); + var prev_price_y = (use_log ? pinescript.log(pinescript.hist(18, close, 1)) : pinescript.hist(19, close, 1)); + var median_y_at_upper = (anchor_y + (slope_ml * (upper_x - anchor_x))); + var alert_offset_up = (upper_y - median_y_at_upper); + var median_y_at_lower = (anchor_y + (slope_ml * (lower_x - anchor_x))); + var alert_offset_lo = (lower_y - median_y_at_lower); + var crossed_pitchfork = false; + var crossed_action = false; + var crossed_reaction = false; + var crossed_lattice_h = false; if (((!pinescript.na(anchor_x) && !pinescript.na(slope_ml)) && (bar_index > pivotC_x))) { - let ml_y_curr = f_line_y_at_bar(anchor_x, anchor_y, slope_ml, bar_index); - let ml_y_prev = f_line_y_at_bar(anchor_x, anchor_y, slope_ml, (bar_index - 1)); + var ml_y_curr = f_line_y_at_bar(anchor_x, anchor_y, slope_ml, bar_index); + var ml_y_prev = f_line_y_at_bar(anchor_x, anchor_y, slope_ml, (bar_index - 1)); if (f_price_crossed_line(prev_price_y, price_y, ml_y_prev, ml_y_curr)) { crossed_pitchfork = true; } - let up_y_curr = f_line_y_at_bar(upper_x, upper_y, slope_ml, bar_index); - let up_y_prev = f_line_y_at_bar(upper_x, upper_y, slope_ml, (bar_index - 1)); + var up_y_curr = f_line_y_at_bar(upper_x, upper_y, slope_ml, bar_index); + var up_y_prev = f_line_y_at_bar(upper_x, upper_y, slope_ml, (bar_index - 1)); if (f_price_crossed_line(prev_price_y, price_y, up_y_prev, up_y_curr)) { crossed_pitchfork = true; } - let lo_y_curr = f_line_y_at_bar(lower_x, lower_y, slope_ml, bar_index); - let lo_y_prev = f_line_y_at_bar(lower_x, lower_y, slope_ml, (bar_index - 1)); + var lo_y_curr = f_line_y_at_bar(lower_x, lower_y, slope_ml, bar_index); + var lo_y_prev = f_line_y_at_bar(lower_x, lower_y, slope_ml, (bar_index - 1)); if (f_price_crossed_line(prev_price_y, price_y, lo_y_prev, lo_y_curr)) { crossed_pitchfork = true; } if ((state.effective_extra_upper > 0)) { for (let i = 1; i <= state.effective_extra_upper; i++) { - let extra_up_ref_y = (upper_y + (float(i) * alert_offset_up)); - let ex_up_y_curr = f_line_y_at_bar(upper_x, extra_up_ref_y, slope_ml, bar_index); - let ex_up_y_prev = f_line_y_at_bar(upper_x, extra_up_ref_y, slope_ml, (bar_index - 1)); + var extra_up_ref_y = (upper_y + (float(i) * alert_offset_up)); + var ex_up_y_curr = f_line_y_at_bar(upper_x, extra_up_ref_y, slope_ml, bar_index); + var ex_up_y_prev = f_line_y_at_bar(upper_x, extra_up_ref_y, slope_ml, (bar_index - 1)); if (f_price_crossed_line(prev_price_y, price_y, ex_up_y_prev, ex_up_y_curr)) { crossed_pitchfork = true; } @@ -2237,43 +2398,43 @@ function main() { } if ((state.effective_extra_lower > 0)) { for (let i = 1; i <= state.effective_extra_lower; i++) { - let extra_lo_ref_y = (lower_y + (float(i) * alert_offset_lo)); - let ex_lo_y_curr = f_line_y_at_bar(lower_x, extra_lo_ref_y, slope_ml, bar_index); - let ex_lo_y_prev = f_line_y_at_bar(lower_x, extra_lo_ref_y, slope_ml, (bar_index - 1)); + var extra_lo_ref_y = (lower_y + (float(i) * alert_offset_lo)); + var ex_lo_y_curr = f_line_y_at_bar(lower_x, extra_lo_ref_y, slope_ml, bar_index); + var ex_lo_y_prev = f_line_y_at_bar(lower_x, extra_lo_ref_y, slope_ml, (bar_index - 1)); if (f_price_crossed_line(prev_price_y, price_y, ex_lo_y_prev, ex_lo_y_curr)) { crossed_pitchfork = true; } } } - let alert_handle_dx = (mid_x - anchor_x); - let alert_handle_dy = (mid_y - anchor_y); - let alert_handle_len = pinescript.sqrt((pinescript.pow(alert_handle_dx, 2) + pinescript.pow(alert_handle_dy, 2))); - let auto_alert_forward = 0; + var alert_handle_dx = (mid_x - anchor_x); + var alert_handle_dy = (mid_y - anchor_y); + var alert_handle_len = pinescript.sqrt((pinescript.pow(alert_handle_dx, 2) + pinescript.pow(alert_handle_dy, 2))); + var auto_alert_forward = 0; if ((alert_handle_dx !== 0)) { - let alert_bars_to_current = (last_bar_index - anchor_x); + var alert_bars_to_current = (last_bar_index - anchor_x); auto_alert_forward = int(pinescript.ceil(pinescript.abs((alert_bars_to_current / alert_handle_dx)))); auto_alert_forward = pinescript.max(1, auto_alert_forward); } - let alert_effective_forward = pinescript.max(1, (auto_alert_forward + forward_count)); + var alert_effective_forward = pinescript.max(1, (auto_alert_forward + forward_count)); if (((alert_handle_len > 0) && (draw_type !== "None"))) { - let alert_ux = (alert_handle_dx / alert_handle_len); - let alert_uy = (alert_handle_dy / alert_handle_len); - let eff_up_y = (upper_y + (state.effective_extra_upper * alert_offset_up)); - let eff_lo_y = (lower_y + (state.effective_extra_lower * alert_offset_lo)); + var alert_ux = (alert_handle_dx / alert_handle_len); + var alert_uy = (alert_handle_dy / alert_handle_len); + var eff_up_y = (upper_y + (state.effective_extra_upper * alert_offset_up)); + var eff_lo_y = (lower_y + (state.effective_extra_lower * alert_offset_lo)); if ((backward_count > 0)) { for (let i = 0; i <= backward_count; i++) { - let k = -i; - let p_x = (anchor_x + ((float(k) * alert_handle_len) * alert_ux)); - let p_y = (anchor_y + ((float(k) * alert_handle_len) * alert_uy)); + k = -i; + p_x = (anchor_x + ((float(k) * alert_handle_len) * alert_ux)); + p_y = (anchor_y + ((float(k) * alert_handle_len) * alert_uy)); if ((((draw_type === "Reaction Only") || (draw_type === "Both")) && !pinescript.na(slope_trans))) { - let rx_upper = f_intersect_x(slope_trans, p_x, p_y, slope_ml, upper_x, eff_up_y); - let rx_lower = f_intersect_x(slope_trans, p_x, p_y, slope_ml, lower_x, eff_lo_y); + var rx_upper = f_intersect_x(slope_trans, p_x, p_y, slope_ml, upper_x, eff_up_y); + var rx_lower = f_intersect_x(slope_trans, p_x, p_y, slope_ml, lower_x, eff_lo_y); if ((!pinescript.na(rx_upper) && !pinescript.na(rx_lower))) { - let seg_min_x = pinescript.min(rx_upper, rx_lower); - let seg_max_x = pinescript.max(rx_upper, rx_lower); + var seg_min_x = pinescript.min(rx_upper, rx_lower); + var seg_max_x = pinescript.max(rx_upper, rx_lower); if (((bar_index >= seg_min_x) && (bar_index <= seg_max_x))) { - let ry_curr = (p_y + (slope_trans * (bar_index - p_x))); - let ry_prev = (p_y + (slope_trans * ((bar_index - 1) - p_x))); + var ry_curr = (p_y + (slope_trans * (bar_index - p_x))); + var ry_prev = (p_y + (slope_trans * ((bar_index - 1) - p_x))); if (f_price_crossed_line(prev_price_y, price_y, ry_prev, ry_curr)) { crossed_reaction = true; } @@ -2281,14 +2442,14 @@ function main() { } } if ((((draw_type === "Action Only") || (draw_type === "Both")) && !pinescript.na(slope_action))) { - let ax_upper = f_intersect_x(slope_action, p_x, p_y, slope_ml, upper_x, eff_up_y); - let ax_lower = f_intersect_x(slope_action, p_x, p_y, slope_ml, lower_x, eff_lo_y); + var ax_upper = f_intersect_x(slope_action, p_x, p_y, slope_ml, upper_x, eff_up_y); + var ax_lower = f_intersect_x(slope_action, p_x, p_y, slope_ml, lower_x, eff_lo_y); if ((!pinescript.na(ax_upper) && !pinescript.na(ax_lower))) { - let seg_min_x = pinescript.min(ax_upper, ax_lower); - let seg_max_x = pinescript.max(ax_upper, ax_lower); + seg_min_x = pinescript.min(ax_upper, ax_lower); + seg_max_x = pinescript.max(ax_upper, ax_lower); if (((bar_index >= seg_min_x) && (bar_index <= seg_max_x))) { - let ay_curr = (p_y + (slope_action * (bar_index - p_x))); - let ay_prev = (p_y + (slope_action * ((bar_index - 1) - p_x))); + var ay_curr = (p_y + (slope_action * (bar_index - p_x))); + var ay_prev = (p_y + (slope_action * ((bar_index - 1) - p_x))); if (f_price_crossed_line(prev_price_y, price_y, ay_prev, ay_curr)) { crossed_action = true; } @@ -2298,18 +2459,18 @@ function main() { } } for (let i = 1; i <= alert_effective_forward; i++) { - let k = i; - let p_x = (anchor_x + ((float(k) * alert_handle_len) * alert_ux)); - let p_y = (anchor_y + ((float(k) * alert_handle_len) * alert_uy)); + k = i; + p_x = (anchor_x + ((float(k) * alert_handle_len) * alert_ux)); + p_y = (anchor_y + ((float(k) * alert_handle_len) * alert_uy)); if ((((draw_type === "Reaction Only") || (draw_type === "Both")) && !pinescript.na(slope_trans))) { - let rx_upper = f_intersect_x(slope_trans, p_x, p_y, slope_ml, upper_x, eff_up_y); - let rx_lower = f_intersect_x(slope_trans, p_x, p_y, slope_ml, lower_x, eff_lo_y); + rx_upper = f_intersect_x(slope_trans, p_x, p_y, slope_ml, upper_x, eff_up_y); + rx_lower = f_intersect_x(slope_trans, p_x, p_y, slope_ml, lower_x, eff_lo_y); if ((!pinescript.na(rx_upper) && !pinescript.na(rx_lower))) { - let seg_min_x = pinescript.min(rx_upper, rx_lower); - let seg_max_x = pinescript.max(rx_upper, rx_lower); + seg_min_x = pinescript.min(rx_upper, rx_lower); + seg_max_x = pinescript.max(rx_upper, rx_lower); if (((bar_index >= seg_min_x) && (bar_index <= seg_max_x))) { - let ry_curr = (p_y + (slope_trans * (bar_index - p_x))); - let ry_prev = (p_y + (slope_trans * ((bar_index - 1) - p_x))); + ry_curr = (p_y + (slope_trans * (bar_index - p_x))); + ry_prev = (p_y + (slope_trans * ((bar_index - 1) - p_x))); if (f_price_crossed_line(prev_price_y, price_y, ry_prev, ry_curr)) { crossed_reaction = true; } @@ -2317,14 +2478,14 @@ function main() { } } if ((((draw_type === "Action Only") || (draw_type === "Both")) && !pinescript.na(slope_action))) { - let ax_upper = f_intersect_x(slope_action, p_x, p_y, slope_ml, upper_x, eff_up_y); - let ax_lower = f_intersect_x(slope_action, p_x, p_y, slope_ml, lower_x, eff_lo_y); + ax_upper = f_intersect_x(slope_action, p_x, p_y, slope_ml, upper_x, eff_up_y); + ax_lower = f_intersect_x(slope_action, p_x, p_y, slope_ml, lower_x, eff_lo_y); if ((!pinescript.na(ax_upper) && !pinescript.na(ax_lower))) { - let seg_min_x = pinescript.min(ax_upper, ax_lower); - let seg_max_x = pinescript.max(ax_upper, ax_lower); + seg_min_x = pinescript.min(ax_upper, ax_lower); + seg_max_x = pinescript.max(ax_upper, ax_lower); if (((bar_index >= seg_min_x) && (bar_index <= seg_max_x))) { - let ay_curr = (p_y + (slope_action * (bar_index - p_x))); - let ay_prev = (p_y + (slope_action * ((bar_index - 1) - p_x))); + ay_curr = (p_y + (slope_action * (bar_index - p_x))); + ay_prev = (p_y + (slope_action * ((bar_index - 1) - p_x))); if (f_price_crossed_line(prev_price_y, price_y, ay_prev, ay_curr)) { crossed_action = true; } @@ -2337,7 +2498,7 @@ function main() { pivotC_y; pivotB_y; pivotA_y; - let lattice_horiz_y = ((lattice_pivot === "A") ? undefined : ((lattice_pivot === "B") ? undefined : ((lattice_pivot === "C") ? undefined : null))); + var lattice_horiz_y = ((lattice_pivot === "A") ? undefined : ((lattice_pivot === "B") ? undefined : ((lattice_pivot === "C") ? undefined : null))); if (!pinescript.na(lattice_horiz_y)) { if (f_price_crossed_line(prev_price_y, price_y, lattice_horiz_y, lattice_horiz_y)) { crossed_lattice_h = true; @@ -2394,6 +2555,10 @@ function run(data, options = {}) { rt.__shapeIdx = 0; globalThis.bar_index = i; globalThis.last_bar_index = n - 1; + globalThis.time_tradingday = inTime[i]; + globalThis.time_close = inTime[i]; + globalThis.last_bar_time = inTime[n - 1]; + globalThis.timenow = inTime[n - 1]; globalThis.barstate = { isfirst: i === 0, islast: i === n - 1, isrealtime: false, ishistory: true, isconfirmed: true, isnew: true, islastconfirmedhistory: i === n - 1, diff --git a/converted/Impulse_Trend_Arrows.js b/converted/Impulse_Trend_Arrows.js index d00cdcf..5e90cb6 100644 --- a/converted/Impulse_Trend_Arrows.js +++ b/converted/Impulse_Trend_Arrows.js @@ -42,6 +42,80 @@ const pinescript = { if (value != null && typeof value[Symbol.iterator] === 'function') return Array.from(value); return new Array(count || 0).fill(null); }, + __decArr: function(arr) { + if (!Array.isArray(arr) || arr.__pineDecorated) return arr; + const self = this; + const def = (name, fn) => Object.defineProperty(arr, name, { value: fn, writable: true, configurable: true }); + Object.defineProperty(arr, '__pineDecorated', { value: true, configurable: true }); + def('get', (i) => (arr[i] === undefined ? null : arr[i])); + def('set', (i, v) => { arr[i] = v; return v; }); + def('size', () => arr.length); + def('clear', () => { arr.length = 0; }); + def('insert', (i, v) => { arr.splice(i, 0, v); }); + def('remove', (i) => arr.splice(i, 1)[0]); + def('contains', (v) => arr.includes(v)); + def('indexof', (v) => arr.indexOf(v)); + def('lastindexof', (v) => arr.lastIndexOf(v)); + def('first', () => (arr.length ? arr[0] : null)); + def('last', () => (arr.length ? arr[arr.length - 1] : null)); + def('sum', () => arr.reduce((a, b) => a + b, 0)); + def('avg', () => (arr.length ? arr.reduce((a, b) => a + b, 0) / arr.length : 0)); + def('min', () => (arr.length ? Math.min(...arr) : null)); + def('max', () => (arr.length ? Math.max(...arr) : null)); + def('range', () => (arr.length ? Math.max(...arr) - Math.min(...arr) : null)); + // Statistical methods that route to the array.* built-ins (these names are not + // native to JS arrays, so attaching them here is safe from recursion). + def('median', () => self.arrayMedian(arr)); + def('mode', () => self.arrayMode(arr)); + def('stdev', () => self.arrayStdev(arr)); + def('variance', () => self.arrayVariance(arr)); + def('covariance', (other) => self.arrayCovariance(arr, other)); + def('percentile_linear_interpolation', (p) => self.arrayPercentileLinearInterpolation(arr, p)); + def('percentile_nearest_rank', (p) => self.arrayPercentileNearestRank(arr, p)); + def('abs', () => self.__decArr(self.arrayAbs(arr))); + def('binary_search', (v) => self.arrayBinarySearch(arr, v)); + // Pine's array.sort takes an order string, not a comparator. Use the native + // sort via .call so we don't recurse through this overridden method. + def('sort', (order) => { Array.prototype.sort.call(arr, (a, b) => (order === 'descending' ? b - a : a - b)); return arr; }); + def('sort_indices', (order) => self.__decArr(arr.map((_, i) => i).sort((a, b) => (order === 'descending' ? arr[b] - arr[a] : arr[a] - arr[b])))); + // join/slice/reverse/concat/includes/fill already exist natively on Array with + // compatible semantics, so we deliberately leave them to the native methods. + return arr; + }, + __decMap: function(m) { + if (!(m instanceof Map) || m.__pineDecorated) return m; + const def = (name, fn) => Object.defineProperty(m, name, { value: fn, writable: true, configurable: true }); + Object.defineProperty(m, '__pineDecorated', { value: true, configurable: true }); + def('put', (k, v) => { m.set(k, v); return v; }); + def('contains', (k) => m.has(k)); + def('remove', (k) => m.delete(k)); + def('keys', () => Array.from(Map.prototype.keys.call(m))); + def('values', () => Array.from(Map.prototype.values.call(m))); + def('size_', () => m.size); + return m; + }, + __decMatrix: function(m) { + if (!m || typeof m !== 'object' || m.__pineDecorated) return m; + const self = this; + const def = (name, fn) => Object.defineProperty(m, name, { value: fn, writable: true, configurable: true }); + Object.defineProperty(m, '__pineDecorated', { value: true, configurable: true }); + def('get', (r, c) => self.matrixGet(m, r, c)); + def('set', (r, c, v) => self.matrixSet(m, r, c, v)); + def('rows_', () => m.rows); + def('columns', () => m.cols); + def('fill', (v) => self.matrixFill(m, v)); + return m; + }, + __decDraw: function(obj) { + if (!obj || typeof obj !== 'object') return obj; + const p = new Proxy(obj, { + get(t, k) { + if (k in t || typeof k === 'symbol') return t[k]; + return function() { return p; }; + }, + }); + return p; + }, alertcondition: function(condition, ...rest) { if (globalThis.__pineRuntime) { globalThis.__pineRuntime.alerts.push({ condition, args: rest }); @@ -49,6 +123,32 @@ const pinescript = { return null; }, barcolor: function(color) { return null; }, + plotchar: function(series, ...rest) { + const rt = globalThis.__pineRuntime; + if (!rt) return series; + const bar = rt.__barIndex | 0; + const ord = (rt.__shapeIdx = (rt.__shapeIdx | 0) + 1) - 1; + let key = 'char_' + ord; + for (const r of rest) { + if (typeof r === 'string') { key = r; break; } + if (r && typeof r === 'object' && r.title) { key = String(r.title); break; } + } + let s = rt.plotshapes[key]; + if (!s) s = rt.plotshapes[key] = { title: key, data: [] }; + s.data[bar] = this.__scalar(series); + return series; + }, + plotarrow: function(series, ...rest) { + const rt = globalThis.__pineRuntime; + if (!rt) return series; + const bar = rt.__barIndex | 0; + const ord = (rt.__plotIdx = (rt.__plotIdx | 0) + 1) - 1; + const key = 'arrow_' + ord; + let p = rt.plots[key]; + if (!p) p = rt.plots[key] = { title: key, data: [] }; + p.data[bar] = this.__scalar(series); + return series; + }, bgcolor: function(color, title, editable, showLast) { return null; }, @@ -470,6 +570,12 @@ const pinescript = { atan: function(value) { return Math.atan(value); }, + todegrees: function(radians) { + return radians * (180 / Math.PI); + }, + toradians: function(degrees) { + return degrees * (Math.PI / 180); + }, floor: function(value) { return Math.floor(value); }, @@ -641,7 +747,7 @@ const pinescript = { return series; }, lineNew: function(x1, y1, x2, y2, opts = {}) { - return { x1, y1, x2, y2, opts, _type: 'line' }; + return this.__decDraw({ x1, y1, x2, y2, opts, _type: 'line' }); }, lineDelete: function(l) { return null; @@ -661,7 +767,7 @@ const pinescript = { return point === 0 || point === 'y1' ? line.y1 : line.y2; }, labelNew: function(x, y, text = '', opts = {}) { - return { x, y, text, opts, _type: 'label' }; + return this.__decDraw({ x, y, text, opts, _type: 'label' }); }, labelDelete: function(l) { return null; @@ -676,7 +782,7 @@ const pinescript = { return label.text || ''; }, boxNew: function(left, top, right, bottom, opts = {}) { - return { left, top, right, bottom, opts, _type: 'box' }; + return this.__decDraw({ left, top, right, bottom, opts, _type: 'box' }); }, boxDelete: function(box) { return null; @@ -694,7 +800,7 @@ const pinescript = { return box; }, polylineNew: function(points, opts = {}) { - return { points: points || [], opts, _type: 'polyline' }; + return this.__decDraw({ points: points || [], opts, _type: 'polyline' }); }, polylineDelete: function(poly) { return null; @@ -765,7 +871,7 @@ const pinescript = { return { time: _time ?? null, price: _price ?? null }; }, mapNew: function() { - return new Map(); + return this.__decMap(new Map()); }, mapSize: function(m) { return m instanceof Map ? m.size : 0; @@ -800,7 +906,7 @@ const pinescript = { const r = Math.max(0, rows ?? 0); const c = Math.max(0, cols ?? 0); const data = Array.from({ length: r }, () => Array.from({ length: c }, () => initialValue)); - return { rows: r, cols: c, data }; + return this.__decMatrix({ rows: r, cols: c, data }); }, matrixRows: function(m) { return m?.rows ?? 0; @@ -923,11 +1029,62 @@ const pinescript = { } return null; }, + __jacobiEigen: function(m) { + if (!m || !Array.isArray(m.data)) return null; + const n = m.rows ?? 0; + if (n === 0 || n !== (m.cols ?? 0)) return null; + // Work on a copy so the input matrix is untouched. + const a = m.data.map(row => row.slice()); + const v = Array.from({ length: n }, (_, i) => Array.from({ length: n }, (_, j) => (i === j ? 1 : 0))); + for (let sweep = 0; sweep < 100; sweep++) { + let off = 0; + for (let p = 0; p < n; p++) for (let q = p + 1; q < n; q++) off += a[p][q] * a[p][q]; + if (off < 1e-20) break; + for (let p = 0; p < n; p++) { + for (let q = p + 1; q < n; q++) { + if (Math.abs(a[p][q]) < 1e-18) continue; + const theta = (a[q][q] - a[p][p]) / (2 * a[p][q]); + const t = Math.sign(theta || 1) / (Math.abs(theta) + Math.sqrt(theta * theta + 1)); + const cos = 1 / Math.sqrt(t * t + 1); + const sin = t * cos; + for (let k = 0; k < n; k++) { + const akp = a[k][p], akq = a[k][q]; + a[k][p] = cos * akp - sin * akq; + a[k][q] = sin * akp + cos * akq; + } + for (let k = 0; k < n; k++) { + const apk = a[p][k], aqk = a[q][k]; + a[p][k] = cos * apk - sin * aqk; + a[q][k] = sin * apk + cos * aqk; + } + for (let k = 0; k < n; k++) { + const vkp = v[k][p], vkq = v[k][q]; + v[k][p] = cos * vkp - sin * vkq; + v[k][q] = sin * vkp + cos * vkq; + } + } + } + } + // Sort eigenpairs by eigenvalue, descending. + const order = Array.from({ length: n }, (_, i) => i).sort((i, j) => a[j][j] - a[i][i]); + const values = order.map(i => a[i][i]); + const vectors = Array.from({ length: n }, (_, r) => order.map(c => v[r][c])); + return { values, vectors }; + }, + matrixEigenvalues: function(m) { + const e = this.__jacobiEigen(m); + return this.__decArr(e ? e.values : []); + }, + matrixEigenvectors: function(m) { + const e = this.__jacobiEigen(m); + if (!e) return this.__decMatrix({ rows: 0, cols: 0, data: [] }); + return this.__decMatrix({ rows: e.vectors.length, cols: e.vectors.length, data: e.vectors }); + }, requestSecurity: function(symbol, timeframe, expression) { return expression; }, arrayNew: function(initialSize = 0, initialValue = 0) { - return Array(initialSize).fill(initialValue); + return this.__decArr(Array(initialSize).fill(initialValue)); }, arraySize: function(arr) { return arr ? arr.length : 0; @@ -978,10 +1135,10 @@ const pinescript = { return arr; }, arraySlice: function(arr, startIndex = 0, endIndex = null) { - if (!arr) return []; + if (!arr) return this.__decArr([]); const start = Number(startIndex) || 0; const end = endIndex === null || endIndex === undefined ? arr.length : Number(endIndex) || 0; - return arr.slice(start, end); + return this.__decArr(arr.slice(start, end)); }, arraySort: function(arr, order = 'ascending') { if (arr) arr.sort((a, b) => order === 'ascending' ? a - b : b - a); @@ -1191,7 +1348,7 @@ const pinescript = { const info = { ticker: 'AAPL', tickerid: 'NASDAQ:AAPL', prefix: 'NASDAQ', root: 'AAPL', suffix: '' }; return info[type] || ''; }, - timenow: 1781574527280, + timenow: 1782783576477, barstate: "LAST", dividends: {}, splits: {}, @@ -1245,11 +1402,11 @@ const pinescript = { arrayConcat: function(arr1, arr2) { if (!arr1) return arr2 || []; if (!arr2) return arr1; - return arr1.concat(arr2); + return this.__decArr(arr1.concat(arr2)); }, arrayCopy: function(arr) { - if (!arr) return []; - return [...arr]; + if (!arr) return this.__decArr([]); + return this.__decArr([...arr]); }, arrayBinarySearch: function(arr, value) { if (!arr || arr.length === 0) return -1; @@ -1339,7 +1496,7 @@ globalThis.input.timeframe = globalThis.input.timeframe || ((defval) => defval); globalThis.array = globalThis.array || { - from: (...items) => items, + from: (...items) => pinescript.__decArr(items), size: (arr) => pinescript.arraySize(arr), @@ -1508,6 +1665,8 @@ globalThis.line = globalThis.line || __pineNS({ style_solid: "solid", style_dash globalThis.box = globalThis.box || __pineNS({}); +globalThis.color = globalThis.color || __pineNS(Object.assign(function(c) { return c; }, { new: function(c, t) { return c; }, rgb: function(r, g, b, t) { return "#rgb(" + [r, g, b].join(",") + ")"; }, from_gradient: function(v, lo, hi, c1, c2) { return c1; }, r: function() { return 0; }, g: function() { return 0; }, b: function() { return 0; }, t: function() { return 0; }, aqua: "#00BCD4", black: "#363A45", blue: "#2962FF", fuchsia: "#E040FB", gray: "#787B86", green: "#4CAF50", lime: "#00E676", maroon: "#880E4F", navy: "#311B92", olive: "#808000", orange: "#FF9800", purple: "#9C27B0", red: "#FF5252", silver: "#B2B5BE", teal: "#00897B", white: "#FFFFFF", yellow: "#FFEB3B" })); + globalThis.label = globalThis.label || __pineNS({ style_label_down: "label_down", style_label_up: "label_up", style_none: "none" }); globalThis.polyline = globalThis.polyline || __pineNS({}); @@ -1520,7 +1679,9 @@ globalThis.map = globalThis.map || __pineNS({}); globalThis.session = globalThis.session || __pineNS({ regular: "regular", extended: "extended" }); -globalThis.dayofweek = globalThis.dayofweek || __pineNS({ sunday: 1, monday: 2, tuesday: 3, wednesday: 4, thursday: 5, friday: 6, saturday: 7 }); +globalThis.ticker = globalThis.ticker || __pineNS({}); + +globalThis.dayofweek = globalThis.dayofweek || __pineNS(Object.assign(function(t) { return new Date(t != null ? t : (globalThis.time || 0)).getUTCDay() + 1; }, { sunday: 1, monday: 2, tuesday: 3, wednesday: 4, thursday: 5, friday: 6, saturday: 7 })); globalThis.timeframe = __pineNS(Object.assign(globalThis.timeframe || {}, { period: (globalThis.timeframe && globalThis.timeframe.period) || "D", isintraday: false, isdaily: true, multiplier: 1 })); @@ -1568,8 +1729,6 @@ globalThis.bool = globalThis.bool || function(x) { return Boolean(x); }; globalThis.string = globalThis.string || function(x) { return x == null ? null : String(x); }; -globalThis.dayofweek = globalThis.dayofweek || __pineNS({ sunday: 1, monday: 2, tuesday: 3, wednesday: 4, thursday: 5, friday: 6, saturday: 7 }); - globalThis.str = globalThis.str || __pineNS({}); pinescript.math = globalThis.math; @@ -1655,7 +1814,7 @@ pinescript.text = { align_center: "center" }; pinescript.table = { - new: function(position, columns, rows, opts) { return { position, columns, rows, opts: opts || {}, cells: [] }; }, + new: function(position, columns, rows, opts) { return pinescript.__decDraw({ position, columns, rows, opts: opts || {}, cells: [] }); }, cell: function(table, column, row, text, opts) { @@ -1688,38 +1847,38 @@ function main() { null; // Study: Impulse Trend Arrows // Options: {"shorttitle":"Impulse Trend Arrows","overlay":true} - let basisLen = pinescript.inputInt(50, "Adaptive Baseline Length"); - let showBands = pinescript.inputBool(true, "Show ATR Bands"); - let showBg = pinescript.inputBool(true, "Background Shading"); - let showArrows = pinescript.inputBool(true, "Show Entry Arrows"); - let smoothLen = pinescript.inputInt(8, "Channel Smoothing", ({ minval: 1 })); - let baseColorIn = pinescript.inputColor(pinescript.color.new(pinescript.color.hex("#5f5f5f"), 0), "Baseline Color"); - let upColorIn = pinescript.inputColor(pinescript.color.new(pinescript.color.hex("#000000"), 0), "Upper Band Color"); - let dnColorIn = pinescript.inputColor(pinescript.color.new(pinescript.color.hex("#000000"), 0), "Lower Band Color"); - let fillColorIn = pinescript.inputColor(pinescript.color.new(pinescript.color.blue, 75), "Channel Fill Color"); - let atrLen = 14; - let atrMult = 2; - let rsiLen = 14; - let macdFast = 12; - let macdSlow = 26; - let macdSig = 9; - let basis = pinescript.ema(close, basisLen); - let atr = pinescript.atr(atrLen); - let upBand = (basis + (atrMult * atr)); - let dnBand = (basis - (atrMult * atr)); - let impulseUp = ((close > basis) && (close > upBand)); - let impulseDn = ((close < basis) && (close < dnBand)); - let rsi = pinescript.rsi(close, rsiLen); - let macdLine = (pinescript.ema(close, macdFast) - pinescript.ema(close, macdSlow)); - let macdSigLn = pinescript.ema(macdLine, macdSig); - let macdHist = (macdLine - macdSigLn); - let momUp = ((rsi > 50) && (macdHist > 0)); - let momDn = ((rsi < 50) && (macdHist < 0)); - let longCond = (impulseUp && momUp); - let shortCond = (impulseDn && momDn); + var basisLen = pinescript.inputInt(50, "Adaptive Baseline Length"); + var showBands = pinescript.inputBool(true, "Show ATR Bands"); + var showBg = pinescript.inputBool(true, "Background Shading"); + var showArrows = pinescript.inputBool(true, "Show Entry Arrows"); + var smoothLen = pinescript.inputInt(8, "Channel Smoothing", ({ minval: 1 })); + var baseColorIn = pinescript.inputColor(pinescript.color.new(pinescript.color.hex("#5f5f5f"), 0), "Baseline Color"); + var upColorIn = pinescript.inputColor(pinescript.color.new(pinescript.color.hex("#000000"), 0), "Upper Band Color"); + var dnColorIn = pinescript.inputColor(pinescript.color.new(pinescript.color.hex("#000000"), 0), "Lower Band Color"); + var fillColorIn = pinescript.inputColor(pinescript.color.new(pinescript.color.blue, 75), "Channel Fill Color"); + var atrLen = 14; + var atrMult = 2; + var rsiLen = 14; + var macdFast = 12; + var macdSlow = 26; + var macdSig = 9; + var basis = pinescript.ema(pinescript.series(0, close), basisLen); + var atr = pinescript.atr(atrLen); + var upBand = (basis + (atrMult * atr)); + var dnBand = (basis - (atrMult * atr)); + var impulseUp = ((close > basis) && (close > upBand)); + var impulseDn = ((close < basis) && (close < dnBand)); + var rsi = pinescript.rsi(pinescript.series(1, close), rsiLen); + var macdLine = (pinescript.ema(pinescript.series(2, close), macdFast) - pinescript.ema(pinescript.series(3, close), macdSlow)); + var macdSigLn = pinescript.ema(pinescript.series(4, macdLine), macdSig); + var macdHist = (macdLine - macdSigLn); + var momUp = ((rsi > 50) && (macdHist > 0)); + var momDn = ((rsi < 50) && (macdHist < 0)); + var longCond = (impulseUp && momUp); + var shortCond = (impulseDn && momDn); if (state.lastSignal === undefined) state.lastSignal = "NONE"; - let newBuy = (longCond && (state.lastSignal !== "BUY")); - let newSell = (shortCond && (state.lastSignal !== "SELL")); + var newBuy = (longCond && (state.lastSignal !== "BUY")); + var newSell = (shortCond && (state.lastSignal !== "SELL")); if (newBuy) { state.lastSignal = "BUY"; } else { @@ -1727,19 +1886,19 @@ function main() { state.lastSignal = "SELL"; } } - let basisSmoothed = pinescript.ema(basis, smoothLen); - let upBandSmoothed = pinescript.ema(upBand, smoothLen); - let dnBandSmoothed = pinescript.ema(dnBand, smoothLen); + var basisSmoothed = pinescript.ema(pinescript.series(5, basis), smoothLen); + var upBandSmoothed = pinescript.ema(pinescript.series(6, upBand), smoothLen); + var dnBandSmoothed = pinescript.ema(pinescript.series(7, dnBand), smoothLen); pinescript.plot(basisSmoothed, "Baseline", baseColorIn, 3); - let pUp = pinescript.plot((showBands ? upBandSmoothed : null), "ATR Upper", upColorIn); - let pDn = pinescript.plot((showBands ? dnBandSmoothed : null), "ATR Lower", dnColorIn); + var pUp = pinescript.plot((showBands ? upBandSmoothed : null), "ATR Upper", upColorIn); + var pDn = pinescript.plot((showBands ? dnBandSmoothed : null), "ATR Lower", dnColorIn); pinescript.fill(pUp, pDn, fillColorIn); - let arrowOffsetFactor = 2; - let textOffsetFactor = 0.8; - let buyArrowY = (dnBandSmoothed - (atr * arrowOffsetFactor)); - let buyTextY = (buyArrowY - (atr * textOffsetFactor)); - let sellArrowY = (upBandSmoothed + (atr * arrowOffsetFactor)); - let sellTextY = (sellArrowY + (atr * textOffsetFactor)); + var arrowOffsetFactor = 2; + var textOffsetFactor = 0.8; + var buyArrowY = (dnBandSmoothed - (atr * arrowOffsetFactor)); + var buyTextY = (buyArrowY - (atr * textOffsetFactor)); + var sellArrowY = (upBandSmoothed + (atr * arrowOffsetFactor)); + var sellTextY = (sellArrowY + (atr * textOffsetFactor)); if ((showArrows && newBuy)) { pinescript.labelNew(bar_index, buyArrowY, ({ text: "", style: label.style_triangleup, color: pinescript.color.new(pinescript.color.green, 0), textcolor: pinescript.color.white, size: pinescript.size.large })); pinescript.labelNew(bar_index, buyTextY, ({ text: "BUY", style: label.style_label_center, color: pinescript.color.new(pinescript.color.green, 85), textcolor: pinescript.color.new(pinescript.color.hex("#6186ff"), 0), size: pinescript.size.small })); @@ -1748,14 +1907,14 @@ function main() { pinescript.labelNew(bar_index, sellArrowY, ({ text: "", style: label.style_triangledown, color: pinescript.color.new(pinescript.color.red, 0), textcolor: pinescript.color.white, size: pinescript.size.large })); pinescript.labelNew(bar_index, sellTextY, ({ text: "SELL", style: label.style_label_center, color: pinescript.color.new(pinescript.color.red, 85), textcolor: pinescript.color.new(pinescript.color.hex("#6186ff"), 0), size: pinescript.size.small })); } - let neutralColor = pinescript.color.new(pinescript.color.gray, 82); - let bgColorUp = pinescript.color.new(pinescript.color.green, 65); - let bgColorDn = pinescript.color.new(pinescript.color.red, 65); + var neutralColor = pinescript.color.new(pinescript.color.gray, 82); + var bgColorUp = pinescript.color.new(pinescript.color.green, 65); + var bgColorDn = pinescript.color.new(pinescript.color.red, 65); pinescript.bgcolor((showBg ? ((state.lastSignal === "BUY") ? bgColorUp : ((state.lastSignal === "SELL") ? bgColorDn : neutralColor)) : null)); if (state.panel === undefined) state.panel = pinescript.table.new(pinescript.position.top_center, 1, 3, ({ border_width: 1 })); - let trend = ((state.lastSignal === "BUY") ? 1 : ((state.lastSignal === "SELL") ? -1 : 0)); - let trendText = ((trend === 1) ? "UPTREND" : ((trend === -1) ? "DOWNTREND" : "NEUTRAL")); - let trendColor = ((trend === 1) ? pinescript.color.rgb(1, 126, 66) : ((trend === -1) ? pinescript.color.rgb(200, 0, 0) : pinescript.color.gray)); + var trend = ((state.lastSignal === "BUY") ? 1 : ((state.lastSignal === "SELL") ? -1 : 0)); + var trendText = ((trend === 1) ? "UPTREND" : ((trend === -1) ? "DOWNTREND" : "NEUTRAL")); + var trendColor = ((trend === 1) ? pinescript.color.rgb(1, 126, 66) : ((trend === -1) ? pinescript.color.rgb(200, 0, 0) : pinescript.color.gray)); if (barstate.islast) { pinescript.table.cell(state.panel, 0, 0, "CURRENT TREND DIRECTION", ({ text_color: pinescript.color.white, bgcolor: pinescript.color.black, width: 27 })); pinescript.table.cell(state.panel, 0, 1, trendText, ({ text_color: pinescript.color.white, bgcolor: trendColor, width: 25 })); @@ -1804,6 +1963,10 @@ function run(data, options = {}) { rt.__shapeIdx = 0; globalThis.bar_index = i; globalThis.last_bar_index = n - 1; + globalThis.time_tradingday = inTime[i]; + globalThis.time_close = inTime[i]; + globalThis.last_bar_time = inTime[n - 1]; + globalThis.timenow = inTime[n - 1]; globalThis.barstate = { isfirst: i === 0, islast: i === n - 1, isrealtime: false, ishistory: true, isconfirmed: true, isnew: true, islastconfirmedhistory: i === n - 1, diff --git a/converted/Institutional_Order_Flow_Strength_Classifier_LuxAlgo.js b/converted/Institutional_Order_Flow_Strength_Classifier_LuxAlgo.js index e8adfa6..077b47e 100644 --- a/converted/Institutional_Order_Flow_Strength_Classifier_LuxAlgo.js +++ b/converted/Institutional_Order_Flow_Strength_Classifier_LuxAlgo.js @@ -42,6 +42,80 @@ const pinescript = { if (value != null && typeof value[Symbol.iterator] === 'function') return Array.from(value); return new Array(count || 0).fill(null); }, + __decArr: function(arr) { + if (!Array.isArray(arr) || arr.__pineDecorated) return arr; + const self = this; + const def = (name, fn) => Object.defineProperty(arr, name, { value: fn, writable: true, configurable: true }); + Object.defineProperty(arr, '__pineDecorated', { value: true, configurable: true }); + def('get', (i) => (arr[i] === undefined ? null : arr[i])); + def('set', (i, v) => { arr[i] = v; return v; }); + def('size', () => arr.length); + def('clear', () => { arr.length = 0; }); + def('insert', (i, v) => { arr.splice(i, 0, v); }); + def('remove', (i) => arr.splice(i, 1)[0]); + def('contains', (v) => arr.includes(v)); + def('indexof', (v) => arr.indexOf(v)); + def('lastindexof', (v) => arr.lastIndexOf(v)); + def('first', () => (arr.length ? arr[0] : null)); + def('last', () => (arr.length ? arr[arr.length - 1] : null)); + def('sum', () => arr.reduce((a, b) => a + b, 0)); + def('avg', () => (arr.length ? arr.reduce((a, b) => a + b, 0) / arr.length : 0)); + def('min', () => (arr.length ? Math.min(...arr) : null)); + def('max', () => (arr.length ? Math.max(...arr) : null)); + def('range', () => (arr.length ? Math.max(...arr) - Math.min(...arr) : null)); + // Statistical methods that route to the array.* built-ins (these names are not + // native to JS arrays, so attaching them here is safe from recursion). + def('median', () => self.arrayMedian(arr)); + def('mode', () => self.arrayMode(arr)); + def('stdev', () => self.arrayStdev(arr)); + def('variance', () => self.arrayVariance(arr)); + def('covariance', (other) => self.arrayCovariance(arr, other)); + def('percentile_linear_interpolation', (p) => self.arrayPercentileLinearInterpolation(arr, p)); + def('percentile_nearest_rank', (p) => self.arrayPercentileNearestRank(arr, p)); + def('abs', () => self.__decArr(self.arrayAbs(arr))); + def('binary_search', (v) => self.arrayBinarySearch(arr, v)); + // Pine's array.sort takes an order string, not a comparator. Use the native + // sort via .call so we don't recurse through this overridden method. + def('sort', (order) => { Array.prototype.sort.call(arr, (a, b) => (order === 'descending' ? b - a : a - b)); return arr; }); + def('sort_indices', (order) => self.__decArr(arr.map((_, i) => i).sort((a, b) => (order === 'descending' ? arr[b] - arr[a] : arr[a] - arr[b])))); + // join/slice/reverse/concat/includes/fill already exist natively on Array with + // compatible semantics, so we deliberately leave them to the native methods. + return arr; + }, + __decMap: function(m) { + if (!(m instanceof Map) || m.__pineDecorated) return m; + const def = (name, fn) => Object.defineProperty(m, name, { value: fn, writable: true, configurable: true }); + Object.defineProperty(m, '__pineDecorated', { value: true, configurable: true }); + def('put', (k, v) => { m.set(k, v); return v; }); + def('contains', (k) => m.has(k)); + def('remove', (k) => m.delete(k)); + def('keys', () => Array.from(Map.prototype.keys.call(m))); + def('values', () => Array.from(Map.prototype.values.call(m))); + def('size_', () => m.size); + return m; + }, + __decMatrix: function(m) { + if (!m || typeof m !== 'object' || m.__pineDecorated) return m; + const self = this; + const def = (name, fn) => Object.defineProperty(m, name, { value: fn, writable: true, configurable: true }); + Object.defineProperty(m, '__pineDecorated', { value: true, configurable: true }); + def('get', (r, c) => self.matrixGet(m, r, c)); + def('set', (r, c, v) => self.matrixSet(m, r, c, v)); + def('rows_', () => m.rows); + def('columns', () => m.cols); + def('fill', (v) => self.matrixFill(m, v)); + return m; + }, + __decDraw: function(obj) { + if (!obj || typeof obj !== 'object') return obj; + const p = new Proxy(obj, { + get(t, k) { + if (k in t || typeof k === 'symbol') return t[k]; + return function() { return p; }; + }, + }); + return p; + }, alertcondition: function(condition, ...rest) { if (globalThis.__pineRuntime) { globalThis.__pineRuntime.alerts.push({ condition, args: rest }); @@ -49,6 +123,32 @@ const pinescript = { return null; }, barcolor: function(color) { return null; }, + plotchar: function(series, ...rest) { + const rt = globalThis.__pineRuntime; + if (!rt) return series; + const bar = rt.__barIndex | 0; + const ord = (rt.__shapeIdx = (rt.__shapeIdx | 0) + 1) - 1; + let key = 'char_' + ord; + for (const r of rest) { + if (typeof r === 'string') { key = r; break; } + if (r && typeof r === 'object' && r.title) { key = String(r.title); break; } + } + let s = rt.plotshapes[key]; + if (!s) s = rt.plotshapes[key] = { title: key, data: [] }; + s.data[bar] = this.__scalar(series); + return series; + }, + plotarrow: function(series, ...rest) { + const rt = globalThis.__pineRuntime; + if (!rt) return series; + const bar = rt.__barIndex | 0; + const ord = (rt.__plotIdx = (rt.__plotIdx | 0) + 1) - 1; + const key = 'arrow_' + ord; + let p = rt.plots[key]; + if (!p) p = rt.plots[key] = { title: key, data: [] }; + p.data[bar] = this.__scalar(series); + return series; + }, bgcolor: function(color, title, editable, showLast) { return null; }, @@ -470,6 +570,12 @@ const pinescript = { atan: function(value) { return Math.atan(value); }, + todegrees: function(radians) { + return radians * (180 / Math.PI); + }, + toradians: function(degrees) { + return degrees * (Math.PI / 180); + }, floor: function(value) { return Math.floor(value); }, @@ -641,7 +747,7 @@ const pinescript = { return series; }, lineNew: function(x1, y1, x2, y2, opts = {}) { - return { x1, y1, x2, y2, opts, _type: 'line' }; + return this.__decDraw({ x1, y1, x2, y2, opts, _type: 'line' }); }, lineDelete: function(l) { return null; @@ -661,7 +767,7 @@ const pinescript = { return point === 0 || point === 'y1' ? line.y1 : line.y2; }, labelNew: function(x, y, text = '', opts = {}) { - return { x, y, text, opts, _type: 'label' }; + return this.__decDraw({ x, y, text, opts, _type: 'label' }); }, labelDelete: function(l) { return null; @@ -676,7 +782,7 @@ const pinescript = { return label.text || ''; }, boxNew: function(left, top, right, bottom, opts = {}) { - return { left, top, right, bottom, opts, _type: 'box' }; + return this.__decDraw({ left, top, right, bottom, opts, _type: 'box' }); }, boxDelete: function(box) { return null; @@ -694,7 +800,7 @@ const pinescript = { return box; }, polylineNew: function(points, opts = {}) { - return { points: points || [], opts, _type: 'polyline' }; + return this.__decDraw({ points: points || [], opts, _type: 'polyline' }); }, polylineDelete: function(poly) { return null; @@ -765,7 +871,7 @@ const pinescript = { return { time: _time ?? null, price: _price ?? null }; }, mapNew: function() { - return new Map(); + return this.__decMap(new Map()); }, mapSize: function(m) { return m instanceof Map ? m.size : 0; @@ -800,7 +906,7 @@ const pinescript = { const r = Math.max(0, rows ?? 0); const c = Math.max(0, cols ?? 0); const data = Array.from({ length: r }, () => Array.from({ length: c }, () => initialValue)); - return { rows: r, cols: c, data }; + return this.__decMatrix({ rows: r, cols: c, data }); }, matrixRows: function(m) { return m?.rows ?? 0; @@ -923,11 +1029,62 @@ const pinescript = { } return null; }, + __jacobiEigen: function(m) { + if (!m || !Array.isArray(m.data)) return null; + const n = m.rows ?? 0; + if (n === 0 || n !== (m.cols ?? 0)) return null; + // Work on a copy so the input matrix is untouched. + const a = m.data.map(row => row.slice()); + const v = Array.from({ length: n }, (_, i) => Array.from({ length: n }, (_, j) => (i === j ? 1 : 0))); + for (let sweep = 0; sweep < 100; sweep++) { + let off = 0; + for (let p = 0; p < n; p++) for (let q = p + 1; q < n; q++) off += a[p][q] * a[p][q]; + if (off < 1e-20) break; + for (let p = 0; p < n; p++) { + for (let q = p + 1; q < n; q++) { + if (Math.abs(a[p][q]) < 1e-18) continue; + const theta = (a[q][q] - a[p][p]) / (2 * a[p][q]); + const t = Math.sign(theta || 1) / (Math.abs(theta) + Math.sqrt(theta * theta + 1)); + const cos = 1 / Math.sqrt(t * t + 1); + const sin = t * cos; + for (let k = 0; k < n; k++) { + const akp = a[k][p], akq = a[k][q]; + a[k][p] = cos * akp - sin * akq; + a[k][q] = sin * akp + cos * akq; + } + for (let k = 0; k < n; k++) { + const apk = a[p][k], aqk = a[q][k]; + a[p][k] = cos * apk - sin * aqk; + a[q][k] = sin * apk + cos * aqk; + } + for (let k = 0; k < n; k++) { + const vkp = v[k][p], vkq = v[k][q]; + v[k][p] = cos * vkp - sin * vkq; + v[k][q] = sin * vkp + cos * vkq; + } + } + } + } + // Sort eigenpairs by eigenvalue, descending. + const order = Array.from({ length: n }, (_, i) => i).sort((i, j) => a[j][j] - a[i][i]); + const values = order.map(i => a[i][i]); + const vectors = Array.from({ length: n }, (_, r) => order.map(c => v[r][c])); + return { values, vectors }; + }, + matrixEigenvalues: function(m) { + const e = this.__jacobiEigen(m); + return this.__decArr(e ? e.values : []); + }, + matrixEigenvectors: function(m) { + const e = this.__jacobiEigen(m); + if (!e) return this.__decMatrix({ rows: 0, cols: 0, data: [] }); + return this.__decMatrix({ rows: e.vectors.length, cols: e.vectors.length, data: e.vectors }); + }, requestSecurity: function(symbol, timeframe, expression) { return expression; }, arrayNew: function(initialSize = 0, initialValue = 0) { - return Array(initialSize).fill(initialValue); + return this.__decArr(Array(initialSize).fill(initialValue)); }, arraySize: function(arr) { return arr ? arr.length : 0; @@ -978,10 +1135,10 @@ const pinescript = { return arr; }, arraySlice: function(arr, startIndex = 0, endIndex = null) { - if (!arr) return []; + if (!arr) return this.__decArr([]); const start = Number(startIndex) || 0; const end = endIndex === null || endIndex === undefined ? arr.length : Number(endIndex) || 0; - return arr.slice(start, end); + return this.__decArr(arr.slice(start, end)); }, arraySort: function(arr, order = 'ascending') { if (arr) arr.sort((a, b) => order === 'ascending' ? a - b : b - a); @@ -1191,7 +1348,7 @@ const pinescript = { const info = { ticker: 'AAPL', tickerid: 'NASDAQ:AAPL', prefix: 'NASDAQ', root: 'AAPL', suffix: '' }; return info[type] || ''; }, - timenow: 1781574527375, + timenow: 1782783576560, barstate: "LAST", dividends: {}, splits: {}, @@ -1245,11 +1402,11 @@ const pinescript = { arrayConcat: function(arr1, arr2) { if (!arr1) return arr2 || []; if (!arr2) return arr1; - return arr1.concat(arr2); + return this.__decArr(arr1.concat(arr2)); }, arrayCopy: function(arr) { - if (!arr) return []; - return [...arr]; + if (!arr) return this.__decArr([]); + return this.__decArr([...arr]); }, arrayBinarySearch: function(arr, value) { if (!arr || arr.length === 0) return -1; @@ -1339,7 +1496,7 @@ globalThis.input.timeframe = globalThis.input.timeframe || ((defval) => defval); globalThis.array = globalThis.array || { - from: (...items) => items, + from: (...items) => pinescript.__decArr(items), size: (arr) => pinescript.arraySize(arr), @@ -1508,6 +1665,8 @@ globalThis.line = globalThis.line || __pineNS({ style_solid: "solid", style_dash globalThis.box = globalThis.box || __pineNS({}); +globalThis.color = globalThis.color || __pineNS(Object.assign(function(c) { return c; }, { new: function(c, t) { return c; }, rgb: function(r, g, b, t) { return "#rgb(" + [r, g, b].join(",") + ")"; }, from_gradient: function(v, lo, hi, c1, c2) { return c1; }, r: function() { return 0; }, g: function() { return 0; }, b: function() { return 0; }, t: function() { return 0; }, aqua: "#00BCD4", black: "#363A45", blue: "#2962FF", fuchsia: "#E040FB", gray: "#787B86", green: "#4CAF50", lime: "#00E676", maroon: "#880E4F", navy: "#311B92", olive: "#808000", orange: "#FF9800", purple: "#9C27B0", red: "#FF5252", silver: "#B2B5BE", teal: "#00897B", white: "#FFFFFF", yellow: "#FFEB3B" })); + globalThis.label = globalThis.label || __pineNS({ style_label_down: "label_down", style_label_up: "label_up", style_none: "none" }); globalThis.polyline = globalThis.polyline || __pineNS({}); @@ -1520,7 +1679,9 @@ globalThis.map = globalThis.map || __pineNS({}); globalThis.session = globalThis.session || __pineNS({ regular: "regular", extended: "extended" }); -globalThis.dayofweek = globalThis.dayofweek || __pineNS({ sunday: 1, monday: 2, tuesday: 3, wednesday: 4, thursday: 5, friday: 6, saturday: 7 }); +globalThis.ticker = globalThis.ticker || __pineNS({}); + +globalThis.dayofweek = globalThis.dayofweek || __pineNS(Object.assign(function(t) { return new Date(t != null ? t : (globalThis.time || 0)).getUTCDay() + 1; }, { sunday: 1, monday: 2, tuesday: 3, wednesday: 4, thursday: 5, friday: 6, saturday: 7 })); globalThis.timeframe = __pineNS(Object.assign(globalThis.timeframe || {}, { period: (globalThis.timeframe && globalThis.timeframe.period) || "D", isintraday: false, isdaily: true, multiplier: 1 })); @@ -1568,8 +1729,6 @@ globalThis.bool = globalThis.bool || function(x) { return Boolean(x); }; globalThis.string = globalThis.string || function(x) { return x == null ? null : String(x); }; -globalThis.dayofweek = globalThis.dayofweek || __pineNS({ sunday: 1, monday: 2, tuesday: 3, wednesday: 4, thursday: 5, friday: 6, saturday: 7 }); - globalThis.str = globalThis.str || __pineNS({}); pinescript.math = globalThis.math; @@ -1655,7 +1814,7 @@ pinescript.text = { align_center: "center" }; pinescript.table = { - new: function(position, columns, rows, opts) { return { position, columns, rows, opts: opts || {}, cells: [] }; }, + new: function(position, columns, rows, opts) { return pinescript.__decDraw({ position, columns, rows, opts: opts || {}, cells: [] }); }, cell: function(table, column, row, text, opts) { @@ -1689,29 +1848,29 @@ function main() { null; // Study: Institutional Order Flow Strength Classifier [LuxAlgo] // Options: {"overlay":true,"max_boxes_count":100,"max_labels_count":100} - let BULL_COLOR = pinescript.color.hex("#089981"); - let BEAR_COLOR = pinescript.color.hex("#f23645"); - let TEXT_COLOR = chart.fg_color; - let OB_GROUP = "Order Block Settings"; - let pivotLenInput = pinescript.inputInt(5, "Pivot Lookback", ({ minval: 2, group: OB_GROUP })); - let maxOBsInput = pinescript.inputInt(5, "Max Unmitigated OBs", ({ minval: 1, maxval: 50, group: OB_GROUP })); - let VIS_GROUP = "Visualization"; - let bullColorInput = pinescript.inputColor(pinescript.color.new(BULL_COLOR, 80), "Bullish OB Color", ({ group: VIS_GROUP })); - let bearColorInput = pinescript.inputColor(pinescript.color.new(BEAR_COLOR, 80), "Bearish OB Color", ({ group: VIS_GROUP })); - let hideOverlappedInput = pinescript.inputBool(true, "Hide Overlapped Zones", ({ group: VIS_GROUP })); - let showLabelsInput = pinescript.inputBool(true, "Show Strength Labels", ({ group: VIS_GROUP })); - let showStrongestInput = pinescript.inputBool(true, "Show Strongest OB Plot", ({ group: VIS_GROUP })); - let bufferSizeInput = pinescript.inputInt(5, "Strongest OB Buffer Size", ({ minval: 1, maxval: 100, group: VIS_GROUP })); + var BULL_COLOR = pinescript.color.hex("#089981"); + var BEAR_COLOR = pinescript.color.hex("#f23645"); + var TEXT_COLOR = chart.fg_color; + var OB_GROUP = "Order Block Settings"; + var pivotLenInput = pinescript.inputInt(5, "Pivot Lookback", ({ minval: 2, group: OB_GROUP })); + var maxOBsInput = pinescript.inputInt(5, "Max Unmitigated OBs", ({ minval: 1, maxval: 50, group: OB_GROUP })); + var VIS_GROUP = "Visualization"; + var bullColorInput = pinescript.inputColor(pinescript.color.new(BULL_COLOR, 80), "Bullish OB Color", ({ group: VIS_GROUP })); + var bearColorInput = pinescript.inputColor(pinescript.color.new(BEAR_COLOR, 80), "Bearish OB Color", ({ group: VIS_GROUP })); + var hideOverlappedInput = pinescript.inputBool(true, "Hide Overlapped Zones", ({ group: VIS_GROUP })); + var showLabelsInput = pinescript.inputBool(true, "Show Strength Labels", ({ group: VIS_GROUP })); + var showStrongestInput = pinescript.inputBool(true, "Show Strongest OB Plot", ({ group: VIS_GROUP })); + var bufferSizeInput = pinescript.inputInt(5, "Strongest OB Buffer Size", ({ minval: 1, maxval: 100, group: VIS_GROUP })); function calculateStrength(obRange, breakoutDist, relVol) { - let dispFactor = pinescript.min((breakoutDist / (obRange * 5)), 1); - let volFactor = pinescript.min((relVol / 2), 1); - let score = (((dispFactor * 0.6) + (volFactor * 0.4)) * 100); + var dispFactor = pinescript.min((breakoutDist / (obRange * 5)), 1); + var volFactor = pinescript.min((relVol / 2), 1); + var score = (((dispFactor * 0.6) + (volFactor * 0.4)) * 100); return pinescript.round(score, 1); } if (state.obArray === undefined) state.obArray = pinescript.arrayNew(); - let volSMA = pinescript.sma(volume, 20); - let hi = ta.pivothigh(pivotLenInput, pivotLenInput); - let lo = ta.pivotlow(pivotLenInput, pivotLenInput); + var volSMA = pinescript.sma(pinescript.series(0, volume), 20); + var hi = pinescript.ta.pivothigh(pivotLenInput, pivotLenInput); + var lo = pinescript.ta.pivotlow(pivotLenInput, pivotLenInput); if (state.lastHi === undefined) state.lastHi = null; if (state.lastLo === undefined) state.lastLo = null; if (state.lastHiIdx === undefined) state.lastHiIdx = null; @@ -1724,72 +1883,72 @@ function main() { state.lastLo = lo; state.lastLoIdx = (bar_index - pivotLenInput); } - let bullBOS = pinescript.crossover(pinescript.series(0, close), pinescript.series(1, state.lastHi)); - let bearBOS = pinescript.crossunder(pinescript.series(2, close), pinescript.series(3, state.lastLo)); + var bullBOS = pinescript.crossover(pinescript.series(1, close), pinescript.series(2, state.lastHi)); + var bearBOS = pinescript.crossunder(pinescript.series(3, close), pinescript.series(4, state.lastLo)); if ((bullBOS || bearBOS)) { - let searchLen = (bar_index - (bullBOS ? state.lastHiIdx : state.lastLoIdx)); - let obTop = null; - let obBtm = null; - let obIdx = null; - let obVol = null; + var searchLen = (bar_index - (bullBOS ? state.lastHiIdx : state.lastLoIdx)); + var obTop = null; + var obBtm = null; + var obIdx = null; + var obVol = null; if (bullBOS) { - let minLow = null; + var minLow = null; for (let i = 1; i <= searchLen; i++) { - if ((pinescript.hist(4, close, i) < pinescript.hist(5, open, i))) { - if ((pinescript.na(minLow) || (pinescript.hist(6, low, i) < minLow))) { - minLow = pinescript.hist(7, low, i); - obTop = pinescript.hist(8, high, i); - obBtm = pinescript.hist(9, low, i); + if ((pinescript.hist(5, close, i) < pinescript.hist(6, open, i))) { + if ((pinescript.na(minLow) || (pinescript.hist(7, low, i) < minLow))) { + minLow = pinescript.hist(8, low, i); + obTop = pinescript.hist(9, high, i); + obBtm = pinescript.hist(10, low, i); obIdx = i; - obVol = pinescript.hist(10, volume, i); + obVol = pinescript.hist(11, volume, i); } } } state.lastHi = null; } else { - let maxHigh = null; + var maxHigh = null; for (let i = 1; i <= searchLen; i++) { - if ((pinescript.hist(11, close, i) > pinescript.hist(12, open, i))) { - if ((pinescript.na(maxHigh) || (pinescript.hist(13, high, i) > maxHigh))) { - maxHigh = pinescript.hist(14, high, i); - obTop = pinescript.hist(15, high, i); - obBtm = pinescript.hist(16, low, i); + if ((pinescript.hist(12, close, i) > pinescript.hist(13, open, i))) { + if ((pinescript.na(maxHigh) || (pinescript.hist(14, high, i) > maxHigh))) { + maxHigh = pinescript.hist(15, high, i); + obTop = pinescript.hist(16, high, i); + obBtm = pinescript.hist(17, low, i); obIdx = i; - obVol = pinescript.hist(17, volume, i); + obVol = pinescript.hist(18, volume, i); } } } state.lastLo = null; } if (!pinescript.na(obIdx)) { - let obHeight = (obTop - obBtm); - let breakoutDist = (bullBOS ? (close - obTop) : (obBtm - close)); - let avgVol = pinescript.hist(18, volSMA, obIdx); - let strength = calculateStrength(obHeight, breakoutDist, (obVol / avgVol)); - let newOB = OrderBlock.new(obTop, obBtm, pinescript.hist(19, time, obIdx), strength, bullBOS, null, null, false); + var obHeight = (obTop - obBtm); + var breakoutDist = (bullBOS ? (close - obTop) : (obBtm - close)); + var avgVol = pinescript.hist(19, volSMA, obIdx); + var strength = calculateStrength(obHeight, breakoutDist, (obVol / avgVol)); + var newOB = OrderBlock.new(obTop, obBtm, pinescript.hist(20, time, obIdx), strength, bullBOS, null, null, false); pinescript.arrayUnshift(state.obArray, newOB); } } if ((pinescript.arraySize(state.obArray) > 0)) { for (let i = 0; i <= (pinescript.arraySize(state.obArray) - 1); i++) { - let ob = pinescript.arrayGet(state.obArray, i); + var ob = pinescript.arrayGet(state.obArray, i); if (!ob.mitigated) { - let isMitigated = (ob.isBullish ? (low < ob.bottom) : (high > ob.top)); + var isMitigated = (ob.isBullish ? (low < ob.bottom) : (high > ob.top)); if (isMitigated) { ob.mitigated = true; } } } } - let strongestTop = null; - let strongestBottom = null; - let strongestAvgPrice = null; - let strongestIsBullish = false; - let maxStrength = -1; + var strongestTop = null; + var strongestBottom = null; + var strongestAvgPrice = null; + var strongestIsBullish = false; + var maxStrength = -1; if ((pinescript.arraySize(state.obArray) > 0)) { - let checkedCount = 0; + var checkedCount = 0; for (let i = 0; i <= (pinescript.arraySize(state.obArray) - 1); i++) { - let ob = pinescript.arrayGet(state.obArray, i); + ob = pinescript.arrayGet(state.obArray, i); if (!ob.mitigated) { if ((ob.strength > maxStrength)) { maxStrength = ob.strength; @@ -1805,26 +1964,26 @@ function main() { } } } - let plotCol = (pinescript.na(strongestAvgPrice) ? null : (strongestIsBullish ? bullColorInput : bearColorInput)); - let hasChanged = (strongestAvgPrice !== pinescript.hist(20, strongestAvgPrice, 1)); - let plotTop = pinescript.plot((showStrongestInput ? strongestTop : null), "Strongest OB Top", ({ color: (hasChanged ? null : pinescript.color.new(plotCol, 70)) })); - let plotBtm = pinescript.plot((showStrongestInput ? strongestBottom : null), "Strongest OB Bottom", ({ color: (hasChanged ? null : pinescript.color.new(plotCol, 70)) })); + var plotCol = (pinescript.na(strongestAvgPrice) ? null : (strongestIsBullish ? bullColorInput : bearColorInput)); + var hasChanged = (strongestAvgPrice !== pinescript.hist(21, strongestAvgPrice, 1)); + var plotTop = pinescript.plot((showStrongestInput ? strongestTop : null), "Strongest OB Top", ({ color: (hasChanged ? null : pinescript.color.new(plotCol, 70)) })); + var plotBtm = pinescript.plot((showStrongestInput ? strongestBottom : null), "Strongest OB Bottom", ({ color: (hasChanged ? null : pinescript.color.new(plotCol, 70)) })); pinescript.fill(plotTop, plotBtm, (hasChanged ? null : pinescript.color.new(plotCol, 90)), "Strongest OB Zone Fill"); pinescript.plot((showStrongestInput ? strongestAvgPrice : null), "Strongest OB Median", ({ color: (hasChanged ? null : pinescript.color.new(plotCol, 40)), linewidth: 2 })); if (barstate.islast) { - let activeCount = 0; + var activeCount = 0; if (state.occupiedTops === undefined) state.occupiedTops = pinescript.arrayNew(0); if (state.occupiedBtms === undefined) state.occupiedBtms = pinescript.arrayNew(0); pinescript.arrayClear(state.occupiedTops); pinescript.arrayClear(state.occupiedBtms); if ((pinescript.arraySize(state.obArray) > 0)) { for (let i = 0; i <= (pinescript.arraySize(state.obArray) - 1); i++) { - let ob = pinescript.arrayGet(state.obArray, i); - let isOverlapped = false; + ob = pinescript.arrayGet(state.obArray, i); + var isOverlapped = false; if (((!ob.mitigated && hideOverlappedInput) && (pinescript.arraySize(state.occupiedTops) > 0))) { for (let j = 0; j <= (pinescript.arraySize(state.occupiedTops) - 1); j++) { - let otherTop = pinescript.arrayGet(state.occupiedTops, j); - let otherBtm = pinescript.arrayGet(state.occupiedBtms, j); + var otherTop = pinescript.arrayGet(state.occupiedTops, j); + var otherBtm = pinescript.arrayGet(state.occupiedBtms, j); if (((((ob.top <= otherTop) && (ob.top >= otherBtm)) || ((ob.bottom <= otherTop) && (ob.bottom >= otherBtm))) || ((ob.top >= otherTop) && (ob.bottom <= otherBtm)))) { isOverlapped = true; break; @@ -1835,8 +1994,8 @@ function main() { activeCount += 1; pinescript.arrayPush(state.occupiedTops, ob.top); pinescript.arrayPush(state.occupiedBtms, ob.bottom); - let obCol = (ob.isBullish ? bullColorInput : bearColorInput); - let labelCol = (ob.isBullish ? BULL_COLOR : BEAR_COLOR); + var obCol = (ob.isBullish ? bullColorInput : bearColorInput); + var labelCol = (ob.isBullish ? BULL_COLOR : BEAR_COLOR); if (pinescript.na(ob.boxId)) { ob.boxId = pinescript.boxNew(ob.startTime, ob.top, time, ob.bottom, ({ bgcolor: obCol, border_color: obCol, border_style: line.style_dashed, xloc: xloc.bar_time })); } @@ -1860,7 +2019,7 @@ function main() { } if ((pinescript.arraySize(state.obArray) > 100)) { for (let i = (pinescript.arraySize(state.obArray) - 1); i <= 0; i++) { - let ob = pinescript.arrayGet(state.obArray, i); + ob = pinescript.arrayGet(state.obArray, i); if ((ob.mitigated && (pinescript.arraySize(state.obArray) > 50))) { pinescript.arrayRemove(state.obArray, i); } else { @@ -1914,6 +2073,10 @@ function run(data, options = {}) { rt.__shapeIdx = 0; globalThis.bar_index = i; globalThis.last_bar_index = n - 1; + globalThis.time_tradingday = inTime[i]; + globalThis.time_close = inTime[i]; + globalThis.last_bar_time = inTime[n - 1]; + globalThis.timenow = inTime[n - 1]; globalThis.barstate = { isfirst: i === 0, islast: i === n - 1, isrealtime: false, ishistory: true, isconfirmed: true, isnew: true, islastconfirmedhistory: i === n - 1, diff --git a/converted/Liquidity_Sweep_Detector.js b/converted/Liquidity_Sweep_Detector.js index 7ed56bd..565d21c 100644 --- a/converted/Liquidity_Sweep_Detector.js +++ b/converted/Liquidity_Sweep_Detector.js @@ -42,6 +42,80 @@ const pinescript = { if (value != null && typeof value[Symbol.iterator] === 'function') return Array.from(value); return new Array(count || 0).fill(null); }, + __decArr: function(arr) { + if (!Array.isArray(arr) || arr.__pineDecorated) return arr; + const self = this; + const def = (name, fn) => Object.defineProperty(arr, name, { value: fn, writable: true, configurable: true }); + Object.defineProperty(arr, '__pineDecorated', { value: true, configurable: true }); + def('get', (i) => (arr[i] === undefined ? null : arr[i])); + def('set', (i, v) => { arr[i] = v; return v; }); + def('size', () => arr.length); + def('clear', () => { arr.length = 0; }); + def('insert', (i, v) => { arr.splice(i, 0, v); }); + def('remove', (i) => arr.splice(i, 1)[0]); + def('contains', (v) => arr.includes(v)); + def('indexof', (v) => arr.indexOf(v)); + def('lastindexof', (v) => arr.lastIndexOf(v)); + def('first', () => (arr.length ? arr[0] : null)); + def('last', () => (arr.length ? arr[arr.length - 1] : null)); + def('sum', () => arr.reduce((a, b) => a + b, 0)); + def('avg', () => (arr.length ? arr.reduce((a, b) => a + b, 0) / arr.length : 0)); + def('min', () => (arr.length ? Math.min(...arr) : null)); + def('max', () => (arr.length ? Math.max(...arr) : null)); + def('range', () => (arr.length ? Math.max(...arr) - Math.min(...arr) : null)); + // Statistical methods that route to the array.* built-ins (these names are not + // native to JS arrays, so attaching them here is safe from recursion). + def('median', () => self.arrayMedian(arr)); + def('mode', () => self.arrayMode(arr)); + def('stdev', () => self.arrayStdev(arr)); + def('variance', () => self.arrayVariance(arr)); + def('covariance', (other) => self.arrayCovariance(arr, other)); + def('percentile_linear_interpolation', (p) => self.arrayPercentileLinearInterpolation(arr, p)); + def('percentile_nearest_rank', (p) => self.arrayPercentileNearestRank(arr, p)); + def('abs', () => self.__decArr(self.arrayAbs(arr))); + def('binary_search', (v) => self.arrayBinarySearch(arr, v)); + // Pine's array.sort takes an order string, not a comparator. Use the native + // sort via .call so we don't recurse through this overridden method. + def('sort', (order) => { Array.prototype.sort.call(arr, (a, b) => (order === 'descending' ? b - a : a - b)); return arr; }); + def('sort_indices', (order) => self.__decArr(arr.map((_, i) => i).sort((a, b) => (order === 'descending' ? arr[b] - arr[a] : arr[a] - arr[b])))); + // join/slice/reverse/concat/includes/fill already exist natively on Array with + // compatible semantics, so we deliberately leave them to the native methods. + return arr; + }, + __decMap: function(m) { + if (!(m instanceof Map) || m.__pineDecorated) return m; + const def = (name, fn) => Object.defineProperty(m, name, { value: fn, writable: true, configurable: true }); + Object.defineProperty(m, '__pineDecorated', { value: true, configurable: true }); + def('put', (k, v) => { m.set(k, v); return v; }); + def('contains', (k) => m.has(k)); + def('remove', (k) => m.delete(k)); + def('keys', () => Array.from(Map.prototype.keys.call(m))); + def('values', () => Array.from(Map.prototype.values.call(m))); + def('size_', () => m.size); + return m; + }, + __decMatrix: function(m) { + if (!m || typeof m !== 'object' || m.__pineDecorated) return m; + const self = this; + const def = (name, fn) => Object.defineProperty(m, name, { value: fn, writable: true, configurable: true }); + Object.defineProperty(m, '__pineDecorated', { value: true, configurable: true }); + def('get', (r, c) => self.matrixGet(m, r, c)); + def('set', (r, c, v) => self.matrixSet(m, r, c, v)); + def('rows_', () => m.rows); + def('columns', () => m.cols); + def('fill', (v) => self.matrixFill(m, v)); + return m; + }, + __decDraw: function(obj) { + if (!obj || typeof obj !== 'object') return obj; + const p = new Proxy(obj, { + get(t, k) { + if (k in t || typeof k === 'symbol') return t[k]; + return function() { return p; }; + }, + }); + return p; + }, alertcondition: function(condition, ...rest) { if (globalThis.__pineRuntime) { globalThis.__pineRuntime.alerts.push({ condition, args: rest }); @@ -49,6 +123,32 @@ const pinescript = { return null; }, barcolor: function(color) { return null; }, + plotchar: function(series, ...rest) { + const rt = globalThis.__pineRuntime; + if (!rt) return series; + const bar = rt.__barIndex | 0; + const ord = (rt.__shapeIdx = (rt.__shapeIdx | 0) + 1) - 1; + let key = 'char_' + ord; + for (const r of rest) { + if (typeof r === 'string') { key = r; break; } + if (r && typeof r === 'object' && r.title) { key = String(r.title); break; } + } + let s = rt.plotshapes[key]; + if (!s) s = rt.plotshapes[key] = { title: key, data: [] }; + s.data[bar] = this.__scalar(series); + return series; + }, + plotarrow: function(series, ...rest) { + const rt = globalThis.__pineRuntime; + if (!rt) return series; + const bar = rt.__barIndex | 0; + const ord = (rt.__plotIdx = (rt.__plotIdx | 0) + 1) - 1; + const key = 'arrow_' + ord; + let p = rt.plots[key]; + if (!p) p = rt.plots[key] = { title: key, data: [] }; + p.data[bar] = this.__scalar(series); + return series; + }, bgcolor: function(color, title, editable, showLast) { return null; }, @@ -470,6 +570,12 @@ const pinescript = { atan: function(value) { return Math.atan(value); }, + todegrees: function(radians) { + return radians * (180 / Math.PI); + }, + toradians: function(degrees) { + return degrees * (Math.PI / 180); + }, floor: function(value) { return Math.floor(value); }, @@ -641,7 +747,7 @@ const pinescript = { return series; }, lineNew: function(x1, y1, x2, y2, opts = {}) { - return { x1, y1, x2, y2, opts, _type: 'line' }; + return this.__decDraw({ x1, y1, x2, y2, opts, _type: 'line' }); }, lineDelete: function(l) { return null; @@ -661,7 +767,7 @@ const pinescript = { return point === 0 || point === 'y1' ? line.y1 : line.y2; }, labelNew: function(x, y, text = '', opts = {}) { - return { x, y, text, opts, _type: 'label' }; + return this.__decDraw({ x, y, text, opts, _type: 'label' }); }, labelDelete: function(l) { return null; @@ -676,7 +782,7 @@ const pinescript = { return label.text || ''; }, boxNew: function(left, top, right, bottom, opts = {}) { - return { left, top, right, bottom, opts, _type: 'box' }; + return this.__decDraw({ left, top, right, bottom, opts, _type: 'box' }); }, boxDelete: function(box) { return null; @@ -694,7 +800,7 @@ const pinescript = { return box; }, polylineNew: function(points, opts = {}) { - return { points: points || [], opts, _type: 'polyline' }; + return this.__decDraw({ points: points || [], opts, _type: 'polyline' }); }, polylineDelete: function(poly) { return null; @@ -765,7 +871,7 @@ const pinescript = { return { time: _time ?? null, price: _price ?? null }; }, mapNew: function() { - return new Map(); + return this.__decMap(new Map()); }, mapSize: function(m) { return m instanceof Map ? m.size : 0; @@ -800,7 +906,7 @@ const pinescript = { const r = Math.max(0, rows ?? 0); const c = Math.max(0, cols ?? 0); const data = Array.from({ length: r }, () => Array.from({ length: c }, () => initialValue)); - return { rows: r, cols: c, data }; + return this.__decMatrix({ rows: r, cols: c, data }); }, matrixRows: function(m) { return m?.rows ?? 0; @@ -923,11 +1029,62 @@ const pinescript = { } return null; }, + __jacobiEigen: function(m) { + if (!m || !Array.isArray(m.data)) return null; + const n = m.rows ?? 0; + if (n === 0 || n !== (m.cols ?? 0)) return null; + // Work on a copy so the input matrix is untouched. + const a = m.data.map(row => row.slice()); + const v = Array.from({ length: n }, (_, i) => Array.from({ length: n }, (_, j) => (i === j ? 1 : 0))); + for (let sweep = 0; sweep < 100; sweep++) { + let off = 0; + for (let p = 0; p < n; p++) for (let q = p + 1; q < n; q++) off += a[p][q] * a[p][q]; + if (off < 1e-20) break; + for (let p = 0; p < n; p++) { + for (let q = p + 1; q < n; q++) { + if (Math.abs(a[p][q]) < 1e-18) continue; + const theta = (a[q][q] - a[p][p]) / (2 * a[p][q]); + const t = Math.sign(theta || 1) / (Math.abs(theta) + Math.sqrt(theta * theta + 1)); + const cos = 1 / Math.sqrt(t * t + 1); + const sin = t * cos; + for (let k = 0; k < n; k++) { + const akp = a[k][p], akq = a[k][q]; + a[k][p] = cos * akp - sin * akq; + a[k][q] = sin * akp + cos * akq; + } + for (let k = 0; k < n; k++) { + const apk = a[p][k], aqk = a[q][k]; + a[p][k] = cos * apk - sin * aqk; + a[q][k] = sin * apk + cos * aqk; + } + for (let k = 0; k < n; k++) { + const vkp = v[k][p], vkq = v[k][q]; + v[k][p] = cos * vkp - sin * vkq; + v[k][q] = sin * vkp + cos * vkq; + } + } + } + } + // Sort eigenpairs by eigenvalue, descending. + const order = Array.from({ length: n }, (_, i) => i).sort((i, j) => a[j][j] - a[i][i]); + const values = order.map(i => a[i][i]); + const vectors = Array.from({ length: n }, (_, r) => order.map(c => v[r][c])); + return { values, vectors }; + }, + matrixEigenvalues: function(m) { + const e = this.__jacobiEigen(m); + return this.__decArr(e ? e.values : []); + }, + matrixEigenvectors: function(m) { + const e = this.__jacobiEigen(m); + if (!e) return this.__decMatrix({ rows: 0, cols: 0, data: [] }); + return this.__decMatrix({ rows: e.vectors.length, cols: e.vectors.length, data: e.vectors }); + }, requestSecurity: function(symbol, timeframe, expression) { return expression; }, arrayNew: function(initialSize = 0, initialValue = 0) { - return Array(initialSize).fill(initialValue); + return this.__decArr(Array(initialSize).fill(initialValue)); }, arraySize: function(arr) { return arr ? arr.length : 0; @@ -978,10 +1135,10 @@ const pinescript = { return arr; }, arraySlice: function(arr, startIndex = 0, endIndex = null) { - if (!arr) return []; + if (!arr) return this.__decArr([]); const start = Number(startIndex) || 0; const end = endIndex === null || endIndex === undefined ? arr.length : Number(endIndex) || 0; - return arr.slice(start, end); + return this.__decArr(arr.slice(start, end)); }, arraySort: function(arr, order = 'ascending') { if (arr) arr.sort((a, b) => order === 'ascending' ? a - b : b - a); @@ -1191,7 +1348,7 @@ const pinescript = { const info = { ticker: 'AAPL', tickerid: 'NASDAQ:AAPL', prefix: 'NASDAQ', root: 'AAPL', suffix: '' }; return info[type] || ''; }, - timenow: 1781574527470, + timenow: 1782783576652, barstate: "LAST", dividends: {}, splits: {}, @@ -1245,11 +1402,11 @@ const pinescript = { arrayConcat: function(arr1, arr2) { if (!arr1) return arr2 || []; if (!arr2) return arr1; - return arr1.concat(arr2); + return this.__decArr(arr1.concat(arr2)); }, arrayCopy: function(arr) { - if (!arr) return []; - return [...arr]; + if (!arr) return this.__decArr([]); + return this.__decArr([...arr]); }, arrayBinarySearch: function(arr, value) { if (!arr || arr.length === 0) return -1; @@ -1339,7 +1496,7 @@ globalThis.input.timeframe = globalThis.input.timeframe || ((defval) => defval); globalThis.array = globalThis.array || { - from: (...items) => items, + from: (...items) => pinescript.__decArr(items), size: (arr) => pinescript.arraySize(arr), @@ -1508,6 +1665,8 @@ globalThis.line = globalThis.line || __pineNS({ style_solid: "solid", style_dash globalThis.box = globalThis.box || __pineNS({}); +globalThis.color = globalThis.color || __pineNS(Object.assign(function(c) { return c; }, { new: function(c, t) { return c; }, rgb: function(r, g, b, t) { return "#rgb(" + [r, g, b].join(",") + ")"; }, from_gradient: function(v, lo, hi, c1, c2) { return c1; }, r: function() { return 0; }, g: function() { return 0; }, b: function() { return 0; }, t: function() { return 0; }, aqua: "#00BCD4", black: "#363A45", blue: "#2962FF", fuchsia: "#E040FB", gray: "#787B86", green: "#4CAF50", lime: "#00E676", maroon: "#880E4F", navy: "#311B92", olive: "#808000", orange: "#FF9800", purple: "#9C27B0", red: "#FF5252", silver: "#B2B5BE", teal: "#00897B", white: "#FFFFFF", yellow: "#FFEB3B" })); + globalThis.label = globalThis.label || __pineNS({ style_label_down: "label_down", style_label_up: "label_up", style_none: "none" }); globalThis.polyline = globalThis.polyline || __pineNS({}); @@ -1520,7 +1679,9 @@ globalThis.map = globalThis.map || __pineNS({}); globalThis.session = globalThis.session || __pineNS({ regular: "regular", extended: "extended" }); -globalThis.dayofweek = globalThis.dayofweek || __pineNS({ sunday: 1, monday: 2, tuesday: 3, wednesday: 4, thursday: 5, friday: 6, saturday: 7 }); +globalThis.ticker = globalThis.ticker || __pineNS({}); + +globalThis.dayofweek = globalThis.dayofweek || __pineNS(Object.assign(function(t) { return new Date(t != null ? t : (globalThis.time || 0)).getUTCDay() + 1; }, { sunday: 1, monday: 2, tuesday: 3, wednesday: 4, thursday: 5, friday: 6, saturday: 7 })); globalThis.timeframe = __pineNS(Object.assign(globalThis.timeframe || {}, { period: (globalThis.timeframe && globalThis.timeframe.period) || "D", isintraday: false, isdaily: true, multiplier: 1 })); @@ -1568,8 +1729,6 @@ globalThis.bool = globalThis.bool || function(x) { return Boolean(x); }; globalThis.string = globalThis.string || function(x) { return x == null ? null : String(x); }; -globalThis.dayofweek = globalThis.dayofweek || __pineNS({ sunday: 1, monday: 2, tuesday: 3, wednesday: 4, thursday: 5, friday: 6, saturday: 7 }); - globalThis.str = globalThis.str || __pineNS({}); pinescript.math = globalThis.math; @@ -1655,7 +1814,7 @@ pinescript.text = { align_center: "center" }; pinescript.table = { - new: function(position, columns, rows, opts) { return { position, columns, rows, opts: opts || {}, cells: [] }; }, + new: function(position, columns, rows, opts) { return pinescript.__decDraw({ position, columns, rows, opts: opts || {}, cells: [] }); }, cell: function(table, column, row, text, opts) { @@ -1688,29 +1847,29 @@ function main() { null; // Study: Liquidity Sweep Detector [DefinedEdge] // Options: {"shorttitle":"Sweep Detector","overlay":true,"max_boxes_count":500,"max_labels_count":500,"max_lines_count":500} - let zoneGroup = "⚙️ Zone Detection"; - let length = pinescript.inputInt(8, "Pivot Lookback", ({ minval: 3, maxval: 50, group: zoneGroup, tooltip: "Higher = fewer but more significant swing points. Lower = more zones and more sweep signals." })); - let maxZones = pinescript.inputInt(8, "Max Active Zones", ({ minval: 2, maxval: 15, group: zoneGroup, tooltip: "How many unbroken zones to track per side" })); - let zoneRange = pinescript.inputString("Wick Rejection", "Zone Range", ({ options: ["Wick Rejection", "Full Bar"], group: zoneGroup })); - let showInvalidated = pinescript.inputBool(false, "Show Invalidated Zones (faded)", ({ group: zoneGroup })); - let sweepGroup = "🎯 Sweep Detection"; - let showSweeps = pinescript.inputBool(true, "Show Sweep Signals", ({ group: sweepGroup })); - let showSweepBG = pinescript.inputBool(true, "Highlight Sweep Bars", ({ group: sweepGroup })); - let filterGroup = "🔬 Signal Quality Filters"; - let useTrendFilter = pinescript.inputBool(false, "Trend Filter (EMA)", ({ group: filterGroup, tooltip: "Only show bull sweeps above EMA, bear sweeps below EMA. With-trend sweeps = institutional reloading." })); - let trendEmaLen = pinescript.inputInt(50, "Trend EMA Length", ({ minval: 10, maxval: 200, group: filterGroup })); - let useVolFilter = pinescript.inputBool(false, "Volume Spike Filter", ({ group: filterGroup, tooltip: "Sweep bar must have higher-than-average volume. Real liquidity grabs leave a volume footprint." })); - let volMultiplier = pinescript.inputFloat(1.2, "Volume Multiplier", ({ minval: 1, maxval: 5, step: 0.1, group: filterGroup })); - let volAvgLen = pinescript.inputInt(20, "Volume Avg Length", ({ minval: 5, maxval: 100, group: filterGroup })); - let useWickFilter = pinescript.inputBool(false, "Wick Rejection Filter", ({ group: filterGroup, tooltip: "Sweep bar must show strong rejection — big wick, small body relative to total range." })); - let wickMinPct = pinescript.inputFloat(40, "Min Wick %", ({ minval: 20, maxval: 90, step: 5, group: filterGroup, tooltip: "Minimum percentage of bar range that must be wick (rejection strength)" })); - let vizGroup = "🎨 Visual Settings"; - let showDash = pinescript.inputBool(true, "Show Dashboard", ({ group: vizGroup })); - let dashPos = pinescript.inputString("Top Right", "Dashboard Position", ({ options: ["Top Right", "Top Left", "Bottom Right", "Bottom Left"], group: vizGroup })); - let dashSize = pinescript.inputInt(2, "Dashboard Size (1-3)", ({ minval: 1, maxval: 3, group: vizGroup })); - let bullCol = pinescript.inputColor(pinescript.color.hex("#00b8d4"), "Support Zones", ({ group: vizGroup })); - let bearCol = pinescript.inputColor(pinescript.color.hex("#ff6d00"), "Resistance Zones", ({ group: vizGroup })); - let sweepCol = pinescript.inputColor(pinescript.color.hex("#e040fb"), "Sweep Signal", ({ group: vizGroup })); + var zoneGroup = "⚙️ Zone Detection"; + var length = pinescript.inputInt(8, "Pivot Lookback", ({ minval: 3, maxval: 50, group: zoneGroup, tooltip: "Higher = fewer but more significant swing points. Lower = more zones and more sweep signals." })); + var maxZones = pinescript.inputInt(8, "Max Active Zones", ({ minval: 2, maxval: 15, group: zoneGroup, tooltip: "How many unbroken zones to track per side" })); + var zoneRange = pinescript.inputString("Wick Rejection", "Zone Range", ({ options: ["Wick Rejection", "Full Bar"], group: zoneGroup })); + var showInvalidated = pinescript.inputBool(false, "Show Invalidated Zones (faded)", ({ group: zoneGroup })); + var sweepGroup = "🎯 Sweep Detection"; + var showSweeps = pinescript.inputBool(true, "Show Sweep Signals", ({ group: sweepGroup })); + var showSweepBG = pinescript.inputBool(true, "Highlight Sweep Bars", ({ group: sweepGroup })); + var filterGroup = "🔬 Signal Quality Filters"; + var useTrendFilter = pinescript.inputBool(false, "Trend Filter (EMA)", ({ group: filterGroup, tooltip: "Only show bull sweeps above EMA, bear sweeps below EMA. With-trend sweeps = institutional reloading." })); + var trendEmaLen = pinescript.inputInt(50, "Trend EMA Length", ({ minval: 10, maxval: 200, group: filterGroup })); + var useVolFilter = pinescript.inputBool(false, "Volume Spike Filter", ({ group: filterGroup, tooltip: "Sweep bar must have higher-than-average volume. Real liquidity grabs leave a volume footprint." })); + var volMultiplier = pinescript.inputFloat(1.2, "Volume Multiplier", ({ minval: 1, maxval: 5, step: 0.1, group: filterGroup })); + var volAvgLen = pinescript.inputInt(20, "Volume Avg Length", ({ minval: 5, maxval: 100, group: filterGroup })); + var useWickFilter = pinescript.inputBool(false, "Wick Rejection Filter", ({ group: filterGroup, tooltip: "Sweep bar must show strong rejection — big wick, small body relative to total range." })); + var wickMinPct = pinescript.inputFloat(40, "Min Wick %", ({ minval: 20, maxval: 90, step: 5, group: filterGroup, tooltip: "Minimum percentage of bar range that must be wick (rejection strength)" })); + var vizGroup = "🎨 Visual Settings"; + var showDash = pinescript.inputBool(true, "Show Dashboard", ({ group: vizGroup })); + var dashPos = pinescript.inputString("Top Right", "Dashboard Position", ({ options: ["Top Right", "Top Left", "Bottom Right", "Bottom Left"], group: vizGroup })); + var dashSize = pinescript.inputInt(2, "Dashboard Size (1-3)", ({ minval: 1, maxval: 3, group: vizGroup })); + var bullCol = pinescript.inputColor(pinescript.color.hex("#00b8d4"), "Support Zones", ({ group: vizGroup })); + var bearCol = pinescript.inputColor(pinescript.color.hex("#ff6d00"), "Resistance Zones", ({ group: vizGroup })); + var sweepCol = pinescript.inputColor(pinescript.color.hex("#e040fb"), "Sweep Signal", ({ group: vizGroup })); if (state.rz_top === undefined) state.rz_top = pinescript.arrayNew(); if (state.rz_btm === undefined) state.rz_btm = pinescript.arrayNew(); if (state.rz_x1 === undefined) state.rz_x1 = pinescript.arrayNew(); @@ -1769,11 +1928,11 @@ function main() { pinescript.arrayPush(state.sz_box, pinescript.boxNew(x, top, x, btm, ({ border_color: pinescript.color.new(bullCol, 60), border_width: 1, bgcolor: pinescript.color.new(bullCol, 88), border_style: line.style_solid }))); return pinescript.arrayPush(state.sz_line, pinescript.lineNew(x, btm, x, btm, ({ color: bullCol, width: 1, style: line.style_dotted }))); } - let ph = ta.pivothigh(length, length); - let pl = ta.pivotlow(length, length); + var ph = pinescript.ta.pivothigh(length, length); + var pl = pinescript.ta.pivotlow(length, length); if (ph) { - let _top = pinescript.hist(0, high, length); - let _btm = ((zoneRange === "Wick Rejection") ? pinescript.max(pinescript.hist(1, open, length), pinescript.hist(2, close, length)) : pinescript.hist(3, low, length)); + var _top = pinescript.hist(0, high, length); + var _btm = ((zoneRange === "Wick Rejection") ? pinescript.max(pinescript.hist(1, open, length), pinescript.hist(2, close, length)) : pinescript.hist(3, low, length)); addResistanceZone(_top, _btm, (bar_index - length)); } if (pl) { @@ -1785,33 +1944,33 @@ function main() { if (state.bearSweepNow === undefined) state.bearSweepNow = false; state.bullSweepNow = false; state.bearSweepNow = false; - let trendEma = pinescript.ema(close, trendEmaLen); - let volAvg = pinescript.sma(volume, volAvgLen); - let barRange = (high - low); - let bodySize = pinescript.abs((close - open)); - let wickPct = ((barRange > 0) ? (((barRange - bodySize) / barRange) * 100) : 0); - let isAboveEma = (close > trendEma); - let isBelowEma = (close < trendEma); - let isVolSpike = (volume > (volAvg * volMultiplier)); - let isStrongWick = (wickPct >= wickMinPct); - let bullFilterOk = (((!useTrendFilter || isAboveEma) && (!useVolFilter || isVolSpike)) && (!useWickFilter || isStrongWick)); - let bearFilterOk = (((!useTrendFilter || isBelowEma) && (!useVolFilter || isVolSpike)) && (!useWickFilter || isStrongWick)); + var trendEma = pinescript.ema(pinescript.series(8, close), trendEmaLen); + var volAvg = pinescript.sma(pinescript.series(9, volume), volAvgLen); + var barRange = (high - low); + var bodySize = pinescript.abs((close - open)); + var wickPct = ((barRange > 0) ? (((barRange - bodySize) / barRange) * 100) : 0); + var isAboveEma = (close > trendEma); + var isBelowEma = (close < trendEma); + var isVolSpike = (volume > (volAvg * volMultiplier)); + var isStrongWick = (wickPct >= wickMinPct); + var bullFilterOk = (((!useTrendFilter || isAboveEma) && (!useVolFilter || isVolSpike)) && (!useWickFilter || isStrongWick)); + var bearFilterOk = (((!useTrendFilter || isBelowEma) && (!useVolFilter || isVolSpike)) && (!useWickFilter || isStrongWick)); pinescript.plot((useTrendFilter ? trendEma : null), "Trend EMA", ({ color: pinescript.color.new(pinescript.color.white, 70), linewidth: 1 })); if ((pinescript.arraySize(state.rz_top) > 0)) { for (let i = (pinescript.arraySize(state.rz_top) - 1); i <= 0; i++) { _top = pinescript.arrayGet(state.rz_top, i); _btm = pinescript.arrayGet(state.rz_btm, i); - let _broken = pinescript.arrayGet(state.rz_broken, i); - let _swept = pinescript.arrayGet(state.rz_swept, i); - let _tests = pinescript.arrayGet(state.rz_tests, i); - let _bx = pinescript.arrayGet(state.rz_box, i); - let _ln = pinescript.arrayGet(state.rz_line, i); + var _broken = pinescript.arrayGet(state.rz_broken, i); + var _swept = pinescript.arrayGet(state.rz_swept, i); + var _tests = pinescript.arrayGet(state.rz_tests, i); + var _bx = pinescript.arrayGet(state.rz_box, i); + var _ln = pinescript.arrayGet(state.rz_line, i); if (!_broken) { box.set_right(_bx, (bar_index + 5)); line.set_x2(_ln, (bar_index + 5)); if ((((high > _btm) && (high <= _top)) && (close < _btm))) { pinescript.arraySet(state.rz_tests, i, (_tests + 1)); - let _newAlpha = pinescript.max(70, (88 - (_tests * 6))); + var _newAlpha = pinescript.max(70, (88 - (_tests * 6))); box.set_bgcolor(_bx, pinescript.color.new(bearCol, _newAlpha)); } if (((((high > _top) && (close <= _top)) && !_swept) && bearFilterOk)) { @@ -1822,7 +1981,7 @@ function main() { box.set_border_width(_bx, 2); box.set_bgcolor(_bx, pinescript.color.new(sweepCol, 80)); } - if (((close > _top) && (pinescript.hist(8, close, 1) > _top))) { + if (((close > _top) && (pinescript.hist(10, close, 1) > _top))) { pinescript.arraySet(state.rz_broken, i, true); if (showInvalidated) { box.set_bgcolor(_bx, pinescript.color.new(bearCol, 95)); @@ -1870,7 +2029,7 @@ function main() { box.set_border_width(_bx, 2); box.set_bgcolor(_bx, pinescript.color.new(sweepCol, 80)); } - if (((close < _btm) && (pinescript.hist(9, close, 1) < _btm))) { + if (((close < _btm) && (pinescript.hist(11, close, 1) < _btm))) { pinescript.arraySet(state.sz_broken, i, true); if (showInvalidated) { box.set_bgcolor(_bx, pinescript.color.new(bullCol, 95)); @@ -1902,10 +2061,10 @@ function main() { if ((showSweeps && state.bearSweepNow)) { pinescript.labelNew(bar_index, high, "SWEEP", ({ style: label.style_label_down, color: pinescript.color.new(sweepCol, 20), textcolor: pinescript.color.white, size: pinescript.size.tiny })); } - let activeResist = 0; - let activeSup = 0; - let sweptResist = 0; - let sweptSup = 0; + var activeResist = 0; + var activeSup = 0; + var sweptResist = 0; + var sweptSup = 0; if ((pinescript.arraySize(state.rz_broken) > 0)) { for (let i = 0; i <= (pinescript.arraySize(state.rz_broken) - 1); i++) { if (!pinescript.arrayGet(state.rz_broken, i)) { @@ -1926,12 +2085,12 @@ function main() { } } } - let nearestResist = null; - let nearestSupport = null; + var nearestResist = null; + var nearestSupport = null; if ((pinescript.arraySize(state.rz_top) > 0)) { for (let i = 0; i <= (pinescript.arraySize(state.rz_top) - 1); i++) { if (!pinescript.arrayGet(state.rz_broken, i)) { - let _dist = (pinescript.arrayGet(state.rz_btm, i) - close); + var _dist = (pinescript.arrayGet(state.rz_btm, i) - close); if (((_dist > 0) && (pinescript.na(nearestResist) || (_dist < nearestResist)))) { nearestResist = _dist; } @@ -1948,25 +2107,25 @@ function main() { } } } - let nearResistPct = (!pinescript.na(nearestResist) ? ((nearestResist / close) * 100) : null); - let nearSupPct = (!pinescript.na(nearestSupport) ? ((nearestSupport / close) * 100) : null); + var nearResistPct = (!pinescript.na(nearestResist) ? ((nearestResist / close) * 100) : null); + var nearSupPct = (!pinescript.na(nearestSupport) ? ((nearestSupport / close) * 100) : null); pinescript.position.bottom_left; pinescript.position.bottom_right; pinescript.position.top_left; pinescript.position.top_right; - let dashPosTV = ((dashPos === "Top Right") ? undefined : ((dashPos === "Top Left") ? undefined : ((dashPos === "Bottom Right") ? undefined : ((dashPos === "Bottom Left") ? undefined : null)))); + var dashPosTV = ((dashPos === "Top Right") ? undefined : ((dashPos === "Top Left") ? undefined : ((dashPos === "Bottom Right") ? undefined : ((dashPos === "Bottom Left") ? undefined : null)))); pinescript.size.huge; pinescript.size.large; pinescript.size.normal; - let textTitle = ((dashSize === 1) ? undefined : ((dashSize === 2) ? undefined : ((dashSize === 3) ? undefined : null))); + var textTitle = ((dashSize === 1) ? undefined : ((dashSize === 2) ? undefined : ((dashSize === 3) ? undefined : null))); pinescript.size.large; pinescript.size.normal; pinescript.size.small; - let textLabel = ((dashSize === 1) ? undefined : ((dashSize === 2) ? undefined : ((dashSize === 3) ? undefined : null))); + var textLabel = ((dashSize === 1) ? undefined : ((dashSize === 2) ? undefined : ((dashSize === 3) ? undefined : null))); pinescript.size.huge; pinescript.size.large; pinescript.size.normal; - let textValue = ((dashSize === 1) ? undefined : ((dashSize === 2) ? undefined : ((dashSize === 3) ? undefined : null))); + var textValue = ((dashSize === 1) ? undefined : ((dashSize === 2) ? undefined : ((dashSize === 3) ? undefined : null))); if (state.dash === undefined) state.dash = pinescript.table.new(dashPosTV, 3, 9, ({ bgcolor: pinescript.color.new(pinescript.color.hex("#0a0e17"), 5), border_color: pinescript.color.new(pinescript.color.hex("#1e2a3a"), 0), border_width: 2, frame_color: pinescript.color.new(pinescript.color.hex("#1e2a3a"), 0), frame_width: 3 })); if ((showDash && barstate.islast)) { pinescript.table.cell(state.dash, 0, 0, "🎯 SWEEP DETECTOR", ({ text_color: pinescript.color.white, text_size: textTitle, bgcolor: pinescript.color.new(sweepCol, 40), text_halign: pinescript.text.align_center })); @@ -1974,28 +2133,28 @@ function main() { pinescript.table.cell(state.dash, 0, 1, "ZONES", ({ text_color: pinescript.color.new(pinescript.color.white, 40), text_size: textLabel, bgcolor: pinescript.color.new(pinescript.color.hex("#0d1117"), 0) })); pinescript.table.cell(state.dash, 1, 1, (pinescript.strToString(activeResist) + " 🔴"), ({ text_color: bearCol, text_size: textValue, bgcolor: pinescript.color.new(pinescript.color.hex("#0d1117"), 0), text_halign: pinescript.text.align_center })); pinescript.table.cell(state.dash, 2, 1, (pinescript.strToString(activeSup) + " 🟢"), ({ text_color: bullCol, text_size: textValue, bgcolor: pinescript.color.new(pinescript.color.hex("#0d1117"), 0), text_halign: pinescript.text.align_center })); - let nrText = (!pinescript.na(nearResistPct) ? (pinescript.strToString(nearResistPct, "#.##") + "% away") : "— none —"); - let nrCol = (!pinescript.na(nearResistPct) ? ((nearResistPct < 1) ? bearCol : ((nearResistPct < 3) ? pinescript.color.new(bearCol, 40) : pinescript.color.new(pinescript.color.white, 40))) : pinescript.color.new(pinescript.color.white, 60)); + var nrText = (!pinescript.na(nearResistPct) ? (pinescript.strToString(nearResistPct, "#.##") + "% away") : "— none —"); + var nrCol = (!pinescript.na(nearResistPct) ? ((nearResistPct < 1) ? bearCol : ((nearResistPct < 3) ? pinescript.color.new(bearCol, 40) : pinescript.color.new(pinescript.color.white, 40))) : pinescript.color.new(pinescript.color.white, 60)); pinescript.table.cell(state.dash, 0, 2, "RESIST", ({ text_color: pinescript.color.new(pinescript.color.white, 40), text_size: textLabel, bgcolor: pinescript.color.new(pinescript.color.hex("#0d1117"), 0) })); pinescript.table.cell(state.dash, 1, 2, nrText, ({ text_color: nrCol, text_size: textValue, bgcolor: pinescript.color.new(pinescript.color.hex("#0d1117"), 0), text_halign: pinescript.text.align_center })); pinescript.tableMergeCells(state.dash, 1, 2, 2, 2); - let nsText = (!pinescript.na(nearSupPct) ? (pinescript.strToString(nearSupPct, "#.##") + "% away") : "— none —"); - let nsCol = (!pinescript.na(nearSupPct) ? ((nearSupPct < 1) ? bullCol : ((nearSupPct < 3) ? pinescript.color.new(bullCol, 40) : pinescript.color.new(pinescript.color.white, 40))) : pinescript.color.new(pinescript.color.white, 60)); + var nsText = (!pinescript.na(nearSupPct) ? (pinescript.strToString(nearSupPct, "#.##") + "% away") : "— none —"); + var nsCol = (!pinescript.na(nearSupPct) ? ((nearSupPct < 1) ? bullCol : ((nearSupPct < 3) ? pinescript.color.new(bullCol, 40) : pinescript.color.new(pinescript.color.white, 40))) : pinescript.color.new(pinescript.color.white, 60)); pinescript.table.cell(state.dash, 0, 3, "SUPPORT", ({ text_color: pinescript.color.new(pinescript.color.white, 40), text_size: textLabel, bgcolor: pinescript.color.new(pinescript.color.hex("#0d1117"), 0) })); pinescript.table.cell(state.dash, 1, 3, nsText, ({ text_color: nsCol, text_size: textValue, bgcolor: pinescript.color.new(pinescript.color.hex("#0d1117"), 0), text_halign: pinescript.text.align_center })); pinescript.tableMergeCells(state.dash, 1, 3, 2, 3); pinescript.table.cell(state.dash, 0, 4, "SWEEPS", ({ text_color: pinescript.color.new(pinescript.color.white, 40), text_size: textLabel, bgcolor: pinescript.color.new(pinescript.color.hex("#0d1117"), 0) })); pinescript.table.cell(state.dash, 1, 4, ("▼ " + pinescript.strToString(state.totalBearSweeps)), ({ text_color: bearCol, text_size: textValue, bgcolor: pinescript.color.new(pinescript.color.hex("#0d1117"), 0), text_halign: pinescript.text.align_center })); pinescript.table.cell(state.dash, 2, 4, ("▲ " + pinescript.strToString(state.totalBullSweeps)), ({ text_color: bullCol, text_size: textValue, bgcolor: pinescript.color.new(pinescript.color.hex("#0d1117"), 0), text_halign: pinescript.text.align_center })); - let biasText = ((state.totalBullSweeps > state.totalBearSweeps) ? "🟢 BULLISH BIAS" : ((state.totalBearSweeps > state.totalBullSweeps) ? "🔴 BEARISH BIAS" : "⚪ NEUTRAL")); - let biasBG = ((state.totalBullSweeps > state.totalBearSweeps) ? pinescript.color.new(bullCol, 80) : ((state.totalBearSweeps > state.totalBullSweeps) ? pinescript.color.new(bearCol, 80) : pinescript.color.new(pinescript.color.gray, 80))); + var biasText = ((state.totalBullSweeps > state.totalBearSweeps) ? "🟢 BULLISH BIAS" : ((state.totalBearSweeps > state.totalBullSweeps) ? "🔴 BEARISH BIAS" : "⚪ NEUTRAL")); + var biasBG = ((state.totalBullSweeps > state.totalBearSweeps) ? pinescript.color.new(bullCol, 80) : ((state.totalBearSweeps > state.totalBullSweeps) ? pinescript.color.new(bearCol, 80) : pinescript.color.new(pinescript.color.gray, 80))); pinescript.table.cell(state.dash, 0, 5, biasText, ({ text_color: pinescript.color.white, text_size: textValue, bgcolor: biasBG, text_halign: pinescript.text.align_center })); pinescript.tableMergeCells(state.dash, 0, 5, 2, 5); - let tF = (useTrendFilter ? "✅ Trend" : "❌ Trend"); - let vF = (useVolFilter ? "✅ Vol" : "❌ Vol"); - let wF = (useWickFilter ? "✅ Wick" : "❌ Wick"); - let filtersOn = (((useTrendFilter ? 1 : 0) + (useVolFilter ? 1 : 0)) + (useWickFilter ? 1 : 0)); - let filterBG = ((filtersOn === 3) ? pinescript.color.new(pinescript.color.hex("#00e676"), 85) : ((filtersOn >= 1) ? pinescript.color.new(pinescript.color.yellow, 85) : pinescript.color.new(pinescript.color.hex("#ff1744"), 85))); + var tF = (useTrendFilter ? "✅ Trend" : "❌ Trend"); + var vF = (useVolFilter ? "✅ Vol" : "❌ Vol"); + var wF = (useWickFilter ? "✅ Wick" : "❌ Wick"); + var filtersOn = (((useTrendFilter ? 1 : 0) + (useVolFilter ? 1 : 0)) + (useWickFilter ? 1 : 0)); + var filterBG = ((filtersOn === 3) ? pinescript.color.new(pinescript.color.hex("#00e676"), 85) : ((filtersOn >= 1) ? pinescript.color.new(pinescript.color.yellow, 85) : pinescript.color.new(pinescript.color.hex("#ff1744"), 85))); pinescript.table.cell(state.dash, 0, 6, "FILTERS", ({ text_color: pinescript.color.new(pinescript.color.white, 40), text_size: textLabel, bgcolor: pinescript.color.new(pinescript.color.hex("#0d1117"), 0) })); pinescript.table.cell(state.dash, 1, 6, ((((tF + " ") + vF) + " ") + wF), ({ text_color: pinescript.color.new(pinescript.color.white, 30), text_size: textLabel, bgcolor: filterBG, text_halign: pinescript.text.align_center })); pinescript.tableMergeCells(state.dash, 1, 6, 2, 6); @@ -2051,6 +2210,10 @@ function run(data, options = {}) { rt.__shapeIdx = 0; globalThis.bar_index = i; globalThis.last_bar_index = n - 1; + globalThis.time_tradingday = inTime[i]; + globalThis.time_close = inTime[i]; + globalThis.last_bar_time = inTime[n - 1]; + globalThis.timenow = inTime[n - 1]; globalThis.barstate = { isfirst: i === 0, islast: i === n - 1, isrealtime: false, ishistory: true, isconfirmed: true, isnew: true, islastconfirmedhistory: i === n - 1, diff --git a/converted/Manias_Panics_and_Crashes.js b/converted/Manias_Panics_and_Crashes.js index 951649d..6d716a1 100644 --- a/converted/Manias_Panics_and_Crashes.js +++ b/converted/Manias_Panics_and_Crashes.js @@ -42,6 +42,80 @@ const pinescript = { if (value != null && typeof value[Symbol.iterator] === 'function') return Array.from(value); return new Array(count || 0).fill(null); }, + __decArr: function(arr) { + if (!Array.isArray(arr) || arr.__pineDecorated) return arr; + const self = this; + const def = (name, fn) => Object.defineProperty(arr, name, { value: fn, writable: true, configurable: true }); + Object.defineProperty(arr, '__pineDecorated', { value: true, configurable: true }); + def('get', (i) => (arr[i] === undefined ? null : arr[i])); + def('set', (i, v) => { arr[i] = v; return v; }); + def('size', () => arr.length); + def('clear', () => { arr.length = 0; }); + def('insert', (i, v) => { arr.splice(i, 0, v); }); + def('remove', (i) => arr.splice(i, 1)[0]); + def('contains', (v) => arr.includes(v)); + def('indexof', (v) => arr.indexOf(v)); + def('lastindexof', (v) => arr.lastIndexOf(v)); + def('first', () => (arr.length ? arr[0] : null)); + def('last', () => (arr.length ? arr[arr.length - 1] : null)); + def('sum', () => arr.reduce((a, b) => a + b, 0)); + def('avg', () => (arr.length ? arr.reduce((a, b) => a + b, 0) / arr.length : 0)); + def('min', () => (arr.length ? Math.min(...arr) : null)); + def('max', () => (arr.length ? Math.max(...arr) : null)); + def('range', () => (arr.length ? Math.max(...arr) - Math.min(...arr) : null)); + // Statistical methods that route to the array.* built-ins (these names are not + // native to JS arrays, so attaching them here is safe from recursion). + def('median', () => self.arrayMedian(arr)); + def('mode', () => self.arrayMode(arr)); + def('stdev', () => self.arrayStdev(arr)); + def('variance', () => self.arrayVariance(arr)); + def('covariance', (other) => self.arrayCovariance(arr, other)); + def('percentile_linear_interpolation', (p) => self.arrayPercentileLinearInterpolation(arr, p)); + def('percentile_nearest_rank', (p) => self.arrayPercentileNearestRank(arr, p)); + def('abs', () => self.__decArr(self.arrayAbs(arr))); + def('binary_search', (v) => self.arrayBinarySearch(arr, v)); + // Pine's array.sort takes an order string, not a comparator. Use the native + // sort via .call so we don't recurse through this overridden method. + def('sort', (order) => { Array.prototype.sort.call(arr, (a, b) => (order === 'descending' ? b - a : a - b)); return arr; }); + def('sort_indices', (order) => self.__decArr(arr.map((_, i) => i).sort((a, b) => (order === 'descending' ? arr[b] - arr[a] : arr[a] - arr[b])))); + // join/slice/reverse/concat/includes/fill already exist natively on Array with + // compatible semantics, so we deliberately leave them to the native methods. + return arr; + }, + __decMap: function(m) { + if (!(m instanceof Map) || m.__pineDecorated) return m; + const def = (name, fn) => Object.defineProperty(m, name, { value: fn, writable: true, configurable: true }); + Object.defineProperty(m, '__pineDecorated', { value: true, configurable: true }); + def('put', (k, v) => { m.set(k, v); return v; }); + def('contains', (k) => m.has(k)); + def('remove', (k) => m.delete(k)); + def('keys', () => Array.from(Map.prototype.keys.call(m))); + def('values', () => Array.from(Map.prototype.values.call(m))); + def('size_', () => m.size); + return m; + }, + __decMatrix: function(m) { + if (!m || typeof m !== 'object' || m.__pineDecorated) return m; + const self = this; + const def = (name, fn) => Object.defineProperty(m, name, { value: fn, writable: true, configurable: true }); + Object.defineProperty(m, '__pineDecorated', { value: true, configurable: true }); + def('get', (r, c) => self.matrixGet(m, r, c)); + def('set', (r, c, v) => self.matrixSet(m, r, c, v)); + def('rows_', () => m.rows); + def('columns', () => m.cols); + def('fill', (v) => self.matrixFill(m, v)); + return m; + }, + __decDraw: function(obj) { + if (!obj || typeof obj !== 'object') return obj; + const p = new Proxy(obj, { + get(t, k) { + if (k in t || typeof k === 'symbol') return t[k]; + return function() { return p; }; + }, + }); + return p; + }, alertcondition: function(condition, ...rest) { if (globalThis.__pineRuntime) { globalThis.__pineRuntime.alerts.push({ condition, args: rest }); @@ -49,6 +123,32 @@ const pinescript = { return null; }, barcolor: function(color) { return null; }, + plotchar: function(series, ...rest) { + const rt = globalThis.__pineRuntime; + if (!rt) return series; + const bar = rt.__barIndex | 0; + const ord = (rt.__shapeIdx = (rt.__shapeIdx | 0) + 1) - 1; + let key = 'char_' + ord; + for (const r of rest) { + if (typeof r === 'string') { key = r; break; } + if (r && typeof r === 'object' && r.title) { key = String(r.title); break; } + } + let s = rt.plotshapes[key]; + if (!s) s = rt.plotshapes[key] = { title: key, data: [] }; + s.data[bar] = this.__scalar(series); + return series; + }, + plotarrow: function(series, ...rest) { + const rt = globalThis.__pineRuntime; + if (!rt) return series; + const bar = rt.__barIndex | 0; + const ord = (rt.__plotIdx = (rt.__plotIdx | 0) + 1) - 1; + const key = 'arrow_' + ord; + let p = rt.plots[key]; + if (!p) p = rt.plots[key] = { title: key, data: [] }; + p.data[bar] = this.__scalar(series); + return series; + }, bgcolor: function(color, title, editable, showLast) { return null; }, @@ -470,6 +570,12 @@ const pinescript = { atan: function(value) { return Math.atan(value); }, + todegrees: function(radians) { + return radians * (180 / Math.PI); + }, + toradians: function(degrees) { + return degrees * (Math.PI / 180); + }, floor: function(value) { return Math.floor(value); }, @@ -641,7 +747,7 @@ const pinescript = { return series; }, lineNew: function(x1, y1, x2, y2, opts = {}) { - return { x1, y1, x2, y2, opts, _type: 'line' }; + return this.__decDraw({ x1, y1, x2, y2, opts, _type: 'line' }); }, lineDelete: function(l) { return null; @@ -661,7 +767,7 @@ const pinescript = { return point === 0 || point === 'y1' ? line.y1 : line.y2; }, labelNew: function(x, y, text = '', opts = {}) { - return { x, y, text, opts, _type: 'label' }; + return this.__decDraw({ x, y, text, opts, _type: 'label' }); }, labelDelete: function(l) { return null; @@ -676,7 +782,7 @@ const pinescript = { return label.text || ''; }, boxNew: function(left, top, right, bottom, opts = {}) { - return { left, top, right, bottom, opts, _type: 'box' }; + return this.__decDraw({ left, top, right, bottom, opts, _type: 'box' }); }, boxDelete: function(box) { return null; @@ -694,7 +800,7 @@ const pinescript = { return box; }, polylineNew: function(points, opts = {}) { - return { points: points || [], opts, _type: 'polyline' }; + return this.__decDraw({ points: points || [], opts, _type: 'polyline' }); }, polylineDelete: function(poly) { return null; @@ -765,7 +871,7 @@ const pinescript = { return { time: _time ?? null, price: _price ?? null }; }, mapNew: function() { - return new Map(); + return this.__decMap(new Map()); }, mapSize: function(m) { return m instanceof Map ? m.size : 0; @@ -800,7 +906,7 @@ const pinescript = { const r = Math.max(0, rows ?? 0); const c = Math.max(0, cols ?? 0); const data = Array.from({ length: r }, () => Array.from({ length: c }, () => initialValue)); - return { rows: r, cols: c, data }; + return this.__decMatrix({ rows: r, cols: c, data }); }, matrixRows: function(m) { return m?.rows ?? 0; @@ -923,11 +1029,62 @@ const pinescript = { } return null; }, + __jacobiEigen: function(m) { + if (!m || !Array.isArray(m.data)) return null; + const n = m.rows ?? 0; + if (n === 0 || n !== (m.cols ?? 0)) return null; + // Work on a copy so the input matrix is untouched. + const a = m.data.map(row => row.slice()); + const v = Array.from({ length: n }, (_, i) => Array.from({ length: n }, (_, j) => (i === j ? 1 : 0))); + for (let sweep = 0; sweep < 100; sweep++) { + let off = 0; + for (let p = 0; p < n; p++) for (let q = p + 1; q < n; q++) off += a[p][q] * a[p][q]; + if (off < 1e-20) break; + for (let p = 0; p < n; p++) { + for (let q = p + 1; q < n; q++) { + if (Math.abs(a[p][q]) < 1e-18) continue; + const theta = (a[q][q] - a[p][p]) / (2 * a[p][q]); + const t = Math.sign(theta || 1) / (Math.abs(theta) + Math.sqrt(theta * theta + 1)); + const cos = 1 / Math.sqrt(t * t + 1); + const sin = t * cos; + for (let k = 0; k < n; k++) { + const akp = a[k][p], akq = a[k][q]; + a[k][p] = cos * akp - sin * akq; + a[k][q] = sin * akp + cos * akq; + } + for (let k = 0; k < n; k++) { + const apk = a[p][k], aqk = a[q][k]; + a[p][k] = cos * apk - sin * aqk; + a[q][k] = sin * apk + cos * aqk; + } + for (let k = 0; k < n; k++) { + const vkp = v[k][p], vkq = v[k][q]; + v[k][p] = cos * vkp - sin * vkq; + v[k][q] = sin * vkp + cos * vkq; + } + } + } + } + // Sort eigenpairs by eigenvalue, descending. + const order = Array.from({ length: n }, (_, i) => i).sort((i, j) => a[j][j] - a[i][i]); + const values = order.map(i => a[i][i]); + const vectors = Array.from({ length: n }, (_, r) => order.map(c => v[r][c])); + return { values, vectors }; + }, + matrixEigenvalues: function(m) { + const e = this.__jacobiEigen(m); + return this.__decArr(e ? e.values : []); + }, + matrixEigenvectors: function(m) { + const e = this.__jacobiEigen(m); + if (!e) return this.__decMatrix({ rows: 0, cols: 0, data: [] }); + return this.__decMatrix({ rows: e.vectors.length, cols: e.vectors.length, data: e.vectors }); + }, requestSecurity: function(symbol, timeframe, expression) { return expression; }, arrayNew: function(initialSize = 0, initialValue = 0) { - return Array(initialSize).fill(initialValue); + return this.__decArr(Array(initialSize).fill(initialValue)); }, arraySize: function(arr) { return arr ? arr.length : 0; @@ -978,10 +1135,10 @@ const pinescript = { return arr; }, arraySlice: function(arr, startIndex = 0, endIndex = null) { - if (!arr) return []; + if (!arr) return this.__decArr([]); const start = Number(startIndex) || 0; const end = endIndex === null || endIndex === undefined ? arr.length : Number(endIndex) || 0; - return arr.slice(start, end); + return this.__decArr(arr.slice(start, end)); }, arraySort: function(arr, order = 'ascending') { if (arr) arr.sort((a, b) => order === 'ascending' ? a - b : b - a); @@ -1191,7 +1348,7 @@ const pinescript = { const info = { ticker: 'AAPL', tickerid: 'NASDAQ:AAPL', prefix: 'NASDAQ', root: 'AAPL', suffix: '' }; return info[type] || ''; }, - timenow: 1781574527577, + timenow: 1782783576746, barstate: "LAST", dividends: {}, splits: {}, @@ -1245,11 +1402,11 @@ const pinescript = { arrayConcat: function(arr1, arr2) { if (!arr1) return arr2 || []; if (!arr2) return arr1; - return arr1.concat(arr2); + return this.__decArr(arr1.concat(arr2)); }, arrayCopy: function(arr) { - if (!arr) return []; - return [...arr]; + if (!arr) return this.__decArr([]); + return this.__decArr([...arr]); }, arrayBinarySearch: function(arr, value) { if (!arr || arr.length === 0) return -1; @@ -1339,7 +1496,7 @@ globalThis.input.timeframe = globalThis.input.timeframe || ((defval) => defval); globalThis.array = globalThis.array || { - from: (...items) => items, + from: (...items) => pinescript.__decArr(items), size: (arr) => pinescript.arraySize(arr), @@ -1508,6 +1665,8 @@ globalThis.line = globalThis.line || __pineNS({ style_solid: "solid", style_dash globalThis.box = globalThis.box || __pineNS({}); +globalThis.color = globalThis.color || __pineNS(Object.assign(function(c) { return c; }, { new: function(c, t) { return c; }, rgb: function(r, g, b, t) { return "#rgb(" + [r, g, b].join(",") + ")"; }, from_gradient: function(v, lo, hi, c1, c2) { return c1; }, r: function() { return 0; }, g: function() { return 0; }, b: function() { return 0; }, t: function() { return 0; }, aqua: "#00BCD4", black: "#363A45", blue: "#2962FF", fuchsia: "#E040FB", gray: "#787B86", green: "#4CAF50", lime: "#00E676", maroon: "#880E4F", navy: "#311B92", olive: "#808000", orange: "#FF9800", purple: "#9C27B0", red: "#FF5252", silver: "#B2B5BE", teal: "#00897B", white: "#FFFFFF", yellow: "#FFEB3B" })); + globalThis.label = globalThis.label || __pineNS({ style_label_down: "label_down", style_label_up: "label_up", style_none: "none" }); globalThis.polyline = globalThis.polyline || __pineNS({}); @@ -1520,7 +1679,9 @@ globalThis.map = globalThis.map || __pineNS({}); globalThis.session = globalThis.session || __pineNS({ regular: "regular", extended: "extended" }); -globalThis.dayofweek = globalThis.dayofweek || __pineNS({ sunday: 1, monday: 2, tuesday: 3, wednesday: 4, thursday: 5, friday: 6, saturday: 7 }); +globalThis.ticker = globalThis.ticker || __pineNS({}); + +globalThis.dayofweek = globalThis.dayofweek || __pineNS(Object.assign(function(t) { return new Date(t != null ? t : (globalThis.time || 0)).getUTCDay() + 1; }, { sunday: 1, monday: 2, tuesday: 3, wednesday: 4, thursday: 5, friday: 6, saturday: 7 })); globalThis.timeframe = __pineNS(Object.assign(globalThis.timeframe || {}, { period: (globalThis.timeframe && globalThis.timeframe.period) || "D", isintraday: false, isdaily: true, multiplier: 1 })); @@ -1568,8 +1729,6 @@ globalThis.bool = globalThis.bool || function(x) { return Boolean(x); }; globalThis.string = globalThis.string || function(x) { return x == null ? null : String(x); }; -globalThis.dayofweek = globalThis.dayofweek || __pineNS({ sunday: 1, monday: 2, tuesday: 3, wednesday: 4, thursday: 5, friday: 6, saturday: 7 }); - globalThis.str = globalThis.str || __pineNS({}); pinescript.math = globalThis.math; @@ -1655,7 +1814,7 @@ pinescript.text = { align_center: "center" }; pinescript.table = { - new: function(position, columns, rows, opts) { return { position, columns, rows, opts: opts || {}, cells: [] }; }, + new: function(position, columns, rows, opts) { return pinescript.__decDraw({ position, columns, rows, opts: opts || {}, cells: [] }); }, cell: function(table, column, row, text, opts) { @@ -1688,103 +1847,103 @@ function main() { null; // Study: Manias, Panics and Crashes // Options: {"shorttitle":"MPC","overlay":false,"max_labels_count":500} - let color_green_bright = pinescript.color.hex("#00ff00"); - let color_green_soft = pinescript.color.hex("#26a69a"); - let color_yellow_bright = pinescript.color.hex("#ffd700"); - let color_orange_alert = pinescript.color.hex("#ff6b35"); - let color_red_danger = pinescript.color.hex("#ff1744"); - let color_purple_composite = pinescript.color.hex("#ab47bc"); - let color_gray_neutral = pinescript.color.hex("#78909c"); - let color_dark_bg = pinescript.color.hex("#1e1e1e"); - let display_mode = pinescript.inputString("Smoothed All Lines", "Display Mode", ({ options: ["Smoothed All Lines", "Single Dominant Line", "Both"], group: "Display" })); - let smooth_period = pinescript.inputInt(3, "Smoothing Period", ({ minval: 1, maxval: 20, group: "Display" })); - let show_zones = pinescript.inputBool(true, "Show Background Phase Zones", ({ group: "Display" })); - let show_phase_labels = pinescript.inputBool(true, "Show Phase Change Labels", ({ group: "Display" })); - let show_historical_context = pinescript.inputBool(true, "Show Historical Context", ({ group: "Display" })); - let dashboard_position = pinescript.inputString("Top Right", "Dashboard Position", ({ options: ["Top Left", "Top Center", "Top Right", "Middle Left", "Middle Right", "Bottom Left", "Bottom Right"], group: "Dashboard" })); - let dashboard_size = pinescript.inputString("Normal", "Dashboard Size", ({ options: ["Small", "Normal", "Large", "Extra Large"], group: "Dashboard" })); - let show_components = pinescript.inputBool(true, "Show Component Health Panel", ({ group: "Dashboard" })); - let show_action_hints = pinescript.inputBool(true, "Show Action Guidance", ({ group: "Dashboard" })); - let mania_threshold = pinescript.inputInt(75, "Mania Alert Threshold", ({ minval: 0, maxval: 100, group: "Alert Thresholds" })); - let panic_threshold = pinescript.inputInt(60, "Panic Alert Threshold", ({ minval: 0, maxval: 100, group: "Alert Thresholds" })); - let crash_threshold = pinescript.inputInt(70, "Crash Alert Threshold", ({ minval: 0, maxval: 100, group: "Alert Thresholds" })); - let ma_period = pinescript.inputInt(200, "Long-term MA Period", ({ minval: 50, maxval: 300, group: "Technical Parameters" })); - let rsi_period = pinescript.inputInt(14, "RSI Period", ({ minval: 5, maxval: 30, group: "Technical Parameters" })); - let vol_period = pinescript.inputInt(20, "Volume Average Period", ({ minval: 10, maxval: 50, group: "Technical Parameters" })); - let show_credit = pinescript.inputBool(true, "Include Credit Risk Indicators", ({ group: "Components" })); - let show_breadth = pinescript.inputBool(true, "Include Market Breadth", ({ group: "Components" })); - let show_vix = pinescript.inputBool(true, "Include VIX Analysis", ({ group: "Components" })); - let vix = (show_vix ? pinescript.requestSecurity("TVC:VIX", timeframe.period, close) : null); - let vix_5 = (show_vix ? pinescript.requestSecurity("TVC:VIX", timeframe.period, pinescript.hist(0, close, 5)) : null); - let hyg = (show_credit ? pinescript.requestSecurity("AMEX:HYG", timeframe.period, close) : null); - let lqd = (show_credit ? pinescript.requestSecurity("AMEX:LQD", timeframe.period, close) : null); - let tlt = (show_credit ? pinescript.requestSecurity("NASDAQ:TLT", timeframe.period, close) : null); - let xlf = (show_credit ? pinescript.requestSecurity("AMEX:XLF", timeframe.period, close) : null); - let spy = (show_credit ? pinescript.requestSecurity("AMEX:SPY", timeframe.period, close) : null); - let adv_dec = (show_breadth ? pinescript.requestSecurity("INDEX:ADDN", timeframe.period, close) : null); - let hyg_lqd_ratio = (((show_credit && !pinescript.na(hyg)) && !pinescript.na(lqd)) ? (lqd / hyg) : null); - let hyg_lqd_spread = hyg_lqd_ratio; - let hyg_lqd_baseline = pinescript.sma(hyg_lqd_spread, 60); - let hyg_lqd_stress = (((show_credit && !pinescript.na(hyg_lqd_spread)) && !pinescript.na(hyg_lqd_baseline)) ? (((hyg_lqd_spread - hyg_lqd_baseline) / hyg_lqd_baseline) * 100) : 0); - let tlt_spy_ratio = (((show_credit && !pinescript.na(tlt)) && !pinescript.na(spy)) ? (tlt / spy) : null); - let tlt_spy_baseline = pinescript.sma(tlt_spy_ratio, 60); - let tlt_spy_flight = (((show_credit && !pinescript.na(tlt_spy_ratio)) && !pinescript.na(tlt_spy_baseline)) ? (((tlt_spy_ratio - tlt_spy_baseline) / tlt_spy_baseline) * 100) : 0); - let xlf_spy_ratio = (((show_credit && !pinescript.na(xlf)) && !pinescript.na(spy)) ? (xlf / spy) : null); - let xlf_spy_baseline = pinescript.sma(xlf_spy_ratio, 60); - let xlf_spy_weakness = (((show_credit && !pinescript.na(xlf_spy_ratio)) && !pinescript.na(xlf_spy_baseline)) ? (((xlf_spy_baseline - xlf_spy_ratio) / xlf_spy_baseline) * 100) : 0); - let credit_stress_raw = (show_credit ? (((hyg_lqd_stress * 0.4) + (tlt_spy_flight * 0.3)) + (xlf_spy_weakness * 0.3)) : 0); - let credit_stress = pinescript.max(0, pinescript.min(100, (credit_stress_raw * 5))); - let ma_long = pinescript.sma(close, ma_period); - let price_deviation = (((close - ma_long) / ma_long) * 100); - let rsi = pinescript.rsi(close, rsi_period); - let rsi_overbought_days = pinescript.barssince(pinescript.series(1, (rsi < 70))); - let rsi_oversold = (rsi < 30); - let roc = pinescript.roc(close, 20); - let roc_ma = pinescript.sma(roc, 20); - let roc_accelerating = (roc > roc_ma); - let vol_avg = pinescript.sma(volume, vol_period); - let vol_spike = (volume / vol_avg); - let high_vol_down = ((vol_spike > 1.5) && (close < open)); - let [macd_line, signal_line, ] = pinescript.unpack(pinescript.macd(close, 12, 26, 9), 3); - let macd_bearish = pinescript.crossunder(pinescript.series(2, macd_line), pinescript.series(3, signal_line)); - let vix_spike = (((show_vix && !pinescript.na(vix)) && !pinescript.na(vix_5)) ? ((vix > (vix_5 * 1.3)) || (vix > 25)) : false); - let vix_extreme = ((show_vix && !pinescript.na(vix)) ? (vix > 40) : false); - let vix_low = ((show_vix && !pinescript.na(vix)) ? (vix < 20) : false); - let peak_20 = pinescript.highest(pinescript.series(4, close), 20); - let peak_60 = pinescript.highest(pinescript.series(5, close), 60); - let decline_20 = (((close - peak_20) / peak_20) * 100); - let decline_60 = (((close - peak_60) / peak_60) * 100); - let price_above_50ma = (close > pinescript.sma(close, 50)); - let breadth_proxy = (show_breadth ? (pinescript.sma((price_above_50ma ? 1 : 0), 20) * 100) : 50); - let correlation_proxy = (pinescript.abs(ta.correlation(close, ma_long, 20)) * 100); - let mania_price = ((price_deviation > 15) ? 25 : ((price_deviation > 10) ? 15 : 0)); - let mania_rsi = (((rsi > 70) && (rsi_overbought_days > 10)) ? 25 : ((rsi > 70) ? 15 : 0)); - let mania_breadth = ((breadth_proxy > 60) ? 20 : ((breadth_proxy > 50) ? 10 : 0)); - let mania_vix = (vix_low ? 15 : 0); - let mania_momentum = ((roc_accelerating && (roc > 0)) ? 15 : 0); - let mania_score_raw = ((((mania_price + mania_rsi) + mania_breadth) + mania_vix) + mania_momentum); - let panic_decline = (((decline_20 < -5) && (decline_20 > -15)) ? 20 : 0); - let panic_vix_spike = (vix_spike ? 20 : 0); - let panic_volume = (high_vol_down ? 15 : 0); - let panic_breadth = ((breadth_proxy < 40) ? 15 : 0); - let panic_credit = ((credit_stress > 30) ? 15 : 0); - let panic_momentum = (macd_bearish ? 15 : 0); - let panic_score_raw = (((((panic_decline + panic_vix_spike) + panic_volume) + panic_breadth) + panic_credit) + panic_momentum); - let crash_extreme_decline = ((decline_60 < -15) ? 25 : ((decline_60 < -10) ? 15 : 0)); - let crash_vix_extreme = (vix_extreme ? 20 : 0); - let crash_volume_climax = (((vol_spike > 2) && (close < open)) ? 15 : 0); - let crash_breadth_collapse = ((breadth_proxy < 20) ? 15 : 0); - let crash_correlation = ((correlation_proxy > 80) ? 15 : 0); - let crash_credit_freeze = ((credit_stress > 60) ? 10 : 0); - let crash_score_raw = (((((crash_extreme_decline + crash_vix_extreme) + crash_volume_climax) + crash_breadth_collapse) + crash_correlation) + crash_credit_freeze); - let mania_score = pinescript.sma(mania_score_raw, smooth_period); - let panic_score = pinescript.sma(panic_score_raw, smooth_period); - let crash_score = pinescript.sma(crash_score_raw, smooth_period); - let composite_risk = pinescript.max(mania_score, pinescript.max(panic_score, crash_score)); + var color_green_bright = pinescript.color.hex("#00ff00"); + var color_green_soft = pinescript.color.hex("#26a69a"); + var color_yellow_bright = pinescript.color.hex("#ffd700"); + var color_orange_alert = pinescript.color.hex("#ff6b35"); + var color_red_danger = pinescript.color.hex("#ff1744"); + var color_purple_composite = pinescript.color.hex("#ab47bc"); + var color_gray_neutral = pinescript.color.hex("#78909c"); + var color_dark_bg = pinescript.color.hex("#1e1e1e"); + var display_mode = pinescript.inputString("Smoothed All Lines", "Display Mode", ({ options: ["Smoothed All Lines", "Single Dominant Line", "Both"], group: "Display" })); + var smooth_period = pinescript.inputInt(3, "Smoothing Period", ({ minval: 1, maxval: 20, group: "Display" })); + var show_zones = pinescript.inputBool(true, "Show Background Phase Zones", ({ group: "Display" })); + var show_phase_labels = pinescript.inputBool(true, "Show Phase Change Labels", ({ group: "Display" })); + var show_historical_context = pinescript.inputBool(true, "Show Historical Context", ({ group: "Display" })); + var dashboard_position = pinescript.inputString("Top Right", "Dashboard Position", ({ options: ["Top Left", "Top Center", "Top Right", "Middle Left", "Middle Right", "Bottom Left", "Bottom Right"], group: "Dashboard" })); + var dashboard_size = pinescript.inputString("Normal", "Dashboard Size", ({ options: ["Small", "Normal", "Large", "Extra Large"], group: "Dashboard" })); + var show_components = pinescript.inputBool(true, "Show Component Health Panel", ({ group: "Dashboard" })); + var show_action_hints = pinescript.inputBool(true, "Show Action Guidance", ({ group: "Dashboard" })); + var mania_threshold = pinescript.inputInt(75, "Mania Alert Threshold", ({ minval: 0, maxval: 100, group: "Alert Thresholds" })); + var panic_threshold = pinescript.inputInt(60, "Panic Alert Threshold", ({ minval: 0, maxval: 100, group: "Alert Thresholds" })); + var crash_threshold = pinescript.inputInt(70, "Crash Alert Threshold", ({ minval: 0, maxval: 100, group: "Alert Thresholds" })); + var ma_period = pinescript.inputInt(200, "Long-term MA Period", ({ minval: 50, maxval: 300, group: "Technical Parameters" })); + var rsi_period = pinescript.inputInt(14, "RSI Period", ({ minval: 5, maxval: 30, group: "Technical Parameters" })); + var vol_period = pinescript.inputInt(20, "Volume Average Period", ({ minval: 10, maxval: 50, group: "Technical Parameters" })); + var show_credit = pinescript.inputBool(true, "Include Credit Risk Indicators", ({ group: "Components" })); + var show_breadth = pinescript.inputBool(true, "Include Market Breadth", ({ group: "Components" })); + var show_vix = pinescript.inputBool(true, "Include VIX Analysis", ({ group: "Components" })); + var vix = (show_vix ? pinescript.requestSecurity("TVC:VIX", timeframe.period, close) : null); + var vix_5 = (show_vix ? pinescript.requestSecurity("TVC:VIX", timeframe.period, pinescript.hist(0, close, 5)) : null); + var hyg = (show_credit ? pinescript.requestSecurity("AMEX:HYG", timeframe.period, close) : null); + var lqd = (show_credit ? pinescript.requestSecurity("AMEX:LQD", timeframe.period, close) : null); + var tlt = (show_credit ? pinescript.requestSecurity("NASDAQ:TLT", timeframe.period, close) : null); + var xlf = (show_credit ? pinescript.requestSecurity("AMEX:XLF", timeframe.period, close) : null); + var spy = (show_credit ? pinescript.requestSecurity("AMEX:SPY", timeframe.period, close) : null); + var adv_dec = (show_breadth ? pinescript.requestSecurity("INDEX:ADDN", timeframe.period, close) : null); + var hyg_lqd_ratio = (((show_credit && !pinescript.na(hyg)) && !pinescript.na(lqd)) ? (lqd / hyg) : null); + var hyg_lqd_spread = hyg_lqd_ratio; + var hyg_lqd_baseline = pinescript.sma(pinescript.series(1, hyg_lqd_spread), 60); + var hyg_lqd_stress = (((show_credit && !pinescript.na(hyg_lqd_spread)) && !pinescript.na(hyg_lqd_baseline)) ? (((hyg_lqd_spread - hyg_lqd_baseline) / hyg_lqd_baseline) * 100) : 0); + var tlt_spy_ratio = (((show_credit && !pinescript.na(tlt)) && !pinescript.na(spy)) ? (tlt / spy) : null); + var tlt_spy_baseline = pinescript.sma(pinescript.series(2, tlt_spy_ratio), 60); + var tlt_spy_flight = (((show_credit && !pinescript.na(tlt_spy_ratio)) && !pinescript.na(tlt_spy_baseline)) ? (((tlt_spy_ratio - tlt_spy_baseline) / tlt_spy_baseline) * 100) : 0); + var xlf_spy_ratio = (((show_credit && !pinescript.na(xlf)) && !pinescript.na(spy)) ? (xlf / spy) : null); + var xlf_spy_baseline = pinescript.sma(pinescript.series(3, xlf_spy_ratio), 60); + var xlf_spy_weakness = (((show_credit && !pinescript.na(xlf_spy_ratio)) && !pinescript.na(xlf_spy_baseline)) ? (((xlf_spy_baseline - xlf_spy_ratio) / xlf_spy_baseline) * 100) : 0); + var credit_stress_raw = (show_credit ? (((hyg_lqd_stress * 0.4) + (tlt_spy_flight * 0.3)) + (xlf_spy_weakness * 0.3)) : 0); + var credit_stress = pinescript.max(0, pinescript.min(100, (credit_stress_raw * 5))); + var ma_long = pinescript.sma(pinescript.series(4, close), ma_period); + var price_deviation = (((close - ma_long) / ma_long) * 100); + var rsi = pinescript.rsi(pinescript.series(5, close), rsi_period); + var rsi_overbought_days = pinescript.barssince(pinescript.series(6, (rsi < 70))); + var rsi_oversold = (rsi < 30); + var roc = pinescript.roc(pinescript.series(7, close), 20); + var roc_ma = pinescript.sma(pinescript.series(8, roc), 20); + var roc_accelerating = (roc > roc_ma); + var vol_avg = pinescript.sma(pinescript.series(9, volume), vol_period); + var vol_spike = (volume / vol_avg); + var high_vol_down = ((vol_spike > 1.5) && (close < open)); + var [macd_line, signal_line, ] = pinescript.unpack(pinescript.macd(close, 12, 26, 9), 3); + var macd_bearish = pinescript.crossunder(pinescript.series(10, macd_line), pinescript.series(11, signal_line)); + var vix_spike = (((show_vix && !pinescript.na(vix)) && !pinescript.na(vix_5)) ? ((vix > (vix_5 * 1.3)) || (vix > 25)) : false); + var vix_extreme = ((show_vix && !pinescript.na(vix)) ? (vix > 40) : false); + var vix_low = ((show_vix && !pinescript.na(vix)) ? (vix < 20) : false); + var peak_20 = pinescript.highest(pinescript.series(12, close), 20); + var peak_60 = pinescript.highest(pinescript.series(13, close), 60); + var decline_20 = (((close - peak_20) / peak_20) * 100); + var decline_60 = (((close - peak_60) / peak_60) * 100); + var price_above_50ma = (close > pinescript.sma(pinescript.series(14, close), 50)); + var breadth_proxy = (show_breadth ? (pinescript.sma(pinescript.series(15, (price_above_50ma ? 1 : 0)), 20) * 100) : 50); + var correlation_proxy = (pinescript.abs(pinescript.ta.correlation(close, ma_long, 20)) * 100); + var mania_price = ((price_deviation > 15) ? 25 : ((price_deviation > 10) ? 15 : 0)); + var mania_rsi = (((rsi > 70) && (rsi_overbought_days > 10)) ? 25 : ((rsi > 70) ? 15 : 0)); + var mania_breadth = ((breadth_proxy > 60) ? 20 : ((breadth_proxy > 50) ? 10 : 0)); + var mania_vix = (vix_low ? 15 : 0); + var mania_momentum = ((roc_accelerating && (roc > 0)) ? 15 : 0); + var mania_score_raw = ((((mania_price + mania_rsi) + mania_breadth) + mania_vix) + mania_momentum); + var panic_decline = (((decline_20 < -5) && (decline_20 > -15)) ? 20 : 0); + var panic_vix_spike = (vix_spike ? 20 : 0); + var panic_volume = (high_vol_down ? 15 : 0); + var panic_breadth = ((breadth_proxy < 40) ? 15 : 0); + var panic_credit = ((credit_stress > 30) ? 15 : 0); + var panic_momentum = (macd_bearish ? 15 : 0); + var panic_score_raw = (((((panic_decline + panic_vix_spike) + panic_volume) + panic_breadth) + panic_credit) + panic_momentum); + var crash_extreme_decline = ((decline_60 < -15) ? 25 : ((decline_60 < -10) ? 15 : 0)); + var crash_vix_extreme = (vix_extreme ? 20 : 0); + var crash_volume_climax = (((vol_spike > 2) && (close < open)) ? 15 : 0); + var crash_breadth_collapse = ((breadth_proxy < 20) ? 15 : 0); + var crash_correlation = ((correlation_proxy > 80) ? 15 : 0); + var crash_credit_freeze = ((credit_stress > 60) ? 10 : 0); + var crash_score_raw = (((((crash_extreme_decline + crash_vix_extreme) + crash_volume_climax) + crash_breadth_collapse) + crash_correlation) + crash_credit_freeze); + var mania_score = pinescript.sma(pinescript.series(16, mania_score_raw), smooth_period); + var panic_score = pinescript.sma(pinescript.series(17, panic_score_raw), smooth_period); + var crash_score = pinescript.sma(pinescript.series(18, crash_score_raw), smooth_period); + var composite_risk = pinescript.max(mania_score, pinescript.max(panic_score, crash_score)); if (state.phase === undefined) state.phase = "NORMAL"; if (state.phase_color === undefined) state.phase_color = color_green_soft; if (state.phase_start_bar === undefined) state.phase_start_bar = null; - let new_phase = "NORMAL"; + var new_phase = "NORMAL"; if ((crash_score > crash_threshold)) { new_phase = "CRASH"; } else { @@ -1796,13 +1955,13 @@ function main() { } } } - let phase_changed = (new_phase !== state.phase); + var phase_changed = (new_phase !== state.phase); if (phase_changed) { state.phase = new_phase; state.phase_start_bar = bar_index; } state.phase_color = ((state.phase === "CRASH") ? color_red_danger : ((state.phase === "PANIC") ? color_orange_alert : ((state.phase === "MANIA") ? color_yellow_bright : color_green_soft))); - let days_in_phase = (!pinescript.na(state.phase_start_bar) ? ((bar_index - state.phase_start_bar) + 1) : 0); + var days_in_phase = (!pinescript.na(state.phase_start_bar) ? ((bar_index - state.phase_start_bar) + 1) : 0); if (state.historical_context === undefined) state.historical_context = ""; if (show_historical_context) { if ((state.phase === "CRASH")) { @@ -1855,38 +2014,38 @@ function main() { } } } - let vix_status = ((show_vix && !pinescript.na(vix)) ? ((vix > 40) ? "🔴 EXTREME" : ((vix > 25) ? "🟠 HIGH" : ((vix < 20) ? "🟢 LOW" : "🟡 MODERATE"))) : "N/A"); - let credit_status = (show_credit ? ((credit_stress > 70) ? "🔴 FREEZE" : ((credit_stress > 40) ? "🟠 STRESS" : ((credit_stress > 20) ? "🟡 ELEVATED" : "🟢 NORMAL"))) : "N/A"); - let breadth_status = (show_breadth ? ((breadth_proxy < 20) ? "🔴 COLLAPSE" : ((breadth_proxy < 40) ? "🟠 WEAK" : ((breadth_proxy > 60) ? "🟢 STRONG" : "🟡 NEUTRAL"))) : "N/A"); - let momentum_status = ((rsi > 70) ? "🔴 OVERBOUGHT" : ((rsi < 30) ? "🟢 OVERSOLD" : ((roc > 5) ? "🟢 BULLISH" : ((roc < -5) ? "🔴 BEARISH" : "🟡 NEUTRAL")))); + var vix_status = ((show_vix && !pinescript.na(vix)) ? ((vix > 40) ? "🔴 EXTREME" : ((vix > 25) ? "🟠 HIGH" : ((vix < 20) ? "🟢 LOW" : "🟡 MODERATE"))) : "N/A"); + var credit_status = (show_credit ? ((credit_stress > 70) ? "🔴 FREEZE" : ((credit_stress > 40) ? "🟠 STRESS" : ((credit_stress > 20) ? "🟡 ELEVATED" : "🟢 NORMAL"))) : "N/A"); + var breadth_status = (show_breadth ? ((breadth_proxy < 20) ? "🔴 COLLAPSE" : ((breadth_proxy < 40) ? "🟠 WEAK" : ((breadth_proxy > 60) ? "🟢 STRONG" : "🟡 NEUTRAL"))) : "N/A"); + var momentum_status = ((rsi > 70) ? "🔴 OVERBOUGHT" : ((rsi < 30) ? "🟢 OVERSOLD" : ((roc > 5) ? "🟢 BULLISH" : ((roc < -5) ? "🔴 BEARISH" : "🟡 NEUTRAL")))); if (((show_phase_labels && phase_changed) && barstate.isconfirmed)) { if ((state.phase !== "NORMAL")) { - let label_text = ((state.phase === "CRASH") ? "🔴 CRASH" : ((state.phase === "PANIC") ? "🟠 PANIC" : ((state.phase === "MANIA") ? "🟡 MANIA" : ""))); - let label_color = ((state.phase === "CRASH") ? pinescript.color.new(color_red_danger, 0) : ((state.phase === "PANIC") ? pinescript.color.new(color_orange_alert, 0) : ((state.phase === "MANIA") ? pinescript.color.new(color_yellow_bright, 0) : null))); + var label_text = ((state.phase === "CRASH") ? "🔴 CRASH" : ((state.phase === "PANIC") ? "🟠 PANIC" : ((state.phase === "MANIA") ? "🟡 MANIA" : ""))); + var label_color = ((state.phase === "CRASH") ? pinescript.color.new(color_red_danger, 0) : ((state.phase === "PANIC") ? pinescript.color.new(color_orange_alert, 0) : ((state.phase === "MANIA") ? pinescript.color.new(color_yellow_bright, 0) : null))); if ((label_text !== "")) { pinescript.labelNew(bar_index, composite_risk, label_text, ({ style: label.style_label_down, color: label_color, textcolor: pinescript.color.white, size: pinescript.size.large, textalign: pinescript.text.align_center })); } } } - let show_all_lines = ((display_mode === "Smoothed All Lines") || (display_mode === "Both")); - let show_dominant_only = ((display_mode === "Single Dominant Line") || (display_mode === "Both")); + var show_all_lines = ((display_mode === "Smoothed All Lines") || (display_mode === "Both")); + var show_dominant_only = ((display_mode === "Single Dominant Line") || (display_mode === "Both")); pinescript.plot((show_all_lines ? mania_score : null), ({ title: "Mania Score", color: pinescript.color.new(color_yellow_bright, 0), linewidth: 2 })); pinescript.plot((show_all_lines ? panic_score : null), ({ title: "Panic Score", color: pinescript.color.new(color_orange_alert, 0), linewidth: 2 })); pinescript.plot((show_all_lines ? crash_score : null), ({ title: "Crash Score", color: pinescript.color.new(color_red_danger, 0), linewidth: 2 })); - let dominant_color = ((state.phase === "CRASH") ? color_red_danger : ((state.phase === "PANIC") ? color_orange_alert : ((state.phase === "MANIA") ? color_yellow_bright : color_gray_neutral))); + var dominant_color = ((state.phase === "CRASH") ? color_red_danger : ((state.phase === "PANIC") ? color_orange_alert : ((state.phase === "MANIA") ? color_yellow_bright : color_gray_neutral))); pinescript.plot((show_dominant_only ? composite_risk : null), ({ title: "Dominant Risk", color: pinescript.color.new(dominant_color, 0), linewidth: 4, style: plot.style_line })); - let bg_color = (show_zones ? ((state.phase === "CRASH") ? pinescript.color.new(color_red_danger, 88) : ((state.phase === "PANIC") ? pinescript.color.new(color_orange_alert, 92) : ((state.phase === "MANIA") ? pinescript.color.new(color_yellow_bright, 94) : pinescript.color.new(color_green_soft, 96)))) : null); + var bg_color = (show_zones ? ((state.phase === "CRASH") ? pinescript.color.new(color_red_danger, 88) : ((state.phase === "PANIC") ? pinescript.color.new(color_orange_alert, 92) : ((state.phase === "MANIA") ? pinescript.color.new(color_yellow_bright, 94) : pinescript.color.new(color_green_soft, 96)))) : null); pinescript.bgcolor(bg_color, ({ title: "Phase Background" })); pinescript.hline(100, "Max", ({ color: pinescript.color.new(color_gray_neutral, 70), linestyle: hline.style_solid })); pinescript.hline(crash_threshold, "Crash Threshold", ({ color: pinescript.color.new(color_red_danger, 50), linestyle: hline.style_dashed, linewidth: 2 })); pinescript.hline(panic_threshold, "Panic Threshold", ({ color: pinescript.color.new(color_orange_alert, 50), linestyle: hline.style_dashed, linewidth: 2 })); pinescript.hline(mania_threshold, "Mania Threshold", ({ color: pinescript.color.new(color_yellow_bright, 50), linestyle: hline.style_dashed, linewidth: 2 })); pinescript.hline(0, "Min", ({ color: pinescript.color.new(color_gray_neutral, 70), linestyle: hline.style_solid })); - let h100 = pinescript.hline(100, ({ color: pinescript.color.new(pinescript.color.gray, 100) })); - let h85 = pinescript.hline(85, ({ color: pinescript.color.new(pinescript.color.gray, 100) })); - let h60 = pinescript.hline(60, ({ color: pinescript.color.new(pinescript.color.gray, 100) })); - let h30 = pinescript.hline(30, ({ color: pinescript.color.new(pinescript.color.gray, 100) })); - let h0 = pinescript.hline(0, ({ color: pinescript.color.new(pinescript.color.gray, 100) })); + var h100 = pinescript.hline(100, ({ color: pinescript.color.new(pinescript.color.gray, 100) })); + var h85 = pinescript.hline(85, ({ color: pinescript.color.new(pinescript.color.gray, 100) })); + var h60 = pinescript.hline(60, ({ color: pinescript.color.new(pinescript.color.gray, 100) })); + var h30 = pinescript.hline(30, ({ color: pinescript.color.new(pinescript.color.gray, 100) })); + var h0 = pinescript.hline(0, ({ color: pinescript.color.new(pinescript.color.gray, 100) })); pinescript.fill(h85, h100, ({ color: pinescript.color.new(color_red_danger, 92), title: "Extreme Risk" })); pinescript.fill(h60, h85, ({ color: pinescript.color.new(color_orange_alert, 92), title: "High Risk" })); pinescript.fill(h30, h60, ({ color: pinescript.color.new(color_yellow_bright, 95), title: "Elevated Risk" })); @@ -1939,21 +2098,21 @@ function main() { } } } - let title_rows = 1; - let base_rows = 4; - let component_rows = (show_components ? 5 : 0); - let total_rows = ((title_rows + base_rows) + component_rows); + var title_rows = 1; + var base_rows = 4; + var component_rows = (show_components ? 5 : 0); + var total_rows = ((title_rows + base_rows) + component_rows); if (state.dashboard === undefined) state.dashboard = pinescript.table.new(state.h_position, 2, total_rows, ({ bgcolor: pinescript.color.new(color_dark_bg, 80), border_width: 3, border_color: state.phase_color })); if (barstate.islast) { - let row = 0; + var row = 0; pinescript.table.cell(state.dashboard, 0, row, "", ({ bgcolor: pinescript.color.new(state.phase_color, 20) })); pinescript.table.cell(state.dashboard, 1, row, "🟡 Manias, 🟠 Panics and 🔴 Crashes", ({ text_color: pinescript.color.white, text_size: state.text_size_header, text_halign: pinescript.text.align_center, bgcolor: pinescript.color.new(state.phase_color, 20) })); row = (row + 1); pinescript.table.cell(state.dashboard, 0, row, "PHASE", ({ text_color: pinescript.color.white, text_size: state.text_size_normal, bgcolor: pinescript.color.new(color_gray_neutral, 60) })); pinescript.table.cell(state.dashboard, 1, row, state.phase, ({ text_color: state.phase_color, text_size: state.text_size_header, bgcolor: pinescript.color.new(color_dark_bg, 50) })); row = (row + 1); - let risk_display = (pinescript.strToString(pinescript.round(composite_risk)) + " / 100"); - let risk_label = ((composite_risk > 85) ? "🔴 EXTREME RISK" : ((composite_risk > 60) ? "🟠 HIGH RISK" : ((composite_risk > 30) ? "🟡 ELEVATED RISK" : "🟢 LOW RISK"))); + var risk_display = (pinescript.strToString(pinescript.round(composite_risk)) + " / 100"); + var risk_label = ((composite_risk > 85) ? "🔴 EXTREME RISK" : ((composite_risk > 60) ? "🟠 HIGH RISK" : ((composite_risk > 30) ? "🟡 ELEVATED RISK" : "🟢 LOW RISK"))); pinescript.table.cell(state.dashboard, 0, row, "RISK", ({ text_color: pinescript.color.white, text_size: state.text_size_normal, bgcolor: pinescript.color.new(color_gray_neutral, 60) })); pinescript.table.cell(state.dashboard, 1, row, ((risk_display + "n") + risk_label), ({ text_color: state.phase_color, text_size: state.text_size_normal, bgcolor: pinescript.color.new(color_dark_bg, 50) })); row = (row + 1); @@ -1971,42 +2130,42 @@ function main() { pinescript.table.cell(state.dashboard, 0, row, "COMPONENTS", ({ text_color: pinescript.color.white, text_size: state.text_size_normal, bgcolor: pinescript.color.new(color_purple_composite, 40) })); pinescript.table.cell(state.dashboard, 1, row, "STATUS", ({ text_color: pinescript.color.white, text_size: state.text_size_normal, bgcolor: pinescript.color.new(color_purple_composite, 40) })); row = (row + 1); - let vix_value = ((show_vix && !pinescript.na(vix)) ? pinescript.strToString((pinescript.round((vix * 10)) / 10)) : "N/A"); + var vix_value = ((show_vix && !pinescript.na(vix)) ? pinescript.strToString((pinescript.round((vix * 10)) / 10)) : "N/A"); pinescript.table.cell(state.dashboard, 0, row, ("VIX (Fear): " + vix_value), ({ text_color: pinescript.color.white, text_size: state.text_size_normal, bgcolor: pinescript.color.new(color_dark_bg, 65) })); pinescript.table.cell(state.dashboard, 1, row, vix_status, ({ text_color: pinescript.color.white, text_size: state.text_size_normal, bgcolor: pinescript.color.new(color_dark_bg, 65) })); row = (row + 1); - let credit_value = (show_credit ? pinescript.strToString(pinescript.round(credit_stress)) : "N/A"); + var credit_value = (show_credit ? pinescript.strToString(pinescript.round(credit_stress)) : "N/A"); pinescript.table.cell(state.dashboard, 0, row, ("Credit (Bonds): " + credit_value), ({ text_color: pinescript.color.white, text_size: state.text_size_normal, bgcolor: pinescript.color.new(color_dark_bg, 65) })); pinescript.table.cell(state.dashboard, 1, row, credit_status, ({ text_color: pinescript.color.white, text_size: state.text_size_normal, bgcolor: pinescript.color.new(color_dark_bg, 65) })); row = (row + 1); - let breadth_value = (show_breadth ? (pinescript.strToString(pinescript.round(breadth_proxy)) + "%") : "N/A"); + var breadth_value = (show_breadth ? (pinescript.strToString(pinescript.round(breadth_proxy)) + "%") : "N/A"); pinescript.table.cell(state.dashboard, 0, row, ("Breadth (Participation): " + breadth_value), ({ text_color: pinescript.color.white, text_size: state.text_size_normal, bgcolor: pinescript.color.new(color_dark_bg, 65) })); pinescript.table.cell(state.dashboard, 1, row, breadth_status, ({ text_color: pinescript.color.white, text_size: state.text_size_normal, bgcolor: pinescript.color.new(color_dark_bg, 65) })); row = (row + 1); - let rsi_value = pinescript.strToString(pinescript.round(rsi)); + var rsi_value = pinescript.strToString(pinescript.round(rsi)); pinescript.table.cell(state.dashboard, 0, row, ("Momentum (RSI): " + rsi_value), ({ text_color: pinescript.color.white, text_size: state.text_size_normal, bgcolor: pinescript.color.new(color_dark_bg, 65) })); pinescript.table.cell(state.dashboard, 1, row, momentum_status, ({ text_color: pinescript.color.white, text_size: state.text_size_normal, bgcolor: pinescript.color.new(color_dark_bg, 65) })); } } - let mania_alert = ((state.phase === "MANIA") && (pinescript.hist(6, state.phase, 1) !== "MANIA")); - let panic_alert = ((state.phase === "PANIC") && (pinescript.hist(7, state.phase, 1) !== "PANIC")); - let crash_alert = ((state.phase === "CRASH") && (pinescript.hist(8, state.phase, 1) !== "CRASH")); - let breadth_collapse_alert = ((breadth_proxy < 30) && (pinescript.hist(9, breadth_proxy, 1) >= 30)); - let vix_spike_alert = ((((show_vix && !pinescript.na(vix)) && !pinescript.na(vix_5)) && (vix > (vix_5 * 1.3))) && (pinescript.hist(10, vix, 1) <= (pinescript.hist(11, vix_5, 1) * 1.3))); - let credit_freeze_alert = ((credit_stress > 70) && (pinescript.hist(12, credit_stress, 1) <= 70)); + var mania_alert = ((state.phase === "MANIA") && (pinescript.hist(19, state.phase, 1) !== "MANIA")); + var panic_alert = ((state.phase === "PANIC") && (pinescript.hist(20, state.phase, 1) !== "PANIC")); + var crash_alert = ((state.phase === "CRASH") && (pinescript.hist(21, state.phase, 1) !== "CRASH")); + var breadth_collapse_alert = ((breadth_proxy < 30) && (pinescript.hist(22, breadth_proxy, 1) >= 30)); + var vix_spike_alert = ((((show_vix && !pinescript.na(vix)) && !pinescript.na(vix_5)) && (vix > (vix_5 * 1.3))) && (pinescript.hist(23, vix, 1) <= (pinescript.hist(24, vix_5, 1) * 1.3))); + var credit_freeze_alert = ((credit_stress > 70) && (pinescript.hist(25, credit_stress, 1) <= 70)); pinescript.alertcondition(mania_alert, ({ title: "🟡 Mania Phase Detected", message: "🟡 MANIA PHASE: Elevated crash risk detected. Consider reducing exposure and taking profits." })); pinescript.alertcondition(panic_alert, ({ title: "🟠 Panic Phase Detected", message: "🟠 PANIC PHASE: Severe market stress detected. Consider raising cash and avoiding buying dips." })); pinescript.alertcondition(crash_alert, ({ title: "🔴 CRASH DETECTED", message: "🔴 CRASH PHASE: Capitulation event detected. Extreme risk - prepare to buy quality after capitulation." })); pinescript.alertcondition(breadth_collapse_alert, ({ title: "⚠️ Breadth Collapse", message: "⚠️ Market breadth collapsed below 30% - participation deteriorating rapidly." })); pinescript.alertcondition(vix_spike_alert, ({ title: "⚠️ VIX Spike", message: "⚠️ VIX spiked >30% in 5 days - fear increasing rapidly." })); pinescript.alertcondition(credit_freeze_alert, ({ title: "⚠️ Credit Freeze", message: "⚠️ Credit stress extreme - potential liquidity crisis developing." })); - plotchar(composite_risk, ({ title: "Composite Risk", char: "", location: pinescript.location.top, display: display.data_window })); - plotchar(mania_score, ({ title: "Mania Score", char: "", location: pinescript.location.top, display: display.data_window })); - plotchar(panic_score, ({ title: "Panic Score", char: "", location: pinescript.location.top, display: display.data_window })); - plotchar(crash_score, ({ title: "Crash Score", char: "", location: pinescript.location.top, display: display.data_window })); - plotchar((show_vix ? vix : null), ({ title: "VIX", char: "", location: pinescript.location.top, display: display.data_window })); - plotchar(credit_stress, ({ title: "Credit Stress", char: "", location: pinescript.location.top, display: display.data_window })); - plotchar(breadth_proxy, ({ title: "Breadth %", char: "", location: pinescript.location.top, display: display.data_window })); + pinescript.plotchar(composite_risk, ({ title: "Composite Risk", char: "", location: pinescript.location.top, display: display.data_window })); + pinescript.plotchar(mania_score, ({ title: "Mania Score", char: "", location: pinescript.location.top, display: display.data_window })); + pinescript.plotchar(panic_score, ({ title: "Panic Score", char: "", location: pinescript.location.top, display: display.data_window })); + pinescript.plotchar(crash_score, ({ title: "Crash Score", char: "", location: pinescript.location.top, display: display.data_window })); + pinescript.plotchar((show_vix ? vix : null), ({ title: "VIX", char: "", location: pinescript.location.top, display: display.data_window })); + pinescript.plotchar(credit_stress, ({ title: "Credit Stress", char: "", location: pinescript.location.top, display: display.data_window })); + pinescript.plotchar(breadth_proxy, ({ title: "Breadth %", char: "", location: pinescript.location.top, display: display.data_window })); } @@ -2051,6 +2210,10 @@ function run(data, options = {}) { rt.__shapeIdx = 0; globalThis.bar_index = i; globalThis.last_bar_index = n - 1; + globalThis.time_tradingday = inTime[i]; + globalThis.time_close = inTime[i]; + globalThis.last_bar_time = inTime[n - 1]; + globalThis.timenow = inTime[n - 1]; globalThis.barstate = { isfirst: i === 0, islast: i === n - 1, isrealtime: false, ishistory: true, isconfirmed: true, isnew: true, islastconfirmedhistory: i === n - 1, diff --git a/converted/NY_5m_15m_Orb_-_Statistics_LTF_Candle_structure.js b/converted/NY_5m_15m_Orb_-_Statistics_LTF_Candle_structure.js index 45c5041..7e3d0b8 100644 --- a/converted/NY_5m_15m_Orb_-_Statistics_LTF_Candle_structure.js +++ b/converted/NY_5m_15m_Orb_-_Statistics_LTF_Candle_structure.js @@ -42,6 +42,80 @@ const pinescript = { if (value != null && typeof value[Symbol.iterator] === 'function') return Array.from(value); return new Array(count || 0).fill(null); }, + __decArr: function(arr) { + if (!Array.isArray(arr) || arr.__pineDecorated) return arr; + const self = this; + const def = (name, fn) => Object.defineProperty(arr, name, { value: fn, writable: true, configurable: true }); + Object.defineProperty(arr, '__pineDecorated', { value: true, configurable: true }); + def('get', (i) => (arr[i] === undefined ? null : arr[i])); + def('set', (i, v) => { arr[i] = v; return v; }); + def('size', () => arr.length); + def('clear', () => { arr.length = 0; }); + def('insert', (i, v) => { arr.splice(i, 0, v); }); + def('remove', (i) => arr.splice(i, 1)[0]); + def('contains', (v) => arr.includes(v)); + def('indexof', (v) => arr.indexOf(v)); + def('lastindexof', (v) => arr.lastIndexOf(v)); + def('first', () => (arr.length ? arr[0] : null)); + def('last', () => (arr.length ? arr[arr.length - 1] : null)); + def('sum', () => arr.reduce((a, b) => a + b, 0)); + def('avg', () => (arr.length ? arr.reduce((a, b) => a + b, 0) / arr.length : 0)); + def('min', () => (arr.length ? Math.min(...arr) : null)); + def('max', () => (arr.length ? Math.max(...arr) : null)); + def('range', () => (arr.length ? Math.max(...arr) - Math.min(...arr) : null)); + // Statistical methods that route to the array.* built-ins (these names are not + // native to JS arrays, so attaching them here is safe from recursion). + def('median', () => self.arrayMedian(arr)); + def('mode', () => self.arrayMode(arr)); + def('stdev', () => self.arrayStdev(arr)); + def('variance', () => self.arrayVariance(arr)); + def('covariance', (other) => self.arrayCovariance(arr, other)); + def('percentile_linear_interpolation', (p) => self.arrayPercentileLinearInterpolation(arr, p)); + def('percentile_nearest_rank', (p) => self.arrayPercentileNearestRank(arr, p)); + def('abs', () => self.__decArr(self.arrayAbs(arr))); + def('binary_search', (v) => self.arrayBinarySearch(arr, v)); + // Pine's array.sort takes an order string, not a comparator. Use the native + // sort via .call so we don't recurse through this overridden method. + def('sort', (order) => { Array.prototype.sort.call(arr, (a, b) => (order === 'descending' ? b - a : a - b)); return arr; }); + def('sort_indices', (order) => self.__decArr(arr.map((_, i) => i).sort((a, b) => (order === 'descending' ? arr[b] - arr[a] : arr[a] - arr[b])))); + // join/slice/reverse/concat/includes/fill already exist natively on Array with + // compatible semantics, so we deliberately leave them to the native methods. + return arr; + }, + __decMap: function(m) { + if (!(m instanceof Map) || m.__pineDecorated) return m; + const def = (name, fn) => Object.defineProperty(m, name, { value: fn, writable: true, configurable: true }); + Object.defineProperty(m, '__pineDecorated', { value: true, configurable: true }); + def('put', (k, v) => { m.set(k, v); return v; }); + def('contains', (k) => m.has(k)); + def('remove', (k) => m.delete(k)); + def('keys', () => Array.from(Map.prototype.keys.call(m))); + def('values', () => Array.from(Map.prototype.values.call(m))); + def('size_', () => m.size); + return m; + }, + __decMatrix: function(m) { + if (!m || typeof m !== 'object' || m.__pineDecorated) return m; + const self = this; + const def = (name, fn) => Object.defineProperty(m, name, { value: fn, writable: true, configurable: true }); + Object.defineProperty(m, '__pineDecorated', { value: true, configurable: true }); + def('get', (r, c) => self.matrixGet(m, r, c)); + def('set', (r, c, v) => self.matrixSet(m, r, c, v)); + def('rows_', () => m.rows); + def('columns', () => m.cols); + def('fill', (v) => self.matrixFill(m, v)); + return m; + }, + __decDraw: function(obj) { + if (!obj || typeof obj !== 'object') return obj; + const p = new Proxy(obj, { + get(t, k) { + if (k in t || typeof k === 'symbol') return t[k]; + return function() { return p; }; + }, + }); + return p; + }, alertcondition: function(condition, ...rest) { if (globalThis.__pineRuntime) { globalThis.__pineRuntime.alerts.push({ condition, args: rest }); @@ -49,6 +123,32 @@ const pinescript = { return null; }, barcolor: function(color) { return null; }, + plotchar: function(series, ...rest) { + const rt = globalThis.__pineRuntime; + if (!rt) return series; + const bar = rt.__barIndex | 0; + const ord = (rt.__shapeIdx = (rt.__shapeIdx | 0) + 1) - 1; + let key = 'char_' + ord; + for (const r of rest) { + if (typeof r === 'string') { key = r; break; } + if (r && typeof r === 'object' && r.title) { key = String(r.title); break; } + } + let s = rt.plotshapes[key]; + if (!s) s = rt.plotshapes[key] = { title: key, data: [] }; + s.data[bar] = this.__scalar(series); + return series; + }, + plotarrow: function(series, ...rest) { + const rt = globalThis.__pineRuntime; + if (!rt) return series; + const bar = rt.__barIndex | 0; + const ord = (rt.__plotIdx = (rt.__plotIdx | 0) + 1) - 1; + const key = 'arrow_' + ord; + let p = rt.plots[key]; + if (!p) p = rt.plots[key] = { title: key, data: [] }; + p.data[bar] = this.__scalar(series); + return series; + }, bgcolor: function(color, title, editable, showLast) { return null; }, @@ -470,6 +570,12 @@ const pinescript = { atan: function(value) { return Math.atan(value); }, + todegrees: function(radians) { + return radians * (180 / Math.PI); + }, + toradians: function(degrees) { + return degrees * (Math.PI / 180); + }, floor: function(value) { return Math.floor(value); }, @@ -641,7 +747,7 @@ const pinescript = { return series; }, lineNew: function(x1, y1, x2, y2, opts = {}) { - return { x1, y1, x2, y2, opts, _type: 'line' }; + return this.__decDraw({ x1, y1, x2, y2, opts, _type: 'line' }); }, lineDelete: function(l) { return null; @@ -661,7 +767,7 @@ const pinescript = { return point === 0 || point === 'y1' ? line.y1 : line.y2; }, labelNew: function(x, y, text = '', opts = {}) { - return { x, y, text, opts, _type: 'label' }; + return this.__decDraw({ x, y, text, opts, _type: 'label' }); }, labelDelete: function(l) { return null; @@ -676,7 +782,7 @@ const pinescript = { return label.text || ''; }, boxNew: function(left, top, right, bottom, opts = {}) { - return { left, top, right, bottom, opts, _type: 'box' }; + return this.__decDraw({ left, top, right, bottom, opts, _type: 'box' }); }, boxDelete: function(box) { return null; @@ -694,7 +800,7 @@ const pinescript = { return box; }, polylineNew: function(points, opts = {}) { - return { points: points || [], opts, _type: 'polyline' }; + return this.__decDraw({ points: points || [], opts, _type: 'polyline' }); }, polylineDelete: function(poly) { return null; @@ -765,7 +871,7 @@ const pinescript = { return { time: _time ?? null, price: _price ?? null }; }, mapNew: function() { - return new Map(); + return this.__decMap(new Map()); }, mapSize: function(m) { return m instanceof Map ? m.size : 0; @@ -800,7 +906,7 @@ const pinescript = { const r = Math.max(0, rows ?? 0); const c = Math.max(0, cols ?? 0); const data = Array.from({ length: r }, () => Array.from({ length: c }, () => initialValue)); - return { rows: r, cols: c, data }; + return this.__decMatrix({ rows: r, cols: c, data }); }, matrixRows: function(m) { return m?.rows ?? 0; @@ -923,11 +1029,62 @@ const pinescript = { } return null; }, + __jacobiEigen: function(m) { + if (!m || !Array.isArray(m.data)) return null; + const n = m.rows ?? 0; + if (n === 0 || n !== (m.cols ?? 0)) return null; + // Work on a copy so the input matrix is untouched. + const a = m.data.map(row => row.slice()); + const v = Array.from({ length: n }, (_, i) => Array.from({ length: n }, (_, j) => (i === j ? 1 : 0))); + for (let sweep = 0; sweep < 100; sweep++) { + let off = 0; + for (let p = 0; p < n; p++) for (let q = p + 1; q < n; q++) off += a[p][q] * a[p][q]; + if (off < 1e-20) break; + for (let p = 0; p < n; p++) { + for (let q = p + 1; q < n; q++) { + if (Math.abs(a[p][q]) < 1e-18) continue; + const theta = (a[q][q] - a[p][p]) / (2 * a[p][q]); + const t = Math.sign(theta || 1) / (Math.abs(theta) + Math.sqrt(theta * theta + 1)); + const cos = 1 / Math.sqrt(t * t + 1); + const sin = t * cos; + for (let k = 0; k < n; k++) { + const akp = a[k][p], akq = a[k][q]; + a[k][p] = cos * akp - sin * akq; + a[k][q] = sin * akp + cos * akq; + } + for (let k = 0; k < n; k++) { + const apk = a[p][k], aqk = a[q][k]; + a[p][k] = cos * apk - sin * aqk; + a[q][k] = sin * apk + cos * aqk; + } + for (let k = 0; k < n; k++) { + const vkp = v[k][p], vkq = v[k][q]; + v[k][p] = cos * vkp - sin * vkq; + v[k][q] = sin * vkp + cos * vkq; + } + } + } + } + // Sort eigenpairs by eigenvalue, descending. + const order = Array.from({ length: n }, (_, i) => i).sort((i, j) => a[j][j] - a[i][i]); + const values = order.map(i => a[i][i]); + const vectors = Array.from({ length: n }, (_, r) => order.map(c => v[r][c])); + return { values, vectors }; + }, + matrixEigenvalues: function(m) { + const e = this.__jacobiEigen(m); + return this.__decArr(e ? e.values : []); + }, + matrixEigenvectors: function(m) { + const e = this.__jacobiEigen(m); + if (!e) return this.__decMatrix({ rows: 0, cols: 0, data: [] }); + return this.__decMatrix({ rows: e.vectors.length, cols: e.vectors.length, data: e.vectors }); + }, requestSecurity: function(symbol, timeframe, expression) { return expression; }, arrayNew: function(initialSize = 0, initialValue = 0) { - return Array(initialSize).fill(initialValue); + return this.__decArr(Array(initialSize).fill(initialValue)); }, arraySize: function(arr) { return arr ? arr.length : 0; @@ -978,10 +1135,10 @@ const pinescript = { return arr; }, arraySlice: function(arr, startIndex = 0, endIndex = null) { - if (!arr) return []; + if (!arr) return this.__decArr([]); const start = Number(startIndex) || 0; const end = endIndex === null || endIndex === undefined ? arr.length : Number(endIndex) || 0; - return arr.slice(start, end); + return this.__decArr(arr.slice(start, end)); }, arraySort: function(arr, order = 'ascending') { if (arr) arr.sort((a, b) => order === 'ascending' ? a - b : b - a); @@ -1191,7 +1348,7 @@ const pinescript = { const info = { ticker: 'AAPL', tickerid: 'NASDAQ:AAPL', prefix: 'NASDAQ', root: 'AAPL', suffix: '' }; return info[type] || ''; }, - timenow: 1781574527774, + timenow: 1782783576913, barstate: "LAST", dividends: {}, splits: {}, @@ -1245,11 +1402,11 @@ const pinescript = { arrayConcat: function(arr1, arr2) { if (!arr1) return arr2 || []; if (!arr2) return arr1; - return arr1.concat(arr2); + return this.__decArr(arr1.concat(arr2)); }, arrayCopy: function(arr) { - if (!arr) return []; - return [...arr]; + if (!arr) return this.__decArr([]); + return this.__decArr([...arr]); }, arrayBinarySearch: function(arr, value) { if (!arr || arr.length === 0) return -1; @@ -1339,7 +1496,7 @@ globalThis.input.timeframe = globalThis.input.timeframe || ((defval) => defval); globalThis.array = globalThis.array || { - from: (...items) => items, + from: (...items) => pinescript.__decArr(items), size: (arr) => pinescript.arraySize(arr), @@ -1508,6 +1665,8 @@ globalThis.line = globalThis.line || __pineNS({ style_solid: "solid", style_dash globalThis.box = globalThis.box || __pineNS({}); +globalThis.color = globalThis.color || __pineNS(Object.assign(function(c) { return c; }, { new: function(c, t) { return c; }, rgb: function(r, g, b, t) { return "#rgb(" + [r, g, b].join(",") + ")"; }, from_gradient: function(v, lo, hi, c1, c2) { return c1; }, r: function() { return 0; }, g: function() { return 0; }, b: function() { return 0; }, t: function() { return 0; }, aqua: "#00BCD4", black: "#363A45", blue: "#2962FF", fuchsia: "#E040FB", gray: "#787B86", green: "#4CAF50", lime: "#00E676", maroon: "#880E4F", navy: "#311B92", olive: "#808000", orange: "#FF9800", purple: "#9C27B0", red: "#FF5252", silver: "#B2B5BE", teal: "#00897B", white: "#FFFFFF", yellow: "#FFEB3B" })); + globalThis.label = globalThis.label || __pineNS({ style_label_down: "label_down", style_label_up: "label_up", style_none: "none" }); globalThis.polyline = globalThis.polyline || __pineNS({}); @@ -1520,7 +1679,9 @@ globalThis.map = globalThis.map || __pineNS({}); globalThis.session = globalThis.session || __pineNS({ regular: "regular", extended: "extended" }); -globalThis.dayofweek = globalThis.dayofweek || __pineNS({ sunday: 1, monday: 2, tuesday: 3, wednesday: 4, thursday: 5, friday: 6, saturday: 7 }); +globalThis.ticker = globalThis.ticker || __pineNS({}); + +globalThis.dayofweek = globalThis.dayofweek || __pineNS(Object.assign(function(t) { return new Date(t != null ? t : (globalThis.time || 0)).getUTCDay() + 1; }, { sunday: 1, monday: 2, tuesday: 3, wednesday: 4, thursday: 5, friday: 6, saturday: 7 })); globalThis.timeframe = __pineNS(Object.assign(globalThis.timeframe || {}, { period: (globalThis.timeframe && globalThis.timeframe.period) || "D", isintraday: false, isdaily: true, multiplier: 1 })); @@ -1568,8 +1729,6 @@ globalThis.bool = globalThis.bool || function(x) { return Boolean(x); }; globalThis.string = globalThis.string || function(x) { return x == null ? null : String(x); }; -globalThis.dayofweek = globalThis.dayofweek || __pineNS({ sunday: 1, monday: 2, tuesday: 3, wednesday: 4, thursday: 5, friday: 6, saturday: 7 }); - globalThis.str = globalThis.str || __pineNS({}); pinescript.math = globalThis.math; @@ -1655,7 +1814,7 @@ pinescript.text = { align_center: "center" }; pinescript.table = { - new: function(position, columns, rows, opts) { return { position, columns, rows, opts: opts || {}, cells: [] }; }, + new: function(position, columns, rows, opts) { return pinescript.__decDraw({ position, columns, rows, opts: opts || {}, cells: [] }); }, cell: function(table, column, row, text, opts) { @@ -1690,65 +1849,65 @@ function main() { // Study: // Options: {"title":"NY Opening Range (Enhanced with LTF)","shorttitle":"NY ORB","overlay":true,"max_lines_count":500,"max_labels_count":500,"max_boxes_count":500,"max_bars_back":5000} if (state.g_5MIN === undefined) state.g_5MIN = "━━━━━━━━━ 5-MINUTE ORB ━━━━━━━━━"; - let show_5min = pinescript.inputBool(true, "Show 5-Minute ORB", ({ group: state.g_5MIN })); - let bullish_color_5m = pinescript.inputColor(pinescript.color.rgb(0, 255, 149), "Bullish Color", ({ inline: "5mc", group: state.g_5MIN })); - let bearish_color_5m = pinescript.inputColor(pinescript.color.rgb(195, 0, 255), "Bearish Color", ({ inline: "5mc", group: state.g_5MIN })); - let bullish_fill_5m = pinescript.inputColor(pinescript.color.rgb(0, 255, 149, 85), "Bullish Fill", ({ inline: "5mf", group: state.g_5MIN })); - let bearish_fill_5m = pinescript.inputColor(pinescript.color.rgb(195, 0, 255, 85), "Bearish Fill", ({ inline: "5mf", group: state.g_5MIN })); - let boxStyle_5m = pinescript.inputString("Solid", "Range Style", ({ options: ["Solid", "Dashed", "Dotted"], inline: "5box", group: state.g_5MIN })); - let borderWidth_5m = pinescript.inputInt(1, "Width", 1, 10, ({ inline: "5box", group: state.g_5MIN })); - let show_midLine_5m = pinescript.inputBool(true, "Show Midline", ({ inline: "5mid", group: state.g_5MIN })); - let lineStyle_5m = pinescript.inputString("Dashed", "Style", ({ options: ["Solid", "Dashed", "Dotted"], inline: "5mid", group: state.g_5MIN })); - let lineWidth_5m = pinescript.inputInt(1, "Width", 1, 10, ({ inline: "5mid", group: state.g_5MIN })); - let show_extensions_5m = pinescript.inputBool(true, "Show Median Extensions", ({ inline: "5ext", group: state.g_5MIN })); - let ext_line_style_5m = pinescript.inputString("Dotted", "Style", ({ options: ["Solid", "Dashed", "Dotted"], inline: "5ext", group: state.g_5MIN })); - let ext_line_width_5m = pinescript.inputInt(1, "Width", ({ minval: 1, maxval: 5, inline: "5ext", group: state.g_5MIN })); - let show_ext_labels_5m = pinescript.inputBool(true, "Show Extension Labels", ({ group: state.g_5MIN })); - let show_ltf_5m = pinescript.inputBool(true, "Show LTF Candles", ({ group: state.g_5MIN })); - let ltf_5m_count = pinescript.inputInt(5, " └─ Number of Candles", ({ minval: 1, maxval: 20, group: state.g_5MIN })); - let ltf_offset_5m = pinescript.inputInt(5, " └─ Offset (bars from right)", ({ minval: 1, maxval: 100, group: state.g_5MIN })); - let ltf_spacing_5m = pinescript.inputInt(3, " └─ Candle Spacing", ({ minval: 1, maxval: 5, group: state.g_5MIN })); - let ltf_width_5m = pinescript.inputInt(2, " └─ Candle Width", ({ minval: 1, maxval: 5, group: state.g_5MIN })); - let ltf_bull_color_5m = pinescript.inputColor(pinescript.color.rgb(0, 255, 149, 60), " └─ LTF Bullish Color", ({ group: state.g_5MIN })); - let ltf_bear_color_5m = pinescript.inputColor(pinescript.color.rgb(195, 0, 255, 60), " └─ LTF Bearish Color", ({ group: state.g_5MIN })); - let show_ltf_range_5m = pinescript.inputBool(true, " └─ Show LTF High/Low Lines", ({ group: state.g_5MIN })); - let ltf_range_style_5m = pinescript.inputString("Dotted", " └─ LTF Range Style", ({ options: ["Solid", "Dashed", "Dotted"], group: state.g_5MIN })); - let ltf_range_width_5m = pinescript.inputInt(1, " └─ LTF Range Width", ({ minval: 1, maxval: 5, group: state.g_5MIN })); - let ltf_range_color_5m = pinescript.inputColor(pinescript.color.rgb(0, 255, 149, 50), " └─ LTF Range Color", ({ group: state.g_5MIN })); + var show_5min = pinescript.inputBool(true, "Show 5-Minute ORB", ({ group: state.g_5MIN })); + var bullish_color_5m = pinescript.inputColor(pinescript.color.rgb(0, 255, 149), "Bullish Color", ({ inline: "5mc", group: state.g_5MIN })); + var bearish_color_5m = pinescript.inputColor(pinescript.color.rgb(195, 0, 255), "Bearish Color", ({ inline: "5mc", group: state.g_5MIN })); + var bullish_fill_5m = pinescript.inputColor(pinescript.color.rgb(0, 255, 149, 85), "Bullish Fill", ({ inline: "5mf", group: state.g_5MIN })); + var bearish_fill_5m = pinescript.inputColor(pinescript.color.rgb(195, 0, 255, 85), "Bearish Fill", ({ inline: "5mf", group: state.g_5MIN })); + var boxStyle_5m = pinescript.inputString("Solid", "Range Style", ({ options: ["Solid", "Dashed", "Dotted"], inline: "5box", group: state.g_5MIN })); + var borderWidth_5m = pinescript.inputInt(1, "Width", 1, 10, ({ inline: "5box", group: state.g_5MIN })); + var show_midLine_5m = pinescript.inputBool(true, "Show Midline", ({ inline: "5mid", group: state.g_5MIN })); + var lineStyle_5m = pinescript.inputString("Dashed", "Style", ({ options: ["Solid", "Dashed", "Dotted"], inline: "5mid", group: state.g_5MIN })); + var lineWidth_5m = pinescript.inputInt(1, "Width", 1, 10, ({ inline: "5mid", group: state.g_5MIN })); + var show_extensions_5m = pinescript.inputBool(true, "Show Median Extensions", ({ inline: "5ext", group: state.g_5MIN })); + var ext_line_style_5m = pinescript.inputString("Dotted", "Style", ({ options: ["Solid", "Dashed", "Dotted"], inline: "5ext", group: state.g_5MIN })); + var ext_line_width_5m = pinescript.inputInt(1, "Width", ({ minval: 1, maxval: 5, inline: "5ext", group: state.g_5MIN })); + var show_ext_labels_5m = pinescript.inputBool(true, "Show Extension Labels", ({ group: state.g_5MIN })); + var show_ltf_5m = pinescript.inputBool(true, "Show LTF Candles", ({ group: state.g_5MIN })); + var ltf_5m_count = pinescript.inputInt(5, " └─ Number of Candles", ({ minval: 1, maxval: 20, group: state.g_5MIN })); + var ltf_offset_5m = pinescript.inputInt(5, " └─ Offset (bars from right)", ({ minval: 1, maxval: 100, group: state.g_5MIN })); + var ltf_spacing_5m = pinescript.inputInt(3, " └─ Candle Spacing", ({ minval: 1, maxval: 5, group: state.g_5MIN })); + var ltf_width_5m = pinescript.inputInt(2, " └─ Candle Width", ({ minval: 1, maxval: 5, group: state.g_5MIN })); + var ltf_bull_color_5m = pinescript.inputColor(pinescript.color.rgb(0, 255, 149, 60), " └─ LTF Bullish Color", ({ group: state.g_5MIN })); + var ltf_bear_color_5m = pinescript.inputColor(pinescript.color.rgb(195, 0, 255, 60), " └─ LTF Bearish Color", ({ group: state.g_5MIN })); + var show_ltf_range_5m = pinescript.inputBool(true, " └─ Show LTF High/Low Lines", ({ group: state.g_5MIN })); + var ltf_range_style_5m = pinescript.inputString("Dotted", " └─ LTF Range Style", ({ options: ["Solid", "Dashed", "Dotted"], group: state.g_5MIN })); + var ltf_range_width_5m = pinescript.inputInt(1, " └─ LTF Range Width", ({ minval: 1, maxval: 5, group: state.g_5MIN })); + var ltf_range_color_5m = pinescript.inputColor(pinescript.color.rgb(0, 255, 149, 50), " └─ LTF Range Color", ({ group: state.g_5MIN })); if (state.g_15MIN === undefined) state.g_15MIN = "━━━━━━━━━ 15-MINUTE ORB ━━━━━━━━━"; - let show_15min = pinescript.inputBool(true, "Show 15-Minute ORB", ({ group: state.g_15MIN })); - let bullish_color_15m = pinescript.inputColor(pinescript.color.rgb(33, 150, 243), "Bullish Color", ({ inline: "15mc", group: state.g_15MIN })); - let bearish_color_15m = pinescript.inputColor(pinescript.color.rgb(255, 152, 0), "Bearish Color", ({ inline: "15mc", group: state.g_15MIN })); - let bullish_fill_15m = pinescript.inputColor(pinescript.color.rgb(33, 150, 243, 85), "Bullish Fill", ({ inline: "15mf", group: state.g_15MIN })); - let bearish_fill_15m = pinescript.inputColor(pinescript.color.rgb(255, 152, 0, 85), "Bearish Fill", ({ inline: "15mf", group: state.g_15MIN })); - let boxStyle_15m = pinescript.inputString("Solid", "Range Style", ({ options: ["Solid", "Dashed", "Dotted"], inline: "15box", group: state.g_15MIN })); - let borderWidth_15m = pinescript.inputInt(1, "Width", 1, 10, ({ inline: "15box", group: state.g_15MIN })); - let show_midLine_15m = pinescript.inputBool(true, "Show Midline", ({ inline: "15mid", group: state.g_15MIN })); - let lineStyle_15m = pinescript.inputString("Dashed", "Style", ({ options: ["Solid", "Dashed", "Dotted"], inline: "15mid", group: state.g_15MIN })); - let lineWidth_15m = pinescript.inputInt(1, "Width", 1, 10, ({ inline: "15mid", group: state.g_15MIN })); - let show_extensions_15m = pinescript.inputBool(true, "Show Median Extensions", ({ inline: "15ext", group: state.g_15MIN })); - let ext_line_style_15m = pinescript.inputString("Dotted", "Style", ({ options: ["Solid", "Dashed", "Dotted"], inline: "15ext", group: state.g_15MIN })); - let ext_line_width_15m = pinescript.inputInt(1, "Width", ({ minval: 1, maxval: 5, inline: "15ext", group: state.g_15MIN })); - let show_ext_labels_15m = pinescript.inputBool(true, "Show Extension Labels", ({ group: state.g_15MIN })); - let show_ltf_15m = pinescript.inputBool(true, "Show LTF Candles", ({ group: state.g_15MIN })); - let ltf_15m_count = pinescript.inputInt(15, " └─ Number of Candles", ({ minval: 1, maxval: 20, group: state.g_15MIN })); - let ltf_offset_15m = pinescript.inputInt(35, " └─ Offset (bars from right)", ({ minval: 1, maxval: 100, group: state.g_15MIN })); - let ltf_spacing_15m = pinescript.inputInt(3, " └─ Candle Spacing", ({ minval: 1, maxval: 5, group: state.g_15MIN })); - let ltf_width_15m = pinescript.inputInt(2, " └─ Candle Width", ({ minval: 1, maxval: 5, group: state.g_15MIN })); - let ltf_bull_color_15m = pinescript.inputColor(pinescript.color.rgb(0, 255, 149, 60), " └─ LTF Bullish Color", ({ group: state.g_15MIN })); - let ltf_bear_color_15m = pinescript.inputColor(pinescript.color.rgb(195, 0, 255, 60), " └─ LTF Bearish Color", ({ group: state.g_15MIN })); - let show_ltf_range_15m = pinescript.inputBool(true, " └─ Show LTF High/Low Lines", ({ group: state.g_15MIN })); - let ltf_range_style_15m = pinescript.inputString("Dotted", " └─ LTF Range Style", ({ options: ["Solid", "Dashed", "Dotted"], group: state.g_15MIN })); - let ltf_range_width_15m = pinescript.inputInt(1, " └─ LTF Range Width", ({ minval: 1, maxval: 5, group: state.g_15MIN })); - let ltf_range_color_15m = pinescript.inputColor(pinescript.color.rgb(33, 150, 243, 50), " └─ LTF Range Color", ({ group: state.g_15MIN })); + var show_15min = pinescript.inputBool(true, "Show 15-Minute ORB", ({ group: state.g_15MIN })); + var bullish_color_15m = pinescript.inputColor(pinescript.color.rgb(33, 150, 243), "Bullish Color", ({ inline: "15mc", group: state.g_15MIN })); + var bearish_color_15m = pinescript.inputColor(pinescript.color.rgb(255, 152, 0), "Bearish Color", ({ inline: "15mc", group: state.g_15MIN })); + var bullish_fill_15m = pinescript.inputColor(pinescript.color.rgb(33, 150, 243, 85), "Bullish Fill", ({ inline: "15mf", group: state.g_15MIN })); + var bearish_fill_15m = pinescript.inputColor(pinescript.color.rgb(255, 152, 0, 85), "Bearish Fill", ({ inline: "15mf", group: state.g_15MIN })); + var boxStyle_15m = pinescript.inputString("Solid", "Range Style", ({ options: ["Solid", "Dashed", "Dotted"], inline: "15box", group: state.g_15MIN })); + var borderWidth_15m = pinescript.inputInt(1, "Width", 1, 10, ({ inline: "15box", group: state.g_15MIN })); + var show_midLine_15m = pinescript.inputBool(true, "Show Midline", ({ inline: "15mid", group: state.g_15MIN })); + var lineStyle_15m = pinescript.inputString("Dashed", "Style", ({ options: ["Solid", "Dashed", "Dotted"], inline: "15mid", group: state.g_15MIN })); + var lineWidth_15m = pinescript.inputInt(1, "Width", 1, 10, ({ inline: "15mid", group: state.g_15MIN })); + var show_extensions_15m = pinescript.inputBool(true, "Show Median Extensions", ({ inline: "15ext", group: state.g_15MIN })); + var ext_line_style_15m = pinescript.inputString("Dotted", "Style", ({ options: ["Solid", "Dashed", "Dotted"], inline: "15ext", group: state.g_15MIN })); + var ext_line_width_15m = pinescript.inputInt(1, "Width", ({ minval: 1, maxval: 5, inline: "15ext", group: state.g_15MIN })); + var show_ext_labels_15m = pinescript.inputBool(true, "Show Extension Labels", ({ group: state.g_15MIN })); + var show_ltf_15m = pinescript.inputBool(true, "Show LTF Candles", ({ group: state.g_15MIN })); + var ltf_15m_count = pinescript.inputInt(15, " └─ Number of Candles", ({ minval: 1, maxval: 20, group: state.g_15MIN })); + var ltf_offset_15m = pinescript.inputInt(35, " └─ Offset (bars from right)", ({ minval: 1, maxval: 100, group: state.g_15MIN })); + var ltf_spacing_15m = pinescript.inputInt(3, " └─ Candle Spacing", ({ minval: 1, maxval: 5, group: state.g_15MIN })); + var ltf_width_15m = pinescript.inputInt(2, " └─ Candle Width", ({ minval: 1, maxval: 5, group: state.g_15MIN })); + var ltf_bull_color_15m = pinescript.inputColor(pinescript.color.rgb(0, 255, 149, 60), " └─ LTF Bullish Color", ({ group: state.g_15MIN })); + var ltf_bear_color_15m = pinescript.inputColor(pinescript.color.rgb(195, 0, 255, 60), " └─ LTF Bearish Color", ({ group: state.g_15MIN })); + var show_ltf_range_15m = pinescript.inputBool(true, " └─ Show LTF High/Low Lines", ({ group: state.g_15MIN })); + var ltf_range_style_15m = pinescript.inputString("Dotted", " └─ LTF Range Style", ({ options: ["Solid", "Dashed", "Dotted"], group: state.g_15MIN })); + var ltf_range_width_15m = pinescript.inputInt(1, " └─ LTF Range Width", ({ minval: 1, maxval: 5, group: state.g_15MIN })); + var ltf_range_color_15m = pinescript.inputColor(pinescript.color.rgb(33, 150, 243, 50), " └─ LTF Range Color", ({ group: state.g_15MIN })); if (state.g_LABELS === undefined) state.g_LABELS = "Labels & Display"; - let showL = pinescript.inputBool(true, "Show High/Low/Mid Labels", ({ inline: "Labels", group: state.g_LABELS })); - let showP = pinescript.inputBool(false, "Show Prices", ({ inline: "Labels", group: state.g_LABELS })); - let pos = pinescript.inputString("Left", "Position", ({ options: ["Left", "Right"], group: state.g_LABELS })); + var showL = pinescript.inputBool(true, "Show High/Low/Mid Labels", ({ inline: "Labels", group: state.g_LABELS })); + var showP = pinescript.inputBool(false, "Show Prices", ({ inline: "Labels", group: state.g_LABELS })); + var pos = pinescript.inputString("Left", "Position", ({ options: ["Left", "Right"], group: state.g_LABELS })); if (state.g_TABLE === undefined) state.g_TABLE = "Statistics Table"; - let show_stats_table = pinescript.inputBool(true, "Show Statistics Table", ({ group: state.g_TABLE })); - let stats_table_pos = pinescript.inputString("Top Right", "Table Position", ({ options: ["Bottom Center", "Bottom Left", "Bottom Right", "Middle Center", "Middle Left", "Middle Right", "Top Center", "Top Left", "Top Right"], group: state.g_TABLE })); - let stats_table_size = pinescript.inputString("Small", "Table Size", ({ options: ["Auto", "Tiny", "Small", "Normal", "Large", "Huge"], group: state.g_TABLE })); + var show_stats_table = pinescript.inputBool(true, "Show Statistics Table", ({ group: state.g_TABLE })); + var stats_table_pos = pinescript.inputString("Top Right", "Table Position", ({ options: ["Bottom Center", "Bottom Left", "Bottom Right", "Middle Center", "Middle Left", "Middle Right", "Top Center", "Top Left", "Top Right"], group: state.g_TABLE })); + var stats_table_size = pinescript.inputString("Small", "Table Size", ({ options: ["Auto", "Tiny", "Small", "Normal", "Large", "Huge"], group: state.g_TABLE })); function get_style(style) { switch (style) { case "Solid": @@ -1880,11 +2039,11 @@ function main() { } } } - let MEDIAN_EXT_5M_HIGH = 0.411; - let MEDIAN_EXT_5M_LOW = 0.45; - let MEDIAN_EXT_15M_HIGH = 0.38; - let MEDIAN_EXT_15M_LOW = 0.415; - let tz = "America/New_York"; + var MEDIAN_EXT_5M_HIGH = 0.411; + var MEDIAN_EXT_5M_LOW = 0.45; + var MEDIAN_EXT_15M_HIGH = 0.38; + var MEDIAN_EXT_15M_LOW = 0.415; + var tz = "America/New_York"; if (state.or5_high === undefined) state.or5_high = null; if (state.or5_low === undefined) state.or5_low = null; if (state.or5_mid === undefined) state.or5_mid = null; @@ -1957,22 +2116,22 @@ function main() { if (state.ib_close === undefined) state.ib_close = null; if (state.ib_captured === undefined) state.ib_captured = false; if (state.ib_is_bullish === undefined) state.ib_is_bullish = false; - let is_5m_session = !pinescript.na(pinescript.time(timeframe.period, "0930-0935", tz)); - let is_5m_start = (is_5m_session && !pinescript.hist(0, is_5m_session, 1)); - let is_5m_end = (pinescript.hist(1, is_5m_session, 1) && !is_5m_session); - let is_15m_session = !pinescript.na(pinescript.time(timeframe.period, "0930-0945", tz)); - let is_15m_start = (is_15m_session && !pinescript.hist(2, is_15m_session, 1)); - let is_15m_end = (pinescript.hist(3, is_15m_session, 1) && !is_15m_session); - let is_ib_session = !pinescript.na(pinescript.time(timeframe.period, "0930-1030", tz)); - let is_ib_start = (is_ib_session && !pinescript.hist(4, is_ib_session, 1)); - let is_ib_end = (pinescript.hist(5, is_ib_session, 1) && !is_ib_session); - let is_ny_session = !pinescript.na(pinescript.time(timeframe.period, "0930-1600", tz)); - let is_ny_session_end = (pinescript.hist(6, is_ny_session, 1) && !is_ny_session); - let [H5, L5, M5, O5, C5] = pinescript.unpack(pinescript.requestSecurity(ticker.standard(syminfo.tickerid), "5", [high, low, hl2, open, close], ({ lookahead: barmerge.lookahead_on })), 5); - let [H15, L15, M15, O15, C15] = pinescript.unpack(pinescript.requestSecurity(ticker.standard(syminfo.tickerid), "15", [high, low, hl2, open, close], ({ lookahead: barmerge.lookahead_on })), 5); - let [H60, L60, O60, C60] = pinescript.unpack(pinescript.requestSecurity(ticker.standard(syminfo.tickerid), "60", [high, low, open, close], ({ lookahead: barmerge.lookahead_on })), 4); - let [high_1m, low_1m, open_1m, close_1m] = pinescript.unpack(request.security_lower_tf(ticker.standard(syminfo.tickerid), "1", [high, low, open, close]), 4); - let _style = ((pos === "Left") ? label.style_label_right : label.style_label_left); + var is_5m_session = !pinescript.na(pinescript.time(timeframe.period, "0930-0935", tz)); + var is_5m_start = (is_5m_session && !pinescript.hist(0, is_5m_session, 1)); + var is_5m_end = (pinescript.hist(1, is_5m_session, 1) && !is_5m_session); + var is_15m_session = !pinescript.na(pinescript.time(timeframe.period, "0930-0945", tz)); + var is_15m_start = (is_15m_session && !pinescript.hist(2, is_15m_session, 1)); + var is_15m_end = (pinescript.hist(3, is_15m_session, 1) && !is_15m_session); + var is_ib_session = !pinescript.na(pinescript.time(timeframe.period, "0930-1030", tz)); + var is_ib_start = (is_ib_session && !pinescript.hist(4, is_ib_session, 1)); + var is_ib_end = (pinescript.hist(5, is_ib_session, 1) && !is_ib_session); + var is_ny_session = !pinescript.na(pinescript.time(timeframe.period, "0930-1600", tz)); + var is_ny_session_end = (pinescript.hist(6, is_ny_session, 1) && !is_ny_session); + var [H5, L5, M5, O5, C5] = pinescript.unpack(pinescript.requestSecurity(ticker.standard(syminfo.tickerid), "5", [high, low, hl2, open, close], ({ lookahead: barmerge.lookahead_on })), 5); + var [H15, L15, M15, O15, C15] = pinescript.unpack(pinescript.requestSecurity(ticker.standard(syminfo.tickerid), "15", [high, low, hl2, open, close], ({ lookahead: barmerge.lookahead_on })), 5); + var [H60, L60, O60, C60] = pinescript.unpack(pinescript.requestSecurity(ticker.standard(syminfo.tickerid), "60", [high, low, open, close], ({ lookahead: barmerge.lookahead_on })), 4); + var [high_1m, low_1m, open_1m, close_1m] = pinescript.unpack(request.security_lower_tf(ticker.standard(syminfo.tickerid), "1", [high, low, open, close]), 4); + var _style = ((pos === "Left") ? label.style_label_right : label.style_label_left); if (((is_ib_start && !pinescript.na(H60)) && !pinescript.na(L60))) { state.ib_high = H60; state.ib_low = L60; @@ -1997,30 +2156,32 @@ function main() { } ltf.highs.clear(); ltf.lows.clear(); - let start_index = pinescript.max(0, (pinescript.arraySize(o_array) - max_candles)); - let num_candles = (pinescript.arraySize(o_array) - start_index); + var start_index = pinescript.max(0, (pinescript.arraySize(o_array) - max_candles)); + var num_candles = (pinescript.arraySize(o_array) - start_index); for (let i = start_index; i <= (pinescript.arraySize(o_array) - 1); i++) { - let o = pinescript.arrayGet(o_array, i); - let h = pinescript.arrayGet(h_array, i); - let l = pinescript.arrayGet(l_array, i); - let c = pinescript.arrayGet(c_array, i); - let is_bull = (c >= o); - let candle_color = (is_bull ? bull_color : bear_color); - let candle_index = (i - start_index); - let x_start = ((bar_index + offset_from_right) + (candle_index * spacing)); - let x_end = (x_start + width); - let x_mid = pinescript.round((x_start + (width / 2))); - let body_top = pinescript.max(o, c); - let body_bottom = pinescript.min(o, c); + var o = pinescript.arrayGet(o_array, i); + var h = pinescript.arrayGet(h_array, i); + var l = pinescript.arrayGet(l_array, i); + var c = pinescript.arrayGet(c_array, i); + var is_bull = (c >= o); + var candle_color = (is_bull ? bull_color : bear_color); + var candle_index = (i - start_index); + var x_start = ((bar_index + offset_from_right) + (candle_index * spacing)); + var x_end = (x_start + width); + var x_mid = pinescript.round((x_start + (width / 2))); + var body_top = pinescript.max(o, c); + var body_bottom = pinescript.min(o, c); ltf.bodies.push(pinescript.boxNew(x_start, body_top, x_end, body_bottom, ({ border_color: candle_color, bgcolor: candle_color, border_width: 1 }))); ltf.wicks_high.push(pinescript.lineNew(x_mid, body_top, x_mid, h, ({ color: candle_color, width: 1 }))); ltf.wicks_low.push(pinescript.lineNew(x_mid, body_bottom, x_mid, l, ({ color: candle_color, width: 1 }))); ltf.highs.push(h); ltf.lows.push(l); } - let ltf_start_x = (bar_index + offset_from_right); - let ltf_end_x = (((bar_index + offset_from_right) + (num_candles * spacing)) + width); - [ltf_start_x, ltf_end_x]; + var ltf_start_x = (bar_index + offset_from_right); + var ltf_end_x = (((bar_index + offset_from_right) + (num_candles * spacing)) + width); + return [ltf_start_x, ltf_end_x]; + } else { + return null; } } if (show_5min) { @@ -2047,10 +2208,10 @@ function main() { state.or5_ltf_frozen = false; state.or5_ext_high_reached = false; state.or5_ext_low_reached = false; - or5_ltf_opens_frozen.clear(); - or5_ltf_highs_frozen.clear(); - or5_ltf_lows_frozen.clear(); - or5_ltf_closes_frozen.clear(); + state.or5_ltf_opens_frozen.clear(); + state.or5_ltf_highs_frozen.clear(); + state.or5_ltf_lows_frozen.clear(); + state.or5_ltf_closes_frozen.clear(); state.or5_top_line = null; state.or5_bot_line = null; state.or5_mid_line = null; @@ -2076,18 +2237,18 @@ function main() { } if ((((is_5m_session && !state.or5_ltf_frozen) && !pinescript.na(open_1m)) && (pinescript.arraySize(open_1m) > 0))) { for (let i = 0; i <= (pinescript.arraySize(open_1m) - 1); i++) { - or5_ltf_opens_frozen.push(pinescript.arrayGet(open_1m, i)); - or5_ltf_highs_frozen.push(pinescript.arrayGet(high_1m, i)); - or5_ltf_lows_frozen.push(pinescript.arrayGet(low_1m, i)); - or5_ltf_closes_frozen.push(pinescript.arrayGet(close_1m, i)); + state.or5_ltf_opens_frozen.push(pinescript.arrayGet(open_1m, i)); + state.or5_ltf_highs_frozen.push(pinescript.arrayGet(high_1m, i)); + state.or5_ltf_lows_frozen.push(pinescript.arrayGet(low_1m, i)); + state.or5_ltf_closes_frozen.push(pinescript.arrayGet(close_1m, i)); } } if ((is_5m_end && !state.or5_ltf_frozen)) { state.or5_ltf_frozen = true; if (((state.or5_extreme_first === "none") && (pinescript.arraySize(state.or5_ltf_highs_frozen) > 0))) { for (let i = 0; i <= (pinescript.arraySize(state.or5_ltf_highs_frozen) - 1); i++) { - let h_1m = pinescript.arrayGet(state.or5_ltf_highs_frozen, i); - let l_1m = pinescript.arrayGet(state.or5_ltf_lows_frozen, i); + var h_1m = pinescript.arrayGet(state.or5_ltf_highs_frozen, i); + var l_1m = pinescript.arrayGet(state.or5_ltf_lows_frozen, i); if ((h_1m >= state.or5_high)) { state.or5_extreme_first = "high"; break; @@ -2106,9 +2267,9 @@ function main() { } } if ((((state.or5_captured && !pinescript.na(state.or5_high)) && !pinescript.na(state.or5_low)) && pinescript.na(state.or5_top_line))) { - let is_bullish = (state.or5_close >= state.or5_open); - let _color = (is_bullish ? bullish_color_5m : bearish_color_5m); - let _fillcolor = (is_bullish ? bullish_fill_5m : bearish_fill_5m); + var is_bullish = (state.or5_close >= state.or5_open); + var _color = (is_bullish ? bullish_color_5m : bearish_color_5m); + var _fillcolor = (is_bullish ? bullish_fill_5m : bearish_fill_5m); state.or5_top_line = pinescript.lineNew(state.or5_start_bar, state.or5_high, bar_index, state.or5_high, ({ color: _color, style: get_style(boxStyle_5m), width: borderWidth_5m })); state.or5_bot_line = pinescript.lineNew(state.or5_start_bar, state.or5_low, bar_index, state.or5_low, ({ color: _color, style: get_style(boxStyle_5m), width: borderWidth_5m })); state.or5_fill = linefill.new(state.or5_top_line, state.or5_bot_line, _fillcolor); @@ -2116,12 +2277,12 @@ function main() { state.or5_mid_line = pinescript.lineNew(state.or5_start_bar, state.or5_mid, bar_index, state.or5_mid, ({ color: _color, style: get_style(lineStyle_5m), width: lineWidth_5m })); } if (show_extensions_5m) { - let ext_high_price = (state.or5_high * (1 + (MEDIAN_EXT_5M_HIGH / 100))); - let ext_low_price = (state.or5_low * (1 - (MEDIAN_EXT_5M_LOW / 100))); + var ext_high_price = (state.or5_high * (1 + (MEDIAN_EXT_5M_HIGH / 100))); + var ext_low_price = (state.or5_low * (1 - (MEDIAN_EXT_5M_LOW / 100))); state.or5_ext_high = pinescript.lineNew(state.or5_start_bar, ext_high_price, bar_index, ext_high_price, ({ color: _color, style: get_style(ext_line_style_5m), width: ext_line_width_5m })); state.or5_ext_low = pinescript.lineNew(state.or5_start_bar, ext_low_price, bar_index, ext_low_price, ({ color: _color, style: get_style(ext_line_style_5m), width: ext_line_width_5m })); } - let x = ((pos === "Left") ? state.or5_start_bar : bar_index); + var x = ((pos === "Left") ? state.or5_start_bar : bar_index); if (showL) { state.or5_high_label = pinescript.labelNew(x, state.or5_high, ("5m High" + (showP ? ((" (" + pinescript.strToString(state.or5_high, format.mintick)) + ")") : "")), ({ style: _style, color: pinescript.color.new(_color, 100), textcolor: _color, size: get_label_size("Normal") })); state.or5_low_label = pinescript.labelNew(x, state.or5_low, ("5m Low" + (showP ? ((" (" + pinescript.strToString(state.or5_low, format.mintick)) + ")") : "")), ({ style: _style, color: pinescript.color.new(_color, 100), textcolor: _color, size: get_label_size("Normal") })); @@ -2137,12 +2298,12 @@ function main() { } } if (((((state.or5_captured && show_ltf_5m) && state.or5_ltf_frozen) && (pinescript.arraySize(state.or5_ltf_opens_frozen) > 0)) && is_ny_session)) { - [ltf_start_x, ltf_end_x] = pinescript.unpack(draw_ltf_candles(state.ltf5, state.or5_ltf_opens_frozen, state.or5_ltf_highs_frozen, state.or5_ltf_lows_frozen, state.or5_ltf_closes_frozen, ltf_bull_color_5m, ltf_bear_color_5m, ltf_offset_5m, ltf_5m_count, ltf_spacing_5m, ltf_width_5m), 2); + var [ltf_start_x, ltf_end_x] = pinescript.unpack(draw_ltf_candles(state.ltf5, state.or5_ltf_opens_frozen, state.or5_ltf_highs_frozen, state.or5_ltf_lows_frozen, state.or5_ltf_closes_frozen, ltf_bull_color_5m, ltf_bear_color_5m, ltf_offset_5m, ltf_5m_count, ltf_spacing_5m, ltf_width_5m), 2); if ((show_ltf_range_5m && (pinescript.arraySize(state.ltf5.highs) > 0))) { pinescript.lineDelete(state.or5_ltf_high_line); pinescript.lineDelete(state.or5_ltf_low_line); - let ltf_high = pinescript.arrayMax(state.ltf5.highs); - let ltf_low = pinescript.arrayMin(state.ltf5.lows); + var ltf_high = pinescript.arrayMax(state.ltf5.highs); + var ltf_low = pinescript.arrayMin(state.ltf5.lows); state.or5_ltf_high_line = pinescript.lineNew(ltf_start_x, ltf_high, ltf_end_x, ltf_high, ({ color: ltf_range_color_5m, style: get_style(ltf_range_style_5m), width: ltf_range_width_5m })); state.or5_ltf_low_line = pinescript.lineNew(ltf_start_x, ltf_low, ltf_end_x, ltf_low, ({ color: ltf_range_color_5m, style: get_style(ltf_range_style_5m), width: ltf_range_width_5m })); } @@ -2227,10 +2388,10 @@ function main() { state.or15_ltf_frozen = false; state.or15_ext_high_reached = false; state.or15_ext_low_reached = false; - or15_ltf_opens_frozen.clear(); - or15_ltf_highs_frozen.clear(); - or15_ltf_lows_frozen.clear(); - or15_ltf_closes_frozen.clear(); + state.or15_ltf_opens_frozen.clear(); + state.or15_ltf_highs_frozen.clear(); + state.or15_ltf_lows_frozen.clear(); + state.or15_ltf_closes_frozen.clear(); state.or15_top_line = null; state.or15_bot_line = null; state.or15_mid_line = null; @@ -2256,10 +2417,10 @@ function main() { } if ((((is_15m_session && !state.or15_ltf_frozen) && !pinescript.na(open_1m)) && (pinescript.arraySize(open_1m) > 0))) { for (let i = 0; i <= (pinescript.arraySize(open_1m) - 1); i++) { - or15_ltf_opens_frozen.push(pinescript.arrayGet(open_1m, i)); - or15_ltf_highs_frozen.push(pinescript.arrayGet(high_1m, i)); - or15_ltf_lows_frozen.push(pinescript.arrayGet(low_1m, i)); - or15_ltf_closes_frozen.push(pinescript.arrayGet(close_1m, i)); + state.or15_ltf_opens_frozen.push(pinescript.arrayGet(open_1m, i)); + state.or15_ltf_highs_frozen.push(pinescript.arrayGet(high_1m, i)); + state.or15_ltf_lows_frozen.push(pinescript.arrayGet(low_1m, i)); + state.or15_ltf_closes_frozen.push(pinescript.arrayGet(close_1m, i)); } } if ((is_15m_end && !state.or15_ltf_frozen)) { @@ -2386,46 +2547,46 @@ function main() { if ((show_stats_table && (state.or5_captured || state.or15_captured))) { if (state.stats_table === undefined) state.stats_table = null; pinescript.tableDelete(pinescript.hist(11, state.stats_table, 1)); - let table_pos = get_table_position(stats_table_pos); - let table_size = get_table_text_size(stats_table_size); - let header_bg = pinescript.color.rgb(64, 64, 64); - let header_text = pinescript.color.white; - let cell_bg = pinescript.color.white; - let cell_text = pinescript.color.rgb(64, 64, 64); - let pending_bg = pinescript.color.rgb(255, 235, 59); - let pending_text = pinescript.color.rgb(64, 64, 64); - let validated_bg = pinescript.color.rgb(0, 150, 136); - let validated_text = pinescript.color.white; + var table_pos = get_table_position(stats_table_pos); + var table_size = get_table_text_size(stats_table_size); + var header_bg = pinescript.color.rgb(64, 64, 64); + var header_text = pinescript.color.white; + var cell_bg = pinescript.color.white; + var cell_text = pinescript.color.rgb(64, 64, 64); + var pending_bg = pinescript.color.rgb(255, 235, 59); + var pending_text = pinescript.color.rgb(64, 64, 64); + var validated_bg = pinescript.color.rgb(0, 150, 136); + var validated_text = pinescript.color.white; state.stats_table = pinescript.table.new(table_pos, 3, 35, ({ border_width: 1, border_color: pinescript.color.rgb(64, 64, 64), bgcolor: cell_bg })); - let session_ended = (is_ny_session_end || !is_ny_session); - let row = 0; + var session_ended = (is_ny_session_end || !is_ny_session); + var row = 0; pinescript.table.cell(state.stats_table, 0, row, "━━━ ORB STATISTICS ━━━", ({ bgcolor: header_bg, text_color: header_text, text_size: table_size, text_font_family: font.family_monospace })); pinescript.tableMergeCells(state.stats_table, 0, row, 2, row); row = (row + 1); if ((show_5min && state.or5_captured)) { - let is_bull_5m = (state.or5_close >= state.or5_open); - let extreme_5m = state.or5_extreme_first; + var is_bull_5m = (state.or5_close >= state.or5_open); + var extreme_5m = state.or5_extreme_first; pinescript.table.cell(state.stats_table, 0, row, "━━━ 5-MIN ORB ━━━", ({ bgcolor: header_bg, text_color: header_text, text_size: table_size, text_font_family: font.family_monospace })); pinescript.tableMergeCells(state.stats_table, 0, row, 2, row); row = (row + 1); pinescript.table.cell(state.stats_table, 0, row, "Direction", ({ bgcolor: cell_bg, text_color: cell_text, text_size: table_size, text_font_family: font.family_monospace })); - let dir_text = (is_bull_5m ? "Bullish" : "Bearish"); - let dir_color = (is_bull_5m ? bullish_color_5m : bearish_color_5m); + var dir_text = (is_bull_5m ? "Bullish" : "Bearish"); + var dir_color = (is_bull_5m ? bullish_color_5m : bearish_color_5m); pinescript.table.cell(state.stats_table, 1, row, dir_text, ({ bgcolor: cell_bg, text_color: dir_color, text_size: table_size, text_font_family: font.family_monospace })); pinescript.table.cell(state.stats_table, 2, row, "Validated", ({ bgcolor: validated_bg, text_color: validated_text, text_size: table_size, text_font_family: font.family_monospace })); row = (row + 1); pinescript.table.cell(state.stats_table, 0, row, "Extreme First", ({ bgcolor: cell_bg, text_color: cell_text, text_size: table_size, text_font_family: font.family_monospace })); - let extreme_text = ((extreme_5m === "high") ? "High First" : ((extreme_5m === "low") ? "Low First" : "Unknown")); + var extreme_text = ((extreme_5m === "high") ? "High First" : ((extreme_5m === "low") ? "Low First" : "Unknown")); pinescript.table.cell(state.stats_table, 1, row, extreme_text, ({ bgcolor: cell_bg, text_color: cell_text, text_size: table_size, text_font_family: font.family_monospace })); - let extreme_validated = (extreme_5m !== "none"); - let status_bg = (extreme_validated ? validated_bg : pending_bg); - let status_text = (extreme_validated ? validated_text : pending_text); - let status_symbol = (extreme_validated ? "Validated" : "Pending"); + var extreme_validated = (extreme_5m !== "none"); + var status_bg = (extreme_validated ? validated_bg : pending_bg); + var status_text = (extreme_validated ? validated_text : pending_text); + var status_symbol = (extreme_validated ? "Validated" : "Pending"); pinescript.table.cell(state.stats_table, 2, row, status_symbol, ({ bgcolor: status_bg, text_color: status_text, text_size: table_size, text_font_family: font.family_monospace })); row = (row + 1); pinescript.table.cell(state.stats_table, 0, row, "Extension ↑", ({ bgcolor: cell_bg, text_color: cell_text, text_size: table_size, text_font_family: font.family_monospace })); pinescript.table.cell(state.stats_table, 1, row, "84.9%", ({ bgcolor: cell_bg, text_color: cell_text, text_size: table_size, text_font_family: font.family_monospace })); - let ext_up_validated = state.or5_ext_high_reached; + var ext_up_validated = state.or5_ext_high_reached; status_bg = (ext_up_validated ? validated_bg : pending_bg); status_text = (ext_up_validated ? validated_text : pending_text); status_symbol = (ext_up_validated ? "Validated" : "Pending"); @@ -2433,14 +2594,14 @@ function main() { row = (row + 1); pinescript.table.cell(state.stats_table, 0, row, "Extension ↓", ({ bgcolor: cell_bg, text_color: cell_text, text_size: table_size, text_font_family: font.family_monospace })); pinescript.table.cell(state.stats_table, 1, row, "81.0%", ({ bgcolor: cell_bg, text_color: cell_text, text_size: table_size, text_font_family: font.family_monospace })); - let ext_down_validated = state.or5_ext_low_reached; + var ext_down_validated = state.or5_ext_low_reached; status_bg = (ext_down_validated ? validated_bg : pending_bg); status_text = (ext_down_validated ? validated_text : pending_text); status_symbol = (ext_down_validated ? "Validated" : "Pending"); pinescript.table.cell(state.stats_table, 2, row, status_symbol, ({ bgcolor: status_bg, text_color: status_text, text_size: table_size, text_font_family: font.family_monospace })); row = (row + 1); if ((extreme_5m !== "none")) { - let retest_prob = 0; + var retest_prob = 0; if ((is_bull_5m && (extreme_5m === "high"))) { retest_prob = 88.4; } else { @@ -2465,8 +2626,8 @@ function main() { row = (row + 1); } if ((extreme_5m !== "none")) { - let ib_prob = 0; - let ib_dir = ""; + var ib_prob = 0; + var ib_dir = ""; if ((is_bull_5m && (extreme_5m === "low"))) { ib_prob = 68.7; ib_dir = "Bullish"; @@ -2495,8 +2656,8 @@ function main() { row = (row + 1); } if ((extreme_5m !== "none")) { - let break_prob = 0; - let break_dir = ""; + var break_prob = 0; + var break_dir = ""; if ((is_bull_5m && (extreme_5m === "low"))) { break_prob = 79.9; break_dir = "High"; @@ -2518,7 +2679,7 @@ function main() { } pinescript.table.cell(state.stats_table, 0, row, "Next Break", ({ bgcolor: cell_bg, text_color: cell_text, text_size: table_size, text_font_family: font.family_monospace })); pinescript.table.cell(state.stats_table, 1, row, (((break_dir + " ") + pinescript.strToString(break_prob, "#.#")) + "%"), ({ bgcolor: cell_bg, text_color: cell_text, text_size: table_size, text_font_family: font.family_monospace })); - let break_validated = (state.or5_high_broken || state.or5_low_broken); + var break_validated = (state.or5_high_broken || state.or5_low_broken); status_bg = (break_validated ? validated_bg : pending_bg); status_text = (break_validated ? validated_text : pending_text); status_symbol = (break_validated ? "Validated" : "Pending"); @@ -2526,8 +2687,8 @@ function main() { row = (row + 1); } if ((state.or5_high_broken || state.or5_low_broken)) { - let close_prob = 0; - let close_dir = ""; + var close_prob = 0; + var close_dir = ""; if ((is_bull_5m && state.or5_high_broken)) { close_prob = 67.1; close_dir = "Bullish"; @@ -2560,8 +2721,8 @@ function main() { row = (row + 1); } if ((show_15min && state.or15_captured)) { - let is_bull_15m = (state.or15_close >= state.or15_open); - let extreme_15m = state.or15_extreme_first; + var is_bull_15m = (state.or15_close >= state.or15_open); + var extreme_15m = state.or15_extreme_first; pinescript.table.cell(state.stats_table, 0, row, "━━━ 15-MIN ORB ━━━", ({ bgcolor: header_bg, text_color: header_text, text_size: table_size, text_font_family: font.family_monospace })); pinescript.tableMergeCells(state.stats_table, 0, row, 2, row); row = (row + 1); @@ -2758,6 +2919,10 @@ function run(data, options = {}) { rt.__shapeIdx = 0; globalThis.bar_index = i; globalThis.last_bar_index = n - 1; + globalThis.time_tradingday = inTime[i]; + globalThis.time_close = inTime[i]; + globalThis.last_bar_time = inTime[n - 1]; + globalThis.timenow = inTime[n - 1]; globalThis.barstate = { isfirst: i === 0, islast: i === n - 1, isrealtime: false, ishistory: true, isconfirmed: true, isnew: true, islastconfirmedhistory: i === n - 1, diff --git a/converted/Net_Buy_Volume_theUltimator5.js b/converted/Net_Buy_Volume_theUltimator5.js index 3b480a2..0db348c 100644 --- a/converted/Net_Buy_Volume_theUltimator5.js +++ b/converted/Net_Buy_Volume_theUltimator5.js @@ -42,6 +42,80 @@ const pinescript = { if (value != null && typeof value[Symbol.iterator] === 'function') return Array.from(value); return new Array(count || 0).fill(null); }, + __decArr: function(arr) { + if (!Array.isArray(arr) || arr.__pineDecorated) return arr; + const self = this; + const def = (name, fn) => Object.defineProperty(arr, name, { value: fn, writable: true, configurable: true }); + Object.defineProperty(arr, '__pineDecorated', { value: true, configurable: true }); + def('get', (i) => (arr[i] === undefined ? null : arr[i])); + def('set', (i, v) => { arr[i] = v; return v; }); + def('size', () => arr.length); + def('clear', () => { arr.length = 0; }); + def('insert', (i, v) => { arr.splice(i, 0, v); }); + def('remove', (i) => arr.splice(i, 1)[0]); + def('contains', (v) => arr.includes(v)); + def('indexof', (v) => arr.indexOf(v)); + def('lastindexof', (v) => arr.lastIndexOf(v)); + def('first', () => (arr.length ? arr[0] : null)); + def('last', () => (arr.length ? arr[arr.length - 1] : null)); + def('sum', () => arr.reduce((a, b) => a + b, 0)); + def('avg', () => (arr.length ? arr.reduce((a, b) => a + b, 0) / arr.length : 0)); + def('min', () => (arr.length ? Math.min(...arr) : null)); + def('max', () => (arr.length ? Math.max(...arr) : null)); + def('range', () => (arr.length ? Math.max(...arr) - Math.min(...arr) : null)); + // Statistical methods that route to the array.* built-ins (these names are not + // native to JS arrays, so attaching them here is safe from recursion). + def('median', () => self.arrayMedian(arr)); + def('mode', () => self.arrayMode(arr)); + def('stdev', () => self.arrayStdev(arr)); + def('variance', () => self.arrayVariance(arr)); + def('covariance', (other) => self.arrayCovariance(arr, other)); + def('percentile_linear_interpolation', (p) => self.arrayPercentileLinearInterpolation(arr, p)); + def('percentile_nearest_rank', (p) => self.arrayPercentileNearestRank(arr, p)); + def('abs', () => self.__decArr(self.arrayAbs(arr))); + def('binary_search', (v) => self.arrayBinarySearch(arr, v)); + // Pine's array.sort takes an order string, not a comparator. Use the native + // sort via .call so we don't recurse through this overridden method. + def('sort', (order) => { Array.prototype.sort.call(arr, (a, b) => (order === 'descending' ? b - a : a - b)); return arr; }); + def('sort_indices', (order) => self.__decArr(arr.map((_, i) => i).sort((a, b) => (order === 'descending' ? arr[b] - arr[a] : arr[a] - arr[b])))); + // join/slice/reverse/concat/includes/fill already exist natively on Array with + // compatible semantics, so we deliberately leave them to the native methods. + return arr; + }, + __decMap: function(m) { + if (!(m instanceof Map) || m.__pineDecorated) return m; + const def = (name, fn) => Object.defineProperty(m, name, { value: fn, writable: true, configurable: true }); + Object.defineProperty(m, '__pineDecorated', { value: true, configurable: true }); + def('put', (k, v) => { m.set(k, v); return v; }); + def('contains', (k) => m.has(k)); + def('remove', (k) => m.delete(k)); + def('keys', () => Array.from(Map.prototype.keys.call(m))); + def('values', () => Array.from(Map.prototype.values.call(m))); + def('size_', () => m.size); + return m; + }, + __decMatrix: function(m) { + if (!m || typeof m !== 'object' || m.__pineDecorated) return m; + const self = this; + const def = (name, fn) => Object.defineProperty(m, name, { value: fn, writable: true, configurable: true }); + Object.defineProperty(m, '__pineDecorated', { value: true, configurable: true }); + def('get', (r, c) => self.matrixGet(m, r, c)); + def('set', (r, c, v) => self.matrixSet(m, r, c, v)); + def('rows_', () => m.rows); + def('columns', () => m.cols); + def('fill', (v) => self.matrixFill(m, v)); + return m; + }, + __decDraw: function(obj) { + if (!obj || typeof obj !== 'object') return obj; + const p = new Proxy(obj, { + get(t, k) { + if (k in t || typeof k === 'symbol') return t[k]; + return function() { return p; }; + }, + }); + return p; + }, alertcondition: function(condition, ...rest) { if (globalThis.__pineRuntime) { globalThis.__pineRuntime.alerts.push({ condition, args: rest }); @@ -49,6 +123,32 @@ const pinescript = { return null; }, barcolor: function(color) { return null; }, + plotchar: function(series, ...rest) { + const rt = globalThis.__pineRuntime; + if (!rt) return series; + const bar = rt.__barIndex | 0; + const ord = (rt.__shapeIdx = (rt.__shapeIdx | 0) + 1) - 1; + let key = 'char_' + ord; + for (const r of rest) { + if (typeof r === 'string') { key = r; break; } + if (r && typeof r === 'object' && r.title) { key = String(r.title); break; } + } + let s = rt.plotshapes[key]; + if (!s) s = rt.plotshapes[key] = { title: key, data: [] }; + s.data[bar] = this.__scalar(series); + return series; + }, + plotarrow: function(series, ...rest) { + const rt = globalThis.__pineRuntime; + if (!rt) return series; + const bar = rt.__barIndex | 0; + const ord = (rt.__plotIdx = (rt.__plotIdx | 0) + 1) - 1; + const key = 'arrow_' + ord; + let p = rt.plots[key]; + if (!p) p = rt.plots[key] = { title: key, data: [] }; + p.data[bar] = this.__scalar(series); + return series; + }, bgcolor: function(color, title, editable, showLast) { return null; }, @@ -470,6 +570,12 @@ const pinescript = { atan: function(value) { return Math.atan(value); }, + todegrees: function(radians) { + return radians * (180 / Math.PI); + }, + toradians: function(degrees) { + return degrees * (Math.PI / 180); + }, floor: function(value) { return Math.floor(value); }, @@ -641,7 +747,7 @@ const pinescript = { return series; }, lineNew: function(x1, y1, x2, y2, opts = {}) { - return { x1, y1, x2, y2, opts, _type: 'line' }; + return this.__decDraw({ x1, y1, x2, y2, opts, _type: 'line' }); }, lineDelete: function(l) { return null; @@ -661,7 +767,7 @@ const pinescript = { return point === 0 || point === 'y1' ? line.y1 : line.y2; }, labelNew: function(x, y, text = '', opts = {}) { - return { x, y, text, opts, _type: 'label' }; + return this.__decDraw({ x, y, text, opts, _type: 'label' }); }, labelDelete: function(l) { return null; @@ -676,7 +782,7 @@ const pinescript = { return label.text || ''; }, boxNew: function(left, top, right, bottom, opts = {}) { - return { left, top, right, bottom, opts, _type: 'box' }; + return this.__decDraw({ left, top, right, bottom, opts, _type: 'box' }); }, boxDelete: function(box) { return null; @@ -694,7 +800,7 @@ const pinescript = { return box; }, polylineNew: function(points, opts = {}) { - return { points: points || [], opts, _type: 'polyline' }; + return this.__decDraw({ points: points || [], opts, _type: 'polyline' }); }, polylineDelete: function(poly) { return null; @@ -765,7 +871,7 @@ const pinescript = { return { time: _time ?? null, price: _price ?? null }; }, mapNew: function() { - return new Map(); + return this.__decMap(new Map()); }, mapSize: function(m) { return m instanceof Map ? m.size : 0; @@ -800,7 +906,7 @@ const pinescript = { const r = Math.max(0, rows ?? 0); const c = Math.max(0, cols ?? 0); const data = Array.from({ length: r }, () => Array.from({ length: c }, () => initialValue)); - return { rows: r, cols: c, data }; + return this.__decMatrix({ rows: r, cols: c, data }); }, matrixRows: function(m) { return m?.rows ?? 0; @@ -923,11 +1029,62 @@ const pinescript = { } return null; }, + __jacobiEigen: function(m) { + if (!m || !Array.isArray(m.data)) return null; + const n = m.rows ?? 0; + if (n === 0 || n !== (m.cols ?? 0)) return null; + // Work on a copy so the input matrix is untouched. + const a = m.data.map(row => row.slice()); + const v = Array.from({ length: n }, (_, i) => Array.from({ length: n }, (_, j) => (i === j ? 1 : 0))); + for (let sweep = 0; sweep < 100; sweep++) { + let off = 0; + for (let p = 0; p < n; p++) for (let q = p + 1; q < n; q++) off += a[p][q] * a[p][q]; + if (off < 1e-20) break; + for (let p = 0; p < n; p++) { + for (let q = p + 1; q < n; q++) { + if (Math.abs(a[p][q]) < 1e-18) continue; + const theta = (a[q][q] - a[p][p]) / (2 * a[p][q]); + const t = Math.sign(theta || 1) / (Math.abs(theta) + Math.sqrt(theta * theta + 1)); + const cos = 1 / Math.sqrt(t * t + 1); + const sin = t * cos; + for (let k = 0; k < n; k++) { + const akp = a[k][p], akq = a[k][q]; + a[k][p] = cos * akp - sin * akq; + a[k][q] = sin * akp + cos * akq; + } + for (let k = 0; k < n; k++) { + const apk = a[p][k], aqk = a[q][k]; + a[p][k] = cos * apk - sin * aqk; + a[q][k] = sin * apk + cos * aqk; + } + for (let k = 0; k < n; k++) { + const vkp = v[k][p], vkq = v[k][q]; + v[k][p] = cos * vkp - sin * vkq; + v[k][q] = sin * vkp + cos * vkq; + } + } + } + } + // Sort eigenpairs by eigenvalue, descending. + const order = Array.from({ length: n }, (_, i) => i).sort((i, j) => a[j][j] - a[i][i]); + const values = order.map(i => a[i][i]); + const vectors = Array.from({ length: n }, (_, r) => order.map(c => v[r][c])); + return { values, vectors }; + }, + matrixEigenvalues: function(m) { + const e = this.__jacobiEigen(m); + return this.__decArr(e ? e.values : []); + }, + matrixEigenvectors: function(m) { + const e = this.__jacobiEigen(m); + if (!e) return this.__decMatrix({ rows: 0, cols: 0, data: [] }); + return this.__decMatrix({ rows: e.vectors.length, cols: e.vectors.length, data: e.vectors }); + }, requestSecurity: function(symbol, timeframe, expression) { return expression; }, arrayNew: function(initialSize = 0, initialValue = 0) { - return Array(initialSize).fill(initialValue); + return this.__decArr(Array(initialSize).fill(initialValue)); }, arraySize: function(arr) { return arr ? arr.length : 0; @@ -978,10 +1135,10 @@ const pinescript = { return arr; }, arraySlice: function(arr, startIndex = 0, endIndex = null) { - if (!arr) return []; + if (!arr) return this.__decArr([]); const start = Number(startIndex) || 0; const end = endIndex === null || endIndex === undefined ? arr.length : Number(endIndex) || 0; - return arr.slice(start, end); + return this.__decArr(arr.slice(start, end)); }, arraySort: function(arr, order = 'ascending') { if (arr) arr.sort((a, b) => order === 'ascending' ? a - b : b - a); @@ -1191,7 +1348,7 @@ const pinescript = { const info = { ticker: 'AAPL', tickerid: 'NASDAQ:AAPL', prefix: 'NASDAQ', root: 'AAPL', suffix: '' }; return info[type] || ''; }, - timenow: 1781574527681, + timenow: 1782783576833, barstate: "LAST", dividends: {}, splits: {}, @@ -1245,11 +1402,11 @@ const pinescript = { arrayConcat: function(arr1, arr2) { if (!arr1) return arr2 || []; if (!arr2) return arr1; - return arr1.concat(arr2); + return this.__decArr(arr1.concat(arr2)); }, arrayCopy: function(arr) { - if (!arr) return []; - return [...arr]; + if (!arr) return this.__decArr([]); + return this.__decArr([...arr]); }, arrayBinarySearch: function(arr, value) { if (!arr || arr.length === 0) return -1; @@ -1339,7 +1496,7 @@ globalThis.input.timeframe = globalThis.input.timeframe || ((defval) => defval); globalThis.array = globalThis.array || { - from: (...items) => items, + from: (...items) => pinescript.__decArr(items), size: (arr) => pinescript.arraySize(arr), @@ -1508,6 +1665,8 @@ globalThis.line = globalThis.line || __pineNS({ style_solid: "solid", style_dash globalThis.box = globalThis.box || __pineNS({}); +globalThis.color = globalThis.color || __pineNS(Object.assign(function(c) { return c; }, { new: function(c, t) { return c; }, rgb: function(r, g, b, t) { return "#rgb(" + [r, g, b].join(",") + ")"; }, from_gradient: function(v, lo, hi, c1, c2) { return c1; }, r: function() { return 0; }, g: function() { return 0; }, b: function() { return 0; }, t: function() { return 0; }, aqua: "#00BCD4", black: "#363A45", blue: "#2962FF", fuchsia: "#E040FB", gray: "#787B86", green: "#4CAF50", lime: "#00E676", maroon: "#880E4F", navy: "#311B92", olive: "#808000", orange: "#FF9800", purple: "#9C27B0", red: "#FF5252", silver: "#B2B5BE", teal: "#00897B", white: "#FFFFFF", yellow: "#FFEB3B" })); + globalThis.label = globalThis.label || __pineNS({ style_label_down: "label_down", style_label_up: "label_up", style_none: "none" }); globalThis.polyline = globalThis.polyline || __pineNS({}); @@ -1520,7 +1679,9 @@ globalThis.map = globalThis.map || __pineNS({}); globalThis.session = globalThis.session || __pineNS({ regular: "regular", extended: "extended" }); -globalThis.dayofweek = globalThis.dayofweek || __pineNS({ sunday: 1, monday: 2, tuesday: 3, wednesday: 4, thursday: 5, friday: 6, saturday: 7 }); +globalThis.ticker = globalThis.ticker || __pineNS({}); + +globalThis.dayofweek = globalThis.dayofweek || __pineNS(Object.assign(function(t) { return new Date(t != null ? t : (globalThis.time || 0)).getUTCDay() + 1; }, { sunday: 1, monday: 2, tuesday: 3, wednesday: 4, thursday: 5, friday: 6, saturday: 7 })); globalThis.timeframe = __pineNS(Object.assign(globalThis.timeframe || {}, { period: (globalThis.timeframe && globalThis.timeframe.period) || "D", isintraday: false, isdaily: true, multiplier: 1 })); @@ -1568,8 +1729,6 @@ globalThis.bool = globalThis.bool || function(x) { return Boolean(x); }; globalThis.string = globalThis.string || function(x) { return x == null ? null : String(x); }; -globalThis.dayofweek = globalThis.dayofweek || __pineNS({ sunday: 1, monday: 2, tuesday: 3, wednesday: 4, thursday: 5, friday: 6, saturday: 7 }); - globalThis.str = globalThis.str || __pineNS({}); pinescript.math = globalThis.math; @@ -1655,7 +1814,7 @@ pinescript.text = { align_center: "center" }; pinescript.table = { - new: function(position, columns, rows, opts) { return { position, columns, rows, opts: opts || {}, cells: [] }; }, + new: function(position, columns, rows, opts) { return pinescript.__decDraw({ position, columns, rows, opts: opts || {}, cells: [] }); }, cell: function(table, column, row, text, opts) { @@ -1688,14 +1847,14 @@ function main() { null; // Study: Net Buy Volume [theUltimator5] // Options: {"overlay":false,"format":null} - let dataType = pinescript.inputString("Volume", ({ title: "Data Type", options: ["Volume", "Dollars", "Delta"] })); - let priceCalc = pinescript.inputString("Close", ({ title: "Price Calculation (for Dollars)", options: ["Close", "Open", "(Open + Close) / 2", "(High + Low + Open + Close) / 4", "(High + Low) / 2"] })); - let fp = request.footprint(10000, 100); - let buy = null; - let sell = null; - let fpChange = 0; - let net = 0; - let netDollars = 0; + var dataType = pinescript.inputString("Volume", ({ title: "Data Type", options: ["Volume", "Dollars", "Delta"] })); + var priceCalc = pinescript.inputString("Close", ({ title: "Price Calculation (for Dollars)", options: ["Close", "Open", "(Open + Close) / 2", "(High + Low + Open + Close) / 4", "(High + Low) / 2"] })); + var fp = request.footprint(10000, 100); + var buy = null; + var sell = null; + var fpChange = 0; + var net = 0; + var netDollars = 0; if (!pinescript.na(fp)) { buy = fp.buy_volume(); sell = fp.sell_volume(); @@ -1707,7 +1866,7 @@ function main() { ((open + close) / 2); open; close; - let price = ((priceCalc === "Close") ? undefined : ((priceCalc === "Open") ? undefined : ((priceCalc === "(Open + Close) / 2") ? undefined : ((priceCalc === "(High + Low + Open + Close) / 4") ? undefined : ((priceCalc === "(High + Low) / 2") ? undefined : undefined))))); + var price = ((priceCalc === "Close") ? undefined : ((priceCalc === "Open") ? undefined : ((priceCalc === "(Open + Close) / 2") ? undefined : ((priceCalc === "(High + Low + Open + Close) / 4") ? undefined : ((priceCalc === "(High + Low) / 2") ? undefined : undefined))))); if ((bar_index === 1)) { net = 0; netDollars = 0; @@ -1715,7 +1874,7 @@ function main() { net = (pinescript.hist(0, net, 1) + fpChange); netDollars = (pinescript.hist(1, netDollars, 1) + (fpChange * price)); } - let deltaColor = ((fpChange >= 0) ? pinescript.color.green : pinescript.color.red); + var deltaColor = ((fpChange >= 0) ? pinescript.color.green : pinescript.color.red); pinescript.plot(((dataType === "Volume") ? net : null), "Net Vol", ({ color: pinescript.color.aqua, style: plot.style_stepline, linewidth: 2 })); pinescript.plot(((dataType === "Dollars") ? netDollars : null), "Net $", ({ color: pinescript.color.hex("#ef78b7"), style: plot.style_stepline, linewidth: 2 })); pinescript.plot(((dataType === "Delta") ? fpChange : null), "Delta", ({ color: deltaColor, style: plot.style_columns, linewidth: 2 })); @@ -1764,6 +1923,10 @@ function run(data, options = {}) { rt.__shapeIdx = 0; globalThis.bar_index = i; globalThis.last_bar_index = n - 1; + globalThis.time_tradingday = inTime[i]; + globalThis.time_close = inTime[i]; + globalThis.last_bar_time = inTime[n - 1]; + globalThis.timenow = inTime[n - 1]; globalThis.barstate = { isfirst: i === 0, islast: i === n - 1, isrealtime: false, ishistory: true, isconfirmed: true, isnew: true, islastconfirmedhistory: i === n - 1, diff --git a/converted/Peak_Trading_Activity_Graphs_LuxAlgo.js b/converted/Peak_Trading_Activity_Graphs_LuxAlgo.js index bbef0e9..ae3dfa0 100644 --- a/converted/Peak_Trading_Activity_Graphs_LuxAlgo.js +++ b/converted/Peak_Trading_Activity_Graphs_LuxAlgo.js @@ -42,6 +42,80 @@ const pinescript = { if (value != null && typeof value[Symbol.iterator] === 'function') return Array.from(value); return new Array(count || 0).fill(null); }, + __decArr: function(arr) { + if (!Array.isArray(arr) || arr.__pineDecorated) return arr; + const self = this; + const def = (name, fn) => Object.defineProperty(arr, name, { value: fn, writable: true, configurable: true }); + Object.defineProperty(arr, '__pineDecorated', { value: true, configurable: true }); + def('get', (i) => (arr[i] === undefined ? null : arr[i])); + def('set', (i, v) => { arr[i] = v; return v; }); + def('size', () => arr.length); + def('clear', () => { arr.length = 0; }); + def('insert', (i, v) => { arr.splice(i, 0, v); }); + def('remove', (i) => arr.splice(i, 1)[0]); + def('contains', (v) => arr.includes(v)); + def('indexof', (v) => arr.indexOf(v)); + def('lastindexof', (v) => arr.lastIndexOf(v)); + def('first', () => (arr.length ? arr[0] : null)); + def('last', () => (arr.length ? arr[arr.length - 1] : null)); + def('sum', () => arr.reduce((a, b) => a + b, 0)); + def('avg', () => (arr.length ? arr.reduce((a, b) => a + b, 0) / arr.length : 0)); + def('min', () => (arr.length ? Math.min(...arr) : null)); + def('max', () => (arr.length ? Math.max(...arr) : null)); + def('range', () => (arr.length ? Math.max(...arr) - Math.min(...arr) : null)); + // Statistical methods that route to the array.* built-ins (these names are not + // native to JS arrays, so attaching them here is safe from recursion). + def('median', () => self.arrayMedian(arr)); + def('mode', () => self.arrayMode(arr)); + def('stdev', () => self.arrayStdev(arr)); + def('variance', () => self.arrayVariance(arr)); + def('covariance', (other) => self.arrayCovariance(arr, other)); + def('percentile_linear_interpolation', (p) => self.arrayPercentileLinearInterpolation(arr, p)); + def('percentile_nearest_rank', (p) => self.arrayPercentileNearestRank(arr, p)); + def('abs', () => self.__decArr(self.arrayAbs(arr))); + def('binary_search', (v) => self.arrayBinarySearch(arr, v)); + // Pine's array.sort takes an order string, not a comparator. Use the native + // sort via .call so we don't recurse through this overridden method. + def('sort', (order) => { Array.prototype.sort.call(arr, (a, b) => (order === 'descending' ? b - a : a - b)); return arr; }); + def('sort_indices', (order) => self.__decArr(arr.map((_, i) => i).sort((a, b) => (order === 'descending' ? arr[b] - arr[a] : arr[a] - arr[b])))); + // join/slice/reverse/concat/includes/fill already exist natively on Array with + // compatible semantics, so we deliberately leave them to the native methods. + return arr; + }, + __decMap: function(m) { + if (!(m instanceof Map) || m.__pineDecorated) return m; + const def = (name, fn) => Object.defineProperty(m, name, { value: fn, writable: true, configurable: true }); + Object.defineProperty(m, '__pineDecorated', { value: true, configurable: true }); + def('put', (k, v) => { m.set(k, v); return v; }); + def('contains', (k) => m.has(k)); + def('remove', (k) => m.delete(k)); + def('keys', () => Array.from(Map.prototype.keys.call(m))); + def('values', () => Array.from(Map.prototype.values.call(m))); + def('size_', () => m.size); + return m; + }, + __decMatrix: function(m) { + if (!m || typeof m !== 'object' || m.__pineDecorated) return m; + const self = this; + const def = (name, fn) => Object.defineProperty(m, name, { value: fn, writable: true, configurable: true }); + Object.defineProperty(m, '__pineDecorated', { value: true, configurable: true }); + def('get', (r, c) => self.matrixGet(m, r, c)); + def('set', (r, c, v) => self.matrixSet(m, r, c, v)); + def('rows_', () => m.rows); + def('columns', () => m.cols); + def('fill', (v) => self.matrixFill(m, v)); + return m; + }, + __decDraw: function(obj) { + if (!obj || typeof obj !== 'object') return obj; + const p = new Proxy(obj, { + get(t, k) { + if (k in t || typeof k === 'symbol') return t[k]; + return function() { return p; }; + }, + }); + return p; + }, alertcondition: function(condition, ...rest) { if (globalThis.__pineRuntime) { globalThis.__pineRuntime.alerts.push({ condition, args: rest }); @@ -49,6 +123,32 @@ const pinescript = { return null; }, barcolor: function(color) { return null; }, + plotchar: function(series, ...rest) { + const rt = globalThis.__pineRuntime; + if (!rt) return series; + const bar = rt.__barIndex | 0; + const ord = (rt.__shapeIdx = (rt.__shapeIdx | 0) + 1) - 1; + let key = 'char_' + ord; + for (const r of rest) { + if (typeof r === 'string') { key = r; break; } + if (r && typeof r === 'object' && r.title) { key = String(r.title); break; } + } + let s = rt.plotshapes[key]; + if (!s) s = rt.plotshapes[key] = { title: key, data: [] }; + s.data[bar] = this.__scalar(series); + return series; + }, + plotarrow: function(series, ...rest) { + const rt = globalThis.__pineRuntime; + if (!rt) return series; + const bar = rt.__barIndex | 0; + const ord = (rt.__plotIdx = (rt.__plotIdx | 0) + 1) - 1; + const key = 'arrow_' + ord; + let p = rt.plots[key]; + if (!p) p = rt.plots[key] = { title: key, data: [] }; + p.data[bar] = this.__scalar(series); + return series; + }, bgcolor: function(color, title, editable, showLast) { return null; }, @@ -470,6 +570,12 @@ const pinescript = { atan: function(value) { return Math.atan(value); }, + todegrees: function(radians) { + return radians * (180 / Math.PI); + }, + toradians: function(degrees) { + return degrees * (Math.PI / 180); + }, floor: function(value) { return Math.floor(value); }, @@ -641,7 +747,7 @@ const pinescript = { return series; }, lineNew: function(x1, y1, x2, y2, opts = {}) { - return { x1, y1, x2, y2, opts, _type: 'line' }; + return this.__decDraw({ x1, y1, x2, y2, opts, _type: 'line' }); }, lineDelete: function(l) { return null; @@ -661,7 +767,7 @@ const pinescript = { return point === 0 || point === 'y1' ? line.y1 : line.y2; }, labelNew: function(x, y, text = '', opts = {}) { - return { x, y, text, opts, _type: 'label' }; + return this.__decDraw({ x, y, text, opts, _type: 'label' }); }, labelDelete: function(l) { return null; @@ -676,7 +782,7 @@ const pinescript = { return label.text || ''; }, boxNew: function(left, top, right, bottom, opts = {}) { - return { left, top, right, bottom, opts, _type: 'box' }; + return this.__decDraw({ left, top, right, bottom, opts, _type: 'box' }); }, boxDelete: function(box) { return null; @@ -694,7 +800,7 @@ const pinescript = { return box; }, polylineNew: function(points, opts = {}) { - return { points: points || [], opts, _type: 'polyline' }; + return this.__decDraw({ points: points || [], opts, _type: 'polyline' }); }, polylineDelete: function(poly) { return null; @@ -765,7 +871,7 @@ const pinescript = { return { time: _time ?? null, price: _price ?? null }; }, mapNew: function() { - return new Map(); + return this.__decMap(new Map()); }, mapSize: function(m) { return m instanceof Map ? m.size : 0; @@ -800,7 +906,7 @@ const pinescript = { const r = Math.max(0, rows ?? 0); const c = Math.max(0, cols ?? 0); const data = Array.from({ length: r }, () => Array.from({ length: c }, () => initialValue)); - return { rows: r, cols: c, data }; + return this.__decMatrix({ rows: r, cols: c, data }); }, matrixRows: function(m) { return m?.rows ?? 0; @@ -923,11 +1029,62 @@ const pinescript = { } return null; }, + __jacobiEigen: function(m) { + if (!m || !Array.isArray(m.data)) return null; + const n = m.rows ?? 0; + if (n === 0 || n !== (m.cols ?? 0)) return null; + // Work on a copy so the input matrix is untouched. + const a = m.data.map(row => row.slice()); + const v = Array.from({ length: n }, (_, i) => Array.from({ length: n }, (_, j) => (i === j ? 1 : 0))); + for (let sweep = 0; sweep < 100; sweep++) { + let off = 0; + for (let p = 0; p < n; p++) for (let q = p + 1; q < n; q++) off += a[p][q] * a[p][q]; + if (off < 1e-20) break; + for (let p = 0; p < n; p++) { + for (let q = p + 1; q < n; q++) { + if (Math.abs(a[p][q]) < 1e-18) continue; + const theta = (a[q][q] - a[p][p]) / (2 * a[p][q]); + const t = Math.sign(theta || 1) / (Math.abs(theta) + Math.sqrt(theta * theta + 1)); + const cos = 1 / Math.sqrt(t * t + 1); + const sin = t * cos; + for (let k = 0; k < n; k++) { + const akp = a[k][p], akq = a[k][q]; + a[k][p] = cos * akp - sin * akq; + a[k][q] = sin * akp + cos * akq; + } + for (let k = 0; k < n; k++) { + const apk = a[p][k], aqk = a[q][k]; + a[p][k] = cos * apk - sin * aqk; + a[q][k] = sin * apk + cos * aqk; + } + for (let k = 0; k < n; k++) { + const vkp = v[k][p], vkq = v[k][q]; + v[k][p] = cos * vkp - sin * vkq; + v[k][q] = sin * vkp + cos * vkq; + } + } + } + } + // Sort eigenpairs by eigenvalue, descending. + const order = Array.from({ length: n }, (_, i) => i).sort((i, j) => a[j][j] - a[i][i]); + const values = order.map(i => a[i][i]); + const vectors = Array.from({ length: n }, (_, r) => order.map(c => v[r][c])); + return { values, vectors }; + }, + matrixEigenvalues: function(m) { + const e = this.__jacobiEigen(m); + return this.__decArr(e ? e.values : []); + }, + matrixEigenvectors: function(m) { + const e = this.__jacobiEigen(m); + if (!e) return this.__decMatrix({ rows: 0, cols: 0, data: [] }); + return this.__decMatrix({ rows: e.vectors.length, cols: e.vectors.length, data: e.vectors }); + }, requestSecurity: function(symbol, timeframe, expression) { return expression; }, arrayNew: function(initialSize = 0, initialValue = 0) { - return Array(initialSize).fill(initialValue); + return this.__decArr(Array(initialSize).fill(initialValue)); }, arraySize: function(arr) { return arr ? arr.length : 0; @@ -978,10 +1135,10 @@ const pinescript = { return arr; }, arraySlice: function(arr, startIndex = 0, endIndex = null) { - if (!arr) return []; + if (!arr) return this.__decArr([]); const start = Number(startIndex) || 0; const end = endIndex === null || endIndex === undefined ? arr.length : Number(endIndex) || 0; - return arr.slice(start, end); + return this.__decArr(arr.slice(start, end)); }, arraySort: function(arr, order = 'ascending') { if (arr) arr.sort((a, b) => order === 'ascending' ? a - b : b - a); @@ -1191,7 +1348,7 @@ const pinescript = { const info = { ticker: 'AAPL', tickerid: 'NASDAQ:AAPL', prefix: 'NASDAQ', root: 'AAPL', suffix: '' }; return info[type] || ''; }, - timenow: 1781574527898, + timenow: 1782783577035, barstate: "LAST", dividends: {}, splits: {}, @@ -1245,11 +1402,11 @@ const pinescript = { arrayConcat: function(arr1, arr2) { if (!arr1) return arr2 || []; if (!arr2) return arr1; - return arr1.concat(arr2); + return this.__decArr(arr1.concat(arr2)); }, arrayCopy: function(arr) { - if (!arr) return []; - return [...arr]; + if (!arr) return this.__decArr([]); + return this.__decArr([...arr]); }, arrayBinarySearch: function(arr, value) { if (!arr || arr.length === 0) return -1; @@ -1339,7 +1496,7 @@ globalThis.input.timeframe = globalThis.input.timeframe || ((defval) => defval); globalThis.array = globalThis.array || { - from: (...items) => items, + from: (...items) => pinescript.__decArr(items), size: (arr) => pinescript.arraySize(arr), @@ -1508,6 +1665,8 @@ globalThis.line = globalThis.line || __pineNS({ style_solid: "solid", style_dash globalThis.box = globalThis.box || __pineNS({}); +globalThis.color = globalThis.color || __pineNS(Object.assign(function(c) { return c; }, { new: function(c, t) { return c; }, rgb: function(r, g, b, t) { return "#rgb(" + [r, g, b].join(",") + ")"; }, from_gradient: function(v, lo, hi, c1, c2) { return c1; }, r: function() { return 0; }, g: function() { return 0; }, b: function() { return 0; }, t: function() { return 0; }, aqua: "#00BCD4", black: "#363A45", blue: "#2962FF", fuchsia: "#E040FB", gray: "#787B86", green: "#4CAF50", lime: "#00E676", maroon: "#880E4F", navy: "#311B92", olive: "#808000", orange: "#FF9800", purple: "#9C27B0", red: "#FF5252", silver: "#B2B5BE", teal: "#00897B", white: "#FFFFFF", yellow: "#FFEB3B" })); + globalThis.label = globalThis.label || __pineNS({ style_label_down: "label_down", style_label_up: "label_up", style_none: "none" }); globalThis.polyline = globalThis.polyline || __pineNS({}); @@ -1520,7 +1679,9 @@ globalThis.map = globalThis.map || __pineNS({}); globalThis.session = globalThis.session || __pineNS({ regular: "regular", extended: "extended" }); -globalThis.dayofweek = globalThis.dayofweek || __pineNS({ sunday: 1, monday: 2, tuesday: 3, wednesday: 4, thursday: 5, friday: 6, saturday: 7 }); +globalThis.ticker = globalThis.ticker || __pineNS({}); + +globalThis.dayofweek = globalThis.dayofweek || __pineNS(Object.assign(function(t) { return new Date(t != null ? t : (globalThis.time || 0)).getUTCDay() + 1; }, { sunday: 1, monday: 2, tuesday: 3, wednesday: 4, thursday: 5, friday: 6, saturday: 7 })); globalThis.timeframe = __pineNS(Object.assign(globalThis.timeframe || {}, { period: (globalThis.timeframe && globalThis.timeframe.period) || "D", isintraday: false, isdaily: true, multiplier: 1 })); @@ -1568,8 +1729,6 @@ globalThis.bool = globalThis.bool || function(x) { return Boolean(x); }; globalThis.string = globalThis.string || function(x) { return x == null ? null : String(x); }; -globalThis.dayofweek = globalThis.dayofweek || __pineNS({ sunday: 1, monday: 2, tuesday: 3, wednesday: 4, thursday: 5, friday: 6, saturday: 7 }); - globalThis.str = globalThis.str || __pineNS({}); pinescript.math = globalThis.math; @@ -1655,7 +1814,7 @@ pinescript.text = { align_center: "center" }; pinescript.table = { - new: function(position, columns, rows, opts) { return { position, columns, rows, opts: opts || {}, cells: [] }; }, + new: function(position, columns, rows, opts) { return pinescript.__decDraw({ position, columns, rows, opts: opts || {}, cells: [] }); }, cell: function(table, column, row, text, opts) { @@ -1689,66 +1848,66 @@ function main() { null; // Study: Peak Trading Activity Graphs [LuxAlgo] // Options: {"overlay":true} - let GREEN = pinescript.color.hex("#089981"); - let RED = pinescript.color.hex("#F23645"); - let YELLOW = pinescript.color.hex("#F2E805"); - let DATA = pinescript.color.hex("#DBDBDB"); - let BACKGROUND = pinescript.color.hex("#161616"); - let BORDERS = pinescript.color.hex("#2E2E2E"); - let ORANGE = pinescript.color.hex("#FF5D00"); - let ORANGE_LIGHT = pinescript.color.hex("#FEF1E8"); - let COLD_VIRIDIS = pinescript.color.hex("#400A53"); - let LUKEWARM_VIRIDIS = pinescript.color.hex("#408E8B"); - let HOT_VIRIDIS = pinescript.color.hex("#F8E650"); - let COLD_PLASMA = pinescript.color.hex("#110A81"); - let LUKEWARM_PLASMA = pinescript.color.hex("#B8487D"); - let HOT_PLASMA = pinescript.color.hex("#F1F455"); - let COLD_MAGMA = pinescript.color.hex("#000005"); - let LUKEWARM_MAGMA = pinescript.color.hex("#9D3B7A"); - let HOT_MAGMA = pinescript.color.hex("#FCF7BE"); - let VIRIDIS = "Viridis"; - let PLASMA = "Plasma"; - let MAGMA = "Magma"; - let CUSTOM = "Custom"; - let TOP_RIGHT = "Top Right"; - let BOTTOM_RIGHT = "Bottom Right"; - let BOTTOM_LEFT = "Bottom Left"; - let TINY = "Tiny"; - let SMALL = "Small"; - let NORMAL = "Normal"; - let LARGE = "Large"; - let HUGE = "Huge"; - let VOLUME = "Volume"; - let VOLATILITY = "Volatility"; - let DAYOFWEEK = "Days of Week"; - let DAYOFMONTH = "Days of Month"; - let MONTH = "Months"; - let HOURS = "Hours"; - let DASHBOARD_GROUP = "Graph"; - let STYLE_GROUP = "Style"; - let deltaTooltip = "Display the difference between the medians as a percentage. The smaller median is 0, and the larger median is 100. Enabling this feature highlights the differences between values."; - let dashboardPositionTooltip = "Select the graph location."; - let dashboardSizeTooltip = "Select the graph text size."; - let dashboardWidthTooltip = "Select the graph width."; - let dashboardHeightTooltip = "Select the graph height."; - let baseDataInput = pinescript.inputString(VOLUME, "Data", ({ options: [VOLATILITY, VOLUME] })); - let periodInput = pinescript.inputString(DAYOFMONTH, "Period", ({ options: [MONTH, DAYOFMONTH, DAYOFWEEK, HOURS] })); - let deltaInput = pinescript.inputBool(false, "Display Delta Between Medians", ({ tooltip: deltaTooltip })); - let dashboardPositionInput = pinescript.inputString(TOP_RIGHT, "Position", ({ group: DASHBOARD_GROUP, tooltip: dashboardPositionTooltip, options: [TOP_RIGHT, BOTTOM_RIGHT, BOTTOM_LEFT] })); - let dashboardSizeInput = pinescript.inputString(SMALL, "Size", ({ group: DASHBOARD_GROUP, tooltip: dashboardSizeTooltip, options: [TINY, SMALL, NORMAL, LARGE, HUGE] })); - let dashboardWidthInput = pinescript.inputFloat(1, "Width", ({ group: DASHBOARD_GROUP, tooltip: dashboardWidthTooltip, minval: 1, step: 0.25 })); - let dashboardHeightInput = pinescript.inputFloat(1, "Height", ({ group: DASHBOARD_GROUP, tooltip: dashboardHeightTooltip, minval: 1, step: 0.25 })); - let colormapInput = pinescript.inputString(VIRIDIS, "Colors", ({ group: STYLE_GROUP, options: [VIRIDIS, PLASMA, MAGMA, CUSTOM] })); - let coldColorInput = pinescript.inputColor(RED, "Custom Cold", ({ group: STYLE_GROUP })); - let lukewarmColorInput = pinescript.inputColor(YELLOW, "Custom Lukewarm", ({ group: STYLE_GROUP })); - let hotColorInput = pinescript.inputColor(GREEN, "Custom Hot", ({ group: STYLE_GROUP })); - if (state.fullMonth === undefined) state.fullMonth = array.from(data.new(pinescript.arrayNew()), data.new(pinescript.arrayNew()), data.new(pinescript.arrayNew()), data.new(pinescript.arrayNew()), data.new(pinescript.arrayNew()), data.new(pinescript.arrayNew()), data.new(pinescript.arrayNew()), data.new(pinescript.arrayNew()), data.new(pinescript.arrayNew()), data.new(pinescript.arrayNew()), data.new(pinescript.arrayNew()), data.new(pinescript.arrayNew()), data.new(pinescript.arrayNew()), data.new(pinescript.arrayNew()), data.new(pinescript.arrayNew()), data.new(pinescript.arrayNew()), data.new(pinescript.arrayNew()), data.new(pinescript.arrayNew()), data.new(pinescript.arrayNew()), data.new(pinescript.arrayNew()), data.new(pinescript.arrayNew()), data.new(pinescript.arrayNew()), data.new(pinescript.arrayNew()), data.new(pinescript.arrayNew()), data.new(pinescript.arrayNew()), data.new(pinescript.arrayNew()), data.new(pinescript.arrayNew()), data.new(pinescript.arrayNew()), data.new(pinescript.arrayNew()), data.new(pinescript.arrayNew()), data.new(pinescript.arrayNew())); - if (state.fullWeek === undefined) state.fullWeek = ((syminfo.type === "crypto") ? array.from(data.new(pinescript.arrayNew(), "SUN"), data.new(pinescript.arrayNew(), "MON"), data.new(pinescript.arrayNew(), "TUE"), data.new(pinescript.arrayNew(), "WED"), data.new(pinescript.arrayNew(), "THU"), data.new(pinescript.arrayNew(), "FRI"), data.new(pinescript.arrayNew(), "SAT")) : array.from(data.new(pinescript.arrayNew(), "MON"), data.new(pinescript.arrayNew(), "TUE"), data.new(pinescript.arrayNew(), "WED"), data.new(pinescript.arrayNew(), "THU"), data.new(pinescript.arrayNew(), "FRI"))); - if (state.fullYear === undefined) state.fullYear = array.from(data.new(pinescript.arrayNew(), "JAN"), data.new(pinescript.arrayNew(), "FEB"), data.new(pinescript.arrayNew(), "MAR"), data.new(pinescript.arrayNew(), "APR"), data.new(pinescript.arrayNew(), "MAY"), data.new(pinescript.arrayNew(), "JUN"), data.new(pinescript.arrayNew(), "JUL"), data.new(pinescript.arrayNew(), "AUG"), data.new(pinescript.arrayNew(), "SEP"), data.new(pinescript.arrayNew(), "OCT"), data.new(pinescript.arrayNew(), "NOV"), data.new(pinescript.arrayNew(), "DEC")); - if (state.fullDay === undefined) state.fullDay = array.from(data.new(pinescript.arrayNew(), "0"), data.new(pinescript.arrayNew(), "1"), data.new(pinescript.arrayNew(), "2"), data.new(pinescript.arrayNew(), "3"), data.new(pinescript.arrayNew(), "4"), data.new(pinescript.arrayNew(), "5"), data.new(pinescript.arrayNew(), "6"), data.new(pinescript.arrayNew(), "7"), data.new(pinescript.arrayNew(), "8"), data.new(pinescript.arrayNew(), "9"), data.new(pinescript.arrayNew(), "10"), data.new(pinescript.arrayNew(), "11"), data.new(pinescript.arrayNew(), "12"), data.new(pinescript.arrayNew(), "13"), data.new(pinescript.arrayNew(), "14"), data.new(pinescript.arrayNew(), "15"), data.new(pinescript.arrayNew(), "16"), data.new(pinescript.arrayNew(), "17"), data.new(pinescript.arrayNew(), "18"), data.new(pinescript.arrayNew(), "19"), data.new(pinescript.arrayNew(), "20"), data.new(pinescript.arrayNew(), "21"), data.new(pinescript.arrayNew(), "22"), data.new(pinescript.arrayNew(), "23")); - let parsedDayofweek = dayofweek(time_tradingday); - let parsedDayofmonth = ((syminfo.type === "crypto") ? dayofmonth : pinescript.dayofmonth(time_close)); - let parsedMonth = ((syminfo.type === "crypto") ? month : pinescript.month(time_close)); + var GREEN = pinescript.color.hex("#089981"); + var RED = pinescript.color.hex("#F23645"); + var YELLOW = pinescript.color.hex("#F2E805"); + var DATA = pinescript.color.hex("#DBDBDB"); + var BACKGROUND = pinescript.color.hex("#161616"); + var BORDERS = pinescript.color.hex("#2E2E2E"); + var ORANGE = pinescript.color.hex("#FF5D00"); + var ORANGE_LIGHT = pinescript.color.hex("#FEF1E8"); + var COLD_VIRIDIS = pinescript.color.hex("#400A53"); + var LUKEWARM_VIRIDIS = pinescript.color.hex("#408E8B"); + var HOT_VIRIDIS = pinescript.color.hex("#F8E650"); + var COLD_PLASMA = pinescript.color.hex("#110A81"); + var LUKEWARM_PLASMA = pinescript.color.hex("#B8487D"); + var HOT_PLASMA = pinescript.color.hex("#F1F455"); + var COLD_MAGMA = pinescript.color.hex("#000005"); + var LUKEWARM_MAGMA = pinescript.color.hex("#9D3B7A"); + var HOT_MAGMA = pinescript.color.hex("#FCF7BE"); + var VIRIDIS = "Viridis"; + var PLASMA = "Plasma"; + var MAGMA = "Magma"; + var CUSTOM = "Custom"; + var TOP_RIGHT = "Top Right"; + var BOTTOM_RIGHT = "Bottom Right"; + var BOTTOM_LEFT = "Bottom Left"; + var TINY = "Tiny"; + var SMALL = "Small"; + var NORMAL = "Normal"; + var LARGE = "Large"; + var HUGE = "Huge"; + var VOLUME = "Volume"; + var VOLATILITY = "Volatility"; + var DAYOFWEEK = "Days of Week"; + var DAYOFMONTH = "Days of Month"; + var MONTH = "Months"; + var HOURS = "Hours"; + var DASHBOARD_GROUP = "Graph"; + var STYLE_GROUP = "Style"; + var deltaTooltip = "Display the difference between the medians as a percentage. The smaller median is 0, and the larger median is 100. Enabling this feature highlights the differences between values."; + var dashboardPositionTooltip = "Select the graph location."; + var dashboardSizeTooltip = "Select the graph text size."; + var dashboardWidthTooltip = "Select the graph width."; + var dashboardHeightTooltip = "Select the graph height."; + var baseDataInput = pinescript.inputString(VOLUME, "Data", ({ options: [VOLATILITY, VOLUME] })); + var periodInput = pinescript.inputString(DAYOFMONTH, "Period", ({ options: [MONTH, DAYOFMONTH, DAYOFWEEK, HOURS] })); + var deltaInput = pinescript.inputBool(false, "Display Delta Between Medians", ({ tooltip: deltaTooltip })); + var dashboardPositionInput = pinescript.inputString(TOP_RIGHT, "Position", ({ group: DASHBOARD_GROUP, tooltip: dashboardPositionTooltip, options: [TOP_RIGHT, BOTTOM_RIGHT, BOTTOM_LEFT] })); + var dashboardSizeInput = pinescript.inputString(SMALL, "Size", ({ group: DASHBOARD_GROUP, tooltip: dashboardSizeTooltip, options: [TINY, SMALL, NORMAL, LARGE, HUGE] })); + var dashboardWidthInput = pinescript.inputFloat(1, "Width", ({ group: DASHBOARD_GROUP, tooltip: dashboardWidthTooltip, minval: 1, step: 0.25 })); + var dashboardHeightInput = pinescript.inputFloat(1, "Height", ({ group: DASHBOARD_GROUP, tooltip: dashboardHeightTooltip, minval: 1, step: 0.25 })); + var colormapInput = pinescript.inputString(VIRIDIS, "Colors", ({ group: STYLE_GROUP, options: [VIRIDIS, PLASMA, MAGMA, CUSTOM] })); + var coldColorInput = pinescript.inputColor(RED, "Custom Cold", ({ group: STYLE_GROUP })); + var lukewarmColorInput = pinescript.inputColor(YELLOW, "Custom Lukewarm", ({ group: STYLE_GROUP })); + var hotColorInput = pinescript.inputColor(GREEN, "Custom Hot", ({ group: STYLE_GROUP })); + if (state.fullMonth === undefined) state.fullMonth = pinescript.array.from(data.new(pinescript.arrayNew()), data.new(pinescript.arrayNew()), data.new(pinescript.arrayNew()), data.new(pinescript.arrayNew()), data.new(pinescript.arrayNew()), data.new(pinescript.arrayNew()), data.new(pinescript.arrayNew()), data.new(pinescript.arrayNew()), data.new(pinescript.arrayNew()), data.new(pinescript.arrayNew()), data.new(pinescript.arrayNew()), data.new(pinescript.arrayNew()), data.new(pinescript.arrayNew()), data.new(pinescript.arrayNew()), data.new(pinescript.arrayNew()), data.new(pinescript.arrayNew()), data.new(pinescript.arrayNew()), data.new(pinescript.arrayNew()), data.new(pinescript.arrayNew()), data.new(pinescript.arrayNew()), data.new(pinescript.arrayNew()), data.new(pinescript.arrayNew()), data.new(pinescript.arrayNew()), data.new(pinescript.arrayNew()), data.new(pinescript.arrayNew()), data.new(pinescript.arrayNew()), data.new(pinescript.arrayNew()), data.new(pinescript.arrayNew()), data.new(pinescript.arrayNew()), data.new(pinescript.arrayNew()), data.new(pinescript.arrayNew())); + if (state.fullWeek === undefined) state.fullWeek = ((syminfo.type === "crypto") ? pinescript.array.from(data.new(pinescript.arrayNew(), "SUN"), data.new(pinescript.arrayNew(), "MON"), data.new(pinescript.arrayNew(), "TUE"), data.new(pinescript.arrayNew(), "WED"), data.new(pinescript.arrayNew(), "THU"), data.new(pinescript.arrayNew(), "FRI"), data.new(pinescript.arrayNew(), "SAT")) : pinescript.array.from(data.new(pinescript.arrayNew(), "MON"), data.new(pinescript.arrayNew(), "TUE"), data.new(pinescript.arrayNew(), "WED"), data.new(pinescript.arrayNew(), "THU"), data.new(pinescript.arrayNew(), "FRI"))); + if (state.fullYear === undefined) state.fullYear = pinescript.array.from(data.new(pinescript.arrayNew(), "JAN"), data.new(pinescript.arrayNew(), "FEB"), data.new(pinescript.arrayNew(), "MAR"), data.new(pinescript.arrayNew(), "APR"), data.new(pinescript.arrayNew(), "MAY"), data.new(pinescript.arrayNew(), "JUN"), data.new(pinescript.arrayNew(), "JUL"), data.new(pinescript.arrayNew(), "AUG"), data.new(pinescript.arrayNew(), "SEP"), data.new(pinescript.arrayNew(), "OCT"), data.new(pinescript.arrayNew(), "NOV"), data.new(pinescript.arrayNew(), "DEC")); + if (state.fullDay === undefined) state.fullDay = pinescript.array.from(data.new(pinescript.arrayNew(), "0"), data.new(pinescript.arrayNew(), "1"), data.new(pinescript.arrayNew(), "2"), data.new(pinescript.arrayNew(), "3"), data.new(pinescript.arrayNew(), "4"), data.new(pinescript.arrayNew(), "5"), data.new(pinescript.arrayNew(), "6"), data.new(pinescript.arrayNew(), "7"), data.new(pinescript.arrayNew(), "8"), data.new(pinescript.arrayNew(), "9"), data.new(pinescript.arrayNew(), "10"), data.new(pinescript.arrayNew(), "11"), data.new(pinescript.arrayNew(), "12"), data.new(pinescript.arrayNew(), "13"), data.new(pinescript.arrayNew(), "14"), data.new(pinescript.arrayNew(), "15"), data.new(pinescript.arrayNew(), "16"), data.new(pinescript.arrayNew(), "17"), data.new(pinescript.arrayNew(), "18"), data.new(pinescript.arrayNew(), "19"), data.new(pinescript.arrayNew(), "20"), data.new(pinescript.arrayNew(), "21"), data.new(pinescript.arrayNew(), "22"), data.new(pinescript.arrayNew(), "23")); + var parsedDayofweek = dayofweek(time_tradingday); + var parsedDayofmonth = ((syminfo.type === "crypto") ? dayofmonth : pinescript.dayofmonth(time_close)); + var parsedMonth = ((syminfo.type === "crypto") ? month : pinescript.month(time_close)); pinescript.position.bottom_left; pinescript.position.bottom_right; pinescript.position.top_right; @@ -1768,41 +1927,49 @@ function main() { [COLD_MAGMA, LUKEWARM_MAGMA, HOT_MAGMA]; [COLD_PLASMA, LUKEWARM_PLASMA, HOT_VIRIDIS]; [COLD_VIRIDIS, LUKEWARM_VIRIDIS, HOT_VIRIDIS]; - let [cold, lukewarm, hot] = pinescript.unpack(((colormapInput === VIRIDIS) ? undefined : ((colormapInput === PLASMA) ? undefined : ((colormapInput === MAGMA) ? undefined : ((colormapInput === CUSTOM) ? undefined : null)))), 3); + var [cold, lukewarm, hot] = pinescript.unpack(((colormapInput === VIRIDIS) ? undefined : ((colormapInput === PLASMA) ? undefined : ((colormapInput === MAGMA) ? undefined : ((colormapInput === CUSTOM) ? undefined : null)))), 3); function error() { if (state.t_able === undefined) state.t_able = pinescript.table.new(pinescript.position.top_center, 1, 1, ({ bgcolor: ORANGE_LIGHT, border_color: ORANGE, border_width: 1, frame_color: ORANGE, frame_width: 1 })); - return t_able.cell(0, 0, "⚠ Warning: The tool will not display the dashboard on higher timeframes.nPlease select a chart timeframe that is equal to or lower than the period parameter in the settings panel.", ({ text_color: ORANGE, text_size: 0 })); + return state.t_able.cell(0, 0, "⚠ Warning: The tool will not display the dashboard on higher timeframes.nPlease select a chart timeframe that is equal to or lower than the period parameter in the settings panel.", ({ text_color: ORANGE, text_size: 0 })); } function gatherDaysOfWeek(output) { - let [dailyData, currentDay] = pinescript.unpack(pinescript.requestSecurity(syminfo.tickerid, "1D", [output, parsedDayofweek]), 2); - let current = fullWeek.get((currentDay - 1)); + var [dailyData, currentDay] = pinescript.unpack(pinescript.requestSecurity(syminfo.tickerid, "1D", [output, parsedDayofweek]), 2); + var current = state.fullWeek.get((currentDay - 1)); if (!pinescript.na(current)) { - current.values.push(pinescript.nz(dailyData)); + return current.values.push(pinescript.nz(dailyData)); + } else { + return null; } } function gatherMonths(output) { - let [montlhyData, currentMonth] = pinescript.unpack(pinescript.requestSecurity(syminfo.tickerid, "1M", [output, parsedMonth]), 2); - let current = fullYear.get((currentMonth - 1)); + var [montlhyData, currentMonth] = pinescript.unpack(pinescript.requestSecurity(syminfo.tickerid, "1M", [output, parsedMonth]), 2); + var current = state.fullYear.get((currentMonth - 1)); if (!pinescript.na(current)) { - current.values.push(pinescript.nz(montlhyData)); + return current.values.push(pinescript.nz(montlhyData)); + } else { + return null; } } function gatherDayOfMonth(output) { - let [dailyData, dayOfMonth] = pinescript.unpack(pinescript.requestSecurity(syminfo.tickerid, "1D", [output, parsedDayofmonth]), 2); - let current = fullMonth.get((dayOfMonth - 1)); + var [dailyData, dayOfMonth] = pinescript.unpack(pinescript.requestSecurity(syminfo.tickerid, "1D", [output, parsedDayofmonth]), 2); + var current = state.fullMonth.get((dayOfMonth - 1)); if (!pinescript.na(current)) { - current.values.push(pinescript.nz(dailyData)); + return current.values.push(pinescript.nz(dailyData)); + } else { + return null; } } function gatherHours(output) { - let [hourlyData, currentHour] = pinescript.unpack(pinescript.requestSecurity(syminfo.tickerid, "60", [output, hour]), 2); - let current = fullDay.get(currentHour); + var [hourlyData, currentHour] = pinescript.unpack(pinescript.requestSecurity(syminfo.tickerid, "60", [output, hour]), 2); + var current = state.fullDay.get(currentHour); if (!pinescript.na(current)) { - current.values.push(pinescript.nz(hourlyData)); + return current.values.push(pinescript.nz(hourlyData)); + } else { + return null; } } function gatherData() { - let output = ((baseDataInput === VOLUME) ? volume : pinescript.ta.tr); + var output = ((baseDataInput === VOLUME) ? volume : pinescript.ta.tr); switch (periodInput) { case DAYOFWEEK: { @@ -1827,79 +1994,79 @@ function main() { } } function cell(t_able, column, row, data, color = pinescript.color.hex("#FFFFFF"), align = pinescript.text.align_center, background = null, height = 0) { - return t_able.cell(column, row, data, ({ text_color: pinescript.color, text_size: state.parsedDashboardSize, text_halign: align, bgcolor: background, height: height })); + return state.t_able.cell(column, row, data, ({ text_color: pinescript.color, text_size: state.parsedDashboardSize, text_halign: align, bgcolor: background, height: height })); } function divider(t_able, row, column) { - let rowDivider = "━━━━━━━━━━━"; - t_able.merge_cells(0, row, column, row); + var rowDivider = "━━━━━━━━━━━"; + state.t_able.merge_cells(0, row, column, row); return cell(state.t_able, 0, row, rowDivider, ({ align: pinescript.text.align_center, height: (0.5 * dashboardHeightInput), color: DATA })); } function displayColumns(fullData) { if (state.t_able === undefined) state.t_able = pinescript.table.new(state.parsedDashboardPosition, ((fullData.size() * 2) + 1), 104, ({ bgcolor: BACKGROUND, border_width: 0, frame_color: BORDERS, frame_width: 1, force_overlay: false })); if (state.parsedData === undefined) state.parsedData = pinescript.arrayNew(); - for (const [index, eachDataPoint] of fullData) { - parsedData.push(eachDataPoint.values.median()); + for (const [index, eachDataPoint] of (fullData ?? []).entries()) { + state.parsedData.push(eachDataPoint.values.median()); } - t_able.merge_cells(0, 0, ((fullData.size() * 2) - 2), 0); - t_able.merge_cells(0, 1, ((fullData.size() * 2) - 2), 1); - let min = pinescript.strToString(parsedData.min(), ((baseDataInput === VOLATILITY) ? format.mintick : format.volume)); - let max = pinescript.strToString(parsedData.max(), ((baseDataInput === VOLATILITY) ? format.mintick : format.volume)); + state.t_able.merge_cells(0, 0, ((fullData.size() * 2) - 2), 0); + state.t_able.merge_cells(0, 1, ((fullData.size() * 2) - 2), 1); + var min = pinescript.strToString(state.parsedData.min(), ((baseDataInput === VOLATILITY) ? format.mintick : format.volume)); + var max = pinescript.strToString(state.parsedData.max(), ((baseDataInput === VOLATILITY) ? format.mintick : format.volume)); "Daily"; "Hourly"; "Monthly"; - let periodTag = ((periodInput === MONTH) ? undefined : ((periodInput === HOURS) ? undefined : undefined)); - let header = (deltaInput ? "(Delta)" : ""); + var periodTag = ((periodInput === MONTH) ? undefined : ((periodInput === HOURS) ? undefined : undefined)); + var header = (deltaInput ? "(Delta)" : ""); cell(state.t_able, 0, 0, pinescript.strFormat("{0} Median {1} {2}nMin: {3} Max: {4}", periodTag, baseDataInput, header, min, max), ({ align: pinescript.text.align_center, color: DATA })); - let column = 0; - for (const [index, eachDataPoint] of state.parsedData) { + var column = 0; + for (const [index, eachDataPoint] of (state.parsedData ?? []).entries()) { if ((index > 0)) { column += 2; } - t_able.merge_cells(column, 2, column, 103); - t_able.cell_set_height(column, 2, (0.2 * dashboardHeightInput)); - let scale = (deltaInput ? ((eachDataPoint - parsedData.min()) / parsedData.range()) : (eachDataPoint / parsedData.max())); - let value = pinescript.max(1, pinescript.floor((100 * scale))); - let customColor = ((value >= 50) ? pinescript.color.from_gradient(value, 50, 100, pinescript.color.new(lukewarm, 0), pinescript.color.new(hot, 0)) : pinescript.color.from_gradient(value, 1, 50, pinescript.color.new(cold, 0), pinescript.color.new(lukewarm, 0))); - let topRow = (101 - value); + state.t_able.merge_cells(column, 2, column, 103); + state.t_able.cell_set_height(column, 2, (0.2 * dashboardHeightInput)); + var scale = (deltaInput ? ((eachDataPoint - state.parsedData.min()) / state.parsedData.range()) : (eachDataPoint / state.parsedData.max())); + var value = pinescript.max(1, pinescript.floor((100 * scale))); + var customColor = ((value >= 50) ? pinescript.color.from_gradient(value, 50, 100, pinescript.color.new(lukewarm, 0), pinescript.color.new(hot, 0)) : pinescript.color.from_gradient(value, 1, 50, pinescript.color.new(cold, 0), pinescript.color.new(lukewarm, 0))); + var topRow = (101 - value); for (let row = 1; row <= 100; row++) { - t_able.cell((column + 1), (row + 1), ({ height: (0.1 * dashboardHeightInput) })); + state.t_able.cell((column + 1), (row + 1), ({ height: (0.1 * dashboardHeightInput) })); if ((row >= topRow)) { - t_able.cell_set_bgcolor((column + 1), (row + 1), customColor); + state.t_able.cell_set_bgcolor((column + 1), (row + 1), customColor); } } - t_able.cell((column + 1), 103, fullData.get(index).tag, ({ width: dashboardWidthInput, text_color: DATA, text_size: state.parsedDashboardSize })); - if ((index === (parsedData.size() - 1))) { - t_able.merge_cells((column + 2), 2, (column + 2), 103); - t_able.cell_set_height((column + 2), 2, (0.2 * dashboardHeightInput)); + state.t_able.cell((column + 1), 103, fullData.get(index).tag, ({ width: dashboardWidthInput, text_color: DATA, text_size: state.parsedDashboardSize })); + if ((index === (state.parsedData.size() - 1))) { + state.t_able.merge_cells((column + 2), 2, (column + 2), 103); + state.t_able.cell_set_height((column + 2), 2, (0.2 * dashboardHeightInput)); } } } function displayMonth() { if (state.t_able === undefined) state.t_able = pinescript.table.new(state.parsedDashboardPosition, 9, 7, ({ bgcolor: BACKGROUND, border_width: 0, frame_color: BORDERS, frame_width: 1, force_overlay: false })); if (state.parsedData === undefined) state.parsedData = pinescript.arrayNew(); - for (const [index, eachDataPoint] of state.fullMonth) { - parsedData.push(eachDataPoint.values.median()); + for (const [index, eachDataPoint] of (state.fullMonth ?? []).entries()) { + state.parsedData.push(eachDataPoint.values.median()); } - t_able.merge_cells(0, 0, 6, 0); - let min = pinescript.strToString(parsedData.min(), ((baseDataInput === VOLATILITY) ? format.mintick : format.volume)); - let max = pinescript.strToString(parsedData.max(), ((baseDataInput === VOLATILITY) ? format.mintick : format.volume)); + state.t_able.merge_cells(0, 0, 6, 0); + var min = pinescript.strToString(state.parsedData.min(), ((baseDataInput === VOLATILITY) ? format.mintick : format.volume)); + var max = pinescript.strToString(state.parsedData.max(), ((baseDataInput === VOLATILITY) ? format.mintick : format.volume)); "Daily"; "Hourly"; "Monthly"; - let periodTag = ((periodInput === MONTH) ? undefined : ((periodInput === HOURS) ? undefined : undefined)); - let header = (deltaInput ? "(Delta)" : ""); + var periodTag = ((periodInput === MONTH) ? undefined : ((periodInput === HOURS) ? undefined : undefined)); + var header = (deltaInput ? "(Delta)" : ""); cell(state.t_able, 0, 0, pinescript.strFormat("{0} Median {1} {2}nMax: {3}nMin: {4}", periodTag, baseDataInput, header, max, min), ({ align: pinescript.text.align_center, color: DATA })); divider(state.t_able, 1, 6); - let row = 2; - let column0 = array.from(1, 8, 15, 22, 29); - let column1 = array.from(2, 9, 16, 23, 30); - let column2 = array.from(3, 10, 17, 24, 31); - let column3 = array.from(4, 11, 18, 25); - let column4 = array.from(5, 12, 19, 26); - let column5 = array.from(6, 13, 20, 27); - let column6 = array.from(7, 14, 21, 28); - let nextRow = false; - for (const [index, eachDataPoint] of state.parsedData) { + var row = 2; + var column0 = pinescript.array.from(1, 8, 15, 22, 29); + var column1 = pinescript.array.from(2, 9, 16, 23, 30); + var column2 = pinescript.array.from(3, 10, 17, 24, 31); + var column3 = pinescript.array.from(4, 11, 18, 25); + var column4 = pinescript.array.from(5, 12, 19, 26); + var column5 = pinescript.array.from(6, 13, 20, 27); + var column6 = pinescript.array.from(7, 14, 21, 28); + var nextRow = false; + for (const [index, eachDataPoint] of (state.parsedData ?? []).entries()) { 6; 5; 4; @@ -1907,7 +2074,7 @@ function main() { 2; 1; 0; - let column = ((column0.includes((index + 1))) ? undefined : ((column1.includes((index + 1))) ? undefined : ((column2.includes((index + 1))) ? undefined : ((column3.includes((index + 1))) ? undefined : ((column4.includes((index + 1))) ? undefined : ((column5.includes((index + 1))) ? undefined : ((column6.includes((index + 1))) ? undefined : null))))))); + var column = ((column0.includes((index + 1))) ? undefined : ((column1.includes((index + 1))) ? undefined : ((column2.includes((index + 1))) ? undefined : ((column3.includes((index + 1))) ? undefined : ((column4.includes((index + 1))) ? undefined : ((column5.includes((index + 1))) ? undefined : ((column6.includes((index + 1))) ? undefined : null))))))); if (nextRow) { row += 1; nextRow = false; @@ -1915,9 +2082,9 @@ function main() { if (((((index + 1) % 7) === 0) && !nextRow)) { nextRow = true; } - let scale = (deltaInput ? ((eachDataPoint - parsedData.min()) / parsedData.range()) : (eachDataPoint / parsedData.max())); - let value = pinescript.max(1, pinescript.floor((100 * scale))); - let customColor = ((value >= 50) ? pinescript.color.from_gradient(value, 50, 100, pinescript.color.new(lukewarm, 0), pinescript.color.new(hot, 0)) : pinescript.color.from_gradient(value, 1, 50, pinescript.color.new(cold, 0), pinescript.color.new(lukewarm, 0))); + var scale = (deltaInput ? ((eachDataPoint - state.parsedData.min()) / state.parsedData.range()) : (eachDataPoint / state.parsedData.max())); + var value = pinescript.max(1, pinescript.floor((100 * scale))); + var customColor = ((value >= 50) ? pinescript.color.from_gradient(value, 50, 100, pinescript.color.new(lukewarm, 0), pinescript.color.new(hot, 0)) : pinescript.color.from_gradient(value, 1, 50, pinescript.color.new(cold, 0), pinescript.color.new(lukewarm, 0))); cell(state.t_able, column, row, pinescript.strToString((index + 1)), ({ background: customColor })); } } @@ -1997,6 +2164,10 @@ function run(data, options = {}) { rt.__shapeIdx = 0; globalThis.bar_index = i; globalThis.last_bar_index = n - 1; + globalThis.time_tradingday = inTime[i]; + globalThis.time_close = inTime[i]; + globalThis.last_bar_time = inTime[n - 1]; + globalThis.timenow = inTime[n - 1]; globalThis.barstate = { isfirst: i === 0, islast: i === n - 1, isrealtime: false, ishistory: true, isconfirmed: true, isnew: true, islastconfirmedhistory: i === n - 1, diff --git a/converted/Pro_Pattern_Long-Short_High_Winrate.js b/converted/Pro_Pattern_Long-Short_High_Winrate.js index 8a8e03e..051592f 100644 --- a/converted/Pro_Pattern_Long-Short_High_Winrate.js +++ b/converted/Pro_Pattern_Long-Short_High_Winrate.js @@ -42,6 +42,80 @@ const pinescript = { if (value != null && typeof value[Symbol.iterator] === 'function') return Array.from(value); return new Array(count || 0).fill(null); }, + __decArr: function(arr) { + if (!Array.isArray(arr) || arr.__pineDecorated) return arr; + const self = this; + const def = (name, fn) => Object.defineProperty(arr, name, { value: fn, writable: true, configurable: true }); + Object.defineProperty(arr, '__pineDecorated', { value: true, configurable: true }); + def('get', (i) => (arr[i] === undefined ? null : arr[i])); + def('set', (i, v) => { arr[i] = v; return v; }); + def('size', () => arr.length); + def('clear', () => { arr.length = 0; }); + def('insert', (i, v) => { arr.splice(i, 0, v); }); + def('remove', (i) => arr.splice(i, 1)[0]); + def('contains', (v) => arr.includes(v)); + def('indexof', (v) => arr.indexOf(v)); + def('lastindexof', (v) => arr.lastIndexOf(v)); + def('first', () => (arr.length ? arr[0] : null)); + def('last', () => (arr.length ? arr[arr.length - 1] : null)); + def('sum', () => arr.reduce((a, b) => a + b, 0)); + def('avg', () => (arr.length ? arr.reduce((a, b) => a + b, 0) / arr.length : 0)); + def('min', () => (arr.length ? Math.min(...arr) : null)); + def('max', () => (arr.length ? Math.max(...arr) : null)); + def('range', () => (arr.length ? Math.max(...arr) - Math.min(...arr) : null)); + // Statistical methods that route to the array.* built-ins (these names are not + // native to JS arrays, so attaching them here is safe from recursion). + def('median', () => self.arrayMedian(arr)); + def('mode', () => self.arrayMode(arr)); + def('stdev', () => self.arrayStdev(arr)); + def('variance', () => self.arrayVariance(arr)); + def('covariance', (other) => self.arrayCovariance(arr, other)); + def('percentile_linear_interpolation', (p) => self.arrayPercentileLinearInterpolation(arr, p)); + def('percentile_nearest_rank', (p) => self.arrayPercentileNearestRank(arr, p)); + def('abs', () => self.__decArr(self.arrayAbs(arr))); + def('binary_search', (v) => self.arrayBinarySearch(arr, v)); + // Pine's array.sort takes an order string, not a comparator. Use the native + // sort via .call so we don't recurse through this overridden method. + def('sort', (order) => { Array.prototype.sort.call(arr, (a, b) => (order === 'descending' ? b - a : a - b)); return arr; }); + def('sort_indices', (order) => self.__decArr(arr.map((_, i) => i).sort((a, b) => (order === 'descending' ? arr[b] - arr[a] : arr[a] - arr[b])))); + // join/slice/reverse/concat/includes/fill already exist natively on Array with + // compatible semantics, so we deliberately leave them to the native methods. + return arr; + }, + __decMap: function(m) { + if (!(m instanceof Map) || m.__pineDecorated) return m; + const def = (name, fn) => Object.defineProperty(m, name, { value: fn, writable: true, configurable: true }); + Object.defineProperty(m, '__pineDecorated', { value: true, configurable: true }); + def('put', (k, v) => { m.set(k, v); return v; }); + def('contains', (k) => m.has(k)); + def('remove', (k) => m.delete(k)); + def('keys', () => Array.from(Map.prototype.keys.call(m))); + def('values', () => Array.from(Map.prototype.values.call(m))); + def('size_', () => m.size); + return m; + }, + __decMatrix: function(m) { + if (!m || typeof m !== 'object' || m.__pineDecorated) return m; + const self = this; + const def = (name, fn) => Object.defineProperty(m, name, { value: fn, writable: true, configurable: true }); + Object.defineProperty(m, '__pineDecorated', { value: true, configurable: true }); + def('get', (r, c) => self.matrixGet(m, r, c)); + def('set', (r, c, v) => self.matrixSet(m, r, c, v)); + def('rows_', () => m.rows); + def('columns', () => m.cols); + def('fill', (v) => self.matrixFill(m, v)); + return m; + }, + __decDraw: function(obj) { + if (!obj || typeof obj !== 'object') return obj; + const p = new Proxy(obj, { + get(t, k) { + if (k in t || typeof k === 'symbol') return t[k]; + return function() { return p; }; + }, + }); + return p; + }, alertcondition: function(condition, ...rest) { if (globalThis.__pineRuntime) { globalThis.__pineRuntime.alerts.push({ condition, args: rest }); @@ -49,6 +123,32 @@ const pinescript = { return null; }, barcolor: function(color) { return null; }, + plotchar: function(series, ...rest) { + const rt = globalThis.__pineRuntime; + if (!rt) return series; + const bar = rt.__barIndex | 0; + const ord = (rt.__shapeIdx = (rt.__shapeIdx | 0) + 1) - 1; + let key = 'char_' + ord; + for (const r of rest) { + if (typeof r === 'string') { key = r; break; } + if (r && typeof r === 'object' && r.title) { key = String(r.title); break; } + } + let s = rt.plotshapes[key]; + if (!s) s = rt.plotshapes[key] = { title: key, data: [] }; + s.data[bar] = this.__scalar(series); + return series; + }, + plotarrow: function(series, ...rest) { + const rt = globalThis.__pineRuntime; + if (!rt) return series; + const bar = rt.__barIndex | 0; + const ord = (rt.__plotIdx = (rt.__plotIdx | 0) + 1) - 1; + const key = 'arrow_' + ord; + let p = rt.plots[key]; + if (!p) p = rt.plots[key] = { title: key, data: [] }; + p.data[bar] = this.__scalar(series); + return series; + }, bgcolor: function(color, title, editable, showLast) { return null; }, @@ -470,6 +570,12 @@ const pinescript = { atan: function(value) { return Math.atan(value); }, + todegrees: function(radians) { + return radians * (180 / Math.PI); + }, + toradians: function(degrees) { + return degrees * (Math.PI / 180); + }, floor: function(value) { return Math.floor(value); }, @@ -641,7 +747,7 @@ const pinescript = { return series; }, lineNew: function(x1, y1, x2, y2, opts = {}) { - return { x1, y1, x2, y2, opts, _type: 'line' }; + return this.__decDraw({ x1, y1, x2, y2, opts, _type: 'line' }); }, lineDelete: function(l) { return null; @@ -661,7 +767,7 @@ const pinescript = { return point === 0 || point === 'y1' ? line.y1 : line.y2; }, labelNew: function(x, y, text = '', opts = {}) { - return { x, y, text, opts, _type: 'label' }; + return this.__decDraw({ x, y, text, opts, _type: 'label' }); }, labelDelete: function(l) { return null; @@ -676,7 +782,7 @@ const pinescript = { return label.text || ''; }, boxNew: function(left, top, right, bottom, opts = {}) { - return { left, top, right, bottom, opts, _type: 'box' }; + return this.__decDraw({ left, top, right, bottom, opts, _type: 'box' }); }, boxDelete: function(box) { return null; @@ -694,7 +800,7 @@ const pinescript = { return box; }, polylineNew: function(points, opts = {}) { - return { points: points || [], opts, _type: 'polyline' }; + return this.__decDraw({ points: points || [], opts, _type: 'polyline' }); }, polylineDelete: function(poly) { return null; @@ -765,7 +871,7 @@ const pinescript = { return { time: _time ?? null, price: _price ?? null }; }, mapNew: function() { - return new Map(); + return this.__decMap(new Map()); }, mapSize: function(m) { return m instanceof Map ? m.size : 0; @@ -800,7 +906,7 @@ const pinescript = { const r = Math.max(0, rows ?? 0); const c = Math.max(0, cols ?? 0); const data = Array.from({ length: r }, () => Array.from({ length: c }, () => initialValue)); - return { rows: r, cols: c, data }; + return this.__decMatrix({ rows: r, cols: c, data }); }, matrixRows: function(m) { return m?.rows ?? 0; @@ -923,11 +1029,62 @@ const pinescript = { } return null; }, + __jacobiEigen: function(m) { + if (!m || !Array.isArray(m.data)) return null; + const n = m.rows ?? 0; + if (n === 0 || n !== (m.cols ?? 0)) return null; + // Work on a copy so the input matrix is untouched. + const a = m.data.map(row => row.slice()); + const v = Array.from({ length: n }, (_, i) => Array.from({ length: n }, (_, j) => (i === j ? 1 : 0))); + for (let sweep = 0; sweep < 100; sweep++) { + let off = 0; + for (let p = 0; p < n; p++) for (let q = p + 1; q < n; q++) off += a[p][q] * a[p][q]; + if (off < 1e-20) break; + for (let p = 0; p < n; p++) { + for (let q = p + 1; q < n; q++) { + if (Math.abs(a[p][q]) < 1e-18) continue; + const theta = (a[q][q] - a[p][p]) / (2 * a[p][q]); + const t = Math.sign(theta || 1) / (Math.abs(theta) + Math.sqrt(theta * theta + 1)); + const cos = 1 / Math.sqrt(t * t + 1); + const sin = t * cos; + for (let k = 0; k < n; k++) { + const akp = a[k][p], akq = a[k][q]; + a[k][p] = cos * akp - sin * akq; + a[k][q] = sin * akp + cos * akq; + } + for (let k = 0; k < n; k++) { + const apk = a[p][k], aqk = a[q][k]; + a[p][k] = cos * apk - sin * aqk; + a[q][k] = sin * apk + cos * aqk; + } + for (let k = 0; k < n; k++) { + const vkp = v[k][p], vkq = v[k][q]; + v[k][p] = cos * vkp - sin * vkq; + v[k][q] = sin * vkp + cos * vkq; + } + } + } + } + // Sort eigenpairs by eigenvalue, descending. + const order = Array.from({ length: n }, (_, i) => i).sort((i, j) => a[j][j] - a[i][i]); + const values = order.map(i => a[i][i]); + const vectors = Array.from({ length: n }, (_, r) => order.map(c => v[r][c])); + return { values, vectors }; + }, + matrixEigenvalues: function(m) { + const e = this.__jacobiEigen(m); + return this.__decArr(e ? e.values : []); + }, + matrixEigenvectors: function(m) { + const e = this.__jacobiEigen(m); + if (!e) return this.__decMatrix({ rows: 0, cols: 0, data: [] }); + return this.__decMatrix({ rows: e.vectors.length, cols: e.vectors.length, data: e.vectors }); + }, requestSecurity: function(symbol, timeframe, expression) { return expression; }, arrayNew: function(initialSize = 0, initialValue = 0) { - return Array(initialSize).fill(initialValue); + return this.__decArr(Array(initialSize).fill(initialValue)); }, arraySize: function(arr) { return arr ? arr.length : 0; @@ -978,10 +1135,10 @@ const pinescript = { return arr; }, arraySlice: function(arr, startIndex = 0, endIndex = null) { - if (!arr) return []; + if (!arr) return this.__decArr([]); const start = Number(startIndex) || 0; const end = endIndex === null || endIndex === undefined ? arr.length : Number(endIndex) || 0; - return arr.slice(start, end); + return this.__decArr(arr.slice(start, end)); }, arraySort: function(arr, order = 'ascending') { if (arr) arr.sort((a, b) => order === 'ascending' ? a - b : b - a); @@ -1191,7 +1348,7 @@ const pinescript = { const info = { ticker: 'AAPL', tickerid: 'NASDAQ:AAPL', prefix: 'NASDAQ', root: 'AAPL', suffix: '' }; return info[type] || ''; }, - timenow: 1781574528093, + timenow: 1782783577193, barstate: "LAST", dividends: {}, splits: {}, @@ -1245,11 +1402,11 @@ const pinescript = { arrayConcat: function(arr1, arr2) { if (!arr1) return arr2 || []; if (!arr2) return arr1; - return arr1.concat(arr2); + return this.__decArr(arr1.concat(arr2)); }, arrayCopy: function(arr) { - if (!arr) return []; - return [...arr]; + if (!arr) return this.__decArr([]); + return this.__decArr([...arr]); }, arrayBinarySearch: function(arr, value) { if (!arr || arr.length === 0) return -1; @@ -1339,7 +1496,7 @@ globalThis.input.timeframe = globalThis.input.timeframe || ((defval) => defval); globalThis.array = globalThis.array || { - from: (...items) => items, + from: (...items) => pinescript.__decArr(items), size: (arr) => pinescript.arraySize(arr), @@ -1508,6 +1665,8 @@ globalThis.line = globalThis.line || __pineNS({ style_solid: "solid", style_dash globalThis.box = globalThis.box || __pineNS({}); +globalThis.color = globalThis.color || __pineNS(Object.assign(function(c) { return c; }, { new: function(c, t) { return c; }, rgb: function(r, g, b, t) { return "#rgb(" + [r, g, b].join(",") + ")"; }, from_gradient: function(v, lo, hi, c1, c2) { return c1; }, r: function() { return 0; }, g: function() { return 0; }, b: function() { return 0; }, t: function() { return 0; }, aqua: "#00BCD4", black: "#363A45", blue: "#2962FF", fuchsia: "#E040FB", gray: "#787B86", green: "#4CAF50", lime: "#00E676", maroon: "#880E4F", navy: "#311B92", olive: "#808000", orange: "#FF9800", purple: "#9C27B0", red: "#FF5252", silver: "#B2B5BE", teal: "#00897B", white: "#FFFFFF", yellow: "#FFEB3B" })); + globalThis.label = globalThis.label || __pineNS({ style_label_down: "label_down", style_label_up: "label_up", style_none: "none" }); globalThis.polyline = globalThis.polyline || __pineNS({}); @@ -1520,7 +1679,9 @@ globalThis.map = globalThis.map || __pineNS({}); globalThis.session = globalThis.session || __pineNS({ regular: "regular", extended: "extended" }); -globalThis.dayofweek = globalThis.dayofweek || __pineNS({ sunday: 1, monday: 2, tuesday: 3, wednesday: 4, thursday: 5, friday: 6, saturday: 7 }); +globalThis.ticker = globalThis.ticker || __pineNS({}); + +globalThis.dayofweek = globalThis.dayofweek || __pineNS(Object.assign(function(t) { return new Date(t != null ? t : (globalThis.time || 0)).getUTCDay() + 1; }, { sunday: 1, monday: 2, tuesday: 3, wednesday: 4, thursday: 5, friday: 6, saturday: 7 })); globalThis.timeframe = __pineNS(Object.assign(globalThis.timeframe || {}, { period: (globalThis.timeframe && globalThis.timeframe.period) || "D", isintraday: false, isdaily: true, multiplier: 1 })); @@ -1568,8 +1729,6 @@ globalThis.bool = globalThis.bool || function(x) { return Boolean(x); }; globalThis.string = globalThis.string || function(x) { return x == null ? null : String(x); }; -globalThis.dayofweek = globalThis.dayofweek || __pineNS({ sunday: 1, monday: 2, tuesday: 3, wednesday: 4, thursday: 5, friday: 6, saturday: 7 }); - globalThis.str = globalThis.str || __pineNS({}); pinescript.math = globalThis.math; @@ -1655,7 +1814,7 @@ pinescript.text = { align_center: "center" }; pinescript.table = { - new: function(position, columns, rows, opts) { return { position, columns, rows, opts: opts || {}, cells: [] }; }, + new: function(position, columns, rows, opts) { return pinescript.__decDraw({ position, columns, rows, opts: opts || {}, cells: [] }); }, cell: function(table, column, row, text, opts) { @@ -1688,96 +1847,96 @@ function main() { null; // Study: Pro Pattern Al-Sat (High Winrate) // Options: {"overlay":true,"max_labels_count":500,"max_lines_count":500,"max_boxes_count":500} - let showBullishPatterns = pinescript.inputBool(true, "Yükseliş Formasyonlarını Göster", ({ group: "Pattern Ayarları" })); - let showBearishPatterns = pinescript.inputBool(true, "Düşüş Formasyonlarını Göster", ({ group: "Pattern Ayarları" })); - let riskRewardRatio = pinescript.inputFloat(2, "Risk/Ödül Oranı", ({ minval: 0.5, maxval: 5, step: 0.1, group: "Risk Yönetimi" })); - let stopLossATR = pinescript.inputFloat(1.5, "Stop Loss (ATR Çarpanı)", ({ minval: 0.5, maxval: 5, step: 0.1, group: "Risk Yönetimi" })); - let showTable = pinescript.inputBool(true, "Sinyal Tablosunu Göster", ({ group: "Görünüm" })); - let tablePosition = pinescript.inputString("top_right", "Tablo Pozisyonu", ({ options: ["top_right", "top_left", "bottom_right", "bottom_left"], group: "Görünüm" })); - let showTradeResults = pinescript.inputBool(true, "İşlem Sonuçlarını Göster", ({ group: "Görünüm" })); - let showExtendedLines = pinescript.inputBool(true, "Uzatılmış Çizgileri Göster", ({ group: "Görünüm" })); - let useTrendFilter = pinescript.inputBool(true, "Trend Filtresi Kullan", ({ group: "🎯 Filtreler" })); - let useVolumeFilter = pinescript.inputBool(true, "Volume Filtresi Kullan", ({ group: "🎯 Filtreler" })); - let useRSIFilter = pinescript.inputBool(true, "RSI Filtresi Kullan", ({ group: "🎯 Filtreler" })); - let useSupportResistance = pinescript.inputBool(true, "Destek/Direnç Filtresi", ({ group: "🎯 Filtreler" })); - let useTimeFilter = pinescript.inputBool(true, "Zaman Filtresi Kullan", ({ group: "🎯 Filtreler" })); - let emaFastLength = pinescript.inputInt(50, "Hızlı EMA", ({ minval: 10, group: "📈 Trend Filtresi" })); - let emaSlowLength = pinescript.inputInt(200, "Yavaş EMA", ({ minval: 50, group: "📈 Trend Filtresi" })); - let supertrendMultiplier = pinescript.inputFloat(3, "Supertrend Çarpanı", ({ minval: 1, maxval: 10, step: 0.5, group: "📈 Trend Filtresi" })); - let supertrendPeriod = pinescript.inputInt(10, "Supertrend Periyodu", ({ minval: 5, group: "📈 Trend Filtresi" })); - let adxThreshold = pinescript.inputInt(25, "ADX Minimum", ({ minval: 10, maxval: 50, group: "📈 Trend Filtresi" })); - let volumeMultiplier = pinescript.inputFloat(1.5, "Volume Çarpanı", ({ minval: 1, maxval: 3, step: 0.1, group: "📊 Volume Filtresi" })); - let volumePeriod = pinescript.inputInt(20, "Volume SMA Periyodu", ({ minval: 10, group: "📊 Volume Filtresi" })); - let rsiPeriod = pinescript.inputInt(14, "RSI Periyodu", ({ minval: 5, group: "📉 RSI Filtresi" })); - let rsiOverbought = pinescript.inputInt(70, "Aşırı Alım Seviyesi", ({ minval: 60, maxval: 90, group: "📉 RSI Filtresi" })); - let rsiOversold = pinescript.inputInt(30, "Aşırı Satım Seviyesi", ({ minval: 10, maxval: 40, group: "📉 RSI Filtresi" })); - let pivotLookback = pinescript.inputInt(10, "Pivot Lookback", ({ minval: 5, maxval: 50, group: "🎚️ Destek/Direnç" })); - let srProximity = pinescript.inputFloat(0.5, "Seviye Yakınlığı (%)", ({ minval: 0.1, maxval: 2, step: 0.1, group: "🎚️ Destek/Direnç" })); - let avoidFirstHour = pinescript.inputBool(true, "İlk 1 Saati Atla", ({ group: "⏰ Zaman Filtresi" })); - let avoidLastHour = pinescript.inputBool(true, "Son 1 Saati Atla", ({ group: "⏰ Zaman Filtresi" })); - let tradingStartHour = pinescript.inputInt(9, "İşlem Başlangıç Saati", ({ minval: 0, maxval: 23, group: "⏰ Zaman Filtresi" })); - let tradingEndHour = pinescript.inputInt(17, "İşlem Bitiş Saati", ({ minval: 0, maxval: 23, group: "⏰ Zaman Filtresi" })); - let atrValue = pinescript.atr(14); - let emaFast = pinescript.ema(close, emaFastLength); - let emaSlow = pinescript.ema(close, emaSlowLength); - let uptrend = (emaFast > emaSlow); - let downtrend = (emaFast < emaSlow); - let [supertrendValue, supertrendDirection] = pinescript.unpack(pinescript.supertrend(supertrendMultiplier, supertrendPeriod), 2); - let supertrendBullish = (supertrendDirection < 0); - let supertrendBearish = (supertrendDirection > 0); - let [diPlus, diMinus, adx] = pinescript.unpack(pinescript.dmi(14, 14), 3); - let strongTrend = (adx > adxThreshold); - let trendFilterBullish = ((uptrend && supertrendBullish) && strongTrend); - let trendFilterBearish = ((downtrend && supertrendBearish) && strongTrend); - let volumeSMA = pinescript.sma(volume, volumePeriod); - let volumeFilter = (volume > (volumeSMA * volumeMultiplier)); - let rsi = pinescript.rsi(close, rsiPeriod); - let rsiFilterBullish = (rsi < rsiOverbought); - let rsiFilterBearish = (rsi > rsiOversold); - let pivotHigh = ta.pivothigh(high, pivotLookback, pivotLookback); - let pivotLow = ta.pivotlow(low, pivotLookback, pivotLookback); + var showBullishPatterns = pinescript.inputBool(true, "Yükseliş Formasyonlarını Göster", ({ group: "Pattern Ayarları" })); + var showBearishPatterns = pinescript.inputBool(true, "Düşüş Formasyonlarını Göster", ({ group: "Pattern Ayarları" })); + var riskRewardRatio = pinescript.inputFloat(2, "Risk/Ödül Oranı", ({ minval: 0.5, maxval: 5, step: 0.1, group: "Risk Yönetimi" })); + var stopLossATR = pinescript.inputFloat(1.5, "Stop Loss (ATR Çarpanı)", ({ minval: 0.5, maxval: 5, step: 0.1, group: "Risk Yönetimi" })); + var showTable = pinescript.inputBool(true, "Sinyal Tablosunu Göster", ({ group: "Görünüm" })); + var tablePosition = pinescript.inputString("top_right", "Tablo Pozisyonu", ({ options: ["top_right", "top_left", "bottom_right", "bottom_left"], group: "Görünüm" })); + var showTradeResults = pinescript.inputBool(true, "İşlem Sonuçlarını Göster", ({ group: "Görünüm" })); + var showExtendedLines = pinescript.inputBool(true, "Uzatılmış Çizgileri Göster", ({ group: "Görünüm" })); + var useTrendFilter = pinescript.inputBool(true, "Trend Filtresi Kullan", ({ group: "🎯 Filtreler" })); + var useVolumeFilter = pinescript.inputBool(true, "Volume Filtresi Kullan", ({ group: "🎯 Filtreler" })); + var useRSIFilter = pinescript.inputBool(true, "RSI Filtresi Kullan", ({ group: "🎯 Filtreler" })); + var useSupportResistance = pinescript.inputBool(true, "Destek/Direnç Filtresi", ({ group: "🎯 Filtreler" })); + var useTimeFilter = pinescript.inputBool(true, "Zaman Filtresi Kullan", ({ group: "🎯 Filtreler" })); + var emaFastLength = pinescript.inputInt(50, "Hızlı EMA", ({ minval: 10, group: "📈 Trend Filtresi" })); + var emaSlowLength = pinescript.inputInt(200, "Yavaş EMA", ({ minval: 50, group: "📈 Trend Filtresi" })); + var supertrendMultiplier = pinescript.inputFloat(3, "Supertrend Çarpanı", ({ minval: 1, maxval: 10, step: 0.5, group: "📈 Trend Filtresi" })); + var supertrendPeriod = pinescript.inputInt(10, "Supertrend Periyodu", ({ minval: 5, group: "📈 Trend Filtresi" })); + var adxThreshold = pinescript.inputInt(25, "ADX Minimum", ({ minval: 10, maxval: 50, group: "📈 Trend Filtresi" })); + var volumeMultiplier = pinescript.inputFloat(1.5, "Volume Çarpanı", ({ minval: 1, maxval: 3, step: 0.1, group: "📊 Volume Filtresi" })); + var volumePeriod = pinescript.inputInt(20, "Volume SMA Periyodu", ({ minval: 10, group: "📊 Volume Filtresi" })); + var rsiPeriod = pinescript.inputInt(14, "RSI Periyodu", ({ minval: 5, group: "📉 RSI Filtresi" })); + var rsiOverbought = pinescript.inputInt(70, "Aşırı Alım Seviyesi", ({ minval: 60, maxval: 90, group: "📉 RSI Filtresi" })); + var rsiOversold = pinescript.inputInt(30, "Aşırı Satım Seviyesi", ({ minval: 10, maxval: 40, group: "📉 RSI Filtresi" })); + var pivotLookback = pinescript.inputInt(10, "Pivot Lookback", ({ minval: 5, maxval: 50, group: "🎚️ Destek/Direnç" })); + var srProximity = pinescript.inputFloat(0.5, "Seviye Yakınlığı (%)", ({ minval: 0.1, maxval: 2, step: 0.1, group: "🎚️ Destek/Direnç" })); + var avoidFirstHour = pinescript.inputBool(true, "İlk 1 Saati Atla", ({ group: "⏰ Zaman Filtresi" })); + var avoidLastHour = pinescript.inputBool(true, "Son 1 Saati Atla", ({ group: "⏰ Zaman Filtresi" })); + var tradingStartHour = pinescript.inputInt(9, "İşlem Başlangıç Saati", ({ minval: 0, maxval: 23, group: "⏰ Zaman Filtresi" })); + var tradingEndHour = pinescript.inputInt(17, "İşlem Bitiş Saati", ({ minval: 0, maxval: 23, group: "⏰ Zaman Filtresi" })); + var atrValue = pinescript.atr(14); + var emaFast = pinescript.ema(pinescript.series(0, close), emaFastLength); + var emaSlow = pinescript.ema(pinescript.series(1, close), emaSlowLength); + var uptrend = (emaFast > emaSlow); + var downtrend = (emaFast < emaSlow); + var [supertrendValue, supertrendDirection] = pinescript.unpack(pinescript.supertrend(supertrendMultiplier, supertrendPeriod), 2); + var supertrendBullish = (supertrendDirection < 0); + var supertrendBearish = (supertrendDirection > 0); + var [diPlus, diMinus, adx] = pinescript.unpack(pinescript.dmi(14, 14), 3); + var strongTrend = (adx > adxThreshold); + var trendFilterBullish = ((uptrend && supertrendBullish) && strongTrend); + var trendFilterBearish = ((downtrend && supertrendBearish) && strongTrend); + var volumeSMA = pinescript.sma(pinescript.series(2, volume), volumePeriod); + var volumeFilter = (volume > (volumeSMA * volumeMultiplier)); + var rsi = pinescript.rsi(pinescript.series(3, close), rsiPeriod); + var rsiFilterBullish = (rsi < rsiOverbought); + var rsiFilterBearish = (rsi > rsiOversold); + var pivotHigh = pinescript.ta.pivothigh(high, pivotLookback, pivotLookback); + var pivotLow = pinescript.ta.pivotlow(low, pivotLookback, pivotLookback); if (state.lastResistance === undefined) state.lastResistance = null; if (state.lastSupport === undefined) state.lastSupport = null; if (!pinescript.na(pivotHigh)) { - state.lastResistance = pinescript.hist(0, high, pivotLookback); + state.lastResistance = pinescript.hist(4, high, pivotLookback); } if (!pinescript.na(pivotLow)) { - state.lastSupport = pinescript.hist(1, low, pivotLookback); + state.lastSupport = pinescript.hist(5, low, pivotLookback); } - let proximityPercent = (srProximity / 100); - let nearSupport = (!pinescript.na(state.lastSupport) && ((pinescript.abs((close - state.lastSupport)) / state.lastSupport) < proximityPercent)); - let nearResistance = (!pinescript.na(state.lastResistance) && ((pinescript.abs((close - state.lastResistance)) / state.lastResistance) < proximityPercent)); - let srFilterBullish = nearSupport; - let srFilterBearish = nearResistance; - let currentHour = pinescript.hour(time); - let currentMinute = pinescript.minute(time); - let firstHourAvoid = (avoidFirstHour && (currentHour === tradingStartHour)); - let lastHourAvoid = (avoidLastHour && (currentHour === tradingEndHour)); - let inTradingHours = ((currentHour >= tradingStartHour) && (currentHour <= tradingEndHour)); - let timeFilter = ((inTradingHours && !firstHourAvoid) && !lastHourAvoid); - let bullishEngulfing = ((((pinescript.hist(2, close, 1) < pinescript.hist(3, open, 1)) && (close > open)) && (open <= pinescript.hist(4, close, 1))) && (close >= pinescript.hist(5, open, 1))); - let bearishEngulfing = ((((pinescript.hist(6, close, 1) > pinescript.hist(7, open, 1)) && (close < open)) && (open >= pinescript.hist(8, close, 1))) && (close <= pinescript.hist(9, open, 1))); - let bodySize = pinescript.abs((close - open)); - let upperWick = (high - pinescript.max(close, open)); - let lowerWick = (pinescript.min(close, open) - low); - let lowestLow = pinescript.lowest(pinescript.series(10, low), 10); - let highestHigh = pinescript.highest(pinescript.series(11, high), 10); - let isHammer = ((((lowerWick > (bodySize * 2)) && (upperWick < bodySize)) && (close > open)) && (low === lowestLow)); - let isShootingStar = ((((upperWick > (bodySize * 2)) && (lowerWick < bodySize)) && (close < open)) && (high === highestHigh)); - let avgBody2 = pinescript.abs((pinescript.hist(12, close, 2) - pinescript.hist(13, open, 2))); - let isMorningStar = (pinescript.hist(14, close, 2)((pinescript.hist(15, open, 2) + pinescript.hist(16, close, 2))) / 2); - let isEveningStar = ((((pinescript.hist(17, close, 2) > pinescript.hist(18, open, 2)) && (pinescript.abs((pinescript.hist(19, close, 1) - pinescript.hist(20, open, 1))) < (avgBody2 * 0.3))) && (close < open)) && (close < ((pinescript.hist(21, open, 2) + pinescript.hist(22, close, 2)) / 2))); - let candleRange = (high - low); - let avgRange = pinescript.sma((high - low), 14); - let isDoji = ((pinescript.abs((close - open)) < (candleRange * 0.1)) && (candleRange > avgRange)); - let threeWhiteSoldiers = (((((((((pinescript.hist(23, close, 2) > pinescript.hist(24, open, 2)) && (pinescript.hist(25, close, 1) > pinescript.hist(26, open, 1))) && (close > open)) && (pinescript.hist(27, close, 1) > pinescript.hist(28, close, 2))) && (close > pinescript.hist(29, close, 1))) && (pinescript.hist(30, open, 1) > pinescript.hist(31, open, 2))) && (open > pinescript.hist(32, open, 1))) && (pinescript.hist(33, open, 1) < pinescript.hist(34, close, 2))) && (open < pinescript.hist(35, close, 1))); - let threeBlackCrows = (((((((((pinescript.hist(36, close, 2) < pinescript.hist(37, open, 2)) && (pinescript.hist(38, close, 1) < pinescript.hist(39, open, 1))) && (close < open)) && (pinescript.hist(40, close, 1) < pinescript.hist(41, close, 2))) && (close < pinescript.hist(42, close, 1))) && (pinescript.hist(43, open, 1) < pinescript.hist(44, open, 2))) && (open < pinescript.hist(45, open, 1))) && (pinescript.hist(46, open, 1) > pinescript.hist(47, close, 2))) && (open > pinescript.hist(48, close, 1))); - let piercingLine = ((((pinescript.hist(49, close, 1) < pinescript.hist(50, open, 1)) && (close > open)) && (pinescript.open((pinescript.hist(51, open, 1) + pinescript.hist(52, close, 1))) / 2)) && (close < pinescript.hist(53, open, 1))); - let darkCloudCover = (((((pinescript.hist(54, close, 1) > pinescript.hist(55, open, 1)) && (close < open)) && (open > pinescript.hist(56, close, 1))) && (close < ((pinescript.hist(57, open, 1) + pinescript.hist(58, close, 1)) / 2))) && (close > pinescript.hist(59, open, 1))); - let bullishPattern = (((((bullishEngulfing || isHammer) || isMorningStar) || threeWhiteSoldiers) || piercingLine) && showBullishPatterns); - let bearishPattern = (((((bearishEngulfing || isShootingStar) || isEveningStar) || threeBlackCrows) || darkCloudCover) && showBearishPatterns); - let bullishSignal = (((((bullishPattern && (!useTrendFilter || trendFilterBullish)) && (!useVolumeFilter || volumeFilter)) && (!useRSIFilter || rsiFilterBullish)) && (!useSupportResistance || srFilterBullish)) && (!useTimeFilter || timeFilter)); - let bearishSignal = (((((bearishPattern && (!useTrendFilter || trendFilterBearish)) && (!useVolumeFilter || volumeFilter)) && (!useRSIFilter || rsiFilterBearish)) && (!useSupportResistance || srFilterBearish)) && (!useTimeFilter || timeFilter)); + var proximityPercent = (srProximity / 100); + var nearSupport = (!pinescript.na(state.lastSupport) && ((pinescript.abs((close - state.lastSupport)) / state.lastSupport) < proximityPercent)); + var nearResistance = (!pinescript.na(state.lastResistance) && ((pinescript.abs((close - state.lastResistance)) / state.lastResistance) < proximityPercent)); + var srFilterBullish = nearSupport; + var srFilterBearish = nearResistance; + var currentHour = pinescript.hour(time); + var currentMinute = pinescript.minute(time); + var firstHourAvoid = (avoidFirstHour && (currentHour === tradingStartHour)); + var lastHourAvoid = (avoidLastHour && (currentHour === tradingEndHour)); + var inTradingHours = ((currentHour >= tradingStartHour) && (currentHour <= tradingEndHour)); + var timeFilter = ((inTradingHours && !firstHourAvoid) && !lastHourAvoid); + var bullishEngulfing = ((((pinescript.hist(6, close, 1) < pinescript.hist(7, open, 1)) && (close > open)) && (open <= pinescript.hist(8, close, 1))) && (close >= pinescript.hist(9, open, 1))); + var bearishEngulfing = ((((pinescript.hist(10, close, 1) > pinescript.hist(11, open, 1)) && (close < open)) && (open >= pinescript.hist(12, close, 1))) && (close <= pinescript.hist(13, open, 1))); + var bodySize = pinescript.abs((close - open)); + var upperWick = (high - pinescript.max(close, open)); + var lowerWick = (pinescript.min(close, open) - low); + var lowestLow = pinescript.lowest(pinescript.series(14, low), 10); + var highestHigh = pinescript.highest(pinescript.series(15, high), 10); + var isHammer = ((((lowerWick > (bodySize * 2)) && (upperWick < bodySize)) && (close > open)) && (low === lowestLow)); + var isShootingStar = ((((upperWick > (bodySize * 2)) && (lowerWick < bodySize)) && (close < open)) && (high === highestHigh)); + var avgBody2 = pinescript.abs((pinescript.hist(16, close, 2) - pinescript.hist(17, open, 2))); + var isMorningStar = ((((pinescript.hist(18, close, 2) < pinescript.hist(19, open, 2)) && (pinescript.abs((pinescript.hist(20, close, 1) - pinescript.hist(21, open, 1))) < (avgBody2 * 0.3))) && (close > open)) && (close > ((pinescript.hist(22, open, 2) + pinescript.hist(23, close, 2)) / 2))); + var isEveningStar = ((((pinescript.hist(24, close, 2) > pinescript.hist(25, open, 2)) && (pinescript.abs((pinescript.hist(26, close, 1) - pinescript.hist(27, open, 1))) < (avgBody2 * 0.3))) && (close < open)) && (close < ((pinescript.hist(28, open, 2) + pinescript.hist(29, close, 2)) / 2))); + var candleRange = (high - low); + var avgRange = pinescript.sma(pinescript.series(30, (high - low)), 14); + var isDoji = ((pinescript.abs((close - open)) < (candleRange * 0.1)) && (candleRange > avgRange)); + var threeWhiteSoldiers = (((((((((pinescript.hist(31, close, 2) > pinescript.hist(32, open, 2)) && (pinescript.hist(33, close, 1) > pinescript.hist(34, open, 1))) && (close > open)) && (pinescript.hist(35, close, 1) > pinescript.hist(36, close, 2))) && (close > pinescript.hist(37, close, 1))) && (pinescript.hist(38, open, 1) > pinescript.hist(39, open, 2))) && (open > pinescript.hist(40, open, 1))) && (pinescript.hist(41, open, 1) < pinescript.hist(42, close, 2))) && (open < pinescript.hist(43, close, 1))); + var threeBlackCrows = (((((((((pinescript.hist(44, close, 2) < pinescript.hist(45, open, 2)) && (pinescript.hist(46, close, 1) < pinescript.hist(47, open, 1))) && (close < open)) && (pinescript.hist(48, close, 1) < pinescript.hist(49, close, 2))) && (close < pinescript.hist(50, close, 1))) && (pinescript.hist(51, open, 1) < pinescript.hist(52, open, 2))) && (open < pinescript.hist(53, open, 1))) && (pinescript.hist(54, open, 1) > pinescript.hist(55, close, 2))) && (open > pinescript.hist(56, close, 1))); + var piercingLine = (((((pinescript.hist(57, close, 1) < pinescript.hist(58, open, 1)) && (close > open)) && (open < pinescript.hist(59, close, 1))) && (close > ((pinescript.hist(60, open, 1) + pinescript.hist(61, close, 1)) / 2))) && (close < pinescript.hist(62, open, 1))); + var darkCloudCover = (((((pinescript.hist(63, close, 1) > pinescript.hist(64, open, 1)) && (close < open)) && (open > pinescript.hist(65, close, 1))) && (close < ((pinescript.hist(66, open, 1) + pinescript.hist(67, close, 1)) / 2))) && (close > pinescript.hist(68, open, 1))); + var bullishPattern = (((((bullishEngulfing || isHammer) || isMorningStar) || threeWhiteSoldiers) || piercingLine) && showBullishPatterns); + var bearishPattern = (((((bearishEngulfing || isShootingStar) || isEveningStar) || threeBlackCrows) || darkCloudCover) && showBearishPatterns); + var bullishSignal = (((((bullishPattern && (!useTrendFilter || trendFilterBullish)) && (!useVolumeFilter || volumeFilter)) && (!useRSIFilter || rsiFilterBullish)) && (!useSupportResistance || srFilterBullish)) && (!useTimeFilter || timeFilter)); + var bearishSignal = (((((bearishPattern && (!useTrendFilter || trendFilterBearish)) && (!useVolumeFilter || volumeFilter)) && (!useRSIFilter || rsiFilterBearish)) && (!useSupportResistance || srFilterBearish)) && (!useTimeFilter || timeFilter)); if (state.lastPattern === undefined) state.lastPattern = "Yok"; if (state.lastEntry === undefined) state.lastEntry = null; if (state.lastStop === undefined) state.lastStop = null; @@ -1800,7 +1959,7 @@ function main() { if (state.activeEntryLabel === undefined) state.activeEntryLabel = null; if (state.activeStopLabel === undefined) state.activeStopLabel = null; if (state.activeTpLabel === undefined) state.activeTpLabel = null; - let patternName = ""; + var patternName = ""; if ((bullishPattern || bearishPattern)) { state.totalSignalsBeforeFilter = 1; } @@ -1836,7 +1995,7 @@ function main() { if ((high >= state.lastTarget)) { state.totalTrades = 1; state.winningTrades = 1; - let profit = (state.lastTarget - state.lastEntry); + var profit = (state.lastTarget - state.lastEntry); state.totalProfit = profit; state.tradeActive = false; if (showTradeResults) { @@ -1846,7 +2005,7 @@ function main() { if ((low <= state.lastStop)) { state.totalTrades = 1; state.losingTrades = 1; - let loss = (state.lastEntry - state.lastStop); + var loss = (state.lastEntry - state.lastStop); state.totalLoss = loss; state.tradeActive = false; if (showTradeResults) { @@ -1880,9 +2039,9 @@ function main() { } } } - let winRate = ((state.totalTrades > 0) ? ((state.winningTrades / state.totalTrades) * 100) : 0); - let profitLossRatio = ((state.totalLoss > 0) ? (state.totalProfit / state.totalLoss) : 0); - let filterEfficiency = ((state.totalSignalsBeforeFilter > 0) ? (((state.totalSignalsBeforeFilter - state.totalSignalsAfterFilter) / state.totalSignalsBeforeFilter) * 100) : 0); + var winRate = ((state.totalTrades > 0) ? ((state.winningTrades / state.totalTrades) * 100) : 0); + var profitLossRatio = ((state.totalLoss > 0) ? (state.totalProfit / state.totalLoss) : 0); + var filterEfficiency = ((state.totalSignalsBeforeFilter > 0) ? (((state.totalSignalsBeforeFilter - state.totalSignalsAfterFilter) / state.totalSignalsBeforeFilter) * 100) : 0); pinescript.plotshape(bullishSignal, "Alış", pinescript.shape.triangleup, pinescript.location.belowbar, pinescript.color.green, ({ size: pinescript.size.small })); pinescript.plotshape(bearishSignal, "Satış", pinescript.shape.triangledown, pinescript.location.abovebar, pinescript.color.red, ({ size: pinescript.size.small })); pinescript.plot((useTrendFilter ? emaFast : null), "EMA Fast", pinescript.color.new(pinescript.color.blue, 0), ({ linewidth: 1 })); @@ -1915,10 +2074,10 @@ function main() { if (state.lastLabel === undefined) state.lastLabel = null; if ((bullishSignal || bearishSignal)) { pinescript.labelDelete(state.lastLabel); - let y_pos = (bullishSignal ? low : high); - let label_style = (bullishSignal ? label.style_label_up : label.style_label_down); - let label_color = (bullishSignal ? pinescript.color.new(pinescript.color.green, 70) : pinescript.color.new(pinescript.color.red, 70)); - let signal_text = (bullishSignal ? "ALIŞ" : "SATIŞ"); + var y_pos = (bullishSignal ? low : high); + var label_style = (bullishSignal ? label.style_label_up : label.style_label_down); + var label_color = (bullishSignal ? pinescript.color.new(pinescript.color.green, 70) : pinescript.color.new(pinescript.color.red, 70)); + var signal_text = (bullishSignal ? "ALIŞ" : "SATIŞ"); state.lastLabel = pinescript.labelNew(state.lastSignalIndex, y_pos, ((((((((signal_text + "n") + state.lastPattern) + "nGiriş: ") + pinescript.strToString(state.lastEntry, "#.####")) + "nSL: ") + pinescript.strToString(state.lastStop, "#.####")) + "nTP: ") + pinescript.strToString(state.lastTarget, "#.####")), ({ yloc: (bullishSignal ? yloc.belowbar : yloc.abovebar), color: label_color, textcolor: pinescript.color.white, style: label_style, size: pinescript.size.small })); } if (showTable) { @@ -1953,11 +2112,11 @@ function main() { pinescript.table.cell(state.t, 1, 9, pinescript.strToString(state.totalTrades), ({ text_color: pinescript.color.aqua })); pinescript.table.cell(state.t, 2, 9, "📊", ({ text_color: pinescript.color.white })); pinescript.table.cell(state.t, 0, 10, "🎯 WINRATE", ({ text_color: pinescript.color.white, text_size: pinescript.size.normal })); - let winRateColor = ((winRate >= 70) ? pinescript.color.new(pinescript.color.green, 0) : ((winRate >= 50) ? pinescript.color.orange : pinescript.color.red)); + var winRateColor = ((winRate >= 70) ? pinescript.color.new(pinescript.color.green, 0) : ((winRate >= 50) ? pinescript.color.orange : pinescript.color.red)); pinescript.table.cell(state.t, 1, 10, (pinescript.strToString(winRate, "#.##") + "%"), ({ text_color: winRateColor, text_size: pinescript.size.normal })); pinescript.table.cell(state.t, 2, 10, (((pinescript.strToString(state.winningTrades) + "W/") + pinescript.strToString(state.losingTrades)) + "L"), ({ text_color: pinescript.color.orange, text_size: pinescript.size.tiny })); pinescript.table.cell(state.t, 0, 11, "Kar/Zarar Oranı", ({ text_color: pinescript.color.white })); - let plRatioColor = ((profitLossRatio >= 2) ? pinescript.color.new(pinescript.color.green, 0) : ((profitLossRatio >= 1) ? pinescript.color.orange : pinescript.color.red)); + var plRatioColor = ((profitLossRatio >= 2) ? pinescript.color.new(pinescript.color.green, 0) : ((profitLossRatio >= 1) ? pinescript.color.orange : pinescript.color.red)); pinescript.table.cell(state.t, 1, 11, pinescript.strToString(profitLossRatio, "#.##"), ({ text_color: plRatioColor })); pinescript.table.cell(state.t, 2, 11, ((profitLossRatio >= 1) ? "✓" : "✗"), ({ text_color: plRatioColor })); pinescript.table.cell(state.t, 0, 12, "━━━━━ FİLTRELER ━━━━━", ({ text_color: pinescript.color.purple })); @@ -1969,7 +2128,7 @@ function main() { pinescript.table.cell(state.t, 1, 14, pinescript.strToString(state.totalSignalsAfterFilter), ({ text_color: pinescript.color.green })); pinescript.table.cell(state.t, 2, 14, "✓", ({ text_color: pinescript.color.green })); pinescript.table.cell(state.t, 0, 15, "Filtre Etkinliği", ({ text_color: pinescript.color.white })); - let filterColor = ((filterEfficiency >= 60) ? pinescript.color.green : ((filterEfficiency >= 40) ? pinescript.color.orange : pinescript.color.red)); + var filterColor = ((filterEfficiency >= 60) ? pinescript.color.green : ((filterEfficiency >= 40) ? pinescript.color.orange : pinescript.color.red)); pinescript.table.cell(state.t, 1, 15, (pinescript.strToString(filterEfficiency, "#.#") + "%"), ({ text_color: filterColor })); pinescript.table.cell(state.t, 2, 15, ((filterEfficiency >= 50) ? "🔥" : "⚡"), ({ text_color: filterColor })); } @@ -2019,6 +2178,10 @@ function run(data, options = {}) { rt.__shapeIdx = 0; globalThis.bar_index = i; globalThis.last_bar_index = n - 1; + globalThis.time_tradingday = inTime[i]; + globalThis.time_close = inTime[i]; + globalThis.last_bar_time = inTime[n - 1]; + globalThis.timenow = inTime[n - 1]; globalThis.barstate = { isfirst: i === 0, islast: i === n - 1, isrealtime: false, ishistory: true, isconfirmed: true, isnew: true, islastconfirmedhistory: i === n - 1, diff --git a/converted/Proximal_Range_Filter_LuxAlgo.js b/converted/Proximal_Range_Filter_LuxAlgo.js index 8d7b327..ee90712 100644 --- a/converted/Proximal_Range_Filter_LuxAlgo.js +++ b/converted/Proximal_Range_Filter_LuxAlgo.js @@ -42,6 +42,80 @@ const pinescript = { if (value != null && typeof value[Symbol.iterator] === 'function') return Array.from(value); return new Array(count || 0).fill(null); }, + __decArr: function(arr) { + if (!Array.isArray(arr) || arr.__pineDecorated) return arr; + const self = this; + const def = (name, fn) => Object.defineProperty(arr, name, { value: fn, writable: true, configurable: true }); + Object.defineProperty(arr, '__pineDecorated', { value: true, configurable: true }); + def('get', (i) => (arr[i] === undefined ? null : arr[i])); + def('set', (i, v) => { arr[i] = v; return v; }); + def('size', () => arr.length); + def('clear', () => { arr.length = 0; }); + def('insert', (i, v) => { arr.splice(i, 0, v); }); + def('remove', (i) => arr.splice(i, 1)[0]); + def('contains', (v) => arr.includes(v)); + def('indexof', (v) => arr.indexOf(v)); + def('lastindexof', (v) => arr.lastIndexOf(v)); + def('first', () => (arr.length ? arr[0] : null)); + def('last', () => (arr.length ? arr[arr.length - 1] : null)); + def('sum', () => arr.reduce((a, b) => a + b, 0)); + def('avg', () => (arr.length ? arr.reduce((a, b) => a + b, 0) / arr.length : 0)); + def('min', () => (arr.length ? Math.min(...arr) : null)); + def('max', () => (arr.length ? Math.max(...arr) : null)); + def('range', () => (arr.length ? Math.max(...arr) - Math.min(...arr) : null)); + // Statistical methods that route to the array.* built-ins (these names are not + // native to JS arrays, so attaching them here is safe from recursion). + def('median', () => self.arrayMedian(arr)); + def('mode', () => self.arrayMode(arr)); + def('stdev', () => self.arrayStdev(arr)); + def('variance', () => self.arrayVariance(arr)); + def('covariance', (other) => self.arrayCovariance(arr, other)); + def('percentile_linear_interpolation', (p) => self.arrayPercentileLinearInterpolation(arr, p)); + def('percentile_nearest_rank', (p) => self.arrayPercentileNearestRank(arr, p)); + def('abs', () => self.__decArr(self.arrayAbs(arr))); + def('binary_search', (v) => self.arrayBinarySearch(arr, v)); + // Pine's array.sort takes an order string, not a comparator. Use the native + // sort via .call so we don't recurse through this overridden method. + def('sort', (order) => { Array.prototype.sort.call(arr, (a, b) => (order === 'descending' ? b - a : a - b)); return arr; }); + def('sort_indices', (order) => self.__decArr(arr.map((_, i) => i).sort((a, b) => (order === 'descending' ? arr[b] - arr[a] : arr[a] - arr[b])))); + // join/slice/reverse/concat/includes/fill already exist natively on Array with + // compatible semantics, so we deliberately leave them to the native methods. + return arr; + }, + __decMap: function(m) { + if (!(m instanceof Map) || m.__pineDecorated) return m; + const def = (name, fn) => Object.defineProperty(m, name, { value: fn, writable: true, configurable: true }); + Object.defineProperty(m, '__pineDecorated', { value: true, configurable: true }); + def('put', (k, v) => { m.set(k, v); return v; }); + def('contains', (k) => m.has(k)); + def('remove', (k) => m.delete(k)); + def('keys', () => Array.from(Map.prototype.keys.call(m))); + def('values', () => Array.from(Map.prototype.values.call(m))); + def('size_', () => m.size); + return m; + }, + __decMatrix: function(m) { + if (!m || typeof m !== 'object' || m.__pineDecorated) return m; + const self = this; + const def = (name, fn) => Object.defineProperty(m, name, { value: fn, writable: true, configurable: true }); + Object.defineProperty(m, '__pineDecorated', { value: true, configurable: true }); + def('get', (r, c) => self.matrixGet(m, r, c)); + def('set', (r, c, v) => self.matrixSet(m, r, c, v)); + def('rows_', () => m.rows); + def('columns', () => m.cols); + def('fill', (v) => self.matrixFill(m, v)); + return m; + }, + __decDraw: function(obj) { + if (!obj || typeof obj !== 'object') return obj; + const p = new Proxy(obj, { + get(t, k) { + if (k in t || typeof k === 'symbol') return t[k]; + return function() { return p; }; + }, + }); + return p; + }, alertcondition: function(condition, ...rest) { if (globalThis.__pineRuntime) { globalThis.__pineRuntime.alerts.push({ condition, args: rest }); @@ -49,6 +123,32 @@ const pinescript = { return null; }, barcolor: function(color) { return null; }, + plotchar: function(series, ...rest) { + const rt = globalThis.__pineRuntime; + if (!rt) return series; + const bar = rt.__barIndex | 0; + const ord = (rt.__shapeIdx = (rt.__shapeIdx | 0) + 1) - 1; + let key = 'char_' + ord; + for (const r of rest) { + if (typeof r === 'string') { key = r; break; } + if (r && typeof r === 'object' && r.title) { key = String(r.title); break; } + } + let s = rt.plotshapes[key]; + if (!s) s = rt.plotshapes[key] = { title: key, data: [] }; + s.data[bar] = this.__scalar(series); + return series; + }, + plotarrow: function(series, ...rest) { + const rt = globalThis.__pineRuntime; + if (!rt) return series; + const bar = rt.__barIndex | 0; + const ord = (rt.__plotIdx = (rt.__plotIdx | 0) + 1) - 1; + const key = 'arrow_' + ord; + let p = rt.plots[key]; + if (!p) p = rt.plots[key] = { title: key, data: [] }; + p.data[bar] = this.__scalar(series); + return series; + }, bgcolor: function(color, title, editable, showLast) { return null; }, @@ -470,6 +570,12 @@ const pinescript = { atan: function(value) { return Math.atan(value); }, + todegrees: function(radians) { + return radians * (180 / Math.PI); + }, + toradians: function(degrees) { + return degrees * (Math.PI / 180); + }, floor: function(value) { return Math.floor(value); }, @@ -641,7 +747,7 @@ const pinescript = { return series; }, lineNew: function(x1, y1, x2, y2, opts = {}) { - return { x1, y1, x2, y2, opts, _type: 'line' }; + return this.__decDraw({ x1, y1, x2, y2, opts, _type: 'line' }); }, lineDelete: function(l) { return null; @@ -661,7 +767,7 @@ const pinescript = { return point === 0 || point === 'y1' ? line.y1 : line.y2; }, labelNew: function(x, y, text = '', opts = {}) { - return { x, y, text, opts, _type: 'label' }; + return this.__decDraw({ x, y, text, opts, _type: 'label' }); }, labelDelete: function(l) { return null; @@ -676,7 +782,7 @@ const pinescript = { return label.text || ''; }, boxNew: function(left, top, right, bottom, opts = {}) { - return { left, top, right, bottom, opts, _type: 'box' }; + return this.__decDraw({ left, top, right, bottom, opts, _type: 'box' }); }, boxDelete: function(box) { return null; @@ -694,7 +800,7 @@ const pinescript = { return box; }, polylineNew: function(points, opts = {}) { - return { points: points || [], opts, _type: 'polyline' }; + return this.__decDraw({ points: points || [], opts, _type: 'polyline' }); }, polylineDelete: function(poly) { return null; @@ -765,7 +871,7 @@ const pinescript = { return { time: _time ?? null, price: _price ?? null }; }, mapNew: function() { - return new Map(); + return this.__decMap(new Map()); }, mapSize: function(m) { return m instanceof Map ? m.size : 0; @@ -800,7 +906,7 @@ const pinescript = { const r = Math.max(0, rows ?? 0); const c = Math.max(0, cols ?? 0); const data = Array.from({ length: r }, () => Array.from({ length: c }, () => initialValue)); - return { rows: r, cols: c, data }; + return this.__decMatrix({ rows: r, cols: c, data }); }, matrixRows: function(m) { return m?.rows ?? 0; @@ -923,11 +1029,62 @@ const pinescript = { } return null; }, + __jacobiEigen: function(m) { + if (!m || !Array.isArray(m.data)) return null; + const n = m.rows ?? 0; + if (n === 0 || n !== (m.cols ?? 0)) return null; + // Work on a copy so the input matrix is untouched. + const a = m.data.map(row => row.slice()); + const v = Array.from({ length: n }, (_, i) => Array.from({ length: n }, (_, j) => (i === j ? 1 : 0))); + for (let sweep = 0; sweep < 100; sweep++) { + let off = 0; + for (let p = 0; p < n; p++) for (let q = p + 1; q < n; q++) off += a[p][q] * a[p][q]; + if (off < 1e-20) break; + for (let p = 0; p < n; p++) { + for (let q = p + 1; q < n; q++) { + if (Math.abs(a[p][q]) < 1e-18) continue; + const theta = (a[q][q] - a[p][p]) / (2 * a[p][q]); + const t = Math.sign(theta || 1) / (Math.abs(theta) + Math.sqrt(theta * theta + 1)); + const cos = 1 / Math.sqrt(t * t + 1); + const sin = t * cos; + for (let k = 0; k < n; k++) { + const akp = a[k][p], akq = a[k][q]; + a[k][p] = cos * akp - sin * akq; + a[k][q] = sin * akp + cos * akq; + } + for (let k = 0; k < n; k++) { + const apk = a[p][k], aqk = a[q][k]; + a[p][k] = cos * apk - sin * aqk; + a[q][k] = sin * apk + cos * aqk; + } + for (let k = 0; k < n; k++) { + const vkp = v[k][p], vkq = v[k][q]; + v[k][p] = cos * vkp - sin * vkq; + v[k][q] = sin * vkp + cos * vkq; + } + } + } + } + // Sort eigenpairs by eigenvalue, descending. + const order = Array.from({ length: n }, (_, i) => i).sort((i, j) => a[j][j] - a[i][i]); + const values = order.map(i => a[i][i]); + const vectors = Array.from({ length: n }, (_, r) => order.map(c => v[r][c])); + return { values, vectors }; + }, + matrixEigenvalues: function(m) { + const e = this.__jacobiEigen(m); + return this.__decArr(e ? e.values : []); + }, + matrixEigenvectors: function(m) { + const e = this.__jacobiEigen(m); + if (!e) return this.__decMatrix({ rows: 0, cols: 0, data: [] }); + return this.__decMatrix({ rows: e.vectors.length, cols: e.vectors.length, data: e.vectors }); + }, requestSecurity: function(symbol, timeframe, expression) { return expression; }, arrayNew: function(initialSize = 0, initialValue = 0) { - return Array(initialSize).fill(initialValue); + return this.__decArr(Array(initialSize).fill(initialValue)); }, arraySize: function(arr) { return arr ? arr.length : 0; @@ -978,10 +1135,10 @@ const pinescript = { return arr; }, arraySlice: function(arr, startIndex = 0, endIndex = null) { - if (!arr) return []; + if (!arr) return this.__decArr([]); const start = Number(startIndex) || 0; const end = endIndex === null || endIndex === undefined ? arr.length : Number(endIndex) || 0; - return arr.slice(start, end); + return this.__decArr(arr.slice(start, end)); }, arraySort: function(arr, order = 'ascending') { if (arr) arr.sort((a, b) => order === 'ascending' ? a - b : b - a); @@ -1191,7 +1348,7 @@ const pinescript = { const info = { ticker: 'AAPL', tickerid: 'NASDAQ:AAPL', prefix: 'NASDAQ', root: 'AAPL', suffix: '' }; return info[type] || ''; }, - timenow: 1781574528000, + timenow: 1782783577115, barstate: "LAST", dividends: {}, splits: {}, @@ -1245,11 +1402,11 @@ const pinescript = { arrayConcat: function(arr1, arr2) { if (!arr1) return arr2 || []; if (!arr2) return arr1; - return arr1.concat(arr2); + return this.__decArr(arr1.concat(arr2)); }, arrayCopy: function(arr) { - if (!arr) return []; - return [...arr]; + if (!arr) return this.__decArr([]); + return this.__decArr([...arr]); }, arrayBinarySearch: function(arr, value) { if (!arr || arr.length === 0) return -1; @@ -1339,7 +1496,7 @@ globalThis.input.timeframe = globalThis.input.timeframe || ((defval) => defval); globalThis.array = globalThis.array || { - from: (...items) => items, + from: (...items) => pinescript.__decArr(items), size: (arr) => pinescript.arraySize(arr), @@ -1508,6 +1665,8 @@ globalThis.line = globalThis.line || __pineNS({ style_solid: "solid", style_dash globalThis.box = globalThis.box || __pineNS({}); +globalThis.color = globalThis.color || __pineNS(Object.assign(function(c) { return c; }, { new: function(c, t) { return c; }, rgb: function(r, g, b, t) { return "#rgb(" + [r, g, b].join(",") + ")"; }, from_gradient: function(v, lo, hi, c1, c2) { return c1; }, r: function() { return 0; }, g: function() { return 0; }, b: function() { return 0; }, t: function() { return 0; }, aqua: "#00BCD4", black: "#363A45", blue: "#2962FF", fuchsia: "#E040FB", gray: "#787B86", green: "#4CAF50", lime: "#00E676", maroon: "#880E4F", navy: "#311B92", olive: "#808000", orange: "#FF9800", purple: "#9C27B0", red: "#FF5252", silver: "#B2B5BE", teal: "#00897B", white: "#FFFFFF", yellow: "#FFEB3B" })); + globalThis.label = globalThis.label || __pineNS({ style_label_down: "label_down", style_label_up: "label_up", style_none: "none" }); globalThis.polyline = globalThis.polyline || __pineNS({}); @@ -1520,7 +1679,9 @@ globalThis.map = globalThis.map || __pineNS({}); globalThis.session = globalThis.session || __pineNS({ regular: "regular", extended: "extended" }); -globalThis.dayofweek = globalThis.dayofweek || __pineNS({ sunday: 1, monday: 2, tuesday: 3, wednesday: 4, thursday: 5, friday: 6, saturday: 7 }); +globalThis.ticker = globalThis.ticker || __pineNS({}); + +globalThis.dayofweek = globalThis.dayofweek || __pineNS(Object.assign(function(t) { return new Date(t != null ? t : (globalThis.time || 0)).getUTCDay() + 1; }, { sunday: 1, monday: 2, tuesday: 3, wednesday: 4, thursday: 5, friday: 6, saturday: 7 })); globalThis.timeframe = __pineNS(Object.assign(globalThis.timeframe || {}, { period: (globalThis.timeframe && globalThis.timeframe.period) || "D", isintraday: false, isdaily: true, multiplier: 1 })); @@ -1568,8 +1729,6 @@ globalThis.bool = globalThis.bool || function(x) { return Boolean(x); }; globalThis.string = globalThis.string || function(x) { return x == null ? null : String(x); }; -globalThis.dayofweek = globalThis.dayofweek || __pineNS({ sunday: 1, monday: 2, tuesday: 3, wednesday: 4, thursday: 5, friday: 6, saturday: 7 }); - globalThis.str = globalThis.str || __pineNS({}); pinescript.math = globalThis.math; @@ -1655,7 +1814,7 @@ pinescript.text = { align_center: "center" }; pinescript.table = { - new: function(position, columns, rows, opts) { return { position, columns, rows, opts: opts || {}, cells: [] }; }, + new: function(position, columns, rows, opts) { return pinescript.__decDraw({ position, columns, rows, opts: opts || {}, cells: [] }); }, cell: function(table, column, row, text, opts) { @@ -1688,22 +1847,22 @@ function main() { null; // Study: L1 Proximal Filter [LuxAlgo] // Options: {"overlay":true} - let BULL_COLOR = pinescript.color.hex("#089981"); - let BEAR_COLOR = pinescript.color.hex("#f23645"); - let srcInput = pinescript.inputSource(close, "Source"); - let atrMultInput = pinescript.inputFloat(1.5, "ATR Multiplier", ({ minval: 0, step: 0.1, tooltip: "Defines the noise threshold by multiplying the 200-period ATR." })); - let muInput = pinescript.inputFloat(0.6, "Adaptation Rate (μ)", ({ minval: 0, maxval: 1, step: 0.1, tooltip: "Controls how fast the filter adapts to price changes." })); - let threshold = (pinescript.atr(200) * atrMultInput); + var BULL_COLOR = pinescript.color.hex("#089981"); + var BEAR_COLOR = pinescript.color.hex("#f23645"); + var srcInput = pinescript.inputSource(close, "Source"); + var atrMultInput = pinescript.inputFloat(1.5, "ATR Multiplier", ({ minval: 0, step: 0.1, tooltip: "Defines the noise threshold by multiplying the 200-period ATR." })); + var muInput = pinescript.inputFloat(0.6, "Adaptation Rate (μ)", ({ minval: 0, maxval: 1, step: 0.1, tooltip: "Controls how fast the filter adapts to price changes." })); + var threshold = (pinescript.atr(200) * atrMultInput); if (state.z === undefined) state.z = null; if (state.v === undefined) state.v = 0; if ((bar_index === 0)) { state.z = srcInput; } else { - let zPrev = pinescript.hist(0, state.z, 1); - let vPrev = pinescript.hist(1, state.v, 1); - let zPred = (zPrev + vPrev); - let zTemp = (zPred + (muInput * (srcInput - zPred))); - let diff = (zTemp - zPrev); + var zPrev = pinescript.hist(0, state.z, 1); + var vPrev = pinescript.hist(1, state.v, 1); + var zPred = (zPrev + vPrev); + var zTemp = (zPred + (muInput * (srcInput - zPred))); + var diff = (zTemp - zPrev); if ((pinescript.abs(diff) > threshold)) { state.v = (pinescript.sign(diff) * (pinescript.abs(diff) - threshold)); } else { @@ -1719,17 +1878,17 @@ function main() { state.trend = -1; } } - let upChange = ((state.trend === 1) && (pinescript.hist(4, state.trend, 1) === -1)); - let downChange = ((state.trend === -1) && (pinescript.hist(5, state.trend, 1) === 1)); - let zColor = ((state.trend === 1) ? BULL_COLOR : BEAR_COLOR); - let zPlot = pinescript.plot(state.z, "L1 Proximal Filter", ({ color: zColor, linewidth: 3 })); - let srcPlot = pinescript.plot(srcInput, "Source Plot", ({ color: null })); - let showFill = (((state.trend === 1) && (srcInput > state.z)) || ((state.trend === -1) && (srcInput < state.z))); - let fillTopColor = (showFill ? pinescript.color.new(zColor, 50) : null); - let fillBottomColor = (showFill ? pinescript.color.new(zColor, 100) : null); + var upChange = ((state.trend === 1) && (pinescript.hist(4, state.trend, 1) === -1)); + var downChange = ((state.trend === -1) && (pinescript.hist(5, state.trend, 1) === 1)); + var zColor = ((state.trend === 1) ? BULL_COLOR : BEAR_COLOR); + var zPlot = pinescript.plot(state.z, "L1 Proximal Filter", ({ color: zColor, linewidth: 3 })); + var srcPlot = pinescript.plot(srcInput, "Source Plot", ({ color: null })); + var showFill = (((state.trend === 1) && (srcInput > state.z)) || ((state.trend === -1) && (srcInput < state.z))); + var fillTopColor = (showFill ? pinescript.color.new(zColor, 50) : null); + var fillBottomColor = (showFill ? pinescript.color.new(zColor, 100) : null); pinescript.fill(srcPlot, zPlot, srcInput, state.z, fillTopColor, fillBottomColor, "Trend Fill"); - let switchVal = ((upChange || downChange) ? pinescript.hist(6, state.z, 1) : null); - let switchColor = (upChange ? BULL_COLOR : BEAR_COLOR); + var switchVal = ((upChange || downChange) ? pinescript.hist(6, state.z, 1) : null); + var switchColor = (upChange ? BULL_COLOR : BEAR_COLOR); pinescript.plot(switchVal, "Trend Switch Dot", ({ color: switchColor, style: plot.style_circles, linewidth: 4, offset: -1 })); } @@ -1775,6 +1934,10 @@ function run(data, options = {}) { rt.__shapeIdx = 0; globalThis.bar_index = i; globalThis.last_bar_index = n - 1; + globalThis.time_tradingday = inTime[i]; + globalThis.time_close = inTime[i]; + globalThis.last_bar_time = inTime[n - 1]; + globalThis.timenow = inTime[n - 1]; globalThis.barstate = { isfirst: i === 0, islast: i === n - 1, isrealtime: false, ishistory: true, isconfirmed: true, isnew: true, islastconfirmedhistory: i === n - 1, diff --git a/converted/Quantum_Structure_jjart.err.txt b/converted/Quantum_Structure_jjart.err.txt deleted file mode 100644 index 73e233d..0000000 --- a/converted/Quantum_Structure_jjart.err.txt +++ /dev/null @@ -1,4 +0,0 @@ -Error: Unexpected token ")" at line 489, column 143 while parsing if statement - -Command failed: node src/cli.js "examples\Quantum_Structure_jjart.pine" "converted\Quantum_Structure_jjart.js" -Error: Unexpected token ")" at line 489, column 143 while parsing if statement diff --git a/converted/Quantum_Structure_jjart.js b/converted/Quantum_Structure_jjart.js new file mode 100644 index 0000000..9621667 --- /dev/null +++ b/converted/Quantum_Structure_jjart.js @@ -0,0 +1,3165 @@ +// Auto-generated JavaScript from PineScript source + +// Do not edit by hand -- re-run the converter instead + + +// All the built-in PineScript functions that scripts depend on at runtime + +const pinescript = { + + __scalar: function(v) { + if (v === null || v === undefined) return v; + if (typeof v === 'object' && typeof v.valueOf === 'function') { + const s = v.valueOf(); + if (typeof s === 'number' || typeof s === 'boolean' || s === null) return s; + } + return v; + }, + series: function(id, value) { + const rt = this.__rt || (this.__rt = {}); + const store = rt.series || (rt.series = {}); + let buf = store[id]; + if (!buf) buf = store[id] = []; + buf[this.__bar | 0] = this.__scalar(value); + return buf; + }, + hist: function(id, value, n) { + const buf = this.series(id, value); + const idx = (this.__bar | 0) - (n | 0); + return idx >= 0 && idx < buf.length ? buf[idx] : null; + }, + time: function(timeframe, session) { + const t = globalThis.time; + if (t && typeof t.valueOf === 'function') return t.valueOf(); + return Array.isArray(t) ? t[t.length - 1] : t; + }, + sign: function(x) { + if (x === null || x === undefined || (typeof x === 'number' && isNaN(x))) return null; + return Math.sign(Number(x)); + }, + unpack: function(value, count) { + if (Array.isArray(value)) return value; + if (value != null && typeof value[Symbol.iterator] === 'function') return Array.from(value); + return new Array(count || 0).fill(null); + }, + __decArr: function(arr) { + if (!Array.isArray(arr) || arr.__pineDecorated) return arr; + const self = this; + const def = (name, fn) => Object.defineProperty(arr, name, { value: fn, writable: true, configurable: true }); + Object.defineProperty(arr, '__pineDecorated', { value: true, configurable: true }); + def('get', (i) => (arr[i] === undefined ? null : arr[i])); + def('set', (i, v) => { arr[i] = v; return v; }); + def('size', () => arr.length); + def('clear', () => { arr.length = 0; }); + def('insert', (i, v) => { arr.splice(i, 0, v); }); + def('remove', (i) => arr.splice(i, 1)[0]); + def('contains', (v) => arr.includes(v)); + def('indexof', (v) => arr.indexOf(v)); + def('lastindexof', (v) => arr.lastIndexOf(v)); + def('first', () => (arr.length ? arr[0] : null)); + def('last', () => (arr.length ? arr[arr.length - 1] : null)); + def('sum', () => arr.reduce((a, b) => a + b, 0)); + def('avg', () => (arr.length ? arr.reduce((a, b) => a + b, 0) / arr.length : 0)); + def('min', () => (arr.length ? Math.min(...arr) : null)); + def('max', () => (arr.length ? Math.max(...arr) : null)); + def('range', () => (arr.length ? Math.max(...arr) - Math.min(...arr) : null)); + // Statistical methods that route to the array.* built-ins (these names are not + // native to JS arrays, so attaching them here is safe from recursion). + def('median', () => self.arrayMedian(arr)); + def('mode', () => self.arrayMode(arr)); + def('stdev', () => self.arrayStdev(arr)); + def('variance', () => self.arrayVariance(arr)); + def('covariance', (other) => self.arrayCovariance(arr, other)); + def('percentile_linear_interpolation', (p) => self.arrayPercentileLinearInterpolation(arr, p)); + def('percentile_nearest_rank', (p) => self.arrayPercentileNearestRank(arr, p)); + def('abs', () => self.__decArr(self.arrayAbs(arr))); + def('binary_search', (v) => self.arrayBinarySearch(arr, v)); + // Pine's array.sort takes an order string, not a comparator. Use the native + // sort via .call so we don't recurse through this overridden method. + def('sort', (order) => { Array.prototype.sort.call(arr, (a, b) => (order === 'descending' ? b - a : a - b)); return arr; }); + def('sort_indices', (order) => self.__decArr(arr.map((_, i) => i).sort((a, b) => (order === 'descending' ? arr[b] - arr[a] : arr[a] - arr[b])))); + // join/slice/reverse/concat/includes/fill already exist natively on Array with + // compatible semantics, so we deliberately leave them to the native methods. + return arr; + }, + __decMap: function(m) { + if (!(m instanceof Map) || m.__pineDecorated) return m; + const def = (name, fn) => Object.defineProperty(m, name, { value: fn, writable: true, configurable: true }); + Object.defineProperty(m, '__pineDecorated', { value: true, configurable: true }); + def('put', (k, v) => { m.set(k, v); return v; }); + def('contains', (k) => m.has(k)); + def('remove', (k) => m.delete(k)); + def('keys', () => Array.from(Map.prototype.keys.call(m))); + def('values', () => Array.from(Map.prototype.values.call(m))); + def('size_', () => m.size); + return m; + }, + __decMatrix: function(m) { + if (!m || typeof m !== 'object' || m.__pineDecorated) return m; + const self = this; + const def = (name, fn) => Object.defineProperty(m, name, { value: fn, writable: true, configurable: true }); + Object.defineProperty(m, '__pineDecorated', { value: true, configurable: true }); + def('get', (r, c) => self.matrixGet(m, r, c)); + def('set', (r, c, v) => self.matrixSet(m, r, c, v)); + def('rows_', () => m.rows); + def('columns', () => m.cols); + def('fill', (v) => self.matrixFill(m, v)); + return m; + }, + __decDraw: function(obj) { + if (!obj || typeof obj !== 'object') return obj; + const p = new Proxy(obj, { + get(t, k) { + if (k in t || typeof k === 'symbol') return t[k]; + return function() { return p; }; + }, + }); + return p; + }, + alertcondition: function(condition, ...rest) { + if (globalThis.__pineRuntime) { + globalThis.__pineRuntime.alerts.push({ condition, args: rest }); + } + return null; + }, + barcolor: function(color) { return null; }, + plotchar: function(series, ...rest) { + const rt = globalThis.__pineRuntime; + if (!rt) return series; + const bar = rt.__barIndex | 0; + const ord = (rt.__shapeIdx = (rt.__shapeIdx | 0) + 1) - 1; + let key = 'char_' + ord; + for (const r of rest) { + if (typeof r === 'string') { key = r; break; } + if (r && typeof r === 'object' && r.title) { key = String(r.title); break; } + } + let s = rt.plotshapes[key]; + if (!s) s = rt.plotshapes[key] = { title: key, data: [] }; + s.data[bar] = this.__scalar(series); + return series; + }, + plotarrow: function(series, ...rest) { + const rt = globalThis.__pineRuntime; + if (!rt) return series; + const bar = rt.__barIndex | 0; + const ord = (rt.__plotIdx = (rt.__plotIdx | 0) + 1) - 1; + const key = 'arrow_' + ord; + let p = rt.plots[key]; + if (!p) p = rt.plots[key] = { title: key, data: [] }; + p.data[bar] = this.__scalar(series); + return series; + }, + bgcolor: function(color, title, editable, showLast) { + return null; + }, + fill: function(series1, series2, color, title, editable) { + return null; + }, + hline: function(_price, _title, _opts) { + return null; + }, + sma: function(series, length) { + if (series === null || series === undefined) return null; + if (!Array.isArray(series)) series = [series]; + if (!series || series.length < length) return null; + const slice = series.slice(-length); + return slice.reduce((a, b) => a + b, 0) / length; + }, + ema: function(series, length) { + if (series === null || series === undefined) return null; + if (!Array.isArray(series)) series = [series]; + if (!series || series.length < 1) return null; + const alpha = 2 / (length + 1); + let result = series[0]; + for (let i = 1; i < series.length; i++) { + result = alpha * series[i] + (1 - alpha) * result; + } + return result; + }, + wma: function(series, length) { + if (series === null || series === undefined) return null; + if (!Array.isArray(series)) series = [series]; + if (!series || series.length < length) return null; + let sum = 0; + let weightSum = 0; + const slice = series.slice(-length); + for (let i = 0; i < length; i++) { + const weight = length - i; + sum += slice[i] * weight; + weightSum += weight; + } + return sum / weightSum; + }, + vwma: function(close, volume, length) { + if (!close || close.length < length) return null; + let sum = 0; + let volSum = 0; + for (let i = length - 1; i >= 0; i--) { + sum += close[close.length - 1 - i] * volume[volume.length - 1 - i]; + volSum += volume[volume.length - 1 - i]; + } + return volSum > 0 ? sum / volSum : null; + }, + rma: function(series, length) { + if (series === null || series === undefined) return null; + if (!Array.isArray(series)) series = [series]; + if (!series || series.length < 1) return null; + const alpha = 1 / length; + let result = series[0]; + for (let i = 1; i < series.length; i++) { + result = alpha * series[i] + (1 - alpha) * result; + } + return result; + }, + hma: function(series, length) { + if (series === null || series === undefined) return null; + if (!Array.isArray(series)) series = [series]; + if (!series || series.length < length) return null; + const halfLength = Math.floor(length / 2); + const wma1 = builtins.get('wma')(series, halfLength); + const wma2 = builtins.get('wma')(series, length); + const diff = wma2 - wma1; + return builtins.get('wma')([diff, diff], Math.floor(Math.sqrt(length))); + }, + alma: function(series, length, offset = 0.85, sigma = 6) { + if (series === null || series === undefined) return null; + if (!Array.isArray(series)) series = [series]; + if (!series || series.length < length) return null; + const slice = series.slice(-length); + let sum = 0; + let weightSum = 0; + for (let i = 0; i < length; i++) { + const weight = Math.exp(-Math.pow(i - offset * (length - 1), 2) / (2 * Math.pow(sigma, 2))); + sum += slice[i] * weight; + weightSum += weight; + } + return weightSum > 0 ? sum / weightSum : null; + }, + swma: function(series) { + if (series === null || series === undefined) return null; + if (!Array.isArray(series)) series = [series]; + if (series.length < 4) return null; + const s = series.slice(-4); + return (s[0] * 1 + s[1] * 2 + s[2] * 2 + s[3] * 1) / 6; + }, + atr: function(high, low, close, length = 14) { + if (!Array.isArray(high) || !Array.isArray(low) || !Array.isArray(close)) return null; + if (high.length < length + 1 || low.length < length + 1 || close.length < length + 1) return null; + let sum = 0; + for (let i = 1; i <= length; i++) { + const prevClose = close[close.length - i - 1]; + const tr = Math.max( + high[high.length - i] - low[low.length - i], + Math.abs(high[high.length - i] - prevClose), + Math.abs(low[low.length - i] - prevClose) + ); + sum += tr; + } + return sum / length; + }, + bb: function(source, length, mult = 2) { + if (!source || source.length < length) return null; + const sma = builtins.get('sma')(source, length); + const slice = source.slice(-length); + const variance = slice.reduce((sum, val) => sum + Math.pow(val - sma, 2), 0) / length; + const stdDev = Math.sqrt(variance); + return { middle: sma, upper: sma + mult * stdDev, lower: sma - mult * stdDev }; + }, + kc: function(high, low, close, length = 20, mult = 2) { + if (!high || high.length < length) return null; + const typicalPrice = high.map((h, i) => (h + low[i] + close[i]) / 3); + const sma = builtins.get('sma')(typicalPrice, length); + const atr = builtins.get('atr')(high, low, close, length); + return { middle: sma, upper: sma + mult * atr, lower: sma - mult * atr }; + }, + supertrend: function(factor, atrPeriod, high, low, close) { + if (!Array.isArray(high) || !Array.isArray(low) || !Array.isArray(close)) return [null, null]; + if (high.length < atrPeriod + 1) return [null, null]; + const atrVal = builtins.get('atr')(high, low, close, atrPeriod); + if (atrVal === null) return [null, null]; + const lastIdx = close.length - 1; + const hl2 = (high[lastIdx] + low[lastIdx]) / 2; + const upperBand = hl2 + factor * atrVal; + const lowerBand = hl2 - factor * atrVal; + // Determine direction: 1 means downtrend (price below band), -1 means uptrend + const direction = close[lastIdx] > upperBand ? -1 : close[lastIdx] < lowerBand ? 1 : -1; + const supertrendValue = direction === -1 ? lowerBand : upperBand; + return [supertrendValue, direction]; + }, + ta_supertrend: function(factor, atrPeriod) { + const H = globalThis.high; + const L = globalThis.low; + const C = globalThis.close; + return builtins.get('supertrend')(factor, atrPeriod, H, L, C); + }, + dmi: function(diLength, adxSmoothing, high, low, close) { + if (!Array.isArray(high) || !Array.isArray(low) || !Array.isArray(close)) return { plusDI: null, minusDI: null, adx: null }; + if (high.length < diLength + 1) return { plusDI: null, minusDI: null, adx: null }; + let plusDMSum = 0, minusDMSum = 0, trSum = 0; + for (let i = 1; i <= diLength; i++) { + const idx = high.length - 1 - diLength + i; + const upMove = high[idx] - high[idx - 1]; + const downMove = low[idx - 1] - low[idx]; + plusDMSum += (upMove > downMove && upMove > 0) ? upMove : 0; + minusDMSum += (downMove > upMove && downMove > 0) ? downMove : 0; + const tr = Math.max( + high[idx] - low[idx], + Math.abs(high[idx] - close[idx - 1]), + Math.abs(low[idx] - close[idx - 1]) + ); + trSum += tr; + } + const plusDI = trSum > 0 ? (plusDMSum / trSum) * 100 : 0; + const minusDI = trSum > 0 ? (minusDMSum / trSum) * 100 : 0; + const diSum = plusDI + minusDI; + const dx = diSum > 0 ? Math.abs(plusDI - minusDI) / diSum * 100 : 0; + return { plusDI, minusDI, adx: dx }; + }, + adx: function(diLength, adxSmoothing, high, low, close) { + const result = builtins.get('dmi')(diLength, adxSmoothing, high, low, close); + return result ? result.adx : null; + }, + rsi: function(close, length = 14) { + if (close === null || close === undefined) return null; + if (!Array.isArray(close)) close = [close]; + if (!close || close.length < length + 1) return null; + let gains = 0; + let losses = 0; + for (let i = 1; i <= length; i++) { + const change = close[close.length - i] - close[close.length - i - 1]; + if (change > 0) gains += change; + else losses -= change; + } + const avgGain = gains / length; + const avgLoss = losses / length; + if (avgLoss === 0) return 100; + const rs = avgGain / avgLoss; + return 100 - (100 / (1 + rs)); + }, + roc: function(series, length = 9) { + if (series === null || series === undefined) return null; + if (!Array.isArray(series)) series = [series]; + if (!series || series.length < length + 1) return null; + const prev = series[series.length - 1 - length]; + const curr = series[series.length - 1]; + if (prev === 0 || prev === null || prev === undefined) return null; + return ((curr - prev) / prev) * 100; + }, + percentrank: function(series, length = 100) { + if (series === null || series === undefined) return null; + if (!Array.isArray(series)) series = [series]; + if (series.length < 1) return null; + const slice = series.slice(-length); + const last = slice[slice.length - 1]; + const sorted = [...slice].sort((a, b) => a - b); + let idx = sorted.findIndex(v => v >= last); + if (idx < 0) idx = sorted.length - 1; + if (sorted.length <= 1) return 0; + return (idx / (sorted.length - 1)) * 100; + }, + stoch: function(high, low, close, kLength = 14, dLength = 3, smoothK = 3) { + if (!high || high.length < kLength) return null; + const highestHigh = Math.max(...high.slice(-kLength)); + const lowestLow = Math.min(...low.slice(-kLength)); + const range = highestHigh - lowestLow; + if (range === 0) return { k: 50, d: 50 }; + const rawK = ((close[close.length - 1] - lowestLow) / range) * 100; + return { k: rawK, d: builtins.get('sma')([rawK], smoothK) }; + }, + macd: function(close, fastLen = 12, slowLen = 26, signalLen = 9) { + if (!close || close.length < slowLen + signalLen) return null; + const fastEMA = builtins.get('ema')(close, fastLen); + const slowEMA = builtins.get('ema')(close, slowLen); + const macdLine = fastEMA - slowEMA; + const signalLine = builtins.get('ema')([macdLine], signalLen); + const histogram = macdLine - signalLine; + return { macd: macdLine, signal: signalLine, histogram: histogram }; + }, + cci: function(a, b, c, d) { + // Supports both ta.cci(source, length) and ta.cci(high, low, close, length) signatures + let source; + let length; + if (Array.isArray(a) && typeof b === 'number' && c === undefined) { + source = a; + length = b; + } else { + const high = a; + const low = b; + const close = c; + length = d ?? 20; + if (!high || !low || !close) return null; + if (!Array.isArray(high) || !Array.isArray(low) || !Array.isArray(close)) return null; + source = high.map((h, i) => (h + low[i] + close[i]) / 3); + } + + if (!source || source.length < length) return null; + const sma = builtins.get('sma')(source, length); + const slice = source.slice(-length); + const meanDev = slice.reduce((sum, val) => sum + Math.abs(val - sma), 0) / length; + if (meanDev === 0) return 0; + return (source[source.length - 1] - sma) / (0.015 * meanDev); + }, + change: function(series) { + if (series === null || series === undefined) return null; + if (!Array.isArray(series)) series = [series]; + if (series.length < 2) return null; + return series[series.length - 1] - series[series.length - 2]; + }, + mfi: function(high, low, close, volume, length = 14) { + if (!high || high.length < length + 1) return null; + let posSum = 0; + let negSum = 0; + for (let i = 1; i <= length; i++) { + const tp = (high[high.length - i] + low[low.length - i] + close[close.length - i]) / 3; + const prevTp = (high[high.length - i - 1] + low[low.length - i - 1] + close[close.length - i - 1]) / 3; + const flow = tp * volume[volume.length - i]; + if (tp > prevTp) posSum += flow; + else if (tp < prevTp) negSum += flow; + } + if (negSum === 0) return 100; + const ratio = posSum / negSum; + return 100 - (100 / (1 + ratio)); + }, + obv: function(close, volume) { + if (!close || close.length < 2) return null; + let result = 0; + for (let i = 1; i < close.length; i++) { + if (close[i] > close[i - 1]) result += volume[i]; + else if (close[i] < close[i - 1]) result -= volume[i]; + } + return result; + }, + ad: function(high, low, close, volume) { + if (!high || high.length < 1) return null; + let result = 0; + for (let i = 0; i < high.length; i++) { + const mf = ((close[i] - low[i]) - (high[i] - close[i])) / (high[i] - low[i]) * volume[i]; + result += mf; + } + return result; + }, + adosc: function(high, low, close, volume, fastLen = 3, slowLen = 10) { + if (!high || high.length < slowLen) return null; + const ad = builtins.get('ad')(high, low, close, volume); + const fastEMA = builtins.get('ema')(ad, fastLen); + const slowEMA = builtins.get('ema')(ad, slowLen); + return fastEMA - slowEMA; + }, + cmf: function(high, low, close, volume, length = 20) { + if (!high || high.length < length) return null; + let sum = 0; + let volSum = 0; + for (let i = high.length - length; i < high.length; i++) { + const mfv = ((close[i] - low[i]) - (high[i] - close[i])) / (high[i] - low[i]) * volume[i]; + sum += mfv; + volSum += volume[i]; + } + return volSum > 0 ? sum / volSum : 0; + }, + vwap: function(high, low, close, volume) { + if (!high || high.length < 1) return null; + let priceVolSum = 0; + let volSum = 0; + for (let i = 0; i < high.length; i++) { + const tp = (high[i] + low[i] + close[i]) / 3; + priceVolSum += tp * volume[i]; + volSum += volume[i]; + } + return volSum > 0 ? priceVolSum / volSum : null; + }, + linreg: function(source, length, offset = 0) { + if (!source || source.length < length) return null; + const slice = source.slice(-length - offset).slice(offset); + let sumX = 0, sumY = 0, sumXY = 0, sumXX = 0; + for (let i = 0; i < length; i++) { + sumX += i; + sumY += slice[i]; + sumXY += i * slice[i]; + sumXX += i * i; + } + const slope = (length * sumXY - sumX * sumY) / (length * sumXX - sumX * sumX); + const intercept = (sumY - slope * sumX) / length; + return intercept + slope * (length - 1); + }, + correlation: function(source1, source2, length) { + if (!source1 || source1.length < length) return null; + const slice1 = source1.slice(-length); + const slice2 = source2.slice(-length); + const mean1 = slice1.reduce((a, b) => a + b, 0) / length; + const mean2 = slice2.reduce((a, b) => a + b, 0) / length; + let num = 0, den1 = 0, den2 = 0; + for (let i = 0; i < length; i++) { + const dx = slice1[i] - mean1; + const dy = slice2[i] - mean2; + num += dx * dy; + den1 += dx * dx; + den2 += dy * dy; + } + const den = Math.sqrt(den1 * den2); + return den > 0 ? num / den : 0; + }, + variance: function(source, length) { + if (!source || source.length < length) return null; + const slice = source.slice(-length); + const mean = slice.reduce((a, b) => a + b, 0) / length; + return slice.reduce((sum, val) => sum + Math.pow(val - mean, 2), 0) / length; + }, + stdev: function(source, length) { + return Math.sqrt(builtins.get('variance')(source, length)); + }, + median: function(source, length) { + if (!source || source.length < length) return null; + const sorted = [...source.slice(-length)].sort((a, b) => a - b); + const mid = Math.floor(sorted.length / 2); + return sorted.length % 2 !== 0 ? sorted[mid] : (sorted[mid - 1] + sorted[mid]) / 2; + }, + ta_median: function(source, length) { + return builtins.get('median')(source, length); + }, + hl2: function(high, low) { + return high && low ? (high[high.length - 1] + low[low.length - 1]) / 2 : null; + }, + hlc3: function(high, low, close) { + return high && low && close ? (high[high.length - 1] + low[low.length - 1] + close[close.length - 1]) / 3 : null; + }, + ohlc4: function(open, high, low, close) { + return open && high && low && close ? (open[open.length - 1] + high[high.length - 1] + low[low.length - 1] + close[close.length - 1]) / 4 : null; + }, + avg: function(...args) { + return args.reduce((a, b) => a + b, 0) / args.length; + }, + max: function(...args) { + return Math.max(...args.filter(v => v !== null && v !== undefined)); + }, + min: function(...args) { + return Math.min(...args.filter(v => v !== null && v !== undefined)); + }, + abs: function(value) { + return Math.abs(value); + }, + sqrt: function(value) { + return Math.sqrt(value); + }, + pow: function(base, exp) { + return Math.pow(base, exp); + }, + exp: function(value) { + return Math.exp(value); + }, + log: function(value) { + return Math.log(value); + }, + log10: function(value) { + return Math.log10(value); + }, + sin: function(value) { + return Math.sin(value); + }, + cos: function(value) { + return Math.cos(value); + }, + tan: function(value) { + return Math.tan(value); + }, + asin: function(value) { + return Math.asin(value); + }, + acos: function(value) { + return Math.acos(value); + }, + atan: function(value) { + return Math.atan(value); + }, + todegrees: function(radians) { + return radians * (180 / Math.PI); + }, + toradians: function(degrees) { + return degrees * (Math.PI / 180); + }, + floor: function(value) { + return Math.floor(value); + }, + ceil: function(value) { + return Math.ceil(value); + }, + round: function(value, decimals = 0) { + const factor = Math.pow(10, decimals); + return Math.round(value * factor) / factor; + }, + cum: function(series) { + if (!series) return null; + return series.reduce((sum, val) => sum + val, 0); + }, + sum: function(series, length) { + if (!series || series.length < length) return null; + return series.slice(-length).reduce((a, b) => a + b, 0); + }, + highest: function(series, length) { + if (!series || series.length < length) return null; + return Math.max(...series.slice(-length)); + }, + lowest: function(series, length) { + if (!series || series.length < length) return null; + return Math.min(...series.slice(-length)); + }, + highestbars: function(series, length) { + if (!series || series.length < length) return null; + const slice = series.slice(-length); + const maxVal = Math.max(...slice); + return length - 1 - slice.indexOf(maxVal); + }, + lowestbars: function(series, length) { + if (!series || series.length < length) return null; + const slice = series.slice(-length); + const minVal = Math.min(...slice); + return length - 1 - slice.indexOf(minVal); + }, + rising: function(series, length) { + if (!series || series.length < length) return false; + for (let i = series.length - length; i < series.length - 1; i++) { + if (series[i] >= series[i + 1]) return false; + } + return true; + }, + falling: function(series, length) { + if (!series || series.length < length) return false; + for (let i = series.length - length; i < series.length - 1; i++) { + if (series[i] <= series[i + 1]) return false; + } + return true; + }, + cross: function(series1, series2) { + if (series1 === null || series1 === undefined) return false; + if (series2 === null || series2 === undefined) return false; + if (!Array.isArray(series1)) series1 = [series1]; + if (!Array.isArray(series2)) series2 = [series2]; + if (series1.length < 2 || series2.length < 2) return false; + const i = series1.length - 1; + return (series1[i - 1] < series2[i - 1] && series1[i] >= series2[i]) || + (series1[i - 1] > series2[i - 1] && series1[i] <= series2[i]); + }, + crossover: function(series1, series2) { + if (series1 === null || series1 === undefined) return false; + if (series2 === null || series2 === undefined) return false; + if (!Array.isArray(series1)) series1 = [series1]; + if (!Array.isArray(series2)) series2 = [series2]; + if (series1.length < 2 || series2.length < 2) return false; + const i = series1.length - 1; + return series1[i - 1] < series2[i - 1] && series1[i] >= series2[i]; + }, + crossunder: function(series1, series2) { + if (series1 === null || series1 === undefined) return false; + if (series2 === null || series2 === undefined) return false; + if (!Array.isArray(series1)) series1 = [series1]; + if (!Array.isArray(series2)) series2 = [series2]; + if (series1.length < 2 || series2.length < 2) return false; + const i = series1.length - 1; + return series1[i - 1] > series2[i - 1] && series1[i] <= series2[i]; + }, + offset: function(series, offset) { + if (!Array.isArray(series)) return null; + const idx = series.length - 1 - offset; + return idx >= 0 ? series[idx] : null; + }, + valuewhen: function(condition, source, occurrence = 1) { + if (condition === null || condition === undefined) return null; + if (source === null || source === undefined) return null; + if (!Array.isArray(condition)) condition = [condition]; + if (!Array.isArray(source)) source = [source]; + let count = 0; + for (let i = condition.length - 1; i >= 0; i--) { + if (condition[i]) { + count++; + if (count === occurrence) return source[i]; + } + } + return null; + }, + barssince: function(condition) { + if (condition === null || condition === undefined) return null; + if (!Array.isArray(condition)) { + return condition ? 0 : null; + } + for (let i = condition.length - 1; i >= 0; i--) { + if (condition[i]) return condition.length - 1 - i; + } + return null; + }, + year: function(timestamp) { + const date = timestamp ? new Date(timestamp) : new Date(); + return date.getFullYear(); + }, + month: function(timestamp) { + const date = timestamp ? new Date(timestamp) : new Date(); + return date.getMonth() + 1; + }, + weekofyear: function(timestamp) { + const date = timestamp ? new Date(timestamp) : new Date(); + const d = new Date(Date.UTC(date.getFullYear(), date.getMonth(), date.getDate())); + const dayNum = d.getUTCDay(); + d.setUTCDate(d.getUTCDate() + 4 - (dayNum || 7)); + const yearStart = new Date(Date.UTC(d.getUTCFullYear(), 0, 1)); + return Math.ceil((((d - yearStart) / 86400000) + 1) / 7); + }, + dayofmonth: function(timestamp) { + const date = timestamp ? new Date(timestamp) : new Date(); + return date.getDate(); + }, + hour: function(timestamp) { + const date = timestamp ? new Date(timestamp) : new Date(); + return date.getHours(); + }, + minute: function(timestamp) { + const date = timestamp ? new Date(timestamp) : new Date(); + return date.getMinutes(); + }, + second: function(timestamp) { + const date = timestamp ? new Date(timestamp) : new Date(); + return date.getSeconds(); + }, + timestamp: function(year, month, day, hour = 0, minute = 0, second = 0) { + return Date.UTC(year, month - 1, day, hour, minute, second); + }, + na: function(value) { + return value === null || value === undefined || (typeof value === 'number' && isNaN(value)); + }, + nz: function(value, replacement = 0) { + return value === null || value === undefined || isNaN(value) ? replacement : value; + }, + isna: function(value) { + return value === null || value === undefined || isNaN(value); + }, + isempty: function(value) { + return value === null || value === undefined || value === ''; + }, + fixnan: function(value, replacement = 0) { + return builtins.get('nz')(value, replacement); + }, + plot: function(series, title = '', color = null, linewidth = 1) { + const rt = globalThis.__pineRuntime; + if (!rt) return series; + const bar = rt.__barIndex | 0; + const ord = (rt.__plotIdx = (rt.__plotIdx | 0) + 1) - 1; + const key = (title && String(title)) || ('plot_' + ord); + let p = rt.plots[key]; + if (!p) p = rt.plots[key] = { title: key, color, linewidth, data: [] }; + p.data[bar] = this.__scalar(series); + return series; + }, + lineNew: function(x1, y1, x2, y2, opts = {}) { + return this.__decDraw({ x1, y1, x2, y2, opts, _type: 'line' }); + }, + lineDelete: function(l) { + return null; + }, + lineSetXY: function(line, point, x, y) { + if (!line) return null; + if (point === 0 || point === 'x1') { line.x1 = x; line.y1 = y; } + else { line.x2 = x; line.y2 = y; } + return line; + }, + lineGetX: function(line, point) { + if (!line) return null; + return point === 0 || point === 'x1' ? line.x1 : line.x2; + }, + lineGetY: function(line, point) { + if (!line) return null; + return point === 0 || point === 'y1' ? line.y1 : line.y2; + }, + labelNew: function(x, y, text = '', opts = {}) { + return this.__decDraw({ x, y, text, opts, _type: 'label' }); + }, + labelDelete: function(l) { + return null; + }, + labelSetText: function(label, text) { + if (!label) return null; + label.text = text; + return label; + }, + labelGetText: function(label) { + if (!label) return ''; + return label.text || ''; + }, + boxNew: function(left, top, right, bottom, opts = {}) { + return this.__decDraw({ left, top, right, bottom, opts, _type: 'box' }); + }, + boxDelete: function(box) { + return null; + }, + boxSetLeftTop: function(box, left, top) { + if (!box) return null; + box.left = left; + box.top = top; + return box; + }, + boxSetRightBottom: function(box, right, bottom) { + if (!box) return null; + box.right = right; + box.bottom = bottom; + return box; + }, + polylineNew: function(points, opts = {}) { + return this.__decDraw({ points: points || [], opts, _type: 'polyline' }); + }, + polylineDelete: function(poly) { + return null; + }, + plotshape: function(condition, ...rest) { + const rt = globalThis.__pineRuntime; + if (!rt) return condition; + const bar = rt.__barIndex | 0; + const ord = (rt.__shapeIdx = (rt.__shapeIdx | 0) + 1) - 1; + let key = 'shape_' + ord; + for (const r of rest) { + if (typeof r === 'string') { key = r; break; } + if (r && typeof r === 'object' && r.title) { key = String(r.title); break; } + } + let s = rt.plotshapes[key]; + if (!s) s = rt.plotshapes[key] = { title: key, data: [] }; + s.data[bar] = !!this.__scalar(condition); + return condition; + }, + indicator: function(title, shorttitle, overlay, opts) { + if (globalThis.__pineRuntime) { + globalThis.__pineRuntime.indicator = { title, shorttitle, overlay, opts }; + } + return null; + }, + strategy: function(title, shorttitle, overlay, opts) { + if (globalThis.__pineRuntime) { + globalThis.__pineRuntime.strategy = { title, shorttitle, overlay, opts }; + } + return null; + }, + plotbar: function(open, high, low, close, title, color, editable, showLast) { + return { open, high, low, close }; + }, + plotcandle: function(open, high, low, close, title, color, wickColor, borderColor, editable, showLast) { + return { open, high, low, close }; + }, + alert: function(condition, message, frequency) { + console.log('Alert:', condition ? message : 'Condition not met'); + return condition; + }, + strategyEntry: function(id, direction, qty, limit, stop, ocaName, ocaType, comment) { + console.log(`Strategy Entry: ${id} ${direction}`); + return true; + }, + strategyExit: function(id, fromEntry, qty, limit, stop, ocaName, comment) { + console.log(`Strategy Exit: ${id}`); + return true; + }, + strategyOrder: function(id, direction, qty, limit, stop, ocaName, ocaType, comment) { + console.log(`Strategy Order: ${id} ${direction}`); + return true; + }, + strategyClose: function(id, comment, qty, percent, immediately) { + console.log(`Strategy Close: ${id}`); + return true; + }, + strategyLong: function() { + return 'long'; + }, + strategyShort: function() { + return 'short'; + }, + chartPointFromIndex: function(_index, _price) { + return { index: _index ?? null, price: _price ?? null }; + }, + chartPointNew: function(_time, _price) { + return { time: _time ?? null, price: _price ?? null }; + }, + mapNew: function() { + return this.__decMap(new Map()); + }, + mapSize: function(m) { + return m instanceof Map ? m.size : 0; + }, + mapGet: function(m, key, defval) { + if (!(m instanceof Map)) return defval ?? null; + if (!m.has(key)) return defval ?? null; + return m.get(key); + }, + mapSet: function(m, key, value) { + if (!(m instanceof Map)) return null; + m.set(key, value); + return value; + }, + mapRemove: function(m, key) { + if (!(m instanceof Map)) return false; + return m.delete(key); + }, + mapKeys: function(m) { + if (!(m instanceof Map)) return []; + return Array.from(m.keys()); + }, + mapValues: function(m) { + if (!(m instanceof Map)) return []; + return Array.from(m.values()); + }, + mapContains: function(m, key) { + if (!(m instanceof Map)) return false; + return m.has(key); + }, + matrixNew: function(rows, cols, initialValue = 0) { + const r = Math.max(0, rows ?? 0); + const c = Math.max(0, cols ?? 0); + const data = Array.from({ length: r }, () => Array.from({ length: c }, () => initialValue)); + return this.__decMatrix({ rows: r, cols: c, data }); + }, + matrixRows: function(m) { + return m?.rows ?? 0; + }, + matrixCols: function(m) { + return m?.cols ?? 0; + }, + matrixGet: function(m, row, col) { + if (!m || !Array.isArray(m.data)) return null; + return m.data?.[row]?.[col] ?? null; + }, + matrixSet: function(m, row, col, value) { + if (!m || !Array.isArray(m.data)) return null; + if (!Array.isArray(m.data[row])) return null; + m.data[row][col] = value; + return value; + }, + matrixFill: function(m, value) { + if (!m || !Array.isArray(m.data)) return null; + for (let r = 0; r < (m.rows ?? 0); r++) { + for (let c = 0; c < (m.cols ?? 0); c++) { + if (m.data[r]) m.data[r][c] = value; + } + } + return m; + }, + matrixSum: function(m) { + if (!m || !Array.isArray(m.data)) return null; + let s = 0; + for (let r = 0; r < (m.rows ?? 0); r++) { + for (let c = 0; c < (m.cols ?? 0); c++) { + const v = m.data?.[r]?.[c]; + if (typeof v === 'number' && !Number.isNaN(v)) s += v; + } + } + return s; + }, + matrixAvg: function(m) { + if (!m || !Array.isArray(m.data)) return null; + const n = (m.rows ?? 0) * (m.cols ?? 0); + if (n === 0) return null; + const s = builtins.get('matrixSum')(m); + return (typeof s === 'number') ? (s / n) : null; + }, + matrixMin: function(m) { + if (!m || !Array.isArray(m.data)) return null; + let out = null; + for (let r = 0; r < (m.rows ?? 0); r++) { + for (let c = 0; c < (m.cols ?? 0); c++) { + const v = m.data?.[r]?.[c]; + if (typeof v !== 'number' || Number.isNaN(v)) continue; + out = (out === null) ? v : Math.min(out, v); + } + } + return out; + }, + matrixMax: function(m) { + if (!m || !Array.isArray(m.data)) return null; + let out = null; + for (let r = 0; r < (m.rows ?? 0); r++) { + for (let c = 0; c < (m.cols ?? 0); c++) { + const v = m.data?.[r]?.[c]; + if (typeof v !== 'number' || Number.isNaN(v)) continue; + out = (out === null) ? v : Math.max(out, v); + } + } + return out; + }, + matrixTranspose: function(m) { + if (!m || !Array.isArray(m.data)) return null; + const r = m.rows ?? 0; + const c = m.cols ?? 0; + const out = builtins.get('matrixNew')(c, r, 0); + for (let i = 0; i < r; i++) { + for (let j = 0; j < c; j++) { + out.data[j][i] = m.data?.[i]?.[j] ?? 0; + } + } + return out; + }, + matrixMult: function(a, b) { + if (!a || !b || !Array.isArray(a.data) || !Array.isArray(b.data)) return null; + const aRows = a.rows ?? 0; + const aCols = a.cols ?? 0; + const bRows = b.rows ?? 0; + const bCols = b.cols ?? 0; + if (aCols !== bRows) return null; + const out = builtins.get('matrixNew')(aRows, bCols, 0); + for (let i = 0; i < aRows; i++) { + for (let j = 0; j < bCols; j++) { + let s = 0; + for (let k = 0; k < aCols; k++) { + const av = a.data?.[i]?.[k] ?? 0; + const bv = b.data?.[k]?.[j] ?? 0; + s += av * bv; + } + out.data[i][j] = s; + } + } + return out; + }, + matrixInv: function(m) { + if (!m || !Array.isArray(m.data)) return null; + const r = m.rows ?? 0; + const c = m.cols ?? 0; + if (r !== c) return null; + if (r === 2) { + const a = m.data?.[0]?.[0] ?? 0; + const b = m.data?.[0]?.[1] ?? 0; + const d = m.data?.[1]?.[0] ?? 0; + const e = m.data?.[1]?.[1] ?? 0; + const det = (a * e) - (b * d); + if (det === 0) return null; + const out = builtins.get('matrixNew')(2, 2, 0); + out.data[0][0] = e / det; + out.data[0][1] = -b / det; + out.data[1][0] = -d / det; + out.data[1][1] = a / det; + return out; + } + return null; + }, + __jacobiEigen: function(m) { + if (!m || !Array.isArray(m.data)) return null; + const n = m.rows ?? 0; + if (n === 0 || n !== (m.cols ?? 0)) return null; + // Work on a copy so the input matrix is untouched. + const a = m.data.map(row => row.slice()); + const v = Array.from({ length: n }, (_, i) => Array.from({ length: n }, (_, j) => (i === j ? 1 : 0))); + for (let sweep = 0; sweep < 100; sweep++) { + let off = 0; + for (let p = 0; p < n; p++) for (let q = p + 1; q < n; q++) off += a[p][q] * a[p][q]; + if (off < 1e-20) break; + for (let p = 0; p < n; p++) { + for (let q = p + 1; q < n; q++) { + if (Math.abs(a[p][q]) < 1e-18) continue; + const theta = (a[q][q] - a[p][p]) / (2 * a[p][q]); + const t = Math.sign(theta || 1) / (Math.abs(theta) + Math.sqrt(theta * theta + 1)); + const cos = 1 / Math.sqrt(t * t + 1); + const sin = t * cos; + for (let k = 0; k < n; k++) { + const akp = a[k][p], akq = a[k][q]; + a[k][p] = cos * akp - sin * akq; + a[k][q] = sin * akp + cos * akq; + } + for (let k = 0; k < n; k++) { + const apk = a[p][k], aqk = a[q][k]; + a[p][k] = cos * apk - sin * aqk; + a[q][k] = sin * apk + cos * aqk; + } + for (let k = 0; k < n; k++) { + const vkp = v[k][p], vkq = v[k][q]; + v[k][p] = cos * vkp - sin * vkq; + v[k][q] = sin * vkp + cos * vkq; + } + } + } + } + // Sort eigenpairs by eigenvalue, descending. + const order = Array.from({ length: n }, (_, i) => i).sort((i, j) => a[j][j] - a[i][i]); + const values = order.map(i => a[i][i]); + const vectors = Array.from({ length: n }, (_, r) => order.map(c => v[r][c])); + return { values, vectors }; + }, + matrixEigenvalues: function(m) { + const e = this.__jacobiEigen(m); + return this.__decArr(e ? e.values : []); + }, + matrixEigenvectors: function(m) { + const e = this.__jacobiEigen(m); + if (!e) return this.__decMatrix({ rows: 0, cols: 0, data: [] }); + return this.__decMatrix({ rows: e.vectors.length, cols: e.vectors.length, data: e.vectors }); + }, + requestSecurity: function(symbol, timeframe, expression) { + return expression; + }, + arrayNew: function(initialSize = 0, initialValue = 0) { + return this.__decArr(Array(initialSize).fill(initialValue)); + }, + arraySize: function(arr) { + return arr ? arr.length : 0; + }, + arrayGet: function(arr, index) { + return arr && arr[index] !== undefined ? arr[index] : null; + }, + arraySet: function(arr, index, value) { + if (arr) arr[index] = value; + return value; + }, + arrayPush: function(arr, value) { + if (arr) arr.push(value); + return value; + }, + arrayPop: function(arr) { + return arr ? arr.pop() : null; + }, + arrayShift: function(arr) { + return arr ? arr.shift() : null; + }, + arrayUnshift: function(arr, value) { + if (arr) arr.unshift(value); + return value; + }, + arrayClear: function(arr) { + if (arr) arr.length = 0; + return null; + }, + arrayInsert: function(arr, index, value) { + if (!arr) return value; + const i = Math.max(0, Math.min(arr.length, Number(index) || 0)); + arr.splice(i, 0, value); + return value; + }, + arrayRemove: function(arr, index) { + if (!arr) return null; + const i = Number(index) || 0; + if (i < 0 || i >= arr.length) return null; + const removed = arr.splice(i, 1); + return removed.length ? removed[0] : null; + }, + arrayFill: function(arr, value, startIndex = 0, endIndex = null) { + if (!arr) return arr; + const start = Number(startIndex) || 0; + const end = endIndex === null || endIndex === undefined ? arr.length : Number(endIndex) || 0; + arr.fill(value, start, end); + return arr; + }, + arraySlice: function(arr, startIndex = 0, endIndex = null) { + if (!arr) return this.__decArr([]); + const start = Number(startIndex) || 0; + const end = endIndex === null || endIndex === undefined ? arr.length : Number(endIndex) || 0; + return this.__decArr(arr.slice(start, end)); + }, + arraySort: function(arr, order = 'ascending') { + if (arr) arr.sort((a, b) => order === 'ascending' ? a - b : b - a); + return arr; + }, + arrayReverse: function(arr) { + if (arr) arr.reverse(); + return arr; + }, + arrayContains: function(arr, value) { + return arr ? arr.includes(value) : false; + }, + arrayIndexOf: function(arr, value) { + return arr ? arr.indexOf(value) : -1; + }, + arrayLastIndexOf: function(arr, value) { + return arr ? arr.lastIndexOf(value) : -1; + }, + arraySum: function(arr) { + return arr ? arr.reduce((a, b) => a + b, 0) : 0; + }, + arrayAvg: function(arr) { + return arr && arr.length > 0 ? arr.reduce((a, b) => a + b, 0) / arr.length : 0; + }, + arrayMin: function(arr) { + return arr && arr.length > 0 ? Math.min(...arr) : null; + }, + arrayMax: function(arr) { + return arr && arr.length > 0 ? Math.max(...arr) : null; + }, + arrayStdev: function(arr) { + if (!arr || arr.length === 0) return null; + const mean = arr.reduce((a, b) => a + b, 0) / arr.length; + const variance = arr.reduce((sum, val) => sum + Math.pow(val - mean, 2), 0) / arr.length; + return Math.sqrt(variance); + }, + arrayVariance: function(arr) { + if (!arr || arr.length === 0) return null; + const mean = arr.reduce((a, b) => a + b, 0) / arr.length; + return arr.reduce((sum, val) => sum + Math.pow(val - mean, 2), 0) / arr.length; + }, + arrayCovariance: function(arr1, arr2) { + if (!arr1 || !arr2 || arr1.length === 0 || arr1.length !== arr2.length) return null; + const mean1 = arr1.reduce((a, b) => a + b, 0) / arr1.length; + const mean2 = arr2.reduce((a, b) => a + b, 0) / arr2.length; + let cov = 0; + for (let i = 0; i < arr1.length; i++) { + cov += (arr1[i] - mean1) * (arr2[i] - mean2); + } + return cov / arr1.length; + }, + mode: function(arr) { + if (!arr || arr.length === 0) return null; + const counts = new Map(); + let maxCount = 0; + let modeVal = arr[0]; + for (const val of arr) { + const c = (counts.get(val) || 0) + 1; + counts.set(val, c); + if (c > maxCount) { + maxCount = c; + modeVal = val; + } + } + return modeVal; + }, + strLength: function(str) { + return str ? str.length : 0; + }, + strSubstring: function(str, from, to) { + return str ? str.substring(from, to) : ''; + }, + strConcat: function(...args) { + return args.join(''); + }, + strContains: function(str, substring) { + return str ? str.includes(substring) : false; + }, + strStartsWith: function(str, prefix) { + return str ? str.startsWith(prefix) : false; + }, + strEndsWith: function(str, suffix) { + return str ? str.endsWith(suffix) : false; + }, + strReplace: function(str, from, to) { + return str ? str.replace(from, to) : ''; + }, + strReplaceAll: function(str, from, to) { + return str ? str.split(from).join(to) : ''; + }, + strLower: function(str) { + return str ? str.toLowerCase() : ''; + }, + strUpper: function(str) { + return str ? str.toUpperCase() : ''; + }, + strToNumber: function(str) { + return str ? parseFloat(str) : 0; + }, + strToString: function(value) { + return String(value); + }, + strSplit: function(str, delimiter) { + return str ? str.split(delimiter) : []; + }, + strMatch: function(str, regex) { + const match = str.match(new RegExp(regex)); + return match ? match[0] : ''; + }, + strPos: function(str, substring) { + return str ? str.indexOf(substring) : -1; + }, + strRPos: function(str, substring) { + return str ? str.lastIndexOf(substring) : -1; + }, + strRemove: function(str, from, to) { + return str ? str.substring(0, from) + str.substring(to) : ''; + }, + strReverse: function(str) { + return str ? str.split('').reverse().join('') : ''; + }, + strFormat: function(formatStr, ...args) { + if (!formatStr) return ''; + return formatStr.replace(/\{(\d+)\}/g, (match, idx) => { + const i = parseInt(idx, 10); + return i < args.length ? String(args[i]) : match; + }); + }, + input: function(defval, title, tooltip, inline, group) { + return defval; + }, + inputInt: function(defval = 0, title, minval, maxval, step, tooltip, inline, group) { + return defval; + }, + inputFloat: function(defval = 0.0, title, minval, maxval, step, tooltip, inline, group) { + return defval; + }, + inputBool: function(defval = false, title, tooltip, inline, group) { + return defval; + }, + inputString: function(defval = '', title, options, tooltip, inline, group) { + return defval; + }, + inputSource: function(defval = null, title, tooltip, inline, group) { + return defval; + }, + inputColor: function(defval = '#000000', title, tooltip, inline, group) { + return defval; + }, + inputTime: function(defval = 0, title, tooltip, inline, group) { + return defval; + }, + mathSign: function(value) { + return Math.sign(value); + }, + mathAvg: function(...args) { + const valid = args.filter(v => v !== null && v !== undefined && !isNaN(v)); + return valid.length > 0 ? valid.reduce((a, b) => a + b, 0) / valid.length : null; + }, + mathSum: function(series, length) { + if (!series || series.length < length) return null; + return series.slice(-length).reduce((a, b) => a + b, 0); + }, + mathRandom: function(min = 0, max = 1) { + return min + Math.random() * (max - min); + }, + mathTodegrees: function(radians) { + return radians * (180 / Math.PI); + }, + mathToradians: function(degrees) { + return degrees * (Math.PI / 180); + }, + colorT: function(color, transparency) { + if (transparency !== undefined) { + return { color, transparency }; + } + if (color && typeof color === 'object' && 'transparency' in color) { + return color.transparency; + } + return 0; + }, + tableCellSetText: function(table, column, row, text) { + return null; + }, + tableCellSetBgcolor: function(table, column, row, bgcolor) { + return null; + }, + tableMergeCells: function(table, startColumn, startRow, endColumn, endRow) { + return null; + }, + tableDelete: function(table) { + return null; + }, + tableClear: function(table, startColumn, startRow, endColumn, endRow) { + return null; + }, + maxBarsBack: function(series, length) { + return null; + }, + pi: 3.141592653589793, + e: 2.718281828459045, + true: true, + false: false, + ticker: "AAPL", + tickerid: "NASDAQ:AAPL", + syminfo: function(type) { + const info = { ticker: 'AAPL', tickerid: 'NASDAQ:AAPL', prefix: 'NASDAQ', root: 'AAPL', suffix: '' }; + return info[type] || ''; + }, + timenow: 1782783577299, + barstate: "LAST", + dividends: {}, + splits: {}, + earnings: {}, + volume: 0, + open: 0, + high: 0, + low: 0, + close: 0, + pivothigh: function(_leftBars, _rightBars) { + return null; + }, + pivotlow: function(_leftBars, _rightBars) { + return null; + }, + tr: function(high, low, close) { + // Handles both the boolean shorthand ta.tr(true) and explicit high/low/close arguments + if (typeof high === 'boolean') { + const H = globalThis.high; + const L = globalThis.low; + const C = globalThis.close; + if (!Array.isArray(H) || !Array.isArray(L) || !Array.isArray(C) || C.length < 2) return null; + const i = C.length - 1; + const prevClose = C[i - 1]; + return Math.max(H[i] - L[i], Math.abs(H[i] - prevClose), Math.abs(L[i] - prevClose)); + } + + // When arrays are passed, compute true range from the last bar using the previous close + if (Array.isArray(close)) { + const prevClose = close[close.length - 2] || close[0]; + const currHigh = Array.isArray(high) ? high[high.length - 1] : high; + const currLow = Array.isArray(low) ? low[low.length - 1] : low; + const currClose = close[close.length - 1]; + return Math.max(currHigh - currLow, Math.abs(currHigh - prevClose), Math.abs(currLow - prevClose)); + } + + return Math.max(high - low, Math.abs(high - close), Math.abs(low - close)); + }, + arrayFirst: function(arr) { + if (!arr || arr.length === 0) return null; + return arr[0]; + }, + arrayLast: function(arr) { + if (!arr || arr.length === 0) return null; + return arr[arr.length - 1]; + }, + arrayJoin: function(arr, separator = ',') { + if (!arr) return ''; + return arr.join(separator); + }, + arrayConcat: function(arr1, arr2) { + if (!arr1) return arr2 || []; + if (!arr2) return arr1; + return this.__decArr(arr1.concat(arr2)); + }, + arrayCopy: function(arr) { + if (!arr) return this.__decArr([]); + return this.__decArr([...arr]); + }, + arrayBinarySearch: function(arr, value) { + if (!arr || arr.length === 0) return -1; + let lo = 0, hi = arr.length - 1; + while (lo <= hi) { + const mid = (lo + hi) >>> 1; + if (arr[mid] === value) return mid; + if (arr[mid] < value) lo = mid + 1; + else hi = mid - 1; + } + return -1; + }, + arrayRange: function(arr) { + if (!arr || arr.length === 0) return 0; + return Math.max(...arr) - Math.min(...arr); + }, + arrayMedian: function(arr) { + if (!arr || arr.length === 0) return null; + const sorted = [...arr].sort((a, b) => a - b); + const mid = Math.floor(sorted.length / 2); + return sorted.length % 2 !== 0 ? sorted[mid] : (sorted[mid - 1] + sorted[mid]) / 2; + }, + arrayMode: function(arr) { + if (!arr || arr.length === 0) return null; + const freq = {}; + let maxCount = 0; + let mode = arr[0]; + for (const v of arr) { + freq[v] = (freq[v] || 0) + 1; + if (freq[v] > maxCount) { maxCount = freq[v]; mode = v; } + } + return mode; + }, + arrayPercentile: function(arr, percentile) { + if (!arr || arr.length === 0) return null; + const sorted = [...arr].sort((a, b) => a - b); + const idx = (percentile / 100) * (sorted.length - 1); + const lo = Math.floor(idx); + const hi = Math.ceil(idx); + if (lo === hi) return sorted[lo]; + return sorted[lo] + (sorted[hi] - sorted[lo]) * (idx - lo); + }, + arrayPercentileLinearInterpolation: function(arr, percentile) { + return builtins.get('arrayPercentile')(arr, percentile); + }, + arrayPercentileNearestRank: function(arr, percentile) { + if (!arr || arr.length === 0) return null; + const sorted = [...arr].sort((a, b) => a - b); + const idx = Math.ceil((percentile / 100) * sorted.length) - 1; + return sorted[Math.max(0, idx)]; + }, + arrayAbs: function(arr) { + if (!arr) return []; + return arr.map(v => Math.abs(v)); + }, + arrayEvery: function(arr, callback) { + if (!arr) return false; + return arr.every(callback); + }, + arraySome: function(arr, callback) { + if (!arr) return false; + return arr.some(callback); + }, + requestFinancial: function() { + return null; + }, +}; + + +const builtins = new Map(Object.entries(pinescript)); + + +globalThis.__pineRuntime = globalThis.__pineRuntime || { plots: [], plotshapes: [], alerts: [] }; + +globalThis.pinescript = pinescript; + + +pinescript.strategy.long = pinescript.strategyLong(); + +pinescript.strategy.short = pinescript.strategyShort(); + + +globalThis.input = globalThis.input || {}; + +globalThis.input.timeframe = globalThis.input.timeframe || ((defval) => defval); + + +globalThis.array = globalThis.array || { + + from: (...items) => pinescript.__decArr(items), + + size: (arr) => pinescript.arraySize(arr), + + get: (arr, i) => pinescript.arrayGet(arr, i), + + set: (arr, i, v) => pinescript.arraySet(arr, i, v), + + push: (arr, v) => pinescript.arrayPush(arr, v), + + pop: (arr) => pinescript.arrayPop(arr), + + unshift: (arr, v) => pinescript.arrayUnshift(arr, v), + + shift: (arr) => pinescript.arrayShift(arr), + + clear: (arr) => pinescript.arrayClear(arr), + + remove: (arr, i) => pinescript.arrayRemove(arr, i), + +}; + + +globalThis.hline = globalThis.hline || { style_dotted: "style_dotted", style_solid: "style_solid" }; + + +class SeriesArray extends Array { + + valueOf() { return this.length ? this[this.length - 1] : NaN; } + + toString() { return String(this.valueOf()); } + +} + +pinescript.asSeries = function(v) { + + if (v === null || v === undefined) return v; + + if (v instanceof SeriesArray) return v; + + if (Array.isArray(v)) return Object.setPrototypeOf(v, SeriesArray.prototype); + + return v; + +}; + + +globalThis.timeframe = globalThis.timeframe || { period: "D" }; + +pinescript._parseTimeframeMs = function(tf) { + + if (tf === null || tf === undefined) return null; + + const s = String(tf).trim().toUpperCase(); + + if (/^\d+$/.test(s)) return parseInt(s, 10) * 60 * 1000; + + if (s.endsWith("S")) return parseInt(s.slice(0, -1), 10) * 1000; + + if (s.endsWith("H")) return parseInt(s.slice(0, -1), 10) * 60 * 60 * 1000; + + if (s === "D") return 24 * 60 * 60 * 1000; + + if (s === "W") return 7 * 24 * 60 * 60 * 1000; + + if (s === "M") return 30 * 24 * 60 * 60 * 1000; + + return null; + +}; + + +pinescript.requestSecurity = function(symbol, tf, series, opts) { + + const timeSeries = globalThis.time; + + if (!Array.isArray(timeSeries) || !Array.isArray(series)) return series; + + const ms = pinescript._parseTimeframeMs(tf); + + if (!ms) return series; + + const lookahead = opts?.lookahead ?? false; + + const gaps = opts?.gaps ?? false; + + const out = []; + + let bucket = null; + + let bucketVal = null; + + for (let i = 0; i < timeSeries.length; i++) { + + const t = timeSeries[i]; + + const b = Math.floor(t / ms); + + if (bucket === null) { bucket = b; bucketVal = series[i]; } + + if (b !== bucket) { bucket = b; bucketVal = series[i]; } else { bucketVal = series[i]; } + + if (lookahead) out.push(bucketVal); else out.push(gaps ? null : bucketVal); + + } + + return pinescript.asSeries(out); + +}; + + +globalThis.request = globalThis.request || { + + security: function(symbol, tf, series, opts) { return pinescript.requestSecurity(symbol, tf, series, opts); }, + + financial: function() { return null; } + +}; + + +globalThis.barmerge = globalThis.barmerge || { + + gaps_off: false, + + gaps_on: true, + + lookahead_off: false, + + lookahead_on: true, + +}; + + +function __pineNS(real) { + + const base = real || {}; + + return new Proxy(base, { + + get(target, key) { + + if (key in target) return target[key]; + + if (typeof key === "symbol") return target[key]; + + // Unknown member: usable as a value (null), a constant, or a no-op function. + + const noop = function() { return null; }; + + noop.toString = () => ""; + + return noop; + + } + + }); + +} + +globalThis.ta = globalThis.ta || __pineNS({}); + +globalThis.math = globalThis.math || __pineNS({ pi: Math.PI, e: Math.E, phi: 1.618033988749895, rphi: 0.618033988749895 }); + +globalThis.chart = globalThis.chart || __pineNS({ point: __pineNS({}), bg_color: null, fg_color: null }); + +globalThis.line = globalThis.line || __pineNS({ style_solid: "solid", style_dashed: "dashed", style_dotted: "dotted" }); + +globalThis.box = globalThis.box || __pineNS({}); + +globalThis.color = globalThis.color || __pineNS(Object.assign(function(c) { return c; }, { new: function(c, t) { return c; }, rgb: function(r, g, b, t) { return "#rgb(" + [r, g, b].join(",") + ")"; }, from_gradient: function(v, lo, hi, c1, c2) { return c1; }, r: function() { return 0; }, g: function() { return 0; }, b: function() { return 0; }, t: function() { return 0; }, aqua: "#00BCD4", black: "#363A45", blue: "#2962FF", fuchsia: "#E040FB", gray: "#787B86", green: "#4CAF50", lime: "#00E676", maroon: "#880E4F", navy: "#311B92", olive: "#808000", orange: "#FF9800", purple: "#9C27B0", red: "#FF5252", silver: "#B2B5BE", teal: "#00897B", white: "#FFFFFF", yellow: "#FFEB3B" })); + +globalThis.label = globalThis.label || __pineNS({ style_label_down: "label_down", style_label_up: "label_up", style_none: "none" }); + +globalThis.polyline = globalThis.polyline || __pineNS({}); + +globalThis.linefill = globalThis.linefill || __pineNS({}); + +globalThis.matrix = globalThis.matrix || __pineNS({}); + +globalThis.map = globalThis.map || __pineNS({}); + +globalThis.session = globalThis.session || __pineNS({ regular: "regular", extended: "extended" }); + +globalThis.ticker = globalThis.ticker || __pineNS({}); + +globalThis.dayofweek = globalThis.dayofweek || __pineNS(Object.assign(function(t) { return new Date(t != null ? t : (globalThis.time || 0)).getUTCDay() + 1; }, { sunday: 1, monday: 2, tuesday: 3, wednesday: 4, thursday: 5, friday: 6, saturday: 7 })); + +globalThis.timeframe = __pineNS(Object.assign(globalThis.timeframe || {}, { period: (globalThis.timeframe && globalThis.timeframe.period) || "D", isintraday: false, isdaily: true, multiplier: 1 })); + +globalThis.request = __pineNS(globalThis.request || {}); + +globalThis.input = __pineNS(globalThis.input || {}); + +globalThis.adjustment = globalThis.adjustment || __pineNS({}); + +globalThis.earnings = globalThis.earnings || __pineNS({}); + +globalThis.dividends = globalThis.dividends || __pineNS({}); + +globalThis.splits = globalThis.splits || __pineNS({}); + +globalThis.currency = globalThis.currency || __pineNS({}); + +globalThis.display = globalThis.display || __pineNS({ none: "none", all: "all", pane: "pane", data_window: "data_window", status_line: "status_line", price_scale: "price_scale" }); + +globalThis.format = globalThis.format || __pineNS({ price: "price", volume: "volume", percent: "percent", mintick: "mintick", inherit: "inherit" }); + +globalThis.scale = globalThis.scale || __pineNS({ left: "left", right: "right", none: "none" }); + +globalThis.font = globalThis.font || __pineNS({ family_default: "default", family_monospace: "monospace" }); + +globalThis.xloc = globalThis.xloc || __pineNS({ bar_index: "bar_index", bar_time: "bar_time" }); + +globalThis.yloc = globalThis.yloc || __pineNS({ price: "price", abovebar: "abovebar", belowbar: "belowbar" }); + +globalThis.extend = globalThis.extend || __pineNS({ none: "none", left: "left", right: "right", both: "both" }); + +globalThis.order = globalThis.order || __pineNS({ ascending: "ascending", descending: "descending" }); + +globalThis.alert = globalThis.alert || Object.assign(function() { return null; }, { freq_once_per_bar: "once_per_bar", freq_once_per_bar_close: "once_per_bar_close", freq_all: "all" }); + +globalThis.dayofmonth = globalThis.dayofmonth || function(t) { return new Date(t || 0).getUTCDate(); }; + +globalThis.plot = globalThis.plot || __pineNS({ style_line: "line", style_linebr: "linebr", style_stepline: "stepline", style_histogram: "histogram", style_cross: "cross", style_area: "area", style_areabr: "areabr", style_columns: "columns", style_circles: "circles" }); + +globalThis.int = globalThis.int || function(x) { return x == null ? null : Math.trunc(Number(x)); }; + +globalThis.float = globalThis.float || function(x) { return x == null ? null : Number(x); }; + +globalThis.bool = globalThis.bool || function(x) { return Boolean(x); }; + +globalThis.string = globalThis.string || function(x) { return x == null ? null : String(x); }; + +globalThis.str = globalThis.str || __pineNS({}); + +pinescript.math = globalThis.math; + +pinescript.ta = globalThis.ta; + +pinescript.str = globalThis.str; + +pinescript.array = __pineNS(globalThis.array); + + +pinescript.color = { + + hex: function(s) { + + if (typeof s !== "string" || s[0] !== "#") return { r: 0, g: 0, b: 0, a: 255 }; + + const hex = s.slice(1); + + const hasAlpha = hex.length === 8; + + const r = parseInt(hex.slice(0, 2), 16) || 0; + + const g = parseInt(hex.slice(2, 4), 16) || 0; + + const b = parseInt(hex.slice(4, 6), 16) || 0; + + const a = hasAlpha ? (parseInt(hex.slice(6, 8), 16) || 255) : 255; + + return { r, g, b, a }; + + }, + + from_gradient: function(value, min, max, color1, color2) { + + const c1 = color1 || { r: 0, g: 0, b: 0, a: 255 }; + + const c2 = color2 || { r: 255, g: 255, b: 255, a: 255 }; + + if (value === null || value === undefined || min === null || min === undefined || max === null || max === undefined) return c1; + + const denom = (max - min); + + const t = denom === 0 ? 0 : Math.max(0, Math.min(1, (value - min) / denom)); + + const lerp = (a, b) => Math.round(a + (b - a) * t); + + return { r: lerp(c1.r ?? 0, c2.r ?? 0), g: lerp(c1.g ?? 0, c2.g ?? 0), b: lerp(c1.b ?? 0, c2.b ?? 0), a: lerp(c1.a ?? 255, c2.a ?? 255) }; + + }, + + rgb: function(r, g, b, a) { return { r, g, b, a: a ?? 255 }; }, + + new: function(c, transp) { return { ...(c || {}), transp: transp ?? 0 }; }, + + r: function(c) { return c?.r ?? 0; }, + + g: function(c) { return c?.g ?? 0; }, + + b: function(c) { return c?.b ?? 0; }, + + red: { r: 255, g: 0, b: 0, a: 255 }, + + green: { r: 0, g: 255, b: 0, a: 255 }, + + blue: { r: 0, g: 0, b: 255, a: 255 }, + + gray: { r: 128, g: 128, b: 128, a: 255 }, + +}; + + +pinescript.size = { small: "small", normal: "normal", large: "large" }; + +pinescript.shape = { triangleup: "triangleup", triangledown: "triangledown", circle: "circle", square: "square" }; + +pinescript.location = { belowbar: "belowbar", abovebar: "abovebar" }; + +pinescript.position = { top_right: "top_right", top_left: "top_left", bottom_right: "bottom_right", bottom_left: "bottom_left" }; + +pinescript.text = { align_center: "center" }; + + +pinescript.table = { + + new: function(position, columns, rows, opts) { return pinescript.__decDraw({ position, columns, rows, opts: opts || {}, cells: [] }); }, + + cell: function(table, column, row, text, opts) { + + if (!table) return null; + + table.cells.push({ column, row, text, opts: opts || {} }); + + return null; + + } + +}; + + +// Script input parameters and their defaults + + +// The transpiled script logic, called once per bar + +function main() { + + globalThis.__pineState = globalThis.__pineState || {}; + const state = globalThis.__pineState["main"] = globalThis.__pineState["main"] || {}; + open = pinescript.asSeries(globalThis.open); + high = pinescript.asSeries(globalThis.high); + low = pinescript.asSeries(globalThis.low); + close = pinescript.asSeries(globalThis.close); + volume = pinescript.asSeries(globalThis.volume); + time = pinescript.asSeries(globalThis.time); + null; + // Study: Quantum Structure | jjart + // Options: {"overlay":true,"max_boxes_count":500,"max_lines_count":500,"max_labels_count":500} + var current_tf_minutes = (timeframe.in_seconds() / 60); + var is_ltf_15 = (current_tf_minutes <= 15); + var input_htf = input.timeframe("60", "Base Higher Timeframe (Auto-adjusts)", ({ group: "Multi-Timeframe" })); + var resolved_htf = ((current_tf_minutes >= 240) ? "D" : ((current_tf_minutes >= 60) ? "240" : input_htf)); + var profile = pinescript.inputString("Scalping", "Auto Profile", ({ options: ["Scalping", "Intraday", "Swing"], group: "Core Settings", tooltip: "Scalping - my default. Intraday = longer trades. Swing = slower, cleaner structure - multi day trades." })); + function get_profiled_swing_length() { + if ((profile === "Scalping")) { + if ((current_tf_minutes <= 5)) { + 4; + } + else if ((current_tf_minutes <= 15)) { + 5; + } + else if ((current_tf_minutes <= 30)) { + 8; + } + else if ((current_tf_minutes <= 60)) { + 5; + } + else if ((current_tf_minutes <= 240)) { + 3; + } + else { + 2; + } + } else { + if ((profile === "Swing")) { + if ((current_tf_minutes <= 5)) { + 4; + } + else if ((current_tf_minutes <= 15)) { + 5; + } + else if ((current_tf_minutes <= 30)) { + 17; + } + else if ((current_tf_minutes <= 60)) { + 10; + } + else if ((current_tf_minutes <= 240)) { + 7; + } + else { + 4; + } + } else { + if ((current_tf_minutes <= 5)) { + 4; + } + else if ((current_tf_minutes <= 15)) { + 5; + } + else if ((current_tf_minutes <= 30)) { + 12; + } + else if ((current_tf_minutes <= 60)) { + 7; + } + else if ((current_tf_minutes <= 240)) { + 5; + } + else { + 3; + } + } + } + } + function get_profiled_consolidation_bars() { + if ((profile === "Scalping")) { + if ((current_tf_minutes <= 5)) { + 35; + } + else if ((current_tf_minutes <= 15)) { + 21; + } + else if ((current_tf_minutes <= 30)) { + 13; + } + else if ((current_tf_minutes <= 60)) { + 8; + } + else if ((current_tf_minutes <= 240)) { + 6; + } + else { + 3; + } + } else { + if ((profile === "Swing")) { + if ((current_tf_minutes <= 5)) { + 70; + } + else if ((current_tf_minutes <= 15)) { + 42; + } + else if ((current_tf_minutes <= 30)) { + 25; + } + else if ((current_tf_minutes <= 60)) { + 17; + } + else if ((current_tf_minutes <= 240)) { + 11; + } + else { + 7; + } + } else { + if ((current_tf_minutes <= 5)) { + 50; + } + else if ((current_tf_minutes <= 15)) { + 30; + } + else if ((current_tf_minutes <= 30)) { + 18; + } + else if ((current_tf_minutes <= 60)) { + 12; + } + else if ((current_tf_minutes <= 240)) { + 8; + } + else { + 5; + } + } + } + } + function f_tfPretty(tf) { + switch (tf) { + case "1": + { + "1m"; + break; + } + case "3": + { + "3m"; + break; + } + case "5": + { + "5m"; + break; + } + case "15": + { + "15m"; + break; + } + case "30": + { + "30m"; + break; + } + case "45": + { + "45m"; + break; + } + case "60": + { + "1H"; + break; + } + case "120": + { + "2H"; + break; + } + case "180": + { + "3H"; + break; + } + case "240": + { + "4H"; + break; + } + case "D": + { + "1D"; + break; + } + case "W": + { + "1W"; + break; + } + case "M": + { + "1M"; + break; + } + default: + { + tf; + break; + } + } + } + var use_htf = pinescript.inputBool(false, "Show Higher Timeframe Support/Resistance Boxes", ({ group: "Multi-Timeframe" })); + var align_entries_with_htf = pinescript.inputBool(false, "Filter Entries by HTF Direction", ({ group: "Multi-Timeframe", tooltip: "When enabled, continuation entries only count as valid if they align with the HTF ICC direction." })); + var use_manual_settings = pinescript.inputBool(false, "Use Manual Timeframe Settings", ({ group: "Core Settings", tooltip: "Override Auto Profile --when checked, uses the manual Swing Length / Consolidation Bars (adjust below) instead of the Auto Profile values." })); + var manual_swing_length = pinescript.inputInt(7, "Manual Swing Length", ({ minval: 3, maxval: 50, group: "Core Settings" })); + var manual_consolidation_bars = pinescript.inputInt(20, "Manual Consolidation Bars", ({ minval: 3, maxval: 100, group: "Core Settings" })); + var consol_wiggle_pct = pinescript.inputFloat(0.2, "Consolidation Wiggle (%)", ({ minval: 0, maxval: 5, group: "Core Settings", tooltip: "Extra % wiggle around last swing high/low for consolidation detection" })); + var swing_length = (use_manual_settings ? manual_swing_length : get_profiled_swing_length()); + var consolidation_bars = (use_manual_settings ? manual_consolidation_bars : get_profiled_consolidation_bars()); + function get_profiled_wiggle_pct() { + if ((profile === "Entry")) { + return 0.4; + } else { + if ((profile === "Scalping")) { + return 0.3; + } else { + if ((profile === "Intraday")) { + return 0.2; + } else { + if ((profile === "Swing")) { + return 0.1; + } else { + return 0.2; + } + } + } + } + } + var wiggle_pct = (use_manual_settings ? consol_wiggle_pct : get_profiled_wiggle_pct()); + var show_tp_sl = pinescript.inputBool(true, "Show TP/SL Lines", ({ group: "Risk Management", tooltip: "TP/SL lines preview live intrabar and may change until the bar closes. After a confirmed continuation entry, the TP/SL levels are frozen for a short window." })); + var use_structure_stop = pinescript.inputBool(true, "Use Structure Based Stop and Take Profit", ({ group: "Risk Management", tooltip: "If true, SL is below last swing low for longs and above last swing high for shorts. If false or unavailable, ATR-based stop is used." })); + var structure_sl_factor = pinescript.inputFloat(1, " - Structure SL Tightness", ({ minval: 0.1, maxval: 1, step: 0.05, group: "Risk Management", tooltip: "1.0 = SL at swing high/low. 0.5 = halfway between entry and swing. Smaller = tighter structure stop." })); + var max_structure_atr_mult = pinescript.inputFloat(2, " - Max Structure SL (ATR)", ({ minval: 0.1, maxval: 10, step: 0.1, group: "Risk Management", tooltip: "Caps the structure-based SL so it is never farther than this many ATRs away from the entry price." })); + var sl_atr_mult = pinescript.inputFloat(1, "ATR SL Multiple", ({ minval: 0.1, step: 0.1, group: "Risk Management", tooltip: "Only in effect if Structure Based is unchecked above - stop loss and take profit based on multiples of the current Average True Range (ATR)" })); + var tp1_r_mult = pinescript.inputFloat(1, " - ATR Take Profit 1 (R)", ({ minval: 0.1, step: 0.1, group: "Risk Management" })); + var tp2_r_mult = pinescript.inputFloat(2, " - ATR Take Profit 2 (R)", ({ minval: 0.1, step: 0.1, group: "Risk Management" })); + var show_reversal_entries = pinescript.inputBool(false, "Show Reversal Entries", ({ group: "Display", tooltip: "Show counter-trend reversal entries (⚠BUY / ⚠SELL) when price breaks out of correction range in opposite direction. - Flag display only, does not show TP/SL lines or change overall structure until it confirms itself." })); + var MAX_HIST_ZONES = 10; + var show_indication_labels = true; + var show_zones = true; + var num_historical_zones = pinescript.inputInt(5, "Number of Historical S/R Zones", ({ minval: 0, maxval: MAX_HIST_ZONES, group: "Display", tooltip: "Number of additional historical zones in addition to the current zone" })); + var show_signals = true; + var show_phase_background = pinescript.inputBool(true, "Show Phase Background", ({ group: "Display", tooltip: "Toggle ICC phase background shading (bull-green, bear-red, neutral-grey, entry-white) on or off." })); + var phase_bg_transparency = pinescript.inputInt(90, "Phase Background Transparency", ({ minval: 80, maxval: 99, group: "Display", tooltip: "Transparency of background zone colors, set to 99 for no background shading" })); + var auto_hide_phase_bg = (current_tf_minutes <= 15); + var alert_freq_opt = pinescript.inputString("Once Per Bar Close", "Alert Frequency", ({ options: ["Every Tick", "Once Per Bar", "Once Per Bar Close"], group: "Alerts", tooltip: "Controls how often ICC entry alerts can fire." })); + var alert_tz = pinescript.inputString("America/Chicago", "Alert Timezone", ({ options: ["America/Chicago", "America/New_York", "Etc/UTC", "Exchange"], group: "Alerts" })); + function f_pad2(v) { + return pinescript.strFormat("{0,number,00}", v); + } + function f_ts(ts) { + var tz = ((alert_tz === "Exchange") ? syminfo.timezone : alert_tz); + return ((((((((((pinescript.strToString(pinescript.year(ts, tz)) + "-") + f_pad2(pinescript.month(ts, tz))) + "-") + f_pad2(pinescript.dayofmonth(ts, tz))) + " ") + f_pad2(pinescript.hour(ts, tz))) + ":") + f_pad2(pinescript.minute(ts, tz))) + ":") + f_pad2(pinescript.second(ts, tz))); + } + var htf_bullish_color = pinescript.inputColor(pinescript.color.new(pinescript.color.blue, 0), "HTF Bullish Color", ({ group: "Multi-Timeframe" })); + var htf_bearish_color = pinescript.inputColor(pinescript.color.new(pinescript.color.purple, 0), "HTF Bearish Color", ({ group: "Multi-Timeframe" })); + var zone_transparency = 75; + var bullish_color = pinescript.color.new(pinescript.color.green, 0); + var bearish_color = pinescript.color.new(pinescript.color.red, 0); + var zone_bull_color = pinescript.color.new(pinescript.color.green, zone_transparency); + var zone_bear_color = pinescript.color.new(pinescript.color.red, zone_transparency); + var cont_bg_tan = pinescript.color.new(pinescript.color.rgb(243, 212, 164), phase_bg_transparency); + var swing_high = pinescript.ta.pivothigh(high, swing_length, swing_length); + var swing_low = pinescript.ta.pivotlow(low, swing_length, swing_length); + var atr_value = pinescript.atr(14); + var atr_percent = (atr_value / close); + function get_zone_buffer(price) { + var base_buffer = (price * 0.0003); + return (base_buffer * (1 + (atr_percent * 10))); + } + if (state.last_swing_high === undefined) state.last_swing_high = null; + if (state.last_swing_low === undefined) state.last_swing_low = null; + if (state.prev_swing_high === undefined) state.prev_swing_high = null; + if (state.prev_swing_low === undefined) state.prev_swing_low = null; + if (state.prev_bullish_indication === undefined) state.prev_bullish_indication = null; + if (state.prev_bearish_indication === undefined) state.prev_bearish_indication = null; + if (state.last_high_bar === undefined) state.last_high_bar = null; + if (state.last_low_bar === undefined) state.last_low_bar = null; + if (state.bars_since_last_structure === undefined) state.bars_since_last_structure = 0; + if (state.bars_in_correction === undefined) state.bars_in_correction = 0; + if (state.bars_in_continuation === undefined) state.bars_in_continuation = 0; + if (state.correction_range_high === undefined) state.correction_range_high = null; + if (state.correction_range_low === undefined) state.correction_range_low = null; + if (state.market_structure === undefined) state.market_structure = "no_setup"; + if (state.no_setup_label === undefined) state.no_setup_label = "No Setup"; + if (state.last_trend_dir === undefined) state.last_trend_dir = null; + if (state.indication_detected === undefined) state.indication_detected = false; + if (state.indication_level === undefined) state.indication_level = null; + if (state.indication_type === undefined) state.indication_type = null; + if (state.correction_phase === undefined) state.correction_phase = false; + if (state.last_entry_type === undefined) state.last_entry_type = null; + if (state.prev_profile === undefined) state.prev_profile = profile; + var profile_changed = (profile !== state.prev_profile); + if (profile_changed) { + state.prev_profile = profile; + state.correction_phase = false; + state.correction_range_high = null; + state.correction_range_low = null; + state.bars_in_correction = 0; + state.bars_in_continuation = 0; + state.indication_detected = false; + state.market_structure = "no_setup"; + } + if (state.last_entry_price === undefined) state.last_entry_price = null; + if (state.last_stop_loss === undefined) state.last_stop_loss = null; + if (state.last_tp1_price === undefined) state.last_tp1_price = null; + if (state.last_tp2_price === undefined) state.last_tp2_price = null; + if (state.sl_line === undefined) state.sl_line = null; + if (state.tp1_line === undefined) state.tp1_line = null; + if (state.tp2_line === undefined) state.tp2_line = null; + if (state.sl_label === undefined) state.sl_label = null; + if (state.tp1_label === undefined) state.tp1_label = null; + if (state.tp2_label === undefined) state.tp2_label = null; + if (state.entry_line === undefined) state.entry_line = null; + if (state.entry_label === undefined) state.entry_label = null; + if (state.cont_active === undefined) state.cont_active = false; + if (!pinescript.na(swing_high)) { + state.prev_swing_high = state.last_swing_high; + state.last_swing_high = swing_high; + state.last_high_bar = (bar_index - swing_length); + state.bars_since_last_structure = 0; + } else { + state.bars_since_last_structure = 1; + } + if (!pinescript.na(swing_low)) { + state.prev_swing_low = state.last_swing_low; + state.last_swing_low = swing_low; + state.last_low_bar = (bar_index - swing_length); + state.bars_since_last_structure = 0; + } + var higher_high = ((!pinescript.na(state.prev_swing_high) && !pinescript.na(swing_high)) && (swing_high > state.prev_swing_high)); + var lower_low = ((!pinescript.na(state.prev_swing_low) && !pinescript.na(swing_low)) && (swing_low < state.prev_swing_low)); + var lower_high = ((!pinescript.na(state.prev_swing_high) && !pinescript.na(swing_high)) && (swing_high < state.prev_swing_high)); + var higher_low = ((!pinescript.na(state.prev_swing_low) && !pinescript.na(swing_low)) && (swing_low > state.prev_swing_low)); + var bullish_indication = (!pinescript.na(swing_high) && higher_high); + var bearish_indication = (!pinescript.na(swing_low) && lower_low); + var in_consolidation = (((state.bars_since_last_structure >= consolidation_bars) && !pinescript.na(state.last_swing_high)) && !pinescript.na(state.last_swing_low)); + var upper_band = (pinescript.na(state.last_swing_high) ? null : (state.last_swing_high * (1 + (wiggle_pct / 100)))); + var lower_band = (pinescript.na(state.last_swing_low) ? null : (state.last_swing_low * (1 - (wiggle_pct / 100)))); + var price_in_range = (((!pinescript.na(upper_band) && !pinescript.na(lower_band)) && (close <= upper_band)) && (close >= lower_band)); + var is_consolidating = (in_consolidation && price_in_range); + if (bullish_indication) { + state.prev_bullish_indication = state.last_swing_high; + state.indication_detected = true; + state.indication_level = state.last_swing_high; + state.indication_type = "bullish"; + state.correction_phase = false; + state.market_structure = "bullish_indication"; + state.bars_in_correction = 0; + state.correction_range_high = null; + state.correction_range_low = null; + state.last_entry_type = null; + state.last_trend_dir = "bullish"; + state.no_setup_label = "No Setup"; + } + if (bearish_indication) { + state.prev_bearish_indication = state.last_swing_low; + state.indication_detected = true; + state.indication_level = state.last_swing_low; + state.indication_type = "bearish"; + state.correction_phase = false; + state.market_structure = "bearish_indication"; + state.bars_in_correction = 0; + state.correction_range_high = null; + state.correction_range_low = null; + state.last_entry_type = null; + state.last_trend_dir = "bearish"; + state.no_setup_label = "No Setup"; + } + if ((state.correction_phase && (state.indication_type === "bearish"))) { + if ((!pinescript.na(state.prev_bullish_indication) && (close > state.prev_bullish_indication))) { + state.indication_detected = false; + state.correction_phase = false; + state.market_structure = "no_setup"; + state.no_setup_label = "Breaking Structure"; + state.bars_in_correction = 0; + state.correction_range_high = null; + state.correction_range_low = null; + } else { + if (((!pinescript.na(swing_high) && !pinescript.na(state.prev_bullish_indication)) && (swing_high > state.prev_bullish_indication))) { + state.prev_bullish_indication = state.last_swing_high; + state.indication_detected = true; + state.indication_level = state.last_swing_high; + state.indication_type = "bullish"; + state.correction_phase = false; + state.market_structure = "bullish_indication"; + state.bars_in_correction = 0; + state.correction_range_high = null; + state.correction_range_low = null; + } + } + } + if ((state.correction_phase && (state.indication_type === "bullish"))) { + if ((!pinescript.na(state.prev_bearish_indication) && (close < state.prev_bearish_indication))) { + state.indication_detected = false; + state.correction_phase = false; + state.market_structure = "no_setup"; + state.no_setup_label = "Breaking Structure"; + state.bars_in_correction = 0; + state.correction_range_high = null; + state.correction_range_low = null; + } else { + if (((!pinescript.na(swing_low) && !pinescript.na(state.prev_bearish_indication)) && (swing_low < state.prev_bearish_indication))) { + state.prev_bearish_indication = state.last_swing_low; + state.indication_detected = true; + state.indication_level = state.last_swing_low; + state.indication_type = "bearish"; + state.correction_phase = false; + state.market_structure = "bearish_indication"; + state.bars_in_correction = 0; + state.correction_range_high = null; + state.correction_range_low = null; + } + } + } + if ((is_consolidating && !state.indication_detected)) { + state.market_structure = "consolidation"; + state.indication_detected = false; + state.correction_phase = false; + state.indication_level = null; + state.indication_type = null; + state.last_entry_type = null; + } + var neutral_consolidation = (state.market_structure === "consolidation"); + var live_buf = pinescript.max((syminfo.mintick * 2), (atr_value * 0.05)); + var idle_state = ((!state.indication_detected && !state.correction_phase) && !state.cont_active); + var break_struct_live = (idle_state && ((((state.last_trend_dir === "bullish") && !pinescript.na(state.last_swing_low)) && (close < (state.last_swing_low - live_buf))) || (((state.last_trend_dir === "bearish") && !pinescript.na(state.last_swing_high)) && (close > (state.last_swing_high + live_buf))))); + var trend_drift_live = (((idle_state && !break_struct_live) && (((state.last_trend_dir === "bullish") && !pinescript.na(state.last_swing_high)) && (close > (state.last_swing_high + live_buf)))) || (((state.last_trend_dir === "bearish") && !pinescript.na(state.last_swing_low)) && (close < (state.last_swing_low - live_buf)))); + var phase_display_override = null; + if (break_struct_live) { + phase_display_override = "Breaking Structure (Live)"; + } else { + if (trend_drift_live) { + phase_display_override = ((state.last_trend_dir === "bullish") ? "Trend Drift (Live Bull)" : "Trend Drift (Live Bear)"); + } + } + var in_no_setup_state = (((!state.indication_detected && !state.correction_phase) && !neutral_consolidation) && !state.cont_active); + if (in_no_setup_state) { + if (((!pinescript.na(swing_high) && higher_high) && (state.last_trend_dir === "bullish"))) { + state.no_setup_label = "Trend Drift (Bull)"; + } + if (((!pinescript.na(swing_low) && lower_low) && (state.last_trend_dir === "bearish"))) { + state.no_setup_label = "Trend Drift (Bear)"; + } + } + if ((state.market_structure !== "no_setup")) { + state.no_setup_label = "No Setup"; + } + if ((state.indication_detected && !state.correction_phase)) { + if (((state.indication_type === "bullish") && (close < state.indication_level))) { + state.correction_phase = true; + state.market_structure = "bullish_correction"; + state.bars_in_correction = 0; + state.correction_range_high = state.last_swing_high; + state.correction_range_low = state.last_swing_low; + } + if (((state.indication_type === "bearish") && (close > state.indication_level))) { + state.correction_phase = true; + state.market_structure = "bearish_correction"; + state.bars_in_correction = 0; + state.correction_range_high = state.last_swing_high; + state.correction_range_low = state.last_swing_low; + } + } + if (state.correction_phase) { + if ((!pinescript.na(state.last_swing_high) && !pinescript.na(state.last_swing_low))) { + state.correction_range_high = state.last_swing_high; + state.correction_range_low = state.last_swing_low; + } + state.bars_in_correction = 1; + state.bars_in_continuation = 0; + } else { + if (state.cont_active) { + state.bars_in_continuation = 1; + state.bars_in_correction = 0; + } else { + state.bars_in_correction = 0; + state.bars_in_continuation = 0; + } + } + if (state.left_correction_box === undefined) state.left_correction_box = false; + if (!state.correction_phase) { + state.left_correction_box = false; + } else { + if ((!pinescript.na(state.correction_range_high) && !pinescript.na(state.correction_range_low))) { + if (((high > state.correction_range_high) || (low < state.correction_range_low))) { + state.left_correction_box = true; + } + } + } + var correction_consolidation = ((((state.correction_phase && !state.left_correction_box) && (state.bars_in_correction >= consolidation_bars)) && !pinescript.na(state.correction_range_high)) && !pinescript.na(state.correction_range_low)); + var bullish_entry_traditional = ((((state.indication_detected && state.correction_phase) && (state.indication_type === "bullish")) && (close > state.indication_level)) && (pinescript.hist(0, close, 1) <= state.indication_level)); + var bearish_entry_traditional = ((((state.indication_detected && state.correction_phase) && (state.indication_type === "bearish")) && (close < state.indication_level)) && (pinescript.hist(1, close, 1) >= state.indication_level)); + var bullish_entry_breakout = (((((state.indication_detected && state.correction_phase) && (state.indication_type === "bullish")) && !pinescript.na(state.correction_range_high)) && (close > state.correction_range_high)) && (pinescript.hist(2, close, 1) <= state.correction_range_high)); + var bearish_entry_breakout = (((((state.indication_detected && state.correction_phase) && (state.indication_type === "bearish")) && !pinescript.na(state.correction_range_low)) && (close < state.correction_range_low)) && (pinescript.hist(3, close, 1) >= state.correction_range_low)); + var bullish_entry_reversal = (((((state.indication_detected && state.correction_phase) && (state.indication_type === "bearish")) && !pinescript.na(state.correction_range_high)) && (close > state.correction_range_high)) && (pinescript.hist(4, close, 1) <= state.correction_range_high)); + var bearish_entry_reversal = (((((state.indication_detected && state.correction_phase) && (state.indication_type === "bullish")) && !pinescript.na(state.correction_range_low)) && (close < state.correction_range_low)) && (pinescript.hist(5, close, 1) >= state.correction_range_low)); + var bullish_entry_cont = (bullish_entry_traditional || bullish_entry_breakout); + var bearish_entry_cont = (bearish_entry_traditional || bearish_entry_breakout); + var [htf_indication_level_entry, htf_indication_type_entry] = pinescript.unpack(pinescript.requestSecurity(syminfo.tickerid, resolved_htf, [state.indication_level, state.indication_type], ({ lookahead: barmerge.lookahead_off })), 2); + var htf_bullish = (htf_indication_type_entry === "bullish"); + var htf_bearish = (htf_indication_type_entry === "bearish"); + var htf_ok_bull = (!align_entries_with_htf || htf_bullish); + var htf_ok_bear = (!align_entries_with_htf || htf_bearish); + var bullish_entry_cont_allowed = (bullish_entry_cont && htf_ok_bull); + var bearish_entry_cont_allowed = (bearish_entry_cont && htf_ok_bear); + if (state.last_cont_entry_bar === undefined) state.last_cont_entry_bar = null; + var tp_visible_bars = 2; + var bull_cont_fire = (bullish_entry_cont_allowed && show_signals); + var bear_cont_fire = (bearish_entry_cont_allowed && show_signals); + var any_entry_candidate = (bull_cont_fire || bear_cont_fire); + function f_calc_preview_tpsl(_dir) { + var _entry = close; + var _sl = null; + var _tp1 = null; + var _tp2 = null; + if (use_structure_stop) { + if (((_dir === "bullish") && !pinescript.na(state.last_swing_low))) { + var raw_dist = pinescript.abs((_entry - state.last_swing_low)); + var struct_dist = (raw_dist * structure_sl_factor); + var max_dist = (atr_value * max_structure_atr_mult); + var final_dist = pinescript.min(struct_dist, max_dist); + _sl = (_entry - final_dist); + } else { + if (((_dir === "bearish") && !pinescript.na(state.last_swing_high))) { + raw_dist = pinescript.abs((state.last_swing_high - _entry)); + struct_dist = (raw_dist * structure_sl_factor); + max_dist = (atr_value * max_structure_atr_mult); + final_dist = pinescript.min(struct_dist, max_dist); + _sl = (_entry + final_dist); + } + } + } + if (pinescript.na(_sl)) { + _sl = ((_dir === "bullish") ? (_entry - (atr_value * sl_atr_mult)) : (_entry + (atr_value * sl_atr_mult))); + } + var _risk = pinescript.abs((_entry - _sl)); + if ((_dir === "bullish")) { + _tp1 = (_entry + (_risk * tp1_r_mult)); + _tp2 = (_entry + (_risk * tp2_r_mult)); + } else { + _tp1 = (_entry - (_risk * tp1_r_mult)); + _tp2 = (_entry - (_risk * tp2_r_mult)); + } + return [_entry, _sl, _tp1, _tp2]; + } + if (state.preview_active === undefined) state.preview_active = false; + if (any_entry_candidate) { + var cand_dir = (bull_cont_fire ? "bullish" : "bearish"); + var [p_entry, p_sl, p_tp1, p_tp2] = pinescript.unpack(f_calc_preview_tpsl(cand_dir), 4); + state.last_entry_price = p_entry; + state.last_stop_loss = p_sl; + state.last_tp1_price = p_tp1; + state.last_tp2_price = p_tp2; + if (show_tp_sl) { + if (!state.preview_active) { + if (!pinescript.na(state.entry_line)) { + pinescript.lineDelete(state.entry_line); + } + if (!pinescript.na(state.sl_line)) { + pinescript.lineDelete(state.sl_line); + } + if (!pinescript.na(state.tp1_line)) { + pinescript.lineDelete(state.tp1_line); + } + if (!pinescript.na(state.tp2_line)) { + pinescript.lineDelete(state.tp2_line); + } + if (!pinescript.na(state.entry_label)) { + pinescript.labelDelete(state.entry_label); + } + if (!pinescript.na(state.sl_label)) { + pinescript.labelDelete(state.sl_label); + } + if (!pinescript.na(state.tp1_label)) { + pinescript.labelDelete(state.tp1_label); + } + if (!pinescript.na(state.tp2_label)) { + pinescript.labelDelete(state.tp2_label); + } + state.entry_line = pinescript.lineNew(bar_index, state.last_entry_price, (bar_index + 50), state.last_entry_price, ({ color: pinescript.color.new(pinescript.color.blue, 0), width: 2 })); + state.entry_label = pinescript.labelNew((bar_index + 50), state.last_entry_price, "ENTRY", ({ style: label.style_label_right, textcolor: pinescript.color.white, color: pinescript.color.new(pinescript.color.blue, 70), size: pinescript.size.small })); + state.sl_line = pinescript.lineNew(bar_index, state.last_stop_loss, (bar_index + 50), state.last_stop_loss, ({ color: pinescript.color.new(pinescript.color.red, 0), width: 2, style: line.style_dashed })); + state.sl_label = pinescript.labelNew((bar_index + 50), state.last_stop_loss, "SL", ({ style: label.style_label_right, textcolor: pinescript.color.white, color: pinescript.color.new(pinescript.color.red, 80), size: pinescript.size.small })); + state.tp1_line = pinescript.lineNew(bar_index, state.last_tp1_price, (bar_index + 50), state.last_tp1_price, ({ color: pinescript.color.new(pinescript.color.green, 0), width: 2, style: line.style_dashed })); + state.tp1_label = pinescript.labelNew((bar_index + 50), state.last_tp1_price, "TP1", ({ style: label.style_label_right, textcolor: pinescript.color.black, color: pinescript.color.new(pinescript.color.green, 70), size: pinescript.size.small })); + state.tp2_line = pinescript.lineNew(bar_index, state.last_tp2_price, (bar_index + 50), state.last_tp2_price, ({ color: pinescript.color.new(pinescript.color.green, 0), width: 2, style: line.style_dotted })); + state.tp2_label = pinescript.labelNew((bar_index + 50), state.last_tp2_price, "TP2", ({ style: label.style_label_right, textcolor: pinescript.color.black, color: pinescript.color.new(pinescript.color.green, 85), size: pinescript.size.small })); + state.preview_active = true; + } + line.set_x1(state.entry_line, bar_index); + line.set_x2(state.entry_line, (bar_index + 50)); + line.set_y1(state.entry_line, state.last_entry_price); + line.set_y2(state.entry_line, state.last_entry_price); + label.set_x(state.entry_label, (bar_index + 50)); + label.set_y(state.entry_label, state.last_entry_price); + line.set_x1(state.sl_line, bar_index); + line.set_x2(state.sl_line, (bar_index + 50)); + line.set_y1(state.sl_line, state.last_stop_loss); + line.set_y2(state.sl_line, state.last_stop_loss); + label.set_x(state.sl_label, (bar_index + 50)); + label.set_y(state.sl_label, state.last_stop_loss); + line.set_x1(state.tp1_line, bar_index); + line.set_x2(state.tp1_line, (bar_index + 50)); + line.set_y1(state.tp1_line, state.last_tp1_price); + line.set_y2(state.tp1_line, state.last_tp1_price); + label.set_x(state.tp1_label, (bar_index + 50)); + label.set_y(state.tp1_label, state.last_tp1_price); + line.set_x1(state.tp2_line, bar_index); + line.set_x2(state.tp2_line, (bar_index + 50)); + line.set_y1(state.tp2_line, state.last_tp2_price); + line.set_y2(state.tp2_line, state.last_tp2_price); + label.set_x(state.tp2_label, (bar_index + 50)); + label.set_y(state.tp2_label, state.last_tp2_price); + } + } else { + if (state.preview_active) { + if (!pinescript.na(state.entry_line)) { + pinescript.lineDelete(state.entry_line); + state.entry_line = null; + } + if (!pinescript.na(state.sl_line)) { + pinescript.lineDelete(state.sl_line); + state.sl_line = null; + } + if (!pinescript.na(state.tp1_line)) { + pinescript.lineDelete(state.tp1_line); + state.tp1_line = null; + } + if (!pinescript.na(state.tp2_line)) { + pinescript.lineDelete(state.tp2_line); + state.tp2_line = null; + } + if (!pinescript.na(state.entry_label)) { + pinescript.labelDelete(state.entry_label); + state.entry_label = null; + } + if (!pinescript.na(state.sl_label)) { + pinescript.labelDelete(state.sl_label); + state.sl_label = null; + } + if (!pinescript.na(state.tp1_label)) { + pinescript.labelDelete(state.tp1_label); + state.tp1_label = null; + } + if (!pinescript.na(state.tp2_label)) { + pinescript.labelDelete(state.tp2_label); + state.tp2_label = null; + } + state.preview_active = false; + } + } + var any_entry_confirmed = ((bull_cont_fire || bear_cont_fire) && barstate.isconfirmed); + if (any_entry_confirmed) { + state.indication_detected = false; + state.correction_phase = false; + state.cont_active = true; + state.last_cont_entry_bar = bar_index; + state.last_entry_type = (bull_cont_fire ? "bullish" : "bearish"); + state.last_trend_dir = state.last_entry_type; + state.no_setup_label = "No Setup"; + state.bars_in_correction = 0; + state.bars_in_continuation = 0; + state.correction_range_high = null; + state.correction_range_low = null; + state.preview_active = false; + } + if ((state.cont_active && !pinescript.na(state.last_entry_type))) { + var exit_now = false; + if ((state.last_entry_type === "bullish")) { + var hit_sl = (!pinescript.na(state.last_stop_loss) && (low <= state.last_stop_loss)); + var hit_tp2 = (!pinescript.na(state.last_tp2_price) && (high >= state.last_tp2_price)); + if (((bearish_indication || hit_sl) || hit_tp2)) { + exit_now = true; + } + } else { + if ((state.last_entry_type === "bearish")) { + hit_sl = (!pinescript.na(state.last_stop_loss) && (high >= state.last_stop_loss)); + hit_tp2 = (!pinescript.na(state.last_tp2_price) && (low <= state.last_tp2_price)); + if (((bullish_indication || hit_sl) || hit_tp2)) { + exit_now = true; + } + } + } + if (exit_now) { + state.cont_active = false; + state.last_entry_type = null; + } + } + var tp_window_active = ((show_tp_sl && !pinescript.na(state.last_cont_entry_bar)) && ((bar_index - state.last_cont_entry_bar) <= tp_visible_bars)); + if (tp_window_active) { + if (!pinescript.na(state.entry_line)) { + line.set_x1(state.entry_line, bar_index); + line.set_x2(state.entry_line, (bar_index + 50)); + line.set_y1(state.entry_line, state.last_entry_price); + line.set_y2(state.entry_line, state.last_entry_price); + if (!pinescript.na(state.entry_label)) { + label.set_x(state.entry_label, (bar_index + 50)); + label.set_y(state.entry_label, state.last_entry_price); + } + } + if (!pinescript.na(state.sl_line)) { + line.set_x1(state.sl_line, bar_index); + line.set_x2(state.sl_line, (bar_index + 50)); + line.set_y1(state.sl_line, state.last_stop_loss); + line.set_y2(state.sl_line, state.last_stop_loss); + if (!pinescript.na(state.sl_label)) { + label.set_x(state.sl_label, (bar_index + 50)); + label.set_y(state.sl_label, state.last_stop_loss); + } + } + if (!pinescript.na(state.tp1_line)) { + line.set_x1(state.tp1_line, bar_index); + line.set_x2(state.tp1_line, (bar_index + 50)); + line.set_y1(state.tp1_line, state.last_tp1_price); + line.set_y2(state.tp1_line, state.last_tp1_price); + if (!pinescript.na(state.tp1_label)) { + label.set_x(state.tp1_label, (bar_index + 50)); + label.set_y(state.tp1_label, state.last_tp1_price); + } + } + if (!pinescript.na(state.tp2_line)) { + line.set_x1(state.tp2_line, bar_index); + line.set_x2(state.tp2_line, (bar_index + 50)); + line.set_y1(state.tp2_line, state.last_tp2_price); + line.set_y2(state.tp2_line, state.last_tp2_price); + if (!pinescript.na(state.tp2_label)) { + label.set_x(state.tp2_label, (bar_index + 50)); + label.set_y(state.tp2_label, state.last_tp2_price); + } + } + } else { + if (!state.preview_active) { + if (!pinescript.na(state.entry_line)) { + pinescript.lineDelete(state.entry_line); + state.entry_line = null; + } + if (!pinescript.na(state.sl_line)) { + pinescript.lineDelete(state.sl_line); + state.sl_line = null; + } + if (!pinescript.na(state.tp1_line)) { + pinescript.lineDelete(state.tp1_line); + state.tp1_line = null; + } + if (!pinescript.na(state.tp2_line)) { + pinescript.lineDelete(state.tp2_line); + state.tp2_line = null; + } + if (!pinescript.na(state.entry_label)) { + pinescript.labelDelete(state.entry_label); + state.entry_label = null; + } + if (!pinescript.na(state.sl_label)) { + pinescript.labelDelete(state.sl_label); + state.sl_label = null; + } + if (!pinescript.na(state.tp1_label)) { + pinescript.labelDelete(state.tp1_label); + state.tp1_label = null; + } + if (!pinescript.na(state.tp2_label)) { + pinescript.labelDelete(state.tp2_label); + state.tp2_label = null; + } + state.last_cont_entry_bar = null; + } + } + var waiting_for_bullish_entry = (((state.indication_detected && state.correction_phase) && (state.indication_type === "bullish")) && (close <= state.indication_level)); + var waiting_for_bearish_entry = (((state.indication_detected && state.correction_phase) && (state.indication_type === "bearish")) && (close >= state.indication_level)); + pinescript.plotchar(((!is_ltf_15 && waiting_for_bullish_entry) && show_signals), ({ title: "Waiting Bullish", char: "↑", location: pinescript.location.belowbar, color: pinescript.color.new(pinescript.color.green, 50), size: pinescript.size.tiny })); + pinescript.plotchar(((!is_ltf_15 && waiting_for_bearish_entry) && show_signals), ({ title: "Waiting Bearish", char: "↓", location: pinescript.location.abovebar, color: pinescript.color.new(pinescript.color.red, 50), size: pinescript.size.tiny })); + pinescript.plotshape(swing_high, ({ title: "Swing High", location: pinescript.location.abovebar, color: bearish_color, style: pinescript.shape.triangledown, size: pinescript.size.small, offset: -swing_length })); + pinescript.plotshape(swing_low, ({ title: "Swing Low", location: pinescript.location.belowbar, color: bullish_color, style: pinescript.shape.triangleup, size: pinescript.size.small, offset: -swing_length })); + if ((bullish_indication && show_indication_labels)) { + pinescript.labelNew(state.last_high_bar, state.last_swing_high, "IND", ({ style: label.style_label_down, textcolor: pinescript.color.white, color: bullish_color, size: pinescript.size.small })); + } + if ((bearish_indication && show_indication_labels)) { + pinescript.labelNew(state.last_low_bar, state.last_swing_low, "IND", ({ style: label.style_label_up, textcolor: pinescript.color.white, color: bearish_color, size: pinescript.size.small })); + } + var ZN = (MAX_HIST_ZONES + 1); + if (state.resistance_boxes === undefined) state.resistance_boxes = pinescript.arrayNew(ZN); + if (state.resistance_levels === undefined) state.resistance_levels = pinescript.arrayNew(ZN, null); + if (state.resistance_start_bars === undefined) state.resistance_start_bars = pinescript.arrayNew(ZN, null); + if (state.resistance_hist_lines === undefined) state.resistance_hist_lines = pinescript.arrayNew(ZN); + if (state.support_boxes === undefined) state.support_boxes = pinescript.arrayNew(ZN); + if (state.support_levels === undefined) state.support_levels = pinescript.arrayNew(ZN, null); + if (state.support_start_bars === undefined) state.support_start_bars = pinescript.arrayNew(ZN, null); + if (state.support_hist_lines === undefined) state.support_hist_lines = pinescript.arrayNew(ZN); + if (state.consolidation_box === undefined) state.consolidation_box = null; + if (state.last_zone_edge_entry === undefined) state.last_zone_edge_entry = null; + if (state.last_zone_edge_entry_bar === undefined) state.last_zone_edge_entry_bar = null; + function f_hist_x2(start_i, start_prev) { + var baseLen = pinescript.max(1, (start_prev - start_i)); + var extBars = int(pinescript.round((baseLen * 0.15))); + return (start_prev + extBars); + } + function f_delete_box(b) { + if (!pinescript.na(b)) { + return pinescript.boxDelete(b); + } else { + return null; + } + } + function shift_resistance_zones() { + var last = (pinescript.arraySize(state.resistance_boxes) - 1); + var old_box = pinescript.arrayGet(state.resistance_boxes, last); + f_delete_box(old_box); + var old_ln = pinescript.arrayGet(state.resistance_hist_lines, last); + if (!pinescript.na(old_ln)) { + pinescript.lineDelete(old_ln); + pinescript.arraySet(state.resistance_hist_lines, last, null); + } + for (let i = last; i <= 1; i++) { + pinescript.arraySet(state.resistance_boxes, i, pinescript.arrayGet(state.resistance_boxes, (i - 1))); + pinescript.arraySet(state.resistance_levels, i, pinescript.arrayGet(state.resistance_levels, (i - 1))); + pinescript.arraySet(state.resistance_start_bars, i, pinescript.arrayGet(state.resistance_start_bars, (i - 1))); + pinescript.arraySet(state.resistance_hist_lines, i, pinescript.arrayGet(state.resistance_hist_lines, (i - 1))); + } + return pinescript.arraySet(state.resistance_hist_lines, 0, null); + } + function shift_support_zones() { + var last = (pinescript.arraySize(state.support_boxes) - 1); + var old_box = pinescript.arrayGet(state.support_boxes, last); + f_delete_box(old_box); + var old_ln = pinescript.arrayGet(state.support_hist_lines, last); + if (!pinescript.na(old_ln)) { + pinescript.lineDelete(old_ln); + pinescript.arraySet(state.support_hist_lines, last, null); + } + for (let i = last; i <= 1; i++) { + pinescript.arraySet(state.support_boxes, i, pinescript.arrayGet(state.support_boxes, (i - 1))); + pinescript.arraySet(state.support_levels, i, pinescript.arrayGet(state.support_levels, (i - 1))); + pinescript.arraySet(state.support_start_bars, i, pinescript.arrayGet(state.support_start_bars, (i - 1))); + pinescript.arraySet(state.support_hist_lines, i, pinescript.arrayGet(state.support_hist_lines, (i - 1))); + } + return pinescript.arraySet(state.support_hist_lines, 0, null); + } + if ((show_zones && !pinescript.na(swing_high))) { + shift_resistance_zones(); + var zone_buffer = get_zone_buffer(swing_high); + var new_box = pinescript.boxNew((bar_index - swing_length), (swing_high + zone_buffer), bar_index, (swing_high - zone_buffer), ({ bgcolor: zone_bear_color, border_color: bearish_color, border_width: 2 })); + var ln0 = pinescript.arrayGet(state.resistance_hist_lines, 0); + if (!pinescript.na(ln0)) { + pinescript.lineDelete(ln0); + pinescript.arraySet(state.resistance_hist_lines, 0, null); + } + pinescript.arraySet(state.resistance_boxes, 0, new_box); + pinescript.arraySet(state.resistance_levels, 0, swing_high); + pinescript.arraySet(state.resistance_start_bars, 0, (bar_index - swing_length)); + state.last_zone_edge_entry = null; + state.last_zone_edge_entry_bar = null; + } + if ((show_zones && !pinescript.na(swing_low))) { + shift_support_zones(); + zone_buffer = get_zone_buffer(swing_low); + new_box = pinescript.boxNew((bar_index - swing_length), (swing_low + zone_buffer), bar_index, (swing_low - zone_buffer), ({ bgcolor: zone_bull_color, border_color: bullish_color, border_width: 2 })); + ln0 = pinescript.arrayGet(state.support_hist_lines, 0); + if (!pinescript.na(ln0)) { + pinescript.lineDelete(ln0); + pinescript.arraySet(state.support_hist_lines, 0, null); + } + pinescript.arraySet(state.support_boxes, 0, new_box); + pinescript.arraySet(state.support_levels, 0, swing_low); + pinescript.arraySet(state.support_start_bars, 0, (bar_index - swing_length)); + state.last_zone_edge_entry = null; + state.last_zone_edge_entry_bar = null; + } + if (show_zones) { + var res0 = pinescript.arrayGet(state.resistance_boxes, 0); + if (!pinescript.na(res0)) { + box.set_right(res0, bar_index); + box.set_bgcolor(res0, zone_bear_color); + box.set_border_color(res0, bearish_color); + box.set_border_width(res0, 2); + } + var sup0 = pinescript.arrayGet(state.support_boxes, 0); + if (!pinescript.na(sup0)) { + box.set_right(sup0, bar_index); + box.set_bgcolor(sup0, zone_bull_color); + box.set_border_color(sup0, bullish_color); + box.set_border_width(sup0, 2); + } + var last = (pinescript.arraySize(state.resistance_levels) - 1); + var maxHist = pinescript.min(num_historical_zones, last); + for (let i = 1; i <= last; i++) { + var r_lvl = pinescript.arrayGet(state.resistance_levels, i); + var r_x1 = pinescript.arrayGet(state.resistance_start_bars, i); + var r_rep = pinescript.arrayGet(state.resistance_start_bars, (i - 1)); + var r_box = pinescript.arrayGet(state.resistance_boxes, i); + if (!pinescript.na(r_box)) { + pinescript.boxDelete(r_box); + pinescript.arraySet(state.resistance_boxes, i, null); + } + var r_ln = pinescript.arrayGet(state.resistance_hist_lines, i); + var r_ok = ((((i <= maxHist) && !pinescript.na(r_lvl)) && !pinescript.na(r_x1)) && !pinescript.na(r_rep)); + if (r_ok) { + var r_x2 = f_hist_x2(r_x1, r_rep); + if (pinescript.na(r_ln)) { + r_ln = pinescript.lineNew(r_x1, r_lvl, r_x2, r_lvl, ({ color: bearish_color, width: 4, style: line.style_dotted })); + pinescript.arraySet(state.resistance_hist_lines, i, r_ln); + } else { + line.set_x1(r_ln, r_x1); + line.set_x2(r_ln, r_x2); + line.set_y1(r_ln, r_lvl); + line.set_y2(r_ln, r_lvl); + line.set_color(r_ln, bearish_color); + line.set_style(r_ln, line.style_dotted); + line.set_width(r_ln, 4); + } + } else { + if (!pinescript.na(r_ln)) { + pinescript.lineDelete(r_ln); + pinescript.arraySet(state.resistance_hist_lines, i, null); + } + } + var s_lvl = pinescript.arrayGet(state.support_levels, i); + var s_x1 = pinescript.arrayGet(state.support_start_bars, i); + var s_rep = pinescript.arrayGet(state.support_start_bars, (i - 1)); + var s_box = pinescript.arrayGet(state.support_boxes, i); + if (!pinescript.na(s_box)) { + pinescript.boxDelete(s_box); + pinescript.arraySet(state.support_boxes, i, null); + } + var s_ln = pinescript.arrayGet(state.support_hist_lines, i); + var s_ok = ((((i <= maxHist) && !pinescript.na(s_lvl)) && !pinescript.na(s_x1)) && !pinescript.na(s_rep)); + if (s_ok) { + var s_x2 = f_hist_x2(s_x1, s_rep); + if (pinescript.na(s_ln)) { + s_ln = pinescript.lineNew(s_x1, s_lvl, s_x2, s_lvl, ({ color: bullish_color, width: 4, style: line.style_dotted })); + pinescript.arraySet(state.support_hist_lines, i, s_ln); + } else { + line.set_x1(s_ln, s_x1); + line.set_x2(s_ln, s_x2); + line.set_y1(s_ln, s_lvl); + line.set_y2(s_ln, s_lvl); + line.set_color(s_ln, bullish_color); + line.set_style(s_ln, line.style_dotted); + line.set_width(s_ln, 4); + } + } else { + if (!pinescript.na(s_ln)) { + pinescript.lineDelete(s_ln); + pinescript.arraySet(state.support_hist_lines, i, null); + } + } + } + } else { + last = (pinescript.arraySize(state.resistance_boxes) - 1); + for (let i = 0; i <= last; i++) { + var b = pinescript.arrayGet(state.resistance_boxes, i); + if (!pinescript.na(b)) { + pinescript.boxDelete(b); + pinescript.arraySet(state.resistance_boxes, i, null); + } + var l = pinescript.arrayGet(state.resistance_hist_lines, i); + if (!pinescript.na(l)) { + pinescript.lineDelete(l); + pinescript.arraySet(state.resistance_hist_lines, i, null); + } + var b2 = pinescript.arrayGet(state.support_boxes, i); + if (!pinescript.na(b2)) { + pinescript.boxDelete(b2); + pinescript.arraySet(state.support_boxes, i, null); + } + var l2 = pinescript.arrayGet(state.support_hist_lines, i); + if (!pinescript.na(l2)) { + pinescript.lineDelete(l2); + pinescript.arraySet(state.support_hist_lines, i, null); + } + } + } + var is_consolidating_overlay = correction_consolidation; + if ((is_consolidating_overlay && show_zones)) { + if (pinescript.na(state.consolidation_box)) { + state.consolidation_box = pinescript.boxNew((bar_index - state.bars_in_correction), state.correction_range_high, bar_index, state.correction_range_low, ({ bgcolor: pinescript.color.new(pinescript.color.gray, 70), border_color: pinescript.color.new(pinescript.color.gray, 20), border_width: 2, border_style: line.style_dashed })); + } else { + box.set_right(state.consolidation_box, bar_index); + box.set_top(state.consolidation_box, state.correction_range_high); + box.set_bottom(state.consolidation_box, state.correction_range_low); + } + } else { + if (!pinescript.na(state.consolidation_box)) { + pinescript.boxDelete(state.consolidation_box); + state.consolidation_box = null; + } + } + var _res0 = pinescript.arrayGet(state.resistance_boxes, 0); + var _sup0 = pinescript.arrayGet(state.support_boxes, 0); + var res_top = (!pinescript.na(_res0) ? box.get_top(_res0) : null); + var sup_bot = (!pinescript.na(_sup0) ? box.get_bottom(_sup0) : null); + var bull_entry_zone_edge = (((barstate.isconfirmed && !pinescript.na(res_top)) && (close > res_top)) && (pinescript.hist(6, close, 1) <= res_top)); + var bear_entry_zone_edge = (((barstate.isconfirmed && !pinescript.na(sup_bot)) && (close < sup_bot)) && (pinescript.hist(7, close, 1) >= sup_bot)); + if (bull_entry_zone_edge) { + state.last_zone_edge_entry = "BULL"; + state.last_zone_edge_entry_bar = bar_index; + } + if (bear_entry_zone_edge) { + state.last_zone_edge_entry = "BEAR"; + state.last_zone_edge_entry_bar = bar_index; + } + var [htf_indication_level, htf_indication_type, htf_market_structure, htf_last_swing_high, htf_last_swing_low] = pinescript.unpack(pinescript.requestSecurity(syminfo.tickerid, resolved_htf, [state.indication_level, state.indication_type, state.market_structure, state.last_swing_high, state.last_swing_low], ({ lookahead: barmerge.lookahead_off })), 5); + if (state.htf_bias_label === undefined) state.htf_bias_label = null; + var show_htf_bias = (((use_htf && show_zones) && !pinescript.na(htf_indication_level)) && !pinescript.na(htf_indication_type)); + if (show_htf_bias) { + var htf_y = htf_indication_level; + var bias_txt = ((htf_indication_type === "bullish") ? "HTF Bull" : ((htf_indication_type === "bearish") ? "HTF Bear" : "")); + if ((bias_txt === "")) { + if (!pinescript.na(state.htf_bias_label)) { + pinescript.labelDelete(state.htf_bias_label); + state.htf_bias_label = null; + } + } else { + var bias_bg = ((htf_indication_type === "bullish") ? pinescript.color.new(htf_bullish_color, 75) : pinescript.color.new(htf_bearish_color, 75)); + var bias_txt_col = pinescript.color.white; + var x_off = 15; + if (pinescript.na(state.htf_bias_label)) { + state.htf_bias_label = pinescript.labelNew((bar_index + x_off), htf_y, bias_txt, ({ style: label.style_label_left, textcolor: bias_txt_col, color: bias_bg, size: pinescript.size.small })); + } else { + label.set_x(state.htf_bias_label, (bar_index + x_off)); + label.set_y(state.htf_bias_label, htf_y); + pinescript.labelSetText(state.htf_bias_label, bias_txt); + label.set_color(state.htf_bias_label, bias_bg); + label.set_textcolor(state.htf_bias_label, bias_txt_col); + } + } + } else { + if (!pinescript.na(state.htf_bias_label)) { + pinescript.labelDelete(state.htf_bias_label); + state.htf_bias_label = null; + } + } + if (state.htf_resistance_box === undefined) state.htf_resistance_box = null; + if (state.htf_support_box === undefined) state.htf_support_box = null; + if (((use_htf && show_zones) && !pinescript.na(htf_last_swing_high))) { + zone_buffer = get_zone_buffer(htf_last_swing_high); + if (pinescript.na(state.htf_resistance_box)) { + state.htf_resistance_box = pinescript.boxNew((bar_index - 25), (htf_last_swing_high + zone_buffer), bar_index, (htf_last_swing_high - zone_buffer), ({ bgcolor: pinescript.color.new(htf_bearish_color, 90), border_color: htf_bearish_color, border_width: 2, border_style: line.style_dashed })); + } else { + box.set_left(state.htf_resistance_box, (bar_index - 25)); + box.set_right(state.htf_resistance_box, bar_index); + box.set_top(state.htf_resistance_box, (htf_last_swing_high + zone_buffer)); + box.set_bottom(state.htf_resistance_box, (htf_last_swing_high - zone_buffer)); + } + } + if (((use_htf && show_zones) && !pinescript.na(htf_last_swing_low))) { + zone_buffer = get_zone_buffer(htf_last_swing_low); + if (pinescript.na(state.htf_support_box)) { + state.htf_support_box = pinescript.boxNew((bar_index - 25), (htf_last_swing_low + zone_buffer), bar_index, (htf_last_swing_low - zone_buffer), ({ bgcolor: pinescript.color.new(htf_bullish_color, 90), border_color: htf_bullish_color, border_width: 2, border_style: line.style_dashed })); + } else { + box.set_left(state.htf_support_box, (bar_index - 25)); + box.set_right(state.htf_support_box, bar_index); + box.set_top(state.htf_support_box, (htf_last_swing_low + zone_buffer)); + box.set_bottom(state.htf_support_box, (htf_last_swing_low - zone_buffer)); + } + } + if ((!use_htf || !show_zones)) { + if (!pinescript.na(state.htf_resistance_box)) { + pinescript.boxDelete(state.htf_resistance_box); + state.htf_resistance_box = null; + } + if (!pinescript.na(state.htf_support_box)) { + pinescript.boxDelete(state.htf_support_box); + state.htf_support_box = null; + } + if (!pinescript.na(state.htf_bias_label)) { + pinescript.labelDelete(state.htf_bias_label); + state.htf_bias_label = null; + } + } + var cur_support = ((pinescript.arraySize(state.support_levels) > 0) ? pinescript.arrayGet(state.support_levels, 0) : null); + var cur_resistance = ((pinescript.arraySize(state.resistance_levels) > 0) ? pinescript.arrayGet(state.resistance_levels, 0) : null); + var sup_break = (!pinescript.na(pinescript.hist(8, cur_support, 1)) ? pinescript.hist(9, cur_support, 1) : cur_support); + var res_break = (!pinescript.na(pinescript.hist(10, cur_resistance, 1)) ? pinescript.hist(11, cur_resistance, 1) : cur_resistance); + var ltf_bear_entry = (((barstate.isconfirmed && !pinescript.na(sup_break)) && (close < sup_break)) && (pinescript.hist(12, close, 1) >= sup_break)); + var ltf_bull_entry = (((barstate.isconfirmed && !pinescript.na(res_break)) && (close > res_break)) && (pinescript.hist(13, close, 1) <= res_break)); + var bullish_cont_raw = (show_signals && (is_ltf_15 ? ltf_bull_entry : bullish_entry_cont_allowed)); + var bearish_cont_raw = (show_signals && (is_ltf_15 ? ltf_bear_entry : bearish_entry_cont_allowed)); + var bullish_reversal_vis = (((!is_ltf_15 && bullish_entry_reversal) && show_signals) && show_reversal_entries); + var bearish_reversal_vis = (((!is_ltf_15 && bearish_entry_reversal) && show_signals) && show_reversal_entries); + var use_htf_filter_for_color = (align_entries_with_htf && !is_ltf_15); + var bullish_light_vis = (bullish_cont_raw && !use_htf_filter_for_color); + var bearish_light_vis = (bearish_cont_raw && !use_htf_filter_for_color); + var bullish_dark_vis = ((bullish_cont_raw && use_htf_filter_for_color) && htf_bullish); + var bearish_dark_vis = ((bearish_cont_raw && use_htf_filter_for_color) && htf_bearish); + pinescript.plotshape((bullish_light_vis && is_ltf_15), ({ title: "Bullish Entry (Normal) LTF", location: pinescript.location.belowbar, color: pinescript.color.new(pinescript.color.green, 40), style: pinescript.shape.labelup, size: pinescript.size.small, text: "ENTRY", textcolor: pinescript.color.white })); + pinescript.plotshape((bullish_light_vis && !is_ltf_15), ({ title: "Bullish Entry (Normal)", location: pinescript.location.belowbar, color: pinescript.color.new(pinescript.color.green, 40), style: pinescript.shape.labelup, size: pinescript.size.small, text: "BUY", textcolor: pinescript.color.white })); + pinescript.plotshape((bullish_dark_vis && is_ltf_15), ({ title: "Bullish Entry (HTF Aligned) LTF", location: pinescript.location.belowbar, color: pinescript.color.new(pinescript.color.green, 0), style: pinescript.shape.labelup, size: pinescript.size.small, text: "ENTRY+", textcolor: pinescript.color.white })); + pinescript.plotshape((bullish_dark_vis && !is_ltf_15), ({ title: "Bullish Entry (HTF Aligned)", location: pinescript.location.belowbar, color: pinescript.color.new(pinescript.color.green, 0), style: pinescript.shape.labelup, size: pinescript.size.small, text: "BUY+", textcolor: pinescript.color.white })); + pinescript.plotshape((bearish_light_vis && is_ltf_15), ({ title: "Bearish Entry (Normal) LTF", location: pinescript.location.abovebar, color: pinescript.color.new(pinescript.color.red, 40), style: pinescript.shape.labeldown, size: pinescript.size.small, text: "ENTRY", textcolor: pinescript.color.white })); + pinescript.plotshape((bearish_light_vis && !is_ltf_15), ({ title: "Bearish Entry (Normal)", location: pinescript.location.abovebar, color: pinescript.color.new(pinescript.color.red, 40), style: pinescript.shape.labeldown, size: pinescript.size.small, text: "SELL", textcolor: pinescript.color.white })); + pinescript.plotshape((bearish_dark_vis && is_ltf_15), ({ title: "Bearish Entry (HTF Aligned) LTF", location: pinescript.location.abovebar, color: pinescript.color.new(pinescript.color.red, 0), style: pinescript.shape.labeldown, size: pinescript.size.small, text: "ENTRY+", textcolor: pinescript.color.white })); + pinescript.plotshape((bearish_dark_vis && !is_ltf_15), ({ title: "Bearish Entry (HTF Aligned)", location: pinescript.location.abovebar, color: pinescript.color.new(pinescript.color.red, 0), style: pinescript.shape.labeldown, size: pinescript.size.small, text: "SELL+", textcolor: pinescript.color.white })); + pinescript.plotshape(bullish_reversal_vis, ({ title: "Bullish Entry (Reversal)", location: pinescript.location.belowbar, color: pinescript.color.new(pinescript.color.yellow, 0), style: pinescript.shape.labelup, size: pinescript.size.small, text: "⚠BUY", textcolor: pinescript.color.black })); + pinescript.plotshape(bearish_reversal_vis, ({ title: "Bearish Entry (Reversal)", location: pinescript.location.abovebar, color: pinescript.color.new(pinescript.color.yellow, 0), style: pinescript.shape.labeldown, size: pinescript.size.small, text: "⚠SELL", textcolor: pinescript.color.black })); + var any_bullish_flag_vis = ((bullish_light_vis || bullish_dark_vis) || bullish_reversal_vis); + var any_bearish_flag_vis = ((bearish_light_vis || bearish_dark_vis) || bearish_reversal_vis); + if (state.last_visible_bull_flag_bar === undefined) state.last_visible_bull_flag_bar = null; + if (state.last_visible_bear_flag_bar === undefined) state.last_visible_bear_flag_bar = null; + if (any_bullish_flag_vis) { + state.last_visible_bull_flag_bar = bar_index; + } + if (any_bearish_flag_vis) { + state.last_visible_bear_flag_bar = bar_index; + } + null; + pinescript.color.new(pinescript.color.gray, pinescript.min((phase_bg_transparency + 2), 99)); + pinescript.color.new(pinescript.color.gray, pinescript.min((phase_bg_transparency + 2), 99)); + pinescript.color.new(pinescript.color.red, pinescript.min((phase_bg_transparency + 3), 99)); + pinescript.color.new(pinescript.color.red, phase_bg_transparency); + pinescript.color.new(pinescript.color.green, pinescript.min((phase_bg_transparency + 3), 99)); + pinescript.color.new(pinescript.color.green, phase_bg_transparency); + var struct_bg_color = ((state.market_structure === "bullish_indication") ? undefined : ((state.market_structure === "bullish_correction") ? undefined : ((state.market_structure === "bearish_indication") ? undefined : ((state.market_structure === "bearish_correction") ? undefined : ((state.market_structure === "consolidation") ? undefined : ((state.market_structure === "no_setup") ? undefined : undefined)))))); + var cont_bg_bar = (bullish_entry_cont_allowed || bearish_entry_cont_allowed); + var phase_bg_allowed = (show_phase_background && !auto_hide_phase_bg); + var bgcolor_color = (phase_bg_allowed ? (cont_bg_bar ? cont_bg_tan : struct_bg_color) : null); + pinescript.bgcolor(bgcolor_color, ({ title: "Market Structure Phase" })); + var table_pos_in = pinescript.inputString(({ defval: "Top Right", title: "Info Table Position", options: ["Top Right", "Top Left", "Bottom Right", "Bottom Left", "Middle Right", "Middle Left", "Top Center", "Bottom Center"], group: "Info Table" })); + var table_pos = ((table_pos_in === "Top Right") ? pinescript.position.top_right : ((table_pos_in === "Top Left") ? pinescript.position.top_left : ((table_pos_in === "Bottom Right") ? pinescript.position.bottom_right : ((table_pos_in === "Bottom Left") ? pinescript.position.bottom_left : ((table_pos_in === "Middle Right") ? pinescript.position.middle_right : ((table_pos_in === "Middle Left") ? pinescript.position.middle_left : ((table_pos_in === "Top Center") ? pinescript.position.top_center : pinescript.position.bottom_center))))))); + var bg = chart.bg_color; + var lum = (((pinescript.color.r(bg) + pinescript.color.g(bg)) + pinescript.color.b(bg)) / 3); + var is_dark = (lum < 128); + var T_TABLE_BG = (is_dark ? pinescript.color.new(pinescript.color.black, 85) : pinescript.color.new(pinescript.color.white, 90)); + var T_HDR_BG = (is_dark ? pinescript.color.new(pinescript.color.gray, 80) : pinescript.color.new(pinescript.color.gray, 70)); + var T_VAL_BG = (is_dark ? pinescript.color.new(pinescript.color.black, 70) : pinescript.color.new(pinescript.color.white, 10)); + var T_ENTRY_BG = (is_dark ? pinescript.color.new(pinescript.color.black, 85) : pinescript.color.new(pinescript.color.white, 100)); + var T_TEXT = (is_dark ? pinescript.color.white : pinescript.color.black); + var T_NEUTRAL_BG = (is_dark ? pinescript.color.new(pinescript.color.gray, 80) : pinescript.color.new(pinescript.color.gray, 85)); + var T_BULL_BG = (is_dark ? pinescript.color.new(pinescript.color.green, 65) : pinescript.color.new(pinescript.color.green, 70)); + var T_BEAR_BG = (is_dark ? pinescript.color.new(pinescript.color.red, 65) : pinescript.color.new(pinescript.color.red, 70)); + var T_WARN_BG = (is_dark ? pinescript.color.new(pinescript.color.orange, 55) : pinescript.color.new(pinescript.color.orange, 65)); + function f_phase_bg(_is_bull, _is_bear) { + return (_is_bull ? T_BULL_BG : (_is_bear ? T_BEAR_BG : T_NEUTRAL_BG)); + } + if (state.info_table === undefined) state.info_table = null; + if (state._last_pos_in === undefined) state._last_pos_in = null; + if (barstate.islast) { + var pos_changed = (pinescript.na(state._last_pos_in) || (state._last_pos_in !== table_pos_in)); + state._last_pos_in = table_pos_in; + if ((pinescript.na(state.info_table) || pos_changed)) { + state.info_table = pinescript.table.new(table_pos, 2, 19, ({ bgcolor: T_TABLE_BG, border_width: 1 })); + } + var is_bullish_phase = pinescript.strContains(state.market_structure, "bullish"); + var is_bearish_phase = pinescript.strContains(state.market_structure, "bearish"); + var phase_color = f_phase_bg(is_bullish_phase, is_bearish_phase); + if (is_ltf_15) { + pinescript.table.cell(state.info_table, 0, 0, "ICC Phase:", ({ text_color: T_TEXT, bgcolor: T_ENTRY_BG })); + pinescript.table.cell(state.info_table, 1, 0, "ICC ENTRY MODE", ({ text_color: T_TEXT, bgcolor: T_ENTRY_BG })); + } else { + pinescript.table.cell(state.info_table, 0, 0, "ICC Phase:", ({ text_color: T_TEXT, bgcolor: T_HDR_BG })); + state.market_structure; + "Neutral Range"; + "Bearish Correction"; + "Bearish Indication"; + "Bullish Correction"; + "Bullish Indication"; + state.no_setup_label; + var struct_phase_display = ((state.market_structure === "no_setup") ? undefined : ((state.market_structure === "bullish_indication") ? undefined : ((state.market_structure === "bullish_correction") ? undefined : ((state.market_structure === "bearish_indication") ? undefined : ((state.market_structure === "bearish_correction") ? undefined : ((state.market_structure === "consolidation") ? undefined : undefined)))))); + var phase_display_final = (!pinescript.na(phase_display_override) ? phase_display_override : struct_phase_display); + var phase_bg_final = (!pinescript.na(phase_display_override) ? T_WARN_BG : phase_color); + pinescript.table.cell(state.info_table, 1, 0, phase_display_final, ({ text_color: T_TEXT, bgcolor: phase_bg_final })); + } + if (is_ltf_15) { + pinescript.table.cell(state.info_table, 0, 1, "Indication Level:", ({ text_color: T_TEXT, bgcolor: T_ENTRY_BG })); + pinescript.table.cell(state.info_table, 1, 1, "N/A", ({ text_color: T_TEXT, bgcolor: T_ENTRY_BG })); + } else { + pinescript.table.cell(state.info_table, 0, 1, "Indication Level:", ({ text_color: T_TEXT, bgcolor: T_HDR_BG })); + var indication_color = (pinescript.na(state.indication_type) ? T_NEUTRAL_BG : ((state.indication_type === "bullish") ? T_BULL_BG : T_BEAR_BG)); + pinescript.table.cell(state.info_table, 1, 1, (pinescript.na(state.indication_level) ? "None" : pinescript.strToString(state.indication_level, "#.####")), ({ text_color: T_TEXT, bgcolor: indication_color })); + } + pinescript.table.cell(state.info_table, 0, 2, "Last Swing High:", ({ text_color: T_TEXT, bgcolor: T_HDR_BG })); + pinescript.table.cell(state.info_table, 1, 2, pinescript.strToString(state.last_swing_high, "#.####"), ({ text_color: T_TEXT, bgcolor: T_VAL_BG })); + pinescript.table.cell(state.info_table, 0, 3, "Last Swing Low:", ({ text_color: T_TEXT, bgcolor: T_HDR_BG })); + pinescript.table.cell(state.info_table, 1, 3, pinescript.strToString(state.last_swing_low, "#.####"), ({ text_color: T_TEXT, bgcolor: T_VAL_BG })); + pinescript.table.cell(state.info_table, 0, 4, "Swing Length:", ({ text_color: T_TEXT, bgcolor: T_HDR_BG })); + pinescript.table.cell(state.info_table, 1, 4, pinescript.strToString(swing_length), ({ text_color: T_TEXT, bgcolor: T_VAL_BG })); + pinescript.table.cell(state.info_table, 0, 5, "Last Signal:", ({ text_color: T_TEXT, bgcolor: T_HDR_BG })); + if (is_ltf_15) { + var ztxt = (pinescript.na(state.last_zone_edge_entry) ? "N/A" : ((state.last_zone_edge_entry === "BULL") ? "BULL ENTRY" : "BEAR ENTRY")); + var zbg = (pinescript.na(state.last_zone_edge_entry) ? T_NEUTRAL_BG : ((state.last_zone_edge_entry === "BULL") ? T_BULL_BG : T_BEAR_BG)); + pinescript.table.cell(state.info_table, 1, 5, ztxt, ({ text_color: T_TEXT, bgcolor: zbg })); + } else { + var last_sig_bull = (!pinescript.na(state.last_visible_bull_flag_bar) && (pinescript.na(state.last_visible_bear_flag_bar) || (state.last_visible_bull_flag_bar > state.last_visible_bear_flag_bar))); + var last_sig_bear = (!pinescript.na(state.last_visible_bear_flag_bar) && (pinescript.na(state.last_visible_bull_flag_bar) || (state.last_visible_bull_flag_bar > state.last_visible_bull_flag_bar))); + var last_signal_text = (last_sig_bull ? "Bullish (BUY)" : (last_sig_bear ? "Bearish (SELL)" : "None")); + var last_signal_bg = (last_sig_bull ? T_BULL_BG : (last_sig_bear ? T_BEAR_BG : T_NEUTRAL_BG)); + pinescript.table.cell(state.info_table, 1, 5, last_signal_text, ({ text_color: T_TEXT, bgcolor: last_signal_bg })); + } + var htfPretty = f_tfPretty(resolved_htf); + var htfPhaseHeader = (("HTF Phase (" + htfPretty) + "):"); + pinescript.table.cell(state.info_table, 0, 6, htfPhaseHeader, ({ text_color: T_TEXT, bgcolor: T_HDR_BG })); + (pinescript.na(htf_market_structure) ? "None" : htf_market_structure); + "Neutral Range"; + "Bearish Correction"; + "Bearish Indication"; + "Bullish Correction"; + "Bullish Indication"; + "No Setup"; + var htf_phase_display = ((htf_market_structure === "no_setup") ? undefined : ((htf_market_structure === "bullish_indication") ? undefined : ((htf_market_structure === "bullish_correction") ? undefined : ((htf_market_structure === "bearish_indication") ? undefined : ((htf_market_structure === "bearish_correction") ? undefined : ((htf_market_structure === "consolidation") ? undefined : undefined)))))); + var htf_is_bull = (!pinescript.na(htf_market_structure) && pinescript.strContains(htf_market_structure, "bullish")); + var htf_is_bear = (!pinescript.na(htf_market_structure) && pinescript.strContains(htf_market_structure, "bearish")); + var htf_phase_bg = f_phase_bg(htf_is_bull, htf_is_bear); + pinescript.table.cell(state.info_table, 1, 6, htf_phase_display, ({ text_color: T_TEXT, bgcolor: htf_phase_bg })); + } + if (state.alert_trigger_price === undefined) state.alert_trigger_price = null; + if (state.alert_bar_time === undefined) state.alert_bar_time = null; + if (state.prev_entry_fire === undefined) state.prev_entry_fire = false; + var entry_alert_fire = (any_bullish_flag_vis || any_bearish_flag_vis); + if ((pinescript.na(state.alert_bar_time) || (time !== state.alert_bar_time))) { + state.alert_bar_time = time; + state.alert_trigger_price = null; + state.prev_entry_fire = false; + } + var fire_edge = (entry_alert_fire && !state.prev_entry_fire); + if ((fire_edge && pinescript.na(state.alert_trigger_price))) { + state.alert_trigger_price = close; + } + state.prev_entry_fire = entry_alert_fire; + var entry_dir = (bullish_dark_vis ? (is_ltf_15 ? "BULL ENTRY+" : "BUY+") : (bullish_light_vis ? (is_ltf_15 ? "BULL ENTRY" : "BUY") : (bullish_reversal_vis ? "⚠BUY" : (bearish_dark_vis ? (is_ltf_15 ? "BEAR ENTRY+" : "SELL+") : (bearish_light_vis ? (is_ltf_15 ? "BEAR ENTRY" : "SELL") : (bearish_reversal_vis ? "⚠SELL" : "NONE")))))); + var alert_freq = alert.freq_once_per_bar; + if ((alert_freq_opt === "Every Tick")) { + alert_freq = alert.freq_all; + } else { + if ((alert_freq_opt === "Once Per Bar")) { + alert_freq = alert.freq_once_per_bar; + } else { + alert_freq = alert.freq_once_per_bar_close; + } + } + var is_close_mode = (alert_freq_opt === "Once Per Bar Close"); + var do_alert = (is_close_mode ? (entry_alert_fire && barstate.isconfirmed) : fire_edge); + if (do_alert) { + var ts = (is_close_mode ? time_close : (barstate.isrealtime ? timenow : time)); + var px = (is_close_mode ? close : state.alert_trigger_price); + var time_txt = f_ts(ts); + var price_txt = pinescript.strToString(px, format.mintick); + var msg = ((((("ICC " + entry_dir) + " @ ") + price_txt) + " ") + time_txt); + pinescript.alert(msg, alert_freq); + } + if (state.prev_correction_phase === undefined) state.prev_correction_phase = false; + var entered_correction = (state.correction_phase && !state.prev_correction_phase); + state.prev_correction_phase = state.correction_phase; + pinescript.alertcondition(entered_correction, ({ title: "ICC Phase: Entered Correction", message: "ICC ENTERED CORRECTION | {{ticker}} {{interval}} | Close={{close}}" })); +} + + + +// The bar-by-bar engine: runs main() once per bar over the dataset. +function run(data, options = {}) { + data = data || {}; + const inClose = (data.close || []).map(Number); + const n = inClose.length; + const inOpen = (data.open || inClose).map(Number); + const inHigh = (data.high || inClose).map(Number); + const inLow = (data.low || inClose).map(Number); + const inVol = (data.volume || new Array(n).fill(0)).map(Number); + const inTime = (data.time || inClose.map((_, i) => i)).map(Number); + + // Reset all persistent and per-run state so repeated runs are independent. + globalThis.__pineState = {}; + globalThis.__pineRuntime = { plots: {}, plotshapes: {}, alerts: [], bars: n, inputs: options.inputs || {} }; + pinescript.__rt = {}; + pinescript.__bar = 0; + + // Growing OHLCV windows: each is a SeriesArray that gains one element per bar, + // so window built-ins like sma and highest see history up to the current bar. + const O = pinescript.asSeries([]), H = pinescript.asSeries([]), L = pinescript.asSeries([]); + const C = pinescript.asSeries([]), V = pinescript.asSeries([]), T = pinescript.asSeries([]); + const HL2 = pinescript.asSeries([]), HLC3 = pinescript.asSeries([]), OHLC4 = pinescript.asSeries([]); + globalThis.open = O; globalThis.high = H; globalThis.low = L; globalThis.close = C; + globalThis.volume = V; globalThis.time = T; + globalThis.hl2 = HL2; globalThis.hlc3 = HLC3; globalThis.ohlc4 = OHLC4; + globalThis.syminfo = globalThis.syminfo || { tickerid: 'SYNTHETIC', ticker: 'SYNTHETIC', mintick: 0.01 }; + + const rt = globalThis.__pineRuntime; + for (let i = 0; i < n; i++) { + O.push(inOpen[i]); H.push(inHigh[i]); L.push(inLow[i]); C.push(inClose[i]); V.push(inVol[i]); T.push(inTime[i]); + HL2.push((inHigh[i] + inLow[i]) / 2); + HLC3.push((inHigh[i] + inLow[i] + inClose[i]) / 3); + OHLC4.push((inOpen[i] + inHigh[i] + inLow[i] + inClose[i]) / 4); + + pinescript.__bar = i; + rt.__barIndex = i; + rt.__plotIdx = 0; + rt.__shapeIdx = 0; + globalThis.bar_index = i; + globalThis.last_bar_index = n - 1; + globalThis.time_tradingday = inTime[i]; + globalThis.time_close = inTime[i]; + globalThis.last_bar_time = inTime[n - 1]; + globalThis.timenow = inTime[n - 1]; + globalThis.barstate = { + isfirst: i === 0, islast: i === n - 1, isrealtime: false, ishistory: true, + isconfirmed: true, isnew: true, islastconfirmedhistory: i === n - 1, + }; + + main(); + } + + // Backfill skipped bars so every output series has exactly n entries. + for (const k of Object.keys(rt.plots)) { + const d = rt.plots[k].data; + for (let i = 0; i < n; i++) if (d[i] === undefined) d[i] = null; + } + for (const k of Object.keys(rt.plotshapes)) { + const d = rt.plotshapes[k].data; + for (let i = 0; i < n; i++) if (d[i] === undefined) d[i] = false; + } + return rt; +} + + +export { main, run +}; diff --git a/converted/RBT_strategy.js b/converted/RBT_strategy.js index 3468aca..fe5f157 100644 --- a/converted/RBT_strategy.js +++ b/converted/RBT_strategy.js @@ -42,6 +42,80 @@ const pinescript = { if (value != null && typeof value[Symbol.iterator] === 'function') return Array.from(value); return new Array(count || 0).fill(null); }, + __decArr: function(arr) { + if (!Array.isArray(arr) || arr.__pineDecorated) return arr; + const self = this; + const def = (name, fn) => Object.defineProperty(arr, name, { value: fn, writable: true, configurable: true }); + Object.defineProperty(arr, '__pineDecorated', { value: true, configurable: true }); + def('get', (i) => (arr[i] === undefined ? null : arr[i])); + def('set', (i, v) => { arr[i] = v; return v; }); + def('size', () => arr.length); + def('clear', () => { arr.length = 0; }); + def('insert', (i, v) => { arr.splice(i, 0, v); }); + def('remove', (i) => arr.splice(i, 1)[0]); + def('contains', (v) => arr.includes(v)); + def('indexof', (v) => arr.indexOf(v)); + def('lastindexof', (v) => arr.lastIndexOf(v)); + def('first', () => (arr.length ? arr[0] : null)); + def('last', () => (arr.length ? arr[arr.length - 1] : null)); + def('sum', () => arr.reduce((a, b) => a + b, 0)); + def('avg', () => (arr.length ? arr.reduce((a, b) => a + b, 0) / arr.length : 0)); + def('min', () => (arr.length ? Math.min(...arr) : null)); + def('max', () => (arr.length ? Math.max(...arr) : null)); + def('range', () => (arr.length ? Math.max(...arr) - Math.min(...arr) : null)); + // Statistical methods that route to the array.* built-ins (these names are not + // native to JS arrays, so attaching them here is safe from recursion). + def('median', () => self.arrayMedian(arr)); + def('mode', () => self.arrayMode(arr)); + def('stdev', () => self.arrayStdev(arr)); + def('variance', () => self.arrayVariance(arr)); + def('covariance', (other) => self.arrayCovariance(arr, other)); + def('percentile_linear_interpolation', (p) => self.arrayPercentileLinearInterpolation(arr, p)); + def('percentile_nearest_rank', (p) => self.arrayPercentileNearestRank(arr, p)); + def('abs', () => self.__decArr(self.arrayAbs(arr))); + def('binary_search', (v) => self.arrayBinarySearch(arr, v)); + // Pine's array.sort takes an order string, not a comparator. Use the native + // sort via .call so we don't recurse through this overridden method. + def('sort', (order) => { Array.prototype.sort.call(arr, (a, b) => (order === 'descending' ? b - a : a - b)); return arr; }); + def('sort_indices', (order) => self.__decArr(arr.map((_, i) => i).sort((a, b) => (order === 'descending' ? arr[b] - arr[a] : arr[a] - arr[b])))); + // join/slice/reverse/concat/includes/fill already exist natively on Array with + // compatible semantics, so we deliberately leave them to the native methods. + return arr; + }, + __decMap: function(m) { + if (!(m instanceof Map) || m.__pineDecorated) return m; + const def = (name, fn) => Object.defineProperty(m, name, { value: fn, writable: true, configurable: true }); + Object.defineProperty(m, '__pineDecorated', { value: true, configurable: true }); + def('put', (k, v) => { m.set(k, v); return v; }); + def('contains', (k) => m.has(k)); + def('remove', (k) => m.delete(k)); + def('keys', () => Array.from(Map.prototype.keys.call(m))); + def('values', () => Array.from(Map.prototype.values.call(m))); + def('size_', () => m.size); + return m; + }, + __decMatrix: function(m) { + if (!m || typeof m !== 'object' || m.__pineDecorated) return m; + const self = this; + const def = (name, fn) => Object.defineProperty(m, name, { value: fn, writable: true, configurable: true }); + Object.defineProperty(m, '__pineDecorated', { value: true, configurable: true }); + def('get', (r, c) => self.matrixGet(m, r, c)); + def('set', (r, c, v) => self.matrixSet(m, r, c, v)); + def('rows_', () => m.rows); + def('columns', () => m.cols); + def('fill', (v) => self.matrixFill(m, v)); + return m; + }, + __decDraw: function(obj) { + if (!obj || typeof obj !== 'object') return obj; + const p = new Proxy(obj, { + get(t, k) { + if (k in t || typeof k === 'symbol') return t[k]; + return function() { return p; }; + }, + }); + return p; + }, alertcondition: function(condition, ...rest) { if (globalThis.__pineRuntime) { globalThis.__pineRuntime.alerts.push({ condition, args: rest }); @@ -49,6 +123,32 @@ const pinescript = { return null; }, barcolor: function(color) { return null; }, + plotchar: function(series, ...rest) { + const rt = globalThis.__pineRuntime; + if (!rt) return series; + const bar = rt.__barIndex | 0; + const ord = (rt.__shapeIdx = (rt.__shapeIdx | 0) + 1) - 1; + let key = 'char_' + ord; + for (const r of rest) { + if (typeof r === 'string') { key = r; break; } + if (r && typeof r === 'object' && r.title) { key = String(r.title); break; } + } + let s = rt.plotshapes[key]; + if (!s) s = rt.plotshapes[key] = { title: key, data: [] }; + s.data[bar] = this.__scalar(series); + return series; + }, + plotarrow: function(series, ...rest) { + const rt = globalThis.__pineRuntime; + if (!rt) return series; + const bar = rt.__barIndex | 0; + const ord = (rt.__plotIdx = (rt.__plotIdx | 0) + 1) - 1; + const key = 'arrow_' + ord; + let p = rt.plots[key]; + if (!p) p = rt.plots[key] = { title: key, data: [] }; + p.data[bar] = this.__scalar(series); + return series; + }, bgcolor: function(color, title, editable, showLast) { return null; }, @@ -470,6 +570,12 @@ const pinescript = { atan: function(value) { return Math.atan(value); }, + todegrees: function(radians) { + return radians * (180 / Math.PI); + }, + toradians: function(degrees) { + return degrees * (Math.PI / 180); + }, floor: function(value) { return Math.floor(value); }, @@ -641,7 +747,7 @@ const pinescript = { return series; }, lineNew: function(x1, y1, x2, y2, opts = {}) { - return { x1, y1, x2, y2, opts, _type: 'line' }; + return this.__decDraw({ x1, y1, x2, y2, opts, _type: 'line' }); }, lineDelete: function(l) { return null; @@ -661,7 +767,7 @@ const pinescript = { return point === 0 || point === 'y1' ? line.y1 : line.y2; }, labelNew: function(x, y, text = '', opts = {}) { - return { x, y, text, opts, _type: 'label' }; + return this.__decDraw({ x, y, text, opts, _type: 'label' }); }, labelDelete: function(l) { return null; @@ -676,7 +782,7 @@ const pinescript = { return label.text || ''; }, boxNew: function(left, top, right, bottom, opts = {}) { - return { left, top, right, bottom, opts, _type: 'box' }; + return this.__decDraw({ left, top, right, bottom, opts, _type: 'box' }); }, boxDelete: function(box) { return null; @@ -694,7 +800,7 @@ const pinescript = { return box; }, polylineNew: function(points, opts = {}) { - return { points: points || [], opts, _type: 'polyline' }; + return this.__decDraw({ points: points || [], opts, _type: 'polyline' }); }, polylineDelete: function(poly) { return null; @@ -765,7 +871,7 @@ const pinescript = { return { time: _time ?? null, price: _price ?? null }; }, mapNew: function() { - return new Map(); + return this.__decMap(new Map()); }, mapSize: function(m) { return m instanceof Map ? m.size : 0; @@ -800,7 +906,7 @@ const pinescript = { const r = Math.max(0, rows ?? 0); const c = Math.max(0, cols ?? 0); const data = Array.from({ length: r }, () => Array.from({ length: c }, () => initialValue)); - return { rows: r, cols: c, data }; + return this.__decMatrix({ rows: r, cols: c, data }); }, matrixRows: function(m) { return m?.rows ?? 0; @@ -923,11 +1029,62 @@ const pinescript = { } return null; }, + __jacobiEigen: function(m) { + if (!m || !Array.isArray(m.data)) return null; + const n = m.rows ?? 0; + if (n === 0 || n !== (m.cols ?? 0)) return null; + // Work on a copy so the input matrix is untouched. + const a = m.data.map(row => row.slice()); + const v = Array.from({ length: n }, (_, i) => Array.from({ length: n }, (_, j) => (i === j ? 1 : 0))); + for (let sweep = 0; sweep < 100; sweep++) { + let off = 0; + for (let p = 0; p < n; p++) for (let q = p + 1; q < n; q++) off += a[p][q] * a[p][q]; + if (off < 1e-20) break; + for (let p = 0; p < n; p++) { + for (let q = p + 1; q < n; q++) { + if (Math.abs(a[p][q]) < 1e-18) continue; + const theta = (a[q][q] - a[p][p]) / (2 * a[p][q]); + const t = Math.sign(theta || 1) / (Math.abs(theta) + Math.sqrt(theta * theta + 1)); + const cos = 1 / Math.sqrt(t * t + 1); + const sin = t * cos; + for (let k = 0; k < n; k++) { + const akp = a[k][p], akq = a[k][q]; + a[k][p] = cos * akp - sin * akq; + a[k][q] = sin * akp + cos * akq; + } + for (let k = 0; k < n; k++) { + const apk = a[p][k], aqk = a[q][k]; + a[p][k] = cos * apk - sin * aqk; + a[q][k] = sin * apk + cos * aqk; + } + for (let k = 0; k < n; k++) { + const vkp = v[k][p], vkq = v[k][q]; + v[k][p] = cos * vkp - sin * vkq; + v[k][q] = sin * vkp + cos * vkq; + } + } + } + } + // Sort eigenpairs by eigenvalue, descending. + const order = Array.from({ length: n }, (_, i) => i).sort((i, j) => a[j][j] - a[i][i]); + const values = order.map(i => a[i][i]); + const vectors = Array.from({ length: n }, (_, r) => order.map(c => v[r][c])); + return { values, vectors }; + }, + matrixEigenvalues: function(m) { + const e = this.__jacobiEigen(m); + return this.__decArr(e ? e.values : []); + }, + matrixEigenvectors: function(m) { + const e = this.__jacobiEigen(m); + if (!e) return this.__decMatrix({ rows: 0, cols: 0, data: [] }); + return this.__decMatrix({ rows: e.vectors.length, cols: e.vectors.length, data: e.vectors }); + }, requestSecurity: function(symbol, timeframe, expression) { return expression; }, arrayNew: function(initialSize = 0, initialValue = 0) { - return Array(initialSize).fill(initialValue); + return this.__decArr(Array(initialSize).fill(initialValue)); }, arraySize: function(arr) { return arr ? arr.length : 0; @@ -978,10 +1135,10 @@ const pinescript = { return arr; }, arraySlice: function(arr, startIndex = 0, endIndex = null) { - if (!arr) return []; + if (!arr) return this.__decArr([]); const start = Number(startIndex) || 0; const end = endIndex === null || endIndex === undefined ? arr.length : Number(endIndex) || 0; - return arr.slice(start, end); + return this.__decArr(arr.slice(start, end)); }, arraySort: function(arr, order = 'ascending') { if (arr) arr.sort((a, b) => order === 'ascending' ? a - b : b - a); @@ -1191,7 +1348,7 @@ const pinescript = { const info = { ticker: 'AAPL', tickerid: 'NASDAQ:AAPL', prefix: 'NASDAQ', root: 'AAPL', suffix: '' }; return info[type] || ''; }, - timenow: 1781574528290, + timenow: 1782783577395, barstate: "LAST", dividends: {}, splits: {}, @@ -1245,11 +1402,11 @@ const pinescript = { arrayConcat: function(arr1, arr2) { if (!arr1) return arr2 || []; if (!arr2) return arr1; - return arr1.concat(arr2); + return this.__decArr(arr1.concat(arr2)); }, arrayCopy: function(arr) { - if (!arr) return []; - return [...arr]; + if (!arr) return this.__decArr([]); + return this.__decArr([...arr]); }, arrayBinarySearch: function(arr, value) { if (!arr || arr.length === 0) return -1; @@ -1339,7 +1496,7 @@ globalThis.input.timeframe = globalThis.input.timeframe || ((defval) => defval); globalThis.array = globalThis.array || { - from: (...items) => items, + from: (...items) => pinescript.__decArr(items), size: (arr) => pinescript.arraySize(arr), @@ -1508,6 +1665,8 @@ globalThis.line = globalThis.line || __pineNS({ style_solid: "solid", style_dash globalThis.box = globalThis.box || __pineNS({}); +globalThis.color = globalThis.color || __pineNS(Object.assign(function(c) { return c; }, { new: function(c, t) { return c; }, rgb: function(r, g, b, t) { return "#rgb(" + [r, g, b].join(",") + ")"; }, from_gradient: function(v, lo, hi, c1, c2) { return c1; }, r: function() { return 0; }, g: function() { return 0; }, b: function() { return 0; }, t: function() { return 0; }, aqua: "#00BCD4", black: "#363A45", blue: "#2962FF", fuchsia: "#E040FB", gray: "#787B86", green: "#4CAF50", lime: "#00E676", maroon: "#880E4F", navy: "#311B92", olive: "#808000", orange: "#FF9800", purple: "#9C27B0", red: "#FF5252", silver: "#B2B5BE", teal: "#00897B", white: "#FFFFFF", yellow: "#FFEB3B" })); + globalThis.label = globalThis.label || __pineNS({ style_label_down: "label_down", style_label_up: "label_up", style_none: "none" }); globalThis.polyline = globalThis.polyline || __pineNS({}); @@ -1520,7 +1679,9 @@ globalThis.map = globalThis.map || __pineNS({}); globalThis.session = globalThis.session || __pineNS({ regular: "regular", extended: "extended" }); -globalThis.dayofweek = globalThis.dayofweek || __pineNS({ sunday: 1, monday: 2, tuesday: 3, wednesday: 4, thursday: 5, friday: 6, saturday: 7 }); +globalThis.ticker = globalThis.ticker || __pineNS({}); + +globalThis.dayofweek = globalThis.dayofweek || __pineNS(Object.assign(function(t) { return new Date(t != null ? t : (globalThis.time || 0)).getUTCDay() + 1; }, { sunday: 1, monday: 2, tuesday: 3, wednesday: 4, thursday: 5, friday: 6, saturday: 7 })); globalThis.timeframe = __pineNS(Object.assign(globalThis.timeframe || {}, { period: (globalThis.timeframe && globalThis.timeframe.period) || "D", isintraday: false, isdaily: true, multiplier: 1 })); @@ -1568,8 +1729,6 @@ globalThis.bool = globalThis.bool || function(x) { return Boolean(x); }; globalThis.string = globalThis.string || function(x) { return x == null ? null : String(x); }; -globalThis.dayofweek = globalThis.dayofweek || __pineNS({ sunday: 1, monday: 2, tuesday: 3, wednesday: 4, thursday: 5, friday: 6, saturday: 7 }); - globalThis.str = globalThis.str || __pineNS({}); pinescript.math = globalThis.math; @@ -1655,7 +1814,7 @@ pinescript.text = { align_center: "center" }; pinescript.table = { - new: function(position, columns, rows, opts) { return { position, columns, rows, opts: opts || {}, cells: [] }; }, + new: function(position, columns, rows, opts) { return pinescript.__decDraw({ position, columns, rows, opts: opts || {}, cells: [] }); }, cell: function(table, column, row, text, opts) { @@ -1688,52 +1847,53 @@ function main() { null; // Study: LuckyFX RBtrader strategy // Options: {"overlay":true} - let enable_prev_candles = pinescript.inputBool(true, "Check: Prev Candles Same Dir?"); - let same_prev_candles = pinescript.inputInt(2, "Prev Candles Same (0–4)", ({ minval: 0, maxval: 4 })); - let enable_body_percent = pinescript.inputBool(true, "Check: Cur > % Prev Body?"); - let body_percent_min = pinescript.inputFloat(0.3, "Body % Min (0.3)", ({ minval: 0.3, maxval: 2, step: 0.05 })); - let body_percent_max = pinescript.inputFloat(3, "Body % Max (3.0)", ({ minval: 0.3, maxval: 3, step: 0.05 })); - let ignore_doji = pinescript.inputBool(true, "Ignore if Prev is Doji"); - let enable_mean_range = pinescript.inputBool(true, "Check: Cur >= Mean Full Candle?"); - let mean_length = pinescript.inputInt(5, "Mean Candle Range (2-50)", ({ minval: 2, maxval: 50 })); - let ignore_engulf = pinescript.inputBool(true, "Ignore if Engulfing (Body Size)"); - let enable_wick_compare = pinescript.inputBool(false, "Check: Cur wick > Prev wick (on/off)"); - let prev_open = pinescript.hist(0, open, 1); - let prev_close = pinescript.hist(1, close, 1); - let prev_high = pinescript.hist(2, high, 1); - let prev_low = pinescript.hist(3, low, 1); - let prev_body = pinescript.abs((prev_close - prev_open)); - let prev_upper_wick = (prev_high - pinescript.max(prev_open, prev_close)); - let prev_lower_wick = (pinescript.min(prev_open, prev_close) - prev_low); - let prev_total_wick = (prev_upper_wick + prev_lower_wick); - let prev_is_doji = (prev_total_wick >= (2 * prev_body)); - let prev_is_bull = (prev_close > prev_open); - let prev_is_bear = (prev_close < prev_open); - let cur_body = pinescript.abs((close - open)); - let cur_range = (high - low); - let is_bull = (close > open); - let is_bear = pinescript.close((prev_body + prev_upper_wick)); - let is_bearish_engulf = (cur_body > (prev_body + prev_lower_wick)); - let cur_upper_wick = (high - pinescript.max(open, close)); - let cur_lower_wick = (pinescript.min(open, close) - low); - let sum_range = 0; + var enable_prev_candles = pinescript.inputBool(true, "Check: Prev Candles Same Dir?"); + var same_prev_candles = pinescript.inputInt(2, "Prev Candles Same (0–4)", ({ minval: 0, maxval: 4 })); + var enable_body_percent = pinescript.inputBool(true, "Check: Cur > % Prev Body?"); + var body_percent_min = pinescript.inputFloat(0.3, "Body % Min (0.3)", ({ minval: 0.3, maxval: 2, step: 0.05 })); + var body_percent_max = pinescript.inputFloat(3, "Body % Max (3.0)", ({ minval: 0.3, maxval: 3, step: 0.05 })); + var ignore_doji = pinescript.inputBool(true, "Ignore if Prev is Doji"); + var enable_mean_range = pinescript.inputBool(true, "Check: Cur >= Mean Full Candle?"); + var mean_length = pinescript.inputInt(5, "Mean Candle Range (2-50)", ({ minval: 2, maxval: 50 })); + var ignore_engulf = pinescript.inputBool(true, "Ignore if Engulfing (Body Size)"); + var enable_wick_compare = pinescript.inputBool(false, "Check: Cur wick > Prev wick (on/off)"); + var prev_open = pinescript.hist(0, open, 1); + var prev_close = pinescript.hist(1, close, 1); + var prev_high = pinescript.hist(2, high, 1); + var prev_low = pinescript.hist(3, low, 1); + var prev_body = pinescript.abs((prev_close - prev_open)); + var prev_upper_wick = (prev_high - pinescript.max(prev_open, prev_close)); + var prev_lower_wick = (pinescript.min(prev_open, prev_close) - prev_low); + var prev_total_wick = (prev_upper_wick + prev_lower_wick); + var prev_is_doji = (prev_total_wick >= (2 * prev_body)); + var prev_is_bull = (prev_close > prev_open); + var prev_is_bear = (prev_close < prev_open); + var cur_body = pinescript.abs((close - open)); + var cur_range = (high - low); + var is_bull = (close > open); + var is_bear = (close < open); + var is_bullish_engulf = (cur_body > (prev_body + prev_upper_wick)); + var is_bearish_engulf = (cur_body > (prev_body + prev_lower_wick)); + var cur_upper_wick = (high - pinescript.max(open, close)); + var cur_lower_wick = (pinescript.min(open, close) - low); + var sum_range = 0; for (let i = 1; i <= mean_length; i++) { sum_range = (sum_range + (pinescript.hist(4, high, i) - pinescript.hist(5, low, i))); } - let mean_prev_range = (sum_range / mean_length); - let is_cur_range_big_enough = (!enable_mean_range || (cur_range >= mean_prev_range)); - let prev_dir = (prev_is_bull ? 1 : (prev_is_bear ? -1 : 0)); - let match = true; + var mean_prev_range = (sum_range / mean_length); + var is_cur_range_big_enough = (!enable_mean_range || (cur_range >= mean_prev_range)); + var prev_dir = (prev_is_bull ? 1 : (prev_is_bear ? -1 : 0)); + var match = true; if ((same_prev_candles > 0)) { for (let i = 2; i <= (same_prev_candles + 1); i++) { match = (match && (((pinescript.hist(6, close, i) > pinescript.hist(7, open, i)) ? 1 : ((pinescript.hist(8, close, i) < pinescript.hist(9, open, i)) ? -1 : 0)) === prev_dir)); } } - let last_n_same_as_prev = (!enable_prev_candles || ((prev_dir !== 0) && match)); - let body_percent_ok = (!enable_body_percent || ((cur_body > (body_percent_min * prev_body)) && (cur_body < (body_percent_max * prev_body)))); - let wick_condition = (!enable_wick_compare || ((is_bull && (cur_lower_wick > prev_lower_wick)) || (is_bear && (cur_upper_wick > prev_upper_wick)))); - let bull_bigger_than_bear = (((((((is_bull && prev_is_bear) && body_percent_ok) && (!ignore_doji || !prev_is_doji)) && (!ignore_engulf || !is_bullish_engulf)) && last_n_same_as_prev) && is_cur_range_big_enough) && wick_condition); - let bear_bigger_than_bull = (((((((is_bear && prev_is_bull) && body_percent_ok) && (!ignore_doji || !prev_is_doji)) && (!ignore_engulf || !is_bearish_engulf)) && last_n_same_as_prev) && is_cur_range_big_enough) && wick_condition); + var last_n_same_as_prev = (!enable_prev_candles || ((prev_dir !== 0) && match)); + var body_percent_ok = (!enable_body_percent || ((cur_body > (body_percent_min * prev_body)) && (cur_body < (body_percent_max * prev_body)))); + var wick_condition = (!enable_wick_compare || ((is_bull && (cur_lower_wick > prev_lower_wick)) || (is_bear && (cur_upper_wick > prev_upper_wick)))); + var bull_bigger_than_bear = (((((((is_bull && prev_is_bear) && body_percent_ok) && (!ignore_doji || !prev_is_doji)) && (!ignore_engulf || !is_bullish_engulf)) && last_n_same_as_prev) && is_cur_range_big_enough) && wick_condition); + var bear_bigger_than_bull = (((((((is_bear && prev_is_bull) && body_percent_ok) && (!ignore_doji || !prev_is_doji)) && (!ignore_engulf || !is_bearish_engulf)) && last_n_same_as_prev) && is_cur_range_big_enough) && wick_condition); pinescript.plotshape(bull_bigger_than_bear, ({ style: pinescript.shape.triangleup, color: pinescript.color.rgb(38, 129, 11), size: pinescript.size.small, location: pinescript.location.belowbar, title: "Bullish Signal" })); pinescript.plotshape(bear_bigger_than_bull, ({ style: pinescript.shape.triangledown, color: pinescript.color.rgb(226, 10, 10), size: pinescript.size.small, location: pinescript.location.abovebar, title: "Bearish Signal" })); pinescript.alertcondition(bull_bigger_than_bear, ({ title: "Bullish Signal", message: "Bullish > Bearish Setup" })); @@ -1782,6 +1942,10 @@ function run(data, options = {}) { rt.__shapeIdx = 0; globalThis.bar_index = i; globalThis.last_bar_index = n - 1; + globalThis.time_tradingday = inTime[i]; + globalThis.time_close = inTime[i]; + globalThis.last_bar_time = inTime[n - 1]; + globalThis.timenow = inTime[n - 1]; globalThis.barstate = { isfirst: i === 0, islast: i === n - 1, isrealtime: false, ishistory: true, isconfirmed: true, isnew: true, islastconfirmedhistory: i === n - 1, diff --git a/converted/SMC_Trade_with_Dhruv.js b/converted/SMC_Trade_with_Dhruv.js index 38648e4..1a365d4 100644 --- a/converted/SMC_Trade_with_Dhruv.js +++ b/converted/SMC_Trade_with_Dhruv.js @@ -42,6 +42,80 @@ const pinescript = { if (value != null && typeof value[Symbol.iterator] === 'function') return Array.from(value); return new Array(count || 0).fill(null); }, + __decArr: function(arr) { + if (!Array.isArray(arr) || arr.__pineDecorated) return arr; + const self = this; + const def = (name, fn) => Object.defineProperty(arr, name, { value: fn, writable: true, configurable: true }); + Object.defineProperty(arr, '__pineDecorated', { value: true, configurable: true }); + def('get', (i) => (arr[i] === undefined ? null : arr[i])); + def('set', (i, v) => { arr[i] = v; return v; }); + def('size', () => arr.length); + def('clear', () => { arr.length = 0; }); + def('insert', (i, v) => { arr.splice(i, 0, v); }); + def('remove', (i) => arr.splice(i, 1)[0]); + def('contains', (v) => arr.includes(v)); + def('indexof', (v) => arr.indexOf(v)); + def('lastindexof', (v) => arr.lastIndexOf(v)); + def('first', () => (arr.length ? arr[0] : null)); + def('last', () => (arr.length ? arr[arr.length - 1] : null)); + def('sum', () => arr.reduce((a, b) => a + b, 0)); + def('avg', () => (arr.length ? arr.reduce((a, b) => a + b, 0) / arr.length : 0)); + def('min', () => (arr.length ? Math.min(...arr) : null)); + def('max', () => (arr.length ? Math.max(...arr) : null)); + def('range', () => (arr.length ? Math.max(...arr) - Math.min(...arr) : null)); + // Statistical methods that route to the array.* built-ins (these names are not + // native to JS arrays, so attaching them here is safe from recursion). + def('median', () => self.arrayMedian(arr)); + def('mode', () => self.arrayMode(arr)); + def('stdev', () => self.arrayStdev(arr)); + def('variance', () => self.arrayVariance(arr)); + def('covariance', (other) => self.arrayCovariance(arr, other)); + def('percentile_linear_interpolation', (p) => self.arrayPercentileLinearInterpolation(arr, p)); + def('percentile_nearest_rank', (p) => self.arrayPercentileNearestRank(arr, p)); + def('abs', () => self.__decArr(self.arrayAbs(arr))); + def('binary_search', (v) => self.arrayBinarySearch(arr, v)); + // Pine's array.sort takes an order string, not a comparator. Use the native + // sort via .call so we don't recurse through this overridden method. + def('sort', (order) => { Array.prototype.sort.call(arr, (a, b) => (order === 'descending' ? b - a : a - b)); return arr; }); + def('sort_indices', (order) => self.__decArr(arr.map((_, i) => i).sort((a, b) => (order === 'descending' ? arr[b] - arr[a] : arr[a] - arr[b])))); + // join/slice/reverse/concat/includes/fill already exist natively on Array with + // compatible semantics, so we deliberately leave them to the native methods. + return arr; + }, + __decMap: function(m) { + if (!(m instanceof Map) || m.__pineDecorated) return m; + const def = (name, fn) => Object.defineProperty(m, name, { value: fn, writable: true, configurable: true }); + Object.defineProperty(m, '__pineDecorated', { value: true, configurable: true }); + def('put', (k, v) => { m.set(k, v); return v; }); + def('contains', (k) => m.has(k)); + def('remove', (k) => m.delete(k)); + def('keys', () => Array.from(Map.prototype.keys.call(m))); + def('values', () => Array.from(Map.prototype.values.call(m))); + def('size_', () => m.size); + return m; + }, + __decMatrix: function(m) { + if (!m || typeof m !== 'object' || m.__pineDecorated) return m; + const self = this; + const def = (name, fn) => Object.defineProperty(m, name, { value: fn, writable: true, configurable: true }); + Object.defineProperty(m, '__pineDecorated', { value: true, configurable: true }); + def('get', (r, c) => self.matrixGet(m, r, c)); + def('set', (r, c, v) => self.matrixSet(m, r, c, v)); + def('rows_', () => m.rows); + def('columns', () => m.cols); + def('fill', (v) => self.matrixFill(m, v)); + return m; + }, + __decDraw: function(obj) { + if (!obj || typeof obj !== 'object') return obj; + const p = new Proxy(obj, { + get(t, k) { + if (k in t || typeof k === 'symbol') return t[k]; + return function() { return p; }; + }, + }); + return p; + }, alertcondition: function(condition, ...rest) { if (globalThis.__pineRuntime) { globalThis.__pineRuntime.alerts.push({ condition, args: rest }); @@ -49,6 +123,32 @@ const pinescript = { return null; }, barcolor: function(color) { return null; }, + plotchar: function(series, ...rest) { + const rt = globalThis.__pineRuntime; + if (!rt) return series; + const bar = rt.__barIndex | 0; + const ord = (rt.__shapeIdx = (rt.__shapeIdx | 0) + 1) - 1; + let key = 'char_' + ord; + for (const r of rest) { + if (typeof r === 'string') { key = r; break; } + if (r && typeof r === 'object' && r.title) { key = String(r.title); break; } + } + let s = rt.plotshapes[key]; + if (!s) s = rt.plotshapes[key] = { title: key, data: [] }; + s.data[bar] = this.__scalar(series); + return series; + }, + plotarrow: function(series, ...rest) { + const rt = globalThis.__pineRuntime; + if (!rt) return series; + const bar = rt.__barIndex | 0; + const ord = (rt.__plotIdx = (rt.__plotIdx | 0) + 1) - 1; + const key = 'arrow_' + ord; + let p = rt.plots[key]; + if (!p) p = rt.plots[key] = { title: key, data: [] }; + p.data[bar] = this.__scalar(series); + return series; + }, bgcolor: function(color, title, editable, showLast) { return null; }, @@ -470,6 +570,12 @@ const pinescript = { atan: function(value) { return Math.atan(value); }, + todegrees: function(radians) { + return radians * (180 / Math.PI); + }, + toradians: function(degrees) { + return degrees * (Math.PI / 180); + }, floor: function(value) { return Math.floor(value); }, @@ -641,7 +747,7 @@ const pinescript = { return series; }, lineNew: function(x1, y1, x2, y2, opts = {}) { - return { x1, y1, x2, y2, opts, _type: 'line' }; + return this.__decDraw({ x1, y1, x2, y2, opts, _type: 'line' }); }, lineDelete: function(l) { return null; @@ -661,7 +767,7 @@ const pinescript = { return point === 0 || point === 'y1' ? line.y1 : line.y2; }, labelNew: function(x, y, text = '', opts = {}) { - return { x, y, text, opts, _type: 'label' }; + return this.__decDraw({ x, y, text, opts, _type: 'label' }); }, labelDelete: function(l) { return null; @@ -676,7 +782,7 @@ const pinescript = { return label.text || ''; }, boxNew: function(left, top, right, bottom, opts = {}) { - return { left, top, right, bottom, opts, _type: 'box' }; + return this.__decDraw({ left, top, right, bottom, opts, _type: 'box' }); }, boxDelete: function(box) { return null; @@ -694,7 +800,7 @@ const pinescript = { return box; }, polylineNew: function(points, opts = {}) { - return { points: points || [], opts, _type: 'polyline' }; + return this.__decDraw({ points: points || [], opts, _type: 'polyline' }); }, polylineDelete: function(poly) { return null; @@ -765,7 +871,7 @@ const pinescript = { return { time: _time ?? null, price: _price ?? null }; }, mapNew: function() { - return new Map(); + return this.__decMap(new Map()); }, mapSize: function(m) { return m instanceof Map ? m.size : 0; @@ -800,7 +906,7 @@ const pinescript = { const r = Math.max(0, rows ?? 0); const c = Math.max(0, cols ?? 0); const data = Array.from({ length: r }, () => Array.from({ length: c }, () => initialValue)); - return { rows: r, cols: c, data }; + return this.__decMatrix({ rows: r, cols: c, data }); }, matrixRows: function(m) { return m?.rows ?? 0; @@ -923,11 +1029,62 @@ const pinescript = { } return null; }, + __jacobiEigen: function(m) { + if (!m || !Array.isArray(m.data)) return null; + const n = m.rows ?? 0; + if (n === 0 || n !== (m.cols ?? 0)) return null; + // Work on a copy so the input matrix is untouched. + const a = m.data.map(row => row.slice()); + const v = Array.from({ length: n }, (_, i) => Array.from({ length: n }, (_, j) => (i === j ? 1 : 0))); + for (let sweep = 0; sweep < 100; sweep++) { + let off = 0; + for (let p = 0; p < n; p++) for (let q = p + 1; q < n; q++) off += a[p][q] * a[p][q]; + if (off < 1e-20) break; + for (let p = 0; p < n; p++) { + for (let q = p + 1; q < n; q++) { + if (Math.abs(a[p][q]) < 1e-18) continue; + const theta = (a[q][q] - a[p][p]) / (2 * a[p][q]); + const t = Math.sign(theta || 1) / (Math.abs(theta) + Math.sqrt(theta * theta + 1)); + const cos = 1 / Math.sqrt(t * t + 1); + const sin = t * cos; + for (let k = 0; k < n; k++) { + const akp = a[k][p], akq = a[k][q]; + a[k][p] = cos * akp - sin * akq; + a[k][q] = sin * akp + cos * akq; + } + for (let k = 0; k < n; k++) { + const apk = a[p][k], aqk = a[q][k]; + a[p][k] = cos * apk - sin * aqk; + a[q][k] = sin * apk + cos * aqk; + } + for (let k = 0; k < n; k++) { + const vkp = v[k][p], vkq = v[k][q]; + v[k][p] = cos * vkp - sin * vkq; + v[k][q] = sin * vkp + cos * vkq; + } + } + } + } + // Sort eigenpairs by eigenvalue, descending. + const order = Array.from({ length: n }, (_, i) => i).sort((i, j) => a[j][j] - a[i][i]); + const values = order.map(i => a[i][i]); + const vectors = Array.from({ length: n }, (_, r) => order.map(c => v[r][c])); + return { values, vectors }; + }, + matrixEigenvalues: function(m) { + const e = this.__jacobiEigen(m); + return this.__decArr(e ? e.values : []); + }, + matrixEigenvectors: function(m) { + const e = this.__jacobiEigen(m); + if (!e) return this.__decMatrix({ rows: 0, cols: 0, data: [] }); + return this.__decMatrix({ rows: e.vectors.length, cols: e.vectors.length, data: e.vectors }); + }, requestSecurity: function(symbol, timeframe, expression) { return expression; }, arrayNew: function(initialSize = 0, initialValue = 0) { - return Array(initialSize).fill(initialValue); + return this.__decArr(Array(initialSize).fill(initialValue)); }, arraySize: function(arr) { return arr ? arr.length : 0; @@ -978,10 +1135,10 @@ const pinescript = { return arr; }, arraySlice: function(arr, startIndex = 0, endIndex = null) { - if (!arr) return []; + if (!arr) return this.__decArr([]); const start = Number(startIndex) || 0; const end = endIndex === null || endIndex === undefined ? arr.length : Number(endIndex) || 0; - return arr.slice(start, end); + return this.__decArr(arr.slice(start, end)); }, arraySort: function(arr, order = 'ascending') { if (arr) arr.sort((a, b) => order === 'ascending' ? a - b : b - a); @@ -1191,7 +1348,7 @@ const pinescript = { const info = { ticker: 'AAPL', tickerid: 'NASDAQ:AAPL', prefix: 'NASDAQ', root: 'AAPL', suffix: '' }; return info[type] || ''; }, - timenow: 1781574528763, + timenow: 1782783577834, barstate: "LAST", dividends: {}, splits: {}, @@ -1245,11 +1402,11 @@ const pinescript = { arrayConcat: function(arr1, arr2) { if (!arr1) return arr2 || []; if (!arr2) return arr1; - return arr1.concat(arr2); + return this.__decArr(arr1.concat(arr2)); }, arrayCopy: function(arr) { - if (!arr) return []; - return [...arr]; + if (!arr) return this.__decArr([]); + return this.__decArr([...arr]); }, arrayBinarySearch: function(arr, value) { if (!arr || arr.length === 0) return -1; @@ -1339,7 +1496,7 @@ globalThis.input.timeframe = globalThis.input.timeframe || ((defval) => defval); globalThis.array = globalThis.array || { - from: (...items) => items, + from: (...items) => pinescript.__decArr(items), size: (arr) => pinescript.arraySize(arr), @@ -1508,6 +1665,8 @@ globalThis.line = globalThis.line || __pineNS({ style_solid: "solid", style_dash globalThis.box = globalThis.box || __pineNS({}); +globalThis.color = globalThis.color || __pineNS(Object.assign(function(c) { return c; }, { new: function(c, t) { return c; }, rgb: function(r, g, b, t) { return "#rgb(" + [r, g, b].join(",") + ")"; }, from_gradient: function(v, lo, hi, c1, c2) { return c1; }, r: function() { return 0; }, g: function() { return 0; }, b: function() { return 0; }, t: function() { return 0; }, aqua: "#00BCD4", black: "#363A45", blue: "#2962FF", fuchsia: "#E040FB", gray: "#787B86", green: "#4CAF50", lime: "#00E676", maroon: "#880E4F", navy: "#311B92", olive: "#808000", orange: "#FF9800", purple: "#9C27B0", red: "#FF5252", silver: "#B2B5BE", teal: "#00897B", white: "#FFFFFF", yellow: "#FFEB3B" })); + globalThis.label = globalThis.label || __pineNS({ style_label_down: "label_down", style_label_up: "label_up", style_none: "none" }); globalThis.polyline = globalThis.polyline || __pineNS({}); @@ -1520,7 +1679,9 @@ globalThis.map = globalThis.map || __pineNS({}); globalThis.session = globalThis.session || __pineNS({ regular: "regular", extended: "extended" }); -globalThis.dayofweek = globalThis.dayofweek || __pineNS({ sunday: 1, monday: 2, tuesday: 3, wednesday: 4, thursday: 5, friday: 6, saturday: 7 }); +globalThis.ticker = globalThis.ticker || __pineNS({}); + +globalThis.dayofweek = globalThis.dayofweek || __pineNS(Object.assign(function(t) { return new Date(t != null ? t : (globalThis.time || 0)).getUTCDay() + 1; }, { sunday: 1, monday: 2, tuesday: 3, wednesday: 4, thursday: 5, friday: 6, saturday: 7 })); globalThis.timeframe = __pineNS(Object.assign(globalThis.timeframe || {}, { period: (globalThis.timeframe && globalThis.timeframe.period) || "D", isintraday: false, isdaily: true, multiplier: 1 })); @@ -1568,8 +1729,6 @@ globalThis.bool = globalThis.bool || function(x) { return Boolean(x); }; globalThis.string = globalThis.string || function(x) { return x == null ? null : String(x); }; -globalThis.dayofweek = globalThis.dayofweek || __pineNS({ sunday: 1, monday: 2, tuesday: 3, wednesday: 4, thursday: 5, friday: 6, saturday: 7 }); - globalThis.str = globalThis.str || __pineNS({}); pinescript.math = globalThis.math; @@ -1655,7 +1814,7 @@ pinescript.text = { align_center: "center" }; pinescript.table = { - new: function(position, columns, rows, opts) { return { position, columns, rows, opts: opts || {}, cells: [] }; }, + new: function(position, columns, rows, opts) { return pinescript.__decDraw({ position, columns, rows, opts: opts || {}, cells: [] }); }, cell: function(table, column, row, text, opts) { @@ -1695,113 +1854,113 @@ function main() { null; // Study: SMC [Trade with Dhruv] // Options: {"overlay":true,"max_labels_count":500,"max_lines_count":500,"max_boxes_count":500} - let BULLISH_LEG = 1; - let BEARISH_LEG = 0; - let BULLISH = 1; - let BEARISH = -1; - let GREEN = pinescript.color.hex("#089981"); - let RED = pinescript.color.hex("#F23645"); - let BLUE = pinescript.color.rgb(33, 243, 219); - let GRAY = pinescript.color.hex("#878b94"); - let MONO_BULLISH = pinescript.color.rgb(255, 255, 255); - let MONO_BEARISH = pinescript.color.rgb(255, 255, 255); - let HISTORICAL = "Historical"; - let PRESENT = "Present"; - let COLORED = "Colored"; - let MONOCHROME = "Monochrome"; - let ALL = "All"; - let BOS = "BOS"; - let CHOCH = "CHoCH"; - let TINY = pinescript.size.tiny; - let SMALL = pinescript.size.small; - let NORMAL = pinescript.size.normal; - let ATR = "Atr"; - let RANGE = "Cumulative Mean Range"; - let CLOSE = "Close"; - let HIGHLOW = "High/Low"; - let SOLID = "⎯⎯⎯"; - let DASHED = "----"; - let DOTTED = "····"; - let SMART_GROUP = "Smart Money Concepts"; - let INTERNAL_GROUP = "Real Time Internal Structure"; - let SWING_GROUP = "Real Time Swing Structure"; - let BLOCKS_GROUP = "Order Blocks"; - let EQUAL_GROUP = "EQH/EQL"; - let GAPS_GROUP = "Fair Value Gaps"; - let LEVELS_GROUP = "Highs & Lows MTF"; - let ZONES_GROUP = "Premium & Discount Zones"; - let modeTooltip = "Allows to display historical Structure or only the recent ones"; - let styleTooltip = "Indicator color theme"; - let showTrendTooltip = "Display additional candles with a color reflecting the current trend detected by structure"; - let showInternalsTooltip = "Display internal market structure"; - let internalFilterConfluenceTooltip = "Filter non significant internal structure breakouts"; - let showStructureTooltip = "Display swing market Structure"; - let showSwingsTooltip = "Display swing point as labels on the chart"; - let showHighLowSwingsTooltip = "Highlight most recent strong and weak high/low points on the chart"; - let showInternalOrderBlocksTooltip = "Display internal order blocks on the chartnnNumber of internal order blocks to display on the chart"; - let showSwingOrderBlocksTooltip = "Display swing order blocks on the chartnnNumber of internal swing blocks to display on the chart"; - let orderBlockFilterTooltip = "Method used to filter out volatile order blocks nnIt is recommended to use the cumulative mean range method when a low amount of data is available"; - let orderBlockMitigationTooltip = "Select what values to use for order block mitigation"; - let showEqualHighsLowsTooltip = "Display equal highs and equal lows on the chart"; - let equalHighsLowsLengthTooltip = "Number of bars used to confirm equal highs and equal lows"; - let equalHighsLowsThresholdTooltip = "Sensitivity threshold in a range (0, 1) used for the detection of equal highs & lowsnnLower values will return fewer but more pertinent results"; - let showFairValueGapsTooltip = "Display fair values gaps on the chart"; - let fairValueGapsThresholdTooltip = "Filter out non significant fair value gaps"; - let fairValueGapsTimeframeTooltip = "Fair value gaps timeframe"; - let fairValueGapsExtendTooltip = "Determine how many bars to extend the Fair Value Gap boxes on chart"; - let showPremiumDiscountZonesTooltip = "Display premium, discount, and equilibrium zones on chart"; - let modeInput = pinescript.inputString(HISTORICAL, "Mode", ({ group: SMART_GROUP, tooltip: modeTooltip, options: [HISTORICAL, PRESENT] })); - let styleInput = pinescript.inputString(COLORED, "Style", ({ group: SMART_GROUP, tooltip: styleTooltip, options: [COLORED, MONOCHROME] })); - let showTrendInput = pinescript.input(false, "Color Candles", ({ group: SMART_GROUP, tooltip: showTrendTooltip })); - let showInternalsInput = pinescript.input(true, "Show Internal Structure", ({ group: INTERNAL_GROUP, tooltip: showInternalsTooltip })); - let showInternalBullInput = pinescript.inputString(ALL, "Bullish Structure", ({ group: INTERNAL_GROUP, inline: "ibull", options: [ALL, BOS, CHOCH] })); - let internalBullColorInput = pinescript.input(GREEN, "", ({ group: INTERNAL_GROUP, inline: "ibull" })); - let showInternalBearInput = pinescript.inputString(ALL, "Bearish Structure", ({ group: INTERNAL_GROUP, inline: "ibear", options: [ALL, BOS, CHOCH] })); - let internalBearColorInput = pinescript.input(RED, "", ({ group: INTERNAL_GROUP, inline: "ibear" })); - let internalFilterConfluenceInput = pinescript.input(false, "Confluence Filter", ({ group: INTERNAL_GROUP, tooltip: internalFilterConfluenceTooltip })); - let internalStructureSize = pinescript.inputString(TINY, "Internal Label Size", ({ group: INTERNAL_GROUP, options: [TINY, SMALL, NORMAL] })); - let showStructureInput = pinescript.input(true, "Show Swing Structure", ({ group: SWING_GROUP, tooltip: showStructureTooltip })); - let showSwingBullInput = pinescript.inputString(ALL, "Bullish Structure", ({ group: SWING_GROUP, inline: "bull", options: [ALL, BOS, CHOCH] })); - let swingBullColorInput = pinescript.input(GREEN, "", ({ group: SWING_GROUP, inline: "bull" })); - let showSwingBearInput = pinescript.inputString(ALL, "Bearish Structure", ({ group: SWING_GROUP, inline: "bear", options: [ALL, BOS, CHOCH] })); - let swingBearColorInput = pinescript.input(RED, "", ({ group: SWING_GROUP, inline: "bear" })); - let swingStructureSize = pinescript.inputString(SMALL, "Swing Label Size", ({ group: SWING_GROUP, options: [TINY, SMALL, NORMAL] })); - let showSwingsInput = pinescript.input(false, "Show Swings Points", ({ group: SWING_GROUP, tooltip: showSwingsTooltip, inline: "swings" })); - let swingsLengthInput = pinescript.inputInt(50, "", ({ group: SWING_GROUP, minval: 10, inline: "swings" })); - let showHighLowSwingsInput = pinescript.input(true, "Show Strong/Weak High/Low", ({ group: SWING_GROUP, tooltip: showHighLowSwingsTooltip })); - let showInternalOrderBlocksInput = pinescript.input(true, "Internal Order Blocks", ({ group: BLOCKS_GROUP, tooltip: showInternalOrderBlocksTooltip, inline: "iob" })); - let internalOrderBlocksSizeInput = pinescript.inputInt(5, "", ({ group: BLOCKS_GROUP, minval: 1, maxval: 20, inline: "iob" })); - let showSwingOrderBlocksInput = pinescript.input(false, "Swing Order Blocks", ({ group: BLOCKS_GROUP, tooltip: showSwingOrderBlocksTooltip, inline: "ob" })); - let swingOrderBlocksSizeInput = pinescript.inputInt(5, "", ({ group: BLOCKS_GROUP, minval: 1, maxval: 20, inline: "ob" })); - let orderBlockFilterInput = pinescript.inputString("Atr", "Order Block Filter", ({ group: BLOCKS_GROUP, tooltip: orderBlockFilterTooltip, options: [ATR, RANGE] })); - let orderBlockMitigationInput = pinescript.inputString(HIGHLOW, "Order Block Mitigation", ({ group: BLOCKS_GROUP, tooltip: orderBlockMitigationTooltip, options: [CLOSE, HIGHLOW] })); - let internalBullishOrderBlockColor = pinescript.inputColor(pinescript.color.rgb(49, 245, 186, 80), "Internal Bullish OB", ({ group: BLOCKS_GROUP })); - let internalBearishOrderBlockColor = pinescript.inputColor(pinescript.color.new(pinescript.color.hex("#f77c80"), 80), "Internal Bearish OB", ({ group: BLOCKS_GROUP })); - let swingBullishOrderBlockColor = pinescript.inputColor(pinescript.color.new(pinescript.color.hex("#1848cc"), 80), "Bullish OB", ({ group: BLOCKS_GROUP })); - let swingBearishOrderBlockColor = pinescript.inputColor(pinescript.color.new(pinescript.color.hex("#b22833"), 80), "Bearish OB", ({ group: BLOCKS_GROUP })); - let showEqualHighsLowsInput = pinescript.input(true, "Equal High/Low", ({ group: EQUAL_GROUP, tooltip: showEqualHighsLowsTooltip })); - let equalHighsLowsLengthInput = pinescript.inputInt(3, "Bars Confirmation", ({ group: EQUAL_GROUP, tooltip: equalHighsLowsLengthTooltip, minval: 1 })); - let equalHighsLowsThresholdInput = pinescript.inputFloat(0.1, "Threshold", ({ group: EQUAL_GROUP, tooltip: equalHighsLowsThresholdTooltip, minval: 0, maxval: 0.5, step: 0.1 })); - let equalHighsLowsSizeInput = pinescript.inputString(TINY, "Label Size", ({ group: EQUAL_GROUP, options: [TINY, SMALL, NORMAL] })); - let showFairValueGapsInput = pinescript.input(false, "Fair Value Gaps", ({ group: GAPS_GROUP, tooltip: showFairValueGapsTooltip })); - let fairValueGapsThresholdInput = pinescript.input(true, "Auto Threshold", ({ group: GAPS_GROUP, tooltip: fairValueGapsThresholdTooltip })); - let fairValueGapsTimeframeInput = input.timeframe("", "Timeframe", ({ group: GAPS_GROUP, tooltip: fairValueGapsTimeframeTooltip })); - let fairValueGapsBullColorInput = pinescript.inputColor(pinescript.color.new(pinescript.color.hex("#00ff68"), 70), "Bullish FVG", ({ group: GAPS_GROUP })); - let fairValueGapsBearColorInput = pinescript.inputColor(pinescript.color.new(pinescript.color.hex("#ff0008"), 70), "Bearish FVG", ({ group: GAPS_GROUP })); - let fairValueGapsExtendInput = pinescript.inputInt(1, "Extend FVG", ({ group: GAPS_GROUP, tooltip: fairValueGapsExtendTooltip, minval: 0 })); - let showDailyLevelsInput = pinescript.input(false, "Daily", ({ group: LEVELS_GROUP, inline: "daily" })); - let dailyLevelsStyleInput = pinescript.inputString(SOLID, "", ({ group: LEVELS_GROUP, inline: "daily", options: [SOLID, DASHED, DOTTED] })); - let dailyLevelsColorInput = pinescript.input(BLUE, "", ({ group: LEVELS_GROUP, inline: "daily" })); - let showWeeklyLevelsInput = pinescript.input(false, "Weekly", ({ group: LEVELS_GROUP, inline: "weekly" })); - let weeklyLevelsStyleInput = pinescript.inputString(SOLID, "", ({ group: LEVELS_GROUP, inline: "weekly", options: [SOLID, DASHED, DOTTED] })); - let weeklyLevelsColorInput = pinescript.input(BLUE, "", ({ group: LEVELS_GROUP, inline: "weekly" })); - let showMonthlyLevelsInput = pinescript.input(false, "Monthly", ({ group: LEVELS_GROUP, inline: "monthly" })); - let monthlyLevelsStyleInput = pinescript.inputString(SOLID, "", ({ group: LEVELS_GROUP, inline: "monthly", options: [SOLID, DASHED, DOTTED] })); - let monthlyLevelsColorInput = pinescript.input(BLUE, "", ({ group: LEVELS_GROUP, inline: "monthly" })); - let showPremiumDiscountZonesInput = pinescript.input(false, "Premium/Discount Zones", ({ group: ZONES_GROUP, tooltip: showPremiumDiscountZonesTooltip })); - let premiumZoneColorInput = pinescript.inputColor(RED, "Premium Zone", ({ group: ZONES_GROUP })); - let equilibriumZoneColorInput = pinescript.inputColor(GRAY, "Equilibrium Zone", ({ group: ZONES_GROUP })); - let discountZoneColorInput = pinescript.inputColor(GREEN, "Discount Zone", ({ group: ZONES_GROUP })); + var BULLISH_LEG = 1; + var BEARISH_LEG = 0; + var BULLISH = 1; + var BEARISH = -1; + var GREEN = pinescript.color.hex("#089981"); + var RED = pinescript.color.hex("#F23645"); + var BLUE = pinescript.color.rgb(33, 243, 219); + var GRAY = pinescript.color.hex("#878b94"); + var MONO_BULLISH = pinescript.color.rgb(255, 255, 255); + var MONO_BEARISH = pinescript.color.rgb(255, 255, 255); + var HISTORICAL = "Historical"; + var PRESENT = "Present"; + var COLORED = "Colored"; + var MONOCHROME = "Monochrome"; + var ALL = "All"; + var BOS = "BOS"; + var CHOCH = "CHoCH"; + var TINY = pinescript.size.tiny; + var SMALL = pinescript.size.small; + var NORMAL = pinescript.size.normal; + var ATR = "Atr"; + var RANGE = "Cumulative Mean Range"; + var CLOSE = "Close"; + var HIGHLOW = "High/Low"; + var SOLID = "⎯⎯⎯"; + var DASHED = "----"; + var DOTTED = "····"; + var SMART_GROUP = "Smart Money Concepts"; + var INTERNAL_GROUP = "Real Time Internal Structure"; + var SWING_GROUP = "Real Time Swing Structure"; + var BLOCKS_GROUP = "Order Blocks"; + var EQUAL_GROUP = "EQH/EQL"; + var GAPS_GROUP = "Fair Value Gaps"; + var LEVELS_GROUP = "Highs & Lows MTF"; + var ZONES_GROUP = "Premium & Discount Zones"; + var modeTooltip = "Allows to display historical Structure or only the recent ones"; + var styleTooltip = "Indicator color theme"; + var showTrendTooltip = "Display additional candles with a color reflecting the current trend detected by structure"; + var showInternalsTooltip = "Display internal market structure"; + var internalFilterConfluenceTooltip = "Filter non significant internal structure breakouts"; + var showStructureTooltip = "Display swing market Structure"; + var showSwingsTooltip = "Display swing point as labels on the chart"; + var showHighLowSwingsTooltip = "Highlight most recent strong and weak high/low points on the chart"; + var showInternalOrderBlocksTooltip = "Display internal order blocks on the chartnnNumber of internal order blocks to display on the chart"; + var showSwingOrderBlocksTooltip = "Display swing order blocks on the chartnnNumber of internal swing blocks to display on the chart"; + var orderBlockFilterTooltip = "Method used to filter out volatile order blocks nnIt is recommended to use the cumulative mean range method when a low amount of data is available"; + var orderBlockMitigationTooltip = "Select what values to use for order block mitigation"; + var showEqualHighsLowsTooltip = "Display equal highs and equal lows on the chart"; + var equalHighsLowsLengthTooltip = "Number of bars used to confirm equal highs and equal lows"; + var equalHighsLowsThresholdTooltip = "Sensitivity threshold in a range (0, 1) used for the detection of equal highs & lowsnnLower values will return fewer but more pertinent results"; + var showFairValueGapsTooltip = "Display fair values gaps on the chart"; + var fairValueGapsThresholdTooltip = "Filter out non significant fair value gaps"; + var fairValueGapsTimeframeTooltip = "Fair value gaps timeframe"; + var fairValueGapsExtendTooltip = "Determine how many bars to extend the Fair Value Gap boxes on chart"; + var showPremiumDiscountZonesTooltip = "Display premium, discount, and equilibrium zones on chart"; + var modeInput = pinescript.inputString(HISTORICAL, "Mode", ({ group: SMART_GROUP, tooltip: modeTooltip, options: [HISTORICAL, PRESENT] })); + var styleInput = pinescript.inputString(COLORED, "Style", ({ group: SMART_GROUP, tooltip: styleTooltip, options: [COLORED, MONOCHROME] })); + var showTrendInput = pinescript.input(false, "Color Candles", ({ group: SMART_GROUP, tooltip: showTrendTooltip })); + var showInternalsInput = pinescript.input(true, "Show Internal Structure", ({ group: INTERNAL_GROUP, tooltip: showInternalsTooltip })); + var showInternalBullInput = pinescript.inputString(ALL, "Bullish Structure", ({ group: INTERNAL_GROUP, inline: "ibull", options: [ALL, BOS, CHOCH] })); + var internalBullColorInput = pinescript.input(GREEN, "", ({ group: INTERNAL_GROUP, inline: "ibull" })); + var showInternalBearInput = pinescript.inputString(ALL, "Bearish Structure", ({ group: INTERNAL_GROUP, inline: "ibear", options: [ALL, BOS, CHOCH] })); + var internalBearColorInput = pinescript.input(RED, "", ({ group: INTERNAL_GROUP, inline: "ibear" })); + var internalFilterConfluenceInput = pinescript.input(false, "Confluence Filter", ({ group: INTERNAL_GROUP, tooltip: internalFilterConfluenceTooltip })); + var internalStructureSize = pinescript.inputString(TINY, "Internal Label Size", ({ group: INTERNAL_GROUP, options: [TINY, SMALL, NORMAL] })); + var showStructureInput = pinescript.input(true, "Show Swing Structure", ({ group: SWING_GROUP, tooltip: showStructureTooltip })); + var showSwingBullInput = pinescript.inputString(ALL, "Bullish Structure", ({ group: SWING_GROUP, inline: "bull", options: [ALL, BOS, CHOCH] })); + var swingBullColorInput = pinescript.input(GREEN, "", ({ group: SWING_GROUP, inline: "bull" })); + var showSwingBearInput = pinescript.inputString(ALL, "Bearish Structure", ({ group: SWING_GROUP, inline: "bear", options: [ALL, BOS, CHOCH] })); + var swingBearColorInput = pinescript.input(RED, "", ({ group: SWING_GROUP, inline: "bear" })); + var swingStructureSize = pinescript.inputString(SMALL, "Swing Label Size", ({ group: SWING_GROUP, options: [TINY, SMALL, NORMAL] })); + var showSwingsInput = pinescript.input(false, "Show Swings Points", ({ group: SWING_GROUP, tooltip: showSwingsTooltip, inline: "swings" })); + var swingsLengthInput = pinescript.inputInt(50, "", ({ group: SWING_GROUP, minval: 10, inline: "swings" })); + var showHighLowSwingsInput = pinescript.input(true, "Show Strong/Weak High/Low", ({ group: SWING_GROUP, tooltip: showHighLowSwingsTooltip })); + var showInternalOrderBlocksInput = pinescript.input(true, "Internal Order Blocks", ({ group: BLOCKS_GROUP, tooltip: showInternalOrderBlocksTooltip, inline: "iob" })); + var internalOrderBlocksSizeInput = pinescript.inputInt(5, "", ({ group: BLOCKS_GROUP, minval: 1, maxval: 20, inline: "iob" })); + var showSwingOrderBlocksInput = pinescript.input(false, "Swing Order Blocks", ({ group: BLOCKS_GROUP, tooltip: showSwingOrderBlocksTooltip, inline: "ob" })); + var swingOrderBlocksSizeInput = pinescript.inputInt(5, "", ({ group: BLOCKS_GROUP, minval: 1, maxval: 20, inline: "ob" })); + var orderBlockFilterInput = pinescript.inputString("Atr", "Order Block Filter", ({ group: BLOCKS_GROUP, tooltip: orderBlockFilterTooltip, options: [ATR, RANGE] })); + var orderBlockMitigationInput = pinescript.inputString(HIGHLOW, "Order Block Mitigation", ({ group: BLOCKS_GROUP, tooltip: orderBlockMitigationTooltip, options: [CLOSE, HIGHLOW] })); + var internalBullishOrderBlockColor = pinescript.inputColor(pinescript.color.rgb(49, 245, 186, 80), "Internal Bullish OB", ({ group: BLOCKS_GROUP })); + var internalBearishOrderBlockColor = pinescript.inputColor(pinescript.color.new(pinescript.color.hex("#f77c80"), 80), "Internal Bearish OB", ({ group: BLOCKS_GROUP })); + var swingBullishOrderBlockColor = pinescript.inputColor(pinescript.color.new(pinescript.color.hex("#1848cc"), 80), "Bullish OB", ({ group: BLOCKS_GROUP })); + var swingBearishOrderBlockColor = pinescript.inputColor(pinescript.color.new(pinescript.color.hex("#b22833"), 80), "Bearish OB", ({ group: BLOCKS_GROUP })); + var showEqualHighsLowsInput = pinescript.input(true, "Equal High/Low", ({ group: EQUAL_GROUP, tooltip: showEqualHighsLowsTooltip })); + var equalHighsLowsLengthInput = pinescript.inputInt(3, "Bars Confirmation", ({ group: EQUAL_GROUP, tooltip: equalHighsLowsLengthTooltip, minval: 1 })); + var equalHighsLowsThresholdInput = pinescript.inputFloat(0.1, "Threshold", ({ group: EQUAL_GROUP, tooltip: equalHighsLowsThresholdTooltip, minval: 0, maxval: 0.5, step: 0.1 })); + var equalHighsLowsSizeInput = pinescript.inputString(TINY, "Label Size", ({ group: EQUAL_GROUP, options: [TINY, SMALL, NORMAL] })); + var showFairValueGapsInput = pinescript.input(false, "Fair Value Gaps", ({ group: GAPS_GROUP, tooltip: showFairValueGapsTooltip })); + var fairValueGapsThresholdInput = pinescript.input(true, "Auto Threshold", ({ group: GAPS_GROUP, tooltip: fairValueGapsThresholdTooltip })); + var fairValueGapsTimeframeInput = input.timeframe("", "Timeframe", ({ group: GAPS_GROUP, tooltip: fairValueGapsTimeframeTooltip })); + var fairValueGapsBullColorInput = pinescript.inputColor(pinescript.color.new(pinescript.color.hex("#00ff68"), 70), "Bullish FVG", ({ group: GAPS_GROUP })); + var fairValueGapsBearColorInput = pinescript.inputColor(pinescript.color.new(pinescript.color.hex("#ff0008"), 70), "Bearish FVG", ({ group: GAPS_GROUP })); + var fairValueGapsExtendInput = pinescript.inputInt(1, "Extend FVG", ({ group: GAPS_GROUP, tooltip: fairValueGapsExtendTooltip, minval: 0 })); + var showDailyLevelsInput = pinescript.input(false, "Daily", ({ group: LEVELS_GROUP, inline: "daily" })); + var dailyLevelsStyleInput = pinescript.inputString(SOLID, "", ({ group: LEVELS_GROUP, inline: "daily", options: [SOLID, DASHED, DOTTED] })); + var dailyLevelsColorInput = pinescript.input(BLUE, "", ({ group: LEVELS_GROUP, inline: "daily" })); + var showWeeklyLevelsInput = pinescript.input(false, "Weekly", ({ group: LEVELS_GROUP, inline: "weekly" })); + var weeklyLevelsStyleInput = pinescript.inputString(SOLID, "", ({ group: LEVELS_GROUP, inline: "weekly", options: [SOLID, DASHED, DOTTED] })); + var weeklyLevelsColorInput = pinescript.input(BLUE, "", ({ group: LEVELS_GROUP, inline: "weekly" })); + var showMonthlyLevelsInput = pinescript.input(false, "Monthly", ({ group: LEVELS_GROUP, inline: "monthly" })); + var monthlyLevelsStyleInput = pinescript.inputString(SOLID, "", ({ group: LEVELS_GROUP, inline: "monthly", options: [SOLID, DASHED, DOTTED] })); + var monthlyLevelsColorInput = pinescript.input(BLUE, "", ({ group: LEVELS_GROUP, inline: "monthly" })); + var showPremiumDiscountZonesInput = pinescript.input(false, "Premium/Discount Zones", ({ group: ZONES_GROUP, tooltip: showPremiumDiscountZonesTooltip })); + var premiumZoneColorInput = pinescript.inputColor(RED, "Premium Zone", ({ group: ZONES_GROUP })); + var equilibriumZoneColorInput = pinescript.inputColor(GRAY, "Equilibrium Zone", ({ group: ZONES_GROUP })); + var discountZoneColorInput = pinescript.inputColor(GREEN, "Discount Zone", ({ group: ZONES_GROUP })); if (state.swingHigh === undefined) state.swingHigh = pivot.new(null, null, false); if (state.swingLow === undefined) state.swingLow = pivot.new(null, null, false); if (state.internalHigh === undefined) state.internalHigh = pivot.new(null, null, false); @@ -1831,36 +1990,36 @@ function main() { if (state.discountZoneColor === undefined) state.discountZoneColor = ((styleInput === MONOCHROME) ? MONO_BULLISH : discountZoneColorInput); if (state.currentBarIndex === undefined) state.currentBarIndex = bar_index; if (state.lastBarIndex === undefined) state.lastBarIndex = bar_index; - let currentAlerts = alerts.new(); + var currentAlerts = alerts.new(); if (state.initialTime === undefined) state.initialTime = time; if (barstate.isfirst) { if (showSwingOrderBlocksInput) { for (let index = 1; index <= swingOrderBlocksSizeInput; index++) { - swingOrderBlocksBoxes.push(pinescript.boxNew(null, null, null, null, ({ xloc: xloc.bar_time, extend: extend.right }))); + state.swingOrderBlocksBoxes.push(pinescript.boxNew(null, null, null, null, ({ xloc: xloc.bar_time, extend: extend.right }))); } } if (showInternalOrderBlocksInput) { for (let index = 1; index <= internalOrderBlocksSizeInput; index++) { - internalOrderBlocksBoxes.push(pinescript.boxNew(null, null, null, null, ({ xloc: xloc.bar_time, extend: extend.right }))); + state.internalOrderBlocksBoxes.push(pinescript.boxNew(null, null, null, null, ({ xloc: xloc.bar_time, extend: extend.right }))); } } } - let bearishOrderBlockMitigationSource = ((orderBlockMitigationInput === CLOSE) ? close : high); - let bullishOrderBlockMitigationSource = ((orderBlockMitigationInput === CLOSE) ? close : low); - let atrMeasure = pinescript.atr(200); - let volatilityMeasure = ((orderBlockFilterInput === ATR) ? atrMeasure : (pinescript.cum(pinescript.series(0, pinescript.ta.tr)) / bar_index)); - let highVolatilityBar = ((high - low) >= (2 * volatilityMeasure)); - let parsedHigh = (highVolatilityBar ? low : high); - let parsedLow = (highVolatilityBar ? high : low); - parsedHighs.push(parsedHigh); - parsedLows.push(parsedLow); - highs.push(high); - lows.push(low); - times.push(time); + var bearishOrderBlockMitigationSource = ((orderBlockMitigationInput === CLOSE) ? close : high); + var bullishOrderBlockMitigationSource = ((orderBlockMitigationInput === CLOSE) ? close : low); + var atrMeasure = pinescript.atr(200); + var volatilityMeasure = ((orderBlockFilterInput === ATR) ? atrMeasure : (pinescript.cum(pinescript.series(0, pinescript.ta.tr)) / bar_index)); + var highVolatilityBar = ((high - low) >= (2 * volatilityMeasure)); + var parsedHigh = (highVolatilityBar ? low : high); + var parsedLow = (highVolatilityBar ? high : low); + state.parsedHighs.push(parsedHigh); + state.parsedLows.push(parsedLow); + state.highs.push(high); + state.lows.push(low); + state.times.push(time); function leg(size) { if (state.leg === undefined) state.leg = 0; - let newLegHigh = (pinescript.hist(1, high, pinescript.size) > pinescript.highest(pinescript.series(2, pinescript.size))); - let newLegLow = (pinescript.hist(3, low, pinescript.size) < pinescript.lowest(pinescript.series(4, pinescript.size))); + var newLegHigh = (pinescript.hist(1, high, pinescript.size) > pinescript.highest(pinescript.series(2, pinescript.size))); + var newLegLow = (pinescript.hist(3, low, pinescript.size) < pinescript.lowest(pinescript.series(4, pinescript.size))); if (newLegHigh) { state.leg = BEARISH_LEG; } else { @@ -1882,15 +2041,15 @@ function main() { function drawLabel(labelTime, labelPrice, tag, labelColor, labelStyle) { if (state.l_abel === undefined) state.l_abel = null; if ((modeInput === PRESENT)) { - l_abel.delete(); + state.l_abel.delete(); } state.l_abel = pinescript.labelNew(pinescript.chartPointNew(labelTime, null, labelPrice), tag, xloc.bar_time, ({ color: color(null), textcolor: labelColor, style: labelStyle, size: pinescript.size.small })); } function drawEqualHighLow(p_ivot, level, size, equalHigh) { - let e_qualDisplay = (state.equalHigh ? state.equalHighDisplay : state.equalLowDisplay); - let tag = "EQL"; - let equalColor = state.swingBullishColor; - let labelStyle = label.style_label_up; + var e_qualDisplay = (state.equalHigh ? state.equalHighDisplay : state.equalLowDisplay); + var tag = "EQL"; + var equalColor = state.swingBullishColor; + var labelStyle = label.style_label_up; if (state.equalHigh) { tag = "EQH"; equalColor = state.swingBearishColor; @@ -1901,17 +2060,17 @@ function main() { pinescript.labelDelete(e_qualDisplay.l_abel); } e_qualDisplay.l_ine = pinescript.lineNew(pinescript.chartPointNew(p_ivot.barTime, null, p_ivot.currentLevel), pinescript.chartPointNew(pinescript.hist(8, time, pinescript.size), null, level), ({ xloc: xloc.bar_time, color: equalColor, style: line.style_dotted })); - let labelPosition = pinescript.round((0.5 * ((p_ivot.barIndex + bar_index) - pinescript.size))); + var labelPosition = pinescript.round((0.5 * ((p_ivot.barIndex + bar_index) - pinescript.size))); e_qualDisplay.l_abel = pinescript.labelNew(pinescript.chartPointNew(null, labelPosition, level), tag, xloc.bar_index, ({ color: color(null), textcolor: equalColor, style: labelStyle, size: equalHighsLowsSizeInput })); } function getCurrentStructure(size, equalHighLow = false, internal = false) { - let currentLeg = leg(pinescript.size); - let newPivot = startOfNewLeg(currentLeg); - let pivotLow = startOfBullishLeg(currentLeg); - let pivotHigh = startOfBearishLeg(currentLeg); + var currentLeg = leg(pinescript.size); + var newPivot = startOfNewLeg(currentLeg); + var pivotLow = startOfBullishLeg(currentLeg); + var pivotHigh = startOfBearishLeg(currentLeg); if (newPivot) { if (pivotLow) { - let p_ivot = (equalHighLow ? state.equalLow : (internal ? state.internalLow : state.swingLow)); + var p_ivot = (equalHighLow ? state.equalLow : (internal ? state.internalLow : state.swingLow)); if ((equalHighLow && (pinescript.abs((p_ivot.currentLevel - pinescript.hist(9, low, pinescript.size))) < (equalHighsLowsThresholdInput * atrMeasure)))) { drawEqualHighLow(p_ivot, pinescript.hist(10, low, pinescript.size), pinescript.size, false); } @@ -1927,10 +2086,12 @@ function main() { state.trailing.lastBottomTime = p_ivot.barTime; } if (((showSwingsInput && !internal) && !equalHighLow)) { - drawLabel(pinescript.hist(14, time, pinescript.size), p_ivot.currentLevel, ((p_ivot.currentLevel < p_ivot.lastLevel) ? "LL" : "HL"), state.swingBullishColor, label.style_label_up); + return drawLabel(pinescript.hist(14, time, pinescript.size), p_ivot.currentLevel, ((p_ivot.currentLevel < p_ivot.lastLevel) ? "LL" : "HL"), state.swingBullishColor, label.style_label_up); + } else { + return null; } } else { - let p_ivot = (equalHighLow ? state.equalHigh : (internal ? state.internalHigh : state.swingHigh)); + p_ivot = (equalHighLow ? state.equalHigh : (internal ? state.internalHigh : state.swingHigh)); if ((equalHighLow && (pinescript.abs((p_ivot.currentLevel - pinescript.hist(15, high, pinescript.size))) < (equalHighsLowsThresholdInput * atrMeasure)))) { drawEqualHighLow(p_ivot, pinescript.hist(16, high, pinescript.size), pinescript.size, true); } @@ -1946,25 +2107,29 @@ function main() { state.trailing.lastTopTime = p_ivot.barTime; } if (((showSwingsInput && !internal) && !equalHighLow)) { - drawLabel(pinescript.hist(20, time, pinescript.size), p_ivot.currentLevel, ((p_ivot.currentLevel > p_ivot.lastLevel) ? "HH" : "LH"), state.swingBearishColor, label.style_label_down); + return drawLabel(pinescript.hist(20, time, pinescript.size), p_ivot.currentLevel, ((p_ivot.currentLevel > p_ivot.lastLevel) ? "HH" : "LH"), state.swingBearishColor, label.style_label_down); + } else { + return null; } } + } else { + return null; } } function drawStructure(p_ivot, tag, structureColor, lineStyle, labelStyle, labelSize) { if (state.l_ine === undefined) state.l_ine = pinescript.lineNew(null, null, null, null, ({ xloc: xloc.bar_time })); if (state.l_abel === undefined) state.l_abel = pinescript.labelNew(null, null); if ((modeInput === PRESENT)) { - l_ine.delete(); - l_abel.delete(); + state.l_ine.delete(); + state.l_abel.delete(); } state.l_ine = pinescript.lineNew(pinescript.chartPointNew(p_ivot.barTime, null, p_ivot.currentLevel), pinescript.chartPointNew(time, null, p_ivot.currentLevel), xloc.bar_time, ({ color: structureColor, style: lineStyle })); state.l_abel = pinescript.labelNew(pinescript.chartPointNew(null, pinescript.round((0.5 * (p_ivot.barIndex + bar_index))), p_ivot.currentLevel), tag, xloc.bar_index, ({ color: color(null), textcolor: structureColor, style: labelStyle, size: labelSize })); } function deleteOrderBlocks(internal = false) { - let orderBlocks = (internal ? state.internalOrderBlocks : state.swingOrderBlocks); - for (const [index, eachOrderBlock] of orderBlocks) { - let crossedOderBlock = false; + var orderBlocks = (internal ? state.internalOrderBlocks : state.swingOrderBlocks); + for (const [index, eachOrderBlock] of (orderBlocks ?? []).entries()) { + var crossedOderBlock = false; if (((bearishOrderBlockMitigationSource > eachOrderBlock.barHigh) && (eachOrderBlock.bias === BEARISH))) { crossedOderBlock = true; if (internal) { @@ -1989,38 +2154,42 @@ function main() { } function storeOrdeBlock(p_ivot, internal = false, bias) { if (((!internal && showSwingOrderBlocksInput) || (internal && showInternalOrderBlocksInput))) { - let a_rray = null; - let parsedIndex = null; + var a_rray = null; + var parsedIndex = null; if ((bias === BEARISH)) { - a_rray = parsedHighs.slice(p_ivot.barIndex, bar_index); + a_rray = state.parsedHighs.slice(p_ivot.barIndex, bar_index); parsedIndex = (p_ivot.barIndex + a_rray.indexof(a_rray.max())); } else { - a_rray = parsedLows.slice(p_ivot.barIndex, bar_index); + a_rray = state.parsedLows.slice(p_ivot.barIndex, bar_index); parsedIndex = (p_ivot.barIndex + a_rray.indexof(a_rray.min())); } - let o_rderBlock = orderBlock.new(parsedHighs.get(parsedIndex), parsedLows.get(parsedIndex), times.get(parsedIndex), bias); - let orderBlocks = (internal ? state.internalOrderBlocks : state.swingOrderBlocks); + var o_rderBlock = orderBlock.new(state.parsedHighs.get(parsedIndex), state.parsedLows.get(parsedIndex), state.times.get(parsedIndex), bias); + var orderBlocks = (internal ? state.internalOrderBlocks : state.swingOrderBlocks); if ((orderBlocks.size() >= 100)) { orderBlocks.pop(); } - orderBlocks.unshift(o_rderBlock); + return orderBlocks.unshift(o_rderBlock); + } else { + return null; } } function drawOrderBlocks(internal = false) { - let orderBlocks = (internal ? state.internalOrderBlocks : state.swingOrderBlocks); - let orderBlocksSize = orderBlocks.size(); + var orderBlocks = (internal ? state.internalOrderBlocks : state.swingOrderBlocks); + var orderBlocksSize = orderBlocks.size(); if ((orderBlocksSize > 0)) { - let maxOrderBlocks = (internal ? internalOrderBlocksSizeInput : swingOrderBlocksSizeInput); - let parsedOrdeBlocks = orderBlocks.slice(0, pinescript.min(maxOrderBlocks, orderBlocksSize)); - let b_oxes = (internal ? state.internalOrderBlocksBoxes : state.swingOrderBlocksBoxes); - for (const [index, eachOrderBlock] of parsedOrdeBlocks) { - let orderBlockColor = ((styleInput === MONOCHROME) ? ((eachOrderBlock.bias === BEARISH) ? pinescript.color.new(MONO_BEARISH, 80) : pinescript.color.new(MONO_BULLISH, 80)) : (internal ? ((eachOrderBlock.bias === BEARISH) ? internalBearishOrderBlockColor : internalBullishOrderBlockColor) : ((eachOrderBlock.bias === BEARISH) ? swingBearishOrderBlockColor : swingBullishOrderBlockColor))); - let b_ox = b_oxes.get(index); + var maxOrderBlocks = (internal ? internalOrderBlocksSizeInput : swingOrderBlocksSizeInput); + var parsedOrdeBlocks = orderBlocks.slice(0, pinescript.min(maxOrderBlocks, orderBlocksSize)); + var b_oxes = (internal ? state.internalOrderBlocksBoxes : state.swingOrderBlocksBoxes); + for (const [index, eachOrderBlock] of (parsedOrdeBlocks ?? []).entries()) { + var orderBlockColor = ((styleInput === MONOCHROME) ? ((eachOrderBlock.bias === BEARISH) ? pinescript.color.new(MONO_BEARISH, 80) : pinescript.color.new(MONO_BULLISH, 80)) : (internal ? ((eachOrderBlock.bias === BEARISH) ? internalBearishOrderBlockColor : internalBullishOrderBlockColor) : ((eachOrderBlock.bias === BEARISH) ? swingBearishOrderBlockColor : swingBullishOrderBlockColor))); + var b_ox = b_oxes.get(index); b_ox.set_top_left_point(pinescript.chartPointNew(eachOrderBlock.barTime, null, eachOrderBlock.barHigh)); b_ox.set_bottom_right_point(pinescript.chartPointNew(last_bar_time, null, eachOrderBlock.barLow)); b_ox.set_border_color((internal ? null : orderBlockColor)); b_ox.set_bgcolor(orderBlockColor); } + } else { + return null; } } function displayStructure(internal = false) { @@ -2030,14 +2199,14 @@ function main() { state.bullishBar = ((high - pinescript.max(close, open)) > pinescript.min(close, (open - low))); state.bearishBar = ((high - pinescript.max(close, open)) < pinescript.min(close, (open - low))); } - let p_ivot = (internal ? state.internalHigh : state.swingHigh); - let t_rend = (internal ? state.internalTrend : state.swingTrend); - let lineStyle = (internal ? line.style_dashed : line.style_solid); - let labelSize = (internal ? internalStructureSize : swingStructureSize); - let extraCondition = (internal ? ((state.internalHigh.currentLevel !== state.swingHigh.currentLevel) && state.bullishBar) : true); - let bullishColor = ((styleInput === MONOCHROME) ? MONO_BULLISH : (internal ? internalBullColorInput : swingBullColorInput)); + var p_ivot = (internal ? state.internalHigh : state.swingHigh); + var t_rend = (internal ? state.internalTrend : state.swingTrend); + var lineStyle = (internal ? line.style_dashed : line.style_solid); + var labelSize = (internal ? internalStructureSize : swingStructureSize); + var extraCondition = (internal ? ((state.internalHigh.currentLevel !== state.swingHigh.currentLevel) && state.bullishBar) : true); + var bullishColor = ((styleInput === MONOCHROME) ? MONO_BULLISH : (internal ? internalBullColorInput : swingBullColorInput)); if (((pinescript.crossover(pinescript.series(21, close), pinescript.series(22, p_ivot.currentLevel)) && !p_ivot.crossed) && extraCondition)) { - let tag = ((t_rend.bias === BEARISH) ? CHOCH : BOS); + var tag = ((t_rend.bias === BEARISH) ? CHOCH : BOS); if (internal) { currentAlerts.internalBullishCHoCH = (tag === CHOCH); currentAlerts.internalBullishBOS = (tag === BOS); @@ -2047,7 +2216,7 @@ function main() { } p_ivot.crossed = true; t_rend.bias = BULLISH; - let displayCondition = (internal ? (showInternalsInput && (((showInternalBullInput === ALL) || ((showInternalBullInput === BOS) && (tag !== CHOCH))) || ((showInternalBullInput === CHOCH) && (tag === CHOCH)))) : (showStructureInput && (((showSwingBullInput === ALL) || ((showSwingBullInput === BOS) && (tag !== CHOCH))) || ((showSwingBullInput === CHOCH) && (tag === CHOCH))))); + var displayCondition = (internal ? (showInternalsInput && (((showInternalBullInput === ALL) || ((showInternalBullInput === BOS) && (tag !== CHOCH))) || ((showInternalBullInput === CHOCH) && (tag === CHOCH)))) : (showStructureInput && (((showSwingBullInput === ALL) || ((showSwingBullInput === BOS) && (tag !== CHOCH))) || ((showSwingBullInput === CHOCH) && (tag === CHOCH))))); if (displayCondition) { drawStructure(p_ivot, tag, bullishColor, lineStyle, label.style_label_down, labelSize); } @@ -2057,9 +2226,9 @@ function main() { } p_ivot = (internal ? state.internalLow : state.swingLow); extraCondition = (internal ? ((state.internalLow.currentLevel !== state.swingLow.currentLevel) && state.bearishBar) : true); - let bearishColor = ((styleInput === MONOCHROME) ? MONO_BEARISH : (internal ? internalBearColorInput : swingBearColorInput)); + var bearishColor = ((styleInput === MONOCHROME) ? MONO_BEARISH : (internal ? internalBearColorInput : swingBearColorInput)); if (((pinescript.crossunder(pinescript.series(23, close), pinescript.series(24, p_ivot.currentLevel)) && !p_ivot.crossed) && extraCondition)) { - let tag = ((t_rend.bias === BULLISH) ? CHOCH : BOS); + tag = ((t_rend.bias === BULLISH) ? CHOCH : BOS); if (internal) { currentAlerts.internalBearishCHoCH = (tag === CHOCH); currentAlerts.internalBearishBOS = (tag === BOS); @@ -2074,36 +2243,42 @@ function main() { drawStructure(p_ivot, tag, bearishColor, lineStyle, label.style_label_up, labelSize); } if (((internal && showInternalOrderBlocksInput) || (!internal && showSwingOrderBlocksInput))) { - storeOrdeBlock(p_ivot, internal, BEARISH); + return storeOrdeBlock(p_ivot, internal, BEARISH); + } else { + return null; } + } else { + return null; } } function fairValueGapBox(leftTime, rightTime, topPrice, bottomPrice, boxColor) { return pinescript.boxNew(pinescript.chartPointNew(leftTime, null, topPrice), pinescript.chartPointNew((rightTime + (fairValueGapsExtendInput * (time - pinescript.hist(25, time, 1)))), null, bottomPrice), ({ xloc: xloc.bar_time, border_color: boxColor, bgcolor: boxColor })); } function deleteFairValueGaps() { - for (const [index, eachFairValueGap] of state.fairValueGaps) { + for (const [index, eachFairValueGap] of (state.fairValueGaps ?? []).entries()) { if ((((low < eachFairValueGap.bottom) && (eachFairValueGap.bias === BULLISH)) || ((high > eachFairValueGap.top) && (eachFairValueGap.bias === BEARISH)))) { eachFairValueGap.topBox.delete(); eachFairValueGap.bottomBox.delete(); - fairValueGaps.remove(index); + state.fairValueGaps.remove(index); } } } function drawFairValueGaps() { - let [lastClose, lastOpen, lastTime, currentHigh, currentLow, currentTime, last2High, last2Low] = pinescript.unpack(pinescript.requestSecurity(syminfo.tickerid, fairValueGapsTimeframeInput, [pinescript.hist(26, close, 1), pinescript.hist(27, open, 1), pinescript.hist(28, time, 1), pinescript.hist(29, high, 0), pinescript.hist(30, low, 0), pinescript.hist(31, time, 0), pinescript.hist(32, high, 2), pinescript.hist(33, low, 2)], ({ lookahead: barmerge.lookahead_on })), 8); - let barDeltaPercent = ((lastClose - lastOpen) / (lastOpen * 100)); - let newTimeframe = timeframe.change(fairValueGapsTimeframeInput); - let threshold = (fairValueGapsThresholdInput ? ((pinescript.cum(pinescript.series(34, pinescript.abs((newTimeframe ? barDeltaPercent : 0)))) / bar_index) * 2) : 0); - let bullishFairValueGap = ((((currentLow > last2High) && (lastClose > last2High)) && (barDeltaPercent > threshold)) && newTimeframe); - let bearishFairValueGap = ((((currentHigh < last2Low) && (lastClose < last2Low)) && (-barDeltaPercent > threshold)) && newTimeframe); + var [lastClose, lastOpen, lastTime, currentHigh, currentLow, currentTime, last2High, last2Low] = pinescript.unpack(pinescript.requestSecurity(syminfo.tickerid, fairValueGapsTimeframeInput, [pinescript.hist(26, close, 1), pinescript.hist(27, open, 1), pinescript.hist(28, time, 1), pinescript.hist(29, high, 0), pinescript.hist(30, low, 0), pinescript.hist(31, time, 0), pinescript.hist(32, high, 2), pinescript.hist(33, low, 2)], ({ lookahead: barmerge.lookahead_on })), 8); + var barDeltaPercent = ((lastClose - lastOpen) / (lastOpen * 100)); + var newTimeframe = timeframe.change(fairValueGapsTimeframeInput); + var threshold = (fairValueGapsThresholdInput ? ((pinescript.cum(pinescript.series(34, pinescript.abs((newTimeframe ? barDeltaPercent : 0)))) / bar_index) * 2) : 0); + var bullishFairValueGap = ((((currentLow > last2High) && (lastClose > last2High)) && (barDeltaPercent > threshold)) && newTimeframe); + var bearishFairValueGap = ((((currentHigh < last2Low) && (lastClose < last2Low)) && (-barDeltaPercent > threshold)) && newTimeframe); if (bullishFairValueGap) { currentAlerts.bullishFairValueGap = true; - fairValueGaps.unshift(fairValueGap.new(currentLow, last2High, BULLISH, fairValueGapBox(lastTime, currentTime, currentLow, pinescript.avg(currentLow, last2High), state.fairValueGapBullishColor), fairValueGapBox(lastTime, currentTime, pinescript.avg(currentLow, last2High), last2High, state.fairValueGapBullishColor))); + state.fairValueGaps.unshift(fairValueGap.new(currentLow, last2High, BULLISH, fairValueGapBox(lastTime, currentTime, currentLow, pinescript.avg(currentLow, last2High), state.fairValueGapBullishColor), fairValueGapBox(lastTime, currentTime, pinescript.avg(currentLow, last2High), last2High, state.fairValueGapBullishColor))); } if (bearishFairValueGap) { currentAlerts.bearishFairValueGap = true; - fairValueGaps.unshift(fairValueGap.new(currentHigh, last2Low, BEARISH, fairValueGapBox(lastTime, currentTime, currentHigh, pinescript.avg(currentHigh, last2Low), state.fairValueGapBearishColor), fairValueGapBox(lastTime, currentTime, pinescript.avg(currentHigh, last2Low), last2Low, state.fairValueGapBearishColor))); + return state.fairValueGaps.unshift(fairValueGap.new(currentHigh, last2Low, BEARISH, fairValueGapBox(lastTime, currentTime, currentHigh, pinescript.avg(currentHigh, last2Low), state.fairValueGapBearishColor), fairValueGapBox(lastTime, currentTime, pinescript.avg(currentHigh, last2Low), last2Low, state.fairValueGapBearishColor))); + } else { + return null; } } function getStyle(style) { @@ -2126,19 +2301,19 @@ function main() { } } function drawLevels(timeframe, sameTimeframe, style, levelColor) { - let [topLevel, bottomLevel, leftTime, rightTime] = pinescript.unpack(pinescript.requestSecurity(syminfo.tickerid, timeframe, [pinescript.hist(35, high, 1), pinescript.hist(36, low, 1), pinescript.hist(37, time, 1), time], ({ lookahead: barmerge.lookahead_on })), 4); - let parsedTop = (sameTimeframe ? high : topLevel); - let parsedBottom = (sameTimeframe ? low : bottomLevel); - let parsedLeftTime = (sameTimeframe ? time : leftTime); - let parsedRightTime = (sameTimeframe ? time : rightTime); - let parsedTopTime = time; - let parsedBottomTime = time; + var [topLevel, bottomLevel, leftTime, rightTime] = pinescript.unpack(pinescript.requestSecurity(syminfo.tickerid, timeframe, [pinescript.hist(35, high, 1), pinescript.hist(36, low, 1), pinescript.hist(37, time, 1), time], ({ lookahead: barmerge.lookahead_on })), 4); + var parsedTop = (sameTimeframe ? high : topLevel); + var parsedBottom = (sameTimeframe ? low : bottomLevel); + var parsedLeftTime = (sameTimeframe ? time : leftTime); + var parsedRightTime = (sameTimeframe ? time : rightTime); + var parsedTopTime = time; + var parsedBottomTime = time; if (!sameTimeframe) { - let leftIndex = times.binary_search_rightmost(parsedLeftTime); - let rightIndex = times.binary_search_rightmost(parsedRightTime); - let timeArray = times.slice(leftIndex, rightIndex); - let topArray = highs.slice(leftIndex, rightIndex); - let bottomArray = lows.slice(leftIndex, rightIndex); + var leftIndex = state.times.binary_search_rightmost(parsedLeftTime); + var rightIndex = state.times.binary_search_rightmost(parsedRightTime); + var timeArray = state.times.slice(leftIndex, rightIndex); + var topArray = state.highs.slice(leftIndex, rightIndex); + var bottomArray = state.lows.slice(leftIndex, rightIndex); parsedTopTime = ((timeArray.size() > 0) ? timeArray.get(topArray.indexof(topArray.max())) : state.initialTime); parsedBottomTime = ((timeArray.size() > 0) ? timeArray.get(bottomArray.indexof(bottomArray.min())) : state.initialTime); } @@ -2146,12 +2321,12 @@ function main() { if (state.bottomLine === undefined) state.bottomLine = pinescript.lineNew(null, null, null, null, ({ xloc: xloc.bar_time, color: levelColor, style: getStyle(style) })); if (state.topLabel === undefined) state.topLabel = pinescript.labelNew(null, null, ({ xloc: xloc.bar_time, text: pinescript.strFormat("P{0}H", timeframe), color: color(null), textcolor: levelColor, size: pinescript.size.small, style: label.style_label_left })); if (state.bottomLabel === undefined) state.bottomLabel = pinescript.labelNew(null, null, ({ xloc: xloc.bar_time, text: pinescript.strFormat("P{0}L", timeframe), color: color(null), textcolor: levelColor, size: pinescript.size.small, style: label.style_label_left })); - topLine.set_first_point(pinescript.chartPointNew(parsedTopTime, null, parsedTop)); - topLine.set_second_point(pinescript.chartPointNew((last_bar_time + (20 * (time - pinescript.hist(38, time, 1)))), null, parsedTop)); - topLabel.set_point(pinescript.chartPointNew((last_bar_time + (20 * (time - pinescript.hist(39, time, 1)))), null, parsedTop)); - bottomLine.set_first_point(pinescript.chartPointNew(parsedBottomTime, null, parsedBottom)); - bottomLine.set_second_point(pinescript.chartPointNew((last_bar_time + (20 * (time - pinescript.hist(40, time, 1)))), null, parsedBottom)); - return bottomLabel.set_point(pinescript.chartPointNew((last_bar_time + (20 * (time - pinescript.hist(41, time, 1)))), null, parsedBottom)); + state.topLine.set_first_point(pinescript.chartPointNew(parsedTopTime, null, parsedTop)); + state.topLine.set_second_point(pinescript.chartPointNew((last_bar_time + (20 * (time - pinescript.hist(38, time, 1)))), null, parsedTop)); + state.topLabel.set_point(pinescript.chartPointNew((last_bar_time + (20 * (time - pinescript.hist(39, time, 1)))), null, parsedTop)); + state.bottomLine.set_first_point(pinescript.chartPointNew(parsedBottomTime, null, parsedBottom)); + state.bottomLine.set_second_point(pinescript.chartPointNew((last_bar_time + (20 * (time - pinescript.hist(40, time, 1)))), null, parsedBottom)); + return state.bottomLabel.set_point(pinescript.chartPointNew((last_bar_time + (20 * (time - pinescript.hist(41, time, 1)))), null, parsedBottom)); } function higherTimeframe(timeframe) { return (timeframe.in_seconds() > timeframe.in_seconds(timeframe)); @@ -2167,31 +2342,31 @@ function main() { if (state.bottomLine === undefined) state.bottomLine = pinescript.lineNew(null, null, null, null, ({ color: state.swingBullishColor, xloc: xloc.bar_time })); if (state.topLabel === undefined) state.topLabel = pinescript.labelNew(null, null, ({ color: color(null), textcolor: state.swingBearishColor, xloc: xloc.bar_time, style: label.style_label_down, size: pinescript.size.tiny })); if (state.bottomLabel === undefined) state.bottomLabel = pinescript.labelNew(null, null, ({ color: color(null), textcolor: state.swingBullishColor, xloc: xloc.bar_time, style: label.style_label_up, size: pinescript.size.tiny })); - let rightTimeBar = (last_bar_time + (20 * (time - pinescript.hist(42, time, 1)))); - topLine.set_first_point(pinescript.chartPointNew(state.trailing.lastTopTime, null, state.trailing.top)); - topLine.set_second_point(pinescript.chartPointNew(rightTimeBar, null, state.trailing.top)); - topLabel.set_point(pinescript.chartPointNew(rightTimeBar, null, state.trailing.top)); - topLabel.set_text(((state.swingTrend.bias === BEARISH) ? "Strong High" : "Weak High")); - bottomLine.set_first_point(pinescript.chartPointNew(state.trailing.lastBottomTime, null, state.trailing.bottom)); - bottomLine.set_second_point(pinescript.chartPointNew(rightTimeBar, null, state.trailing.bottom)); - bottomLabel.set_point(pinescript.chartPointNew(rightTimeBar, null, state.trailing.bottom)); - return bottomLabel.set_text(((state.swingTrend.bias === BULLISH) ? "Strong Low" : "Weak Low")); + var rightTimeBar = (last_bar_time + (20 * (time - pinescript.hist(42, time, 1)))); + state.topLine.set_first_point(pinescript.chartPointNew(state.trailing.lastTopTime, null, state.trailing.top)); + state.topLine.set_second_point(pinescript.chartPointNew(rightTimeBar, null, state.trailing.top)); + state.topLabel.set_point(pinescript.chartPointNew(rightTimeBar, null, state.trailing.top)); + state.topLabel.set_text(((state.swingTrend.bias === BEARISH) ? "Strong High" : "Weak High")); + state.bottomLine.set_first_point(pinescript.chartPointNew(state.trailing.lastBottomTime, null, state.trailing.bottom)); + state.bottomLine.set_second_point(pinescript.chartPointNew(rightTimeBar, null, state.trailing.bottom)); + state.bottomLabel.set_point(pinescript.chartPointNew(rightTimeBar, null, state.trailing.bottom)); + return state.bottomLabel.set_text(((state.swingTrend.bias === BULLISH) ? "Strong Low" : "Weak Low")); } function drawZone(labelLevel, labelIndex, top, bottom, tag, zoneColor, style) { if (state.l_abel === undefined) state.l_abel = pinescript.labelNew(null, null, ({ text: tag, color: color(null), textcolor: zoneColor, style: style, size: pinescript.size.small })); if (state.b_ox === undefined) state.b_ox = pinescript.boxNew(null, null, null, null, ({ bgcolor: pinescript.color.new(zoneColor, 80), border_color: color(null), xloc: xloc.bar_time })); - b_ox.set_top_left_point(pinescript.chartPointNew(state.trailing.barTime, null, top)); - b_ox.set_bottom_right_point(pinescript.chartPointNew(last_bar_time, null, bottom)); - return l_abel.set_point(pinescript.chartPointNew(null, labelIndex, labelLevel)); + state.b_ox.set_top_left_point(pinescript.chartPointNew(state.trailing.barTime, null, top)); + state.b_ox.set_bottom_right_point(pinescript.chartPointNew(last_bar_time, null, bottom)); + return state.l_abel.set_point(pinescript.chartPointNew(null, labelIndex, labelLevel)); } function drawPremiumDiscountZones() { drawZone(state.trailing.top, pinescript.round((0.5 * (state.trailing.barIndex + last_bar_index))), state.trailing.top, ((0.95 * state.trailing.top) + (0.05 * state.trailing.bottom)), "Premium", state.premiumZoneColor, label.style_label_down); - let equilibriumLevel = pinescript.avg(state.trailing.top, state.trailing.bottom); + var equilibriumLevel = pinescript.avg(state.trailing.top, state.trailing.bottom); drawZone(equilibriumLevel, last_bar_index, ((0.525 * state.trailing.top) + (0.475 * state.trailing.bottom)), ((0.525 * state.trailing.bottom) + (0.475 * state.trailing.top)), "Equilibrium", equilibriumZoneColorInput, label.style_label_left); return drawZone(state.trailing.bottom, pinescript.round((0.5 * (state.trailing.barIndex + last_bar_index))), ((0.95 * state.trailing.bottom) + (0.05 * state.trailing.top)), state.trailing.bottom, "Discount", state.discountZoneColor, label.style_label_up); } - let parsedOpen = (showTrendInput ? open : null); - let candleColor = ((state.internalTrend.bias === BULLISH) ? state.swingBullishColor : state.swingBearishColor); + var parsedOpen = (showTrendInput ? open : null); + var candleColor = ((state.internalTrend.bias === BULLISH) ? state.swingBullishColor : state.swingBearishColor); pinescript.plotcandle(parsedOpen, high, low, close, ({ color: candleColor, wickcolor: candleColor, bordercolor: candleColor })); if ((showHighLowSwingsInput || showPremiumDiscountZonesInput)) { updateTrailingExtremes(); @@ -2235,7 +2410,7 @@ function main() { } state.lastBarIndex = state.currentBarIndex; state.currentBarIndex = bar_index; - let newBar = (state.currentBarIndex !== state.lastBarIndex); + var newBar = (state.currentBarIndex !== state.lastBarIndex); if ((barstate.islastconfirmedhistory || (barstate.isrealtime && newBar))) { if ((showDailyLevelsInput && !higherTimeframe("D"))) { drawLevels("D", timeframe.isdaily, dailyLevelsStyleInput, dailyLevelsColorInput); @@ -2307,6 +2482,10 @@ function run(data, options = {}) { rt.__shapeIdx = 0; globalThis.bar_index = i; globalThis.last_bar_index = n - 1; + globalThis.time_tradingday = inTime[i]; + globalThis.time_close = inTime[i]; + globalThis.last_bar_time = inTime[n - 1]; + globalThis.timenow = inTime[n - 1]; globalThis.barstate = { isfirst: i === 0, islast: i === n - 1, isrealtime: false, ishistory: true, isconfirmed: true, isnew: true, islastconfirmedhistory: i === n - 1, diff --git a/converted/Scalping_Strategy_3_minutes.js b/converted/Scalping_Strategy_3_minutes.js index 37aeac5..2d70a38 100644 --- a/converted/Scalping_Strategy_3_minutes.js +++ b/converted/Scalping_Strategy_3_minutes.js @@ -42,6 +42,80 @@ const pinescript = { if (value != null && typeof value[Symbol.iterator] === 'function') return Array.from(value); return new Array(count || 0).fill(null); }, + __decArr: function(arr) { + if (!Array.isArray(arr) || arr.__pineDecorated) return arr; + const self = this; + const def = (name, fn) => Object.defineProperty(arr, name, { value: fn, writable: true, configurable: true }); + Object.defineProperty(arr, '__pineDecorated', { value: true, configurable: true }); + def('get', (i) => (arr[i] === undefined ? null : arr[i])); + def('set', (i, v) => { arr[i] = v; return v; }); + def('size', () => arr.length); + def('clear', () => { arr.length = 0; }); + def('insert', (i, v) => { arr.splice(i, 0, v); }); + def('remove', (i) => arr.splice(i, 1)[0]); + def('contains', (v) => arr.includes(v)); + def('indexof', (v) => arr.indexOf(v)); + def('lastindexof', (v) => arr.lastIndexOf(v)); + def('first', () => (arr.length ? arr[0] : null)); + def('last', () => (arr.length ? arr[arr.length - 1] : null)); + def('sum', () => arr.reduce((a, b) => a + b, 0)); + def('avg', () => (arr.length ? arr.reduce((a, b) => a + b, 0) / arr.length : 0)); + def('min', () => (arr.length ? Math.min(...arr) : null)); + def('max', () => (arr.length ? Math.max(...arr) : null)); + def('range', () => (arr.length ? Math.max(...arr) - Math.min(...arr) : null)); + // Statistical methods that route to the array.* built-ins (these names are not + // native to JS arrays, so attaching them here is safe from recursion). + def('median', () => self.arrayMedian(arr)); + def('mode', () => self.arrayMode(arr)); + def('stdev', () => self.arrayStdev(arr)); + def('variance', () => self.arrayVariance(arr)); + def('covariance', (other) => self.arrayCovariance(arr, other)); + def('percentile_linear_interpolation', (p) => self.arrayPercentileLinearInterpolation(arr, p)); + def('percentile_nearest_rank', (p) => self.arrayPercentileNearestRank(arr, p)); + def('abs', () => self.__decArr(self.arrayAbs(arr))); + def('binary_search', (v) => self.arrayBinarySearch(arr, v)); + // Pine's array.sort takes an order string, not a comparator. Use the native + // sort via .call so we don't recurse through this overridden method. + def('sort', (order) => { Array.prototype.sort.call(arr, (a, b) => (order === 'descending' ? b - a : a - b)); return arr; }); + def('sort_indices', (order) => self.__decArr(arr.map((_, i) => i).sort((a, b) => (order === 'descending' ? arr[b] - arr[a] : arr[a] - arr[b])))); + // join/slice/reverse/concat/includes/fill already exist natively on Array with + // compatible semantics, so we deliberately leave them to the native methods. + return arr; + }, + __decMap: function(m) { + if (!(m instanceof Map) || m.__pineDecorated) return m; + const def = (name, fn) => Object.defineProperty(m, name, { value: fn, writable: true, configurable: true }); + Object.defineProperty(m, '__pineDecorated', { value: true, configurable: true }); + def('put', (k, v) => { m.set(k, v); return v; }); + def('contains', (k) => m.has(k)); + def('remove', (k) => m.delete(k)); + def('keys', () => Array.from(Map.prototype.keys.call(m))); + def('values', () => Array.from(Map.prototype.values.call(m))); + def('size_', () => m.size); + return m; + }, + __decMatrix: function(m) { + if (!m || typeof m !== 'object' || m.__pineDecorated) return m; + const self = this; + const def = (name, fn) => Object.defineProperty(m, name, { value: fn, writable: true, configurable: true }); + Object.defineProperty(m, '__pineDecorated', { value: true, configurable: true }); + def('get', (r, c) => self.matrixGet(m, r, c)); + def('set', (r, c, v) => self.matrixSet(m, r, c, v)); + def('rows_', () => m.rows); + def('columns', () => m.cols); + def('fill', (v) => self.matrixFill(m, v)); + return m; + }, + __decDraw: function(obj) { + if (!obj || typeof obj !== 'object') return obj; + const p = new Proxy(obj, { + get(t, k) { + if (k in t || typeof k === 'symbol') return t[k]; + return function() { return p; }; + }, + }); + return p; + }, alertcondition: function(condition, ...rest) { if (globalThis.__pineRuntime) { globalThis.__pineRuntime.alerts.push({ condition, args: rest }); @@ -49,6 +123,32 @@ const pinescript = { return null; }, barcolor: function(color) { return null; }, + plotchar: function(series, ...rest) { + const rt = globalThis.__pineRuntime; + if (!rt) return series; + const bar = rt.__barIndex | 0; + const ord = (rt.__shapeIdx = (rt.__shapeIdx | 0) + 1) - 1; + let key = 'char_' + ord; + for (const r of rest) { + if (typeof r === 'string') { key = r; break; } + if (r && typeof r === 'object' && r.title) { key = String(r.title); break; } + } + let s = rt.plotshapes[key]; + if (!s) s = rt.plotshapes[key] = { title: key, data: [] }; + s.data[bar] = this.__scalar(series); + return series; + }, + plotarrow: function(series, ...rest) { + const rt = globalThis.__pineRuntime; + if (!rt) return series; + const bar = rt.__barIndex | 0; + const ord = (rt.__plotIdx = (rt.__plotIdx | 0) + 1) - 1; + const key = 'arrow_' + ord; + let p = rt.plots[key]; + if (!p) p = rt.plots[key] = { title: key, data: [] }; + p.data[bar] = this.__scalar(series); + return series; + }, bgcolor: function(color, title, editable, showLast) { return null; }, @@ -470,6 +570,12 @@ const pinescript = { atan: function(value) { return Math.atan(value); }, + todegrees: function(radians) { + return radians * (180 / Math.PI); + }, + toradians: function(degrees) { + return degrees * (Math.PI / 180); + }, floor: function(value) { return Math.floor(value); }, @@ -641,7 +747,7 @@ const pinescript = { return series; }, lineNew: function(x1, y1, x2, y2, opts = {}) { - return { x1, y1, x2, y2, opts, _type: 'line' }; + return this.__decDraw({ x1, y1, x2, y2, opts, _type: 'line' }); }, lineDelete: function(l) { return null; @@ -661,7 +767,7 @@ const pinescript = { return point === 0 || point === 'y1' ? line.y1 : line.y2; }, labelNew: function(x, y, text = '', opts = {}) { - return { x, y, text, opts, _type: 'label' }; + return this.__decDraw({ x, y, text, opts, _type: 'label' }); }, labelDelete: function(l) { return null; @@ -676,7 +782,7 @@ const pinescript = { return label.text || ''; }, boxNew: function(left, top, right, bottom, opts = {}) { - return { left, top, right, bottom, opts, _type: 'box' }; + return this.__decDraw({ left, top, right, bottom, opts, _type: 'box' }); }, boxDelete: function(box) { return null; @@ -694,7 +800,7 @@ const pinescript = { return box; }, polylineNew: function(points, opts = {}) { - return { points: points || [], opts, _type: 'polyline' }; + return this.__decDraw({ points: points || [], opts, _type: 'polyline' }); }, polylineDelete: function(poly) { return null; @@ -765,7 +871,7 @@ const pinescript = { return { time: _time ?? null, price: _price ?? null }; }, mapNew: function() { - return new Map(); + return this.__decMap(new Map()); }, mapSize: function(m) { return m instanceof Map ? m.size : 0; @@ -800,7 +906,7 @@ const pinescript = { const r = Math.max(0, rows ?? 0); const c = Math.max(0, cols ?? 0); const data = Array.from({ length: r }, () => Array.from({ length: c }, () => initialValue)); - return { rows: r, cols: c, data }; + return this.__decMatrix({ rows: r, cols: c, data }); }, matrixRows: function(m) { return m?.rows ?? 0; @@ -923,11 +1029,62 @@ const pinescript = { } return null; }, + __jacobiEigen: function(m) { + if (!m || !Array.isArray(m.data)) return null; + const n = m.rows ?? 0; + if (n === 0 || n !== (m.cols ?? 0)) return null; + // Work on a copy so the input matrix is untouched. + const a = m.data.map(row => row.slice()); + const v = Array.from({ length: n }, (_, i) => Array.from({ length: n }, (_, j) => (i === j ? 1 : 0))); + for (let sweep = 0; sweep < 100; sweep++) { + let off = 0; + for (let p = 0; p < n; p++) for (let q = p + 1; q < n; q++) off += a[p][q] * a[p][q]; + if (off < 1e-20) break; + for (let p = 0; p < n; p++) { + for (let q = p + 1; q < n; q++) { + if (Math.abs(a[p][q]) < 1e-18) continue; + const theta = (a[q][q] - a[p][p]) / (2 * a[p][q]); + const t = Math.sign(theta || 1) / (Math.abs(theta) + Math.sqrt(theta * theta + 1)); + const cos = 1 / Math.sqrt(t * t + 1); + const sin = t * cos; + for (let k = 0; k < n; k++) { + const akp = a[k][p], akq = a[k][q]; + a[k][p] = cos * akp - sin * akq; + a[k][q] = sin * akp + cos * akq; + } + for (let k = 0; k < n; k++) { + const apk = a[p][k], aqk = a[q][k]; + a[p][k] = cos * apk - sin * aqk; + a[q][k] = sin * apk + cos * aqk; + } + for (let k = 0; k < n; k++) { + const vkp = v[k][p], vkq = v[k][q]; + v[k][p] = cos * vkp - sin * vkq; + v[k][q] = sin * vkp + cos * vkq; + } + } + } + } + // Sort eigenpairs by eigenvalue, descending. + const order = Array.from({ length: n }, (_, i) => i).sort((i, j) => a[j][j] - a[i][i]); + const values = order.map(i => a[i][i]); + const vectors = Array.from({ length: n }, (_, r) => order.map(c => v[r][c])); + return { values, vectors }; + }, + matrixEigenvalues: function(m) { + const e = this.__jacobiEigen(m); + return this.__decArr(e ? e.values : []); + }, + matrixEigenvectors: function(m) { + const e = this.__jacobiEigen(m); + if (!e) return this.__decMatrix({ rows: 0, cols: 0, data: [] }); + return this.__decMatrix({ rows: e.vectors.length, cols: e.vectors.length, data: e.vectors }); + }, requestSecurity: function(symbol, timeframe, expression) { return expression; }, arrayNew: function(initialSize = 0, initialValue = 0) { - return Array(initialSize).fill(initialValue); + return this.__decArr(Array(initialSize).fill(initialValue)); }, arraySize: function(arr) { return arr ? arr.length : 0; @@ -978,10 +1135,10 @@ const pinescript = { return arr; }, arraySlice: function(arr, startIndex = 0, endIndex = null) { - if (!arr) return []; + if (!arr) return this.__decArr([]); const start = Number(startIndex) || 0; const end = endIndex === null || endIndex === undefined ? arr.length : Number(endIndex) || 0; - return arr.slice(start, end); + return this.__decArr(arr.slice(start, end)); }, arraySort: function(arr, order = 'ascending') { if (arr) arr.sort((a, b) => order === 'ascending' ? a - b : b - a); @@ -1191,7 +1348,7 @@ const pinescript = { const info = { ticker: 'AAPL', tickerid: 'NASDAQ:AAPL', prefix: 'NASDAQ', root: 'AAPL', suffix: '' }; return info[type] || ''; }, - timenow: 1781574528384, + timenow: 1782783577498, barstate: "LAST", dividends: {}, splits: {}, @@ -1245,11 +1402,11 @@ const pinescript = { arrayConcat: function(arr1, arr2) { if (!arr1) return arr2 || []; if (!arr2) return arr1; - return arr1.concat(arr2); + return this.__decArr(arr1.concat(arr2)); }, arrayCopy: function(arr) { - if (!arr) return []; - return [...arr]; + if (!arr) return this.__decArr([]); + return this.__decArr([...arr]); }, arrayBinarySearch: function(arr, value) { if (!arr || arr.length === 0) return -1; @@ -1339,7 +1496,7 @@ globalThis.input.timeframe = globalThis.input.timeframe || ((defval) => defval); globalThis.array = globalThis.array || { - from: (...items) => items, + from: (...items) => pinescript.__decArr(items), size: (arr) => pinescript.arraySize(arr), @@ -1508,6 +1665,8 @@ globalThis.line = globalThis.line || __pineNS({ style_solid: "solid", style_dash globalThis.box = globalThis.box || __pineNS({}); +globalThis.color = globalThis.color || __pineNS(Object.assign(function(c) { return c; }, { new: function(c, t) { return c; }, rgb: function(r, g, b, t) { return "#rgb(" + [r, g, b].join(",") + ")"; }, from_gradient: function(v, lo, hi, c1, c2) { return c1; }, r: function() { return 0; }, g: function() { return 0; }, b: function() { return 0; }, t: function() { return 0; }, aqua: "#00BCD4", black: "#363A45", blue: "#2962FF", fuchsia: "#E040FB", gray: "#787B86", green: "#4CAF50", lime: "#00E676", maroon: "#880E4F", navy: "#311B92", olive: "#808000", orange: "#FF9800", purple: "#9C27B0", red: "#FF5252", silver: "#B2B5BE", teal: "#00897B", white: "#FFFFFF", yellow: "#FFEB3B" })); + globalThis.label = globalThis.label || __pineNS({ style_label_down: "label_down", style_label_up: "label_up", style_none: "none" }); globalThis.polyline = globalThis.polyline || __pineNS({}); @@ -1520,7 +1679,9 @@ globalThis.map = globalThis.map || __pineNS({}); globalThis.session = globalThis.session || __pineNS({ regular: "regular", extended: "extended" }); -globalThis.dayofweek = globalThis.dayofweek || __pineNS({ sunday: 1, monday: 2, tuesday: 3, wednesday: 4, thursday: 5, friday: 6, saturday: 7 }); +globalThis.ticker = globalThis.ticker || __pineNS({}); + +globalThis.dayofweek = globalThis.dayofweek || __pineNS(Object.assign(function(t) { return new Date(t != null ? t : (globalThis.time || 0)).getUTCDay() + 1; }, { sunday: 1, monday: 2, tuesday: 3, wednesday: 4, thursday: 5, friday: 6, saturday: 7 })); globalThis.timeframe = __pineNS(Object.assign(globalThis.timeframe || {}, { period: (globalThis.timeframe && globalThis.timeframe.period) || "D", isintraday: false, isdaily: true, multiplier: 1 })); @@ -1568,8 +1729,6 @@ globalThis.bool = globalThis.bool || function(x) { return Boolean(x); }; globalThis.string = globalThis.string || function(x) { return x == null ? null : String(x); }; -globalThis.dayofweek = globalThis.dayofweek || __pineNS({ sunday: 1, monday: 2, tuesday: 3, wednesday: 4, thursday: 5, friday: 6, saturday: 7 }); - globalThis.str = globalThis.str || __pineNS({}); pinescript.math = globalThis.math; @@ -1655,7 +1814,7 @@ pinescript.text = { align_center: "center" }; pinescript.table = { - new: function(position, columns, rows, opts) { return { position, columns, rows, opts: opts || {}, cells: [] }; }, + new: function(position, columns, rows, opts) { return pinescript.__decDraw({ position, columns, rows, opts: opts || {}, cells: [] }); }, cell: function(table, column, row, text, opts) { @@ -1690,32 +1849,32 @@ function main() { "v6_1_23"; // Strategy: XXX // Options: {"shorttitle":"XXX ","overlay":true,"explicit_plot_zorder":true,"pyramiding":0,"default_qty_type":null,"default_qty_value":10,"calc_on_every_tick":false,"process_orders_on_close":true} - let G_SCRIPT01 = ("■ " + "SAIYAN OCC"); - let res = input.timeframe("15", "TIMEFRAME", ({ group: "NON REPAINT" })); - let useRes = pinescript.input(true, "Use Alternate Signals"); - let intRes = pinescript.input(8, "Multiplier for Alernate Signals"); - let basisType = pinescript.inputString("ALMA", "MA Type: ", ({ options: ["TEMA", "HullMA", "ALMA"] })); - let basisLen = pinescript.inputInt(2, "MA Period", ({ minval: 1 })); - let offsetSigma = pinescript.inputInt(5, "Offset for LSMA / Sigma for ALMA", ({ minval: 0 })); - let offsetALMA = pinescript.inputFloat(0.85, "Offset for ALMA", ({ minval: 0, step: 0.01 })); - let scolor = pinescript.input(false, "Show coloured Bars to indicate Trend?"); - let delayOffset = pinescript.inputInt(0, "Delay Open/Close MA", ({ minval: 0, step: 1, tooltip: "Forces Non-Repainting" })); - let tradeType = pinescript.inputString("BOTH", "What trades should be taken : ", ({ options: ["LONG", "SHORT", "BOTH", "NONE"] })); - let h = pinescript.input(false, "Signals for Heikin Ashi Candles"); - let swing_length = pinescript.inputInt(10, "Swing High/Low Length", ({ group: "Settings", minval: 1, maxval: 50 })); - let history_of_demand_to_keep = pinescript.inputInt(20, "History To Keep", ({ minval: 5, maxval: 50 })); - let box_width = pinescript.inputFloat(2.5, "Supply/Demand Box Width", ({ group: "Settings", minval: 1, maxval: 10, step: 0.5 })); - let show_zigzag = pinescript.inputBool(false, "Show Zig Zag", ({ group: "Visual Settings", inline: "1" })); - let show_price_action_labels = pinescript.inputBool(false, "Show Price Action Labels", ({ group: "Visual Settings", inline: "2" })); - let supply_color = pinescript.inputColor(pinescript.color.hex("#00000000"), "Supply", ({ group: "Visual Settings", inline: "3" })); - let supply_outline_color = pinescript.inputColor(pinescript.color.hex("#00000000"), "Outline", ({ group: "Visual Settings", inline: "3" })); - let demand_color = pinescript.inputColor(pinescript.color.hex("#00000000"), "Demand", ({ group: "Visual Settings", inline: "4" })); - let demand_outline_color = pinescript.inputColor(pinescript.color.hex("#00000000"), "Outline", ({ group: "Visual Settings", inline: "4" })); - let bos_label_color = pinescript.inputColor(pinescript.color.hex("#00000000"), "BOS Label", ({ group: "Visual Settings", inline: "5" })); - let poi_label_color = pinescript.inputColor(pinescript.color.hex("#00000000"), "POI Label", ({ group: "Visual Settings", inline: "7" })); - let poi_border_color = pinescript.inputColor(pinescript.color.hex("#00000000"), "POI border", ({ group: "Visual Settings", inline: "7" })); - let swing_type_color = pinescript.inputColor(pinescript.color.hex("#00000000"), "Price Action Label", ({ group: "Visual Settings", inline: "8" })); - let zigzag_color = pinescript.inputColor(pinescript.color.hex("#00000000"), "Zig Zag", ({ group: "Visual Settings", inline: "9" })); + var G_SCRIPT01 = ("■ " + "SAIYAN OCC"); + var res = input.timeframe("15", "TIMEFRAME", ({ group: "NON REPAINT" })); + var useRes = pinescript.input(true, "Use Alternate Signals"); + var intRes = pinescript.input(8, "Multiplier for Alernate Signals"); + var basisType = pinescript.inputString("ALMA", "MA Type: ", ({ options: ["TEMA", "HullMA", "ALMA"] })); + var basisLen = pinescript.inputInt(2, "MA Period", ({ minval: 1 })); + var offsetSigma = pinescript.inputInt(5, "Offset for LSMA / Sigma for ALMA", ({ minval: 0 })); + var offsetALMA = pinescript.inputFloat(0.85, "Offset for ALMA", ({ minval: 0, step: 0.01 })); + var scolor = pinescript.input(false, "Show coloured Bars to indicate Trend?"); + var delayOffset = pinescript.inputInt(0, "Delay Open/Close MA", ({ minval: 0, step: 1, tooltip: "Forces Non-Repainting" })); + var tradeType = pinescript.inputString("BOTH", "What trades should be taken : ", ({ options: ["LONG", "SHORT", "BOTH", "NONE"] })); + var h = pinescript.input(false, "Signals for Heikin Ashi Candles"); + var swing_length = pinescript.inputInt(10, "Swing High/Low Length", ({ group: "Settings", minval: 1, maxval: 50 })); + var history_of_demand_to_keep = pinescript.inputInt(20, "History To Keep", ({ minval: 5, maxval: 50 })); + var box_width = pinescript.inputFloat(2.5, "Supply/Demand Box Width", ({ group: "Settings", minval: 1, maxval: 10, step: 0.5 })); + var show_zigzag = pinescript.inputBool(false, "Show Zig Zag", ({ group: "Visual Settings", inline: "1" })); + var show_price_action_labels = pinescript.inputBool(false, "Show Price Action Labels", ({ group: "Visual Settings", inline: "2" })); + var supply_color = pinescript.inputColor(pinescript.color.hex("#00000000"), "Supply", ({ group: "Visual Settings", inline: "3" })); + var supply_outline_color = pinescript.inputColor(pinescript.color.hex("#00000000"), "Outline", ({ group: "Visual Settings", inline: "3" })); + var demand_color = pinescript.inputColor(pinescript.color.hex("#00000000"), "Demand", ({ group: "Visual Settings", inline: "4" })); + var demand_outline_color = pinescript.inputColor(pinescript.color.hex("#00000000"), "Outline", ({ group: "Visual Settings", inline: "4" })); + var bos_label_color = pinescript.inputColor(pinescript.color.hex("#00000000"), "BOS Label", ({ group: "Visual Settings", inline: "5" })); + var poi_label_color = pinescript.inputColor(pinescript.color.hex("#00000000"), "POI Label", ({ group: "Visual Settings", inline: "7" })); + var poi_border_color = pinescript.inputColor(pinescript.color.hex("#00000000"), "POI border", ({ group: "Visual Settings", inline: "7" })); + var swing_type_color = pinescript.inputColor(pinescript.color.hex("#00000000"), "Price Action Label", ({ group: "Visual Settings", inline: "8" })); + var zigzag_color = pinescript.inputColor(pinescript.color.hex("#00000000"), "Zig Zag", ({ group: "Visual Settings", inline: "9" })); function f_array_add_pop(array, new_value_to_add) { pinescript.arrayUnshift(pinescript.array, new_value_to_add); return pinescript.arrayPop(pinescript.array); @@ -1728,7 +1887,7 @@ function main() { } else { state.label_text = "LH"; } - pinescript.labelNew((bar_index - swing_length), pinescript.arrayGet(pinescript.array, 0), ({ text: state.label_text, style: label.style_label_down, textcolor: swing_type_color, color: swing_type_color, size: pinescript.size.tiny })); + return pinescript.labelNew((bar_index - swing_length), pinescript.arrayGet(pinescript.array, 0), ({ text: state.label_text, style: label.style_label_down, textcolor: swing_type_color, color: swing_type_color, size: pinescript.size.tiny })); } else { if ((swing_type === -1)) { if ((pinescript.arrayGet(pinescript.array, 0) >= pinescript.arrayGet(pinescript.array, 1))) { @@ -1736,19 +1895,21 @@ function main() { } else { state.label_text = "LL"; } - pinescript.labelNew((bar_index - swing_length), pinescript.arrayGet(pinescript.array, 0), ({ text: state.label_text, style: label.style_label_up, textcolor: swing_type_color, color: swing_type_color, size: pinescript.size.tiny })); + return pinescript.labelNew((bar_index - swing_length), pinescript.arrayGet(pinescript.array, 0), ({ text: state.label_text, style: label.style_label_up, textcolor: swing_type_color, color: swing_type_color, size: pinescript.size.tiny })); + } else { + return null; } } } function f_check_overlapping(new_poi, box_array, atrValue) { - let atr_threshold = (atrValue * 2); - let okay_to_draw = true; + var atr_threshold = (atrValue * 2); + var okay_to_draw = true; for (let i = 0; i <= (pinescript.arraySize(box_array) - 1); i++) { - let top = box.get_top(pinescript.arrayGet(box_array, i)); - let bottom = box.get_bottom(pinescript.arrayGet(box_array, i)); - let poi = ((top + bottom) / 2); - let upper_boundary = (poi + atr_threshold); - let lower_boundary = (poi - atr_threshold); + var top = box.get_top(pinescript.arrayGet(box_array, i)); + var bottom = box.get_bottom(pinescript.arrayGet(box_array, i)); + var poi = ((top + bottom) / 2); + var upper_boundary = (poi + atr_threshold); + var lower_boundary = (poi - atr_threshold); if (((new_poi >= lower_boundary) && (new_poi <= upper_boundary))) { okay_to_draw = false; break; @@ -1759,9 +1920,9 @@ function main() { return okay_to_draw; } function f_supply_demand(value_array, bn_array, box_array, label_array, box_type, atrValue) { - let atr_buffer = (atrValue * (box_width / 10)); - let box_left = pinescript.arrayGet(bn_array, 0); - let box_right = bar_index; + var atr_buffer = (atrValue * (box_width / 10)); + var box_left = pinescript.arrayGet(bn_array, 0); + var box_right = bar_index; if (state.box_top === undefined) state.box_top = 0; if (state.box_bottom === undefined) state.box_bottom = 0; if (state.poi === undefined) state.poi = 0; @@ -1776,29 +1937,31 @@ function main() { state.poi = ((state.box_top + state.box_bottom) / 2); } } - okay_to_draw = f_check_overlapping(state.poi, box_array, atrValue); + var okay_to_draw = f_check_overlapping(state.poi, box_array, atrValue); if (((box_type === 1) && okay_to_draw)) { pinescript.boxDelete(pinescript.arrayGet(box_array, (pinescript.arraySize(box_array) - 1))); f_array_add_pop(box_array, pinescript.boxNew(({ left: box_left, top: state.box_top, right: box_right, bottom: state.box_bottom, border_color: supply_outline_color, bgcolor: supply_color, extend: extend.right, text: "SUPPLY", text_halign: pinescript.text.align_center, text_valign: pinescript.text.align_center, text_color: poi_label_color, text_size: pinescript.size.small, xloc: xloc.bar_index }))); pinescript.boxDelete(pinescript.arrayGet(label_array, (pinescript.arraySize(label_array) - 1))); - f_array_add_pop(label_array, pinescript.boxNew(({ left: box_left, top: state.poi, right: box_right, bottom: state.poi, border_color: poi_border_color, bgcolor: poi_border_color, extend: extend.right, text: "POI", text_halign: pinescript.text.align_left, text_valign: pinescript.text.align_center, text_color: poi_label_color, text_size: pinescript.size.small, xloc: xloc.bar_index }))); + return f_array_add_pop(label_array, pinescript.boxNew(({ left: box_left, top: state.poi, right: box_right, bottom: state.poi, border_color: poi_border_color, bgcolor: poi_border_color, extend: extend.right, text: "POI", text_halign: pinescript.text.align_left, text_valign: pinescript.text.align_center, text_color: poi_label_color, text_size: pinescript.size.small, xloc: xloc.bar_index }))); } else { if (((box_type === -1) && okay_to_draw)) { pinescript.boxDelete(pinescript.arrayGet(box_array, (pinescript.arraySize(box_array) - 1))); f_array_add_pop(box_array, pinescript.boxNew(({ left: box_left, top: state.box_top, right: box_right, bottom: state.box_bottom, border_color: demand_outline_color, bgcolor: demand_color, extend: extend.right, text: "DEMAND", text_halign: pinescript.text.align_center, text_valign: pinescript.text.align_center, text_color: poi_label_color, text_size: pinescript.size.small, xloc: xloc.bar_index }))); pinescript.boxDelete(pinescript.arrayGet(label_array, (pinescript.arraySize(label_array) - 1))); - f_array_add_pop(label_array, pinescript.boxNew(({ left: box_left, top: state.poi, right: box_right, bottom: state.poi, border_color: poi_border_color, bgcolor: poi_border_color, extend: extend.right, text: "POI", text_halign: pinescript.text.align_left, text_valign: pinescript.text.align_center, text_color: poi_label_color, text_size: pinescript.size.small, xloc: xloc.bar_index }))); + return f_array_add_pop(label_array, pinescript.boxNew(({ left: box_left, top: state.poi, right: box_right, bottom: state.poi, border_color: poi_border_color, bgcolor: poi_border_color, extend: extend.right, text: "POI", text_halign: pinescript.text.align_left, text_valign: pinescript.text.align_center, text_color: poi_label_color, text_size: pinescript.size.small, xloc: xloc.bar_index }))); + } else { + return null; } } } function f_sd_to_bos(box_array, bos_array, label_array, zone_type) { if ((zone_type === 1)) { for (let i = 0; i <= (pinescript.arraySize(box_array) - 1); i++) { - let level_to_break = box.get_top(pinescript.arrayGet(box_array, i)); + var level_to_break = box.get_top(pinescript.arrayGet(box_array, i)); if ((close >= level_to_break)) { - let copied_box = box.copy(pinescript.arrayGet(box_array, i)); + var copied_box = box.copy(pinescript.arrayGet(box_array, i)); f_array_add_pop(bos_array, copied_box); - let mid = ((box.get_top(pinescript.arrayGet(box_array, i)) + box.get_bottom(pinescript.arrayGet(box_array, i))) / 2); + var mid = ((box.get_top(pinescript.arrayGet(box_array, i)) + box.get_bottom(pinescript.arrayGet(box_array, i))) / 2); box.set_top(pinescript.arrayGet(bos_array, 0), mid); box.set_bottom(pinescript.arrayGet(bos_array, 0), mid); box.set_extend(pinescript.arrayGet(bos_array, 0), extend.none); @@ -1833,6 +1996,8 @@ function main() { pinescript.boxDelete(pinescript.arrayGet(label_array, i)); } } + } else { + return null; } } function f_extend_box_endpoint(box_array) { @@ -1840,11 +2005,11 @@ function main() { box.set_right(pinescript.arrayGet(box_array, i), (bar_index + 100)); } } - let stratRes = (timeframe.ismonthly ? pinescript.strToString((timeframe.multiplier * intRes), "###M") : (timeframe.isweekly ? pinescript.strToString((timeframe.multiplier * intRes), "###W") : (timeframe.isdaily ? pinescript.strToString((timeframe.multiplier * intRes), "###D") : (timeframe.isintraday ? pinescript.strToString((timeframe.multiplier * intRes), "####") : "60")))); - let src = (h ? pinescript.requestSecurity(ticker.heikinashi(syminfo.tickerid), timeframe.period, close, ({ lookahead: barmerge.lookahead_off })) : close); - let atrValue = pinescript.atr(50); - let swing_high = ta.pivothigh(high, swing_length, swing_length); - let swing_low = ta.pivotlow(low, swing_length, swing_length); + var stratRes = (timeframe.ismonthly ? pinescript.strToString((timeframe.multiplier * intRes), "###M") : (timeframe.isweekly ? pinescript.strToString((timeframe.multiplier * intRes), "###W") : (timeframe.isdaily ? pinescript.strToString((timeframe.multiplier * intRes), "###D") : (timeframe.isintraday ? pinescript.strToString((timeframe.multiplier * intRes), "####") : "60")))); + var src = (h ? pinescript.requestSecurity(ticker.heikinashi(syminfo.tickerid), timeframe.period, close, ({ lookahead: barmerge.lookahead_off })) : close); + var atrValue = pinescript.atr(50); + var swing_high = pinescript.ta.pivothigh(high, swing_length, swing_length); + var swing_low = pinescript.ta.pivotlow(low, swing_length, swing_length); if (state.swing_high_values === undefined) state.swing_high_values = pinescript.arrayNew(5, 0); if (state.swing_low_values === undefined) state.swing_low_values = pinescript.arrayNew(5, 0); if (state.swing_high_bns === undefined) state.swing_high_bns = pinescript.arrayNew(5, 0); @@ -1876,31 +2041,31 @@ function main() { f_sd_to_bos(state.current_demand_box, state.demand_bos, state.current_demand_poi, -1); f_extend_box_endpoint(state.current_supply_box); f_extend_box_endpoint(state.current_demand_box); - let channelBal = pinescript.inputBool(false, "Channel Balance", ({ group: "CHART" })); + var channelBal = pinescript.inputBool(false, "Channel Balance", ({ group: "CHART" })); function lr_slope(_src, _len) { - let x = 0; - let y = 0; - let x2 = 0; - let xy = 0; + var x = 0; + var y = 0; + var x2 = 0; + var xy = 0; for (let i = 0; i <= (_len - 1); i++) { - let val = pinescript.hist(2, _src, i); - let per = (i + 1); + var val = pinescript.hist(2, _src, i); + var per = (i + 1); x += per; y += val; x2 += (per * per); xy += (val * per); } - let _slp = (((_len * xy) - (x * y)) / ((_len * x2) - (x * x))); - let _avg = (y / _len); - let _int = ((_avg - ((_slp * x) / _len)) + _slp); + var _slp = (((_len * xy) - (x * y)) / ((_len * x2) - (x * x))); + var _avg = (y / _len); + var _int = ((_avg - ((_slp * x) / _len)) + _slp); return [_slp, _avg, _int]; } function lr_dev(_src, _len, _slp, _avg, _int) { - let upDev = 0; - let dnDev = 0; - val = _int; + var upDev = 0; + var dnDev = 0; + var val = _int; for (let j = 0; j <= (_len - 1); j++) { - let price = (pinescript.hist(3, high, j) - val); + var price = (pinescript.hist(3, high, j) - val); if ((price > upDev)) { upDev = price; } @@ -1913,40 +2078,40 @@ function main() { } return [upDev, dnDev]; } - let [, upperKC1, lowerKC1] = pinescript.unpack(pinescript.kc(close, 80, 10.5), 3); - let [, upperKC2, lowerKC2] = pinescript.unpack(pinescript.kc(close, 80, 9.5), 3); - let [, upperKC3, lowerKC3] = pinescript.unpack(pinescript.kc(close, 80, 8), 3); - let [, upperKC4, lowerKC4] = pinescript.unpack(pinescript.kc(close, 80, 3), 3); - let barsL = 10; - let barsR = 10; - let pivotHigh = pinescript.fixnan(pinescript.hist(6, ta.pivothigh(barsL, barsR), 1)); - let pivotLow = pinescript.fixnan(pinescript.hist(7, ta.pivotlow(barsL, barsR), 1)); - let source = close; - let period = 150; - let [s, a, i] = pinescript.unpack(lr_slope(source, period), 3); - [upDev, dnDev] = pinescript.unpack(lr_dev(source, period, s, a, i), 2); - let y1 = (low - (pinescript.atr(30) * 2)); - let y1B = (low - pinescript.atr(30)); - let y2 = (high + (pinescript.atr(30) * 2)); - let y2B = (high + pinescript.atr(30)); - let x1 = ((bar_index - period) + 1); - let _y1 = (i + (s * (period - 1))); - x2 = bar_index; - let _y2 = i; + var [, upperKC1, lowerKC1] = pinescript.unpack(pinescript.kc(close, 80, 10.5), 3); + var [, upperKC2, lowerKC2] = pinescript.unpack(pinescript.kc(close, 80, 9.5), 3); + var [, upperKC3, lowerKC3] = pinescript.unpack(pinescript.kc(close, 80, 8), 3); + var [, upperKC4, lowerKC4] = pinescript.unpack(pinescript.kc(close, 80, 3), 3); + var barsL = 10; + var barsR = 10; + var pivotHigh = pinescript.fixnan(pinescript.hist(6, pinescript.ta.pivothigh(barsL, barsR), 1)); + var pivotLow = pinescript.fixnan(pinescript.hist(7, pinescript.ta.pivotlow(barsL, barsR), 1)); + var source = close; + var period = 150; + var [s, a, i] = pinescript.unpack(lr_slope(source, period), 3); + var [upDev, dnDev] = pinescript.unpack(lr_dev(source, period, s, a, i), 2); + var y1 = (low - (pinescript.atr(30) * 2)); + var y1B = (low - pinescript.atr(30)); + var y2 = (high + (pinescript.atr(30) * 2)); + var y2B = (high + pinescript.atr(30)); + var x1 = ((bar_index - period) + 1); + var _y1 = (i + (s * (period - 1))); + var x2 = bar_index; + var _y2 = i; function get_line_style(style) { line.style_dotted; line.style_dashed; line.style_solid; - let out = ((style === "???") ? undefined : ((style === "----") ? undefined : ((style === " ") ? undefined : null))); + var out = ((style === "???") ? undefined : ((style === "----") ? undefined : ((style === " ") ? undefined : null))); } function get_coordinates(condition, top, btm, ob_val) { if (state.ob_top === undefined) state.ob_top = pinescript.arrayNew(0); if (state.ob_btm === undefined) state.ob_btm = pinescript.arrayNew(0); if (state.ob_avg === undefined) state.ob_avg = pinescript.arrayNew(0); if (state.ob_left === undefined) state.ob_left = pinescript.arrayNew(0); - let ob = null; + var ob = null; if (condition) { - let avg = pinescript.avg(top, btm); + var avg = pinescript.avg(top, btm); pinescript.arrayUnshift(state.ob_top, top); pinescript.arrayUnshift(state.ob_btm, btm); pinescript.arrayUnshift(state.ob_avg, avg); @@ -1955,10 +2120,10 @@ function main() { return [state.ob_top, state.ob_btm, state.ob_avg, state.ob_left, ob]; } function remove_mitigated(ob_top, ob_btm, ob_left, ob_avg, target, bull) { - let mitigated = false; - let target_array = (bull ? state.ob_btm : state.ob_top); - for (const element of target_array) { - let idx = pinescript.arrayIndexOf(target_array, element); + var mitigated = false; + var target_array = (bull ? state.ob_btm : state.ob_top); + for (const element of (target_array ?? [])) { + var idx = pinescript.arrayIndexOf(target_array, element); if ((bull ? (target < element) : (target > element))) { mitigated = true; pinescript.arrayRemove(state.ob_top, idx); @@ -1979,112 +2144,112 @@ function main() { function rp_security(_symbol, _res, _src) { return pinescript.requestSecurity(_symbol, _res, pinescript.hist(8, _src, (barstate.isrealtime ? 1 : 0))); } - let htfHigh = rp_security(syminfo.tickerid, res, high); - let htfLow = rp_security(syminfo.tickerid, res, low); + var htfHigh = rp_security(syminfo.tickerid, res, high); + var htfLow = rp_security(syminfo.tickerid, res, low); function smoothrng(x, t, m) { - let wper = ((t * 2) - 1); - let avrng = pinescript.ema(pinescript.abs((x - pinescript.hist(9, x, 1))), t); - let smoothrng = (pinescript.ema(avrng, wper) * m); + var wper = ((t * 2) - 1); + var avrng = pinescript.ema(pinescript.series(10, pinescript.abs((x - pinescript.hist(9, x, 1)))), t); + var smoothrng = (pinescript.ema(pinescript.series(11, avrng), wper) * m); } function rngfilt(x, r) { - let rngfilt = x; - rngfilt = ((x > pinescript.nz(pinescript.hist(10, rngfilt, 1))) ? (((x - r) < pinescript.nz(pinescript.hist(11, rngfilt, 1))) ? pinescript.nz(pinescript.hist(12, rngfilt, 1)) : (x - r)) : (((x + r) > pinescript.nz(pinescript.hist(13, rngfilt, 1))) ? pinescript.nz(pinescript.hist(14, rngfilt, 1)) : (x + r))); + var rngfilt = x; + rngfilt = ((x > pinescript.nz(pinescript.hist(12, rngfilt, 1))) ? (((x - r) < pinescript.nz(pinescript.hist(13, rngfilt, 1))) ? pinescript.nz(pinescript.hist(14, rngfilt, 1)) : (x - r)) : (((x + r) > pinescript.nz(pinescript.hist(15, rngfilt, 1))) ? pinescript.nz(pinescript.hist(16, rngfilt, 1)) : (x + r))); } function percWidth(len, perc) { - return (((pinescript.highest(pinescript.series(15, len)) - pinescript.lowest(pinescript.series(16, len))) * perc) / 100); + return (((pinescript.highest(pinescript.series(17, len)) - pinescript.lowest(pinescript.series(18, len))) * perc) / 100); } function securityNoRep(sym, res, src) { return pinescript.requestSecurity(sym, res, src, barmerge.gaps_off, barmerge.lookahead_on); } function swingPoints(prd) { - let pivHi = ta.pivothigh(prd, prd); - let pivLo = ta.pivotlow(prd, prd); - let last_pivHi = pinescript.valuewhen(pinescript.series(17, pivHi), pinescript.series(18, pivHi), 1); - let last_pivLo = pinescript.valuewhen(pinescript.series(19, pivLo), pinescript.series(20, pivLo), 1); - let hh = ((pivHi && (pivHi > last_pivHi)) ? pivHi : null); - let lh = ((pivHi && (pivHi < last_pivHi)) ? pivHi : null); - let hl = ((pivLo && (pivLo > last_pivLo)) ? pivLo : null); - let ll = ((pivLo && (pivLo < last_pivLo)) ? pivLo : null); + var pivHi = pinescript.ta.pivothigh(prd, prd); + var pivLo = pinescript.ta.pivotlow(prd, prd); + var last_pivHi = pinescript.valuewhen(pinescript.series(19, pivHi), pinescript.series(20, pivHi), 1); + var last_pivLo = pinescript.valuewhen(pinescript.series(21, pivLo), pinescript.series(22, pivLo), 1); + var hh = ((pivHi && (pivHi > last_pivHi)) ? pivHi : null); + var lh = ((pivHi && (pivHi < last_pivHi)) ? pivHi : null); + var hl = ((pivLo && (pivLo > last_pivLo)) ? pivLo : null); + var ll = ((pivLo && (pivLo < last_pivLo)) ? pivLo : null); return [hh, lh, hl, ll]; } function f_chartTfInMinutes() { - let _resInMinutes = (timeframe.multiplier * (timeframe.isseconds ? 1 : (timeframe.isminutes ? 1 : (timeframe.isdaily ? (60 * 24) : (timeframe.isweekly ? ((60 * 24) * 7) : (timeframe.ismonthly ? ((60 * 24) * 30.4375) : null)))))); + var _resInMinutes = (timeframe.multiplier * (timeframe.isseconds ? 1 : (timeframe.isminutes ? 1 : (timeframe.isdaily ? (60 * 24) : (timeframe.isweekly ? ((60 * 24) * 7) : (timeframe.ismonthly ? ((60 * 24) * 30.4375) : null)))))); } function f_kc(src, len, sensitivity) { - let basis = pinescript.sma(src, len); - let span = pinescript.atr(len); + var basis = pinescript.sma(pinescript.series(23, src), len); + var span = pinescript.atr(len); return [(basis + (span * sensitivity)), (basis - (span * sensitivity))]; } function wavetrend(src, chlLen, avgLen) { - let esa = pinescript.ema(src, chlLen); - let d = pinescript.ema(pinescript.abs((src - esa)), chlLen); - let ci = ((src - esa) / (0.015 * d)); - let wt1 = pinescript.ema(ci, avgLen); - let wt2 = pinescript.sma(wt1, 3); + var esa = pinescript.ema(pinescript.series(24, src), chlLen); + var d = pinescript.ema(pinescript.series(25, pinescript.abs((src - esa))), chlLen); + var ci = ((src - esa) / (0.015 * d)); + var wt1 = pinescript.ema(pinescript.series(26, ci), avgLen); + var wt2 = pinescript.sma(pinescript.series(27, wt1), 3); return [wt1, wt2]; } function f_top_fractal(_src) { - return ((((pinescript.hist(21, _src, 4) < pinescript.hist(22, _src, 2)) && (pinescript.hist(23, _src, 3) < pinescript.hist(24, _src, 2))) && (pinescript.hist(25, _src, 2) > pinescript.hist(26, _src, 1))) && (pinescript.hist(27, _src, 2) > pinescript.hist(28, _src, 0))); + return ((((pinescript.hist(28, _src, 4) < pinescript.hist(29, _src, 2)) && (pinescript.hist(30, _src, 3) < pinescript.hist(31, _src, 2))) && (pinescript.hist(32, _src, 2) > pinescript.hist(33, _src, 1))) && (pinescript.hist(34, _src, 2) > pinescript.hist(35, _src, 0))); } function f_bot_fractal(_src) { - return ((((pinescript.hist(29, _src, 4) > pinescript.hist(30, _src, 2)) && (pinescript.hist(31, _src, 3) > pinescript.hist(32, _src, 2))) && (pinescript.hist(33, _src, 2) < pinescript.hist(34, _src, 1))) && (pinescript.hist(35, _src, 2) < pinescript.hist(36, _src, 0))); + return ((((pinescript.hist(36, _src, 4) > pinescript.hist(37, _src, 2)) && (pinescript.hist(38, _src, 3) > pinescript.hist(39, _src, 2))) && (pinescript.hist(40, _src, 2) < pinescript.hist(41, _src, 1))) && (pinescript.hist(42, _src, 2) < pinescript.hist(43, _src, 0))); } - let top_fractal = f_top_fractal(src); - let bot_fractal = f_bot_fractal(src); + var top_fractal = f_top_fractal(src); + var bot_fractal = f_bot_fractal(src); function f_fractalize(_src) { return (top_fractal ? 1 : (bot_fractal ? -1 : 0)); } function f_findDivs(src, topLimit, botLimit) { - let fractalTop = (((f_fractalize(src) > 0) && (pinescript.hist(37, src, 2) >= topLimit)) ? pinescript.hist(38, src, 2) : null); - let fractalBot = (((f_fractalize(src) < 0) && (pinescript.hist(39, src, 2) <= botLimit)) ? pinescript.hist(40, src, 2) : null); - let highPrev = pinescript.hist(41, pinescript.valuewhen(pinescript.series(43, fractalTop), pinescript.series(44, pinescript.hist(42, src, 2)), 0), 2); - let highPrice = pinescript.hist(45, pinescript.valuewhen(pinescript.series(47, fractalTop), pinescript.series(48, pinescript.hist(46, high, 2)), 0), 2); - let lowPrev = pinescript.hist(49, pinescript.valuewhen(pinescript.series(51, fractalBot), pinescript.series(52, pinescript.hist(50, src, 2)), 0), 2); - let lowPrice = pinescript.hist(53, pinescript.valuewhen(pinescript.series(55, fractalBot), pinescript.series(56, pinescript.hist(54, low, 2)), 0), 2); - let bearSignal = ((fractalTop && (pinescript.hist(57, high, 1) > highPrice)) && (pinescript.hist(58, src, 1) < highPrev)); - let bullSignal = ((fractalBot && (pinescript.hist(59, low, 1) < lowPrice)) && (pinescript.hist(60, src, 1) > lowPrev)); + var fractalTop = (((f_fractalize(src) > 0) && (pinescript.hist(44, src, 2) >= topLimit)) ? pinescript.hist(45, src, 2) : null); + var fractalBot = (((f_fractalize(src) < 0) && (pinescript.hist(46, src, 2) <= botLimit)) ? pinescript.hist(47, src, 2) : null); + var highPrev = pinescript.hist(48, pinescript.valuewhen(pinescript.series(50, fractalTop), pinescript.series(51, pinescript.hist(49, src, 2)), 0), 2); + var highPrice = pinescript.hist(52, pinescript.valuewhen(pinescript.series(54, fractalTop), pinescript.series(55, pinescript.hist(53, high, 2)), 0), 2); + var lowPrev = pinescript.hist(56, pinescript.valuewhen(pinescript.series(58, fractalBot), pinescript.series(59, pinescript.hist(57, src, 2)), 0), 2); + var lowPrice = pinescript.hist(60, pinescript.valuewhen(pinescript.series(62, fractalBot), pinescript.series(63, pinescript.hist(61, low, 2)), 0), 2); + var bearSignal = ((fractalTop && (pinescript.hist(64, high, 1) > highPrice)) && (pinescript.hist(65, src, 1) < highPrev)); + var bullSignal = ((fractalBot && (pinescript.hist(66, low, 1) < lowPrice)) && (pinescript.hist(67, src, 1) > lowPrev)); return [bearSignal, bullSignal]; } - let enableSR = pinescript.input(false, "SR On/Off", ({ group: "SR" })); - let colorSup = pinescript.input(pinescript.color.hex("#00000000"), "Support Color", ({ group: "SR" })); - let colorRes = pinescript.input(pinescript.color.hex("#00000000"), "Resistance Color", ({ group: "SR" })); - let strengthSR = pinescript.inputInt(2, "S/R Strength", 1, ({ group: "SR" })); - let lineStyle = pinescript.inputString("Dotted", "Line Style", ["Solid", "Dotted", "Dashed"], ({ group: "SR" })); - let lineWidth = pinescript.inputInt(2, "S/R Line Width", 1, ({ group: "SR" })); - let useZones = pinescript.input(true, "Zones On/Off", ({ group: "SR" })); - let useHLZones = pinescript.input(true, "High Low Zones On/Off", ({ group: "SR" })); - let zoneWidth = pinescript.inputInt(2, "Zone Width %", 0, ({ tooltip: "it's calculated using % of the distance between highest/lowest in last 300 bars", group: "SR" })); - let expandSR = pinescript.input(true, "Expand SR"); - let rb = 10; - let prd = 284; - let ChannelW = 10; - let label_loc = 55; - let style = ((lineStyle === "Solid") ? line.style_solid : ((lineStyle === "Dotted") ? line.style_dotted : line.style_dashed)); - let ph = ta.pivothigh(rb, rb); - let pl = ta.pivotlow(rb, rb); - let sr_levels = pinescript.arrayNew(21, null); - let prdhighest = pinescript.highest(pinescript.series(61, prd)); - let prdlowest = pinescript.lowest(pinescript.series(62, prd)); - let cwidth = percWidth(prd, ChannelW); - let zonePerc = percWidth(300, zoneWidth); - let aas = pinescript.arrayNew(41, true); - let u1 = 0; - u1 = pinescript.nz(pinescript.hist(63, u1, 1)); - let d1 = 0; - d1 = pinescript.nz(pinescript.hist(64, d1, 1)); - let highestph = 0; - highestph = pinescript.hist(65, highestph, 1); - let lowestpl = 0; - lowestpl = pinescript.hist(66, lowestpl, 1); + var enableSR = pinescript.input(false, "SR On/Off", ({ group: "SR" })); + var colorSup = pinescript.input(pinescript.color.hex("#00000000"), "Support Color", ({ group: "SR" })); + var colorRes = pinescript.input(pinescript.color.hex("#00000000"), "Resistance Color", ({ group: "SR" })); + var strengthSR = pinescript.inputInt(2, "S/R Strength", 1, ({ group: "SR" })); + var lineStyle = pinescript.inputString("Dotted", "Line Style", ["Solid", "Dotted", "Dashed"], ({ group: "SR" })); + var lineWidth = pinescript.inputInt(2, "S/R Line Width", 1, ({ group: "SR" })); + var useZones = pinescript.input(true, "Zones On/Off", ({ group: "SR" })); + var useHLZones = pinescript.input(true, "High Low Zones On/Off", ({ group: "SR" })); + var zoneWidth = pinescript.inputInt(2, "Zone Width %", 0, ({ tooltip: "it's calculated using % of the distance between highest/lowest in last 300 bars", group: "SR" })); + var expandSR = pinescript.input(true, "Expand SR"); + var rb = 10; + var prd = 284; + var ChannelW = 10; + var label_loc = 55; + var style = ((lineStyle === "Solid") ? line.style_solid : ((lineStyle === "Dotted") ? line.style_dotted : line.style_dashed)); + var ph = pinescript.ta.pivothigh(rb, rb); + var pl = pinescript.ta.pivotlow(rb, rb); + var sr_levels = pinescript.arrayNew(21, null); + var prdhighest = pinescript.highest(pinescript.series(68, prd)); + var prdlowest = pinescript.lowest(pinescript.series(69, prd)); + var cwidth = percWidth(prd, ChannelW); + var zonePerc = percWidth(300, zoneWidth); + var aas = pinescript.arrayNew(41, true); + var u1 = 0; + u1 = pinescript.nz(pinescript.hist(70, u1, 1)); + var d1 = 0; + d1 = pinescript.nz(pinescript.hist(71, d1, 1)); + var highestph = 0; + highestph = pinescript.hist(72, highestph, 1); + var lowestpl = 0; + lowestpl = pinescript.hist(73, lowestpl, 1); if (state.sr_levs === undefined) state.sr_levs = pinescript.arrayNew(21, null); - let hlabel = null; - pinescript.labelDelete(pinescript.hist(67, hlabel, 1)); - let llabel = null; - pinescript.labelDelete(pinescript.hist(68, llabel, 1)); + var hlabel = null; + pinescript.labelDelete(pinescript.hist(74, hlabel, 1)); + var llabel = null; + pinescript.labelDelete(pinescript.hist(75, llabel, 1)); if (state.sr_lines === undefined) state.sr_lines = pinescript.arrayNew(21, null); if (state.sr_linesH === undefined) state.sr_linesH = pinescript.arrayNew(21, null); if (state.sr_linesL === undefined) state.sr_linesL = pinescript.arrayNew(21, null); - if (state.sr_linesF === undefined) state.sr_linesF = array.new_linefill(21, null); + if (state.sr_linesF === undefined) state.sr_linesF = pinescript.arrayNew(21, null); if (state.sr_labels === undefined) state.sr_labels = pinescript.arrayNew(21, null); if ((ph || pl)) { for (let x = 0; x <= (pinescript.arraySize(sr_levels) - 1); x++) { @@ -2092,45 +2257,45 @@ function main() { } highestph = prdlowest; lowestpl = prdhighest; - let countpp = 0; + var countpp = 0; for (let x = 0; x <= prd; x++) { - if (pinescript.na(pinescript.hist(69, close, x))) { + if (pinescript.na(pinescript.hist(76, close, x))) { break; } - if ((!pinescript.na(pinescript.hist(70, ph, x)) || !pinescript.na(pinescript.hist(71, pl, x)))) { - highestph = pinescript.max(highestph, pinescript.nz(pinescript.hist(72, ph, x), prdlowest), pinescript.nz(pinescript.hist(73, pl, x), prdlowest)); - lowestpl = pinescript.min(lowestpl, pinescript.nz(pinescript.hist(74, ph, x), prdhighest), pinescript.nz(pinescript.hist(75, pl, x), prdhighest)); + if ((!pinescript.na(pinescript.hist(77, ph, x)) || !pinescript.na(pinescript.hist(78, pl, x)))) { + highestph = pinescript.max(highestph, pinescript.nz(pinescript.hist(79, ph, x), prdlowest), pinescript.nz(pinescript.hist(80, pl, x), prdlowest)); + lowestpl = pinescript.min(lowestpl, pinescript.nz(pinescript.hist(81, ph, x), prdhighest), pinescript.nz(pinescript.hist(82, pl, x), prdhighest)); countpp += 1; if ((countpp > 40)) { break; } if (pinescript.arrayGet(aas, countpp)) { - let upl = ((pinescript.hist(76, ph, x) ? pinescript.hist(77, high, (x + rb)) : pinescript.hist(78, low, (x + rb))) + cwidth); - let dnl = ((pinescript.hist(79, ph, x) ? pinescript.hist(80, high, (x + rb)) : pinescript.hist(81, low, (x + rb))) - cwidth); + var upl = ((pinescript.hist(83, ph, x) ? pinescript.hist(84, high, (x + rb)) : pinescript.hist(85, low, (x + rb))) + cwidth); + var dnl = ((pinescript.hist(86, ph, x) ? pinescript.hist(87, high, (x + rb)) : pinescript.hist(88, low, (x + rb))) - cwidth); u1 = ((countpp === 1) ? upl : u1); d1 = ((countpp === 1) ? dnl : d1); - let tmp = pinescript.arrayNew(41, true); - let cnt = 0; - let tpoint = 0; + var tmp = pinescript.arrayNew(41, true); + var cnt = 0; + var tpoint = 0; for (let xx = 0; xx <= prd; xx++) { - if (pinescript.na(pinescript.hist(82, close, xx))) { + if (pinescript.na(pinescript.hist(89, close, xx))) { break; } - if ((!pinescript.na(pinescript.hist(83, ph, xx)) || !pinescript.na(pinescript.hist(84, pl, xx)))) { - let chg = false; + if ((!pinescript.na(pinescript.hist(90, ph, xx)) || !pinescript.na(pinescript.hist(91, pl, xx)))) { + var chg = false; cnt += 1; if ((cnt > 40)) { break; } if (pinescript.arrayGet(aas, cnt)) { - if (!pinescript.na(pinescript.hist(85, ph, xx))) { - if (((pinescript.hist(86, high, (xx + rb)) <= upl) && (pinescript.hist(87, high, (xx + rb)) >= dnl))) { + if (!pinescript.na(pinescript.hist(92, ph, xx))) { + if (((pinescript.hist(93, high, (xx + rb)) <= upl) && (pinescript.hist(94, high, (xx + rb)) >= dnl))) { tpoint += 1; chg = true; } } - if (!pinescript.na(pinescript.hist(88, pl, xx))) { - if (((pinescript.hist(89, low, (xx + rb)) <= upl) && (pinescript.hist(90, low, (xx + rb)) >= dnl))) { + if (!pinescript.na(pinescript.hist(95, pl, xx))) { + if (((pinescript.hist(96, low, (xx + rb)) <= upl) && (pinescript.hist(97, low, (xx + rb)) >= dnl))) { tpoint += 1; chg = true; } @@ -2147,11 +2312,11 @@ function main() { pinescript.arraySet(aas, g, false); } } - if ((pinescript.hist(91, ph, x) && (countpp < 21))) { - pinescript.arraySet(sr_levels, countpp, pinescript.hist(92, high, (x + rb))); + if ((pinescript.hist(98, ph, x) && (countpp < 21))) { + pinescript.arraySet(sr_levels, countpp, pinescript.hist(99, high, (x + rb))); } - if ((pinescript.hist(93, pl, x) && (countpp < 21))) { - pinescript.arraySet(sr_levels, countpp, pinescript.hist(94, low, (x + rb))); + if ((pinescript.hist(100, pl, x) && (countpp < 21))) { + pinescript.arraySet(sr_levels, countpp, pinescript.hist(101, low, (x + rb))); } } } @@ -2170,8 +2335,8 @@ function main() { pinescript.lineDelete(state.lowest_fill1); if (state.lowest_fill2 === undefined) state.lowest_fill2 = null; pinescript.lineDelete(state.lowest_fill2); - let hi_col = ((close >= highestph) ? colorSup : colorRes); - let lo_col = ((close >= lowestpl) ? colorSup : colorRes); + var hi_col = ((close >= highestph) ? colorSup : colorRes); + var lo_col = ((close >= lowestpl) ? colorSup : colorRes); if (enableSR) { state.highest_ = pinescript.lineNew((bar_index - 311), highestph, bar_index, highestph, xloc.bar_index, (expandSR ? extend.both : extend.right), hi_col, style, lineWidth); state.lowest_ = pinescript.lineNew((bar_index - 311), lowestpl, bar_index, lowestpl, xloc.bar_index, (expandSR ? extend.both : extend.right), lo_col, style, lineWidth); @@ -2195,7 +2360,7 @@ function main() { pinescript.lineDelete(pinescript.arrayGet(state.sr_linesL, x)); linefill.delete(pinescript.arrayGet(state.sr_linesF, x)); if ((pinescript.arrayGet(state.sr_levs, x) && enableSR)) { - let line_col = ((close >= pinescript.arrayGet(state.sr_levs, x)) ? colorSup : colorRes); + var line_col = ((close >= pinescript.arrayGet(state.sr_levs, x)) ? colorSup : colorRes); pinescript.arraySet(state.sr_lines, x, pinescript.lineNew((bar_index - 355), pinescript.arrayGet(state.sr_levs, x), bar_index, pinescript.arrayGet(state.sr_levs, x), xloc.bar_index, (expandSR ? extend.both : extend.right), line_col, style, lineWidth)); if (useZones) { pinescript.arraySet(state.sr_linesH, x, pinescript.lineNew((bar_index - 355), (pinescript.arrayGet(state.sr_levs, x) + zonePerc), bar_index, (pinescript.arrayGet(state.sr_levs, x) + zonePerc), xloc.bar_index, (expandSR ? extend.both : extend.right), null)); @@ -2207,21 +2372,21 @@ function main() { for (let x = 0; x <= (pinescript.arraySize(state.sr_labels) - 1); x++) { pinescript.labelDelete(pinescript.arrayGet(state.sr_labels, x)); if ((pinescript.arrayGet(state.sr_levs, x) && enableSR)) { - let lab_loc = ((close >= pinescript.arrayGet(state.sr_levs, x)) ? label.style_label_up : label.style_label_down); - let lab_col = ((close >= pinescript.arrayGet(state.sr_levs, x)) ? colorSup : colorRes); - pinescript.arraySet(state.sr_labels, x, pinescript.labelNew((bar_index + label_loc), pinescript.arrayGet(state.sr_levs, x), pinescript.strToString(math.round_to_mintick(pinescript.arrayGet(state.sr_levs, x))), ({ color: lab_col, textcolor: pinescript.color.hex("#000000"), style: lab_loc }))); + var lab_loc = ((close >= pinescript.arrayGet(state.sr_levs, x)) ? label.style_label_up : label.style_label_down); + var lab_col = ((close >= pinescript.arrayGet(state.sr_levs, x)) ? colorSup : colorRes); + pinescript.arraySet(state.sr_labels, x, pinescript.labelNew((bar_index + label_loc), pinescript.arrayGet(state.sr_levs, x), pinescript.strToString(pinescript.math.round_to_mintick(pinescript.arrayGet(state.sr_levs, x))), ({ color: lab_col, textcolor: pinescript.color.hex("#000000"), style: lab_loc }))); } } hlabel = (enableSR ? pinescript.labelNew(((bar_index + label_loc) + (pinescript.round(pinescript.sign(label_loc)) * 20)), highestph, ("High Level : " + pinescript.strToString(highestph)), ({ color: hi_col, textcolor: pinescript.color.hex("#000000"), style: label.style_label_down })) : null); llabel = (enableSR ? pinescript.labelNew(((bar_index + label_loc) + (pinescript.round(pinescript.sign(label_loc)) * 20)), lowestpl, ("Low Level : " + pinescript.strToString(lowestpl)), ({ color: lo_col, textcolor: pinescript.color.hex("#000000"), style: label.style_label_up })) : null); - let rsi = pinescript.rsi(close, 28); - let rsiOb = ((rsi > 65) && (rsi > pinescript.ema(rsi, 10))); - let rsiOs = ((rsi < 35) && (rsi < pinescript.ema(rsi, 10))); - let dHigh = securityNoRep(syminfo.tickerid, "D", pinescript.hist(95, high, 1)); - let dLow = securityNoRep(syminfo.tickerid, "D", pinescript.hist(96, low, 1)); - let dClose = securityNoRep(syminfo.tickerid, "D", pinescript.hist(97, close, 1)); - let ema = pinescript.ema(close, 144); - let emaBull = (close > ema); + var rsi = pinescript.rsi(pinescript.series(102, close), 28); + var rsiOb = ((rsi > 65) && (rsi > pinescript.ema(pinescript.series(103, rsi), 10))); + var rsiOs = ((rsi < 35) && (rsi < pinescript.ema(pinescript.series(104, rsi), 10))); + var dHigh = securityNoRep(syminfo.tickerid, "D", pinescript.hist(105, high, 1)); + var dLow = securityNoRep(syminfo.tickerid, "D", pinescript.hist(106, low, 1)); + var dClose = securityNoRep(syminfo.tickerid, "D", pinescript.hist(107, close, 1)); + var ema = pinescript.ema(pinescript.series(108, close), 144); + var emaBull = (close > ema); function equal_tf(res) { return ((pinescript.strToNumber(res) === f_chartTfInMinutes()) && !timeframe.isseconds); } @@ -2232,10 +2397,10 @@ function main() { return ((timeframe.isweekly && (res === "1")) || (timeframe.ismonthly && (pinescript.strToNumber(res) < 10))); } function securityNoRep1(sym, res, src) { - let bull_ = null; + var bull_ = null; bull_ = (equal_tf(res) ? src : bull_); bull_ = (higher_tf(res) ? pinescript.requestSecurity(sym, res, src, barmerge.gaps_off, barmerge.lookahead_on) : bull_); - let bull_array = request.security_lower_tf(syminfo.tickerid, (higher_tf(res) ? (pinescript.strToString(f_chartTfInMinutes()) + (timeframe.isseconds ? "S" : "")) : (too_small_tf(res) ? (timeframe.isweekly ? "3" : "10") : res)), src); + var bull_array = request.security_lower_tf(syminfo.tickerid, (higher_tf(res) ? (pinescript.strToString(f_chartTfInMinutes()) + (timeframe.isseconds ? "S" : "")) : (too_small_tf(res) ? (timeframe.isweekly ? "3" : "10") : res)), src); if ((((pinescript.arraySize(bull_array) > 1) && !equal_tf(res)) && !higher_tf(res))) { bull_ = pinescript.arrayPop(bull_array); } @@ -2243,163 +2408,163 @@ function main() { return bull_; } function variant(type, src, len, offSig, offALMA) { - let v1 = pinescript.sma(src, len); - let v2 = pinescript.ema(src, len); - let v3 = ((2 * v2) - pinescript.ema(v2, len)); - let v4 = ((3 * (v2 - pinescript.ema(v2, len))) + pinescript.ema(pinescript.ema(v2, len), len)); - let v5 = pinescript.wma(src, len); - let v6 = pinescript.vwma(src, len); - let v7 = 0; - let sma_1 = pinescript.sma(src, len); - v7 = (pinescript.na(pinescript.hist(98, v7, 1)) ? sma_1 : (((pinescript.hist(99, v7, 1) * (len - 1)) + src) / len)); - let v8 = pinescript.wma(((2 * pinescript.wma(src, (len / 2))) - pinescript.wma(src, len)), pinescript.round(pinescript.sqrt(len))); - let v9 = pinescript.linreg(src, len, offSig); - let v10 = pinescript.alma(src, len, offALMA, offSig); - let v11 = pinescript.sma(v1, len); - let a1 = pinescript.exp(((-1.414 * 3.14159) / len)); - let b1 = ((2 * a1) * pinescript.cos(((1.414 * 3.14159) / len))); - let c2 = b1; - let c3 = (-a1 * a1); - let c1 = ((1 - c2) - c3); - let v12 = 0; - v12 = ((((c1 * (src + pinescript.nz(pinescript.hist(100, src, 1)))) / 2) + (c2 * pinescript.nz(pinescript.hist(101, v12, 1)))) + (c3 * pinescript.nz(pinescript.hist(102, v12, 2)))); + var v1 = pinescript.sma(pinescript.series(109, src), len); + var v2 = pinescript.ema(pinescript.series(110, src), len); + var v3 = ((2 * v2) - pinescript.ema(pinescript.series(111, v2), len)); + var v4 = ((3 * (v2 - pinescript.ema(pinescript.series(112, v2), len))) + pinescript.ema(pinescript.series(114, pinescript.ema(pinescript.series(113, v2), len)), len)); + var v5 = pinescript.wma(pinescript.series(115, src), len); + var v6 = pinescript.vwma(src, len); + var v7 = 0; + var sma_1 = pinescript.sma(pinescript.series(116, src), len); + v7 = (pinescript.na(pinescript.hist(117, v7, 1)) ? sma_1 : (((pinescript.hist(118, v7, 1) * (len - 1)) + src) / len)); + var v8 = pinescript.wma(pinescript.series(121, ((2 * pinescript.wma(pinescript.series(119, src), (len / 2))) - pinescript.wma(pinescript.series(120, src), len))), pinescript.round(pinescript.sqrt(len))); + var v9 = pinescript.linreg(pinescript.series(122, src), len, offSig); + var v10 = pinescript.alma(pinescript.series(123, src), len, offALMA, offSig); + var v11 = pinescript.sma(pinescript.series(124, v1), len); + var a1 = pinescript.exp(((-1.414 * 3.14159) / len)); + var b1 = ((2 * a1) * pinescript.cos(((1.414 * 3.14159) / len))); + var c2 = b1; + var c3 = (-a1 * a1); + var c1 = ((1 - c2) - c3); + var v12 = 0; + v12 = ((((c1 * (src + pinescript.nz(pinescript.hist(125, src, 1)))) / 2) + (c2 * pinescript.nz(pinescript.hist(126, v12, 1)))) + (c3 * pinescript.nz(pinescript.hist(127, v12, 2)))); return ((type === "EMA") ? v2 : ((type === "DEMA") ? v3 : ((type === "TEMA") ? v4 : ((type === "WMA") ? v5 : ((type === "VWMA") ? v6 : ((type === "SMMA") ? v7 : ((type === "HullMA") ? v8 : ((type === "LSMA") ? v9 : ((type === "ALMA") ? v10 : ((type === "TMA") ? v11 : ((type === "SSMA") ? v12 : v1))))))))))); } function reso(exp, use, res) { - let security_1 = pinescript.requestSecurity(syminfo.tickerid, res, exp, ({ gaps: barmerge.gaps_off, lookahead: barmerge.lookahead_on })); + var security_1 = pinescript.requestSecurity(syminfo.tickerid, res, exp, ({ gaps: barmerge.gaps_off, lookahead: barmerge.lookahead_on })); return (use ? security_1 : exp); } - let closeSeries = variant(basisType, pinescript.hist(103, close, delayOffset), basisLen, offsetSigma, offsetALMA); - let openSeries = variant(basisType, pinescript.hist(104, open, delayOffset), basisLen, offsetSigma, offsetALMA); - let closeSeriesAlt = reso(closeSeries, useRes, stratRes); - let openSeriesAlt = reso(openSeries, useRes, stratRes); - let lxTrigger = false; - let sxTrigger = false; - let leTrigger = pinescript.crossover(pinescript.series(105, closeSeriesAlt), pinescript.series(106, openSeriesAlt)); - let seTrigger = pinescript.crossunder(pinescript.series(107, closeSeriesAlt), pinescript.series(108, openSeriesAlt)); - let G_RISK = ("■ " + "Risk Management"); - let T_LVL = "(%) Exit Level"; - let T_QTY = "(%) Adjust trade exit volume"; - let T_MSG = "Paste JSON message for your bot"; - let O_LEMSG = "Long Entry"; - let O_LXMSGSL = "Long SL"; - let O_LXMSGTP1 = "Long TP1"; - let O_LXMSGTP2 = "Long TP2"; - let O_LXMSGTP3 = "Long TP3"; - let O_LXMSG = "Long Exit"; - let O_SEMSG = "Short Entry"; - let O_SXMSGSL = "Short SL"; - let O_SXMSGA = "Short TP1"; - let O_SXMSGB = "Short TP2"; - let O_SXMSGC = "Short TP3"; - let O_SXMSGX = "Short Exit"; - let i_lxLvlTP1 = pinescript.inputFloat(1, "Level TP1", ({ group: G_RISK, tooltip: T_LVL })); - let i_lxQtyTP1 = pinescript.inputFloat(50, "Qty TP1", ({ group: G_RISK, tooltip: T_QTY })); - let i_lxLvlTP2 = pinescript.inputFloat(1.5, "Level TP2", ({ group: G_RISK, tooltip: T_LVL })); - let i_lxQtyTP2 = pinescript.inputFloat(30, "Qty TP2", ({ group: G_RISK, tooltip: T_QTY })); - let i_lxLvlTP3 = pinescript.inputFloat(2, "Level TP3", ({ group: G_RISK, tooltip: T_LVL })); - let i_lxQtyTP3 = pinescript.inputFloat(20, "Qty TP3", ({ group: G_RISK, tooltip: T_QTY })); - let i_lxLvlSL = pinescript.inputFloat(0.5, "Stop Loss", ({ group: G_RISK, tooltip: T_LVL })); - let i_sxLvlTP1 = i_lxLvlTP1; - let i_sxQtyTP1 = i_lxQtyTP1; - let i_sxLvlTP2 = i_lxLvlTP2; - let i_sxQtyTP2 = i_lxQtyTP2; - let i_sxLvlTP3 = i_lxLvlTP3; - let i_sxQtyTP3 = i_lxQtyTP3; - let i_sxLvlSL = i_lxLvlSL; - let G_MSG = ("■ " + "Webhook Message"); - let i_leMsg = pinescript.inputString(O_LEMSG, "Long Entry", ({ group: G_MSG, tooltip: T_MSG })); - let i_lxMsgSL = pinescript.inputString(O_LXMSGSL, "Long SL", ({ group: G_MSG, tooltip: T_MSG })); - let i_lxMsgTP1 = pinescript.inputString(O_LXMSGTP1, "Long TP1", ({ group: G_MSG, tooltip: T_MSG })); - let i_lxMsgTP2 = pinescript.inputString(O_LXMSGTP2, "Long TP2", ({ group: G_MSG, tooltip: T_MSG })); - let i_lxMsgTP3 = pinescript.inputString(O_LXMSGTP3, "Long TP3", ({ group: G_MSG, tooltip: T_MSG })); - let i_lxMsg = pinescript.inputString(O_LXMSG, "Long Exit", ({ group: G_MSG, tooltip: T_MSG })); - let i_seMsg = pinescript.inputString(O_SEMSG, "Short Entry", ({ group: G_MSG, tooltip: T_MSG })); - let i_sxMsgSL = pinescript.inputString(O_SXMSGSL, "Short SL", ({ group: G_MSG, tooltip: T_MSG })); - let i_sxMsgTP1 = pinescript.inputString(O_SXMSGA, "Short TP1", ({ group: G_MSG, tooltip: T_MSG })); - let i_sxMsgTP2 = pinescript.inputString(O_SXMSGB, "Short TP2", ({ group: G_MSG, tooltip: T_MSG })); - let i_sxMsgTP3 = pinescript.inputString(O_SXMSGC, "Short TP3", ({ group: G_MSG, tooltip: T_MSG })); - let i_sxMsg = pinescript.inputString(O_SXMSGX, "Short Exit", ({ group: G_MSG, tooltip: T_MSG })); - let i_src = close; - let G_DISPLAY = "Display"; - let i_alertOn = pinescript.inputBool(true, "Alert Labels On/Off", ({ group: G_DISPLAY })); - let i_barColOn = pinescript.inputBool(true, "Bar Color On/Off", ({ group: G_DISPLAY })); + var closeSeries = variant(basisType, pinescript.hist(128, close, delayOffset), basisLen, offsetSigma, offsetALMA); + var openSeries = variant(basisType, pinescript.hist(129, open, delayOffset), basisLen, offsetSigma, offsetALMA); + var closeSeriesAlt = reso(closeSeries, useRes, stratRes); + var openSeriesAlt = reso(openSeries, useRes, stratRes); + var lxTrigger = false; + var sxTrigger = false; + var leTrigger = pinescript.crossover(pinescript.series(130, closeSeriesAlt), pinescript.series(131, openSeriesAlt)); + var seTrigger = pinescript.crossunder(pinescript.series(132, closeSeriesAlt), pinescript.series(133, openSeriesAlt)); + var G_RISK = ("■ " + "Risk Management"); + var T_LVL = "(%) Exit Level"; + var T_QTY = "(%) Adjust trade exit volume"; + var T_MSG = "Paste JSON message for your bot"; + var O_LEMSG = "Long Entry"; + var O_LXMSGSL = "Long SL"; + var O_LXMSGTP1 = "Long TP1"; + var O_LXMSGTP2 = "Long TP2"; + var O_LXMSGTP3 = "Long TP3"; + var O_LXMSG = "Long Exit"; + var O_SEMSG = "Short Entry"; + var O_SXMSGSL = "Short SL"; + var O_SXMSGA = "Short TP1"; + var O_SXMSGB = "Short TP2"; + var O_SXMSGC = "Short TP3"; + var O_SXMSGX = "Short Exit"; + var i_lxLvlTP1 = pinescript.inputFloat(1, "Level TP1", ({ group: G_RISK, tooltip: T_LVL })); + var i_lxQtyTP1 = pinescript.inputFloat(50, "Qty TP1", ({ group: G_RISK, tooltip: T_QTY })); + var i_lxLvlTP2 = pinescript.inputFloat(1.5, "Level TP2", ({ group: G_RISK, tooltip: T_LVL })); + var i_lxQtyTP2 = pinescript.inputFloat(30, "Qty TP2", ({ group: G_RISK, tooltip: T_QTY })); + var i_lxLvlTP3 = pinescript.inputFloat(2, "Level TP3", ({ group: G_RISK, tooltip: T_LVL })); + var i_lxQtyTP3 = pinescript.inputFloat(20, "Qty TP3", ({ group: G_RISK, tooltip: T_QTY })); + var i_lxLvlSL = pinescript.inputFloat(0.5, "Stop Loss", ({ group: G_RISK, tooltip: T_LVL })); + var i_sxLvlTP1 = i_lxLvlTP1; + var i_sxQtyTP1 = i_lxQtyTP1; + var i_sxLvlTP2 = i_lxLvlTP2; + var i_sxQtyTP2 = i_lxQtyTP2; + var i_sxLvlTP3 = i_lxLvlTP3; + var i_sxQtyTP3 = i_lxQtyTP3; + var i_sxLvlSL = i_lxLvlSL; + var G_MSG = ("■ " + "Webhook Message"); + var i_leMsg = pinescript.inputString(O_LEMSG, "Long Entry", ({ group: G_MSG, tooltip: T_MSG })); + var i_lxMsgSL = pinescript.inputString(O_LXMSGSL, "Long SL", ({ group: G_MSG, tooltip: T_MSG })); + var i_lxMsgTP1 = pinescript.inputString(O_LXMSGTP1, "Long TP1", ({ group: G_MSG, tooltip: T_MSG })); + var i_lxMsgTP2 = pinescript.inputString(O_LXMSGTP2, "Long TP2", ({ group: G_MSG, tooltip: T_MSG })); + var i_lxMsgTP3 = pinescript.inputString(O_LXMSGTP3, "Long TP3", ({ group: G_MSG, tooltip: T_MSG })); + var i_lxMsg = pinescript.inputString(O_LXMSG, "Long Exit", ({ group: G_MSG, tooltip: T_MSG })); + var i_seMsg = pinescript.inputString(O_SEMSG, "Short Entry", ({ group: G_MSG, tooltip: T_MSG })); + var i_sxMsgSL = pinescript.inputString(O_SXMSGSL, "Short SL", ({ group: G_MSG, tooltip: T_MSG })); + var i_sxMsgTP1 = pinescript.inputString(O_SXMSGA, "Short TP1", ({ group: G_MSG, tooltip: T_MSG })); + var i_sxMsgTP2 = pinescript.inputString(O_SXMSGB, "Short TP2", ({ group: G_MSG, tooltip: T_MSG })); + var i_sxMsgTP3 = pinescript.inputString(O_SXMSGC, "Short TP3", ({ group: G_MSG, tooltip: T_MSG })); + var i_sxMsg = pinescript.inputString(O_SXMSGX, "Short Exit", ({ group: G_MSG, tooltip: T_MSG })); + var i_src = close; + var G_DISPLAY = "Display"; + var i_alertOn = pinescript.inputBool(true, "Alert Labels On/Off", ({ group: G_DISPLAY })); + var i_barColOn = pinescript.inputBool(true, "Bar Color On/Off", ({ group: G_DISPLAY })); function f_tp(_condition, _conditionValue, _leTrigger, _seTrigger, _src, _lxLvlTP, _sxLvlTP) { if (state._tpLine === undefined) state._tpLine = 0; - let _topLvl = (_src + (_src * (_lxLvlTP / 100))); - let _botLvl = (_src - (_src * (_sxLvlTP / 100))); - state._tpLine = (((pinescript.hist(109, _condition, 1) !== _conditionValue) && _leTrigger) ? _topLvl : (((pinescript.hist(110, _condition, 1) !== -_conditionValue) && _seTrigger) ? _botLvl : pinescript.nz(pinescript.hist(111, state._tpLine, 1)))); + var _topLvl = (_src + (_src * (_lxLvlTP / 100))); + var _botLvl = (_src - (_src * (_sxLvlTP / 100))); + state._tpLine = (((pinescript.hist(134, _condition, 1) !== _conditionValue) && _leTrigger) ? _topLvl : (((pinescript.hist(135, _condition, 1) !== -_conditionValue) && _seTrigger) ? _botLvl : pinescript.nz(pinescript.hist(136, state._tpLine, 1)))); return [state._tpLine]; } function f_cross(_scr1, _scr2, _over) { - let _cross = (_over ? ((_scr1 > _scr2) && (pinescript.hist(112, _scr1, 1) < pinescript.hist(113, _scr2, 1))) : ((_scr1 < _scr2) && (pinescript.hist(114, _scr1, 1) > pinescript.hist(115, _scr2, 1)))); + var _cross = (_over ? ((_scr1 > _scr2) && (pinescript.hist(137, _scr1, 1) < pinescript.hist(138, _scr2, 1))) : ((_scr1 < _scr2) && (pinescript.hist(139, _scr1, 1) > pinescript.hist(140, _scr2, 1)))); } if (state.condition === undefined) state.condition = 0; if (state.slLine === undefined) state.slLine = 0; if (state.entryLine === undefined) state.entryLine = 0; - state.entryLine = ((leTrigger && (pinescript.hist(116, state.condition, 1) <= 0)) ? close : ((seTrigger && (pinescript.hist(117, state.condition, 1) >= 0)) ? close : pinescript.nz(pinescript.hist(118, state.entryLine, 1)))); - let slTopLvl = (i_src + (i_src * (i_lxLvlSL / 100))); - let slBotLvl = (i_src - (i_src * (i_sxLvlSL / 100))); - state.slLine = (((pinescript.hist(119, state.condition, 1) <= 0) && leTrigger) ? slBotLvl : (((pinescript.hist(120, state.condition, 1) >= 0) && seTrigger) ? slTopLvl : pinescript.nz(pinescript.hist(121, state.slLine, 1)))); - let slLong = f_cross(low, state.slLine, false); - let slShort = f_cross(high, state.slLine, true); - let [tp3Line] = pinescript.unpack(f_tp(state.condition, 1.2, leTrigger, seTrigger, i_src, i_lxLvlTP3, i_sxLvlTP3), 1); - let [tp2Line] = pinescript.unpack(f_tp(state.condition, 1.1, leTrigger, seTrigger, i_src, i_lxLvlTP2, i_sxLvlTP2), 1); - let [tp1Line] = pinescript.unpack(f_tp(state.condition, 1, leTrigger, seTrigger, i_src, i_lxLvlTP1, i_sxLvlTP1), 1); - let tp3Long = f_cross(high, tp3Line, true); - let tp3Short = f_cross(low, tp3Line, false); - let tp2Long = f_cross(high, tp2Line, true); - let tp2Short = f_cross(low, tp2Line, false); - let tp1Long = f_cross(high, tp1Line, true); - let tp1Short = f_cross(low, tp1Line, false); - if ((leTrigger && (pinescript.hist(122, state.condition, 1) <= 0))) { + state.entryLine = ((leTrigger && (pinescript.hist(141, state.condition, 1) <= 0)) ? close : ((seTrigger && (pinescript.hist(142, state.condition, 1) >= 0)) ? close : pinescript.nz(pinescript.hist(143, state.entryLine, 1)))); + var slTopLvl = (i_src + (i_src * (i_lxLvlSL / 100))); + var slBotLvl = (i_src - (i_src * (i_sxLvlSL / 100))); + state.slLine = (((pinescript.hist(144, state.condition, 1) <= 0) && leTrigger) ? slBotLvl : (((pinescript.hist(145, state.condition, 1) >= 0) && seTrigger) ? slTopLvl : pinescript.nz(pinescript.hist(146, state.slLine, 1)))); + var slLong = f_cross(low, state.slLine, false); + var slShort = f_cross(high, state.slLine, true); + var [tp3Line] = pinescript.unpack(f_tp(state.condition, 1.2, leTrigger, seTrigger, i_src, i_lxLvlTP3, i_sxLvlTP3), 1); + var [tp2Line] = pinescript.unpack(f_tp(state.condition, 1.1, leTrigger, seTrigger, i_src, i_lxLvlTP2, i_sxLvlTP2), 1); + var [tp1Line] = pinescript.unpack(f_tp(state.condition, 1, leTrigger, seTrigger, i_src, i_lxLvlTP1, i_sxLvlTP1), 1); + var tp3Long = f_cross(high, tp3Line, true); + var tp3Short = f_cross(low, tp3Line, false); + var tp2Long = f_cross(high, tp2Line, true); + var tp2Short = f_cross(low, tp2Line, false); + var tp1Long = f_cross(high, tp1Line, true); + var tp1Short = f_cross(low, tp1Line, false); + if ((leTrigger && (pinescript.hist(147, state.condition, 1) <= 0))) { state.condition = 1; } - else if ((seTrigger && (pinescript.hist(123, state.condition, 1) >= 0))) { + else if ((seTrigger && (pinescript.hist(148, state.condition, 1) >= 0))) { state.condition = -1; } - else if ((tp3Long && (pinescript.hist(124, state.condition, 1) === 1.2))) { + else if ((tp3Long && (pinescript.hist(149, state.condition, 1) === 1.2))) { state.condition = 1.3; } - else if ((tp3Short && (pinescript.hist(125, state.condition, 1) === -1.2))) { + else if ((tp3Short && (pinescript.hist(150, state.condition, 1) === -1.2))) { state.condition = -1.3; } - else if ((tp2Long && (pinescript.hist(126, state.condition, 1) === 1.1))) { + else if ((tp2Long && (pinescript.hist(151, state.condition, 1) === 1.1))) { state.condition = 1.2; } - else if ((tp2Short && (pinescript.hist(127, state.condition, 1) === -1.1))) { + else if ((tp2Short && (pinescript.hist(152, state.condition, 1) === -1.1))) { state.condition = -1.2; } - else if ((tp1Long && (pinescript.hist(128, state.condition, 1) === 1))) { + else if ((tp1Long && (pinescript.hist(153, state.condition, 1) === 1))) { state.condition = 1.1; } - else if ((tp1Short && (pinescript.hist(129, state.condition, 1) === -1))) { + else if ((tp1Short && (pinescript.hist(154, state.condition, 1) === -1))) { state.condition = -1.1; } - else if ((slLong && (pinescript.hist(130, state.condition, 1) >= 1))) { + else if ((slLong && (pinescript.hist(155, state.condition, 1) >= 1))) { state.condition = 0; } - else if ((slShort && (pinescript.hist(131, state.condition, 1) <= -1))) { + else if ((slShort && (pinescript.hist(156, state.condition, 1) <= -1))) { state.condition = 0; } - else if ((lxTrigger && (pinescript.hist(132, state.condition, 1) >= 1))) { + else if ((lxTrigger && (pinescript.hist(157, state.condition, 1) >= 1))) { state.condition = 0; } - else if ((sxTrigger && (pinescript.hist(133, state.condition, 1) <= -1))) { + else if ((sxTrigger && (pinescript.hist(158, state.condition, 1) <= -1))) { state.condition = 0; } - let longE = ((leTrigger && (pinescript.hist(134, state.condition, 1) <= 0)) && (state.condition === 1)); - let shortE = ((seTrigger && (pinescript.hist(135, state.condition, 1) >= 0)) && (state.condition === -1)); - let longX = ((lxTrigger && (pinescript.hist(136, state.condition, 1) >= 1)) && (state.condition === 0)); - let shortX = ((sxTrigger && (pinescript.hist(137, state.condition, 1) <= -1)) && (state.condition === 0)); - let longSL = ((slLong && (pinescript.hist(138, state.condition, 1) >= 1)) && (state.condition === 0)); - let shortSL = ((slShort && (pinescript.hist(139, state.condition, 1) <= -1)) && (state.condition === 0)); - let longTP3 = ((tp3Long && (pinescript.hist(140, state.condition, 1) === 1.2)) && (state.condition === 1.3)); - let shortTP3 = ((tp3Short && (pinescript.hist(141, state.condition, 1) === -1.2)) && (state.condition === -1.3)); - let longTP2 = ((tp2Long && (pinescript.hist(142, state.condition, 1) === 1.1)) && (state.condition === 1.2)); - let shortTP2 = ((tp2Short && (pinescript.hist(143, state.condition, 1) === -1.1)) && (state.condition === -1.2)); - let longTP1 = ((tp1Long && (pinescript.hist(144, state.condition, 1) === 1)) && (state.condition === 1.1)); - let shortTP1 = ((tp1Short && (pinescript.hist(145, state.condition, 1) === -1)) && (state.condition === -1.1)); + var longE = ((leTrigger && (pinescript.hist(159, state.condition, 1) <= 0)) && (state.condition === 1)); + var shortE = ((seTrigger && (pinescript.hist(160, state.condition, 1) >= 0)) && (state.condition === -1)); + var longX = ((lxTrigger && (pinescript.hist(161, state.condition, 1) >= 1)) && (state.condition === 0)); + var shortX = ((sxTrigger && (pinescript.hist(162, state.condition, 1) <= -1)) && (state.condition === 0)); + var longSL = ((slLong && (pinescript.hist(163, state.condition, 1) >= 1)) && (state.condition === 0)); + var shortSL = ((slShort && (pinescript.hist(164, state.condition, 1) <= -1)) && (state.condition === 0)); + var longTP3 = ((tp3Long && (pinescript.hist(165, state.condition, 1) === 1.2)) && (state.condition === 1.3)); + var shortTP3 = ((tp3Short && (pinescript.hist(166, state.condition, 1) === -1.2)) && (state.condition === -1.3)); + var longTP2 = ((tp2Long && (pinescript.hist(167, state.condition, 1) === 1.1)) && (state.condition === 1.2)); + var shortTP2 = ((tp2Short && (pinescript.hist(168, state.condition, 1) === -1.1)) && (state.condition === -1.2)); + var longTP1 = ((tp1Long && (pinescript.hist(169, state.condition, 1) === 1)) && (state.condition === 1.1)); + var shortTP1 = ((tp1Short && (pinescript.hist(170, state.condition, 1) === -1)) && (state.condition === -1.1)); if ((((pinescript.strategy.position_size <= 0) && longE) && barstate.isconfirmed)) { pinescript.strategyEntry("Long", pinescript.strategy.long, ({ alert_message: i_leMsg, comment: "LE" })); } @@ -2430,20 +2595,20 @@ function main() { if (shortX) { pinescript.strategyClose("Short", ({ alert_message: i_sxMsg, comment: "SX" })); } - let c_tp = ((leTrigger || seTrigger) ? null : ((state.condition === 0) ? null : pinescript.color.green)); - let c_entry = ((leTrigger || seTrigger) ? null : ((state.condition === 0) ? null : pinescript.color.blue)); - let c_sl = ((leTrigger || seTrigger) ? null : ((state.condition === 0) ? null : pinescript.color.red)); - let p_tp1Line = pinescript.plot((((state.condition === 1) || (state.condition === -1)) ? tp1Line : null), ({ title: "TP Line 1", color: c_tp, linewidth: 1, style: plot.style_linebr })); - let p_tp2Line = pinescript.plot((((((state.condition === 1) || (state.condition === -1)) || (state.condition === 1.1)) || (state.condition === -1.1)) ? tp2Line : null), ({ title: "TP Line 2", color: c_tp, linewidth: 1, style: plot.style_linebr })); - let p_tp3Line = pinescript.plot((((((((state.condition === 1) || (state.condition === -1)) || (state.condition === 1.1)) || (state.condition === -1.1)) || (state.condition === 1.2)) || (state.condition === -1.2)) ? tp3Line : null), ({ title: "TP Line 3", color: c_tp, linewidth: 1, style: plot.style_linebr })); - let p_entryLine = pinescript.plot((((state.condition >= 1) || (state.condition <= -1)) ? state.entryLine : null), ({ title: "Entry Line", color: c_entry, linewidth: 1, style: plot.style_linebr })); - let p_slLine = pinescript.plot((((((((state.condition === 1) || (state.condition === -1)) || (state.condition === 1.1)) || (state.condition === -1.1)) || (state.condition === 1.2)) || (state.condition === -1.2)) ? state.slLine : null), ({ title: "SL Line", color: c_sl, linewidth: 1, style: plot.style_linebr })); + var c_tp = ((leTrigger || seTrigger) ? null : ((state.condition === 0) ? null : pinescript.color.green)); + var c_entry = ((leTrigger || seTrigger) ? null : ((state.condition === 0) ? null : pinescript.color.blue)); + var c_sl = ((leTrigger || seTrigger) ? null : ((state.condition === 0) ? null : pinescript.color.red)); + var p_tp1Line = pinescript.plot((((state.condition === 1) || (state.condition === -1)) ? tp1Line : null), ({ title: "TP Line 1", color: c_tp, linewidth: 1, style: plot.style_linebr })); + var p_tp2Line = pinescript.plot((((((state.condition === 1) || (state.condition === -1)) || (state.condition === 1.1)) || (state.condition === -1.1)) ? tp2Line : null), ({ title: "TP Line 2", color: c_tp, linewidth: 1, style: plot.style_linebr })); + var p_tp3Line = pinescript.plot((((((((state.condition === 1) || (state.condition === -1)) || (state.condition === 1.1)) || (state.condition === -1.1)) || (state.condition === 1.2)) || (state.condition === -1.2)) ? tp3Line : null), ({ title: "TP Line 3", color: c_tp, linewidth: 1, style: plot.style_linebr })); + var p_entryLine = pinescript.plot((((state.condition >= 1) || (state.condition <= -1)) ? state.entryLine : null), ({ title: "Entry Line", color: c_entry, linewidth: 1, style: plot.style_linebr })); + var p_slLine = pinescript.plot((((((((state.condition === 1) || (state.condition === -1)) || (state.condition === 1.1)) || (state.condition === -1.1)) || (state.condition === 1.2)) || (state.condition === -1.2)) ? state.slLine : null), ({ title: "SL Line", color: c_sl, linewidth: 1, style: plot.style_linebr })); pinescript.fill(p_tp3Line, p_entryLine, ({ color: ((leTrigger || seTrigger) ? null : pinescript.color.new(pinescript.color.green, 90)) })); pinescript.fill(p_entryLine, p_slLine, ({ color: ((leTrigger || seTrigger) ? null : pinescript.color.new(pinescript.color.red, 90)) })); pinescript.plotshape((i_alertOn && longE), ({ title: "Long", text: "Long", textcolor: pinescript.color.white, color: pinescript.color.green, style: pinescript.shape.labelup, size: pinescript.size.tiny, location: pinescript.location.belowbar })); pinescript.plotshape((i_alertOn && shortE), ({ title: "Short", text: "Short", textcolor: pinescript.color.white, color: pinescript.color.red, style: pinescript.shape.labeldown, size: pinescript.size.tiny, location: pinescript.location.abovebar })); pinescript.plotshape(((i_alertOn && (longX || shortX)) ? close : null), ({ title: "Close", text: "Close", textcolor: pinescript.color.white, color: pinescript.color.gray, style: pinescript.shape.labelup, size: pinescript.size.tiny, location: pinescript.location.absolute })); - let l_tp = ((i_alertOn && (longTP1 || shortTP1)) ? close : null); + var l_tp = ((i_alertOn && (longTP1 || shortTP1)) ? close : null); pinescript.plotshape(l_tp, ({ title: "TP1 Cross", text: "TP1", textcolor: pinescript.color.white, color: pinescript.color.olive, style: pinescript.shape.labelup, size: pinescript.size.tiny, location: pinescript.location.absolute })); pinescript.plotshape(((i_alertOn && (longTP2 || shortTP2)) ? close : null), ({ title: "TP2 Cross", text: "TP2", textcolor: pinescript.color.white, color: pinescript.color.olive, style: pinescript.shape.labelup, size: pinescript.size.tiny, location: pinescript.location.absolute })); pinescript.plotshape(((i_alertOn && (longTP3 || shortTP3)) ? close : null), ({ title: "TP3 Cross", text: "TP3", textcolor: pinescript.color.white, color: pinescript.color.olive, style: pinescript.shape.labelup, size: pinescript.size.tiny, location: pinescript.location.absolute })); @@ -2453,7 +2618,7 @@ function main() { pinescript.plot((pinescript.strategy.position_size * 100), ({ title: ".position_size", editable: false, display: display.data_window })); function f_qDq(_array, _val) { pinescript.arrayPush(_array, _val); - let _return = pinescript.arrayShift(_array); + var _return = pinescript.arrayShift(_array); return _return; } if (state.a_slLine === undefined) state.a_slLine = pinescript.arrayNew(1); @@ -2466,11 +2631,11 @@ function main() { if (state.a_tp2label === undefined) state.a_tp2label = pinescript.arrayNew(1); if (state.a_tp1label === undefined) state.a_tp1label = pinescript.arrayNew(1); if (state.a_entryLabel === undefined) state.a_entryLabel = pinescript.arrayNew(1); - let newEntry = (longE || shortE); - let entryIndex = 1; - entryIndex = (newEntry ? bar_index : pinescript.nz(pinescript.hist(146, entryIndex, 1))); - let lasTrade = (bar_index >= entryIndex); - let l_right = 10; + var newEntry = (longE || shortE); + var entryIndex = 1; + entryIndex = (newEntry ? bar_index : pinescript.nz(pinescript.hist(171, entryIndex, 1))); + var lasTrade = (bar_index >= entryIndex); + var l_right = 10; pinescript.lineDelete(f_qDq(state.a_slLine, pinescript.lineNew(entryIndex, state.slLine, (last_bar_index + l_right), state.slLine, ({ style: line.style_solid, color: c_sl })))); pinescript.lineDelete(f_qDq(state.a_entryLine, pinescript.lineNew(entryIndex, state.entryLine, (last_bar_index + l_right), state.entryLine, ({ style: line.style_solid, color: pinescript.color.blue })))); pinescript.lineDelete(f_qDq(state.a_tp3Line, pinescript.lineNew(entryIndex, tp3Line, (last_bar_index + l_right), tp3Line, ({ style: line.style_solid, color: c_tp })))); @@ -2481,7 +2646,7 @@ function main() { pinescript.labelDelete(f_qDq(state.a_tp3label, pinescript.labelNew((last_bar_index + l_right), tp3Line, ("TP3: " + pinescript.strToString(tp3Line, "##.###")), ({ style: label.style_label_left, textcolor: pinescript.color.white, color: c_tp })))); pinescript.labelDelete(f_qDq(state.a_tp2label, pinescript.labelNew((last_bar_index + l_right), tp2Line, ("TP2: " + pinescript.strToString(tp2Line, "##.###")), ({ style: label.style_label_left, textcolor: pinescript.color.white, color: c_tp })))); pinescript.labelDelete(f_qDq(state.a_tp1label, pinescript.labelNew((last_bar_index + l_right), tp1Line, ("TP1: " + pinescript.strToString(tp1Line, "##.###")), ({ style: label.style_label_left, textcolor: pinescript.color.white, color: c_tp })))); - let c_barCol = ((close > open) ? pinescript.color.rgb(120, 9, 139) : pinescript.color.rgb(69, 155, 225)); + var c_barCol = ((close > open) ? pinescript.color.rgb(120, 9, 139) : pinescript.color.rgb(69, 155, 225)); pinescript.barcolor((i_barColOn ? c_barCol : null)); if ((((longE || shortE) || longX) || shortX)) { pinescript.alert(({ message: "Any Alert", freq: alert.freq_once_per_bar_close })); @@ -2542,6 +2707,10 @@ function run(data, options = {}) { rt.__shapeIdx = 0; globalThis.bar_index = i; globalThis.last_bar_index = n - 1; + globalThis.time_tradingday = inTime[i]; + globalThis.time_close = inTime[i]; + globalThis.last_bar_time = inTime[n - 1]; + globalThis.timenow = inTime[n - 1]; globalThis.barstate = { isfirst: i === 0, islast: i === n - 1, isrealtime: false, ishistory: true, isconfirmed: true, isnew: true, islastconfirmedhistory: i === n - 1, diff --git a/converted/Singular_Spectrum_Decomposition_LuxAlgo.js b/converted/Singular_Spectrum_Decomposition_LuxAlgo.js index a03f67e..91e92c3 100644 --- a/converted/Singular_Spectrum_Decomposition_LuxAlgo.js +++ b/converted/Singular_Spectrum_Decomposition_LuxAlgo.js @@ -42,6 +42,80 @@ const pinescript = { if (value != null && typeof value[Symbol.iterator] === 'function') return Array.from(value); return new Array(count || 0).fill(null); }, + __decArr: function(arr) { + if (!Array.isArray(arr) || arr.__pineDecorated) return arr; + const self = this; + const def = (name, fn) => Object.defineProperty(arr, name, { value: fn, writable: true, configurable: true }); + Object.defineProperty(arr, '__pineDecorated', { value: true, configurable: true }); + def('get', (i) => (arr[i] === undefined ? null : arr[i])); + def('set', (i, v) => { arr[i] = v; return v; }); + def('size', () => arr.length); + def('clear', () => { arr.length = 0; }); + def('insert', (i, v) => { arr.splice(i, 0, v); }); + def('remove', (i) => arr.splice(i, 1)[0]); + def('contains', (v) => arr.includes(v)); + def('indexof', (v) => arr.indexOf(v)); + def('lastindexof', (v) => arr.lastIndexOf(v)); + def('first', () => (arr.length ? arr[0] : null)); + def('last', () => (arr.length ? arr[arr.length - 1] : null)); + def('sum', () => arr.reduce((a, b) => a + b, 0)); + def('avg', () => (arr.length ? arr.reduce((a, b) => a + b, 0) / arr.length : 0)); + def('min', () => (arr.length ? Math.min(...arr) : null)); + def('max', () => (arr.length ? Math.max(...arr) : null)); + def('range', () => (arr.length ? Math.max(...arr) - Math.min(...arr) : null)); + // Statistical methods that route to the array.* built-ins (these names are not + // native to JS arrays, so attaching them here is safe from recursion). + def('median', () => self.arrayMedian(arr)); + def('mode', () => self.arrayMode(arr)); + def('stdev', () => self.arrayStdev(arr)); + def('variance', () => self.arrayVariance(arr)); + def('covariance', (other) => self.arrayCovariance(arr, other)); + def('percentile_linear_interpolation', (p) => self.arrayPercentileLinearInterpolation(arr, p)); + def('percentile_nearest_rank', (p) => self.arrayPercentileNearestRank(arr, p)); + def('abs', () => self.__decArr(self.arrayAbs(arr))); + def('binary_search', (v) => self.arrayBinarySearch(arr, v)); + // Pine's array.sort takes an order string, not a comparator. Use the native + // sort via .call so we don't recurse through this overridden method. + def('sort', (order) => { Array.prototype.sort.call(arr, (a, b) => (order === 'descending' ? b - a : a - b)); return arr; }); + def('sort_indices', (order) => self.__decArr(arr.map((_, i) => i).sort((a, b) => (order === 'descending' ? arr[b] - arr[a] : arr[a] - arr[b])))); + // join/slice/reverse/concat/includes/fill already exist natively on Array with + // compatible semantics, so we deliberately leave them to the native methods. + return arr; + }, + __decMap: function(m) { + if (!(m instanceof Map) || m.__pineDecorated) return m; + const def = (name, fn) => Object.defineProperty(m, name, { value: fn, writable: true, configurable: true }); + Object.defineProperty(m, '__pineDecorated', { value: true, configurable: true }); + def('put', (k, v) => { m.set(k, v); return v; }); + def('contains', (k) => m.has(k)); + def('remove', (k) => m.delete(k)); + def('keys', () => Array.from(Map.prototype.keys.call(m))); + def('values', () => Array.from(Map.prototype.values.call(m))); + def('size_', () => m.size); + return m; + }, + __decMatrix: function(m) { + if (!m || typeof m !== 'object' || m.__pineDecorated) return m; + const self = this; + const def = (name, fn) => Object.defineProperty(m, name, { value: fn, writable: true, configurable: true }); + Object.defineProperty(m, '__pineDecorated', { value: true, configurable: true }); + def('get', (r, c) => self.matrixGet(m, r, c)); + def('set', (r, c, v) => self.matrixSet(m, r, c, v)); + def('rows_', () => m.rows); + def('columns', () => m.cols); + def('fill', (v) => self.matrixFill(m, v)); + return m; + }, + __decDraw: function(obj) { + if (!obj || typeof obj !== 'object') return obj; + const p = new Proxy(obj, { + get(t, k) { + if (k in t || typeof k === 'symbol') return t[k]; + return function() { return p; }; + }, + }); + return p; + }, alertcondition: function(condition, ...rest) { if (globalThis.__pineRuntime) { globalThis.__pineRuntime.alerts.push({ condition, args: rest }); @@ -49,6 +123,32 @@ const pinescript = { return null; }, barcolor: function(color) { return null; }, + plotchar: function(series, ...rest) { + const rt = globalThis.__pineRuntime; + if (!rt) return series; + const bar = rt.__barIndex | 0; + const ord = (rt.__shapeIdx = (rt.__shapeIdx | 0) + 1) - 1; + let key = 'char_' + ord; + for (const r of rest) { + if (typeof r === 'string') { key = r; break; } + if (r && typeof r === 'object' && r.title) { key = String(r.title); break; } + } + let s = rt.plotshapes[key]; + if (!s) s = rt.plotshapes[key] = { title: key, data: [] }; + s.data[bar] = this.__scalar(series); + return series; + }, + plotarrow: function(series, ...rest) { + const rt = globalThis.__pineRuntime; + if (!rt) return series; + const bar = rt.__barIndex | 0; + const ord = (rt.__plotIdx = (rt.__plotIdx | 0) + 1) - 1; + const key = 'arrow_' + ord; + let p = rt.plots[key]; + if (!p) p = rt.plots[key] = { title: key, data: [] }; + p.data[bar] = this.__scalar(series); + return series; + }, bgcolor: function(color, title, editable, showLast) { return null; }, @@ -470,6 +570,12 @@ const pinescript = { atan: function(value) { return Math.atan(value); }, + todegrees: function(radians) { + return radians * (180 / Math.PI); + }, + toradians: function(degrees) { + return degrees * (Math.PI / 180); + }, floor: function(value) { return Math.floor(value); }, @@ -641,7 +747,7 @@ const pinescript = { return series; }, lineNew: function(x1, y1, x2, y2, opts = {}) { - return { x1, y1, x2, y2, opts, _type: 'line' }; + return this.__decDraw({ x1, y1, x2, y2, opts, _type: 'line' }); }, lineDelete: function(l) { return null; @@ -661,7 +767,7 @@ const pinescript = { return point === 0 || point === 'y1' ? line.y1 : line.y2; }, labelNew: function(x, y, text = '', opts = {}) { - return { x, y, text, opts, _type: 'label' }; + return this.__decDraw({ x, y, text, opts, _type: 'label' }); }, labelDelete: function(l) { return null; @@ -676,7 +782,7 @@ const pinescript = { return label.text || ''; }, boxNew: function(left, top, right, bottom, opts = {}) { - return { left, top, right, bottom, opts, _type: 'box' }; + return this.__decDraw({ left, top, right, bottom, opts, _type: 'box' }); }, boxDelete: function(box) { return null; @@ -694,7 +800,7 @@ const pinescript = { return box; }, polylineNew: function(points, opts = {}) { - return { points: points || [], opts, _type: 'polyline' }; + return this.__decDraw({ points: points || [], opts, _type: 'polyline' }); }, polylineDelete: function(poly) { return null; @@ -765,7 +871,7 @@ const pinescript = { return { time: _time ?? null, price: _price ?? null }; }, mapNew: function() { - return new Map(); + return this.__decMap(new Map()); }, mapSize: function(m) { return m instanceof Map ? m.size : 0; @@ -800,7 +906,7 @@ const pinescript = { const r = Math.max(0, rows ?? 0); const c = Math.max(0, cols ?? 0); const data = Array.from({ length: r }, () => Array.from({ length: c }, () => initialValue)); - return { rows: r, cols: c, data }; + return this.__decMatrix({ rows: r, cols: c, data }); }, matrixRows: function(m) { return m?.rows ?? 0; @@ -923,11 +1029,62 @@ const pinescript = { } return null; }, + __jacobiEigen: function(m) { + if (!m || !Array.isArray(m.data)) return null; + const n = m.rows ?? 0; + if (n === 0 || n !== (m.cols ?? 0)) return null; + // Work on a copy so the input matrix is untouched. + const a = m.data.map(row => row.slice()); + const v = Array.from({ length: n }, (_, i) => Array.from({ length: n }, (_, j) => (i === j ? 1 : 0))); + for (let sweep = 0; sweep < 100; sweep++) { + let off = 0; + for (let p = 0; p < n; p++) for (let q = p + 1; q < n; q++) off += a[p][q] * a[p][q]; + if (off < 1e-20) break; + for (let p = 0; p < n; p++) { + for (let q = p + 1; q < n; q++) { + if (Math.abs(a[p][q]) < 1e-18) continue; + const theta = (a[q][q] - a[p][p]) / (2 * a[p][q]); + const t = Math.sign(theta || 1) / (Math.abs(theta) + Math.sqrt(theta * theta + 1)); + const cos = 1 / Math.sqrt(t * t + 1); + const sin = t * cos; + for (let k = 0; k < n; k++) { + const akp = a[k][p], akq = a[k][q]; + a[k][p] = cos * akp - sin * akq; + a[k][q] = sin * akp + cos * akq; + } + for (let k = 0; k < n; k++) { + const apk = a[p][k], aqk = a[q][k]; + a[p][k] = cos * apk - sin * aqk; + a[q][k] = sin * apk + cos * aqk; + } + for (let k = 0; k < n; k++) { + const vkp = v[k][p], vkq = v[k][q]; + v[k][p] = cos * vkp - sin * vkq; + v[k][q] = sin * vkp + cos * vkq; + } + } + } + } + // Sort eigenpairs by eigenvalue, descending. + const order = Array.from({ length: n }, (_, i) => i).sort((i, j) => a[j][j] - a[i][i]); + const values = order.map(i => a[i][i]); + const vectors = Array.from({ length: n }, (_, r) => order.map(c => v[r][c])); + return { values, vectors }; + }, + matrixEigenvalues: function(m) { + const e = this.__jacobiEigen(m); + return this.__decArr(e ? e.values : []); + }, + matrixEigenvectors: function(m) { + const e = this.__jacobiEigen(m); + if (!e) return this.__decMatrix({ rows: 0, cols: 0, data: [] }); + return this.__decMatrix({ rows: e.vectors.length, cols: e.vectors.length, data: e.vectors }); + }, requestSecurity: function(symbol, timeframe, expression) { return expression; }, arrayNew: function(initialSize = 0, initialValue = 0) { - return Array(initialSize).fill(initialValue); + return this.__decArr(Array(initialSize).fill(initialValue)); }, arraySize: function(arr) { return arr ? arr.length : 0; @@ -978,10 +1135,10 @@ const pinescript = { return arr; }, arraySlice: function(arr, startIndex = 0, endIndex = null) { - if (!arr) return []; + if (!arr) return this.__decArr([]); const start = Number(startIndex) || 0; const end = endIndex === null || endIndex === undefined ? arr.length : Number(endIndex) || 0; - return arr.slice(start, end); + return this.__decArr(arr.slice(start, end)); }, arraySort: function(arr, order = 'ascending') { if (arr) arr.sort((a, b) => order === 'ascending' ? a - b : b - a); @@ -1191,7 +1348,7 @@ const pinescript = { const info = { ticker: 'AAPL', tickerid: 'NASDAQ:AAPL', prefix: 'NASDAQ', root: 'AAPL', suffix: '' }; return info[type] || ''; }, - timenow: 1781574528509, + timenow: 1782783577611, barstate: "LAST", dividends: {}, splits: {}, @@ -1245,11 +1402,11 @@ const pinescript = { arrayConcat: function(arr1, arr2) { if (!arr1) return arr2 || []; if (!arr2) return arr1; - return arr1.concat(arr2); + return this.__decArr(arr1.concat(arr2)); }, arrayCopy: function(arr) { - if (!arr) return []; - return [...arr]; + if (!arr) return this.__decArr([]); + return this.__decArr([...arr]); }, arrayBinarySearch: function(arr, value) { if (!arr || arr.length === 0) return -1; @@ -1339,7 +1496,7 @@ globalThis.input.timeframe = globalThis.input.timeframe || ((defval) => defval); globalThis.array = globalThis.array || { - from: (...items) => items, + from: (...items) => pinescript.__decArr(items), size: (arr) => pinescript.arraySize(arr), @@ -1508,6 +1665,8 @@ globalThis.line = globalThis.line || __pineNS({ style_solid: "solid", style_dash globalThis.box = globalThis.box || __pineNS({}); +globalThis.color = globalThis.color || __pineNS(Object.assign(function(c) { return c; }, { new: function(c, t) { return c; }, rgb: function(r, g, b, t) { return "#rgb(" + [r, g, b].join(",") + ")"; }, from_gradient: function(v, lo, hi, c1, c2) { return c1; }, r: function() { return 0; }, g: function() { return 0; }, b: function() { return 0; }, t: function() { return 0; }, aqua: "#00BCD4", black: "#363A45", blue: "#2962FF", fuchsia: "#E040FB", gray: "#787B86", green: "#4CAF50", lime: "#00E676", maroon: "#880E4F", navy: "#311B92", olive: "#808000", orange: "#FF9800", purple: "#9C27B0", red: "#FF5252", silver: "#B2B5BE", teal: "#00897B", white: "#FFFFFF", yellow: "#FFEB3B" })); + globalThis.label = globalThis.label || __pineNS({ style_label_down: "label_down", style_label_up: "label_up", style_none: "none" }); globalThis.polyline = globalThis.polyline || __pineNS({}); @@ -1520,7 +1679,9 @@ globalThis.map = globalThis.map || __pineNS({}); globalThis.session = globalThis.session || __pineNS({ regular: "regular", extended: "extended" }); -globalThis.dayofweek = globalThis.dayofweek || __pineNS({ sunday: 1, monday: 2, tuesday: 3, wednesday: 4, thursday: 5, friday: 6, saturday: 7 }); +globalThis.ticker = globalThis.ticker || __pineNS({}); + +globalThis.dayofweek = globalThis.dayofweek || __pineNS(Object.assign(function(t) { return new Date(t != null ? t : (globalThis.time || 0)).getUTCDay() + 1; }, { sunday: 1, monday: 2, tuesday: 3, wednesday: 4, thursday: 5, friday: 6, saturday: 7 })); globalThis.timeframe = __pineNS(Object.assign(globalThis.timeframe || {}, { period: (globalThis.timeframe && globalThis.timeframe.period) || "D", isintraday: false, isdaily: true, multiplier: 1 })); @@ -1568,8 +1729,6 @@ globalThis.bool = globalThis.bool || function(x) { return Boolean(x); }; globalThis.string = globalThis.string || function(x) { return x == null ? null : String(x); }; -globalThis.dayofweek = globalThis.dayofweek || __pineNS({ sunday: 1, monday: 2, tuesday: 3, wednesday: 4, thursday: 5, friday: 6, saturday: 7 }); - globalThis.str = globalThis.str || __pineNS({}); pinescript.math = globalThis.math; @@ -1655,7 +1814,7 @@ pinescript.text = { align_center: "center" }; pinescript.table = { - new: function(position, columns, rows, opts) { return { position, columns, rows, opts: opts || {}, cells: [] }; }, + new: function(position, columns, rows, opts) { return pinescript.__decDraw({ position, columns, rows, opts: opts || {}, cells: [] }); }, cell: function(table, column, row, text, opts) { @@ -1688,38 +1847,38 @@ function main() { null; // Study: Singular Spectrum Decomposition [LuxAlgo] // Options: {"overlay":false} - let lagInput = pinescript.inputInt(20, "Window Length (L)", ({ minval: 2, tooltip: "The embedding window length (L). Larger values capture longer cycles." })); - let trainingInput = pinescript.inputInt(100, "Buffer Length (N)", ({ minval: 50, tooltip: "The number of recent bars (N) used for the static decomposition." })); - let forecastInput = pinescript.inputInt(20, "Forecast Length", ({ minval: 1, tooltip: "Number of bars to extrapolate into the future." })); - let GROUP_STYLE = "Style"; - let showTrend = pinescript.inputBool(true, "Trend on Price", ({ inline: "Trend", group: GROUP_STYLE })); - let trendColorInput = pinescript.inputColor(pinescript.color.hex("#ff5d00"), "", ({ inline: "Trend", group: GROUP_STYLE })); - let showP1 = pinescript.inputBool(true, "Long Term Periodic", ({ inline: "P1", group: GROUP_STYLE })); - let p1ColorInput = pinescript.inputColor(pinescript.color.hex("#2962ff"), "", ({ inline: "P1", group: GROUP_STYLE })); - let showP2 = pinescript.inputBool(true, "Short Term Periodic", ({ inline: "P2", group: GROUP_STYLE })); - let p2ColorInput = pinescript.inputColor(pinescript.color.hex("#f06292"), "", ({ inline: "P2", group: GROUP_STYLE })); - let showNoise = pinescript.inputBool(true, "Noise", ({ inline: "Noise", group: GROUP_STYLE })); - let noiseColorInput = pinescript.inputColor(pinescript.color.new(pinescript.color.hex("#9c9c9c"), 50), "", ({ inline: "Noise", group: GROUP_STYLE })); - let showExtrapolation = pinescript.inputBool(true, "Show Extrapolation", ({ group: GROUP_STYLE, tooltip: "Show the forecast as dashed lines." })); - let DATA = pinescript.color.hex("#DBDBDB"); - let HEADERS = pinescript.color.hex("#808080"); - let BACKGROUND = pinescript.color.hex("#161616"); - let BORDERS = pinescript.color.hex("#2E2E2E"); - let TOP_RIGHT = "Top Right"; - let BOTTOM_RIGHT = "Bottom Right"; - let BOTTOM_LEFT = "Bottom Left"; - let TINY = "Tiny"; - let SMALL = "Small"; - let NORMAL = "Normal"; - let LARGE = "Large"; - let HUGE = "Huge"; - let DASHBOARD_GROUP = "Dashboard"; - let dashboardTooltip = "Enable or disable the dashboard."; - let dashboardPositionTooltip = "Select the dashboard location."; - let dashboardSizeTooltip = "Select the dashboard size."; - let dashboardInput = pinescript.inputBool(true, "Dashboard", ({ group: DASHBOARD_GROUP, tooltip: dashboardTooltip })); - let dashboardPositionInput = pinescript.inputString(TOP_RIGHT, "Position", ({ group: DASHBOARD_GROUP, tooltip: dashboardPositionTooltip, options: [TOP_RIGHT, BOTTOM_RIGHT, BOTTOM_LEFT] })); - let dashboardSizeInput = pinescript.inputString(SMALL, "Size", ({ group: DASHBOARD_GROUP, tooltip: dashboardSizeTooltip, options: [TINY, SMALL, NORMAL, LARGE, HUGE] })); + var lagInput = pinescript.inputInt(20, "Window Length (L)", ({ minval: 2, tooltip: "The embedding window length (L). Larger values capture longer cycles." })); + var trainingInput = pinescript.inputInt(100, "Buffer Length (N)", ({ minval: 50, tooltip: "The number of recent bars (N) used for the static decomposition." })); + var forecastInput = pinescript.inputInt(20, "Forecast Length", ({ minval: 1, tooltip: "Number of bars to extrapolate into the future." })); + var GROUP_STYLE = "Style"; + var showTrend = pinescript.inputBool(true, "Trend on Price", ({ inline: "Trend", group: GROUP_STYLE })); + var trendColorInput = pinescript.inputColor(pinescript.color.hex("#ff5d00"), "", ({ inline: "Trend", group: GROUP_STYLE })); + var showP1 = pinescript.inputBool(true, "Long Term Periodic", ({ inline: "P1", group: GROUP_STYLE })); + var p1ColorInput = pinescript.inputColor(pinescript.color.hex("#2962ff"), "", ({ inline: "P1", group: GROUP_STYLE })); + var showP2 = pinescript.inputBool(true, "Short Term Periodic", ({ inline: "P2", group: GROUP_STYLE })); + var p2ColorInput = pinescript.inputColor(pinescript.color.hex("#f06292"), "", ({ inline: "P2", group: GROUP_STYLE })); + var showNoise = pinescript.inputBool(true, "Noise", ({ inline: "Noise", group: GROUP_STYLE })); + var noiseColorInput = pinescript.inputColor(pinescript.color.new(pinescript.color.hex("#9c9c9c"), 50), "", ({ inline: "Noise", group: GROUP_STYLE })); + var showExtrapolation = pinescript.inputBool(true, "Show Extrapolation", ({ group: GROUP_STYLE, tooltip: "Show the forecast as dashed lines." })); + var DATA = pinescript.color.hex("#DBDBDB"); + var HEADERS = pinescript.color.hex("#808080"); + var BACKGROUND = pinescript.color.hex("#161616"); + var BORDERS = pinescript.color.hex("#2E2E2E"); + var TOP_RIGHT = "Top Right"; + var BOTTOM_RIGHT = "Bottom Right"; + var BOTTOM_LEFT = "Bottom Left"; + var TINY = "Tiny"; + var SMALL = "Small"; + var NORMAL = "Normal"; + var LARGE = "Large"; + var HUGE = "Huge"; + var DASHBOARD_GROUP = "Dashboard"; + var dashboardTooltip = "Enable or disable the dashboard."; + var dashboardPositionTooltip = "Select the dashboard location."; + var dashboardSizeTooltip = "Select the dashboard size."; + var dashboardInput = pinescript.inputBool(true, "Dashboard", ({ group: DASHBOARD_GROUP, tooltip: dashboardTooltip })); + var dashboardPositionInput = pinescript.inputString(TOP_RIGHT, "Position", ({ group: DASHBOARD_GROUP, tooltip: dashboardPositionTooltip, options: [TOP_RIGHT, BOTTOM_RIGHT, BOTTOM_LEFT] })); + var dashboardSizeInput = pinescript.inputString(SMALL, "Size", ({ group: DASHBOARD_GROUP, tooltip: dashboardSizeTooltip, options: [TINY, SMALL, NORMAL, LARGE, HUGE] })); pinescript.position.bottom_left; pinescript.position.bottom_right; pinescript.position.top_right; @@ -1731,23 +1890,23 @@ function main() { pinescript.size.tiny; if (state.parsedDashboardSize === undefined) state.parsedDashboardSize = ((dashboardSizeInput === TINY) ? undefined : ((dashboardSizeInput === SMALL) ? undefined : ((dashboardSizeInput === NORMAL) ? undefined : ((dashboardSizeInput === LARGE) ? undefined : ((dashboardSizeInput === HUGE) ? undefined : null))))); function sortEigen(vectors, values) { - let size_1 = values.size(); - let indices = pinescript.arrayNew(size_1); + var size_1 = values.size(); + var indices = pinescript.arrayNew(size_1); for (let i = 0; i <= (size_1 - 1); i++) { indices.set(i, i); } for (let i = 0; i <= (size_1 - 2); i++) { for (let j = 0; j <= ((size_1 - i) - 2); j++) { if ((values.get(indices.get(j)) < values.get(indices.get((j + 1))))) { - let temp = indices.get(j); + var temp = indices.get(j); indices.set(j, indices.get((j + 1))); indices.set((j + 1), temp); } } } - let sortedVectors = pinescript.matrixNew(vectors.rows(), vectors.columns()); + var sortedVectors = pinescript.matrixNew(vectors.rows(), vectors.columns()); for (let col = 0; col <= (size_1 - 1); col++) { - let oldCol = indices.get(col); + var oldCol = indices.get(col); for (let row = 0; row <= (vectors.rows() - 1); row++) { sortedVectors.set(row, col, vectors.get(row, oldCol)); } @@ -1755,21 +1914,21 @@ function main() { return sortedVectors; } function diagonalAveraging(V, k, y, L, N) { - let K = ((N - L) + 1); - let pc = pinescript.arrayNew(K, 0); + var K = ((N - L) + 1); + var pc = pinescript.arrayNew(K, 0); for (let j = 0; j <= (K - 1); j++) { - let val = 0; + var val = 0; for (let i = 0; i <= (L - 1); i++) { val += (pinescript.matrixGet(V, i, k) * pinescript.arrayGet(y, (j + i))); } pinescript.arraySet(pc, j, val); } - let rc = pinescript.arrayNew(N, 0); + var rc = pinescript.arrayNew(N, 0); for (let n = 0; n <= (N - 1); n++) { - let sum = 0; - let count = 0; + var sum = 0; + var count = 0; for (let i = pinescript.max(0, ((n - K) + 1)); i <= pinescript.min(n, (L - 1)); i++) { - let j = (n - i); + var j = (n - i); sum += (pinescript.matrixGet(V, i, k) * pinescript.arrayGet(pc, j)); count += 1; } @@ -1778,16 +1937,16 @@ function main() { return rc; } function getLRR(V, indices, L) { - let a = pinescript.arrayNew((L - 1), 0); - let verticalSum = 0; - for (const k of indices) { - let pi_k = V.get((L - 1), k); + var a = pinescript.arrayNew((L - 1), 0); + var verticalSum = 0; + for (const k of (indices ?? [])) { + var pi_k = V.get((L - 1), k); verticalSum += pinescript.pow(pi_k, 2); } if ((verticalSum < 1)) { for (let i = 0; i <= (L - 2); i++) { - let sum = 0; - for (const k of indices) { + var sum = 0; + for (const k of (indices ?? [])) { sum += (V.get((L - 1), k) * V.get(i, k)); } a.set(i, (sum / (1 - verticalSum))); @@ -1796,23 +1955,23 @@ function main() { return a; } function extrapolate(src, lrr, nForecast) { - let L = (lrr.size() + 1); - let res = pinescript.arrayCopy(src); - let lastRecon = src.get((src.size() - 1)); - let f0 = 0; + var L = (lrr.size() + 1); + var res = pinescript.arrayCopy(src); + var lastRecon = src.get((src.size() - 1)); + var f0 = 0; for (let j = 0; j <= (L - 2); j++) { f0 += (lrr.get(j) * src.get(((src.size() - L) + j))); } - let adjusted = pinescript.arrayNew(0); + var adjusted = pinescript.arrayNew(0); adjusted.push(lastRecon); - let runningVal = lastRecon; - let prevRaw = f0; + var runningVal = lastRecon; + var prevRaw = f0; for (let i = 1; i <= nForecast; i++) { - let val = 0; + var val = 0; for (let j = 0; j <= (L - 2); j++) { val += (lrr.get(j) * res.get(((res.size() - (L - 1)) + j))); } - let diff = (val - prevRaw); + var diff = (val - prevRaw); runningVal += diff; adjusted.push(runningVal); res.push(val); @@ -1821,28 +1980,28 @@ function main() { return adjusted; } function createPolyline(values, startBar, c, width = 2, style = line.style_solid) { - let points = pinescript.arrayNew(0); + var points = pinescript.arrayNew(0); for (let i = 0; i <= (values.size() - 1); i++) { pinescript.arrayPush(points, pinescript.chartPointFromIndex((startBar + i), pinescript.arrayGet(values, i))); } return pinescript.polylineNew(points, ({ line_color: c, line_width: width, line_style: style })); } function createOverlayPolyline(values, startBar, c, width = 2, style = line.style_solid) { - let points = pinescript.arrayNew(0); + var points = pinescript.arrayNew(0); for (let i = 0; i <= (values.size() - 1); i++) { pinescript.arrayPush(points, pinescript.chartPointFromIndex((startBar + i), pinescript.arrayGet(values, i))); } return pinescript.polylineNew(points, ({ line_color: c, line_width: width, line_style: style, force_overlay: true })); } function getPeriod(src) { - let n = src.size(); - let count = 0; - let firstIdx = -1; - let lastIdx = -1; + var n = src.size(); + var count = 0; + var firstIdx = -1; + var lastIdx = -1; if ((n > 1)) { for (let i = 1; i <= (n - 1); i++) { - let prev = src.get((i - 1)); - let curr = src.get(i); + var prev = src.get((i - 1)); + var curr = src.get(i); if ((((prev > 0) && (curr <= 0)) || ((prev < 0) && (curr >= 0)))) { if ((firstIdx === -1)) { firstIdx = i; @@ -1858,7 +2017,7 @@ function main() { return t_able.cell(column, row, data, ({ text_color: pinescript.color, text_size: state.parsedDashboardSize, text_halign: align, bgcolor: background, height: height })); } function divider(t_able, row, lastColumn) { - let rowDivider = "━━━━━━━━━━━━━━━━━━━━"; + var rowDivider = "━━━━━━━━━━━━━━━━━━━━"; t_able.merge_cells(0, row, lastColumn, row); return cell(t_able, 0, row, rowDivider, ({ align: pinescript.text.align_center, height: 0.5, color: BORDERS })); } @@ -1869,17 +2028,17 @@ function main() { if (state.noiseLine === undefined) state.noiseLine = null; if (state.t_able === undefined) state.t_able = pinescript.table.new(state.parsedDashboardPosition, 2, 5, ({ bgcolor: BACKGROUND, border_width: 0, frame_color: BORDERS, frame_width: 1, force_overlay: true })); if (barstate.islast) { - let startIdx = (bar_index - (trainingInput - 1)); - let foreIdx = bar_index; - let y = pinescript.arrayNew(0); + var startIdx = (bar_index - (trainingInput - 1)); + var foreIdx = bar_index; + var y = pinescript.arrayNew(0); for (let i = 0; i <= (trainingInput - 1); i++) { pinescript.arrayPush(y, pinescript.hist(0, close, ((trainingInput - 1) - i))); } - let S = pinescript.matrixNew(lagInput, lagInput, 0); - let nRowsX = ((trainingInput - lagInput) + 1); + var S = pinescript.matrixNew(lagInput, lagInput, 0); + var nRowsX = ((trainingInput - lagInput) + 1); for (let i = 0; i <= (lagInput - 1); i++) { for (let j = i; j <= (lagInput - 1); j++) { - let sum = 0; + var sum = 0; for (let k = 0; k <= (nRowsX - 1); k++) { sum += (pinescript.arrayGet(y, (k + i)) * pinescript.arrayGet(y, (k + j))); } @@ -1887,27 +2046,27 @@ function main() { pinescript.matrixSet(S, j, i, sum); } } - let evals = matrix.eigenvalues(S); - let evecs = matrix.eigenvectors(S); - let sortedV = sortEigen(evecs, evals); - let trendValues = diagonalAveraging(sortedV, 0, y, lagInput, trainingInput); - let trendExtrap = extrapolate(trendValues, getLRR(sortedV, array.from(0), lagInput), forecastInput); - let rc1 = diagonalAveraging(sortedV, 1, y, lagInput, trainingInput); - let rc2 = diagonalAveraging(sortedV, 2, y, lagInput, trainingInput); - let p1Values = pinescript.arrayNew(trainingInput, 0); + var evals = matrix.eigenvalues(S); + var evecs = matrix.eigenvectors(S); + var sortedV = sortEigen(evecs, evals); + var trendValues = diagonalAveraging(sortedV, 0, y, lagInput, trainingInput); + var trendExtrap = extrapolate(trendValues, getLRR(sortedV, pinescript.array.from(0), lagInput), forecastInput); + var rc1 = diagonalAveraging(sortedV, 1, y, lagInput, trainingInput); + var rc2 = diagonalAveraging(sortedV, 2, y, lagInput, trainingInput); + var p1Values = pinescript.arrayNew(trainingInput, 0); for (let i = 0; i <= (trainingInput - 1); i++) { pinescript.arraySet(p1Values, i, (pinescript.arrayGet(rc1, i) + pinescript.arrayGet(rc2, i))); } - let rc3 = diagonalAveraging(sortedV, 3, y, lagInput, trainingInput); - let rc4 = diagonalAveraging(sortedV, 4, y, lagInput, trainingInput); - let p2Values = pinescript.arrayNew(trainingInput, 0); + var rc3 = diagonalAveraging(sortedV, 3, y, lagInput, trainingInput); + var rc4 = diagonalAveraging(sortedV, 4, y, lagInput, trainingInput); + var p2Values = pinescript.arrayNew(trainingInput, 0); for (let i = 0; i <= (trainingInput - 1); i++) { pinescript.arraySet(p2Values, i, (pinescript.arrayGet(rc3, i) + pinescript.arrayGet(rc4, i))); } - let noiseValues = pinescript.arrayNew(trainingInput, 0); + var noiseValues = pinescript.arrayNew(trainingInput, 0); if ((lagInput > 5)) { for (let k = 5; k <= (lagInput - 1); k++) { - let rck = diagonalAveraging(sortedV, k, y, lagInput, trainingInput); + var rck = diagonalAveraging(sortedV, k, y, lagInput, trainingInput); for (let i = 0; i <= (trainingInput - 1); i++) { pinescript.arraySet(noiseValues, i, (pinescript.arrayGet(noiseValues, i) + pinescript.arrayGet(rck, i))); } @@ -1934,10 +2093,10 @@ function main() { state.noiseLine = createPolyline(noiseValues, startIdx, noiseColorInput, 1); } if (dashboardInput) { - let period1 = getPeriod(p1Values); - let period2 = getPeriod(p2Values); - t_able.clear(0, 0, 1, 4); - t_able.merge_cells(0, 0, 1, 0); + var period1 = getPeriod(p1Values); + var period2 = getPeriod(p2Values); + state.t_able.clear(0, 0, 1, 4); + state.t_able.merge_cells(0, 0, 1, 0); cell(state.t_able, 0, 0, "SSA Components", ({ color: DATA, align: pinescript.text.align_center })); divider(state.t_able, 1, 1); cell(state.t_able, 0, 2, "Long Term Avg Period", ({ color: HEADERS, align: pinescript.text.align_left })); @@ -1946,7 +2105,7 @@ function main() { cell(state.t_able, 0, 4, "Short Term Avg Period", ({ color: HEADERS, align: pinescript.text.align_left })); cell(state.t_able, 1, 4, pinescript.strToString(period2, "#.##"), ({ color: DATA })); } else { - t_able.clear(0, 0, 1, 4); + state.t_able.clear(0, 0, 1, 4); } } pinescript.hline(0, "Zero Line", ({ color: pinescript.color.new(pinescript.color.gray, 50), linestyle: hline.style_dotted })); @@ -1994,6 +2153,10 @@ function run(data, options = {}) { rt.__shapeIdx = 0; globalThis.bar_index = i; globalThis.last_bar_index = n - 1; + globalThis.time_tradingday = inTime[i]; + globalThis.time_close = inTime[i]; + globalThis.last_bar_time = inTime[n - 1]; + globalThis.timenow = inTime[n - 1]; globalThis.barstate = { isfirst: i === 0, islast: i === n - 1, isrealtime: false, ishistory: true, isconfirmed: true, isnew: true, islastconfirmedhistory: i === n - 1, diff --git a/converted/Smart_Trader_Episode_03_by_Ata_Sabanci_Candles_and_Tradelines.js b/converted/Smart_Trader_Episode_03_by_Ata_Sabanci_Candles_and_Tradelines.js index 20311b4..0970c53 100644 --- a/converted/Smart_Trader_Episode_03_by_Ata_Sabanci_Candles_and_Tradelines.js +++ b/converted/Smart_Trader_Episode_03_by_Ata_Sabanci_Candles_and_Tradelines.js @@ -42,6 +42,80 @@ const pinescript = { if (value != null && typeof value[Symbol.iterator] === 'function') return Array.from(value); return new Array(count || 0).fill(null); }, + __decArr: function(arr) { + if (!Array.isArray(arr) || arr.__pineDecorated) return arr; + const self = this; + const def = (name, fn) => Object.defineProperty(arr, name, { value: fn, writable: true, configurable: true }); + Object.defineProperty(arr, '__pineDecorated', { value: true, configurable: true }); + def('get', (i) => (arr[i] === undefined ? null : arr[i])); + def('set', (i, v) => { arr[i] = v; return v; }); + def('size', () => arr.length); + def('clear', () => { arr.length = 0; }); + def('insert', (i, v) => { arr.splice(i, 0, v); }); + def('remove', (i) => arr.splice(i, 1)[0]); + def('contains', (v) => arr.includes(v)); + def('indexof', (v) => arr.indexOf(v)); + def('lastindexof', (v) => arr.lastIndexOf(v)); + def('first', () => (arr.length ? arr[0] : null)); + def('last', () => (arr.length ? arr[arr.length - 1] : null)); + def('sum', () => arr.reduce((a, b) => a + b, 0)); + def('avg', () => (arr.length ? arr.reduce((a, b) => a + b, 0) / arr.length : 0)); + def('min', () => (arr.length ? Math.min(...arr) : null)); + def('max', () => (arr.length ? Math.max(...arr) : null)); + def('range', () => (arr.length ? Math.max(...arr) - Math.min(...arr) : null)); + // Statistical methods that route to the array.* built-ins (these names are not + // native to JS arrays, so attaching them here is safe from recursion). + def('median', () => self.arrayMedian(arr)); + def('mode', () => self.arrayMode(arr)); + def('stdev', () => self.arrayStdev(arr)); + def('variance', () => self.arrayVariance(arr)); + def('covariance', (other) => self.arrayCovariance(arr, other)); + def('percentile_linear_interpolation', (p) => self.arrayPercentileLinearInterpolation(arr, p)); + def('percentile_nearest_rank', (p) => self.arrayPercentileNearestRank(arr, p)); + def('abs', () => self.__decArr(self.arrayAbs(arr))); + def('binary_search', (v) => self.arrayBinarySearch(arr, v)); + // Pine's array.sort takes an order string, not a comparator. Use the native + // sort via .call so we don't recurse through this overridden method. + def('sort', (order) => { Array.prototype.sort.call(arr, (a, b) => (order === 'descending' ? b - a : a - b)); return arr; }); + def('sort_indices', (order) => self.__decArr(arr.map((_, i) => i).sort((a, b) => (order === 'descending' ? arr[b] - arr[a] : arr[a] - arr[b])))); + // join/slice/reverse/concat/includes/fill already exist natively on Array with + // compatible semantics, so we deliberately leave them to the native methods. + return arr; + }, + __decMap: function(m) { + if (!(m instanceof Map) || m.__pineDecorated) return m; + const def = (name, fn) => Object.defineProperty(m, name, { value: fn, writable: true, configurable: true }); + Object.defineProperty(m, '__pineDecorated', { value: true, configurable: true }); + def('put', (k, v) => { m.set(k, v); return v; }); + def('contains', (k) => m.has(k)); + def('remove', (k) => m.delete(k)); + def('keys', () => Array.from(Map.prototype.keys.call(m))); + def('values', () => Array.from(Map.prototype.values.call(m))); + def('size_', () => m.size); + return m; + }, + __decMatrix: function(m) { + if (!m || typeof m !== 'object' || m.__pineDecorated) return m; + const self = this; + const def = (name, fn) => Object.defineProperty(m, name, { value: fn, writable: true, configurable: true }); + Object.defineProperty(m, '__pineDecorated', { value: true, configurable: true }); + def('get', (r, c) => self.matrixGet(m, r, c)); + def('set', (r, c, v) => self.matrixSet(m, r, c, v)); + def('rows_', () => m.rows); + def('columns', () => m.cols); + def('fill', (v) => self.matrixFill(m, v)); + return m; + }, + __decDraw: function(obj) { + if (!obj || typeof obj !== 'object') return obj; + const p = new Proxy(obj, { + get(t, k) { + if (k in t || typeof k === 'symbol') return t[k]; + return function() { return p; }; + }, + }); + return p; + }, alertcondition: function(condition, ...rest) { if (globalThis.__pineRuntime) { globalThis.__pineRuntime.alerts.push({ condition, args: rest }); @@ -49,6 +123,32 @@ const pinescript = { return null; }, barcolor: function(color) { return null; }, + plotchar: function(series, ...rest) { + const rt = globalThis.__pineRuntime; + if (!rt) return series; + const bar = rt.__barIndex | 0; + const ord = (rt.__shapeIdx = (rt.__shapeIdx | 0) + 1) - 1; + let key = 'char_' + ord; + for (const r of rest) { + if (typeof r === 'string') { key = r; break; } + if (r && typeof r === 'object' && r.title) { key = String(r.title); break; } + } + let s = rt.plotshapes[key]; + if (!s) s = rt.plotshapes[key] = { title: key, data: [] }; + s.data[bar] = this.__scalar(series); + return series; + }, + plotarrow: function(series, ...rest) { + const rt = globalThis.__pineRuntime; + if (!rt) return series; + const bar = rt.__barIndex | 0; + const ord = (rt.__plotIdx = (rt.__plotIdx | 0) + 1) - 1; + const key = 'arrow_' + ord; + let p = rt.plots[key]; + if (!p) p = rt.plots[key] = { title: key, data: [] }; + p.data[bar] = this.__scalar(series); + return series; + }, bgcolor: function(color, title, editable, showLast) { return null; }, @@ -470,6 +570,12 @@ const pinescript = { atan: function(value) { return Math.atan(value); }, + todegrees: function(radians) { + return radians * (180 / Math.PI); + }, + toradians: function(degrees) { + return degrees * (Math.PI / 180); + }, floor: function(value) { return Math.floor(value); }, @@ -641,7 +747,7 @@ const pinescript = { return series; }, lineNew: function(x1, y1, x2, y2, opts = {}) { - return { x1, y1, x2, y2, opts, _type: 'line' }; + return this.__decDraw({ x1, y1, x2, y2, opts, _type: 'line' }); }, lineDelete: function(l) { return null; @@ -661,7 +767,7 @@ const pinescript = { return point === 0 || point === 'y1' ? line.y1 : line.y2; }, labelNew: function(x, y, text = '', opts = {}) { - return { x, y, text, opts, _type: 'label' }; + return this.__decDraw({ x, y, text, opts, _type: 'label' }); }, labelDelete: function(l) { return null; @@ -676,7 +782,7 @@ const pinescript = { return label.text || ''; }, boxNew: function(left, top, right, bottom, opts = {}) { - return { left, top, right, bottom, opts, _type: 'box' }; + return this.__decDraw({ left, top, right, bottom, opts, _type: 'box' }); }, boxDelete: function(box) { return null; @@ -694,7 +800,7 @@ const pinescript = { return box; }, polylineNew: function(points, opts = {}) { - return { points: points || [], opts, _type: 'polyline' }; + return this.__decDraw({ points: points || [], opts, _type: 'polyline' }); }, polylineDelete: function(poly) { return null; @@ -765,7 +871,7 @@ const pinescript = { return { time: _time ?? null, price: _price ?? null }; }, mapNew: function() { - return new Map(); + return this.__decMap(new Map()); }, mapSize: function(m) { return m instanceof Map ? m.size : 0; @@ -800,7 +906,7 @@ const pinescript = { const r = Math.max(0, rows ?? 0); const c = Math.max(0, cols ?? 0); const data = Array.from({ length: r }, () => Array.from({ length: c }, () => initialValue)); - return { rows: r, cols: c, data }; + return this.__decMatrix({ rows: r, cols: c, data }); }, matrixRows: function(m) { return m?.rows ?? 0; @@ -923,11 +1029,62 @@ const pinescript = { } return null; }, + __jacobiEigen: function(m) { + if (!m || !Array.isArray(m.data)) return null; + const n = m.rows ?? 0; + if (n === 0 || n !== (m.cols ?? 0)) return null; + // Work on a copy so the input matrix is untouched. + const a = m.data.map(row => row.slice()); + const v = Array.from({ length: n }, (_, i) => Array.from({ length: n }, (_, j) => (i === j ? 1 : 0))); + for (let sweep = 0; sweep < 100; sweep++) { + let off = 0; + for (let p = 0; p < n; p++) for (let q = p + 1; q < n; q++) off += a[p][q] * a[p][q]; + if (off < 1e-20) break; + for (let p = 0; p < n; p++) { + for (let q = p + 1; q < n; q++) { + if (Math.abs(a[p][q]) < 1e-18) continue; + const theta = (a[q][q] - a[p][p]) / (2 * a[p][q]); + const t = Math.sign(theta || 1) / (Math.abs(theta) + Math.sqrt(theta * theta + 1)); + const cos = 1 / Math.sqrt(t * t + 1); + const sin = t * cos; + for (let k = 0; k < n; k++) { + const akp = a[k][p], akq = a[k][q]; + a[k][p] = cos * akp - sin * akq; + a[k][q] = sin * akp + cos * akq; + } + for (let k = 0; k < n; k++) { + const apk = a[p][k], aqk = a[q][k]; + a[p][k] = cos * apk - sin * aqk; + a[q][k] = sin * apk + cos * aqk; + } + for (let k = 0; k < n; k++) { + const vkp = v[k][p], vkq = v[k][q]; + v[k][p] = cos * vkp - sin * vkq; + v[k][q] = sin * vkp + cos * vkq; + } + } + } + } + // Sort eigenpairs by eigenvalue, descending. + const order = Array.from({ length: n }, (_, i) => i).sort((i, j) => a[j][j] - a[i][i]); + const values = order.map(i => a[i][i]); + const vectors = Array.from({ length: n }, (_, r) => order.map(c => v[r][c])); + return { values, vectors }; + }, + matrixEigenvalues: function(m) { + const e = this.__jacobiEigen(m); + return this.__decArr(e ? e.values : []); + }, + matrixEigenvectors: function(m) { + const e = this.__jacobiEigen(m); + if (!e) return this.__decMatrix({ rows: 0, cols: 0, data: [] }); + return this.__decMatrix({ rows: e.vectors.length, cols: e.vectors.length, data: e.vectors }); + }, requestSecurity: function(symbol, timeframe, expression) { return expression; }, arrayNew: function(initialSize = 0, initialValue = 0) { - return Array(initialSize).fill(initialValue); + return this.__decArr(Array(initialSize).fill(initialValue)); }, arraySize: function(arr) { return arr ? arr.length : 0; @@ -978,10 +1135,10 @@ const pinescript = { return arr; }, arraySlice: function(arr, startIndex = 0, endIndex = null) { - if (!arr) return []; + if (!arr) return this.__decArr([]); const start = Number(startIndex) || 0; const end = endIndex === null || endIndex === undefined ? arr.length : Number(endIndex) || 0; - return arr.slice(start, end); + return this.__decArr(arr.slice(start, end)); }, arraySort: function(arr, order = 'ascending') { if (arr) arr.sort((a, b) => order === 'ascending' ? a - b : b - a); @@ -1191,7 +1348,7 @@ const pinescript = { const info = { ticker: 'AAPL', tickerid: 'NASDAQ:AAPL', prefix: 'NASDAQ', root: 'AAPL', suffix: '' }; return info[type] || ''; }, - timenow: 1781574528607, + timenow: 1782783577699, barstate: "LAST", dividends: {}, splits: {}, @@ -1245,11 +1402,11 @@ const pinescript = { arrayConcat: function(arr1, arr2) { if (!arr1) return arr2 || []; if (!arr2) return arr1; - return arr1.concat(arr2); + return this.__decArr(arr1.concat(arr2)); }, arrayCopy: function(arr) { - if (!arr) return []; - return [...arr]; + if (!arr) return this.__decArr([]); + return this.__decArr([...arr]); }, arrayBinarySearch: function(arr, value) { if (!arr || arr.length === 0) return -1; @@ -1339,7 +1496,7 @@ globalThis.input.timeframe = globalThis.input.timeframe || ((defval) => defval); globalThis.array = globalThis.array || { - from: (...items) => items, + from: (...items) => pinescript.__decArr(items), size: (arr) => pinescript.arraySize(arr), @@ -1508,6 +1665,8 @@ globalThis.line = globalThis.line || __pineNS({ style_solid: "solid", style_dash globalThis.box = globalThis.box || __pineNS({}); +globalThis.color = globalThis.color || __pineNS(Object.assign(function(c) { return c; }, { new: function(c, t) { return c; }, rgb: function(r, g, b, t) { return "#rgb(" + [r, g, b].join(",") + ")"; }, from_gradient: function(v, lo, hi, c1, c2) { return c1; }, r: function() { return 0; }, g: function() { return 0; }, b: function() { return 0; }, t: function() { return 0; }, aqua: "#00BCD4", black: "#363A45", blue: "#2962FF", fuchsia: "#E040FB", gray: "#787B86", green: "#4CAF50", lime: "#00E676", maroon: "#880E4F", navy: "#311B92", olive: "#808000", orange: "#FF9800", purple: "#9C27B0", red: "#FF5252", silver: "#B2B5BE", teal: "#00897B", white: "#FFFFFF", yellow: "#FFEB3B" })); + globalThis.label = globalThis.label || __pineNS({ style_label_down: "label_down", style_label_up: "label_up", style_none: "none" }); globalThis.polyline = globalThis.polyline || __pineNS({}); @@ -1520,7 +1679,9 @@ globalThis.map = globalThis.map || __pineNS({}); globalThis.session = globalThis.session || __pineNS({ regular: "regular", extended: "extended" }); -globalThis.dayofweek = globalThis.dayofweek || __pineNS({ sunday: 1, monday: 2, tuesday: 3, wednesday: 4, thursday: 5, friday: 6, saturday: 7 }); +globalThis.ticker = globalThis.ticker || __pineNS({}); + +globalThis.dayofweek = globalThis.dayofweek || __pineNS(Object.assign(function(t) { return new Date(t != null ? t : (globalThis.time || 0)).getUTCDay() + 1; }, { sunday: 1, monday: 2, tuesday: 3, wednesday: 4, thursday: 5, friday: 6, saturday: 7 })); globalThis.timeframe = __pineNS(Object.assign(globalThis.timeframe || {}, { period: (globalThis.timeframe && globalThis.timeframe.period) || "D", isintraday: false, isdaily: true, multiplier: 1 })); @@ -1568,8 +1729,6 @@ globalThis.bool = globalThis.bool || function(x) { return Boolean(x); }; globalThis.string = globalThis.string || function(x) { return x == null ? null : String(x); }; -globalThis.dayofweek = globalThis.dayofweek || __pineNS({ sunday: 1, monday: 2, tuesday: 3, wednesday: 4, thursday: 5, friday: 6, saturday: 7 }); - globalThis.str = globalThis.str || __pineNS({}); pinescript.math = globalThis.math; @@ -1655,7 +1814,7 @@ pinescript.text = { align_center: "center" }; pinescript.table = { - new: function(position, columns, rows, opts) { return { position, columns, rows, opts: opts || {}, cells: [] }; }, + new: function(position, columns, rows, opts) { return pinescript.__decDraw({ position, columns, rows, opts: opts || {}, cells: [] }); }, cell: function(table, column, row, text, opts) { @@ -1696,35 +1855,37 @@ function main() { null; // Study: Smart Trader, Episode 03, by Ata Sabancı | Candles and Tradelines // Options: {"shorttitle":"ST-EP03","overlay":true,"max_bars_back":5000,"max_labels_count":500,"max_boxes_count":500,"max_lines_count":500} - let G_MAIN = "═══ Main Settings ═══"; - let i_window = pinescript.inputInt(100, "Window Bars", ({ minval: 1, group: G_MAIN })); - let i_groups = pinescript.inputInt(20, "Group Count", ({ minval: 1, group: G_MAIN })); - let i_calcBasis = pinescript.inputString("Closed (bar[1])", "Calculation Basis", ({ options: ["Closed (bar[1])", "Current (bar[0])"], group: G_MAIN })); - let baseOff = ((i_calcBasis === "Current (bar[0])") ? 0 : 1); - let i_showGroupNums = pinescript.inputBool(true, "Show Group Numbers", ({ group: G_MAIN })); - let i_showGroupBoxes = pinescript.inputBool(true, "Show Group Boxes", ({ group: G_MAIN })); - let i_boxColor = pinescript.inputColor(pinescript.color.gray, "Box Color", ({ group: G_MAIN })); - let i_boxTransp = pinescript.inputInt(85, "Box Transparency", ({ minval: 0, maxval: 100, group: G_MAIN })); - let i_globalTextSizeStr = pinescript.inputString("Small", "Global Text Size", ({ options: ["Tiny", "Small", "Normal", "Large"], group: G_MAIN, tooltip: "Applies to ALL text elements on chart: labels, dashboard, trend lines, etc." })); - let GLOBAL_TEXT_SIZE = ((i_globalTextSizeStr === "Tiny") ? pinescript.size.tiny : ((i_globalTextSizeStr === "Normal") ? pinescript.size.normal : ((i_globalTextSizeStr === "Large") ? pinescript.size.large : pinescript.size.small))); - let G_ANALYTICS = "═══ Block Analytics ═══"; - let i_showCompositeCandle = pinescript.inputBool(false, "Show Composite Candle", ({ group: G_ANALYTICS, tooltip: "Displays a ghost-like composite candle at the center of each block representing aggregated OHLC." })); - let i_compositeCandleTransp = pinescript.inputInt(50, "Composite Candle Transparency", ({ minval: 50, maxval: 95, group: G_ANALYTICS, tooltip: "Transparency level for the ghost-like composite candle (higher = more transparent)." })); - let i_dimOriginalCandles = pinescript.inputBool(true, "Dim Original Candles", ({ group: G_ANALYTICS, tooltip: "When composite candles are shown, dims the original chart candles to make composites stand out." })); - let i_originalCandleDimLevel = pinescript.inputInt(95, "Original Candle Dim Level", ({ minval: 50, maxval: 95, group: G_ANALYTICS, tooltip: "Transparency level for dimmed original candles (higher = more transparent/faded)." })); - let groupSize = pinescript.max(1, int(pinescript.round((float(i_window) / i_groups)))); - let effWindow = (groupSize * i_groups); - let _dimBullish = pinescript.color.new(pinescript.color.rgb(34, 197, 94), i_originalCandleDimLevel); - let _dimBearish = pinescript.color.new(pinescript.color.rgb(239, 68, 68), i_originalCandleDimLevel); - let _dimNeutral = pinescript.color.new(pinescript.color.rgb(120, 120, 120), i_originalCandleDimLevel); - let _originalCandleColor = ((close > open) ? _dimBullish : ((close < open) ? _dimBearish : _dimNeutral)); + var G_MAIN = "═══ Main Settings ═══"; + var i_window = pinescript.inputInt(100, "Window Bars", ({ minval: 1, group: G_MAIN })); + var i_groups = pinescript.inputInt(20, "Group Count", ({ minval: 1, group: G_MAIN })); + var i_calcBasis = pinescript.inputString("Closed (bar[1])", "Calculation Basis", ({ options: ["Closed (bar[1])", "Current (bar[0])"], group: G_MAIN })); + var baseOff = ((i_calcBasis === "Current (bar[0])") ? 0 : 1); + var i_showGroupNums = pinescript.inputBool(true, "Show Group Numbers", ({ group: G_MAIN })); + var i_showGroupBoxes = pinescript.inputBool(true, "Show Group Boxes", ({ group: G_MAIN })); + var i_boxColor = pinescript.inputColor(pinescript.color.gray, "Box Color", ({ group: G_MAIN })); + var i_boxTransp = pinescript.inputInt(85, "Box Transparency", ({ minval: 0, maxval: 100, group: G_MAIN })); + var i_globalTextSizeStr = pinescript.inputString("Small", "Global Text Size", ({ options: ["Tiny", "Small", "Normal", "Large"], group: G_MAIN, tooltip: "Applies to ALL text elements on chart: labels, dashboard, trend lines, etc." })); + var GLOBAL_TEXT_SIZE = ((i_globalTextSizeStr === "Tiny") ? pinescript.size.tiny : ((i_globalTextSizeStr === "Normal") ? pinescript.size.normal : ((i_globalTextSizeStr === "Large") ? pinescript.size.large : pinescript.size.small))); + var G_ANALYTICS = "═══ Block Analytics ═══"; + var i_showCompositeCandle = pinescript.inputBool(false, "Show Composite Candle", ({ group: G_ANALYTICS, tooltip: "Displays a ghost-like composite candle at the center of each block representing aggregated OHLC." })); + var i_compositeCandleTransp = pinescript.inputInt(50, "Composite Candle Transparency", ({ minval: 50, maxval: 95, group: G_ANALYTICS, tooltip: "Transparency level for the ghost-like composite candle (higher = more transparent)." })); + var i_dimOriginalCandles = pinescript.inputBool(true, "Dim Original Candles", ({ group: G_ANALYTICS, tooltip: "When composite candles are shown, dims the original chart candles to make composites stand out." })); + var i_originalCandleDimLevel = pinescript.inputInt(95, "Original Candle Dim Level", ({ minval: 50, maxval: 95, group: G_ANALYTICS, tooltip: "Transparency level for dimmed original candles (higher = more transparent/faded)." })); + var groupSize = pinescript.max(1, int(pinescript.round((float(i_window) / i_groups)))); + var effWindow = (groupSize * i_groups); + var _dimBullish = pinescript.color.new(pinescript.color.rgb(34, 197, 94), i_originalCandleDimLevel); + var _dimBearish = pinescript.color.new(pinescript.color.rgb(239, 68, 68), i_originalCandleDimLevel); + var _dimNeutral = pinescript.color.new(pinescript.color.rgb(120, 120, 120), i_originalCandleDimLevel); + var _originalCandleColor = ((close > open) ? _dimBullish : ((close < open) ? _dimBearish : _dimNeutral)); pinescript.barcolor(((i_showCompositeCandle && i_dimOriginalCandles) ? _originalCandleColor : null)); function clear(_this) { if (!pinescript.na(_this.upperLine)) { pinescript.lineDelete(_this.upperLine); } if (!pinescript.na(_this.lowerLine)) { - pinescript.lineDelete(_this.lowerLine); + return pinescript.lineDelete(_this.lowerLine); + } else { + return null; } } function init(_this) { @@ -1801,6 +1962,8 @@ function main() { for (let j = 0; j <= (pinescript.arraySize(state.lblNums) - 1); j++) { label.set_size(pinescript.arrayGet(state.lblNums, j), GLOBAL_TEXT_SIZE); } + } else { + return null; } } function f_ensureBoxCount(n) { @@ -1857,13 +2020,13 @@ function main() { pinescript.arrayPop(state.coreBoxes); } } - let DIR_UP = 1; - let DIR_DOWN = -1; - let DIR_RANGE = 0; - let G_NARRATIVE = "═══ Market Narrative ═══"; - let i_narrativeEnable = pinescript.inputBool(true, "Enable Market Narrative", ({ group: G_NARRATIVE, tooltip: "Displays -generated market analysis based on calculated data. Educational purposes only." })); - let i_narrativeLang = pinescript.inputString("English", "Narrative Language", ({ options: ["English", "Türkçe", "हिन्दी", "العربية"], group: G_NARRATIVE, tooltip: "Select language for market narrative output." })); - let i_narrativeShowDisclaimer = pinescript.inputBool(true, "Show Educational Disclaimer", ({ group: G_NARRATIVE, tooltip: "Display legal disclaimer: Not investment advice, educational purposes only." })); + var DIR_UP = 1; + var DIR_DOWN = -1; + var DIR_RANGE = 0; + var G_NARRATIVE = "═══ Market Narrative ═══"; + var i_narrativeEnable = pinescript.inputBool(true, "Enable Market Narrative", ({ group: G_NARRATIVE, tooltip: "Displays -generated market analysis based on calculated data. Educational purposes only." })); + var i_narrativeLang = pinescript.inputString("English", "Narrative Language", ({ options: ["English", "Türkçe", "हिन्दी", "العربية"], group: G_NARRATIVE, tooltip: "Select language for market narrative output." })); + var i_narrativeShowDisclaimer = pinescript.inputBool(true, "Show Educational Disclaimer", ({ group: G_NARRATIVE, tooltip: "Display legal disclaimer: Not investment advice, educational purposes only." })); function f_L(en, tr, hi, ar) { return ((i_narrativeLang === "Türkçe") ? tr : ((i_narrativeLang === "हिन्दी") ? hi : ((i_narrativeLang === "العربية") ? ar : en))); } @@ -1877,7 +2040,7 @@ function main() { return ((q >= 80) ? f_L("HIGH", "YÜKSEK", "उच्च", "مرتفع") : ((q >= 60) ? f_L("MEDIUM", "ORTA", "मध्यम", "متوسط") : f_L("LOW", "DÜŞÜK", "निम्न", "منخفض"))); } function f_channelStatusStr(upperA, lowerA) { - let diff = (pinescript.abs(upperA) - pinescript.abs(lowerA)); + var diff = (pinescript.abs(upperA) - pinescript.abs(lowerA)); return ((diff > 3) ? f_L("EXPANDING", "GENİŞLEYEN", "विस्तारित", "متوسع") : ((diff < -3) ? f_L("CONTRACTING", "DARALAN", "संकुचित", "متقلص") : f_L("PARALLEL", "PARALEL", "समानांतर", "متوازي"))); } function f_dominanceStr(delta) { @@ -2128,56 +2291,56 @@ function main() { } function f_isHigherPosition(a, b) { if ((!a.valid || !b.valid)) { - DIR_RANGE; + return DIR_RANGE; } else { - let midA = ((a.newHigh + a.newLow) / 2); - let midB = ((b.newHigh + b.newLow) / 2); - ((midA > midB) ? DIR_UP : ((midA < midB) ? DIR_DOWN : DIR_RANGE)); + var midA = ((a.newHigh + a.newLow) / 2); + var midB = ((b.newHigh + b.newLow) / 2); + return ((midA > midB) ? DIR_UP : ((midA < midB) ? DIR_DOWN : DIR_RANGE)); } } function f_yangZhangVolatility(period) { - let sumOvSq = 0; - let sumOv = 0; - let sumClSq = 0; - let sumCl = 0; - let sumRS = 0; - let n = pinescript.max(2, period); + var sumOvSq = 0; + var sumOv = 0; + var sumClSq = 0; + var sumCl = 0; + var sumRS = 0; + var n = pinescript.max(2, period); for (let i = 0; i <= (n - 1); i++) { - let ov = pinescript.log((pinescript.hist(0, open, i) / pinescript.hist(1, close, (i + 1)))); + var ov = pinescript.log((pinescript.hist(0, open, i) / pinescript.hist(1, close, (i + 1)))); sumOv += ov; sumOvSq += (ov * ov); - let cl = pinescript.log((pinescript.hist(2, close, i) / pinescript.hist(3, open, i))); + var cl = pinescript.log((pinescript.hist(2, close, i) / pinescript.hist(3, open, i))); sumCl += cl; sumClSq += (cl * cl); - let logHO = pinescript.log((pinescript.hist(4, high, i) / pinescript.hist(5, open, i))); - let logHC = pinescript.log((pinescript.hist(6, high, i) / pinescript.hist(7, close, i))); - let logLO = pinescript.log((pinescript.hist(8, low, i) / pinescript.hist(9, open, i))); - let logLC = pinescript.log((pinescript.hist(10, low, i) / pinescript.hist(11, close, i))); + var logHO = pinescript.log((pinescript.hist(4, high, i) / pinescript.hist(5, open, i))); + var logHC = pinescript.log((pinescript.hist(6, high, i) / pinescript.hist(7, close, i))); + var logLO = pinescript.log((pinescript.hist(8, low, i) / pinescript.hist(9, open, i))); + var logLC = pinescript.log((pinescript.hist(10, low, i) / pinescript.hist(11, close, i))); sumRS += ((logHO * logHC) + (logLO * logLC)); } - let nm1 = float((n - 1)); - let varOv = ((sumOvSq / nm1) - ((sumOv * sumOv) / (float(n) * nm1))); - let varCl = ((sumClSq / nm1) - ((sumCl * sumCl) / (float(n) * nm1))); - let varRS = (sumRS / float(n)); - let k = (0.34 / (1.34 + (float((n + 1)) / float((n - 1))))); - let varYZ = ((varOv + (k * varCl)) + ((1 - k) * varRS)); - let sigma = pinescript.sqrt(pinescript.max(0, varYZ)); + var nm1 = float((n - 1)); + var varOv = ((sumOvSq / nm1) - ((sumOv * sumOv) / (float(n) * nm1))); + var varCl = ((sumClSq / nm1) - ((sumCl * sumCl) / (float(n) * nm1))); + var varRS = (sumRS / float(n)); + var k = (0.34 / (1.34 + (float((n + 1)) / float((n - 1))))); + var varYZ = ((varOv + (k * varCl)) + ((1 - k) * varRS)); + var sigma = pinescript.sqrt(pinescript.max(0, varYZ)); return (sigma * close); } function f_calcChannelAngle(priceChange, barSpan, yzVolPrice) { if (((barSpan === 0) || (yzVolPrice <= 0))) { - 0; + return 0; } else { - let expectedMove = (yzVolPrice * pinescript.sqrt(float(barSpan))); - let normalizedChange = ((expectedMove > 0) ? (priceChange / expectedMove) : 0); - pinescript.todegrees(pinescript.atan(normalizedChange)); + var expectedMove = (yzVolPrice * pinescript.sqrt(float(barSpan))); + var normalizedChange = ((expectedMove > 0) ? (priceChange / expectedMove) : 0); + return pinescript.todegrees(pinescript.atan(normalizedChange)); } } function f_clearAllChannels() { - let n = pinescript.arraySize(state.trendChannels); + var n = pinescript.arraySize(state.trendChannels); if ((n > 0)) { for (let i = 0; i <= (n - 1); i++) { - let ch = pinescript.arrayGet(state.trendChannels, i); + var ch = pinescript.arrayGet(state.trendChannels, i); clear(ch); } } @@ -2185,26 +2348,30 @@ function main() { } function f_lockBlockTrend(blockIdx, trendType) { if ((blockIdx < pinescript.arraySize(state.analyticsData))) { - let blk = pinescript.arrayGet(state.analyticsData, blockIdx); + var blk = pinescript.arrayGet(state.analyticsData, blockIdx); if (!blk.trendLocked) { blk.trendType = trendType; blk.trendLocked = true; - pinescript.arraySet(state.analyticsData, blockIdx, blk); + return pinescript.arraySet(state.analyticsData, blockIdx, blk); + } else { + return null; } + } else { + return null; } } function f_findSegmentExtremes(segStart, segEnd) { - let highestHigh = null; - let lowestHigh = null; - let highestLow = null; - let lowestLow = null; - let highestHighIdx = null; - let lowestHighIdx = null; - let highestLowIdx = null; - let lowestLowIdx = null; + var highestHigh = null; + var lowestHigh = null; + var highestLow = null; + var lowestLow = null; + var highestHighIdx = null; + var lowestHighIdx = null; + var highestLowIdx = null; + var lowestLowIdx = null; for (let i = segStart; i <= segEnd; i++) { if ((i < pinescript.arraySize(state.analyticsData))) { - let blk = pinescript.arrayGet(state.analyticsData, i); + var blk = pinescript.arrayGet(state.analyticsData, i); if (blk.valid) { if ((pinescript.na(highestHigh) || (blk.newHigh > highestHigh))) { highestHigh = blk.newHigh; @@ -2228,15 +2395,15 @@ function main() { return [highestHigh, lowestHigh, highestLow, lowestLow, highestHighIdx, lowestHighIdx, highestLowIdx, lowestLowIdx]; } function f_detectTrendChannels(rangeThreshold, yzVol) { - let numBlocks = pinescript.arraySize(state.analyticsData); + var numBlocks = pinescript.arraySize(state.analyticsData); if ((numBlocks < 2)) { - false; + return false; } else { - let i = 0; + var i = 0; while ((i < (numBlocks - 1))) { - let newerBlk = pinescript.arrayGet(state.analyticsData, i); - let olderBlk = pinescript.arrayGet(state.analyticsData, (i + 1)); - let dir = 0; + var newerBlk = pinescript.arrayGet(state.analyticsData, i); + var olderBlk = pinescript.arrayGet(state.analyticsData, (i + 1)); + var dir = 0; if (newerBlk.trendLocked) { dir = newerBlk.trendType; } else { @@ -2247,13 +2414,13 @@ function main() { i += 1; continue; } - let segStart = i; - let segEnd = (i + 1); + var segStart = i; + var segEnd = (i + 1); f_lockBlockTrend(segStart, dir); while ((segEnd < (numBlocks - 1))) { - let blkNewer = pinescript.arrayGet(state.analyticsData, segEnd); - let blkOlder = pinescript.arrayGet(state.analyticsData, (segEnd + 1)); - let nextDir = 0; + var blkNewer = pinescript.arrayGet(state.analyticsData, segEnd); + var blkOlder = pinescript.arrayGet(state.analyticsData, (segEnd + 1)); + var nextDir = 0; if (blkNewer.trendLocked) { nextDir = blkNewer.trendType; } else { @@ -2268,21 +2435,21 @@ function main() { } } f_lockBlockTrend(segEnd, dir); - let [highestHigh, lowestHigh, highestLow, lowestLow, hhIdx, lhIdx, hlIdx, llIdx] = pinescript.unpack(f_findSegmentExtremes(segStart, segEnd), 8); + var [highestHigh, lowestHigh, highestLow, lowestLow, hhIdx, lhIdx, hlIdx, llIdx] = pinescript.unpack(f_findSegmentExtremes(segStart, segEnd), 8); if ((((!pinescript.na(highestHigh) && !pinescript.na(lowestHigh)) && !pinescript.na(highestLow)) && !pinescript.na(lowestLow))) { - let blkOldest = pinescript.arrayGet(state.analyticsData, segEnd); - let blkNewest = pinescript.arrayGet(state.analyticsData, segStart); - let segmentX1 = blkOldest.centerIdx; - let segmentX2 = blkNewest.centerIdx; - let upperSlope = null; - let lowerSlope = null; - let upperY1 = null; - let upperY2 = null; - let lowerY1 = null; - let lowerY2 = null; + var blkOldest = pinescript.arrayGet(state.analyticsData, segEnd); + var blkNewest = pinescript.arrayGet(state.analyticsData, segStart); + var segmentX1 = blkOldest.centerIdx; + var segmentX2 = blkNewest.centerIdx; + var upperSlope = null; + var lowerSlope = null; + var upperY1 = null; + var upperY2 = null; + var lowerY1 = null; + var lowerY2 = null; if ((dir === DIR_DOWN)) { - let upperSpan = (lhIdx - hhIdx); - let lowerSpan = (llIdx - hlIdx); + var upperSpan = (lhIdx - hhIdx); + var lowerSpan = (llIdx - hlIdx); upperSlope = ((upperSpan !== 0) ? ((lowestHigh - highestHigh) / upperSpan) : 0); lowerSlope = ((lowerSpan !== 0) ? ((lowestLow - highestLow) / lowerSpan) : 0); upperY1 = (highestHigh + (upperSlope * (segmentX1 - hhIdx))); @@ -2291,8 +2458,8 @@ function main() { lowerY2 = (highestLow + (lowerSlope * (segmentX2 - hlIdx))); } else { if ((dir === DIR_UP)) { - let upperSpan = (hhIdx - lhIdx); - let lowerSpan = (hlIdx - llIdx); + upperSpan = (hhIdx - lhIdx); + lowerSpan = (hlIdx - llIdx); upperSlope = ((upperSpan !== 0) ? ((highestHigh - lowestHigh) / upperSpan) : 0); lowerSlope = ((lowerSpan !== 0) ? ((highestLow - lowestLow) / lowerSpan) : 0); upperY1 = (lowestHigh + (upperSlope * (segmentX1 - lhIdx))); @@ -2308,89 +2475,91 @@ function main() { lowerY2 = blkNewest.newLow; } } - let midY1 = ((upperY1 + lowerY1) / 2); - let midY2 = ((upperY2 + lowerY2) / 2); - let priceChange = (midY2 - midY1); - let segmentSpan = pinescript.abs((segmentX2 - segmentX1)); - let angleDeg = f_calcChannelAngle(priceChange, segmentSpan, yzVol); - let upperX1 = segmentX1; - let upperX2 = segmentX2; - let lowerX1 = segmentX1; - let lowerX2 = segmentX2; - let finalType = dir; + var midY1 = ((upperY1 + lowerY1) / 2); + var midY2 = ((upperY2 + lowerY2) / 2); + var priceChange = (midY2 - midY1); + var segmentSpan = pinescript.abs((segmentX2 - segmentX1)); + var angleDeg = f_calcChannelAngle(priceChange, segmentSpan, yzVol); + var upperX1 = segmentX1; + var upperX2 = segmentX2; + var lowerX1 = segmentX1; + var lowerX2 = segmentX2; + var finalType = dir; if ((pinescript.abs(angleDeg) <= rangeThreshold)) { finalType = 0; } - let channel = TrendChannel.new(finalType, (segStart + 1), (segEnd + 1), angleDeg, upperX1, upperX2, lowerX1, lowerX2, upperY1, upperY2, lowerY1, lowerY2, null, null); + var channel = TrendChannel.new(finalType, (segStart + 1), (segEnd + 1), angleDeg, upperX1, upperX2, lowerX1, lowerX2, upperY1, upperY2, lowerY1, lowerY2, null, null); pinescript.arrayPush(state.trendChannels, channel); } i = (segEnd + 1); } - true; + return true; } } function f_drawAllChannels(projLen, upCol, dnCol, rangeCol, lineWidth, lineStyle, showRange) { - let lstyle = ((lineStyle === "solid") ? line.style_solid : ((lineStyle === "dashed") ? line.style_dashed : line.style_dotted)); - let n = pinescript.arraySize(state.trendChannels); + var lstyle = ((lineStyle === "solid") ? line.style_solid : ((lineStyle === "dashed") ? line.style_dashed : line.style_dotted)); + var n = pinescript.arraySize(state.trendChannels); if ((n > 0)) { for (let i = 0; i <= (n - 1); i++) { - let ch = pinescript.arrayGet(state.trendChannels, i); + var ch = pinescript.arrayGet(state.trendChannels, i); if (((ch.channelType === DIR_RANGE) && !showRange)) { continue; } - let chCol = ((ch.channelType === DIR_UP) ? upCol : ((ch.channelType === DIR_DOWN) ? dnCol : rangeCol)); - let currentProjLen = ((i === 0) ? projLen : 0); - let upperBarSpan = (ch.upperX2 - ch.upperX1); - let lowerBarSpan = (ch.lowerX2 - ch.lowerX1); - let upperSlope = ((upperBarSpan > 0) ? ((ch.upperY2 - ch.upperY1) / upperBarSpan) : 0); - let lowerSlope = ((lowerBarSpan > 0) ? ((ch.lowerY2 - ch.lowerY1) / lowerBarSpan) : 0); - let upperX2Proj = (ch.upperX2 + currentProjLen); - let lowerX2Proj = (ch.lowerX2 + currentProjLen); - let upperY2Proj = (ch.upperY2 + (upperSlope * currentProjLen)); - let lowerY2Proj = (ch.lowerY2 + (lowerSlope * currentProjLen)); - let upperLn = pinescript.lineNew(ch.upperX1, ch.upperY1, upperX2Proj, upperY2Proj, ({ xloc: xloc.bar_index, extend: extend.none, color: chCol, width: lineWidth, style: lstyle })); - let lowerLn = pinescript.lineNew(ch.lowerX1, ch.lowerY1, lowerX2Proj, lowerY2Proj, ({ xloc: xloc.bar_index, extend: extend.none, color: chCol, width: lineWidth, style: lstyle })); + var chCol = ((ch.channelType === DIR_UP) ? upCol : ((ch.channelType === DIR_DOWN) ? dnCol : rangeCol)); + var currentProjLen = ((i === 0) ? projLen : 0); + var upperBarSpan = (ch.upperX2 - ch.upperX1); + var lowerBarSpan = (ch.lowerX2 - ch.lowerX1); + var upperSlope = ((upperBarSpan > 0) ? ((ch.upperY2 - ch.upperY1) / upperBarSpan) : 0); + var lowerSlope = ((lowerBarSpan > 0) ? ((ch.lowerY2 - ch.lowerY1) / lowerBarSpan) : 0); + var upperX2Proj = (ch.upperX2 + currentProjLen); + var lowerX2Proj = (ch.lowerX2 + currentProjLen); + var upperY2Proj = (ch.upperY2 + (upperSlope * currentProjLen)); + var lowerY2Proj = (ch.lowerY2 + (lowerSlope * currentProjLen)); + var upperLn = pinescript.lineNew(ch.upperX1, ch.upperY1, upperX2Proj, upperY2Proj, ({ xloc: xloc.bar_index, extend: extend.none, color: chCol, width: lineWidth, style: lstyle })); + var lowerLn = pinescript.lineNew(ch.lowerX1, ch.lowerY1, lowerX2Proj, lowerY2Proj, ({ xloc: xloc.bar_index, extend: extend.none, color: chCol, width: lineWidth, style: lstyle })); ch.upperLine = upperLn; ch.lowerLine = lowerLn; pinescript.arraySet(state.trendChannels, i, ch); } + } else { + return null; } } - let G_VOL = "═══ Volume Engine ═══"; - let G_DASH = "═══ Smart Dashboard ═══"; - let i_calcMethod = pinescript.inputString("Geometry (Approx)", "Calculation Method", ({ options: ["Geometry (Approx)", "Intrabar (Precise)"], group: G_VOL })); - let i_useCustomLTF = pinescript.inputBool(true, "Use custom lower timeframe", ({ group: G_VOL })); - let i_customLTF = input.timeframe("15S", "Lower timeframe (LTF)", ({ group: G_VOL })); - let i_globalPeriodBoxes = pinescript.inputInt(5, "Global Period (Boxes)", ({ minval: 1, maxval: 200, group: G_VOL })); - let i_showDash = pinescript.inputBool(true, "Show Dashboard", ({ group: G_DASH })); - let i_dashPos = pinescript.inputString(pinescript.position.top_right, "Position", ({ options: [pinescript.position.top_right, pinescript.position.bottom_right, pinescript.position.bottom_left], group: G_DASH })); - let G_TREND = "═══ Multi-Segment Trend ═══"; - let i_trendEnable = pinescript.inputBool(true, "Enable Trend Detection", ({ group: G_TREND, tooltip: "Multi-segment trend system using ghost candle HH/HL/LH/LL pattern detection." })); - let i_rangeAngleThreshold = pinescript.inputFloat(10, "Range Angle Threshold (°)", ({ minval: 1, maxval: 45, step: 1, group: G_TREND, tooltip: "Lines with angle ≤ this value are classified as RANGE." })); - let i_trendMinBlocks = pinescript.inputInt(2, "Minimum Blocks per Segment", ({ minval: 2, maxval: 10, group: G_TREND, tooltip: "Minimum blocks required to draw a segment line." })); - let i_showRangeSegments = pinescript.inputBool(true, "Show Range Segments", ({ group: G_TREND, tooltip: "Draw lines for range/sideways segments." })); - let i_trendUpColor = pinescript.inputColor(pinescript.color.rgb(34, 197, 94), "Uptrend Line Color", ({ group: G_TREND, tooltip: "Color for uptrend support lines (green)." })); - let i_trendDnColor = pinescript.inputColor(pinescript.color.rgb(239, 68, 68), "Downtrend Line Color", ({ group: G_TREND, tooltip: "Color for downtrend resistance lines (red)." })); - let i_trendRangeColor = pinescript.inputColor(pinescript.color.rgb(148, 163, 184), "Range Line Color", ({ group: G_TREND, tooltip: "Color for range/sideways lines (gray)." })); - let i_trendLineWidth = pinescript.inputInt(2, "Trend Line Width", ({ minval: 1, maxval: 5, group: G_TREND })); - let i_trendLineStyle = pinescript.inputString("solid", "Trend Line Style", ({ options: ["solid", "dashed", "dotted"], group: G_TREND })); - let i_trendProject = pinescript.inputBool(true, "Project to Future", ({ group: G_TREND, tooltip: "Extend trend lines into the future by one block width." })); - let G_CORE = "═══ Core Calculation ═══"; - let i_coreEnable = pinescript.inputBool(true, "Enable Core Calculation", ({ group: G_CORE })); - let i_coreDrawLines = pinescript.inputBool(true, "Draw Core Lines", ({ group: G_CORE })); - let i_coreProjectFwd = pinescript.inputBool(true, "Offset / Projection (to Future)", ({ group: G_CORE })); - let i_coreNearestOnly = pinescript.inputBool(true, "Nearest Only (1 Up / 1 Down)", ({ group: G_CORE })); - let i_coreShowMax = pinescript.inputBool(true, "Include MAX Buy/Sell Levels", ({ group: G_CORE })); - let i_coreShowMin = pinescript.inputBool(true, "Include MIN Buy/Sell Levels", ({ group: G_CORE })); - let i_pocEnable = pinescript.inputBool(true, "Enable POC", ({ group: G_CORE })); - let i_pocShowNearest = pinescript.inputBool(true, "Show POC Nearest Up/Down", ({ group: G_CORE })); - let i_pocBinProfile = pinescript.inputBool(true, "POC: Volume Profile Bins (All Plans)", ({ group: G_CORE, tooltip: "True Volume Profile POC — available on ALL TradingView plans (Free, Plus, Premium, Ultimate).nnHow it works: Divides each block's price range into equal bins, distributes each bar's volume proportionally across the bins its range overlaps, and selects the bin with the highest accumulated volume as the Point of Control.nnThis is significantly more accurate than the default single-bar method (which simply uses the average price of the highest-volume bar). When ON, POC reflects the actual high-volume price zone across all bars in the block.nnWhen OFF, the original single-bar method is used as fallback.nnNote: A future update will add native TradingView Footprint POC via request.footprint(), which provides tick-level precision but requires a Premium or Ultimate plan." })); - let i_pocBinCount = pinescript.inputInt(25, "POC Bin Resolution", ({ minval: 10, maxval: 100, group: G_CORE, tooltip: "Number of equal price bins used to build the Volume Profile within each block.nnHigher values = finer price resolution = more precise POC, but heavier computation.nLower values = coarser resolution = faster, but POC may be less precise.nnRecommended ranges:n• 10–15: Fast, suitable for high-timeframe or low-volatility instruments.n• 20–30: Good balance for most instruments and timeframes (default: 25).n• 50–100: High precision for scalping or tick-level analysis on lower timeframes.nnThis setting has no effect when 'POC: Volume Profile Bins' is OFF." })); - let i_coreLineWidth = pinescript.inputInt(1, "Line Width", ({ minval: 1, maxval: 4, group: G_CORE })); - let _coreBuyCol = pinescript.color.rgb(59, 130, 246); - let _coreSellCol = pinescript.color.rgb(244, 63, 94); - let _pocCol = pinescript.color.rgb(234, 179, 8); - let _pocColDim = pinescript.color.new(_pocCol, 25); + var G_VOL = "═══ Volume Engine ═══"; + var G_DASH = "═══ Smart Dashboard ═══"; + var i_calcMethod = pinescript.inputString("Geometry (Approx)", "Calculation Method", ({ options: ["Geometry (Approx)", "Intrabar (Precise)"], group: G_VOL })); + var i_useCustomLTF = pinescript.inputBool(true, "Use custom lower timeframe", ({ group: G_VOL })); + var i_customLTF = input.timeframe("15S", "Lower timeframe (LTF)", ({ group: G_VOL })); + var i_globalPeriodBoxes = pinescript.inputInt(5, "Global Period (Boxes)", ({ minval: 1, maxval: 200, group: G_VOL })); + var i_showDash = pinescript.inputBool(true, "Show Dashboard", ({ group: G_DASH })); + var i_dashPos = pinescript.inputString(pinescript.position.top_right, "Position", ({ options: [pinescript.position.top_right, pinescript.position.bottom_right, pinescript.position.bottom_left], group: G_DASH })); + var G_TREND = "═══ Multi-Segment Trend ═══"; + var i_trendEnable = pinescript.inputBool(true, "Enable Trend Detection", ({ group: G_TREND, tooltip: "Multi-segment trend system using ghost candle HH/HL/LH/LL pattern detection." })); + var i_rangeAngleThreshold = pinescript.inputFloat(10, "Range Angle Threshold (°)", ({ minval: 1, maxval: 45, step: 1, group: G_TREND, tooltip: "Lines with angle ≤ this value are classified as RANGE." })); + var i_trendMinBlocks = pinescript.inputInt(2, "Minimum Blocks per Segment", ({ minval: 2, maxval: 10, group: G_TREND, tooltip: "Minimum blocks required to draw a segment line." })); + var i_showRangeSegments = pinescript.inputBool(true, "Show Range Segments", ({ group: G_TREND, tooltip: "Draw lines for range/sideways segments." })); + var i_trendUpColor = pinescript.inputColor(pinescript.color.rgb(34, 197, 94), "Uptrend Line Color", ({ group: G_TREND, tooltip: "Color for uptrend support lines (green)." })); + var i_trendDnColor = pinescript.inputColor(pinescript.color.rgb(239, 68, 68), "Downtrend Line Color", ({ group: G_TREND, tooltip: "Color for downtrend resistance lines (red)." })); + var i_trendRangeColor = pinescript.inputColor(pinescript.color.rgb(148, 163, 184), "Range Line Color", ({ group: G_TREND, tooltip: "Color for range/sideways lines (gray)." })); + var i_trendLineWidth = pinescript.inputInt(2, "Trend Line Width", ({ minval: 1, maxval: 5, group: G_TREND })); + var i_trendLineStyle = pinescript.inputString("solid", "Trend Line Style", ({ options: ["solid", "dashed", "dotted"], group: G_TREND })); + var i_trendProject = pinescript.inputBool(true, "Project to Future", ({ group: G_TREND, tooltip: "Extend trend lines into the future by one block width." })); + var G_CORE = "═══ Core Calculation ═══"; + var i_coreEnable = pinescript.inputBool(true, "Enable Core Calculation", ({ group: G_CORE })); + var i_coreDrawLines = pinescript.inputBool(true, "Draw Core Lines", ({ group: G_CORE })); + var i_coreProjectFwd = pinescript.inputBool(true, "Offset / Projection (to Future)", ({ group: G_CORE })); + var i_coreNearestOnly = pinescript.inputBool(true, "Nearest Only (1 Up / 1 Down)", ({ group: G_CORE })); + var i_coreShowMax = pinescript.inputBool(true, "Include MAX Buy/Sell Levels", ({ group: G_CORE })); + var i_coreShowMin = pinescript.inputBool(true, "Include MIN Buy/Sell Levels", ({ group: G_CORE })); + var i_pocEnable = pinescript.inputBool(true, "Enable POC", ({ group: G_CORE })); + var i_pocShowNearest = pinescript.inputBool(true, "Show POC Nearest Up/Down", ({ group: G_CORE })); + var i_pocBinProfile = pinescript.inputBool(true, "POC: Volume Profile Bins (All Plans)", ({ group: G_CORE, tooltip: "True Volume Profile POC — available on ALL TradingView plans (Free, Plus, Premium, Ultimate).nnHow it works: Divides each block's price range into equal bins, distributes each bar's volume proportionally across the bins its range overlaps, and selects the bin with the highest accumulated volume as the Point of Control.nnThis is significantly more accurate than the default single-bar method (which simply uses the average price of the highest-volume bar). When ON, POC reflects the actual high-volume price zone across all bars in the block.nnWhen OFF, the original single-bar method is used as fallback.nnNote: A future update will add native TradingView Footprint POC via request.footprint(), which provides tick-level precision but requires a Premium or Ultimate plan." })); + var i_pocBinCount = pinescript.inputInt(25, "POC Bin Resolution", ({ minval: 10, maxval: 100, group: G_CORE, tooltip: "Number of equal price bins used to build the Volume Profile within each block.nnHigher values = finer price resolution = more precise POC, but heavier computation.nLower values = coarser resolution = faster, but POC may be less precise.nnRecommended ranges:n• 10–15: Fast, suitable for high-timeframe or low-volatility instruments.n• 20–30: Good balance for most instruments and timeframes (default: 25).n• 50–100: High precision for scalping or tick-level analysis on lower timeframes.nnThis setting has no effect when 'POC: Volume Profile Bins' is OFF." })); + var i_coreLineWidth = pinescript.inputInt(1, "Line Width", ({ minval: 1, maxval: 4, group: G_CORE })); + var _coreBuyCol = pinescript.color.rgb(59, 130, 246); + var _coreSellCol = pinescript.color.rgb(244, 63, 94); + var _pocCol = pinescript.color.rgb(234, 179, 8); + var _pocColDim = pinescript.color.new(_pocCol, 25); if (state.lnCoreUp === undefined) state.lnCoreUp = null; if (state.lnCoreDn === undefined) state.lnCoreDn = null; if (state.lnPOCUp === undefined) state.lnPOCUp = null; @@ -2399,19 +2568,19 @@ function main() { if (state.lblCoreDn === undefined) state.lblCoreDn = null; if (state.lblPOCUp === undefined) state.lblPOCUp = null; if (state.lblPOCDn === undefined) state.lblPOCDn = null; - let _T_MAX_BUY = 0; - let _T_MAX_SELL = 1; - let _T_MIN_BUY = 2; - let _T_MIN_SELL = 3; - let _T_POC = 10; + var _T_MAX_BUY = 0; + var _T_MAX_SELL = 1; + var _T_MIN_BUY = 2; + var _T_MIN_SELL = 3; + var _T_POC = 10; if (state._consSrc === undefined) state._consSrc = pinescript.arrayNew(); if (state._consType === undefined) state._consType = pinescript.arrayNew(); if (state._consPrice === undefined) state._consPrice = pinescript.arrayNew(); - let _CONS_MAX = 260; + var _CONS_MAX = 260; function f_isConsumed(src, typ, prc) { - let tol = (syminfo.mintick * 0.5); - let hit = false; - let n = pinescript.arraySize(state._consSrc); + var tol = (syminfo.mintick * 0.5); + var hit = false; + var n = pinescript.arraySize(state._consSrc); if ((n > 0)) { for (let i = 0; i <= (n - 1); i++) { if ((((pinescript.arrayGet(state._consSrc, i) === src) && (pinescript.arrayGet(state._consType, i) === typ)) && (pinescript.abs((pinescript.arrayGet(state._consPrice, i) - prc)) <= tol))) { @@ -2439,7 +2608,7 @@ function main() { } } function f_ensureLine(l, col, st) { - let out = l; + var out = l; if (pinescript.na(out)) { out = pinescript.lineNew(bar_index, close, bar_index, close, ({ xloc: xloc.bar_index, extend: extend.none, color: col, style: st, width: i_coreLineWidth })); } else { @@ -2453,7 +2622,9 @@ function main() { if (!pinescript.na(l)) { pinescript.lineSetXY(l, x, y); pinescript.lineSetXY(l, x, y); - line.set_color(l, pinescript.color.new(pinescript.color.gray, 100)); + return line.set_color(l, pinescript.color.new(pinescript.color.gray, 100)); + } else { + return null; } } function f_setHLine(l, x1, x2, y, col) { @@ -2461,14 +2632,16 @@ function main() { pinescript.lineSetXY(l, x1, y); pinescript.lineSetXY(l, x2, y); line.set_color(l, col); - line.set_width(l, i_coreLineWidth); + return line.set_width(l, i_coreLineWidth); + } else { + return null; } } function f_typeName(typ) { return ((typ === _T_MAX_BUY) ? "MAX BUY" : ((typ === _T_MAX_SELL) ? "MAX SELL" : ((typ === _T_MIN_BUY) ? "MIN BUY" : ((typ === _T_MIN_SELL) ? "MIN SELL" : "LEVEL")))); } function f_ensureLabel(lb, col) { - let out = lb; + var out = lb; if (pinescript.na(out)) { out = pinescript.labelNew(bar_index, close, "", ({ style: label.style_label_left, size: GLOBAL_TEXT_SIZE, textcolor: col, color: pinescript.color.new(pinescript.color.black, 85) })); } else { @@ -2480,7 +2653,9 @@ function main() { if (!pinescript.na(lb)) { pinescript.labelSetText(lb, ""); label.set_xy(lb, bar_index, close); - label.set_textcolor(lb, pinescript.color.new(pinescript.color.gray, 100)); + return label.set_textcolor(lb, pinescript.color.new(pinescript.color.gray, 100)); + } else { + return null; } } function f_setLineLabel(lb, x, y, txt, col) { @@ -2489,29 +2664,31 @@ function main() { pinescript.labelSetText(lb, txt); label.set_textcolor(lb, col); label.set_style(lb, label.style_label_left); - label.set_size(lb, GLOBAL_TEXT_SIZE); + return label.set_size(lb, GLOBAL_TEXT_SIZE); + } else { + return null; } } function f_pickNearestExt(refP) { - let upP = null; - let upDist = null; - let upSrc = null; - let upTyp = null; - let upBox = null; - let dnP = null; - let dnDist = null; - let dnSrc = null; - let dnTyp = null; - let dnBox = null; + var upP = null; + var upDist = null; + var upSrc = null; + var upTyp = null; + var upBox = null; + var dnP = null; + var dnDist = null; + var dnSrc = null; + var dnTyp = null; + var dnBox = null; for (let gi = 0; gi <= (i_groups - 1); gi++) { - let bc = pinescript.arrayGet(state.coreBoxes, gi); + var bc = pinescript.arrayGet(state.coreBoxes, gi); if (bc.valid) { if (i_coreShowMax) { - let p1 = bc.maxBuyHigh; - let s1 = (bar_index - bc.maxBuyOff); + var p1 = bc.maxBuyHigh; + var s1 = (bar_index - bc.maxBuyOff); if ((!pinescript.na(p1) && !f_isConsumed(s1, _T_MAX_BUY, p1))) { if ((p1 > refP)) { - let d1 = (p1 - refP); + var d1 = (p1 - refP); if ((pinescript.na(upDist) || (d1 < upDist))) { upDist = d1; upP = p1; @@ -2521,7 +2698,7 @@ function main() { } } if ((p1 < refP)) { - let d1d = (refP - p1); + var d1d = (refP - p1); if ((pinescript.na(dnDist) || (d1d < dnDist))) { dnDist = d1d; dnP = p1; @@ -2531,11 +2708,11 @@ function main() { } } } - let p2 = bc.maxSellLow; - let s2 = (bar_index - bc.maxSellOff); + var p2 = bc.maxSellLow; + var s2 = (bar_index - bc.maxSellOff); if ((!pinescript.na(p2) && !f_isConsumed(s2, _T_MAX_SELL, p2))) { if ((p2 > refP)) { - let d2 = (p2 - refP); + var d2 = (p2 - refP); if ((pinescript.na(upDist) || (d2 < upDist))) { upDist = d2; upP = p2; @@ -2545,7 +2722,7 @@ function main() { } } if ((p2 < refP)) { - let d2d = (refP - p2); + var d2d = (refP - p2); if ((pinescript.na(dnDist) || (d2d < dnDist))) { dnDist = d2d; dnP = p2; @@ -2557,11 +2734,11 @@ function main() { } } if (i_coreShowMin) { - let p3 = bc.minBuyHigh; - let s3 = (bar_index - bc.minBuyOff); + var p3 = bc.minBuyHigh; + var s3 = (bar_index - bc.minBuyOff); if ((!pinescript.na(p3) && !f_isConsumed(s3, _T_MIN_BUY, p3))) { if ((p3 > refP)) { - let d3 = (p3 - refP); + var d3 = (p3 - refP); if ((pinescript.na(upDist) || (d3 < upDist))) { upDist = d3; upP = p3; @@ -2571,7 +2748,7 @@ function main() { } } if ((p3 < refP)) { - let d3d = (refP - p3); + var d3d = (refP - p3); if ((pinescript.na(dnDist) || (d3d < dnDist))) { dnDist = d3d; dnP = p3; @@ -2581,11 +2758,11 @@ function main() { } } } - let p4 = bc.minSellLow; - let s4 = (bar_index - bc.minSellOff); + var p4 = bc.minSellLow; + var s4 = (bar_index - bc.minSellOff); if ((!pinescript.na(p4) && !f_isConsumed(s4, _T_MIN_SELL, p4))) { if ((p4 > refP)) { - let d4 = (p4 - refP); + var d4 = (p4 - refP); if ((pinescript.na(upDist) || (d4 < upDist))) { upDist = d4; upP = p4; @@ -2595,7 +2772,7 @@ function main() { } } if ((p4 < refP)) { - let d4d = (refP - p4); + var d4d = (refP - p4); if ((pinescript.na(dnDist) || (d4d < dnDist))) { dnDist = d4d; dnP = p4; @@ -2611,22 +2788,22 @@ function main() { return [upP, upSrc, upTyp, upBox, dnP, dnSrc, dnTyp, dnBox]; } function f_pickNearestPOC(refP) { - let upP = null; - let upSrc = null; - let upDist = null; - let upBox = null; - let dnP = null; - let dnSrc = null; - let dnDist = null; - let dnBox = null; + var upP = null; + var upSrc = null; + var upDist = null; + var upBox = null; + var dnP = null; + var dnSrc = null; + var dnDist = null; + var dnBox = null; for (let gi = 0; gi <= (i_groups - 1); gi++) { - let bc = pinescript.arrayGet(state.coreBoxes, gi); + var bc = pinescript.arrayGet(state.coreBoxes, gi); if (bc.valid) { - let p = bc.pocPrice; - let src = (bar_index - bc.pocOff); + var p = bc.pocPrice; + var src = (bar_index - bc.pocOff); if ((!pinescript.na(p) && !f_isConsumed(src, _T_POC, p))) { if ((p > refP)) { - let d = (p - refP); + var d = (p - refP); if ((pinescript.na(upDist) || (d < upDist))) { upDist = d; upP = p; @@ -2635,7 +2812,7 @@ function main() { } } if ((p < refP)) { - let d2 = (refP - p); + var d2 = (refP - p); if ((pinescript.na(dnDist) || (d2 < dnDist))) { dnDist = d2; dnP = p; @@ -2649,8 +2826,8 @@ function main() { return [upP, upSrc, upBox, dnP, dnSrc, dnBox]; } function f_classifyCandlePattern(bodyR, upperR, lowerR, isBull) { - let pattern = "STANDARD"; - let bias = "NEUTRAL"; + var pattern = "STANDARD"; + var bias = "NEUTRAL"; if ((bodyR < 10)) { if (((lowerR > 65) && (upperR < 15))) { pattern = "DRAGONFLY"; @@ -2703,67 +2880,67 @@ function main() { return [pattern, bias]; } function f_calcRangeStatus(barRange, atr) { - let ratio = ((atr > 0) ? (barRange / atr) : 1); - let status = ((ratio > 1.5) ? "WIDE" : ((ratio < 0.6) ? "NARROW" : "NORMAL")); + var ratio = ((atr > 0) ? (barRange / atr) : 1); + var status = ((ratio > 1.5) ? "WIDE" : ((ratio < 0.6) ? "NARROW" : "NORMAL")); return [ratio, status]; } function f_calcVolumeStatus(vol, period, offset) { - let sumVol = 0; - let count = 0; + var sumVol = 0; + var count = 0; for (let i = offset; i <= ((offset + period) - 1); i++) { - let v = pinescript.hist(12, volume, i); + var v = pinescript.hist(12, volume, i); if (!pinescript.na(v)) { sumVol += v; count += 1; } } - let avgVol = ((count > 0) ? (sumVol / count) : null); - let ratio = ((!pinescript.na(avgVol) && (avgVol > 0)) ? (vol / avgVol) : 1); - let status = ((ratio > 2) ? "SPIKE" : ((ratio < 0.5) ? "LOW" : "NORMAL")); + var avgVol = ((count > 0) ? (sumVol / count) : null); + var ratio = ((!pinescript.na(avgVol) && (avgVol > 0)) ? (vol / avgVol) : 1); + var status = ((ratio > 2) ? "SPIKE" : ((ratio < 0.5) ? "LOW" : "NORMAL")); return [ratio, status]; } function f_calcPressureStatus(buyVol, sellVol) { - let total = (buyVol + sellVol); + var total = (buyVol + sellVol); if ((total === 0)) { - "BALANCED"; + return "BALANCED"; } else { - let buyPct = ((buyVol / total) * 100); - ((buyPct > 55) ? "BUYING" : ((buyPct < 45) ? "SELLING" : "BALANCED")); + var buyPct = ((buyVol / total) * 100); + return ((buyPct > 55) ? "BUYING" : ((buyPct < 45) ? "SELLING" : "BALANCED")); } } function f_analyzeCurrentBar(offset, atr, buyV, sellV) { - let h = pinescript.hist(13, high, offset); - let l = pinescript.hist(14, low, offset); - let o = pinescript.hist(15, open, offset); - let c = pinescript.hist(16, close, offset); - let v = pinescript.hist(17, volume, offset); - let barRange = (h - l); - let bodySize = pinescript.abs((c - o)); - let upperWick = (h - pinescript.max(o, c)); - let lowerWick = (pinescript.min(o, c) - l); - let bodyR = ((barRange > 0) ? ((bodySize / barRange) * 100) : 0); - let upperR = ((barRange > 0) ? ((upperWick / barRange) * 100) : 0); - let lowerR = ((barRange > 0) ? ((lowerWick / barRange) * 100) : 0); - let isBull = (c > o); - let [pattern, patternBias] = pinescript.unpack(f_classifyCandlePattern(bodyR, upperR, lowerR, isBull), 2); - let [rangeRatio, rangeStatus] = pinescript.unpack(f_calcRangeStatus(barRange, atr), 2); - let [volRatio, volStatus] = pinescript.unpack(f_calcVolumeStatus(v, 20, offset), 2); - let delta = (buyV - sellV); - let pressure = f_calcPressureStatus(buyV, sellV); + var h = pinescript.hist(13, high, offset); + var l = pinescript.hist(14, low, offset); + var o = pinescript.hist(15, open, offset); + var c = pinescript.hist(16, close, offset); + var v = pinescript.hist(17, volume, offset); + var barRange = (h - l); + var bodySize = pinescript.abs((c - o)); + var upperWick = (h - pinescript.max(o, c)); + var lowerWick = (pinescript.min(o, c) - l); + var bodyR = ((barRange > 0) ? ((bodySize / barRange) * 100) : 0); + var upperR = ((barRange > 0) ? ((upperWick / barRange) * 100) : 0); + var lowerR = ((barRange > 0) ? ((lowerWick / barRange) * 100) : 0); + var isBull = (c > o); + var [pattern, patternBias] = pinescript.unpack(f_classifyCandlePattern(bodyR, upperR, lowerR, isBull), 2); + var [rangeRatio, rangeStatus] = pinescript.unpack(f_calcRangeStatus(barRange, atr), 2); + var [volRatio, volStatus] = pinescript.unpack(f_calcVolumeStatus(v, 20, offset), 2); + var delta = (buyV - sellV); + var pressure = f_calcPressureStatus(buyV, sellV); return CurrentBarAnalysis.new(barRange, bodySize, upperWick, lowerWick, bodyR, upperR, lowerR, pattern, patternBias, isBull, rangeRatio, rangeStatus, volRatio, volStatus, buyV, sellV, delta, pressure); } function f_analyzeComposite(blk1, blk2) { - let result = CompositeAnalysis.new(); + var result = CompositeAnalysis.new(); if (blk1.valid) { - let cRange = (blk1.newHigh - blk1.newLow); - let cBody = pinescript.abs((blk1.newClose - blk1.newOpen)); - let cUpper = (blk1.newHigh - pinescript.max(blk1.newOpen, blk1.newClose)); - let cLower = (pinescript.min(blk1.newOpen, blk1.newClose) - blk1.newLow); - let cBodyR = ((cRange > 0) ? ((cBody / cRange) * 100) : 0); - let cUpperR = ((cRange > 0) ? ((cUpper / cRange) * 100) : 0); - let cLowerR = ((cRange > 0) ? ((cLower / cRange) * 100) : 0); - let isBull = (blk1.newClose > blk1.newOpen); - [pattern, bias] = pinescript.unpack(f_classifyCandlePattern(cBodyR, cUpperR, cLowerR, isBull), 2); + var cRange = (blk1.newHigh - blk1.newLow); + var cBody = pinescript.abs((blk1.newClose - blk1.newOpen)); + var cUpper = (blk1.newHigh - pinescript.max(blk1.newOpen, blk1.newClose)); + var cLower = (pinescript.min(blk1.newOpen, blk1.newClose) - blk1.newLow); + var cBodyR = ((cRange > 0) ? ((cBody / cRange) * 100) : 0); + var cUpperR = ((cRange > 0) ? ((cUpper / cRange) * 100) : 0); + var cLowerR = ((cRange > 0) ? ((cLower / cRange) * 100) : 0); + var isBull = (blk1.newClose > blk1.newOpen); + var [pattern, bias] = pinescript.unpack(f_classifyCandlePattern(cBodyR, cUpperR, cLowerR, isBull), 2); result.compRange = cRange; result.compBodySize = cBody; result.compUpperWick = cUpper; @@ -2772,12 +2949,12 @@ function main() { result.compositePattern = pattern; result.compositeBias = bias; if (blk2.valid) { - let b1High = blk1.newHigh; - let b1Low = blk1.newLow; - let b2High = blk2.newHigh; - let b2Low = blk2.newLow; - let b1Body = pinescript.abs((blk1.newClose - blk1.newOpen)); - let b2Body = pinescript.abs((blk2.newClose - blk2.newOpen)); + var b1High = blk1.newHigh; + var b1Low = blk1.newLow; + var b2High = blk2.newHigh; + var b2Low = blk2.newLow; + var b1Body = pinescript.abs((blk1.newClose - blk1.newOpen)); + var b2Body = pinescript.abs((blk2.newClose - blk2.newOpen)); if (((b1High <= b2High) && (b1Low >= b2Low))) { result.blockRelation = "INSIDE"; result.blockRelationBias = "NEUTRAL"; @@ -2803,15 +2980,15 @@ function main() { return result; } function f_analyzeTrendContext(channels, quality) { - let result = TrendContext.new(); - let numChannels = pinescript.arraySize(channels); + var result = TrendContext.new(); + var numChannels = pinescript.arraySize(channels); if ((numChannels > 0)) { - let current = pinescript.arrayGet(channels, 0); + var current = pinescript.arrayGet(channels, 0); result.currentType = current.channelType; result.currentBlocks = ((current.endBlock - current.startBlock) + 1); result.currentLabel = ((current.channelType === DIR_UP) ? "UPTREND" : ((current.channelType === DIR_DOWN) ? "DOWNTREND" : "RANGE")); if ((numChannels > 1)) { - let prev = pinescript.arrayGet(channels, 1); + var prev = pinescript.arrayGet(channels, 1); result.prevType = prev.channelType; result.prevLabel = ((prev.channelType === DIR_UP) ? "UPTREND" : ((prev.channelType === DIR_DOWN) ? "DOWNTREND" : "RANGE")); if ((result.prevType !== result.currentType)) { @@ -2835,8 +3012,8 @@ function main() { result.trendChanges = 0; if ((numChannels > 1)) { for (let i = 0; i <= (numChannels - 2); i++) { - let ch1 = pinescript.arrayGet(channels, i); - let ch2 = pinescript.arrayGet(channels, (i + 1)); + var ch1 = pinescript.arrayGet(channels, i); + var ch2 = pinescript.arrayGet(channels, (i + 1)); if ((ch1.channelType !== ch2.channelType)) { result.trendChanges += 1; } @@ -2851,7 +3028,7 @@ function main() { result.qualityScore = quality; result.confidenceLevel = ((quality >= 80) ? "HIGH" : ((quality >= 60) ? "MEDIUM" : "LOW")); if ((numChannels > 0)) { - let ch = pinescript.arrayGet(channels, 0); + var ch = pinescript.arrayGet(channels, 0); result.strengthRating = ((pinescript.abs(ch.angleDeg) > 45) ? "VERY_STRONG" : ((pinescript.abs(ch.angleDeg) > 30) ? "STRONG" : ((pinescript.abs(ch.angleDeg) > 15) ? "MODERATE" : "WEAK"))); } else { result.strengthRating = "WEAK"; @@ -2860,12 +3037,12 @@ function main() { } function f_checkProximity(price, level, threshold) { if (pinescript.na(level)) { - [false, 0]; + return [false, 0]; } else { - let dist = pinescript.abs((price - level)); - let pct = ((price !== 0) ? ((dist / price) * 100) : 0); - let isNear = (pct <= threshold); - [isNear, pct]; + var dist = pinescript.abs((price - level)); + var pct = ((price !== 0) ? ((dist / price) * 100) : 0); + var isNear = (pct <= threshold); + return [isNear, pct]; } } function f_connect(key) { @@ -2878,19 +3055,19 @@ function main() { return ((relation === "ENGULFING") ? f_L("completely encompasses the previous block, indicating increased power in current direction", "önceki bloğu tamamen kapsar ve mevcut yöndeki gücün arttığını gösterir", "पिछले ब्लॉक को पूरी तरह घेरता है, वर्तमान दिशा में बढ़ी शक्ति का संकेत देता है", "تبتلع الكتلة السابقة بالكامل، مما يدل على تزايد القوة في الاتجاه الحالي") : ((relation === "INSIDE") ? f_L("is completely within the previous block range, indicating reduced volatility and potential accumulation", "tamamen önceki blok aralığında ve azalan oynaklık gösterir", "पिछले ब्लॉक के दायरे में है, कम अस्थिरता का संकेत देता है", "تقع بالكامل ضمن نطاق الكتلة السابقة، مما يدل على انخفاض التذبذب واحتمال التجميع") : ((relation === "OUTSIDE") ? f_L("exceeds the previous block range, indicating increased volatility and market strength", "önceki blok aralığını aşar ve artan oynaklık gösterir", "पिछले ब्लॉक की सीमा से अधिक है, बढ़ी अस्थिरता का संकेत देता है", "تتجاوز نطاق الكتلة السابقة، مما يدل على تزايد التذبذب وقوة السوق") : ""))); } function f_wrapNarrative(txt, maxWords, maxChars) { - let lines = pinescript.arrayNew(); - let words = pinescript.strSplit(txt, " "); - let n = pinescript.arraySize(words); + var lines = pinescript.arrayNew(); + var words = pinescript.strSplit(txt, " "); + var n = pinescript.arraySize(words); if ((n > 0)) { - let line = ""; - let wcnt = 0; + var line = ""; + var wcnt = 0; for (let i = 0; i <= (n - 1); i++) { - let w = pinescript.arrayGet(words, i); + var w = pinescript.arrayGet(words, i); if ((pinescript.strLength(w) === 0)) { continue; } - let cand = ((wcnt === 0) ? w : ((line + " ") + w)); - let overflow = ((wcnt >= maxWords) || (pinescript.strLength(cand) > maxChars)); + var cand = ((wcnt === 0) ? w : ((line + " ") + w)); + var overflow = ((wcnt >= maxWords) || (pinescript.strLength(cand) > maxChars)); if ((overflow && (wcnt > 0))) { pinescript.arrayPush(lines, line); line = w; @@ -2907,8 +3084,8 @@ function main() { return lines; } function f_buildProfessionalNarrativeParts(cBar, comp, ctx, ch, trendBuy, trendSell, trendDelta, trendDeltaPct, bodyStatus, upperPinChg, lowerPinChg, volMom, channelPos, posStatus, resistPrice, supportPrice, lvls, contradictions, hasDivergence) { - let narrative = ""; - let currentPrice = pinescript.hist(18, close, baseOff); + var narrative = ""; + var currentPrice = pinescript.hist(18, close, baseOff); narrative += (((f_connect("CURRENT_CANDLE") + " ") + f_patternDesc(cBar.pattern)) + " "); narrative += (((f_connect("WITH_RANGE") + " ") + f_rangeStatusStr(cBar.rangeStatus)) + ". "); if ((cBar.pressureStatus === "BUYING")) { @@ -2936,7 +3113,7 @@ function main() { if (((comp.blockRelation !== "NORMAL") && (comp.blockRelation !== ""))) { narrative += (((f_connect("CURRENT_BLOCK") + " ") + f_blockRelDesc(comp.blockRelation, comp.blockRelationBias)) + ". "); } - let part1 = narrative; + var part1 = narrative; narrative = ""; if (hasDivergence) { narrative += (((f_connect("HOWEVER") + ", ") + f_connect("IMPORTANT_CONTRADICTION")) + ": "); @@ -2974,7 +3151,7 @@ function main() { } else { narrative += (f_L("decreased market participation", "azalan piyasa katılımı", "घटती बाजार भागीदारी", "تراجع المشاركة في السوق") + ". "); } - let part2 = narrative; + var part2 = narrative; narrative = ""; narrative += ((f_connect("CANDLE_BODIES") + " ") + f_bodyStatusStr(bodyStatus)); if ((bodyStatus === "EXPANDING")) { @@ -3009,19 +3186,19 @@ function main() { } } } - let part3 = narrative; + var part3 = narrative; narrative = ""; - let pocUpLevel = (lvls.hasPOCUp ? lvls.pocUp : null); - let pocDnLevel = (lvls.hasPOCDn ? lvls.pocDn : null); - let coreUpLevel = (lvls.hasCoreUp ? lvls.coreUp : null); - let coreDnLevel = (lvls.hasCoreDn ? lvls.coreDn : null); - let [nearResist, resistPct] = pinescript.unpack(f_checkProximity(currentPrice, resistPrice, 2), 2); - let [nearSupport, supportPct] = pinescript.unpack(f_checkProximity(currentPrice, supportPrice, 2), 2); - let [nearPOCUp, pocUpPct] = pinescript.unpack(f_checkProximity(currentPrice, pocUpLevel, 1.5), 2); - let [nearPOCDn, pocDnPct] = pinescript.unpack(f_checkProximity(currentPrice, pocDnLevel, 1.5), 2); - let [nearCoreUp, coreUpPct] = pinescript.unpack(f_checkProximity(currentPrice, coreUpLevel, 1), 2); - let [nearCoreDn, coreDnPct] = pinescript.unpack(f_checkProximity(currentPrice, coreDnLevel, 1), 2); - let hasKeyEvent = (((((nearResist || nearSupport) || nearPOCUp) || nearPOCDn) || nearCoreUp) || nearCoreDn); + var pocUpLevel = (lvls.hasPOCUp ? lvls.pocUp : null); + var pocDnLevel = (lvls.hasPOCDn ? lvls.pocDn : null); + var coreUpLevel = (lvls.hasCoreUp ? lvls.coreUp : null); + var coreDnLevel = (lvls.hasCoreDn ? lvls.coreDn : null); + var [nearResist, resistPct] = pinescript.unpack(f_checkProximity(currentPrice, resistPrice, 2), 2); + var [nearSupport, supportPct] = pinescript.unpack(f_checkProximity(currentPrice, supportPrice, 2), 2); + var [nearPOCUp, pocUpPct] = pinescript.unpack(f_checkProximity(currentPrice, pocUpLevel, 1.5), 2); + var [nearPOCDn, pocDnPct] = pinescript.unpack(f_checkProximity(currentPrice, pocDnLevel, 1.5), 2); + var [nearCoreUp, coreUpPct] = pinescript.unpack(f_checkProximity(currentPrice, coreUpLevel, 1), 2); + var [nearCoreDn, coreDnPct] = pinescript.unpack(f_checkProximity(currentPrice, coreDnLevel, 1), 2); + var hasKeyEvent = (((((nearResist || nearSupport) || nearPOCUp) || nearPOCDn) || nearCoreUp) || nearCoreDn); if (hasKeyEvent) { narrative += (f_connect("KEY_NOTE") + ": "); if (nearResist) { @@ -3044,26 +3221,26 @@ function main() { } } else { if ((!pinescript.na(resistPrice) && !pinescript.na(supportPrice))) { - let resistDist = (resistPrice - currentPrice); - let supportDist = (currentPrice - supportPrice); + var resistDist = (resistPrice - currentPrice); + var supportDist = (currentPrice - supportPrice); narrative += (((((((f_connect("DISTANCE_TO_LEVELS") + " ") + pinescript.strToString(resistDist, "#.##")) + ", ") + f_connect("TO_SUPPORT")) + " ") + pinescript.strToString(supportDist, "#.##")) + ". "); } if ((!pinescript.na(pocUpLevel) || !pinescript.na(pocDnLevel))) { - let dUp = (!pinescript.na(pocUpLevel) ? pinescript.abs((currentPrice - pocUpLevel)) : null); - let dDn = (!pinescript.na(pocDnLevel) ? pinescript.abs((currentPrice - pocDnLevel)) : null); - let nearestPOC = (pinescript.na(dUp) ? pocDnLevel : (pinescript.na(dDn) ? pocUpLevel : ((dUp <= dDn) ? pocUpLevel : pocDnLevel))); - let [, pocPct] = pinescript.unpack(f_checkProximity(currentPrice, nearestPOC, 100), 2); + var dUp = (!pinescript.na(pocUpLevel) ? pinescript.abs((currentPrice - pocUpLevel)) : null); + var dDn = (!pinescript.na(pocDnLevel) ? pinescript.abs((currentPrice - pocDnLevel)) : null); + var nearestPOC = (pinescript.na(dUp) ? pocDnLevel : (pinescript.na(dDn) ? pocUpLevel : ((dUp <= dDn) ? pocUpLevel : pocDnLevel))); + var [, pocPct] = pinescript.unpack(f_checkProximity(currentPrice, nearestPOC, 100), 2); narrative += ((((f_L(" Nearest POC: ", " En yakın POC: ", " निकटतम POC: ", " أقرب POC: ") + pinescript.strToString(nearestPOC, format.mintick)) + " (") + pinescript.strToString(pocPct, "#.##")) + "%). "); } if ((!pinescript.na(coreUpLevel) || !pinescript.na(coreDnLevel))) { - let cdUp = (!pinescript.na(coreUpLevel) ? pinescript.abs((currentPrice - coreUpLevel)) : null); - let cdDn = (!pinescript.na(coreDnLevel) ? pinescript.abs((currentPrice - coreDnLevel)) : null); - let nearestCore = (pinescript.na(cdUp) ? coreDnLevel : (pinescript.na(cdDn) ? coreUpLevel : ((cdUp <= cdDn) ? coreUpLevel : coreDnLevel))); - let [, corePct] = pinescript.unpack(f_checkProximity(currentPrice, nearestCore, 100), 2); + var cdUp = (!pinescript.na(coreUpLevel) ? pinescript.abs((currentPrice - coreUpLevel)) : null); + var cdDn = (!pinescript.na(coreDnLevel) ? pinescript.abs((currentPrice - coreDnLevel)) : null); + var nearestCore = (pinescript.na(cdUp) ? coreDnLevel : (pinescript.na(cdDn) ? coreUpLevel : ((cdUp <= cdDn) ? coreUpLevel : coreDnLevel))); + var [, corePct] = pinescript.unpack(f_checkProximity(currentPrice, nearestCore, 100), 2); narrative += ((((f_L(" Nearest core level: ", " En yakın çekirdek seviye: ", " निकटतम कोर स्तर: ", " أقرب مستوى جوهري: ") + pinescript.strToString(nearestCore, format.mintick)) + " (") + pinescript.strToString(corePct, "#.##")) + "%). "); } } - let part4 = narrative; + var part4 = narrative; narrative = ""; narrative += (f_connect("OVERALL") + ", "); if (((contradictions > 0) || hasDivergence)) { @@ -3090,8 +3267,8 @@ function main() { } } } - let part5 = narrative; - let parts = pinescript.arrayNew(); + var part5 = narrative; + var parts = pinescript.arrayNew(); pinescript.arrayPush(parts, part1); pinescript.arrayPush(parts, part2); pinescript.arrayPush(parts, part3); @@ -3103,13 +3280,13 @@ function main() { return (useCustom ? customTF : (timeframe.isseconds ? "1S" : (timeframe.isintraday ? "1" : (timeframe.isdaily ? "5" : "60")))); } function f_ltfVol(ltf) { - let [u, d, dl] = pinescript.unpack(tvta.requestUpAndDownVolume(ltf), 3); + var [u, d, dl] = pinescript.unpack(tvta.requestUpAndDownVolume(ltf), 3); return [pinescript.abs(u), pinescript.abs(d), dl]; } function f_geoVol() { - let r = (high - low); - let b = null; - let s = null; + var r = (high - low); + var b = null; + var s = null; if (pinescript.na(volume)) { b = null; s = null; @@ -3127,24 +3304,24 @@ function main() { if (state.vBuy === undefined) state.vBuy = null; if (state.vSell === undefined) state.vSell = null; if (state.vDel === undefined) state.vDel = null; - let ltf = f_resolveLTF(i_useCustomLTF, i_customLTF); + var ltf = f_resolveLTF(i_useCustomLTF, i_customLTF); if ((i_calcMethod === "Intrabar (Precise)")) { - let [b1, s1, d1] = pinescript.unpack(f_ltfVol(ltf), 3); + var [b1, s1, d1] = pinescript.unpack(f_ltfVol(ltf), 3); state.vBuy = b1; state.vSell = s1; state.vDel = d1; } else { - let [b2, s2, d2] = pinescript.unpack(f_geoVol(), 3); + var [b2, s2, d2] = pinescript.unpack(f_geoVol(), 3); state.vBuy = b2; state.vSell = s2; state.vDel = d2; } function f_binProfilePOC(startOff, endOff, numBins) { - let blockHigh = null; - let blockLow = null; + var blockHigh = null; + var blockLow = null; for (let k = startOff; k <= endOff; k++) { - let h = pinescript.hist(19, high, k); - let l = pinescript.hist(20, low, k); + var h = pinescript.hist(19, high, k); + var l = pinescript.hist(20, low, k); if ((pinescript.na(blockHigh) || (h > blockHigh))) { blockHigh = h; } @@ -3152,34 +3329,34 @@ function main() { blockLow = l; } } - let resultPrice = null; - let resultOff = null; - let resultVol = 0; + var resultPrice = null; + var resultOff = null; + var resultVol = 0; if (((!pinescript.na(blockHigh) && !pinescript.na(blockLow)) && (blockHigh > blockLow))) { - let binSize = ((blockHigh - blockLow) / numBins); - let binVol = pinescript.arrayNew(numBins, 0); + var binSize = ((blockHigh - blockLow) / numBins); + var binVol = pinescript.arrayNew(numBins, 0); for (let k = startOff; k <= endOff; k++) { - let barH = pinescript.hist(21, high, k); - let barL = pinescript.hist(22, low, k); - let barTotalVol = (pinescript.nz(pinescript.hist(23, state.vBuy, k), 0) + pinescript.nz(pinescript.hist(24, state.vSell, k), 0)); + var barH = pinescript.hist(21, high, k); + var barL = pinescript.hist(22, low, k); + var barTotalVol = (pinescript.nz(pinescript.hist(23, state.vBuy, k), 0) + pinescript.nz(pinescript.hist(24, state.vSell, k), 0)); if (((barTotalVol > 0) && (barH > barL))) { - let firstBin = pinescript.max(0, int(pinescript.floor(((barL - blockLow) / binSize)))); - let lastBin = pinescript.min((numBins - 1), int(pinescript.floor((((barH - blockLow) - (syminfo.mintick * 0.01)) / binSize)))); + var firstBin = pinescript.max(0, int(pinescript.floor(((barL - blockLow) / binSize)))); + var lastBin = pinescript.min((numBins - 1), int(pinescript.floor((((barH - blockLow) - (syminfo.mintick * 0.01)) / binSize)))); lastBin = pinescript.max(firstBin, lastBin); - let barRange = (barH - barL); + var barRange = (barH - barL); for (let b = firstBin; b <= lastBin; b++) { - let bLow = (blockLow + (b * binSize)); - let bHigh = (bLow + binSize); - let overlap = (pinescript.min(barH, bHigh) - pinescript.max(barL, bLow)); - let proportion = ((barRange > 0) ? pinescript.max(0, (overlap / barRange)) : 0); + var bLow = (blockLow + (b * binSize)); + var bHigh = (bLow + binSize); + var overlap = (pinescript.min(barH, bHigh) - pinescript.max(barL, bLow)); + var proportion = ((barRange > 0) ? pinescript.max(0, (overlap / barRange)) : 0); pinescript.arraySet(binVol, b, (pinescript.arrayGet(binVol, b) + (barTotalVol * proportion))); } } } - let maxVol = 0; - let maxBin = 0; + var maxVol = 0; + var maxBin = 0; for (let b = 0; b <= (numBins - 1); b++) { - let bv = pinescript.arrayGet(binVol, b); + var bv = pinescript.arrayGet(binVol, b); if ((bv > maxVol)) { maxVol = bv; maxBin = b; @@ -3187,9 +3364,9 @@ function main() { } resultPrice = (blockLow + ((maxBin + 0.5) * binSize)); resultVol = maxVol; - let minDist = null; + var minDist = null; for (let k = startOff; k <= endOff; k++) { - let dist = pinescript.abs((pinescript.hist(25, hlc3, k) - resultPrice)); + var dist = pinescript.abs((pinescript.hist(25, hlc3, k) - resultPrice)); if ((pinescript.na(minDist) || (dist < minDist))) { minDist = dist; resultOff = k; @@ -3198,15 +3375,15 @@ function main() { } return [resultPrice, resultOff, resultVol]; } - let globalAvgRange = pinescript.atr(20); - let globalYZVol = f_yangZhangVolatility(20); - let histBarsDash = pinescript.max(0, ((bar_index - baseOff) + 1)); + var globalAvgRange = pinescript.atr(20); + var globalYZVol = f_yangZhangVolatility(20); + var histBarsDash = pinescript.max(0, ((bar_index - baseOff) + 1)); histBarsDash = pinescript.min(histBarsDash, 5000); - let scannableBars = 0; + var scannableBars = 0; if ((i_calcMethod === "Intrabar (Precise)")) { - let probe = pinescript.min(histBarsDash, effWindow); + var probe = pinescript.min(histBarsDash, effWindow); for (let off = baseOff; off <= ((baseOff + probe) - 1); off++) { - let ok = ((!pinescript.na(pinescript.hist(26, state.vBuy, off)) && !pinescript.na(pinescript.hist(27, state.vSell, off))) && !pinescript.na(pinescript.hist(28, state.vDel, off))); + var ok = ((!pinescript.na(pinescript.hist(26, state.vBuy, off)) && !pinescript.na(pinescript.hist(27, state.vSell, off))) && !pinescript.na(pinescript.hist(28, state.vDel, off))); if (ok) { scannableBars += 1; } else { @@ -3218,10 +3395,10 @@ function main() { } if (state.lastAnalyticsBar === undefined) state.lastAnalyticsBar = -1; if (barstate.islast) { - let maxOff = ((baseOff + effWindow) - 1); - let enoughHistory = (bar_index >= maxOff); - let isNewBarForAnalytics = (bar_index !== state.lastAnalyticsBar); - let shouldRebuildAnalytics = ((baseOff === 0) || isNewBarForAnalytics); + var maxOff = ((baseOff + effWindow) - 1); + var enoughHistory = (bar_index >= maxOff); + var isNewBarForAnalytics = (bar_index !== state.lastAnalyticsBar); + var shouldRebuildAnalytics = ((baseOff === 0) || isNewBarForAnalytics); if (shouldRebuildAnalytics) { state.lastAnalyticsBar = bar_index; } @@ -3232,8 +3409,8 @@ function main() { if (i_showGroupNums) { f_ensureLabelCount(i_groups); for (let gi = 0; gi <= (i_groups - 1); gi++) { - let endOff = ((baseOff + ((gi + 1) * groupSize)) - 1); - let lb = pinescript.arrayGet(state.lblNums, gi); + var endOff = ((baseOff + ((gi + 1) * groupSize)) - 1); + var lb = pinescript.arrayGet(state.lblNums, gi); label.set_xy(lb, (bar_index - endOff), pinescript.hist(29, high, endOff)); pinescript.labelSetText(lb, pinescript.strToString((gi + 1))); } @@ -3252,46 +3429,46 @@ function main() { f_ensureCompositeUpperWickCount(0); f_ensureCompositeLowerWickCount(0); } - let bullishCol = pinescript.color.rgb(34, 197, 94); - let bearishCol = pinescript.color.rgb(239, 68, 68); - let neutralCol = pinescript.color.rgb(156, 163, 175); + var bullishCol = pinescript.color.rgb(34, 197, 94); + var bearishCol = pinescript.color.rgb(239, 68, 68); + var neutralCol = pinescript.color.rgb(156, 163, 175); for (let gi = 0; gi <= (i_groups - 1); gi++) { if (!shouldRebuildAnalytics) { - let startOff = (baseOff + (gi * groupSize)); - let endOff = ((baseOff + ((gi + 1) * groupSize)) - 1); - let xRight = (bar_index - startOff); - let xLeft = (bar_index - endOff); - let bx = pinescript.arrayGet(state.grpBoxes, gi); + var startOff = (baseOff + (gi * groupSize)); + endOff = ((baseOff + ((gi + 1) * groupSize)) - 1); + var xRight = (bar_index - startOff); + var xLeft = (bar_index - endOff); + var bx = pinescript.arrayGet(state.grpBoxes, gi); box.set_lefttop(bx, xLeft, pinescript.hist(30, high, endOff)); box.set_rightbottom(bx, xRight, pinescript.hist(31, low, startOff)); continue; } - let startOff = (baseOff + (gi * groupSize)); - let endOff = ((baseOff + ((gi + 1) * groupSize)) - 1); - let top = null; - let bot = null; - let sumBuyBlock = 0; - let sumSellBlock = 0; - let sumUpperPin = 0; - let sumLowerPin = 0; - let sumBody = 0; - let compOpen = null; - let compHigh = null; - let compLow = null; - let compClose = null; + startOff = (baseOff + (gi * groupSize)); + endOff = ((baseOff + ((gi + 1) * groupSize)) - 1); + var top = null; + var bot = null; + var sumBuyBlock = 0; + var sumSellBlock = 0; + var sumUpperPin = 0; + var sumLowerPin = 0; + var sumBody = 0; + var compOpen = null; + var compHigh = null; + var compLow = null; + var compClose = null; for (let k = startOff; k <= endOff; k++) { - let h = pinescript.hist(32, high, k); - let l = pinescript.hist(33, low, k); - let o = pinescript.hist(34, open, k); - let c = pinescript.hist(35, close, k); + var h = pinescript.hist(32, high, k); + var l = pinescript.hist(33, low, k); + var o = pinescript.hist(34, open, k); + var c = pinescript.hist(35, close, k); if ((pinescript.na(top) || (h > top))) { top = h; } if ((pinescript.na(bot) || (l < bot))) { bot = l; } - let bVol = pinescript.hist(36, state.vBuy, k); - let sVol = pinescript.hist(37, state.vSell, k); + var bVol = pinescript.hist(36, state.vBuy, k); + var sVol = pinescript.hist(37, state.vSell, k); if ((!pinescript.na(bVol) && !pinescript.na(sVol))) { sumBuyBlock += bVol; sumSellBlock += sVol; @@ -3312,42 +3489,42 @@ function main() { compLow = l; } } - let xRight = (bar_index - startOff); - let xLeft = (bar_index - endOff); - let xCenter = int(pinescript.round(((xLeft + xRight) / 2))); - let bx = pinescript.arrayGet(state.grpBoxes, gi); + xRight = (bar_index - startOff); + xLeft = (bar_index - endOff); + var xCenter = int(pinescript.round(((xLeft + xRight) / 2))); + bx = pinescript.arrayGet(state.grpBoxes, gi); box.set_lefttop(bx, xLeft, top); box.set_rightbottom(bx, xRight, bot); box.set_bgcolor(bx, pinescript.color.new(i_boxColor, i_boxTransp)); box.set_border_color(bx, pinescript.color.new(pinescript.color.black, 100)); box.set_border_width(bx, 0); - let n = groupSize; - let blockDelta = (sumBuyBlock - sumSellBlock); - let blockHeight = (top - bot); - let blockArea = (blockHeight * n); - let ba = BlockAnalytics.new(true, sumBuyBlock, sumSellBlock, blockDelta, blockHeight, blockArea, ((n > 0) ? (sumUpperPin / n) : null), ((n > 0) ? (sumLowerPin / n) : null), ((n > 0) ? (sumBody / n) : null), compOpen, compHigh, compLow, compClose, xLeft, xRight, xCenter, bot); + var n = groupSize; + var blockDelta = (sumBuyBlock - sumSellBlock); + var blockHeight = (top - bot); + var blockArea = (blockHeight * n); + var ba = BlockAnalytics.new(true, sumBuyBlock, sumSellBlock, blockDelta, blockHeight, blockArea, ((n > 0) ? (sumUpperPin / n) : null), ((n > 0) ? (sumLowerPin / n) : null), ((n > 0) ? (sumBody / n) : null), compOpen, compHigh, compLow, compClose, xLeft, xRight, xCenter, bot); pinescript.arraySet(state.analyticsData, gi, ba); if (((((i_showCompositeCandle && !pinescript.na(compOpen)) && !pinescript.na(compHigh)) && !pinescript.na(compLow)) && !pinescript.na(compClose))) { - let candleCol = ((blockDelta > 0) ? bullishCol : ((blockDelta < 0) ? bearishCol : neutralCol)); - let wickCol = pinescript.color.new(candleCol, (i_compositeCandleTransp - 15)); - let bodyCol = pinescript.color.new(candleCol, i_compositeCandleTransp); - let borderCol = pinescript.color.new(candleCol, (i_compositeCandleTransp - 25)); - let bodyTop = pinescript.max(compOpen, compClose); - let bodyBot = pinescript.min(compOpen, compClose); - let bodyLeft = (xCenter - 1); - let bodyRight = (xCenter + 1); - let bodyBox = pinescript.arrayGet(state.compositeBodies, gi); + var candleCol = ((blockDelta > 0) ? bullishCol : ((blockDelta < 0) ? bearishCol : neutralCol)); + var wickCol = pinescript.color.new(candleCol, (i_compositeCandleTransp - 15)); + var bodyCol = pinescript.color.new(candleCol, i_compositeCandleTransp); + var borderCol = pinescript.color.new(candleCol, (i_compositeCandleTransp - 25)); + var bodyTop = pinescript.max(compOpen, compClose); + var bodyBot = pinescript.min(compOpen, compClose); + var bodyLeft = (xCenter - 1); + var bodyRight = (xCenter + 1); + var bodyBox = pinescript.arrayGet(state.compositeBodies, gi); box.set_lefttop(bodyBox, bodyLeft, bodyTop); box.set_rightbottom(bodyBox, bodyRight, bodyBot); box.set_bgcolor(bodyBox, bodyCol); box.set_border_color(bodyBox, borderCol); box.set_border_width(bodyBox, 1); - let upperWick = pinescript.arrayGet(state.compositeUpperWicks, gi); + var upperWick = pinescript.arrayGet(state.compositeUpperWicks, gi); pinescript.lineSetXY(upperWick, xCenter, bodyTop); pinescript.lineSetXY(upperWick, xCenter, compHigh); line.set_color(upperWick, wickCol); line.set_width(upperWick, 1); - let lowerWick = pinescript.arrayGet(state.compositeLowerWicks, gi); + var lowerWick = pinescript.arrayGet(state.compositeLowerWicks, gi); pinescript.lineSetXY(lowerWick, xCenter, bodyBot); pinescript.lineSetXY(lowerWick, xCenter, compLow); line.set_color(lowerWick, wickCol); @@ -3373,13 +3550,13 @@ function main() { pinescript.arrayPop(state.g_binPocVol); } for (let _gi = 0; _gi <= (i_groups - 1); _gi++) { - let _sOff = (baseOff + (_gi * groupSize)); - let _eOff = ((baseOff + ((_gi + 1) * groupSize)) - 1); - let _blockHigh = null; - let _blockLow = null; + var _sOff = (baseOff + (_gi * groupSize)); + var _eOff = ((baseOff + ((_gi + 1) * groupSize)) - 1); + var _blockHigh = null; + var _blockLow = null; for (let k = _sOff; k <= _eOff; k++) { - let _h = pinescript.hist(38, high, k); - let _l = pinescript.hist(39, low, k); + var _h = pinescript.hist(38, high, k); + var _l = pinescript.hist(39, low, k); if ((pinescript.na(_blockHigh) || (_h > _blockHigh))) { _blockHigh = _h; } @@ -3387,34 +3564,34 @@ function main() { _blockLow = _l; } } - let _bp = null; - let _bo = null; - let _bv = 0; + var _bp = null; + var _bo = null; + var _bv = 0; if (((!pinescript.na(_blockHigh) && !pinescript.na(_blockLow)) && (_blockHigh > _blockLow))) { - let _binSize = ((_blockHigh - _blockLow) / i_pocBinCount); - let _binVol = pinescript.arrayNew(i_pocBinCount, 0); + var _binSize = ((_blockHigh - _blockLow) / i_pocBinCount); + var _binVol = pinescript.arrayNew(i_pocBinCount, 0); for (let k = _sOff; k <= _eOff; k++) { - let _barH = pinescript.hist(40, high, k); - let _barL = pinescript.hist(41, low, k); - let _barTotalVol = (pinescript.nz(pinescript.hist(42, state.vBuy, k), 0) + pinescript.nz(pinescript.hist(43, state.vSell, k), 0)); + var _barH = pinescript.hist(40, high, k); + var _barL = pinescript.hist(41, low, k); + var _barTotalVol = (pinescript.nz(pinescript.hist(42, state.vBuy, k), 0) + pinescript.nz(pinescript.hist(43, state.vSell, k), 0)); if (((_barTotalVol > 0) && (_barH > _barL))) { - let _firstBin = pinescript.max(0, int(pinescript.floor(((_barL - _blockLow) / _binSize)))); - let _lastBin = pinescript.min((i_pocBinCount - 1), int(pinescript.floor((((_barH - _blockLow) - (syminfo.mintick * 0.01)) / _binSize)))); + var _firstBin = pinescript.max(0, int(pinescript.floor(((_barL - _blockLow) / _binSize)))); + var _lastBin = pinescript.min((i_pocBinCount - 1), int(pinescript.floor((((_barH - _blockLow) - (syminfo.mintick * 0.01)) / _binSize)))); _lastBin = pinescript.max(_firstBin, _lastBin); - let _barRange = (_barH - _barL); + var _barRange = (_barH - _barL); for (let b = _firstBin; b <= _lastBin; b++) { - let _bLow = (_blockLow + (b * _binSize)); - let _bHigh = (_bLow + _binSize); - let _overlap = (pinescript.min(_barH, _bHigh) - pinescript.max(_barL, _bLow)); - let _proportion = ((_barRange > 0) ? pinescript.max(0, (_overlap / _barRange)) : 0); + var _bLow = (_blockLow + (b * _binSize)); + var _bHigh = (_bLow + _binSize); + var _overlap = (pinescript.min(_barH, _bHigh) - pinescript.max(_barL, _bLow)); + var _proportion = ((_barRange > 0) ? pinescript.max(0, (_overlap / _barRange)) : 0); pinescript.arraySet(_binVol, b, (pinescript.arrayGet(_binVol, b) + (_barTotalVol * _proportion))); } } } - let _maxVol = 0; - let _maxBin = 0; + var _maxVol = 0; + var _maxBin = 0; for (let b = 0; b <= (i_pocBinCount - 1); b++) { - let _bvol = pinescript.arrayGet(_binVol, b); + var _bvol = pinescript.arrayGet(_binVol, b); if ((_bvol > _maxVol)) { _maxVol = _bvol; _maxBin = b; @@ -3422,9 +3599,9 @@ function main() { } _bp = (_blockLow + ((_maxBin + 0.5) * _binSize)); _bv = _maxVol; - let _minDist = null; + var _minDist = null; for (let k = _sOff; k <= _eOff; k++) { - let _dist = pinescript.abs((pinescript.hist(44, hlc3, k) - _bp)); + var _dist = pinescript.abs((pinescript.hist(44, hlc3, k) - _bp)); if ((pinescript.na(_minDist) || (_dist < _minDist))) { _minDist = _dist; _bo = k; @@ -3437,14 +3614,14 @@ function main() { } } if (barstate.islast) { - let enoughHistoryCore = (bar_index >= ((baseOff + effWindow) - 1)); - let refX = (bar_index - baseOff); - let projLen = (i_coreProjectFwd ? groupSize : 0); - let x2 = (refX + projLen); - let refPrice = pinescript.hist(45, close, baseOff); - let hiBasis = pinescript.hist(46, high, baseOff); - let loBasis = pinescript.hist(47, low, baseOff); - let minKeepSrc = (bar_index - ((effWindow + groupSize) + 25)); + var enoughHistoryCore = (bar_index >= ((baseOff + effWindow) - 1)); + var refX = (bar_index - baseOff); + var projLen = (i_coreProjectFwd ? groupSize : 0); + var x2 = (refX + projLen); + var refPrice = pinescript.hist(45, close, baseOff); + var hiBasis = pinescript.hist(46, high, baseOff); + var loBasis = pinescript.hist(47, low, baseOff); + var minKeepSrc = (bar_index - ((effWindow + groupSize) + 25)); f_pruneConsumed(minKeepSrc); state.lnCoreUp = f_ensureLine(state.lnCoreUp, _coreBuyCol, line.style_solid); state.lnCoreDn = f_ensureLine(state.lnCoreDn, _coreSellCol, line.style_solid); @@ -3461,8 +3638,8 @@ function main() { state.g_pocDnP = null; } else { f_ensureCoreCount(i_groups); - let validBarsCore = ((i_calcMethod === "Intrabar (Precise)") ? scannableBars : pinescript.min(histBarsDash, effWindow)); - let validGroupsCore = ((groupSize > 0) ? int(pinescript.floor((float(validBarsCore) / groupSize))) : 0); + var validBarsCore = ((i_calcMethod === "Intrabar (Precise)") ? scannableBars : pinescript.min(histBarsDash, effWindow)); + var validGroupsCore = ((groupSize > 0) ? int(pinescript.floor((float(validBarsCore) / groupSize))) : 0); validGroupsCore = pinescript.min(validGroupsCore, i_groups); if (!i_coreEnable) { for (let gi = 0; gi <= (i_groups - 1); gi++) { @@ -3470,31 +3647,31 @@ function main() { } } else { for (let gi = 0; gi <= (i_groups - 1); gi++) { - let gActive = (gi < validGroupsCore); - let startOff = (baseOff + (gi * groupSize)); - let endOff = ((baseOff + ((gi + 1) * groupSize)) - 1); - let _bpP = pinescript.arrayGet(state.g_binPocPrice, gi); - let _bpOff = pinescript.arrayGet(state.g_binPocOff, gi); - let _bpV = pinescript.arrayGet(state.g_binPocVol, gi); + var gActive = (gi < validGroupsCore); + startOff = (baseOff + (gi * groupSize)); + endOff = ((baseOff + ((gi + 1) * groupSize)) - 1); + var _bpP = pinescript.arrayGet(state.g_binPocPrice, gi); + var _bpOff = pinescript.arrayGet(state.g_binPocOff, gi); + var _bpV = pinescript.arrayGet(state.g_binPocVol, gi); if (!gActive) { pinescript.arraySet(state.coreBoxes, gi, f_coreEmpty()); } else { - let gValid = true; - let sumB = 0; - let sumS = 0; - let maxB = -1; - let maxBOff = null; - let minB = null; - let minBOff = null; - let maxS = -1; - let maxSOff = null; - let minS = null; - let minSOff = null; - let pocV = -1; - let pocOff = null; + var gValid = true; + var sumB = 0; + var sumS = 0; + var maxB = -1; + var maxBOff = null; + var minB = null; + var minBOff = null; + var maxS = -1; + var maxSOff = null; + var minS = null; + var minSOff = null; + var pocV = -1; + var pocOff = null; for (let k = startOff; k <= endOff; k++) { - let b = pinescript.hist(48, state.vBuy, k); - let s = pinescript.hist(49, state.vSell, k); + var b = pinescript.hist(48, state.vBuy, k); + var s = pinescript.hist(49, state.vSell, k); if ((pinescript.na(b) || pinescript.na(s))) { gValid = false; } else { @@ -3516,7 +3693,7 @@ function main() { minS = s; minSOff = k; } - let tv = (b + s); + var tv = (b + s); if ((tv > pocV)) { pocV = tv; pocOff = k; @@ -3526,31 +3703,31 @@ function main() { if ((((((!gValid || pinescript.na(maxBOff)) || pinescript.na(maxSOff)) || pinescript.na(minBOff)) || pinescript.na(minSOff)) || pinescript.na(pocOff))) { pinescript.arraySet(state.coreBoxes, gi, f_coreEmpty()); } else { - let finalPocPrice = pinescript.hist(50, hlc3, pocOff); - let finalPocOff = pocOff; - let finalPocVol = pocV; + var finalPocPrice = pinescript.hist(50, hlc3, pocOff); + var finalPocOff = pocOff; + var finalPocVol = pocV; if ((i_pocBinProfile && !pinescript.na(_bpP))) { finalPocPrice = _bpP; finalPocOff = pinescript.nz(_bpOff, pocOff); finalPocVol = _bpV; } - let bc = BoxCore.new(true, sumB, sumS, (sumB - sumS), finalPocPrice, finalPocOff, finalPocVol, maxB, maxBOff, pinescript.hist(51, high, maxBOff), maxS, maxSOff, pinescript.hist(52, low, maxSOff), minB, minBOff, pinescript.hist(53, high, minBOff), minS, minSOff, pinescript.hist(54, low, minSOff)); + var bc = BoxCore.new(true, sumB, sumS, (sumB - sumS), finalPocPrice, finalPocOff, finalPocVol, maxB, maxBOff, pinescript.hist(51, high, maxBOff), maxS, maxSOff, pinescript.hist(52, low, maxSOff), minB, minBOff, pinescript.hist(53, high, minBOff), minS, minSOff, pinescript.hist(54, low, minSOff)); pinescript.arraySet(state.coreBoxes, gi, bc); } } } } - let showCoreNearest = ((i_coreDrawLines && i_coreNearestOnly) && (i_coreShowMax || i_coreShowMin)); - let extUpP = null; - let extUpSrc = null; - let extUpTyp = null; - let extUpBox = null; - let extDnP = null; - let extDnSrc = null; - let extDnTyp = null; - let extDnBox = null; + var showCoreNearest = ((i_coreDrawLines && i_coreNearestOnly) && (i_coreShowMax || i_coreShowMin)); + var extUpP = null; + var extUpSrc = null; + var extUpTyp = null; + var extUpBox = null; + var extDnP = null; + var extDnSrc = null; + var extDnTyp = null; + var extDnBox = null; if ((showCoreNearest && i_coreEnable)) { - let [_eUpP, _eUpSrc, _eUpTyp, _eUpBox, _eDnP, _eDnSrc, _eDnTyp, _eDnBox] = pinescript.unpack(f_pickNearestExt(refPrice), 8); + var [_eUpP, _eUpSrc, _eUpTyp, _eUpBox, _eDnP, _eDnSrc, _eDnTyp, _eDnBox] = pinescript.unpack(f_pickNearestExt(refPrice), 8); extUpP = _eUpP; extUpSrc = _eUpSrc; extUpTyp = _eUpTyp; @@ -3560,15 +3737,15 @@ function main() { extDnTyp = _eDnTyp; extDnBox = _eDnBox; } - let showPOCNearest = ((i_pocEnable && i_pocShowNearest) && i_coreEnable); - let pocUpP = null; - let pocUpSrc = null; - let pocUpBox = null; - let pocDnP = null; - let pocDnSrc = null; - let pocDnBox = null; + var showPOCNearest = ((i_pocEnable && i_pocShowNearest) && i_coreEnable); + var pocUpP = null; + var pocUpSrc = null; + var pocUpBox = null; + var pocDnP = null; + var pocDnSrc = null; + var pocDnBox = null; if (showPOCNearest) { - let [_pUpP, _pUpSrc, _pUpBox, _pDnP, _pDnSrc, _pDnBox] = pinescript.unpack(f_pickNearestPOC(refPrice), 6); + var [_pUpP, _pUpSrc, _pUpBox, _pDnP, _pDnSrc, _pDnBox] = pinescript.unpack(f_pickNearestPOC(refPrice), 6); pocUpP = _pUpP; pocUpSrc = _pUpSrc; pocUpBox = _pUpBox; @@ -3584,7 +3761,7 @@ function main() { pocDnBox = null; } for (let pass = 0; pass <= 1; pass++) { - let changed = false; + var changed = false; if ((((showCoreNearest && !pinescript.na(extUpP)) && (loBasis <= extUpP)) && (extUpP <= hiBasis))) { f_markConsumed(extUpSrc, extUpTyp, extUpP); (changed = true); @@ -3603,7 +3780,7 @@ function main() { } if (changed) { if (showCoreNearest) { - let [_extUpP, _extUpSrc, _extUpTyp, _extUpBox, _extDnP, _extDnSrc, _extDnTyp, _extDnBox] = pinescript.unpack(f_pickNearestExt(refPrice), 8); + var [_extUpP, _extUpSrc, _extUpTyp, _extUpBox, _extDnP, _extDnSrc, _extDnTyp, _extDnBox] = pinescript.unpack(f_pickNearestExt(refPrice), 8); extUpP = _extUpP; extUpSrc = _extUpSrc; extUpTyp = _extUpTyp; @@ -3614,7 +3791,7 @@ function main() { extDnBox = _extDnBox; } if (showPOCNearest) { - let [_pocUpP, _pocUpSrc, _pocUpBox, _pocDnP, _pocDnSrc, _pocDnBox] = pinescript.unpack(f_pickNearestPOC(refPrice), 6); + var [_pocUpP, _pocUpSrc, _pocUpBox, _pocDnP, _pocDnSrc, _pocDnBox] = pinescript.unpack(f_pickNearestPOC(refPrice), 6); pocUpP = _pocUpP; pocUpSrc = _pocUpSrc; pocUpBox = _pocUpBox; @@ -3663,36 +3840,36 @@ function main() { } if (state.lastTrendCalcBar === undefined) state.lastTrendCalcBar = -1; if (barstate.islast) { - let isNewBar = (bar_index !== state.lastTrendCalcBar); - let shouldUpdateTrends = ((baseOff === 0) || isNewBar); + var isNewBar = (bar_index !== state.lastTrendCalcBar); + var shouldUpdateTrends = ((baseOff === 0) || isNewBar); if (shouldUpdateTrends) { f_clearAllChannels(); state.lastTrendCalcBar = bar_index; if ((i_trendEnable && (pinescript.arraySize(state.analyticsData) >= i_trendMinBlocks))) { f_detectTrendChannels(i_rangeAngleThreshold, globalYZVol); - let projLen = (i_trendProject ? (groupSize + 2) : 0); + projLen = (i_trendProject ? (groupSize + 2) : 0); f_drawAllChannels(projLen, i_trendUpColor, i_trendDnColor, i_trendRangeColor, i_trendLineWidth, i_trendLineStyle, i_showRangeSegments); if ((pinescript.arraySize(state.trendChannels) > 0)) { - let latestCh = pinescript.arrayGet(state.trendChannels, 0); - let upperBarSpan = (latestCh.upperX2 - latestCh.upperX1); - let lowerBarSpan = (latestCh.lowerX2 - latestCh.lowerX1); - let upperSlope = ((upperBarSpan > 0) ? ((latestCh.upperY2 - latestCh.upperY1) / upperBarSpan) : 0); - let lowerSlope = ((lowerBarSpan > 0) ? ((latestCh.lowerY2 - latestCh.lowerY1) / lowerBarSpan) : 0); - let upperX2Proj = (latestCh.upperX2 + projLen); - let lowerX2Proj = (latestCh.lowerX2 + projLen); - let upperY2Proj = (latestCh.upperY2 + (upperSlope * projLen)); - let lowerY2Proj = (latestCh.lowerY2 + (lowerSlope * projLen)); - let upperAngleDeg = (((upperBarSpan > 0) && (globalYZVol > 0)) ? pinescript.todegrees(pinescript.atan(((upperSlope * pinescript.sqrt(float(upperBarSpan))) / globalYZVol))) : 0); - let lowerAngleDeg = (((lowerBarSpan > 0) && (globalYZVol > 0)) ? pinescript.todegrees(pinescript.atan(((lowerSlope * pinescript.sqrt(float(lowerBarSpan))) / globalYZVol))) : 0); - let currentPrice = pinescript.hist(55, close, baseOff); - let resistDiff = (upperY2Proj - currentPrice); - let supportDiff = (currentPrice - lowerY2Proj); - let resistPct = ((currentPrice !== 0) ? ((resistDiff / currentPrice) * 100) : 0); - let supportPct = ((currentPrice !== 0) ? ((supportDiff / currentPrice) * 100) : 0); - let chCol = ((latestCh.channelType === DIR_UP) ? i_trendUpColor : ((latestCh.channelType === DIR_DOWN) ? i_trendDnColor : i_trendRangeColor)); - let blockRange = ((("Blk " + pinescript.strToString(latestCh.startBlock)) + "-") + pinescript.strToString(latestCh.endBlock)); - let upperText = (((((((((("▲ Upper | " + blockRange) + " | ") + pinescript.strToString(upperY2Proj, format.mintick)) + " (+") + pinescript.strToString(resistDiff, "#.##")) + " | +") + pinescript.strToString(resistPct, "#.##")) + "%) | ") + pinescript.strToString(upperAngleDeg, "#.#")) + "°"); - let lowerText = (((((((((("▼ Lower | " + blockRange) + " | ") + pinescript.strToString(lowerY2Proj, format.mintick)) + " (-") + pinescript.strToString(supportDiff, "#.##")) + " | -") + pinescript.strToString(supportPct, "#.##")) + "%) | ") + pinescript.strToString(lowerAngleDeg, "#.#")) + "°"); + var latestCh = pinescript.arrayGet(state.trendChannels, 0); + var upperBarSpan = (latestCh.upperX2 - latestCh.upperX1); + var lowerBarSpan = (latestCh.lowerX2 - latestCh.lowerX1); + var upperSlope = ((upperBarSpan > 0) ? ((latestCh.upperY2 - latestCh.upperY1) / upperBarSpan) : 0); + var lowerSlope = ((lowerBarSpan > 0) ? ((latestCh.lowerY2 - latestCh.lowerY1) / lowerBarSpan) : 0); + var upperX2Proj = (latestCh.upperX2 + projLen); + var lowerX2Proj = (latestCh.lowerX2 + projLen); + var upperY2Proj = (latestCh.upperY2 + (upperSlope * projLen)); + var lowerY2Proj = (latestCh.lowerY2 + (lowerSlope * projLen)); + var upperAngleDeg = (((upperBarSpan > 0) && (globalYZVol > 0)) ? pinescript.todegrees(pinescript.atan(((upperSlope * pinescript.sqrt(float(upperBarSpan))) / globalYZVol))) : 0); + var lowerAngleDeg = (((lowerBarSpan > 0) && (globalYZVol > 0)) ? pinescript.todegrees(pinescript.atan(((lowerSlope * pinescript.sqrt(float(lowerBarSpan))) / globalYZVol))) : 0); + var currentPrice = pinescript.hist(55, close, baseOff); + var resistDiff = (upperY2Proj - currentPrice); + var supportDiff = (currentPrice - lowerY2Proj); + var resistPct = ((currentPrice !== 0) ? ((resistDiff / currentPrice) * 100) : 0); + var supportPct = ((currentPrice !== 0) ? ((supportDiff / currentPrice) * 100) : 0); + var chCol = ((latestCh.channelType === DIR_UP) ? i_trendUpColor : ((latestCh.channelType === DIR_DOWN) ? i_trendDnColor : i_trendRangeColor)); + var blockRange = ((("Blk " + pinescript.strToString(latestCh.startBlock)) + "-") + pinescript.strToString(latestCh.endBlock)); + var upperText = (((((((((("▲ Upper | " + blockRange) + " | ") + pinescript.strToString(upperY2Proj, format.mintick)) + " (+") + pinescript.strToString(resistDiff, "#.##")) + " | +") + pinescript.strToString(resistPct, "#.##")) + "%) | ") + pinescript.strToString(upperAngleDeg, "#.#")) + "°"); + var lowerText = (((((((((("▼ Lower | " + blockRange) + " | ") + pinescript.strToString(lowerY2Proj, format.mintick)) + " (-") + pinescript.strToString(supportDiff, "#.##")) + " | -") + pinescript.strToString(supportPct, "#.##")) + "%) | ") + pinescript.strToString(lowerAngleDeg, "#.#")) + "°"); if (pinescript.na(state.lblTrendResist)) { state.lblTrendResist = pinescript.labelNew(upperX2Proj, upperY2Proj, upperText, ({ style: label.style_label_left, size: GLOBAL_TEXT_SIZE, textcolor: chCol, color: pinescript.color.new(pinescript.color.black, 85), tooltip: "Upper channel line: Block range | Price | Distance | Angle" })); } else { @@ -3726,14 +3903,14 @@ function main() { } } } - let maxBoxesByHist = ((groupSize > 0) ? int(pinescript.floor((float(pinescript.min(histBarsDash, effWindow)) / groupSize))) : 0); - let maxBoxesByScan = ((groupSize > 0) ? int(pinescript.floor((float(scannableBars) / groupSize))) : 0); - let maxBoxesAllowedDash = pinescript.max(0, pinescript.min(i_groups, maxBoxesByHist)); + var maxBoxesByHist = ((groupSize > 0) ? int(pinescript.floor((float(pinescript.min(histBarsDash, effWindow)) / groupSize))) : 0); + var maxBoxesByScan = ((groupSize > 0) ? int(pinescript.floor((float(scannableBars) / groupSize))) : 0); + var maxBoxesAllowedDash = pinescript.max(0, pinescript.min(i_groups, maxBoxesByHist)); if ((i_calcMethod === "Intrabar (Precise)")) { maxBoxesAllowedDash = pinescript.min(maxBoxesAllowedDash, maxBoxesByScan); } - let boxesEff = pinescript.max(0, pinescript.min(i_globalPeriodBoxes, maxBoxesAllowedDash)); - let globalPeriodBars = (boxesEff * groupSize); + var boxesEff = pinescript.max(0, pinescript.min(i_globalPeriodBoxes, maxBoxesAllowedDash)); + var globalPeriodBars = (boxesEff * groupSize); function f_fmtVol(v) { return (pinescript.na(v) ? "-" : pinescript.strToString(v, format.volume)); } @@ -3741,8 +3918,8 @@ function main() { return (pinescript.na(v) ? "-" : pinescript.strToString(v, format.mintick)); } if (state.dashT === undefined) state.dashT = null; - let DASH_COLS = 4; - let DASH_ROWS = 50; + var DASH_COLS = 4; + var DASH_ROWS = 50; if (i_showDash) { if (pinescript.na(state.dashT)) { state.dashT = pinescript.table.new(i_dashPos, DASH_COLS, DASH_ROWS, ({ frame_color: pinescript.color.new(pinescript.color.white, 85), frame_width: 1, border_color: pinescript.color.new(pinescript.color.white, 92), border_width: 1 })); @@ -3755,31 +3932,31 @@ function main() { } if (((i_showDash && !pinescript.na(state.dashT)) && barstate.islast)) { clear(pinescript.table, state.dashT, 0, 0, (DASH_COLS - 1), (DASH_ROWS - 1)); - let latestTrend = ((pinescript.arraySize(state.trendChannels) > 0) ? pinescript.arrayGet(state.trendChannels, 0) : null); - let trendValid = (!pinescript.na(latestTrend) && i_trendEnable); - let trendBuy = 0; - let trendSell = 0; - let sumUpperPin = 0; - let sumLowerPin = 0; - let sumBody = 0; - let firstBlockUpperPin = null; - let lastBlockUpperPin = null; - let firstBlockLowerPin = null; - let lastBlockLowerPin = null; - let firstBlockBody = null; - let lastBlockBody = null; - let firstBlockVol = null; - let lastBlockVol = null; - let firstBlockDelta = null; - let lastBlockDelta = null; - let trendBlocks = 0; + var latestTrend = ((pinescript.arraySize(state.trendChannels) > 0) ? pinescript.arrayGet(state.trendChannels, 0) : null); + var trendValid = (!pinescript.na(latestTrend) && i_trendEnable); + var trendBuy = 0; + var trendSell = 0; + sumUpperPin = 0; + sumLowerPin = 0; + sumBody = 0; + var firstBlockUpperPin = null; + var lastBlockUpperPin = null; + var firstBlockLowerPin = null; + var lastBlockLowerPin = null; + var firstBlockBody = null; + var lastBlockBody = null; + var firstBlockVol = null; + var lastBlockVol = null; + var firstBlockDelta = null; + var lastBlockDelta = null; + var trendBlocks = 0; if (trendValid) { - let trendStart = (latestTrend.startBlock - 1); - let trendEnd = (latestTrend.endBlock - 1); + var trendStart = (latestTrend.startBlock - 1); + var trendEnd = (latestTrend.endBlock - 1); trendBlocks = ((trendEnd - trendStart) + 1); for (let i = trendStart; i <= trendEnd; i++) { if ((i < pinescript.arraySize(state.analyticsData))) { - let ba = pinescript.arrayGet(state.analyticsData, i); + ba = pinescript.arrayGet(state.analyticsData, i); if (ba.valid) { trendBuy += ba.totalBuy; trendSell += ba.totalSell; @@ -3804,53 +3981,53 @@ function main() { } } } - let trendDelta = (trendBuy - trendSell); - let trendDeltaPct = (((trendBuy + trendSell) > 0) ? ((trendDelta / (trendBuy + trendSell)) * 100) : 0); - let avgUpperPin = ((trendBlocks > 0) ? (sumUpperPin / trendBlocks) : null); - let avgLowerPin = ((trendBlocks > 0) ? (sumLowerPin / trendBlocks) : null); - let avgBody = ((trendBlocks > 0) ? (sumBody / trendBlocks) : null); - let upperPinChg = (((!pinescript.na(firstBlockUpperPin) && !pinescript.na(lastBlockUpperPin)) && (firstBlockUpperPin !== 0)) ? (((lastBlockUpperPin - firstBlockUpperPin) / firstBlockUpperPin) * 100) : 0); - let lowerPinChg = (((!pinescript.na(firstBlockLowerPin) && !pinescript.na(lastBlockLowerPin)) && (firstBlockLowerPin !== 0)) ? (((lastBlockLowerPin - firstBlockLowerPin) / firstBlockLowerPin) * 100) : 0); - let bodyStatus = ((!pinescript.na(firstBlockBody) && !pinescript.na(lastBlockBody)) ? ((lastBlockBody > firstBlockBody) ? "EXPANDING" : ((lastBlockBody < firstBlockBody) ? "CONTRACTING" : "STABLE")) : "-"); - let volMom = ((!pinescript.na(firstBlockVol) && !pinescript.na(lastBlockVol)) ? ((lastBlockVol > firstBlockVol) ? "INCREASING ▲" : "DECREASING ▼") : "-"); - let deltaDir = ((!pinescript.na(firstBlockDelta) && !pinescript.na(lastBlockDelta)) ? ((lastBlockDelta > firstBlockDelta) ? "POSITIVE ▲" : "NEGATIVE ▼") : "-"); - let pinRatio = (((!pinescript.na(avgUpperPin) && !pinescript.na(avgLowerPin)) && (avgLowerPin !== 0)) ? (avgUpperPin / avgLowerPin) : 1); - let control = ((trendDelta > 0) ? "BUYERS" : ((trendDelta < 0) ? "SELLERS" : "BALANCED")); - let currentPrice = pinescript.hist(56, close, baseOff); - let projLenCh = ((trendValid && i_trendProject) ? (groupSize + 2) : 0); - let upperChPrice = null; - let lowerChPrice = null; + var trendDelta = (trendBuy - trendSell); + var trendDeltaPct = (((trendBuy + trendSell) > 0) ? ((trendDelta / (trendBuy + trendSell)) * 100) : 0); + var avgUpperPin = ((trendBlocks > 0) ? (sumUpperPin / trendBlocks) : null); + var avgLowerPin = ((trendBlocks > 0) ? (sumLowerPin / trendBlocks) : null); + var avgBody = ((trendBlocks > 0) ? (sumBody / trendBlocks) : null); + var upperPinChg = (((!pinescript.na(firstBlockUpperPin) && !pinescript.na(lastBlockUpperPin)) && (firstBlockUpperPin !== 0)) ? (((lastBlockUpperPin - firstBlockUpperPin) / firstBlockUpperPin) * 100) : 0); + var lowerPinChg = (((!pinescript.na(firstBlockLowerPin) && !pinescript.na(lastBlockLowerPin)) && (firstBlockLowerPin !== 0)) ? (((lastBlockLowerPin - firstBlockLowerPin) / firstBlockLowerPin) * 100) : 0); + var bodyStatus = ((!pinescript.na(firstBlockBody) && !pinescript.na(lastBlockBody)) ? ((lastBlockBody > firstBlockBody) ? "EXPANDING" : ((lastBlockBody < firstBlockBody) ? "CONTRACTING" : "STABLE")) : "-"); + var volMom = ((!pinescript.na(firstBlockVol) && !pinescript.na(lastBlockVol)) ? ((lastBlockVol > firstBlockVol) ? "INCREASING ▲" : "DECREASING ▼") : "-"); + var deltaDir = ((!pinescript.na(firstBlockDelta) && !pinescript.na(lastBlockDelta)) ? ((lastBlockDelta > firstBlockDelta) ? "POSITIVE ▲" : "NEGATIVE ▼") : "-"); + var pinRatio = (((!pinescript.na(avgUpperPin) && !pinescript.na(avgLowerPin)) && (avgLowerPin !== 0)) ? (avgUpperPin / avgLowerPin) : 1); + var control = ((trendDelta > 0) ? "BUYERS" : ((trendDelta < 0) ? "SELLERS" : "BALANCED")); + currentPrice = pinescript.hist(56, close, baseOff); + var projLenCh = ((trendValid && i_trendProject) ? (groupSize + 2) : 0); + var upperChPrice = null; + var lowerChPrice = null; if (trendValid) { - let upperSpanCh = (latestTrend.upperX2 - latestTrend.upperX1); - let lowerSpanCh = (latestTrend.lowerX2 - latestTrend.lowerX1); - let upperSlopeCh = ((upperSpanCh !== 0) ? ((latestTrend.upperY2 - latestTrend.upperY1) / upperSpanCh) : 0); - let lowerSlopeCh = ((lowerSpanCh !== 0) ? ((latestTrend.lowerY2 - latestTrend.lowerY1) / lowerSpanCh) : 0); + var upperSpanCh = (latestTrend.upperX2 - latestTrend.upperX1); + var lowerSpanCh = (latestTrend.lowerX2 - latestTrend.lowerX1); + var upperSlopeCh = ((upperSpanCh !== 0) ? ((latestTrend.upperY2 - latestTrend.upperY1) / upperSpanCh) : 0); + var lowerSlopeCh = ((lowerSpanCh !== 0) ? ((latestTrend.lowerY2 - latestTrend.lowerY1) / lowerSpanCh) : 0); upperChPrice = (latestTrend.upperY2 + (upperSlopeCh * projLenCh)); lowerChPrice = (latestTrend.lowerY2 + (lowerSlopeCh * projLenCh)); } - let resistPrice = upperChPrice; - let supportPrice = lowerChPrice; - let resistDiff = (!pinescript.na(resistPrice) ? (resistPrice - currentPrice) : null); - let supportDiff = (!pinescript.na(supportPrice) ? (currentPrice - supportPrice) : null); - let resistPct = ((!pinescript.na(resistDiff) && (currentPrice !== 0)) ? ((resistDiff / currentPrice) * 100) : null); - let supportPct = ((!pinescript.na(supportDiff) && (currentPrice !== 0)) ? ((supportDiff / currentPrice) * 100) : null); - let channelWidth = ((!pinescript.na(resistPrice) && !pinescript.na(supportPrice)) ? (resistPrice - supportPrice) : null); - let rrRatio = (((!pinescript.na(resistDiff) && !pinescript.na(supportDiff)) && (supportDiff !== 0)) ? (resistDiff / supportDiff) : null); - let channelPos = ((!pinescript.na(channelWidth) && (channelWidth !== 0)) ? (((currentPrice - supportPrice) / channelWidth) * 100) : null); - let posStatus = (!pinescript.na(channelPos) ? ((channelPos > 70) ? "OVERBOUGHT" : ((channelPos < 30) ? "OVERSOLD" : "NEUTRAL")) : "-"); - let quality = 50; - let contradictions = 0; + var resistPrice = upperChPrice; + var supportPrice = lowerChPrice; + resistDiff = (!pinescript.na(resistPrice) ? (resistPrice - currentPrice) : null); + supportDiff = (!pinescript.na(supportPrice) ? (currentPrice - supportPrice) : null); + resistPct = ((!pinescript.na(resistDiff) && (currentPrice !== 0)) ? ((resistDiff / currentPrice) * 100) : null); + supportPct = ((!pinescript.na(supportDiff) && (currentPrice !== 0)) ? ((supportDiff / currentPrice) * 100) : null); + var channelWidth = ((!pinescript.na(resistPrice) && !pinescript.na(supportPrice)) ? (resistPrice - supportPrice) : null); + var rrRatio = (((!pinescript.na(resistDiff) && !pinescript.na(supportDiff)) && (supportDiff !== 0)) ? (resistDiff / supportDiff) : null); + var channelPos = ((!pinescript.na(channelWidth) && (channelWidth !== 0)) ? (((currentPrice - supportPrice) / channelWidth) * 100) : null); + var posStatus = (!pinescript.na(channelPos) ? ((channelPos > 70) ? "OVERBOUGHT" : ((channelPos < 30) ? "OVERSOLD" : "NEUTRAL")) : "-"); + var quality = 50; + var contradictions = 0; if (trendValid) { - let absAngle = pinescript.abs(latestTrend.angleDeg); - let angleRange = (45 - i_rangeAngleThreshold); - let angleScore = ((angleRange > 0) ? (15 * pinescript.max(0, pinescript.min(1, ((absAngle - i_rangeAngleThreshold) / angleRange)))) : 0); + var absAngle = pinescript.abs(latestTrend.angleDeg); + var angleRange = (45 - i_rangeAngleThreshold); + var angleScore = ((angleRange > 0) ? (15 * pinescript.max(0, pinescript.min(1, ((absAngle - i_rangeAngleThreshold) / angleRange)))) : 0); quality += angleScore; - let totalPairs = 0; - let consistentPairs = 0; + var totalPairs = 0; + var consistentPairs = 0; for (let i = (latestTrend.startBlock - 1); i <= (latestTrend.endBlock - 1); i++) { if (((i < pinescript.arraySize(state.analyticsData)) && ((i + 1) < pinescript.arraySize(state.analyticsData)))) { - let d1 = pinescript.arrayGet(state.analyticsData, i).delta; - let d2 = pinescript.arrayGet(state.analyticsData, (i + 1)).delta; + d1 = pinescript.arrayGet(state.analyticsData, i).delta; + d2 = pinescript.arrayGet(state.analyticsData, (i + 1)).delta; if ((!pinescript.na(d1) && !pinescript.na(d2))) { totalPairs += 1; if ((pinescript.sign(d1) === pinescript.sign(d2))) { @@ -3859,21 +4036,21 @@ function main() { } } } - let deltaConsistent = ((totalPairs > 0) && (consistentPairs === totalPairs)); - let deltaRatio = ((totalPairs > 0) ? (float(consistentPairs) / float(totalPairs)) : 0); + var deltaConsistent = ((totalPairs > 0) && (consistentPairs === totalPairs)); + var deltaRatio = ((totalPairs > 0) ? (float(consistentPairs) / float(totalPairs)) : 0); quality += (10 * deltaRatio); if (((!pinescript.na(firstBlockVol) && !pinescript.na(lastBlockVol)) && (firstBlockVol > 0))) { - let volGrowth = ((lastBlockVol - firstBlockVol) / firstBlockVol); - let volScore = (10 * pinescript.max(0, pinescript.min(1, (volGrowth / 0.5)))); + var volGrowth = ((lastBlockVol - firstBlockVol) / firstBlockVol); + var volScore = (10 * pinescript.max(0, pinescript.min(1, (volGrowth / 0.5)))); quality += volScore; } if (((!pinescript.na(firstBlockBody) && !pinescript.na(lastBlockBody)) && (firstBlockBody > 0))) { - let bodyGrowth = ((lastBlockBody - firstBlockBody) / firstBlockBody); - let bodyScore = (10 * pinescript.max(0, pinescript.min(1, (bodyGrowth / 0.5)))); + var bodyGrowth = ((lastBlockBody - firstBlockBody) / firstBlockBody); + var bodyScore = (10 * pinescript.max(0, pinescript.min(1, (bodyGrowth / 0.5)))); quality += bodyScore; } - let pinTotal = (pinescript.nz(avgUpperPin, 0) + pinescript.nz(avgLowerPin, 0)); - let pinScore = 0; + var pinTotal = (pinescript.nz(avgUpperPin, 0) + pinescript.nz(avgLowerPin, 0)); + var pinScore = 0; if ((pinTotal > 0)) { if ((latestTrend.channelType === DIR_UP)) { pinScore = (8 * pinescript.max(0, pinescript.min(1, (((pinescript.nz(avgLowerPin, 0) / pinTotal) - 0.5) / 0.3)))); @@ -3883,7 +4060,7 @@ function main() { } } } - let pinAligned = (pinScore > 4); + var pinAligned = (pinScore > 4); quality += pinScore; if ((((latestTrend.channelType === DIR_UP) && (trendDelta < 0)) || ((latestTrend.channelType === DIR_DOWN) && (trendDelta > 0)))) { contradictions += 1; @@ -3897,28 +4074,28 @@ function main() { } } quality = pinescript.max(0, pinescript.min(100, quality)); - let confidenceLevel = ((quality >= 80) ? "HIGH" : ((quality >= 60) ? "MEDIUM" : "LOW")); - let strengthRating = (trendValid ? ((pinescript.abs(latestTrend.angleDeg) > 45) ? "VERY STRONG" : ((pinescript.abs(latestTrend.angleDeg) > 30) ? "STRONG" : ((pinescript.abs(latestTrend.angleDeg) > 15) ? "MODERATE" : "WEAK"))) : "-"); - let trendPrimaryCol = (trendValid ? ((latestTrend.channelType === DIR_UP) ? pinescript.color.rgb(16, 185, 129) : ((latestTrend.channelType === DIR_DOWN) ? pinescript.color.rgb(239, 68, 68) : pinescript.color.rgb(100, 116, 139))) : pinescript.color.rgb(100, 116, 139)); - let trendAccentCol = (trendValid ? ((latestTrend.channelType === DIR_UP) ? pinescript.color.rgb(52, 211, 153) : ((latestTrend.channelType === DIR_DOWN) ? pinescript.color.rgb(248, 113, 113) : pinescript.color.rgb(148, 163, 184))) : pinescript.color.rgb(148, 163, 184)); - let qualityCol = ((quality >= 80) ? pinescript.color.rgb(34, 197, 94) : ((quality >= 60) ? pinescript.color.rgb(234, 179, 8) : pinescript.color.rgb(239, 68, 68))); - let hdrBg = pinescript.color.new(pinescript.color.rgb(30, 41, 59), 5); - let rowBg1 = pinescript.color.new(pinescript.color.rgb(51, 65, 85), 15); - let rowBg2 = pinescript.color.new(pinescript.color.rgb(71, 85, 105), 20); - let txtHdr = pinescript.color.rgb(248, 250, 252); - let txtLbl = pinescript.color.rgb(148, 163, 184); - let txtVal = pinescript.color.rgb(226, 232, 240); - let posCol = pinescript.color.rgb(34, 197, 94); - let negCol = pinescript.color.rgb(239, 68, 68); - let accentCol = pinescript.color.rgb(59, 130, 246); - let cBuy = pinescript.hist(57, state.vBuy, baseOff); - let cSell = pinescript.hist(58, state.vSell, baseOff); - let cTot = (cBuy + cSell); - let cDel = (cBuy - cSell); - let delCol = (pinescript.na(cDel) ? txtVal : ((cDel >= 0) ? posCol : negCol)); - let _alignL = ((i_narrativeLang === "العربية") ? pinescript.text.align_right : pinescript.text.align_left); - let basisTitle = ((baseOff === 0) ? f_L("CURRENT", "MEVCUT", "वर्तमान", "الحالية") : f_L("CLOSED", "KAPALI", "बंद", "المغلقة")); - let engTxt = ((i_calcMethod === "Intrabar (Precise)") ? (((f_dashIntrabar() + " ") + ltf) + ")") : f_dashGeometric()); + var confidenceLevel = ((quality >= 80) ? "HIGH" : ((quality >= 60) ? "MEDIUM" : "LOW")); + var strengthRating = (trendValid ? ((pinescript.abs(latestTrend.angleDeg) > 45) ? "VERY STRONG" : ((pinescript.abs(latestTrend.angleDeg) > 30) ? "STRONG" : ((pinescript.abs(latestTrend.angleDeg) > 15) ? "MODERATE" : "WEAK"))) : "-"); + var trendPrimaryCol = (trendValid ? ((latestTrend.channelType === DIR_UP) ? pinescript.color.rgb(16, 185, 129) : ((latestTrend.channelType === DIR_DOWN) ? pinescript.color.rgb(239, 68, 68) : pinescript.color.rgb(100, 116, 139))) : pinescript.color.rgb(100, 116, 139)); + var trendAccentCol = (trendValid ? ((latestTrend.channelType === DIR_UP) ? pinescript.color.rgb(52, 211, 153) : ((latestTrend.channelType === DIR_DOWN) ? pinescript.color.rgb(248, 113, 113) : pinescript.color.rgb(148, 163, 184))) : pinescript.color.rgb(148, 163, 184)); + var qualityCol = ((quality >= 80) ? pinescript.color.rgb(34, 197, 94) : ((quality >= 60) ? pinescript.color.rgb(234, 179, 8) : pinescript.color.rgb(239, 68, 68))); + var hdrBg = pinescript.color.new(pinescript.color.rgb(30, 41, 59), 5); + var rowBg1 = pinescript.color.new(pinescript.color.rgb(51, 65, 85), 15); + var rowBg2 = pinescript.color.new(pinescript.color.rgb(71, 85, 105), 20); + var txtHdr = pinescript.color.rgb(248, 250, 252); + var txtLbl = pinescript.color.rgb(148, 163, 184); + var txtVal = pinescript.color.rgb(226, 232, 240); + var posCol = pinescript.color.rgb(34, 197, 94); + var negCol = pinescript.color.rgb(239, 68, 68); + var accentCol = pinescript.color.rgb(59, 130, 246); + var cBuy = pinescript.hist(57, state.vBuy, baseOff); + var cSell = pinescript.hist(58, state.vSell, baseOff); + var cTot = (cBuy + cSell); + var cDel = (cBuy - cSell); + var delCol = (pinescript.na(cDel) ? txtVal : ((cDel >= 0) ? posCol : negCol)); + var _alignL = ((i_narrativeLang === "العربية") ? pinescript.text.align_right : pinescript.text.align_left); + var basisTitle = ((baseOff === 0) ? f_L("CURRENT", "MEVCUT", "वर्तमान", "الحالية") : f_L("CLOSED", "KAPALI", "बंद", "المغلقة")); + var engTxt = ((i_calcMethod === "Intrabar (Precise)") ? (((f_dashIntrabar() + " ") + ltf) + ")") : f_dashGeometric()); pinescript.tableMergeCells(state.dashT, 0, 0, 3, 0); pinescript.table.cell(state.dashT, 0, 0, f_dashVolumeEngine(), ({ text_color: txtHdr, bgcolor: hdrBg, text_size: GLOBAL_TEXT_SIZE, text_halign: pinescript.text.align_center, tooltip: f_dashTooltipEngine() })); pinescript.table.cell(state.dashT, 0, 1, f_dashEngine(), ({ text_color: txtLbl, bgcolor: rowBg1, text_size: GLOBAL_TEXT_SIZE, text_halign: pinescript.text.align_right, tooltip: f_L("Volume calculation method", "Hacim hesaplama yöntemi", "वॉल्यूम गणना विधि", "طريقة حساب الحجم") })); @@ -3926,8 +4103,8 @@ function main() { pinescript.table.cell(state.dashT, 2, 1, f_dashData(), ({ text_color: txtLbl, bgcolor: rowBg1, text_size: GLOBAL_TEXT_SIZE, text_halign: pinescript.text.align_right, tooltip: f_dashTooltipData() })); pinescript.table.cell(state.dashT, 3, 1, ((pinescript.strToString(scannableBars) + " ") + f_dashBar()), ({ text_color: txtVal, bgcolor: rowBg1, text_size: GLOBAL_TEXT_SIZE, text_halign: _alignL, tooltip: f_dashTooltipScannable() })); pinescript.tableMergeCells(state.dashT, 0, 2, 3, 2); - let candleHeader = ((baseOff === 0) ? f_dashCurrentCandle() : f_dashClosedCandle()); - let candleTooltip = ((f_L("Most recent ", "En son ", "सबसे हाल का ", "أحدث ") + ((baseOff === 0) ? f_L("live", "canlı", "लाइव", "مباشرة") : f_L("closed", "kapalı", "बंद", "مغلقة"))) + f_L(" candle analysis", " mum analizi", " कैंडल विश्लेषण", " تحليل الشمعة")); + var candleHeader = ((baseOff === 0) ? f_dashCurrentCandle() : f_dashClosedCandle()); + var candleTooltip = ((f_L("Most recent ", "En son ", "सबसे हाल का ", "أحدث ") + ((baseOff === 0) ? f_L("live", "canlı", "लाइव", "مباشرة") : f_L("closed", "kapalı", "बंद", "مغلقة"))) + f_L(" candle analysis", " mum analizi", " कैंडल विश्लेषण", " تحليل الشمعة")); pinescript.table.cell(state.dashT, 0, 2, candleHeader, ({ text_color: txtHdr, bgcolor: hdrBg, text_size: GLOBAL_TEXT_SIZE, text_halign: pinescript.text.align_center, tooltip: candleTooltip })); pinescript.table.cell(state.dashT, 0, 3, f_dashBuy(), ({ text_color: posCol, bgcolor: rowBg1, text_size: GLOBAL_TEXT_SIZE, text_halign: pinescript.text.align_right, tooltip: f_dashTooltipBuy() })); pinescript.table.cell(state.dashT, 1, 3, f_fmtVol(cBuy), ({ text_color: posCol, bgcolor: rowBg1, text_size: GLOBAL_TEXT_SIZE, text_halign: _alignL })); @@ -3935,12 +4112,12 @@ function main() { pinescript.table.cell(state.dashT, 3, 3, f_fmtVol(cSell), ({ text_color: negCol, bgcolor: rowBg1, text_size: GLOBAL_TEXT_SIZE, text_halign: _alignL })); pinescript.table.cell(state.dashT, 0, 4, f_dashTotal(), ({ text_color: txtLbl, bgcolor: rowBg2, text_size: GLOBAL_TEXT_SIZE, text_halign: pinescript.text.align_right, tooltip: f_dashTooltipTotal() })); pinescript.table.cell(state.dashT, 1, 4, f_fmtVol(cTot), ({ text_color: txtVal, bgcolor: rowBg2, text_size: GLOBAL_TEXT_SIZE, text_halign: _alignL })); - let deltaIcon = (pinescript.na(cDel) ? "●" : ((cDel >= 0) ? "▲" : "▼")); - let deltaSign = (pinescript.na(cDel) ? "" : ((cDel > 0) ? "+" : "")); + var deltaIcon = (pinescript.na(cDel) ? "●" : ((cDel >= 0) ? "▲" : "▼")); + var deltaSign = (pinescript.na(cDel) ? "" : ((cDel > 0) ? "+" : "")); pinescript.table.cell(state.dashT, 2, 4, ((deltaIcon + " ") + f_dashDelta()), ({ text_color: delCol, bgcolor: rowBg2, text_size: GLOBAL_TEXT_SIZE, text_halign: pinescript.text.align_right, tooltip: f_dashTooltipDelta() })); pinescript.table.cell(state.dashT, 3, 4, (deltaSign + f_fmtVol(cDel)), ({ text_color: delCol, bgcolor: rowBg2, text_size: GLOBAL_TEXT_SIZE, text_halign: _alignL })); pinescript.tableMergeCells(state.dashT, 0, 5, 3, 5); - let trendTitle = (trendValid ? ((((((f_dashTrendVolumetrics() + " • ") + f_L("Blk", "Blk", "ब्लॉक", "كتلة")) + " ") + pinescript.strToString(latestTrend.startBlock)) + "-") + pinescript.strToString(latestTrend.endBlock)) : (f_dashTrendVolumetrics() + " • N/A")); + var trendTitle = (trendValid ? ((((((f_dashTrendVolumetrics() + " • ") + f_L("Blk", "Blk", "ब्लॉक", "كتلة")) + " ") + pinescript.strToString(latestTrend.startBlock)) + "-") + pinescript.strToString(latestTrend.endBlock)) : (f_dashTrendVolumetrics() + " • N/A")); pinescript.table.cell(state.dashT, 0, 5, trendTitle, ({ text_color: txtHdr, bgcolor: hdrBg, text_size: GLOBAL_TEXT_SIZE, text_halign: pinescript.text.align_center, tooltip: f_dashTooltipTrendVolume() })); pinescript.table.cell(state.dashT, 0, 6, f_dashBuy(), ({ text_color: posCol, bgcolor: rowBg1, text_size: GLOBAL_TEXT_SIZE, text_halign: pinescript.text.align_center, tooltip: f_dashTooltipTrendBuy() })); pinescript.table.cell(state.dashT, 1, 6, f_dashSell(), ({ text_color: negCol, bgcolor: rowBg1, text_size: GLOBAL_TEXT_SIZE, text_halign: pinescript.text.align_center, tooltip: f_dashTooltipTrendSell() })); @@ -3948,14 +4125,14 @@ function main() { pinescript.table.cell(state.dashT, 3, 6, f_dashType(), ({ text_color: txtLbl, bgcolor: rowBg1, text_size: GLOBAL_TEXT_SIZE, text_halign: pinescript.text.align_center, tooltip: f_dashTooltipType() })); pinescript.table.cell(state.dashT, 0, 7, f_fmtVol(trendBuy), ({ text_color: posCol, bgcolor: rowBg2, text_size: GLOBAL_TEXT_SIZE, text_halign: pinescript.text.align_center })); pinescript.table.cell(state.dashT, 1, 7, f_fmtVol(trendSell), ({ text_color: negCol, bgcolor: rowBg2, text_size: GLOBAL_TEXT_SIZE, text_halign: pinescript.text.align_center })); - let trendDeltaStr = ((((((trendDelta > 0) ? "+" : "") + f_fmtVol(trendDelta)) + " (") + pinescript.strToString(trendDeltaPct, "#.##")) + "%)"); + var trendDeltaStr = ((((((trendDelta > 0) ? "+" : "") + f_fmtVol(trendDelta)) + " (") + pinescript.strToString(trendDeltaPct, "#.##")) + "%)"); pinescript.table.cell(state.dashT, 2, 7, trendDeltaStr, ({ text_color: ((trendDelta >= 0) ? posCol : negCol), bgcolor: rowBg2, text_size: GLOBAL_TEXT_SIZE, text_halign: pinescript.text.align_center })); - let trendTypeName = (trendValid ? ((latestTrend.channelType === DIR_UP) ? f_dashUptrend() : ((latestTrend.channelType === DIR_DOWN) ? f_dashDowntrend() : f_dashRange())) : "-"); + var trendTypeName = (trendValid ? ((latestTrend.channelType === DIR_UP) ? f_dashUptrend() : ((latestTrend.channelType === DIR_DOWN) ? f_dashDowntrend() : f_dashRange())) : "-"); pinescript.table.cell(state.dashT, 3, 7, trendTypeName, ({ text_color: trendPrimaryCol, bgcolor: rowBg2, text_size: GLOBAL_TEXT_SIZE, text_halign: pinescript.text.align_center })); pinescript.tableMergeCells(state.dashT, 0, 8, 3, 8); pinescript.table.cell(state.dashT, 0, 8, f_dashPressureMomentum(), ({ text_color: txtHdr, bgcolor: hdrBg, text_size: GLOBAL_TEXT_SIZE, text_halign: pinescript.text.align_center, tooltip: f_dashTooltipPressure() })); - let upperPinChgStr = (pinescript.strToString(upperPinChg, "#.##") + "%"); - let lowerPinChgStr = (pinescript.strToString(lowerPinChg, "#.##") + "%"); + var upperPinChgStr = (pinescript.strToString(upperPinChg, "#.##") + "%"); + var lowerPinChgStr = (pinescript.strToString(lowerPinChg, "#.##") + "%"); pinescript.table.cell(state.dashT, 0, 9, f_dashSellers(), ({ text_color: negCol, bgcolor: rowBg1, text_size: GLOBAL_TEXT_SIZE, text_halign: pinescript.text.align_center, tooltip: f_dashTooltipSellersPin() })); pinescript.table.cell(state.dashT, 1, 9, f_dashBuyers(), ({ text_color: posCol, bgcolor: rowBg1, text_size: GLOBAL_TEXT_SIZE, text_halign: pinescript.text.align_center, tooltip: f_dashTooltipBuyersPin() })); pinescript.table.cell(state.dashT, 2, 9, f_dashBodyAVG(), ({ text_color: txtLbl, bgcolor: rowBg1, text_size: GLOBAL_TEXT_SIZE, text_halign: pinescript.text.align_center, tooltip: f_dashTooltipBodyAVG() })); @@ -3963,64 +4140,64 @@ function main() { pinescript.table.cell(state.dashT, 0, 10, (((f_fmtPrice(avgUpperPin) + " (") + upperPinChgStr) + ")"), ({ text_color: negCol, bgcolor: rowBg2, text_size: GLOBAL_TEXT_SIZE, text_halign: pinescript.text.align_center })); pinescript.table.cell(state.dashT, 1, 10, (((f_fmtPrice(avgLowerPin) + " (") + lowerPinChgStr) + ")"), ({ text_color: posCol, bgcolor: rowBg2, text_size: GLOBAL_TEXT_SIZE, text_halign: pinescript.text.align_center })); pinescript.table.cell(state.dashT, 2, 10, f_fmtPrice(avgBody), ({ text_color: txtVal, bgcolor: rowBg2, text_size: GLOBAL_TEXT_SIZE, text_halign: pinescript.text.align_center })); - let bodyStatusLoc = ((bodyStatus === "EXPANDING") ? f_dashExpanding() : ((bodyStatus === "CONTRACTING") ? f_dashContracting() : f_dashStable())); - let bodyStatusCol = ((bodyStatus === "EXPANDING") ? posCol : ((bodyStatus === "CONTRACTING") ? negCol : txtLbl)); + var bodyStatusLoc = ((bodyStatus === "EXPANDING") ? f_dashExpanding() : ((bodyStatus === "CONTRACTING") ? f_dashContracting() : f_dashStable())); + var bodyStatusCol = ((bodyStatus === "EXPANDING") ? posCol : ((bodyStatus === "CONTRACTING") ? negCol : txtLbl)); pinescript.table.cell(state.dashT, 3, 10, bodyStatusLoc, ({ text_color: bodyStatusCol, bgcolor: rowBg2, text_size: GLOBAL_TEXT_SIZE, text_halign: pinescript.text.align_center })); pinescript.tableMergeCells(state.dashT, 0, 11, 3, 11); pinescript.table.cell(state.dashT, 0, 11, f_dashTrendChannelBoundaries(), ({ text_color: txtHdr, bgcolor: hdrBg, text_size: GLOBAL_TEXT_SIZE, text_halign: pinescript.text.align_center, tooltip: f_dashTooltipChannelBounds() })); - let upDiffSign = ((!pinescript.na(resistDiff) && (resistDiff > 0)) ? "+" : ""); - let upPctSign = ((!pinescript.na(resistPct) && (resistPct > 0)) ? "+" : ""); - let upperStr = (trendValid ? (((((((((f_dashUpperChannel() + " ") + f_fmtPrice(resistPrice)) + " (Δ ") + upDiffSign) + pinescript.strToString(resistDiff, "#.##")) + " | ") + upPctSign) + pinescript.strToString(resistPct, "#.##")) + "%)") : "-"); - let lowerStr = (trendValid ? (((((((f_dashLowerChannel() + " ") + f_fmtPrice(supportPrice)) + " (Δ -") + pinescript.strToString(supportDiff, "#.##")) + " | -") + pinescript.strToString(supportPct, "#.##")) + "%)") : "-"); + var upDiffSign = ((!pinescript.na(resistDiff) && (resistDiff > 0)) ? "+" : ""); + var upPctSign = ((!pinescript.na(resistPct) && (resistPct > 0)) ? "+" : ""); + var upperStr = (trendValid ? (((((((((f_dashUpperChannel() + " ") + f_fmtPrice(resistPrice)) + " (Δ ") + upDiffSign) + pinescript.strToString(resistDiff, "#.##")) + " | ") + upPctSign) + pinescript.strToString(resistPct, "#.##")) + "%)") : "-"); + var lowerStr = (trendValid ? (((((((f_dashLowerChannel() + " ") + f_fmtPrice(supportPrice)) + " (Δ -") + pinescript.strToString(supportDiff, "#.##")) + " | -") + pinescript.strToString(supportPct, "#.##")) + "%)") : "-"); pinescript.tableMergeCells(state.dashT, 0, 12, 1, 12); pinescript.table.cell(state.dashT, 0, 12, upperStr, ({ text_color: txtVal, bgcolor: rowBg1, text_size: GLOBAL_TEXT_SIZE, text_halign: pinescript.text.align_center, tooltip: f_dashTooltipUpper() })); pinescript.tableMergeCells(state.dashT, 2, 12, 3, 12); pinescript.table.cell(state.dashT, 2, 12, lowerStr, ({ text_color: txtVal, bgcolor: rowBg1, text_size: GLOBAL_TEXT_SIZE, text_halign: pinescript.text.align_center, tooltip: f_dashTooltipLower() })); - let widthStr = (trendValid ? f_fmtPrice(channelWidth) : "-"); - let posStr = (!pinescript.na(channelPos) ? (pinescript.strToString(channelPos, "#.##") + "%") : "-"); - let rrStr = (!pinescript.na(rrRatio) ? ("1:" + pinescript.strToString(rrRatio, "#.##")) : "-"); - let posStatusLoc = ((posStatus === "OVERBOUGHT") ? f_dashOverbought() : ((posStatus === "OVERSOLD") ? f_dashOversold() : f_dashNeutral())); - let statusCol = ((posStatus === "OVERBOUGHT") ? negCol : ((posStatus === "OVERSOLD") ? posCol : txtLbl)); + var widthStr = (trendValid ? f_fmtPrice(channelWidth) : "-"); + var posStr = (!pinescript.na(channelPos) ? (pinescript.strToString(channelPos, "#.##") + "%") : "-"); + var rrStr = (!pinescript.na(rrRatio) ? ("1:" + pinescript.strToString(rrRatio, "#.##")) : "-"); + var posStatusLoc = ((posStatus === "OVERBOUGHT") ? f_dashOverbought() : ((posStatus === "OVERSOLD") ? f_dashOversold() : f_dashNeutral())); + var statusCol = ((posStatus === "OVERBOUGHT") ? negCol : ((posStatus === "OVERSOLD") ? posCol : txtLbl)); pinescript.table.cell(state.dashT, 0, 13, widthStr, ({ text_color: accentCol, bgcolor: rowBg2, text_size: GLOBAL_TEXT_SIZE, text_halign: pinescript.text.align_center, tooltip: f_dashTooltipWidth() })); pinescript.table.cell(state.dashT, 1, 13, posStr, ({ text_color: txtVal, bgcolor: rowBg2, text_size: GLOBAL_TEXT_SIZE, text_halign: pinescript.text.align_center, tooltip: f_dashTooltipPosition() })); pinescript.table.cell(state.dashT, 2, 13, rrStr, ({ text_color: txtVal, bgcolor: rowBg2, text_size: GLOBAL_TEXT_SIZE, text_halign: pinescript.text.align_center, tooltip: f_dashTooltipRR() })); pinescript.table.cell(state.dashT, 3, 13, posStatusLoc, ({ text_color: statusCol, bgcolor: rowBg2, text_size: GLOBAL_TEXT_SIZE, text_halign: pinescript.text.align_center, tooltip: f_dashTooltipStatus() })); pinescript.tableMergeCells(state.dashT, 0, 14, 3, 14); pinescript.table.cell(state.dashT, 0, 14, f_dashTrendIntelligence(), ({ text_color: txtHdr, bgcolor: hdrBg, text_size: GLOBAL_TEXT_SIZE, text_halign: pinescript.text.align_center, tooltip: f_dashTooltipQuality(quality, contradictions) })); - let qualityStr = (pinescript.strToString(quality, "#") + "/100"); - let confidenceLoc = ((confidenceLevel === "HIGH") ? f_dashHigh() : ((confidenceLevel === "MEDIUM") ? f_dashMedium() : f_dashLow())); - let strengthLoc = ((strengthRating === "VERY STRONG") ? f_dashVeryStrong() : ((strengthRating === "STRONG") ? f_dashStrong() : ((strengthRating === "MODERATE") ? f_dashModerate() : ((strengthRating === "WEAK") ? f_dashWeak() : "-")))); - let volMomLoc = ((pinescript.strPos(volMom, "INCREASING") >= 0) ? f_dashIncreasing() : f_dashDecreasing()); + var qualityStr = (pinescript.strToString(quality, "#") + "/100"); + var confidenceLoc = ((confidenceLevel === "HIGH") ? f_dashHigh() : ((confidenceLevel === "MEDIUM") ? f_dashMedium() : f_dashLow())); + var strengthLoc = ((strengthRating === "VERY STRONG") ? f_dashVeryStrong() : ((strengthRating === "STRONG") ? f_dashStrong() : ((strengthRating === "MODERATE") ? f_dashModerate() : ((strengthRating === "WEAK") ? f_dashWeak() : "-")))); + var volMomLoc = ((pinescript.strPos(volMom, "INCREASING") >= 0) ? f_dashIncreasing() : f_dashDecreasing()); pinescript.table.cell(state.dashT, 0, 15, qualityStr, ({ text_color: qualityCol, bgcolor: rowBg1, text_size: GLOBAL_TEXT_SIZE, text_halign: pinescript.text.align_center, tooltip: f_dashTooltipQuality(quality, contradictions) })); pinescript.table.cell(state.dashT, 1, 15, confidenceLoc, ({ text_color: qualityCol, bgcolor: rowBg1, text_size: GLOBAL_TEXT_SIZE, text_halign: pinescript.text.align_center, tooltip: f_dashTooltipConfidence() })); pinescript.table.cell(state.dashT, 2, 15, strengthLoc, ({ text_color: trendAccentCol, bgcolor: rowBg1, text_size: GLOBAL_TEXT_SIZE, text_halign: pinescript.text.align_center, tooltip: f_dashTooltipStrength() })); pinescript.table.cell(state.dashT, 3, 15, volMomLoc, ({ text_color: ((pinescript.strPos(volMom, "INCREASING") >= 0) ? posCol : negCol), bgcolor: rowBg1, text_size: GLOBAL_TEXT_SIZE, text_halign: pinescript.text.align_center, tooltip: f_dashTooltipVolMomentum() })); if ((i_narrativeEnable && trendValid)) { state.g_currentBar = f_analyzeCurrentBar(baseOff, globalAvgRange, cBuy, cSell); - let blk1 = ((pinescript.arraySize(state.analyticsData) > 0) ? pinescript.arrayGet(state.analyticsData, 0) : f_analyticsEmpty()); - let blk2 = ((pinescript.arraySize(state.analyticsData) > 1) ? pinescript.arrayGet(state.analyticsData, 1) : f_analyticsEmpty()); + var blk1 = ((pinescript.arraySize(state.analyticsData) > 0) ? pinescript.arrayGet(state.analyticsData, 0) : f_analyticsEmpty()); + var blk2 = ((pinescript.arraySize(state.analyticsData) > 1) ? pinescript.arrayGet(state.analyticsData, 1) : f_analyticsEmpty()); state.g_composite = f_analyzeComposite(blk1, blk2); state.g_trendContext = f_analyzeTrendContext(state.trendChannels, quality); - let hasDivergence = (((latestTrend.channelType === DIR_UP) && (trendDelta < 0)) || ((latestTrend.channelType === DIR_DOWN) && (trendDelta > 0))); - let narrativeParts = f_buildProfessionalNarrativeParts(state.g_currentBar, state.g_composite, state.g_trendContext, latestTrend, trendBuy, trendSell, trendDelta, trendDeltaPct, bodyStatus, upperPinChg, lowerPinChg, volMom, channelPos, posStatus, resistPrice, supportPrice, state.g_levels, contradictions, hasDivergence); - let narrHdrBg = pinescript.color.new(pinescript.color.rgb(15, 23, 42), 5); - let narrRowBg = pinescript.color.new(pinescript.color.rgb(30, 41, 59), 10); - let narrTxtHdr = pinescript.color.rgb(234, 179, 8); - let narrTxtMain = pinescript.color.rgb(248, 250, 252); - let narrTxtSub = pinescript.color.rgb(148, 163, 184); - let narrHeader = f_L("Market Analysis", "Piyasa Analizi", "बाजार विश्लेषण", "تحليل السوق"); - let textAlign = _alignL; + var hasDivergence = (((latestTrend.channelType === DIR_UP) && (trendDelta < 0)) || ((latestTrend.channelType === DIR_DOWN) && (trendDelta > 0))); + var narrativeParts = f_buildProfessionalNarrativeParts(state.g_currentBar, state.g_composite, state.g_trendContext, latestTrend, trendBuy, trendSell, trendDelta, trendDeltaPct, bodyStatus, upperPinChg, lowerPinChg, volMom, channelPos, posStatus, resistPrice, supportPrice, state.g_levels, contradictions, hasDivergence); + var narrHdrBg = pinescript.color.new(pinescript.color.rgb(15, 23, 42), 5); + var narrRowBg = pinescript.color.new(pinescript.color.rgb(30, 41, 59), 10); + var narrTxtHdr = pinescript.color.rgb(234, 179, 8); + var narrTxtMain = pinescript.color.rgb(248, 250, 252); + var narrTxtSub = pinescript.color.rgb(148, 163, 184); + var narrHeader = f_L("Market Analysis", "Piyasa Analizi", "बाजार विश्लेषण", "تحليل السوق"); + var textAlign = _alignL; pinescript.tableMergeCells(state.dashT, 0, 16, 3, 16); pinescript.table.cell(state.dashT, 0, 16, narrHeader, ({ text_color: narrTxtHdr, bgcolor: narrHdrBg, text_size: GLOBAL_TEXT_SIZE, text_halign: pinescript.text.align_center })); - let wordsPerLine = 18; - let maxCharsPerLine = 90; - let firstNarrRow = 17; - let narrLines = pinescript.arrayNew(); - let partsCount = pinescript.min(5, pinescript.arraySize(narrativeParts)); + var wordsPerLine = 18; + var maxCharsPerLine = 90; + var firstNarrRow = 17; + var narrLines = pinescript.arrayNew(); + var partsCount = pinescript.min(5, pinescript.arraySize(narrativeParts)); if ((partsCount > 0)) { for (let pi = 0; pi <= (partsCount - 1); pi++) { - let wrapped = f_wrapNarrative(pinescript.arrayGet(narrativeParts, pi), wordsPerLine, maxCharsPerLine); - let wn = pinescript.arraySize(wrapped); + var wrapped = f_wrapNarrative(pinescript.arrayGet(narrativeParts, pi), wordsPerLine, maxCharsPerLine); + var wn = pinescript.arraySize(wrapped); if ((wn > 0)) { for (let wi = 0; wi <= (wn - 1); wi++) { pinescript.arrayPush(narrLines, pinescript.arrayGet(wrapped, wi)); @@ -4028,13 +4205,13 @@ function main() { } } } - let totalLines = pinescript.arraySize(narrLines); - let narrativeRowsNeeded = pinescript.min(totalLines, ((DASH_ROWS - firstNarrRow) - (i_narrativeShowDisclaimer ? 3 : 0))); + var totalLines = pinescript.arraySize(narrLines); + var narrativeRowsNeeded = pinescript.min(totalLines, ((DASH_ROWS - firstNarrRow) - (i_narrativeShowDisclaimer ? 3 : 0))); if ((narrativeRowsNeeded > 0)) { for (let r = 0; r <= (narrativeRowsNeeded - 1); r++) { - let row = (firstNarrRow + r); + var row = (firstNarrRow + r); pinescript.tableMergeCells(state.dashT, 0, row, 3, row); - let txt = pinescript.arrayGet(narrLines, r); + var txt = pinescript.arrayGet(narrLines, r); if (((r === (narrativeRowsNeeded - 1)) && (totalLines > narrativeRowsNeeded))) { txt += " …"; } @@ -4042,14 +4219,14 @@ function main() { } } if (i_narrativeShowDisclaimer) { - let disclaimerStartRow = (firstNarrRow + narrativeRowsNeeded); - let disclaimerLine1 = f_L("⚠️ IMPORTANT: This analysis is for educational purposes only", "⚠️ ÖNEMLİ: Bu analiz sadece eğitim amaçlıdır", "⚠️ महत्वपूर्ण: यह विश्लेषण केवल शैक्षिक उद्देश्यों के लिए है", "⚠️ هام: هذا التحليل لأغراض تعليمية فقط"); - let disclaimerLine2 = f_L("and does not constitute investment advice. Always conduct your own research", "ve yatırım tavsiyesi teşkil etmez. Her zaman kendi araştırmanızı yapın", "और निवेश सलाह नहीं है। हमेशा अपना शोध करें", "ولا يُشكّل نصيحة استثمارية. قم دائماً بإجراء بحثك الخاص"); - let disclaimerLine3 = f_L("and consult with a qualified financial advisor before making investment decisions.", "ve yatırım kararları vermeden önce kalifiye bir finansal danışmana danışın.", "और निवेश निर्णय लेने से पहले योग्य वित्तीय सलाहकार से परामर्श करें।", "واستشر مستشاراً مالياً مؤهلاً قبل اتخاذ قرارات الاستثمار."); + var disclaimerStartRow = (firstNarrRow + narrativeRowsNeeded); + var disclaimerLine1 = f_L("⚠️ IMPORTANT: This analysis is for educational purposes only", "⚠️ ÖNEMLİ: Bu analiz sadece eğitim amaçlıdır", "⚠️ महत्वपूर्ण: यह विश्लेषण केवल शैक्षिक उद्देश्यों के लिए है", "⚠️ هام: هذا التحليل لأغراض تعليمية فقط"); + var disclaimerLine2 = f_L("and does not constitute investment advice. Always conduct your own research", "ve yatırım tavsiyesi teşkil etmez. Her zaman kendi araştırmanızı yapın", "और निवेश सलाह नहीं है। हमेशा अपना शोध करें", "ولا يُشكّل نصيحة استثمارية. قم دائماً بإجراء بحثك الخاص"); + var disclaimerLine3 = f_L("and consult with a qualified financial advisor before making investment decisions.", "ve yatırım kararları vermeden önce kalifiye bir finansal danışmana danışın.", "और निवेश निर्णय लेने से पहले योग्य वित्तीय सलाहकार से परामर्श करें।", "واستشر مستشاراً مالياً مؤهلاً قبل اتخاذ قرارات الاستثمار."); for (let disclaimerLineNum = 0; disclaimerLineNum <= 2; disclaimerLineNum++) { - let row = (disclaimerStartRow + disclaimerLineNum); + row = (disclaimerStartRow + disclaimerLineNum); pinescript.tableMergeCells(state.dashT, 0, row, 3, row); - let lineText = ((disclaimerLineNum === 0) ? disclaimerLine1 : ((disclaimerLineNum === 1) ? disclaimerLine2 : disclaimerLine3)); + var lineText = ((disclaimerLineNum === 0) ? disclaimerLine1 : ((disclaimerLineNum === 1) ? disclaimerLine2 : disclaimerLine3)); pinescript.table.cell(state.dashT, 0, row, lineText, ({ text_color: narrTxtSub, bgcolor: pinescript.color.new(pinescript.color.rgb(30, 30, 30), 10), text_size: GLOBAL_TEXT_SIZE, text_halign: pinescript.text.align_center })); } } @@ -4099,6 +4276,10 @@ function run(data, options = {}) { rt.__shapeIdx = 0; globalThis.bar_index = i; globalThis.last_bar_index = n - 1; + globalThis.time_tradingday = inTime[i]; + globalThis.time_close = inTime[i]; + globalThis.last_bar_time = inTime[n - 1]; + globalThis.timenow = inTime[n - 1]; globalThis.barstate = { isfirst: i === 0, islast: i === n - 1, isrealtime: false, ishistory: true, isconfirmed: true, isnew: true, islastconfirmedhistory: i === n - 1, diff --git a/converted/TehThomas_-_Aligned_Timeframe_Fair_Value_Gaps.js b/converted/TehThomas_-_Aligned_Timeframe_Fair_Value_Gaps.js index 7152546..a09e226 100644 --- a/converted/TehThomas_-_Aligned_Timeframe_Fair_Value_Gaps.js +++ b/converted/TehThomas_-_Aligned_Timeframe_Fair_Value_Gaps.js @@ -42,6 +42,80 @@ const pinescript = { if (value != null && typeof value[Symbol.iterator] === 'function') return Array.from(value); return new Array(count || 0).fill(null); }, + __decArr: function(arr) { + if (!Array.isArray(arr) || arr.__pineDecorated) return arr; + const self = this; + const def = (name, fn) => Object.defineProperty(arr, name, { value: fn, writable: true, configurable: true }); + Object.defineProperty(arr, '__pineDecorated', { value: true, configurable: true }); + def('get', (i) => (arr[i] === undefined ? null : arr[i])); + def('set', (i, v) => { arr[i] = v; return v; }); + def('size', () => arr.length); + def('clear', () => { arr.length = 0; }); + def('insert', (i, v) => { arr.splice(i, 0, v); }); + def('remove', (i) => arr.splice(i, 1)[0]); + def('contains', (v) => arr.includes(v)); + def('indexof', (v) => arr.indexOf(v)); + def('lastindexof', (v) => arr.lastIndexOf(v)); + def('first', () => (arr.length ? arr[0] : null)); + def('last', () => (arr.length ? arr[arr.length - 1] : null)); + def('sum', () => arr.reduce((a, b) => a + b, 0)); + def('avg', () => (arr.length ? arr.reduce((a, b) => a + b, 0) / arr.length : 0)); + def('min', () => (arr.length ? Math.min(...arr) : null)); + def('max', () => (arr.length ? Math.max(...arr) : null)); + def('range', () => (arr.length ? Math.max(...arr) - Math.min(...arr) : null)); + // Statistical methods that route to the array.* built-ins (these names are not + // native to JS arrays, so attaching them here is safe from recursion). + def('median', () => self.arrayMedian(arr)); + def('mode', () => self.arrayMode(arr)); + def('stdev', () => self.arrayStdev(arr)); + def('variance', () => self.arrayVariance(arr)); + def('covariance', (other) => self.arrayCovariance(arr, other)); + def('percentile_linear_interpolation', (p) => self.arrayPercentileLinearInterpolation(arr, p)); + def('percentile_nearest_rank', (p) => self.arrayPercentileNearestRank(arr, p)); + def('abs', () => self.__decArr(self.arrayAbs(arr))); + def('binary_search', (v) => self.arrayBinarySearch(arr, v)); + // Pine's array.sort takes an order string, not a comparator. Use the native + // sort via .call so we don't recurse through this overridden method. + def('sort', (order) => { Array.prototype.sort.call(arr, (a, b) => (order === 'descending' ? b - a : a - b)); return arr; }); + def('sort_indices', (order) => self.__decArr(arr.map((_, i) => i).sort((a, b) => (order === 'descending' ? arr[b] - arr[a] : arr[a] - arr[b])))); + // join/slice/reverse/concat/includes/fill already exist natively on Array with + // compatible semantics, so we deliberately leave them to the native methods. + return arr; + }, + __decMap: function(m) { + if (!(m instanceof Map) || m.__pineDecorated) return m; + const def = (name, fn) => Object.defineProperty(m, name, { value: fn, writable: true, configurable: true }); + Object.defineProperty(m, '__pineDecorated', { value: true, configurable: true }); + def('put', (k, v) => { m.set(k, v); return v; }); + def('contains', (k) => m.has(k)); + def('remove', (k) => m.delete(k)); + def('keys', () => Array.from(Map.prototype.keys.call(m))); + def('values', () => Array.from(Map.prototype.values.call(m))); + def('size_', () => m.size); + return m; + }, + __decMatrix: function(m) { + if (!m || typeof m !== 'object' || m.__pineDecorated) return m; + const self = this; + const def = (name, fn) => Object.defineProperty(m, name, { value: fn, writable: true, configurable: true }); + Object.defineProperty(m, '__pineDecorated', { value: true, configurable: true }); + def('get', (r, c) => self.matrixGet(m, r, c)); + def('set', (r, c, v) => self.matrixSet(m, r, c, v)); + def('rows_', () => m.rows); + def('columns', () => m.cols); + def('fill', (v) => self.matrixFill(m, v)); + return m; + }, + __decDraw: function(obj) { + if (!obj || typeof obj !== 'object') return obj; + const p = new Proxy(obj, { + get(t, k) { + if (k in t || typeof k === 'symbol') return t[k]; + return function() { return p; }; + }, + }); + return p; + }, alertcondition: function(condition, ...rest) { if (globalThis.__pineRuntime) { globalThis.__pineRuntime.alerts.push({ condition, args: rest }); @@ -49,6 +123,32 @@ const pinescript = { return null; }, barcolor: function(color) { return null; }, + plotchar: function(series, ...rest) { + const rt = globalThis.__pineRuntime; + if (!rt) return series; + const bar = rt.__barIndex | 0; + const ord = (rt.__shapeIdx = (rt.__shapeIdx | 0) + 1) - 1; + let key = 'char_' + ord; + for (const r of rest) { + if (typeof r === 'string') { key = r; break; } + if (r && typeof r === 'object' && r.title) { key = String(r.title); break; } + } + let s = rt.plotshapes[key]; + if (!s) s = rt.plotshapes[key] = { title: key, data: [] }; + s.data[bar] = this.__scalar(series); + return series; + }, + plotarrow: function(series, ...rest) { + const rt = globalThis.__pineRuntime; + if (!rt) return series; + const bar = rt.__barIndex | 0; + const ord = (rt.__plotIdx = (rt.__plotIdx | 0) + 1) - 1; + const key = 'arrow_' + ord; + let p = rt.plots[key]; + if (!p) p = rt.plots[key] = { title: key, data: [] }; + p.data[bar] = this.__scalar(series); + return series; + }, bgcolor: function(color, title, editable, showLast) { return null; }, @@ -470,6 +570,12 @@ const pinescript = { atan: function(value) { return Math.atan(value); }, + todegrees: function(radians) { + return radians * (180 / Math.PI); + }, + toradians: function(degrees) { + return degrees * (Math.PI / 180); + }, floor: function(value) { return Math.floor(value); }, @@ -641,7 +747,7 @@ const pinescript = { return series; }, lineNew: function(x1, y1, x2, y2, opts = {}) { - return { x1, y1, x2, y2, opts, _type: 'line' }; + return this.__decDraw({ x1, y1, x2, y2, opts, _type: 'line' }); }, lineDelete: function(l) { return null; @@ -661,7 +767,7 @@ const pinescript = { return point === 0 || point === 'y1' ? line.y1 : line.y2; }, labelNew: function(x, y, text = '', opts = {}) { - return { x, y, text, opts, _type: 'label' }; + return this.__decDraw({ x, y, text, opts, _type: 'label' }); }, labelDelete: function(l) { return null; @@ -676,7 +782,7 @@ const pinescript = { return label.text || ''; }, boxNew: function(left, top, right, bottom, opts = {}) { - return { left, top, right, bottom, opts, _type: 'box' }; + return this.__decDraw({ left, top, right, bottom, opts, _type: 'box' }); }, boxDelete: function(box) { return null; @@ -694,7 +800,7 @@ const pinescript = { return box; }, polylineNew: function(points, opts = {}) { - return { points: points || [], opts, _type: 'polyline' }; + return this.__decDraw({ points: points || [], opts, _type: 'polyline' }); }, polylineDelete: function(poly) { return null; @@ -765,7 +871,7 @@ const pinescript = { return { time: _time ?? null, price: _price ?? null }; }, mapNew: function() { - return new Map(); + return this.__decMap(new Map()); }, mapSize: function(m) { return m instanceof Map ? m.size : 0; @@ -800,7 +906,7 @@ const pinescript = { const r = Math.max(0, rows ?? 0); const c = Math.max(0, cols ?? 0); const data = Array.from({ length: r }, () => Array.from({ length: c }, () => initialValue)); - return { rows: r, cols: c, data }; + return this.__decMatrix({ rows: r, cols: c, data }); }, matrixRows: function(m) { return m?.rows ?? 0; @@ -923,11 +1029,62 @@ const pinescript = { } return null; }, + __jacobiEigen: function(m) { + if (!m || !Array.isArray(m.data)) return null; + const n = m.rows ?? 0; + if (n === 0 || n !== (m.cols ?? 0)) return null; + // Work on a copy so the input matrix is untouched. + const a = m.data.map(row => row.slice()); + const v = Array.from({ length: n }, (_, i) => Array.from({ length: n }, (_, j) => (i === j ? 1 : 0))); + for (let sweep = 0; sweep < 100; sweep++) { + let off = 0; + for (let p = 0; p < n; p++) for (let q = p + 1; q < n; q++) off += a[p][q] * a[p][q]; + if (off < 1e-20) break; + for (let p = 0; p < n; p++) { + for (let q = p + 1; q < n; q++) { + if (Math.abs(a[p][q]) < 1e-18) continue; + const theta = (a[q][q] - a[p][p]) / (2 * a[p][q]); + const t = Math.sign(theta || 1) / (Math.abs(theta) + Math.sqrt(theta * theta + 1)); + const cos = 1 / Math.sqrt(t * t + 1); + const sin = t * cos; + for (let k = 0; k < n; k++) { + const akp = a[k][p], akq = a[k][q]; + a[k][p] = cos * akp - sin * akq; + a[k][q] = sin * akp + cos * akq; + } + for (let k = 0; k < n; k++) { + const apk = a[p][k], aqk = a[q][k]; + a[p][k] = cos * apk - sin * aqk; + a[q][k] = sin * apk + cos * aqk; + } + for (let k = 0; k < n; k++) { + const vkp = v[k][p], vkq = v[k][q]; + v[k][p] = cos * vkp - sin * vkq; + v[k][q] = sin * vkp + cos * vkq; + } + } + } + } + // Sort eigenpairs by eigenvalue, descending. + const order = Array.from({ length: n }, (_, i) => i).sort((i, j) => a[j][j] - a[i][i]); + const values = order.map(i => a[i][i]); + const vectors = Array.from({ length: n }, (_, r) => order.map(c => v[r][c])); + return { values, vectors }; + }, + matrixEigenvalues: function(m) { + const e = this.__jacobiEigen(m); + return this.__decArr(e ? e.values : []); + }, + matrixEigenvectors: function(m) { + const e = this.__jacobiEigen(m); + if (!e) return this.__decMatrix({ rows: 0, cols: 0, data: [] }); + return this.__decMatrix({ rows: e.vectors.length, cols: e.vectors.length, data: e.vectors }); + }, requestSecurity: function(symbol, timeframe, expression) { return expression; }, arrayNew: function(initialSize = 0, initialValue = 0) { - return Array(initialSize).fill(initialValue); + return this.__decArr(Array(initialSize).fill(initialValue)); }, arraySize: function(arr) { return arr ? arr.length : 0; @@ -978,10 +1135,10 @@ const pinescript = { return arr; }, arraySlice: function(arr, startIndex = 0, endIndex = null) { - if (!arr) return []; + if (!arr) return this.__decArr([]); const start = Number(startIndex) || 0; const end = endIndex === null || endIndex === undefined ? arr.length : Number(endIndex) || 0; - return arr.slice(start, end); + return this.__decArr(arr.slice(start, end)); }, arraySort: function(arr, order = 'ascending') { if (arr) arr.sort((a, b) => order === 'ascending' ? a - b : b - a); @@ -1191,7 +1348,7 @@ const pinescript = { const info = { ticker: 'AAPL', tickerid: 'NASDAQ:AAPL', prefix: 'NASDAQ', root: 'AAPL', suffix: '' }; return info[type] || ''; }, - timenow: 1781574528878, + timenow: 1782783577939, barstate: "LAST", dividends: {}, splits: {}, @@ -1245,11 +1402,11 @@ const pinescript = { arrayConcat: function(arr1, arr2) { if (!arr1) return arr2 || []; if (!arr2) return arr1; - return arr1.concat(arr2); + return this.__decArr(arr1.concat(arr2)); }, arrayCopy: function(arr) { - if (!arr) return []; - return [...arr]; + if (!arr) return this.__decArr([]); + return this.__decArr([...arr]); }, arrayBinarySearch: function(arr, value) { if (!arr || arr.length === 0) return -1; @@ -1339,7 +1496,7 @@ globalThis.input.timeframe = globalThis.input.timeframe || ((defval) => defval); globalThis.array = globalThis.array || { - from: (...items) => items, + from: (...items) => pinescript.__decArr(items), size: (arr) => pinescript.arraySize(arr), @@ -1508,6 +1665,8 @@ globalThis.line = globalThis.line || __pineNS({ style_solid: "solid", style_dash globalThis.box = globalThis.box || __pineNS({}); +globalThis.color = globalThis.color || __pineNS(Object.assign(function(c) { return c; }, { new: function(c, t) { return c; }, rgb: function(r, g, b, t) { return "#rgb(" + [r, g, b].join(",") + ")"; }, from_gradient: function(v, lo, hi, c1, c2) { return c1; }, r: function() { return 0; }, g: function() { return 0; }, b: function() { return 0; }, t: function() { return 0; }, aqua: "#00BCD4", black: "#363A45", blue: "#2962FF", fuchsia: "#E040FB", gray: "#787B86", green: "#4CAF50", lime: "#00E676", maroon: "#880E4F", navy: "#311B92", olive: "#808000", orange: "#FF9800", purple: "#9C27B0", red: "#FF5252", silver: "#B2B5BE", teal: "#00897B", white: "#FFFFFF", yellow: "#FFEB3B" })); + globalThis.label = globalThis.label || __pineNS({ style_label_down: "label_down", style_label_up: "label_up", style_none: "none" }); globalThis.polyline = globalThis.polyline || __pineNS({}); @@ -1520,7 +1679,9 @@ globalThis.map = globalThis.map || __pineNS({}); globalThis.session = globalThis.session || __pineNS({ regular: "regular", extended: "extended" }); -globalThis.dayofweek = globalThis.dayofweek || __pineNS({ sunday: 1, monday: 2, tuesday: 3, wednesday: 4, thursday: 5, friday: 6, saturday: 7 }); +globalThis.ticker = globalThis.ticker || __pineNS({}); + +globalThis.dayofweek = globalThis.dayofweek || __pineNS(Object.assign(function(t) { return new Date(t != null ? t : (globalThis.time || 0)).getUTCDay() + 1; }, { sunday: 1, monday: 2, tuesday: 3, wednesday: 4, thursday: 5, friday: 6, saturday: 7 })); globalThis.timeframe = __pineNS(Object.assign(globalThis.timeframe || {}, { period: (globalThis.timeframe && globalThis.timeframe.period) || "D", isintraday: false, isdaily: true, multiplier: 1 })); @@ -1568,8 +1729,6 @@ globalThis.bool = globalThis.bool || function(x) { return Boolean(x); }; globalThis.string = globalThis.string || function(x) { return x == null ? null : String(x); }; -globalThis.dayofweek = globalThis.dayofweek || __pineNS({ sunday: 1, monday: 2, tuesday: 3, wednesday: 4, thursday: 5, friday: 6, saturday: 7 }); - globalThis.str = globalThis.str || __pineNS({}); pinescript.math = globalThis.math; @@ -1655,7 +1814,7 @@ pinescript.text = { align_center: "center" }; pinescript.table = { - new: function(position, columns, rows, opts) { return { position, columns, rows, opts: opts || {}, cells: [] }; }, + new: function(position, columns, rows, opts) { return pinescript.__decDraw({ position, columns, rows, opts: opts || {}, cells: [] }); }, cell: function(table, column, row, text, opts) { @@ -1690,7 +1849,7 @@ function main() { // Study: [TehThomas] - Aligned Timeframe Fair Value Gaps // Options: {"overlay":true,"max_boxes_count":500,"max_lines_count":500,"max_bars_back":2000} function getAlignedTimeframe() { - let result = ""; + var result = ""; if ((timeframe.in_seconds() <= 60)) { result = "15"; } else { @@ -1719,7 +1878,7 @@ function main() { return result; } function getTimeframeDisplay(tf) { - let result = ""; + var result = ""; if ((tf === "15")) { result = "15M"; } else { @@ -1751,27 +1910,27 @@ function main() { } return result; } - let alignedTF = getAlignedTimeframe(); - let tfDisplay = getTimeframeDisplay(alignedTF); - let extendBars = pinescript.inputInt(20, "Extend boxes", ({ minval: 0, group: "Boxes" })); - let Gapsize = pinescript.inputFloat(0, "Min Gap Size (%)", ({ minval: 0, step: 0.1, group: "Boxes", tooltip: "Set the percentage size of how big the fvg should be" })); - let dynamicGaps = pinescript.inputBool(false, "Dynamic (shrinking) gaps", ({ group: "Boxes" })); - let maxGaps = pinescript.inputInt(10, "Max Unfilled Gaps to Display", ({ minval: 1, group: "Boxes" })); - let maxHistory = pinescript.inputInt(50, "Max FVGs in History", ({ minval: 50, maxval: 200, group: "Boxes", tooltip: "Higher = more unfilled gaps available, but slower performance" })); - let bullColor = pinescript.inputColor(pinescript.color.new(pinescript.color.hex("#47f7414d"), 85), "Bullish Gap Color", ({ group: "Colors" })); - let bearColor = pinescript.inputColor(pinescript.color.hex("#fa7b7b26"), "Bearish Gap Color", ({ group: "Colors" })); - let gapBorderColor = pinescript.inputColor(pinescript.color.black, "Gap Border Color", ({ group: "Colors" })); - let showMidline = pinescript.inputBool(true, "Show Midline", ({ group: "Midline" })); - let midlineColor = pinescript.inputColor(pinescript.color.new(pinescript.color.black, 0), "Midline Color", ({ group: "Midline" })); - let midlineStyleOpt = pinescript.inputString("Dotted", "Midline Style", ({ options: ["Solid", "Dashed", "Dotted"], group: "Midline" })); - let showLabel = pinescript.inputBool(true, "Show FVG Label", ({ group: "Label" })); - let labelColor = pinescript.inputColor(pinescript.color.new(pinescript.color.black, 0), "Label Text Color", ({ group: "Label" })); - let labelSize = pinescript.inputString("Small", "Label Size", ({ options: ["Tiny", "Small", "Normal", "Large"], group: "Label" })); - let midlineStyle = ((midlineStyleOpt === "Solid") ? line.style_solid : ((midlineStyleOpt === "Dashed") ? line.style_dashed : line.style_dotted)); - let textSize = ((labelSize === "Tiny") ? pinescript.size.tiny : ((labelSize === "Small") ? pinescript.size.small : ((labelSize === "Normal") ? pinescript.size.normal : pinescript.size.large))); + var alignedTF = getAlignedTimeframe(); + var tfDisplay = getTimeframeDisplay(alignedTF); + var extendBars = pinescript.inputInt(20, "Extend boxes", ({ minval: 0, group: "Boxes" })); + var Gapsize = pinescript.inputFloat(0, "Min Gap Size (%)", ({ minval: 0, step: 0.1, group: "Boxes", tooltip: "Set the percentage size of how big the fvg should be" })); + var dynamicGaps = pinescript.inputBool(false, "Dynamic (shrinking) gaps", ({ group: "Boxes" })); + var maxGaps = pinescript.inputInt(10, "Max Unfilled Gaps to Display", ({ minval: 1, group: "Boxes" })); + var maxHistory = pinescript.inputInt(50, "Max FVGs in History", ({ minval: 50, maxval: 200, group: "Boxes", tooltip: "Higher = more unfilled gaps available, but slower performance" })); + var bullColor = pinescript.inputColor(pinescript.color.new(pinescript.color.hex("#47f7414d"), 85), "Bullish Gap Color", ({ group: "Colors" })); + var bearColor = pinescript.inputColor(pinescript.color.hex("#fa7b7b26"), "Bearish Gap Color", ({ group: "Colors" })); + var gapBorderColor = pinescript.inputColor(pinescript.color.black, "Gap Border Color", ({ group: "Colors" })); + var showMidline = pinescript.inputBool(true, "Show Midline", ({ group: "Midline" })); + var midlineColor = pinescript.inputColor(pinescript.color.new(pinescript.color.black, 0), "Midline Color", ({ group: "Midline" })); + var midlineStyleOpt = pinescript.inputString("Dotted", "Midline Style", ({ options: ["Solid", "Dashed", "Dotted"], group: "Midline" })); + var showLabel = pinescript.inputBool(true, "Show FVG Label", ({ group: "Label" })); + var labelColor = pinescript.inputColor(pinescript.color.new(pinescript.color.black, 0), "Label Text Color", ({ group: "Label" })); + var labelSize = pinescript.inputString("Small", "Label Size", ({ options: ["Tiny", "Small", "Normal", "Large"], group: "Label" })); + var midlineStyle = ((midlineStyleOpt === "Solid") ? line.style_solid : ((midlineStyleOpt === "Dashed") ? line.style_dashed : line.style_dotted)); + var textSize = ((labelSize === "Tiny") ? pinescript.size.tiny : ((labelSize === "Small") ? pinescript.size.small : ((labelSize === "Normal") ? pinescript.size.normal : pinescript.size.large))); if (state.fvg_records === undefined) state.fvg_records = pinescript.arrayNew(); - let [htfLow, htfHigh, htfLow2, htfHigh2, htfTime] = pinescript.unpack(pinescript.requestSecurity(syminfo.tickerid, alignedTF, [pinescript.hist(0, low, 1), pinescript.hist(1, high, 1), pinescript.hist(2, low, 3), pinescript.hist(3, high, 3), pinescript.hist(4, time, 1)], ({ lookahead: barmerge.lookahead_on })), 5); - let htfChanged = (pinescript.change(pinescript.series(5, htfTime)) !== 0); + var [htfLow, htfHigh, htfLow2, htfHigh2, htfTime] = pinescript.unpack(pinescript.requestSecurity(syminfo.tickerid, alignedTF, [pinescript.hist(0, low, 1), pinescript.hist(1, high, 1), pinescript.hist(2, low, 3), pinescript.hist(3, high, 3), pinescript.hist(4, time, 1)], ({ lookahead: barmerge.lookahead_on })), 5); + var htfChanged = (pinescript.change(pinescript.series(5, htfTime)) !== 0); if (state.htfBarStart === undefined) state.htfBarStart = null; if (state.htfBarStart1 === undefined) state.htfBarStart1 = null; if (state.htfBarStart2 === undefined) state.htfBarStart2 = null; @@ -1782,25 +1941,25 @@ function main() { state.htfBarStart1 = state.htfBarStart; state.htfBarStart = bar_index; } - let threshold = (Gapsize / 100); - let bullFVG = ((htfChanged && (htfLow > htfHigh2)) && (((htfLow - htfHigh2) / htfHigh2) > threshold)); - let bearFVG = ((htfChanged && (htfHigh < htfLow2)) && (((htfLow2 - htfHigh) / htfHigh) > threshold)); - let bullFVG_entry = false; - let bearFVG_entry = false; - let i = 0; + var threshold = (Gapsize / 100); + var bullFVG = ((htfChanged && (htfLow > htfHigh2)) && (((htfLow - htfHigh2) / htfHigh2) > threshold)); + var bearFVG = ((htfChanged && (htfHigh < htfLow2)) && (((htfLow2 - htfHigh) / htfHigh) > threshold)); + var bullFVG_entry = false; + var bearFVG_entry = false; + var i = 0; while ((i < pinescript.arraySize(state.fvg_records))) { - let get = pinescript.arrayGet(state.fvg_records, i); + var get = pinescript.arrayGet(state.fvg_records, i); if (get.isFilled) { i += 1; continue; } - let right_edge = (bar_index + extendBars); - let top = get.top; - let bot = get.bot; - let changed = false; - let shouldMarkFilled = false; + var right_edge = (bar_index + extendBars); + var top = get.top; + var bot = get.bot; + var changed = false; + var shouldMarkFilled = false; if ((!get.triggered && (bar_index > get.created_bar))) { - let inGap = ((low <= top) && (high >= bot)); + var inGap = ((low <= top) && (high >= bot)); if (inGap) { if (get.Bullish) { bullFVG_entry = true; @@ -1860,7 +2019,7 @@ function main() { if (get.isVisible) { box.set_right(get.fvgBox, right_edge); if ((showMidline && !pinescript.na(get.midLine))) { - let mid = ((top + bot) / 2); + var mid = ((top + bot) / 2); line.set_x2(get.midLine, right_edge); line.set_y1(get.midLine, mid); line.set_y2(get.midLine, mid); @@ -1870,7 +2029,7 @@ function main() { i += 1; } function addFVG(isBull, top, bot, color, startBar) { - let fvgBox = pinescript.boxNew(({ left: startBar, top: top, right: (bar_index + extendBars), bottom: bot, bgcolor: pinescript.color, border_color: gapBorderColor })); + var fvgBox = pinescript.boxNew(({ left: startBar, top: top, right: (bar_index + extendBars), bottom: bot, bgcolor: pinescript.color, border_color: gapBorderColor })); if (showLabel) { box.set_text(fvgBox, (tfDisplay + " FVG")); box.set_text_color(fvgBox, labelColor); @@ -1878,18 +2037,24 @@ function main() { box.set_text_halign(fvgBox, pinescript.text.align_center); box.set_text_valign(fvgBox, pinescript.text.align_center); } - mid = ((top + bot) / 2); - let midLine = (showMidline ? pinescript.lineNew(startBar, mid, (bar_index + extendBars), mid, xloc.bar_index, ({ color: midlineColor, style: midlineStyle })) : null); - let newFvg = FVG.new(top, bot, isBull, bar_index, startBar, false, fvgBox, midLine, false, true); - fvg_records.unshift(newFvg); + var mid = ((top + bot) / 2); + var midLine = (showMidline ? pinescript.lineNew(startBar, mid, (bar_index + extendBars), mid, xloc.bar_index, ({ color: midlineColor, style: midlineStyle })) : null); + var newFvg = FVG.new(top, bot, isBull, bar_index, startBar, false, fvgBox, midLine, false, true); + state.fvg_records.unshift(newFvg); if ((pinescript.arraySize(state.fvg_records) > maxHistory)) { - let oldFvg = pinescript.arrayPop(state.fvg_records); + var oldFvg = pinescript.arrayPop(state.fvg_records); if ((!oldFvg.isFilled && oldFvg.isVisible)) { pinescript.boxDelete(oldFvg.fvgBox); if ((showMidline && !pinescript.na(oldFvg.midLine))) { - pinescript.lineDelete(oldFvg.midLine); + return pinescript.lineDelete(oldFvg.midLine); + } else { + return null; } + } else { + return null; } + } else { + return null; } } if ((bullFVG && !pinescript.na(state.htfBarStart2))) { @@ -1898,16 +2063,16 @@ function main() { if ((bearFVG && !pinescript.na(state.htfBarStart2))) { addFVG(false, htfLow2, htfHigh, bearColor, state.htfBarStart2); } - let unfilledCount = 0; - let i2 = 0; + var unfilledCount = 0; + var i2 = 0; while ((i2 < pinescript.arraySize(state.fvg_records))) { - let get2 = pinescript.arrayGet(state.fvg_records, i2); + var get2 = pinescript.arrayGet(state.fvg_records, i2); if (!get2.isFilled) { unfilledCount += 1; if ((unfilledCount <= maxGaps)) { if (!get2.isVisible) { - let color_1 = (get2.Bullish ? bullColor : bearColor); - let newBox = pinescript.boxNew(({ left: get2.start_bar, top: get2.top, right: (bar_index + extendBars), bottom: get2.bot, bgcolor: color_1, border_color: gapBorderColor })); + var color_1 = (get2.Bullish ? bullColor : bearColor); + var newBox = pinescript.boxNew(({ left: get2.start_bar, top: get2.top, right: (bar_index + extendBars), bottom: get2.bot, bgcolor: color_1, border_color: gapBorderColor })); if (showLabel) { box.set_text(newBox, (tfDisplay + " FVG")); box.set_text_color(newBox, labelColor); @@ -1916,7 +2081,7 @@ function main() { box.set_text_valign(newBox, pinescript.text.align_center); } mid = ((get2.top + get2.bot) / 2); - let newLine = (showMidline ? pinescript.lineNew(get2.start_bar, mid, (bar_index + extendBars), mid, xloc.bar_index, ({ color: midlineColor, style: midlineStyle })) : null); + var newLine = (showMidline ? pinescript.lineNew(get2.start_bar, mid, (bar_index + extendBars), mid, xloc.bar_index, ({ color: midlineColor, style: midlineStyle })) : null); get2 = FVG.new(get2.top, get2.bot, get2.Bullish, get2.created_bar, get2.start_bar, get2.triggered, newBox, newLine, get2.isFilled, true); pinescript.arraySet(state.fvg_records, i2, get2); } @@ -1977,6 +2142,10 @@ function run(data, options = {}) { rt.__shapeIdx = 0; globalThis.bar_index = i; globalThis.last_bar_index = n - 1; + globalThis.time_tradingday = inTime[i]; + globalThis.time_close = inTime[i]; + globalThis.last_bar_time = inTime[n - 1]; + globalThis.timenow = inTime[n - 1]; globalThis.barstate = { isfirst: i === 0, islast: i === n - 1, isrealtime: false, ishistory: true, isconfirmed: true, isnew: true, islastconfirmedhistory: i === n - 1, diff --git a/converted/VSA_Pro_Trend_and_Rejection.js b/converted/VSA_Pro_Trend_and_Rejection.js index 2e44146..168ef79 100644 --- a/converted/VSA_Pro_Trend_and_Rejection.js +++ b/converted/VSA_Pro_Trend_and_Rejection.js @@ -42,6 +42,80 @@ const pinescript = { if (value != null && typeof value[Symbol.iterator] === 'function') return Array.from(value); return new Array(count || 0).fill(null); }, + __decArr: function(arr) { + if (!Array.isArray(arr) || arr.__pineDecorated) return arr; + const self = this; + const def = (name, fn) => Object.defineProperty(arr, name, { value: fn, writable: true, configurable: true }); + Object.defineProperty(arr, '__pineDecorated', { value: true, configurable: true }); + def('get', (i) => (arr[i] === undefined ? null : arr[i])); + def('set', (i, v) => { arr[i] = v; return v; }); + def('size', () => arr.length); + def('clear', () => { arr.length = 0; }); + def('insert', (i, v) => { arr.splice(i, 0, v); }); + def('remove', (i) => arr.splice(i, 1)[0]); + def('contains', (v) => arr.includes(v)); + def('indexof', (v) => arr.indexOf(v)); + def('lastindexof', (v) => arr.lastIndexOf(v)); + def('first', () => (arr.length ? arr[0] : null)); + def('last', () => (arr.length ? arr[arr.length - 1] : null)); + def('sum', () => arr.reduce((a, b) => a + b, 0)); + def('avg', () => (arr.length ? arr.reduce((a, b) => a + b, 0) / arr.length : 0)); + def('min', () => (arr.length ? Math.min(...arr) : null)); + def('max', () => (arr.length ? Math.max(...arr) : null)); + def('range', () => (arr.length ? Math.max(...arr) - Math.min(...arr) : null)); + // Statistical methods that route to the array.* built-ins (these names are not + // native to JS arrays, so attaching them here is safe from recursion). + def('median', () => self.arrayMedian(arr)); + def('mode', () => self.arrayMode(arr)); + def('stdev', () => self.arrayStdev(arr)); + def('variance', () => self.arrayVariance(arr)); + def('covariance', (other) => self.arrayCovariance(arr, other)); + def('percentile_linear_interpolation', (p) => self.arrayPercentileLinearInterpolation(arr, p)); + def('percentile_nearest_rank', (p) => self.arrayPercentileNearestRank(arr, p)); + def('abs', () => self.__decArr(self.arrayAbs(arr))); + def('binary_search', (v) => self.arrayBinarySearch(arr, v)); + // Pine's array.sort takes an order string, not a comparator. Use the native + // sort via .call so we don't recurse through this overridden method. + def('sort', (order) => { Array.prototype.sort.call(arr, (a, b) => (order === 'descending' ? b - a : a - b)); return arr; }); + def('sort_indices', (order) => self.__decArr(arr.map((_, i) => i).sort((a, b) => (order === 'descending' ? arr[b] - arr[a] : arr[a] - arr[b])))); + // join/slice/reverse/concat/includes/fill already exist natively on Array with + // compatible semantics, so we deliberately leave them to the native methods. + return arr; + }, + __decMap: function(m) { + if (!(m instanceof Map) || m.__pineDecorated) return m; + const def = (name, fn) => Object.defineProperty(m, name, { value: fn, writable: true, configurable: true }); + Object.defineProperty(m, '__pineDecorated', { value: true, configurable: true }); + def('put', (k, v) => { m.set(k, v); return v; }); + def('contains', (k) => m.has(k)); + def('remove', (k) => m.delete(k)); + def('keys', () => Array.from(Map.prototype.keys.call(m))); + def('values', () => Array.from(Map.prototype.values.call(m))); + def('size_', () => m.size); + return m; + }, + __decMatrix: function(m) { + if (!m || typeof m !== 'object' || m.__pineDecorated) return m; + const self = this; + const def = (name, fn) => Object.defineProperty(m, name, { value: fn, writable: true, configurable: true }); + Object.defineProperty(m, '__pineDecorated', { value: true, configurable: true }); + def('get', (r, c) => self.matrixGet(m, r, c)); + def('set', (r, c, v) => self.matrixSet(m, r, c, v)); + def('rows_', () => m.rows); + def('columns', () => m.cols); + def('fill', (v) => self.matrixFill(m, v)); + return m; + }, + __decDraw: function(obj) { + if (!obj || typeof obj !== 'object') return obj; + const p = new Proxy(obj, { + get(t, k) { + if (k in t || typeof k === 'symbol') return t[k]; + return function() { return p; }; + }, + }); + return p; + }, alertcondition: function(condition, ...rest) { if (globalThis.__pineRuntime) { globalThis.__pineRuntime.alerts.push({ condition, args: rest }); @@ -49,6 +123,32 @@ const pinescript = { return null; }, barcolor: function(color) { return null; }, + plotchar: function(series, ...rest) { + const rt = globalThis.__pineRuntime; + if (!rt) return series; + const bar = rt.__barIndex | 0; + const ord = (rt.__shapeIdx = (rt.__shapeIdx | 0) + 1) - 1; + let key = 'char_' + ord; + for (const r of rest) { + if (typeof r === 'string') { key = r; break; } + if (r && typeof r === 'object' && r.title) { key = String(r.title); break; } + } + let s = rt.plotshapes[key]; + if (!s) s = rt.plotshapes[key] = { title: key, data: [] }; + s.data[bar] = this.__scalar(series); + return series; + }, + plotarrow: function(series, ...rest) { + const rt = globalThis.__pineRuntime; + if (!rt) return series; + const bar = rt.__barIndex | 0; + const ord = (rt.__plotIdx = (rt.__plotIdx | 0) + 1) - 1; + const key = 'arrow_' + ord; + let p = rt.plots[key]; + if (!p) p = rt.plots[key] = { title: key, data: [] }; + p.data[bar] = this.__scalar(series); + return series; + }, bgcolor: function(color, title, editable, showLast) { return null; }, @@ -470,6 +570,12 @@ const pinescript = { atan: function(value) { return Math.atan(value); }, + todegrees: function(radians) { + return radians * (180 / Math.PI); + }, + toradians: function(degrees) { + return degrees * (Math.PI / 180); + }, floor: function(value) { return Math.floor(value); }, @@ -641,7 +747,7 @@ const pinescript = { return series; }, lineNew: function(x1, y1, x2, y2, opts = {}) { - return { x1, y1, x2, y2, opts, _type: 'line' }; + return this.__decDraw({ x1, y1, x2, y2, opts, _type: 'line' }); }, lineDelete: function(l) { return null; @@ -661,7 +767,7 @@ const pinescript = { return point === 0 || point === 'y1' ? line.y1 : line.y2; }, labelNew: function(x, y, text = '', opts = {}) { - return { x, y, text, opts, _type: 'label' }; + return this.__decDraw({ x, y, text, opts, _type: 'label' }); }, labelDelete: function(l) { return null; @@ -676,7 +782,7 @@ const pinescript = { return label.text || ''; }, boxNew: function(left, top, right, bottom, opts = {}) { - return { left, top, right, bottom, opts, _type: 'box' }; + return this.__decDraw({ left, top, right, bottom, opts, _type: 'box' }); }, boxDelete: function(box) { return null; @@ -694,7 +800,7 @@ const pinescript = { return box; }, polylineNew: function(points, opts = {}) { - return { points: points || [], opts, _type: 'polyline' }; + return this.__decDraw({ points: points || [], opts, _type: 'polyline' }); }, polylineDelete: function(poly) { return null; @@ -765,7 +871,7 @@ const pinescript = { return { time: _time ?? null, price: _price ?? null }; }, mapNew: function() { - return new Map(); + return this.__decMap(new Map()); }, mapSize: function(m) { return m instanceof Map ? m.size : 0; @@ -800,7 +906,7 @@ const pinescript = { const r = Math.max(0, rows ?? 0); const c = Math.max(0, cols ?? 0); const data = Array.from({ length: r }, () => Array.from({ length: c }, () => initialValue)); - return { rows: r, cols: c, data }; + return this.__decMatrix({ rows: r, cols: c, data }); }, matrixRows: function(m) { return m?.rows ?? 0; @@ -923,11 +1029,62 @@ const pinescript = { } return null; }, + __jacobiEigen: function(m) { + if (!m || !Array.isArray(m.data)) return null; + const n = m.rows ?? 0; + if (n === 0 || n !== (m.cols ?? 0)) return null; + // Work on a copy so the input matrix is untouched. + const a = m.data.map(row => row.slice()); + const v = Array.from({ length: n }, (_, i) => Array.from({ length: n }, (_, j) => (i === j ? 1 : 0))); + for (let sweep = 0; sweep < 100; sweep++) { + let off = 0; + for (let p = 0; p < n; p++) for (let q = p + 1; q < n; q++) off += a[p][q] * a[p][q]; + if (off < 1e-20) break; + for (let p = 0; p < n; p++) { + for (let q = p + 1; q < n; q++) { + if (Math.abs(a[p][q]) < 1e-18) continue; + const theta = (a[q][q] - a[p][p]) / (2 * a[p][q]); + const t = Math.sign(theta || 1) / (Math.abs(theta) + Math.sqrt(theta * theta + 1)); + const cos = 1 / Math.sqrt(t * t + 1); + const sin = t * cos; + for (let k = 0; k < n; k++) { + const akp = a[k][p], akq = a[k][q]; + a[k][p] = cos * akp - sin * akq; + a[k][q] = sin * akp + cos * akq; + } + for (let k = 0; k < n; k++) { + const apk = a[p][k], aqk = a[q][k]; + a[p][k] = cos * apk - sin * aqk; + a[q][k] = sin * apk + cos * aqk; + } + for (let k = 0; k < n; k++) { + const vkp = v[k][p], vkq = v[k][q]; + v[k][p] = cos * vkp - sin * vkq; + v[k][q] = sin * vkp + cos * vkq; + } + } + } + } + // Sort eigenpairs by eigenvalue, descending. + const order = Array.from({ length: n }, (_, i) => i).sort((i, j) => a[j][j] - a[i][i]); + const values = order.map(i => a[i][i]); + const vectors = Array.from({ length: n }, (_, r) => order.map(c => v[r][c])); + return { values, vectors }; + }, + matrixEigenvalues: function(m) { + const e = this.__jacobiEigen(m); + return this.__decArr(e ? e.values : []); + }, + matrixEigenvectors: function(m) { + const e = this.__jacobiEigen(m); + if (!e) return this.__decMatrix({ rows: 0, cols: 0, data: [] }); + return this.__decMatrix({ rows: e.vectors.length, cols: e.vectors.length, data: e.vectors }); + }, requestSecurity: function(symbol, timeframe, expression) { return expression; }, arrayNew: function(initialSize = 0, initialValue = 0) { - return Array(initialSize).fill(initialValue); + return this.__decArr(Array(initialSize).fill(initialValue)); }, arraySize: function(arr) { return arr ? arr.length : 0; @@ -978,10 +1135,10 @@ const pinescript = { return arr; }, arraySlice: function(arr, startIndex = 0, endIndex = null) { - if (!arr) return []; + if (!arr) return this.__decArr([]); const start = Number(startIndex) || 0; const end = endIndex === null || endIndex === undefined ? arr.length : Number(endIndex) || 0; - return arr.slice(start, end); + return this.__decArr(arr.slice(start, end)); }, arraySort: function(arr, order = 'ascending') { if (arr) arr.sort((a, b) => order === 'ascending' ? a - b : b - a); @@ -1191,7 +1348,7 @@ const pinescript = { const info = { ticker: 'AAPL', tickerid: 'NASDAQ:AAPL', prefix: 'NASDAQ', root: 'AAPL', suffix: '' }; return info[type] || ''; }, - timenow: 1781574529258, + timenow: 1782783578287, barstate: "LAST", dividends: {}, splits: {}, @@ -1245,11 +1402,11 @@ const pinescript = { arrayConcat: function(arr1, arr2) { if (!arr1) return arr2 || []; if (!arr2) return arr1; - return arr1.concat(arr2); + return this.__decArr(arr1.concat(arr2)); }, arrayCopy: function(arr) { - if (!arr) return []; - return [...arr]; + if (!arr) return this.__decArr([]); + return this.__decArr([...arr]); }, arrayBinarySearch: function(arr, value) { if (!arr || arr.length === 0) return -1; @@ -1339,7 +1496,7 @@ globalThis.input.timeframe = globalThis.input.timeframe || ((defval) => defval); globalThis.array = globalThis.array || { - from: (...items) => items, + from: (...items) => pinescript.__decArr(items), size: (arr) => pinescript.arraySize(arr), @@ -1508,6 +1665,8 @@ globalThis.line = globalThis.line || __pineNS({ style_solid: "solid", style_dash globalThis.box = globalThis.box || __pineNS({}); +globalThis.color = globalThis.color || __pineNS(Object.assign(function(c) { return c; }, { new: function(c, t) { return c; }, rgb: function(r, g, b, t) { return "#rgb(" + [r, g, b].join(",") + ")"; }, from_gradient: function(v, lo, hi, c1, c2) { return c1; }, r: function() { return 0; }, g: function() { return 0; }, b: function() { return 0; }, t: function() { return 0; }, aqua: "#00BCD4", black: "#363A45", blue: "#2962FF", fuchsia: "#E040FB", gray: "#787B86", green: "#4CAF50", lime: "#00E676", maroon: "#880E4F", navy: "#311B92", olive: "#808000", orange: "#FF9800", purple: "#9C27B0", red: "#FF5252", silver: "#B2B5BE", teal: "#00897B", white: "#FFFFFF", yellow: "#FFEB3B" })); + globalThis.label = globalThis.label || __pineNS({ style_label_down: "label_down", style_label_up: "label_up", style_none: "none" }); globalThis.polyline = globalThis.polyline || __pineNS({}); @@ -1520,7 +1679,9 @@ globalThis.map = globalThis.map || __pineNS({}); globalThis.session = globalThis.session || __pineNS({ regular: "regular", extended: "extended" }); -globalThis.dayofweek = globalThis.dayofweek || __pineNS({ sunday: 1, monday: 2, tuesday: 3, wednesday: 4, thursday: 5, friday: 6, saturday: 7 }); +globalThis.ticker = globalThis.ticker || __pineNS({}); + +globalThis.dayofweek = globalThis.dayofweek || __pineNS(Object.assign(function(t) { return new Date(t != null ? t : (globalThis.time || 0)).getUTCDay() + 1; }, { sunday: 1, monday: 2, tuesday: 3, wednesday: 4, thursday: 5, friday: 6, saturday: 7 })); globalThis.timeframe = __pineNS(Object.assign(globalThis.timeframe || {}, { period: (globalThis.timeframe && globalThis.timeframe.period) || "D", isintraday: false, isdaily: true, multiplier: 1 })); @@ -1568,8 +1729,6 @@ globalThis.bool = globalThis.bool || function(x) { return Boolean(x); }; globalThis.string = globalThis.string || function(x) { return x == null ? null : String(x); }; -globalThis.dayofweek = globalThis.dayofweek || __pineNS({ sunday: 1, monday: 2, tuesday: 3, wednesday: 4, thursday: 5, friday: 6, saturday: 7 }); - globalThis.str = globalThis.str || __pineNS({}); pinescript.math = globalThis.math; @@ -1655,7 +1814,7 @@ pinescript.text = { align_center: "center" }; pinescript.table = { - new: function(position, columns, rows, opts) { return { position, columns, rows, opts: opts || {}, cells: [] }; }, + new: function(position, columns, rows, opts) { return pinescript.__decDraw({ position, columns, rows, opts: opts || {}, cells: [] }); }, cell: function(table, column, row, text, opts) { @@ -1688,42 +1847,45 @@ function main() { null; // Strategy: VSA Pro: Trend and Rejection 🚀 // Options: {"overlay":true,"initial_capital":100000,"default_qty_type":null,"default_qty_value":100,"currency":null,"process_orders_on_close":true} - let group_trend = "Trend Ayarları (SuperTrend)"; - let st_period = pinescript.inputInt(10, "ST Periyodu", ({ group: group_trend })); - let st_multiplier = pinescript.inputFloat(3, "ST Çarpanı", ({ step: 0.1, group: group_trend })); - let group_volatility = "Hacim & Volatilite (VSA)"; - let atr_length = pinescript.inputInt(14, "ATR Uzunluğu", ({ group: group_volatility })); - let min_vol_mult = pinescript.inputFloat(1, "Min. Hacim Eşiği (Ortalama)", ({ group: group_volatility })); - let effort_mult = pinescript.inputFloat(2, "Yüksek Hacim (Görsel Ekleme)", ({ step: 0.1, group: group_volatility })); - let result_mult = pinescript.inputFloat(1.5, "Gövde Boyu (Görsel Momentum)", ({ step: 0.1, group: group_volatility })); - let group_labels = "Etiket İsimleri"; - let txt_start_long = pinescript.inputString("AL", "Trend Başlangıcı (Long)", ({ group: group_labels })); - let txt_start_short = pinescript.inputString("NAKİT", "Trend Bitişi (Sat)", ({ group: group_labels })); - let [superTrendVal, superTrendDir] = pinescript.unpack(pinescript.supertrend(st_multiplier, st_period), 2); - let isTrendNewGreen = ((superTrendDir === -1) && (pinescript.hist(0, superTrendDir, 1) === 1)); - let isTrendNewRed = ((superTrendDir === 1) && (pinescript.hist(1, superTrendDir, 1) === -1)); - let isGreenZone = (superTrendDir === -1); - let isRedZone = (superTrendDir === 1); - let atr = pinescript.atr(atr_length); - let avgVol = pinescript.sma(volume, 50); - let bodySize = pinescript.abs((close - open)); - let isGreenCandle = (close > open); - let isRedCandle = pinescript.close((avgVol * min_vol_mult)); - let signal_AL = isTrendNewGreen; - let signal_SAT = isTrendNewRed; - let cond_L1 = (isGreenCandle && (relBodyATR > result_mult)); - let cond_L2 = ((isRedCandle && (relVol > effort_mult)) && (relBodyATR < 0.8)); - let signal_EKLE_Gorsel = (((isGreenZone && !isTrendNewGreen) && isVolumeValid) && (cond_L1 || cond_L2)); - let cond_S1 = (isRedCandle && (relBodyATR > result_mult)); - let cond_S2 = ((isGreenCandle && (relVol > effort_mult)) && (relBodyATR < 0.8)); - let signal_ZAYIFLIK_Gorsel = (((isRedZone && !isTrendNewRed) && isVolumeValid) && (cond_S1 || cond_S2)); + var group_trend = "Trend Ayarları (SuperTrend)"; + var st_period = pinescript.inputInt(10, "ST Periyodu", ({ group: group_trend })); + var st_multiplier = pinescript.inputFloat(3, "ST Çarpanı", ({ step: 0.1, group: group_trend })); + var group_volatility = "Hacim & Volatilite (VSA)"; + var atr_length = pinescript.inputInt(14, "ATR Uzunluğu", ({ group: group_volatility })); + var min_vol_mult = pinescript.inputFloat(1, "Min. Hacim Eşiği (Ortalama)", ({ group: group_volatility })); + var effort_mult = pinescript.inputFloat(2, "Yüksek Hacim (Görsel Ekleme)", ({ step: 0.1, group: group_volatility })); + var result_mult = pinescript.inputFloat(1.5, "Gövde Boyu (Görsel Momentum)", ({ step: 0.1, group: group_volatility })); + var group_labels = "Etiket İsimleri"; + var txt_start_long = pinescript.inputString("AL", "Trend Başlangıcı (Long)", ({ group: group_labels })); + var txt_start_short = pinescript.inputString("NAKİT", "Trend Bitişi (Sat)", ({ group: group_labels })); + var [superTrendVal, superTrendDir] = pinescript.unpack(pinescript.supertrend(st_multiplier, st_period), 2); + var isTrendNewGreen = ((superTrendDir === -1) && (pinescript.hist(0, superTrendDir, 1) === 1)); + var isTrendNewRed = ((superTrendDir === 1) && (pinescript.hist(1, superTrendDir, 1) === -1)); + var isGreenZone = (superTrendDir === -1); + var isRedZone = (superTrendDir === 1); + var atr = pinescript.atr(atr_length); + var avgVol = pinescript.sma(pinescript.series(2, volume), 50); + var bodySize = pinescript.abs((close - open)); + var isGreenCandle = (close > open); + var isRedCandle = (close < open); + var relBodyATR = (bodySize / atr); + var relVol = (volume / avgVol); + var isVolumeValid = (volume > (avgVol * min_vol_mult)); + var signal_AL = isTrendNewGreen; + var signal_SAT = isTrendNewRed; + var cond_L1 = (isGreenCandle && (relBodyATR > result_mult)); + var cond_L2 = ((isRedCandle && (relVol > effort_mult)) && (relBodyATR < 0.8)); + var signal_EKLE_Gorsel = (((isGreenZone && !isTrendNewGreen) && isVolumeValid) && (cond_L1 || cond_L2)); + var cond_S1 = (isRedCandle && (relBodyATR > result_mult)); + var cond_S2 = ((isGreenCandle && (relVol > effort_mult)) && (relBodyATR < 0.8)); + var signal_ZAYIFLIK_Gorsel = (((isRedZone && !isTrendNewRed) && isVolumeValid) && (cond_S1 || cond_S2)); if (signal_AL) { pinescript.strategyEntry("Long_Giris", pinescript.strategy.long, ({ comment: "AL %100" })); } if (signal_SAT) { - strategy.close_all(({ comment: "NAKİTE GEÇ 🔴" })); + pinescript.strategy.close_all(({ comment: "NAKİTE GEÇ 🔴" })); } - let lineColor = (isGreenZone ? pinescript.color.new(pinescript.color.green, 0) : pinescript.color.new(pinescript.color.red, 0)); + var lineColor = (isGreenZone ? pinescript.color.new(pinescript.color.green, 0) : pinescript.color.new(pinescript.color.red, 0)); pinescript.plot(superTrendVal, "Trend Hattı", ({ color: lineColor, linewidth: 2 })); pinescript.bgcolor((isGreenZone ? pinescript.color.new(pinescript.color.green, 90) : pinescript.color.new(pinescript.color.red, 90)), ({ title: "Trend Bölgeleri" })); if (signal_AL) { @@ -1742,8 +1904,8 @@ function main() { if (barstate.islast) { pinescript.table.cell(state.legend, 0, 0, "STRATEJİ DURUMU", ({ text_color: pinescript.color.yellow })); pinescript.tableMergeCells(state.legend, 0, 0, 1, 0); - let statusText = (isGreenZone ? "ALIMDA (Yükseliş)" : "NAKİTTE (Düşüş)"); - let statusColor = (isGreenZone ? pinescript.color.green : pinescript.color.red); + var statusText = (isGreenZone ? "ALIMDA (Yükseliş)" : "NAKİTTE (Düşüş)"); + var statusColor = (isGreenZone ? pinescript.color.green : pinescript.color.red); pinescript.table.cell(state.legend, 0, 1, statusText, ({ text_color: statusColor, text_size: pinescript.size.normal })); pinescript.tableMergeCells(state.legend, 0, 1, 1, 1); } @@ -1791,6 +1953,10 @@ function run(data, options = {}) { rt.__shapeIdx = 0; globalThis.bar_index = i; globalThis.last_bar_index = n - 1; + globalThis.time_tradingday = inTime[i]; + globalThis.time_close = inTime[i]; + globalThis.last_bar_time = inTime[n - 1]; + globalThis.timenow = inTime[n - 1]; globalThis.barstate = { isfirst: i === 0, islast: i === n - 1, isrealtime: false, ishistory: true, isconfirmed: true, isnew: true, islastconfirmedhistory: i === n - 1, diff --git a/converted/Volatility_Contraction_Score.js b/converted/Volatility_Contraction_Score.js index 2e6cad8..7985037 100644 --- a/converted/Volatility_Contraction_Score.js +++ b/converted/Volatility_Contraction_Score.js @@ -42,6 +42,80 @@ const pinescript = { if (value != null && typeof value[Symbol.iterator] === 'function') return Array.from(value); return new Array(count || 0).fill(null); }, + __decArr: function(arr) { + if (!Array.isArray(arr) || arr.__pineDecorated) return arr; + const self = this; + const def = (name, fn) => Object.defineProperty(arr, name, { value: fn, writable: true, configurable: true }); + Object.defineProperty(arr, '__pineDecorated', { value: true, configurable: true }); + def('get', (i) => (arr[i] === undefined ? null : arr[i])); + def('set', (i, v) => { arr[i] = v; return v; }); + def('size', () => arr.length); + def('clear', () => { arr.length = 0; }); + def('insert', (i, v) => { arr.splice(i, 0, v); }); + def('remove', (i) => arr.splice(i, 1)[0]); + def('contains', (v) => arr.includes(v)); + def('indexof', (v) => arr.indexOf(v)); + def('lastindexof', (v) => arr.lastIndexOf(v)); + def('first', () => (arr.length ? arr[0] : null)); + def('last', () => (arr.length ? arr[arr.length - 1] : null)); + def('sum', () => arr.reduce((a, b) => a + b, 0)); + def('avg', () => (arr.length ? arr.reduce((a, b) => a + b, 0) / arr.length : 0)); + def('min', () => (arr.length ? Math.min(...arr) : null)); + def('max', () => (arr.length ? Math.max(...arr) : null)); + def('range', () => (arr.length ? Math.max(...arr) - Math.min(...arr) : null)); + // Statistical methods that route to the array.* built-ins (these names are not + // native to JS arrays, so attaching them here is safe from recursion). + def('median', () => self.arrayMedian(arr)); + def('mode', () => self.arrayMode(arr)); + def('stdev', () => self.arrayStdev(arr)); + def('variance', () => self.arrayVariance(arr)); + def('covariance', (other) => self.arrayCovariance(arr, other)); + def('percentile_linear_interpolation', (p) => self.arrayPercentileLinearInterpolation(arr, p)); + def('percentile_nearest_rank', (p) => self.arrayPercentileNearestRank(arr, p)); + def('abs', () => self.__decArr(self.arrayAbs(arr))); + def('binary_search', (v) => self.arrayBinarySearch(arr, v)); + // Pine's array.sort takes an order string, not a comparator. Use the native + // sort via .call so we don't recurse through this overridden method. + def('sort', (order) => { Array.prototype.sort.call(arr, (a, b) => (order === 'descending' ? b - a : a - b)); return arr; }); + def('sort_indices', (order) => self.__decArr(arr.map((_, i) => i).sort((a, b) => (order === 'descending' ? arr[b] - arr[a] : arr[a] - arr[b])))); + // join/slice/reverse/concat/includes/fill already exist natively on Array with + // compatible semantics, so we deliberately leave them to the native methods. + return arr; + }, + __decMap: function(m) { + if (!(m instanceof Map) || m.__pineDecorated) return m; + const def = (name, fn) => Object.defineProperty(m, name, { value: fn, writable: true, configurable: true }); + Object.defineProperty(m, '__pineDecorated', { value: true, configurable: true }); + def('put', (k, v) => { m.set(k, v); return v; }); + def('contains', (k) => m.has(k)); + def('remove', (k) => m.delete(k)); + def('keys', () => Array.from(Map.prototype.keys.call(m))); + def('values', () => Array.from(Map.prototype.values.call(m))); + def('size_', () => m.size); + return m; + }, + __decMatrix: function(m) { + if (!m || typeof m !== 'object' || m.__pineDecorated) return m; + const self = this; + const def = (name, fn) => Object.defineProperty(m, name, { value: fn, writable: true, configurable: true }); + Object.defineProperty(m, '__pineDecorated', { value: true, configurable: true }); + def('get', (r, c) => self.matrixGet(m, r, c)); + def('set', (r, c, v) => self.matrixSet(m, r, c, v)); + def('rows_', () => m.rows); + def('columns', () => m.cols); + def('fill', (v) => self.matrixFill(m, v)); + return m; + }, + __decDraw: function(obj) { + if (!obj || typeof obj !== 'object') return obj; + const p = new Proxy(obj, { + get(t, k) { + if (k in t || typeof k === 'symbol') return t[k]; + return function() { return p; }; + }, + }); + return p; + }, alertcondition: function(condition, ...rest) { if (globalThis.__pineRuntime) { globalThis.__pineRuntime.alerts.push({ condition, args: rest }); @@ -49,6 +123,32 @@ const pinescript = { return null; }, barcolor: function(color) { return null; }, + plotchar: function(series, ...rest) { + const rt = globalThis.__pineRuntime; + if (!rt) return series; + const bar = rt.__barIndex | 0; + const ord = (rt.__shapeIdx = (rt.__shapeIdx | 0) + 1) - 1; + let key = 'char_' + ord; + for (const r of rest) { + if (typeof r === 'string') { key = r; break; } + if (r && typeof r === 'object' && r.title) { key = String(r.title); break; } + } + let s = rt.plotshapes[key]; + if (!s) s = rt.plotshapes[key] = { title: key, data: [] }; + s.data[bar] = this.__scalar(series); + return series; + }, + plotarrow: function(series, ...rest) { + const rt = globalThis.__pineRuntime; + if (!rt) return series; + const bar = rt.__barIndex | 0; + const ord = (rt.__plotIdx = (rt.__plotIdx | 0) + 1) - 1; + const key = 'arrow_' + ord; + let p = rt.plots[key]; + if (!p) p = rt.plots[key] = { title: key, data: [] }; + p.data[bar] = this.__scalar(series); + return series; + }, bgcolor: function(color, title, editable, showLast) { return null; }, @@ -470,6 +570,12 @@ const pinescript = { atan: function(value) { return Math.atan(value); }, + todegrees: function(radians) { + return radians * (180 / Math.PI); + }, + toradians: function(degrees) { + return degrees * (Math.PI / 180); + }, floor: function(value) { return Math.floor(value); }, @@ -641,7 +747,7 @@ const pinescript = { return series; }, lineNew: function(x1, y1, x2, y2, opts = {}) { - return { x1, y1, x2, y2, opts, _type: 'line' }; + return this.__decDraw({ x1, y1, x2, y2, opts, _type: 'line' }); }, lineDelete: function(l) { return null; @@ -661,7 +767,7 @@ const pinescript = { return point === 0 || point === 'y1' ? line.y1 : line.y2; }, labelNew: function(x, y, text = '', opts = {}) { - return { x, y, text, opts, _type: 'label' }; + return this.__decDraw({ x, y, text, opts, _type: 'label' }); }, labelDelete: function(l) { return null; @@ -676,7 +782,7 @@ const pinescript = { return label.text || ''; }, boxNew: function(left, top, right, bottom, opts = {}) { - return { left, top, right, bottom, opts, _type: 'box' }; + return this.__decDraw({ left, top, right, bottom, opts, _type: 'box' }); }, boxDelete: function(box) { return null; @@ -694,7 +800,7 @@ const pinescript = { return box; }, polylineNew: function(points, opts = {}) { - return { points: points || [], opts, _type: 'polyline' }; + return this.__decDraw({ points: points || [], opts, _type: 'polyline' }); }, polylineDelete: function(poly) { return null; @@ -765,7 +871,7 @@ const pinescript = { return { time: _time ?? null, price: _price ?? null }; }, mapNew: function() { - return new Map(); + return this.__decMap(new Map()); }, mapSize: function(m) { return m instanceof Map ? m.size : 0; @@ -800,7 +906,7 @@ const pinescript = { const r = Math.max(0, rows ?? 0); const c = Math.max(0, cols ?? 0); const data = Array.from({ length: r }, () => Array.from({ length: c }, () => initialValue)); - return { rows: r, cols: c, data }; + return this.__decMatrix({ rows: r, cols: c, data }); }, matrixRows: function(m) { return m?.rows ?? 0; @@ -923,11 +1029,62 @@ const pinescript = { } return null; }, + __jacobiEigen: function(m) { + if (!m || !Array.isArray(m.data)) return null; + const n = m.rows ?? 0; + if (n === 0 || n !== (m.cols ?? 0)) return null; + // Work on a copy so the input matrix is untouched. + const a = m.data.map(row => row.slice()); + const v = Array.from({ length: n }, (_, i) => Array.from({ length: n }, (_, j) => (i === j ? 1 : 0))); + for (let sweep = 0; sweep < 100; sweep++) { + let off = 0; + for (let p = 0; p < n; p++) for (let q = p + 1; q < n; q++) off += a[p][q] * a[p][q]; + if (off < 1e-20) break; + for (let p = 0; p < n; p++) { + for (let q = p + 1; q < n; q++) { + if (Math.abs(a[p][q]) < 1e-18) continue; + const theta = (a[q][q] - a[p][p]) / (2 * a[p][q]); + const t = Math.sign(theta || 1) / (Math.abs(theta) + Math.sqrt(theta * theta + 1)); + const cos = 1 / Math.sqrt(t * t + 1); + const sin = t * cos; + for (let k = 0; k < n; k++) { + const akp = a[k][p], akq = a[k][q]; + a[k][p] = cos * akp - sin * akq; + a[k][q] = sin * akp + cos * akq; + } + for (let k = 0; k < n; k++) { + const apk = a[p][k], aqk = a[q][k]; + a[p][k] = cos * apk - sin * aqk; + a[q][k] = sin * apk + cos * aqk; + } + for (let k = 0; k < n; k++) { + const vkp = v[k][p], vkq = v[k][q]; + v[k][p] = cos * vkp - sin * vkq; + v[k][q] = sin * vkp + cos * vkq; + } + } + } + } + // Sort eigenpairs by eigenvalue, descending. + const order = Array.from({ length: n }, (_, i) => i).sort((i, j) => a[j][j] - a[i][i]); + const values = order.map(i => a[i][i]); + const vectors = Array.from({ length: n }, (_, r) => order.map(c => v[r][c])); + return { values, vectors }; + }, + matrixEigenvalues: function(m) { + const e = this.__jacobiEigen(m); + return this.__decArr(e ? e.values : []); + }, + matrixEigenvectors: function(m) { + const e = this.__jacobiEigen(m); + if (!e) return this.__decMatrix({ rows: 0, cols: 0, data: [] }); + return this.__decMatrix({ rows: e.vectors.length, cols: e.vectors.length, data: e.vectors }); + }, requestSecurity: function(symbol, timeframe, expression) { return expression; }, arrayNew: function(initialSize = 0, initialValue = 0) { - return Array(initialSize).fill(initialValue); + return this.__decArr(Array(initialSize).fill(initialValue)); }, arraySize: function(arr) { return arr ? arr.length : 0; @@ -978,10 +1135,10 @@ const pinescript = { return arr; }, arraySlice: function(arr, startIndex = 0, endIndex = null) { - if (!arr) return []; + if (!arr) return this.__decArr([]); const start = Number(startIndex) || 0; const end = endIndex === null || endIndex === undefined ? arr.length : Number(endIndex) || 0; - return arr.slice(start, end); + return this.__decArr(arr.slice(start, end)); }, arraySort: function(arr, order = 'ascending') { if (arr) arr.sort((a, b) => order === 'ascending' ? a - b : b - a); @@ -1191,7 +1348,7 @@ const pinescript = { const info = { ticker: 'AAPL', tickerid: 'NASDAQ:AAPL', prefix: 'NASDAQ', root: 'AAPL', suffix: '' }; return info[type] || ''; }, - timenow: 1781574528980, + timenow: 1782783578026, barstate: "LAST", dividends: {}, splits: {}, @@ -1245,11 +1402,11 @@ const pinescript = { arrayConcat: function(arr1, arr2) { if (!arr1) return arr2 || []; if (!arr2) return arr1; - return arr1.concat(arr2); + return this.__decArr(arr1.concat(arr2)); }, arrayCopy: function(arr) { - if (!arr) return []; - return [...arr]; + if (!arr) return this.__decArr([]); + return this.__decArr([...arr]); }, arrayBinarySearch: function(arr, value) { if (!arr || arr.length === 0) return -1; @@ -1339,7 +1496,7 @@ globalThis.input.timeframe = globalThis.input.timeframe || ((defval) => defval); globalThis.array = globalThis.array || { - from: (...items) => items, + from: (...items) => pinescript.__decArr(items), size: (arr) => pinescript.arraySize(arr), @@ -1508,6 +1665,8 @@ globalThis.line = globalThis.line || __pineNS({ style_solid: "solid", style_dash globalThis.box = globalThis.box || __pineNS({}); +globalThis.color = globalThis.color || __pineNS(Object.assign(function(c) { return c; }, { new: function(c, t) { return c; }, rgb: function(r, g, b, t) { return "#rgb(" + [r, g, b].join(",") + ")"; }, from_gradient: function(v, lo, hi, c1, c2) { return c1; }, r: function() { return 0; }, g: function() { return 0; }, b: function() { return 0; }, t: function() { return 0; }, aqua: "#00BCD4", black: "#363A45", blue: "#2962FF", fuchsia: "#E040FB", gray: "#787B86", green: "#4CAF50", lime: "#00E676", maroon: "#880E4F", navy: "#311B92", olive: "#808000", orange: "#FF9800", purple: "#9C27B0", red: "#FF5252", silver: "#B2B5BE", teal: "#00897B", white: "#FFFFFF", yellow: "#FFEB3B" })); + globalThis.label = globalThis.label || __pineNS({ style_label_down: "label_down", style_label_up: "label_up", style_none: "none" }); globalThis.polyline = globalThis.polyline || __pineNS({}); @@ -1520,7 +1679,9 @@ globalThis.map = globalThis.map || __pineNS({}); globalThis.session = globalThis.session || __pineNS({ regular: "regular", extended: "extended" }); -globalThis.dayofweek = globalThis.dayofweek || __pineNS({ sunday: 1, monday: 2, tuesday: 3, wednesday: 4, thursday: 5, friday: 6, saturday: 7 }); +globalThis.ticker = globalThis.ticker || __pineNS({}); + +globalThis.dayofweek = globalThis.dayofweek || __pineNS(Object.assign(function(t) { return new Date(t != null ? t : (globalThis.time || 0)).getUTCDay() + 1; }, { sunday: 1, monday: 2, tuesday: 3, wednesday: 4, thursday: 5, friday: 6, saturday: 7 })); globalThis.timeframe = __pineNS(Object.assign(globalThis.timeframe || {}, { period: (globalThis.timeframe && globalThis.timeframe.period) || "D", isintraday: false, isdaily: true, multiplier: 1 })); @@ -1568,8 +1729,6 @@ globalThis.bool = globalThis.bool || function(x) { return Boolean(x); }; globalThis.string = globalThis.string || function(x) { return x == null ? null : String(x); }; -globalThis.dayofweek = globalThis.dayofweek || __pineNS({ sunday: 1, monday: 2, tuesday: 3, wednesday: 4, thursday: 5, friday: 6, saturday: 7 }); - globalThis.str = globalThis.str || __pineNS({}); pinescript.math = globalThis.math; @@ -1655,7 +1814,7 @@ pinescript.text = { align_center: "center" }; pinescript.table = { - new: function(position, columns, rows, opts) { return { position, columns, rows, opts: opts || {}, cells: [] }; }, + new: function(position, columns, rows, opts) { return pinescript.__decDraw({ position, columns, rows, opts: opts || {}, cells: [] }); }, cell: function(table, column, row, text, opts) { @@ -1688,85 +1847,85 @@ function main() { null; // Study: Volatility Contraction Score // Options: {"shorttitle":"VCS","overlay":false} - let grpCore = "VCP Logic Settings"; - let grpScreen = "Screener Data for Pine Screener"; - let lenShortInput = pinescript.inputInt(13, "Short Term", ({ minval: 3, group: grpCore })); - let lenLongInput = pinescript.inputInt(63, "Long Term", ({ minval: 20, group: grpCore })); - let lenVolInput = pinescript.inputInt(50, "Volume Average (Logic)", ({ minval: 10, group: grpCore })); - let sensitivity = 2; - let grpTrend = "Trend Filter"; - let trendPenaltyWeight = pinescript.inputFloat(1, "Trend Penalty Strength", ({ minval: 0, maxval: 5, step: 0.5, group: grpTrend })); - let grpStruct = "Structure Settings"; - let hlLookbackInput = pinescript.inputInt(63, "Structure Lookback", ({ minval: 20, group: grpStruct })); - let penaltyFactor = pinescript.inputFloat(0.75, "Penalty for Lower Low", ({ minval: 0, maxval: 1, step: 0.05, group: grpStruct })); - let grpBonus = "Consistency Weight"; - let bonusMax = pinescript.inputInt(15, "Max Consistency Points", ({ minval: 5, maxval: 30, group: grpBonus })); - let scr_vol_len = pinescript.inputInt(50, "Avg Volume Length", ({ minval: 10, group: grpScreen })); - let rs_benchmark = input.symbol("SPY", "RS Benchmark", ({ group: grpScreen })); - let rs_len = pinescript.inputInt(21, "RS Period", ({ group: grpScreen })); - let adr_len = pinescript.inputInt(20, "ADR Period", ({ group: grpScreen })); - let grpColor = "Color & Thresholds"; - let thHigh = pinescript.inputInt(80, "Level 2 (High)", ({ minval: 60, maxval: 100, group: grpColor })); - let thLow = pinescript.inputInt(60, "Level 1 (Base)", ({ minval: 0, maxval: 80, group: grpColor })); - let cHigh = pinescript.inputColor(pinescript.color.hex("#00E676"), "Level 2 Color", ({ group: grpColor })); - let cLow = pinescript.inputColor(pinescript.color.hex("#2962FF"), "Level 1 Color", ({ group: grpColor })); - let cBase = pinescript.inputColor(pinescript.color.hex("#787B86"), "Base Color (<60)", ({ group: grpColor })); - let barCount = (bar_index + 1); - let lenLong = pinescript.min(barCount, lenLongInput); - let lenShort = pinescript.min(barCount, lenShortInput); - let lenVol = pinescript.min(barCount, lenVolInput); - let trVal = pinescript.tr(true); - let trShort = pinescript.sma(trVal, lenShort); - let trLongAvg = pinescript.sma(trVal, lenLong); - let ratioATR = (trShort / pinescript.max(trLongAvg, 0.000001)); - let stdShort = ta.stdev(close, lenShort); - let stdLongAvg = ta.stdev(close, lenLong); - let ratioStd = (stdShort / pinescript.max(stdLongAvg, 0.000001)); - let volAvg = pinescript.sma(volume, lenVol); - let volShortAvg = pinescript.sma(volume, 5); - let volRatio = (volShortAvg / pinescript.max(volAvg, 1)); - let netChange = pinescript.abs((close - pinescript.hist(0, close, lenShort))); - let totalTravel = pinescript.sum(pinescript.series(1, pinescript.tr(true)), lenShort); - let efficiency = (netChange / pinescript.max(totalTravel, 0.000001)); - let trendFactor = pinescript.max(0, (1 - (efficiency * trendPenaltyWeight))); - let lowRecent = pinescript.lowest(pinescript.series(2, low), lenShort); - let barsBeforeShort = (barCount - lenShortInput); - let hasHistory = (barsBeforeShort > 0); - let lowBase = 0; - let isHigherLow = true; + var grpCore = "VCP Logic Settings"; + var grpScreen = "Screener Data for Pine Screener"; + var lenShortInput = pinescript.inputInt(13, "Short Term", ({ minval: 3, group: grpCore })); + var lenLongInput = pinescript.inputInt(63, "Long Term", ({ minval: 20, group: grpCore })); + var lenVolInput = pinescript.inputInt(50, "Volume Average (Logic)", ({ minval: 10, group: grpCore })); + var sensitivity = 2; + var grpTrend = "Trend Filter"; + var trendPenaltyWeight = pinescript.inputFloat(1, "Trend Penalty Strength", ({ minval: 0, maxval: 5, step: 0.5, group: grpTrend })); + var grpStruct = "Structure Settings"; + var hlLookbackInput = pinescript.inputInt(63, "Structure Lookback", ({ minval: 20, group: grpStruct })); + var penaltyFactor = pinescript.inputFloat(0.75, "Penalty for Lower Low", ({ minval: 0, maxval: 1, step: 0.05, group: grpStruct })); + var grpBonus = "Consistency Weight"; + var bonusMax = pinescript.inputInt(15, "Max Consistency Points", ({ minval: 5, maxval: 30, group: grpBonus })); + var scr_vol_len = pinescript.inputInt(50, "Avg Volume Length", ({ minval: 10, group: grpScreen })); + var rs_benchmark = input.symbol("SPY", "RS Benchmark", ({ group: grpScreen })); + var rs_len = pinescript.inputInt(21, "RS Period", ({ group: grpScreen })); + var adr_len = pinescript.inputInt(20, "ADR Period", ({ group: grpScreen })); + var grpColor = "Color & Thresholds"; + var thHigh = pinescript.inputInt(80, "Level 2 (High)", ({ minval: 60, maxval: 100, group: grpColor })); + var thLow = pinescript.inputInt(60, "Level 1 (Base)", ({ minval: 0, maxval: 80, group: grpColor })); + var cHigh = pinescript.inputColor(pinescript.color.hex("#00E676"), "Level 2 Color", ({ group: grpColor })); + var cLow = pinescript.inputColor(pinescript.color.hex("#2962FF"), "Level 1 Color", ({ group: grpColor })); + var cBase = pinescript.inputColor(pinescript.color.hex("#787B86"), "Base Color (<60)", ({ group: grpColor })); + var barCount = (bar_index + 1); + var lenLong = pinescript.min(barCount, lenLongInput); + var lenShort = pinescript.min(barCount, lenShortInput); + var lenVol = pinescript.min(barCount, lenVolInput); + var trVal = pinescript.tr(true); + var trShort = pinescript.sma(pinescript.series(0, trVal), lenShort); + var trLongAvg = pinescript.sma(pinescript.series(1, trVal), lenLong); + var ratioATR = (trShort / pinescript.max(trLongAvg, 0.000001)); + var stdShort = pinescript.ta.stdev(close, lenShort); + var stdLongAvg = pinescript.ta.stdev(close, lenLong); + var ratioStd = (stdShort / pinescript.max(stdLongAvg, 0.000001)); + var volAvg = pinescript.sma(pinescript.series(2, volume), lenVol); + var volShortAvg = pinescript.sma(pinescript.series(3, volume), 5); + var volRatio = (volShortAvg / pinescript.max(volAvg, 1)); + var netChange = pinescript.abs((close - pinescript.hist(4, close, lenShort))); + var totalTravel = pinescript.sum(pinescript.series(5, pinescript.tr(true)), lenShort); + var efficiency = (netChange / pinescript.max(totalTravel, 0.000001)); + var trendFactor = pinescript.max(0, (1 - (efficiency * trendPenaltyWeight))); + var lowRecent = pinescript.lowest(pinescript.series(6, low), lenShort); + var barsBeforeShort = (barCount - lenShortInput); + var hasHistory = (barsBeforeShort > 0); + var lowBase = 0; + var isHigherLow = true; if (hasHistory) { - lowBase = pinescript.hist(3, pinescript.lowest(pinescript.series(4, low), hlLookbackInput), lenShortInput); + lowBase = pinescript.hist(7, pinescript.lowest(pinescript.series(8, low), hlLookbackInput), lenShortInput); isHigherLow = (lowRecent >= lowBase); } - let s_atr = (pinescript.max(0, (1 - pinescript.nz(ratioATR, 1))) * sensitivity); - let s_std = (pinescript.max(0, (1 - pinescript.nz(ratioStd, 1))) * sensitivity); - let s_vol = pinescript.max(0, (1 - pinescript.nz(volRatio, 1))); - let rawScore = (((s_atr * 0.4) + (s_std * 0.4)) + (s_vol * 0.2)); - let filteredScore = (rawScore * trendFactor); - let physicsScore = pinescript.min(100, (filteredScore * 100)); - let smoothPhysics = pinescript.ema(physicsScore, 3); - let isTight = (smoothPhysics >= 70); + var s_atr = (pinescript.max(0, (1 - pinescript.nz(ratioATR, 1))) * sensitivity); + var s_std = (pinescript.max(0, (1 - pinescript.nz(ratioStd, 1))) * sensitivity); + var s_vol = pinescript.max(0, (1 - pinescript.nz(volRatio, 1))); + var rawScore = (((s_atr * 0.4) + (s_std * 0.4)) + (s_vol * 0.2)); + var filteredScore = (rawScore * trendFactor); + var physicsScore = pinescript.min(100, (filteredScore * 100)); + var smoothPhysics = pinescript.ema(pinescript.series(9, physicsScore), 3); + var isTight = (smoothPhysics >= 70); if (state.daysTight === undefined) state.daysTight = 0; state.daysTight = (isTight ? (state.daysTight + 1) : 0); - let weightPhysics = ((100 - bonusMax) / 100); - let weightedPhysicsScore = (smoothPhysics * weightPhysics); - let consistencyScore = pinescript.min(bonusMax, state.daysTight); - let totalScore = (weightedPhysicsScore + consistencyScore); - let finalScore = (isHigherLow ? totalScore : (totalScore * penaltyFactor)); + var weightPhysics = ((100 - bonusMax) / 100); + var weightedPhysicsScore = (smoothPhysics * weightPhysics); + var consistencyScore = pinescript.min(bonusMax, state.daysTight); + var totalScore = (weightedPhysicsScore + consistencyScore); + var finalScore = (isHigherLow ? totalScore : (totalScore * penaltyFactor)); finalScore = pinescript.nz(finalScore, 0); - let colZone = ((finalScore >= thHigh) ? cHigh : ((finalScore >= thLow) ? cLow : cBase)); + var colZone = ((finalScore >= thHigh) ? cHigh : ((finalScore >= thLow) ? cLow : cBase)); pinescript.plot(finalScore, "VCS", ({ color: colZone, style: plot.style_histogram, linewidth: 1 })); pinescript.hline(thLow, "Threshold", ({ color: pinescript.color.gray, linestyle: hline.style_dotted })); - let scr_vol_val = pinescript.sma(volume, scr_vol_len); + var scr_vol_val = pinescript.sma(pinescript.series(10, volume), scr_vol_len); pinescript.plot(scr_vol_val, "Avg Vol", ({ display: display.data_window })); - let mCapBasic = pinescript.requestFinancial(syminfo.tickerid, "MARKET_CAP_BASIC", "D", ({ ignore_invalid_symbol: true })); - let mCapFinal = (pinescript.na(mCapBasic) ? (pinescript.nz(syminfo.shares_outstanding_total) * close) : mCapBasic); + var mCapBasic = pinescript.requestFinancial(syminfo.tickerid, "MARKET_CAP_BASIC", "D", ({ ignore_invalid_symbol: true })); + var mCapFinal = (pinescript.na(mCapBasic) ? (pinescript.nz(syminfo.shares_outstanding_total) * close) : mCapBasic); pinescript.plot(mCapFinal, "Market Cap", ({ display: display.data_window })); - let adr_val = (100 * (pinescript.sma((high / low), adr_len) - 1)); + var adr_val = (100 * (pinescript.sma(pinescript.series(11, (high / low)), adr_len) - 1)); pinescript.plot(adr_val, "ADR %", ({ display: display.data_window })); - let benchmark_close = pinescript.requestSecurity(rs_benchmark, timeframe.period, close, ({ ignore_invalid_symbol: true })); - let rs_ratio = (close / benchmark_close); - let rs_rank = pinescript.percentrank(rs_ratio, rs_len); + var benchmark_close = pinescript.requestSecurity(rs_benchmark, timeframe.period, close, ({ ignore_invalid_symbol: true })); + var rs_ratio = (close / benchmark_close); + var rs_rank = pinescript.percentrank(rs_ratio, rs_len); pinescript.plot(rs_rank, "Relative Strength", ({ display: display.data_window })); } @@ -1812,6 +1971,10 @@ function run(data, options = {}) { rt.__shapeIdx = 0; globalThis.bar_index = i; globalThis.last_bar_index = n - 1; + globalThis.time_tradingday = inTime[i]; + globalThis.time_close = inTime[i]; + globalThis.last_bar_time = inTime[n - 1]; + globalThis.timenow = inTime[n - 1]; globalThis.barstate = { isfirst: i === 0, islast: i === n - 1, isrealtime: false, ishistory: true, isconfirmed: true, isnew: true, islastconfirmedhistory: i === n - 1, diff --git a/converted/Volume_Surprise_LuxAlgo.js b/converted/Volume_Surprise_LuxAlgo.js index ffa2591..ae5a8a0 100644 --- a/converted/Volume_Surprise_LuxAlgo.js +++ b/converted/Volume_Surprise_LuxAlgo.js @@ -42,6 +42,80 @@ const pinescript = { if (value != null && typeof value[Symbol.iterator] === 'function') return Array.from(value); return new Array(count || 0).fill(null); }, + __decArr: function(arr) { + if (!Array.isArray(arr) || arr.__pineDecorated) return arr; + const self = this; + const def = (name, fn) => Object.defineProperty(arr, name, { value: fn, writable: true, configurable: true }); + Object.defineProperty(arr, '__pineDecorated', { value: true, configurable: true }); + def('get', (i) => (arr[i] === undefined ? null : arr[i])); + def('set', (i, v) => { arr[i] = v; return v; }); + def('size', () => arr.length); + def('clear', () => { arr.length = 0; }); + def('insert', (i, v) => { arr.splice(i, 0, v); }); + def('remove', (i) => arr.splice(i, 1)[0]); + def('contains', (v) => arr.includes(v)); + def('indexof', (v) => arr.indexOf(v)); + def('lastindexof', (v) => arr.lastIndexOf(v)); + def('first', () => (arr.length ? arr[0] : null)); + def('last', () => (arr.length ? arr[arr.length - 1] : null)); + def('sum', () => arr.reduce((a, b) => a + b, 0)); + def('avg', () => (arr.length ? arr.reduce((a, b) => a + b, 0) / arr.length : 0)); + def('min', () => (arr.length ? Math.min(...arr) : null)); + def('max', () => (arr.length ? Math.max(...arr) : null)); + def('range', () => (arr.length ? Math.max(...arr) - Math.min(...arr) : null)); + // Statistical methods that route to the array.* built-ins (these names are not + // native to JS arrays, so attaching them here is safe from recursion). + def('median', () => self.arrayMedian(arr)); + def('mode', () => self.arrayMode(arr)); + def('stdev', () => self.arrayStdev(arr)); + def('variance', () => self.arrayVariance(arr)); + def('covariance', (other) => self.arrayCovariance(arr, other)); + def('percentile_linear_interpolation', (p) => self.arrayPercentileLinearInterpolation(arr, p)); + def('percentile_nearest_rank', (p) => self.arrayPercentileNearestRank(arr, p)); + def('abs', () => self.__decArr(self.arrayAbs(arr))); + def('binary_search', (v) => self.arrayBinarySearch(arr, v)); + // Pine's array.sort takes an order string, not a comparator. Use the native + // sort via .call so we don't recurse through this overridden method. + def('sort', (order) => { Array.prototype.sort.call(arr, (a, b) => (order === 'descending' ? b - a : a - b)); return arr; }); + def('sort_indices', (order) => self.__decArr(arr.map((_, i) => i).sort((a, b) => (order === 'descending' ? arr[b] - arr[a] : arr[a] - arr[b])))); + // join/slice/reverse/concat/includes/fill already exist natively on Array with + // compatible semantics, so we deliberately leave them to the native methods. + return arr; + }, + __decMap: function(m) { + if (!(m instanceof Map) || m.__pineDecorated) return m; + const def = (name, fn) => Object.defineProperty(m, name, { value: fn, writable: true, configurable: true }); + Object.defineProperty(m, '__pineDecorated', { value: true, configurable: true }); + def('put', (k, v) => { m.set(k, v); return v; }); + def('contains', (k) => m.has(k)); + def('remove', (k) => m.delete(k)); + def('keys', () => Array.from(Map.prototype.keys.call(m))); + def('values', () => Array.from(Map.prototype.values.call(m))); + def('size_', () => m.size); + return m; + }, + __decMatrix: function(m) { + if (!m || typeof m !== 'object' || m.__pineDecorated) return m; + const self = this; + const def = (name, fn) => Object.defineProperty(m, name, { value: fn, writable: true, configurable: true }); + Object.defineProperty(m, '__pineDecorated', { value: true, configurable: true }); + def('get', (r, c) => self.matrixGet(m, r, c)); + def('set', (r, c, v) => self.matrixSet(m, r, c, v)); + def('rows_', () => m.rows); + def('columns', () => m.cols); + def('fill', (v) => self.matrixFill(m, v)); + return m; + }, + __decDraw: function(obj) { + if (!obj || typeof obj !== 'object') return obj; + const p = new Proxy(obj, { + get(t, k) { + if (k in t || typeof k === 'symbol') return t[k]; + return function() { return p; }; + }, + }); + return p; + }, alertcondition: function(condition, ...rest) { if (globalThis.__pineRuntime) { globalThis.__pineRuntime.alerts.push({ condition, args: rest }); @@ -49,6 +123,32 @@ const pinescript = { return null; }, barcolor: function(color) { return null; }, + plotchar: function(series, ...rest) { + const rt = globalThis.__pineRuntime; + if (!rt) return series; + const bar = rt.__barIndex | 0; + const ord = (rt.__shapeIdx = (rt.__shapeIdx | 0) + 1) - 1; + let key = 'char_' + ord; + for (const r of rest) { + if (typeof r === 'string') { key = r; break; } + if (r && typeof r === 'object' && r.title) { key = String(r.title); break; } + } + let s = rt.plotshapes[key]; + if (!s) s = rt.plotshapes[key] = { title: key, data: [] }; + s.data[bar] = this.__scalar(series); + return series; + }, + plotarrow: function(series, ...rest) { + const rt = globalThis.__pineRuntime; + if (!rt) return series; + const bar = rt.__barIndex | 0; + const ord = (rt.__plotIdx = (rt.__plotIdx | 0) + 1) - 1; + const key = 'arrow_' + ord; + let p = rt.plots[key]; + if (!p) p = rt.plots[key] = { title: key, data: [] }; + p.data[bar] = this.__scalar(series); + return series; + }, bgcolor: function(color, title, editable, showLast) { return null; }, @@ -470,6 +570,12 @@ const pinescript = { atan: function(value) { return Math.atan(value); }, + todegrees: function(radians) { + return radians * (180 / Math.PI); + }, + toradians: function(degrees) { + return degrees * (Math.PI / 180); + }, floor: function(value) { return Math.floor(value); }, @@ -641,7 +747,7 @@ const pinescript = { return series; }, lineNew: function(x1, y1, x2, y2, opts = {}) { - return { x1, y1, x2, y2, opts, _type: 'line' }; + return this.__decDraw({ x1, y1, x2, y2, opts, _type: 'line' }); }, lineDelete: function(l) { return null; @@ -661,7 +767,7 @@ const pinescript = { return point === 0 || point === 'y1' ? line.y1 : line.y2; }, labelNew: function(x, y, text = '', opts = {}) { - return { x, y, text, opts, _type: 'label' }; + return this.__decDraw({ x, y, text, opts, _type: 'label' }); }, labelDelete: function(l) { return null; @@ -676,7 +782,7 @@ const pinescript = { return label.text || ''; }, boxNew: function(left, top, right, bottom, opts = {}) { - return { left, top, right, bottom, opts, _type: 'box' }; + return this.__decDraw({ left, top, right, bottom, opts, _type: 'box' }); }, boxDelete: function(box) { return null; @@ -694,7 +800,7 @@ const pinescript = { return box; }, polylineNew: function(points, opts = {}) { - return { points: points || [], opts, _type: 'polyline' }; + return this.__decDraw({ points: points || [], opts, _type: 'polyline' }); }, polylineDelete: function(poly) { return null; @@ -765,7 +871,7 @@ const pinescript = { return { time: _time ?? null, price: _price ?? null }; }, mapNew: function() { - return new Map(); + return this.__decMap(new Map()); }, mapSize: function(m) { return m instanceof Map ? m.size : 0; @@ -800,7 +906,7 @@ const pinescript = { const r = Math.max(0, rows ?? 0); const c = Math.max(0, cols ?? 0); const data = Array.from({ length: r }, () => Array.from({ length: c }, () => initialValue)); - return { rows: r, cols: c, data }; + return this.__decMatrix({ rows: r, cols: c, data }); }, matrixRows: function(m) { return m?.rows ?? 0; @@ -923,11 +1029,62 @@ const pinescript = { } return null; }, + __jacobiEigen: function(m) { + if (!m || !Array.isArray(m.data)) return null; + const n = m.rows ?? 0; + if (n === 0 || n !== (m.cols ?? 0)) return null; + // Work on a copy so the input matrix is untouched. + const a = m.data.map(row => row.slice()); + const v = Array.from({ length: n }, (_, i) => Array.from({ length: n }, (_, j) => (i === j ? 1 : 0))); + for (let sweep = 0; sweep < 100; sweep++) { + let off = 0; + for (let p = 0; p < n; p++) for (let q = p + 1; q < n; q++) off += a[p][q] * a[p][q]; + if (off < 1e-20) break; + for (let p = 0; p < n; p++) { + for (let q = p + 1; q < n; q++) { + if (Math.abs(a[p][q]) < 1e-18) continue; + const theta = (a[q][q] - a[p][p]) / (2 * a[p][q]); + const t = Math.sign(theta || 1) / (Math.abs(theta) + Math.sqrt(theta * theta + 1)); + const cos = 1 / Math.sqrt(t * t + 1); + const sin = t * cos; + for (let k = 0; k < n; k++) { + const akp = a[k][p], akq = a[k][q]; + a[k][p] = cos * akp - sin * akq; + a[k][q] = sin * akp + cos * akq; + } + for (let k = 0; k < n; k++) { + const apk = a[p][k], aqk = a[q][k]; + a[p][k] = cos * apk - sin * aqk; + a[q][k] = sin * apk + cos * aqk; + } + for (let k = 0; k < n; k++) { + const vkp = v[k][p], vkq = v[k][q]; + v[k][p] = cos * vkp - sin * vkq; + v[k][q] = sin * vkp + cos * vkq; + } + } + } + } + // Sort eigenpairs by eigenvalue, descending. + const order = Array.from({ length: n }, (_, i) => i).sort((i, j) => a[j][j] - a[i][i]); + const values = order.map(i => a[i][i]); + const vectors = Array.from({ length: n }, (_, r) => order.map(c => v[r][c])); + return { values, vectors }; + }, + matrixEigenvalues: function(m) { + const e = this.__jacobiEigen(m); + return this.__decArr(e ? e.values : []); + }, + matrixEigenvectors: function(m) { + const e = this.__jacobiEigen(m); + if (!e) return this.__decMatrix({ rows: 0, cols: 0, data: [] }); + return this.__decMatrix({ rows: e.vectors.length, cols: e.vectors.length, data: e.vectors }); + }, requestSecurity: function(symbol, timeframe, expression) { return expression; }, arrayNew: function(initialSize = 0, initialValue = 0) { - return Array(initialSize).fill(initialValue); + return this.__decArr(Array(initialSize).fill(initialValue)); }, arraySize: function(arr) { return arr ? arr.length : 0; @@ -978,10 +1135,10 @@ const pinescript = { return arr; }, arraySlice: function(arr, startIndex = 0, endIndex = null) { - if (!arr) return []; + if (!arr) return this.__decArr([]); const start = Number(startIndex) || 0; const end = endIndex === null || endIndex === undefined ? arr.length : Number(endIndex) || 0; - return arr.slice(start, end); + return this.__decArr(arr.slice(start, end)); }, arraySort: function(arr, order = 'ascending') { if (arr) arr.sort((a, b) => order === 'ascending' ? a - b : b - a); @@ -1191,7 +1348,7 @@ const pinescript = { const info = { ticker: 'AAPL', tickerid: 'NASDAQ:AAPL', prefix: 'NASDAQ', root: 'AAPL', suffix: '' }; return info[type] || ''; }, - timenow: 1781574529071, + timenow: 1782783578120, barstate: "LAST", dividends: {}, splits: {}, @@ -1245,11 +1402,11 @@ const pinescript = { arrayConcat: function(arr1, arr2) { if (!arr1) return arr2 || []; if (!arr2) return arr1; - return arr1.concat(arr2); + return this.__decArr(arr1.concat(arr2)); }, arrayCopy: function(arr) { - if (!arr) return []; - return [...arr]; + if (!arr) return this.__decArr([]); + return this.__decArr([...arr]); }, arrayBinarySearch: function(arr, value) { if (!arr || arr.length === 0) return -1; @@ -1339,7 +1496,7 @@ globalThis.input.timeframe = globalThis.input.timeframe || ((defval) => defval); globalThis.array = globalThis.array || { - from: (...items) => items, + from: (...items) => pinescript.__decArr(items), size: (arr) => pinescript.arraySize(arr), @@ -1508,6 +1665,8 @@ globalThis.line = globalThis.line || __pineNS({ style_solid: "solid", style_dash globalThis.box = globalThis.box || __pineNS({}); +globalThis.color = globalThis.color || __pineNS(Object.assign(function(c) { return c; }, { new: function(c, t) { return c; }, rgb: function(r, g, b, t) { return "#rgb(" + [r, g, b].join(",") + ")"; }, from_gradient: function(v, lo, hi, c1, c2) { return c1; }, r: function() { return 0; }, g: function() { return 0; }, b: function() { return 0; }, t: function() { return 0; }, aqua: "#00BCD4", black: "#363A45", blue: "#2962FF", fuchsia: "#E040FB", gray: "#787B86", green: "#4CAF50", lime: "#00E676", maroon: "#880E4F", navy: "#311B92", olive: "#808000", orange: "#FF9800", purple: "#9C27B0", red: "#FF5252", silver: "#B2B5BE", teal: "#00897B", white: "#FFFFFF", yellow: "#FFEB3B" })); + globalThis.label = globalThis.label || __pineNS({ style_label_down: "label_down", style_label_up: "label_up", style_none: "none" }); globalThis.polyline = globalThis.polyline || __pineNS({}); @@ -1520,7 +1679,9 @@ globalThis.map = globalThis.map || __pineNS({}); globalThis.session = globalThis.session || __pineNS({ regular: "regular", extended: "extended" }); -globalThis.dayofweek = globalThis.dayofweek || __pineNS({ sunday: 1, monday: 2, tuesday: 3, wednesday: 4, thursday: 5, friday: 6, saturday: 7 }); +globalThis.ticker = globalThis.ticker || __pineNS({}); + +globalThis.dayofweek = globalThis.dayofweek || __pineNS(Object.assign(function(t) { return new Date(t != null ? t : (globalThis.time || 0)).getUTCDay() + 1; }, { sunday: 1, monday: 2, tuesday: 3, wednesday: 4, thursday: 5, friday: 6, saturday: 7 })); globalThis.timeframe = __pineNS(Object.assign(globalThis.timeframe || {}, { period: (globalThis.timeframe && globalThis.timeframe.period) || "D", isintraday: false, isdaily: true, multiplier: 1 })); @@ -1568,8 +1729,6 @@ globalThis.bool = globalThis.bool || function(x) { return Boolean(x); }; globalThis.string = globalThis.string || function(x) { return x == null ? null : String(x); }; -globalThis.dayofweek = globalThis.dayofweek || __pineNS({ sunday: 1, monday: 2, tuesday: 3, wednesday: 4, thursday: 5, friday: 6, saturday: 7 }); - globalThis.str = globalThis.str || __pineNS({}); pinescript.math = globalThis.math; @@ -1655,7 +1814,7 @@ pinescript.text = { align_center: "center" }; pinescript.table = { - new: function(position, columns, rows, opts) { return { position, columns, rows, opts: opts || {}, cells: [] }; }, + new: function(position, columns, rows, opts) { return pinescript.__decDraw({ position, columns, rows, opts: opts || {}, cells: [] }); }, cell: function(table, column, row, text, opts) { @@ -1689,25 +1848,25 @@ function main() { null; // Study: Volume Surprise [LuxAlgo] // Options: {"overlay":false,"format":null} - let length = pinescript.inputInt(50, ({ minval: 1 })); - let smooth = pinescript.inputInt(1, ({ minval: 1 })); - let autoFormat = pinescript.input(true, "Auto Selection", ({ group: "Periods", tooltip: "Automatically choose a practical combination of periods based on the chart timeframe" })); - let considerMinutes = pinescript.input(true, "Minutes", ({ group: "Periods" })); - let considerHours = pinescript.input(true, "Hours", ({ group: "Periods" })); - let considerDays = pinescript.input(false, "Days", ({ inline: "day", group: "Periods" })); - let daysOptions = pinescript.inputString("Day of Week", "", ({ options: ["Day of Week", "Day of Month", "Day of Year"], inline: "day", group: "Periods" })); - let considerMonths = pinescript.input(false, "Months", ({ group: "Periods" })); - let considerQuarters = pinescript.input(false, "Quarters", ({ group: "Periods" })); - let summaryMethod = pinescript.inputString("Mean", "Method", ({ options: ["Mean", "Percentile"], group: "Summary" })); - let percentile = pinescript.input(50, ({ active: (summaryMethod === "Percentile"), group: "Summary" })); - let showForecast = pinescript.input(true, "Show Forecast", ({ group: "Forecast" })); - let forecastWindow = pinescript.inputInt(50, "Forecast Window", ({ minval: 1, maxval: 499, group: "Forecast" })); - let forecastStyleColor = pinescript.input(pinescript.color.hex("#2962ff"), "Style", ({ inline: "forecast_style", group: "Forecast" })); - let forecastStyleLine = pinescript.inputString("Dotted", "", ({ options: ["Solid", "Dashed", "Dotted"], inline: "forecast_style", group: "Forecast" })); + var length = pinescript.inputInt(50, ({ minval: 1 })); + var smooth = pinescript.inputInt(1, ({ minval: 1 })); + var autoFormat = pinescript.input(true, "Auto Selection", ({ group: "Periods", tooltip: "Automatically choose a practical combination of periods based on the chart timeframe" })); + var considerMinutes = pinescript.input(true, "Minutes", ({ group: "Periods" })); + var considerHours = pinescript.input(true, "Hours", ({ group: "Periods" })); + var considerDays = pinescript.input(false, "Days", ({ inline: "day", group: "Periods" })); + var daysOptions = pinescript.inputString("Day of Week", "", ({ options: ["Day of Week", "Day of Month", "Day of Year"], inline: "day", group: "Periods" })); + var considerMonths = pinescript.input(false, "Months", ({ group: "Periods" })); + var considerQuarters = pinescript.input(false, "Quarters", ({ group: "Periods" })); + var summaryMethod = pinescript.inputString("Mean", "Method", ({ options: ["Mean", "Percentile"], group: "Summary" })); + var percentile = pinescript.input(50, ({ active: (summaryMethod === "Percentile"), group: "Summary" })); + var showForecast = pinescript.input(true, "Show Forecast", ({ group: "Forecast" })); + var forecastWindow = pinescript.inputInt(50, "Forecast Window", ({ minval: 1, maxval: 499, group: "Forecast" })); + var forecastStyleColor = pinescript.input(pinescript.color.hex("#2962ff"), "Style", ({ inline: "forecast_style", group: "Forecast" })); + var forecastStyleLine = pinescript.inputString("Dotted", "", ({ options: ["Solid", "Dashed", "Dotted"], inline: "forecast_style", group: "Forecast" })); function getSummary(id, key) { id.get(key).v.percentile_linear_interpolation(percentile); id.get(key).v.avg(); - let summary = ((summaryMethod === "Mean") ? undefined : ((summaryMethod === "Percentile") ? undefined : null)); + var summary = ((summaryMethod === "Mean") ? undefined : ((summaryMethod === "Percentile") ? undefined : null)); } if ((barstate.isfirst && autoFormat)) { if (timeframe.isseconds) { @@ -1742,38 +1901,38 @@ function main() { } if (state.format === undefined) state.format = pinescript.strFormat("{0}{1}{2}{3}{4}ss", (considerQuarters ? pinescript.strToString(pinescript.ceil((month / 3))) : ""), (considerMonths ? "MM:" : ""), (considerDays ? ((daysOptions === "Day of Week") ? "EEE:" : ((daysOptions === "Day of Month") ? "dd:" : "DDD:")) : ""), (considerHours ? "hh:" : ""), (considerMinutes ? "mm:" : "")); if (state.data === undefined) state.data = pinescript.mapNew(); - let vol = pinescript.sma(volume, smooth); - let key = str.format_time(time, state.format); - if (!data.keys().includes(key)) { - data.put(key, vector.new(pinescript.arrayNew(0))); + var vol = pinescript.sma(pinescript.series(0, volume), smooth); + var key = pinescript.str.format_time(time, state.format); + if (!state.data.keys().includes(key)) { + state.data.put(key, vector.new(pinescript.arrayNew(0))); } - summary = getSummary(state.data, key); - data.get(key).v.push(vol); - if ((data.get(key).v.size() > length)) { - data.get(key).v.shift(); + var summary = getSummary(state.data, key); + state.data.get(key).v.push(vol); + if ((state.data.get(key).v.size() > length)) { + state.data.get(key).v.shift(); } line.style_dotted; line.style_dashed; line.style_solid; if (state.forecastStyle === undefined) state.forecastStyle = ((forecastStyleLine === "Solid") ? undefined : ((forecastStyleLine === "Dashed") ? undefined : ((forecastStyleLine === "Dotted") ? undefined : null))); if ((showForecast && barstate.islast)) { - let forecastCoordinates = pinescript.arrayNew(0); + var forecastCoordinates = pinescript.arrayNew(0); forecastCoordinates.push(chart.point.from_time(time, summary)); for (let i = 1; i <= forecastWindow; i++) { - let t = pinescript.time(timeframe.period, ({ bars_back: -i })); - let forecastKey = str.format_time(t, state.format); - if (data.keys().includes(forecastKey)) { - let forecastValue = getSummary(state.data, forecastKey); + var t = pinescript.time(timeframe.period, ({ bars_back: -i })); + var forecastKey = pinescript.str.format_time(t, state.format); + if (state.data.keys().includes(forecastKey)) { + var forecastValue = getSummary(state.data, forecastKey); forecastCoordinates.push(chart.point.from_time(t, forecastValue)); } else { forecastCoordinates.push(chart.point.from_time(t, null)); } } - pinescript.polylineDelete(pinescript.hist(0, pinescript.polylineNew(forecastCoordinates, ({ xloc: xloc.bar_time, line_style: state.forecastStyle, line_color: forecastStyleColor })), 1)); + pinescript.polylineDelete(pinescript.hist(1, pinescript.polylineNew(forecastCoordinates, ({ xloc: xloc.bar_time, line_style: state.forecastStyle, line_color: forecastStyleColor })), 1)); } - let css = ((vol > summary) ? pinescript.color.new(pinescript.color.hex("#089981"), 50) : pinescript.color.new(pinescript.color.hex("#f23645"), 50)); - let plotVolume = pinescript.plot(vol, "Volume", ({ color: pinescript.color.gray })); - let plotEv = pinescript.plot(summary, "Expected Volume", ({ color: pinescript.color.orange, linestyle: plot.linestyle_dotted })); + var css = ((vol > summary) ? pinescript.color.new(pinescript.color.hex("#089981"), 50) : pinescript.color.new(pinescript.color.hex("#f23645"), 50)); + var plotVolume = pinescript.plot(vol, "Volume", ({ color: pinescript.color.gray })); + var plotEv = pinescript.plot(summary, "Expected Volume", ({ color: pinescript.color.orange, linestyle: plot.linestyle_dotted })); pinescript.plot((vol - summary), "Difference", ({ style: plot.style_columns, color: css })); } @@ -1819,6 +1978,10 @@ function run(data, options = {}) { rt.__shapeIdx = 0; globalThis.bar_index = i; globalThis.last_bar_index = n - 1; + globalThis.time_tradingday = inTime[i]; + globalThis.time_close = inTime[i]; + globalThis.last_bar_time = inTime[n - 1]; + globalThis.timenow = inTime[n - 1]; globalThis.barstate = { isfirst: i === 0, islast: i === n - 1, isrealtime: false, ishistory: true, isconfirmed: true, isnew: true, islastconfirmedhistory: i === n - 1, diff --git a/converted/Volume_Weighted_Trend_QuantAlgo.js b/converted/Volume_Weighted_Trend_QuantAlgo.js index 179bfab..5e740fc 100644 --- a/converted/Volume_Weighted_Trend_QuantAlgo.js +++ b/converted/Volume_Weighted_Trend_QuantAlgo.js @@ -42,6 +42,80 @@ const pinescript = { if (value != null && typeof value[Symbol.iterator] === 'function') return Array.from(value); return new Array(count || 0).fill(null); }, + __decArr: function(arr) { + if (!Array.isArray(arr) || arr.__pineDecorated) return arr; + const self = this; + const def = (name, fn) => Object.defineProperty(arr, name, { value: fn, writable: true, configurable: true }); + Object.defineProperty(arr, '__pineDecorated', { value: true, configurable: true }); + def('get', (i) => (arr[i] === undefined ? null : arr[i])); + def('set', (i, v) => { arr[i] = v; return v; }); + def('size', () => arr.length); + def('clear', () => { arr.length = 0; }); + def('insert', (i, v) => { arr.splice(i, 0, v); }); + def('remove', (i) => arr.splice(i, 1)[0]); + def('contains', (v) => arr.includes(v)); + def('indexof', (v) => arr.indexOf(v)); + def('lastindexof', (v) => arr.lastIndexOf(v)); + def('first', () => (arr.length ? arr[0] : null)); + def('last', () => (arr.length ? arr[arr.length - 1] : null)); + def('sum', () => arr.reduce((a, b) => a + b, 0)); + def('avg', () => (arr.length ? arr.reduce((a, b) => a + b, 0) / arr.length : 0)); + def('min', () => (arr.length ? Math.min(...arr) : null)); + def('max', () => (arr.length ? Math.max(...arr) : null)); + def('range', () => (arr.length ? Math.max(...arr) - Math.min(...arr) : null)); + // Statistical methods that route to the array.* built-ins (these names are not + // native to JS arrays, so attaching them here is safe from recursion). + def('median', () => self.arrayMedian(arr)); + def('mode', () => self.arrayMode(arr)); + def('stdev', () => self.arrayStdev(arr)); + def('variance', () => self.arrayVariance(arr)); + def('covariance', (other) => self.arrayCovariance(arr, other)); + def('percentile_linear_interpolation', (p) => self.arrayPercentileLinearInterpolation(arr, p)); + def('percentile_nearest_rank', (p) => self.arrayPercentileNearestRank(arr, p)); + def('abs', () => self.__decArr(self.arrayAbs(arr))); + def('binary_search', (v) => self.arrayBinarySearch(arr, v)); + // Pine's array.sort takes an order string, not a comparator. Use the native + // sort via .call so we don't recurse through this overridden method. + def('sort', (order) => { Array.prototype.sort.call(arr, (a, b) => (order === 'descending' ? b - a : a - b)); return arr; }); + def('sort_indices', (order) => self.__decArr(arr.map((_, i) => i).sort((a, b) => (order === 'descending' ? arr[b] - arr[a] : arr[a] - arr[b])))); + // join/slice/reverse/concat/includes/fill already exist natively on Array with + // compatible semantics, so we deliberately leave them to the native methods. + return arr; + }, + __decMap: function(m) { + if (!(m instanceof Map) || m.__pineDecorated) return m; + const def = (name, fn) => Object.defineProperty(m, name, { value: fn, writable: true, configurable: true }); + Object.defineProperty(m, '__pineDecorated', { value: true, configurable: true }); + def('put', (k, v) => { m.set(k, v); return v; }); + def('contains', (k) => m.has(k)); + def('remove', (k) => m.delete(k)); + def('keys', () => Array.from(Map.prototype.keys.call(m))); + def('values', () => Array.from(Map.prototype.values.call(m))); + def('size_', () => m.size); + return m; + }, + __decMatrix: function(m) { + if (!m || typeof m !== 'object' || m.__pineDecorated) return m; + const self = this; + const def = (name, fn) => Object.defineProperty(m, name, { value: fn, writable: true, configurable: true }); + Object.defineProperty(m, '__pineDecorated', { value: true, configurable: true }); + def('get', (r, c) => self.matrixGet(m, r, c)); + def('set', (r, c, v) => self.matrixSet(m, r, c, v)); + def('rows_', () => m.rows); + def('columns', () => m.cols); + def('fill', (v) => self.matrixFill(m, v)); + return m; + }, + __decDraw: function(obj) { + if (!obj || typeof obj !== 'object') return obj; + const p = new Proxy(obj, { + get(t, k) { + if (k in t || typeof k === 'symbol') return t[k]; + return function() { return p; }; + }, + }); + return p; + }, alertcondition: function(condition, ...rest) { if (globalThis.__pineRuntime) { globalThis.__pineRuntime.alerts.push({ condition, args: rest }); @@ -49,6 +123,32 @@ const pinescript = { return null; }, barcolor: function(color) { return null; }, + plotchar: function(series, ...rest) { + const rt = globalThis.__pineRuntime; + if (!rt) return series; + const bar = rt.__barIndex | 0; + const ord = (rt.__shapeIdx = (rt.__shapeIdx | 0) + 1) - 1; + let key = 'char_' + ord; + for (const r of rest) { + if (typeof r === 'string') { key = r; break; } + if (r && typeof r === 'object' && r.title) { key = String(r.title); break; } + } + let s = rt.plotshapes[key]; + if (!s) s = rt.plotshapes[key] = { title: key, data: [] }; + s.data[bar] = this.__scalar(series); + return series; + }, + plotarrow: function(series, ...rest) { + const rt = globalThis.__pineRuntime; + if (!rt) return series; + const bar = rt.__barIndex | 0; + const ord = (rt.__plotIdx = (rt.__plotIdx | 0) + 1) - 1; + const key = 'arrow_' + ord; + let p = rt.plots[key]; + if (!p) p = rt.plots[key] = { title: key, data: [] }; + p.data[bar] = this.__scalar(series); + return series; + }, bgcolor: function(color, title, editable, showLast) { return null; }, @@ -470,6 +570,12 @@ const pinescript = { atan: function(value) { return Math.atan(value); }, + todegrees: function(radians) { + return radians * (180 / Math.PI); + }, + toradians: function(degrees) { + return degrees * (Math.PI / 180); + }, floor: function(value) { return Math.floor(value); }, @@ -641,7 +747,7 @@ const pinescript = { return series; }, lineNew: function(x1, y1, x2, y2, opts = {}) { - return { x1, y1, x2, y2, opts, _type: 'line' }; + return this.__decDraw({ x1, y1, x2, y2, opts, _type: 'line' }); }, lineDelete: function(l) { return null; @@ -661,7 +767,7 @@ const pinescript = { return point === 0 || point === 'y1' ? line.y1 : line.y2; }, labelNew: function(x, y, text = '', opts = {}) { - return { x, y, text, opts, _type: 'label' }; + return this.__decDraw({ x, y, text, opts, _type: 'label' }); }, labelDelete: function(l) { return null; @@ -676,7 +782,7 @@ const pinescript = { return label.text || ''; }, boxNew: function(left, top, right, bottom, opts = {}) { - return { left, top, right, bottom, opts, _type: 'box' }; + return this.__decDraw({ left, top, right, bottom, opts, _type: 'box' }); }, boxDelete: function(box) { return null; @@ -694,7 +800,7 @@ const pinescript = { return box; }, polylineNew: function(points, opts = {}) { - return { points: points || [], opts, _type: 'polyline' }; + return this.__decDraw({ points: points || [], opts, _type: 'polyline' }); }, polylineDelete: function(poly) { return null; @@ -765,7 +871,7 @@ const pinescript = { return { time: _time ?? null, price: _price ?? null }; }, mapNew: function() { - return new Map(); + return this.__decMap(new Map()); }, mapSize: function(m) { return m instanceof Map ? m.size : 0; @@ -800,7 +906,7 @@ const pinescript = { const r = Math.max(0, rows ?? 0); const c = Math.max(0, cols ?? 0); const data = Array.from({ length: r }, () => Array.from({ length: c }, () => initialValue)); - return { rows: r, cols: c, data }; + return this.__decMatrix({ rows: r, cols: c, data }); }, matrixRows: function(m) { return m?.rows ?? 0; @@ -923,11 +1029,62 @@ const pinescript = { } return null; }, + __jacobiEigen: function(m) { + if (!m || !Array.isArray(m.data)) return null; + const n = m.rows ?? 0; + if (n === 0 || n !== (m.cols ?? 0)) return null; + // Work on a copy so the input matrix is untouched. + const a = m.data.map(row => row.slice()); + const v = Array.from({ length: n }, (_, i) => Array.from({ length: n }, (_, j) => (i === j ? 1 : 0))); + for (let sweep = 0; sweep < 100; sweep++) { + let off = 0; + for (let p = 0; p < n; p++) for (let q = p + 1; q < n; q++) off += a[p][q] * a[p][q]; + if (off < 1e-20) break; + for (let p = 0; p < n; p++) { + for (let q = p + 1; q < n; q++) { + if (Math.abs(a[p][q]) < 1e-18) continue; + const theta = (a[q][q] - a[p][p]) / (2 * a[p][q]); + const t = Math.sign(theta || 1) / (Math.abs(theta) + Math.sqrt(theta * theta + 1)); + const cos = 1 / Math.sqrt(t * t + 1); + const sin = t * cos; + for (let k = 0; k < n; k++) { + const akp = a[k][p], akq = a[k][q]; + a[k][p] = cos * akp - sin * akq; + a[k][q] = sin * akp + cos * akq; + } + for (let k = 0; k < n; k++) { + const apk = a[p][k], aqk = a[q][k]; + a[p][k] = cos * apk - sin * aqk; + a[q][k] = sin * apk + cos * aqk; + } + for (let k = 0; k < n; k++) { + const vkp = v[k][p], vkq = v[k][q]; + v[k][p] = cos * vkp - sin * vkq; + v[k][q] = sin * vkp + cos * vkq; + } + } + } + } + // Sort eigenpairs by eigenvalue, descending. + const order = Array.from({ length: n }, (_, i) => i).sort((i, j) => a[j][j] - a[i][i]); + const values = order.map(i => a[i][i]); + const vectors = Array.from({ length: n }, (_, r) => order.map(c => v[r][c])); + return { values, vectors }; + }, + matrixEigenvalues: function(m) { + const e = this.__jacobiEigen(m); + return this.__decArr(e ? e.values : []); + }, + matrixEigenvectors: function(m) { + const e = this.__jacobiEigen(m); + if (!e) return this.__decMatrix({ rows: 0, cols: 0, data: [] }); + return this.__decMatrix({ rows: e.vectors.length, cols: e.vectors.length, data: e.vectors }); + }, requestSecurity: function(symbol, timeframe, expression) { return expression; }, arrayNew: function(initialSize = 0, initialValue = 0) { - return Array(initialSize).fill(initialValue); + return this.__decArr(Array(initialSize).fill(initialValue)); }, arraySize: function(arr) { return arr ? arr.length : 0; @@ -978,10 +1135,10 @@ const pinescript = { return arr; }, arraySlice: function(arr, startIndex = 0, endIndex = null) { - if (!arr) return []; + if (!arr) return this.__decArr([]); const start = Number(startIndex) || 0; const end = endIndex === null || endIndex === undefined ? arr.length : Number(endIndex) || 0; - return arr.slice(start, end); + return this.__decArr(arr.slice(start, end)); }, arraySort: function(arr, order = 'ascending') { if (arr) arr.sort((a, b) => order === 'ascending' ? a - b : b - a); @@ -1191,7 +1348,7 @@ const pinescript = { const info = { ticker: 'AAPL', tickerid: 'NASDAQ:AAPL', prefix: 'NASDAQ', root: 'AAPL', suffix: '' }; return info[type] || ''; }, - timenow: 1781574529162, + timenow: 1782783578204, barstate: "LAST", dividends: {}, splits: {}, @@ -1245,11 +1402,11 @@ const pinescript = { arrayConcat: function(arr1, arr2) { if (!arr1) return arr2 || []; if (!arr2) return arr1; - return arr1.concat(arr2); + return this.__decArr(arr1.concat(arr2)); }, arrayCopy: function(arr) { - if (!arr) return []; - return [...arr]; + if (!arr) return this.__decArr([]); + return this.__decArr([...arr]); }, arrayBinarySearch: function(arr, value) { if (!arr || arr.length === 0) return -1; @@ -1339,7 +1496,7 @@ globalThis.input.timeframe = globalThis.input.timeframe || ((defval) => defval); globalThis.array = globalThis.array || { - from: (...items) => items, + from: (...items) => pinescript.__decArr(items), size: (arr) => pinescript.arraySize(arr), @@ -1508,6 +1665,8 @@ globalThis.line = globalThis.line || __pineNS({ style_solid: "solid", style_dash globalThis.box = globalThis.box || __pineNS({}); +globalThis.color = globalThis.color || __pineNS(Object.assign(function(c) { return c; }, { new: function(c, t) { return c; }, rgb: function(r, g, b, t) { return "#rgb(" + [r, g, b].join(",") + ")"; }, from_gradient: function(v, lo, hi, c1, c2) { return c1; }, r: function() { return 0; }, g: function() { return 0; }, b: function() { return 0; }, t: function() { return 0; }, aqua: "#00BCD4", black: "#363A45", blue: "#2962FF", fuchsia: "#E040FB", gray: "#787B86", green: "#4CAF50", lime: "#00E676", maroon: "#880E4F", navy: "#311B92", olive: "#808000", orange: "#FF9800", purple: "#9C27B0", red: "#FF5252", silver: "#B2B5BE", teal: "#00897B", white: "#FFFFFF", yellow: "#FFEB3B" })); + globalThis.label = globalThis.label || __pineNS({ style_label_down: "label_down", style_label_up: "label_up", style_none: "none" }); globalThis.polyline = globalThis.polyline || __pineNS({}); @@ -1520,7 +1679,9 @@ globalThis.map = globalThis.map || __pineNS({}); globalThis.session = globalThis.session || __pineNS({ regular: "regular", extended: "extended" }); -globalThis.dayofweek = globalThis.dayofweek || __pineNS({ sunday: 1, monday: 2, tuesday: 3, wednesday: 4, thursday: 5, friday: 6, saturday: 7 }); +globalThis.ticker = globalThis.ticker || __pineNS({}); + +globalThis.dayofweek = globalThis.dayofweek || __pineNS(Object.assign(function(t) { return new Date(t != null ? t : (globalThis.time || 0)).getUTCDay() + 1; }, { sunday: 1, monday: 2, tuesday: 3, wednesday: 4, thursday: 5, friday: 6, saturday: 7 })); globalThis.timeframe = __pineNS(Object.assign(globalThis.timeframe || {}, { period: (globalThis.timeframe && globalThis.timeframe.period) || "D", isintraday: false, isdaily: true, multiplier: 1 })); @@ -1568,8 +1729,6 @@ globalThis.bool = globalThis.bool || function(x) { return Boolean(x); }; globalThis.string = globalThis.string || function(x) { return x == null ? null : String(x); }; -globalThis.dayofweek = globalThis.dayofweek || __pineNS({ sunday: 1, monday: 2, tuesday: 3, wednesday: 4, thursday: 5, friday: 6, saturday: 7 }); - globalThis.str = globalThis.str || __pineNS({}); pinescript.math = globalThis.math; @@ -1655,7 +1814,7 @@ pinescript.text = { align_center: "center" }; pinescript.table = { - new: function(position, columns, rows, opts) { return { position, columns, rows, opts: opts || {}, cells: [] }; }, + new: function(position, columns, rows, opts) { return pinescript.__decDraw({ position, columns, rows, opts: opts || {}, cells: [] }); }, cell: function(table, column, row, text, opts) { @@ -1690,27 +1849,27 @@ function main() { // Options: {"overlay":true,"max_labels_count":500} if (state.calculation_settings === undefined) state.calculation_settings = "════════ Calculation Parameters ════════"; if (state.visual_settings === undefined) state.visual_settings = "════════ Visualization Settings ════════"; - let tooltip_vwma = "Period for the Volume Weighted Moving Average, which forms the central trend baseline weighted by trading volume. Shorter periods (14-21) make the indicator highly responsive to recent price action and volume surges, generating more frequent trend changes - ideal for intraday trading and scalping where you need to capture quick momentum shifts. Medium periods (34-55) provide balanced sensitivity that captures meaningful trend changes while filtering out minor fluctuations - suitable for swing trading on 4H and daily charts. Longer periods (89-144) create a stable trend baseline focused on major market direction, generating fewer but higher-conviction trend signals - best for position trading and identifying primary trends. Standard value is 34 for balanced performance across most trading styles."; - let tooltip_atr = "Multiplier for the Average True Range to define volatility buffers around the VWMA. Lower values (0.8-1.2) create tighter bands closer to the trend line, triggering more frequent trend changes as price makes smaller moves - useful in low-volatility environments and range-bound markets where you want early trend confirmation. Medium values (1.5-2.0) provide balanced filtering that requires meaningful price movement beyond normal volatility - suitable for most market conditions and reduces whipsaws. Higher values (2.5-3.5) create wider bands that only trigger on significant breakouts, generating fewer but more reliable trend changes - best for volatile markets and traders seeking high-conviction signals. Standard value is 1.5 for optimal noise filtering."; - let tooltip_preset = "Select a predefined configuration optimized for different trading styles, timeframes, and market conditions."; - let tooltip_preset_details = "Default (34, 1.5): Standard configuration suitable for swing trading on 4H and daily charts. Balanced VWMA period captures meaningful trend changes, moderate ATR multiplier filters noise effectively. Generates quality trend signals without excessive whipsaws, ideal for most traders and market conditions.nnFast Response (21, 1.2): Aggressive configuration for intraday trading and scalping on 5min-1H charts. Shorter VWMA reacts quickly to volume-weighted price shifts, tighter bands trigger earlier on breakouts. Best for active traders who can monitor positions closely and capitalize on short-term momentum.nnSmooth Trend (55, 2.0): Conservative configuration for position trading on daily and weekly charts. Extended VWMA establishes stable trend baseline, wider bands only signal on major breakouts. Best for patient traders seeking primary trend direction with minimal false signals and reduced trading frequency."; - let tooltip_color_preset = "Pre-configured color schemes optimized for different chart themes and visual preferences. Classic uses traditional green/red. Aqua provides ocean-inspired blues and oranges. Cosmic offers futuristic cyan and purple. Cyber features warm orange and cool cyan contrast. Neon delivers high-contrast yellow and magenta for maximum visibility. Custom allows full color customization."; - let tooltip_bullish = "Color for bullish trend conditions when price breaks above the upper volatility band. This represents statistically significant upward momentum confirmed by volume-weighted price action exceeding normal volatility bounds. Use vibrant colors to highlight uptrend zones and potential long opportunities."; - let tooltip_bearish = "Color for bearish trend conditions when price breaks below the lower volatility band. This represents statistically significant downward momentum with volume-weighted price falling below normal volatility range. Use warning colors to signal downtrend zones and potential short opportunities or exit signals."; - let tooltip_enable_glow = "Enable/disable the neon glow effect around the VWMA line. When enabled, creates a layered visual effect with three overlapping plots at different transparencies, making the trend line more prominent and easier to track across the chart. The glow effect provides depth and emphasis to the central trend line. Disable for a cleaner, minimalist appearance or if running many indicators where visual clarity is paramount."; - let tooltip_enable_ribbons = "Enable/disable gradient ribbon fills between the VWMA and volatility bands. When enabled, creates semi-transparent colored zones showing the buffer range around the trend line, providing visual context for price position relative to trend boundaries. The ribbons help identify when price is approaching breakout thresholds. Disable to reduce chart clutter or focus solely on the central VWMA line and band boundaries."; - let tooltip_band_transparency = "Transparency level for the upper and lower volatility band lines. Higher values (80-95) create subtle band outlines that provide boundary context without visual clutter - useful when running multiple indicators or preferring minimalist charts. Lower values (50-70) create more prominent band lines for stronger emphasis on breakout thresholds - useful for traders who want clear visual boundaries for trend trigger zones. Standard value is 90 for subtle guidance lines that don't dominate the chart."; - let tooltip_enable_barcolor = "Enable/disable bar coloring based on current trend direction. When enabled, price bars are tinted with bullish color during uptrends and bearish color during downtrends, providing instant visual confirmation of trend state without checking the indicator. This helps quickly identify trend alignment across multiple timeframes. Disable if you prefer default bar colors or use other bar coloring indicators."; - let vwma_length = pinescript.inputInt(34, "VWMA Length", ({ minval: 5, group: state.calculation_settings, tooltip: tooltip_vwma })); - let atr_multiplier = pinescript.inputFloat(1.5, "ATR Multiplier", ({ minval: 0.5, step: 0.1, group: state.calculation_settings, tooltip: tooltip_atr })); - let preset_config = pinescript.inputString("Default", "Preset Configuration", ({ options: ["Default", "Fast Response", "Smooth Trend"], group: state.calculation_settings, tooltip: ((tooltip_preset + "nn") + tooltip_preset_details) })); - let color_preset = pinescript.inputString("Custom", "Color Preset", ({ options: ["Classic", "Aqua", "Cosmic", "Cyber", "Neon", "Custom"], group: state.visual_settings, tooltip: tooltip_color_preset })); - let bullish_input = pinescript.inputColor(pinescript.color.hex("#00ffaa"), "Bullish Trend Color", ({ group: state.visual_settings, tooltip: tooltip_bullish })); - let bearish_input = pinescript.inputColor(pinescript.color.hex("#ff0000"), "Bearish Trend Color", ({ group: state.visual_settings, tooltip: tooltip_bearish })); - let enable_glow = pinescript.inputBool(true, "Enable Neon Glow Effect", ({ group: state.visual_settings, tooltip: tooltip_enable_glow })); - let enable_ribbons = pinescript.inputBool(true, "Enable Volatility Ribbons", ({ group: state.visual_settings, tooltip: tooltip_enable_ribbons })); - let band_transparency = pinescript.inputInt(90, "Band Transparency", ({ minval: 0, maxval: 100, group: state.visual_settings, tooltip: tooltip_band_transparency })); - let enable_barcolor = pinescript.inputBool(true, "Enable Bar Coloring", ({ group: state.visual_settings, tooltip: tooltip_enable_barcolor })); + var tooltip_vwma = "Period for the Volume Weighted Moving Average, which forms the central trend baseline weighted by trading volume. Shorter periods (14-21) make the indicator highly responsive to recent price action and volume surges, generating more frequent trend changes - ideal for intraday trading and scalping where you need to capture quick momentum shifts. Medium periods (34-55) provide balanced sensitivity that captures meaningful trend changes while filtering out minor fluctuations - suitable for swing trading on 4H and daily charts. Longer periods (89-144) create a stable trend baseline focused on major market direction, generating fewer but higher-conviction trend signals - best for position trading and identifying primary trends. Standard value is 34 for balanced performance across most trading styles."; + var tooltip_atr = "Multiplier for the Average True Range to define volatility buffers around the VWMA. Lower values (0.8-1.2) create tighter bands closer to the trend line, triggering more frequent trend changes as price makes smaller moves - useful in low-volatility environments and range-bound markets where you want early trend confirmation. Medium values (1.5-2.0) provide balanced filtering that requires meaningful price movement beyond normal volatility - suitable for most market conditions and reduces whipsaws. Higher values (2.5-3.5) create wider bands that only trigger on significant breakouts, generating fewer but more reliable trend changes - best for volatile markets and traders seeking high-conviction signals. Standard value is 1.5 for optimal noise filtering."; + var tooltip_preset = "Select a predefined configuration optimized for different trading styles, timeframes, and market conditions."; + var tooltip_preset_details = "Default (34, 1.5): Standard configuration suitable for swing trading on 4H and daily charts. Balanced VWMA period captures meaningful trend changes, moderate ATR multiplier filters noise effectively. Generates quality trend signals without excessive whipsaws, ideal for most traders and market conditions.nnFast Response (21, 1.2): Aggressive configuration for intraday trading and scalping on 5min-1H charts. Shorter VWMA reacts quickly to volume-weighted price shifts, tighter bands trigger earlier on breakouts. Best for active traders who can monitor positions closely and capitalize on short-term momentum.nnSmooth Trend (55, 2.0): Conservative configuration for position trading on daily and weekly charts. Extended VWMA establishes stable trend baseline, wider bands only signal on major breakouts. Best for patient traders seeking primary trend direction with minimal false signals and reduced trading frequency."; + var tooltip_color_preset = "Pre-configured color schemes optimized for different chart themes and visual preferences. Classic uses traditional green/red. Aqua provides ocean-inspired blues and oranges. Cosmic offers futuristic cyan and purple. Cyber features warm orange and cool cyan contrast. Neon delivers high-contrast yellow and magenta for maximum visibility. Custom allows full color customization."; + var tooltip_bullish = "Color for bullish trend conditions when price breaks above the upper volatility band. This represents statistically significant upward momentum confirmed by volume-weighted price action exceeding normal volatility bounds. Use vibrant colors to highlight uptrend zones and potential long opportunities."; + var tooltip_bearish = "Color for bearish trend conditions when price breaks below the lower volatility band. This represents statistically significant downward momentum with volume-weighted price falling below normal volatility range. Use warning colors to signal downtrend zones and potential short opportunities or exit signals."; + var tooltip_enable_glow = "Enable/disable the neon glow effect around the VWMA line. When enabled, creates a layered visual effect with three overlapping plots at different transparencies, making the trend line more prominent and easier to track across the chart. The glow effect provides depth and emphasis to the central trend line. Disable for a cleaner, minimalist appearance or if running many indicators where visual clarity is paramount."; + var tooltip_enable_ribbons = "Enable/disable gradient ribbon fills between the VWMA and volatility bands. When enabled, creates semi-transparent colored zones showing the buffer range around the trend line, providing visual context for price position relative to trend boundaries. The ribbons help identify when price is approaching breakout thresholds. Disable to reduce chart clutter or focus solely on the central VWMA line and band boundaries."; + var tooltip_band_transparency = "Transparency level for the upper and lower volatility band lines. Higher values (80-95) create subtle band outlines that provide boundary context without visual clutter - useful when running multiple indicators or preferring minimalist charts. Lower values (50-70) create more prominent band lines for stronger emphasis on breakout thresholds - useful for traders who want clear visual boundaries for trend trigger zones. Standard value is 90 for subtle guidance lines that don't dominate the chart."; + var tooltip_enable_barcolor = "Enable/disable bar coloring based on current trend direction. When enabled, price bars are tinted with bullish color during uptrends and bearish color during downtrends, providing instant visual confirmation of trend state without checking the indicator. This helps quickly identify trend alignment across multiple timeframes. Disable if you prefer default bar colors or use other bar coloring indicators."; + var vwma_length = pinescript.inputInt(34, "VWMA Length", ({ minval: 5, group: state.calculation_settings, tooltip: tooltip_vwma })); + var atr_multiplier = pinescript.inputFloat(1.5, "ATR Multiplier", ({ minval: 0.5, step: 0.1, group: state.calculation_settings, tooltip: tooltip_atr })); + var preset_config = pinescript.inputString("Default", "Preset Configuration", ({ options: ["Default", "Fast Response", "Smooth Trend"], group: state.calculation_settings, tooltip: ((tooltip_preset + "nn") + tooltip_preset_details) })); + var color_preset = pinescript.inputString("Custom", "Color Preset", ({ options: ["Classic", "Aqua", "Cosmic", "Cyber", "Neon", "Custom"], group: state.visual_settings, tooltip: tooltip_color_preset })); + var bullish_input = pinescript.inputColor(pinescript.color.hex("#00ffaa"), "Bullish Trend Color", ({ group: state.visual_settings, tooltip: tooltip_bullish })); + var bearish_input = pinescript.inputColor(pinescript.color.hex("#ff0000"), "Bearish Trend Color", ({ group: state.visual_settings, tooltip: tooltip_bearish })); + var enable_glow = pinescript.inputBool(true, "Enable Neon Glow Effect", ({ group: state.visual_settings, tooltip: tooltip_enable_glow })); + var enable_ribbons = pinescript.inputBool(true, "Enable Volatility Ribbons", ({ group: state.visual_settings, tooltip: tooltip_enable_ribbons })); + var band_transparency = pinescript.inputInt(90, "Band Transparency", ({ minval: 0, maxval: 100, group: state.visual_settings, tooltip: tooltip_band_transparency })); + var enable_barcolor = pinescript.inputBool(true, "Enable Bar Coloring", ({ group: state.visual_settings, tooltip: tooltip_enable_barcolor })); if ((preset_config === "Fast Response")) { vwma_length = 21; atr_multiplier = 1.2; @@ -1726,11 +1885,11 @@ function main() { [pinescript.color.hex("#49ffce"), pinescript.color.hex("#9932cc")]; [pinescript.color.hex("#00d4ff"), pinescript.color.hex("#ff8c00")]; [pinescript.color.hex("#00ff00"), pinescript.color.hex("#ff0000")]; - let [bullish_color, bearish_color] = pinescript.unpack(((color_preset === "Classic") ? undefined : ((color_preset === "Aqua") ? undefined : ((color_preset === "Cosmic") ? undefined : ((color_preset === "Cyber") ? undefined : ((color_preset === "Neon") ? undefined : ((color_preset === "Custom") ? undefined : null)))))), 2); - let vwma_basis = pinescript.vwma(close, vwma_length); - let atr_value = pinescript.atr(vwma_length); - let upper_band = (vwma_basis + (atr_value * atr_multiplier)); - let lower_band = (vwma_basis - (atr_value * atr_multiplier)); + var [bullish_color, bearish_color] = pinescript.unpack(((color_preset === "Classic") ? undefined : ((color_preset === "Aqua") ? undefined : ((color_preset === "Cosmic") ? undefined : ((color_preset === "Cyber") ? undefined : ((color_preset === "Neon") ? undefined : ((color_preset === "Custom") ? undefined : null)))))), 2); + var vwma_basis = pinescript.vwma(close, vwma_length); + var atr_value = pinescript.atr(vwma_length); + var upper_band = (vwma_basis + (atr_value * atr_multiplier)); + var lower_band = (vwma_basis - (atr_value * atr_multiplier)); if (state.trend_direction === undefined) state.trend_direction = 0; if ((close > upper_band)) { state.trend_direction = 1; @@ -1739,17 +1898,17 @@ function main() { state.trend_direction = -1; } } - let trend_turned_bullish = ((state.trend_direction === 1) && (pinescript.hist(0, state.trend_direction, 1) !== 1)); - let trend_turned_bearish = ((state.trend_direction === -1) && (pinescript.hist(1, state.trend_direction, 1) !== -1)); - let trend_changed = (trend_turned_bullish || trend_turned_bearish); - let trend_color = ((state.trend_direction === 1) ? bullish_color : bearish_color); + var trend_turned_bullish = ((state.trend_direction === 1) && (pinescript.hist(0, state.trend_direction, 1) !== 1)); + var trend_turned_bearish = ((state.trend_direction === -1) && (pinescript.hist(1, state.trend_direction, 1) !== -1)); + var trend_changed = (trend_turned_bullish || trend_turned_bearish); + var trend_color = ((state.trend_direction === 1) ? bullish_color : bearish_color); pinescript.plot((enable_glow ? vwma_basis : null), "VWMA Glow Outer", pinescript.color.new(trend_color, 80), 8); pinescript.plot((enable_glow ? vwma_basis : null), "VWMA Glow Inner", pinescript.color.new(trend_color, 50), 4); - let vwma_plot = pinescript.plot(vwma_basis, "VWMA Line", pinescript.color.new(trend_color, 0), 2); - let upper_band_plot = pinescript.plot(upper_band, "Upper Band", ({ color: pinescript.color.new(trend_color, band_transparency) })); - let upper_mid_plot = pinescript.plot(pinescript.avg(upper_band, vwma_basis), "Upper Mid", ({ color: pinescript.color.new(trend_color, 95) })); - let lower_band_plot = pinescript.plot(lower_band, "Lower Band", ({ color: pinescript.color.new(trend_color, band_transparency) })); - let lower_mid_plot = pinescript.plot(pinescript.avg(lower_band, vwma_basis), "Lower Mid", ({ color: pinescript.color.new(trend_color, 95) })); + var vwma_plot = pinescript.plot(vwma_basis, "VWMA Line", pinescript.color.new(trend_color, 0), 2); + var upper_band_plot = pinescript.plot(upper_band, "Upper Band", ({ color: pinescript.color.new(trend_color, band_transparency) })); + var upper_mid_plot = pinescript.plot(pinescript.avg(upper_band, vwma_basis), "Upper Mid", ({ color: pinescript.color.new(trend_color, 95) })); + var lower_band_plot = pinescript.plot(lower_band, "Lower Band", ({ color: pinescript.color.new(trend_color, band_transparency) })); + var lower_mid_plot = pinescript.plot(pinescript.avg(lower_band, vwma_basis), "Lower Mid", ({ color: pinescript.color.new(trend_color, 95) })); pinescript.fill(upper_band_plot, upper_mid_plot, (enable_ribbons ? pinescript.color.new(trend_color, 85) : null), "Upper Outer Ribbon"); pinescript.fill(upper_mid_plot, vwma_plot, (enable_ribbons ? pinescript.color.new(trend_color, 70) : null), "Upper Inner Ribbon"); pinescript.fill(lower_band_plot, lower_mid_plot, (enable_ribbons ? pinescript.color.new(trend_color, 85) : null), "Lower Outer Ribbon"); @@ -1802,6 +1961,10 @@ function run(data, options = {}) { rt.__shapeIdx = 0; globalThis.bar_index = i; globalThis.last_bar_index = n - 1; + globalThis.time_tradingday = inTime[i]; + globalThis.time_close = inTime[i]; + globalThis.last_bar_time = inTime[n - 1]; + globalThis.timenow = inTime[n - 1]; globalThis.barstate = { isfirst: i === 0, islast: i === n - 1, isrealtime: false, ishistory: true, isconfirmed: true, isnew: true, islastconfirmedhistory: i === n - 1, diff --git a/converted/status.json b/converted/status.json index ab8ad7f..9d5ce2f 100644 --- a/converted/status.json +++ b/converted/status.json @@ -1,240 +1,240 @@ { - "timestamp": "2026-06-16T01:48:49.291Z", + "timestamp": "2026-06-30T01:39:38.304Z", "total": 29, - "passed": 28, - "failed": 1, - "totalTimeMs": 2995, - "averageConfidenceScore": 67, + "passed": 29, + "failed": 0, + "totalTimeMs": 2680, + "averageConfidenceScore": 98.8, "files": [ { "name": "3D_MACD_Bar_Plot_LuxAlgo.pine", "status": "converted", - "confidenceScore": 65, - "elapsedMs": 97, + "confidenceScore": 100, + "elapsedMs": 86, "jsExists": true, "errorExists": false }, { "name": "Arbitrage_Matrix_LuxAlgo.pine", "status": "converted", - "confidenceScore": 65, - "elapsedMs": 103, + "confidenceScore": 100, + "elapsedMs": 90, "jsExists": true, "errorExists": false }, { "name": "Asset_Drift_Model.pine", "status": "converted", - "confidenceScore": 70, - "elapsedMs": 105, + "confidenceScore": 100, + "elapsedMs": 92, "jsExists": true, "errorExists": false }, { "name": "Automatic_Trendline_Metrify.pine", "status": "converted", - "confidenceScore": 70, - "elapsedMs": 103, + "confidenceScore": 100, + "elapsedMs": 87, "jsExists": true, "errorExists": false }, { "name": "CE_1M_3M_Buy_Sell_Hold.pine", "status": "converted", - "confidenceScore": 70, - "elapsedMs": 93, + "confidenceScore": 100, + "elapsedMs": 84, "jsExists": true, "errorExists": false }, { "name": "Crossing_Moving_Averages_with_ADX_Filter.pine", "status": "converted", - "confidenceScore": 70, - "elapsedMs": 100, + "confidenceScore": 100, + "elapsedMs": 80, "jsExists": true, "errorExists": false }, { "name": "Folded_RSI.pine", "status": "converted", - "confidenceScore": 70, - "elapsedMs": 102, + "confidenceScore": 100, + "elapsedMs": 86, "jsExists": true, "errorExists": false }, { "name": "Golden_Pocket_Zones.pine", "status": "converted", - "confidenceScore": 70, - "elapsedMs": 102, + "confidenceScore": 100, + "elapsedMs": 87, "jsExists": true, "errorExists": false }, { "name": "Hyperfork_Matrix.pine", "status": "converted", - "confidenceScore": 70, - "elapsedMs": 117, + "confidenceScore": 100, + "elapsedMs": 101, "jsExists": true, "errorExists": false }, { "name": "Impulse_Trend_Arrows.pine", "status": "converted", - "confidenceScore": 70, - "elapsedMs": 97, + "confidenceScore": 100, + "elapsedMs": 86, "jsExists": true, "errorExists": false }, { "name": "Institutional_Order_Flow_Strength_Classifier_LuxAlgo.pine", "status": "converted", - "confidenceScore": 70, - "elapsedMs": 98, + "confidenceScore": 100, + "elapsedMs": 89, "jsExists": true, "errorExists": false }, { "name": "Liquidity_Sweep_Detector.pine", "status": "converted", - "confidenceScore": 70, - "elapsedMs": 108, + "confidenceScore": 100, + "elapsedMs": 98, "jsExists": true, "errorExists": false }, { "name": "Manias_Panics_and_Crashes.pine", "status": "converted", - "confidenceScore": 70, - "elapsedMs": 103, + "confidenceScore": 100, + "elapsedMs": 92, "jsExists": true, "errorExists": false }, { "name": "Net_Buy_Volume_theUltimator5.pine", "status": "converted", - "confidenceScore": 70, - "elapsedMs": 89, + "confidenceScore": 100, + "elapsedMs": 79, "jsExists": true, "errorExists": false }, { "name": "NY_5m_15m_Orb_-_Statistics_LTF_Candle_structure.pine", "status": "converted", - "confidenceScore": 70, - "elapsedMs": 126, + "confidenceScore": 100, + "elapsedMs": 107, "jsExists": true, "errorExists": false }, { "name": "Peak_Trading_Activity_Graphs_LuxAlgo.pine", "status": "converted", - "confidenceScore": 70, - "elapsedMs": 104, + "confidenceScore": 100, + "elapsedMs": 101, "jsExists": true, "errorExists": false }, { "name": "Proximal_Range_Filter_LuxAlgo.pine", "status": "converted", - "confidenceScore": 70, - "elapsedMs": 90, + "confidenceScore": 100, + "elapsedMs": 80, "jsExists": true, "errorExists": false }, { "name": "Pro_Pattern_Long-Short_High_Winrate.pine", "status": "converted", - "confidenceScore": 70, - "elapsedMs": 107, + "confidenceScore": 100, + "elapsedMs": 91, "jsExists": true, "errorExists": false }, { "name": "Quantum_Structure_jjart.pine", - "status": "failed", - "confidenceScore": null, - "elapsedMs": 92, - "jsExists": false, - "errorExists": true + "status": "converted", + "confidenceScore": 100, + "elapsedMs": 111, + "jsExists": true, + "errorExists": false }, { "name": "RBT_strategy.pine", "status": "converted", - "confidenceScore": 70, - "elapsedMs": 92, + "confidenceScore": 100, + "elapsedMs": 81, "jsExists": true, "errorExists": false }, { "name": "Scalping_Strategy_3_minutes.pine", "status": "converted", - "confidenceScore": 45, - "elapsedMs": 123, + "confidenceScore": 75, + "elapsedMs": 113, "jsExists": true, "errorExists": false }, { "name": "Singular_Spectrum_Decomposition_LuxAlgo.pine", "status": "converted", - "confidenceScore": 70, - "elapsedMs": 102, + "confidenceScore": 100, + "elapsedMs": 93, "jsExists": true, "errorExists": false }, { "name": "Smart_Trader_Episode_03_by_Ata_Sabanci_Candles_and_Tradelines.pine", "status": "converted", - "confidenceScore": 50, - "elapsedMs": 153, + "confidenceScore": 90, + "elapsedMs": 134, "jsExists": true, "errorExists": false }, { "name": "SMC_Trade_with_Dhruv.pine", "status": "converted", - "confidenceScore": 40, - "elapsedMs": 118, + "confidenceScore": 100, + "elapsedMs": 103, "jsExists": true, "errorExists": false }, { "name": "TehThomas_-_Aligned_Timeframe_Fair_Value_Gaps.pine", "status": "converted", - "confidenceScore": 70, - "elapsedMs": 96, + "confidenceScore": 100, + "elapsedMs": 89, "jsExists": true, "errorExists": false }, { "name": "Volatility_Contraction_Score.pine", "status": "converted", - "confidenceScore": 70, - "elapsedMs": 96, + "confidenceScore": 100, + "elapsedMs": 89, "jsExists": true, "errorExists": false }, { "name": "Volume_Surprise_LuxAlgo.pine", "status": "converted", - "confidenceScore": 70, - "elapsedMs": 92, + "confidenceScore": 100, + "elapsedMs": 87, "jsExists": true, "errorExists": false }, { "name": "Volume_Weighted_Trend_QuantAlgo.pine", "status": "converted", - "confidenceScore": 70, - "elapsedMs": 93, + "confidenceScore": 100, + "elapsedMs": 83, "jsExists": true, "errorExists": false }, { "name": "VSA_Pro_Trend_and_Rejection.pine", "status": "converted", - "confidenceScore": 70, - "elapsedMs": 94, + "confidenceScore": 100, + "elapsedMs": 81, "jsExists": true, "errorExists": false } diff --git a/src/builtins.js b/src/builtins.js index 938c026..ad4bd29 100644 --- a/src/builtins.js +++ b/src/builtins.js @@ -68,6 +68,97 @@ export const builtins = new Map([ return new Array(count || 0).fill(null); }], + // PineScript v6 lets you call collection functions as methods: arr.get(i), + // arr.push(v), m.put(k, v). JS arrays/Maps don't have those exact methods, so we + // attach them to the collections we hand back. We only add names that don't + // already exist natively (defining e.g. push in terms of arr.push would recurse). + ['__decArr', function(arr) { + if (!Array.isArray(arr) || arr.__pineDecorated) return arr; + const self = this; + const def = (name, fn) => Object.defineProperty(arr, name, { value: fn, writable: true, configurable: true }); + Object.defineProperty(arr, '__pineDecorated', { value: true, configurable: true }); + def('get', (i) => (arr[i] === undefined ? null : arr[i])); + def('set', (i, v) => { arr[i] = v; return v; }); + def('size', () => arr.length); + def('clear', () => { arr.length = 0; }); + def('insert', (i, v) => { arr.splice(i, 0, v); }); + def('remove', (i) => arr.splice(i, 1)[0]); + def('contains', (v) => arr.includes(v)); + def('indexof', (v) => arr.indexOf(v)); + def('lastindexof', (v) => arr.lastIndexOf(v)); + def('first', () => (arr.length ? arr[0] : null)); + def('last', () => (arr.length ? arr[arr.length - 1] : null)); + def('sum', () => arr.reduce((a, b) => a + b, 0)); + def('avg', () => (arr.length ? arr.reduce((a, b) => a + b, 0) / arr.length : 0)); + def('min', () => (arr.length ? Math.min(...arr) : null)); + def('max', () => (arr.length ? Math.max(...arr) : null)); + def('range', () => (arr.length ? Math.max(...arr) - Math.min(...arr) : null)); + // Statistical methods that route to the array.* built-ins (these names are not + // native to JS arrays, so attaching them here is safe from recursion). + def('median', () => self.arrayMedian(arr)); + def('mode', () => self.arrayMode(arr)); + def('stdev', () => self.arrayStdev(arr)); + def('variance', () => self.arrayVariance(arr)); + def('covariance', (other) => self.arrayCovariance(arr, other)); + def('percentile_linear_interpolation', (p) => self.arrayPercentileLinearInterpolation(arr, p)); + def('percentile_nearest_rank', (p) => self.arrayPercentileNearestRank(arr, p)); + def('abs', () => self.__decArr(self.arrayAbs(arr))); + def('binary_search', (v) => self.arrayBinarySearch(arr, v)); + // Pine's array.sort takes an order string, not a comparator. Use the native + // sort via .call so we don't recurse through this overridden method. + def('sort', (order) => { Array.prototype.sort.call(arr, (a, b) => (order === 'descending' ? b - a : a - b)); return arr; }); + def('sort_indices', (order) => self.__decArr(arr.map((_, i) => i).sort((a, b) => (order === 'descending' ? arr[b] - arr[a] : arr[a] - arr[b])))); + // join/slice/reverse/concat/includes/fill already exist natively on Array with + // compatible semantics, so we deliberately leave them to the native methods. + return arr; + }], + + // Decorate a Map with PineScript map method names (put/contains/remove and + // array-returning keys/values, which Pine expects instead of JS iterators). + ['__decMap', function(m) { + if (!(m instanceof Map) || m.__pineDecorated) return m; + const def = (name, fn) => Object.defineProperty(m, name, { value: fn, writable: true, configurable: true }); + Object.defineProperty(m, '__pineDecorated', { value: true, configurable: true }); + def('put', (k, v) => { m.set(k, v); return v; }); + def('contains', (k) => m.has(k)); + def('remove', (k) => m.delete(k)); + def('keys', () => Array.from(Map.prototype.keys.call(m))); + def('values', () => Array.from(Map.prototype.values.call(m))); + def('size_', () => m.size); + return m; + }], + + // Decorate a matrix object ({rows, cols, data}) with Pine v6 method-call names. + // rows/cols stay numeric properties (built-ins read them), so the method forms + // are exposed as rows_/columns to avoid clobbering them. + ['__decMatrix', function(m) { + if (!m || typeof m !== 'object' || m.__pineDecorated) return m; + const self = this; + const def = (name, fn) => Object.defineProperty(m, name, { value: fn, writable: true, configurable: true }); + Object.defineProperty(m, '__pineDecorated', { value: true, configurable: true }); + def('get', (r, c) => self.matrixGet(m, r, c)); + def('set', (r, c, v) => self.matrixSet(m, r, c, v)); + def('rows_', () => m.rows); + def('columns', () => m.cols); + def('fill', (v) => self.matrixFill(m, v)); + return m; + }], + + // Wrap a drawing object (line/label/box/table/polyline) so any method we don't + // explicitly model (table.cell, line.set_xy, box.set_bgcolor, ...) becomes a + // chainable no-op. We don't render charts, so these calls just need to not throw + // while real fields stay readable. + ['__decDraw', function(obj) { + if (!obj || typeof obj !== 'object') return obj; + const p = new Proxy(obj, { + get(t, k) { + if (k in t || typeof k === 'symbol') return t[k]; + return function() { return p; }; + }, + }); + return p; + }], + // Visual-output built-ins. We don't render charts, so these record the call and // return safely instead of throwing, letting indicators that use them still run. ['alertcondition', function(condition, title, message) { @@ -81,6 +172,36 @@ export const builtins = new Map([ ['barcolor', function(color) { return null; }], + // plotchar/plotarrow behave like plotshape for our purposes: record a per-bar + // series so the script runs and produces inspectable output. + ['plotchar', function(series, ...rest) { + const rt = globalThis.__pineRuntime; + if (!rt) return series; + const bar = rt.__barIndex | 0; + const ord = (rt.__shapeIdx = (rt.__shapeIdx | 0) + 1) - 1; + let key = 'char_' + ord; + for (const r of rest) { + if (typeof r === 'string') { key = r; break; } + if (r && typeof r === 'object' && r.title) { key = String(r.title); break; } + } + let s = rt.plotshapes[key]; + if (!s) s = rt.plotshapes[key] = { title: key, data: [] }; + s.data[bar] = this.__scalar(series); + return series; + }], + + ['plotarrow', function(series, ...rest) { + const rt = globalThis.__pineRuntime; + if (!rt) return series; + const bar = rt.__barIndex | 0; + const ord = (rt.__plotIdx = (rt.__plotIdx | 0) + 1) - 1; + const key = 'arrow_' + ord; + let p = rt.plots[key]; + if (!p) p = rt.plots[key] = { title: key, data: [] }; + p.data[bar] = this.__scalar(series); + return series; + }], + ['bgcolor', function(color, opts) { return null; }], ['fill', function(plot1, plot2, color, opts) { return null; }], @@ -566,6 +687,14 @@ export const builtins = new Map([ return Math.atan(value); }], + ['todegrees', function(radians) { + return radians * (180 / Math.PI); + }], + + ['toradians', function(degrees) { + return degrees * (Math.PI / 180); + }], + ['floor', function(value) { return Math.floor(value); }], @@ -777,7 +906,7 @@ export const builtins = new Map([ }], ['lineNew', function(x1, y1, x2, y2, opts = {}) { - return { x1, y1, x2, y2, opts, _type: 'line' }; + return this.__decDraw({ x1, y1, x2, y2, opts, _type: 'line' }); }], ['lineDelete', function(l) { @@ -803,7 +932,7 @@ export const builtins = new Map([ }], ['labelNew', function(x, y, text = '', opts = {}) { - return { x, y, text, opts, _type: 'label' }; + return this.__decDraw({ x, y, text, opts, _type: 'label' }); }], ['labelDelete', function(l) { @@ -824,7 +953,7 @@ export const builtins = new Map([ // Box drawing functions for creating rectangular shapes on the chart ['boxNew', function(left, top, right, bottom, opts = {}) { - return { left, top, right, bottom, opts, _type: 'box' }; + return this.__decDraw({ left, top, right, bottom, opts, _type: 'box' }); }], ['boxDelete', function(box) { @@ -847,7 +976,7 @@ export const builtins = new Map([ // Polyline drawing functions for multi-point paths on the chart ['polylineNew', function(points, opts = {}) { - return { points: points || [], opts, _type: 'polyline' }; + return this.__decDraw({ points: points || [], opts, _type: 'polyline' }); }], ['polylineDelete', function(poly) { @@ -958,7 +1087,7 @@ export const builtins = new Map([ // Map data structure functions for key-value storage within scripts ['mapNew', function() { - return new Map(); + return this.__decMap(new Map()); }], ['mapSize', function(m) { @@ -1002,7 +1131,7 @@ export const builtins = new Map([ const r = Math.max(0, rows ?? 0); const c = Math.max(0, cols ?? 0); const data = Array.from({ length: r }, () => Array.from({ length: c }, () => initialValue)); - return { rows: r, cols: c, data }; + return this.__decMatrix({ rows: r, cols: c, data }); }], ['matrixRows', function(m) { @@ -1138,6 +1267,63 @@ export const builtins = new Map([ return null; }], + // Jacobi eigenvalue algorithm for symmetric matrices. Returns eigenvalues sorted + // descending and their eigenvectors as columns of a matrix, matching how Pine's + // matrix.eigenvalues / matrix.eigenvectors are typically used (covariance, SSA). + ['__jacobiEigen', function(m) { + if (!m || !Array.isArray(m.data)) return null; + const n = m.rows ?? 0; + if (n === 0 || n !== (m.cols ?? 0)) return null; + // Work on a copy so the input matrix is untouched. + const a = m.data.map(row => row.slice()); + const v = Array.from({ length: n }, (_, i) => Array.from({ length: n }, (_, j) => (i === j ? 1 : 0))); + for (let sweep = 0; sweep < 100; sweep++) { + let off = 0; + for (let p = 0; p < n; p++) for (let q = p + 1; q < n; q++) off += a[p][q] * a[p][q]; + if (off < 1e-20) break; + for (let p = 0; p < n; p++) { + for (let q = p + 1; q < n; q++) { + if (Math.abs(a[p][q]) < 1e-18) continue; + const theta = (a[q][q] - a[p][p]) / (2 * a[p][q]); + const t = Math.sign(theta || 1) / (Math.abs(theta) + Math.sqrt(theta * theta + 1)); + const cos = 1 / Math.sqrt(t * t + 1); + const sin = t * cos; + for (let k = 0; k < n; k++) { + const akp = a[k][p], akq = a[k][q]; + a[k][p] = cos * akp - sin * akq; + a[k][q] = sin * akp + cos * akq; + } + for (let k = 0; k < n; k++) { + const apk = a[p][k], aqk = a[q][k]; + a[p][k] = cos * apk - sin * aqk; + a[q][k] = sin * apk + cos * aqk; + } + for (let k = 0; k < n; k++) { + const vkp = v[k][p], vkq = v[k][q]; + v[k][p] = cos * vkp - sin * vkq; + v[k][q] = sin * vkp + cos * vkq; + } + } + } + } + // Sort eigenpairs by eigenvalue, descending. + const order = Array.from({ length: n }, (_, i) => i).sort((i, j) => a[j][j] - a[i][i]); + const values = order.map(i => a[i][i]); + const vectors = Array.from({ length: n }, (_, r) => order.map(c => v[r][c])); + return { values, vectors }; + }], + + ['matrixEigenvalues', function(m) { + const e = this.__jacobiEigen(m); + return this.__decArr(e ? e.values : []); + }], + + ['matrixEigenvectors', function(m) { + const e = this.__jacobiEigen(m); + if (!e) return this.__decMatrix({ rows: 0, cols: 0, data: [] }); + return this.__decMatrix({ rows: e.vectors.length, cols: e.vectors.length, data: e.vectors }); + }], + // External data request stub that passes through the expression in single-security mode ['requestSecurity', function(symbol, timeframe, expression) { return expression; @@ -1145,7 +1331,7 @@ export const builtins = new Map([ // Array manipulation functions that mirror PineScript's array namespace ['arrayNew', function(initialSize = 0, initialValue = 0) { - return Array(initialSize).fill(initialValue); + return this.__decArr(Array(initialSize).fill(initialValue)); }], ['arraySize', function(arr) { @@ -1208,10 +1394,10 @@ export const builtins = new Map([ }], ['arraySlice', function(arr, startIndex = 0, endIndex = null) { - if (!arr) return []; + if (!arr) return this.__decArr([]); const start = Number(startIndex) || 0; const end = endIndex === null || endIndex === undefined ? arr.length : Number(endIndex) || 0; - return arr.slice(start, end); + return this.__decArr(arr.slice(start, end)); }], ['arraySort', function(arr, order = 'ascending') { @@ -1554,12 +1740,12 @@ export const builtins = new Map([ ['arrayConcat', function(arr1, arr2) { if (!arr1) return arr2 || []; if (!arr2) return arr1; - return arr1.concat(arr2); + return this.__decArr(arr1.concat(arr2)); }], ['arrayCopy', function(arr) { - if (!arr) return []; - return [...arr]; + if (!arr) return this.__decArr([]); + return this.__decArr([...arr]); }], ['arrayBinarySearch', function(arr, value) { diff --git a/src/cli.js b/src/cli.js index 1e1998e..3c76787 100644 --- a/src/cli.js +++ b/src/cli.js @@ -26,7 +26,6 @@ Options: --tokens Output tokens instead of generated code --no-review Disable post-conversion review --no-review-import Disable runtime import smoke test - --review-ai Enable optional local AI review (requires optional Python deps) Examples: node cli.js script.pine output.js @@ -46,8 +45,7 @@ async function main() { verbose: false, outputMode: 'code', review: process.env.PINE_REVIEW !== '0', - reviewImport: process.env.PINE_REVIEW_IMPORT !== '0', - reviewAI: process.env.PINE_REVIEW_AI === '1' + reviewImport: process.env.PINE_REVIEW_IMPORT !== '0' }; for (let i = 0; i < args.length; i++) { @@ -93,11 +91,6 @@ async function main() { continue; } - if (arg === '--review-ai') { - options.reviewAI = true; - continue; - } - if (!arg.startsWith('-')) { if (!inputFile) { inputFile = arg; @@ -126,9 +119,9 @@ async function main() { console.log(`Lines: ${source.split('\n').length}`); } - if (options.verbose || options.reviewAI) console.log('Transpiling...'); + if (options.verbose) console.log('Transpiling...'); const result = transpile(source, options); - if (options.verbose || options.reviewAI) console.log('Transpile done.'); + if (options.verbose) console.log('Transpile done.'); if (!result.success) { console.error(`Error: ${result.error}`); @@ -143,14 +136,12 @@ async function main() { const output = result.code; if (options.review) { - if (options.verbose || options.reviewAI) console.log('Reviewing generated code...'); - if (options.reviewAI) console.log('Running local AI review (this can take a while on first run)...'); + if (options.verbose) console.log('Reviewing generated code...'); const report = await reviewGeneratedCode(output, { - executeImport: options.reviewImport, - ai: options.reviewAI + executeImport: options.reviewImport }); console.log(formatReviewReport(report)); - if (options.verbose || options.reviewAI) console.log('Review done.'); + if (options.verbose) console.log('Review done.'); if (!report.ok) process.exitCode = 1; } diff --git a/src/generator.js b/src/generator.js index 78df63a..2e34918 100644 --- a/src/generator.js +++ b/src/generator.js @@ -24,6 +24,23 @@ const SERIES_ARG_FUNCS = { 'pinescript.lowestbars': [0], 'pinescript.sum': [0], 'pinescript.offset': [0], + // Moving-average / dispersion indicators slice their source as an array, so a + // scalar source must be recorded into a per-bar history buffer first. + 'pinescript.sma': [0], + 'pinescript.ema': [0], + 'pinescript.wma': [0], + 'pinescript.rma': [0], + 'pinescript.hma': [0], + 'pinescript.alma': [0], + 'pinescript.swma': [0], + 'pinescript.rsi': [0], + 'pinescript.roc': [0], + 'pinescript.linreg': [0], + 'pinescript.variance': [0], + 'pinescript.stdev': [0], + 'pinescript.median': [0], + 'pinescript.bb': [0], + 'pinescript.correlation': [0, 1], }; class CodeGenerator { @@ -51,6 +68,14 @@ class CodeGenerator { // engine can keep a separate per-bar history buffer for it. this.siteCounter = 0; + // Per-scope record of which variable names have already been declared, so we + // know when to emit a fresh `var` vs. a plain reassignment. PineScript is + // function-scoped (a variable first set inside an if/for block is visible for + // the rest of the function), so we declare with `var` -- which JavaScript + // hoists to the function top -- and track declarations per function scope so + // locals in one function don't suppress declarations in another. + this.scopeStack = []; + this.reservedNamespaces = new Set([ 'ta', 'math', 'array', 'str', 'color', 'table', 'position', 'location', 'shape', 'size', 'text', @@ -64,6 +89,30 @@ class CodeGenerator { return this.siteCounter++; } + // --- Scope tracking for declarations --- + pushScope(names = []) { + this.scopeStack.push(new Set(names)); + } + + popScope() { + this.scopeStack.pop(); + } + + // Has `name` already been declared in the current function scope? Falls back to + // the global name set when no scope is active (e.g. module-level constructors). + scopeDeclared(name) { + const s = this.scopeStack[this.scopeStack.length - 1]; + return s ? s.has(name) : this.context.variables.has(name); + } + + // Record `name` as declared in the current scope (and globally, for getSafeName + // collision checks). + scopeDeclare(name) { + const s = this.scopeStack[this.scopeStack.length - 1]; + if (s) s.add(name); + this.context.variables.add(name); + } + // Renames user-declared variables that collide with reserved Pine namespaces // (like "ta" or "strategy") so they don't shadow the runtime objects. getSafeName(name) { @@ -157,12 +206,12 @@ class CodeGenerator { // of throwing "is not iterable" (Pine functions often return na early on). const rhs = `pinescript.unpack(${this.generate(node.value)}, ${(node.targets || []).length})`; - const needsDeclaration = targets.some(t => !this.context.variables.has(t) && !this.reservedNamespaces.has(t)); + const needsDeclaration = targets.some(t => !this.scopeDeclared(t) && !this.reservedNamespaces.has(t)); if (needsDeclaration) { for (const t of targets) { - if (!this.reservedNamespaces.has(t)) this.context.variables.add(t); + if (!this.reservedNamespaces.has(t)) this.scopeDeclare(t); } - this.writeln(`let ${lhs} = ${rhs};`); + this.writeln(`var ${lhs} = ${rhs};`); return; } @@ -220,7 +269,7 @@ class CodeGenerator { // Shim for Pine's array namespace -- delegates to the pinescript runtime functions. this.write('globalThis.array = globalThis.array || {\n'); - this.write(' from: (...items) => items,\n'); + this.write(' from: (...items) => pinescript.__decArr(items),\n'); this.write(' size: (arr) => pinescript.arraySize(arr),\n'); this.write(' get: (arr, i) => pinescript.arrayGet(arr, i),\n'); this.write(' set: (arr, i, v) => pinescript.arraySet(arr, i, v),\n'); @@ -326,13 +375,19 @@ class CodeGenerator { this.write('globalThis.chart = globalThis.chart || __pineNS({ point: __pineNS({}), bg_color: null, fg_color: null });\n'); this.write('globalThis.line = globalThis.line || __pineNS({ style_solid: "solid", style_dashed: "dashed", style_dotted: "dotted" });\n'); this.write('globalThis.box = globalThis.box || __pineNS({});\n'); + // color is a namespace of constants (color.red) plus helpers (color.new/rgb). + // We don't render, so colors are opaque string tokens and helpers pass through. + this.write('globalThis.color = globalThis.color || __pineNS(Object.assign(function(c) { return c; }, { new: function(c, t) { return c; }, rgb: function(r, g, b, t) { return "#rgb(" + [r, g, b].join(",") + ")"; }, from_gradient: function(v, lo, hi, c1, c2) { return c1; }, r: function() { return 0; }, g: function() { return 0; }, b: function() { return 0; }, t: function() { return 0; }, aqua: "#00BCD4", black: "#363A45", blue: "#2962FF", fuchsia: "#E040FB", gray: "#787B86", green: "#4CAF50", lime: "#00E676", maroon: "#880E4F", navy: "#311B92", olive: "#808000", orange: "#FF9800", purple: "#9C27B0", red: "#FF5252", silver: "#B2B5BE", teal: "#00897B", white: "#FFFFFF", yellow: "#FFEB3B" }));\n'); this.write('globalThis.label = globalThis.label || __pineNS({ style_label_down: "label_down", style_label_up: "label_up", style_none: "none" });\n'); this.write('globalThis.polyline = globalThis.polyline || __pineNS({});\n'); this.write('globalThis.linefill = globalThis.linefill || __pineNS({});\n'); this.write('globalThis.matrix = globalThis.matrix || __pineNS({});\n'); this.write('globalThis.map = globalThis.map || __pineNS({});\n'); this.write('globalThis.session = globalThis.session || __pineNS({ regular: "regular", extended: "extended" });\n'); - this.write('globalThis.dayofweek = globalThis.dayofweek || __pineNS({ sunday: 1, monday: 2, tuesday: 3, wednesday: 4, thursday: 5, friday: 6, saturday: 7 });\n'); + this.write('globalThis.ticker = globalThis.ticker || __pineNS({});\n'); + // dayofweek is both a function (dayofweek(time) -> 1..7, Sunday=1) and a holder + // of day-name constants (dayofweek.monday). Wrap a callable so both forms work. + this.write('globalThis.dayofweek = globalThis.dayofweek || __pineNS(Object.assign(function(t) { return new Date(t != null ? t : (globalThis.time || 0)).getUTCDay() + 1; }, { sunday: 1, monday: 2, tuesday: 3, wednesday: 4, thursday: 5, friday: 6, saturday: 7 }));\n'); this.write('globalThis.timeframe = __pineNS(Object.assign(globalThis.timeframe || {}, { period: (globalThis.timeframe && globalThis.timeframe.period) || "D", isintraday: false, isdaily: true, multiplier: 1 }));\n'); this.write('globalThis.request = __pineNS(globalThis.request || {});\n'); this.write('globalThis.input = __pineNS(globalThis.input || {});\n'); @@ -359,7 +414,6 @@ class CodeGenerator { this.write('globalThis.float = globalThis.float || function(x) { return x == null ? null : Number(x); };\n'); this.write('globalThis.bool = globalThis.bool || function(x) { return Boolean(x); };\n'); this.write('globalThis.string = globalThis.string || function(x) { return x == null ? null : String(x); };\n'); - this.write('globalThis.dayofweek = globalThis.dayofweek || __pineNS({ sunday: 1, monday: 2, tuesday: 3, wednesday: 4, thursday: 5, friday: 6, saturday: 7 });\n'); this.write('globalThis.str = globalThis.str || __pineNS({});\n'); // The reserved namespaces (ta, math, str, array) are emitted as pinescript. // when a script uses them as a value (e.g. `math.pi`), so mirror the global @@ -412,7 +466,9 @@ class CodeGenerator { // Table namespace -- enough to let scripts create tables and populate cells // without crashing, even though we don't render them visually. this.write('pinescript.table = {\n'); - this.write(' new: function(position, columns, rows, opts) { return { position, columns, rows, opts: opts || {}, cells: [] }; },\n'); + // __decDraw lets Pine v6 method syntax (t.cell(...), t.merge_cells(...)) work as + // chainable no-ops while the real `cells` array still backs the function form. + this.write(' new: function(position, columns, rows, opts) { return pinescript.__decDraw({ position, columns, rows, opts: opts || {}, cells: [] }); },\n'); this.write(' cell: function(table, column, row, text, opts) {\n'); this.write(' if (!table) return null;\n'); this.write(' table.cells.push({ column, row, text, opts: opts || {} });\n'); @@ -446,6 +502,7 @@ class CodeGenerator { this.pushIndent(); this.functionLocalDeclStack.push(new Set()); + this.pushScope(); this.writeln('globalThis.__pineState = globalThis.__pineState || {};'); this.writeln('const state = globalThis.__pineState["main"] = globalThis.__pineState["main"] || {};'); @@ -461,6 +518,7 @@ class CodeGenerator { this.generate(statement); } + this.popScope(); this.functionLocalDeclStack.pop(); this.popIndent(); @@ -529,6 +587,10 @@ function run(data, options = {}) { rt.__shapeIdx = 0; globalThis.bar_index = i; globalThis.last_bar_index = n - 1; + globalThis.time_tradingday = inTime[i]; + globalThis.time_close = inTime[i]; + globalThis.last_bar_time = inTime[n - 1]; + globalThis.timenow = inTime[n - 1]; globalThis.barstate = { isfirst: i === 0, islast: i === n - 1, isrealtime: false, ishistory: true, isconfirmed: true, isnew: true, islastconfirmedhistory: i === n - 1, @@ -575,7 +637,6 @@ function run(data, options = {}) { generateVariableDeclaration(node) { const name = this.getSafeName(node.name); - const prefix = (node.isVarip || node.isVar || node.declaredType) ? 'let' : 'const'; if (node.isVar || node.isVarip) { // var/varip declarations persist across bars, so we store them in the state object // and only initialize on the very first bar. @@ -584,22 +645,26 @@ function run(data, options = {}) { return; } - this.context.variables.add(name); - this.writeln(`${prefix} ${name} = ${this.generate(node.value)};`); + // `var` (function-scoped) so the variable matches PineScript's scoping even + // when declared inside a block. + const decl = this.scopeDeclared(name) ? '' : 'var '; + this.scopeDeclare(name); + this.writeln(`${decl}${name} = ${this.generate(node.value)};`); } generateAssignment(node) { - // Pine lets you assign to a variable without declaring it first. - // We detect the first assignment and emit a `let` declaration automatically. + // Pine lets you assign to a variable without declaring it first. We detect the + // first assignment in a scope and emit a `var` declaration automatically (var, + // not let, so it is function-scoped like PineScript). if (node.target && node.target.type === 'Identifier') { const name = this.getSafeName(node.target.name); if (this.context.stateVars.has(name)) { this.writeln(`state.${name} = ${this.generate(node.value)};`); return; } - if (!this.context.variables.has(name) && !this.reservedNamespaces.has(name)) { - this.context.variables.add(name); - this.writeln(`let ${name} = ${this.generate(node.value)};`); + if (!this.scopeDeclared(name) && !this.reservedNamespaces.has(name)) { + this.scopeDeclare(name); + this.writeln(`var ${name} = ${this.generate(node.value)};`); return; } } @@ -657,8 +722,15 @@ function run(data, options = {}) { } generateForInStatement(node) { - const variable = Array.isArray(node.variable) ? `[${node.variable.join(', ')}]` : node.variable; - this.writeln(`for (const ${variable} of ${this.generate(node.iterable)}) {`); + const iterable = this.generate(node.iterable); + if (Array.isArray(node.variable)) { + // Pine's `for [index, value] in collection` yields the index/key alongside + // the value, which maps to JS .entries() (works for arrays and Maps alike). + // Guard against a null/undefined collection so the loop simply doesn't run. + this.writeln(`for (const [${node.variable.join(', ')}] of (${iterable} ?? []).entries()) {`); + } else { + this.writeln(`for (const ${node.variable} of (${iterable} ?? [])) {`); + } this.pushIndent(); this.generate(node.body); this.popIndent(); @@ -749,22 +821,27 @@ function run(data, options = {}) { } const localRename = new Map(); + const paramNames = []; const params = (node.params || []).map(p => { - if (typeof p === 'string') return p; + if (typeof p === 'string') { paramNames.push(p); return p; } if (p && typeof p === 'object') { let paramName = p.name; if (node.isMethod && paramName === 'this') { paramName = '_this'; localRename.set('this', '_this'); } + paramNames.push(paramName); if (p.defaultValue) return `${paramName} = ${this.generate(p.defaultValue)}`; return paramName; } + paramNames.push(String(p)); return String(p); }); this.localRenameStack.push(localRename); this.functionLocalDeclStack.push(new Set()); + // New function scope, seeded with the parameter names so they aren't re-declared. + this.pushScope(paramNames); this.writeln(`function ${node.name}(${params.join(', ')}) {`); this.pushIndent(); @@ -773,8 +850,10 @@ function run(data, options = {}) { for (let i = 0; i < stmts.length; i++) { const statement = stmts[i]; const isLast = i === stmts.length - 1; - if (isLast && statement && statement.type === 'ExpressionStatement') { - this.writeln(`return ${this.generate(statement.expression)};`); + if (isLast) { + // A Pine function returns the value of its last expression -- including + // when that expression is an if/else, which we turn into returns. + this.generateReturnable(statement); } else { this.generate(statement); } @@ -785,6 +864,58 @@ function run(data, options = {}) { this.popIndent(); this.writeln('}'); + this.popScope(); + this.functionLocalDeclStack.pop(); + this.localRenameStack.pop(); + } + + // Emits a statement in "return position": the value it produces becomes the + // function's return value. Handles plain expressions and if/else (whose taken + // branch's last expression is what Pine returns). + generateReturnable(stmt) { + if (!stmt) { + this.writeln('return null;'); + return; + } + if (stmt.type === 'ExpressionStatement') { + this.writeln(`return ${this.generate(stmt.expression)};`); + return; + } + if (stmt.type === 'IfStatement') { + this.writeln(`if (${this.generate(stmt.condition)}) {`); + this.pushIndent(); + this.generateBranchReturnable(stmt.thenBranch); + this.popIndent(); + this.writeln('} else {'); + this.pushIndent(); + if (stmt.elseBranch) this.generateBranchReturnable(stmt.elseBranch); + else this.writeln('return null;'); + this.popIndent(); + this.writeln('}'); + return; + } + // Anything else (assignment, loop, ...) can't be a return value; emit it as a + // normal statement. The function then returns undefined down this path, which + // matches Pine returning na from a branch that ends in a non-expression. + this.generate(stmt); + } + + // Generates a branch (Block or single statement) with its last statement in + // return position. + generateBranchReturnable(branch) { + if (branch && branch.type === 'Block') { + const stmts = branch.statements || []; + if (stmts.length === 0) { + this.writeln('return null;'); + return; + } + for (let i = 0; i < stmts.length; i++) { + if (i === stmts.length - 1) this.generateReturnable(stmts[i]); + else this.generate(stmts[i]); + } + } else { + this.generateReturnable(branch); + } } generateBlock(node) { @@ -947,7 +1078,14 @@ function run(data, options = {}) { } if (node.type === 'PropertyAccess') { const fullName = this.flattenPropertyAccess(node); - if (fullName) return this.mapFunctionName(fullName); + if (fullName) { + const mapped = this.mapFunctionName(fullName); + // Only use the mapping if the name actually resolved to a runtime function. + // Otherwise fall through to normal generation so the receiver resolves + // correctly -- e.g. a method call on a `var` array (uniqueLengths.push(x)) + // becomes state.uniqueLengths.push(x), not bare uniqueLengths.push(x). + if (mapped !== fullName) return mapped; + } } return this.generate(node); } @@ -1087,6 +1225,7 @@ function run(data, options = {}) { 'array.new_line': 'pinescript.arrayNew', 'array.new_box': 'pinescript.arrayNew', 'array.new_polyline': 'pinescript.arrayNew', + 'array.new_linefill': 'pinescript.arrayNew', 'array.size': 'pinescript.arraySize', 'array.get': 'pinescript.arrayGet', 'array.set': 'pinescript.arraySet', @@ -1203,6 +1342,8 @@ function run(data, options = {}) { // Plotting and visual output 'plot': 'pinescript.plot', 'plotshape': 'pinescript.plotshape', + 'plotchar': 'pinescript.plotchar', + 'plotarrow': 'pinescript.plotarrow', 'plotbar': 'pinescript.plotbar', 'plotcandle': 'pinescript.plotcandle', 'bgcolor': 'pinescript.bgcolor', diff --git a/src/lexer.js b/src/lexer.js index 6bfd3e2..b6d8f0f 100644 --- a/src/lexer.js +++ b/src/lexer.js @@ -178,10 +178,14 @@ class Lexer { } skipComments() { - // Directive-style comments like //@version=5 carry meaning and should not be skipped + // Only //@version carries meaning for us. Other //@ annotations (//@description, + // //@param, //@Metrify author tags, ...) are documentation and must be skipped as + // ordinary comments, otherwise the word after //@ leaks out as a stray identifier. if (this.currentChar === '/' && this.peek(1) === '/' && this.peek(2) === '@') { - // Leave it alone so the tokenizer can process it as a directive - return false; + let word = ''; + for (let o = 3; o < 10 && this.peek(o) && /[A-Za-z]/.test(this.peek(o)); o++) word += this.peek(o); + if (word.toLowerCase() === 'version') return false; // process the version directive + // otherwise fall through and skip the rest of the line like any other comment } if (this.currentChar === '/' && this.peek(1) === '/') { while (this.currentChar && this.currentChar !== '\n') { @@ -482,6 +486,32 @@ class Lexer { return contTypes.has(lastTok.type); }; + // Looks ahead from the current newline: if the next non-blank, non-comment + // line begins with an operator that cannot start a statement (e.g. a leading + // "?" / ":" of a wrapped ternary, or "+", "and", "."), it's a continuation. + // The backward check above misses these because the trailing token is an + // ordinary operand. Pine has no statement that begins with these operators. + const nextLineStartsContinuation = () => { + const src = this.source; + let i = this.pos; // sitting on '\n' + while (i < src.length) { + const c = src[i]; + if (c === '\n' || c === ' ' || c === '\t') { i++; continue; } + if (c === '/' && src[i + 1] === '/') { // skip a comment-only line + i += 2; + while (i < src.length && src[i] !== '\n') i++; + continue; + } + break; + } + if (i >= src.length) return false; + const rest = src.slice(i); + if (/^(and|or)\b/.test(rest)) return true; + const two = src.slice(i, i + 2); + if (two === '==' || two === '!=' || two === '<=' || two === '>=') return true; + return '?:*/%+-<>.'.includes(src[i]); + }; + const flushPendingIndents = () => { while (this.pendingIndents.length > 0) { tokens.push(this.pendingIndents.shift()); @@ -514,7 +544,7 @@ class Lexer { continue; } - if (isLineContinuation()) { + if (isLineContinuation() || nextLineStartsContinuation()) { this.advance(); // Eat the leading whitespace on the continuation line without emitting indentation tokens while (this.currentChar === ' ' || this.currentChar === '\t') { diff --git a/src/parser.js b/src/parser.js index 2afc98c..756ce7b 100644 --- a/src/parser.js +++ b/src/parser.js @@ -40,9 +40,19 @@ class Parser { isGenericAnnotationStart() { if (!this.match(TokenType.LESS)) return false; + // A real generic type argument list (e.g. array.new, matrix) + // contains only type tokens: identifiers, dotted names, commas and nested <>. + // If we encounter anything else (an operator, `=`, a literal, a newline), this is + // not a generic but a comparison like `close < open` — bail out so it parses as + // a relational expression instead of swallowing the rest of the line. + const allowed = new Set([ + TokenType.IDENTIFIER, TokenType.DOT, TokenType.COMMA, + TokenType.LESS, TokenType.GREATER, + ]); let depth = 0; for (let i = 0; this.peek(i); i++) { const t = this.peek(i); + if (!allowed.has(t.type)) return false; if (t.type === TokenType.LESS) depth++; else if (t.type === TokenType.GREATER) { depth--; diff --git a/src/reviewer.js b/src/reviewer.js index 4e19c31..ce0bd83 100644 --- a/src/reviewer.js +++ b/src/reviewer.js @@ -1,9 +1,21 @@ import vm from 'node:vm'; -import { spawn } from 'node:child_process'; import { builtins as runtimeBuiltins } from './builtins.js'; const uniq = (arr) => Array.from(new Set(arr)); +// Blank out string literals and comments so heuristic scans don't trip over +// "word(" sequences inside text. Strings are removed before comments so a "//" +// inside a string can't be mistaken for a comment. Good enough for review +// heuristics, not a full tokenizer. +function stripNonCode(code) { + return code + .replace(/`(?:\\.|[^`\\])*`/g, '``') + .replace(/"(?:\\.|[^"\\])*"/g, '""') + .replace(/'(?:\\.|[^'\\])*'/g, "''") + .replace(/\/\*[\s\S]*?\*\//g, ' ') + .replace(/\/\/[^\n]*/g, ' '); +} + function extractPinescriptMembers(code) { const re = /\bpinescript\.([A-Za-z_$][A-Za-z0-9_$]*)/g; const out = []; @@ -86,21 +98,6 @@ function allowedMembers() { ]); } -function buildAiReviewSnippet(code) { - const lines = code.split(/\r?\n/); - if (lines.length <= 240) return code; - - // The generator emits a marker right before the user's script body. - const marker = '// Main script logic'; - const idx = lines.findIndex((l) => l.includes(marker)); - if (idx >= 0) { - return lines.slice(idx, idx + 420).join('\n'); - } - - // Fallback: last N lines. - return lines.slice(-300).join('\n'); -} - // --------------------------------------------------------------------------- // New review stages // --------------------------------------------------------------------------- @@ -157,17 +154,24 @@ function checkCompleteness(code) { function checkUndefinedFunctions(code) { const warnings = []; + // Comments and string literals routinely contain "word(" sequences (e.g. + // "// Stop Loss (SL)") that are not function calls. Strip them first so we + // only inspect actual code. + const scan = stripNonCode(code); + // Collect declared functions / const arrow functions const declaredRe = /(?:function\s+(\w+)|const\s+(\w+)\s*=\s*(?:\([^)]*\)|[^=])\s*=>)/g; const declared = new Set(); - for (let m; (m = declaredRe.exec(code)) !== null;) { + for (let m; (m = declaredRe.exec(scan)) !== null;) { declared.add(m[1] || m[2]); } - // Collect all function-call-like identifiers - const callRe = /\b([a-zA-Z_$][a-zA-Z0-9_$]*)\s*\(/g; + // Collect bare function-call identifiers. The negative lookbehind for "." + // skips member calls like color.rgb() or obj.set_x1() -- those resolve at + // runtime against the runtime object, not as standalone functions. + const callRe = /(? m.trim())); @@ -464,95 +474,10 @@ export async function reviewGeneratedCode(code, opts = {}) { const stateWarnings = checkStatePersistence(code); report.warnings.push(...stateWarnings); - // 8) AI review (optional) - if (options.ai) { - const python = process.env.PINE_REVIEWER_PYTHON || 'python'; - const script = process.env.PINE_REVIEWER_SCRIPT || 'ai_reviewer/review.py'; - try { - const input = JSON.stringify({ code: buildAiReviewSnippet(code) }); - - const payload = await new Promise((resolve, reject) => { - const child = spawn(python, [script], { - stdio: ['pipe', 'pipe', 'pipe'], - env: { - ...process.env, - HF_HUB_DISABLE_SYMLINKS_WARNING: process.env.HF_HUB_DISABLE_SYMLINKS_WARNING ?? '1', - TRANSFORMERS_VERBOSITY: process.env.TRANSFORMERS_VERBOSITY ?? 'error', - TF_CPP_MIN_LOG_LEVEL: process.env.TF_CPP_MIN_LOG_LEVEL ?? '3', - TOKENIZERS_PARALLELISM: process.env.TOKENIZERS_PARALLELISM ?? 'false', - PYTHONWARNINGS: process.env.PYTHONWARNINGS ?? 'ignore', - TRANSFORMERS_NO_TF: process.env.TRANSFORMERS_NO_TF ?? '1', - TRANSFORMERS_NO_FLAX: process.env.TRANSFORMERS_NO_FLAX ?? '1' - } - }); - let stdout = ''; - let stderr = ''; - - const start = Date.now(); - const spinnerFrames = ['|', '/', '-', '\\']; - let spinnerIdx = 0; - const spinner = setInterval(() => { - const elapsedSec = Math.floor((Date.now() - start) / 1000); - const frame = spinnerFrames[spinnerIdx++ % spinnerFrames.length]; - process.stderr.write(`\r[ai-review] ${frame} running... ${elapsedSec}s`); - }, 250); - - const clearSpinnerLine = () => { - process.stderr.write('\r\x1b[2K'); - }; - - child.stdout.setEncoding('utf8'); - child.stderr.setEncoding('utf8'); - - child.stdout.on('data', (d) => { - stdout += d; - }); - child.stderr.on('data', (d) => { - stderr += d; - clearSpinnerLine(); - process.stderr.write(d); - }); - - child.on('error', (err) => reject(err)); - child.on('close', (code) => { - clearInterval(spinner); - clearSpinnerLine(); - if (code !== 0) { - const msg = `AI reviewer exited with code ${code}${stderr ? `\n${stderr}` : ''}`; - reject(new Error(msg)); - return; - } - - try { - resolve(JSON.parse(stdout || '{}')); - } catch (e) { - reject(new Error(`AI reviewer returned invalid JSON: ${e?.message || String(e)}${stdout ? `\n${stdout}` : ''}`)); - } - }); - - child.stdin.write(input); - child.stdin.end(); - }); - - if (payload && typeof payload === 'object') { - if (Array.isArray(payload.errors)) report.errors.push(...payload.errors.map(String)); - if (Array.isArray(payload.warnings)) report.warnings.push(...payload.warnings.map(String)); - if (Array.isArray(payload.notes)) report.notes.push(...payload.notes.map(String)); - if (payload.ok === false) report.ok = false; - } else { - report.warnings.push('AI review returned non-object JSON.'); - } - } catch (e) { - report.notes.push( - `AI review unavailable: ${e?.message || String(e)} (install optional deps: pip install -r ai_reviewer/requirements.txt)` - ); - } - } - - // 9) Generate suggestions + // 8) Generate suggestions report.suggestions = generateSuggestions(code, report); - // 10) Compute confidence score + // 9) Compute confidence score report.confidence = computeConfidence(report); return report; diff --git a/test/engine_tests.js b/test/engine_tests.js index 23ddb15..8dc0ca4 100644 --- a/test/engine_tests.js +++ b/test/engine_tests.js @@ -173,6 +173,17 @@ plot(ta.ema(close, 3), "e")`, assertSeries(rt.plots['e'].data, expected, 'ema mismatch'); }); +await test('math.todegrees / math.toradians convert correctly', async () => { + const rt = await runPine( + `//@version=5 +indicator("t") +plot(math.todegrees(math.toradians(close)), "d")`, + rampData, + ); + // toradians then todegrees is a round-trip, so output equals the input series. + assertSeries(rt.plots['d'].data, ramp, 'todegrees/toradians round-trip mismatch'); +}); + console.log('='.repeat(60)); console.log(`Results: ${passed} passed, ${failed} failed out of ${passed + failed} tests`); if (failed > 0) process.exit(1); diff --git a/test/test.js b/test/test.js index adaafb3..369feb7 100644 --- a/test/test.js +++ b/test/test.js @@ -8,7 +8,8 @@ const tests = [ { name: 'Simple variable declaration', input: `fastLength = 10`, - check: (result) => result.success && (result.code.includes('const fastLength = 10') || result.code.includes('let fastLength = 10')) + // PineScript is function-scoped, so variables are declared with `var`. + check: (result) => result.success && /\b(var|let|const) fastLength = 10/.test(result.code) }, { name: 'Input declaration', diff --git a/web/index.html b/web/index.html new file mode 100644 index 0000000..de44e8b --- /dev/null +++ b/web/index.html @@ -0,0 +1,274 @@ + + + + + +PineScript → JavaScript Converter + + + +
+

PineScript → JavaScript

+

Paste your TradingView PineScript (v5/v6) and convert it to runnable JavaScript — entirely in your browser, nothing is uploaded. + Source on GitHub

+
+ +
+
+ + +
+ + + +
+ +
+
+
PineScript
+ +
+
+
JavaScript
+

+    
+
+ +
+
+ + + +