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_payment → active 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: active → passed → paid_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_payment → active. No API call is involved — this is the only path that activates a bought account.
Evaluation Rule monitoring runs in the risk engine connected to your instance, not in the order path.
Breach The risk engine blocks the account with POST /api/v1/accounts/{id}/block. A blocked account can only reduce.
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.

The platform does not check prop rules when an order is placed. Drawdown, daily loss, profit target and the other rules are monitored by the risk engine connected to your instance.

When the risk engine decides an account must stop, it calls POST /api/v1/accounts/{id}/block. From then on every opening order on that account is refused with 403 account_suspended; reduce-only orders still go through, so open positions stay closeable. POST /api/v1/accounts/{id}/unblock lifts the block.

At order time the platform refuses a prop order in only two cases:

  • It would break capital custody on a live prop account — prop_breach_would_occur (HTTP 409): venue credentials not provisioned yet, or a venue the firm holds no capital on.
  • It would take the account above a contract limit — above_max_open_contracts (HTTP 400). These are the max_position_size (one symbol) and max_total_lots_open (whole account) rules, set from the template’s maxPositionSize and maxTotalContracts. Working orders count as if they filled; reduce-only orders and orders that do not grow the count always pass. A rule whose action is warn never blocks.

PATCH /api/v1/firms/{id}/hard-enforcement is still accepted but no longer changes order handling.

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. No close on payout. Approving leaves the account passed — the funded trader keeps trading, and only leaves that state via breach, quit, or expiry. But the firm’s decision is recorded once per prop account: a second call answers 409 already_decided. Approving records an authorisation, not a transfer — the record carries onChainStatus: "queued_pending_contract_deploy" and no money moves on that call.
  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.