| layout | default |
|---|---|
| title | Technical Analysis (ta) |
| parent | Namespaces |
| nav_order | 1 |
| permalink | /architecture/namespaces/ta/ |
This directory contains the implementation of Pine Script's ta.* namespace functions. These functions are designed to replicate Pine Script's behavior in a JavaScript environment, with a strong focus on incremental calculation and Series compatibility.
Each TA function is implemented as a factory function that takes the execution context and returns the actual calculation function. This closure pattern allows functions to access the global state and other context methods efficiently.
export function ema(context: any) {
return (source: any, period: any, callId?: string) => {
// Implementation...
};
}The param() method is a critical component of the PineTS transpiler system. It is automatically injected by the transpiler to wrap arguments passed to namespace functions.
- Normalization: Converts various input types (raw values, arrays, existing Series) into a unified
Seriesobject. - Lookback Handling: Manages the offset logic when accessing historical data (e.g.,
close[1]). - Caching: Generates unique IDs (
p0,p1, etc.) to cache Series objects and avoid redundant object creation.
When you write ta.ema(close, 14) in PineTS code, the transpiler converts it to:
ta.ema(ta.param(close, undefined, 'p0'), ta.param(14, undefined, 'p1'), '_ta0');Inside a TA function implementation, inputs are always unwrapped using Series.from():
const value = Series.from(source).get(0); // Get current value
const length = Series.from(period).get(0); // Get periodPineTS uses incremental calculation to process time-series data efficiently. Instead of recalculating indicators over the entire history for every new bar, functions maintain state and update it with the new bar's data.
All TA functions store their state in context.taState. A unique callId (generated by the transpiler) is used as the key to isolate state for different calls to the same function.
Example (EMA):
// Unique key for this specific function call
const stateKey = _callId || `ema_${period}`;
// Initialize state if not present
if (!context.taState[stateKey]) {
context.taState[stateKey] = { prevEma: null, initSum: 0, initCount: 0 };
}
const state = context.taState[stateKey];
// Update state with new value
const ema = calculateEma(currentValue, state.prevEma);
state.prevEma = ema;- Performance: O(1) calculation per bar for most indicators.
- Memory Efficiency: Only necessary state (e.g., previous value, running sum) is stored, not full history arrays for intermediate steps.
When a Pine Script function returns a tuple (multiple values), PineTS requires a specific return format to ensure it's treated as a single "element" of a Series, rather than an array of values spread across time.
Rule: Return tuples wrapped in double brackets.
// Pine Script: [macd, signal, hist] = ta.macd(...)
// PineTS Implementation:
return [[macdLine, signalLine, histLine]];- Inner Array: The actual tuple values
[a, b, c]. - Outer Array: Wraps the tuple so the Series initialization logic (
$.init()) treats the inner array as a single value for the current bar.
All numeric returns should be formatted using context.precision(). This ensures consistent decimal handling matching Pine Script defaults (usually 10 decimals).
return context.precision(result);Functions must gracefully handle NaN inputs and initialization phases.
- Input: Check for
NaNbefore updating state to avoid corruption (e.g.,initSum += NaNresults in permanentNaN). - Output: Return
NaNduring the initialization phase (warm-up period) before enough data is available.
The optional _callId parameter is crucial. It allows multiple calls to the same function (e.g., ta.ema(close, 14) called in two different places) to maintain separate states. The transpiler automatically injects these IDs.
import { Series } from '../../../Series';
export function myIndicator(context: any) {
return (source: any, length: any, _callId?: string) => {
// 1. Unwrap inputs
const period = Series.from(length).get(0);
// 2. Initialize State
if (!context.taState) context.taState = {};
const stateKey = _callId || `myInd_${period}`;
if (!context.taState[stateKey]) {
context.taState[stateKey] = {
/* initial state */
};
}
// 3. Calculate
const current = Series.from(source).get(0);
// ... calculation logic ...
// 4. Return result
return context.precision(result);
};
}Some Pine Script TA functions can be accessed both as properties and as methods. PineTS handles this through transpiler transformation.
Pine Script behavior:
// As property (getter)
tr1 = ta.tr // Uses default behavior
// As method with parameter
tr2 = ta.tr(true) // handle_na = true
tr3 = ta.tr(false) // handle_na = false
PineTS implementation:
export function tr(context: any) {
return (handle_na?: any) => {
// Default to true for backward compatibility
const handleNa = handle_na !== undefined ? Series.from(handle_na).get(0) : true;
const high0 = context.get(context.data.high, 0);
const low0 = context.get(context.data.low, 0);
const close1 = context.get(context.data.close, 1);
if (isNaN(close1)) {
return handleNa ? high0 - low0 : NaN;
}
return Math.max(high0 - low0, Math.abs(high0 - close1), Math.abs(low0 - close1));
};
}Usage in PineTS:
// All these work correctly:
const tr1 = ta.tr; // Auto-converted to ta.tr()
const tr2 = ta.tr(); // Explicit call with default
const tr3 = ta.tr(true); // Explicit parameter
const tr4 = ta.tr(false); // Different behaviorWhen implementing getter-like methods:
- Always implement as a method in
methods/directory - Use optional parameters with sensible defaults
- Document the default behavior in comments
The transpiler automatically handles the conversion from property access to method calls for all known namespaces (ta, math, request, array, input).
When you add a new method (e.g., methods/newIndicator.ts), you must regenerate the namespace index file (ta.index.ts) to export it.
Command:
npm run generate:ta-indexThis script automatically:
- Scans the
methods/directory. - Generates imports for all detected files.
- Updates
ta.index.tsto register the new functions in theTechnicalAnalysisclass.
Note: getter-like functions should be implemented as methods with no parameter.