Metadata-Version: 2.4
Name: float-sdk
Version: 0.1.3
Summary: Float SDK: core API models and trading extensions
Author: Float
License-Expression: Apache-2.0
License-File: LICENSE
Requires-Dist: pydantic>=2.12
Requires-Dist: httpx>=0.27
Requires-Dist: httpcore>=1.0
Requires-Dist: requests>=2.31
Requires-Dist: loguru>=0.7
Requires-Dist: tenacity>=8.2
Requires-Dist: cachetools>=5.3
Requires-Dist: inflection>=0.5.1
Requires-Dist: pydash>=7.0
Requires-Dist: python-dateutil>=2.8
Requires-Dist: pytest>=8.3.4 ; extra == 'dev'
Requires-Dist: pytest-asyncio>=0.23 ; extra == 'dev'
Requires-Dist: ruff>=0.15.10 ; extra == 'dev'
Requires-Python: >=3.11
Provides-Extra: dev
Description-Content-Type: text/markdown

# float-sdk

Python SDK for the Float platform. Provides typed domain models, API plumbing,
and trading primitives used to build applications against Float services.

## Installation

Requires Python 3.11+.

```bash
pip install float-sdk
```

From source:

```bash
pip install -e '.[dev]'
```

## Quick start

Create a `FloatClient` (or `FloatClientAsync` for asyncio) using OAuth2 client
credentials. The client manages authentication, token refresh, and HTTP
transport for you.

```python
import datetime as dt

from float_sdk import Environment, FloatClient
from float_sdk.float_api_trading.products.base.contractual import OptionType
from float_sdk.float_api_trading.products.fx.options import FXOptionModel
from float_sdk.float_api_trading.trading.parties import CounterpartyRole, PartyModel
from float_sdk.float_api_trading.trading.trades import TradeModel, TradeStatus, TradeType

with FloatClient(
    environment=Environment.PROD,
    client_id="...",
    client_secret="...",
) as client:
    trade = TradeModel(
        organization="org_123",
        trade_date=dt.date.today(),
        trade_time=dt.datetime.now(dt.UTC),
        trade_type=TradeType.EXECUTION,
        status=TradeStatus.LIVE,
        product=FXOptionModel(
            asset="EURUSD",
            notional_amount=1_000_000,
            notional_currency="EUR",
            option_type=OptionType.CALL,
            strike_price=1.20,
            expiration_date=dt.date.today() + dt.timedelta(days=30),
        ),
        party_1=PartyModel(entity="FOR8UP27PHTHYVLBNG30", role=CounterpartyRole.PARTY_1),
        party_2=PartyModel(entity="784F5XJDJFIQDTBV3E84", role=CounterpartyRole.PARTY_2),
    )

    created = client.trading.trades.create(trade)
    print(created.id)
```

## Client structure

The client exposes resources as nested namespaces:

```
client.trading.trades   # /v1/trading/trades
client.trading.deals    # /v1/trading/deals
```

Each resource exposes the same lifecycle methods where supported by the API:
`create`, `get`, `list`, `update`, `delete` (and `cancel` on trades).

### Trades

```python
created = client.trading.trades.create(trade)

trade = client.trading.trades.get(trade_id)

results = client.trading.trades.list(
    organization=["org_123"],
    type=[TradeType.EXECUTION],
    side=[CounterpartyRole.PARTY_2],
    limit=50,
    offset=0,
)
for t in results.results:
    ...
print(results.total_results, results.number_returned)

updated = client.trading.trades.update(trade)
client.trading.trades.delete(trade_id)
client.trading.trades.cancel(trade_id)
```

`list()` accepts `ids`, `limit`, `offset`, `expand`, `type`, `deal`, `view`,
`side`, `identifier`, `identifier_type`, `organization`. See the docstring on
`TradesResource.list` for the full reference.

### Deals

```python
deal = client.trading.deals.get(deal_id)

results = client.trading.deals.list(organization=["org_123"], limit=50)
```

## Async client

`FloatClientAsync` mirrors `FloatClient` for asyncio code. Use it as an async
context manager:

```python
import asyncio

from float_sdk import Environment, FloatClientAsync


async def main() -> None:
    async with FloatClientAsync(
        environment=Environment.PROD,
        client_id="...",
        client_secret="...",
    ) as client:
        trade = await client.trading.trades.get("trade_123")
        print(trade.id)


asyncio.run(main())
```

## Errors

All SDK errors derive from `FloatError`. HTTP responses are mapped to typed
exceptions so callers can handle specific failure modes without inspecting
status codes:

```
FloatError
└── APIError                    # has .detail, .request
    ├── APIConnectionError      # network failure
    │   └── APITimeoutError     # request timed out
    └── APIStatusError          # non-2xx response, has .response, .status_code
        ├── BadRequestError              # 400
        ├── AuthenticationError          # 401
        ├── PermissionDeniedError        # 403
        ├── NotFoundError                # 404
        ├── ConflictError                # 409
        ├── UnprocessableEntityError     # 422
        ├── RateLimitError               # 429
        └── InternalServerError          # 5xx
```

`exc.detail` is the human-readable string from the API's `{"detail": ...}`
response. For 422 validation errors, the list of pydantic errors is flattened
into a single `field.path: msg; field.path: msg` string. The full parsed body
is still available via `exc.response.json()`.

```python
from float_sdk import FloatClient, NotFoundError, RateLimitError

with FloatClient(...) as client:
    try:
        trade = client.trading.trades.get("missing")
    except NotFoundError as exc:
        print(exc.detail)
    except RateLimitError as exc:
        print("backoff:", exc.response.headers.get("Retry-After"))
```

5xx responses (502/503) and timeouts are retried automatically with
exponential backoff before being raised.

## License

See [LICENSE](LICENSE).
