Load when comparing, rounding, formatting, or parsing prices, or designing price types.
81%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
Passed
No findings from the security scan
Problems with a kendex-owned skill go through kendex report; check ownership in the file first.
f64IEEE 754 double precision. No fixed-point, no decimal types.
Use i64 fixed-point only for a matching engine (bit-exact required), a regulatory audit trail mandating reproducibility, or a settlement system with legal precision requirements. Hybrid: i64 on the execution hot path, f64 for display and analytics.
== on pricesuse float_cmp::{approx_eq, F64Margin};
pub const PRICE_EPSILON: f64 = 1e-10; // sub-pipette tolerance
pub fn prices_equal(a: f64, b: f64) -> bool {
approx_eq!(f64, a, b, epsilon = PRICE_EPSILON, ulps = 4)
}
pub fn price_gte(a: f64, b: f64) -> bool { a > b || prices_equal(a, b) }
pub fn price_lte(a: f64, b: f64) -> bool { a < b || prices_equal(a, b) }| Boundary | Rounding |
|---|---|
| Order submission (OrderRequest → broker API) | Round to tick, then validate alignment |
| Display formatting | Round to the symbol's display_decimals |
| Market data ingestion (ticks, bars, quotes) | Never. Preserve full feed precision |
| P&L calculation | Never. Use raw values |
pub fn round_to_tick(price: f64, tick_size: f64) -> f64 {
(price / tick_size).round() * tick_size
}
pub fn validate_tick_alignment(price: f64, tick_size: f64) -> bool {
prices_equal(price, round_to_tick(price, tick_size))
}Order submission order: round to tick, validate alignment (on failure return an error, never re-round), then format for the broker API if it takes a string.
Format with the symbol's precision, never hardcoded decimals (EURUSD 5, AAPL 2, BTC 8):
format!("{:.1$}", price, symbol.display_decimals as usize)Tick size and display precision belong to the symbol, not to the price value. A Price { value, decimals } struct is the wrong shape.
#[derive(Clone, Copy)]
pub struct SymbolSpec {
pub symbol_id: u32,
pub tick_size: f64, // minimum price increment
pub display_decimals: u8, // decimal places for UI
pub lot_size: f64, // minimum quantity
}
impl SymbolSpec {
pub fn round_price(&self, price: f64) -> f64 {
round_to_tick(price, self.tick_size)
}
pub fn format_price(&self, price: f64) -> String {
format!("{:.1$}", price, self.display_decimals as usize)
}
}The symbol table loads at subscription setup (cold path), is keyed by symbol ID, and re-syncs on reconnect or symbol list change.
Optional: wrap f64 in a #[repr(transparent)] newtype with PartialOrd but no PartialEq; equality goes through prices_equal. Constructor debug_assert!s is_finite(). Use for order types; skip on the market-data hot path.
f64 at ingestConvert at the entry boundary; downstream code sees plain f64 regardless of feed source.
// doubles (IB, dxFeed, Rithmic): pass through
fn ingest_double(value: f64) -> f64 { value }
// strings (Binance, Coinbase): parse
fn ingest_string(s: &str) -> Result<f64, ParseFloatError> { s.parse() }
// scaled integers (CME MDP): unscale
fn ingest_scaled(mantissa: i64, exponent: i8) -> f64 {
mantissa as f64 * 10f64.powi(exponent as i32)
}vanillagreencom/kendex
32d7bc7
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.