Skip to content

mendurim/strikegrok

v3.1.2MIT

Turn your Grok Bot into a seven-agent Strike Finance trading desk: research, risk, execution and review, with market data from Strike's public Price Service and execution through Strike's signed API. Seven specialist roles with full system prompts and twenty-two skills, a universe scan that brings opportunities to you, a signed standing-approval layer enforced in code, and an autopilot that runs the scan and the rule monitors on a clock without a model in the send path. Includes a zero-key Opening Bell and desk doctor.

Changelog

All notable changes to StrikeGrok are recorded here. Versions follow the release tags the bootstrap skill pins.

v3.1.2

The scan's sampling cadence stops depending on the cron line.

autopilot.py scan appended a funding and a depth sample on every run. Run hourly that is correct. Run every five minutes - which anyone might, to catch an opportunity sooner - it wrote twelve samples an hour, and a rule's funding_pct30d then ranked against two and a half days of history under a thirty-day name. The depth check's seven-day median had the same problem. Neither said anything about it; the sample count was met, so the statistic simply described a shorter window than it claimed.

Desk.append_history now takes an optional min_interval and keeps at most one sample per slot, and the funding and depth call sites pass the hourly one. Open interest deliberately does not: that check looks for a row twenty-two to twenty-six hours old rather than counting samples, so a finer series only sharpens it. Six tests pin the behaviour, including thirty-six five-minute runs across three hours leaving three samples.

This is the defect v3.1.1 reported and did not fix.

v3.1.1

Two things a first real run of v3.1.0 turned up.

  • monitor exited 2 in silence. When every market came back could_not_tell, the run returned the blind code and printed nothing to either stream - the reason reached only watch/rule-<name>/log on the desk computer. From cron that is a bare exit 2 in the mail with nothing to act on. It now prints a one-line tally and a line per blind or held market to stderr, and still writes the log. Nothing about the decision changed; it says what it decided.
  • scripts/funding_collect.py (new). A rule ranking funding_pct30d needs 240 hourly samples before it can fire, and the only way to get them is to observe them. The collector reads /v2/premiumIndex, appends to the same data/funding/<symbol>.csv the scan writes, keeps one sample per funding interval so a tighter cron cannot inflate the count, and --status reports progress against the rule's own threshold and when it becomes measurable. 19 tests.

There is no backfill, and there should not be one. Strike publishes no historical funding: /v2/klines carries last, mark and index and no premium series, no fundingRate history endpoint exists, and /v2/history/funding is one account's own charges. Reconstructing it from the premium was measured and rejected - the published rate does not follow from the instantaneous premium, and the averagePremiumIndex behind it is accumulated inside the interval and never exposed historically. Binance's formula applied to Strike's own numbers misses the published rate by orders of magnitude. A reconstructed series would corrupt the percentile a rule ranks against, invisibly, which is the failure desk-strategy-lab exists to prevent.

One thing this found and did not fix: autopilot.py scan does not deduplicate by funding interval. Run hourly that is correct; run every five minutes it writes twelve samples an hour, and a "30-day" window becomes two and a half days without saying so. The collector guards against this for the series it owns, and desk-autopilot now says to keep the scan hourly.

v3.1.0

The desk's two autonomy runbooks are now executable. desk-signal-scan and desk-standing-approvals described an hourly scan, live rule monitors, four clocks and a Tier 1 send at 03:00 with nobody awake - and left all of it to a Bot to perform. A Bot deciding at 03:00 whether a rule fired is a Bot writing its own evidence. scripts/autopilot.py performs the same runbooks deterministically: it evaluates a frozen rule on closed bars, checks the four clocks, sizes on a stressed stop, writes the proposal and its RISK | … | PASS block, and asks the signer for exactly one bracketed order.

  • scripts/autopilot.py (new, standard library only). Three subcommands: scan appends the funding, open-interest and depth history the monitors rank against and writes the universe scan to signals/; monitor --rule R.json evaluates one frozen rule and may send; sweep expires stale fires and cancels dead entries under Tier 0. --dry rehearses everything and stops before the send, which is the closest thing to a dry run an API without one can offer.
  • It never holds the API wallet. Every authenticated call, reads included, is handed to strike_request.py through sudo -u strike-signer, so desk_policy.py gates it exactly as it gates a person. The test suite asserts autopilot never so much as creates the policy state directory.
  • desk-autopilot (new skill, 22 now ship) and SETUP.md section 11 document the two-OS-user split that makes this a boundary rather than a claim, and say plainly that a shared Grok workspace is not that computer.
  • template/ gains a worked example rule, a register example, the signer's five-minute equity snapshot script, and the cron and sudoers files. The snapshot script is what opens the trading day: its GET /v2/account is what captures the start-of-day equity the daily-loss stop measures against, and autopilot refuses to size on a snapshot older than ten minutes, so a stopped cron stops the desk rather than sizing it on yesterday's account.
  • A preflight that predicts every refusal. Open incidents, suspensions, the rule hash, the notional and risk caps, pacing, the book ceiling, the approval's occupied markets and the venue's own filters are all checked before the send. A refusal that gets through anyway files an incident and is never retried: it means this script is wrong about the gate, not that the market is wrong about the trade.
  • 170 tests in scripts/test_autopilot.py. The arithmetic is pinned to a worked example that can be checked with a pencil; the whole fire runs against the real desk_policy.Policy with a genuine Ed25519 key and a genuinely signed register, so the PASS block and the gate that reads it cannot drift apart without a test failing.

Five things the build found, most of them only because the code was run against the live Price Service and the real policy layer rather than against its own fixtures:

  • A market order is bounded by MARKET_LOT_SIZE, not LOT_SIZE. Strike publishes both, and the market filter is the smaller one - 2000 against 10000 on ETH-USD. Sizing against the wrong filter is a rejection the desk could have read out of exchangeInfo first. Autopilot now prefers the market filter where it exists and checks minQty, maxQty and the price filter before sending.
  • The protective leg is market-on-trigger. exchangeInfo lists STOP, STOP_MARKET, TAKE_PROFIT and TAKE_PROFIT_MARKET and no STOP_LIMIT, which is the Binance scheme this API follows: the plain forms rest at a price after triggering and can fail to fill on the move that triggered them. A stop that might not fill is not protection, so the default is stop_market, the rule file may choose otherwise, and whichever is chosen is checked against the market's own orderType list before anything is sent. strike-orders reads the plain forms the other way round; confirm on testnet during the forward test, because the venue is the authority and neither the skill nor this changelog is.
  • A held fire was never tried again. The first live rehearsal held on a notional cap, and the bar was then marked decided - so a hold that cleared inside the same bar, a book that thinned and recovered, a blackout window that closed, would never have been taken. Sent, rehearsed and refused bars stay decided; a held one is retried until the fire goes stale.
  • autopilot.py monitor --rule R --no-signer failed because the flag came after the verb. A cron line is written once and read at 03:00; it now accepts the options on either side of the subcommand.
  • A deleted proposal handed its ticket id back. The proposals directory is the ticket ledger, so removing a file freed its number - and the venue and the policy layer both still remembered the client order id, so the next fire would have met a replay refusal and an incident. The ids this script has issued are now kept in its own state as well, and the two are unioned.

Two things this release does not claim. No order has been sent to Strike by this code - the send path is exercised against a stub that runs the real policy layer, and the venue's own acceptance of the bracket body is what the testnet forward test is for. And the example rule is a format, not a strategy: it has no backtest behind it here, and desk-autopilot sets out the order - freeze, backtest, rehearse, twenty forward-tested trades at Tier 2 - in which a rule earns a standing approval.

v3.0.2

The documentation caught up with the code. Two releases added a policy layer, three tiers and a signing key, and README.md changed by two lines - the version pin - while never mentioning any of it. A reader of the front page still met the desk as it was before v3.

  • README.md now describes the policy layer: the three tiers, what ships enabled, and the one file that turns "any Bot's PASS block opens a position" into "no open without a signature". Five stale references to executing through the crowdtime MCP are gone; the desk has signed its own requests since v1.1.
  • SETUP.md step 7 was one paragraph about a chat rule. It is now the runbook it should always have been: the platform rule and the limit of what it can gate, generating the key pair on the user's own machine, copying only the public half, what that buys in a before-and-after table, and how to sign a ticket. Written by running it and correcting what did not work.

Two defects that runbook found, which would otherwise have shipped:

  • The documented desk_policy.py init no longer exists - the state directory has been created on first use since v3.0.0. The step is gone. Nothing was broken, but a user following the runbook would have hit an error on their first command.
  • desk_policy.py verify reported a Tier-2-only desk as REFUSE signature: standing-approvals.json is missing, exit 2. A desk that approves each trade with a signed token has no standing-approval register, and on a shared workspace that is the recommended shape rather than a failure. It now says so and exits 0.

v3.0.1

Six defects found by review of the v3.0.0 policy layer, each reproduced against the code before it was changed.

  • Four ceilings were reported but not enforced. effective_ceilings() applied the environment override for the standing-approval lifetime, per-SA risk, per-SA open count and consecutive-loss kill, while the check sites read the raw constants. STRIKEGROK_SA_MAX_LIFETIME_DAYS=14 printed 14 and still allowed a 31-day approval. Every ceiling now goes through the same function, and there is a test per ceiling asserting the printed value is the one that refuses. A reported bound nothing enforces is worse than an unreported one.
  • A bracket's stop leg was not required to be reduce-only. strike-positions already warns that a resting stop can open a position when it triggers; a bracket leg is no different. A stop without reduce_only (or close_position) is now refused.
  • A daily bar allowed a fire from this morning to be sent tonight. Signal age is now bounded by one bar and by an absolute ceiling of one hour, whichever is tighter. A rule that enters at the next open is inside it; a daily-bar rule keeps working.
  • A reservation the venue never confirmed held its market for a whole bar. Held for five minutes now, which is long enough for an order to appear at the venue, after which live positions and resting orders decide. With no venue reader the reservation is held indefinitely and the book ceiling refuses first.
  • Sizes and prices were compared as floats with a 1e-12 tolerance. Ticks and steps are decimal, so they are compared as exact decimals: 0.430 still equals 0.43, and a size differing in the fifteenth place is now a different order rather than the same one.
  • The signed User-Agent still read strikegrok-desk/1.0. It tracks the release, pinned to plugin.json by a test.

Two things the review was right about that are not code defects, and are now said plainly by the tooling rather than only in a document:

  • Attended mode is not a weaker Tier 1; it is no approval control in this code at all. With no public key installed, a RISK | ... | PASS block is markdown any Bot can write, and that is all the layer requires before signing an opening order. The control is the platform's Require Approval rule, which lives in chat - and a Bot running scripts/strike_request.py directly with the wallet in its environment never passes through chat. Every attended-mode allow is now logged WARN, says so in its own record, and verify states it. Installing this release does not make an unsigned open impossible; installing a public key and approving with signed tokens does.
  • STRIKEGROK_STATE_TRUSTED=1 asserts something that is false on a shared workspace. On Grok Bot the Bots and the signer are the same OS user. verify now warns when the flag is set, and says to unset it and stay at Tier 2 rather than set it to make standing approvals work.

Also: each skill's metadata.version is its own and not the release tag, which skills/README.md now states, and the bootstrap skill - which embeds the clone pin - carries the release version so the two cannot be read as disagreeing.

v3.0.0

The desk can now run unattended, and the control that lets it is enforced in code rather than in a prompt.

Every earlier version approved each trade by hand. That is the right control for a discretionary idea and the wrong one for a frozen rule that fires at 03:00 UTC. This release moves the approval from the trade to the rule, and moves the gate from the conversation into scripts/strike_request.py, which is the one thing that holds the API wallet.

  • scripts/desk_policy.py runs on every non-GET before it is signed. Three tiers: reduce-only and cancels are free, opening exposure needs either a standing approval in a register the user signed with a key that never touches the desk computer, or a signed per-trade token. It refuses anything else and names the step that failed.
  • scripts/test_desk_policy.py builds a whole desk per test - a real key pair, a genuinely signed register, a state directory - and asserts each refusal. One class runs the PASS template out of agents/risk-manager.md through the policy, so the prompt and the code cannot drift apart without the build failing. Another forks six processes at one free slot.
  • Two new skills: desk-signal-scan, how the desk finds and times opportunities without being asked, and desk-standing-approvals, the approval model and what the layer checks.
  • All seven role prompts rewritten around the scan, the signals file and the standing-approval register.

What the layer will not let through, each because it was demonstrated first:

  • an opening order with no stop, a stop on the wrong side of the entry, or a stop that loses more than the ticket's stated risk
  • a ticket whose bytes differ from the Risk PASS block that approved it, or whose fields come from a rejected amendment elsewhere in the file
  • a client order id the desk or the venue has already seen, which is what makes a resend after a timeout impossible rather than merely discouraged
  • a second order under a standing approval that already has exposure or a working order on that market, including when two Bots ask at the same instant
  • opening exposure past the daily loss stop, inside an open incident, inside a blackout window the Research Analyst wrote from the calendar, or more often than once a minute
  • removing margin, or changing leverage on a live position, both of which move the liquidation price and are not configuration
  • more than three open positions across the account, which no approval raises

Being honest about the limit: on a box where the Bots run as the same OS user as the signer, the state directory is bookkeeping rather than a boundary. Anything the state could lie about is therefore asked of the venue instead, and Tier 1 refuses outright unless the operator sets STRIKEGROK_STATE_TRUSTED=1 to assert that the split is real. Without it, keep the platform's Require Approval rule on and trade at Tier 2.

The desk still ships no strategies and makes no return claims. None of this creates an edge; it decides when the user's own tested rules are allowed to act on one.

v2.0.0

Every file in this repository is now original work, and LICENSE carries a single copyright.

The release-check tooling has been replaced rather than edited. Five scripts and four test files - around 1,460 lines - are gone, and in their place:

  • scripts/validate.py, the whole repository contract in eleven checks: skills parse and stay inside their budget, agents reference real skills and exactly one writes to the exchange, the runbook and index list everything that ships, relative links resolve, all six manifests agree, declared component paths exist and stay inside the repository, instruction files name only this release and pin their clones, install commands name declared ids, the template pins the current skill bytes, no emoji, and no committed credential. --list describes them.
  • scripts/test_validate.py, which breaks a copy of the repository one way per fixture and asserts the matching check notices. A check that never fails is not a check.
  • scripts/desk_doctor.py, rewritten. It reports on the release, the pinned skill bytes, the setup pin and the public Price Service, and it will not load a credential or call a signed endpoint - a doctor able to verify the write path would be a doctor able to place an order.

Two things fixed along the way:

  • The CI workflow targeted self-hosted runners that do not exist for this repository, so it could never have run. It now uses a GitHub-hosted runner.
  • A scheduled workflow invoked a checker that no longer exists.

The new tooling is roughly 40% smaller than what it replaces and keeps every guard that has actually caught a defect: skill-hash drift, an agent pointing at a skill that was renamed, manifests disagreeing after a version bump, and a document left naming an older tag.

v1.1.0

The desk's process documentation and all seven role prompts rewritten from scratch. Same seven roles, same lifecycle, same controls - new text throughout, and several things corrected on the way:

  • The workspace layout still showed HG- proposal ids. Now SG-, matching everything else.
  • The engagement levels still offered a testnet practice tier. Strike's testnet lists four markets with empty books and cannot fill an order, so the levels are now research and trading, with the preview block and a minimum-size live run in place of practice.
  • strike-market-data still told Bots that execution went through an MCP and warned of a -PERP/-USD symbol split. Both untrue since execution moved to the signed API, and both would have confused the Market Analyst on every brief.
  • Several skills still carried mechanics from a venue this desk does not use - grouped TP/SL parents, a funding-history request that does not exist here, a rate-limit endpoint, an /info path - and the Research Analyst's worked example cited sources from that venue.
  • The strategy lab still described paper trading on testnet. It now describes forward testing at minimum size through the ordinary lifecycle, and says plainly that this tests the rules and the plumbing rather than returns at size.
  • The $schema URL in plugin.json had been caught by an earlier version bump and pointed at a schema version that does not exist.

v1.0.1

Presentation pass. Skill authorship, the changelog, the attribution page and the README credit block rewritten so the repository describes itself rather than its lineage, and LICENSE carries a Mendurim copyright line.

v1.0.0

First release. A seven-agent Strike Finance trading desk for Grok Bot: nineteen skills, seven role prompts, and a zero-key Opening Bell.

The desk

  • Seven roles with full system prompts - Desk Lead, Market Analyst, Research Analyst, Strategist, Risk Manager, Execution Trader, Trade Reviewer. Six on a Trading Floor group chat, the Trade Reviewer off it.
  • One lifecycle for every trade: idea, evidence, risk sign-off, approval by ticket id, one send, reconciliation, review.
  • One writer. Six Bots read; the Execution Trader alone sends, and only on a ticket the user approved by id.
  • An evidence standard with an unavailable verdict of its own, so a dead feed never passes for a calm market.

Market data - Strike's public Price Service (api.strikefinance.org/price/v2), unauthenticated, thirty-one markets. scripts/opening_bell.py renders a timestamped snapshot with mark, index, funding, open interest, spread and depth at 5/10/25 bps. Depth is read at limit=1000 because the endpoint defaults to twenty levels a side, and a band the resting book does not reach is reported as a floor rather than a total.

Execution - the signed Strike API (api.strikefinance.org/v2), authenticated with an Ed25519 API wallet the user registers at app.strikefinance.org/api-keys. scripts/strike_request.py is the single signing primitive; it uses the cryptography package when present and openssl when not, so the desk installs nothing. Its signing is verified against RFC 8032 test vector 1 on both backends.

Order handling

  • Bracket strategy orders carry entry, take-profit and stop-loss in one request, with a client order id per leg.
  • Every order carries a client_order_id the desk chooses before it sends, so a lost response is a lookup against GET /v2/order rather than a guess. A replacement always gets a fresh id.
  • Limit, bounded market, standalone triggers, trailing stops, replace, replace-batch, batch, cancel, cancel-all, leverage, margin mode, isolated margin and TWAP are all supported and each carries the approval it needs.
  • Every request is built as a JSON file and posted to the floor as a preview block before it is sent. Strike has no dry-run mode, so the bytes previewed being the bytes sent is the desk's own discipline, backed by Grok Bot's Require Approval rule.

Known limits, stated plainly

  • No order expiry. An order cannot age out into safety, so elapsed time never proves a lost send is dead. Only the client-order-id lookup does.
  • No usable testnet. Strike's testnet lists four markets with empty books. It proves a request is well-formed and correctly signed, and nothing else. Rehearsal is the preview block plus a minimum-size live run ($10 notional) for each new kind of action.
  • No dead-man's switch. Strike has no such endpoint, and the desk does not improvise one or claim to have it.

Optional research add-on - the crowdtime MCP supplies a market liquidity screen, computed technical indicators, crypto/equity/dividend research playbooks, Bodega prediction markets and Discord alerts. It is not on the trading path, its order tools are deliberately unused, and the desk trades fully without it.