Risk Level
CRITICAL
Location
core/src/exchanges/gemini-titan/normalizer.ts:259
Code
normalizePosition(raw: GeminiRawPosition): Position {
const currentPrice = raw.prices?.bestBid
? parseFloat(raw.prices.bestBid)
: 0;
const entryPrice = parseFloat(raw.avgPrice);
const size = parseFloat(raw.totalQuantity);
return {
...
unrealizedPnL: (currentPrice - entryPrice) * size,
};
}
Problem
Three independent parseFloat values are chained into a compound arithmetic expression (currentPrice - entryPrice) * size. Each conversion carries up to ±0.5 ULP of error. The subtraction of two nearly-equal floats (entry price close to current price) causes catastrophic cancellation. The result is then multiplied by size, which amplifies the error.
Example Failure
entryPrice = parseFloat("0.55") // 0.5499999999999999778
currentPrice = parseFloat("0.56") // 0.5600000000000000089
size = parseFloat("100.0") // 100.0
// Subtraction: 0.5600000000000000089 - 0.5499999999999999778
// = 0.0100000000000000311 (not exactly 0.01)
// Multiply: 0.0100000000000000311 * 100 = 1.0000000000000031 (not 1.0)
// Correct: (0.56 - 0.55) * 100 = 1.0 USD P&L
// Reported: 1.0000000000000031 USD
With larger sizes (1000 contracts, price moves 3 ticks), the error grows proportionally and is directly visible to users.
Suggested Fix
Use Decimal.js or represent prices in integer cents/basis-points throughout:
// With Decimal.js:
unrealizedPnL: new Decimal(currentPrice).minus(entryPrice).times(size).toNumber()
// Or use integer cents stored as numbers until final display:
unrealizedPnL: (currentPriceCents - entryPriceCents) * size / 100
Found by automated float safety audit
Risk Level
CRITICAL
Location
core/src/exchanges/gemini-titan/normalizer.ts:259Code
Problem
Three independent
parseFloatvalues are chained into a compound arithmetic expression(currentPrice - entryPrice) * size. Each conversion carries up to ±0.5 ULP of error. The subtraction of two nearly-equal floats (entry price close to current price) causes catastrophic cancellation. The result is then multiplied bysize, which amplifies the error.Example Failure
With larger sizes (1000 contracts, price moves 3 ticks), the error grows proportionally and is directly visible to users.
Suggested Fix
Use
Decimal.jsor represent prices in integer cents/basis-points throughout:Found by automated float safety audit