# Technical Analysis (ta) (/developers/pinets/architecture/namespaces/ta)



## PineTS Technical Analysis (`ta`) Namespace [#pinets-technical-analysis-ta-namespace]

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**.

## Architecture Overview [#architecture-overview]

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.

```typescript
export function ema(context: any) {
    return (source: any, period: any, callId?: string) => {
        // Implementation...
    };
}
```

## The `param()` Method [#the-param-method]

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.

### Purpose [#purpose]

1. **Normalization**: Converts various input types (raw values, arrays, existing Series) into a unified `Series` object.
2. **Lookback Handling**: Manages the offset logic when accessing historical data (e.g., `close[1]`).
3. **Caching**: Generates unique IDs (`p0`, `p1`, etc.) to cache Series objects and avoid redundant object creation.

### How it Works [#how-it-works]

When you write `ta.ema(close, 14)` in PineTS code, the transpiler converts it to:

```javascript
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()`:

```typescript
const value = Series.from(source).get(0); // Get current value
const length = Series.from(period).get(0); // Get period
```

## Incremental Calculation [#incremental-calculation]

PineTS 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.

### State Management [#state-management]

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):

```typescript
// 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;
```

### Benefits [#benefits]

* **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.

## Implementation Specifics [#implementation-specifics]

### 1. Tuple Returns (Double Bracket Convention) [#1-tuple-returns-double-bracket-convention]

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**.

```typescript
// 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.

### 2. Precision [#2-precision]

All numeric returns should be formatted using `context.precision()`. This ensures consistent decimal handling matching Pine Script defaults (usually 10 decimals).

```typescript
return context.precision(result);
```

### 3. NaN Handling [#3-nan-handling]

Functions must gracefully handle `NaN` inputs and initialization phases.

* **Input**: Check for `NaN` before updating state to avoid corruption (e.g., `initSum += NaN` results in permanent `NaN`).
* **Output**: Return `NaN` during the initialization phase (warm-up period) before enough data is available.

### 4. Unique Call IDs [#4-unique-call-ids]

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.

## Example Implementation Structure [#example-implementation-structure]

```typescript
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);
    };
}
```

## Getter-Like Methods in TA Namespace [#getter-like-methods-in-ta-namespace]

Some Pine Script TA functions can be accessed both as properties and as methods. PineTS handles this through transpiler transformation.

### Example: `ta.tr` (True Range) [#example-tatr-true-range]

**Pine Script behavior:**

```javascript
// 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:**

```typescript
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:**

```javascript
// 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 behavior
```

### Implementation Guidelines [#implementation-guidelines]

When implementing getter-like methods:

1. **Always implement as a method** in `methods/` directory
2. **Use optional parameters** with sensible defaults
3. **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`).

## Generating the Barrel File [#generating-the-barrel-file]

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:**

```bash
npm run generate:ta-index
```

This script automatically:

1. Scans the `methods/` directory.
2. Generates imports for all detected files.
3. Updates `ta.index.ts` to register the new functions in the `TechnicalAnalysis` class.

**Note**: getter-like functions should be implemented as methods with no parameter.
