Skip to content

Prop account model

A prop account is an evaluation contract over real signed trades. The platform never holds the trader’s trading funds — traders sign their own trades — while collateral and payouts settle on your firm’s on-chain treasury.

Each firm is one prop tenant with its own PropInstance contract, deployed by a shared factory. Your multisig owns the instance and its treasury. The platform’s risk engine acts as the contract’s observer: it marks pass, breach, and expiry, and sets payout caps. The trader deposits, quits, and requests payouts directly from their own wallet.

Two program kinds share one engine: challenge (tier ladder, profit or points target) and flash (collateral-to-funding ratio, time as currency).

Two paths reach active, and they don’t overlap. An account the trader buys goes pending_paymentactive when the deposit lands on-chain. A child account created by a tier-ladder or next-step promotion starts in pending_agreement and goes → active when the trader agrees. From there: activepassedpaid_out, with breached, expired, and cancelled as terminal branches.

Phase What happens
Catalog → buy The trader browses active templates and creates an account in pending_payment (POST /api/v1/prop-accounts).
Deposit The trader’s wallet calls the instance’s deposit function. The entry fee is skimmed, the remainder becomes collateral, and the account activates on-chain. The platform composes the calldata via POST /api/prop/deposit-calldata; the wallet broadcasts.
Deposit observed → active The claim watcher sees the on-chain ChallengeDeposited event and flips the account pending_paymentactive. No API call is involved — this is the only path that activates a bought account.
Evaluation The engine re-evaluates every rule on each trade, position, and funding change, plus a UTC-midnight roll.
Breach Any rule returning breach is terminal. The on-chain forfeiture split applies and positions are flattened.
Pass Pass conditions met. No money moves — the observer records the on-chain payout ceiling.
Promotion → agree If the passed account’s template carries a tier ladder or a next-step template, the engine creates a child account in pending_agreement. The trader accepts it with POST /api/v1/prop-accounts/{id}/agree, which moves that child to active. Called against any other status, /agree returns 409 invalid_state.
Funded A passed account keeps trading. The trader requests payouts; your multisig approves.
Quit The trader voluntarily closes an active account; the quit forfeiture split applies and the remainder is refunded. Terminal.
Expiry The observer marks expiry; the expiry split applies and collateral remainder is refunded. Terminal.

This is the core enforcement primitive, and it is not advisory.

On every order, the platform evaluates a hypothetical fill against each active prop account on the trading account the order is placed from, and rejects a breaching intent with prop_breach_would_occur (HTTP 409) — before the venue ever sees the order.

PATCH /api/v1/firms/{id}/hard-enforcement writes a per-firm override, but program rules are enforced by design: with the platform’s default settings the override is not consulted at all. Do not treat it as a way to run a firm in observe-only mode.

The practical consequence: a trader cannot breach by racing the evaluator. The order simply doesn’t reach the market.

Rules are snapshot-frozen at account creation, so editing a template never retroactively passes or fails an in-flight account.

Rule types include daily_loss_cap_usd · max_drawdown_usd · min_trading_days · profit_target_usd · max_concentration_pct · consistency_pct · allowed_venues · forbidden_symbols · points_target · inactivity_timer_s · collateral_drawdown_pct. The registry is larger than this list.

Each rule evaluates to { type, status, detail, metric?, threshold? }, where status is one of ok, breach, pass, pending, na, or missing_evaluator.

breach is terminal. pass satisfies a pass condition. pending means a goal is defined but not reached yet (min_trading_days at 3 of 5) — it does not breach the account, but it is not inert: a single pending rule holds the pass back, and it blocks a payout the same way a breach does. na (the rule doesn’t apply yet) and missing_evaluator (a rule type with no registered evaluator) never block anything.

Render pending as in-progress, not as a failure — most in-flight challenges carry at least one. GET /api/v1/prop-accounts/{id} returns these objects verbatim as results.

A client consuming the catalog also gets parsed headline thresholds (maxDdUsd, dailyLossUsd, profitTargetUsd) as first-class fields, so you can render a challenge card without interpreting the rule objects.

Manage them with prop:manage: /api/v1/prop-templates/* for templates, /api/v1/risk-profiles/* for reusable rule sets, /api/v1/trading-groups/* for fee, leverage, and venue scoping.

A passed account is a funded account that keeps trading. Four properties define the mechanic:

  1. The ceiling is profit × split. At pass, the observer records a per-account payout ceiling equal to the trader’s split-adjusted profit. A trader can never withdraw more than they earned.
  2. Two independent caps. Every payout is gated by both the profit entitlement and treasury solvency.
  3. Multiple withdrawals, no close on payout. Approving accumulates the payout total and leaves the account passed — the funded trader keeps trading. It only leaves that state via breach, quit, or expiry.
  4. Lazy ceiling raise. When a funded trader requests a payout, their current entitlement is recomputed and the on-chain ceiling raised if they’ve earned more since passing. Raise-only.

Settlement is two-step: the trader’s request creates a pending on-chain request, and your multisig settles it. POST /api/prop/payout-calldata pre-checks the live ceiling, account, and treasury, returning exceeds_profit_entitlement (400) or treasury_underfunded (409) — though the contract enforces both regardless of what the API says.

Two operator controls that read alike and are not alike. Both need the prop:manage scope and are hard-scoped to your own tenant.

Call What it does
PATCH /api/v1/accounts/{id}/suspension Sets or clears the suspension. Positions are untouched. New opens are refused; the trader keeps what they hold.
POST /api/v1/accounts/{id}/block Suspends and flattens every open position, then writes an account.block audit row.
POST /api/v1/accounts/{id}/unblock Clears the suspension. It does not restore anything.

block accepts an optional reason, which lands in the audit row alongside how many positions were flattened and any that failed to close. Read those errors — a flatten that partially failed leaves the account suspended with positions still open, which is the one state neither name describes.

Both resolve the account by UUID or public account number.

GET /api/v1/analytics/prop-diagnosis/{propAccountId} answers “where does this challenge stand?” in one call, so you can build a trader-facing status screen without re-deriving the rules yourself.

Terminal window
curl -s https://api.troncharts.xyz/api/v1/analytics/prop-diagnosis/$PROP_ACCOUNT_ID \
-H "authorization: Bearer $TOKEN" \
-H "x-tenant-slug: $TC_TENANT_SLUG"

Returns the account’s status, its template (templateId, templateName, programKind), the lifecycle timestamps (startedAt, expiresAt, closedAt, closedReason), payoutOwedUsd, up to 50 recentTrades scoped to this account’s own fills within the challenge window, and a recommendation.

recommendation is one of continue, pause, closed, claim_ready.

Scoped to the calling session’s account: another account’s prop record answers 404.

Endpoint Returns
GET /api/v1/prop-accounts Accounts you can see.
GET /api/v1/prop-accounts/{id} One account with its frozen rule set and live metrics.
GET /api/v1/prop-accounts/{id}/events The full state-transition history.

Live progress is also pushed as Prop-Account-Update on /ws/risk.