Vest: record every trade, the bot's and outside ones, and list them
Vest's position history (GET /v3/positions) gives each position's size, open and close price, P&L, fees, funding and why it closed. The client reads it hourly and a minute after a position closes (or a flat balance moves) into a new trades table, and stores open positions as they appear. Vest can't tell an API order from a click, so the bot marks its own opens and closes as it sends them; an unmarked trade since marking began was someone else's. The dashboard's Vest firm row gets a Trades button listing every trade on its accounts with who opened it and how it closed; the account page shows its own. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
29eaf64a6b
commit
95cc3bd23f
+34
-1
@@ -11,7 +11,7 @@ import assert from 'node:assert/strict';
|
||||
import {
|
||||
isVestWalletAddress, vestAccountId, num, toMs, formatDecimal, vestDayOf, lastVestResetMs, parseCapitalAccount,
|
||||
vestAccountName, parsePosition, awaitingLegs, parseOpenOrder, unrealizedPnl, parseTxn, bookTxns, txnKind, VEST_TXN,
|
||||
hourlyVolatility, marketOpen, MIN_HOURLY_RETURNS, nextVestResetMs, resetSafeStop,
|
||||
hourlyVolatility, marketOpen, MIN_HOURLY_RETURNS, nextVestResetMs, resetSafeStop, parseHistoryPosition,
|
||||
} from '../lib/vest-helpers.ts';
|
||||
import { putVestSession, vestSessionFor, dropVestSession, listVestSessions } from '../lib/vest-session.ts';
|
||||
|
||||
@@ -263,3 +263,36 @@ test('resetSafeStop: fit an open position into the room the reset will leave', (
|
||||
assert.equal(dd.action, 'stop');
|
||||
assert.ok(dd.price > want);
|
||||
});
|
||||
|
||||
test('parseHistoryPosition: a closed position from GET /v3/positions', () => {
|
||||
// Shaped like the app's History tab reads it (camelCase, decimal strings).
|
||||
const t = parseHistoryPosition({
|
||||
positionId: 'bdda9024-63a9-4abc-a5a8-f83df8ba030a', accountId: '843f60e2-e013-5a0e-8b6c-face84bc5509', symbol: 'NDX-USD-PERP',
|
||||
side: 'short', quantity: '4.2712', leverage: '50', openPrice: '29529.75', closePrice: '29556.50',
|
||||
pnl: '-114.25', fee: '6.31', funding: '-0.12', collateral: '2522.54', takeProfits: [], stopLosses: [],
|
||||
openDate: 1793065590123, closeDate: 1793066346456, createdAt: 1793065590123, updatedAt: 1793066346456, closeReason: 'user_requested',
|
||||
});
|
||||
assert.deepEqual(t, {
|
||||
positionId: 'bdda9024-63a9-4abc-a5a8-f83df8ba030a', symbol: 'NDX-USD-PERP', side: 'short', quantity: 4.2712, leverage: 50,
|
||||
openPrice: 29529.75, closePrice: 29556.5, pnl: -114.25, fee: 6.31, funding: -0.12,
|
||||
openedAt: 1793065590123, closedAt: 1793066346456, closeReason: 'user_requested',
|
||||
});
|
||||
});
|
||||
|
||||
test('parseHistoryPosition: open, snake_case, seconds, and junk', () => {
|
||||
const open = parseHistoryPosition({
|
||||
position_id: 'p1', symbol: 'GC-PERP', side: 'LONG', quantity: '0', open_price: '4208.9', close_price: '',
|
||||
pnl: '', open_date: 1793065590, close_date: null, close_reason: '',
|
||||
});
|
||||
assert.equal(open.side, 'long');
|
||||
assert.equal(open.quantity, 0, 'a size Vest leaves out reads 0 — the store keeps the one it knows');
|
||||
assert.equal(open.openPrice, 4208.9);
|
||||
assert.equal(open.closePrice, null);
|
||||
assert.equal(open.pnl, null);
|
||||
assert.equal(open.openedAt, 1793065590000);
|
||||
assert.equal(open.closedAt, null);
|
||||
assert.equal(open.closeReason, null);
|
||||
assert.equal(parseHistoryPosition({ symbol: 'GC-PERP', openDate: 1 }), null, 'no id');
|
||||
assert.equal(parseHistoryPosition({ positionId: 'p2', symbol: 'GC-PERP' }), null, 'no open time');
|
||||
assert.equal(parseHistoryPosition(null), null);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user