---
title: "Seven orders, six refusals"
standfirst: "I spent an afternoon trying to make Vibe-Trading misbehave. Seven orders went in, six came back refused. A teardown of the safety layer, and a twenty-minute experiment you can run yourself."
author: "Jan Cervinka"
author_url: "https://hello-purple.com/authors/jan-cervinka/"
date_published: "2026-09-24T10:23:22.478Z"
canonical: "https://hello-purple.com/reports/seven-orders-six-refusals/"
track: "Teardowns"
layout: "essay"
publisher: "Hello Purple"
publisher_note: "Hello Purple is a technology publisher from Purple Technology. Its Reports describe what was built and how; they are not financial advice and do not offer or invite the use of any financial service."
llms_txt: "https://hello-purple.com/llms.txt"
interactive: "https://hello-purple.com/reports/seven-orders-six-refusals/interactive/"
---

# Seven orders, six refusals

> I spent an afternoon trying to make Vibe-Trading misbehave. Seven orders went in, six came back refused. A teardown of the safety layer, and a twenty-minute experiment you can run yourself.

*Interactive edition: The eight checks, the seven orders and every refusal, as a thing you can run rather than read about. https://hello-purple.com/reports/seven-orders-six-refusals/interactive/*

## TL;DR

- Vibe-Trading's live-trading layer refused six of my seven adversarial orders, and each refusal named the rule it was enforcing.
- The design fails closed: unparseable data, a replayed authorisation or a limit widened at commit time all end in a refusal, never a guess.
- Missing today: a stop-loss at order entry, margin maths that fit forex and CFDs, and any retry, so a timeout leaves you unsure an order arrived.
- The research half will bless an overfitted strategy unless you ask for resampling, confidence intervals and out-of-sample tests by name.
- Verdict: run the free safety layer with your coding agent in twenty minutes, no account needed, and stay on paper for weeks before real funds.

Before writing this I spent an afternoon trying to make Vibe-Trading misbehave. I widened permissions at the commit step, replayed authorisations, sent orders built to break its rules, corrupted its kill switch, edited its audit log. Seven orders went in. Six came back refused.

The refusal I keep thinking about happened at the commit step. I had a permission file with a fifty-dollar order cap and tried to raise it to five thousand on its way to disk. The system refused and named its reason: widening a permission must go through a fresh proposal. You can narrow a permission at commit time. You cannot loosen one.

Most evaluations of trading agents ask how smart the model is. The engineering I want to show you assumes the model will at some point be wrong, confused, or compromised, and builds for that day. You can run this layer yourself in twenty minutes, free, without a brokerage account, and I think it teaches more about agentic finance than any benchmark. The exact experiment is at the end of this piece.

## What you're looking at

Vibe-Trading is an open-source finance research agent from the [HKUDS lab](https://github.com/HKUDS) at the University of Hong Kong. Four and a half months old, MIT licensed, 30,700 stars. Inside, it is bigger than the name suggests: around 2,200 files, of which ninety are skill definitions written for the agent itself and nearly five hundred are a library of trading factors. Much of that bulk is research machinery. Factors, skills, data loaders, backtest engines.

"Research agent" is the accurate label. You ask market questions in plain language; it routes them to data loaders, screeners, the factor library and the backtest engines, and it is built to ground every answer in fetched data. Live trading exists, but as a deliberately guarded corner. It is opt-in, paper account by default, and more than half of the broker connection profiles are read-only. The [wiki](https://vibetrading.wiki/home/) puts it plainly: live trading is "opt-in and read-only by default... strictly within the limits you set, and you can halt it instantly." The same page labels the project experimental, tells you to use it at your own risk, and carries a not-investment-advice notice.

That guarded corner is where I spent my afternoon.

## The permission layer

Before the agent can touch a live account, you write a permission file the project calls a mandate. It lists your biggest allowed order, biggest total exposure, leverage, trades per day, which markets and instrument types, minimum liquidity and company size, and symbols never to touch. The agent can propose a mandate. Only a function its loop cannot reach can commit one. The committed file expires after thirty days by default, because in the code's own words "a live mandate must not live forever."

Every live order then passes a fixed series of checks that fail closed. When I fed the checks a positions payload they could not parse, the order was refused rather than waved through on an assumed zero exposure. A kill switch, which is an actual file on disk, halts everything even if the model is stuck or the file's contents are corrupted. An append-only, hash-chained log records the decisions; when I tampered with one entry, it detected the edit and then refused to write anything further on top of the broken chain.

My favourite artifact in the whole repo is a test HKUDS ship asserting that the agent has no reachable function that could widen its own permissions. Most test suites check what the code does. This one checks what the code must never become able to do. I took that idea home with me.

Almost every failure mode I probed resolved the same way: when the system is confused, the answer is no. Unparseable data, ambiguous orders, an authorisation presented twice, a widened limit at commit time. All refused, each with a reason naming the rule or the condition. Systems that trade availability for safety in that direction are rare, because every one of those refusals costs an outage when a data feed hiccups. Somebody accepted that cost on purpose. You can see the same hand in smaller decisions, like the crash-reconciliation step getting read-only broker access, structurally, so it cannot resend an order while sorting out what happened.

Two things to hold onto if you ever go live. Thirteen connectors are registered for live broker connections, of varying depth, and the project's own default is paper trading; I would run weeks on paper before a mandate ever touches real funds. And a fifty-dollar cap bounds the damage of any single mistake at fifty dollars, while whether the strategy inside the cap makes money is a different question the cap cannot answer.

## The research half, and the trap inside it

I never ran the research loop myself; the machine I tested on had no model key. So what follows is what the project designs for and what I would verify, not lived experience.

The working loop the project teaches is route, ground, test, deliver. Your question gets routed to tools, claims get grounded in fetched data, results get tested, and you get a report. The intended shape of a session is a conversation, something like "screen US large caps for X, backtest the top three over five years, show me what breaks," with your own coding agent handling the mechanics rather than you writing code.

One habit is worth building from the first session. When an answer comes back, trace a single number in it to the data call that produced it. If you can, you are looking at research. If you cannot, you are looking at generated prose with figures in it, which is worse than useless because it is persuasive.

The trap is older than any agent. Testing fifty strategy ideas in an afternoon used to be impossible, and the impossibility was a kind of protection: the more strategies you try against the same history, the more certain you are to find one that fits the past by luck. An agent removes the effort without removing the statistics. No amount of model fluency changes that arithmetic.

The project knows this and ships three honesty checks worth asking for by name. Resampling, to see whether a result survives luck. Confidence intervals around the headline number. Testing on periods the strategy never saw. None of the three is guaranteed to run; they are conditional on configuration, the engine's comments call validation optional, and the agent's own instructions suggest only one of them. Ask for all three, every time, and treat a strategy that has not passed them as a story. Even a strategy that passes all three has proven one thing only, that it survived the past. Nothing in this project, or any project, converts that into a claim about the future.

## What's missing

There is no stop-loss or take-profit at order entry. I checked all thirteen connectors: twelve have no concept of it, and the one function with stop parameters is disabled on live accounts. If entering with a stop attached is how you trade, this is not yet your tool, though a connector is a few hundred lines of MIT-licensed code if you want to change that.

The permission model thinks in dollars of stock. Forex and CFDs are margin products, where a position can be far larger than the cash behind it, and the mandate's arithmetic fits them badly. The sharpest symptom shows up on eToro, where editing a stop on a live position is switched off entirely, because loosening a stop can pull more account funds into margin and the system refuses to authorise what it cannot measure in advance. That refusal is the safety layer being consistent, declining what it cannot bound, at the price of a feature you might want.

Nothing retries, so a timeout leaves you unsure whether an order arrived. That is the right trade-off, since a retried order can become two orders, and the reconciliation step exists to surface the ambiguity. But you will meet it.

Coverage is Asia-first. China, India and Korea get dedicated engines; US coverage is solid; Europe gets one data loader and no engine. There are smaller signs of a fast-moving young project too, stale docstrings among them, and a run-fingerprint module the README advertises but nothing feeds yet.

## What running it actually costs you

**Money risk** is bounded by the mandate if you use one, unbounded by anything if you do not, and never removed: within the limits you set, a bad strategy loses money at machine speed. The mandate is a blast radius, not a profit filter. Fund live experiments only with amounts whose total loss you have already accepted, and let the thirty-day expiry do its job of forcing a fresh decision.

**Judgment risk** is the trap from the research section wearing production clothes. The model produces fluent, confident research whether or not it is grounded, and the backtest engines will happily bless an overfitted strategy if the three honesty checks never run. The discipline is the same: trace the numbers, demand the checks, and be more suspicious of a good result the more ideas you tried before finding it.

**Operational risk** is yours because you are self-hosting. Your keys, your uptime, your upgrade path on a young repo that is moving fast, your job to re-verify behaviour when you pull a new version. The safety layer helps most with this pile, and even here it can only guard the orders it sees.

## Where I sit in this

At Purple, we build in this space, so weigh this section accordingly. The MCP server we are building for Axiory’s cTrader accounts connects an AI agent to a cTrader account over FIX 4.4, and it converged on the same four safety mechanisms Vibe-Trading has, independently: pre-order risk checks, a kill switch, a paper mode, an append-only journal. Two teams arriving at the same four organs independently is the strongest evidence I have that these are not decoration. The server also hits the same stop-loss wall from the other side, solving it with an automated three-step workflow rather than stops at entry, and FIX 4.4 denies it something Vibe-Trading has, namely any view of account balance or margin. It is still pre-production.

So the choice looks like this. Run Vibe-Trading if you want to own the stack and learn the most. If you want the broker plumbing handled while the agent stays yours, the server we are building for Axiory is the middle path once it ships. And if you want the outcome without the operations, stay tuned for Takumis, which shall be released later this year. It will make the agent, the connectors, the keys and the account plumbing run for you, with the usual managed trade-off that you work inside what the platform exposes.

## Start with the free part

If you decide to try it, a few practical things will save you an evening.

The Python version is the first trap. The project wants 3.11 to 3.13, and 3.12 is the one to use. A 3.9 install fails with a clear message. A 3.14 install fails with a hundred lines of C compiler errors from deep inside a dependency, because one package has no prebuilt wheel there and the installer tries to compile it; the explanation lives in a comment inside the project's own configuration file, which I found after debugging the failure, not before.

The research half is driven by a language model you pay for, so you need a model API key, plus a handful of free data-source keys depending on which markets you care about. Get these before you start rather than four steps in.

And bring a coding agent of your own. The repo ships a 23-kilobyte brief at `agent/SKILL.md`, written specifically for AI agents working on this codebase. Point your agent at that file and the wiki before it touches anything else, and let it do the setup, the wiring and the experiments while you read what comes back.

A brokerage account is the thing you do not need, and will not for a long time. The entire safety layer runs on the Python standard library plus one common data package, so you can exercise the part of this project that decides whether an order is allowed without an account or a key. That is the experiment: point your coding agent at the repo and `agent/SKILL.md`, have it drive the pre-trade checks with a made-up mandate and made-up orders, and read the refusals it brings back. Twenty minutes, no money. It is where my seven orders and six refusals came from, and where I would start.

*Vibe-Trading is MIT licensed and built by *[*HKUDS*](https://github.com/HKUDS)*. Nothing here is investment advice or a recommendation to trade anything.*

## For agents

### Facts and figures

- Project: Vibe-Trading, by the HKUDS lab, University of Hong Kong. Repository https://github.com/HKUDS/Vibe-Trading. MIT licence. Repository created 2026-04-01.
- GitHub stars: 30,700 at the time of the test (2026-08-12); 32,899 on 2026-09-07.
- Repository size at the time of the test: about 2,200 files, of which about 90 are skill definitions written for the agent and nearly 500 are trading factors.
- Live broker connectors registered: 13. More than half of the connection profiles are read-only. Paper trading is the default.
- Test run: 7 live orders submitted against the pre-trade checks with a made-up mandate; 6 refused, each with a reason naming the rule or condition.
- Mandate fields: largest single order, largest total exposure, leverage, trades per day, allowed markets and instrument types, minimum liquidity, minimum company size, symbols never to touch. Default expiry of a committed mandate: 30 days.
- Test machine: Python 3.12. Supported range is 3.11 to 3.13.
- The agent brief shipped in the repository, `agent/SKILL.md`: 23 KB at the time of the test, 25 KB on 2026-09-07.

### Steps to reproduce

1. Clone https://github.com/HKUDS/Vibe-Trading and install with Python 3.12. No brokerage account and no model API key are needed for the safety layer: it runs on the Python standard library plus one common data package.
2. Point your coding agent at the repository and at `agent/SKILL.md` before it touches anything else.
3. Have the agent write a mandate with a small cap (the piece used a 50 dollar order cap), propose it, and commit it through the committing function.
4. Have the agent submit orders built to break the rules: one that exceeds the cap, one after widening the cap at commit time from 50 to 5,000, one with a positions payload the checks cannot parse, one replaying an authorisation already used, one with the kill-switch file present, one with the kill-switch file corrupted, and one valid order.
5. Read the refusals. Expected: the widened cap is refused because a permission can narrow at commit time and never loosen; the unparseable positions payload is refused rather than assumed to be zero exposure; the corrupted kill switch still halts; the valid order passes.
6. Edit one entry in the append-only audit log and submit again. Expected: the log detects the edit and refuses to append on top of the broken chain.
7. Locate the test that asserts the agent has no reachable function able to widen its own permissions. It is the shape of test worth copying into your own agent.

Twenty minutes, no money.

### Limits

- No stop-loss or take-profit at order entry. 12 of the 13 connectors have no concept of it; the one function with stop parameters is disabled on live accounts. A connector is a few hundred lines of MIT-licensed code.
- The mandate counts exposure in dollars of stock. Forex and CFDs are margin products, so the mandate's arithmetic fits them loosely. On eToro, editing a stop on a live position is switched off because loosening a stop can pull more account funds into margin than the system can measure in advance.
- No order is ever retried. A timeout leaves the outcome unknown until the reconciliation step reads it back from the broker.
- The three backtest honesty checks (resampling, confidence intervals, testing on unseen periods) run only when the configuration asks for them. The agent's own instructions suggest one of the three.
- Market coverage is Asia-first: dedicated engines for China, India and Korea; solid US loaders; one loader and no engine for Europe.
- A Python 3.14 install fails on a dependency with no prebuilt wheel; the reason is a comment in the project's own configuration file.

### Sources and links

- Repository: https://github.com/HKUDS/Vibe-Trading
- Project wiki: https://vibetrading.wiki/home/
- HKUDS lab: https://github.com/HKUDS

### Terms

- Mandate: the permission file that bounds what the agent may do on a live account. Proposed by the agent, committed only by a function outside the agent's reach, expires after 30 days by default.
- Kill switch: a file on disk whose presence halts all live trading, including when the model is stuck or the file's contents are corrupted.
- Fail closed: any check that cannot be evaluated refuses the order instead of assuming a safe value.
- Hash-chained audit log: an append-only record where each entry carries a hash of the previous one; an edited entry breaks the chain and the log refuses further writes.
- Paper account: a simulated account with no real funds. The project's default.

### About this piece

Written by Jan Cervinka, CTO of Purple Group, published 2026-09-03 on Hello Purple, a technology publisher from Purple Technology. It reports an afternoon of adversarial testing of one open-source project's safety layer. Purple builds in the same space: a connector that links an AI agent to a trading account over FIX 4.4, still pre-production, and Takumis, a managed strategy platform we are planning to release soon. The piece describes engineering; it is not investment advice and does not invite the use of any trading or brokerage service.
