Skip to content

Commit c9dc203

Browse files
committed
Fix : PineScript "var" interpretation
Add : bar_index Add : TA methods : cross, dmi, falling, rising, sar Fix : TA supertrend
1 parent d8ffaed commit c9dc203

File tree

16 files changed

+1536
-175
lines changed

16 files changed

+1536
-175
lines changed

docs/api-coverage/ta.md

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -60,14 +60,14 @@ All functions listed below are verified to exist in Pine Script v5.
6060

6161
| Function | Status | Description |
6262
| ----------------- | ------ | ---------------------------------- |
63-
| `ta.crossover()` | ✔️ | Crossover Detection |
64-
| `ta.crossunder()` | ✔️ | Crossunder Detection |
65-
| `ta.cross()` | | Cross Detection (either direction) |
66-
| `ta.rising()` | | Rising Trend Detection |
67-
| `ta.falling()` | | Falling Trend Detection |
68-
| `ta.dmi()` | | Directional Movement Index |
69-
| `ta.supertrend()` | ✔️ | SuperTrend Indicator |
70-
| `ta.sar()` | | Parabolic SAR |
63+
| `ta.crossover()` | | Crossover Detection |
64+
| `ta.crossunder()` | | Crossunder Detection |
65+
| `ta.cross()` | | Cross Detection (either direction) |
66+
| `ta.rising()` | | Rising Trend Detection |
67+
| `ta.falling()` | | Falling Trend Detection |
68+
| `ta.dmi()` | | Directional Movement Index |
69+
| `ta.supertrend()` | | SuperTrend Indicator |
70+
| `ta.sar()` | | Parabolic SAR |
7171

7272
### Volume Indicators
7373

docs/index.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,10 @@ Learn the basics of converting Pine Script to PineTS and understand key differen
5151

5252
Complete guide on how to initialize PineTS and run indicators with detailed API documentation, including all constructor parameters, run method options, and return value formats.
5353

54+
### [Syntax Guide](syntax-guide.md)
55+
56+
Detailed guide on PineTS syntax and how to write code equivalent to Pine Script, including variable declarations (`var` vs `let`), series access, and control structures.
57+
5458
### [Language Coverage](lang-coverage.md)
5559

5660
Click here to [explore](lang-coverage.md) the [Pine Script language](lang-coverage.md) features implemented in PineTS, including execution model, time series, and more.

docs/syntax-guide.md

Lines changed: 279 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,279 @@
1+
---
2+
layout: default
3+
title: Syntax Guide
4+
nav_order: 3
5+
---
6+
7+
# PineTS Syntax Guide
8+
9+
This guide explains how to write PineTS code that is equivalent to Pine Script. PineTS is designed to be written in JavaScript/TypeScript but behaves like Pine Script's runtime execution model.
10+
11+
## Variable Declarations
12+
13+
PineTS distinguishes between `let` and `var` declarations to mimic Pine Script's behavior. This is a critical difference from standard JavaScript.
14+
15+
### `let` vs `var`
16+
17+
| Feature | Pine Script | PineTS (JS/TS) | Behavior |
18+
| :-------------------- | :------------------ | :-------------- | :----------------------------------------------------------------------------------- |
19+
| **Re-initialization** | `float x = close` | `let x = close` | Variable is re-initialized/calculated **on every bar**. |
20+
| **State Persistence** | `var float x = 0.0` | `var x = 0.0` | Variable is initialized **only once** (first bar) and retains its value across bars. |
21+
22+
**⚠️ Important for JS Developers:** In PineTS, `var` does **not** behave like standard JavaScript `var`. It adopts Pine Script's `var` semantics (persistent state). If you need standard JS function-scoped variables that reset every time, use `let`.
23+
24+
#### Example: State Persistence
25+
26+
**Pine Script:**
27+
28+
```pinescript
29+
// 'sum' retains its value across bars
30+
var float sum = 0.0
31+
sum := sum + close
32+
```
33+
34+
**PineTS:**
35+
36+
```javascript
37+
// 'sum' retains its value across bars
38+
var sum = 0.0;
39+
sum = sum + close;
40+
```
41+
42+
## Loops
43+
44+
PineTS supports standard JavaScript loops, which map to Pine Script's loops.
45+
46+
| Feature | Pine Script | PineTS (JS/TS) |
47+
| :------------- | :---------------- | :------------------------------ |
48+
| **For Loop** | `for i = 0 to 10` | `for (let i = 0; i <= 10; i++)` |
49+
| **While Loop** | `while i < 10` | `while (i < 10)` |
50+
51+
#### Example: For Loop
52+
53+
**Pine Script:**
54+
55+
```pinescript
56+
float sum = 0.0
57+
for i = 0 to 9
58+
sum := sum + close[i]
59+
```
60+
61+
**PineTS:**
62+
63+
```javascript
64+
let sum = 0.0;
65+
for (let i = 0; i < 10; i++) {
66+
sum += close[i];
67+
}
68+
```
69+
70+
## Control Structures
71+
72+
### Switch Statement
73+
74+
PineTS supports the JavaScript `switch` statement, which is equivalent to Pine Script's `switch`.
75+
76+
**Pine Script:**
77+
78+
```pinescript
79+
switch type
80+
"ema" => ta.ema(close, len)
81+
"sma" => ta.sma(close, len)
82+
=> ta.rma(close, len)
83+
```
84+
85+
**PineTS:**
86+
87+
```javascript
88+
switch (type) {
89+
case 'ema':
90+
return ta.ema(close, len);
91+
case 'sma':
92+
return ta.sma(close, len);
93+
default:
94+
return ta.rma(close, len);
95+
}
96+
```
97+
98+
## Functions
99+
100+
User-defined functions in PineTS are written as standard JavaScript functions.
101+
102+
**Pine Script:**
103+
104+
```pinescript
105+
f_ma(source, length) =>
106+
ta.sma(source, length)
107+
```
108+
109+
**PineTS:**
110+
111+
```javascript
112+
function f_ma(source, length) {
113+
return ta.sma(source, length);
114+
}
115+
```
116+
117+
## Tuples and Multiple Return Values
118+
119+
Pine Script allows functions to return multiple values (tuples). PineTS handles this using array destructuring.
120+
121+
**Pine Script:**
122+
123+
```pinescript
124+
[macdLine, signalLine, histLine] = ta.macd(close, 12, 26, 9)
125+
```
126+
127+
**PineTS:**
128+
129+
```javascript
130+
const [macdLine, signalLine, histLine] = ta.macd(close, 12, 26, 9);
131+
```
132+
133+
## Series and History Access
134+
135+
Accessing historical values is done using the `[]` operator in Pine Script. In PineTS, array access syntax is supported and transpiled to safe series access.
136+
137+
| Feature | Pine Script | PineTS (JS/TS) | Notes |
138+
| :----------------- | :---------- | :------------- | :---------------------------------- |
139+
| **Current Value** | `close` | `close` | References the current bar's value. |
140+
| **Previous Value** | `close[1]` | `close[1]` | References the value 1 bar ago. |
141+
| **History Access** | `close[10]` | `close[10]` | References the value 10 bars ago. |
142+
143+
**PineTS:**
144+
145+
```javascript
146+
// Calculate momentum
147+
let mom = close - close[10];
148+
```
149+
150+
## Conditional Logic
151+
152+
PineTS supports standard JavaScript control flow, which maps to Pine Script's execution model.
153+
154+
| Feature | Pine Script | PineTS (JS/TS) | Notes |
155+
| :--------------- | :---------------------------------------------- | :--------------------------------------------------------- | :------------------ |
156+
| **If Statement** | `if condition`<br>&nbsp;&nbsp;&nbsp;&nbsp;`...` | `if (condition) {`<br>&nbsp;&nbsp;&nbsp;&nbsp;`...`<br>`}` | Standard JS syntax. |
157+
| **Ternary** | `cond ? val1 : val2` | `cond ? val1 : val2` | Standard JS syntax. |
158+
159+
#### Example: Trend Direction
160+
161+
**Pine Script:**
162+
163+
```pinescript
164+
if close > open
165+
direction := 1
166+
else
167+
direction := -1
168+
```
169+
170+
**PineTS:**
171+
172+
```javascript
173+
if (close > open) {
174+
direction = 1;
175+
} else {
176+
direction = -1;
177+
}
178+
```
179+
180+
## Built-in Variables
181+
182+
PineTS exposes Pine Script's built-in variables through the `context` object, but usually, you destructure them for easier access.
183+
184+
| Variable | Pine Script | PineTS (JS/TS) |
185+
| :-------------- | :---------- | :-------------------------------- |
186+
| **Close Price** | `close` | `close` (from `context.data`) |
187+
| **Open Price** | `open` | `open` (from `context.data`) |
188+
| **High Price** | `high` | `high` (from `context.data`) |
189+
| **Low Price** | `low` | `low` (from `context.data`) |
190+
| **Volume** | `volume` | `volume` (from `context.data`) |
191+
| **Bar Index** | `bar_index` | `bar_index` (from `context.pine`) |
192+
193+
**PineTS Setup:**
194+
195+
```javascript
196+
const { close, high, low } = context.data;
197+
const { bar_index } = context.pine;
198+
```
199+
200+
## Functions and Namespaces
201+
202+
PineTS organizes built-in functions into namespaces similar to Pine Script v5.
203+
204+
| Namespace | Pine Script | PineTS (JS/TS) | Example |
205+
| :--------------------- | :---------- | :------------- | :---------------------- |
206+
| **Technical Analysis** | `ta.*` | `ta.*` | `ta.sma(close, 14)` |
207+
| **Math** | `math.*` | `math.*` | `math.max(high, low)` |
208+
| **Request** | `request.*` | `request.*` | `request.security(...)` |
209+
210+
**PineTS Setup:**
211+
212+
```javascript
213+
const { ta, math } = context.pine;
214+
// Usage
215+
const sma = ta.sma(close, 14);
216+
```
217+
218+
## Full Example: Parabolic SAR
219+
220+
This example demonstrates `var` for state, `if/else` logic, and history access.
221+
222+
**Pine Script:**
223+
224+
```pinescript
225+
pine_sar(start, inc, max) =>
226+
var float result = na
227+
var float maxMin = na
228+
var float acceleration = na
229+
var bool isBelow = false
230+
bool isFirstTrendBar = false
231+
232+
if bar_index == 1
233+
if close > close[1]
234+
isBelow := true
235+
maxMin := high
236+
result := low[1]
237+
else
238+
isBelow := false
239+
maxMin := low
240+
result := high[1]
241+
isFirstTrendBar := true
242+
acceleration := start
243+
244+
// ... logic continues ...
245+
result
246+
```
247+
248+
**PineTS:**
249+
250+
```javascript
251+
function pine_sar(start, inc, max) {
252+
// Use 'var' for state variables (persistent)
253+
var result = na;
254+
var maxMin = na;
255+
var acceleration = na;
256+
var isBelow = false;
257+
258+
// Use 'let' for temporary variables (reset every bar)
259+
let isFirstTrendBar = false;
260+
261+
if (bar_index == 1) {
262+
if (close > close[1]) {
263+
isBelow = true;
264+
maxMin = high;
265+
result = low[1];
266+
} else {
267+
isBelow = false;
268+
maxMin = low;
269+
result = high[1];
270+
}
271+
isFirstTrendBar = true;
272+
acceleration = start;
273+
}
274+
275+
// ... logic continues ...
276+
277+
return result;
278+
}
279+
```

src/Context.class.ts

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ export class Context {
4141
color: any;
4242
plot: (...args: any[]) => any;
4343
nz: (...args: any[]) => any;
44+
bar_index: number;
4445
};
4546

4647
// Track deprecation warnings to avoid spam
@@ -102,6 +103,7 @@ export class Context {
102103
};
103104

104105
// Initialize everything directly in pine - the default way to access everything
106+
const _this = this;
105107
this.pine = {
106108
input: new Input(this),
107109
ta: new TechnicalAnalysis(this),
@@ -113,6 +115,9 @@ export class Context {
113115
color: coreFunctions.color,
114116
plot: coreFunctions.plot,
115117
nz: coreFunctions.nz,
118+
get bar_index() {
119+
return _this.idx;
120+
},
116121
};
117122
}
118123

@@ -166,12 +171,44 @@ export class Context {
166171
return new Series([value]);
167172
}
168173

174+
/**
175+
* Initializes a 'var' variable.
176+
* - First bar: uses the initial value.
177+
* - Subsequent bars: maintains the previous value (state).
178+
* @param trg - The target variable
179+
* @param src - The source initializer value
180+
* @returns Series object
181+
*/
182+
initVar(trg, src: any): Series {
183+
// If target exists (subsequent bars), return it as is.
184+
// PineTS automatically shifts context variables by copying the last value,
185+
// so the previous value is already carried over to the current slot.
186+
if (trg) {
187+
return trg;
188+
}
189+
190+
// First bar: Initialize with source value
191+
let value;
192+
if (src instanceof Series) {
193+
value = src.get(0);
194+
} else if (Array.isArray(src)) {
195+
if (Array.isArray(src[0])) {
196+
value = src[0];
197+
} else {
198+
value = this.precision(src[src.length - 1]);
199+
}
200+
} else {
201+
value = this.precision(src);
202+
}
203+
204+
return new Series([value]);
205+
}
206+
169207
/**
170208
* this function is used to set the floating point precision of a number
171209
* by default it is set to 10 decimals which is the same as pine script
172210
* @param n - the number to be precision
173211
* @param decimals - the number of decimals to precision to
174-
175212
* @returns the precision number
176213
*/
177214
precision(n: number, decimals: number = 10) {

src/namespaces/Core.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,10 @@ export class Core {
7474
});
7575
}
7676

77+
get bar_index() {
78+
return this.context.idx;
79+
}
80+
7781
na(series: any) {
7882
return isNaN(Series.from(series).get(0));
7983
}

0 commit comments

Comments
 (0)