GuidesTrading & accounts
Paper trading
Paper trading gives every account a simulated USD balance and a set of order routes that look like the live ones. Orders are filled by the paper engine against the real order books Predictefy already streams, so a strategy can be exercised end to end before any money is involved.
Nothing here touches a venue. No order is sent, no balance moves, and no venue account is required — the fills, positions, and PnL are Predictefy’s simulation of what those books would have given you.
Plan availability
Section titled “Plan availability”Paper trading is included on every plan, with these per-account limits (live since 2026-09-11):
| Plan | Open paper orders at once |
|---|---|
| Free | Up to 10 |
| Builder | Up to 50 |
| Pro | Up to 200 |
| Enterprise | No cap |
Only orders currently open or partially_filled count towards the cap. Fills,
cancels, and account resets are unlimited. Reaching the cap returns
409 PAPER_WORKING_ORDER_CAP, with a message naming the cap and your plan.
Cancel an open paper order or upgrade before placing another.
Credit metering is unchanged. See the plan table.
Same parameters as live
Section titled “Same parameters as live”A paper order carries the parameters you would use to trade for real: the venue, the market
and outcome, a side, a limit price on the market’s tick grid, a size, and a time in force.
Prices are probabilities in (0, 1]; the tick is the market’s published tickSize, or one
cent when it publishes none.
Every route needs an API key with the trade scope, which new keys do not carry by
default. Select “Allow this key to place trades” when creating a key in the console.
| Route | What it does |
|---|---|
GET /v1/paper/account | The simulated cash account, created on first touch |
POST /v1/paper/account/reset | Restart the simulation, keeping fills as history |
POST /v1/paper/orders | Place an order (Idempotency-Key required) |
GET /v1/paper/orders | Your orders, newest first, cursor-paged |
GET /v1/paper/orders/{id} | One order together with its fills |
POST /v1/paper/orders/{id}/cancel | Cancel a working order and release its hold |
GET /v1/paper/fills | Every simulated fill, cursor-paged |
GET /v1/paper/positions | Per-outcome size, cost, and a catalog mark |
GET /v1/paper/portfolio | The account, its positions, and their totals |
Place an order
Section titled “Place an order”Idempotency-Key is required on POST /v1/paper/orders; without it the request is
refused with 400 IDEMPOTENCY_KEY_REQUIRED and nothing is stored. Reuse the same key
when you retry: a first-seen key returns 201 with the new order, and a repeat returns
200 with the stored one plus the header Idempotency-Replay: true, whatever body the
retry carried.
curl -s -X POST "$PREDICTEFY_API_URL/v1/paper/orders" \ -H "Authorization: Bearer pk_live_YOUR_KEY" \ -H "Idempotency-Key: 9d2f0f1c-order-1" \ -H "content-type: application/json" \ -d '{ "venue": "kalshi", "marketId": "PRES-2028-DEM", "outcomeId": "kalshi:PRES-2028-DEM:yes", "side": "buy", "price": 0.62, "size": 100, "tif": "GTC" }'import { Predictefy } from '@predictefy/sdk';
const client = new Predictefy({ apiKey: process.env.PREDICTEFY_API_KEY });
const order = await client.placePaperOrder({ venue: 'kalshi', marketId: 'PRES-2028-DEM', outcomeId: 'kalshi:PRES-2028-DEM:yes', side: 'buy', price: 0.62, size: 100, tif: 'GTC', idempotencyKey: '9d2f0f1c-order-1', // reuse this exact key on a retry});
const working = await client.listPaperOrders({ status: 'open' });const portfolio = await client.paperPortfolio();from predictefy import Predictefy
client = Predictefy(api_key="pk_live_YOUR_KEY")
order = client.place_paper_order( "kalshi", "PRES-2028-DEM", "kalshi:PRES-2028-DEM:yes", "buy", 0.62, 100, tif="GTC", idempotency_key="9d2f0f1c-order-1", # reuse this exact key on a retry)
working = client.list_paper_orders(status="open")portfolio = client.paper_portfolio()A buy reserves price × size plus the modelled fee out of your cash and is refused with
409 PAPER_INSUFFICIENT_CASH when free cash cannot cover it. A sell may never exceed the
size you hold minus what your working sells already offer — there is no shorting, and a
sell past that line is refused with 409 PAPER_POSITION_TOO_SMALL. reserved is a hold
inside cash, so your free cash is cash - reserved.
Cancelling releases the unspent part of the hold, recomputed from the order’s own fills, so
a partially filled cancel never hands back cash the engine already spent. Only open and
partially_filled orders can be cancelled; anything terminal returns
409 PAPER_ORDER_NOT_OPEN.
How fills are decided
Section titled “How fills are decided”Fills come from Fill Model v1, one pure rule set shared by paper trading and the backtester. It is deliberately pessimistic: wherever the honest answer is unknown, it declines to fill rather than inventing an edge you could not have had.
- Marketable orders walk the displayed depth. A buy at or above the best ask, or a sell at or below the best bid, walks the ladder from the touch and pays each level its own price, stopping at your limit. A top-of-book-only book is one level, so the walk caps at the displayed touch size.
- Resting orders fill only when the market trades through them. Once an order has rested, the opposite quote must cross strictly through your limit, not merely touch it — a touch would require knowing your queue position. The fill takes only the crossing level and prints at your own limit price.
- At most a quarter of a displayed lot per book update. One order may take no more than 25% of the size shown at a price level on any single book update, and orders evaluated against the same book share what is left of that level’s lot.
- Exact venue fees where they are verified. Fees are charged per fill, at that fill’s
own price and size, from the verified per-venue taker schedules. A venue with no proven
fee model gets a fee of
0labelledunverifiedon the fill — a labelled zero, never a silent one. A venue’s published per-order minimum or flat charge (Opinion’s $0.25 minimum, Myriad’s $0.0085 per transaction) is held with a buy order and paid over its fills. There are no maker rebates in v1; the fill records which side supplied liquidity. - No fills on a stale book. A book older than 30 seconds is not evidence about the present, so nothing fills against it and the order simply waits. This is the one case where time in force does not apply: even an IOC waits for a fresher book rather than giving up a chance it never actually got.
Time in force then decides the remainder: GTC rests, IOC gives up what is left, and
FOK is all or nothing — a short book leaves the order rejected with no fills and no
consumption, so the lot stays whole for the next order.
What the model does not simulate
Section titled “What the model does not simulate”Every item below is a real cost or advantage of live trading that Fill Model v1 does not represent. Read simulated PnL with them in mind.
- Queue position. Your place in a venue’s order queue is not modelled, which is why a resting order needs a strict cross rather than a touch.
- Market impact. Your simulated order never moves the book it trades against.
- Latency slippage. The delay between deciding and arriving at the venue is not modelled; there is no re-quote and no missed touch.
- Price improvement. A resting order earns its own limit price, never the better price of the quote that crossed it.
- Hidden liquidity. Only displayed size is available. Iceberg and other undisplayed size never fills you.
- Continuity across an engine restart. A working order’s evaluation history does not survive a restart of the simulation engine, so it is re-evaluated from scratch against the next book it sees.
Positions, portfolio, and settlement
Section titled “Positions, portfolio, and settlement”GET /v1/paper/positions returns one row per venue and outcome you have traded. A
flattened position stays at size 0 so its realized PnL survives as history.
Marks come from Predictefy’s catalog snapshot, reported as markSource: "catalog". A
position that cannot be marked reports mark: null, markSource: null, and
unrealizedPnl: null rather than a fabricated number, and one unmarkable open position
makes totals.equity and totals.unrealizedPnl null too. totals.realizedPnl is always a
number and survives a reset.
Settlement coverage is partial and says so:
- Gemini and Predict.fun resolutions are read from the venue’s published outcome, and positions in a resolved market settle automatically.
- Every other venue leaves the position at
settlement: "pending", untouched. An unparseable resolution payload does the same. Predictefy does not guess a winner it cannot read.
POST /v1/paper/account/reset restarts the simulation: working orders are cancelled, their
holds released, position sizes flattened, and cash returned to initialCash — or to a new
initialCash you supply, which becomes the new baseline. Fills, ledger rows, and realized
PnL are kept as history. A reset restarts the simulation; it does not erase what
happened.
Credits
Section titled “Credits”Paper trading is metered at 0 credits. Calls are recorded as usage, but simulated orders, fills, positions, and portfolio reads cost nothing — there is no venue behind them. See Pricing, credits & billing for everything that is metered.
Errors
Section titled “Errors”| Status | Code | When |
|---|---|---|
| 400 | IDEMPOTENCY_KEY_REQUIRED | POST /v1/paper/orders arrived without an Idempotency-Key |
| 400 | VALIDATION_ERROR | Bad shape or range, an off-tick price, an unknown status, a stale cursor |
| 403 | PLAN_REQUIRED | The account’s plan does not include paper trading |
| 404 | MARKET_NOT_FOUND | Unknown market, or an outcome the market does not carry |
| 404 | PAPER_ORDER_NOT_FOUND | Unknown order id, or one that is not yours |
| 409 | PAPER_MARKET_CLOSED | The market is not open, or already resolved |
| 409 | PAPER_INSUFFICIENT_CASH | Free cash cannot cover the buy’s reservation |
| 409 | PAPER_POSITION_TOO_SMALL | The sell exceeds the size you hold less your working sells |
| 409 | PAPER_WORKING_ORDER_CAP | Your plan’s open paper orders are all in use |
| 409 | PAPER_NO_LIVE_BOOK | No live order book is captured for that venue and outcome |
PAPER_NO_LIVE_BOOK means Predictefy holds no book for that outcome, or none with a fresh
frame, so the order is refused at intake rather than accepted against stale prices it could
never honestly have filled against. The message names the venue, the outcome, and which of
the two it was. Retry once that market’s book is live again, or place the order on a more
liquid market. This is the intake check; the 30-second staleness rule above is the separate
one the engine applies to orders that are already resting.
PAPER_WORKING_ORDER_CAP names both the cap and your plan — cancel an open paper order or
upgrade. PLAN_REQUIRED is only reachable if a plan ever loses paper access; every plan
carries it today.
Unknown, malformed, and other accounts’ order ids all return the same
PAPER_ORDER_NOT_FOUND, so the route is never an oracle for another account’s orders.