oakscriptjs 0.8.1 → 0.9.0
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/LICENSE +1 -1
- package/README.md +31 -9
- package/dist/cjs/array/index.d.ts +701 -0
- package/dist/cjs/array/index.d.ts.map +1 -0
- package/dist/cjs/array/index.js +392 -0
- package/dist/cjs/array/index.js.map +7 -0
- package/dist/cjs/box/index.d.ts +444 -0
- package/dist/cjs/box/index.d.ts.map +1 -0
- package/dist/cjs/box/index.js +246 -0
- package/dist/cjs/box/index.js.map +7 -0
- package/dist/cjs/callsite/index.d.ts +61 -0
- package/dist/cjs/callsite/index.d.ts.map +1 -0
- package/dist/cjs/callsite/index.js +65 -0
- package/dist/cjs/callsite/index.js.map +7 -0
- package/dist/cjs/chartpoint/index.d.ts +90 -0
- package/dist/cjs/chartpoint/index.d.ts.map +1 -0
- package/dist/cjs/chartpoint/index.js +56 -0
- package/dist/cjs/chartpoint/index.js.map +7 -0
- package/dist/cjs/color/index.d.ts +285 -0
- package/dist/cjs/color/index.d.ts.map +1 -0
- package/dist/cjs/color/index.js +143 -0
- package/dist/cjs/color/index.js.map +7 -0
- package/dist/cjs/compare/index.d.ts +26 -0
- package/dist/cjs/compare/index.d.ts.map +1 -0
- package/dist/cjs/compare/index.js +50 -0
- package/dist/cjs/compare/index.js.map +7 -0
- package/dist/cjs/drawing/registry.d.ts +42 -0
- package/dist/cjs/drawing/registry.d.ts.map +1 -0
- package/dist/cjs/drawing/registry.js +87 -0
- package/dist/cjs/drawing/registry.js.map +7 -0
- package/dist/cjs/index.d.ts +85 -0
- package/dist/cjs/index.d.ts.map +1 -0
- package/dist/cjs/index.js +184 -0
- package/dist/cjs/index.js.map +7 -0
- package/dist/cjs/indicator.d.ts +117 -0
- package/dist/cjs/indicator.d.ts.map +1 -0
- package/dist/cjs/indicator.js +74 -0
- package/dist/cjs/indicator.js.map +7 -0
- package/dist/cjs/input.d.ts +196 -0
- package/dist/cjs/input.d.ts.map +1 -0
- package/dist/cjs/input.js +197 -0
- package/dist/cjs/input.js.map +7 -0
- package/dist/cjs/label/index.d.ts +303 -0
- package/dist/cjs/label/index.d.ts.map +1 -0
- package/dist/cjs/label/index.js +190 -0
- package/dist/cjs/label/index.js.map +7 -0
- package/dist/cjs/lib/index.d.ts +8 -0
- package/dist/cjs/lib/index.d.ts.map +1 -0
- package/dist/cjs/lib/index.js +19 -0
- package/dist/cjs/lib/index.js.map +7 -0
- package/dist/{lib → cjs/lib}/zigzag/index.d.ts +4 -1
- package/dist/cjs/lib/zigzag/index.d.ts.map +1 -0
- package/dist/cjs/lib/zigzag/index.js +27 -0
- package/dist/cjs/lib/zigzag/index.js.map +7 -0
- package/dist/cjs/lib/zigzag/zigzag.d.ts +100 -0
- package/dist/cjs/lib/zigzag/zigzag.d.ts.map +1 -0
- package/dist/cjs/lib/zigzag/zigzag.js +207 -0
- package/dist/cjs/lib/zigzag/zigzag.js.map +7 -0
- package/dist/cjs/line/index.d.ts +342 -0
- package/dist/cjs/line/index.d.ts.map +1 -0
- package/dist/cjs/line/index.js +193 -0
- package/dist/cjs/line/index.js.map +7 -0
- package/dist/cjs/linefill/index.d.ts +124 -0
- package/dist/cjs/linefill/index.d.ts.map +1 -0
- package/dist/cjs/linefill/index.js +56 -0
- package/dist/cjs/linefill/index.js.map +7 -0
- package/dist/cjs/map/index.d.ts +69 -0
- package/dist/cjs/map/index.d.ts.map +1 -0
- package/dist/cjs/map/index.js +80 -0
- package/dist/cjs/map/index.js.map +7 -0
- package/dist/cjs/math/index.d.ts +560 -0
- package/dist/cjs/math/index.d.ts.map +1 -0
- package/dist/cjs/math/index.js +441 -0
- package/dist/cjs/math/index.js.map +7 -0
- package/dist/cjs/matrix/index.d.ts +1231 -0
- package/dist/cjs/matrix/index.d.ts.map +1 -0
- package/dist/cjs/matrix/index.js +1200 -0
- package/dist/cjs/matrix/index.js.map +7 -0
- package/dist/cjs/package.json +1 -0
- package/dist/cjs/plot.d.ts +76 -0
- package/dist/cjs/plot.d.ts.map +1 -0
- package/dist/cjs/plot.js +51 -0
- package/dist/cjs/plot.js.map +7 -0
- package/dist/cjs/polyline/index.d.ts +115 -0
- package/dist/cjs/polyline/index.d.ts.map +1 -0
- package/dist/cjs/polyline/index.js +76 -0
- package/dist/cjs/polyline/index.js.map +7 -0
- package/dist/cjs/runtime/adapters/LightweightChartsAdapter.d.ts +86 -0
- package/dist/cjs/runtime/adapters/LightweightChartsAdapter.d.ts.map +1 -0
- package/dist/cjs/runtime/adapters/LightweightChartsAdapter.js +141 -0
- package/dist/cjs/runtime/adapters/LightweightChartsAdapter.js.map +7 -0
- package/dist/cjs/runtime/adapters/SimpleInputAdapter.d.ts +59 -0
- package/dist/cjs/runtime/adapters/SimpleInputAdapter.d.ts.map +1 -0
- package/dist/cjs/runtime/adapters/SimpleInputAdapter.js +117 -0
- package/dist/cjs/runtime/adapters/SimpleInputAdapter.js.map +7 -0
- package/dist/{runtime → cjs/runtime}/index.d.ts +12 -5
- package/dist/cjs/runtime/index.d.ts.map +1 -0
- package/dist/cjs/runtime/index.js +49 -0
- package/dist/cjs/runtime/index.js.map +7 -0
- package/dist/cjs/runtime/inputs.d.ts +91 -0
- package/dist/cjs/runtime/inputs.d.ts.map +1 -0
- package/dist/cjs/runtime/inputs.js +262 -0
- package/dist/cjs/runtime/inputs.js.map +7 -0
- package/dist/cjs/runtime/runtime.d.ts +70 -0
- package/dist/cjs/runtime/runtime.d.ts.map +1 -0
- package/dist/cjs/runtime/runtime.js +148 -0
- package/dist/cjs/runtime/runtime.js.map +7 -0
- package/dist/cjs/runtime/series.d.ts +362 -0
- package/dist/cjs/runtime/series.d.ts.map +1 -0
- package/dist/cjs/runtime/series.js +560 -0
- package/dist/cjs/runtime/series.js.map +7 -0
- package/dist/cjs/runtime/types.d.ts +282 -0
- package/dist/cjs/runtime/types.d.ts.map +1 -0
- package/dist/cjs/runtime/types.js +17 -0
- package/dist/cjs/runtime/types.js.map +7 -0
- package/dist/cjs/script/index.d.ts +987 -0
- package/dist/cjs/script/index.d.ts.map +1 -0
- package/dist/cjs/script/index.js +1169 -0
- package/dist/cjs/script/index.js.map +7 -0
- package/dist/cjs/security/resample.d.ts +63 -0
- package/dist/cjs/security/resample.d.ts.map +1 -0
- package/dist/cjs/security/resample.js +84 -0
- package/dist/cjs/security/resample.js.map +7 -0
- package/dist/cjs/session/bars.d.ts +80 -0
- package/dist/cjs/session/bars.d.ts.map +1 -0
- package/dist/cjs/session/bars.js +219 -0
- package/dist/cjs/session/bars.js.map +7 -0
- package/dist/cjs/session/calendar.d.ts +70 -0
- package/dist/cjs/session/calendar.d.ts.map +1 -0
- package/dist/cjs/session/calendar.js +164 -0
- package/dist/cjs/session/calendar.js.map +7 -0
- package/dist/cjs/str/dateformat.d.ts +15 -0
- package/dist/cjs/str/dateformat.d.ts.map +1 -0
- package/dist/cjs/str/dateformat.js +149 -0
- package/dist/cjs/str/dateformat.js.map +7 -0
- package/dist/cjs/str/index.d.ts +459 -0
- package/dist/cjs/str/index.d.ts.map +1 -0
- package/dist/cjs/str/index.js +143 -0
- package/dist/cjs/str/index.js.map +7 -0
- package/dist/cjs/str/messageformat.d.ts +14 -0
- package/dist/cjs/str/messageformat.d.ts.map +1 -0
- package/dist/cjs/str/messageformat.js +114 -0
- package/dist/cjs/str/messageformat.js.map +7 -0
- package/dist/cjs/str/numberformat.d.ts +19 -0
- package/dist/cjs/str/numberformat.d.ts.map +1 -0
- package/dist/cjs/str/numberformat.js +170 -0
- package/dist/cjs/str/numberformat.js.map +7 -0
- package/dist/{strategy → cjs/strategy}/index.d.ts +50 -0
- package/dist/cjs/strategy/index.d.ts.map +1 -0
- package/dist/cjs/strategy/index.js +92 -0
- package/dist/cjs/strategy/index.js.map +7 -0
- package/dist/cjs/ta/index.d.ts +1397 -0
- package/dist/cjs/ta/index.d.ts.map +1 -0
- package/dist/cjs/ta/index.js +1715 -0
- package/dist/cjs/ta/index.js.map +7 -0
- package/dist/cjs/ta/running-sum.d.ts +30 -0
- package/dist/cjs/ta/running-sum.d.ts.map +1 -0
- package/dist/cjs/ta/running-sum.js +99 -0
- package/dist/cjs/ta/running-sum.js.map +7 -0
- package/dist/cjs/ta-series.d.ts +558 -0
- package/dist/cjs/ta-series.d.ts.map +1 -0
- package/dist/cjs/ta-series.js +606 -0
- package/dist/cjs/ta-series.js.map +7 -0
- package/dist/cjs/text/index.d.ts +14 -0
- package/dist/cjs/text/index.d.ts.map +1 -0
- package/dist/cjs/text/index.js +29 -0
- package/dist/cjs/text/index.js.map +7 -0
- package/dist/cjs/time/datestring.d.ts +23 -0
- package/dist/cjs/time/datestring.d.ts.map +1 -0
- package/dist/cjs/time/datestring.js +106 -0
- package/dist/cjs/time/datestring.js.map +7 -0
- package/dist/cjs/time/index.d.ts +89 -0
- package/dist/cjs/time/index.d.ts.map +1 -0
- package/dist/cjs/time/index.js +85 -0
- package/dist/cjs/time/index.js.map +7 -0
- package/dist/cjs/time/session.d.ts +17 -0
- package/dist/cjs/time/session.d.ts.map +1 -0
- package/dist/cjs/time/session.js +65 -0
- package/dist/cjs/time/session.js.map +7 -0
- package/dist/cjs/time/timezone.d.ts +40 -0
- package/dist/cjs/time/timezone.d.ts.map +1 -0
- package/dist/cjs/time/timezone.js +125 -0
- package/dist/cjs/time/timezone.js.map +7 -0
- package/dist/cjs/timeframe/index.d.ts +73 -0
- package/dist/cjs/timeframe/index.d.ts.map +1 -0
- package/dist/cjs/timeframe/index.js +85 -0
- package/dist/cjs/timeframe/index.js.map +7 -0
- package/dist/cjs/types/index.d.ts +343 -0
- package/dist/cjs/types/index.d.ts.map +1 -0
- package/dist/cjs/types/index.js +32 -0
- package/dist/cjs/types/index.js.map +7 -0
- package/dist/cjs/types/metadata.d.ts +300 -0
- package/dist/cjs/types/metadata.d.ts.map +1 -0
- package/dist/cjs/types/metadata.js +17 -0
- package/dist/cjs/types/metadata.js.map +7 -0
- package/dist/cjs/utils/index.d.ts +167 -0
- package/dist/cjs/utils/index.d.ts.map +1 -0
- package/dist/cjs/utils/index.js +209 -0
- package/dist/cjs/utils/index.js.map +7 -0
- package/dist/esm/array/index.d.ts +701 -0
- package/dist/esm/array/index.d.ts.map +1 -0
- package/dist/esm/array/index.js +983 -0
- package/dist/esm/array/index.js.map +1 -0
- package/dist/esm/box/index.d.ts +444 -0
- package/dist/esm/box/index.d.ts.map +1 -0
- package/dist/esm/box/index.js +602 -0
- package/dist/esm/box/index.js.map +1 -0
- package/dist/esm/callsite/index.d.ts +61 -0
- package/dist/esm/callsite/index.d.ts.map +1 -0
- package/dist/esm/callsite/index.js +97 -0
- package/dist/esm/callsite/index.js.map +1 -0
- package/dist/esm/chartpoint/index.d.ts +90 -0
- package/dist/esm/chartpoint/index.d.ts.map +1 -0
- package/dist/esm/chartpoint/index.js +113 -0
- package/dist/esm/chartpoint/index.js.map +1 -0
- package/dist/esm/color/index.d.ts +285 -0
- package/dist/esm/color/index.d.ts.map +1 -0
- package/dist/esm/color/index.js +373 -0
- package/dist/esm/color/index.js.map +1 -0
- package/dist/esm/compare/index.d.ts +26 -0
- package/dist/esm/compare/index.d.ts.map +1 -0
- package/dist/esm/compare/index.js +39 -0
- package/dist/esm/compare/index.js.map +1 -0
- package/dist/esm/drawing/registry.d.ts +42 -0
- package/dist/esm/drawing/registry.d.ts.map +1 -0
- package/dist/esm/drawing/registry.js +89 -0
- package/dist/esm/drawing/registry.js.map +1 -0
- package/dist/esm/index.d.ts +85 -0
- package/dist/esm/index.d.ts.map +1 -0
- package/dist/esm/index.js +96 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/indicator.d.ts +117 -0
- package/dist/esm/indicator.d.ts.map +1 -0
- package/dist/esm/indicator.js +84 -0
- package/dist/esm/indicator.js.map +1 -0
- package/dist/esm/input.d.ts +196 -0
- package/dist/esm/input.d.ts.map +1 -0
- package/dist/esm/input.js +183 -0
- package/dist/esm/input.js.map +1 -0
- package/dist/esm/label/index.d.ts +303 -0
- package/dist/esm/label/index.d.ts.map +1 -0
- package/dist/esm/label/index.js +417 -0
- package/dist/esm/label/index.js.map +1 -0
- package/dist/esm/lib/index.d.ts +8 -0
- package/dist/esm/lib/index.d.ts.map +1 -0
- package/dist/esm/lib/index.js +8 -0
- package/dist/esm/lib/index.js.map +1 -0
- package/dist/esm/lib/zigzag/index.d.ts +5 -0
- package/dist/esm/lib/zigzag/index.d.ts.map +1 -0
- package/dist/esm/lib/zigzag/index.js +5 -0
- package/dist/esm/lib/zigzag/index.js.map +1 -0
- package/dist/esm/lib/zigzag/zigzag.d.ts +100 -0
- package/dist/esm/lib/zigzag/zigzag.d.ts.map +1 -0
- package/dist/esm/lib/zigzag/zigzag.js +229 -0
- package/dist/esm/lib/zigzag/zigzag.js.map +1 -0
- package/dist/esm/line/index.d.ts +342 -0
- package/dist/esm/line/index.d.ts.map +1 -0
- package/dist/esm/line/index.js +470 -0
- package/dist/esm/line/index.js.map +1 -0
- package/dist/esm/linefill/index.d.ts +124 -0
- package/dist/esm/linefill/index.d.ts.map +1 -0
- package/dist/esm/linefill/index.js +144 -0
- package/dist/esm/linefill/index.js.map +1 -0
- package/dist/esm/map/index.d.ts +69 -0
- package/dist/esm/map/index.d.ts.map +1 -0
- package/dist/esm/map/index.js +106 -0
- package/dist/esm/map/index.js.map +1 -0
- package/dist/esm/math/index.d.ts +560 -0
- package/dist/esm/math/index.d.ts.map +1 -0
- package/dist/esm/math/index.js +495 -0
- package/dist/esm/math/index.js.map +1 -0
- package/dist/esm/matrix/index.d.ts +1231 -0
- package/dist/esm/matrix/index.d.ts.map +1 -0
- package/dist/esm/matrix/index.js +2450 -0
- package/dist/esm/matrix/index.js.map +1 -0
- package/dist/esm/plot.d.ts +76 -0
- package/dist/esm/plot.d.ts.map +1 -0
- package/dist/esm/plot.js +63 -0
- package/dist/esm/plot.js.map +1 -0
- package/dist/esm/polyline/index.d.ts +115 -0
- package/dist/esm/polyline/index.d.ts.map +1 -0
- package/dist/esm/polyline/index.js +157 -0
- package/dist/esm/polyline/index.js.map +1 -0
- package/dist/esm/runtime/adapters/LightweightChartsAdapter.d.ts +86 -0
- package/dist/esm/runtime/adapters/LightweightChartsAdapter.d.ts.map +1 -0
- package/dist/esm/runtime/adapters/LightweightChartsAdapter.js +140 -0
- package/dist/esm/runtime/adapters/LightweightChartsAdapter.js.map +1 -0
- package/dist/esm/runtime/adapters/SimpleInputAdapter.d.ts +59 -0
- package/dist/esm/runtime/adapters/SimpleInputAdapter.d.ts.map +1 -0
- package/dist/esm/runtime/adapters/SimpleInputAdapter.js +115 -0
- package/dist/esm/runtime/adapters/SimpleInputAdapter.js.map +1 -0
- package/dist/esm/runtime/index.d.ts +13 -0
- package/dist/esm/runtime/index.d.ts.map +1 -0
- package/dist/esm/runtime/index.js +14 -0
- package/dist/esm/runtime/index.js.map +1 -0
- package/dist/esm/runtime/inputs.d.ts +91 -0
- package/dist/esm/runtime/inputs.d.ts.map +1 -0
- package/dist/esm/runtime/inputs.js +334 -0
- package/dist/esm/runtime/inputs.js.map +1 -0
- package/dist/esm/runtime/runtime.d.ts +70 -0
- package/dist/esm/runtime/runtime.d.ts.map +1 -0
- package/dist/esm/runtime/runtime.js +193 -0
- package/dist/esm/runtime/runtime.js.map +1 -0
- package/dist/esm/runtime/series.d.ts +362 -0
- package/dist/esm/runtime/series.d.ts.map +1 -0
- package/dist/esm/runtime/series.js +583 -0
- package/dist/esm/runtime/series.js.map +1 -0
- package/dist/esm/runtime/types.d.ts +282 -0
- package/dist/esm/runtime/types.d.ts.map +1 -0
- package/dist/esm/runtime/types.js +7 -0
- package/dist/esm/runtime/types.js.map +1 -0
- package/dist/esm/script/index.d.ts +987 -0
- package/dist/esm/script/index.d.ts.map +1 -0
- package/dist/esm/script/index.js +1289 -0
- package/dist/esm/script/index.js.map +1 -0
- package/dist/esm/security/resample.d.ts +63 -0
- package/dist/esm/security/resample.d.ts.map +1 -0
- package/dist/esm/security/resample.js +110 -0
- package/dist/esm/security/resample.js.map +1 -0
- package/dist/esm/session/bars.d.ts +80 -0
- package/dist/esm/session/bars.d.ts.map +1 -0
- package/dist/esm/session/bars.js +232 -0
- package/dist/esm/session/bars.js.map +1 -0
- package/dist/esm/session/calendar.d.ts +70 -0
- package/dist/esm/session/calendar.d.ts.map +1 -0
- package/dist/esm/session/calendar.js +172 -0
- package/dist/esm/session/calendar.js.map +1 -0
- package/dist/esm/str/dateformat.d.ts +15 -0
- package/dist/esm/str/dateformat.d.ts.map +1 -0
- package/dist/esm/str/dateformat.js +136 -0
- package/dist/esm/str/dateformat.js.map +1 -0
- package/dist/esm/str/index.d.ts +459 -0
- package/dist/esm/str/index.d.ts.map +1 -0
- package/dist/esm/str/index.js +540 -0
- package/dist/esm/str/index.js.map +1 -0
- package/dist/esm/str/messageformat.d.ts +14 -0
- package/dist/esm/str/messageformat.d.ts.map +1 -0
- package/dist/esm/str/messageformat.js +111 -0
- package/dist/esm/str/messageformat.js.map +1 -0
- package/dist/esm/str/numberformat.d.ts +19 -0
- package/dist/esm/str/numberformat.d.ts.map +1 -0
- package/dist/esm/str/numberformat.js +189 -0
- package/dist/esm/str/numberformat.js.map +1 -0
- package/dist/esm/strategy/index.d.ts +245 -0
- package/dist/esm/strategy/index.d.ts.map +1 -0
- package/dist/esm/strategy/index.js +84 -0
- package/dist/esm/strategy/index.js.map +1 -0
- package/dist/esm/ta/index.d.ts +1397 -0
- package/dist/esm/ta/index.d.ts.map +1 -0
- package/dist/esm/ta/index.js +3227 -0
- package/dist/esm/ta/index.js.map +1 -0
- package/dist/esm/ta/running-sum.d.ts +30 -0
- package/dist/esm/ta/running-sum.d.ts.map +1 -0
- package/dist/esm/ta/running-sum.js +113 -0
- package/dist/esm/ta/running-sum.js.map +1 -0
- package/dist/esm/ta-series.d.ts +558 -0
- package/dist/esm/ta-series.d.ts.map +1 -0
- package/dist/esm/ta-series.js +910 -0
- package/dist/esm/ta-series.js.map +1 -0
- package/dist/esm/text/index.d.ts +14 -0
- package/dist/esm/text/index.d.ts.map +1 -0
- package/dist/esm/text/index.js +14 -0
- package/dist/esm/text/index.js.map +1 -0
- package/dist/esm/time/datestring.d.ts +23 -0
- package/dist/esm/time/datestring.d.ts.map +1 -0
- package/dist/esm/time/datestring.js +94 -0
- package/dist/esm/time/datestring.js.map +1 -0
- package/dist/esm/time/index.d.ts +89 -0
- package/dist/esm/time/index.d.ts.map +1 -0
- package/dist/esm/time/index.js +108 -0
- package/dist/esm/time/index.js.map +1 -0
- package/dist/esm/time/session.d.ts +17 -0
- package/dist/esm/time/session.d.ts.map +1 -0
- package/dist/esm/time/session.js +69 -0
- package/dist/esm/time/session.js.map +1 -0
- package/dist/esm/time/timezone.d.ts +40 -0
- package/dist/esm/time/timezone.d.ts.map +1 -0
- package/dist/esm/time/timezone.js +144 -0
- package/dist/esm/time/timezone.js.map +1 -0
- package/dist/esm/timeframe/index.d.ts +73 -0
- package/dist/esm/timeframe/index.d.ts.map +1 -0
- package/dist/esm/timeframe/index.js +131 -0
- package/dist/esm/timeframe/index.js.map +1 -0
- package/dist/esm/types/index.d.ts +343 -0
- package/dist/esm/types/index.d.ts.map +1 -0
- package/dist/esm/types/index.js +17 -0
- package/dist/esm/types/index.js.map +1 -0
- package/dist/esm/types/metadata.d.ts +300 -0
- package/dist/esm/types/metadata.d.ts.map +1 -0
- package/dist/esm/types/metadata.js +7 -0
- package/dist/esm/types/metadata.js.map +1 -0
- package/dist/esm/utils/index.d.ts +167 -0
- package/dist/esm/utils/index.d.ts.map +1 -0
- package/dist/esm/utils/index.js +295 -0
- package/dist/esm/utils/index.js.map +1 -0
- package/package.json +53 -23
- package/src/array/index.ts +1056 -0
- package/src/box/index.ts +680 -0
- package/src/callsite/index.ts +104 -0
- package/src/chartpoint/index.ts +126 -0
- package/src/color/index.ts +411 -0
- package/src/compare/index.ts +46 -0
- package/src/drawing/registry.ts +106 -0
- package/src/index.ts +201 -0
- package/src/indicator.ts +185 -0
- package/src/input.ts +280 -0
- package/src/label/index.ts +490 -0
- package/src/lib/index.ts +8 -0
- package/src/lib/zigzag/index.ts +13 -0
- package/src/lib/zigzag/zigzag.ts +318 -0
- package/src/line/index.ts +530 -0
- package/src/linefill/index.ts +152 -0
- package/src/map/index.ts +115 -0
- package/src/math/index.ts +1071 -0
- package/src/matrix/index.ts +2697 -0
- package/src/plot.ts +121 -0
- package/src/polyline/index.ts +184 -0
- package/src/runtime/adapters/LightweightChartsAdapter.ts +201 -0
- package/src/runtime/adapters/SimpleInputAdapter.ts +132 -0
- package/src/runtime/index.ts +52 -0
- package/src/runtime/inputs.ts +377 -0
- package/src/runtime/runtime.ts +241 -0
- package/src/runtime/series.ts +644 -0
- package/src/runtime/types.ts +292 -0
- package/src/script/index.ts +1865 -0
- package/src/security/resample.ts +119 -0
- package/src/session/bars.ts +262 -0
- package/src/session/calendar.ts +199 -0
- package/src/str/dateformat.ts +104 -0
- package/src/str/index.ts +557 -0
- package/src/str/messageformat.ts +109 -0
- package/src/str/numberformat.ts +197 -0
- package/src/strategy/index.ts +281 -0
- package/src/ta/index.ts +3580 -0
- package/src/ta/running-sum.ts +117 -0
- package/src/ta-series.ts +1036 -0
- package/src/text/index.ts +14 -0
- package/src/time/datestring.ts +96 -0
- package/src/time/index.ts +161 -0
- package/src/time/session.ts +79 -0
- package/src/time/timezone.ts +180 -0
- package/src/timeframe/index.ts +144 -0
- package/src/types/index.ts +398 -0
- package/src/types/metadata.ts +334 -0
- package/src/utils/index.ts +334 -0
- package/dist/array/index.d.ts +0 -57
- package/dist/array/index.d.ts.map +0 -1
- package/dist/box/index.d.ts +0 -37
- package/dist/box/index.d.ts.map +0 -1
- package/dist/callsite/index.d.ts +0 -7
- package/dist/callsite/index.d.ts.map +0 -1
- package/dist/chartpoint/index.d.ts +0 -8
- package/dist/chartpoint/index.d.ts.map +0 -1
- package/dist/color/index.d.ts +0 -28
- package/dist/color/index.d.ts.map +0 -1
- package/dist/compare/index.d.ts +0 -8
- package/dist/compare/index.d.ts.map +0 -1
- package/dist/drawing/registry.d.ts +0 -19
- package/dist/drawing/registry.d.ts.map +0 -1
- package/dist/index.cjs +0 -7979
- package/dist/index.d.ts +0 -64
- package/dist/index.d.ts.map +0 -1
- package/dist/index.mjs +0 -7962
- package/dist/indicator.d.ts +0 -42
- package/dist/indicator.d.ts.map +0 -1
- package/dist/input.d.ts +0 -47
- package/dist/input.d.ts.map +0 -1
- package/dist/label/index.d.ts +0 -30
- package/dist/label/index.d.ts.map +0 -1
- package/dist/lib/index.d.ts +0 -2
- package/dist/lib/index.d.ts.map +0 -1
- package/dist/lib/zigzag/index.d.ts.map +0 -1
- package/dist/lib/zigzag/zigzag.d.ts +0 -47
- package/dist/lib/zigzag/zigzag.d.ts.map +0 -1
- package/dist/line/index.d.ts +0 -30
- package/dist/line/index.d.ts.map +0 -1
- package/dist/linefill/index.d.ts +0 -10
- package/dist/linefill/index.d.ts.map +0 -1
- package/dist/map/index.d.ts +0 -14
- package/dist/map/index.d.ts.map +0 -1
- package/dist/math/index.d.ts +0 -53
- package/dist/math/index.d.ts.map +0 -1
- package/dist/matrix/index.d.ts +0 -52
- package/dist/matrix/index.d.ts.map +0 -1
- package/dist/plot.d.ts +0 -20
- package/dist/plot.d.ts.map +0 -1
- package/dist/polyline/index.d.ts +0 -11
- package/dist/polyline/index.d.ts.map +0 -1
- package/dist/runtime/adapters/LightweightChartsAdapter.d.ts +0 -15
- package/dist/runtime/adapters/LightweightChartsAdapter.d.ts.map +0 -1
- package/dist/runtime/adapters/SimpleInputAdapter.d.ts +0 -16
- package/dist/runtime/adapters/SimpleInputAdapter.d.ts.map +0 -1
- package/dist/runtime/index.cjs +0 -600
- package/dist/runtime/index.d.ts.map +0 -1
- package/dist/runtime/index.mjs +0 -577
- package/dist/runtime/inputs.d.ts +0 -13
- package/dist/runtime/inputs.d.ts.map +0 -1
- package/dist/runtime/runtime.d.ts +0 -16
- package/dist/runtime/runtime.d.ts.map +0 -1
- package/dist/runtime/series.d.ts +0 -60
- package/dist/runtime/series.d.ts.map +0 -1
- package/dist/runtime/types.d.ts +0 -122
- package/dist/runtime/types.d.ts.map +0 -1
- package/dist/script/index.cjs +0 -6372
- package/dist/script/index.d.ts +0 -709
- package/dist/script/index.d.ts.map +0 -1
- package/dist/script/index.mjs +0 -6355
- package/dist/security/resample.d.ts +0 -13
- package/dist/security/resample.d.ts.map +0 -1
- package/dist/session/bars.d.ts +0 -34
- package/dist/session/bars.d.ts.map +0 -1
- package/dist/session/calendar.d.ts +0 -31
- package/dist/session/calendar.d.ts.map +0 -1
- package/dist/str/dateformat.d.ts +0 -2
- package/dist/str/dateformat.d.ts.map +0 -1
- package/dist/str/index.d.ts +0 -24
- package/dist/str/index.d.ts.map +0 -1
- package/dist/str/messageformat.d.ts +0 -2
- package/dist/str/messageformat.d.ts.map +0 -1
- package/dist/str/numberformat.d.ts +0 -3
- package/dist/str/numberformat.d.ts.map +0 -1
- package/dist/strategy/index.d.ts.map +0 -1
- package/dist/ta/index.d.ts +0 -72
- package/dist/ta/index.d.ts.map +0 -1
- package/dist/ta-series.d.ts +0 -143
- package/dist/ta-series.d.ts.map +0 -1
- package/dist/text/index.d.ts +0 -4
- package/dist/text/index.d.ts.map +0 -1
- package/dist/time/datestring.d.ts +0 -2
- package/dist/time/datestring.d.ts.map +0 -1
- package/dist/time/index.d.ts +0 -15
- package/dist/time/index.d.ts.map +0 -1
- package/dist/time/session.d.ts +0 -2
- package/dist/time/session.d.ts.map +0 -1
- package/dist/time/timezone.d.ts +0 -13
- package/dist/time/timezone.d.ts.map +0 -1
- package/dist/timeframe/index.d.ts +0 -17
- package/dist/timeframe/index.d.ts.map +0 -1
- package/dist/types/index.d.ts +0 -181
- package/dist/types/index.d.ts.map +0 -1
- package/dist/types/metadata.d.ts +0 -131
- package/dist/types/metadata.d.ts.map +0 -1
- package/dist/utils/index.d.ts +0 -26
- package/dist/utils/index.d.ts.map +0 -1
|
@@ -0,0 +1,1865 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview PineScript-style script API — write an indicator as a flat
|
|
3
|
+
* script instead of the explicit calculate() convention.
|
|
4
|
+
*
|
|
5
|
+
* ```typescript
|
|
6
|
+
* import { indicator, input, plot, ta } from 'oakscriptjs/script';
|
|
7
|
+
*
|
|
8
|
+
* indicator('My SMA', { overlay: true });
|
|
9
|
+
* const length = input.int(20, 'Length', { minval: 1 });
|
|
10
|
+
* const src = input.source('close', 'Source');
|
|
11
|
+
* plot(ta.sma(src, length), 'SMA', { color: '#2962FF' });
|
|
12
|
+
* ```
|
|
13
|
+
*
|
|
14
|
+
* Execution model: the host calls {@link executeScript} with the script body,
|
|
15
|
+
* the bars and the current input values. Declaration calls (indicator, input.*,
|
|
16
|
+
* plot, hline, fill, alertcondition) run as the body executes and are collected
|
|
17
|
+
* into a {@link ScriptRunResult}: the declarative configs (inputConfig,
|
|
18
|
+
* plotConfig, hlineConfig, fillConfig) plus an IndicatorResult with the plot
|
|
19
|
+
* data. Re-running the body with new bars or inputs is PineScript's
|
|
20
|
+
* recalculation model.
|
|
21
|
+
*
|
|
22
|
+
* `input.*` is idempotent like PineScript: it declares the input AND returns
|
|
23
|
+
* its current value (the host-supplied override, else the default).
|
|
24
|
+
*
|
|
25
|
+
* The OHLCV builtins (open/high/low/close/volume/hl2/hlc3/ohlc4/hlcc4) are
|
|
26
|
+
* Series bound to a module-level BarData that the host swaps per run, so
|
|
27
|
+
* derived Series memoize between identical runs and recompute when the bars
|
|
28
|
+
* version bumps.
|
|
29
|
+
*
|
|
30
|
+
* One script executes at a time (module-level context) — hosts that run many
|
|
31
|
+
* scripts must serialize executions, which a Web Worker does naturally.
|
|
32
|
+
*
|
|
33
|
+
* @module script
|
|
34
|
+
*/
|
|
35
|
+
|
|
36
|
+
import type { Bar } from '../types/index.js';
|
|
37
|
+
import type {
|
|
38
|
+
ArrowData,
|
|
39
|
+
BarColorData,
|
|
40
|
+
FillData,
|
|
41
|
+
FillGradient,
|
|
42
|
+
IndicatorResult,
|
|
43
|
+
MarkerData,
|
|
44
|
+
MarkerLocation,
|
|
45
|
+
MarkerSize,
|
|
46
|
+
ShapeStyle,
|
|
47
|
+
TimeValue,
|
|
48
|
+
} from '../types/metadata.js';
|
|
49
|
+
import type {
|
|
50
|
+
ArrowConfig,
|
|
51
|
+
BarColorConfig,
|
|
52
|
+
FillConfig,
|
|
53
|
+
HLineConfig,
|
|
54
|
+
InputConfig,
|
|
55
|
+
PlotConfig,
|
|
56
|
+
ShapeConfig,
|
|
57
|
+
} from '../runtime/types.js';
|
|
58
|
+
import { BarData, Series } from '../runtime/series.js';
|
|
59
|
+
import * as taSeries from '../ta-series.js';
|
|
60
|
+
import * as colorCore from '../color/index.js';
|
|
61
|
+
import { fixnan as fixnanValues, isNA, nz } from '../utils/index.js';
|
|
62
|
+
import * as lineCore from '../line/index.js';
|
|
63
|
+
import * as labelCore from '../label/index.js';
|
|
64
|
+
import * as boxCore from '../box/index.js';
|
|
65
|
+
import * as linefillCore from '../linefill/index.js';
|
|
66
|
+
import * as polylineCore from '../polyline/index.js';
|
|
67
|
+
import * as chartPointCore from '../chartpoint/index.js';
|
|
68
|
+
import * as textCore from '../text/index.js';
|
|
69
|
+
import * as mathCore from '../math/index.js';
|
|
70
|
+
import { resetDrawings, setDrawingLimits } from '../drawing/registry.js';
|
|
71
|
+
import * as timeframeCore from '../timeframe/index.js';
|
|
72
|
+
import * as timeCore from '../time/index.js';
|
|
73
|
+
import * as strCore from '../str/index.js';
|
|
74
|
+
import { formatMessage } from '../str/messageformat.js';
|
|
75
|
+
import { TradingCalendar, type SessionSpec } from '../session/calendar.js';
|
|
76
|
+
import { SessionBars, sessionFlags, type SessionFlags } from '../session/bars.js';
|
|
77
|
+
import { afterCompletion, heikinAshi, mapToChart, periodsOf, resample } from '../security/resample.js';
|
|
78
|
+
import type { ChartPoint } from '../types/index.js';
|
|
79
|
+
import {
|
|
80
|
+
STRATEGY_CONSTANTS,
|
|
81
|
+
STRATEGY_DEFAULTS,
|
|
82
|
+
STRATEGY_NUMBER_VARIABLES,
|
|
83
|
+
STRATEGY_TEXT_VARIABLES,
|
|
84
|
+
type StrategyCloseAllOptions,
|
|
85
|
+
type StrategyCloseOptions,
|
|
86
|
+
type StrategyDirection,
|
|
87
|
+
type StrategyEngine,
|
|
88
|
+
type StrategyEntryOptions,
|
|
89
|
+
type StrategyExitOptions,
|
|
90
|
+
type StrategyNumberVariable,
|
|
91
|
+
type StrategyOptions,
|
|
92
|
+
type StrategyProperties,
|
|
93
|
+
type StrategyRiskDirection,
|
|
94
|
+
type StrategyRiskRule,
|
|
95
|
+
type StrategyRiskValueType,
|
|
96
|
+
type StrategyTextVariable,
|
|
97
|
+
type StrategyTrade,
|
|
98
|
+
type StrategyVariable,
|
|
99
|
+
} from '../strategy/index.js';
|
|
100
|
+
|
|
101
|
+
/** Everything one run of a script produced. */
|
|
102
|
+
export interface ScriptRunResult {
|
|
103
|
+
metadata: { title: string; shortTitle?: string; overlay: boolean; precision?: number; format?: string };
|
|
104
|
+
inputConfig: InputConfig[];
|
|
105
|
+
plotConfig: PlotConfig[];
|
|
106
|
+
hlineConfig: HLineConfig[];
|
|
107
|
+
fillConfig: FillConfig[];
|
|
108
|
+
shapeConfig: ShapeConfig[];
|
|
109
|
+
arrowConfig: ArrowConfig[];
|
|
110
|
+
barColorConfig: BarColorConfig[];
|
|
111
|
+
defaultInputs: Record<string, unknown>;
|
|
112
|
+
alertConfig: AlertConditionConfig[];
|
|
113
|
+
/** Strategy properties (declared values, else the PineScript defaults) when the script declares strategy(). */
|
|
114
|
+
strategyConfig?: StrategyProperties;
|
|
115
|
+
/** The renderable output (plots keyed by plotConfig ids, plot-pair fills). */
|
|
116
|
+
result: IndicatorResult & { alerts?: AlertState[] };
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export interface AlertConditionConfig {
|
|
120
|
+
id: string;
|
|
121
|
+
title: string;
|
|
122
|
+
message?: string;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/** Per-run alert evaluation: whether the condition is true on the last bar. */
|
|
126
|
+
export interface AlertState {
|
|
127
|
+
id: string;
|
|
128
|
+
title: string;
|
|
129
|
+
message?: string;
|
|
130
|
+
triggered: boolean;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/** Handle returned by plot()/hline() so fill() can reference them. */
|
|
134
|
+
export interface PlotHandle {
|
|
135
|
+
id: string;
|
|
136
|
+
kind: 'plot' | 'hline';
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
interface Collector {
|
|
140
|
+
declaration?: 'indicator' | 'strategy';
|
|
141
|
+
title: string;
|
|
142
|
+
shortTitle?: string;
|
|
143
|
+
overlay: boolean;
|
|
144
|
+
precision?: number;
|
|
145
|
+
format?: string;
|
|
146
|
+
inputValues: Record<string, unknown>;
|
|
147
|
+
inputConfig: InputConfig[];
|
|
148
|
+
inputIds: Set<string>;
|
|
149
|
+
defaultInputs: Record<string, unknown>;
|
|
150
|
+
plotConfig: PlotConfig[];
|
|
151
|
+
plots: Record<string, TimeValue[]>;
|
|
152
|
+
hlineConfig: HLineConfig[];
|
|
153
|
+
fills: FillData[];
|
|
154
|
+
fillConfig: FillConfig[];
|
|
155
|
+
shapeConfig: ShapeConfig[];
|
|
156
|
+
markers: MarkerData[];
|
|
157
|
+
arrowConfig: ArrowConfig[];
|
|
158
|
+
arrows: ArrowData[];
|
|
159
|
+
barColorConfig: BarColorConfig[];
|
|
160
|
+
bgcolors: BarColorData[];
|
|
161
|
+
barcolors: BarColorData[];
|
|
162
|
+
alertConfig: AlertConditionConfig[];
|
|
163
|
+
alerts: AlertState[];
|
|
164
|
+
strategy?: StrategyRun;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/** State of the strategy of the current run. */
|
|
168
|
+
interface StrategyRun {
|
|
169
|
+
properties: StrategyProperties;
|
|
170
|
+
engine?: StrategyEngine;
|
|
171
|
+
/** strategy.eachBar() has run (it runs once per script run). */
|
|
172
|
+
looped: boolean;
|
|
173
|
+
/** Inside the strategy.eachBar() callback. */
|
|
174
|
+
inLoop: boolean;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/** Bars for the current run. Module-level and REUSED across runs so builtins
|
|
178
|
+
* and every Series derived from them keep their version-keyed memoization. */
|
|
179
|
+
const ctxBars = new BarData([]);
|
|
180
|
+
|
|
181
|
+
let ctx: Collector | null = null;
|
|
182
|
+
|
|
183
|
+
function collector(): Collector {
|
|
184
|
+
if (!ctx) {
|
|
185
|
+
throw new Error(
|
|
186
|
+
'oakscriptjs/script functions can only run inside executeScript() — ' +
|
|
187
|
+
'the host (chart/worker) drives script execution.'
|
|
188
|
+
);
|
|
189
|
+
}
|
|
190
|
+
return ctx;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
function freshCollector(inputValues: Record<string, unknown>): Collector {
|
|
194
|
+
return {
|
|
195
|
+
title: 'Untitled',
|
|
196
|
+
overlay: true,
|
|
197
|
+
inputValues,
|
|
198
|
+
inputConfig: [],
|
|
199
|
+
inputIds: new Set(),
|
|
200
|
+
defaultInputs: {},
|
|
201
|
+
plotConfig: [],
|
|
202
|
+
plots: {},
|
|
203
|
+
hlineConfig: [],
|
|
204
|
+
fills: [],
|
|
205
|
+
fillConfig: [],
|
|
206
|
+
shapeConfig: [],
|
|
207
|
+
markers: [],
|
|
208
|
+
arrowConfig: [],
|
|
209
|
+
arrows: [],
|
|
210
|
+
barColorConfig: [],
|
|
211
|
+
bgcolors: [],
|
|
212
|
+
barcolors: [],
|
|
213
|
+
alertConfig: [],
|
|
214
|
+
alerts: [],
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/** Stable input id from the title ("ATR / Range Period" → "atr_range_period"). */
|
|
219
|
+
function slugId(title: string | undefined, fallback: string, taken: Set<string>): string {
|
|
220
|
+
const base =
|
|
221
|
+
(title ?? '')
|
|
222
|
+
.toLowerCase()
|
|
223
|
+
.replace(/[^a-z0-9]+/g, '_')
|
|
224
|
+
.replace(/^_+|_+$/g, '') || fallback;
|
|
225
|
+
let id = base;
|
|
226
|
+
for (let n = 2; taken.has(id); n++) id = `${base}_${n}`;
|
|
227
|
+
taken.add(id);
|
|
228
|
+
return id;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// ── Host entry ───────────────────────────────────────────────────────────────
|
|
232
|
+
|
|
233
|
+
/** What the host passes to its strategy engine factory when the script declares strategy(). */
|
|
234
|
+
export interface StrategySetup {
|
|
235
|
+
properties: StrategyProperties;
|
|
236
|
+
bars: readonly Bar[];
|
|
237
|
+
chart: Readonly<ChartContext>;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
export interface ScriptOptions {
|
|
241
|
+
/** Creates the order engine of a strategy script; called by strategy(). */
|
|
242
|
+
strategyEngine?: (setup: StrategySetup) => StrategyEngine;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
let scriptOptions: ScriptOptions = {};
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* Runs a script body against `bars` with the given input overrides and
|
|
249
|
+
* collects everything it declared. `chart` describes the bars (chart timeframe,
|
|
250
|
+
* exchange time zone, session type) for the PineScript values that depend on them. `options.strategyEngine`
|
|
251
|
+
* supplies the order engine of strategy scripts. This is the host-side entry — editor
|
|
252
|
+
* panels, workers and tests call it; scripts never do.
|
|
253
|
+
*/
|
|
254
|
+
export function executeScript(
|
|
255
|
+
body: () => void,
|
|
256
|
+
barsInput: Bar[],
|
|
257
|
+
inputs: Record<string, unknown> = {},
|
|
258
|
+
chart: ChartContext = {},
|
|
259
|
+
options: ScriptOptions = {}
|
|
260
|
+
): ScriptRunResult {
|
|
261
|
+
ctxBars.setAll(barsInput);
|
|
262
|
+
chartCtx = { ...chart };
|
|
263
|
+
scriptOptions = options;
|
|
264
|
+
ctx = freshCollector(inputs);
|
|
265
|
+
resetDrawings();
|
|
266
|
+
try {
|
|
267
|
+
body();
|
|
268
|
+
const c = ctx;
|
|
269
|
+
return {
|
|
270
|
+
metadata: {
|
|
271
|
+
title: c.title,
|
|
272
|
+
shortTitle: c.shortTitle,
|
|
273
|
+
overlay: c.overlay,
|
|
274
|
+
precision: c.precision,
|
|
275
|
+
format: c.format,
|
|
276
|
+
},
|
|
277
|
+
inputConfig: c.inputConfig,
|
|
278
|
+
plotConfig: c.plotConfig,
|
|
279
|
+
hlineConfig: c.hlineConfig,
|
|
280
|
+
fillConfig: c.fillConfig,
|
|
281
|
+
shapeConfig: c.shapeConfig,
|
|
282
|
+
arrowConfig: c.arrowConfig,
|
|
283
|
+
barColorConfig: c.barColorConfig,
|
|
284
|
+
defaultInputs: c.defaultInputs,
|
|
285
|
+
alertConfig: c.alertConfig,
|
|
286
|
+
strategyConfig: c.strategy?.properties,
|
|
287
|
+
result: {
|
|
288
|
+
metadata: { title: c.title, shorttitle: c.shortTitle, overlay: c.overlay, precision: c.precision },
|
|
289
|
+
plots: c.plots,
|
|
290
|
+
fills: c.fills.length ? c.fills : undefined,
|
|
291
|
+
markers: c.markers.length ? c.markers : undefined,
|
|
292
|
+
arrows: c.arrows.length ? c.arrows : undefined,
|
|
293
|
+
bgcolors: c.bgcolors.length ? c.bgcolors : undefined,
|
|
294
|
+
barcolors: c.barcolors.length ? c.barcolors : undefined,
|
|
295
|
+
alerts: c.alerts.length ? c.alerts : undefined,
|
|
296
|
+
},
|
|
297
|
+
};
|
|
298
|
+
} finally {
|
|
299
|
+
ctx = null;
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
// ── Declarations ─────────────────────────────────────────────────────────────
|
|
304
|
+
|
|
305
|
+
export interface IndicatorOptions {
|
|
306
|
+
shorttitle?: string;
|
|
307
|
+
overlay?: boolean;
|
|
308
|
+
precision?: number;
|
|
309
|
+
format?: string;
|
|
310
|
+
/** Lines kept before the oldest are deleted (default 50; PineScript keeps up to 5 more). */
|
|
311
|
+
max_lines_count?: number;
|
|
312
|
+
/** Labels kept before the oldest are deleted (default 50). */
|
|
313
|
+
max_labels_count?: number;
|
|
314
|
+
/** Boxes kept before the oldest are deleted (default 50). */
|
|
315
|
+
max_boxes_count?: number;
|
|
316
|
+
/** Polylines kept before the oldest are deleted (default 50). */
|
|
317
|
+
max_polylines_count?: number;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
/** PineScript `indicator()` — declares the script's metadata. */
|
|
321
|
+
export function indicator(title: string, options: IndicatorOptions = {}): void {
|
|
322
|
+
const c = collector();
|
|
323
|
+
declare(c, 'indicator');
|
|
324
|
+
c.title = title;
|
|
325
|
+
c.shortTitle = options.shorttitle;
|
|
326
|
+
c.overlay = options.overlay ?? true;
|
|
327
|
+
c.precision = options.precision;
|
|
328
|
+
c.format = options.format;
|
|
329
|
+
setDrawingLimits({
|
|
330
|
+
line: options.max_lines_count,
|
|
331
|
+
label: options.max_labels_count,
|
|
332
|
+
box: options.max_boxes_count,
|
|
333
|
+
polyline: options.max_polylines_count,
|
|
334
|
+
});
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
function declare(c: Collector, kind: 'indicator' | 'strategy'): void {
|
|
338
|
+
if (c.declaration) {
|
|
339
|
+
throw new Error(`${kind}(): the script already declared ${c.declaration}(); a script has one declaration.`);
|
|
340
|
+
}
|
|
341
|
+
c.declaration = kind;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
/** Options of every `input.*` kind (PineScript `group`, `inline`, `tooltip`, `confirm`, `display`). */
|
|
345
|
+
export interface InputOptions {
|
|
346
|
+
group?: string;
|
|
347
|
+
inline?: string;
|
|
348
|
+
tooltip?: string;
|
|
349
|
+
confirm?: boolean;
|
|
350
|
+
display?: InputConfig['display'];
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
export interface NumericInputOptions extends InputOptions {
|
|
354
|
+
minval?: number;
|
|
355
|
+
maxval?: number;
|
|
356
|
+
step?: number;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
export interface StringInputOptions extends InputOptions {
|
|
360
|
+
options?: string[];
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
/** The InputConfig fields of the common input options (unset options are left out). */
|
|
364
|
+
function common(opts: InputOptions): Partial<InputConfig> {
|
|
365
|
+
const out: Partial<InputConfig> = {};
|
|
366
|
+
if (opts.group !== undefined) out.group = opts.group;
|
|
367
|
+
if (opts.inline !== undefined) out.inline = opts.inline;
|
|
368
|
+
if (opts.tooltip !== undefined) out.tooltip = opts.tooltip;
|
|
369
|
+
if (opts.confirm !== undefined) out.confirm = opts.confirm;
|
|
370
|
+
if (opts.display !== undefined) out.display = opts.display;
|
|
371
|
+
return out;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
/** Declares the input in the collector and returns its current value (the
|
|
375
|
+
* host-supplied override when present, else the default). */
|
|
376
|
+
function registerInput<T>(config: Omit<InputConfig, 'id'>, fallbackId: string): T {
|
|
377
|
+
const c = collector();
|
|
378
|
+
const id = slugId(config.title, fallbackId, c.inputIds);
|
|
379
|
+
c.inputConfig.push({ id, ...config });
|
|
380
|
+
c.defaultInputs[id] = config.defval;
|
|
381
|
+
const value = c.inputValues[id];
|
|
382
|
+
return (value === undefined ? config.defval : value) as T;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
function registerNumeric(type: 'int' | 'float', defval: number, title?: string, opts: NumericInputOptions = {}): number {
|
|
386
|
+
const v = registerInput<unknown>(
|
|
387
|
+
{ type, defval, title, min: opts.minval, max: opts.maxval, step: opts.step, ...common(opts) },
|
|
388
|
+
`input_${type}`
|
|
389
|
+
);
|
|
390
|
+
return typeof v === 'number' ? v : defval;
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
/** PineScript `input.*` — declares an input AND returns its current value. */
|
|
394
|
+
export const input = {
|
|
395
|
+
int(defval: number, title?: string, opts: NumericInputOptions = {}): number {
|
|
396
|
+
return Math.round(registerNumeric('int', defval, title, opts));
|
|
397
|
+
},
|
|
398
|
+
float(defval: number, title?: string, opts: NumericInputOptions = {}): number {
|
|
399
|
+
return registerNumeric('float', defval, title, opts);
|
|
400
|
+
},
|
|
401
|
+
bool(defval: boolean, title?: string, opts: InputOptions = {}): boolean {
|
|
402
|
+
return registerInput<boolean>({ type: 'bool', defval, title, ...common(opts) }, 'input_bool');
|
|
403
|
+
},
|
|
404
|
+
string(defval: string, title?: string, opts: StringInputOptions = {}): string {
|
|
405
|
+
return registerInput<string>({ type: 'string', defval, title, options: opts.options, ...common(opts) }, 'input_string');
|
|
406
|
+
},
|
|
407
|
+
color(defval: string, title?: string, opts: InputOptions = {}): string {
|
|
408
|
+
return registerInput<string>({ type: 'color', defval, title, ...common(opts) }, 'input_color');
|
|
409
|
+
},
|
|
410
|
+
/** Declares a source input and returns it as a Series. */
|
|
411
|
+
source(defval: string = 'close', title?: string, opts: InputOptions = {}): Series {
|
|
412
|
+
const name = registerInput<string>({ type: 'source', defval, title, ...common(opts) }, 'input_source');
|
|
413
|
+
return sourceSeries(name);
|
|
414
|
+
},
|
|
415
|
+
/** Timeframe string ("60", "1D", "W"). As in PineScript, "" means the chart timeframe. */
|
|
416
|
+
timeframe(defval: string, title?: string, opts: StringInputOptions = {}): string {
|
|
417
|
+
return registerInput<string>(
|
|
418
|
+
{ type: 'timeframe', defval, title, options: opts.options, ...common(opts) },
|
|
419
|
+
'input_timeframe'
|
|
420
|
+
);
|
|
421
|
+
},
|
|
422
|
+
/** Session string ("0930-1600", "0930-1600:23456"). */
|
|
423
|
+
session(defval: string, title?: string, opts: StringInputOptions = {}): string {
|
|
424
|
+
return registerInput<string>(
|
|
425
|
+
{ type: 'session', defval, title, options: opts.options, ...common(opts) },
|
|
426
|
+
'input_session'
|
|
427
|
+
);
|
|
428
|
+
},
|
|
429
|
+
/** Date and time as a UNIX time in milliseconds, e.g. `input.time(timestamp('2024-01-01'), 'Start')`. */
|
|
430
|
+
time(defval: number, title?: string, opts: InputOptions = {}): number {
|
|
431
|
+
const v = registerInput<unknown>({ type: 'time', defval, title, ...common(opts) }, 'input_time');
|
|
432
|
+
return typeof v === 'number' ? v : defval;
|
|
433
|
+
},
|
|
434
|
+
};
|
|
435
|
+
|
|
436
|
+
// ── OHLCV builtins ───────────────────────────────────────────────────────────
|
|
437
|
+
|
|
438
|
+
export const open = Series.fromBars(ctxBars, 'open');
|
|
439
|
+
export const high = Series.fromBars(ctxBars, 'high');
|
|
440
|
+
export const low = Series.fromBars(ctxBars, 'low');
|
|
441
|
+
export const close = Series.fromBars(ctxBars, 'close');
|
|
442
|
+
export const volume = Series.fromBars(ctxBars, 'volume');
|
|
443
|
+
export const hl2 = new Series(ctxBars, (b) => (b.high + b.low) / 2);
|
|
444
|
+
export const hlc3 = new Series(ctxBars, (b) => (b.high + b.low + b.close) / 3);
|
|
445
|
+
export const ohlc4 = new Series(ctxBars, (b) => (b.open + b.high + b.low + b.close) / 4);
|
|
446
|
+
export const hlcc4 = new Series(ctxBars, (b) => (b.high + b.low + b.close + b.close) / 4);
|
|
447
|
+
|
|
448
|
+
const SOURCES: Record<string, Series> = { open, high, low, close, volume, hl2, hlc3, ohlc4, hlcc4 };
|
|
449
|
+
|
|
450
|
+
function sourceSeries(name: string): Series {
|
|
451
|
+
return SOURCES[name] ?? close;
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
// ── Bound ta namespace ───────────────────────────────────────────────────────
|
|
455
|
+
|
|
456
|
+
const bars = (): Bar[] => ctxBars.bars;
|
|
457
|
+
|
|
458
|
+
/** A Series bound to the context bars whose values come from one pass over all bars
|
|
459
|
+
* (cumulative indicators), recomputed when the bars version changes. */
|
|
460
|
+
function barsSeries(compute: (b: Bar[]) => Series): Series {
|
|
461
|
+
let version = -1;
|
|
462
|
+
let values: number[] = [];
|
|
463
|
+
return new Series(ctxBars, (_bar, i, data) => {
|
|
464
|
+
if (version !== ctxBars.version) {
|
|
465
|
+
values = compute(data).toArray();
|
|
466
|
+
version = ctxBars.version;
|
|
467
|
+
}
|
|
468
|
+
return values[i] ?? NaN;
|
|
469
|
+
});
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
/** ta.* with the chart-implicit (bars-first) functions bound to the context,
|
|
473
|
+
* so scripts call them like PineScript: `ta.tr(true)`, `ta.atr(14)`. */
|
|
474
|
+
export const ta = {
|
|
475
|
+
...taSeries,
|
|
476
|
+
tr: (handleNa: boolean = false): Series => taSeries.tr(bars(), handleNa),
|
|
477
|
+
atr: (length: number): Series => taSeries.atr(bars(), length),
|
|
478
|
+
sar: (start?: number, inc?: number, max?: number): Series => taSeries.sar(bars(), start, inc, max),
|
|
479
|
+
wpr: (length?: number): Series => taSeries.wpr(bars(), length),
|
|
480
|
+
supertrend: (factor: number, atrLength: number): [Series, Series] =>
|
|
481
|
+
taSeries.supertrend(bars(), factor, atrLength),
|
|
482
|
+
dmi: (diLength: number, adxSmoothing: number): [Series, Series, Series] =>
|
|
483
|
+
taSeries.dmi(bars(), diLength, adxSmoothing),
|
|
484
|
+
kc: (source: Series, length: number, mult: number, useTrueRange?: boolean): [Series, Series, Series] =>
|
|
485
|
+
taSeries.kc(bars(), source, length, mult, useTrueRange),
|
|
486
|
+
kcw: (source: Series, length?: number, mult?: number, useTrueRange?: boolean): Series =>
|
|
487
|
+
taSeries.kcw(bars(), source, length, mult, useTrueRange),
|
|
488
|
+
ichimoku: (
|
|
489
|
+
conversionPeriods: number,
|
|
490
|
+
basePeriods: number,
|
|
491
|
+
laggingSpan2Periods: number,
|
|
492
|
+
displacement: number
|
|
493
|
+
): [Series, Series, Series, Series, Series] =>
|
|
494
|
+
taSeries.ichimoku(bars(), conversionPeriods, basePeriods, laggingSpan2Periods, displacement),
|
|
495
|
+
/** `ta.highest(source, length)` or `ta.highest(length)` (source = high). */
|
|
496
|
+
highest: (sourceOrLength: Series | number, length?: number): Series =>
|
|
497
|
+
typeof sourceOrLength === 'number' ? taSeries.highest(high, sourceOrLength) : taSeries.highest(sourceOrLength, length!),
|
|
498
|
+
/** `ta.lowest(source, length)` or `ta.lowest(length)` (source = low). */
|
|
499
|
+
lowest: (sourceOrLength: Series | number, length?: number): Series =>
|
|
500
|
+
typeof sourceOrLength === 'number' ? taSeries.lowest(low, sourceOrLength) : taSeries.lowest(sourceOrLength, length!),
|
|
501
|
+
/** `ta.highestbars(source, length)` or `ta.highestbars(length)` (source = high); offsets are 0 or negative. */
|
|
502
|
+
highestbars: (sourceOrLength: Series | number, length?: number): Series =>
|
|
503
|
+
typeof sourceOrLength === 'number'
|
|
504
|
+
? taSeries.highestbars(high, sourceOrLength)
|
|
505
|
+
: taSeries.highestbars(sourceOrLength, length!),
|
|
506
|
+
/** `ta.lowestbars(source, length)` or `ta.lowestbars(length)` (source = low); offsets are 0 or negative. */
|
|
507
|
+
lowestbars: (sourceOrLength: Series | number, length?: number): Series =>
|
|
508
|
+
typeof sourceOrLength === 'number'
|
|
509
|
+
? taSeries.lowestbars(low, sourceOrLength)
|
|
510
|
+
: taSeries.lowestbars(sourceOrLength, length!),
|
|
511
|
+
/** `ta.vwap(source, anchor?, stdev_mult?)` with the chart volume; the default anchor is a new trading
|
|
512
|
+
* day (`timeframe.change("1D")`), which needs the chart context (timeframe, time zone, session). */
|
|
513
|
+
vwap: ((source: Series, anchor?: Series, stdev_mult?: number): Series | [Series, Series, Series] => {
|
|
514
|
+
const a = anchor ?? changeOf('1D'); // PineScript default: timeframe.change("1D")
|
|
515
|
+
return stdev_mult === undefined ? taSeries.vwap(source, volume, a) : taSeries.vwapBands(source, volume, a, stdev_mult);
|
|
516
|
+
}) as {
|
|
517
|
+
(source: Series, anchor?: Series): Series;
|
|
518
|
+
(source: Series, anchor: Series | undefined, stdev_mult: number): [Series, Series, Series];
|
|
519
|
+
},
|
|
520
|
+
/** `ta.mfi(source, length)` with the chart volume (PineScript takes 2 arguments). */
|
|
521
|
+
mfi: (source: Series, length: number): Series => taSeries.mfi(source, length, volume),
|
|
522
|
+
/** `ta.pivothigh(source, leftbars, rightbars)` or `ta.pivothigh(leftbars, rightbars)` (source = high).
|
|
523
|
+
* The value appears `rightbars` bars after the pivot bar, as in PineScript. */
|
|
524
|
+
pivothigh: (sourceOrLeft: Series | number, leftOrRight: number, rightbars?: number): Series =>
|
|
525
|
+
typeof sourceOrLeft === 'number'
|
|
526
|
+
? taSeries.pivothigh(high, sourceOrLeft, leftOrRight)
|
|
527
|
+
: taSeries.pivothigh(sourceOrLeft, leftOrRight, rightbars!),
|
|
528
|
+
/** `ta.pivotlow(source, leftbars, rightbars)` or `ta.pivotlow(leftbars, rightbars)` (source = low). */
|
|
529
|
+
pivotlow: (sourceOrLeft: Series | number, leftOrRight: number, rightbars?: number): Series =>
|
|
530
|
+
typeof sourceOrLeft === 'number'
|
|
531
|
+
? taSeries.pivotlow(low, sourceOrLeft, leftOrRight)
|
|
532
|
+
: taSeries.pivotlow(sourceOrLeft, leftOrRight, rightbars!),
|
|
533
|
+
// PineScript variables of the chart bars: `ta.obv`, not `ta.obv()`.
|
|
534
|
+
obv: barsSeries(taSeries.obv),
|
|
535
|
+
pvt: barsSeries(taSeries.pvt),
|
|
536
|
+
accdist: barsSeries(taSeries.accdist),
|
|
537
|
+
nvi: barsSeries(taSeries.nvi),
|
|
538
|
+
pvi: barsSeries(taSeries.pvi),
|
|
539
|
+
iii: barsSeries(taSeries.iii),
|
|
540
|
+
wad: barsSeries(taSeries.wad),
|
|
541
|
+
wvad: barsSeries(taSeries.wvad),
|
|
542
|
+
};
|
|
543
|
+
|
|
544
|
+
/** PineScript `fixnan(source)`: each `na` value becomes the previous non-`na` value.
|
|
545
|
+
* Accepts a Series or a per-bar color array (`undefined` = `na`, as returned by `color.when`). */
|
|
546
|
+
export function fixnan(source: Series): Series;
|
|
547
|
+
export function fixnan(source: Array<string | undefined>): Array<string | undefined>;
|
|
548
|
+
export function fixnan(source: Series | Array<string | undefined>): Series | Array<string | undefined> {
|
|
549
|
+
return source instanceof Series ? Series.fromArray(ctxBars, fixnanValues(source.toArray())) : fixnanValues(source);
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
// ── Color helpers ────────────────────────────────────────────────────────────
|
|
553
|
+
|
|
554
|
+
/** Per-bar color selection: truthy → colorTrue, else colorFalse.
|
|
555
|
+
* colorFalse may be omitted (PineScript `na`): those bars get no color,
|
|
556
|
+
* which bgcolor()/barcolor() skip. */
|
|
557
|
+
function colorWhen(cond: Series, colorTrue: string, colorFalse: string): string[];
|
|
558
|
+
function colorWhen(cond: Series, colorTrue: string, colorFalse?: string): Array<string | undefined>;
|
|
559
|
+
function colorWhen(cond: Series, colorTrue: string, colorFalse?: string): Array<string | undefined> {
|
|
560
|
+
return cond.toArray().map((v) => (!isNA(v) && v !== 0 ? colorTrue : colorFalse));
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
/** color.* plus `when` — per-bar conditional colors for plot()/fill()/bgcolor()/barcolor(). */
|
|
564
|
+
export const color = {
|
|
565
|
+
...colorCore,
|
|
566
|
+
new: colorCore.new_color,
|
|
567
|
+
when: colorWhen,
|
|
568
|
+
};
|
|
569
|
+
|
|
570
|
+
// ── Drawing declarations ─────────────────────────────────────────────────────
|
|
571
|
+
|
|
572
|
+
export interface ScriptPlotOptions {
|
|
573
|
+
/** Static color, or a per-bar color array from color.when(). */
|
|
574
|
+
color?: string | string[];
|
|
575
|
+
linewidth?: number;
|
|
576
|
+
style?: PlotConfig['style'];
|
|
577
|
+
/** Line style (PineScript `linestyle`); default solid. A constant or an input value, not per bar. */
|
|
578
|
+
linestyle?: PlotConfig['linestyle'];
|
|
579
|
+
display?: PlotConfig['display'];
|
|
580
|
+
histbase?: number;
|
|
581
|
+
/** Draw on the main chart pane even when the script is not an overlay (PineScript `force_overlay`); default false. */
|
|
582
|
+
force_overlay?: boolean;
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
/** PineScript `plot()` — declares the plot and supplies its data in one call. */
|
|
586
|
+
function plotImpl(series: Series, title?: string, options: ScriptPlotOptions = {}): PlotHandle {
|
|
587
|
+
const c = collector();
|
|
588
|
+
const id = `plot${c.plotConfig.length}`;
|
|
589
|
+
const perBar = Array.isArray(options.color) ? options.color : undefined;
|
|
590
|
+
const staticColor = typeof options.color === 'string' ? options.color : (perBar?.[perBar.length - 1] ?? '#2962FF');
|
|
591
|
+
c.plotConfig.push({
|
|
592
|
+
id,
|
|
593
|
+
title: title ?? id,
|
|
594
|
+
color: staticColor,
|
|
595
|
+
lineWidth: options.linewidth,
|
|
596
|
+
style: options.style,
|
|
597
|
+
linestyle: options.linestyle,
|
|
598
|
+
display: options.display,
|
|
599
|
+
histbase: options.histbase,
|
|
600
|
+
forceOverlay: options.force_overlay,
|
|
601
|
+
});
|
|
602
|
+
const values = series.toArray();
|
|
603
|
+
const b = bars();
|
|
604
|
+
const data: TimeValue[] = [];
|
|
605
|
+
for (let i = 0; i < b.length; i++) {
|
|
606
|
+
const value = values[i];
|
|
607
|
+
if (value === undefined || !Number.isFinite(value)) continue; // PineScript plots +/-Infinity as na
|
|
608
|
+
data.push(perBar ? { time: b[i]!.time, value, color: perBar[i] } : { time: b[i]!.time, value });
|
|
609
|
+
}
|
|
610
|
+
c.plots[id] = data;
|
|
611
|
+
return { id, kind: 'plot' };
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
/**
|
|
615
|
+
* PineScript `plot()`, with the `plot.style_*` and `plot.linestyle_*` constants:
|
|
616
|
+
* `plot(src, 'Level', { style: plot.style_stepline, linestyle: plot.linestyle_dashed })`.
|
|
617
|
+
*/
|
|
618
|
+
export const plot = Object.assign(plotImpl, {
|
|
619
|
+
style_line: 'line',
|
|
620
|
+
style_linebr: 'linebr',
|
|
621
|
+
style_stepline: 'stepline',
|
|
622
|
+
style_steplinebr: 'steplinebr',
|
|
623
|
+
style_stepline_diamond: 'stepline_diamond',
|
|
624
|
+
style_histogram: 'histogram',
|
|
625
|
+
style_columns: 'columns',
|
|
626
|
+
style_area: 'area',
|
|
627
|
+
style_areabr: 'areabr',
|
|
628
|
+
style_circles: 'circles',
|
|
629
|
+
style_cross: 'cross',
|
|
630
|
+
linestyle_solid: 'solid',
|
|
631
|
+
linestyle_dotted: 'dotted',
|
|
632
|
+
linestyle_dashed: 'dashed',
|
|
633
|
+
} as const);
|
|
634
|
+
|
|
635
|
+
export interface ScriptHLineOptions {
|
|
636
|
+
color?: string;
|
|
637
|
+
/** Line style; default dashed (PineScript). */
|
|
638
|
+
linestyle?: HLineConfig['linestyle'];
|
|
639
|
+
linewidth?: number;
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
/** PineScript `hline()` — a static horizontal level. Default line style: dashed (PineScript). */
|
|
643
|
+
function hlineImpl(price: number, title?: string, options: ScriptHLineOptions = {}): PlotHandle {
|
|
644
|
+
const c = collector();
|
|
645
|
+
const id = `hline${c.hlineConfig.length}`;
|
|
646
|
+
c.hlineConfig.push({
|
|
647
|
+
id,
|
|
648
|
+
price,
|
|
649
|
+
title,
|
|
650
|
+
color: options.color,
|
|
651
|
+
linestyle: options.linestyle ?? 'dashed',
|
|
652
|
+
linewidth: options.linewidth,
|
|
653
|
+
});
|
|
654
|
+
return { id, kind: 'hline' };
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
/** PineScript `hline()`, with the `hline.style_*` constants: `hline(0, 'Zero', { linestyle: hline.style_dotted })`. */
|
|
658
|
+
export const hline = Object.assign(hlineImpl, {
|
|
659
|
+
style_solid: 'solid',
|
|
660
|
+
style_dotted: 'dotted',
|
|
661
|
+
style_dashed: 'dashed',
|
|
662
|
+
} as const);
|
|
663
|
+
|
|
664
|
+
export interface ScriptFillOptions {
|
|
665
|
+
/** Static color, or a per-bar color array from color.when(). */
|
|
666
|
+
color?: string | string[];
|
|
667
|
+
title?: string;
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
/** A fill colour argument: a colour, na (null / undefined), or a per-bar array from color.when(). */
|
|
671
|
+
export type ScriptFillColor = string | null | undefined | Array<string | null | undefined>;
|
|
672
|
+
|
|
673
|
+
/** A gradient value argument: a number or a Series. */
|
|
674
|
+
export type ScriptFillValue = number | Series;
|
|
675
|
+
|
|
676
|
+
function perBarValues(v: ScriptFillValue, n: number): number[] {
|
|
677
|
+
return v instanceof Series ? v.toArray().slice(0, n) : new Array<number>(n).fill(v);
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
function perBarColors(c: ScriptFillColor, n: number): Array<string | null> {
|
|
681
|
+
if (Array.isArray(c)) return Array.from({ length: n }, (_, i) => c[i] ?? null);
|
|
682
|
+
return new Array<string | null>(n).fill(c ?? null);
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
/**
|
|
686
|
+
* PineScript `fill()`, with the three PineScript overloads (positional arguments), for two plots or two hlines:
|
|
687
|
+
* - `fill(p1, p2, color?, title?, ...)`
|
|
688
|
+
* - `fill(p1, p2, top_value, bottom_value, top_color, bottom_color, title?, ...)`: gradient fill; values and colours
|
|
689
|
+
* can change per bar (a Series / a color.when() array)
|
|
690
|
+
* - `fill(p1, p2, { color, title })`: options object (oakscriptjs form, kept for existing scripts)
|
|
691
|
+
* Arguments that change only how the chart shows the fill (`editable`, `show_last`, `fillgaps`, `display`) are
|
|
692
|
+
* accepted and not used.
|
|
693
|
+
*/
|
|
694
|
+
export function fill(a: PlotHandle, b: PlotHandle, options?: ScriptFillOptions): void;
|
|
695
|
+
export function fill(a: PlotHandle, b: PlotHandle, color: ScriptFillColor, title?: string, ...rest: unknown[]): void;
|
|
696
|
+
export function fill(
|
|
697
|
+
a: PlotHandle,
|
|
698
|
+
b: PlotHandle,
|
|
699
|
+
top_value: ScriptFillValue,
|
|
700
|
+
bottom_value: ScriptFillValue,
|
|
701
|
+
top_color: ScriptFillColor,
|
|
702
|
+
bottom_color: ScriptFillColor,
|
|
703
|
+
title?: string,
|
|
704
|
+
...rest: unknown[]
|
|
705
|
+
): void;
|
|
706
|
+
export function fill(a: PlotHandle, b: PlotHandle, ...args: unknown[]): void {
|
|
707
|
+
const c = collector();
|
|
708
|
+
const third = args[0];
|
|
709
|
+
let perBar: string[] | undefined;
|
|
710
|
+
let staticColor: string | undefined;
|
|
711
|
+
let title: string | undefined;
|
|
712
|
+
let gradient: FillGradient | undefined;
|
|
713
|
+
if (typeof third === 'number' || third instanceof Series) {
|
|
714
|
+
const n = ctxBars.bars.length;
|
|
715
|
+
gradient = {
|
|
716
|
+
topValue: perBarValues(third, n),
|
|
717
|
+
bottomValue: perBarValues(args[1] as ScriptFillValue, n),
|
|
718
|
+
topColor: perBarColors(args[2] as ScriptFillColor, n),
|
|
719
|
+
bottomColor: perBarColors(args[3] as ScriptFillColor, n),
|
|
720
|
+
};
|
|
721
|
+
title = args[4] as string | undefined;
|
|
722
|
+
} else if (third !== null && typeof third === 'object' && !Array.isArray(third)) {
|
|
723
|
+
const options = third as ScriptFillOptions;
|
|
724
|
+
perBar = Array.isArray(options.color) ? options.color : undefined;
|
|
725
|
+
staticColor = typeof options.color === 'string' ? options.color : undefined;
|
|
726
|
+
title = options.title;
|
|
727
|
+
} else {
|
|
728
|
+
const color = third as ScriptFillColor;
|
|
729
|
+
perBar = Array.isArray(color) ? color.map((x) => x ?? '') : undefined;
|
|
730
|
+
staticColor = typeof color === 'string' ? color : undefined;
|
|
731
|
+
title = args[1] as string | undefined;
|
|
732
|
+
}
|
|
733
|
+
if (a.kind === 'hline' && b.kind === 'hline') {
|
|
734
|
+
c.fillConfig.push({
|
|
735
|
+
id: `fill${c.fillConfig.length}`,
|
|
736
|
+
plot1: a.id,
|
|
737
|
+
plot2: b.id,
|
|
738
|
+
color: staticColor,
|
|
739
|
+
colors: perBar,
|
|
740
|
+
gradient,
|
|
741
|
+
title,
|
|
742
|
+
});
|
|
743
|
+
return;
|
|
744
|
+
}
|
|
745
|
+
c.fills.push({
|
|
746
|
+
plot1: a.id,
|
|
747
|
+
plot2: b.id,
|
|
748
|
+
options: { color: staticColor, title },
|
|
749
|
+
colors: perBar,
|
|
750
|
+
gradient,
|
|
751
|
+
});
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
export interface PlotShapeOptions {
|
|
755
|
+
style?: ShapeStyle;
|
|
756
|
+
location?: MarkerLocation;
|
|
757
|
+
color?: string;
|
|
758
|
+
text?: string;
|
|
759
|
+
textcolor?: string;
|
|
760
|
+
size?: MarkerSize;
|
|
761
|
+
/** Display offset in bars (marker shown at bar i + offset). */
|
|
762
|
+
offset?: number;
|
|
763
|
+
/** Hover tooltip (extension over PineScript, for label.new-style ports). */
|
|
764
|
+
tooltip?: string;
|
|
765
|
+
/** Draw on the main chart pane even when the script is not an overlay (PineScript `force_overlay`); default false. */
|
|
766
|
+
force_overlay?: boolean;
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
export interface PlotCharOptions extends Omit<PlotShapeOptions, 'style'> {
|
|
770
|
+
char?: string;
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
/** Shared emitter for plotshape()/plotchar(). Emits one marker per bar where
|
|
774
|
+
* the condition is truthy (not na, not 0). */
|
|
775
|
+
function emitMarkers(
|
|
776
|
+
kind: 'shape' | 'char',
|
|
777
|
+
condition: Series,
|
|
778
|
+
title: string | undefined,
|
|
779
|
+
options: PlotShapeOptions & PlotCharOptions
|
|
780
|
+
): void {
|
|
781
|
+
const c = collector();
|
|
782
|
+
const id = `${kind}${c.shapeConfig.filter((s) => s.kind === kind).length}`;
|
|
783
|
+
const style: ShapeStyle | 'char' = kind === 'shape' ? (options.style ?? 'xcross') : 'char';
|
|
784
|
+
const char = kind === 'char' ? (options.char ?? '*') : undefined;
|
|
785
|
+
const location = options.location ?? 'abovebar';
|
|
786
|
+
c.shapeConfig.push({
|
|
787
|
+
id,
|
|
788
|
+
kind,
|
|
789
|
+
title,
|
|
790
|
+
style: kind === 'shape' ? (style as ShapeStyle) : undefined,
|
|
791
|
+
char,
|
|
792
|
+
location,
|
|
793
|
+
color: options.color,
|
|
794
|
+
text: options.text,
|
|
795
|
+
textcolor: options.textcolor,
|
|
796
|
+
size: options.size,
|
|
797
|
+
offset: options.offset,
|
|
798
|
+
forceOverlay: options.force_overlay,
|
|
799
|
+
});
|
|
800
|
+
const values = condition.toArray();
|
|
801
|
+
const b = bars();
|
|
802
|
+
const off = options.offset ?? 0;
|
|
803
|
+
for (let i = 0; i < b.length; i++) {
|
|
804
|
+
const v = values[i];
|
|
805
|
+
if (v === undefined || Number.isNaN(v) || v === 0) continue;
|
|
806
|
+
const j = i + off;
|
|
807
|
+
if (j < 0 || j >= b.length) continue;
|
|
808
|
+
c.markers.push({
|
|
809
|
+
time: b[j]!.time,
|
|
810
|
+
id,
|
|
811
|
+
location,
|
|
812
|
+
style,
|
|
813
|
+
char,
|
|
814
|
+
color: options.color,
|
|
815
|
+
text: options.text,
|
|
816
|
+
textcolor: options.textcolor,
|
|
817
|
+
size: options.size,
|
|
818
|
+
tooltip: options.tooltip,
|
|
819
|
+
price: location === 'absolute' ? v : undefined,
|
|
820
|
+
});
|
|
821
|
+
}
|
|
822
|
+
}
|
|
823
|
+
|
|
824
|
+
/** PineScript `plotshape()` — a marker on each bar where the condition holds. */
|
|
825
|
+
export function plotshape(condition: Series, title?: string, options: PlotShapeOptions = {}): void {
|
|
826
|
+
emitMarkers('shape', condition, title, options);
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
/** PineScript `plotchar()` — a character marker on each bar where the condition holds. */
|
|
830
|
+
export function plotchar(condition: Series, title?: string, options: PlotCharOptions = {}): void {
|
|
831
|
+
emitMarkers('char', condition, title, options);
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
export interface PlotArrowOptions {
|
|
835
|
+
/** Color of the up arrows: a color or a per-bar array (default #00FF00, PineScript). */
|
|
836
|
+
colorup?: string | Array<string | undefined>;
|
|
837
|
+
/** Color of the down arrows: a color or a per-bar array (default #FF0000, PineScript). */
|
|
838
|
+
colordown?: string | Array<string | undefined>;
|
|
839
|
+
/** Display offset in bars (arrow shown at bar i + offset). */
|
|
840
|
+
offset?: number;
|
|
841
|
+
/** Minimal arrow height in pixels (default 5). */
|
|
842
|
+
minheight?: number;
|
|
843
|
+
/** Maximal arrow height in pixels (default 100). */
|
|
844
|
+
maxheight?: number;
|
|
845
|
+
display?: ArrowConfig['display'];
|
|
846
|
+
/** Draw on the main chart pane even when the script is not an overlay (PineScript `force_overlay`); default false. */
|
|
847
|
+
force_overlay?: boolean;
|
|
848
|
+
}
|
|
849
|
+
|
|
850
|
+
/**
|
|
851
|
+
* PineScript `plotarrow()` — an up arrow below the bar for a positive value, a down arrow above the bar for a
|
|
852
|
+
* negative value, nothing for 0 / na. The arrows go to `result.arrows` (value and color per bar), the declaration to
|
|
853
|
+
* `arrowConfig`; the renderer scales the height with the absolute value between minheight and maxheight.
|
|
854
|
+
*/
|
|
855
|
+
export function plotarrow(series: Series, title?: string, options: PlotArrowOptions = {}): void {
|
|
856
|
+
const c = collector();
|
|
857
|
+
const id = `arrow${c.arrowConfig.length}`;
|
|
858
|
+
const pick = (col: string | Array<string | undefined> | undefined, fallback: string, i: number): string | undefined =>
|
|
859
|
+
col === undefined ? fallback : typeof col === 'string' ? col : col[i];
|
|
860
|
+
const colorup = typeof options.colorup === 'string' ? options.colorup : '#00FF00';
|
|
861
|
+
const colordown = typeof options.colordown === 'string' ? options.colordown : '#FF0000';
|
|
862
|
+
c.arrowConfig.push({
|
|
863
|
+
id,
|
|
864
|
+
title,
|
|
865
|
+
colorup,
|
|
866
|
+
colordown,
|
|
867
|
+
minheight: options.minheight ?? 5,
|
|
868
|
+
maxheight: options.maxheight ?? 100,
|
|
869
|
+
offset: options.offset,
|
|
870
|
+
display: options.display,
|
|
871
|
+
forceOverlay: options.force_overlay,
|
|
872
|
+
});
|
|
873
|
+
const values = series.toArray();
|
|
874
|
+
const b = bars();
|
|
875
|
+
const off = options.offset ?? 0;
|
|
876
|
+
for (let i = 0; i < b.length; i++) {
|
|
877
|
+
const v = values[i];
|
|
878
|
+
if (v === undefined || Number.isNaN(v) || v === 0) continue;
|
|
879
|
+
const j = i + off;
|
|
880
|
+
if (j < 0 || j >= b.length) continue;
|
|
881
|
+
const color = v > 0 ? pick(options.colorup, '#00FF00', i) : pick(options.colordown, '#FF0000', i);
|
|
882
|
+
if (color === undefined) continue; // na color: no arrow
|
|
883
|
+
c.arrows.push({ time: b[j]!.time, id, value: v, color });
|
|
884
|
+
}
|
|
885
|
+
}
|
|
886
|
+
|
|
887
|
+
/** Shared emitter for bgcolor()/barcolor(). A static color applies to every
|
|
888
|
+
* bar; a per-bar array (from color.when) skips undefined entries. The offset
|
|
889
|
+
* is baked in: the color computed at bar i lands on bar i + offset. */
|
|
890
|
+
function emitBarColors(
|
|
891
|
+
kind: 'bgcolor' | 'barcolor',
|
|
892
|
+
colors: string | Array<string | undefined>,
|
|
893
|
+
options: { offset?: number; title?: string; force_overlay?: boolean }
|
|
894
|
+
): void {
|
|
895
|
+
const c = collector();
|
|
896
|
+
const id = `${kind}${c.barColorConfig.filter((x) => x.kind === kind).length}`;
|
|
897
|
+
c.barColorConfig.push({ id, kind, title: options.title, offset: options.offset, forceOverlay: options.force_overlay });
|
|
898
|
+
const b = bars();
|
|
899
|
+
const off = options.offset ?? 0;
|
|
900
|
+
const target = kind === 'bgcolor' ? c.bgcolors : c.barcolors;
|
|
901
|
+
for (let i = 0; i < b.length; i++) {
|
|
902
|
+
const col = typeof colors === 'string' ? colors : colors[i];
|
|
903
|
+
if (!col) continue;
|
|
904
|
+
const j = i + off;
|
|
905
|
+
if (j < 0 || j >= b.length) continue;
|
|
906
|
+
target.push({ time: b[j]!.time, color: col });
|
|
907
|
+
}
|
|
908
|
+
}
|
|
909
|
+
|
|
910
|
+
/**
|
|
911
|
+
* PineScript `bgcolor()` — per-bar background color. `force_overlay: true` colors the main chart pane even when the
|
|
912
|
+
* script is not an overlay.
|
|
913
|
+
*/
|
|
914
|
+
export function bgcolor(
|
|
915
|
+
colors: string | Array<string | undefined>,
|
|
916
|
+
options: { offset?: number; title?: string; force_overlay?: boolean } = {}
|
|
917
|
+
): void {
|
|
918
|
+
emitBarColors('bgcolor', colors, options);
|
|
919
|
+
}
|
|
920
|
+
|
|
921
|
+
/** PineScript `barcolor()` — per-bar candle color override. */
|
|
922
|
+
export function barcolor(
|
|
923
|
+
colors: string | Array<string | undefined>,
|
|
924
|
+
options: { title?: string } = {}
|
|
925
|
+
): void {
|
|
926
|
+
emitBarColors('barcolor', colors, options);
|
|
927
|
+
}
|
|
928
|
+
|
|
929
|
+
// ── Per-bar execution (eachBar) ──────────────────────────────────────────────
|
|
930
|
+
|
|
931
|
+
/**
|
|
932
|
+
* The scalar view of one bar handed to an {@link eachBar} callback.
|
|
933
|
+
* All fields are plain numbers, so native JS operators work.
|
|
934
|
+
*/
|
|
935
|
+
export interface BarContext {
|
|
936
|
+
/** Bar index (PineScript bar_index). */
|
|
937
|
+
readonly i: number;
|
|
938
|
+
/** Total number of bars. */
|
|
939
|
+
readonly n: number;
|
|
940
|
+
/** Time of the current bar. */
|
|
941
|
+
readonly time: number;
|
|
942
|
+
readonly open: number;
|
|
943
|
+
readonly high: number;
|
|
944
|
+
readonly low: number;
|
|
945
|
+
readonly close: number;
|
|
946
|
+
readonly volume: number;
|
|
947
|
+
/** Value of any Series at the current bar minus `offset` (PineScript src[offset]). */
|
|
948
|
+
get(src: Series, offset?: number): number;
|
|
949
|
+
/** This block's own previous output (PineScript self-reference, offset >= 1). */
|
|
950
|
+
prev(offset?: number): number;
|
|
951
|
+
}
|
|
952
|
+
|
|
953
|
+
/**
|
|
954
|
+
* Runs the callback once per bar, in order, and collects the returned values
|
|
955
|
+
* into a Series. This is the escape hatch for PineScript logic the vectorized
|
|
956
|
+
* model cannot express:
|
|
957
|
+
*
|
|
958
|
+
* - `var x = 0` / `x := ...` → a plain `let` variable captured by the closure
|
|
959
|
+
* - per-bar `if` / `for` / `while` → native JS statements
|
|
960
|
+
* - `src[k]` → `c.get(src, k)`; self-reference → `c.prev()`
|
|
961
|
+
*
|
|
962
|
+
* Returning a boolean collects 1/0; returning nothing collects na (NaN).
|
|
963
|
+
*
|
|
964
|
+
* ```typescript
|
|
965
|
+
* let dir = 0; // var dir = 0
|
|
966
|
+
* const trend = eachBar((c) => {
|
|
967
|
+
* if (c.close > c.get(close, 1)) dir = 1; // dir := 1
|
|
968
|
+
* else if (c.close < c.get(close, 1)) dir = -1;
|
|
969
|
+
* return dir;
|
|
970
|
+
* });
|
|
971
|
+
* ```
|
|
972
|
+
*/
|
|
973
|
+
export function eachBar(fn: (c: BarContext) => number | boolean | void): Series {
|
|
974
|
+
collector(); // same lifecycle guard (and error message) as the declaration calls
|
|
975
|
+
const b = bars();
|
|
976
|
+
const out: number[] = new Array(b.length).fill(NaN);
|
|
977
|
+
const cache = new Map<Series, number[]>();
|
|
978
|
+
let idx = 0;
|
|
979
|
+
const ctx: BarContext = {
|
|
980
|
+
get i() {
|
|
981
|
+
return idx;
|
|
982
|
+
},
|
|
983
|
+
n: b.length,
|
|
984
|
+
get time() {
|
|
985
|
+
return b[idx]!.time as number;
|
|
986
|
+
},
|
|
987
|
+
get open() {
|
|
988
|
+
return b[idx]!.open;
|
|
989
|
+
},
|
|
990
|
+
get high() {
|
|
991
|
+
return b[idx]!.high;
|
|
992
|
+
},
|
|
993
|
+
get low() {
|
|
994
|
+
return b[idx]!.low;
|
|
995
|
+
},
|
|
996
|
+
get close() {
|
|
997
|
+
return b[idx]!.close;
|
|
998
|
+
},
|
|
999
|
+
get volume() {
|
|
1000
|
+
return b[idx]!.volume ?? NaN;
|
|
1001
|
+
},
|
|
1002
|
+
get(src: Series, offset: number = 0): number {
|
|
1003
|
+
if (offset < 0) throw new RangeError(`c.get(): the offset cannot be negative (${offset} would read a future bar).`);
|
|
1004
|
+
let vals = cache.get(src);
|
|
1005
|
+
if (!vals) {
|
|
1006
|
+
vals = src.toArray();
|
|
1007
|
+
cache.set(src, vals);
|
|
1008
|
+
}
|
|
1009
|
+
const j = idx - offset;
|
|
1010
|
+
return j >= 0 && j < vals.length ? (vals[j] ?? NaN) : NaN;
|
|
1011
|
+
},
|
|
1012
|
+
prev(offset: number = 1): number {
|
|
1013
|
+
const j = idx - Math.max(1, offset);
|
|
1014
|
+
return j >= 0 ? out[j]! : NaN;
|
|
1015
|
+
},
|
|
1016
|
+
};
|
|
1017
|
+
try {
|
|
1018
|
+
for (idx = 0; idx < b.length; idx++) {
|
|
1019
|
+
currentBar = idx;
|
|
1020
|
+
const r = fn(ctx);
|
|
1021
|
+
out[idx] = typeof r === 'number' ? r : typeof r === 'boolean' ? (r ? 1 : 0) : NaN;
|
|
1022
|
+
}
|
|
1023
|
+
} finally {
|
|
1024
|
+
currentBar = -1;
|
|
1025
|
+
}
|
|
1026
|
+
return Series.fromArray(ctxBars, out);
|
|
1027
|
+
}
|
|
1028
|
+
|
|
1029
|
+
/** Bar index of the running eachBar() callback, -1 outside eachBar(). */
|
|
1030
|
+
let currentBar = -1;
|
|
1031
|
+
|
|
1032
|
+
// ── Strategy (declaration and strategy.* API; the host supplies the order engine) ──
|
|
1033
|
+
|
|
1034
|
+
/** The strategy of the current run; throws when the script did not declare strategy(). */
|
|
1035
|
+
function strategyRun(caller: string): StrategyRun {
|
|
1036
|
+
const run = collector().strategy;
|
|
1037
|
+
if (!run) throw new Error(`${caller}: declare the script with strategy() first.`);
|
|
1038
|
+
return run;
|
|
1039
|
+
}
|
|
1040
|
+
|
|
1041
|
+
function strategyEngine(caller: string): StrategyEngine {
|
|
1042
|
+
const run = strategyRun(caller);
|
|
1043
|
+
if (!run.engine) {
|
|
1044
|
+
throw new Error(
|
|
1045
|
+
`${caller}: no strategy engine; pass it as executeScript(body, bars, inputs, chart, { strategyEngine }).`
|
|
1046
|
+
);
|
|
1047
|
+
}
|
|
1048
|
+
return run.engine;
|
|
1049
|
+
}
|
|
1050
|
+
|
|
1051
|
+
/** The engine for an order call: order calls run inside strategy.eachBar(), at a bar close. */
|
|
1052
|
+
function orderEngine(caller: string): StrategyEngine {
|
|
1053
|
+
const engine = strategyEngine(caller);
|
|
1054
|
+
if (!strategyRun(caller).inLoop) throw new Error(`${caller} must be called inside strategy.eachBar().`);
|
|
1055
|
+
return engine;
|
|
1056
|
+
}
|
|
1057
|
+
|
|
1058
|
+
/** Options without na (NaN) numbers and undefined values: in PineScript an na argument means "not given". */
|
|
1059
|
+
function given<T extends object>(options: T): T {
|
|
1060
|
+
const out: Record<string, unknown> = {};
|
|
1061
|
+
for (const [key, value] of Object.entries(options)) {
|
|
1062
|
+
if (value === undefined || (typeof value === 'number' && Number.isNaN(value))) continue;
|
|
1063
|
+
out[key] = value;
|
|
1064
|
+
}
|
|
1065
|
+
return out as T;
|
|
1066
|
+
}
|
|
1067
|
+
|
|
1068
|
+
/** The engine for a `strategy.*` read: the values are per bar, so they are read inside strategy.eachBar(). */
|
|
1069
|
+
function readEngine(caller: string): StrategyEngine {
|
|
1070
|
+
const engine = strategyEngine(caller);
|
|
1071
|
+
if (!strategyRun(caller).inLoop) throw new Error(`${caller} must be read inside strategy.eachBar().`);
|
|
1072
|
+
return engine;
|
|
1073
|
+
}
|
|
1074
|
+
|
|
1075
|
+
/** `strategy.*` variables that do not change from bar to bar, readable anywhere in the script. */
|
|
1076
|
+
const STRATEGY_CONSTANT_VARIABLES: ReadonlySet<StrategyVariable> = new Set(['initial_capital', 'account_currency']);
|
|
1077
|
+
|
|
1078
|
+
function strategyVariable(name: StrategyVariable): number | string {
|
|
1079
|
+
const caller = `strategy.${name}`;
|
|
1080
|
+
const engine = STRATEGY_CONSTANT_VARIABLES.has(name) ? strategyEngine(caller) : readEngine(caller);
|
|
1081
|
+
const value = engine.get(name);
|
|
1082
|
+
if (value === undefined) throw new Error(`strategy.${name} is not provided by the strategy engine.`);
|
|
1083
|
+
return value;
|
|
1084
|
+
}
|
|
1085
|
+
|
|
1086
|
+
function riskRule(rule: StrategyRiskRule): void {
|
|
1087
|
+
const engine = strategyEngine(`strategy.risk.${rule.rule}()`);
|
|
1088
|
+
if (!engine.risk) throw new Error(`strategy.risk.${rule.rule}() is not provided by the strategy engine.`);
|
|
1089
|
+
engine.risk(rule);
|
|
1090
|
+
}
|
|
1091
|
+
|
|
1092
|
+
/** PineScript `strategy()` declaration: script metadata and strategy properties. */
|
|
1093
|
+
function declareStrategy(title: string, options: StrategyOptions = {}): void {
|
|
1094
|
+
const c = collector();
|
|
1095
|
+
declare(c, 'strategy');
|
|
1096
|
+
c.title = title;
|
|
1097
|
+
c.shortTitle = options.shorttitle;
|
|
1098
|
+
c.overlay = options.overlay ?? false;
|
|
1099
|
+
c.precision = options.precision;
|
|
1100
|
+
c.format = options.format;
|
|
1101
|
+
setDrawingLimits({
|
|
1102
|
+
line: options.max_lines_count,
|
|
1103
|
+
label: options.max_labels_count,
|
|
1104
|
+
box: options.max_boxes_count,
|
|
1105
|
+
polyline: options.max_polylines_count,
|
|
1106
|
+
});
|
|
1107
|
+
const properties: StrategyProperties = { ...STRATEGY_DEFAULTS };
|
|
1108
|
+
const target = properties as unknown as Record<string, unknown>;
|
|
1109
|
+
for (const key of [...Object.keys(STRATEGY_DEFAULTS), 'currency']) {
|
|
1110
|
+
const value = (options as Record<string, unknown>)[key];
|
|
1111
|
+
if (value !== undefined) target[key] = value;
|
|
1112
|
+
}
|
|
1113
|
+
const factory = scriptOptions.strategyEngine;
|
|
1114
|
+
c.strategy = {
|
|
1115
|
+
properties,
|
|
1116
|
+
engine: factory?.({ properties, bars: ctxBars.bars, chart: chartCtx }),
|
|
1117
|
+
looped: false,
|
|
1118
|
+
inLoop: false,
|
|
1119
|
+
};
|
|
1120
|
+
}
|
|
1121
|
+
|
|
1122
|
+
/**
|
|
1123
|
+
* Runs the strategy logic once per bar, in order, like {@link eachBar}. On each bar the engine first
|
|
1124
|
+
* fills the pending orders (`processBar`), then the callback runs at the bar close (order calls,
|
|
1125
|
+
* `strategy.*` variables), then `processClose` runs. A script has one strategy.eachBar() per run.
|
|
1126
|
+
* The callback's return values are collected into a Series, as with eachBar().
|
|
1127
|
+
*/
|
|
1128
|
+
function strategyEachBar(fn: (c: BarContext) => number | boolean | void): Series {
|
|
1129
|
+
const run = strategyRun('strategy.eachBar()');
|
|
1130
|
+
const engine = strategyEngine('strategy.eachBar()');
|
|
1131
|
+
if (run.looped) throw new Error('strategy.eachBar() runs once per script run.');
|
|
1132
|
+
run.looped = true;
|
|
1133
|
+
return eachBar((c) => {
|
|
1134
|
+
engine.processBar(c.i);
|
|
1135
|
+
run.inLoop = true;
|
|
1136
|
+
try {
|
|
1137
|
+
return fn(c);
|
|
1138
|
+
} finally {
|
|
1139
|
+
run.inLoop = false;
|
|
1140
|
+
engine.processClose(c.i);
|
|
1141
|
+
}
|
|
1142
|
+
});
|
|
1143
|
+
}
|
|
1144
|
+
|
|
1145
|
+
/** Numeric `strategy.*` variables, read from the engine (current bar inside strategy.eachBar()). */
|
|
1146
|
+
export type StrategyNumberVariables = { readonly [K in StrategyNumberVariable]: number };
|
|
1147
|
+
/** Text `strategy.*` variables, read from the engine. */
|
|
1148
|
+
export type StrategyTextVariables = { readonly [K in StrategyTextVariable]: string };
|
|
1149
|
+
|
|
1150
|
+
/** PineScript `strategy`: the `strategy()` declaration and the `strategy.*` namespace. */
|
|
1151
|
+
export type StrategyApi = ((title: string, options?: StrategyOptions) => void) &
|
|
1152
|
+
typeof STRATEGY_CONSTANTS &
|
|
1153
|
+
StrategyNumberVariables &
|
|
1154
|
+
StrategyTextVariables & {
|
|
1155
|
+
eachBar: typeof strategyEachBar;
|
|
1156
|
+
entry(id: string, direction: StrategyDirection, options?: StrategyEntryOptions): void;
|
|
1157
|
+
order(id: string, direction: StrategyDirection, options?: StrategyEntryOptions): void;
|
|
1158
|
+
exit(id: string, options?: StrategyExitOptions): void;
|
|
1159
|
+
close(id: string, options?: StrategyCloseOptions): void;
|
|
1160
|
+
close_all(options?: StrategyCloseAllOptions): void;
|
|
1161
|
+
cancel(id: string): void;
|
|
1162
|
+
cancel_all(): void;
|
|
1163
|
+
/** PineScript `strategy.opentrades.*(index)`: open trade `index` (0 = oldest), undefined when it does not exist. */
|
|
1164
|
+
opentrade(index: number): StrategyTrade | undefined;
|
|
1165
|
+
/** PineScript `strategy.closedtrades.*(index)`: closed trade `index` (0 = oldest), undefined when it does not exist. */
|
|
1166
|
+
closedtrade(index: number): StrategyTrade | undefined;
|
|
1167
|
+
default_entry_qty(fill_price: number): number;
|
|
1168
|
+
risk: {
|
|
1169
|
+
allow_entry_in(direction: StrategyRiskDirection): void;
|
|
1170
|
+
max_cons_loss_days(count: number, alert_message?: string): void;
|
|
1171
|
+
max_drawdown(value: number, type: StrategyRiskValueType, alert_message?: string): void;
|
|
1172
|
+
max_intraday_filled_orders(count: number, alert_message?: string): void;
|
|
1173
|
+
max_intraday_loss(value: number, type: StrategyRiskValueType, alert_message?: string): void;
|
|
1174
|
+
max_position_size(contracts: number): void;
|
|
1175
|
+
};
|
|
1176
|
+
};
|
|
1177
|
+
|
|
1178
|
+
const strategyMembers = {
|
|
1179
|
+
...STRATEGY_CONSTANTS,
|
|
1180
|
+
eachBar: strategyEachBar,
|
|
1181
|
+
entry(id: string, direction: StrategyDirection, options: StrategyEntryOptions = {}): void {
|
|
1182
|
+
orderEngine('strategy.entry()').entry(id, direction, given(options));
|
|
1183
|
+
},
|
|
1184
|
+
order(id: string, direction: StrategyDirection, options: StrategyEntryOptions = {}): void {
|
|
1185
|
+
orderEngine('strategy.order()').order(id, direction, given(options));
|
|
1186
|
+
},
|
|
1187
|
+
exit(id: string, options: StrategyExitOptions = {}): void {
|
|
1188
|
+
orderEngine('strategy.exit()').exit(id, given(options));
|
|
1189
|
+
},
|
|
1190
|
+
close(id: string, options: StrategyCloseOptions = {}): void {
|
|
1191
|
+
orderEngine('strategy.close()').close(id, given(options));
|
|
1192
|
+
},
|
|
1193
|
+
close_all(options: StrategyCloseAllOptions = {}): void {
|
|
1194
|
+
orderEngine('strategy.close_all()').close_all(given(options));
|
|
1195
|
+
},
|
|
1196
|
+
cancel(id: string): void {
|
|
1197
|
+
orderEngine('strategy.cancel()').cancel(id);
|
|
1198
|
+
},
|
|
1199
|
+
cancel_all(): void {
|
|
1200
|
+
orderEngine('strategy.cancel_all()').cancel_all();
|
|
1201
|
+
},
|
|
1202
|
+
opentrade(index: number): StrategyTrade | undefined {
|
|
1203
|
+
return readEngine('strategy.opentrade()').openTrade(index);
|
|
1204
|
+
},
|
|
1205
|
+
closedtrade(index: number): StrategyTrade | undefined {
|
|
1206
|
+
return readEngine('strategy.closedtrade()').closedTrade(index);
|
|
1207
|
+
},
|
|
1208
|
+
default_entry_qty(fill_price: number): number {
|
|
1209
|
+
const engine = readEngine('strategy.default_entry_qty()');
|
|
1210
|
+
if (!engine.defaultEntryQty) throw new Error('strategy.default_entry_qty() is not provided by the strategy engine.');
|
|
1211
|
+
return engine.defaultEntryQty(fill_price);
|
|
1212
|
+
},
|
|
1213
|
+
risk: {
|
|
1214
|
+
allow_entry_in: (direction: StrategyRiskDirection) => riskRule({ rule: 'allow_entry_in', direction }),
|
|
1215
|
+
max_cons_loss_days: (count: number, alert_message?: string) =>
|
|
1216
|
+
riskRule(given({ rule: 'max_cons_loss_days' as const, count, alert_message })),
|
|
1217
|
+
max_drawdown: (value: number, type: StrategyRiskValueType, alert_message?: string) =>
|
|
1218
|
+
riskRule(given({ rule: 'max_drawdown' as const, value, type, alert_message })),
|
|
1219
|
+
max_intraday_filled_orders: (count: number, alert_message?: string) =>
|
|
1220
|
+
riskRule(given({ rule: 'max_intraday_filled_orders' as const, count, alert_message })),
|
|
1221
|
+
max_intraday_loss: (value: number, type: StrategyRiskValueType, alert_message?: string) =>
|
|
1222
|
+
riskRule(given({ rule: 'max_intraday_loss' as const, value, type, alert_message })),
|
|
1223
|
+
max_position_size: (contracts: number) => riskRule({ rule: 'max_position_size', contracts }),
|
|
1224
|
+
},
|
|
1225
|
+
};
|
|
1226
|
+
|
|
1227
|
+
const variableGetters: PropertyDescriptorMap = {};
|
|
1228
|
+
for (const name of [...STRATEGY_NUMBER_VARIABLES, ...STRATEGY_TEXT_VARIABLES]) {
|
|
1229
|
+
variableGetters[name] = { get: () => strategyVariable(name), enumerable: true };
|
|
1230
|
+
}
|
|
1231
|
+
|
|
1232
|
+
/**
|
|
1233
|
+
* PineScript `strategy`: call it to declare a strategy, and use its members as the `strategy.*` namespace.
|
|
1234
|
+
* OakScriptJS does not fill orders: the host passes an engine in `executeScript(..., { strategyEngine })`,
|
|
1235
|
+
* and the order calls and `strategy.*` variables are forwarded to it.
|
|
1236
|
+
*
|
|
1237
|
+
* ```typescript
|
|
1238
|
+
* strategy('MA Cross', { initial_capital: 10000, default_qty_type: strategy.percent_of_equity, default_qty_value: 100 });
|
|
1239
|
+
* const up = ta.crossover(ta.sma(close, 9), ta.sma(close, 21));
|
|
1240
|
+
* strategy.eachBar((c) => {
|
|
1241
|
+
* if (c.get(up) && strategy.position_size === 0) {
|
|
1242
|
+
* strategy.entry('L', strategy.long);
|
|
1243
|
+
* strategy.exit('X', { from_entry: 'L', stop: c.close * 0.95, limit: c.close * 1.15 });
|
|
1244
|
+
* }
|
|
1245
|
+
* });
|
|
1246
|
+
* ```
|
|
1247
|
+
*/
|
|
1248
|
+
export const strategy = Object.defineProperties(
|
|
1249
|
+
Object.assign((title: string, options?: StrategyOptions) => declareStrategy(title, options), strategyMembers),
|
|
1250
|
+
variableGetters
|
|
1251
|
+
) as StrategyApi;
|
|
1252
|
+
|
|
1253
|
+
// ── Chart context (issue #100) ───────────────────────────────────────────────
|
|
1254
|
+
|
|
1255
|
+
/**
|
|
1256
|
+
* What the caller knows about the loaded bars. PineScript reads these values from the chart and
|
|
1257
|
+
* the symbol; nothing is fetched here. A script that needs a value the caller did not give throws.
|
|
1258
|
+
*/
|
|
1259
|
+
export interface ChartContext {
|
|
1260
|
+
/** Chart timeframe: "5", "60", "1D", "1W"... (PineScript `timeframe.period`) */
|
|
1261
|
+
timeframe?: string;
|
|
1262
|
+
/** Exchange time zone: "America/New_York", "Etc/UTC"... (PineScript `syminfo.timezone`) */
|
|
1263
|
+
timezone?: string;
|
|
1264
|
+
/** Session type of the bars (PineScript `syminfo.session`); default "regular" */
|
|
1265
|
+
sessionType?: 'regular' | 'extended';
|
|
1266
|
+
/**
|
|
1267
|
+
* Session of the loaded bars in the symbol session format: hours ("0930-1600", "1700-1600", "24x7"),
|
|
1268
|
+
* corrections (early closes, days off) and holidays. Needed by `time(tf)`, `time_close`,
|
|
1269
|
+
* `timeframe.change`, `time_tradingday`, `session.*` and the default anchor of `ta.vwap`.
|
|
1270
|
+
*/
|
|
1271
|
+
session?: SessionSpec | string;
|
|
1272
|
+
/** Regular trading hours when the bars include extended hours (default: `session`); holidays default to `session`'s */
|
|
1273
|
+
regularSession?: SessionSpec | string;
|
|
1274
|
+
/** Chart symbol as PineScript `syminfo.tickerid` (e.g. "NASDAQ:AAPL"); needed by `request.security(syminfo.tickerid, ...)` */
|
|
1275
|
+
tickerid?: string;
|
|
1276
|
+
/** Unit of `Bar.time`: 's' (default, as lightweight-charts) or 'ms'. PineScript times are in ms. */
|
|
1277
|
+
timeUnit?: 's' | 'ms';
|
|
1278
|
+
/** Minimum price move of the symbol (PineScript `syminfo.mintick`), e.g. 0.01; used by `math.round_to_mintick` */
|
|
1279
|
+
mintick?: number;
|
|
1280
|
+
/** Currency value of one point of price move for one contract (PineScript `syminfo.pointvalue`); 1 for stocks */
|
|
1281
|
+
pointvalue?: number;
|
|
1282
|
+
/** Smallest tradable quantity (PineScript `syminfo.mincontract`); 1 for whole shares */
|
|
1283
|
+
mincontract?: number;
|
|
1284
|
+
/**
|
|
1285
|
+
* The last bar is closed (`barstate.isconfirmed` on the last bar). Default false: PineScript treats the last bar
|
|
1286
|
+
* of the data as not confirmed until the data feed closes it.
|
|
1287
|
+
*/
|
|
1288
|
+
lastBarConfirmed?: boolean;
|
|
1289
|
+
/** The last bar receives live updates (`barstate.isrealtime`); default false (a calculation on history). */
|
|
1290
|
+
realtime?: boolean;
|
|
1291
|
+
/** On a realtime last bar: this run is the first update of the bar (`barstate.isnew`); default true. */
|
|
1292
|
+
lastBarNew?: boolean;
|
|
1293
|
+
}
|
|
1294
|
+
|
|
1295
|
+
let chartCtx: ChartContext = {};
|
|
1296
|
+
|
|
1297
|
+
function chartTimeframe(): timeframeCore.TimeframeInfo {
|
|
1298
|
+
collector();
|
|
1299
|
+
if (!chartCtx.timeframe) {
|
|
1300
|
+
throw new Error('The chart timeframe is not known: pass it as executeScript(body, bars, inputs, { timeframe }).');
|
|
1301
|
+
}
|
|
1302
|
+
return timeframeCore.info(chartCtx.timeframe);
|
|
1303
|
+
}
|
|
1304
|
+
|
|
1305
|
+
function exchangeTimezone(): string {
|
|
1306
|
+
collector();
|
|
1307
|
+
if (!chartCtx.timezone) {
|
|
1308
|
+
throw new Error('The exchange time zone is not known: pass it as executeScript(body, bars, inputs, { timezone }).');
|
|
1309
|
+
}
|
|
1310
|
+
return chartCtx.timezone;
|
|
1311
|
+
}
|
|
1312
|
+
|
|
1313
|
+
/**
|
|
1314
|
+
* PineScript `timeframe.*`: the chart timeframe variables (from the chart context), and
|
|
1315
|
+
* `in_seconds(timeframe?)`, `from_seconds(seconds)`.
|
|
1316
|
+
*/
|
|
1317
|
+
export const timeframe = {
|
|
1318
|
+
...timeframeCore,
|
|
1319
|
+
get period(): string {
|
|
1320
|
+
return chartTimeframe().period;
|
|
1321
|
+
},
|
|
1322
|
+
get main_period(): string {
|
|
1323
|
+
return chartTimeframe().period;
|
|
1324
|
+
},
|
|
1325
|
+
get multiplier(): number {
|
|
1326
|
+
return chartTimeframe().multiplier;
|
|
1327
|
+
},
|
|
1328
|
+
get isintraday(): boolean {
|
|
1329
|
+
return chartTimeframe().isintraday;
|
|
1330
|
+
},
|
|
1331
|
+
get isdaily(): boolean {
|
|
1332
|
+
return chartTimeframe().isdaily;
|
|
1333
|
+
},
|
|
1334
|
+
get isweekly(): boolean {
|
|
1335
|
+
return chartTimeframe().isweekly;
|
|
1336
|
+
},
|
|
1337
|
+
get ismonthly(): boolean {
|
|
1338
|
+
return chartTimeframe().ismonthly;
|
|
1339
|
+
},
|
|
1340
|
+
get isminutes(): boolean {
|
|
1341
|
+
return chartTimeframe().isminutes;
|
|
1342
|
+
},
|
|
1343
|
+
get isseconds(): boolean {
|
|
1344
|
+
return chartTimeframe().isseconds;
|
|
1345
|
+
},
|
|
1346
|
+
get isticks(): boolean {
|
|
1347
|
+
return chartTimeframe().isticks;
|
|
1348
|
+
},
|
|
1349
|
+
get isdwm(): boolean {
|
|
1350
|
+
return chartTimeframe().isdwm;
|
|
1351
|
+
},
|
|
1352
|
+
/** PineScript `timeframe.change(tf)`: 1 on bars that open a new `tf` period (0 on the first bar). */
|
|
1353
|
+
change(tf: string): Series {
|
|
1354
|
+
return changeOf(tf || chartTimeframe().period);
|
|
1355
|
+
},
|
|
1356
|
+
/** Seconds in `timeframe`; without it (or with "") the chart timeframe, as in PineScript. */
|
|
1357
|
+
in_seconds(tf?: string): number {
|
|
1358
|
+
return timeframeCore.in_seconds(tf === undefined || tf === '' ? chartTimeframe().period : tf);
|
|
1359
|
+
},
|
|
1360
|
+
};
|
|
1361
|
+
|
|
1362
|
+
/** A symbol property that the host passes in the chart context. */
|
|
1363
|
+
function symbolProperty(key: 'mintick' | 'pointvalue' | 'mincontract'): number {
|
|
1364
|
+
collector();
|
|
1365
|
+
const value = chartCtx[key];
|
|
1366
|
+
if (value === undefined) {
|
|
1367
|
+
throw new Error(`syminfo.${key} is not known: pass it as executeScript(body, bars, inputs, { ${key} }).`);
|
|
1368
|
+
}
|
|
1369
|
+
return value;
|
|
1370
|
+
}
|
|
1371
|
+
|
|
1372
|
+
/**
|
|
1373
|
+
* PineScript `syminfo.timezone`, `syminfo.tickerid`, `syminfo.session` (session type), `syminfo.mintick`,
|
|
1374
|
+
* `syminfo.pointvalue` and `syminfo.mincontract`, from the chart context.
|
|
1375
|
+
*/
|
|
1376
|
+
export const syminfo = {
|
|
1377
|
+
get mintick(): number {
|
|
1378
|
+
return symbolProperty('mintick');
|
|
1379
|
+
},
|
|
1380
|
+
get pointvalue(): number {
|
|
1381
|
+
return symbolProperty('pointvalue');
|
|
1382
|
+
},
|
|
1383
|
+
get mincontract(): number {
|
|
1384
|
+
return symbolProperty('mincontract');
|
|
1385
|
+
},
|
|
1386
|
+
get timezone(): string {
|
|
1387
|
+
return exchangeTimezone();
|
|
1388
|
+
},
|
|
1389
|
+
get tickerid(): string {
|
|
1390
|
+
collector();
|
|
1391
|
+
if (!chartCtx.tickerid) {
|
|
1392
|
+
throw new Error('The chart symbol is not known: pass it as executeScript(body, bars, inputs, { tickerid }).');
|
|
1393
|
+
}
|
|
1394
|
+
return chartCtx.tickerid;
|
|
1395
|
+
},
|
|
1396
|
+
get session(): string {
|
|
1397
|
+
collector();
|
|
1398
|
+
return chartCtx.sessionType ?? 'regular';
|
|
1399
|
+
},
|
|
1400
|
+
};
|
|
1401
|
+
|
|
1402
|
+
/** A calendar function: `(time, timezone?)` on a UNIX time or on a Series of times. */
|
|
1403
|
+
interface CalendarFunction {
|
|
1404
|
+
(time: number, timezone?: string): number;
|
|
1405
|
+
(time: Series, timezone?: string): Series;
|
|
1406
|
+
}
|
|
1407
|
+
|
|
1408
|
+
function calendar(fn: (time: number, timezone: string) => number): CalendarFunction {
|
|
1409
|
+
return ((time: number | Series, timezone?: string) => {
|
|
1410
|
+
const tz = timezone ?? exchangeTimezone();
|
|
1411
|
+
return time instanceof Series ? Series.fromArray(ctxBars, time.toArray().map((t) => fn(t, tz))) : fn(time, tz);
|
|
1412
|
+
}) as CalendarFunction;
|
|
1413
|
+
}
|
|
1414
|
+
|
|
1415
|
+
/** PineScript `year(time, timezone?)`; the default time zone is the exchange time zone. */
|
|
1416
|
+
export const year = calendar(timeCore.year);
|
|
1417
|
+
/** PineScript `month(time, timezone?)`; the default time zone is the exchange time zone. */
|
|
1418
|
+
export const month = calendar(timeCore.month);
|
|
1419
|
+
/** PineScript `weekofyear(time, timezone?)`; the default time zone is the exchange time zone. */
|
|
1420
|
+
export const weekofyear = calendar(timeCore.weekofyear);
|
|
1421
|
+
/** PineScript `dayofmonth(time, timezone?)`; the default time zone is the exchange time zone. */
|
|
1422
|
+
export const dayofmonth = calendar(timeCore.dayofmonth);
|
|
1423
|
+
/** PineScript `dayofweek(time, timezone?)`; the default time zone is the exchange time zone. */
|
|
1424
|
+
export const dayofweek = calendar(timeCore.dayofweek);
|
|
1425
|
+
/** PineScript `hour(time, timezone?)`; the default time zone is the exchange time zone. */
|
|
1426
|
+
export const hour = calendar(timeCore.hour);
|
|
1427
|
+
/** PineScript `minute(time, timezone?)`; the default time zone is the exchange time zone. */
|
|
1428
|
+
export const minute = calendar(timeCore.minute);
|
|
1429
|
+
/** PineScript `second(time, timezone?)`; the default time zone is the exchange time zone. */
|
|
1430
|
+
export const second = calendar(timeCore.second);
|
|
1431
|
+
|
|
1432
|
+
/**
|
|
1433
|
+
* PineScript `timestamp`: date string (GMT+0 by default), `(timezone, year, month, day, ...)`, or
|
|
1434
|
+
* `(year, month, day, ...)` in the exchange time zone.
|
|
1435
|
+
*/
|
|
1436
|
+
export function timestamp(dateString: string): number;
|
|
1437
|
+
export function timestamp(
|
|
1438
|
+
timezone: string,
|
|
1439
|
+
year: number,
|
|
1440
|
+
month: number,
|
|
1441
|
+
day: number,
|
|
1442
|
+
hour?: number,
|
|
1443
|
+
minute?: number,
|
|
1444
|
+
second?: number
|
|
1445
|
+
): number;
|
|
1446
|
+
export function timestamp(year: number, month: number, day: number, hour?: number, minute?: number, second?: number): number;
|
|
1447
|
+
export function timestamp(first: string | number, ...rest: Array<number | undefined>): number {
|
|
1448
|
+
if (typeof first === 'number') {
|
|
1449
|
+
const [m, d, h, mi, s] = rest as [number, number, number?, number?, number?];
|
|
1450
|
+
return timeCore.timestamp(exchangeTimezone(), first, m, d, h, mi, s);
|
|
1451
|
+
}
|
|
1452
|
+
if (rest.length === 0) return timeCore.timestamp(first);
|
|
1453
|
+
const [y, m, d, h, mi, s] = rest as [number, number, number, number?, number?, number?];
|
|
1454
|
+
return timeCore.timestamp(first, y, m, d, h, mi, s);
|
|
1455
|
+
}
|
|
1456
|
+
|
|
1457
|
+
/** Session test of PineScript `time(timeframe, session, timezone)`; the default time zone is the exchange time zone. */
|
|
1458
|
+
export function inSession(time: number, session: string, timezone?: string): boolean {
|
|
1459
|
+
return timeCore.inSession(time, session, timezone ?? exchangeTimezone());
|
|
1460
|
+
}
|
|
1461
|
+
|
|
1462
|
+
/** PineScript `str.*`; `format_time` and the dates of `format` use the exchange time zone by default. */
|
|
1463
|
+
export const str = {
|
|
1464
|
+
...strCore,
|
|
1465
|
+
format_time(time: number, format: string = "yyyy-MM-dd'T'HH:mm:ssZ", timezone?: string): string {
|
|
1466
|
+
return strCore.format_time(time, format, timezone ?? exchangeTimezone());
|
|
1467
|
+
},
|
|
1468
|
+
format(pattern: string, ...args: unknown[]): string {
|
|
1469
|
+
return formatMessage(pattern, args, exchangeTimezone);
|
|
1470
|
+
},
|
|
1471
|
+
};
|
|
1472
|
+
|
|
1473
|
+
// ── Session values (issue #100) ──────────────────────────────────────────────
|
|
1474
|
+
|
|
1475
|
+
/** Bar time in ms (PineScript `time`). */
|
|
1476
|
+
function barTime(bar: Bar): number {
|
|
1477
|
+
return (bar.time as number) * (chartCtx.timeUnit === 'ms' ? 1 : 1000);
|
|
1478
|
+
}
|
|
1479
|
+
|
|
1480
|
+
const toSpec = (s: SessionSpec | string): SessionSpec => (typeof s === 'string' ? { session: s } : s);
|
|
1481
|
+
|
|
1482
|
+
let sessionCache: { ctx: ChartContext; bars: SessionBars; regular: SessionBars } | null = null;
|
|
1483
|
+
|
|
1484
|
+
/** Session calendars of the run (loaded bars and regular hours), built from the chart context. */
|
|
1485
|
+
function sessionBars(): { bars: SessionBars; regular: SessionBars } {
|
|
1486
|
+
const tz = exchangeTimezone();
|
|
1487
|
+
const tf = chartTimeframe().period;
|
|
1488
|
+
if (!chartCtx.session) {
|
|
1489
|
+
throw new Error('The session is not known: pass it as executeScript(body, bars, inputs, { session }).');
|
|
1490
|
+
}
|
|
1491
|
+
if (sessionCache?.ctx !== chartCtx) {
|
|
1492
|
+
const loaded = toSpec(chartCtx.session);
|
|
1493
|
+
const regular = chartCtx.regularSession ? toSpec(chartCtx.regularSession) : loaded;
|
|
1494
|
+
sessionCache = {
|
|
1495
|
+
ctx: chartCtx,
|
|
1496
|
+
bars: new SessionBars(new TradingCalendar(tz, loaded), tf),
|
|
1497
|
+
regular: new SessionBars(new TradingCalendar(tz, { holidays: loaded.holidays, ...regular }), tf),
|
|
1498
|
+
};
|
|
1499
|
+
}
|
|
1500
|
+
return sessionCache;
|
|
1501
|
+
}
|
|
1502
|
+
|
|
1503
|
+
/**
|
|
1504
|
+
* Calendar for `time(tf, session, timezone)` arguments. Without session and time zone: the loaded
|
|
1505
|
+
* session. As in PineScript, an explicit time zone reads the symbol session hours in that zone without
|
|
1506
|
+
* corrections and holidays, and an explicit session follows PineScript's session string rules.
|
|
1507
|
+
*/
|
|
1508
|
+
function calendarFor(session?: string, timezone?: string): { bars: SessionBars; test: boolean } {
|
|
1509
|
+
const { bars } = sessionBars();
|
|
1510
|
+
if (!session && !timezone) return { bars, test: false };
|
|
1511
|
+
const tz = timezone ?? exchangeTimezone();
|
|
1512
|
+
const spec = session ? { session } : { session: toSpec(chartCtx.session!).session };
|
|
1513
|
+
return { bars: new SessionBars(new TradingCalendar(tz, spec, session ? 'pine' : 'symbol'), bars.chart.period), test: true };
|
|
1514
|
+
}
|
|
1515
|
+
|
|
1516
|
+
function perBar(fn: (t: number, i: number, times: number[]) => number): Series {
|
|
1517
|
+
const times = ctxBars.bars.map(barTime);
|
|
1518
|
+
return Series.fromArray(ctxBars, times.map((t, i) => fn(t, i, times)));
|
|
1519
|
+
}
|
|
1520
|
+
|
|
1521
|
+
/** A Series that can also be called, like PineScript's `time` (variable) and `time(...)` (function). */
|
|
1522
|
+
function callableSeries<F extends (...args: never[]) => Series>(series: Series, fn: F): Series & F {
|
|
1523
|
+
Object.setPrototypeOf(fn, Series.prototype);
|
|
1524
|
+
Object.assign(fn, series);
|
|
1525
|
+
return fn as Series & F;
|
|
1526
|
+
}
|
|
1527
|
+
|
|
1528
|
+
function timeFunction(tf?: string, session?: string, timezone?: string): Series {
|
|
1529
|
+
collector();
|
|
1530
|
+
const { bars, test } = calendarFor(session, timezone);
|
|
1531
|
+
const timeframeString = tf || chartTimeframe().period;
|
|
1532
|
+
return perBar((t) => (test && !bars.inside(t) ? NaN : bars.timeOf(t, timeframeString)));
|
|
1533
|
+
}
|
|
1534
|
+
|
|
1535
|
+
function timeCloseFunction(tf?: string, session?: string, timezone?: string): Series {
|
|
1536
|
+
collector();
|
|
1537
|
+
const { bars, test } = calendarFor(session, timezone);
|
|
1538
|
+
const timeframeString = tf || chartTimeframe().period;
|
|
1539
|
+
return perBar((t) => (test && !bars.inside(t) ? NaN : bars.closeOf(t, timeframeString)));
|
|
1540
|
+
}
|
|
1541
|
+
|
|
1542
|
+
/**
|
|
1543
|
+
* PineScript `time`: the bar open time in ms (a Series), and `time(timeframe?, session?, timezone?)`:
|
|
1544
|
+
* the open time of the `timeframe` period that contains the bar, `na` outside `session`.
|
|
1545
|
+
*/
|
|
1546
|
+
export const time = callableSeries(new Series(ctxBars, (b) => barTime(b)), timeFunction);
|
|
1547
|
+
|
|
1548
|
+
/**
|
|
1549
|
+
* PineScript `time_close`: the bar close time in ms (a Series), and `time_close(timeframe?, session?, timezone?)`.
|
|
1550
|
+
* Needs the chart timeframe and session.
|
|
1551
|
+
*/
|
|
1552
|
+
export const time_close = callableSeries(
|
|
1553
|
+
new Series(ctxBars, (_b, i) => sessionBars().bars.closeOf(barTime(ctxBars.bars[i]!), chartTimeframe().period)),
|
|
1554
|
+
timeCloseFunction
|
|
1555
|
+
);
|
|
1556
|
+
|
|
1557
|
+
/** PineScript `time_tradingday`: 00:00 UTC of the trading day of each bar, in ms. */
|
|
1558
|
+
export const time_tradingday = new Series(ctxBars, (b) => sessionBars().bars.tradingDayTime(barTime(b)));
|
|
1559
|
+
|
|
1560
|
+
function changeOf(tf: string): Series {
|
|
1561
|
+
const { bars } = sessionBars();
|
|
1562
|
+
return perBar((t, i, times) => (i > 0 && bars.timeOf(t, tf) !== bars.timeOf(times[i - 1]!, tf) ? 1 : 0));
|
|
1563
|
+
}
|
|
1564
|
+
|
|
1565
|
+
function flag(key: keyof SessionFlags): Series {
|
|
1566
|
+
const { bars, regular } = sessionBars();
|
|
1567
|
+
const values = sessionFlags(ctxBars.bars.map(barTime), bars, regular)[key];
|
|
1568
|
+
return Series.fromArray(ctxBars, values.map((v) => (v ? 1 : 0)));
|
|
1569
|
+
}
|
|
1570
|
+
|
|
1571
|
+
/** A 1 / 0 Series from a per-bar test on the bar index and the number of bars. */
|
|
1572
|
+
function barFlag(test: (i: number, n: number) => boolean): Series {
|
|
1573
|
+
const n = ctxBars.bars.length;
|
|
1574
|
+
return Series.fromArray(ctxBars, Array.from({ length: n }, (_, i) => (test(i, n) ? 1 : 0)));
|
|
1575
|
+
}
|
|
1576
|
+
|
|
1577
|
+
const lastRealtime = (i: number, n: number): boolean => i === n - 1 && chartCtx.realtime === true;
|
|
1578
|
+
|
|
1579
|
+
/**
|
|
1580
|
+
* PineScript `barstate.*` (1 / 0 per bar). All bars but the last are confirmed history bars. The last bar is
|
|
1581
|
+
* confirmed only when the host says so (`ChartContext.lastBarConfirmed`, e.g. the closing update of a realtime
|
|
1582
|
+
* bar), and is a realtime bar only when it receives live updates (`ChartContext.realtime`); without live updates
|
|
1583
|
+
* it is a history bar, as in PineScript.
|
|
1584
|
+
* `islastconfirmedhistory` is the last bar when it is confirmed and not realtime, else the bar before it.
|
|
1585
|
+
*/
|
|
1586
|
+
export const barstate = {
|
|
1587
|
+
get isfirst(): Series { return barFlag((i) => i === 0); },
|
|
1588
|
+
get islast(): Series { return barFlag((i, n) => i === n - 1); },
|
|
1589
|
+
get isconfirmed(): Series { return barFlag((i, n) => i < n - 1 || chartCtx.lastBarConfirmed === true); },
|
|
1590
|
+
get isrealtime(): Series { return barFlag(lastRealtime); },
|
|
1591
|
+
get ishistory(): Series { return barFlag((i, n) => !lastRealtime(i, n)); },
|
|
1592
|
+
get isnew(): Series { return barFlag((i, n) => !lastRealtime(i, n) || chartCtx.lastBarNew !== false); },
|
|
1593
|
+
get islastconfirmedhistory(): Series {
|
|
1594
|
+
return barFlag((i, n) => {
|
|
1595
|
+
const lastConfirmed = chartCtx.lastBarConfirmed === true && chartCtx.realtime !== true;
|
|
1596
|
+
return i === (lastConfirmed ? n - 1 : n - 2);
|
|
1597
|
+
});
|
|
1598
|
+
},
|
|
1599
|
+
};
|
|
1600
|
+
|
|
1601
|
+
/**
|
|
1602
|
+
* PineScript `session.*` (1 / 0 per bar) and the constants `session.regular` / `session.extended`.
|
|
1603
|
+
* First and last bars are per session period; market / pre / post market use the regular hours.
|
|
1604
|
+
*/
|
|
1605
|
+
export const session = {
|
|
1606
|
+
regular: 'regular',
|
|
1607
|
+
extended: 'extended',
|
|
1608
|
+
get isfirstbar(): Series { return flag('isfirstbar'); },
|
|
1609
|
+
get islastbar(): Series { return flag('islastbar'); },
|
|
1610
|
+
get ismarket(): Series { return flag('ismarket'); },
|
|
1611
|
+
get ispremarket(): Series { return flag('ispremarket'); },
|
|
1612
|
+
get ispostmarket(): Series { return flag('ispostmarket'); },
|
|
1613
|
+
get isfirstbar_regular(): Series { return flag('isfirstbar_regular'); },
|
|
1614
|
+
get islastbar_regular(): Series { return flag('islastbar_regular'); },
|
|
1615
|
+
};
|
|
1616
|
+
|
|
1617
|
+
// ── request.security (issue #101) ─────────────────────────────────────────────
|
|
1618
|
+
|
|
1619
|
+
/** PineScript `barmerge.*` constants of `request.security`. */
|
|
1620
|
+
export const barmerge = {
|
|
1621
|
+
gaps_on: 'gaps_on',
|
|
1622
|
+
gaps_off: 'gaps_off',
|
|
1623
|
+
lookahead_on: 'lookahead_on',
|
|
1624
|
+
lookahead_off: 'lookahead_off',
|
|
1625
|
+
} as const;
|
|
1626
|
+
|
|
1627
|
+
const HEIKIN_ASHI = 'heikinashi:';
|
|
1628
|
+
|
|
1629
|
+
/** PineScript `ticker.heikinashi(symbol)` / `ticker.standard(symbol)` for the chart symbol. */
|
|
1630
|
+
export const ticker = {
|
|
1631
|
+
heikinashi(symbol: string): string {
|
|
1632
|
+
return HEIKIN_ASHI + symbol;
|
|
1633
|
+
},
|
|
1634
|
+
standard(symbol: string): string {
|
|
1635
|
+
return symbol.startsWith(HEIKIN_ASHI) ? symbol.slice(HEIKIN_ASHI.length) : symbol;
|
|
1636
|
+
},
|
|
1637
|
+
};
|
|
1638
|
+
|
|
1639
|
+
/** What an expression of `request.security` may return. */
|
|
1640
|
+
export type SecurityValue = Series | number | boolean | Array<Series | number | boolean>;
|
|
1641
|
+
|
|
1642
|
+
function valuesOf(value: Series | number | boolean, length: number): number[] {
|
|
1643
|
+
if (!(value instanceof Series)) return new Array<number>(length).fill(Number(value));
|
|
1644
|
+
if (value.bars !== ctxBars.bars) {
|
|
1645
|
+
throw new Error(
|
|
1646
|
+
'request.security(): the expression returned a series computed on the chart bars. ' +
|
|
1647
|
+
'Compute the series inside the expression, e.g. () => ta.ema(close, 10).'
|
|
1648
|
+
);
|
|
1649
|
+
}
|
|
1650
|
+
return value.toArray().slice();
|
|
1651
|
+
}
|
|
1652
|
+
|
|
1653
|
+
/**
|
|
1654
|
+
* PineScript `request.security` for the chart symbol (`syminfo.tickerid`, `""`, `ticker.heikinashi(...)`,
|
|
1655
|
+
* `ticker.standard(...)`) and a timeframe equal to or higher than the chart's. The higher-timeframe bars are
|
|
1656
|
+
* built by grouping the chart bars, and `expression` runs on them (its `ta.*` calls, tuples and `timeframe.*`
|
|
1657
|
+
* see the higher timeframe). Other symbols need external data and throw; a lower timeframe throws.
|
|
1658
|
+
*
|
|
1659
|
+
* PineScript rules: lookahead off shows a period's value from the
|
|
1660
|
+
* bar that completes it; lookahead on from its first bar; gaps on keeps only the bar where the value appears.
|
|
1661
|
+
* Built bars can differ from the exchange's bars (official open / close, volume).
|
|
1662
|
+
*/
|
|
1663
|
+
function security<T extends SecurityValue>(
|
|
1664
|
+
symbol: string,
|
|
1665
|
+
timeframe: string,
|
|
1666
|
+
expression: () => T,
|
|
1667
|
+
gaps: string = barmerge.gaps_off,
|
|
1668
|
+
lookahead: string = barmerge.lookahead_off
|
|
1669
|
+
): T extends unknown[] ? Series[] : Series {
|
|
1670
|
+
collector();
|
|
1671
|
+
const heikin = symbol.startsWith(HEIKIN_ASHI);
|
|
1672
|
+
const base = heikin ? symbol.slice(HEIKIN_ASHI.length) : symbol;
|
|
1673
|
+
if (base !== '' && base !== chartCtx.tickerid) {
|
|
1674
|
+
throw new Error(`request.security: "${symbol}" is not the chart symbol; other symbols need external data.`);
|
|
1675
|
+
}
|
|
1676
|
+
const chart = chartTimeframe();
|
|
1677
|
+
const tf = timeframeCore.info(timeframe || chart.period).period;
|
|
1678
|
+
if (timeframeCore.in_seconds(tf) < timeframeCore.in_seconds(chart.period)) {
|
|
1679
|
+
throw new Error(`request.security: timeframe "${tf}" is lower than the chart timeframe "${chart.period}".`);
|
|
1680
|
+
}
|
|
1681
|
+
|
|
1682
|
+
const chartBars = ctxBars.bars;
|
|
1683
|
+
const times = chartBars.map(barTime);
|
|
1684
|
+
let htf: Bar[];
|
|
1685
|
+
let group: number[];
|
|
1686
|
+
let latest: number[];
|
|
1687
|
+
if (tf === chart.period) {
|
|
1688
|
+
htf = chartBars;
|
|
1689
|
+
group = chartBars.map((_, i) => i);
|
|
1690
|
+
latest = group;
|
|
1691
|
+
} else {
|
|
1692
|
+
const { bars: loaded, regular } = sessionBars();
|
|
1693
|
+
// D / W / M periods follow the regular hours; intraday periods the loaded session
|
|
1694
|
+
const calendar = timeframeCore.info(tf).isdwm ? regular : loaded;
|
|
1695
|
+
const starts = times.map((t) => calendar.timeOf(t, tf));
|
|
1696
|
+
const dayPeriods = times.map((t, i) => {
|
|
1697
|
+
const at = loaded.calendar.locate(t);
|
|
1698
|
+
const periods = at ? calendar.calendar.periods(at.date) : [];
|
|
1699
|
+
return periods.length ? calendar.timeOf(periods[0]![0], tf) : starts[i]!;
|
|
1700
|
+
});
|
|
1701
|
+
const reaches = times.map((t) => loaded.closeOf(t, chart.period) >= calendar.periodEnd(t, tf));
|
|
1702
|
+
({ group, latest } = periodsOf(starts, dayPeriods, reaches));
|
|
1703
|
+
const unit = chartCtx.timeUnit === 'ms' ? 1 : 1000;
|
|
1704
|
+
// a higher-timeframe bar ends at the bar that completes it (no value from after the completion)
|
|
1705
|
+
const after = afterCompletion(group, latest);
|
|
1706
|
+
const kept = (_: unknown, i: number): boolean => !after[i];
|
|
1707
|
+
htf = resample(chartBars.filter(kept), starts.map((s) => s / unit).filter(kept)).bars;
|
|
1708
|
+
}
|
|
1709
|
+
if (heikin) htf = heikinAshi(htf);
|
|
1710
|
+
|
|
1711
|
+
const savedCtx = chartCtx;
|
|
1712
|
+
let result: T;
|
|
1713
|
+
let values: number[][];
|
|
1714
|
+
ctxBars.setAll(htf);
|
|
1715
|
+
chartCtx = { ...chartCtx, timeframe: tf };
|
|
1716
|
+
try {
|
|
1717
|
+
result = expression();
|
|
1718
|
+
const parts = Array.isArray(result) ? result : [result];
|
|
1719
|
+
values = parts.map((p) => valuesOf(p, htf.length));
|
|
1720
|
+
} finally {
|
|
1721
|
+
ctxBars.setAll(chartBars);
|
|
1722
|
+
chartCtx = savedCtx;
|
|
1723
|
+
}
|
|
1724
|
+
const mapped = values.map((v) =>
|
|
1725
|
+
Series.fromArray(ctxBars, mapToChart(v, group, latest, lookahead === barmerge.lookahead_on, gaps === barmerge.gaps_on))
|
|
1726
|
+
);
|
|
1727
|
+
return (Array.isArray(result) ? mapped : mapped[0]) as T extends unknown[] ? Series[] : Series;
|
|
1728
|
+
}
|
|
1729
|
+
|
|
1730
|
+
/** PineScript `request.*`: `security` for the chart symbol (issue #101). */
|
|
1731
|
+
export const request = { security };
|
|
1732
|
+
|
|
1733
|
+
// ── Drawing objects ──────────────────────────────────────────────────────────
|
|
1734
|
+
// The drawing namespaces come from this bundle so that they share the registry that
|
|
1735
|
+
// executeScript() resets. `line.all` etc. are read like PineScript variables.
|
|
1736
|
+
|
|
1737
|
+
/** PineScript `line.*`; `line.all` lists the live lines in creation order. */
|
|
1738
|
+
export const line = {
|
|
1739
|
+
...lineCore,
|
|
1740
|
+
get all() {
|
|
1741
|
+
return lineCore.all();
|
|
1742
|
+
},
|
|
1743
|
+
};
|
|
1744
|
+
|
|
1745
|
+
/** PineScript `label.*`; `label.all` lists the live labels in creation order. */
|
|
1746
|
+
export const label = {
|
|
1747
|
+
...labelCore,
|
|
1748
|
+
get all() {
|
|
1749
|
+
return labelCore.all();
|
|
1750
|
+
},
|
|
1751
|
+
};
|
|
1752
|
+
|
|
1753
|
+
/** PineScript `box.*`; `box.all` lists the live boxes in creation order. */
|
|
1754
|
+
export const box = {
|
|
1755
|
+
...boxCore,
|
|
1756
|
+
get all() {
|
|
1757
|
+
return boxCore.all();
|
|
1758
|
+
},
|
|
1759
|
+
};
|
|
1760
|
+
|
|
1761
|
+
/** PineScript `linefill.*`; `linefill.all` lists the live linefills in creation order. */
|
|
1762
|
+
export const linefill = {
|
|
1763
|
+
...linefillCore,
|
|
1764
|
+
get all() {
|
|
1765
|
+
return linefillCore.all();
|
|
1766
|
+
},
|
|
1767
|
+
};
|
|
1768
|
+
|
|
1769
|
+
/** PineScript `polyline.*`; `polyline.all` lists the live polylines in creation order. */
|
|
1770
|
+
export const polyline = {
|
|
1771
|
+
...polylineCore,
|
|
1772
|
+
get all() {
|
|
1773
|
+
return polylineCore.all();
|
|
1774
|
+
},
|
|
1775
|
+
};
|
|
1776
|
+
|
|
1777
|
+
/** PineScript `text.format_*` constants. */
|
|
1778
|
+
export const text = { ...textCore };
|
|
1779
|
+
|
|
1780
|
+
/**
|
|
1781
|
+
* PineScript `chart.point.*`. `chart.point.now(price?)` is the point of the current bar
|
|
1782
|
+
* (index, time, and `price`, default the close), so it runs inside eachBar().
|
|
1783
|
+
*/
|
|
1784
|
+
export const chart = {
|
|
1785
|
+
point: {
|
|
1786
|
+
...chartPointCore,
|
|
1787
|
+
now(price?: number): ChartPoint {
|
|
1788
|
+
collector();
|
|
1789
|
+
if (currentBar < 0) {
|
|
1790
|
+
throw new Error('chart.point.now() needs a current bar: call it inside eachBar().');
|
|
1791
|
+
}
|
|
1792
|
+
const bar = ctxBars.bars[currentBar]!;
|
|
1793
|
+
return chartPointCore.new_point(barTime(bar), currentBar, price ?? bar.close);
|
|
1794
|
+
},
|
|
1795
|
+
},
|
|
1796
|
+
};
|
|
1797
|
+
|
|
1798
|
+
/** Wraps a plain value array (e.g. a side output accumulated inside eachBar)
|
|
1799
|
+
* into a Series aligned with the current bars. */
|
|
1800
|
+
export function seriesOf(values: number[]): Series {
|
|
1801
|
+
collector();
|
|
1802
|
+
return Series.fromArray(ctxBars, values);
|
|
1803
|
+
}
|
|
1804
|
+
|
|
1805
|
+
/** PineScript `alertcondition()` — collected for the host's alert engine. */
|
|
1806
|
+
export function alertcondition(condition: Series, title: string, message?: string): void {
|
|
1807
|
+
const c = collector();
|
|
1808
|
+
const id = `alert${c.alertConfig.length}`;
|
|
1809
|
+
c.alertConfig.push({ id, title, message });
|
|
1810
|
+
const last = condition.length() ? condition.last() : NaN;
|
|
1811
|
+
c.alerts.push({ id, title, message, triggered: !isNA(last) && last !== 0 });
|
|
1812
|
+
}
|
|
1813
|
+
|
|
1814
|
+
// ── Convenience re-exports ───────────────────────────────────────────────────
|
|
1815
|
+
|
|
1816
|
+
export { Series, BarData, isNA as na, nz };
|
|
1817
|
+
export { STRATEGY_DEFAULTS } from '../strategy/index.js';
|
|
1818
|
+
export type {
|
|
1819
|
+
StrategyCloseAllOptions,
|
|
1820
|
+
StrategyCloseOptions,
|
|
1821
|
+
StrategyCommissionType,
|
|
1822
|
+
StrategyDirection,
|
|
1823
|
+
StrategyEngine,
|
|
1824
|
+
StrategyEntryOptions,
|
|
1825
|
+
StrategyExitOptions,
|
|
1826
|
+
StrategyNumberVariable,
|
|
1827
|
+
StrategyOcaType,
|
|
1828
|
+
StrategyOptions,
|
|
1829
|
+
StrategyProperties,
|
|
1830
|
+
StrategyQtyType,
|
|
1831
|
+
StrategyRiskDirection,
|
|
1832
|
+
StrategyRiskRule,
|
|
1833
|
+
StrategyRiskValueType,
|
|
1834
|
+
StrategyTextVariable,
|
|
1835
|
+
StrategyTrade,
|
|
1836
|
+
StrategyVariable,
|
|
1837
|
+
} from '../strategy/index.js';
|
|
1838
|
+
|
|
1839
|
+
/** PineScript `math.*`; `math.round_to_mintick(x)` uses `syminfo.mintick` of the chart context. */
|
|
1840
|
+
export const math = {
|
|
1841
|
+
...mathCore,
|
|
1842
|
+
round_to_mintick: ((number: number | Series, mintick?: number) => {
|
|
1843
|
+
const tick = mintick ?? syminfo.mintick;
|
|
1844
|
+
return number instanceof Series ? mathCore.round_to_mintick(number, tick) : mathCore.round_to_mintick(number, tick);
|
|
1845
|
+
}) as typeof mathCore.round_to_mintick,
|
|
1846
|
+
};
|
|
1847
|
+
export * as compare from '../compare/index.js';
|
|
1848
|
+
export type {
|
|
1849
|
+
Bar,
|
|
1850
|
+
IndicatorResult,
|
|
1851
|
+
TimeValue,
|
|
1852
|
+
InputConfig,
|
|
1853
|
+
PlotConfig,
|
|
1854
|
+
HLineConfig,
|
|
1855
|
+
FillConfig,
|
|
1856
|
+
ShapeConfig,
|
|
1857
|
+
ArrowConfig,
|
|
1858
|
+
BarColorConfig,
|
|
1859
|
+
MarkerData,
|
|
1860
|
+
ArrowData,
|
|
1861
|
+
BarColorData,
|
|
1862
|
+
MarkerLocation,
|
|
1863
|
+
MarkerSize,
|
|
1864
|
+
ShapeStyle,
|
|
1865
|
+
};
|