Skip to content

Float safety: Gemini Titan normalizePosition unrealizedPnL float multiply #709

@realfishsam

Description

@realfishsam

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions