A comprehensive TypeScript/JavaScript client for Saxobank's OpenAPI. This library provides easy access to trading, portfolio management, and account operations on the Saxobank platform.
- π OAuth Authentication - Secure authentication with Saxobank OpenAPI
- πΌ Portfolio Management - Access accounts, positions, balances, and exposure
- π Trading Operations - Place, modify, and cancel orders (Market, Limit, Stop, Stop-Limit)
- π― Multi-Asset Support - Trade FX, stocks, options, futures, ETFs, and more
- π Position Tracking - Monitor open, closed, and net positions
- β‘ Order Pre-checking - Validate orders before placement
- π TypeScript Support - Full type definitions included
- π Cross-Platform - Works in browsers, Node.js, Bun, and Deno
# Using npm
npm install @ch99q/sxc
# Using yarn
yarn add @ch99q/sxc
# Using pnpm
pnpm add @ch99q/sxc
# Using bun
bun add @ch99q/sxcimport { createClient } from "@ch99q/sxc";
// Create client with username/password
const client = await createClient(
{
type: "account",
username: "your-username",
password: "your-password"
},
{
appKey: "your-app-key",
appSecret: "your-app-secret",
redirectUri: "http://localhost:5000/callback"
}
);
// Or use an existing token
const client = await createClient(
{
type: "token",
token: "your-access-token"
},
{
appKey: "your-app-key",
appSecret: "your-app-secret",
redirectUri: "http://localhost:5000/callback"
}
);
console.log(`Connected as: ${client.name} (${client.id})`);// Get all accounts
const accounts = await client.getAccounts();
// Get balance for an account
const account = accounts[0];
const balance = await account.getBalance();
console.log(`Balance: ${balance.cashAvailable} ${balance.currency}`);
console.log(`Total Value: ${balance.totalValue} ${balance.currency}`);
console.log(`Margin Used: ${balance.marginUsed} ${balance.currency}`);
console.log(`Unrealized P&L: ${balance.unrealizedPnL} ${balance.currency}`);// Get positions for an account
const positions = await account.getPositions();
positions.forEach(position => {
console.log(`Position ${position.id}:`);
console.log(` Instrument: ${position.uic}`);
console.log(` Quantity: ${position.quantity}`);
console.log(` Price: ${position.price}`);
console.log(` Value: ${position.value} ${position.currency}`);
console.log(` Status: ${position.status}`);
});
// Get net positions (aggregated)
const netPositions = await client.getNetPositions();
// Get closed positions
const closedPositions = await client.getClosedPositions(
account.key,
new Date("2024-01-01"),
new Date()
);// Place a market buy order
const marketOrder = await account.buy(
21, // UIC (instrument ID)
100000, // Quantity
"market", // Order type
undefined, // Price (not needed for market)
undefined, // Stop limit (not needed)
{
assetType: "FxSpot",
externalReference: "my-order-123",
manualOrder: true
}
);
// Place a limit sell order
const limitOrder = await account.sell(
21, // UIC for EUR/USD
100000, // Quantity
"limit", // Order type
1.1500, // Limit price
undefined,
{
assetType: "FxSpot",
duration: { durationType: "GoodTillCancel" },
externalReference: "my-limit-order"
}
);
// Place a stop order
const stopOrder = await account.sell(
21,
100000,
"stop",
1.0800, // Order price
1.0900, // Stop limit price
{
assetType: "FxSpot",
duration: { durationType: "DayOrder" }
}
);// Get all orders
const orders = await account.getOrders();
orders.forEach(order => {
console.log(`Order ${order.id}:`);
console.log(` Type: ${order.type} ${order.order_type}`);
console.log(` Price: ${order.price}`);
console.log(` Quantity: ${order.quantity}`);
console.log(` Status: ${order.status}`);
});
// Modify an existing order
await account.modifyOrder(
"order-id",
1.1600, // New price
150000 // New quantity
);
// Cancel a specific order
await account.cancelOrder("order-id");
// Cancel all orders for an instrument
await account.cancelAllOrders(21, "FxSpot");// Validate an order before placing it
const preCheckResult = await client.preCheckOrder({
accountKey: account.key,
uic: 21,
assetType: "FxSpot",
buySell: "Buy",
orderType: "Market",
amount: 100000,
manualOrder: true
});
console.log("Order validation:", preCheckResult);Creates a new Saxobank client.
Parameters:
credentials: Either{ type: "account", username, password }or{ type: "token", token }config: Configuration objectappKey: Saxobank app keyappSecret: Saxobank app secretredirectUri: OAuth redirect URIapiEndpoint?: API gateway URL (defaults to simulation)authEndpoint?: Auth server URL (defaults to simulation)
Returns: Promise<Client>
getAccounts(): Get all accountsgetPositions(accountKey?): Get positionsgetOrders(accountKey?): Get ordersgetNetPositions(accountKey?): Get aggregated positionsgetClosedPositions(accountKey?, fromDate?, toDate?): Get closed positionsgetExposure(accountKey?): Get exposure informationpreCheckOrder(orderRequest): Pre-validate an order
id: Account identifierkey: Account keyactive: Whether account is activecurrency: Account currency
getBalance(): Get account balancegetPositions(): Get account positionsgetOrders(): Get account ordersbuy(uic, quantity, type?, price?, stopLimit?, options?): Place buy ordersell(uic, quantity, type?, price?, stopLimit?, options?): Place sell ordercancelOrder(orderId): Cancel specific ordercancelAllOrders(uic, assetType?): Cancel all orders for instrumentmodifyOrder(orderId, price?, quantity?): Modify existing order
"market": Market order (executes immediately at current price)"limit": Limit order (executes at specified price or better)"stop": Stop order (triggers at stop price)"stop_limit": Stop-limit order (combines stop and limit)
interface OrderOptions {
assetType?: AssetType;
duration?: OrderDuration;
externalReference?: string;
manualOrder?: boolean;
isForceOpen?: boolean;
trailingStopDistanceToMarket?: number;
trailingStopStep?: number;
}Supported asset types:
- FX:
FxSpot,FxForward,FxVanillaOption,FxKnockInOption,FxKnockOutOption,FxOneTouchOption,FxNoTouchOption,FxBinaryOption - Equities:
Stock,StockOption,StockIndex,StockIndexOption - Fixed Income:
Bond - CFDs:
CfdOnStock,CfdOnIndex,CfdOnFutures - Funds:
Etc,Etf,Etn,Fund,MutualFund - Derivatives:
FuturesStrategy
type OrderDuration = {
durationType: "DayOrder" | "GoodTillCancel" | "FillOrKill" | "ImmediateOrCancel" | "GoodTillDate";
expirationDateTime?: string; // Required for GoodTillDate
};import { createClient } from "@ch99q/sxc";
async function tradingExample() {
// Connect to Saxobank
const client = await createClient(
{ type: "account", username: "user", password: "pass" },
{
appKey: "app-key",
appSecret: "app-secret",
redirectUri: "http://localhost:5000/callback"
}
);
// Get first account
const accounts = await client.getAccounts();
const account = accounts[0];
// Check balance
const balance = await account.getBalance();
console.log(`Available: ${balance.cashAvailable} ${balance.currency}`);
// Pre-check an order
const preCheck = await client.preCheckOrder({
accountKey: account.key,
uic: 21,
assetType: "FxSpot",
buySell: "Buy",
orderType: "Market",
amount: 100000
});
if (preCheck.estimatedCosts) {
console.log("Estimated cost:", preCheck.estimatedCosts);
}
// Place a limit order
const order = await account.buy(
21,
100000,
"limit",
1.1000,
undefined,
{
assetType: "FxSpot",
duration: { durationType: "GoodTillCancel" },
externalReference: "trade-001"
}
);
console.log("Order placed:", order.id);
// Monitor positions
const positions = await account.getPositions();
positions.forEach(pos => {
console.log(`Position ${pos.id}: ${pos.quantity} @ ${pos.price}`);
});
// Modify the order if needed
if ('id' in order && 'type' in order) {
await account.modifyOrder(order.id, 1.0950); // New price
}
// Cancel the order
if ('id' in order && 'type' in order) {
await account.cancelOrder(order.id);
}
}
tradingExample().catch(console.error);async function monitorPortfolio() {
const client = await createClient(/* credentials */, /* config */);
const accounts = await client.getAccounts();
for (const account of accounts) {
console.log(`\\nAccount: ${account.id} (${account.currency})`);
const balance = await account.getBalance();
console.log(` Cash Available: ${balance.cashAvailable}`);
console.log(` Total Value: ${balance.totalValue}`);
console.log(` Unrealized P&L: ${balance.unrealizedPnL}`);
const positions = await account.getPositions();
console.log(` Open Positions: ${positions.length}`);
positions.forEach(pos => {
console.log(` ${pos.uic}: ${pos.quantity} @ ${pos.price}`);
});
const orders = await account.getOrders();
console.log(` Active Orders: ${orders.length}`);
}
}try {
const client = await createClient(credentials, config);
const accounts = await client.getAccounts();
const account = accounts[0];
// Place order
const order = await account.buy(21, 100000, "market", undefined, undefined, {
assetType: "FxSpot"
});
} catch (error) {
if (error instanceof Error) {
console.error("Error:", error.message);
// API errors include detailed information
if (error.message.includes("API request failed")) {
console.error("API Error Details:", error.message);
}
}
}- Browser: Chrome, Firefox, Safari, Edge (latest versions)
- Node.js: 18.0.0+
- Bun: Latest version
- Deno: Latest version
const client = await createClient(credentials, {
appKey: "your-app-key",
appSecret: "your-app-secret",
redirectUri: "http://localhost:5000/callback",
// These are the defaults:
apiEndpoint: "https://gateway.saxobank.com/sim/openapi",
authEndpoint: "https://sim.logonvalidation.net"
});const client = await createClient(credentials, {
appKey: "your-app-key",
appSecret: "your-app-secret",
redirectUri: "http://localhost:5000/callback",
apiEndpoint: "https://gateway.saxobank.com/openapi",
authEndpoint: "https://live.logonvalidation.net"
});- Error Handling: Always wrap API calls in try-catch blocks
- Order Validation: Use
preCheckOrder()before placing orders - Rate Limits: Implement delays between requests in automated trading
- Resource Management: Properly handle positions and orders
- Testing: Use simulation environment for development and testing
// Good: Proper error handling
async function placeOrder(account: Account) {
try {
// Validate first
const preCheck = await client.preCheckOrder({
accountKey: account.key,
uic: 21,
assetType: "FxSpot",
buySell: "Buy",
orderType: "Market",
amount: 100000
});
if (preCheck.errorInfo) {
console.error("Order validation failed:", preCheck.errorInfo);
return;
}
// Then place order
const order = await account.buy(21, 100000, "market");
console.log("Order placed:", order);
} catch (error) {
console.error("Failed to place order:", error);
}
}We welcome contributions! Please see our Contributing Guide for details.
This project is licensed under the MIT License - see the LICENSE file for details.
This library is not officially affiliated with Saxobank. Use at your own risk. The library is provided "as is" without warranty. Always verify trades and account operations. Never trade with funds you cannot afford to lose.
- π Documentation
- π Issue Tracker
- π¬ Discussions
Made with β€οΈ for the trading community