Skip to content

Prices, sizes, and units

Every numeric field that represents a human price, size, balance, or volume is a decimal string — books, fills, candles, trades, and maker balances all share the same Decimal { value } wrapper. This is the load-bearing contract: get it right and everything downstream works.

Why strings

Floating-point arithmetic loses precision for large notionals. Decimal strings preserve the exact representation. Parse with whatever fixed-precision type your language offers:

LanguageLibrary
Rustrust_decimal::Decimal
Pythondecimal.Decimal
TypeScriptbignumber.js or decimal.js

Conventions on the wire

FieldExampleMeaning
price"155.14"Quote per base, human-readable
size"10.0"Base token units (e.g. SOL)
volume"42.137"Candle base-asset volume
open / high / low / close"154.97"Candle OHLC
balance"1.5"Maker balance in token units (server scales by SpotMetadata.decimals)
best_bid / best_ask / midstringsOptional, omitted on empty side

You will never see on the wire:

  • Lot multiples.
  • Oracle units.

MakerService reports balances in human token units — the server applies SpotMetadata.decimals for you, so a 1.5-SOL balance arrives as Decimal("1.5"), not 1_500_000_000 atoms.

The SDK's quoting layer converts the human values you pass to the on-chain integer forms exactly once, at the point of building the instruction. The integer forms are documented in How Flint works for completeness — most integrators never have to think about them.

Pricing helpers

For when you need the conversion outside a QuoteBuilder call:

rust
use sweetspot_api_client::pricing::{human_to_oracle, oracle_to_human, human_size_to_lots};

let market = config.pairs.iter().find(|p| p.base_name == "SOL").unwrap();

let oracle = human_to_oracle(155.14, market);  // → 155_140
let back = oracle_to_human(oracle, market);    // → 155.14
let lots = human_size_to_lots(10.0, market);   // → 10_000

Edge cases (saturate, NaN, negative) are documented in the Rust function-level docs.

Python exposes the same conversion helpers from flint.onchain, but human money inputs must be decimal.Decimal, decimal strings, or integers. Floats, NaN/Infinity, negatives, overflows, fractional atoms, and non-lot-aligned sizes raise instead of being rounded into quote bytes.

python
from decimal import Decimal
from flint.onchain import human_size_to_lots, human_to_oracle, oracle_to_human

oracle = human_to_oracle(Decimal("155.14"), market.units)
back = oracle_to_human(oracle, market.units)
lots = human_size_to_lots("10.0", market.units)

Timestamps

  • Timestamp { micros } — Unix microseconds since epoch. Used on trades, candles, stream events, and range bounds.
  • slot — Solana slot number. Useful for cross-referencing on-chain data.
  • Historical start is inclusive. Historical end is exclusive and defaults to server time when unset or micros = 0.

Enums on the wire

Enum values use their proto variant names in examples and generated code:

FieldExamples
interval"CANDLE_INTERVAL_5M", "CANDLE_INTERVAL_1H", "CANDLE_INTERVAL_1D"
side"SIDE_BUY", "SIDE_SELL"
state"HEALTH_STATE_HEALTHY", "HEALTH_STATE_DEGRADED"

The SDKs all expose typed enums, so you don't have to hardcode the raw variant names or numbers.

Build on Solana