Files
managing-apps/src/Managing.Web3Proxy/test/plugins/get-position-history.test.ts

116 lines
4.8 KiB
TypeScript

import {test} from 'node:test'
import assert from 'node:assert'
import {getClientForAddress, getPositionHistoryImpl} from '../../src/plugins/custom/gmx.js'
test('GMX get position history - Closed positions with actual PnL', async (t) => {
await t.test('should get closed positions with actual GMX PnL data', async () => {
const sdk = await getClientForAddress('0x8767F195D1a3103789230aaaE9c0E0825a9802c6')
const result = await getPositionHistoryImpl(
sdk,
0, // pageIndex
500 // pageSize
)
console.log('\n📊 Closed Positions Summary:')
console.log(`Total closed positions: ${result.length}`)
if (result.length > 0) {
console.log('\n💰 PnL Details:')
result.forEach((position: any, index) => {
console.log(`\n--- Position ${index + 1} ---`)
console.log(`Ticker: ${position.ticker}`)
console.log(`Direction: ${position.direction}`)
console.log(`Close Price: $${position.price?.toFixed(2) || 'N/A'}`)
console.log(`Quantity: ${position.quantity?.toFixed(4) || 'N/A'}`)
console.log(`Leverage: ${position.leverage}x`)
console.log(`Status: ${position.status}`)
console.log(`PnL After Fees: $${position.pnl?.toFixed(2) || 'N/A'}`)
console.log(`UI Fees: $${position.UiFees?.toFixed(2) || 'N/A'}`)
console.log(`Gas Fees: $${position.GasFees?.toFixed(2) || 'N/A'}`)
if (position.ProfitAndLoss) {
console.log(`Realized PnL: $${position.ProfitAndLoss.realized?.toFixed(2) || 'N/A'}`)
console.log(`Net PnL: $${position.ProfitAndLoss.net?.toFixed(2) || 'N/A'}`)
}
// Verify critical data for reconciliation
assert.ok(position.ProfitAndLoss, 'Position should have ProfitAndLoss data')
assert.ok(typeof position.ProfitAndLoss.realized === 'number', 'Realized PnL should be a number')
assert.ok(typeof position.pnl === 'number', 'Position pnl should be a number')
})
// Calculate total PnL
const totalPnL = result.reduce((sum: number, pos: any) => sum + (pos.pnl || 0), 0)
console.log(`\n💵 Total PnL from all closed positions: $${totalPnL.toFixed(2)}`)
}
assert.ok(result, 'Position history result should be defined')
assert.ok(Array.isArray(result), 'Position history should be an array')
})
await t.test('should get closed positions with date range', async () => {
const sdk = await getClientForAddress('0x8767F195D1a3103789230aaaE9c0E0825a9802c6')
// Get positions from the last 1 hour
const toDate = new Date()
const fromDate = new Date(toDate.getTime() - (60 * 60 * 1000)) // 1 hour ago
const fromDateTime = fromDate.toISOString()
const toDateTime = toDate.toISOString()
const result = await getPositionHistoryImpl(
sdk,
0,
10,
undefined, // ticker
fromDateTime,
toDateTime
)
console.log(`\n📅 Closed positions in last 1 hour: ${result.length}`)
console.log(`From: ${fromDateTime}`)
console.log(`To: ${toDateTime}`)
// Verify all positions are within date range
result.forEach(position => {
const positionDate = new Date(position.date)
console.log(`Position date: ${positionDate.toISOString()}`)
const isInRange = positionDate >= fromDate && positionDate <= toDate
// Note: This assertion might fail if GMX returns positions outside the range
// In that case, GMX API might not support the filtering properly
if (!isInRange) {
console.warn(`⚠️ Position date ${positionDate.toISOString()} is outside requested range`)
}
})
assert.ok(result, 'Position history result should be defined')
assert.ok(Array.isArray(result), 'Position history should be an array')
})
await t.test('should verify PnL data is suitable for reconciliation', async () => {
const sdk = await getClientForAddress('0x8767F195D1a3103789230aaaE9c0E0825a9802c6')
const result = await getPositionHistoryImpl(sdk, 0, 5)
console.log('\n🔍 Reconciliation Data Verification:')
result.forEach((position: any, index) => {
console.log(`\nPosition ${index + 1}:`)
console.log(` Has ProfitAndLoss: ${!!position.ProfitAndLoss}`)
console.log(` Has realized PnL: ${typeof position.ProfitAndLoss?.realized === 'number'}`)
console.log(` Realized value: ${position.ProfitAndLoss?.realized}`)
console.log(` Has fees: UI=${position.UiFees}, Gas=${position.GasFees}`)
// These are the critical fields needed for HandleClosedPosition reconciliation
assert.ok(position.ProfitAndLoss, 'Must have ProfitAndLoss for reconciliation')
assert.ok(
typeof position.ProfitAndLoss.realized === 'number',
'Must have numeric realized PnL for reconciliation'
)
assert.ok(position.status === 'Finished', 'Position should be finished')
})
})
})