Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 15 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,18 +21,18 @@ npm install dxtrade-api

- [x] Authentication & session management
- [x] Submit orders (market, limit, stop)
- [x] Positions (get & close)
- [x] Account metrics & trade journal
- [x] Get & cancel orders
- [x] Positions (get, close, close all)
- [x] Position metrics (per-position P&L)
- [x] Account metrics, trade journal & trade history
- [x] Symbol search & instrument info
- [x] OHLC / price bar data
- [x] PnL assessments
- [x] Multi-broker support (FTMO, Eightcap, Lark Funding)
- [x] Full TypeScript support
- [ ] Batch orders
- [ ] Close whole position helper
- [ ] Modify existing orders
- [ ] Real-time price streaming
- [ ] Order history

## Quick Start

Expand Down Expand Up @@ -105,16 +105,22 @@ BROKER.FTMO // "https://dxtrade.ftmo.com"
### Trading

- `client.submitOrder(params)` — Submit an order and wait for WebSocket confirmation
- `client.getOrders()` — Get all pending/open orders via WebSocket
- `client.cancelOrder(orderChainId)` — Cancel a single pending order
- `client.cancelAllOrders()` — Cancel all pending orders

### Positions

- `client.getPositions()` — Get all open positions via WebSocket
- `client.closePosition(params)` — Close a position
- `client.closePosition(params)` — Close a position (supports partial closes via the quantity field)
- `client.closeAllPositions()` — Close all open positions with market orders
- `client.getPositionMetrics()` — Get position-level P&L metrics via WebSocket

### Account

- `client.getAccountMetrics()` — Get account metrics (equity, balance, margin, open P&L, etc.)
- `client.getTradeJournal({ from, to })` — Fetch trade journal entries for a date range (Unix timestamps)
- `client.getTradeHistory({ from, to })` — Fetch trade history for a date range (Unix timestamps)

### Analytics

Expand Down Expand Up @@ -152,8 +158,11 @@ const client = new DxtradeClient({
cp .env.example .env # fill in credentials
npm run example:connect
npm run example:order
npm run example:orders
npm run example:positions
npm run example:close-position
npm run example:close-all-positions
npm run example:position-metrics
npm run example:assessments
npm run example:assessments:btc
npm run example:account
Expand All @@ -163,6 +172,7 @@ npm run example:instruments:forex
npm run example:symbol
npm run example:symbol:btc
npm run example:trade-journal
npm run example:trade-history
npm run example:debug
```

Expand Down
21 changes: 21 additions & 0 deletions examples/close-all-positions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import "dotenv/config";
import { DxtradeClient, BROKER } from "../src";

const client = new DxtradeClient({
username: process.env.DXTRADE_USERNAME!,
password: process.env.DXTRADE_PASSWORD!,
broker: process.env.DXTRADE_BROKER! || BROKER.FTMO,
accountId: process.env.DXTRADE_ACCOUNT_ID,
debug: process.env.DXTRADE_DEBUG || false,
});

(async () => {
await client.connect();

const positions = await client.getPositions();
console.log(`Closing ${positions.length} position(s)...`);

await client.closeAllPositions();

console.log("All positions closed");
})().catch(console.error);
27 changes: 26 additions & 1 deletion examples/submit-order.ts → examples/orders.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ const client = new DxtradeClient({
debug: process.env.DXTRADE_DEBUG || false,
});

const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));

(async () => {
await client.connect();

Expand All @@ -19,13 +21,36 @@ const client = new DxtradeClient({
const info = await client.getSymbolInfo(symbol.name);
console.log(`Min volume: ${info.minVolume}, Lot size: ${info.lotSize}`);

// 1. Submit a market order
const order = await client.submitOrder({
symbol: symbol.name,
side: SIDE.BUY,
quantity: info.minVolume,
orderType: ORDER_TYPE.MARKET,
instrumentId: symbol.id,
});

console.log(`Order filled: ${order.orderId} — status: ${order.status}`);

// 2. Wait 2 seconds, then close the position
await sleep(2000);
console.log("\nClosing position...");
await client.closeAllPositions();
console.log("All positions closed");

// 3. Wait 2 seconds, then submit a limit order and immediately cancel it
await sleep(2000);
console.log("\nPlacing limit order...");
const limitOrder = await client.submitOrder({
symbol: symbol.name,
side: SIDE.BUY,
quantity: info.minVolume,
orderType: ORDER_TYPE.LIMIT,
price: 1.0,
instrumentId: symbol.id,
});
console.log(`Limit order placed: ${limitOrder.orderId} — status: ${limitOrder.status}`);

console.log("Cancelling order...");
await client.cancelAllOrders();
console.log("All orders cancelled");
})().catch(console.error);
17 changes: 17 additions & 0 deletions examples/position-metrics.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import "dotenv/config";
import { DxtradeClient, BROKER } from "../src";

const client = new DxtradeClient({
username: process.env.DXTRADE_USERNAME!,
password: process.env.DXTRADE_PASSWORD!,
broker: process.env.DXTRADE_BROKER! || BROKER.FTMO,
accountId: process.env.DXTRADE_ACCOUNT_ID,
debug: process.env.DXTRADE_DEBUG || false,
});

(async () => {
await client.connect();
const metrics = await client.getPositionMetrics();

console.log("Position metrics:", metrics);
})().catch(console.error);
21 changes: 21 additions & 0 deletions examples/trade-history.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import "dotenv/config";
import { DxtradeClient, BROKER } from "../src";

const client = new DxtradeClient({
username: process.env.DXTRADE_USERNAME!,
password: process.env.DXTRADE_PASSWORD!,
broker: process.env.DXTRADE_BROKER! || BROKER.FTMO,
accountId: process.env.DXTRADE_ACCOUNT_ID,
debug: process.env.DXTRADE_DEBUG || false,
});

(async () => {
await client.connect();

const from = new Date(new Date().setMonth(new Date().getMonth() - 1)).getTime();
const to = Date.now();

const history = await client.getTradeHistory({ from, to });

console.log("Trade history:", history);
})().catch(console.error);
8 changes: 7 additions & 1 deletion llms.txt
Original file line number Diff line number Diff line change
Expand Up @@ -41,14 +41,20 @@ await client.connect();
- client.submitOrder(params: Order.SubmitParams) — Submit order and wait for WebSocket confirmation, returns Order.Update
Required params: symbol, side (SIDE.BUY | SIDE.SELL), quantity, orderType (ORDER_TYPE.MARKET | LIMIT | STOP), instrumentId
Optional params: limitPrice, stopPrice, stopLoss, takeProfit, timeInForce (TIF.GTC | DAY | GTD)
- client.getOrders() — Get all pending/open orders via WebSocket, returns Order.Get[]
- client.cancelOrder(orderChainId: number) — Cancel a single pending order by its order chain ID
- client.cancelAllOrders() — Cancel all pending orders (fetches orders then cancels each)

### Positions
- client.getPositions() — Get all open positions via WebSocket, returns Position.Get[]
- client.closePosition(params: Position.Close) — Close a position
- client.closePosition(params: Position.Close) — Close a position (supports partial closes via the quantity field)
- client.closeAllPositions() — Close all open positions with market orders
- client.getPositionMetrics() — Get position-level P&L metrics via WebSocket, returns Position.Metrics[]

### Account
- client.getAccountMetrics() — Get equity, balance, margin, open P&L, returns Account.Metrics
- client.getTradeJournal({ from: number, to: number }) — Fetch trade journal for date range (Unix timestamps)
- client.getTradeHistory({ from: number, to: number }) — Fetch trade history for date range (Unix timestamps), returns Account.TradeHistory[]

### Analytics
- client.getAssessments(params: Assessments.Params) — Fetch PnL assessments for a date range
Expand Down
7 changes: 5 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,16 @@
"example:connect": "tsx examples/connect.ts",
"example:positions": "tsx examples/positions.ts",
"example:close-position": "tsx examples/close-position.ts",
"example:order": "tsx examples/submit-order.ts",
"example:close-all-positions": "tsx examples/close-all-positions.ts",
"example:position-metrics": "tsx examples/position-metrics.ts",
"example:orders": "tsx examples/orders.ts",
"example:assessments": "tsx examples/get-assessments.ts",
"example:assessments:btc": "tsx examples/get-assessments.ts BTCUSD",
"example:instruments": "tsx examples/instruments.ts",
"example:instruments:forex": "tsx examples/instruments.ts FOREX",
"example:symbol": "tsx examples/symbol-info.ts",
"example:ohlc": "tsx examples/ohlc.ts",
"example:trade-history": "tsx examples/trade-history.ts",
"example:symbol:btc": "tsx examples/symbol-info.ts BTCUSD",
"============= Git =============": "",
"commit": "COMMITIZEN=1 cz",
Expand Down Expand Up @@ -92,4 +95,4 @@
"czConfig": ".czrc.js"
}
}
}
}
45 changes: 44 additions & 1 deletion src/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,20 @@
switchAccount,
connect,
getAccountMetrics,
getTradeHistory,
getPositions,
getPositionMetrics,
closePosition,
closeAllPositions,
getAssessments,
getInstruments,
getSymbolLimits,
getSymbolSuggestions,
getOHLC,
getSymbolInfo,
getOrders,
cancelOrder,
cancelAllOrders,
submitOrder,
getTradeJournal,
} from "@/domains";
Expand Down Expand Up @@ -46,6 +52,7 @@
callbacks,
cookies: {},
csrf: null,
accountId: config.accountId ?? null,
atmosphereId: null,
broker: config.broker,
retries: config.retries ?? 3,
Expand Down Expand Up @@ -109,6 +116,21 @@
return submitOrder(this._ctx, params);
}

/** Get all pending/open orders via WebSocket. */
public async getOrders(): Promise<Order.Get[]> {
return getOrders(this._ctx);
}

/** Cancel a single pending order by its order chain ID. */
public async cancelOrder(orderChainId: number): Promise<void> {
return cancelOrder(this._ctx, orderChainId);
}

/** Cancel all pending orders. */
public async cancelAllOrders(): Promise<void> {
return cancelAllOrders(this._ctx);
}

/** Get account metrics including equity, balance, margin, and open P&L. */
public async getAccountMetrics(): Promise<Account.Metrics> {
return getAccountMetrics(this._ctx);
Expand All @@ -119,20 +141,41 @@
return getPositions(this._ctx);
}

/** Close a position. */
/**
* Close a position. Supports partial closes by specifying a quantity smaller than the full position size.
*/
public async closePosition(position: Position.Close): Promise<void> {
return closePosition(this._ctx, position);
}

/** Close all open positions with market orders. */
public async closeAllPositions(): Promise<void> {
return closeAllPositions(this._ctx);
}

/** Get position-level P&L metrics via WebSocket. */
public async getPositionMetrics(): Promise<Position.Metrics[]> {
return getPositionMetrics(this._ctx);
}

/**
* Fetch trade journal entries for a date range.
* @param params.from - Start timestamp (Unix ms)
* @param params.to - End timestamp (Unix ms)
*/
public async getTradeJournal(params: { from: number; to: number }): Promise<any> {

Check warning on line 166 in src/client.ts

View workflow job for this annotation

GitHub Actions / build

Unexpected any. Specify a different type
return getTradeJournal(this._ctx, params);
}

/**
* Fetch trade history for a date range.
* @param params.from - Start timestamp (Unix ms)
* @param params.to - End timestamp (Unix ms)
*/
public async getTradeHistory(params: { from: number; to: number }): Promise<Account.TradeHistory[]> {
return getTradeHistory(this._ctx, params);
}

/** Get all available instruments, optionally filtered by partial match (e.g. `{ type: "FOREX" }`). */
public async getInstruments(params: Partial<Instrument.Info> = {}): Promise<Instrument.Info[]> {
return getInstruments(this._ctx, params);
Expand Down
1 change: 1 addition & 0 deletions src/client.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ export interface ClientContext {
callbacks: DxtradeCallbacks;
cookies: Record<string, string>;
csrf: string | null;
accountId: string | null;
atmosphereId: string | null;
broker: keyof typeof BROKER;
retries: number;
Expand Down
6 changes: 6 additions & 0 deletions src/constants/endpoints.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ export const endpoints = {

closePosition: (base: string) => `${base}/api/positions/close`,

cancelOrder: (base: string, accountId: string, orderChainId: number) =>
`${base}/api/orders/cancel?accountId=${accountId}&orderChainId=${orderChainId}`,

assessments: (base: string) => `${base}/api/assessments`,

websocket: (base: string, atmosphereId?: string | null) =>
Expand All @@ -30,6 +33,9 @@ export const endpoints = {
tradeJournal: (base: string, params: { from: number; to: number }) =>
`${base}/api/tradejournal?from=${params.from}&to=${params.to}`,

tradeHistory: (base: string, params: { from: number; to: number }) =>
`${base}/api/history?from=${params.from}&to=${params.to}&orderId=`,

subscribeInstruments: (base: string) => `${base}/api/instruments/subscribeInstrumentSymbols`,

charts: (base: string) => `${base}/api/charts`,
Expand Down
9 changes: 8 additions & 1 deletion src/constants/enums.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,14 +44,20 @@ export enum ERROR {

// Trading
ORDER_ERROR = "ORDER_ERROR",
ORDERS_TIMEOUT = "ORDERS_TIMEOUT",
ORDERS_ERROR = "ORDERS_ERROR",
CANCEL_ORDER_ERROR = "CANCEL_ORDER_ERROR",
POSITION_CLOSE_ERROR = "POSITION_CLOSE_ERROR",
POSITION_METRICS_TIMEOUT = "POSITION_METRICS_TIMEOUT",
POSITION_METRICS_ERROR = "POSITION_METRICS_ERROR",

// Account
ACCOUNT_METRICS_TIMEOUT = "ACCOUNT_METRICS_TIMEOUT",
ACCOUNT_METRICS_ERROR = "ACCOUNT_METRICS_ERROR",
ACCOUNT_POSITIONS_TIMEOUT = "ACCOUNT_POSITIONS_TIMEOUT",
ACCOUNT_POSITIONS_ERROR = "ACCOUNT_POSITIONS_ERROR",
TRADE_JOURNAL_ERROR = "TRADE_JOURNAL_ERROR",
TRADE_HISTORY_ERROR = "TRADE_HISTORY_ERROR",

// Analytics
ASSESSMENTS_ERROR = "ASSESSMENTS_ERROR",
Expand All @@ -63,11 +69,12 @@ export enum WS_MESSAGE {
AVAILABLE_WATCHLISTS = "AVAILABLE_WATCHLISTS",
CHART_FEED_SUBTOPIC = "chartFeedSubtopic",
INSTRUMENTS = "INSTRUMENTS",
INSTRUMENT_METRICS = "INSTRUMENT_METRICS",
// INSTRUMENT_METRICS = "INSTRUMENT_METRICS",
LIMITS = "LIMITS",
MESSAGE = "MESSAGE",
ORDERS = "ORDERS",
POSITIONS = "POSITIONS",
POSITION_METRICS = "POSITION_METRICS",
POSITION_CASH_TRANSFERS = "POSITION_CASH_TRANSFERS",
PRIVATE_LAYOUT_NAMES = "PRIVATE_LAYOUT_NAMES",
SHARED_PROPERTIES_MESSAGE = "SHARED_PROPERTIES_MESSAGE",
Expand Down
Loading