docs/API_REFERENCE.md

# ExPostFacto API Reference

This document provides a comprehensive reference for all ExPostFacto modules, functions, and types.

## Core Modules

### ExPostFacto

The main module providing the primary backtesting functions.

#### Main Functions

**`backtest/3`**
```elixir
@spec backtest(
  data :: [map()] | String.t(),
  strategy :: strategy(),
  options :: keyword()
) :: {:ok, Output.t()} | {:error, String.t()}
```

Run a backtest with the given data and strategy.

**Parameters:**
- `data` - Market data as list of maps, CSV file path, or JSON string
- `strategy` - Either `{Module, :function, args}` or `{Module, opts}` for Strategy behaviour
- `options` - Keyword list of options

**Options:**
- `:starting_balance` - Initial capital (default: 10,000.0)
- `:validate_data` - Enable data validation (default: true)
- `:clean_data` - Enable data cleaning (default: true)
- `:enhanced_validation` - Use enhanced validation system (default: false)
- `:debug` - Enable debug logging (default: false)

**Examples:**
```elixir
# Basic backtest
{:ok, result} = ExPostFacto.backtest(data, {MyStrategy, :call, []})

# With options
{:ok, result} = ExPostFacto.backtest(
  data,
  {MyStrategy, [param: 10]},
  starting_balance: 100_000.0,
  enhanced_validation: true
)
```

**`backtest!/3`**

Same as `backtest/3` but raises `ExPostFacto.BacktestError` on failure.

**`optimize/4`**
```elixir
@spec optimize(
  data :: [map()],
  strategy_module :: atom(),
  param_ranges :: [{atom(), Range.t() | [any()]}],
  opts :: keyword()
) :: {:ok, map()} | {:error, String.t()}
```

Optimize strategy parameters using various methods.

**Parameters:**
- `data` - Market data for optimization
- `strategy_module` - Strategy module implementing ExPostFacto.Strategy
- `param_ranges` - Parameter names and their ranges/values to test
- `opts` - Optimization options

**Options:**
- `:method` - `:grid_search`, `:random_search`, or `:walk_forward` (default: `:grid_search`)
- `:maximize` - Metric to optimize (default: `:sharpe_ratio`)
- `:samples` - Number of samples for random search (default: 100)
- `:max_combinations` - Maximum combinations for grid search (default: 1000)

**Available Metrics:**
- `:sharpe_ratio` - Risk-adjusted return
- `:total_return_pct` - Total percentage return
- `:cagr_pct` - Compound Annual Growth Rate
- `:profit_factor` - Gross profit / gross loss
- `:win_rate` - Percentage of winning trades
- `:max_draw_down_percentage` - Maximum drawdown (minimized)

**Examples:**
```elixir
# Grid search optimization
{:ok, result} = ExPostFacto.optimize(
  data,
  MyStrategy,
  [fast: 5..20, slow: 20..50],
  maximize: :sharpe_ratio
)

# Random search
{:ok, result} = ExPostFacto.optimize(
  data,
  MyStrategy,
  [fast: 5..20, slow: 20..50],
  method: :random_search,
  samples: 200
)
```

**`backtest_stream/3`**
```elixir
@spec backtest_stream(
  data_source :: String.t() | Enumerable.t(),
  strategy :: strategy(),
  options :: keyword()
) :: {:ok, Output.t()} | {:error, String.t()}
```

Memory-efficient backtesting for large datasets using streaming.

**Options:**
- `:chunk_size` - Data points per chunk (default: 1000)
- `:window_size` - Rolling window for strategy context (default: 100)
- `:overlap` - Overlap between chunks (default: 10)
- `:memory_limit_mb` - Memory limit in MB (default: 100)

### ExPostFacto.Strategy

Behaviour module for implementing advanced trading strategies.

#### Callbacks

**`init/1`**
```elixir
@callback init(opts :: keyword()) :: {:ok, state :: any()} | {:error, reason :: any()}
```

Initialize strategy with given options. Return initial state.

**`next/1`**
```elixir
@callback next(state :: any()) :: {:ok, new_state :: any()} | {:error, reason :: any()}
```

Process next data point. Called for each market data point.

#### Helper Functions

Available when using `use ExPostFacto.Strategy`:

**Trading Actions:**
- `buy/0` - Enter long position
- `sell/0` - Enter short position  
- `close_buy/0` - Close long position
- `close_sell/0` - Close short position

**Context Access:**
- `data/0` - Get current market data point
- `equity/0` - Get current account equity
- `position/0` - Get current position (`:none`, `:long`, `:short`)

**Technical Analysis:**
- `indicator/2` - Calculate indicator: `indicator(:sma, prices)`
- `indicator/3` - Calculate with params: `indicator(:sma, prices, 20)`
- `crossover?/2` - Check if series A crosses above series B
- `crossunder?/2` - Check if series A crosses below series B

#### Example Strategy

```elixir
defmodule MySMAStrategy do
  use ExPostFacto.Strategy

  def init(opts) do
    fast = Keyword.get(opts, :fast, 10)
    slow = Keyword.get(opts, :slow, 20)
    {:ok, %{fast: fast, slow: slow, prices: []}}
  end

  def next(state) do
    price = data().close
    prices = [price | state.prices]
    
    if length(prices) >= state.slow do
      fast_sma = indicator(:sma, prices, state.fast) |> List.first()
      slow_sma = indicator(:sma, prices, state.slow) |> List.first()
      
      if fast_sma > slow_sma and position() != :long do
        buy()
      elsif fast_sma < slow_sma and position() != :short do
        sell()
      end
    end
    
    {:ok, %{state | prices: prices}}
  end
end
```

### ExPostFacto.Indicators

Technical indicator calculations.

#### Available Indicators

**Moving Averages:**
- `sma/2` - Simple Moving Average
- `ema/2` - Exponential Moving Average
- `wma/2` - Weighted Moving Average

**Momentum Oscillators:**
- `rsi/2` - Relative Strength Index
- `stochastic/2` - Stochastic Oscillator
- `williams_r/2` - Williams %R

**Trend Indicators:**
- `macd/2` - Moving Average Convergence Divergence
- `adx/2` - Average Directional Index
- `aroon/2` - Aroon Indicator

**Volatility Indicators:**
- `bollinger_bands/2` - Bollinger Bands
- `atr/2` - Average True Range
- `keltner_channels/2` - Keltner Channels

**Volume Indicators:**
- `obv/2` - On-Balance Volume
- `ad_line/2` - Accumulation/Distribution Line

#### Usage

```elixir
# Within a strategy
sma_20 = indicator(:sma, price_history, 20)
rsi_14 = indicator(:rsi, price_history, 14)
{macd, signal, histogram} = indicator(:macd, price_history)
{upper, middle, lower} = indicator(:bollinger_bands, price_history, {20, 2.0})

# Direct usage
alias ExPostFacto.Indicators
sma_values = Indicators.sma(prices, 20)
```

### ExPostFacto.Result

Contains backtesting results and performance statistics.

#### Key Fields

**Trade Statistics:**
- `trades_count` - Total number of trades
- `wins_count` - Number of winning trades
- `win_rate` - Percentage of winning trades
- `total_profit_and_loss` - Total P&L in currency units
- `total_return_percentage` - Total return as percentage

**Trade Analysis:**
- `best_trade_percentage` - Best single trade return
- `worst_trade_percentage` - Worst single trade return
- `average_trade_percentage` - Average trade return
- `trade_pairs` - List of individual trades

**Risk Metrics:**
- `max_draw_down_percentage` - Maximum drawdown
- `average_draw_down_percentage` - Average drawdown
- `max_draw_down_duration` - Longest drawdown period
- `sharpe_ratio` - Risk-adjusted return metric
- `cagr_percentage` - Compound Annual Growth Rate

**Time Analysis:**
- `start_date` - Backtest start date
- `end_date` - Backtest end date
- `total_days` - Total days in backtest
- `max_trade_duration` - Longest trade duration
- `average_trade_duration` - Average trade duration

### ExPostFacto.Optimizer

Parameter optimization functionality.

#### Methods

**`grid_search/4`**

Test all combinations of parameters within specified ranges.

**`random_search/4`**

Randomly sample parameter combinations for faster optimization.

**`walk_forward/4`**

Rolling window optimization for more robust parameter selection.

#### Example

```elixir
# Grid search
{:ok, result} = ExPostFacto.Optimizer.grid_search(
  data,
  MyStrategy,
  [fast: 5..15, slow: 20..30],
  maximize: :sharpe_ratio
)

# Access results
best_params = result.best_params  # %{fast: 10, slow: 25}
best_score = result.best_score    # 1.25
all_results = result.all_results  # All parameter combinations tested
```

### ExPostFacto.Validation

Enhanced data validation and error handling.

#### Functions

**`validate_data/2`**

Comprehensive OHLCV data validation with detailed error messages.

**`validate_strategy/2`**

Validate strategy module and parameters.

**`format_error/1`**

Format validation errors for user-friendly display.

#### Usage

```elixir
# Enhanced validation
case ExPostFacto.backtest(
  data,
  strategy,
  enhanced_validation: true,
  debug: true
) do
  {:ok, result} -> result
  {:error, %ExPostFacto.Validation.ValidationError{} = error} ->
    IO.puts(ExPostFacto.Validation.format_error(error))
end
```

## Data Structures

### Market Data Format

Market data should be provided as maps with OHLCV fields:

```elixir
%{
  open: 100.0,           # Opening price (required)
  high: 105.0,           # High price (required)  
  low: 98.0,             # Low price (required)
  close: 102.0,          # Closing price (required)
  volume: 1_000_000,     # Volume (optional)
  timestamp: "2023-01-01" # Timestamp (optional but recommended)
}
```

**Alternative field names:**
- `o`, `h`, `l`, `c` instead of `open`, `high`, `low`, `close`
- `t` instead of `timestamp`

### CSV Format

Supported CSV formats:

```csv
Date,Open,High,Low,Close,Volume
2023-01-01,100.0,105.0,98.0,102.0,1000000
2023-01-02,102.0,108.0,101.0,106.0,1200000
```

Headers are case-insensitive. Common variations supported:
- `Date`, `Time`, `Timestamp` for date column
- `Adj Close` for adjusted closing price

### Strategy Types

**MFA Tuple:**
```elixir
{Module, :function, args}
```

**Strategy Behaviour:**
```elixir
{Module, opts}  # where Module implements ExPostFacto.Strategy
```

### Actions

Valid trading actions returned by strategies:
- `:buy` - Enter long position
- `:sell` - Enter short position
- `:close_buy` - Close long position  
- `:close_sell` - Close short position

## Error Handling

### Exception Types

- `ExPostFacto.BacktestError` - General backtest errors
- `ExPostFacto.DataValidationError` - Data validation errors (legacy)
- `ExPostFacto.Validation.ValidationError` - Enhanced validation errors
- `ExPostFacto.Validation.StrategyError` - Strategy-specific errors

### Best Practices

1. Always use enhanced validation for development:
   ```elixir
   {:ok, result} = ExPostFacto.backtest(
     data,
     strategy,
     enhanced_validation: true,
     debug: true
   )
   ```

2. Validate data before backtesting:
   ```elixir
   case ExPostFacto.validate_data(data) do
     :ok -> run_backtest(data)
     {:error, reason} -> handle_error(reason)
   end
   ```

3. Handle errors gracefully:
   ```elixir
   case ExPostFacto.backtest(data, strategy) do
     {:ok, result} -> process_result(result)
     {:error, error} -> 
       Logger.error("Backtest failed: #{error}")
       :error
   end
   ```

## Performance Considerations

### Memory Usage

- Use `backtest_stream/3` for large datasets
- Limit price history in strategies to what's needed
- Enable data cleaning to remove invalid points

### Optimization

- Use `:random_search` for large parameter spaces
- Set reasonable `:max_combinations` limits
- Consider `:walk_forward` for robust optimization

### Concurrency

- Optimization automatically uses available CPU cores
- Set `:max_concurrent` to control parallelism
- Streaming processes data in chunks to avoid memory issues

## Examples

See the `lib/ex_post_facto/example_strategies/` directory for complete strategy examples and the `docs/` directory for comprehensive guides and tutorials.