Liberal Alpha Python SDK for interacting with gRPC-based backend
Project description
Liberal Alpha Python SDK (Historical HTTP APIs)
This SDK provides historical upload, historical download, and real-time subscription APIs (HTTP + WebSocket).
Install
pip install liberal_alpha
# Optional: override default API base (default is https://api.librealpha.com)
export LIBALPHA_API_BASE="https://api.librealpha.com"
# Upload auth (X-API-Key)
export LIBALPHA_API_KEY="YOUR_API_KEY"
# Download auth (used to obtain JWT via /api/users/auth)
export LIBALPHA_PRIVATE_KEY="0xYOUR_PRIVATE_KEY"
# Optional: record private key (used to encrypt uploads for encrypted records, and decrypt encrypted WS payloads)
export LIBALPHA_RECORD_PRIVATE_KEY="0xYOUR_RECORD_PRIVATE_KEY"
Initialize Client
from liberal_alpha.client import LiberalAlphaClient
# api_base defaults to https://api.librealpha.com
# can be overridden by env LIBALPHA_API_BASE or by passing api_base=...
client = LiberalAlphaClient(
api_key="YOUR_API_KEY", # optional if using env LIBALPHA_API_KEY
private_key="0xYOUR_PRIVATE_KEY", # optional if using env LIBALPHA_PRIVATE_KEY
)
## Historical Upload API (Python) - protobuf stream (/api/entries/history/upload)
This is a **historical backfill** uploader that sends protobuf `DataEntry` messages in a length-prefixed stream:
- Request body format: `[4-byte big-endian length][DataEntry][4-byte length][DataEntry]...`
- Backend groups entries **by minute**; this SDK implementation will also **bucket by minute** and send **1 minute per request**.
- Auth: `X-API-Key` (required).
- Data is carried per-symbol in `symbol_values`:
- For non-encrypted records: `symbol_values[i].values.items = [float, ...]`
- For encrypted records: `symbol_values[i].encrypted_payload = <bytes>` (AES-256-GCM)
Client method (recommended):
```python
def upload_data(
record_id: int,
df: pd.DataFrame,
api_key: str | None = None,
private_key: str | None = None,
) -> None:
...
api_key: optional override; falls back toclient.api_key, then envLIBALPHA_API_KEY.private_key: record private key for encrypting payload when the record is encrypted. If omitted, the SDK uses envLIBALPHA_RECORD_PRIVATE_KEY(if set). This is different fromLIBALPHA_PRIVATE_KEY(user auth key for JWT).
Note: the SDK also exposes a module-level upload_data(...) with the same arguments; client.upload_data(...)
delegates to it internally. (We only show the signature once here to avoid confusion.)
DataFrame format:
- Required columns:
symbol(str): symbol_id string (e.g."1","3"). If you pass non-numeric symbol names, the SDK will best-effort map them to symbol_id via/api/records/user-records(target_symbols).timestamp(datetime64 or unix timestamp; will be normalized to UTC and uploaded in microseconds)
- Feature columns (choose ONE approach):
- Provide a
featurescolumn containing a list of floats (or a string like"[1.0, nan, 2.0]"), OR - Provide user-defined float columns matching the record schema; the SDK will fetch feature order from
/api/records/{id}.
- Provide a
Upload packing behavior:
- 1 request = 1 minute (backend requirement)
- Within that minute, the SDK aggregates rows by exact timestamp into 1
DataEntry. - Each aggregated
DataEntrycarries multiple symbols insymbol_values:[{symbol_id, values.items}, {symbol_id, values.items}, ...]
Example (upload one minute from a previously-downloaded CSV):
import pandas as pd
from liberal_alpha.client import LiberalAlphaClient
df = pd.read_csv("history_1h_record12_BTC.csv")
# pick the first minute only (backend requires 1 minute per upload)
first_minute = int(df["timestamp"].iloc[0] // 60_000_000)
df1 = df[df["timestamp"].apply(lambda x: int(x // 60_000_000) == first_minute)].copy()
df1 = df1[["symbol", "timestamp", "features"]]
client = LiberalAlphaClient(api_key="YOUR_API_KEY")
client.upload_data(
record_id=12,
df=df1,
# api_key="YOUR_API_KEY", # optional override; otherwise uses client.api_key (or env LIBALPHA_API_KEY)
# private_key="YOUR_RECORD_PRIVATE_KEY", # only needed if record is encrypted (or set LIBALPHA_RECORD_PRIVATE_KEY)
)
Historical Download API (Python)
def download_data( record_id: int, symbols: list[str], dates: list[int], tz_info: datetime.tzinfo | str = "Asia/Singapore" ) -> pandas.DataFrame: pass
Historical File Download API (Python) - protobuf (length-prefixed)
def download_history_data(
record_id: int,
symbol: str,
start: datetime | str | int,
end: datetime | str | int,
private_key: str | None = None,
) -> pandas.DataFrame:
pass
Parameters:
- record_id: the record id to download
- symbol: symbol_id string (e.g.
"1","3") - start/end: datetime | ISO string | unix timestamp (sec/ms/us)
- private_key: optional, record private key for decrypting
symbol_values[].encrypted_payload
Notes:
- Backend
/api/entries/download-linksenforcesend-start <= 24h(microseconds). The SDK automatically splits long ranges into multiple 24h windows and merges results locally. - Returned timestamps are in microseconds.
- DataEntry uses
repeated SymbolValues symbol_values; each row corresponds to one symbol_id. - When data is encrypted (
symbol_values[].encrypted_payloadpresent), passprivate_keyto decrypt.
Example
from liberal_alpha.client import LiberalAlphaClient
import datetime as dt
client = LiberalAlphaClient(
private_key="0xYOUR_PRIVATE_KEY", # or api_key="YOUR_API_KEY"
api_base="https://api.librealpha.com",
)
start = dt.datetime(2026, 1, 1, 0, 0, 0, tzinfo=dt.timezone.utc)
end = dt.datetime(2026, 1, 3, 0, 0, 0, tzinfo=dt.timezone.utc) # >24h is OK (SDK will split)
df = client.download_history_data(
record_id=2,
symbol="1",
start=start,
end=end,
private_key="YOUR_RECORD_PRIVATE_KEY", # for decrypting encrypted data
)
print(df.head())
print("rows:", len(df))
Real-time Subscribe API (Python) - WebSocket ws/data
from typing import List, Callable
def subscribe_data(
record_id: int,
*,
symbols: List[str],
on_data_fn: Callable[[str, pd.DataFrame], None],
private_key: str | None = None,
) -> None:
"""Subscribes to live data for a record into a DataFrame (schema matches publish).
Args:
record_id: Backend record identifier.
symbols: Symbols to filter by (required).
on_data_fn: Callback function invoked upon receiving new data. The
function takes two arguments: the symbol (str) and a DataFrame
containing the new data for that symbol.
private_key: Optional, record private key for decrypting encrypted_payload.
"""
Parameters:
- record_id: must be in user's subscriptions (from /api/subscriptions)
- symbols: symbol_id strings to filter by (required). Only rows with symbol_id in this list are passed to on_data_fn.
- on_data_fn: callback invoked upon receiving new data. Takes (symbol: str, df: DataFrame).
- private_key: optional, record private key for decrypting
symbol_values[].encrypted_payload; required if data is encrypted.
Notes:
- Connects to ws/data, sends { wallet_address, record_id } on open.
- If data is encrypted (
symbol_values[].encrypted_payloadhas value), pass private_key or set LIBALPHA_RECORD_PRIVATE_KEY. - DataFrame schema matches download_history_data (entry_id, record_id, symbol, features, timestamps).
- Blocking; run in a daemon thread for long-lived subscription.
Example
import threading
import pandas as pd
from liberal_alpha import LiberalAlphaClient
client = LiberalAlphaClient(api_key="YOUR_API_KEY", api_base="https://api.librealpha.com")
def on_data_fn(symbol: str, df: pd.DataFrame):
print(f"[{symbol}] entry_id={df['entry_id'].iloc[0]} features_len={len(df['features'].iloc[0])}")
t = threading.Thread(
target=client.subscribe_data,
kwargs={"record_id": 2, "symbols": ["1", "3"], "on_data_fn": on_data_fn, "private_key": "YOUR_RECORD_KEY"},
daemon=True,
)
t.start()
Historical Download API (download_data) Parameters
-record_id: the record id to download
-symbols: list of symbols, e.g. ["BTCUSDT", "ETHUSDT"]
If you pass [], the SDK will automatically fetch all symbols (if supported by backend).
-dates: list of local dates in YYYYMMDD format, e.g. [20251214, 20251215]
If you pass [], no date filter is applied.
-tz_info: controls how local_date is computed
"Asia/Singapore" (IANA tz string)
numeric offsets like 8, -4, or strings like "+8", "-4"
8 means UTC+8 (SGT/HKT)
-4 means UTC-4 (NYC during DST)
Example
from liberal_alpha.client import LiberalAlphaClient
client = LiberalAlphaClient( private_key="0xYOUR_PRIVATE_KEY", # api_base defaults to https://api.librealpha.com )
df = client.download_data( record_id=24, symbols=[], # empty => auto fetch all symbols dates=[], # empty => no date filter tz_info="Asia/Singapore" # or tz_info=8 )
print(df.head()) print("rows:", len(df))
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file liberal_alpha-0.2.0.tar.gz.
File metadata
- Download URL: liberal_alpha-0.2.0.tar.gz
- Upload date:
- Size: 45.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.10
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f1fcbca8c7ac71eb9ee8c039a56b38e7b3ea58c6fe58aeedf52fb6a7304c58ca
|
|
| MD5 |
fe08be10ec902e1c1ee5d991f7bf7aae
|
|
| BLAKE2b-256 |
555058c8e22bdb7dddcdd9caba4a38c534c48f97057b33912a421b5bda577a1b
|
File details
Details for the file liberal_alpha-0.2.0-py3-none-any.whl.
File metadata
- Download URL: liberal_alpha-0.2.0-py3-none-any.whl
- Upload date:
- Size: 45.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.10
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
874e79a7ef7fa30aadf0c0381fe71ee7d9989898ae481c44101a77a3d1325e7b
|
|
| MD5 |
7195d70408cc1d3b5ca8746b83c41fcd
|
|
| BLAKE2b-256 |
4e4893eb90c051553e611a3dae3546f7360f81d81ca18cb0b7ce927d1bd78de8
|