Upside Python SDK
A Python client for the Upside decentralized
perpetuals exchange — REST reads (POST /info), signed writes (POST /exchange),
and realtime WebSocket streams.
- EIP-712 request signing (secp256k1) with the Agent and Typed paths — no API keys.
- Synchronous REST over
requests, threaded WebSocket overwebsocket-client. - Raw-dict responses,
TypedDictinputs, full type hints (shipspy.typed). - Agent (API-wallet) delegation, TP/SL, leverage/margin, and collateral actions.
The default environment is the QA testnet (
https://dev.upsidemax.xyz). Contract IDs, scales, and tick/step sizes are server-assigned — always read them fromconfigs, never hardcode.
Installation
pip install upside-python-sdk
Requires Python 3.9+. Runtime dependencies: requests, websocket-client,
eth-account, eth-utils.
Quick start
from upside import Info, Exchange
from upside.utils import constants
# --- reads (no signing) ---
info = Info(base_url=constants.QA_API_URL)
cfg = info.configs()
contract = next(c for c in cfg["contracts"] if c["status"] == "Active")
asset = contract["contractId"]
print(info.market_state(asset))
# --- writes (EIP-712 signed) ---
exchange = Exchange("0x<private-key>", base_url=constants.QA_API_URL)
# Register (QA requires an invite code from the Upside team). A 10,000 USDC
# test airdrop lands within ~10s.
exchange.register_account(invite_code="<invite-code>")
# Place a resting limit buy. Prices/sizes are raw integer strings — scale them
# with the contract's priceScale / qtyScale from configs.
exchange.order(asset=asset, is_buy=True, size="10", price="50")
Reading data — Info
All methods return the raw parsed JSON. See docs.upsidemax.xyz/info for response shapes.
info.configs() # contracts, coins, scales, tiers (cache this)
info.l2_book(asset) # full order book snapshot
info.market_state(asset) # mark/oracle/last price, funding
info.candle_snapshot(asset, "1m", start, end) # historical OHLCV
info.user_account(account_id, market_deployer_id)
info.user_orders(account_id, market_deployer_id, contract_id=0)
info.orders_by_ids(market_deployer_id, ["8280"])
info.orders_by_cloids(account_id, market_deployer_id, ["1778844423064"])
info.user_agents(account_id)
info.user_market_deployers(account_id)
info.share_group_state()
Trading — Exchange
from upside import Cloid
exchange.order(asset=1, is_buy=True, size="10", price="50", cloid=Cloid.from_int(1001))
exchange.market_order(asset=1, is_buy=False, size="5")
exchange.bulk_orders([...]) # up to 10 orders, one signature
exchange.cancel(asset=1, oid=15)
exchange.cancel_by_cloid(asset=1, cloid=1001)
exchange.cancel_all(asset=1)
exchange.modify(asset=1, oid=15, price="151", size="8")
exchange.update_leverage(asset=1, leverage=20)
exchange.update_margin_mode(asset=1, is_cross=False, is_hedge=True)
exchange.update_isolated_margin(asset=1, ntli=5000)
exchange.tp_sl(asset=1, tp_price="90000", sl_price="80000")
exchange.cancel_tp_sl(asset=1)
exchange.cancel_conditional(oid=123)
exchange.lock_collateral(market_deployer_id=1, coin_id=1, amount="1000")
exchange.transfer_between_deployers(1, 2, coin_id=1, amount="1000")
Order placement is asynchronous
A batch returns {"status": "accepted", "response": {"type": "order", "data": {"count": n}}}
— not the resting order id. Read the resulting state from
Info.user_orders / orders_by_cloids, or the orderUpdates / userFills
WebSocket channels. Cancels, modifies, and margin actions respond synchronously.
HTTP 200 ≠ success
Gateway failures (bad signature, reused nonce, rate limit) raise ClientError
(4xx) / ServerError (5xx). Business rejections come back as HTTP 200 — a per-item
error string in statuses[], or a non-zero errorCode in response.data.
Always inspect them.
Agent (API-wallet) delegation
Keep the master key offline; authorize a hot agent key to sign trades. The server routes agent-signed actions to the master account.
response, agent_key = master.approve_agent(agent_name="bot1") # generates a fresh key
agent = Exchange(agent_key, base_url=constants.QA_API_URL, account_id=master.account_id)
agent.order(asset=1, is_buy=True, size="10", price="50")
master.revoke_agent(agent.address)
WebSocket streams
info = Info(base_url=constants.QA_API_URL) # WS starts automatically
sid = info.subscribe({"type": "l2Book", "asset": "1"}, lambda m: print(m["data"]["bookVersion"]))
info.subscribe({"type": "trades", "asset": "1"}, print)
info.subscribe({"type": "orderUpdates", "user": "0x<address>"}, print) # private: pass the wallet address
info.subscribe({"type": "userFills", "user": "0x<address>"}, print)
info.unsubscribe({"type": "l2Book", "asset": "1"}, sid)
info.close()
Channels: l2Book, bbo, trades, candle, config (public) and
orderUpdates, openOrders, userFills (per-address). The client pings every
30s and auto-reconnects, replaying subscriptions. WebSocket does not push
position or balance changes — poll userAccount for those.
Signing
Every /exchange write is authorized by an EIP-712 signature over a fixed
domain (Exchange / 1 / chainId 9767 / zero verifying contract). The SDK
handles both paths automatically:
- Typed path —
registerAccount,approveAgent,revokeAgent,lockCollateral,unlockCollateral,transferBetweenDeployers. - Agent path — every other action (canonical-JSON
actionHash).
Nonces are strictly increasing millisecond timestamps managed per Exchange
instance (NonceManager). See
docs.upsidemax.xyz/guide/authentication.
Examples
Runnable scripts live in examples/. Copy config.json.example to
config.json, set your test wallet and invite code, then:
python examples/01_register_and_airdrop.py
python examples/03_place_and_cancel_order.py
python examples/06_websocket_streams.py
Development
make install # poetry install
make test # pytest
make lint # black --check + ruff
make typecheck # mypy
make check # all of the above
License
MIT — see LICENSE.
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 upside_python_sdk-0.1.0.tar.gz.
File metadata
- Download URL: upside_python_sdk-0.1.0.tar.gz
- Upload date:
- Size: 20.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/6.1.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a2b773032579e4832cd51865bfdd9eff66e5b983c3fae5ae6b96eb8847c336ce
|
|
| MD5 |
dc09040f19a332e9ecc41dae10d5cf0b
|
|
| BLAKE2b-256 |
0b0dde1f4e51e1d43cf64b230c4d3bc3efc37e4fe16b4e9c106ef15cdf73ee92
|
Provenance
The following attestation bundles were made for upside_python_sdk-0.1.0.tar.gz:
Publisher:
release.yml on upsidemax/upside-python-sdk
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
upside_python_sdk-0.1.0.tar.gz -
Subject digest:
a2b773032579e4832cd51865bfdd9eff66e5b983c3fae5ae6b96eb8847c336ce - Sigstore transparency entry: 2269069725
- Sigstore integration time:
-
Permalink:
upsidemax/upside-python-sdk@ef9ef0e741f658354ca412bf98a70b67b059ccb6 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/upsidemax
-
Access:
internal
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@ef9ef0e741f658354ca412bf98a70b67b059ccb6 -
Trigger Event:
workflow_dispatch
-
Statement type:
File details
Details for the file upside_python_sdk-0.1.0-py3-none-any.whl.
File metadata
- Download URL: upside_python_sdk-0.1.0-py3-none-any.whl
- Upload date:
- Size: 23.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/6.1.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
876d59fb7a280f9ea4f3357cd7d91859f3965dd836fcae6f0ef77232c2b9b34f
|
|
| MD5 |
97ed31369f9ac68a495a2c3a02f6a840
|
|
| BLAKE2b-256 |
b7932bfd5a19d55c1473a6c95980160c62878f5d9eeb21e1d293f75fede4dbd6
|
Provenance
The following attestation bundles were made for upside_python_sdk-0.1.0-py3-none-any.whl:
Publisher:
release.yml on upsidemax/upside-python-sdk
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
upside_python_sdk-0.1.0-py3-none-any.whl -
Subject digest:
876d59fb7a280f9ea4f3357cd7d91859f3965dd836fcae6f0ef77232c2b9b34f - Sigstore transparency entry: 2269070068
- Sigstore integration time:
-
Permalink:
upsidemax/upside-python-sdk@ef9ef0e741f658354ca412bf98a70b67b059ccb6 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/upsidemax
-
Access:
internal
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@ef9ef0e741f658354ca412bf98a70b67b059ccb6 -
Trigger Event:
workflow_dispatch
-
Statement type: