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
3 changes: 3 additions & 0 deletions .claude/CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,9 @@ The fix: `connect()` captures the tracking ID from the handshake and stores it i
- **Error codes**: Always use the `ERROR` enum from `@/constants` — never use raw strings for error codes (e.g. `ERROR.OHLC_TIMEOUT`, not `"OHLC_TIMEOUT"`)
- **WebSocket message types**: Always use the `WS_MESSAGE` enum — never use raw strings (e.g. `WS_MESSAGE.POSITIONS`, not `"POSITIONS"`)
- **WebSocket subtopics**: Use `WS_MESSAGE.SUBTOPIC` namespace (e.g. `WS_MESSAGE.SUBTOPIC.BIG_CHART_COMPONENT`)
- **Message categories**: Always use `MESSAGE_CATEGORY` enum — never use raw strings (e.g. `MESSAGE_CATEGORY.TRADE_LOG`, not `"TRADE_LOG"`)
- **Message types**: Always use `MESSAGE_TYPE` enum — never use raw strings (e.g. `MESSAGE_TYPE.ORDER`, not `"ORDER"`)
- **Order statuses**: Always use `ORDER_STATUS` enum — never use raw strings (e.g. `ORDER_STATUS.FILLED`, not `"FILLED"`)
- When adding a new error code, WebSocket message type, or subtopic, add it to `src/constants/enums.ts`

### General:
Expand Down
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ BROKER.FTMO // "https://dxtrade.ftmo.com"
### Positions

- `client.positions.get()` — Get all open positions with P&L metrics merged (margin, plOpen, marketValue, etc.)
- `client.positions.close(params)` — Close a position (supports partial closes via the quantity field)
- `client.positions.close(positionCode, options?)` — Close a position by its position code. Returns `Position.Full` with P&L metrics. Options: `waitForClose: "stream" | "poll"`, `timeout` (ms, default 30000), `pollInterval` (ms, default 1000)
- `client.positions.closeAll()` — Close all open positions with market orders
- `client.positions.stream(callback)` — Stream real-time position updates with live P&L (requires `connect()`). Returns an unsubscribe function.

Expand Down Expand Up @@ -185,6 +185,7 @@ npm run example:debug
npm run example:positions:get
npm run example:positions:close
npm run example:positions:close-all
npm run example:positions:close-by-code
npm run example:positions:metrics
npm run example:positions:stream
npm run example:orders:submit
Expand Down
2 changes: 1 addition & 1 deletion examples/orders.submit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
(async () => {
await client.auth();

const suggestions = await client.symbols.search("EURUSD");
const suggestions = await client.symbols.search("ETHUSD");
const symbol = suggestions[0];
console.log(`Found symbol: ${symbol.name} (id: ${symbol.id})`);

Expand Down
50 changes: 50 additions & 0 deletions examples/positions.close-by-code.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import "dotenv/config";
import { DxtradeClient, ORDER_TYPE, SIDE, 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,
});

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

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

const suggestions = await client.symbols.search("ETHUSD");
const symbol = suggestions[0];
console.log(`Found symbol: ${symbol.name} (id: ${symbol.id})`);

const info = await client.symbols.info(symbol.name);
console.log(`Min volume: ${info.minVolume}, Lot size: ${info.lotSize}`);

// 1. Submit a market order
const order = await client.orders.submit({
symbol: symbol.name,
side: SIDE.BUY,
quantity: info.minVolume,
orderType: ORDER_TYPE.MARKET,
instrumentId: symbol.id,
});
console.log(`Order filled: ${order.orderId} — positionCode: ${order.positionCode}`);

// 2. Wait 3 seconds for P&L to update
console.log("\nWaiting 3 seconds...");
await sleep(3000);

// 3. Close the position by its code and wait for confirmation via streaming
console.log("Closing position by code (waiting for close confirmation)...");
const position = await client.positions.close(order.positionCode!, { waitForClose: "stream" });

console.log("\nPosition closed:");
console.log(` P&L Open: ${position.plOpen}`);
console.log(` P&L Closed: ${position.plClosed}`);
console.log(` Commissions: ${position.totalCommissions}`);
console.log(` Financing: ${position.totalFinancing}`);
console.log(` Market Value: ${position.marketValue}`);

client.disconnect();
})().catch(console.error);
28 changes: 11 additions & 17 deletions examples/positions.close.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,23 +11,17 @@ const client = new DxtradeClient({

(async () => {
await client.auth();
// TODO:: improve parameters! maybe even have a "closeWholePosition" function

const positions = await client.positions.close({
legs: [
{
instrumentId: 3438,
positionCode: "191361108",
positionEffect: "CLOSING",
ratioQuantity: 1,
symbol: "EURUSD",
},
],
limitPrice: 1.18725,
orderType: "MARKET",
quantity: -1000,
timeInForce: "GTC",
});
const positions = await client.positions.get();
if (positions.length === 0) {
console.log("No open positions to close");
return;
}

console.log("Positions: ", positions);
const position = positions[0];
const code = position.positionKey.positionCode;
console.log(`Closing position ${code}...`);

const closed = await client.positions.close(code);
console.log("Position closed:", closed);
})().catch(console.error);
3 changes: 2 additions & 1 deletion llms.txt
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@ await client.connect(); // auth + persistent WebSocket (recommended)

### Positions
- client.positions.get() — Get all open positions with P&L metrics merged (margin, plOpen, marketValue, etc.), returns Position.Full[]
- client.positions.close(params: Position.Close) — Close a position (supports partial closes via the quantity field)
- client.positions.close(positionCode: string, options?: Position.CloseOptions) — Close a position by its position code, returns Position.Full with P&L metrics
Options: waitForClose ("stream" | "poll") to wait for the position to disappear, timeout (ms, default 30000), pollInterval (ms, default 1000, poll mode only)
- client.positions.closeAll() — Close all open positions with market orders
- client.positions.stream(callback: (positions: Position.Full[]) => void) — Stream real-time position updates with live P&L (requires connect()). Returns unsubscribe function.

Expand Down
Loading