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).
Lifecycle
Section titled “Lifecycle”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. |
Where rules are enforced
Section titled “Where rules are enforced”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(HTTP409): 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(HTTP400). These are themax_position_size(one symbol) andmax_total_lots_open(whole account) rules, set from the template’smaxPositionSizeandmaxTotalContracts. Working orders count as if they filled; reduce-only orders and orders that do not grow the count always pass. A rule whoseactioniswarnnever blocks.
PATCH /api/v1/firms/{id}/hard-enforcement is still accepted but no longer
changes order handling.
Rule catalog
Section titled “Rule catalog”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.
The funded-account payout model
Section titled “The funded-account payout model”A passed account is a funded account that keeps trading. Four properties define the mechanic:
- 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.
- Two independent caps. Every payout is gated by both the profit entitlement and treasury solvency.
- 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 carriesonChainStatus: "queued_pending_contract_deploy"and no money moves on that call. - 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.
Blocking vs suspending
Section titled “Blocking vs suspending”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.
Diagnosing an evaluation
Section titled “Diagnosing an evaluation”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.
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.
Reading account state
Section titled “Reading account state”| 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.