Every trading simulator, paper-trading app, and fantasy-market contest eventually has to answer the same question: what price does the game settle on? The moment a leaderboard rank or a cash payout depends on a market price, that price stops being a display detail and becomes a rule of the game. A player who loses a contest by two cents will ask where the number came from. If the honest answer is "whatever the API happened to return at nine o'clock," the dispute is already half lost.
Settlement pricing is a design decision, and it deserves the same care as the scoring logic. Here is how to make it defensible.
Why "the closing price" is not a single number#
For a US stock, "the close" sounds official. In practice, an equity trades on more than a dozen venues at once, and each venue prints its own last trade. Off-venue trades report separately. Two data providers can legitimately disagree by a few cents at 4:00 p.m. because they saw different final prints, applied different filters, or cut the day off milliseconds apart. Neither is lying. They're answering slightly different questions.
Crypto removes even the pretense of an official close. BTCUSD trades around the clock across many independent venues, so a "daily close" is whatever convention you pick: midnight UTC, midnight in your users' timezone, or the top of the contest's final hour. The market doesn't define it. Your app does. The same goes for FX, where the trading week has edges but the day doesn't, and for on-chain pairs, where the price depends on which pool you read and when.
A settlement price therefore embeds three choices: which symbol, which source or aggregation, and which timestamp in which timezone. An app that hasn't written those three down is running on defaults nobody chose.
The three failure cases that generate disputes#
Most player disputes trace back to one of three technical events.
Two sources disagree at the settlement moment. The app settles on provider A, a player checks the same instrument on their phone using provider B, and the two differ by a few cents. If the contest margin was smaller than that gap, you're now explaining venue fragmentation in a support ticket. The gap itself is normal. The missing piece is a published rule that names your source in advance.
A stale or thin quote becomes the settlement print. Thin markets are where this bites: an exotic FX pair late on a Friday, a shallow DEX pool, a quiet instrument after hours. The last trade might be minutes or hours old, or a single tiny print far from the prevailing market. If the settlement job blindly stores "last price at time T," one bad print can decide a contest. This is also how a manipulated print gets in: on a thin venue, nudging the last trade briefly is cheap, and the damage lands on every app that settled against it.
Historical replay ignores corporate actions. Replay modes let players trade against past data. If the price series is unadjusted, a 4-for-1 stock split looks like a 75 percent overnight crash, and players who "shorted" it collect an absurd win. If the series is adjusted, the prices on screen won't match what news archives reported that day, which triggers its own disputes. Both conventions are workable. Making the choice silently is the failure.
What a defensible settlement setup looks like#
Three ingredients turn "whatever the job stored" into a rule you can defend.
A written settlement rule players can read. One short page: the symbol, the data source, the exact timestamp and timezone, what happens when data is degraded or missing, and how ties break. It converts every future dispute from an argument about fairness into a citation.
A price aggregated across venues. A single venue's last print inherits that venue's outages, gaps, and thin moments. An aggregate is harder to distort and easier to justify. SiftingIO publishes one fair price per instrument, computed as a volume-weighted median across multiple independent venues after staleness and outlier filtering, with per-venue reputation scoring; the full pipeline is documented on the data methodology page. A median has a useful property for settlement: one bad venue can't move it, since a majority of venues would have to err in the same direction. On the live stream, each tick also carries source, ingest, and publish timestamps plus an explicit quality flag, so a settlement job can record its evidence and refuse to settle on a tick flagged Degraded. One bound belongs in your rulebook too: the aggregate is a reference value rather than an executable quote on any single venue, which is exactly the role a game settlement needs.
Capturing the consensus quote takes one call:
curl -H "X-API-Key: $SIFTING_KEY" \
"https://api.sifting.io/v1/last/quote/crypto/BTCUSD"
Store the entire response. Its bid, ask, and timestamp become your audit trail, and your rule decides whether you settle on the mid or the last trade.
A deliberate adjusted-versus-unadjusted decision for replay. For return-based scoring, split- and dividend-adjusted bars keep percentage math honest across corporate actions. For "what did the screen show that day" realism, unadjusted prices are the right choice, with splits applied as in-game position adjustments the way a real account would experience them. Pull daily history for the replay engine like this (historical bars require gzip):
curl --compressed -H "X-API-Key: $SIFTING_KEY" \
"https://api.sifting.io/v1/hist/stocks/AAPL/bars?interval=1d&limit=200"
Whichever convention you pick, label it in the UI, and check /docs for how the bars endpoints handle split and dividend adjustment.
Common pitfalls#
Missing gzip on historical bars. The /v1/hist/* endpoints require compression. Without an Accept-Encoding: gzip header (curl's, compressed flag) they return HTTP 406 with error code gzip_required. A backfill job that omits the header fails on every request while the same URL works in a browser, which makes this one confusing to debug.
Treating 503 stale_snapshot as an outage. When live data is older than the freshness threshold, /v1/last/* endpoints return a 503 with code stale_snapshot, and the body includes last_t and server_now so you can see the gap. For a settlement job this is a feature: it's the API refusing to hand you exactly the stale print described above. Retrying in a loop is the wrong response. Your written rule should say what happens instead, such as delaying settlement or voiding the round.
Expecting a provider-defined daily close for FX and crypto. These markets have no official closing auction the way a US stock exchange does: crypto trades around the clock, and the FX week has edges but the day does not. There is no single vendor-blessed daily print to lean on. If the contest advertises a 5 p.m. New York settlement, derive it from a timestamped live quote at that moment and say so in the rule; don't assume the API defines the day for you.
A settlement checklist you can copy#
- Publish the rule: symbol, data source, exact timestamp, timezone, tie-breaks, and the delay-or-void policy.
- Settle on a cross-venue aggregated price instead of one venue's last print.
- At settlement time, store the full API response: the price, its timestamp, and, if you settle off the live stream, the quality flag.
- Refuse to settle on degraded or stale data; apply the published fallback instead.
- For replay modes, pick adjusted or unadjusted bars deliberately and label the choice in the UI.
- Re-read the rule after every disputed round; each dispute points at a missing sentence.
Ready to wire it up? Read the docs.



