Skip to content

Trading Examples

This page provides examples of trading operations with the Meridian Python SDK.

Setup

All examples on this page use the AsyncRESTClient. Here's the basic pattern used throughout:

import asyncio
from decimal import Decimal
from meridian import AsyncRESTClient

async def main():
    # Initialize client
    client = await AsyncRESTClient.create({
        "base_url": "https://api.meridian.xyz",
        "chain_config": {
            "rpc_url": "https://rpc.mainnet.chain.robinhood.com",
            "private_key": "your_private_key"
        }
    })

    # Your code here

    await client.close()

asyncio.run(main())

The examples below show the code that goes in the "Your code here" section. For a complete standalone example, see Creating orders.

Creating orders

This complete example shows the full pattern including imports, initialization, and cleanup:

import asyncio
from decimal import Decimal
from meridian import AsyncRESTClient

async def create_orders():
    # Initialize client with your private key
    client = await AsyncRESTClient.create({
        "base_url": "https://api.meridian.xyz",
        "chain_config": {
            "rpc_url": "https://rpc.mainnet.chain.robinhood.com",
            "private_key": "your_private_key"
        }
    })

    # Place a limit buy order
    limit_buy = await client.create_order(
        order_type="LIMIT",
        quantity=Decimal("0.01"),  # Buy 0.01 BTC
        side=0,         # 0 for buy
        price=Decimal("80000"),  # Price in USD
        ticker="BTCUSD",
        time_in_force="GTD",  # Good Til Date
        post_only=True
    )

    # Place a limit sell order
    limit_sell = await client.create_order(
        order_type="LIMIT",
        quantity=Decimal("0.01"), # Sell 0.01 BTC
        side=1,        # 1 for sell
        price=Decimal("85000"), # Price in USD
        ticker="BTCUSD"
    )

    # Place a market buy order
    market_buy = await client.create_order(
        order_type="MARKET",
        quantity=Decimal("0.01"),  # Buy 0.01 BTC
        side=0,         # 0 for buy
        ticker="BTCUSD"
    )

    await client.close()
    return limit_buy, limit_sell, market_buy

# Run the async function
asyncio.run(create_orders())

Managing orders

# Get all orders
subaccounts = await client.subaccounts()
sid = subaccounts[0].id
sname = subaccounts[0].name
orders = await client.list_orders(subaccount_id=sid)

# Get orders for a specific product
products_by_ticker = await client.products_by_ticker()
btc_product_id = products_by_ticker["BTCUSD"].id
btc_orders = await client.list_orders(subaccount_id=sid, product_ids=[btc_product_id])

# Get a specific order by ID
order_id = '78231588-f2a4-47b9-9b96-9ff8ed58d034'
order = await client.get_order(id=order_id)

# Cancel an order
cancel_results = await client.cancel_orders(
    order_ids=[order_id],
    sender=client.chain.address,
    subaccount=sname
)

# Cancel all working orders for a subaccount
# Optionally pass product_ids=[...] to limit the scope
cancel_results = await client.cancel_all_orders(subaccount_id=sid)

Batch orders

Multiple order submissions and cancellations can be combined into a single signed request. Prepare each instruction with submit=False, then send them together with create_batch_order:

subaccounts = await client.subaccounts()
sname = subaccounts[0].name

# Prepare (but do not submit) two limit orders
buy = await client.create_order(
    order_type="LIMIT", quantity=Decimal("0.01"), side=0,
    price=Decimal("80000"), ticker="BTCUSD", submit=False,
)
sell = await client.create_order(
    order_type="LIMIT", quantity=Decimal("0.01"), side=1,
    price=Decimal("85000"), ticker="BTCUSD", submit=False,
)

# Prepare a cancellation for an existing order
cancel = await client.cancel_orders(
    order_ids=[existing_order_id],
    subaccount=sname,
    submit=False,
)

# Submit everything in one batch
result = await client.create_batch_order(instructions=[buy, sell, cancel])

Isolated margin

Every product has a margin_mode of either CROSS or ISOLATED. Cross-margined products (BTCUSD, ETHUSD, ...) share the subaccount's USD collateral across positions. Isolated products (XAU, XAG, SPY, QQQ, ...) are collateralized in their own quote token (product.quote_token_name, e.g. XAUUSD), so their risk is isolated to the relevant position.

To trade an isolated product, first convert USD into the product's quote token with convert_token, then place orders as usual:

products_by_ticker = await client.products_by_ticker()
product = products_by_ticker["XAUXAUUSD"]
print(product.margin_mode)  # ProductMarginMode.isolated

# Find the USD token and the product's quote token
tokens = await client.list_tokens()
usd = next(t for t in tokens if t.name == "USD")
quote = next(t for t in tokens if t.name == product.quote_token_name)

subaccounts = await client.subaccounts()
sub = subaccounts[0]

# Convert USD into the quote token to use as isolated collateral
conversion = await client.convert_token(
    from_token_id=usd.id,
    to_token_id=quote.id,
    amount=100,
    subaccount=sub.name,
)

# Trade the isolated product as usual
order = await client.create_order(
    order_type="MARKET",
    product_id=product.id,
    side=0,
    quantity=float(product.lot_size),
    subaccount=sub.name,
)

When you are done, close the position and convert the collateral back to USD:

# Close the full position
await client.create_order(
    order_type="MARKET",
    product_id=product.id,
    side=1,
    quantity=0,
    reduce_only=True,
    close=True,
    subaccount=sub.name,
)

# Convert the quote-token balance back to USD
await client.convert_token(
    from_token_id=quote.id,
    to_token_id=usd.id,
    amount=100,
    subaccount=sub.name,
)

See Token conversions for more on convert_token.

Order fills and trade history

# Get trade history
subaccounts = await client.subaccounts()
sid = subaccounts[0].id
trades = await client.list_trades(subaccount_id=sid)

# Get fills
fills = await client.list_fills(subaccount_id=sid)

# Get all fills for a product
products_by_ticker = await client.products_by_ticker()
btc_product_id = products_by_ticker['BTCUSD'].id
btc_fills = await client.list_fills(subaccount_id=sid, product_id=btc_product_id)

# Display fill information
for fill in fills:
    print(fill.model_dump())

Advanced Orders: Stops, OTO & OCO

Stop orders

What they are. A stop becomes active when the oracle crosses the stop_price:

  • Stop-market: executes at market when triggered ⇒ set order_type="MARKET".
  • Stop-limit: when triggered, places a limit at price ⇒ set order_type="LIMIT" and include both stop_price and price.
  • stop_type: 0 = GAIN (take-profit), 1 = LOSS (stop-loss).
  • Use reduce_only=True on exits so you don't increase position size.

Stop-market (long position: take-profit & stop-loss)

# Assuming a long position opened at $1000
# Define the product and quantity for the examples
products_by_ticker = await client.products_by_ticker()
pid = products_by_ticker["BTCUSD"].id  # Product ID
qty = Decimal("0.01")  # Quantity to close

# Take-profit (GAIN): triggers at or above stop_price
# Closes the position in profit if the price reaches 1050
tp = await client.create_order(
    order_type="MARKET", product_id=pid, side=1, quantity=qty,
    reduce_only=True, stop_type=0, stop_price=Decimal("1050"),
)

# Stop-loss (LOSS): triggers at or below stop_price
# Closes the position at a loss if the price reaches 900
sl = await client.create_order(
    order_type="MARKET", product_id=pid, side=1, quantity=qty,
    reduce_only=True, stop_type=1, stop_price=Decimal("900"),
)

Stop-limit (long position exits)

# Assuming a long position opened at $1000
# Define the product and quantity for the examples
products_by_ticker = await client.products_by_ticker()
pid = products_by_ticker["BTCUSD"].id  # Product ID
qty = Decimal("0.01")  # Quantity to close

# Stop-limit take-profit: trigger a limit order
# Creates a limit order at 1050 if the price reaches 1050
tp_limit = await client.create_order(
    order_type="LIMIT", product_id=pid, side=1, quantity=qty, reduce_only=True,
    stop_type=0, stop_price=Decimal("1050"),
    price=Decimal("1050"),  # limit after trigger
)

# Stop-limit stop-loss: trigger a limit order
# Creates a limit order at 899 if the price reaches 900
sl_limit = await client.create_order(
    order_type="LIMIT", product_id=pid, side=1, quantity=qty, reduce_only=True,
    stop_type=1, stop_price=Decimal("900"),
    price=Decimal("899"),
)

One-Triggers-Other and One-Cancels-Other (OTO/OCO)

Orders with a group_id and group_contingency_type are linked and will be triggered accordingly. You can provide any UUID as a group_id in order to easily manage related orders. Group contingency type has two values:

  • OTO (One-Triggers-Other) → group_contingency_type=0 on the primary order only. Secondary orders share a group_id and omit group_contingency_type. Secondaries activate after the primary order fills.
  • OCO (One-Cancels-Other) → every order in the group sets group_contingency_type=1. When one fills/cancels, all orders in the group are auto-canceled.

Important limitations:

  • Only one OTO trigger order is allowed per group
  • Cannot mix OTO and OCO contingency types within the same group
  • Groups are isolated per subaccount (same UUID can be reused across different subaccounts)

OCO: Take-profit and stop-loss exits

import uuid

# Assume a long position opened at $1000
# Define the product for the examples
products_by_ticker = await client.products_by_ticker()
pid = products_by_ticker["BTCUSD"].id  # Product ID

group_id = str(uuid.uuid4())

tp = await client.create_order(
    order_type="LIMIT", product_id=pid, side=1, quantity=Decimal("0.001"),
    price=Decimal("1050"),
    time_in_force="GTD", reduce_only=True,
    stop_type=0, stop_price=Decimal("1050"),
    group_id=group_id, group_contingency_type=1,  # OCO
)

sl = await client.create_order(
    order_type="LIMIT", product_id=pid, side=1, quantity=Decimal("0.001"),
    price=Decimal("899"),
    time_in_force="GTD", reduce_only=True,
    stop_type=1, stop_price=Decimal("900"),
    group_id=group_id, group_contingency_type=1,  # OCO
)

OTO Scale-out: Multiple Profit Targets

Entry order triggers multiple exit orders at different price levels, useful for scaling out of positions.

import uuid

# Define the product for the examples
products_by_ticker = await client.products_by_ticker()
pid = products_by_ticker["BTCUSD"].id  # Product ID

group_id = str(uuid.uuid4())

# Entry order (must fill first)
entry = await client.create_order(
    order_type="LIMIT", product_id=pid, side=0, quantity=Decimal("0.003"),
    price=Decimal("1000"),
    time_in_force="GTD",
    group_id=group_id, group_contingency_type=0,  # OTO
)

# Scale out at different profit levels (all triggered when entry fills)
exits = []
for exit_price in [Decimal("1050"), Decimal("1100"), Decimal("1150")]:
    exit_order = await client.create_order(
        order_type="LIMIT", product_id=pid, side=1, quantity=Decimal("0.001"),
        price=exit_price,
        time_in_force="GTD", reduce_only=True,
        group_id=group_id,
    )
    exits.append(exit_order)

Bracket Order: Entry + OCO Exits

For a bracket where take-profit and stop-loss cancel each other, first enter the position with a market order, then create OCO exit orders.

import uuid

# Define the product for the examples
products_by_ticker = await client.products_by_ticker()
pid = products_by_ticker["BTCUSD"].id  # Product ID

# 1) Enter position immediately
entry = await client.create_order(
    order_type="MARKET", product_id=pid, side=0, quantity=Decimal("0.002"),
)

# 2) Set up OCO exits that cancel each other
exit_group = str(uuid.uuid4())

tp = await client.create_order(
    order_type="LIMIT", product_id=pid, side=1, quantity=Decimal("0.002"),
    price=Decimal("1050"), time_in_force="GTD", reduce_only=True,
    group_id=exit_group, group_contingency_type=1,  # OCO
)

sl = await client.create_order(
    order_type="MARKET", product_id=pid, side=1, quantity=Decimal("0.002"),
    stop_type=1, stop_price=Decimal("900"), reduce_only=True,
    group_id=exit_group, group_contingency_type=1,  # OCO
)