# yukka

A Python client for [YUKKA Lab](https://www.yukkalab.com)'s sentiment and financial data APIs. Pull news sentiment, entity resolution, and market data directly into your Python environment.

```python
from yukka import Session, Asset
from yukka.data import stoxx600

with Session() as session:  # reads YUKKA_TOKEN from environment
    bmw = Asset.from_isin("DE0005190003", session.resolver)
    df = session.sentiment(bmw, date_from="2026-01-01")
```

## Installation

```bash
pip install yukka
```

Requires Python 3.13+.

## Authentication

A JWT token is required to access the YUKKA APIs. Set it as an environment variable:

```bash
export YUKKA_TOKEN="eyJ..."
```

Or pass it explicitly:

```python
from yukka import Session

session = Session(token="eyJ...")
```

> Contact [YUKKA Lab](https://www.yukkalab.com) to request API access.

## Quick Start

### Using a pre-validated universe

The package ships with pre-resolved constituent lists for major indices. No entity resolution needed.

```python
from yukka import Session, Asset
from yukka.data import stoxx600, sp500, ftse100, nasdaq100

# Load STOXX 600 constituents (isin, ric, name, yukka_id, ...)
universe = stoxx600()

# Create assets from YUKKA IDs and fetch sentiment
assets = Asset.from_yukka_id(universe["yukka_id"].to_list())

with Session() as session:
    df = session.sentiment(assets, date_from="2026-01-01", date_to="2026-02-01")
```

### Creating assets from different identifier types

```python
from yukka import Session, Asset

with Session() as session:
    # From YUKKA ID (no API call needed)
    bmw = Asset.from_yukka_id("company:bmw")

    # From ISIN (resolves via Metadata API)
    siemens = Asset.from_isin("DE0007236101", session.resolver)

    # From RIC code (resolves via bundled master file)
    sap = Asset.from_ric("SAPG.DE")

    df = session.sentiment([bmw, siemens, sap], date_from="2026-01-01")
```

### Batch requests

Large entity lists are automatically batched (default 50 per request):

```python
with Session() as session:
    assets = Asset.from_ric(universe["constituent_ric"].to_list())
    df = session.sentiment(assets, date_from="2025-01-01", batch_size=50)
```

## API Reference

### `Session`

High-level entry point. Reads `YUKKA_TOKEN` from the environment automatically.

| Method | Description |
|--------|-------------|
| `session.sentiment(assets, date_from, date_to)` | Fetch daily sentiment counts (positive / neutral / negative) |
| `session.resolver` | `EntityResolver` for ISIN → YUKKA ID lookups |
| `session.today` | Today's date |

Returns a Polars DataFrame with columns: `date`, `entity`, `positive`, `neutral`, `negative`.

### `Asset`

Immutable representation of a YUKKA entity.

| Factory | Description |
|---------|-------------|
| `Asset.from_yukka_id(id)` | From a YUKKA compound key, e.g. `"company:bmw"` |
| `Asset.from_isin(isin, resolver)` | From an ISIN via Metadata API lookup |
| `Asset.from_ric(ric)` | From a RIC code via bundled master file |

All factory methods accept a single value or a list.

### `YukkaClient`

Low-level client for direct API access, if you need more control than `Session` provides.

```python
from yukka import YukkaClient

with YukkaClient.from_token("eyJ...") as client:
    df = client.sentiment(["company:bmw", "company:siemens"], "2026-01-01", "2026-01-31")
```

### Pre-validated universes

```python
from yukka.data import stoxx600, sp500, ftse100, nasdaq100

df = stoxx600()   # STOXX Europe 600
df = sp500()      # S&P 500
df = ftse100()    # FTSE 100
df = nasdaq100()  # NASDAQ 100
```

Each returns a Polars DataFrame with `isin`, `constituent_ric`, `constituent_name`, and `yukka_id` columns.

## Design

The package follows the **abstract API pattern**: all analysis code depends on `YukkaAPI`, an abstract interface, rather than on HTTP calls or infrastructure. This makes it straightforward to swap between live data and cached/test data without changing business logic.

```
YukkaAPI (ABC)
├── YukkaClient   — live HTTP calls to YUKKA APIs
└── (your mock)   — in tests or offline exploration
```

## Development

```bash
git clone https://github.com/yukkalab/yukka-api-client
cd yukka-api-client
uv sync
pytest
```

Linting with ruff:

```bash
ruff check src/
ruff format src/
```

## Releasing

Releases are published to PyPI automatically via GitLab CI when a version tag is pushed. The recommended workflow uses the rhiza `make publish` command, which bumps the version, commits, tags, and pushes in one step:

```bash
make publish
```

Or as two separate steps:

```bash
make bump     # update version in pyproject.toml, commit, and create tag
make release  # push the tag, triggering the CI release pipeline
```

The CI pipeline will build the package and publish it to PyPI. Requires `PYPI_TOKEN` to be set in the GitLab project's CI/CD variables.

## License

Apache 2.0
