⚠ INTERNAL — Operator UX mock · Design preview of V2 development path · demo-wired ≠ production-live · back to dev guide
Strategies/Election Momentum
Polygon · pUSD 10
📓 Developer Guide 📋 Reason Codes 📈 Shadow Mode
JD John Doe · Admin
OperatorEvery set of rules you're running: which rung of the ladder each is on, which bots it uses, and whether it's switched on.When: When you need to pause, resume, or promote a strategy.
Details
What this page is forMonitor and control all live strategies — their state ladder position, wired bots, and current signal mode.
When to use itWhen you need to pause, resume, or promote a strategy.

Election Momentum

Running on demo money · started 06:00 · 4 markets · built from the Maker-Tight Election template
+87pUSD today
595pUSD at risk
19fills today
what this is doing, right now On 3 of your 4 election markets you are offering to buy 1¢ below and sell 1¢ above the going price, 200 pUSD at a time. You are holding 595 pUSD in total and you will not go past 500 on any single market or 5,000 across all four. You are up 87 pUSD today, and you would stop for the day if you were down 400. The fourth market is stood down because its spread is too wide to quote safely.
for developers

This paragraph is generated from the same decision manifest the builder renders — it is not written by hand and it is not stored. Each highlighted value is bound to a parameter path, so it reflects live state and links back to the control that owns it.

If you add a parameter to a manifest and forget its say_template, the build fails. That is deliberate: a parameter a user can set but that we cannot describe in one clause is a parameter we should not have exposed.

Market by market

What it is offering, what it is holding, what it made. A market that is not trading says so on its own card, with the reason — not in a log somewhere else.

Democratic nominee wins Pennsylvaniaquoting
You are offering41¢ → 43¢
You are holding320 pUSD
Today+64 pUSD
Fills today9
Republican nominee wins Pennsylvaniaquoting
You are offering57¢ → 59¢
You are holding180 pUSD
Today+41 pUSD
Fills today7
Turnout exceeds 62%quoting
You are offering33¢ → 36¢
You are holding95 pUSD
Today−18 pUSD
Fills today3
Recount in any statestood down
You are offering— → —
You are holding0 pUSD
Today0 pUSD
Fills today0

Not quoting. The book is 12¢/17¢ — 5¢ wide. Your quality guard pulls quotes above 3¢. It has been like this since 06:00. RISK_SPREAD_WIDE

for developers

Each card reads one market_state envelope. “Stood down” is not a separate flag — it is derived from the most recent REJECT reason code still inside its cooldown window. One source of truth, so the card, the attention queue and the audit log physically cannot disagree about why a market is quiet.

Cards render optimistically from the last envelope and grey out after 2 × expected_interval with no update, rather than showing stale numbers as though they were current. A number with no timestamp is a bug report waiting to happen.

Why it did what it did

The last six decisions, as sentences. Reason codes are still here, but as the small print rather than the headline. Expect this to be the most-read part of the page after the header.

14:41

Somebody sold you 200 pUSD of Democratic nominee wins Pennsylvania at 41¢. That is exactly the price you were offering.

filled · STRAT_MAKER_QUOTE_INSIDE · see the full trace →
14:38

You wanted to buy another 200 pUSD of Democratic nominee wins Pennsylvania, but you already hold 320 and your own limit is 500. Buying was refused. You are still offering to sell.

refused by your own limit · RISK_EXPOSURE_CAP_HIT · see the full trace →
14:31

You sold 200 pUSD of Republican nominee wins Pennsylvania at 59¢, having bought it at 57¢ eleven minutes earlier. That is 4 pUSD of spread earned.

filled on V2 · EXEC_ROUTE_CTFV2_ACCEPTED · see the full trace →
14:22

The book on Recount in any state went 5¢ wide, so you stopped quoting it. You will start again automatically when it tightens below 3¢.

stood down · RISK_SPREAD_WIDE · see the full trace →
14:09

Somebody bought 200 pUSD of Turnout exceeds 62% from you at 36¢.

filled · STRAT_MAKER_QUOTE_INSIDE · see the full trace →
13:58

The price feed for Turnout exceeds 62% went quiet for 17 seconds, so you pulled your quotes rather than trade on a price that had stopped moving. Back 4 seconds later.

paused 21s · RISK_BOOK_STALE_15S · see the full trace →
Everything it did today → How a decision is explained →
for developers

Every row is one envelope. The sentence comes from a per-reason-code narrative template with the envelope’s own fields interpolated. There is no model in this path and there must not be — this is the record a user will quote back at us in a dispute, so it has to be reproducible from the envelope alone.

Colour lives in the dot and the dot is decorative: the state is also stated in words (“filled”, “refused by your own limit”). Never encode meaning in colour alone.

Note the split the copy makes between refused by your own limit and blocked by the platform. Users forgive the first and escalate the second, so the phrasing is chosen by reason-code origin, not by severity: guards the user configured say “your”, always-on guards say “the platform”.

What is stopping it, and how much room is left

Six guards are attached: four you chose, two that are always on. Each shows its current headroom rather than its configured threshold, because a threshold on its own tells you nothing about whether you are near it.

risk.exposureguard

The most you can hold at once

320 of 500 pUSD on one market64%

Fired 3 times today, all on the Pennsylvania Democratic market between 14:00 and 14:41.

risk.marketqualityguard

Markets too thin to quote safely

1 of your 4 markets is stood down25%

Holding Recount out since 06:04.

risk.stalebookguard

Prices that stopped updating

Fires after 15s with no tick

Fired once today, for 21 seconds at 13:58.

risk.drawdownguard

When you stop for the day

Up 87 pUSD. Stop sits at −400.

Quiet today.

risk.killswitch

Stops everything, instantly

Armed, not tripped

Quiet today.

risk.compliancegate

Jurisdiction and builderCode

Both valid. 0 blocks today.

Quiet today.

for developers

A guard exposes headroom() returning a 0–1 float plus a plain current-state string. Guards with no meaningful headroom — kill switch, compliance gate — return None and render without a bar, rather than a bar sitting at 0%. An empty bar reads as “safe” when it actually means “not applicable”.

Platform invariant. Every guard here can refuse an opening order and none of them can refuse a closing one. allow_reduce is not a per-guard setting a user can switch off. If you are writing a new guard and it needs to block a reduce, it is not a guard — it is a halt, and it belongs behind the kill switch with an operator in the loop.

Change something

The same six controls from the builder, in place, on a running strategy. No raw parameter names. Changes take effect at the next requote, which is at most ten seconds away.

the engine · decides what to offer

How tight do you want to quote?

This is the only decision that makes you money. Quote tight and you trade a lot for a thin margin. Quote wide and you trade rarely for a fat one. There is no right answer, so we show the consequence instead of the number.

Pick a starting point

Then drag the handles if you want something in between. Most users never drag.

Or set it by hand

The market is currently trading at 42¢. Drag either handle.

mid 42¢
buy 41¢
sell 43¢
38¢40¢42¢44¢46¢

You will offer to buy at 41¢ and sell at 43¢, in 200 pUSD clips, and you will not change your mind more than once every 10 seconds.

How much per clip?

The size of each individual offer. Not your total exposure — the guard on the next screen handles that.

200pUSD
501,000

At 200 pUSD a clip and a 500 pUSD cap per market, you can be filled 2 and a half times on one market before you stop quoting that side.

Raise this above 400 and a single fill uses most of your per-market room. The engine will spend more of the day flat than quoting.

What this screen writes into the bundle
engine strat.maker_tight {
  half_spread_bps = 10      // from the handles: 1c on a 42c mid
  quote_size_pusd = 200     // from the slider
  cooldown_s      = 10      // preset-derived, not user-facing
  requote_on      = [mid, depth]  // fixed by the template
}
a guard · decides what to refuse

How much are you willing to have riding at once?

A guard has no opinion about the market. It only ever says no. So its screen is not a form — it is a sentence the user completes, with a picture of what that sentence means against the money they actually have.

Complete the sentence

This is the whole configuration for this bot. Two numbers.

Never let me hold more than pUSD on ,
or more than pUSD across .
Your wallet: 12,400 pUSDThis strategy could use up to 5,000 (40%)
your ceiling

Once you hold 500 pUSD of one market, new buy orders on that market are rejected with RISK_EXPOSURE_CAP_HIT. Sells still go through — a guard never traps you in a position.

What should happen when you hit the ceiling?

Keep quoting the side that gets me out

You stop adding, but you keep offering to sell. This is how the position unwinds on its own.

If off: you go completely silent on that market and wait for it to resolve.

Put it in my attention queue

First time it happens each day. Not every rejection — that would be hundreds.

If off: it is still in the log, you just will not be told.

Pause the whole strategy, not just that market

Blunt. Use it if hitting a cap at all means your assumptions were wrong.

Off is the default. Most people want the other three markets to keep working.

What this screen writes into the bundle
guard risk.exposureguard {
  max_pos_market  = 500
  max_pos_cluster = 5000
  scope           = cluster:"US_ELECT_2028_A"
  on_breach       = { allow_reduce: true, notify: once_daily, halt: false }
}
execution · decides how to get it done

When you do trade, what matters more?

Routing has about fourteen tunable parameters. Thirteen of them are trade-offs along one axis: speed versus price. So we expose the axis, not the parameters. An advanced user can open the drawer and set all fourteen.

Pick one

On last week’s data, Balanced on these four markets would have filled 131 of 140 intents, and the nine misses were all in the last hour before close.

Where are you allowed to trade?

Polymarket CLOB

The main order book. Deepest, and where the template expects to work.

CTFExchangeV2

The V2 contract. Occasionally a better price. Adds about 40ms.

If off: you give up roughly 0.2¢ a fill on this cluster.

All fourteen routing parameters (advanced)

Opening this drawer switches the strategy to manual routing and marks it for review before it can leave shadow. The three cards above are the reviewed paths.

route exec.smartrouter {
  objective         = balanced        // set by the card
  max_latency_ms    = 200
  rest_first_ms     = 200
  cross_after_ms    = 200
  venues            = [clob, ctfv2]
  max_child_orders  = 3
  min_child_pusd    = 50
  cancel_on_reject  = true
  // + 6 more, all derived from `objective`
}
a guard · decides when to stop everything

When should this stop for the day?

One number. The screen’s job is to make the number feel like money rather than a percentage, and to say out loud what happens at the moment it trips.

Stop for the day if this strategy is down

8%
1% — twitchy25% — barely a stop

On a 5,000 pUSD ceiling that is 400 pUSD. In the last 90 days this strategy would have stopped 3 times, and on 2 of those days it would have finished green if left alone.

Below 4% you will stop on ordinary noise. The election cluster moves 3–5% intraday without anything happening.

When it trips

Cancel everything resting

Pull your quotes immediately. Takes about 300ms.

Also sell out of what I am holding

Off by default. Selling into the move that just hurt you usually makes it worse.

If off: you keep the position and it resolves on its own.

Start again automatically tomorrow

At 00:00 UTC. If off, it waits for you to press resume.

What this screen writes into the bundle
guard risk.drawdownguard {
  daily_stop_pct = 8
  scope          = strategy
  on_trip        = { cancel_resting: true, flatten: false, auto_resume: "00:00Z" }
}

Before you save, this is what changes — in English

A diff nobody has to parse. If the sentence does not change, the behaviour does not either, and the button stays disabled.

You will offer to buy at 41¢ and sell at 43¢, instead of 40¢ and 44¢. Expect roughly 3× as many fills and about half the margin on each one.

Save — live within 10s Try it on last week first Discard
for developers

Editing a running strategy writes a new immutable bundle version and swaps the pointer; the engine picks it up on its next requote tick. No in-place mutation, no partial application — either the whole new bundle is live or the previous one still is.

The English diff is produced by rendering both bundles through the same say_template set and diffing the sentences, not the JSON. Two bundles that produce identical sentences are a no-op and the save button disables itself. That single rule removes the “I changed something and nothing happened” ticket class entirely.

Loosening a guard — raising a cap, widening a stop — requires re-confirmation. Tightening one does not. Asymmetric friction, on purpose.

Where this sits on the ladder

It is running, but on fake money. Real money is three rungs away and none of them can be skipped.

Rung 2 of 5 — running on fake money
Written down, not running

Spec frozen, schema validated, reason codes registered.

Running on fake money

You are here. 19 fills today against a simulated book.

Watching real markets, placing nothing

48 hours of shadow. Not started.

Placing real orders from an empty wallet

Proves the plumbing end to end without risking anything.

Live with real money

Needs an admin signature on the shadow report.

Start the 48h shadow run → The whole ladder →
for developers

Rung is a property of the bundle, not of the UI, and it is computed as the minimum rung across every bot the bundle references. A strategy can never be more live than its least promoted component, which is why there is no way to hand-set this field.

The two unchecked rows are rendered from the same gate definitions the pipeline page uses, so a gate cannot be described one way here and another way there.

The raw bundle, for anyone who wants it

Read-only here. Editing happens through the controls above, because that is the only path that produces a reviewable English diff. Download it, diff it, attach it to a ticket — but the builder stays the writer.

// election-momentum.bundle  ·  v7  ·  live on demo  ·  since 06:00Z
bind    cluster "US_ELECT_2028_A"
markets [pa-dem, pa-rep, turnout-62, recount-any]
engine  strat.maker_tight       { half_spread_bps = 10, quote_size_pusd = 200, cooldown_s = 10 }
guard   risk.exposureguard      { max_pos_market = 500, max_pos_cluster = 5000, allow_reduce = true }
guard   risk.drawdownguard      { daily_stop_pct = 8, flatten = false }
guard   risk.marketqualityguard { max_spread_c = 3 }
guard   risk.stalebookguard     { max_age_s = 15 }
route   exec.smartrouter        { objective = balanced, venues = [clob, ctfv2] }
always  risk.killswitch, risk.compliancegate, risk.markethaltguard
rung    demo-wired   // = min(rung) across all referenced bots
← All strategies How a strategy like this gets built → Control vocabulary →
Polytraders Operator UX Mock · INTERNAL · demo-wired ≠ production-live · how this works · words we use · the plan · reason codes · shadow-mode pipeline
⚙ Page Configuration
Current page settings
Loading configuration…