Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,30 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [0.3.0] - 2026-10-29

Multi-market perp API (`/v3/perp`) and `GET /v3/markets`.

### Added

- `client.get_markets()` (`GET /v3/markets`): the released markets, plus the early-access markets your API key can access. Returns `Market` models (`lnmarkets_sdk.rest.v3.models.markets`).
- `client.perp` (`PerpClient`) for `/v3/perp`: `get_ticker()`, `get_candles()` and `get_funding_settlements()`.
- `client.perp.isolated` (`PerpIsolatedClient`) for `/v3/perp/isolated`: `new_trade()`, `get_trade()`, `cancel()`, `cancel_all()`, `close()`, `add_margin()`, `cash_in()`, `update_stoploss()`, `update_takeprofit()`, `remove_stoploss()`, `remove_takeprofit()`, `get_open_trades()`, `get_running_trades()`, `get_closed_trades()`, `get_canceled_trades()` and `get_funding_fees()`.
- Perp models in `lnmarkets_sdk.rest.v3.models.perp`. Every perp call takes the market key (`instrument` + `pair`). Trades, tickers, funding settlements and funding fees include the quanto fields (`quanto_adjustment_rate`, `quanto_adjustment_fee`, `sum_quanto_adjustment_fees`), the notional fields, and the `mark` price.
- `client.perp.isolated.get_trade()` (`GET /v3/perp/isolated/trade`): one trade in any state and of any market, by `id` or by `client_id` (`GetPerpTradeParams`). Returns the variant that matches the trade state.
- `label` on `PerpOrder` and on perp trades: a free tag several trades can share. On `/perp`, `client_id` is a unique client order ID: reusing one fails with HTTP 409 `CONFLICT`, and older trades' tags now show as `label`.
- Cursor pagination for the perp history reads: pass `next_cursor` back as `cursor`. The cursor is an object (`PerpCursor(value=..., id=...)`), sent as `cursor[value]` and `cursor[id]` in the query.
- The public perp routes and `get_markets()` sign the request when the client has credentials, so early-access markets work.

### Deprecated

- `client.futures.get_ticker()`, `get_candles()`, `get_funding_settlements()` and every `client.futures.isolated` method. They still work. They are marked with `@deprecated` (PEP 702), so IDEs and type checkers strike the call through and show the `client.perp` method to use, and at runtime they raise a `DeprecationWarning` with the same hint: call it with `instrument="inverse_perp", pair="btc_usd"`. `client.futures.get_leaderboard()` and `client.futures.cross` have no perp replacement yet and are not deprecated.

### Fixed

- Request signature for bodies with nested whole floats (e.g. `{"stoploss": {"value": 64000.0}}`): nested values are now sent as `64000`, like the server's `JSON.stringify`, so the signature matches.
- `futures.isolated.update_takeprofit()` no longer prints its params.

## [0.2.0] - 2026-10-26

Models re-synced with the published v3 API reference.
Expand Down
90 changes: 82 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,18 @@ For public endpoints, you can just do this:

```python
from lnmarkets_sdk.rest.v3.http.client import LNMClient
from lnmarkets_sdk.rest.v3.models.perp import PerpMarketKey
import asyncio

async with LNMClient() as client:
ticker = await client.futures.get_ticker()
markets = await client.get_markets()
await asyncio.sleep(1)
leaderboard = await client.futures.get_leaderboard()
ticker = await client.perp.get_ticker(
PerpMarketKey(instrument="inverse_perp", pair="btc_usd")
)
```

Remember to sleep between requests, as the rate limit is 1 requests per second for public endpoints.
Remember to sleep between requests, as the rate limit is 1 request per second for public endpoints.

For endpoints that need authentication, you need to create an instance of the `LNMClient` class and provide your API credentials:

Expand Down Expand Up @@ -65,6 +68,50 @@ async with LNMClient(config) as client:
)
```

### Perp markets

LN Markets has more than one perp market: the Bitcoin perp (`inverse_perp` + `btc_usd`) and quanto perps such as qGOLD (`quanto_perp` + `xau_usdt`). The `client.perp` methods take the market key (`instrument` + `pair`). `client.get_markets()` lists the markets you can use; it also lists the early-access markets your API key can access.

```python
from lnmarkets_sdk.rest.v3.models.perp import (
GetPerpTradesParams,
PerpOrder,
PerpStoploss,
PerpTradeParams,
PerpUpdateTakeprofitParams,
)

GOLD = {"instrument": "quanto_perp", "pair": "xau_usdt"}

async with LNMClient(config) as client:
trade = await client.perp.isolated.new_trade(
PerpOrder(
**GOLD,
type="market",
side="buy",
margin=10_000, # or quantity=..., in the market's unit (ounces for qGOLD)
leverage=10,
stoploss=PerpStoploss(mode="trailing", value=0.05),
)
)
await client.perp.isolated.update_takeprofit(
PerpUpdateTakeprofitParams(id=trade.id, value=5_000, **GOLD)
)
closed = await client.perp.isolated.close(PerpTradeParams(id=trade.id, **GOLD))

# Reads without a market key cover every market.
running = await client.perp.isolated.get_running_trades()

# History reads use cursor pagination: pass `next_cursor` back as `cursor`.
params = GetPerpTradesParams(**GOLD, limit=50)
page = await client.perp.isolated.get_closed_trades(params)
while page.next_cursor:
params.cursor = page.next_cursor
page = await client.perp.isolated.get_closed_trades(params)
```

The `client.futures` methods that have a `client.perp` replacement (ticker, candles, funding settlements and all isolated methods) are deprecated and raise a `DeprecationWarning`. They still work and trade BTC/USD only. To move, call the `client.perp` method with the same name and `instrument="inverse_perp", pair="btc_usd"`. Note that `PerpOrder.stoploss` is an object (`PerpStoploss(mode=..., value=...)`), not a number plus `stoploss_mode`. On `client.perp`, `client_id` is a unique client order ID (reusing one fails with HTTP 409 `CONFLICT`): put tags that several trades share in `label`, and fetch a trade by either key with `client.perp.isolated.get_trade()`.

Check our [REST example](./examples/rest_v3.py) for more details.

### Available Methods
Expand All @@ -75,6 +122,9 @@ Check our [REST example](./examples/rest_v3.py) for more details.
# Ping
client.ping()

# Markets
client.get_markets()

# Account 🔒
client.account.get_account()
client.account.get_bitcoin_address()
Expand All @@ -88,17 +138,41 @@ client.account.get_on_chain_deposits()
client.account.get_on_chain_withdrawals()
client.account.read_notifications()

# Futures
client.futures.get_ticker()
# Perp
client.perp.get_ticker()
client.perp.get_candles()
client.perp.get_funding_settlements()

# Perp Isolated 🔒
client.perp.isolated.new_trade()
client.perp.isolated.get_trade()
client.perp.isolated.get_running_trades()
client.perp.isolated.get_open_trades()
client.perp.isolated.get_closed_trades()
client.perp.isolated.get_canceled_trades()
client.perp.isolated.close()
client.perp.isolated.cancel()
client.perp.isolated.cancel_all()
client.perp.isolated.add_margin()
client.perp.isolated.cash_in()
client.perp.isolated.update_stoploss()
client.perp.isolated.update_takeprofit()
client.perp.isolated.remove_stoploss()
client.perp.isolated.remove_takeprofit()
client.perp.isolated.get_funding_fees()

# Futures (BTC/USD only)
client.futures.get_ticker() # deprecated, use client.perp
client.futures.get_leaderboard()
client.futures.get_candles()
client.futures.get_funding_settlements()
client.futures.get_candles() # deprecated, use client.perp
client.futures.get_funding_settlements() # deprecated, use client.perp

# Futures Isolated 🔒
# Futures Isolated 🔒 (deprecated, use client.perp.isolated)
client.futures.isolated.new_trade()
client.futures.isolated.get_running_trades()
client.futures.isolated.get_open_trades()
client.futures.isolated.get_closed_trades()
client.futures.isolated.get_canceled_trades()
client.futures.isolated.close()
client.futures.isolated.cancel()
client.futures.isolated.cancel_all()
Expand Down
Loading
Loading