# yfinance Compatibility Policy

## Design Principle

dyfinance is a **complete drop-in replacement** for yfinance.

```python
# This substitution must always hold
import dyfinance as yf  # identical behavior to: import yfinance as yf
```

All public APIs (`Ticker`, `Tickers`, `download`) have **identical I/O, options, and default values** as yfinance. The only addition dyfinance makes is a DuckDB cache layer; the inputs and outputs visible to the user never change.

---

## Compatibility Scope

### Ticker.history()

| Parameter | Type | Default | Matches yfinance |
|-----------|------|---------|:---:|
| period | str \| None | None | Yes |
| interval | str | "1d" | Yes |
| start | str \| datetime \| None | None | Yes |
| end | str \| datetime \| None | None | Yes |
| prepost | bool | False | Yes |
| actions | bool | True | Yes |
| auto_adjust | bool | True | Yes |
| back_adjust | bool | False | Yes |
| repair | bool | False | Yes |
| keepna | bool | False | Yes |
| rounding | bool | False | Yes |
| timeout | float | 10 | Yes |
| raise_errors | bool | False | Yes |

**Output**: Identical DataFrame structure to yfinance.Ticker.history() (column names, column order, dtypes, index format).

### download()

| Parameter | Type | Default | Matches yfinance |
|-----------|------|---------|:---:|
| tickers | str \| list | - | Yes |
| start | str \| datetime \| None | None | Yes |
| end | str \| datetime \| None | None | Yes |
| actions | bool | False | Yes |
| threads | bool \| int | True | Yes |
| ignore_tz | bool \| None | None | Yes |
| group_by | str | "column" | Yes |
| auto_adjust | bool | True | Yes |
| back_adjust | bool | False | Yes |
| repair | bool | False | Yes |
| keepna | bool | False | Yes |
| progress | bool | True | Yes |
| period | str \| None | None | Yes |
| interval | str | "1d" | Yes |
| prepost | bool | False | Yes |
| rounding | bool | False | Yes |
| timeout | float | 10 | Yes |
| session | - | None | Yes |
| multi_level_index | bool | True | Yes |

**Output**: Identical DataFrame structure to yfinance.download() (including MultiIndex presence and group_by behavior).

### Tickers

| Behavior | Matches yfinance |
|----------|:---:|
| Preserves ticker input order (no dedup) | Yes |
| history() / download() accept `**kwargs` | Yes |
| `.tickers` dict holds individual Ticker instances | Yes |

### Module-Level Fallback

Accessing attributes that do not exist on dyfinance transparently retrieves them from yfinance (`__getattr__` fallback). All names in yfinance's `__all__` must be accessible through dyfinance.

---

## Tests That Guarantee Compatibility

The following test files **verify I/O and option compatibility with yfinance** and **must never be modified** (unless the specification of the code under test changes).

### Protected Test Files

| File | What It Verifies |
|------|-----------------|
| `tests/test_ticker_history.py` | All `Ticker.history()` options (actions, auto_adjust, back_adjust, rounding, keepna, period, start/end, timeout, raise_errors, prepost, repair) produce correct output structures |
| `tests/test_download.py` | All `download()` options (ticker formats, group_by, multi_level_index, actions, auto_adjust, ignore_tz) produce correct output structures |
| `tests/test_compat.py` | Layer 2 (fixture) and Layer 3 (live) verification that dyfinance and yfinance outputs match |
| `tests/test_proxy.py` | Module-level fallback, Ticker/Tickers property delegation, all names in yfinance.__all__ are accessible |

### Rules for Test Modifications

1. **Deleting or modifying existing tests is prohibited** - Existing test cases serve as the specification for yfinance compatibility
2. **Adding tests is encouraged** - New tests verifying additional compatibility requirements are welcome
3. **Structural refactoring is allowed** - Changes such as parameterization are permitted as long as the verification logic remains unchanged (e.g., merging two similar tests into one `@pytest.mark.parametrize`)
4. **Modifications allowed only when tracking yfinance spec changes** - If yfinance changes its specification, update both dyfinance code and tests together

---

## Known Constraints for yfinance Interface Compatibility

Constraints discovered during refactoring to maintain yfinance compatibility:

| Constraint | Reason |
|------------|--------|
| `_tickers.py` ticker normalization preserves order, no dedup | `yfinance.Tickers` preserves input order without dedup |
| `_download.py` ticker normalization uses `sorted(set(...))` | `yfinance.download()` deduplicates via `set()` |
| `_tickers.py` `history()` / `download()` keep `**kwargs` | `yfinance.Tickers` accepts `**kwargs` |
| Cache stores raw data (auto_adjust=False), post-processes on read | Enables a cache independent of option combinations |

---

## Test Strategy (3 Layers)

| Layer | Marker | Description | Runs in CI |
|-------|--------|-------------|:---:|
| Layer 1: Mock | (default) | Mock yfinance and unit-test dyfinance logic | Yes |
| Layer 2: Fixture | (default, skip if missing) | Verify output structure against pre-recorded parquet fixtures | Yes |
| Layer 3: Live | `@pytest.mark.network` | Real-time comparison between yfinance and dyfinance output | No (manual) |
