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
6 changes: 3 additions & 3 deletions .github/workflows/check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,6 @@ jobs:
uv run --isolated --python=3.13 pytest
uv run --isolated --python=3.14 pytest
env:
V3_API_KEY: ${{ secrets.V3_API_KEY }}
V3_API_KEY_SECRET: ${{ secrets.V3_API_KEY_SECRET }}
V3_API_KEY_PASSPHRASE: ${{ secrets.V3_API_KEY_PASSPHRASE }}
TEST_API_KEY: ${{ secrets.TEST_API_KEY }}
TEST_API_SECRET: ${{ secrets.TEST_API_SECRET }}
TEST_API_PASSPHRASE: ${{ secrets.TEST_API_PASSPHRASE }}
78 changes: 75 additions & 3 deletions examples/basic.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,16 @@

from lnmarkets_sdk.v3.http.client import APIAuthContext, APIClientConfig, LNMClient
from lnmarkets_sdk.v3.models.account import GetLightningDepositsParams
from lnmarkets_sdk.v3.models.futures_cross import (
FuturesCrossOrderLimit,
from lnmarkets_sdk.v3.models.futures_cross import FuturesCrossOrderLimit
from lnmarkets_sdk.v3.models.futures_data import (
GetCandlesParams,
GetFundingSettlementsParams,
)
from lnmarkets_sdk.v3.models.futures_isolated import (
GetClosedTradesParams,
GetIsolatedFundingFeesParams,
)
from lnmarkets_sdk.v3.models.oracle import GetLastPriceParams

load_dotenv()

Expand Down Expand Up @@ -58,6 +65,44 @@ async def example_public_endpoints():
print("\n--- Ping ---")
print(f"Response: {ping_response}")

# Get server time
await asyncio.sleep(1)
time_response = await client.time()
print("\n--- Server Time ---")
print(f"Response: {time_response}")

# Get futures candles
await asyncio.sleep(1)
candles_params = GetCandlesParams(
from_="2024-01-01T00:00:00.000Z",
range="1h",
limit=5,
)
candles = await client.futures.get_candles(candles_params)
print("\n--- Futures Candles (Last 5) ---")
for candle in candles.data[:3]: # Show first 3
print(
f"Time: {candle.time}, OHLC: {candle.open}/{candle.high}/{candle.low}/{candle.close}"
)

# Get funding settlements
await asyncio.sleep(1)
funding_settlements = await client.futures.get_funding_settlements(
GetFundingSettlementsParams(limit=5)
)
print("\n--- Funding Settlements (Last 5) ---")
for settlement in funding_settlements.data[:3]: # Show first 3
print(
f"Rate: {settlement.funding_rate}, Price: {settlement.fixing_price}, Time: {settlement.time}"
)

# Get oracle last price
await asyncio.sleep(1)
last_prices = await client.oracle.get_last_price(GetLastPriceParams(limit=5))
print("\n--- Oracle Last Price (Last 5) ---")
for price in last_prices[:3]: # Show first 3
print(f"Price: {price.last_price}, Time: {price.time}")


async def example_authenticated_endpoints():
"""Example: Use authenticated endpoints with credentials."""
Expand All @@ -84,7 +129,7 @@ async def example_authenticated_endpoints():
secret=secret,
passphrase=passphrase,
),
network="testnet4",
network="mainnet",
timeout=60.0, # 60 second timeout (default is 30s)
)

Expand Down Expand Up @@ -123,6 +168,33 @@ async def example_authenticated_endpoints():
f" {side} - Margin: {trade.margin} sats, Leverage: {trade.leverage}x, PL: {trade.pl} sats"
)

# Get open trades
await asyncio.sleep(1)
open_trades = await client.futures.isolated.get_open_trades()
print(f"\n--- Open Isolated Trades (Count: {len(open_trades)}) ---")
for trade in open_trades[:3]: # Show first 3
side = "LONG" if trade.side == "b" else "SHORT"
print(f" {side} - Price: {trade.price}, Quantity: {trade.quantity}")

# Get closed trades
await asyncio.sleep(1)
closed_trades = await client.futures.isolated.get_closed_trades(
GetClosedTradesParams(limit=5)
)
print(f"\n--- Closed Isolated Trades (Last {len(closed_trades.data)}) ---")
for trade in closed_trades.data[:3]: # Show first 3
side = "LONG" if trade.side == "b" else "SHORT"
print(f" {side} - PL: {trade.pl} sats, Closed: {trade.closed}")

# Get isolated funding fees
await asyncio.sleep(1)
isolated_fees = await client.futures.isolated.get_funding_fees(
GetIsolatedFundingFeesParams(limit=5)
)
print(f"\n--- Isolated Funding Fees (Last {len(isolated_fees.data)}) ---")
for fee in isolated_fees.data[:3]: # Show first 3
print(f"Fee: {fee.fee} sats, Time: {fee.time}")

# Get cross margin position
try:
position = await client.futures.cross.get_position()
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ build-backend = "hatchling.build"
urls = { "Homepage" = "https://github.com/ln-markets/sdk-python", "Repository" = "https://github.com/ln-markets/sdk-python", "Bug Tracker" = "https://github.com/ln-markets/sdk-python/issues" }

name = "lnmarkets-sdk"
version = "0.0.15"
version = "0.0.18"
description = "LN Markets API Python SDK"
readme = "README.md"
license = { text = "MIT" }
Expand Down
3 changes: 3 additions & 0 deletions src/lnmarkets_sdk/v3/_internal/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"""Internal HTTP client - not part of public API."""

import json
import re
from collections.abc import Mapping
from urllib.parse import urlencode

Expand Down Expand Up @@ -63,6 +64,8 @@ async def request(
if params_dict:
if method == "GET":
data = f"?{urlencode(params_dict)}"
data = re.sub(r"=(True)", "=true", data)
data = re.sub(r"=(False)", "=false", data)
else:
data = json.dumps(params_dict, separators=(",", ":"))
headers.update({"Content-Type": "application/json"})
Expand Down
2 changes: 0 additions & 2 deletions src/lnmarkets_sdk/v3/_internal/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,8 +82,6 @@ class PaginatedResponse[T](BaseModel, BaseConfig):
data: list[T] = Field(..., description="Array of items")
next_cursor: SkipValidation[str] | None = Field(
default=None,
serialization_alias="nextCursor",
validation_alias="nextCursor",
description="Cursor for fetching the next page, null if no more pages",
)

Expand Down
13 changes: 13 additions & 0 deletions src/lnmarkets_sdk/v3/http/client/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -135,5 +135,18 @@ async def ping(self) -> str:
"""Ping the API to check connectivity."""
return await self.request_raw("GET", "/ping", credentials=False)

async def time(self) -> str:
"""
Get server time.

Example:
```python
async with LNMClient(config) as client:
time_response = await client.time()
print(f"Server time: {time_response}")
```
"""
return await self.request_raw("GET", "/time", credentials=False)


__all__ = ["APIAuthContext", "APIClientConfig", "LNMClient"]
24 changes: 12 additions & 12 deletions src/lnmarkets_sdk/v3/http/client/account.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,19 +12,19 @@
DepositLightningResponse,
GetBitcoinAddressResponse,
GetInternalDepositsParams,
GetInternalDepositsResponse,
GetInternalWithdrawalsParams,
GetInternalWithdrawalsResponse,
GetLightningDepositsParams,
GetLightningDepositsResponse,
GetLightningWithdrawalsParams,
GetLightningWithdrawalsResponse,
GetNotificationsParams,
GetOnChainDepositsParams,
GetOnChainDepositsResponse,
GetOnChainWithdrawalsParams,
GetOnChainWithdrawalsResponse,
InternalDeposit,
InternalWithdrawal,
LightningDeposits,
LightningWithdrawal,
Notification,
OnChainDeposit,
OnChainWithdrawal,
WithdrawInternalParams,
WithdrawInternalResponse,
WithdrawLightningParams,
Expand Down Expand Up @@ -217,7 +217,7 @@ async def get_lightning_deposits(
"/account/deposits/lightning",
params=params,
credentials=True,
response_model=PaginatedResponse[GetLightningDepositsResponse],
response_model=PaginatedResponse[LightningDeposits],
)

async def get_lightning_withdrawals(
Expand Down Expand Up @@ -247,7 +247,7 @@ async def get_lightning_withdrawals(
"/account/withdrawals/lightning",
params=params,
credentials=True,
response_model=PaginatedResponse[GetLightningWithdrawalsResponse],
response_model=PaginatedResponse[LightningWithdrawal],
)

async def get_internal_deposits(
Expand All @@ -274,7 +274,7 @@ async def get_internal_deposits(
"/account/deposits/internal",
params=params,
credentials=True,
response_model=PaginatedResponse[GetInternalDepositsResponse],
response_model=PaginatedResponse[InternalDeposit],
)

async def get_internal_withdrawals(
Expand All @@ -301,7 +301,7 @@ async def get_internal_withdrawals(
"/account/withdrawals/internal",
params=params,
credentials=True,
response_model=PaginatedResponse[GetInternalWithdrawalsResponse],
response_model=PaginatedResponse[InternalWithdrawal],
)

async def get_on_chain_deposits(
Expand Down Expand Up @@ -331,7 +331,7 @@ async def get_on_chain_deposits(
"/account/deposits/on-chain",
params=params,
credentials=True,
response_model=PaginatedResponse[GetOnChainDepositsResponse],
response_model=PaginatedResponse[OnChainDeposit],
)

async def get_on_chain_withdrawals(
Expand Down Expand Up @@ -361,7 +361,7 @@ async def get_on_chain_withdrawals(
"/account/withdrawals/on-chain",
params=params,
credentials=True,
response_model=PaginatedResponse[GetOnChainWithdrawalsResponse],
response_model=PaginatedResponse[OnChainWithdrawal],
)

async def get_notifications(self, params: GetNotificationsParams | None = None):
Expand Down
45 changes: 16 additions & 29 deletions src/lnmarkets_sdk/v3/models/account.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@ class Account(BaseModel, BaseConfig):
)


class GetOnChainDepositsResponse(BaseModel, BaseConfig):
class OnChainDeposit(BaseModel, BaseConfig):
"""On-chain deposit item."""

amount: SkipValidation[float] = Field(..., description="The amount of the deposit")
block_height: SkipValidation[int] | None = Field(
default=None, description="The block height of the deposit"
Expand All @@ -47,7 +49,9 @@ class GetOnChainDepositsResponse(BaseModel, BaseConfig):
)


class GetInternalDepositsResponse(BaseModel, BaseConfig):
class InternalDeposit(BaseModel, BaseConfig):
"""Internal deposit item."""

amount: SkipValidation[float] = Field(
..., description="Amount of the deposit (in satoshis)"
)
Expand All @@ -62,7 +66,9 @@ class GetInternalDepositsResponse(BaseModel, BaseConfig):
)


class GetInternalWithdrawalsResponse(BaseModel, BaseConfig):
class InternalWithdrawal(BaseModel, BaseConfig):
"""Internal withdrawal item."""

amount: SkipValidation[float] = Field(
..., description="Amount of the transfer (in satoshis)"
)
Expand All @@ -77,7 +83,7 @@ class GetInternalWithdrawalsResponse(BaseModel, BaseConfig):
)


class GetLightningDepositsResponse(BaseModel, BaseConfig):
class LightningDeposits(BaseModel, BaseConfig):
amount: SkipValidation[float] | None = Field(
None, description="Amount of the deposit (in satoshis)"
)
Expand All @@ -98,7 +104,9 @@ class GetLightningDepositsResponse(BaseModel, BaseConfig):
)


class GetLightningWithdrawalsResponse(BaseModel, BaseConfig):
class LightningWithdrawal(BaseModel, BaseConfig):
"""Lightning withdrawal item."""

amount: SkipValidation[float] = Field(
..., description="Amount of the withdrawal (in satoshis)"
)
Expand All @@ -119,7 +127,9 @@ class GetLightningWithdrawalsResponse(BaseModel, BaseConfig):
)


class GetOnChainWithdrawalsResponse(BaseModel, BaseConfig):
class OnChainWithdrawal(BaseModel, BaseConfig):
"""On-chain withdrawal item."""

address: SkipValidation[str] = Field(..., description="Address to withdraw to")
amount: SkipValidation[float] = Field(..., description="Amount to withdraw")
created_at: SkipValidation[str] = Field(
Expand All @@ -139,29 +149,6 @@ class GetOnChainWithdrawalsResponse(BaseModel, BaseConfig):
)


class InternalTransfer(BaseModel, BaseConfig):
amount: SkipValidation[float]
created_at: SkipValidation[str]
from_uid: SkipValidation[UUID]
from_username: SkipValidation[str]
id: SkipValidation[UUID]
settled_at: SkipValidation[str] | None
success: SkipValidation[bool] | None
to_uid: SkipValidation[UUID]
to_username: SkipValidation[str]


class PendingOnChainWithdrawalRequest(BaseModel, BaseConfig):
address: SkipValidation[str]
amount: SkipValidation[float]
created_at: SkipValidation[str]
fee: SkipValidation[float] | None
id: SkipValidation[UUID]
status: SkipValidation[Literal["pending"]]
tx_id: None = None
updated_at: SkipValidation[str]


class DepositLightningResponse(BaseModel, BaseConfig):
deposit_id: SkipValidation[UUID] = Field(..., description="Deposit ID")
payment_request: SkipValidation[str] = Field(
Expand Down
Loading
Loading