Skip to main content

Mesas workflow

A mesa is a per-currency trading desk. It tracks inventory of a local_currency (for example ARS) priced in a quote_currency (typically USD). Quote perspective (default for new desks): the operator thinks in the quoted currency. Compra = quote in = engine sell (today’s sell local). Venta = quote out = engine buy. The engine stays a local-currency book; only labels and the blotter remap. Manual feed (default for new desks): the blotter starts empty. Add rows with POST /api/mesas/{id}/lines or confirm “Sugerir desde operaciones”. Auto feed: same matching engine as Sugerir / line linking (exact → fx_close → net_quote → usd_leg). On each open-period read (report, blotter, candidates, suggestions) — and after operation approve/cancel/revert — the desk applies high-confidence matches (exact, fx_close) as fabricated blotter lines plus display-only MesaLineOperationLink rows. Low-confidence leftovers stay in Sugerir. Links never write Operation/Transaction. If a linked op is later adjusted, the next reconcile drops that blotter line only. Pending operations (include_pending, default off): existing desks stay completed-only. When on, Sugerir / auto-feed / infer / netting also count pending legs. Cancel (pending → adjusted) heals that blotter line off so position, blotter, and day P&L lose the exposure. Approve (pending → completed) keeps the same MesaLineOperationLink (no second line). A pending amount edit does not create a duplicate; the existing line stays. Work happens inside periods (one day): open with opening stock in quote + FX (for example 47500 USD @ 1540), load compras/ventas, then close. Close only cuts the day (WAC / spray / remanente freeze). pnlQuote default: pnlLocal / sellFx (WAC of the covered short). Not configurable yet. Authenticate with a company x-api-key. The company is taken from the key. Company-key calls skip role checks, and writes store created_by / opened_by / closed_by as null. A personal employee key (fsync_emp_…) uses the same paths via x-api-key or Authorization: Bearer. It is the employee: writes record that employee, reads require mesas.view (or mesas.manage), and creates, edits, and closes require mesas.manage. The dashboard uses the same /api/mesas paths with an employee session and those same permissions. This page is the integrator sequence. Prerequisite: a superadmin must set Company.mesas_enabled first. There is no public PATCH for that flag. If the module is off, every /api/mesas call returns 403 with code: MESAS_DISABLED.

1. Resolve internal accounts

account_ids must be internal accounts (Client.type = 0) that belong to the API-key company and are not already linked to another mesa. Currencies on the mesa must differ.
Pick at least one account id (for example a local caja and a quote caja).

2. Create a mesa

costing_method is fifo or wac. A 201 body is the mesa (id, currencies, account_ids, created_by: null, …). GET /api/mesas lists company mesas. GET /api/mesas/previews?date=YYYY-MM-DD returns blotter preview numbers (status, opening, volume, WAC, spray) for each mesa on that ART day. GET /api/mesas/{id} returns one. PATCH /api/mesas/{id} updates name, currencies, costing method, or account_ids. DELETE /api/mesas/{id} is a soft-delete (same as the dashboard) and frees the linked accounts.

3. Open a period

Trades only derive against an open period. At most one period per mesa can be open.
opening_quote is signed blotter inventory (USD leftover), not a physical cash count. Positive is typical ventanilla remanente; negative (for example -7016) is a net long quote carried into the next day. opening_local still works for local-perspective desks. When either is not zero, opening_fx is required (local units per 1 quote). Valor fijo / Toscas-style physical stock can stay >= 0 in the UI. opened_at defaults to 05:00 ART (America/Argentina/Buenos_Aires) of today — 08:00Z, not midnight. 00:00Z is 21:00 ART the previous calendar day and 00:00 ART (03:00Z) sits on the UTC/ART boundary.

4. Add blotter lines (manual desk)

quote_side is compra or venta. Supply quote_amount plus fx or local_amount. PATCH / DELETE /api/mesas/{id}/lines/{lineId} edit or remove a row. Lines do not write treasury or operations. GET /api/mesas/{id}/report includes blotter (compras | ventas, pile WAC, remaining, day P&L, spray). blotter.spray is { quote, local, buyWac, sellWac, buyQuote }: day-only averages of compras/ventas lines. local = (sellWac − buyWac) × buyQuote; quote = local / sellWac. If compras volume or ventas volume is 0, quote and local are 0. The dashboard blotter shows WAC + Spray + Remanente only. GET /api/mesas/{id}/periods returns { mesa, periods, activePeriod }. While the period is open, PATCH /api/mesas/{id}/periods/{periodId} changes opening stock used by the blotter (opening_quote + opening_fx on quote desks, opening_local on local desks). Live WAC / remanente / spray replay from the new opening plus current lines. Does not rewrite other periods or touch treasury. 409 PERIOD_CLOSED if you send opening fields on a closed day. On an already-closed period the same PATCH accepts closed_at only (late-close correction; does not rewrite the frozen blotter).

5. Let operations feed the desk (feed_mode=auto)

Create or approve operations as usual (POST /api/operations, POST /api/operations/{id}/approve). Auto desks use the same company-wide currency scorer as GET /api/mesas/{id}/day-entry-suggestions (not the MesaAccount allowlist). High-confidence ART-day matches become blotter lines progressively. Approving, cancelling, or reverting an operation triggers the same reconcile for open auto (and include_pending) desks of that company. Revert/adjust never mutates counterpart ops; the mesa only unlinks its own blotter contribution.

6. Review candidates and apply overrides

Include or exclude an operation (or fabricate a fully manual trade with side, local_amount, fx, and trade_date together):
action is include, exclude, or null (clear the override; requires operation_id or override_id). DELETE /api/mesas/{id}/overrides/{overrideId} removes a row. Auto desks prefer POST /day-entry-suggestions so P&L stays on fabricated blotter lines. Optional helpers:
  • GET / POST /api/mesas/{id}/infer?operation_id= suggests a trade from an operation’s legs.
  • GET /api/mesas/{id}/day-entry-suggestions lists unmatched ART-day operations as proposed Compra/Venta rows (same exact / fx_close / net_quote / usd_leg ranks as line linking). On feed_mode=auto the read first reconciles high-confidence matches (exact, fx_close) so this list is leftovers. Closed day: 409.
  • POST /api/mesas/{id}/day-entry-suggestions with { "operation_ids": [88] } infers each op, creates a blotter line (this does change P&L), then attaches display-only links. Does not post treasury movements.
  • POST /api/mesas/{id}/simulate with { "side": "sell", "localAmount": 500, "fx": 1250 } previews P&L against the open period.

7. Read the report (open day or closed snapshot)

Same query as the dashboard: page, pageSize, search, side, status, inclusion, sortKey, sortDir. Add period_id to open a closed day from its frozen snapshot (two-column blotter, WAC, leftover, P&L, lines). Omit period_id for the live open day (409 NO_OPEN_PERIOD when none is open). A closed period_id is not replayed; 409 SNAPSHOT_MISSING if closing_report is unusable. readOnly is true for closed days. Do not PATCH a closed snapshot — POST /api/mesas/{id}/periods/{periodId}/reopen first.
The body is { mesa, period, report, blotter, trades, pagination, readOnly }. Aggregates and blotter are for the full period; report.perTrade and trades are the current page. blotter is the two-column compras | ventas view plus spray. Each blotter row may include links[] (display-only operation references; they do not change P&L). Closing freezes spray on the blotter snapshot.

8. Close the period

Closing freezes inventory, average cost, P&L, and the full report JSON (report + trades + blotter) on the period. View that frozen day with GET /api/mesas/{id}/report?period_id= — the desk UI uses the same two-column blotter, read-only, until you reopen. The dashboard close form does not capture counts or arqueo. Optional legacy count_quote / count_local / close_fx are still accepted on the API for older clients; they are not shown on the desk close dialog. closed_by is null for API-key closes. Optional closed_at (ISO) defaults to 23:00 ART of the period’s opened ART day (02:00Z the next UTC calendar day) if omitted — not now. One open and one close per ART calendar day. It must be >= that period’s opened_at and must not overlap another period on the same mesa. Windows are [opened_at, closed_at): the next period may open at exactly this closed_at. The desk close dialog prefills 23:00 ART; override the time only for the rare late or early close (400 if opened_at would overlap). To move closed_at after a late close without rewriting the frozen blotter:
Same overlap checks as close. After the correction, POST /api/mesas/{id}/periods with opened_at equal to that closed_at succeeds. To correct opening stock or lines on a closed day, reopen it (do not PATCH the snapshot):
The day becomes open again: closing_report is cleared and is no longer the source of truth. Lines and opening are editable. Close again to write a new snapshot. 409 if this period is already open, or if another period is already open (one open day per mesa). GET /api/mesas/{id}/analysis aggregates closed periods. Pass period_ids=4,5 or from/to (YYYY-MM-DD, filter by opened_at). A range with no closed days returns an empty analysis (not 400). Optional include_open=true appends today’s live day (not cached). Each row includes blotter/spread/remaining/arqueo: closing_report.blotter when present, otherwise assembled from frozen trades + position. Range rollups: avgDailyPnlQuote, bestDay, worstDay, winDays, cumulativePnl. Use report?period_id= for the frozen two-column blotter of one day.
  • GET / POST /api/mesas
  • GET / PATCH / DELETE /api/mesas/{id}
  • GET / POST /api/mesas/{id}/periods
  • PATCH /api/mesas/{id}/periods/{periodId} — opening fields on an open day (409 if closed); closed_at late-close correction on a closed day
  • POST /api/mesas/{id}/periods/{periodId}/reopen — closed day becomes open; snapshot is no longer source of truth
  • POST /api/mesas/{id}/periods/{periodId}/close — optional closed_at (defaults to 23:00 ART of the opened desk day) and close_fx (default opening_fx)
  • GET /api/mesas/{id}/report
  • GET /api/mesas/{id}/candidates
  • POST /api/mesas/{id}/overrides
  • DELETE /api/mesas/{id}/overrides/{overrideId}
  • POST /api/mesas/{id}/lines
  • PATCH / DELETE /api/mesas/{id}/lines/{lineId}
  • GET /api/mesas/{id}/lines/{lineId}/link-suggestions
  • PUT /api/mesas/{id}/lines/{lineId}/links — display-only references; does not change P&L. The same operation may sit on several lines (the UI warns before confirming).
  • GET / POST /api/mesas/{id}/infer
  • POST /api/mesas/{id}/simulate
  • GET /api/mesas/{id}/analysis