# Common Mistakes

Pitfalls that trip up alphainfo SDK users and how to avoid them.

---

## 1. Signal too short

**Symptom:** Every result returns `structural_score = 0.5` with a warning.

```python
# Bad — only 20 samples
result = client.analyze(signal=data[:20], sampling_rate=250.0)
# structural_score=0.5, warning="Signal has only 20 samples..."
```

**Fix:** Send at least 200 samples for reliable scores. The hard minimum is 10 (below that the API rejects with 422), but 10-99 samples produce degraded results with a warning.

```python
# Good — 250+ samples
result = client.analyze(signal=data[:500], sampling_rate=250.0)
```

| Samples | Behavior |
|---------|----------|
| < 10 | Rejected (422) |
| 10-99 | Returns 0.5 + warning |
| 100-199 | Active but less reliable |
| **200-500** | **Reliable — recommended** |
| 500+ | Reliable (use windowing for point events) |

---

## 2. Wrong sampling_rate

**Symptom:** Scores seem off — no regime change detected when there should be one, or false positives.

```python
# Bad — daily stock prices are NOT 250 Hz
result = client.analyze(signal=daily_prices, sampling_rate=250.0)
```

`sampling_rate` controls the internal window sizing for multiscale analysis. It should match the actual data acquisition frequency:

```python
# Good
client.analyze(signal=daily_prices, sampling_rate=1.0)        # 1 sample/day
client.analyze(signal=minute_data, sampling_rate=1/60)        # 1 sample/minute
client.analyze(signal=ecg_data, sampling_rate=360.0)          # 360 Hz ECG
client.analyze(signal=accelerometer, sampling_rate=1000.0)    # 1 kHz sensor
```

**Rule of thumb:** `sampling_rate` = number of samples per second in your original data.

---

## 3. Forgetting to close the client

**Symptom:** Resource warnings, connection pool exhaustion in long-running apps.

```python
# Bad — never closed
client = AlphaInfo(api_key="ai_xxx")
result = client.analyze(...)
# connections stay open forever
```

**Fix:** Use a context manager or call `.close()` explicitly.

```python
# Good — context manager
with AlphaInfo(api_key="ai_xxx") as client:
    result = client.analyze(...)

# Good — explicit close
client = AlphaInfo(api_key="ai_xxx")
try:
    result = client.analyze(...)
finally:
    client.close()
```

For async:

```python
async with AsyncAlphaInfo(api_key="ai_xxx") as client:
    result = await client.analyze(...)
```

---

## 4. Using analyze() in a loop instead of analyze_batch()

**Symptom:** Slow processing, unnecessary network overhead, hitting rate limits.

```python
# Bad — N HTTP round trips
results = []
for sig in my_100_signals:
    r = client.analyze(signal=sig, sampling_rate=250.0)
    results.append(r)
```

**Fix:** Use `analyze_batch()` — one HTTP call for up to 100 signals.

```python
# Good — 1 HTTP call
batch = client.analyze_batch(
    signals=my_100_signals,
    sampling_rate=250.0,
    domain="sensors",
)
for item in batch:
    if item.error:
        print(f"Signal {item.index} failed: {item.error}")
    else:
        print(f"Signal {item.index}: {item.confidence_band}")
```

---

## 5. Not checking for per-signal errors in batch

**Symptom:** Code crashes on `.structural_score` for a failed signal.

```python
# Bad — assumes all signals succeed
batch = client.analyze_batch(signals=my_signals, sampling_rate=100.0)
for item in batch.results:
    print(item.structural_score)  # AttributeError if item.error is set!
```

**Fix:** Check `item.error` first, or use `batch.successful` / `batch.failed`.

```python
# Good
for item in batch.successful:
    print(f"{item.index}: score={item.structural_score:.3f}")

for item in batch.failed:
    print(f"{item.index}: FAILED — {item.error}")
```

---

## 6. Ignoring the confidence_band

**Symptom:** Building logic on `structural_score` thresholds that vary by domain.

```python
# Bad — hardcoded threshold
if result.structural_score < 0.5:
    alert("regime change!")
```

The API returns a calibrated `confidence_band` that accounts for domain-specific thresholds. Use it instead:

```python
# Good — uses domain-calibrated band
if result.confidence_band in ("transition", "unstable"):
    alert("regime change!")
```

Bands: `stable` | `transition` | `unstable` (or `divergence` / `anomaly` with semantic layer).

---

## 7. Using use_multiscale=False without understanding the trade-off

**Symptom:** Slightly faster responses (~200ms vs ~250-500ms), but missing structural changes that only appear at certain scales.

```python
# Fast but less thorough
result = client.analyze(signal=data, sampling_rate=250.0, use_multiscale=False)
```

`use_multiscale=True` (default) runs the signal through multiple window sizes to catch changes at different time scales. Turning it off is appropriate for:
- Real-time monitoring where latency matters more than depth
- Very long signals (10k+ samples) where single-scale is sufficient
- Initial screening before a deeper pass

**Not recommended off for:** biomedical signals, short signals (< 500 samples), or when detection accuracy is critical.

---

## 8. Wrong domain

**Symptom:** Scores seem miscalibrated (e.g., finance data analyzed as `biomedical`).

```python
# Bad — wrong domain
result = client.analyze(signal=stock_prices, domain="biomedical")
```

Each domain has calibrated thresholds (`k_calibration`). Using the wrong domain can shift confidence bands. When in doubt, use `"generic"` — it works for everything.

```python
# Good — correct domain
result = client.analyze(signal=stock_prices, domain="finance")

# Also good — generic fallback
result = client.analyze(signal=unknown_data, domain="generic")
```

Available domains: `generic`, `finance`, `biomedical`, `sensors`, `security`, `ai_ml`, `power_grid`, `seismic`, `traffic`.

---

## 9. Catching the wrong exception

**Symptom:** Rate limit errors are silently retried forever, or auth errors trigger retry logic.

```python
# Bad — catches everything the same way
try:
    result = client.analyze(...)
except Exception:
    time.sleep(60)
    # retry
```

**Fix:** Handle each error type appropriately.

```python
from alphainfo.exceptions import (
    AuthError, ValidationError, RateLimitError,
    NotFoundError, APIError, TimeoutError, NetworkError,
)

try:
    result = client.analyze(...)

except AuthError:
    # Don't retry — your key is wrong
    raise

except ValidationError:
    # Don't retry — fix the input
    raise

except RateLimitError as e:
    # Wait the specified time, then retry
    time.sleep(e.retry_after)

except (TimeoutError, NetworkError):
    # Transient — SDK already retried max_retries times
    # Consider alerting or falling back
    pass

except APIError:
    # Server error (5xx) — SDK already retried
    pass
```

---

## 10. Creating a new client per request

**Symptom:** Slow performance, connection overhead on every call.

```python
# Bad — new TCP connection every time
def analyze_signal(signal):
    client = AlphaInfo(api_key="ai_xxx")
    return client.analyze(signal=signal, sampling_rate=250.0)
```

**Fix:** Reuse the client — it manages a connection pool internally.

```python
# Good — one client, many requests
client = AlphaInfo(api_key="ai_xxx")

def analyze_signal(signal):
    return client.analyze(signal=signal, sampling_rate=250.0)
```

With HTTP/2 enabled (`pip install alphainfo[http2]`), connection reuse is even more impactful since all requests multiplex over a single connection.
