# ta_regtest — Universal Regression Test Runner

## What This Is

ta_regtest validates TA-Lib indicator implementations. It has two modes:

1. **C reference testing** — tests the shipped C indicator implementations directly (linked in). Uses hand-written test files (`test_ma.c`, `test_rsi.c`, etc.) with known-good expected values and `doRangeTest` range sweeps.

2. **Codegen verification** — tests the generated indicator implementations (from ta_codegen) across all languages by driving JSON-RPC servers. Compares each language's output against the C reference.

## CLI Flags

| Flag | Description |
|------|-------------|
| `--function=CSV` | Substring filter — run only matching test groups |
| `--codegen` | Run codegen verification after C reference tests |
| `--codegen-only` | Run only codegen verification, skip C reference tests |
| `--language=CSV` | Filter languages for codegen verification (e.g., `c,rust,java`) |
| `-p` | Profile mode |

Examples:
```bash
./ta_regtest                                           # C reference tests only
./ta_regtest --codegen                                 # C tests + all-language codegen
./ta_regtest --codegen-only                            # Codegen only (all languages)
./ta_regtest --codegen --language=c,rust               # Codegen for C and Rust only
./ta_regtest --codegen --function=RSI,SMA              # Filter to specific functions
```

## Key Files

| File | Purpose |
|------|---------|
| `ta_regtest.c` | Main entry point. CLI flags: `--function=CSV`, `--codegen`, `--codegen-only`, `--language=CSV`, `-p` |
| `test_codegen.c` | Codegen verification: spawns servers, sends JSON-RPC, compares results |
| `test_codegen.h` | API: `test_codegen(history, languageFilter, functionFilter)` |
| `codegen_pipe.c/h` | Subprocess pipe abstraction for JSON-RPC over stdin/stdout |
| `ta_test_priv.h` | `doRangeTest()`, `checkExpectedValue()`, `RangeTestFunction` callback type |
| `ta_test_func/test_*.c` | Per-indicator C reference tests (23+ files) |

## doRangeTest — The Core Testing Primitive

`doRangeTest()` is what makes ta_regtest thorough. It calls a `RangeTestFunction` callback hundreds of times with every possible `startIdx`/`endIdx` combination, verifying:
- Output coherency across different ranges (same data regardless of range selection)
- Lookback function consistency
- Unstable period handling (via tolerance: `TA_DO_NOT_COMPARE` to skip)

### RangeTestFunction Callback Interface

```c
typedef TA_RetCode (*RangeTestFunction)(
    TA_Integer startIdx, TA_Integer endIdx,
    TA_Real *outputBuffer,          // Write ONE output here (per outputNb)
    TA_Integer *outputBufferInt,    // For integer outputs (candlestick patterns)
    TA_Integer *outBegIdx,
    TA_Integer *outNbElement,
    TA_Integer *lookback,           // Must set this (call TA_GetLookback or function-specific)
    void *opaqueData,               // Your context struct
    unsigned int outputNb,          // Which output to write (0, 1, 2 for multi-output)
    unsigned int *isOutputInteger   // Set to 1 if output is integer
);
```

**Critical detail for generic callback**: `TA_CallFunc` fills ALL outputs at once. The callback must allocate all output buffers, call `TA_CallFunc`, then copy the requested `outputNb` into `outputBuffer`/`outputBufferInt`. Use `TA_GetLookback` on the `TA_ParamHolder` for the lookback value.

## Codegen Verification Architecture

```
ta_regtest
  ├── ta_abstract API (enumerates functions, provides TA_CallFunc for C reference)
  │
  └── codegen_pipe → server subprocess (stdin/stdout JSON-RPC)
        ├── ta_codegen_serve_c
        ├── ta_codegen_serve_rust   (Rust)
        ├── TaCodegenServe.class    (Java)
        └── TaCodegenServe          (.NET)
```

### Current State

A single generic callback driven by `TA_ForEachFunc` enumeration covers all 161 indicators automatically. The callback uses ta_abstract metadata (`TA_GetFuncInfo`, `TA_GetInputParameterInfo`, `TA_GetOptInputParameterInfo`, `TA_GetOutputParameterInfo`) to build JSON-RPC requests without any per-function hand-coding. `TA_CallFunc` executes the C reference, then the callback copies the requested `outputNb` into the range-test output buffer.

After all functions run, ta_regtest prints:
- A **cross-language timing comparison table** (wall-clock ns per call, speedup vs C)
- A **CLI summary** with pass/fail counts and average timing per language
- A **JSONL rolling report** (one JSON line per function per language) written to disk, tagged with git SHA

### Input Type Complexity

Functions have different input types that affect JSON-RPC serialization:

| Input Type | Example Functions | JSON Fields |
|-----------|-------------------|-------------|
| `TA_Input_Real` (single) | SMA, RSI, EMA | `"inReal": [...]` |
| `TA_Input_Real` (two) | MULT, ADD, SUB | `"inReal0": [...], "inReal1": [...]` |
| `TA_Input_Price` | STOCH, BBANDS, ADX, MACD | `"inHigh": [...], "inLow": [...], "inClose": [...]` |

For `TA_Input_Price`, the `TA_InputParameterInfo.flags` bitmask tells you which OHLCV components are needed. Map from `TA_History`:
- `TA_IN_PRICE_OPEN` → `history->open`
- `TA_IN_PRICE_HIGH` → `history->high`
- `TA_IN_PRICE_LOW` → `history->low`
- `TA_IN_PRICE_CLOSE` → `history->close`
- `TA_IN_PRICE_VOLUME` → `history->volume`
- `TA_IN_PRICE_OPENINTEREST` → `history->openInterest`

See `test_abstract.c` (lines 415-421) for the working reference.

### Output Type Complexity

| Output Type | Example Functions | JSON Fields |
|------------|-------------------|-------------|
| Single real | SMA, RSI, EMA | `"outReal": [...]` |
| Multi real | BBANDS (3), MACD (3), STOCH (2) | `"outReal": [...], "outReal1": [...], "outReal2": [...]` |
| Integer | CDL* patterns, MINMAXINDEX | `"outInteger": [...]` |

Integer outputs use exact match comparison (or tolerance via `TA_DO_NOT_COMPARE`). Real outputs use epsilon comparison (currently `1e-6`).

### Optional Parameter Types

| Type | Example | JSON Parsing |
|------|---------|-------------|
| `TA_OptInput_IntegerRange` | `optInTimePeriod` | `json_find_int` |
| `TA_OptInput_RealRange` | BBANDS `optInNbDevUp`, SAR `optInAcceleration` | `json_find_double` |
| `TA_OptInput_IntegerList` | MA `optInMAType` | `json_find_int` |

### Unstable Period Functions

24 functions have unstable periods that affect output. Must send `unstablePeriod` param to servers:
ADX, ADXR, ATR, CMO, DX, EMA, HT_DCPERIOD, HT_DCPHASE, HT_PHASOR, HT_SINE, HT_TRENDLINE, HT_TRENDMODE, IMI, KAMA, MAMA, MFI, MINUS_DI, MINUS_DM, NATR, PLUS_DI, PLUS_DM, RSI, STOCHRSI, T3

## Buffer Sizes

- `JSON_BUF_SIZE` = 64KB in current code
- `MAX_NB_TEST_ELEMENT` = 280 (max output elements per test)
- At 20 chars/double, one 252-element array ≈ 5KB
- Functions with OHLCV inputs need 5+ arrays in request — may need larger buffers
- `test_abstract.c` uses up to 10000 bars for profiling — not needed for range tests

## Building

```bash
# C-only (standard)
cd cmake-build && cmake .. -DCMAKE_BUILD_TYPE=Release && make ta_regtest -j4
cd ../bin && ./ta_regtest

# With codegen verification
./ta_regtest --codegen --language=c,rust --function=SMA,RSI
```
