Code Examples
Examples for common tasks with the TypeScript and Python SDKs. Each section says what the API does with the call today.
Not published yet
Neither SDK is on npm or PyPI yet. Do not install a package called @trading-platform/sdk or trading-platform-sdk from those registries. Until we publish them, call the REST API directly as the API Quick Start shows.
What the API Does Today
| Area | SDK calls | Today |
|---|---|---|
| Accounts | accounts.list, accounts.get | The broker accounts you linked, with their balances |
| Market orders | orders.create with type market | Sent to your broker, on a demo MetaTrader account only |
| Limit and stop orders | orders.create with type limit, stop or stop_limit | Refused with 501 ORDER_TYPE_NOT_SUPPORTED: nothing is placed |
| Order records | orders.list, orders.get | Not kept after the API restarts |
| Changing and cancelling orders | orders.modify, orders.cancel | Refused: 409 ORDER_IN_FLIGHT while an order is pending, 400 once it has filled or been rejected |
| Positions and trade history | positions, trades | Read from your broker; stop loss, take profit and closing on a demo MetaTrader account only |
| Quotes and candles | marketData.getQuote, marketData.getCandles | No price feed yet: 501 NOT_IMPLEMENTED |
Authentication Setup
Create one client with your API key and keep the key in an environment variable. The examples below reuse this client.
TypeScript
import { AuthenticationError, TradingClient } from "@trading-platform/sdk";
const client = new TradingClient({
apiKey: process.env.TRADING_PLATFORM_API_KEY,
});
// A new key can list accounts, so this checks that the key works
try {
const accounts = await client.accounts.list();
console.log(`Connected: ${accounts.length} linked accounts`);
} catch (error) {
if (!(error instanceof AuthenticationError)) throw error;
console.error("The API refused the key:", error.message);
}Python
import os
from trading_platform import AuthenticationError, TradingClient
client = TradingClient(api_key=os.environ["TRADING_PLATFORM_API_KEY"])
# A new key can list accounts, so this checks that the key works
try:
accounts = client.accounts.list()
print(f"Connected: {len(accounts)} linked accounts")
except AuthenticationError as error:
print(f"The API refused the key: {error.message}")Call client.close() when you are done, or open the client in a with block.
Placing Orders
Placing an order needs Trading → Write or Orders → Write on the key; a new key answers 403 INSUFFICIENT_SCOPE. The API accepts an order only on an account you linked that is classified for trading (purpose is trading), is a demo account (isDemo is true) and is a MetaTrader 4 or 5 account.
Market Order
The API answers with the order in status pending and then sends it to your broker. Read the order again to see whether the broker filled or rejected it.
// TypeScript
const METATRADER = ["mt4", "mt5", "metatrader4", "metatrader5"];
async function findDemoTradingAccount() {
const accounts = await client.accounts.list();
return accounts.find(
(a) =>
a.purpose === "trading" && a.isDemo && METATRADER.includes(a.brokerType),
);
}
async function placeMarketOrder() {
const account = await findDemoTradingAccount();
if (!account) {
throw new Error("Link a demo MetaTrader account for trading first");
}
const order = await client.orders.create({
accountId: account.id,
symbol: "EURUSD",
side: "buy",
type: "market",
volume: 0.1,
stopLoss: 1.092,
takeProfit: 1.105,
});
console.log(order.id, order.status); // "pending"
// The broker answers after the API does
await new Promise((resolve) => setTimeout(resolve, 2000));
const result = await client.orders.get(order.id);
console.log(result.status, result.brokerOrderId); // "filled" or "rejected"
return result;
}# Python
import time
METATRADER = {"mt4", "mt5", "metatrader4", "metatrader5"}
def find_demo_trading_account(client):
for account in client.accounts.list():
if (
account.purpose == "trading"
and account.is_demo
and account.broker_type in METATRADER
):
return account
return None
def place_market_order(client):
account = find_demo_trading_account(client)
if account is None:
raise RuntimeError("Link a demo MetaTrader account for trading first")
order = client.orders.create(
account_id=account.id,
symbol="EURUSD",
side="buy",
volume=0.1,
order_type="market",
stop_loss=1.092,
take_profit=1.105,
)
print(order.id, order.status) # pending
# The broker answers after the API does
time.sleep(2)
result = client.orders.get(order.id)
print(result.status, result.broker_order_id) # filled or rejected
return resultA filled order carries no fill price: the broker's reply to the API does not include one.
Limit and Stop Orders
Not accepted yet
The API refuses limit, stop and stop_limit orders with 501 ORDER_TYPE_NOT_SUPPORTED before it reads the account, and records nothing. Both SDKs raise this as an APIError. Place market orders only.
Position Management
positions.list() reads your open positions from your broker when you call it. Pass a position's id, as it comes, to positions.get, to the stop loss and take profit calls, and to positions.close. A broker that cannot be read raises an APIError (503, 429, or the account's own refusal), never an empty list. It returns one page, 20 positions unless you pass a limit of up to 100: the positions as data and the page as pagination. While pagination.hasMore is true, ask for the next page.
Changes reach your broker, on demo accounts only
Moving a stop loss or take profit and closing are sent to your broker, on a demo account classified for trading; a live account answers 403 LIVE_ACCOUNT_RESTRICTED. On a 504 BROKER_OUTCOME_UNKNOWN the change may have been made: read the position before you try again, because a repeated partial close closes more.
To check what a market order did, read the order: orders.get(id) returns its status and the broker's order id, as in the market order example above.
Market Data
No prices yet
The quote, candle and tick endpoints are not connected to a market data feed yet. marketData.getQuote and marketData.getCandles answer 501 NOT_IMPLEMENTED, which both SDKs raise as an APIError; they return no prices. Take prices from your broker's terminal until the feed is connected.
Risk Management Examples
Position Size Calculator
// TypeScript
// Lots that lose riskPercent of the balance if the stop loss is hit.
// pipValuePerLot is 10 for a standard lot of a USD-quoted pair on a USD
// account.
function positionSize(
balance: number,
riskPercent: number,
entry: number,
stopLoss: number,
pipSize = 0.0001,
pipValuePerLot = 10,
): number {
const riskAmount = balance * (riskPercent / 100);
// To 0.1 pip: 1.1 - 1.095 is not exactly 0.005 in floating point
const stopPips = Math.round((Math.abs(entry - stopLoss) / pipSize) * 10) / 10;
const lots = riskAmount / (stopPips * pipValuePerLot);
return Math.floor(lots * 100) / 100; // round down to 0.01 lots
}
// 10,000 balance, 2% risk, 50-pip stop loss
console.log(positionSize(10000, 2, 1.1, 1.095)); // 0.4Daily Loss Limit Check
The API does not report daily profit and loss for your broker accounts yet. Record the account's equity when your trading day starts and compare its current equity with that figure.
// TypeScript
import type { CreateOrderRequest } from "@trading-platform/sdk";
async function withinDailyLossLimit(
accountId: string,
startOfDayEquity: number,
maxLossPercent = 3,
): Promise<boolean> {
const account = await client.accounts.get(accountId);
const lossPercent = Math.max(
0,
((startOfDayEquity - account.equity) / startOfDayEquity) * 100,
);
console.log(`Daily loss ${lossPercent.toFixed(2)}% of ${maxLossPercent}%`);
return lossPercent < maxLossPercent;
}
// Check before every order
async function placeOrderWithinLimit(
request: CreateOrderRequest,
startOfDayEquity: number,
) {
if (!(await withinDailyLossLimit(request.accountId, startOfDayEquity))) {
throw new Error("Daily loss limit reached: no order placed");
}
return client.orders.create(request);
}Limits the API Enforces
The API refuses an order with 400 RISK_LIMIT_EXCEEDED when it is larger than 10 lots, when you have already placed 100 orders that day, or when 50 of your orders are pending or open.
Error Handling
Every SDK error extends APIError, which carries the API's error code, the HTTP status and the message. The SDK picks the class from the status:
| Status | TypeScript / Python class | Codes you may see |
|---|---|---|
| 400 | ValidationError | VALIDATION_ERROR, RISK_LIMIT_EXCEEDED, UNSUPPORTED_BROKER |
| 401 | AuthenticationError | UNAUTHORIZED |
| 403 | APIError | INSUFFICIENT_SCOPE, ACCOUNT_PURPOSE_RESTRICTED, LIVE_ACCOUNT_RESTRICTED |
| 404 | NotFoundError | NOT_FOUND, ACCOUNT_NOT_FOUND |
| 429 | RateLimitError | RATE_LIMIT_EXCEEDED. The TypeScript SDK waits for Retry-After and retries while it has retries left, except orders.create and positions.close, which are sent once |
| Any other | APIError | 409 APPROVAL_REQUIRED or ORDER_IN_FLIGHT, 501 NOT_IMPLEMENTED or ORDER_TYPE_NOT_SUPPORTED, 503 BROKER_UNAVAILABLE |
| No response | ConnectionError | None: the request timed out or the connection failed |
Do not resend orders blind
Neither SDK resends orders.create: the first attempt may already have placed the order, so a lost answer raises ConnectionError instead. Look for the order before you send it again.
Handling Errors in TypeScript
import { randomUUID } from "node:crypto";
import {
APIError,
AuthenticationError,
ConnectionError,
NotFoundError,
TradingClient,
ValidationError,
type CreateOrderRequest,
} from "@trading-platform/sdk";
// orders.create is sent once: the SDK never resends an order
const orderClient = new TradingClient({
apiKey: process.env.TRADING_PLATFORM_API_KEY,
});
async function placeOrder(request: Omit<CreateOrderRequest, "clientId">) {
// Your own id for the order, to find it after a lost response
const clientId = randomUUID();
try {
return await orderClient.orders.create({ ...request, clientId });
} catch (error) {
if (error instanceof ConnectionError) {
// No response: the order may have been placed. Look for it.
const recent = await orderClient.orders.list({
accountId: request.accountId,
symbol: request.symbol,
});
const placed = recent.data.find((order) => order.clientId === clientId);
if (placed) return placed;
throw error; // not found yet: check again before you resend
}
if (error instanceof AuthenticationError) {
console.error("The key is missing, unknown, expired or revoked");
} else if (error instanceof ValidationError) {
console.error(`Refused: ${error.code} ${error.message}`);
} else if (error instanceof NotFoundError) {
console.error(`Not found: ${error.code} ${error.message}`);
} else if (error instanceof APIError) {
console.error(`${error.statusCode} ${error.code}: ${error.message}`);
}
throw error;
}
}Handling Errors in Python
The Python SDK's orders.create takes no client id. After a ConnectionError, list the account's orders and decide whether one of them is yours before you send it again.
import os
from trading_platform import (
APIError,
AuthenticationError,
TradingClient,
)
from trading_platform import ConnectionError as SDKConnectionError
# orders.create is sent once: the SDK never resends an order
order_client = TradingClient(
api_key=os.environ["TRADING_PLATFORM_API_KEY"],
)
def place_order(account_id, symbol, side, volume):
try:
return order_client.orders.create(
account_id=account_id, symbol=symbol, side=side, volume=volume
)
except SDKConnectionError:
# No response: the order may have been placed. Check
# order_client.orders.list(account_id=account_id, symbol=symbol)
# before you send it again.
raise
except AuthenticationError:
print("The key is missing, unknown, expired or revoked")
raise
except APIError as error:
# ValidationError (400) and NotFoundError (404) land here too
print(f"{error.status_code} {error.code}: {error.message}")
raiseNext Steps
- API Quick Start - Authentication setup
- API Endpoints - Complete reference
- Webhooks - Real-time notifications
Risk Disclosure
Automated trading carries significant risk. Test thoroughly on a demo account, use proper risk management, and never trade money you cannot afford to lose.