{"record":{"id":"86d89b1c58305cb6","repo":"nautechsystems/nautilus_trader","slug":"tick-size-tick-size-must-be-positive","errorCode":null,"errorMessage":"Tick size {tick_size} must be positive","messagePattern":"Tick size (.+?) must be positive","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/adapters/polymarket/src/http/parse.rs","lineNumber":317,"sourceCode":"        .maybe_min_notional(bo.min_notional)\n        .max_price(max_price)\n        .min_price(min_price)\n        .margin_init(bo.margin_init)\n        .margin_maint(bo.margin_maint)\n        .maker_fee(bo.maker_fee)\n        .taker_fee(bo.taker_fee)\n        .maybe_info(bo.info.clone())\n        .ts_event(ts_event)\n        .ts_init(ts_init)\n        .build()?;\n\n    Ok(InstrumentAny::BinaryOption(rebuilt))\n}\n\n// Returns the tradeable price bounds `[tick_size, 1 - tick_size]` for a Polymarket outcome,\n// mirroring the venue range enforced in `PolymarketOrderBuilder::validate_limit_price`.\npub(crate) fn tick_relative_price_bounds(tick_size: Decimal) -> anyhow::Result<(Price, Price)> {\n    anyhow::ensure!(\n        tick_size > Decimal::ZERO,\n        \"Tick size {tick_size} must be positive\"\n    );\n\n    let min_price = Price::from_decimal_dp(tick_size, POLYMARKET_PRICE_PRECISION)?;\n    let max_price = Price::from_decimal_dp(Decimal::ONE - tick_size, POLYMARKET_PRICE_PRECISION)?;\n\n    anyhow::ensure!(\n        min_price.as_decimal() == tick_size,\n        \"Tick size {tick_size} is not exactly representable at Polymarket price precision {POLYMARKET_PRICE_PRECISION}\"\n    );\n    Ok((min_price, max_price))\n}\n\nfn build_info_json(def: &PolymarketInstrumentDef) -> serde_json::Value {\n    let mut map = serde_json::Map::new();\n    map.insert(\n        \"token_id\".to_string(),","sourceCodeStart":299,"sourceCodeEnd":335,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/adapters/polymarket/src/http/parse.rs#L299-L335","documentation":"tick_relative_price_bounds computes the tradeable price range [tick_size, 1 - tick_size] for a Polymarket outcome and requires a strictly positive tick size, since a non-positive tick would produce an invalid/degenerate price band.","triggerScenarios":"Calling tick_relative_price_bounds (via create_instrument_from_def or rebuild_instrument_with_tick_size) with tick_size <= 0, e.g. Decimal::ZERO or a negative value parsed from market data.","commonSituations":"Venue sending tick_size \"0\" or \"0.0\" in a market message before the real value arrives, or a data bug producing negative tick sizes.","solutions":["Check the tick_size value in the market payload before rebuilding the instrument.","Guard with an explicit tick_size > Decimal::ZERO check and skip the update if not positive.","Re-fetch authoritative instrument definitions from the Gamma/CLOB REST API when the streamed tick size looks invalid."],"exampleFix":"// before\nrebuild_instrument_with_tick_size(&instrument, &msg.new_tick_size)?;\n// after\nif Decimal::from_str(&msg.new_tick_size)? <= Decimal::ZERO {\n    tracing::warn!(\"ignoring non-positive tick size {}\", msg.new_tick_size);\n    return Ok(());\n}\nrebuild_instrument_with_tick_size(&instrument, &msg.new_tick_size)?;","handlingStrategy":"validation","validationCode":"let tick = parse_decimal_exact(&new_tick_size)?;\nif tick <= Decimal::ZERO { bail!(\"tick size must be positive, got {tick}\"); }","typeGuard":"fn is_positive_tick(d: Decimal) -> bool { d > Decimal::ZERO }","tryCatchPattern":"match tick_relative_price_bounds(tick_size) {\n    Ok((min, max)) => (min, max),\n    Err(e) => { warn!(\"invalid tick size {tick_size}: {e}\"); return Ok(()); }\n}","preventionTips":["Ignore market messages carrying zero or negative tick sizes (often pre-open placeholders).","Assert tick_size > 0 in instrument definition fixtures.","Keep a whitelist of valid Polymarket tick sizes."],"tags":["rust","validation","decimal","polymarket"],"backgroundTag":"invalid-argument-value","analyzedSha":"18893faf8b356be3320add8de2f861b0b647cf06","analyzedAt":"2026-09-08T20:49:34.690Z","contentChangedAt":"2026-09-08T20:49:34.690Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}