# Comprehensive Report: Wiring Rust to Symbol Registry

**Task:** task-29 - Document findings and propose solutions  
**Generated:** 2026-03-12  
**Author:** MintYak  

---

## Executive Summary

This report synthesizes findings from the codebase audit and proposes concrete solutions for wiring Rust code to the Symbol Registry (`symbols.toml`). The audit identified **45+ locations** with hardcoded threshold values (primarily 250 dbps) that should be sourced from the registry.

**Key Recommendation:** Implement a hybrid approach using:
1. Environment variable fallback for Rust (no Python dependency at runtime)
2. Generated Rust constants from registry at build time
3. Python helper functions for registry access in Python code

---

## 1. Current State Analysis

### 1.1 Symbol Registry Capabilities

The registry already provides:

| Function | Purpose |
|----------|---------|
| `get_thresholds_for_symbol(symbol)` | Returns thresholds tuple for a symbol |
| `get_registered_symbols(tier=n)` | Returns filtered symbol list |
| `get_symbol_entries()` | Full symbol metadata |
| `get_all_symbol_threshold_pairs()` | All (symbol, threshold) pairs |

### 1.2 Hardcoded Values Found

| Category | Locations | Primary Value |
|----------|-----------|---------------|
| Threshold defaults | 40+ | 250 dbps |
| Symbol lists | 2 | TIER1_SYMBOLS tuple |
| Min thresholds | 3 | 1000 dbps (crypto) |

### 1.3 Key Files Requiring Updates

**Python:**
- `constants.py`: `DEFAULT_THRESHOLD`, `TIER1_SYMBOLS`, `THRESHOLD_PRESETS`
- `config/algorithm.py`: `default_threshold_decimal_bps`
- `config/population.py`: `OPENDEVIATIONBAR_CRYPTO_MIN_THRESHOLD`

**Rust:**
- `tools/chart/src/main.rs:59`: `default_value_t = 250`
- `crates/opendeviationbar-core/tests/`: 20+ test files with hardcoded 250

**Scripts:**
- `scripts/*.py`: 15+ scripts with hardcoded thresholds

---

## 2. Proposed Solutions

### 2.1 Solution A: Environment Variable + Build-Time Constants (Recommended for Rust)

**Approach:** Rust reads defaults from environment variables at runtime, with build-time constants as compile-time fallbacks.

**Pros:**
- No Python runtime dependency for Rust tools
- Works standalone without Python interpreter
- Easy to configure via `.mise.toml`

**Cons:**
- Duplication of default values in env vars
- Requires sync between Python registry and Rust defaults

**Implementation:**

```rust
// tools/chart/src/main.rs
const DEFAULT_THRESHOLD: u32 = 250;

fn get_default_threshold() -> u32 {
    std::env::var("OPENDEVIATIONBAR_DEFAULT_THRESHOLD")
        .ok()
        .and_then(|v| v.parse().ok())
        .unwrap_or(DEFAULT_THRESHOLD)
}
```

**Update `.mise.toml`:**
```toml
[env]
OPENDEVIATIONBAR_DEFAULT_THRESHOLD = "250"
```

---

### 2.2 Solution B: Generated Rust Constants (Build-Time)

**Approach:** Generate a Rust constants module from `symbols.toml` at build time using a `build.rs` script.

**Pros:**
- Single source of truth at build time
- No runtime Python dependency
- Compile-time validation

**Cons:**
- More complex build setup
- Requires rebuild when registry changes

**Implementation:**

```rust
// crates/opendeviationbar-core/src/registry.rs (generated)
pub const DEFAULT_THRESHOLD: u32 = 250;
pub const TIER1_SYMBOLS: &[&str] = &["AAVE", "ADA", "AVAX", ...];
pub const CRYPTO_MIN_THRESHOLD: u32 = 1000;
```

**Generate via build.rs:**
```rust
// opendeviationbar-core/build.rs
fn main() {
    let toml = std::fs::read_toмя("../../../python/opendeviationbar/data/symbols.toml").unwrap();
    // Parse and generate constants.rs
}
```

---

### 2.3 Solution C: Python Registry Access via PyO3

**Approach:** Use PyO3 bindings to call Python registry functions from Rust.

**Pros:**
- Direct access to registry functions
- Always in sync with Python registry
- Supports dynamic registry updates

**Cons:**
- Requires Python runtime
- Slower FFI overhead
- More complex error handling

**Implementation:**

```rust
// Use pyo3 to call Python
use pyo3::prelude::*;

fn get_threshold_from_registry(symbol: &str) -> u32 {
    Python::with_gil(|py| {
        let registry = py.import("opendeviationbar.symbol_registry").unwrap();
        let func = registry.getattr("get_thresholds_for_symbol").unwrap();
        let result = func.call1((symbol,)).unwrap();
        // Extract first threshold
        250 // fallback
    })
}
```

---

## 3. Recommended Solution: Hybrid Approach

### For Rust Tools (tools/chart, etc.)

Use **Solution A** (Environment Variables) - simplest, works standalone.

### For Rust Core Library (opendeviationbar-core)

Use **Solution B** (Build-Time Constants) - compile-time safety, no runtime deps.

### For Python Code

Use **Solution C** (Direct Registry Import) - already implemented in symbol_registry.py.

---

## 4. Implementation Roadmap

### Phase 1: Python Helper Functions (Quick Win)

Add to `symbol_registry.py`:

```python
def get_default_threshold() -> int:
    """Universal default threshold (250 dbps)."""
    return 250

def get_tier1_symbols() -> tuple[str, ...]:
    """All tier=1 symbols from registry."""
    return get_registered_symbols(tier=1, enabled_only=True)

def get_default_thresholds() -> tuple[int, ...]:
    """Default threshold list."""
    return (250, 500, 750, 1000)

def get_min_threshold_for_symbol(symbol: str) -> int:
    """Per-symbol min_threshold with fallback."""
    entry = get_symbol_entries().get(normalize_symbol(symbol))
    if entry and entry.min_threshold:
        return entry.min_threshold
    # Asset class fallback
    if entry:
        return 1000 if entry.asset_class == "crypto" else 50
    return 250
```

### Phase 2: Update Python Constants

```python
# constants.py
from opendeviationbar.symbol_registry import get_default_threshold, get_tier1_symbols

DEFAULT_THRESHOLD: int = get_default_threshold()
TIER1_SYMBOLS: tuple[str, ...] = get_tier1_symbols()
```

### Phase 3: Update Rust Tools

```rust
// tools/chart/src/main.rs
#[derive(Parser, Debug)]
struct Args {
    #[arg(short, long, default_value_t = get_default_threshold())]
    threshold: u32,
}
```

Where `get_default_threshold()` reads from env var.

### Phase 4: Update .mise.toml

```toml
[env]
OPENDEVIATIONBAR_DEFAULT_THRESHOLD = "250"
OPENDEVIATIONBAR_CRYPTO_MIN_THRESHOLD = "1000"
```

---

## 5. Verification Commands

```bash
# Verify Python constants match registry
python -c "
from opendeviationbar.constants import DEFAULT_THRESHOLD, TIER1_SYMBOLS
from opendeviationbar.symbol_registry import get_default_threshold, get_tier1_symbols
assert DEFAULT_THRESHOLD == get_default_threshold()
assert set(TIER1_SYMBOLS) == set(get_tier1_symbols())
print('✓ Python registry integration verified')
"

# Verify config uses registry defaults
python -c "
from opendeviationbar.config.algorithm import AlgorithmConfig
cfg = AlgorithmConfig()
print(f'default_threshold: {cfg.default_threshold_decimal_bps}')
"
```

---

## 6. Summary

| Component | Current | Proposed | Effort |
|-----------|---------|----------|--------|
| Python constants.py | Hardcoded 250 | Registry delegate | Low |
| config/algorithm.py | Hardcoded 250 | Registry fallback | Low |
| tools/chart | Hardcoded 250 | Env var | Low |
| Rust core tests | Hardcoded 250 | Build-time constants | Medium |
| Scripts (15+) | Hardcoded 250 | Env var support | Medium |

**Total Impact:** Eliminate 45+ hardcoded values, single source of truth in symbols.toml.

---

## 7. Open Questions

1. **Registry Update Frequency:** How often will symbols.toml be updated? Should trigger rebuilds?
2. **Tier1 vs Enabled:** Should TIER1_SYMBOLS include disabled symbols for testing?
3. **Threshold Override:** Should users be able to override registry thresholds via env vars?

---

*Report generated for task-29: Document findings and propose solutions*
