oakscriptjs 0.10.1 → 0.11.1

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.
Files changed (271) hide show
  1. package/dist/cjs/array/index.d.ts +8 -2
  2. package/dist/cjs/array/index.d.ts.map +1 -1
  3. package/dist/cjs/array/index.js +2 -1
  4. package/dist/cjs/array/index.js.map +2 -2
  5. package/dist/cjs/box/index.d.ts.map +1 -1
  6. package/dist/cjs/box/index.js +7 -1
  7. package/dist/cjs/box/index.js.map +2 -2
  8. package/dist/cjs/callsite/index.d.ts +73 -32
  9. package/dist/cjs/callsite/index.d.ts.map +1 -1
  10. package/dist/cjs/callsite/index.js +69 -65
  11. package/dist/cjs/callsite/index.js.map +2 -2
  12. package/dist/cjs/drawing/registry.d.ts +84 -0
  13. package/dist/cjs/drawing/registry.d.ts.map +1 -1
  14. package/dist/cjs/drawing/registry.js +153 -6
  15. package/dist/cjs/drawing/registry.js.map +2 -2
  16. package/dist/cjs/index.d.ts +8 -2
  17. package/dist/cjs/index.d.ts.map +1 -1
  18. package/dist/cjs/index.js +33 -1
  19. package/dist/cjs/index.js.map +2 -2
  20. package/dist/cjs/input.d.ts +9 -1
  21. package/dist/cjs/input.d.ts.map +1 -1
  22. package/dist/cjs/input.js +16 -0
  23. package/dist/cjs/input.js.map +2 -2
  24. package/dist/cjs/label/index.d.ts.map +1 -1
  25. package/dist/cjs/label/index.js +3 -0
  26. package/dist/cjs/label/index.js.map +2 -2
  27. package/dist/cjs/line/index.d.ts +10 -27
  28. package/dist/cjs/line/index.d.ts.map +1 -1
  29. package/dist/cjs/line/index.js +11 -22
  30. package/dist/cjs/line/index.js.map +2 -2
  31. package/dist/cjs/linefill/index.d.ts +3 -0
  32. package/dist/cjs/linefill/index.d.ts.map +1 -1
  33. package/dist/cjs/linefill/index.js +15 -1
  34. package/dist/cjs/linefill/index.js.map +3 -3
  35. package/dist/cjs/math/index.d.ts.map +1 -1
  36. package/dist/cjs/math/index.js +2 -0
  37. package/dist/cjs/math/index.js.map +2 -2
  38. package/dist/cjs/polyline/index.d.ts.map +1 -1
  39. package/dist/cjs/polyline/index.js +3 -0
  40. package/dist/cjs/polyline/index.js.map +2 -2
  41. package/dist/cjs/runtime/adapters/SimpleInputAdapter.js +1 -1
  42. package/dist/cjs/runtime/adapters/SimpleInputAdapter.js.map +2 -2
  43. package/dist/cjs/runtime/index.d.ts +1 -1
  44. package/dist/cjs/runtime/index.d.ts.map +1 -1
  45. package/dist/cjs/runtime/index.js +1 -0
  46. package/dist/cjs/runtime/index.js.map +2 -2
  47. package/dist/cjs/runtime/inputs.d.ts +9 -0
  48. package/dist/cjs/runtime/inputs.d.ts.map +1 -1
  49. package/dist/cjs/runtime/inputs.js +8 -0
  50. package/dist/cjs/runtime/inputs.js.map +2 -2
  51. package/dist/cjs/runtime/types.d.ts +1 -1
  52. package/dist/cjs/runtime/types.d.ts.map +1 -1
  53. package/dist/cjs/runtime/types.js.map +1 -1
  54. package/dist/cjs/script/index.d.ts +91 -21
  55. package/dist/cjs/script/index.d.ts.map +1 -1
  56. package/dist/cjs/script/index.js +207 -19
  57. package/dist/cjs/script/index.js.map +3 -3
  58. package/dist/cjs/security/other.d.ts +92 -0
  59. package/dist/cjs/security/other.d.ts.map +1 -0
  60. package/dist/cjs/security/other.js +164 -0
  61. package/dist/cjs/security/other.js.map +7 -0
  62. package/dist/cjs/session/bars.d.ts +24 -1
  63. package/dist/cjs/session/bars.d.ts.map +1 -1
  64. package/dist/cjs/session/bars.js +56 -7
  65. package/dist/cjs/session/bars.js.map +3 -3
  66. package/dist/cjs/session/calendar.d.ts +13 -2
  67. package/dist/cjs/session/calendar.d.ts.map +1 -1
  68. package/dist/cjs/session/calendar.js +39 -9
  69. package/dist/cjs/session/calendar.js.map +2 -2
  70. package/dist/cjs/session/context.d.ts +76 -0
  71. package/dist/cjs/session/context.d.ts.map +1 -0
  72. package/dist/cjs/session/context.js +114 -0
  73. package/dist/cjs/session/context.js.map +7 -0
  74. package/dist/cjs/str/floatingdecimal.d.ts +31 -0
  75. package/dist/cjs/str/floatingdecimal.d.ts.map +1 -0
  76. package/dist/cjs/str/floatingdecimal.js +298 -0
  77. package/dist/cjs/str/floatingdecimal.js.map +7 -0
  78. package/dist/cjs/str/index.d.ts +4 -1
  79. package/dist/cjs/str/index.d.ts.map +1 -1
  80. package/dist/cjs/str/index.js +2 -1
  81. package/dist/cjs/str/index.js.map +2 -2
  82. package/dist/cjs/str/numberformat.d.ts +7 -3
  83. package/dist/cjs/str/numberformat.d.ts.map +1 -1
  84. package/dist/cjs/str/numberformat.js +4 -3
  85. package/dist/cjs/str/numberformat.js.map +2 -2
  86. package/dist/cjs/ta/index.d.ts +67 -34
  87. package/dist/cjs/ta/index.d.ts.map +1 -1
  88. package/dist/cjs/ta/index.js +202 -145
  89. package/dist/cjs/ta/index.js.map +2 -2
  90. package/dist/cjs/ta/limits.d.ts +35 -0
  91. package/dist/cjs/ta/limits.d.ts.map +1 -0
  92. package/dist/cjs/ta/limits.js +64 -0
  93. package/dist/cjs/ta/limits.js.map +7 -0
  94. package/dist/cjs/ta/running-sum.d.ts +0 -16
  95. package/dist/cjs/ta/running-sum.d.ts.map +1 -1
  96. package/dist/cjs/ta/running-sum.js +2 -0
  97. package/dist/cjs/ta/running-sum.js.map +2 -2
  98. package/dist/cjs/ta-series.d.ts +8 -8
  99. package/dist/cjs/ta-series.d.ts.map +1 -1
  100. package/dist/cjs/ta-series.js +10 -8
  101. package/dist/cjs/ta-series.js.map +2 -2
  102. package/dist/cjs/table/index.d.ts +3 -1
  103. package/dist/cjs/table/index.d.ts.map +1 -1
  104. package/dist/cjs/table/index.js +1 -0
  105. package/dist/cjs/table/index.js.map +3 -3
  106. package/dist/cjs/time/index.d.ts +5 -2
  107. package/dist/cjs/time/index.d.ts.map +1 -1
  108. package/dist/cjs/time/index.js +2 -2
  109. package/dist/cjs/time/index.js.map +2 -2
  110. package/dist/cjs/time/session.d.ts +26 -1
  111. package/dist/cjs/time/session.d.ts.map +1 -1
  112. package/dist/cjs/time/session.js +94 -12
  113. package/dist/cjs/time/session.js.map +3 -3
  114. package/dist/cjs/time/timezone.d.ts +6 -1
  115. package/dist/cjs/time/timezone.d.ts.map +1 -1
  116. package/dist/cjs/time/timezone.js +2 -2
  117. package/dist/cjs/time/timezone.js.map +2 -2
  118. package/dist/cjs/types/metadata.d.ts +28 -4
  119. package/dist/cjs/types/metadata.d.ts.map +1 -1
  120. package/dist/cjs/types/metadata.js.map +1 -1
  121. package/dist/esm/array/index.d.ts +8 -2
  122. package/dist/esm/array/index.d.ts.map +1 -1
  123. package/dist/esm/array/index.js +12 -3
  124. package/dist/esm/array/index.js.map +1 -1
  125. package/dist/esm/box/index.d.ts.map +1 -1
  126. package/dist/esm/box/index.js +8 -2
  127. package/dist/esm/box/index.js.map +1 -1
  128. package/dist/esm/callsite/index.d.ts +73 -32
  129. package/dist/esm/callsite/index.d.ts.map +1 -1
  130. package/dist/esm/callsite/index.js +143 -106
  131. package/dist/esm/callsite/index.js.map +1 -1
  132. package/dist/esm/drawing/registry.d.ts +84 -0
  133. package/dist/esm/drawing/registry.d.ts.map +1 -1
  134. package/dist/esm/drawing/registry.js +260 -6
  135. package/dist/esm/drawing/registry.js.map +1 -1
  136. package/dist/esm/index.d.ts +8 -2
  137. package/dist/esm/index.d.ts.map +1 -1
  138. package/dist/esm/index.js +13 -2
  139. package/dist/esm/index.js.map +1 -1
  140. package/dist/esm/input.d.ts +9 -1
  141. package/dist/esm/input.d.ts.map +1 -1
  142. package/dist/esm/input.js +16 -0
  143. package/dist/esm/input.js.map +1 -1
  144. package/dist/esm/label/index.d.ts.map +1 -1
  145. package/dist/esm/label/index.js +4 -1
  146. package/dist/esm/label/index.js.map +1 -1
  147. package/dist/esm/line/index.d.ts +10 -27
  148. package/dist/esm/line/index.d.ts.map +1 -1
  149. package/dist/esm/line/index.js +22 -60
  150. package/dist/esm/line/index.js.map +1 -1
  151. package/dist/esm/linefill/index.d.ts +3 -0
  152. package/dist/esm/linefill/index.d.ts.map +1 -1
  153. package/dist/esm/linefill/index.js +20 -1
  154. package/dist/esm/linefill/index.js.map +1 -1
  155. package/dist/esm/math/index.d.ts.map +1 -1
  156. package/dist/esm/math/index.js +3 -0
  157. package/dist/esm/math/index.js.map +1 -1
  158. package/dist/esm/polyline/index.d.ts.map +1 -1
  159. package/dist/esm/polyline/index.js +5 -1
  160. package/dist/esm/polyline/index.js.map +1 -1
  161. package/dist/esm/runtime/adapters/SimpleInputAdapter.js +1 -1
  162. package/dist/esm/runtime/adapters/SimpleInputAdapter.js.map +1 -1
  163. package/dist/esm/runtime/index.d.ts +1 -1
  164. package/dist/esm/runtime/index.d.ts.map +1 -1
  165. package/dist/esm/runtime/index.js +1 -1
  166. package/dist/esm/runtime/index.js.map +1 -1
  167. package/dist/esm/runtime/inputs.d.ts +9 -0
  168. package/dist/esm/runtime/inputs.d.ts.map +1 -1
  169. package/dist/esm/runtime/inputs.js +15 -0
  170. package/dist/esm/runtime/inputs.js.map +1 -1
  171. package/dist/esm/runtime/types.d.ts +1 -1
  172. package/dist/esm/runtime/types.d.ts.map +1 -1
  173. package/dist/esm/script/index.d.ts +91 -21
  174. package/dist/esm/script/index.d.ts.map +1 -1
  175. package/dist/esm/script/index.js +259 -28
  176. package/dist/esm/script/index.js.map +1 -1
  177. package/dist/esm/security/other.d.ts +92 -0
  178. package/dist/esm/security/other.d.ts.map +1 -0
  179. package/dist/esm/security/other.js +211 -0
  180. package/dist/esm/security/other.js.map +1 -0
  181. package/dist/esm/session/bars.d.ts +24 -1
  182. package/dist/esm/session/bars.d.ts.map +1 -1
  183. package/dist/esm/session/bars.js +75 -6
  184. package/dist/esm/session/bars.js.map +1 -1
  185. package/dist/esm/session/calendar.d.ts +13 -2
  186. package/dist/esm/session/calendar.d.ts.map +1 -1
  187. package/dist/esm/session/calendar.js +54 -10
  188. package/dist/esm/session/calendar.js.map +1 -1
  189. package/dist/esm/session/context.d.ts +76 -0
  190. package/dist/esm/session/context.d.ts.map +1 -0
  191. package/dist/esm/session/context.js +124 -0
  192. package/dist/esm/session/context.js.map +1 -0
  193. package/dist/esm/str/floatingdecimal.d.ts +31 -0
  194. package/dist/esm/str/floatingdecimal.d.ts.map +1 -0
  195. package/dist/esm/str/floatingdecimal.js +272 -0
  196. package/dist/esm/str/floatingdecimal.js.map +1 -0
  197. package/dist/esm/str/index.d.ts +4 -1
  198. package/dist/esm/str/index.d.ts.map +1 -1
  199. package/dist/esm/str/index.js +9 -2
  200. package/dist/esm/str/index.js.map +1 -1
  201. package/dist/esm/str/numberformat.d.ts +7 -3
  202. package/dist/esm/str/numberformat.d.ts.map +1 -1
  203. package/dist/esm/str/numberformat.js +11 -5
  204. package/dist/esm/str/numberformat.js.map +1 -1
  205. package/dist/esm/ta/index.d.ts +67 -34
  206. package/dist/esm/ta/index.d.ts.map +1 -1
  207. package/dist/esm/ta/index.js +284 -240
  208. package/dist/esm/ta/index.js.map +1 -1
  209. package/dist/esm/ta/limits.d.ts +35 -0
  210. package/dist/esm/ta/limits.d.ts.map +1 -0
  211. package/dist/esm/ta/limits.js +67 -0
  212. package/dist/esm/ta/limits.js.map +1 -0
  213. package/dist/esm/ta/running-sum.d.ts +0 -16
  214. package/dist/esm/ta/running-sum.d.ts.map +1 -1
  215. package/dist/esm/ta/running-sum.js +2 -0
  216. package/dist/esm/ta/running-sum.js.map +1 -1
  217. package/dist/esm/ta-series.d.ts +8 -8
  218. package/dist/esm/ta-series.d.ts.map +1 -1
  219. package/dist/esm/ta-series.js +11 -8
  220. package/dist/esm/ta-series.js.map +1 -1
  221. package/dist/esm/table/index.d.ts +3 -1
  222. package/dist/esm/table/index.d.ts.map +1 -1
  223. package/dist/esm/table/index.js +6 -1
  224. package/dist/esm/table/index.js.map +1 -1
  225. package/dist/esm/time/index.d.ts +5 -2
  226. package/dist/esm/time/index.d.ts.map +1 -1
  227. package/dist/esm/time/index.js +6 -3
  228. package/dist/esm/time/index.js.map +1 -1
  229. package/dist/esm/time/session.d.ts +26 -1
  230. package/dist/esm/time/session.d.ts.map +1 -1
  231. package/dist/esm/time/session.js +141 -16
  232. package/dist/esm/time/session.js.map +1 -1
  233. package/dist/esm/time/timezone.d.ts +6 -1
  234. package/dist/esm/time/timezone.d.ts.map +1 -1
  235. package/dist/esm/time/timezone.js +8 -2
  236. package/dist/esm/time/timezone.js.map +1 -1
  237. package/dist/esm/types/metadata.d.ts +28 -4
  238. package/dist/esm/types/metadata.d.ts.map +1 -1
  239. package/package.json +1 -1
  240. package/src/array/index.ts +11 -3
  241. package/src/box/index.ts +8 -2
  242. package/src/callsite/index.ts +150 -100
  243. package/src/drawing/registry.ts +278 -5
  244. package/src/index.ts +44 -1
  245. package/src/input.ts +18 -1
  246. package/src/label/index.ts +4 -1
  247. package/src/line/index.ts +22 -64
  248. package/src/linefill/index.ts +20 -1
  249. package/src/math/index.ts +2 -0
  250. package/src/polyline/index.ts +3 -1
  251. package/src/runtime/adapters/SimpleInputAdapter.ts +1 -1
  252. package/src/runtime/index.ts +1 -0
  253. package/src/runtime/inputs.ts +16 -0
  254. package/src/runtime/types.ts +1 -1
  255. package/src/script/index.ts +355 -34
  256. package/src/security/other.ts +269 -0
  257. package/src/session/bars.ts +70 -6
  258. package/src/session/calendar.ts +60 -10
  259. package/src/session/context.ts +162 -0
  260. package/src/str/floatingdecimal.ts +268 -0
  261. package/src/str/index.ts +8 -2
  262. package/src/str/numberformat.ts +13 -6
  263. package/src/ta/index.ts +304 -248
  264. package/src/ta/limits.ts +74 -0
  265. package/src/ta/running-sum.ts +2 -0
  266. package/src/ta-series.ts +19 -16
  267. package/src/table/index.ts +4 -1
  268. package/src/time/index.ts +6 -3
  269. package/src/time/session.ts +137 -15
  270. package/src/time/timezone.ts +9 -2
  271. package/src/types/metadata.ts +28 -4
package/src/index.ts CHANGED
@@ -43,6 +43,48 @@ export { taCore, math, array, map, str, color, time, timeframe, matrix, line, bo
43
43
  export { drawingOutputs, type DrawingOutputs } from './drawing/output.js';
44
44
  export { barInterval, barTime } from './drawing/bar-time.js';
45
45
 
46
+ // Drawing registry limits (PineScript indicator(max_lines_count / max_labels_count / max_boxes_count / max_polylines_count))
47
+ export {
48
+ resetDrawings,
49
+ setDrawingLimits,
50
+ setDrawingBar,
51
+ holdDrawing,
52
+ releaseDrawing,
53
+ varDrawing,
54
+ type DrawingLimits,
55
+ type DrawingVariable,
56
+ } from './drawing/registry.js';
57
+
58
+ // The session of a chart context without a script run: time(tf), time_close(tf), time_tradingday, session.* flags,
59
+ // request.security periods with the session and holidays, and bars after the last bar on the trading calendar
60
+ export {
61
+ chartSessions,
62
+ periodTime,
63
+ periodClose,
64
+ tradingDayTime,
65
+ chartSessionFlags,
66
+ resampleSession,
67
+ barTimeAfter,
68
+ barsUntil,
69
+ type SessionContext,
70
+ type ChartSessions,
71
+ type SessionResample,
72
+ } from './session/context.js';
73
+ export { TradingCalendar, type SessionSpec } from './session/calendar.js';
74
+ export { SessionBars, sessionFlags, type SessionFlags } from './session/bars.js';
75
+
76
+ // Same-symbol request.security on a higher timeframe (completion and lookahead rules) and ticker.heikinashi
77
+ export { resample, periodsOf, afterCompletion, mapToChart, heikinAshi, type Resampled } from './security/resample.js';
78
+ // request.security of another symbol: which bar of the other symbol each chart bar shows (#179)
79
+ export {
80
+ securityOther,
81
+ securityOtherValues,
82
+ securityLowerTf,
83
+ type OtherSymbolChart,
84
+ type OtherSymbolOptions,
85
+ type SymbolCalendar,
86
+ } from './security/other.js';
87
+
46
88
  // Export Series class (self-contained, no context)
47
89
  export { Series, BarData } from './runtime/series.js';
48
90
  export type { SeriesExtractor } from './runtime/series.js';
@@ -137,6 +179,7 @@ export {
137
179
  input_source,
138
180
  input_timeframe,
139
181
  input_session,
182
+ input_symbol,
140
183
  input_time,
141
184
  } from './runtime/inputs.js';
142
185
  export { isInputActive } from './runtime/input-active.js';
@@ -172,7 +215,7 @@ export type { LightweightSeriesDefinitions } from './runtime/adapters/Lightweigh
172
215
  export { SimpleInputAdapter } from './runtime/adapters/SimpleInputAdapter.js';
173
216
 
174
217
  // Version
175
- export const VERSION = '0.10.1';
218
+ export const VERSION = '0.11.1';
176
219
 
177
220
  /**
178
221
  * Library information
package/src/input.ts CHANGED
@@ -9,7 +9,7 @@
9
9
  */
10
10
  export interface InputValue<T> {
11
11
  /** Input type identifier */
12
- type: 'int' | 'float' | 'source' | 'bool' | 'string' | 'timeframe' | 'session' | 'time';
12
+ type: 'int' | 'float' | 'source' | 'bool' | 'string' | 'timeframe' | 'session' | 'time' | 'symbol';
13
13
  /** Default value */
14
14
  defaultValue: T;
15
15
  /** Current value (initially equals defaultValue) */
@@ -254,6 +254,23 @@ export const input = {
254
254
  };
255
255
  },
256
256
 
257
+ /**
258
+ * Create a symbol input parameter (PineScript `input.symbol`): a ticker id ("NASDAQ:MSFT") that an application can
259
+ * edit with a symbol picker (#180)
260
+ * @param defaultValue - Default ticker id
261
+ * @param options - Additional options (title, options for dropdown)
262
+ * @returns Input value object
263
+ */
264
+ symbol(defaultValue: string, options: StringInputOptions = {}): InputValue<string> {
265
+ return {
266
+ type: 'symbol' as const,
267
+ defaultValue,
268
+ value: defaultValue,
269
+ title: options.title,
270
+ options: options.options,
271
+ };
272
+ },
273
+
257
274
  /**
258
275
  * Create a date/time input parameter (PineScript `input.time`)
259
276
  * @param defaultValue - Default UNIX time in milliseconds, e.g. `time.timestamp('2024-01-01')`
@@ -10,7 +10,7 @@
10
10
  * @version 6
11
11
  */
12
12
 
13
- import { all as allDrawings, isDeleted, register, remove } from '../drawing/registry.js';
13
+ import { all as allDrawings, checkBarIndex, isDeleted, register, remove } from '../drawing/registry.js';
14
14
  import type { ChartPoint, Label, color } from '../types/index.js';
15
15
 
16
16
  type LabelXloc = 'bar_index' | 'bar_time';
@@ -80,6 +80,7 @@ export function new_label(...args: unknown[]): Label {
80
80
  string?, LabelXloc?, LabelYloc?, color?, LabelStyle?, color?, (string | number)?,
81
81
  ('left' | 'center' | 'right')?, string?, ('default' | 'monospace')?, boolean?, number?,
82
82
  ];
83
+ x = checkBarIndex('label.new', 'x', x, xloc);
83
84
  return register('label', {
84
85
  x,
85
86
  y,
@@ -188,6 +189,7 @@ export function copy(id: Label): Label {
188
189
  */
189
190
  export function set_x(id: Label, x: number): Label {
190
191
  if (isDeleted(id)) return id;
192
+ x = checkBarIndex('label.set_x', 'x', x, id.xloc);
191
193
  id.x = x;
192
194
  return id;
193
195
  }
@@ -227,6 +229,7 @@ export function set_y(id: Label, y: number): Label {
227
229
  */
228
230
  export function set_xy(id: Label, x: number, y: number): Label {
229
231
  if (isDeleted(id)) return id;
232
+ x = checkBarIndex('label.set_x', 'x', x, id.xloc);
230
233
  id.x = x;
231
234
  id.y = y;
232
235
  return id;
package/src/line/index.ts CHANGED
@@ -10,7 +10,7 @@
10
10
  * @version 6
11
11
  */
12
12
 
13
- import { all as allDrawings, isDeleted, register, remove } from '../drawing/registry.js';
13
+ import { all as allDrawings, checkBarIndex, isDeleted, register, remove } from '../drawing/registry.js';
14
14
  import type { ChartPoint, Line, color } from '../types/index.js';
15
15
 
16
16
  type LineXloc = 'bar_index' | 'bar_time';
@@ -70,6 +70,8 @@ export function new_line(...args: unknown[]): Line {
70
70
  const [xloc, extend, lineColor, style, width, force_overlay] = rest as [
71
71
  LineXloc?, LineExtend?, color?, LineStyle?, number?, boolean?,
72
72
  ];
73
+ x1 = checkBarIndex('line.new', 'x1', x1, xloc);
74
+ x2 = checkBarIndex('line.new', 'x2', x2, xloc);
73
75
  return register('line', {
74
76
  x1,
75
77
  y1,
@@ -79,89 +81,41 @@ export function new_line(...args: unknown[]): Line {
79
81
  extend: extend ?? 'none',
80
82
  color: lineColor,
81
83
  style: style || 'solid',
82
- width: width || 1,
84
+ width: width ?? 1,
83
85
  force_overlay: force_overlay ?? false,
84
86
  });
85
87
  }
86
88
 
87
89
  /**
88
- * Returns the price level of a line at a given bar index.
90
+ * Returns the price level of a line at a given bar index (PineScript `line.get_price`).
89
91
  *
90
92
  * @param id - Line object
91
93
  * @param x - Bar index to calculate price at
92
- * @returns Price level at the given bar index, or NaN if outside bounds and not extended
94
+ * @returns Price level at the given bar index; NaN for a vertical line (x1 == x2) or a deleted line
93
95
  *
94
96
  * @remarks
95
- * - **CRITICAL COMPUTATIONAL FUNCTION** - Enables trend line breakout detection
96
- * - Uses linear interpolation: y = y1 + (y2 - y1) * (x - x1) / (x2 - x1)
97
- * - Respects extend mode:
98
- * - 'none': Returns NaN if x is outside [x1, x2]
99
- * - 'left': Extends infinitely to the left (x < x1)
100
- * - 'right': Extends infinitely to the right (x > x2)
101
- * - 'both': Extends infinitely in both directions
102
- * - Only works with xloc='bar_index' lines (not xloc='bar_time')
103
- * - In PineScript, this function treats all lines as if extend='both' regardless of actual setting
104
- * For compatibility, we follow the actual extend setting
97
+ * As in PineScript (#174):
98
+ * - the line is read as extended on both sides whatever its `extend`: a point before x1 or after x2 has a price
99
+ * - the price is `y1 + (y2 - y1) * (x - x1) / (x2 - x1)`, in that order (bit for bit)
100
+ * - a line placed with `xloc.bar_time` throws "'line.get_price' must be used with lines created using
101
+ * 'xloc=xloc.bar_index'." (runtime error RE10119)
105
102
  *
106
103
  * @example
107
104
  * ```typescript
108
105
  * const trendLine = line.new(0, 100, 50, 150, 'bar_index', 'right');
109
106
  *
110
- * // Within segment
111
- * line.get_price(trendLine, 25); // Returns 125
112
- *
113
- * // Extended right
114
- * line.get_price(trendLine, 100); // Returns 200
115
- *
116
- * // Not extended left (with extend='right')
117
- * line.get_price(trendLine, -10); // Returns NaN
118
- *
119
- * // Use for breakout detection
120
- * const currentPrice = close[close.length - 1];
121
- * const linePrice = line.get_price(trendLine, close.length - 1);
122
- * if (currentPrice > linePrice) {
123
- * console.log('Breakout above trend line!');
124
- * }
107
+ * line.get_price(trendLine, 25); // 125
108
+ * line.get_price(trendLine, 100); // 200
109
+ * line.get_price(trendLine, -10); // 90: extended to the left too
125
110
  * ```
126
111
  */
127
112
  export function get_price(id: Line, x: number): number {
128
- const { x1, y1, x2, y2, extend, xloc } = id;
129
-
130
- // Only works with bar_index xloc
113
+ const { x1, y1, x2, y2, xloc } = id;
131
114
  if (xloc === 'bar_time') {
132
- throw new Error('line.get_price() only works with xloc.bar_index lines, not xloc.bar_time');
133
- }
134
-
135
- // Avoid division by zero
136
- if (x2 === x1) {
137
- // Vertical line - return NaN or could return y1/y2
138
- return NaN;
115
+ throw new Error("'line.get_price' must be used with lines created using 'xloc=xloc.bar_index'.");
139
116
  }
140
-
141
- // Linear interpolation formula: y = y1 + (y2 - y1) * (x - x1) / (x2 - x1)
142
- const slope = (y2 - y1) / (x2 - x1);
143
- const price = y1 + slope * (x - x1);
144
-
145
- // Check bounds based on extend mode
146
- if (extend === 'none') {
147
- // Only valid within the segment
148
- if (x < Math.min(x1, x2) || x > Math.max(x1, x2)) {
149
- return NaN;
150
- }
151
- } else if (extend === 'left') {
152
- // Valid to the left and within, but not to the right
153
- if (x > Math.max(x1, x2)) {
154
- return NaN;
155
- }
156
- } else if (extend === 'right') {
157
- // Valid to the right and within, but not to the left
158
- if (x < Math.min(x1, x2)) {
159
- return NaN;
160
- }
161
- }
162
- // extend === 'both': always valid
163
-
164
- return price;
117
+ if (x2 === x1) return NaN;
118
+ return y1 + ((y2 - y1) * (x - x1)) / (x2 - x1);
165
119
  }
166
120
 
167
121
  /**
@@ -265,6 +219,7 @@ export function copy(id: Line): Line {
265
219
  */
266
220
  export function set_x1(id: Line, x: number): Line {
267
221
  if (isDeleted(id)) return id;
222
+ x = checkBarIndex('line.set_x***', 'x1', x, id.xloc);
268
223
  id.x1 = x;
269
224
  return id;
270
225
  }
@@ -284,6 +239,7 @@ export function set_x1(id: Line, x: number): Line {
284
239
  */
285
240
  export function set_x2(id: Line, x: number): Line {
286
241
  if (isDeleted(id)) return id;
242
+ x = checkBarIndex('line.set_x***', 'x2', x, id.xloc);
287
243
  id.x2 = x;
288
244
  return id;
289
245
  }
@@ -342,6 +298,7 @@ export function set_y2(id: Line, y: number): Line {
342
298
  */
343
299
  export function set_xy1(id: Line, x: number, y: number): Line {
344
300
  if (isDeleted(id)) return id;
301
+ x = checkBarIndex('line.set_x***', 'x1', x, id.xloc);
345
302
  id.x1 = x;
346
303
  id.y1 = y;
347
304
  return id;
@@ -363,6 +320,7 @@ export function set_xy1(id: Line, x: number, y: number): Line {
363
320
  */
364
321
  export function set_xy2(id: Line, x: number, y: number): Line {
365
322
  if (isDeleted(id)) return id;
323
+ x = checkBarIndex('line.set_x***', 'x2', x, id.xloc);
366
324
  id.x2 = x;
367
325
  id.y2 = y;
368
326
  return id;
@@ -24,6 +24,9 @@ import type { Linefill, Line, color } from '../types/index.js';
24
24
  * @remarks
25
25
  * - Automatically follows line movements (if lines are updated, fill updates too)
26
26
  * - Automatically deleted if either line is deleted
27
+ * - One linefill per pair of lines, as in PineScript (#174): when both lines already belong to a live linefill
28
+ * (`linefill.new(b, a)` after `linefill.new(a, b)`, or `linefill.new(a, a)` after either), that linefill is
29
+ * returned with the new colour instead of a new one; with several, the one created or returned last
27
30
  * - If both lines extend in same direction, fill extends too
28
31
  * - Purely visual in PineScript, but getters allow retrieving line references
29
32
  *
@@ -42,13 +45,29 @@ import type { Linefill, Line, color } from '../types/index.js';
42
45
  export function new_linefill(line1: Line, line2: Line, fillColor?: color): Linefill {
43
46
  // PineScript: an na line gives an na linefill
44
47
  if (line1 === null || line1 === undefined || line2 === null || line2 === undefined) return null as unknown as Linefill;
45
- return register('linefill', {
48
+ let found: Linefill | undefined;
49
+ for (const fill of allDrawings('linefill')) {
50
+ const has = (l: Line): boolean => fill.line1 === l || fill.line2 === l;
51
+ if (has(line1) && has(line2) && (!found || (touched.get(fill) ?? 0) > (touched.get(found) ?? 0))) found = fill;
52
+ }
53
+ if (found) {
54
+ found.color = fillColor;
55
+ touched.set(found, ++touchCount);
56
+ return found;
57
+ }
58
+ const fill = register('linefill', {
46
59
  line1,
47
60
  line2,
48
61
  color: fillColor
49
62
  });
63
+ touched.set(fill, ++touchCount);
64
+ return fill;
50
65
  }
51
66
 
67
+ /** Order in which the linefills were created or returned by `linefill.new` (the last one wins a lookup). */
68
+ const touched = new WeakMap<Linefill, number>();
69
+ let touchCount = 0;
70
+
52
71
  /**
53
72
  * Returns the first line reference.
54
73
  *
package/src/math/index.ts CHANGED
@@ -19,6 +19,7 @@
19
19
  import type { float, int, simple_int, series_float } from '../types/index.js';
20
20
  import { Series } from '../runtime/series.js';
21
21
  import { runningSum, runningSumSeries } from '../ta/running-sum.js';
22
+ import { checkSumLength } from '../ta/limits.js';
22
23
 
23
24
  /**
24
25
  * Returns the absolute value of a number.
@@ -489,6 +490,7 @@ export function sum(
489
490
  ): series_float | Series {
490
491
  // Handle Series objects - extract to array first
491
492
  const sourceArray = source instanceof Series ? source.toArray() : source;
493
+ if (typeof length === 'number') checkSumLength('math.sum', length, 0);
492
494
  // PineScript: the sum of the last `length` non-na values (also +/-Infinity skipped); an na bar keeps the previous
493
495
  // result. A compensated running sum, bit for bit as PineScript.
494
496
  const result: series_float =
@@ -11,7 +11,7 @@
11
11
  */
12
12
 
13
13
  import type { ChartPoint, Polyline, color } from '../types/index.js';
14
- import { all as allDrawings, register, remove } from '../drawing/registry.js';
14
+ import { all as allDrawings, checkBarIndex, register, remove } from '../drawing/registry.js';
15
15
 
16
16
  // Re-export the Polyline type for convenience
17
17
  export type { Polyline };
@@ -86,6 +86,8 @@ export function new_polyline(
86
86
  line_width = 1;
87
87
  }
88
88
 
89
+ if (xloc === 'bar_index') for (const point of points) if (point.index !== null) checkBarIndex('polyline.new', 'points', point.index, xloc);
90
+
89
91
  const polyline: Polyline = {
90
92
  id: generateId(),
91
93
  points: [...points], // Create a copy of the points array
@@ -70,7 +70,7 @@ export class SimpleInputAdapter implements InputAdapter {
70
70
 
71
71
  // Validate string, timeframe and session inputs with options
72
72
  if (
73
- (config.type === 'string' || config.type === 'timeframe' || config.type === 'session') &&
73
+ (config.type === 'string' || config.type === 'timeframe' || config.type === 'session' || config.type === 'symbol') &&
74
74
  config.options &&
75
75
  typeof value === 'string'
76
76
  ) {
@@ -27,6 +27,7 @@ export {
27
27
  input_source,
28
28
  input_timeframe,
29
29
  input_session,
30
+ input_symbol,
30
31
  input_time,
31
32
  enableAutoRecalculate,
32
33
  disableAutoRecalculate,
@@ -277,6 +277,22 @@ export function input_session(defval: string, title?: string, options?: string[]
277
277
  return typeof value === 'string' ? value : defval;
278
278
  }
279
279
 
280
+ /**
281
+ * Register a symbol input (PineScript `input.symbol`): a ticker id such as "NASDAQ:MSFT" (#180)
282
+ * First call registers with defval, subsequent calls return current value
283
+ * @param defval - Default ticker id
284
+ * @param title - Display title (optional)
285
+ * @param options - Array of symbols for a dropdown (optional)
286
+ * @returns Current ticker id
287
+ */
288
+ export function input_symbol(defval: string, title?: string, options?: string[]): string {
289
+ if (!getContext()) {
290
+ return defval;
291
+ }
292
+ const value = registerOnce({ id: generateInputId(title, defval, 'symbol'), type: 'symbol', defval, title, options });
293
+ return typeof value === 'string' ? value : defval;
294
+ }
295
+
280
296
  /**
281
297
  * Register a date/time input (PineScript `input.time`)
282
298
  * First call registers with defval, subsequent calls return current value
@@ -72,7 +72,7 @@ export interface InputConfig {
72
72
  * Type of input. `timeframe` and `session` values are strings ("60", "1D", "0930-1600:23456";
73
73
  * an empty timeframe means the chart timeframe), `time` values are UNIX times in milliseconds.
74
74
  */
75
- type: 'int' | 'float' | 'bool' | 'string' | 'source' | 'color' | 'timeframe' | 'session' | 'time';
75
+ type: 'int' | 'float' | 'bool' | 'string' | 'source' | 'color' | 'timeframe' | 'session' | 'time' | 'symbol';
76
76
  /** Default value */
77
77
  defval: unknown;
78
78
  /** Display title */