What We’ll Build Together
In this tutorial, we’ll build a small Python trading script against the DerivaDEX testnet. By the end, you will have a script that:
- connects to your funded
main strategy
- prints a live strategy snapshot
- buys
0.1 ETHP at market
- places a resting limit buy order 10% below the current mark price
- cancels that resting order
We’ll use testnet so you can see one complete trade lifecycle without touching real funds.
Prerequisites
- Python
3.10
- a funded
main strategy on DerivaDEX testnet for the same wallet you will trade with
- a Sepolia RPC URL, which is the HTTPS endpoint your script uses to reach Ethereum
- the private key for that testnet wallet
If you still need to fund the strategy, work through Your First Trade on DerivaDEX first. That tutorial gets collateral into main, which this script uses as-is.
Step 1: Install the Python client
Open a terminal and create a fresh working directory:
mkdir your-first-api-trade
cd your-first-api-trade
python3.10 -m venv .venv
source .venv/bin/activate
python -m pip install --upgrade pip
python -m pip install ddx-python==1.1.2 httpx tomli
tomli matters on Python 3.10 because our script will read config.toml, and tomllib is built into Python only from 3.11 onward.
You should be able to confirm the environment with:
python --version
python -m pip show ddx-python
You should see Python 3.10.x and the installed ddx-python package details.
Step 2: Save your testnet connection settings
Create a file called config.toml in that same directory:
[connection]
base_url = "https://testnet.derivadex.io"
ws_url = "wss://testnet.derivadex.io/realtime-api"
rpc_url = "https://sepolia.infura.io/v3/YOUR_INFURA_PROJECT_ID"
contract_deployment = "testnet"
# Export the signing key before running the script:
# export PRIVATE_KEY="0x..."
Replace YOUR_INFURA_PROJECT_ID with your own Sepolia RPC project, or swap in another Sepolia RPC URL.
At this point, your working directory should contain:
Step 3: Connect to testnet and print your strategy
Now create rest_quickstart.py with this starting version:
import asyncio
import json
import logging
import os
from pathlib import Path
try:
import tomllib
except ModuleNotFoundError:
import tomli as tomllib
from ddx import load_testnet
from ddx.derivadex_client import DerivaDEXClient
CONFIG_PATH = Path(__file__).with_name("config.toml")
STRATEGY_ID = "main"
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s | %(levelname)-8s | %(message)s",
datefmt="%Y-%m-%d %H:%M:%S",
)
logger = logging.getLogger("rest_quickstart")
def load_config() -> dict:
with CONFIG_PATH.open("rb") as config_file:
return tomllib.load(config_file)
def print_json(label: str, payload) -> None:
logger.info("%s", label)
print(json.dumps(payload.model_dump(), indent=2))
async def main() -> None:
config = load_config()
connection = config["connection"]
load_testnet()
async with DerivaDEXClient(
base_url=connection["base_url"],
ws_url=connection["ws_url"],
rpc_url=connection["rpc_url"],
contract_deployment=connection["contract_deployment"],
private_key=os.environ["PRIVATE_KEY"],
) as client:
trader_address = client.web3_account.address
trader_address_with_prefix = f"0x00{trader_address[2:]}"
logger.info("Connected as trader %s", trader_address)
strategy_response = await client.trade.get_strategy(
trader_address_with_prefix,
STRATEGY_ID,
)
print_json("Strategy response", strategy_response)
if __name__ == "__main__":
asyncio.run(main())
This first version does only two things: it opens the DerivaDEX client, then asks for the main strategy that belongs to your wallet.
Use the private key for the same wallet that owns the funded testnet strategy, then run the script:
export PRIVATE_KEY="0xYOUR_TESTNET_PRIVATE_KEY"
python rest_quickstart.py
You should see output in this order:
Connected as trader ...
Strategy response
The JSON printed under Strategy response is your confirmation that the client is connected to the right wallet and can read the strategy you funded earlier.
From here on, each request follows the same path: your script builds the order locally, your wallet signs it, and the client sends only the encrypted request to DerivaDEX.
Step 4: Submit your first market order
Now we’ll teach the script to buy 0.1 ETHP at market.
First, add these imports and constants near the top of rest_quickstart.py:
from ddx._rust.common.enums import OrderSide, OrderType
from ddx._rust.common.requests.intents import OrderIntent
from ddx._rust.decimal import Decimal
SYMBOL = "ETHP"
TRADE_AMOUNT = Decimal("0.1")
Then add this block inside main(), directly after print_json("Strategy response", strategy_response):
market_order_intent = OrderIntent(
SYMBOL,
STRATEGY_ID,
OrderSide.Bid,
OrderType.Market,
client.signed.get_nonce(),
TRADE_AMOUNT,
Decimal("0"),
Decimal("0"),
None,
)
market_order_receipt = await client.signed.place_order(market_order_intent)
print_json("Market order submitted", market_order_receipt)
client.signed.get_nonce() supplies a nonce, which is a one-time unique value that keeps this request distinct from every other one. client.signed.place_order(...) handles the wallet signature and encrypted submission for you, so this tutorial can stay focused on building the order itself.
Run the script again:
python rest_quickstart.py
You should now see a third label in the output:
Market order submitted
The JSON under that label is your confirmation that DerivaDEX accepted the signed market order request.
Note: Each time you run the script from here onward, the market-order block will submit another 0.1 ETHP market buy. That is fine on testnet, but if you want to keep working on the later steps without adding more position, comment out the market-order block before you run the script again.
Step 5: Place and cancel a resting limit order
Next, we’ll fetch the live mark price and use it to place a second buy order well below the market so it rests on the order book instead of filling immediately. The mark price is the fair price DerivaDEX uses for margin and risk checks.
First, update the intent import so it includes CancelOrderIntent:
from ddx._rust.common.requests.intents import CancelOrderIntent, OrderIntent
Then add this block directly after the market-order block:
tickers_response = await client.market.get_tickers(symbol=SYMBOL)
current_price = Decimal(tickers_response.value[0].mark_price)
limit_price = (current_price * Decimal("0.9")).quantize(1)
logger.info("Current %s mark price: %s", SYMBOL, current_price)
logger.info("Limit buy price: %s", limit_price)
limit_order_intent = OrderIntent(
SYMBOL,
STRATEGY_ID,
OrderSide.Bid,
OrderType.Limit,
client.signed.get_nonce(),
TRADE_AMOUNT,
limit_price,
Decimal("0"),
None,
)
limit_order_receipt = await client.signed.place_order(limit_order_intent)
print_json("Limit order submitted", limit_order_receipt)
await asyncio.sleep(5)
cancel_order_intent = CancelOrderIntent(
SYMBOL,
limit_order_intent.hash(),
client.signed.get_nonce(),
None,
)
cancel_order_receipt = await client.signed.cancel_order(cancel_order_intent)
print_json("Cancel request submitted", cancel_order_receipt)
This version reads the current ETHP mark price, multiplies it by 0.9, and rounds to a whole dollar before placing the limit order. That keeps the order about 10% below the market, which usually leaves it resting on the order book instead of filling right away.
The five-second pause gives the limit order a moment to appear on the order book before the cancel request goes out. limit_order_intent.hash() gives the cancel request the exact order hash it needs to target that resting order.
Run the script one more time:
python rest_quickstart.py
You should now see the full sequence:
Connected as trader ...
Strategy response
Market order submitted
Current ETHP mark price: ...
Limit order submitted
Cancel request submitted
Once you can produce that sequence reliably, you have a working baseline script for DerivaDEX’s API trading flow.
Step 7: Watch the trade land in testnet
If you keep the DerivaDEX testnet open in another tab while you run the script, you can usually watch the results land:
Positions shows the 0.1 ETHP market buy after it fills
Open Orders shows the resting limit buy briefly on the order book, then drops it after the cancel request lands
That gives you both views of the same workflow: terminal output from the script and trading state inside the exchange UI.
Lesson complete
You have now:
- connected the DerivaDEX Python client to testnet
- loaded strategy state for your trading wallet
- submitted a market order
- priced and placed a resting limit order from the live mark price
- canceled that resting order cleanly
You now have a working Python baseline for DerivaDEX API trading on testnet.Last modified on April 24, 2026