Synth Data Integration
Access ML-powered price predictions and Polymarket probability estimates from Synthdata.
Overview
Pyx integrates with Synthdata's prediction network to provide ML-derived probability estimates and volatility forecasts directly in your scripts. Synthdata aggregates predictions from 200+ competing AI models to produce probabilistic price forecasts for BTC, ETH, SOL, and other assets.
Data is refreshed approximately every 60 seconds server-side. No configuration is needed - the data is available to all scripts automatically.
pyx.get_synth_data()
Returns the latest Synthdata snapshot, or nil if data is not yet available.
local synth = pyx.get_synth_data()
if synth and synth.hourly then
pyx.log(string.format("Synth fair: %.4f | Poly: %.4f | Edge: %.4f",
synth.hourly.synth_prob_up,
synth.hourly.poly_prob_up,
synth.hourly.synth_prob_up - synth.hourly.poly_prob_up
))
endReturns a table or nil:
| Field | Type | Description |
|---|---|---|
asset | string | Asset symbol (e.g. "BTC") |
updated_at | number | Last update time (ms since epoch) |
hourly | table? | Hourly Up/Down probability data |
percentiles | table? | Price forecast percentiles at multiple horizons |
volatility | table? | Volatility forecast data |
Hourly
The hourly field contains probability estimates for Polymarket's BTC Up/Down markets. The synth_prob_up value represents Synthdata's ML-derived estimate of the probability that BTC will be higher at the end of the period - this is your fair price for the Up token.
| Field | Type | Description |
|---|---|---|
synth_prob_up | number | Synth's probability of "Up" outcome (0–1) |
poly_prob_up | number | Current Polymarket implied probability of "Up" |
current_price | number | Current BTC price |
start_price | number | BTC price at period start |
synth_outcome | string | Synth's predicted outcome ("Up" or "Down") |
event_end_time | string | Market expiry time (ISO 8601) |
local synth = pyx.get_synth_data()
if synth and synth.hourly then
local edge = synth.hourly.synth_prob_up - synth.hourly.poly_prob_up
if edge > 0.05 then
pyx.log(string.format("BUY Up: synth=%.2f poly=%.2f edge=%.2f",
synth.hourly.synth_prob_up,
synth.hourly.poly_prob_up,
edge
))
elseif edge < -0.05 then
pyx.log(string.format("BUY Down: synth_down=%.2f poly_down=%.2f edge=%.2f",
1 - synth.hourly.synth_prob_up,
1 - synth.hourly.poly_prob_up,
-edge
))
end
endPercentiles
Price forecast percentiles at fixed horizons derived from Synthdata's prediction network. Each horizon contains price levels at various probability percentiles.
| Field | Type | Description |
|---|---|---|
current_price | number | Current asset price |
5m | table? | 5-minute forecast |
15m | table? | 15-minute forecast |
30m | table? | 30-minute forecast |
1h | table? | 1-hour forecast |
4h | table? | 4-hour forecast |
Each horizon table contains percentile keys mapping to price levels:
| Key | Description |
|---|---|
"0.005" | 0.5th percentile (extreme low) |
"0.05" | 5th percentile |
"0.2" | 20th percentile |
"0.35" | 35th percentile |
"0.5" | 50th percentile (median) |
"0.65" | 65th percentile |
"0.8" | 80th percentile |
"0.95" | 95th percentile |
"0.995" | 99.5th percentile (extreme high) |
local synth = pyx.get_synth_data()
if synth and synth.percentiles then
local h1 = synth.percentiles["1h"]
if h1 then
local median = h1["0.5"]
local p95 = h1["0.95"]
local p05 = h1["0.05"]
pyx.log(string.format("1h forecast: p5=%.0f median=%.0f p95=%.0f", p05, median, p95))
end
-- Use percentiles to estimate probability of BTC above a strike
-- If current price is between p35 and p50, roughly 35-50% chance of being lower
local m5 = synth.percentiles["5m"]
if m5 then
local current = synth.percentiles.current_price
local range = m5["0.95"] - m5["0.05"]
pyx.log(string.format("5m 90%% range: $%.0f (±$%.0f)", range, range / 2))
end
endVolatility
Forecasted and realized volatility metrics for the asset.
| Field | Type | Description |
|---|---|---|
forecast_avg | number | Average forecasted volatility (annualized) |
realized_avg | number | Average realized volatility (annualized) |
current_price | number | Current asset price |
local synth = pyx.get_synth_data()
if synth and synth.volatility then
local vol_ratio = synth.volatility.forecast_avg / synth.volatility.realized_avg
if vol_ratio > 1.5 then
pyx.warn("High forecast vol - widening spreads recommended")
end
endUse volatility data to dynamically adjust your strategy:
- Widen spreads when
forecast_avgis elevated relative torealized_avg(regime change) - Reduce position sizes during high volatility periods
- Tighten spreads when volatility is low and stable for more competitive quoting
Example: Fair Price Market Maker
Instead of quoting around the market midpoint, use Synth's probability estimate as the fair price:
local CONFIG = {
spread = 0.02,
order_size = 10.0,
min_edge = 0.01, -- minimum edge before quoting
}
local token_id = nil
local condition_id = nil
local last_bid = nil
local last_ask = nil
local function update_quotes(fair)
if not token_id then return end
local pos = pyx.get_position(token_id)
local qty = pos and pos.qty or 0
local bid_price = math.floor((fair - CONFIG.spread) * 100 + 0.5) / 100
local ask_price = math.ceil((fair + CONFIG.spread) * 100 - 0.5) / 100
if bid_price <= 0 or bid_price >= 1 then bid_price = nil end
if ask_price <= 0 or ask_price >= 1 then ask_price = nil end
if qty <= 0 then ask_price = nil end
if bid_price == last_bid and ask_price == last_ask then return end
last_bid = bid_price
last_ask = ask_price
pyx.cancel_all()
if bid_price then
pyx.buy(token_id, condition_id, bid_price, CONFIG.order_size)
end
if ask_price then
pyx.sell(token_id, condition_id, ask_price, math.min(qty, CONFIG.order_size))
end
end
function on_price(ctx)
if not token_id then
token_id = ctx.token_id
condition_id = ctx.condition_id
pyx.log("Locked onto " .. (ctx.outcome or "unknown"))
end
if ctx.token_id ~= token_id then return end
local synth = pyx.get_synth_data()
if not synth or not synth.hourly then return end
local fair = synth.hourly.synth_prob_up
update_quotes(fair)
end
function on_fill(fill)
last_bid = nil
last_ask = nil
pyx.log(string.format("FILL: %s %.4f @ %.4f | pnl=%.4f",
fill.side, fill.qty, fill.price, fill.realized_pnl))
endExample: Edge-Based Directional Trading
Buy when Synth detects a significant mispricing relative to Polymarket:
local CONFIG = {
edge_threshold = 0.05, -- 5% minimum edge
order_size = 20.0,
max_position = 100.0,
}
local token_id = nil
local condition_id = nil
function on_price(ctx)
if not token_id then
token_id = ctx.token_id
condition_id = ctx.condition_id
pyx.log("Tracking " .. (ctx.outcome or "unknown"))
end
if ctx.token_id ~= token_id then return end
local synth = pyx.get_synth_data()
if not synth or not synth.hourly then return end
local pos = pyx.get_position(token_id)
local qty = pos and pos.qty or 0
local edge = synth.hourly.synth_prob_up - ctx.ask
if edge > CONFIG.edge_threshold and qty < CONFIG.max_position then
pyx.buy(token_id, condition_id, ctx.ask, CONFIG.order_size)
pyx.log(string.format("BUY edge=%.3f synth=%.3f ask=%.3f",
edge, synth.hourly.synth_prob_up, ctx.ask))
end
end
function on_fill(fill)
pyx.log(string.format("FILL: %s %.4f @ %.4f (%s)",
fill.side, fill.qty, fill.price, fill.liquidity))
endNotes
- Data availability depends on Synthdata's API uptime. Always check for
nilbefore accessing fields. - The
synth_prob_upvalue is a probability (0–1), which maps directly to the fair price of the Up token on Polymarket. - For the Down token, the fair price is
1 - synth_prob_up. - Synthdata currently supports BTC markets. Additional assets may be added in the future.
- Synth data updates roughly every 60 seconds - avoid making trading decisions that depend on sub-second freshness from this data.