Pricing and settlement
How the Binary AI Engine decides an outcome, how the settlement close price is nudged inside a bounded band, every condition that refuses to steer, and where binary payouts actually come from.
This is the page that explains what the addon actually does to a trade. Read it before you activate an engine on live money — not because the mechanism is complicated, but because every safety rule in it exists for a reason you will otherwise discover the hard way.
Payouts are not set here
Start with what the engine does not control.
What a winning binary trade pays comes from the per-type Profit % under
Finance → Binary → Settings, adjusted by the per-duration
profitAdjustment, and it is stamped onto the order row at the moment the
order is placed. The engine has no input into it. There is deliberately no
payout column on the engine.
| Type | Default payout | Break-even user win rate |
|---|---|---|
| RISE_FALL | 72% | 58.1% |
| HIGHER_LOWER | 68% base, per-barrier override | varies by barrier |
| TOUCH_NO_TOUCH | 95% base, with touch/no-touch multipliers | varies |
| CALL_PUT | 72% base, per-strike override | varies |
| TURBO | 65% base, per-barrier override | varies |
Break-even is 1 / (1 + payout). Duration overrides move the effective payout —
ultra-short expiries get a bonus, hour-plus expiries a penalty, on the reasoning
that skilled traders have more edge over a longer horizon.
Two knobs, two owners: Finance → Binary → Settings decides what a win pays, the engine decides how often a win happens. Change either and you change your margin.
The decision: which side should win
Once a second, each ACTIVE engine looks at the open Rise/Fall orders on its symbol and groups them into expiry buckets — all orders sharing an expiry time.
A bucket is skipped when:
- it has no orders;
- expiry is further out than
adjustmentLeadTimeSeconds(default 30); - expiry has already passed;
- the total staked (
totalRise + totalFall) is belowminPositionForOptimization.
For every surviving bucket the engine computes an effective target win rate,
starting from the engine's targetUserWinRate and adjusting it:
-
A/B arms move the target in aggregate. Because one close resolves the whole bucket, two users in different arms who expire together cannot be given different outcomes. Instead each order contributes its arm's target, count- weighted, and the bucket's target is the average. Over many buckets the arms' realised rates genuinely diverge. Live orders only.
-
Tier bonuses are averaged in. If
enableUserTiersis on, the mean win-rate bonus of the tiered orders in the bucket is added. -
Cooldowns subtract, proportionally. If any order belongs to a user under cooldown,
cooldownWinRateReductionis applied scaled by the share of the bucket those orders represent. -
The global band clamps the result. The effective target is forced back inside the platform min/max from the Settings page. CUSTOM practice mode is exempt, because it deliberately targets a friendlier rate than the live band allows.
-
The whale cap tightens it further. If the bucket contains any whale order, the effective target is capped at
whaleWinRateCap.
The engine then compares that target to the realised win rate for the current
period — wins / (wins + losses) from the engine's own counters:
| Condition | Decision |
|---|---|
| realised < target − variance | BELOW_TARGET — force a user win |
| realised > target + variance | ABOVE_TARGET — force a user loss |
| otherwise | WITHIN_RANGE — take whichever side is more profitable |
With zero settled orders in a period the realised rate is reported as the
configured target, so a fresh period resolves to WITHIN_RANGE and does not open
with a spurious forced outcome.
A forced win lands on the side holding the most orders. That is what makes the daily-loss guard below necessary.
The daily-loss override
Honouring the win-rate target must never cost more than you signed up for. Before
committing, the engine checks: if the chosen side would push the period's platform
profit below −maxDailyLoss, and the other side would not, it takes the other
side and records the reason with an _OVERRIDDEN_BY_DAILY_LOSS_LIMIT suffix.
The win rate drifts; the balance sheet does not. If the period profit cannot be read at all, the guard treats the budget as exhausted rather than assuming headroom.
Recording the decision
The chosen side is written to manipulationDetails.targetSide on every position
row in the bucket, together with the target price, direction, expected profit,
confidence and a timestamp. Positions are created on first sight and refreshed on
every subsequent tick, because bucket composition changes as new orders arrive.
wasManipulated stays false here. It only becomes true at reconciliation,
once the settlement price was genuinely adjusted.
The decision is recorded for live and demo alike; whether it is applied is decided at settlement.
The settlement seam
When an order expires, core Bicrypto asks the engine for an effective close price. The engine either returns the real close unchanged, or returns a nudged one.
The nudge is simple: push the close to the far edge of the allowed band in the house-favourable direction.
maxAdj = realClosePrice × maxPriceAdjustmentPercent
effective = targetSide === "RISE"
? realClosePrice + maxAdj
: realClosePrice - maxAdjAt the default maxPriceAdjustmentPercent of 0.003 that is 0.3% of the close.
The column is hard-capped at 0.01 (1%) in code, and the cap is re-applied when
the configuration is loaded, so a row written by a direct database edit cannot
widen it either.
Only outcomes inside the band flip. A user who won by more than 0.3% still wins.
Seven ways it refuses to steer
Every refusal is deliberate, and every one is recorded on the position row as
manipulationDetails.settlementDecision. The dashboard aggregates them over the
last 24 hours. This is the list to check when an ACTIVE engine appears to be doing
nothing.
| Recorded reason | Meaning |
|---|---|
FAIR:NO_ENGINE_LOADED |
No engines resident in this process — usually the cron is not running. |
FAIR:GLOBAL_PAUSE |
The global pause switch is on. |
FAIR:NOT_ENGINE_TRACKED |
No position row for this order; the engine never saw it in a bucket. |
FAIR:ENGINE_NOT_RESIDENT |
The position's engine is not loaded here. |
FAIR:ENGINE_PAUSED / FAIR:ENGINE_STOPPED |
The engine is not ACTIVE. |
FAIR:SIMULATION_MODE |
Per-engine shadow mode. |
FAIR:GLOBAL_PRACTICE_MODE |
Platform-wide shadow mode. |
FAIR:DEMO_PRACTICE_DISABLED |
A demo order on an engine with practice mode off. |
FAIR:NO_DECISION_RECORDED |
No targetSide — the bucket was skipped, often below minPositionForOptimization. |
FAIR:STEERING_BAND_ZERO |
maxPriceAdjustmentPercent is 0. |
FAIR:MARKET_NOT_ECOSYSTEM |
The binary market is exchange-backed. |
FAIR:NO_ECOSYSTEM_MARKET |
No ecosystem market for the pair. |
FAIR:MARKET_MAKER_PAUSED / _STOPPED / _MISSING |
The market maker is not driving the series. |
FAIR:EXPIRY_MINUTE_CLOSED(+Nm) |
Settlement ran N minutes after the expiry candle sealed. |
FAIR:PUBLISH_FAILED |
The steered price could not be written into the candle series. |
FAIR:EFFECTIVE_PRICE_NOT_POSITIVE |
The nudge would produce a non-positive price. |
FAIR:ERROR |
Something threw. Settled honestly. |
STEERED |
The close was nudged and published. |
STEERED:BUCKET_PIN |
Reused the close already published for this expiry minute. |
It means settlements are running after the minute their expiry candle covers has already closed. The refusal is correct — rewriting a candle a client has already seen is exactly what must not happen — but the trend tells you settlement is running late. Look at cron health and database latency, not at the engine.
The two publication rules
These are the rules that keep the settled price and the visible chart in agreement. Both fail safe.
The market must be one you actually publish
The engine checks that the binary market's source is ECOSYSTEM, that a
matching ecosystem market exists, and that its AI Market Maker is ACTIVE.
The last condition matters more than it looks. An engine can be ACTIVE while its market maker is stopped — restart recovery revives engines without re-checking. In that state nothing else writes the tape: entries read the last candle close and settlements publish into it, both numbers the engine itself last wrote. It would be settling against its own prints. So it does not.
The expiry candle must still be open
The steered value becomes the close of the 1-minute candle covering expiry. If the minute has already rolled over, that candle is history and a client may already have seen it. The engine compares the expiry minute to the current minute and refuses when they differ, recording how late it was.
One bucket, one close
Every order sharing an expiry minute settles on the same number. Without that, each settlement would re-apply the band to the price the previous settlement had just published — six orders would ratchet the series by roughly 1.8% and hand later entrants an outcome the band was never meant to reach.
Two layers enforce it:
- an in-process map keyed by symbol and expiry minute;
- a durable pin recorded by the settlement-close authority, so two processes or a restart mid-bucket cannot each publish a different close for the same minute.
The pin is the same record the market maker and the matching engine defer to, so all three agree by construction.
Publishing the steered price
When the engine steers, it writes the value into the ecosystem candle series and
updates the market maker's lastKnownPrice. The candle write pins the value as
that minute's close for the rest of the minute — without the pin the market
maker's next write (within a second on a trade tick, ten seconds on its
heartbeat) would overwrite it and leave the settled order permanently disagreeing
with the chart.
Publishing happens before the price is returned. If it fails, the engine settles fairly instead — a price nobody could see is not a price it will settle on.
Reconciliation
After the settlement transaction commits, the engine reconciles the order into its position row. This is idempotent and never throws.
- Outcome and status are written; the position moves to
SETTLED. - Platform profit is computed: a user WIN is
−profitto the house; a total LOSS is+stake; a partial loss (Turbo) is−profit, which is positive; a DRAW is zero. wasManipulatedis set true only if the engine was genuinely active, non-simulation, not globally shadowed, not a blocked demo order, and a decision had been recorded.settlementDecisionrecords exactly which of the reasons above applied.- Period counters are updated for live orders; demo orders only count when practice mode is engaged.
- Cooldowns are evaluated against the user's realised profit.
Volatility masking
volatilityMaskingEnabled (on by default) adds a small random noise component,
bounded by volatilityNoisePercent (default 0.001, maximum 0.05), to the
market-side price nudges. Its purpose is to stop the engine's interventions from
forming an obvious repeating pattern on the chart. It does not widen the
settlement band, which stays governed by maxPriceAdjustmentPercent alone.
What to check when nothing is being steered
In order — the first two account for most cases:
processBinaryAiEnginein Admin → System → Cron. No cron, no engines.- The dashboard's settlement verdict breakdown. It names the reason.
- The attached market maker's status.
- The binary market's
source. minPositionForOptimizationagainst the stakes people are actually placing.adjustmentLeadTimeSecondsagainst the durations you offer.
Troubleshooting walks through each of these.