JavaScript bindings for OANDA's v20 REST API enabling programmatic forex and CFD trading operations.
Position tracking and management operations for viewing all positions by instrument, listing open positions, retrieving position details with separate long/short side information, and closing positions.
All position operations are accessed via ctx.position where ctx is a Context instance.
Get a list of all Positions for an Account.
/**
* Get list of all Positions for Account
* @param accountID - Account identifier
* @param responseHandler - Callback receiving Response object
* Response body contains: { positions: Position[], lastTransactionID: string }
*/
list(accountID, responseHandler);Usage Example:
ctx.position.list('001-001-1234567-001', response => {
if (response.isSuccess()) {
response.body.positions.forEach(position => {
console.log(`${position.instrument}:`);
console.log(` Long: ${position.long.units} units, P/L: ${position.long.pl}`);
console.log(` Short: ${position.short.units} units, P/L: ${position.short.pl}`);
console.log(` Total Unrealized P/L: ${position.unrealizedPL}`);
});
}
});Get a list of all open Positions for an Account (positions with non-zero units).
/**
* Get list of open Positions
* @param accountID - Account identifier
* @param responseHandler - Callback receiving Response object
* Response body contains: { positions: Position[], lastTransactionID: string }
*/
listOpen(accountID, responseHandler);Usage Example:
ctx.position.listOpen('001-001-1234567-001', response => {
if (response.isSuccess()) {
const positions = response.body.positions;
console.log(`${positions.length} open positions`);
positions.forEach(position => {
const longUnits = parseFloat(position.long.units);
const shortUnits = parseFloat(position.short.units);
const netUnits = longUnits + shortUnits;
console.log(`${position.instrument}: ${netUnits} units (${netUnits > 0 ? 'LONG' : 'SHORT'})`);
console.log(` Unrealized P/L: ${position.unrealizedPL}`);
console.log(` Margin Used: ${position.marginUsed}`);
});
}
});Get the details of a single Position for a specific instrument.
/**
* Get details of specific Position
* @param accountID - Account identifier
* @param instrument - Instrument name (e.g., "EUR_USD")
* @param responseHandler - Callback receiving Response object
* Response body contains: { position: Position, lastTransactionID: string }
*/
get(accountID, instrument, responseHandler);Usage Example:
ctx.position.get('001-001-1234567-001', 'EUR_USD', response => {
if (response.isSuccess()) {
const position = response.body.position;
console.log(`Position for ${position.instrument}:`);
if (parseFloat(position.long.units) !== 0) {
console.log('Long Side:');
console.log(` Units: ${position.long.units}`);
console.log(` Average Price: ${position.long.averagePrice}`);
console.log(` Unrealized P/L: ${position.long.unrealizedPL}`);
console.log(` Trade IDs: ${position.long.tradeIDs.join(', ')}`);
}
if (parseFloat(position.short.units) !== 0) {
console.log('Short Side:');
console.log(` Units: ${position.short.units}`);
console.log(` Average Price: ${position.short.averagePrice}`);
console.log(` Unrealized P/L: ${position.short.unrealizedPL}`);
console.log(` Trade IDs: ${position.short.tradeIDs.join(', ')}`);
}
console.log(`Total Unrealized P/L: ${position.unrealizedPL}`);
console.log(`Margin Used: ${position.marginUsed}`);
}
});Close a Position by closing all open Trades for the specified instrument.
/**
* Close a Position
* @param accountID - Account identifier
* @param instrument - Instrument name (e.g., "EUR_USD")
* @param bodyParams - Body parameters object
* - longUnits: string (units to close on long side, "ALL" or "NONE", optional)
* - longClientExtensions: ClientExtensions (optional)
* - shortUnits: string (units to close on short side, "ALL" or "NONE", optional)
* - shortClientExtensions: ClientExtensions (optional)
* @param responseHandler - Callback receiving Response object
* Response body contains: {
* longOrderCreateTransaction?: MarketOrderTransaction,
* longOrderFillTransaction?: OrderFillTransaction,
* longOrderCancelTransaction?: OrderCancelTransaction,
* shortOrderCreateTransaction?: MarketOrderTransaction,
* shortOrderFillTransaction?: OrderFillTransaction,
* shortOrderCancelTransaction?: OrderCancelTransaction,
* relatedTransactionIDs: string[],
* lastTransactionID: string
* }
*/
close(accountID, instrument, bodyParams, responseHandler);Usage Example:
// Close entire position (both long and short sides)
ctx.position.close('001-001-1234567-001', 'EUR_USD', {
longUnits: 'ALL',
shortUnits: 'ALL'
}, response => {
if (response.isSuccess()) {
console.log('Position closed');
if (response.body.longOrderFillTransaction) {
const longFill = response.body.longOrderFillTransaction;
console.log(`Long side closed: ${longFill.units} units @ ${longFill.price}`);
console.log(` P/L: ${longFill.pl}`);
}
if (response.body.shortOrderFillTransaction) {
const shortFill = response.body.shortOrderFillTransaction;
console.log(`Short side closed: ${shortFill.units} units @ ${shortFill.price}`);
console.log(` P/L: ${shortFill.pl}`);
}
}
});
// Close only long side
ctx.position.close('001-001-1234567-001', 'EUR_USD', {
longUnits: 'ALL',
shortUnits: 'NONE'
}, response => {
if (response.isSuccess()) {
console.log('Long position closed');
}
});
// Close only short side with client extensions
ctx.position.close('001-001-1234567-001', 'EUR_USD', {
longUnits: 'NONE',
shortUnits: 'ALL',
shortClientExtensions: {
comment: 'Closing short position'
}
}, response => {
if (response.isSuccess()) {
console.log('Short position closed');
}
});Representation of a Position for a single instrument.
interface Position {
// Identification
instrument: string;
// Financial summary over account lifetime
pl: string; // Total realized P/L
resettablePL: string;
financing: string;
commission: string;
guaranteedExecutionFees: string;
// Current state
unrealizedPL: string;
marginUsed: string;
// Long and short sides
long: PositionSide;
short: PositionSide;
}One side (long or short) of a Position.
interface PositionSide {
// Position details
units: string; // Number of units (0 if no position on this side)
averagePrice?: string; // Volume-weighted average price (if units != 0)
// Trade references
tradeIDs: string[]; // Trade IDs contributing to this position side
// Financial summary
pl: string; // Total realized P/L for this side
unrealizedPL: string; // Current unrealized P/L
resettablePL: string;
financing: string;
guaranteedExecutionFees: string;
}Calculated price-dependent state of a Position.
interface CalculatedPositionState {
instrument: string;
netUnrealizedPL: string;
longUnrealizedPL: string;
shortUnrealizedPL: string;
marginUsed: string;
}In OANDA's v20 API, positions are tracked separately for long and short sides:
An account can have simultaneous long and short positions in the same instrument (hedging).
The net position is calculated as: long.units + short.units
When closing a position, you can:
Closing a position creates Market Orders for each side being closed, which immediately close all contributing trades on that side.
Install with Tessl CLI
npx tessl i tessl/npm-oanda--v20