oakscriptjs 0.13.1 → 0.13.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/AGENTS.md ADDED
@@ -0,0 +1,48 @@
1
+ # AGENTS.md
2
+
3
+ OakScriptJS: Pine Script v6-compatible technical analysis and a script API (`oakscriptjs/script`) for writing
4
+ indicators and strategies in JavaScript. No runtime dependencies.
5
+
6
+ **Writing scripts with this library** (indicators, strategies, ports from Pine Script v6): read
7
+ [skills/oakscript/SKILL.md](skills/oakscript/SKILL.md). It ships in the npm package, so it matches the
8
+ installed version under `node_modules/oakscriptjs/skills/oakscript/`.
9
+
10
+ The rest of this file is for working on the library itself.
11
+
12
+ ## Layout
13
+
14
+ - `src/script/index.ts`: the script API (`executeScript`, `indicator`, `strategy`, `input`, `plot`..., `eachBar`,
15
+ `request.security`, the context-bound `ta`).
16
+ - `src/runtime/series.ts`: `Series` and `BarData`. `src/ta-series.ts`: Series wrappers of `src/ta/index.ts`
17
+ (array functions, `taCore`).
18
+ - `src/<namespace>/`: `math`, `array`, `matrix`, `map`, `str`, `color`, `time`, `timeframe`, `line`, `label`,
19
+ `box`, `linefill`, `polyline`, `table`, `chartpoint`, `compare`, `callsite`, `session`, `security`, `strategy`
20
+ (types of the order engine interface).
21
+ - `tests/<namespace>/`: jest tests (ts-jest). `tests/skills/`: runs the guide's examples and code blocks.
22
+ - `docs/guide.md`, `docs/inventory.md`: user guide and coverage of the Pine Script v6 functions.
23
+ - `skills/oakscript/`: the agent guide (`SKILL.md`, `reference/`, `examples/`).
24
+
25
+ ## Commands
26
+
27
+ ```bash
28
+ npm ci
29
+ npm run build # dist/esm + dist/cjs
30
+ npm test # jest, all suites (includes tests/skills)
31
+ npm run lint # eslint src
32
+ npm run typecheck # tsc --noEmit
33
+ npm run check:package
34
+ ```
35
+
36
+ CI (`.github/workflows/ci.yml`) runs build, check:package, test and typecheck on every push to main. Publishing
37
+ runs only on a `v*` tag (`.github/workflows/publish.yml`).
38
+
39
+ ## Conventions
40
+
41
+ - TypeScript strict, ESM: relative imports in `src` end in `.js`.
42
+ - Names, argument order, defaults and results follow Pine Script v6; tests compare with values measured on Pine
43
+ Script. A deliberate difference is written in the JSDoc and in `docs/guide.md`.
44
+ - A new or changed function: tests in `tests/<namespace>/`, then `docs/inventory.md`, `docs/guide.md` and, when it
45
+ changes how scripts are written, `skills/oakscript/`.
46
+ - Examples in `skills/oakscript/` are real code: `examples/*.js` and every ```js block of the guide run in
47
+ `tests/skills/examples.test.ts`. Keep them passing when the API changes.
48
+ - Releases: version in `package.json`, `CHANGELOG.md`, then a `v*` tag.
@@ -65,7 +65,7 @@ export type * from './strategy/index.js';
65
65
  export { LightweightChartsAdapter } from './runtime/adapters/LightweightChartsAdapter.js';
66
66
  export type { LightweightSeriesDefinitions } from './runtime/adapters/LightweightChartsAdapter.js';
67
67
  export { SimpleInputAdapter } from './runtime/adapters/SimpleInputAdapter.js';
68
- export declare const VERSION = "0.12.3";
68
+ export declare const VERSION = "0.13.2";
69
69
  /**
70
70
  * Library information
71
71
  */
package/dist/cjs/index.js CHANGED
@@ -208,7 +208,7 @@ var import_input_active = require("./runtime/input-active.js");
208
208
  var import_strategy = require("./strategy/index.js");
209
209
  var import_LightweightChartsAdapter = require("./runtime/adapters/LightweightChartsAdapter.js");
210
210
  var import_SimpleInputAdapter = require("./runtime/adapters/SimpleInputAdapter.js");
211
- const VERSION = "0.12.3";
211
+ const VERSION = "0.13.2";
212
212
  const info = {
213
213
  name: "OakScriptJS",
214
214
  version: VERSION,
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/index.ts"],
4
- "sourcesContent": ["/**\n * OakScriptJS - Simplified PineScript-like library for JavaScript\n *\n * This library provides Series-based lazy evaluation and technical analysis functions\n * for building trading indicators. The PineScript-style script API is in `oakscriptjs/script`.\n *\n * Key features:\n * - Series class for lazy evaluation and method chaining\n * - Core TA functions (array-based)\n * - TA-Series wrappers (Series-based)\n *\n * @packageDocumentation\n */\n\n// Export all types\nexport * from './types/index.js';\nexport * from './types/metadata.js';\n\n// Export core namespaces (array-based functions)\nimport * as taCore from './ta/index.js';\nimport * as math from './math/index.js';\nimport * as array from './array/index.js';\nimport * as map from './map/index.js';\nimport * as str from './str/index.js';\nimport * as color from './color/index.js';\nimport * as time from './time/index.js';\nimport * as timeframe from './timeframe/index.js';\nimport * as matrix from './matrix/index.js';\nimport * as line from './line/index.js';\nimport * as box from './box/index.js';\nimport * as label from './label/index.js';\nimport * as linefill from './linefill/index.js';\nimport * as chartPoint from './chartpoint/index.js';\nimport * as polyline from './polyline/index.js';\nimport * as table from './table/index.js';\nimport * as text from './text/index.js';\nimport * as compare from './compare/index.js';\nimport * as callsite from './callsite/index.js';\n\nexport { taCore, math, array, map, str, color, time, timeframe, matrix, line, box, label, linefill, chartPoint, polyline, table, text, compare, callsite };\n\n// Renderable drawings (IndicatorResult labels, lines, boxes, linefills, polylines, tables) and future bar times\nexport { drawingOutputs, type DrawingOutputs } from './drawing/output.js';\nexport { barInterval, barTime } from './drawing/bar-time.js';\n\n// Drawing registry limits (PineScript indicator(max_lines_count / max_labels_count / max_boxes_count / max_polylines_count))\nexport {\n resetDrawings,\n setDrawingLimits,\n setDrawingBar,\n holdDrawing,\n releaseDrawing,\n varDrawing,\n createdDrawingKinds,\n type DrawingKind,\n type DrawingLimits,\n type DrawingVariable,\n} from './drawing/registry.js';\n\n// The session of a chart context without a script run: time(tf, session, timezone), time_close(...), time_tradingday,\n// session.* flags, request.security periods with the session and holidays, and bars after the last bar on the trading\n// calendar\nexport {\n chartSessions,\n sessionPeriods,\n periodTime,\n periodClose,\n tradingDayTime,\n chartSessionFlags,\n resampleSession,\n barTimeAfter,\n barsUntil,\n type SessionContext,\n type ChartSessions,\n type SessionPeriods,\n type SessionResample,\n} from './session/context.js';\nexport { TradingCalendar, type SessionSpec } from './session/calendar.js';\nexport { SessionBars, sessionFlags, type SessionFlags } from './session/bars.js';\n\n// Same-symbol request.security on a higher timeframe (completion and lookahead rules) and ticker.heikinashi\nexport { resample, periodsOf, afterCompletion, mapToChart, heikinAshi, type Resampled } from './security/resample.js';\n// request.security of another symbol: which bar of the other symbol each chart bar shows (#179)\nexport {\n securityOther,\n securityOtherValues,\n securityLowerTf,\n type OtherSymbolChart,\n type OtherSymbolOptions,\n type SymbolCalendar,\n} from './security/other.js';\n\n// Export Series class (self-contained, no context)\nexport { Series, BarData } from './runtime/series.js';\nexport type { SeriesExtractor } from './runtime/series.js';\n\n// Export TA-Series namespace (Series-based wrappers)\nimport * as ta from './ta-series.js';\nexport { ta };\n\n// Export metadata types for indicator return values\nexport type {\n PlotOptions,\n HLineOptions,\n FillOptions,\n InputMetadata,\n PlotMetadata,\n IndicatorMetadata,\n TimeValue,\n PlotData,\n HLineData,\n FillData,\n FillGradient,\n IndicatorResult,\n IndicatorFactory,\n PlotStyle,\n LineStyle,\n PlotDisplay,\n InputType\n} from './types/metadata.js';\n\n// Re-export commonly used functions for convenience\nexport { sma, ema, rsi, macd, bb, stdev, crossover, crossunder, change, tr, atr, cum, vwap } from './ta-series.js';\nexport { abs, ceil, floor, round, max, min, avg, sum, sqrt, pow, exp, log, sin, cos, tan } from './math/index.js';\nexport { rgb, from_hex as color_from_hex, new_color } from './color/index.js';\n\n// Export chart data utilities\nexport { ohlcFromBars, getClose, getHigh, getLow, getOpen, getSource, getSourceSeries, at, div } from './utils/index.js';\nexport { setSourceResolver, externalSource, BUILTIN_SOURCES, type SourceResolver } from './utils/source-resolver.js';\n\n// Export libraries (ZigZag, etc.)\nexport * from './lib/index.js';\n\n// Export helper functions for generated indicators (PineScript compatibility)\nexport {isNA as na, nz, fixnan} from './utils/index.js';\n\n// Export indicator infrastructure\nexport {\n indicator,\n type IndicatorMetadataConfig,\n type InputDefinition,\n type IndicatorContext,\n type IndicatorInstance,\n type IndicatorConstructor,\n} from './indicator.js';\n\nexport {\n input,\n type InputValue,\n type IntInputOptions,\n type FloatInputOptions,\n type SourceInputOptions,\n type BoolInputOptions,\n type StringInputOptions,\n type TimeInputOptions,\n type SourceType,\n} from './input.js';\n\nexport {\n plot as plotHelper,\n createPlot,\n type Time,\n type TimeValuePair,\n type PlotOptions as PlotHelperOptions,\n type PlotResult,\n} from './plot.js';\n\n// Runtime exports - global context runtime (setContext, plot, hline) drawing through a ChartAdapter\nexport {\n setContext,\n clearContext,\n getContext,\n registerCalculate,\n recalculate,\n plot,\n hline,\n clearPlots,\n} from './runtime/runtime.js';\n\nexport {\n input_int,\n input_float,\n input_bool,\n input_string,\n input_text_area,\n input_source,\n input_timeframe,\n input_session,\n input_symbol,\n input_time,\n} from './runtime/inputs.js';\nexport { isInputActive } from './runtime/input-active.js';\n\nexport type {\n OakScriptContext,\n ChartAdapter,\n InputAdapter,\n SeriesHandle,\n SeriesOptions,\n InputConfig,\n InputCondition,\n InputOptions,\n OhlcvData,\n PlotConfig,\n HLineConfig,\n FillConfig,\n ArrowConfig,\n CandleConfig,\n ShapeConfig,\n BarColorConfig,\n} from './runtime/types.js';\n\n// Strategy types and constants (the script API forwards strategy.* calls to a host engine)\nexport {\n STRATEGY_CONSTANTS,\n STRATEGY_DEFAULTS,\n STRATEGY_NUMBER_VARIABLES,\n STRATEGY_TEXT_VARIABLES,\n} from './strategy/index.js';\nexport type * from './strategy/index.js';\n\nexport { LightweightChartsAdapter } from './runtime/adapters/LightweightChartsAdapter.js';\nexport type { LightweightSeriesDefinitions } from './runtime/adapters/LightweightChartsAdapter.js';\nexport { SimpleInputAdapter } from './runtime/adapters/SimpleInputAdapter.js';\n\n// Version\nexport const VERSION = '0.12.3';\n\n/**\n * Library information\n */\nexport const info = {\n name: 'OakScriptJS',\n version: VERSION,\n description: 'Simplified PineScript-like library - Series + TA functions',\n features: {\n series: 'Lazy evaluation with Series class',\n ta: 'Technical analysis functions (Series and array-based)',\n script: 'PineScript-style script API (oakscriptjs/script): indicator, strategy, inputs, plots, request.security',\n runtime: 'Global context runtime drawing through a ChartAdapter',\n indicator: 'Indicator function with automatic pane management'\n },\n namespaces: {\n core: ['ta', 'math', 'array', 'map', 'str', 'color', 'time', 'timeframe', 'matrix'],\n drawing: ['line', 'box', 'label', 'linefill', 'chartPoint', 'polyline', 'table', 'text'],\n runtime: ['setContext', 'plot', 'hline', 'input_*'],\n indicator: ['indicator', 'input', 'plotHelper', 'createPlot']\n }\n};\n\n// PineScript compatibility stubs - functions that have no runtime effect\n// These are no-ops so that PineScript-style code runs without modification\n\n/**\n * alertcondition() - Stub for PineScript alert configuration\n * In PineScript, this registers an alert condition. In oakscriptjs, it's a no-op\n * since alert configuration is handled by the host application.\n */\nexport function alertcondition(_condition: unknown, _title?: string, _message?: string): void {\n // No-op: Alert conditions are handled by the host application\n}\n"],
4
+ "sourcesContent": ["/**\n * OakScriptJS - Simplified PineScript-like library for JavaScript\n *\n * This library provides Series-based lazy evaluation and technical analysis functions\n * for building trading indicators. The PineScript-style script API is in `oakscriptjs/script`.\n *\n * Key features:\n * - Series class for lazy evaluation and method chaining\n * - Core TA functions (array-based)\n * - TA-Series wrappers (Series-based)\n *\n * @packageDocumentation\n */\n\n// Export all types\nexport * from './types/index.js';\nexport * from './types/metadata.js';\n\n// Export core namespaces (array-based functions)\nimport * as taCore from './ta/index.js';\nimport * as math from './math/index.js';\nimport * as array from './array/index.js';\nimport * as map from './map/index.js';\nimport * as str from './str/index.js';\nimport * as color from './color/index.js';\nimport * as time from './time/index.js';\nimport * as timeframe from './timeframe/index.js';\nimport * as matrix from './matrix/index.js';\nimport * as line from './line/index.js';\nimport * as box from './box/index.js';\nimport * as label from './label/index.js';\nimport * as linefill from './linefill/index.js';\nimport * as chartPoint from './chartpoint/index.js';\nimport * as polyline from './polyline/index.js';\nimport * as table from './table/index.js';\nimport * as text from './text/index.js';\nimport * as compare from './compare/index.js';\nimport * as callsite from './callsite/index.js';\n\nexport { taCore, math, array, map, str, color, time, timeframe, matrix, line, box, label, linefill, chartPoint, polyline, table, text, compare, callsite };\n\n// Renderable drawings (IndicatorResult labels, lines, boxes, linefills, polylines, tables) and future bar times\nexport { drawingOutputs, type DrawingOutputs } from './drawing/output.js';\nexport { barInterval, barTime } from './drawing/bar-time.js';\n\n// Drawing registry limits (PineScript indicator(max_lines_count / max_labels_count / max_boxes_count / max_polylines_count))\nexport {\n resetDrawings,\n setDrawingLimits,\n setDrawingBar,\n holdDrawing,\n releaseDrawing,\n varDrawing,\n createdDrawingKinds,\n type DrawingKind,\n type DrawingLimits,\n type DrawingVariable,\n} from './drawing/registry.js';\n\n// The session of a chart context without a script run: time(tf, session, timezone), time_close(...), time_tradingday,\n// session.* flags, request.security periods with the session and holidays, and bars after the last bar on the trading\n// calendar\nexport {\n chartSessions,\n sessionPeriods,\n periodTime,\n periodClose,\n tradingDayTime,\n chartSessionFlags,\n resampleSession,\n barTimeAfter,\n barsUntil,\n type SessionContext,\n type ChartSessions,\n type SessionPeriods,\n type SessionResample,\n} from './session/context.js';\nexport { TradingCalendar, type SessionSpec } from './session/calendar.js';\nexport { SessionBars, sessionFlags, type SessionFlags } from './session/bars.js';\n\n// Same-symbol request.security on a higher timeframe (completion and lookahead rules) and ticker.heikinashi\nexport { resample, periodsOf, afterCompletion, mapToChart, heikinAshi, type Resampled } from './security/resample.js';\n// request.security of another symbol: which bar of the other symbol each chart bar shows (#179)\nexport {\n securityOther,\n securityOtherValues,\n securityLowerTf,\n type OtherSymbolChart,\n type OtherSymbolOptions,\n type SymbolCalendar,\n} from './security/other.js';\n\n// Export Series class (self-contained, no context)\nexport { Series, BarData } from './runtime/series.js';\nexport type { SeriesExtractor } from './runtime/series.js';\n\n// Export TA-Series namespace (Series-based wrappers)\nimport * as ta from './ta-series.js';\nexport { ta };\n\n// Export metadata types for indicator return values\nexport type {\n PlotOptions,\n HLineOptions,\n FillOptions,\n InputMetadata,\n PlotMetadata,\n IndicatorMetadata,\n TimeValue,\n PlotData,\n HLineData,\n FillData,\n FillGradient,\n IndicatorResult,\n IndicatorFactory,\n PlotStyle,\n LineStyle,\n PlotDisplay,\n InputType\n} from './types/metadata.js';\n\n// Re-export commonly used functions for convenience\nexport { sma, ema, rsi, macd, bb, stdev, crossover, crossunder, change, tr, atr, cum, vwap } from './ta-series.js';\nexport { abs, ceil, floor, round, max, min, avg, sum, sqrt, pow, exp, log, sin, cos, tan } from './math/index.js';\nexport { rgb, from_hex as color_from_hex, new_color } from './color/index.js';\n\n// Export chart data utilities\nexport { ohlcFromBars, getClose, getHigh, getLow, getOpen, getSource, getSourceSeries, at, div } from './utils/index.js';\nexport { setSourceResolver, externalSource, BUILTIN_SOURCES, type SourceResolver } from './utils/source-resolver.js';\n\n// Export libraries (ZigZag, etc.)\nexport * from './lib/index.js';\n\n// Export helper functions for generated indicators (PineScript compatibility)\nexport {isNA as na, nz, fixnan} from './utils/index.js';\n\n// Export indicator infrastructure\nexport {\n indicator,\n type IndicatorMetadataConfig,\n type InputDefinition,\n type IndicatorContext,\n type IndicatorInstance,\n type IndicatorConstructor,\n} from './indicator.js';\n\nexport {\n input,\n type InputValue,\n type IntInputOptions,\n type FloatInputOptions,\n type SourceInputOptions,\n type BoolInputOptions,\n type StringInputOptions,\n type TimeInputOptions,\n type SourceType,\n} from './input.js';\n\nexport {\n plot as plotHelper,\n createPlot,\n type Time,\n type TimeValuePair,\n type PlotOptions as PlotHelperOptions,\n type PlotResult,\n} from './plot.js';\n\n// Runtime exports - global context runtime (setContext, plot, hline) drawing through a ChartAdapter\nexport {\n setContext,\n clearContext,\n getContext,\n registerCalculate,\n recalculate,\n plot,\n hline,\n clearPlots,\n} from './runtime/runtime.js';\n\nexport {\n input_int,\n input_float,\n input_bool,\n input_string,\n input_text_area,\n input_source,\n input_timeframe,\n input_session,\n input_symbol,\n input_time,\n} from './runtime/inputs.js';\nexport { isInputActive } from './runtime/input-active.js';\n\nexport type {\n OakScriptContext,\n ChartAdapter,\n InputAdapter,\n SeriesHandle,\n SeriesOptions,\n InputConfig,\n InputCondition,\n InputOptions,\n OhlcvData,\n PlotConfig,\n HLineConfig,\n FillConfig,\n ArrowConfig,\n CandleConfig,\n ShapeConfig,\n BarColorConfig,\n} from './runtime/types.js';\n\n// Strategy types and constants (the script API forwards strategy.* calls to a host engine)\nexport {\n STRATEGY_CONSTANTS,\n STRATEGY_DEFAULTS,\n STRATEGY_NUMBER_VARIABLES,\n STRATEGY_TEXT_VARIABLES,\n} from './strategy/index.js';\nexport type * from './strategy/index.js';\n\nexport { LightweightChartsAdapter } from './runtime/adapters/LightweightChartsAdapter.js';\nexport type { LightweightSeriesDefinitions } from './runtime/adapters/LightweightChartsAdapter.js';\nexport { SimpleInputAdapter } from './runtime/adapters/SimpleInputAdapter.js';\n\n// Version\nexport const VERSION = '0.13.2';\n\n/**\n * Library information\n */\nexport const info = {\n name: 'OakScriptJS',\n version: VERSION,\n description: 'Simplified PineScript-like library - Series + TA functions',\n features: {\n series: 'Lazy evaluation with Series class',\n ta: 'Technical analysis functions (Series and array-based)',\n script: 'PineScript-style script API (oakscriptjs/script): indicator, strategy, inputs, plots, request.security',\n runtime: 'Global context runtime drawing through a ChartAdapter',\n indicator: 'Indicator function with automatic pane management'\n },\n namespaces: {\n core: ['ta', 'math', 'array', 'map', 'str', 'color', 'time', 'timeframe', 'matrix'],\n drawing: ['line', 'box', 'label', 'linefill', 'chartPoint', 'polyline', 'table', 'text'],\n runtime: ['setContext', 'plot', 'hline', 'input_*'],\n indicator: ['indicator', 'input', 'plotHelper', 'createPlot']\n }\n};\n\n// PineScript compatibility stubs - functions that have no runtime effect\n// These are no-ops so that PineScript-style code runs without modification\n\n/**\n * alertcondition() - Stub for PineScript alert configuration\n * In PineScript, this registers an alert condition. In oakscriptjs, it's a no-op\n * since alert configuration is handled by the host application.\n */\nexport function alertcondition(_condition: unknown, _title?: string, _message?: string): void {\n // No-op: Alert conditions are handled by the host application\n}\n"],
5
5
  "mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAeA,0BAAc,6BAfd;AAgBA,0BAAc,gCAhBd;AAmBA,aAAwB;AACxB,WAAsB;AACtB,YAAuB;AACvB,UAAqB;AACrB,UAAqB;AACrB,YAAuB;AACvB,WAAsB;AACtB,gBAA2B;AAC3B,aAAwB;AACxB,WAAsB;AACtB,UAAqB;AACrB,YAAuB;AACvB,eAA0B;AAC1B,iBAA4B;AAC5B,eAA0B;AAC1B,YAAuB;AACvB,WAAsB;AACtB,cAAyB;AACzB,eAA0B;AAK1B,oBAAoD;AACpD,sBAAqC;AAGrC,sBAWO;AAKP,qBAcO;AACP,sBAAkD;AAClD,kBAA6D;AAG7D,sBAA6F;AAE7F,mBAOO;AAGP,oBAAgC;AAIhC,SAAoB;AAyBpB,uBAAkG;AAClG,kBAAgG;AAChG,mBAA2D;AAG3D,mBAAsG;AACtG,6BAAwF;AAGxF,0BAAc,2BAnId;AAsIA,IAAAA,gBAAqC;AAGrC,uBAOO;AAEP,mBAUO;AAEP,kBAOO;AAGP,qBASO;AAEP,oBAWO;AACP,0BAA8B;AAsB9B,sBAKO;AAGP,sCAAyC;AAEzC,gCAAmC;AAG5B,MAAM,UAAU;AAKhB,MAAM,OAAO;AAAA,EAClB,MAAM;AAAA,EACN,SAAS;AAAA,EACT,aAAa;AAAA,EACb,UAAU;AAAA,IACR,QAAQ;AAAA,IACR,IAAI;AAAA,IACJ,QAAQ;AAAA,IACR,SAAS;AAAA,IACT,WAAW;AAAA,EACb;AAAA,EACA,YAAY;AAAA,IACV,MAAM,CAAC,MAAM,QAAQ,SAAS,OAAO,OAAO,SAAS,QAAQ,aAAa,QAAQ;AAAA,IAClF,SAAS,CAAC,QAAQ,OAAO,SAAS,YAAY,cAAc,YAAY,SAAS,MAAM;AAAA,IACvF,SAAS,CAAC,cAAc,QAAQ,SAAS,SAAS;AAAA,IAClD,WAAW,CAAC,aAAa,SAAS,cAAc,YAAY;AAAA,EAC9D;AACF;AAUO,SAAS,eAAe,YAAqB,QAAiB,UAAyB;AAE9F;",
6
6
  "names": ["import_utils"]
7
7
  }
@@ -65,7 +65,7 @@ export type * from './strategy/index.js';
65
65
  export { LightweightChartsAdapter } from './runtime/adapters/LightweightChartsAdapter.js';
66
66
  export type { LightweightSeriesDefinitions } from './runtime/adapters/LightweightChartsAdapter.js';
67
67
  export { SimpleInputAdapter } from './runtime/adapters/SimpleInputAdapter.js';
68
- export declare const VERSION = "0.12.3";
68
+ export declare const VERSION = "0.13.2";
69
69
  /**
70
70
  * Library information
71
71
  */
package/dist/esm/index.js CHANGED
@@ -79,7 +79,7 @@ export { STRATEGY_CONSTANTS, STRATEGY_DEFAULTS, STRATEGY_NUMBER_VARIABLES, STRAT
79
79
  export { LightweightChartsAdapter } from './runtime/adapters/LightweightChartsAdapter.js';
80
80
  export { SimpleInputAdapter } from './runtime/adapters/SimpleInputAdapter.js';
81
81
  // Version
82
- export const VERSION = '0.12.3';
82
+ export const VERSION = '0.13.2';
83
83
  /**
84
84
  * Library information
85
85
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "oakscriptjs",
3
- "version": "0.13.1",
3
+ "version": "0.13.2",
4
4
  "type": "module",
5
5
  "description": "PineScript v6 compatible technical analysis library for JavaScript/TypeScript",
6
6
  "main": "./dist/cjs/index.js",
@@ -42,7 +42,9 @@
42
42
  "files": [
43
43
  "dist",
44
44
  "src",
45
+ "skills",
45
46
  "README.md",
47
+ "AGENTS.md",
46
48
  "LICENSE"
47
49
  ],
48
50
  "scripts": {
@@ -0,0 +1,162 @@
1
+ ---
2
+ name: oakscript
3
+ description: Write, port, review or fix OakScript scripts - indicators and strategies written in JavaScript with the oakscriptjs/script API, a Pine Script v6-style API. Use when asked to create or debug an indicator, oscillator, overlay, signal, alert or backtest strategy for OakScriptJS or for an app that runs OakScript scripts (such as the OpenTrader script editor), or to port a Pine Script v6 script to JavaScript.
4
+ ---
5
+
6
+ # Writing OakScript scripts
7
+
8
+ An OakScript script is **plain JavaScript** that reads like Pine Script v6. The host (a chart app, a worker,
9
+ a test) runs the whole script body on all the bars at once, and runs it again when the bars or the inputs change.
10
+
11
+ ```js
12
+ indicator("My SMA", { overlay: true }); // one declaration: indicator() or strategy()
13
+ const length = input.int(20, "Length", { minval: 1 }); // an input: declared AND read here
14
+ const sma = ta.sma(close, length); // Series math, all bars at once
15
+ plot(sma, "SMA", { color: color.blue, linewidth: 2 }); // an output
16
+ plotshape(ta.crossover(close, sma), "Cross", { style: "triangleup", location: "belowbar" });
17
+ ```
18
+
19
+ ## The script format
20
+
21
+ - The file is the script body: top-level statements, no wrapper function, no `export`.
22
+ - Every export of `oakscriptjs/script` is a global: `indicator`, `strategy`, `input`, `plot`, `ta`, `color`,
23
+ `close`, `request`, `eachBar`, `label`, `table`... No import is needed.
24
+ - Write JavaScript, not TypeScript: the app editor runs the source without a compile step, so type annotations
25
+ are syntax errors.
26
+ - Other modules: `import { array, matrix, map, callsite } from "oakscriptjs";` at the top of the script (these
27
+ are not globals). Use JS arrays and `Map` when you can.
28
+ - Outside an app, a host passes the body as a function: see [Running a script](#running-a-script).
29
+ - A source that exports `calculate(bars, inputs, chart)` is an older format (built-in indicator sources use it).
30
+ Write new scripts in the format above; a strategy must use it.
31
+
32
+ ## Workflow
33
+
34
+ 1. **Declare** with `indicator(title, { overlay })` (overlay `true`: on the price pane; `false`: own pane) or
35
+ `strategy(title, { ...properties })`. Exactly one declaration, at the top.
36
+ 2. **Inputs**: `input.int / float / bool / string / color / source / timeframe / session / symbol / time`.
37
+ They return plain values (a Series for `input.source`). The input id is the title in snake case
38
+ ("Fast length" is `fast_length`): hosts pass overrides by id.
39
+ 3. **Compute with Series** before any loop: `ta.*`, the price builtins and the Series methods
40
+ (`.add .sub .mul .div .gt .lt .eq .and .or .not .iff .offset`).
41
+ 4. **Per-bar state** (Pine `var`, `:=`, `if` on series values, loops): `eachBar((c) => ...)`; for a strategy,
42
+ `strategy.eachBar((c) => ...)` holds the orders.
43
+ 5. **Outputs**: `plot`, `hline`, `fill`, `plotshape`, `plotchar`, `plotarrow`, `bgcolor`, `barcolor`,
44
+ `plotcandle`, drawings (`label.new`, `line.new`, `box.new`, `table.new`), `alertcondition`, `alert`.
45
+ 6. **Run it and check the values**: compile errors, runtime errors, then the plot values on the last bars
46
+ (an empty plot means NaN everywhere). See [errors](reference/errors.md).
47
+ 7. Start from the closest [example](#examples) rather than from scratch.
48
+
49
+ ## Rules
50
+
51
+ 1. **No operators on Series.** JavaScript has no operator overloading: `close - open` is `NaN`, and
52
+ `if (ta.crossover(a, b))` is always true (an object). Write `close.sub(open)`, `a.gt(b)`, `cond.iff(x, y)`,
53
+ `x.offset(1)` (Pine `x[1]`), `a.and(b)`, `a.not()`. Conditions are Series of 1 / 0.
54
+ 2. **Plain numbers inside the bar loops.** In `eachBar` / `strategy.eachBar`, `c.close`, `c.get(series)`,
55
+ `c.get(series, 1)` are numbers: JS operators, `if`, `for` work. A condition reads `c.get(cond) === 1`.
56
+ 3. **Compute `ta.*` outside the loops.** A `ta.*` call inside a bar callback returns a Series (not a number) and
57
+ has no per-bar history: compute the Series before the loop and read it with `c.get()`.
58
+ 4. **Pine constants are strings**: `shape.triangleup` is `"triangleup"`, `location.belowbar` `"belowbar"`,
59
+ `size.small` `"small"`, `label.style_label_down` `"label_down"`, `xloc.bar_index` `"bar_index"`,
60
+ `extend.right` `"right"`, `line.style_dashed` `"dashed"`. These namespaces do exist: `color.*`,
61
+ `plot.style_*`, `plot.linestyle_*`, `hline.style_*`, `strategy.*`, `barmerge.*`, `position.*`, `text.*`,
62
+ `alert.freq_*`, `session.*`, `barstate.*`.
63
+ 5. **Arguments**: declarations, inputs, plots and orders take named options in an object after the leading
64
+ arguments (`plot(series, title, { color, linewidth, style })`, `strategy.exit(id, { from_entry, stop })`).
65
+ Drawing functions (`label.new`, `line.new`, `box.new`, `table.cell`...) are positional only, in the
66
+ Pine order: pass `undefined` to skip one.
67
+ 6. **na is NaN.** There is no `na` constant: use `NaN` (`undefined` for a colour). `na(x)` and `nz(x, y)` take
68
+ numbers (use them in a bar loop); on a Series use `x.iff(x, 0)` for `nz(x)`, `fixnan(x)`, or a bar loop.
69
+ Plots skip NaN bars. JS comparisons with NaN are false; for the Pine rules (1e-10 tolerance) use
70
+ `compare.gt(a, b)`, `compare.eq(a, b)`...
71
+ 7. **Per-bar colours** are arrays with one colour per bar: `color.when(cond, colorTrue, colorFalse?)`
72
+ (omitted: na), or an array you fill in a bar loop. `color.new(c, transp)` sets the transparency (0-100).
73
+ 8. **Strategies**: order calls (`strategy.entry/exit/close/...`) and per-bar reads (`strategy.position_size`,
74
+ `strategy.equity`, `strategy.netprofit`...) only inside `strategy.eachBar()`, which runs once per script.
75
+ 9. **Other timeframes**: compute the series **inside** the expression:
76
+ `request.security("", "D", () => ta.ema(close, 20))`, never `() => myEma`.
77
+ 10. **No future bars**: offsets are 0 or positive; history reaches at most 5000 bars back.
78
+ 11. **Self-contained and fast**: no network, timers or DOM; the same bars give the same output. Hosts stop a
79
+ run that takes too long (seconds), so avoid nested loops over all bars.
80
+
81
+ ## Pine Script v6 at a glance
82
+
83
+ | Pine Script v6 | OakScript |
84
+ |---|---|
85
+ | `//@version=6` | nothing |
86
+ | `a + b`, `a > b`, `c ? x : y` (series) | `a.add(b)`, `a.gt(b)`, `c.iff(x, y)` |
87
+ | `close[1]` | `close.offset(1)`; in a loop `c.get(close, 1)` |
88
+ | `var x = 0` / `x := ...` | `let x = 0` before `eachBar`, assigned in the callback |
89
+ | `bar_index`, `last_bar_index` | `c.i`, `c.n - 1` in a bar loop |
90
+ | `plot(x, "T", color=c, linewidth=2)` | `plot(x, "T", { color: c, linewidth: 2 })` |
91
+ | `[a, b, c] = ta.macd(...)` | `const [a, b, c] = ta.macd(...)` |
92
+ | `if cond` + `strategy.entry(...)` | `strategy.eachBar((c) => { if (c.get(cond) === 1) strategy.entry(...) })` |
93
+
94
+ Full table, the deviations and the unsupported parts: [reference/pine-differences.md](reference/pine-differences.md).
95
+
96
+ ## Common mistakes
97
+
98
+ - `close - open`, `ma > 50`, `if (series)` at the top level: use Series methods (rule 1).
99
+ - `ta.sma(...)` inside `eachBar`: compute it before the loop (rule 3).
100
+ - `ta.vwma(close, 20)` throws: pass the volume, `ta.vwma(close, 20, volume)`. `ta.stoch(close, high, low, 14)`
101
+ takes all four arguments, as in Pine.
102
+ - A per-bar colour from a JS ternary on a Series (`fast > slow ? green : red`): use `color.when()`.
103
+ - `strategy.position_size` at the top level: read it inside `strategy.eachBar()`.
104
+ - Two `indicator()` calls, or `indicator()` and `strategy()` in one script: one declaration only.
105
+ - Pine named arguments in drawings (`label.new(x, y, text = "A", style = ...)`): positional, with strings.
106
+
107
+ ## Reading errors
108
+
109
+ - **Syntax errors** come with the line and column of your source (the app editor maps them to your lines).
110
+ - **Runtime errors** stop the run: the message names the call and the fix, e.g. `strategy.entry() must be
111
+ called inside strategy.eachBar().` A host that runs a declaration pass first (zero bars) reports declaration
112
+ errors when the script is saved.
113
+ - **No error, wrong output**: an empty plot (NaN everywhere: `ta.*` inside a loop, a NaN input to the math),
114
+ a branch taken on every bar (a Series used as a JS boolean). Check the plot values on the last bars.
115
+
116
+ The messages and their fixes: [reference/errors.md](reference/errors.md).
117
+
118
+ ## Examples
119
+
120
+ Complete scripts, run by the package's CI on synthetic bars:
121
+
122
+ - [examples/ma-cross.js](examples/ma-cross.js): overlay, inputs (incl. a choice list), two MAs, a per-bar
123
+ coloured fill, cross markers, alert conditions.
124
+ - [examples/macd-oscillator.js](examples/macd-oscillator.js): own pane, tuple from `ta.macd`, four-colour
125
+ histogram built in a bar loop, zero line, background.
126
+ - [examples/mtf-trend-filter.js](examples/mtf-trend-filter.js): `request.security` on a higher timeframe
127
+ (tuple), `var`-style state in `eachBar`, signal labels, a label on the last bar.
128
+ - [examples/ma-cross-strategy.js](examples/ma-cross-strategy.js): `strategy()` with sizing and commission,
129
+ long / short entries, stop loss and take profit with `strategy.exit`, a results table.
130
+
131
+ ## Reference
132
+
133
+ - [reference/script-structure.md](reference/script-structure.md): declarations, inputs, Series, `eachBar`,
134
+ builtins (`time`, `barstate`, `timeframe`, `syminfo`), drawings' lifetime.
135
+ - [reference/plotting.md](reference/plotting.md): lines, histograms, shapes, characters, arrows, fills,
136
+ colours, background and bar colours, candles, labels, lines, boxes, tables, alerts.
137
+ - [reference/timeframes-symbols.md](reference/timeframes-symbols.md): `request.security` (higher and lower
138
+ timeframes, other symbols, tuples, lookahead), `request.security_lower_tf`, `timeframe.*`.
139
+ - [reference/strategies.md](reference/strategies.md): properties, sizing, orders, exits, reading the results,
140
+ risk rules, the host's order engine.
141
+ - [reference/pine-differences.md](reference/pine-differences.md): Pine Script v6 to OakScript, what is not
142
+ supported.
143
+ - [reference/errors.md](reference/errors.md): compiler and runtime errors, silent mistakes.
144
+
145
+ ## Running a script
146
+
147
+ In an app (OpenTrader's script editor) save the script: the app compiles it, then runs it on the chart bars.
148
+ In your own code, wrap the body in a function and pass it to `executeScript` (imports needed there):
149
+
150
+ ```ts
151
+ import { executeScript, indicator, input, plot, ta, close } from 'oakscriptjs/script';
152
+
153
+ const run = executeScript(() => {
154
+ indicator('My SMA', { overlay: true });
155
+ plot(ta.sma(close, input.int(20, 'Length')), 'SMA');
156
+ }, bars, { length: 50 }, { timeframe: '60', timezone: 'Etc/UTC', session: '24x7' });
157
+ // run.result.plots.plot0 -> [{ time, value }, ...]; run.inputConfig, run.plotConfig, run.strategyConfig...
158
+ ```
159
+
160
+ Bars are `{ time, open, high, low, close, volume }`, oldest first, `time` in UNIX seconds. The chart context
161
+ (4th argument) gives the timeframe, time zone and session that `request.security`, `time()` and
162
+ `timeframe.*` need. A strategy also needs an order engine: see [strategies](reference/strategies.md#the-host-side).
@@ -0,0 +1,57 @@
1
+ // Moving-average cross strategy: long and short entries on the crosses, a stop loss
2
+ // and a take profit from the entry price, and a results table on the last bar.
3
+ strategy("MA Cross Strategy", {
4
+ shorttitle: "MA X Strat",
5
+ overlay: true,
6
+ initial_capital: 10000,
7
+ default_qty_type: strategy.percent_of_equity,
8
+ default_qty_value: 50,
9
+ commission_type: strategy.commission.percent,
10
+ commission_value: 0.05,
11
+ });
12
+
13
+ const fastLength = input.int(10, "Fast length", { minval: 1 });
14
+ const slowLength = input.int(30, "Slow length", { minval: 1 });
15
+ const stopPercent = input.float(2, "Stop loss %", { minval: 0.1, step: 0.1 });
16
+ const takePercent = input.float(4, "Take profit %", { minval: 0.1, step: 0.1 });
17
+
18
+ // Series math happens before the bar loop, on all bars at once.
19
+ const fast = ta.sma(close, fastLength);
20
+ const slow = ta.sma(close, slowLength);
21
+ const longSignal = ta.crossover(fast, slow);
22
+ const shortSignal = ta.crossunder(fast, slow);
23
+
24
+ plot(fast, "Fast", { color: color.teal });
25
+ plot(slow, "Slow", { color: color.orange });
26
+
27
+ // Orders and strategy.* reads only work inside strategy.eachBar(): the callback runs at the
28
+ // close of each bar; market orders fill at the open of the next bar.
29
+ strategy.eachBar((c) => {
30
+ if (c.get(longSignal) === 1) strategy.entry("Long", strategy.long);
31
+ if (c.get(shortSignal) === 1) strategy.entry("Short", strategy.short);
32
+
33
+ const size = strategy.position_size;
34
+ const entryPrice = strategy.position_avg_price;
35
+ if (size > 0) {
36
+ strategy.exit("Long exit", {
37
+ from_entry: "Long",
38
+ stop: entryPrice * (1 - stopPercent / 100),
39
+ limit: entryPrice * (1 + takePercent / 100),
40
+ });
41
+ } else if (size < 0) {
42
+ strategy.exit("Short exit", {
43
+ from_entry: "Short",
44
+ stop: entryPrice * (1 + stopPercent / 100),
45
+ limit: entryPrice * (1 - takePercent / 100),
46
+ });
47
+ }
48
+
49
+ // Results so far, read from the order engine.
50
+ if (c.i === c.n - 1) {
51
+ const t = table.new(position.top_right, 2, 2, color.new(color.black, 20));
52
+ table.cell(t, 0, 0, "Net profit", 0, 0, color.white);
53
+ table.cell(t, 1, 0, str.tostring(strategy.netprofit, "#.##"), 0, 0, color.white);
54
+ table.cell(t, 0, 1, "Closed trades", 0, 0, color.white);
55
+ table.cell(t, 1, 1, str.tostring(strategy.closedtrades), 0, 0, color.white);
56
+ }
57
+ });
@@ -0,0 +1,34 @@
1
+ // Moving-average cross on the price pane: two averages, a fill between them,
2
+ // markers on the crosses and two alert conditions.
3
+ indicator("MA Cross", { shorttitle: "MA X", overlay: true });
4
+
5
+ const src = input.source("close", "Source");
6
+ const fastLength = input.int(9, "Fast length", { minval: 1 });
7
+ const slowLength = input.int(21, "Slow length", { minval: 1 });
8
+ const maType = input.string("EMA", "MA type", { options: ["SMA", "EMA", "WMA"] });
9
+
10
+ // Inputs are plain values: a JS `if` on them is fine.
11
+ function ma(source, length) {
12
+ if (maType === "SMA") return ta.sma(source, length);
13
+ if (maType === "WMA") return ta.wma(source, length);
14
+ return ta.ema(source, length);
15
+ }
16
+
17
+ const fast = ma(src, fastLength);
18
+ const slow = ma(src, slowLength);
19
+ const bullish = fast.gt(slow); // Series of 1 / 0: fast > slow
20
+
21
+ const fastPlot = plot(fast, "Fast", { color: color.teal, linewidth: 2 });
22
+ const slowPlot = plot(slow, "Slow", { color: color.orange, linewidth: 2 });
23
+ fill(fastPlot, slowPlot, {
24
+ color: color.when(bullish, color.new(color.teal, 85), color.new(color.red, 85)),
25
+ title: "Trend fill",
26
+ });
27
+
28
+ const crossUp = ta.crossover(fast, slow);
29
+ const crossDown = ta.crossunder(fast, slow);
30
+ plotshape(crossUp, "Cross up", { style: "triangleup", location: "belowbar", color: color.teal, size: "small" });
31
+ plotshape(crossDown, "Cross down", { style: "triangledown", location: "abovebar", color: color.red, size: "small" });
32
+
33
+ alertcondition(crossUp, "Cross up", "Fast MA crossed above the slow MA");
34
+ alertcondition(crossDown, "Cross down", "Fast MA crossed below the slow MA");
@@ -0,0 +1,29 @@
1
+ // MACD in its own pane: a four-colour histogram, the MACD and signal lines,
2
+ // a zero level and a background on the signal crosses.
3
+ indicator("MACD Oscillator", { shorttitle: "MACD", overlay: false, precision: 4 });
4
+
5
+ const src = input.source("close", "Source");
6
+ const fastLength = input.int(12, "Fast length", { minval: 1 });
7
+ const slowLength = input.int(26, "Slow length", { minval: 1 });
8
+ const signalLength = input.int(9, "Signal smoothing", { minval: 1, maxval: 50 });
9
+
10
+ // A tuple comes back as an array: destructure it.
11
+ const [macdLine, signalLine, hist] = ta.macd(src, fastLength, slowLength, signalLength);
12
+
13
+ // Four colours: above / below zero, growing / falling. color.when() takes two colours,
14
+ // so build the per-bar array in a bar loop (one entry per bar, undefined = na).
15
+ const histColors = [];
16
+ eachBar((c) => {
17
+ const h = c.get(hist);
18
+ const previous = c.get(hist, 1);
19
+ if (Number.isNaN(h)) histColors.push(undefined);
20
+ else if (h >= 0) histColors.push(h > previous ? "#26A69A" : "#B2DFDB");
21
+ else histColors.push(h < previous ? "#FF5252" : "#FFCDD2");
22
+ });
23
+
24
+ plot(hist, "Histogram", { style: plot.style_columns, color: histColors });
25
+ plot(macdLine, "MACD", { color: color.blue, linewidth: 2 });
26
+ plot(signalLine, "Signal", { color: color.orange });
27
+ hline(0, "Zero", { color: color.gray, linestyle: hline.style_dotted });
28
+
29
+ bgcolor(color.when(ta.cross(macdLine, signalLine), color.new(color.gray, 85)), { title: "Signal cross" });
@@ -0,0 +1,46 @@
1
+ // Multi-timeframe filter: a higher-timeframe EMA decides the trend, and a cross of the
2
+ // chart EMA gives a signal only when it agrees with that trend, once per trend.
3
+ indicator("MTF Trend Filter", { shorttitle: "MTF", overlay: true });
4
+
5
+ const filterTf = input.timeframe("D", "Filter timeframe");
6
+ const filterLength = input.int(20, "Filter EMA length", { minval: 1 });
7
+ const signalLength = input.int(10, "Signal EMA length", { minval: 1 });
8
+
9
+ // The expression runs on the higher-timeframe bars: compute every series INSIDE it.
10
+ // Returning an array gives one series per element (a PineScript tuple).
11
+ const [htfEma, htfClose] = request.security("", filterTf, () => [ta.ema(close, filterLength), close]);
12
+ const upTrend = htfClose.gt(htfEma);
13
+
14
+ const signalEma = ta.ema(close, signalLength);
15
+ plot(htfEma, "Filter EMA", { color: color.gray, linewidth: 2, style: plot.style_stepline });
16
+ plot(signalEma, "Signal EMA", { color: color.when(upTrend, color.teal, color.red) });
17
+
18
+ const crossUp = ta.crossover(close, signalEma);
19
+ const crossDown = ta.crossunder(close, signalEma);
20
+
21
+ // Stateful logic (PineScript `var` + `:=`): a closure variable in a bar loop.
22
+ let lastSignal = 0; // var int lastSignal = 0
23
+ const signal = eachBar((c) => {
24
+ const up = c.get(upTrend) === 1;
25
+ if (c.get(crossUp) === 1 && up && lastSignal !== 1) {
26
+ lastSignal = 1;
27
+ return 1;
28
+ }
29
+ if (c.get(crossDown) === 1 && !up && lastSignal !== -1) {
30
+ lastSignal = -1;
31
+ return -1;
32
+ }
33
+ return 0;
34
+ });
35
+
36
+ plotshape(signal.eq(1), "Long", { style: "labelup", location: "belowbar", color: color.teal, text: "L", textcolor: color.white });
37
+ plotshape(signal.eq(-1), "Short", { style: "labeldown", location: "abovebar", color: color.red, text: "S", textcolor: color.white });
38
+ bgcolor(color.when(upTrend, color.new(color.teal, 92), color.new(color.red, 92)), { title: "Trend" });
39
+
40
+ // One label on the last bar with the filter state.
41
+ eachBar((c) => {
42
+ if (c.i === c.n - 1) {
43
+ const text = (c.get(upTrend) === 1 ? "Uptrend " : "Downtrend ") + filterTf;
44
+ label.new(c.i, c.high, text, "bar_index", "price", color.gray, "label_down", color.white);
45
+ }
46
+ });
@@ -0,0 +1,68 @@
1
+ # Errors and how to read them
2
+
3
+ A script fails in one of three ways: it does not compile, it stops with a runtime error, or it runs and gives a
4
+ wrong output. Fix them in that order, then check the values.
5
+
6
+ ## Compile errors (syntax)
7
+
8
+ The source is loaded as JavaScript: a syntax error comes with a line and a column of your source.
9
+
10
+ | Message | Cause | Fix |
11
+ |---|---|---|
12
+ | `Missing initializer in const declaration`, `Unexpected token ':'` | a TypeScript annotation (`const a: number = 5`) | remove the types: the script is JavaScript |
13
+ | `Unexpected identifier`, `Unexpected token '='` | Pine syntax: `x := 1`, `var x = 0`, `a = b ? c : d` on one line without `const`, `=>` functions, named arguments `f(a, length = 5)` | JS syntax: `x = 1`, `let x = 0`, `const a = ...`, `function f() {}`, options objects |
14
+ | `Unexpected token 'export'` / `'import'` at a wrong place | an import below other code, an export | imports first, no exports |
15
+
16
+ A host that compiles a script also runs it once on zero bars (a declaration pass: title, inputs, plots,
17
+ strategy properties). Errors in the declarations or in top-level code show up there, with the line.
18
+
19
+ ## Runtime errors
20
+
21
+ The run stops at the first error; the message names the call. The ones you will meet:
22
+
23
+ | Message | Cause | Fix |
24
+ |---|---|---|
25
+ | `TypeError: series.toArray is not a function` | a number or NaN passed where a Series is expected: `plot(close - open)`, `plot(5)`, `ta.sma(a * 2, 10)` | Series methods: `close.sub(open)`, `a.mul(2)`; a constant level: `hline(5)` |
26
+ | `TypeError: ta.xyz is not a function` / `xyz is not defined` | a name that does not exist (typo, Pine-only name such as `shape`, `location`, `size`, `xloc`, `bar_index`) | [pine-differences.md](pine-differences.md): strings for the constants, `c.i` for `bar_index` |
27
+ | `TypeError: Cannot read properties of undefined (reading 'toArray')` | a missing Series argument, e.g. `ta.vwma(close, 20)` | pass it: `ta.vwma(close, 20, volume)` |
28
+ | `Error on bar N: Invalid value of the 'length' argument (0.0) in the 'sma' function. It must be > 0.` | a length of 0, negative or NaN (often an input) | `minval: 1` on the input, or guard the value |
29
+ | `Error on bar N: The 'sma' function references too many historical candles (6000), the limit is 5000.` | a window longer than 5000 bars | a shorter length |
30
+ | `Error on bar N: The script attempts to reference historical data that is too far from the current bar (K bars back)...` | `x.offset(k)` / `c.get(x, k)` with k above 5000, or a history that keeps growing (`c.get(x, c.i)`) | bounded offsets |
31
+ | `c.get(): the offset cannot be negative` / `Series.offset(-1)...` | reading a future bar | offsets >= 0; for a left shift of a marker use `offset` of `plotshape` |
32
+ | `indicator(): the script already declared indicator(); a script has one declaration.` | two declarations | one `indicator()` or `strategy()` |
33
+ | `strategy.entry() must be called inside strategy.eachBar().` | an order at the top level | move it into `strategy.eachBar((c) => ...)` |
34
+ | `strategy.position_size must be read inside strategy.eachBar().` | a per-bar strategy value at the top level | read it in the loop; return it from the loop to plot it |
35
+ | `strategy.eachBar(): declare the script with strategy() first.` | `strategy.*` in an `indicator()` script | declare `strategy(...)` |
36
+ | `strategy.eachBar() runs once per script run.` | two strategy loops | one loop with all the logic |
37
+ | `strategy.X is not provided by the strategy engine.` | the host's engine lacks a variable or a risk rule | use another value, or ask the host |
38
+ | `request.security(): the expression returned a series computed on the chart bars...` | `request.security("", "D", () => myEma)` | compute inside: `() => ta.ema(close, 20)` |
39
+ | `request.security needs the bars of X (D): pass them with ChartContext.bars.` (`MissingSymbolDataError`) | another symbol whose bars the host has not loaded | an app loads them and runs again; in your own host give `bars()` |
40
+ | `Invalid symbol: X` | a symbol without bars | check the ticker id, or `ignore_invalid_symbol = true` |
41
+ | `Invalid timeframe "XYZ"` | a bad timeframe string | `"1"`, `"60"`, `"240"`, `"D"`, `"W"`, `"M"`... |
42
+ | `request.security_lower_tf: timeframe "D" is higher than the chart timeframe "60".` | `security_lower_tf` on a higher timeframe | `request.security` for higher timeframes |
43
+ | `The chart timeframe is not known: pass it as executeScript(body, bars, inputs, { timeframe }).` (also time zone, session, `syminfo.*`) | a direct `executeScript` call without the chart context | pass `{ timeframe, timezone, session, ... }`; an app gives them |
44
+ | `chart.point.now() needs a current bar: call it inside eachBar().` | a per-bar call at the top level | call it in a bar loop |
45
+ | `oakscriptjs/script functions can only run inside executeScript()` | a script API call outside a run (e.g. from a timer, or a Node module that calls `plot` at import) | only the host runs the body |
46
+ | `Script did not finish within Ns and was stopped.` (app) | an endless or very slow loop | bound the loops, compute Series outside the bar loops |
47
+
48
+ ## Silent mistakes (no error, wrong output)
49
+
50
+ | Symptom | Cause | Fix |
51
+ |---|---|---|
52
+ | a branch taken on every bar, or never | `if (ta.crossover(a, b))`, `cond ? x : y`, `a > b` with Series at the top level: an object is always true, an object comparison always false | `cond.iff(x, y)`, `a.gt(b)`, or `c.get(cond) === 1` in a loop |
53
+ | one colour on every bar | `color: fast > slow ? color.green : color.red` | `color.when(fast.gt(slow), color.green, color.red)` |
54
+ | an empty plot | NaN on every bar: a `ta.*` Series returned from a bar loop, a division by zero, a NaN input | compute `ta.*` before the loop, guard divisions (`range.gt(0).iff(x.div(range), 0)`) |
55
+ | a plot that starts late | the warm-up of long lengths, or of a higher timeframe (20 daily bars on an hourly chart is 480 bars) | expected; shorter lengths or more bars |
56
+ | values that look like the future (backtests too good) | `barmerge.lookahead_on` on a series that is not offset | lookahead off, or `.offset(1)` inside the expression |
57
+ | a strategy without trades | conditions read as `c.get(cond)` on NaN-only Series, orders placed outside the conditions you expect, `pyramiding: 0` blocking a second entry in the same direction | plot the conditions (`plotshape`) and check them first |
58
+ | `na(series)` / `nz(series)` has no effect | they take numbers | in a loop on `c.get(x)`, or `x.iff(x, 0)` |
59
+ | an input never changes the output | the input value is not used, or its title changed (new id) | use the returned value; keep titles stable |
60
+
61
+ ## Checking the output
62
+
63
+ After a clean run, read the values instead of trusting the code:
64
+
65
+ - the last values of each plot (`run.result.plots.plot0.slice(-5)` in a direct call; the app shows them in the
66
+ data window and the legend);
67
+ - the number of markers / labels (a signal on every bar, or none, is a bug);
68
+ - for a strategy, the trade count and a few trades (entry and exit bars and prices).