indica 0.1.0

Fast technical analysis indicators for stock markets — SMA, EMA, RSI, MACD, Bollinger Bands, ATR, and more
Documentation
# indica — Implementation Plan

> Fast technical analysis indicators for stock markets. Built in Rust.
> Learning Rust by building something real.

## What This Is

A Rust library that computes stock market technical indicators (SMA, RSI, MACD, Bollinger Bands, etc.) — the same math currently done in JavaScript in the Metis codebase (`lib/stock/indicators.ts`). The goal is to eventually use this from Node.js via NAPI-RS for 10-50x faster batch screening.

## Why Rust

- Learn Rust by building, not reading books
- Real performance gains for batch stock screening (2,000+ stocks)
- Publishable to crates.io (Rust) and npm (Node.js via NAPI)
- Portfolio project — first Rust TA library for Indian markets

## Current JS Reference

The TypeScript file we're reimplementing (`metis-2/lib/stock/indicators.ts`) has these functions:

```
sma(values, period)           → number        Simple Moving Average
ema(values, period)           → number        Exponential Moving Average
rsi(closes, period)           → number        RSI (Wilder's smoothing)
macd(closes, fast, slow, sig) → MACDResult    MACD with crossover detection
bollingerBands(closes, p, m)  → BBResult      Bollinger Bands
atr(highs, lows, closes, p)  → number        Average True Range
pivotPoints(high, low, close) → PivotResult   Classic Pivot Points
volumeTrend(volumes)          → string        Volume trend classification
relativeStrength(stock, bench, period) → number  RS vs benchmark
```

---

## Phase 0: Rust Fundamentals (You Are Here)

**Goal:** Get comfortable with Rust basics before writing indicators.

### Concepts to learn:
- [x] `fn`, return types, `pub`
- [x] `let` (immutable) vs `let mut`
- [x] `println!` macro
- [x] `#[test]` and `assert_eq!`
- [ ] **Ownership & borrowing** — Rust's core concept (`&`, `&mut`)
- [ ] **Slices** (`&[f64]`) — how Rust handles arrays without copying
- [ ] **Option** (`Some(value)` / `None`) — Rust's null replacement
- [ ] **Vec<f64>** — growable arrays (like JS arrays)
- [ ] **Iterators** (`.iter()`, `.map()`, `.sum()`, `.fold()`)
- [ ] **Structs** — like TypeScript interfaces but with data
- [ ] **Enums** — like TypeScript union types but more powerful
- [ ] **Error handling** (`Result<T, E>`) — no try/catch in Rust

### Mini exercises (do these in src/lib.rs):
1. Write a function that takes `&[f64]` and returns the sum
2. Write a function that returns `Option<f64>` (None if empty slice)
3. Write a function that returns a `Vec<f64>` (new computed array)
4. Create a struct with named fields and a method on it
5. Create an enum with variants and match on it

**When you're done:** You'll understand enough Rust to start Phase 1.

---

## Phase 1: Moving Averages — SMA & EMA

**Goal:** First real indicator. Teaches slices, iterators, Option.

### What to build:
```rust
/// Simple Moving Average of the last `period` values
pub fn sma(values: &[f64], period: usize) -> Option<f64>

/// Exponential Moving Average
pub fn ema(values: &[f64], period: usize) -> Option<f64>
```

### Rust concepts you'll use:
- `&[f64]` — borrowing a slice (read-only view of an array)
- `Option<f64>` — returns `None` instead of `NaN` when data is insufficient
- `.len()`, `.iter()`, `.sum::<f64>()` — iterator methods
- `.last()` — safely get last element

### Tests to write:
- SMA of `[1, 2, 3, 4, 5]` with period 3 → 4.0
- SMA with insufficient data → None
- EMA matches known values
- Edge cases: empty slice, period=1, period=len

### File structure:
```
src/
  lib.rs          ← re-exports everything
  moving_avg.rs   ← sma() and ema()
```

---

## Phase 2: RSI — Wilder's Smoothing

**Goal:** More complex math. Teaches mutable state + loops.

### What to build:
```rust
/// Relative Strength Index (Wilder's smoothing)
pub fn rsi(closes: &[f64], period: usize) -> Option<f64>
```

### Rust concepts you'll use:
- `let mut` — mutable variables (for running averages)
- `for i in 1..closes.len()` — range-based loops
- `.abs()` — method on f64
- Pattern: seed with initial average, then smooth iteratively

### Tests:
- RSI of known data matches expected output
- Oversold (RSI < 30) and overbought (RSI > 70) cases
- All gains → RSI = 100, all losses → RSI = 0
- Edge: insufficient data → None

### File:
```
src/
  rsi.rs
```

---

## Phase 3: MACD — Structs & Enums

**Goal:** Return complex data. Teaches structs, enums, derive macros.

### What to build:
```rust
#[derive(Debug, Clone)]
pub enum Crossover {
    Bullish,
    Bearish,
    None,
}

#[derive(Debug, Clone)]
pub struct MacdResult {
    pub value: f64,
    pub signal: f64,
    pub histogram: f64,
    pub crossover: Crossover,
}

pub fn macd(
    closes: &[f64],
    fast: usize,    // default 12
    slow: usize,    // default 26
    signal: usize,  // default 9
) -> Option<MacdResult>
```

### Rust concepts:
- `struct` with `pub` fields
- `enum` with variants (no values)
- `#[derive(Debug, Clone)]` — auto-generate formatting and copying
- `Vec<f64>` — building the MACD line series
- Internal helper: reuse `ema()` from Phase 1 or compute inline

### File:
```
src/
  macd.rs
```

---

## Phase 4: Bollinger Bands & ATR

**Goal:** Statistics (std dev) and multi-input functions.

### What to build:
```rust
#[derive(Debug, Clone)]
pub struct BollingerBands {
    pub upper: f64,
    pub middle: f64,
    pub lower: f64,
    pub percent_b: f64,
}

pub fn bollinger_bands(closes: &[f64], period: usize, std_dev_mult: f64) -> Option<BollingerBands>

/// Average True Range (Wilder's smoothing)
pub fn atr(highs: &[f64], lows: &[f64], closes: &[f64], period: usize) -> Option<f64>
```

### Rust concepts:
- `.sqrt()`, `.powi(2)` — math on f64
- Multiple slice parameters (`highs`, `lows`, `closes`)
- `.zip()` — iterating multiple slices together
- `f64::max()` / `.max()` — finding maximum of values

### Files:
```
src/
  bollinger.rs
  atr.rs
```

---

## Phase 5: Pivot Points, Volume Trend, Relative Strength

**Goal:** Complete the indicator set. Teaches &str returns and simple logic.

### What to build:
```rust
#[derive(Debug, Clone)]
pub struct PivotPoints {
    pub r3: f64, pub r2: f64, pub r1: f64,
    pub pivot: f64,
    pub s1: f64, pub s2: f64, pub s3: f64,
}

pub fn pivot_points(high: f64, low: f64, close: f64) -> PivotPoints

pub fn volume_trend(volumes: &[f64]) -> &'static str

pub fn relative_strength(stock: &[f64], benchmark: &[f64], period: usize) -> Option<f64>
```

### Rust concepts:
- `&'static str` — string literals that live forever (like "surging", "declining")
- Simple arithmetic (no new concepts, just practice)
- Lifetime annotation basics (the `'static` part)

### Files:
```
src/
  pivot.rs
  volume.rs
  relative_strength.rs
```

---

## Phase 6: Batch Processing

**Goal:** Compute indicators for 2,000+ stocks at once. Where Rust shines.

### What to build:
```rust
#[derive(Debug, Clone)]
pub struct StockData {
    pub symbol: String,
    pub closes: Vec<f64>,
    pub highs: Vec<f64>,
    pub lows: Vec<f64>,
    pub volumes: Vec<f64>,
}

#[derive(Debug, Clone)]
pub struct IndicatorSnapshot {
    pub symbol: String,
    pub sma_20: Option<f64>,
    pub sma_50: Option<f64>,
    pub rsi_14: Option<f64>,
    pub macd: Option<MacdResult>,
    pub bb: Option<BollingerBands>,
    pub atr_14: Option<f64>,
}

/// Compute all indicators for multiple stocks
pub fn batch_compute(stocks: &[StockData]) -> Vec<IndicatorSnapshot>
```

### Rust concepts:
- `String` vs `&str` — owned vs borrowed strings
- `Vec<T>` — vectors of structs
- **Rayon** (parallel iterator crate) — `stocks.par_iter().map(...)` for multi-core
- `cargo add rayon` — adding dependencies

### Why this matters:
This is the function that Metis would call from Node.js. Screening 2,000 stocks through all indicators in parallel — Rust + Rayon can do this in milliseconds vs seconds in JS.

---

## Phase 7: NAPI-RS Bindings (Connect to Node.js)

**Goal:** Make the library callable from Metis's TypeScript code.

### What to do:
1. Add napi-rs dependencies to Cargo.toml
2. Create `src/napi.rs` with `#[napi]` annotated wrapper functions
3. Build with `cargo build --release`
4. Import in Metis: `import { sma, rsi, macd } from 'indica'`

### Rust concepts:
- `#[napi]` macro — generates JS bindings
- Type conversion: JS number[] ↔ Rust Vec<f64>
- Publishing to npm via napi-rs CLI

### This is the payoff:
```typescript
// In Metis — same API, 50x faster
import { batchCompute } from 'indica';

const results = batchCompute(allStocks); // milliseconds, not seconds
```

---

## File Structure (final)

```
indica/
├── Cargo.toml
├── src/
│   ├── lib.rs              ← re-exports all modules
│   ├── moving_avg.rs       ← Phase 1: SMA, EMA
│   ├── rsi.rs              ← Phase 2: RSI
│   ├── macd.rs             ← Phase 3: MACD
│   ├── bollinger.rs        ← Phase 4: Bollinger Bands
│   ├── atr.rs              ← Phase 4: ATR
│   ├── pivot.rs            ← Phase 5: Pivot Points
│   ├── volume.rs           ← Phase 5: Volume Trend
│   ├── relative_strength.rs← Phase 5: RS vs Benchmark
│   ├── batch.rs            ← Phase 6: Batch processing
│   └── napi.rs             ← Phase 7: Node.js bindings
├── tests/
│   └── integration.rs      ← Cross-module integration tests
├── benches/
│   └── indicators.rs       ← Performance benchmarks
├── PLAN.md                 ← This file
└── README.md
```

---

## Learning Resources

- **The Rust Book** (free): https://doc.rust-lang.org/book/
  - Ch 4: Ownership (READ THIS FIRST)
  - Ch 5: Structs
  - Ch 6: Enums and Pattern Matching
  - Ch 8: Vectors
  - Ch 13: Iterators
- **Rust by Example** (free): https://doc.rust-lang.org/rust-by-example/
- **Exercism Rust Track** (free): https://exercism.org/tracks/rust
- **ta crate** (study code): https://github.com/greyblake/ta-rs

---

## How We'll Work

1. Start each phase by reading the relevant Rust Book chapter
2. Write the functions with tests
3. `cargo test -- --nocapture` to verify
4. Commit after each phase
5. Ask questions when stuck — that's the whole point