# Running Locally

`eapctf.ctf` lets you run, evaluate, and validate CTF model scripts using local copies
of the CTF data files. This section covers the three core functions: `run_local()`,
`compute_metrics()`, and `validate()`.

## Data Download

The CTF data must be obtained from WRDS before running any local evaluation. Install
the CTF optional dependency first:

```bash
uv add "eapctf[ctf]"
```

Then download the three tables:

```python
from eapctf.ctf import download_ctf_data

download_ctf_data(wrds_username="your_wrds_username", output_dir="data/ctf/")
```

This writes three Parquet files to `data/ctf/`:

| File | Size | Contents |
|------|------|---------|
| `ctff_chars.parquet` | ~2.4 GB | Firm characteristics panel (402 columns) |
| `ctff_daily_ret.parquet` | ~930 MB | Daily excess returns |
| `ctff_features.parquet` | ~4 KB | Feature name list |

Alternatively, set `WRDS_USERNAME` in a `.env` file and omit the argument:

```python
download_ctf_data(output_dir="data/ctf/")
```

## Running a Model Script

`run_local()` dynamically loads a model script, calls its `main()` function with the
CTF data, and returns the resulting weights DataFrame.

```python
from eapctf.ctf import run_local

weights = run_local("models/my-model.py", data_dir="data/ctf/")
# weights: DataFrame with columns [id, eom, w]
```

The function handles both pandas and Polars returns from `main()` transparently.

## Evaluating Performance

`compute_metrics()` applies the same 10% annualized volatility targeting methodology
as the CTF server, then computes Sharpe ratio, annual return, volatility, and maximum
drawdown.

```python
import pandas as pd
from eapctf.ctf import compute_metrics

daily_ret = pd.read_parquet("data/ctf/ctff_daily_ret.parquet")
metrics = compute_metrics(weights, daily_ret, vol_target=0.10)

print(f"Sharpe:        {metrics.sharpe:.3f}")
print(f"Annual Return: {metrics.annual_return:.2%}")
print(f"Volatility:    {metrics.volatility:.2%}")
print(f"Max Drawdown:  {metrics.max_drawdown:.2%}")
```

Setting `vol_target=0.10` (10%) is required to match the CTF server methodology.
The default is `0.10`, so it can be omitted.

## Compliance Validation

`validate()` checks 23 rules before submission. Fourteen are automated (AST analysis
and runtime tests); nine are manual review items surfaced as a checklist.

```python
from eapctf.ctf import validate

report = validate("models/my-model.py", data_dir="data/ctf/")
if report.passed:
    print("All automated checks passed.")
else:
    for check in report.auto_checks:
        if check.severity == "FAIL":
            print(f"FAIL [{check.rule}] {check.name}: {check.detail}")
```

Key automated checks include:

- No look-ahead bias (truncated-vs-full weight comparison)
- No network calls
- No eval/exec/subprocess
- Output schema validation (id/eom/w columns, no NaN, no duplicates)
- Non-determinism detection (two independent runs must match)
- File size under 1 MB

## End-to-End Pipeline

`pipeline()` orchestrates all steps: run, validate, evaluate, fetch leaderboard,
build bundle, and optionally submit.

```python
from eapctf.ctf import pipeline

report = pipeline(
    script_path="models/my-model.py",
    data_dir="data/ctf/",
    submitter={"first_name": "Jane", "last_name": "Doe", "email": "jane@example.com"},
    model_name="My-Model",
    submit=False,   # set True to attempt submission
)
print(report.metrics)
print(report.estimated_rank)
```
