degenerator

one spec in, one degen bot out

momentum.spec.md
# Momentum Degen

venue: hyperliquid
pairs: BTC, SOL, HYPE
timeframe: 5m
max_position: 5%
stop_loss: 3%
leverage: 3x
cash: 1000

rules:
- long when the 20 period average crosses above the 50
- close anything down 3%
- flat before funding
momentum-degen/
├── README.md
├── .env.example
├── .gitignore
├── requirements.txt
├── Makefile
├── Dockerfile
├── compose.yml
├── .github/workflows/ci.yml
├── src/
│   ├── main.py
│   ├── strategy.py
│   ├── risk.py
│   └── venue/
│       ├── base.py
│       └── hyperliquid.py
├── tests/test_risk.py
└── runs/

one spec inone degen bot out

degenerator reads a one-page spec file and writes a complete, working trading-bot repository: source, tests, a Dockerfile, GitHub Actions CI, an initialised git repo with the first commit made, and a JSON receipt of everything it wrote.

pip install -e . github.com/0xbobaaa/degenerator

  • python 3.9+
  • no dependencies
  • MIT

What it saves you

Starting a trading bot is half a day of scaffolding you have already written before: the same risk module, the same venue interface, the same test harness, the same Dockerfile, the same CI.

degenerator does that part in one command and leaves you the strategy, which was the only part that was ever yours.

Three promises the generator keeps

A rerun is safe

The spec is the source of truth and the repo is an artifact of it. Every number in risk.py came from a line you wrote, and the comment above it names that line.

CI exists on commit one

Tests and a docker build run on the first push, not in week three after something broke quietly. The generated repo arrives with a workflow already in it.

Nothing goes live on its own

Adapters refuse to construct with live=True. The order path is left unwritten so that a human writes it and a human reviews it before money is involved.

The spec language

A title, key: value lines, and a rules: list. Unknown keys are kept and copied into the generated README, so nothing you wrote is dropped. Rules are copied into the strategy as comments, verbatim — turning English into trading logic is not something a generator should pretend to do.

keydefaultnotes
venuepaperone of hyperliquid, dydx, robinhood, paper
pairsBTCcomma separated
timeframe5mfree text, copied into the strategy as a comment
max_position0.05share of equity per position, % accepted
stop_loss0.03per position, % accepted
leverage13x and 3 both parse to 3; above 1 is refused on robinhood
cash1000starting paper equity

Three ways a spec fails on purpose

Each of these is the real message, printed on one line, with a non-zero exit code.

  • A venue that does not exist.

    degenerator: v.spec.md: unknown venue 'binance': choose one of hyperliquid, dydx, robinhood, paper
  • A title with nothing in it, so there is no name to give the repo.

    degenerator: t.spec.md:1: the title line is empty: write '# My Bot'
  • No rules at all.

    degenerator: r.spec.md: no rules: a bot with no rules is a random number generator

Venues

  • hyperliquid — perps. Paper-mode adapter.
  • dydx — perps. Paper-mode adapter.
  • robinhood — Robinhood Chain, an Ethereum layer-2 on Arbitrum Orbit. Spot only: there is no leverage to ask for, so a spec that pairs it with leverage above 1 is refused, pointing at the line to delete. Its adapter marks fills model=swap rather than implying an order book, and its .env.example asks for an RPC URL and nothing else.
  • paper — no venue at all, and the default.

The CLI

command or flagwhat it does
plan <spec>print the file tree, write nothing
new <spec>write it
-o, --outoutput directory, default the slugified project name
--forceoverwrite a non-empty directory
--no-gitskip git init and the first commit
--dry-runreport what would happen, touch nothing

The tool that does it is five files:

degenerator/
├── cli.py         argparse front door
├── spec.py        the spec language and its errors
├── scaffold.py    writes the tree, runs git init
├── templates.py   every generated file, as one flat table
└── receipts.py    runs/ receipts and the trace log
tests/
├── test_spec.py   parsing, defaults, % and x suffixes, refusals
└── test_scaffold.py  scaffolds into a temp dir, runs the child repo's tests

Receipts

Every new run writes a JSON receipt next to where you ran it and appends a line to runs/trace.log. If you cannot say which run wrote a file and why, it is not a generator, it is a folder.

{
  "at": "2026-09-08T19:25:57+00:00",
  "spec": "examples/momentum.spec.md",
  "project": "Momentum Degen",
  "venue": "hyperliquid",
  "risk": {
    "max_position": 0.05,
    "stop_loss": 0.03,
    "leverage": 3
  },
  "files": [
    "README.md",
    ".env.example",
    "...",
    "tests/test_risk.py"
  ],
  "bytes": 13255,
  "ms": 1044,
  "git": "initialised · 1 commit"
}

The receipt stays where you invoked the tool rather than inside the generated repo, so running new --force twice leaves that repo byte-identical.

$DGEN

degenerator writes bots for Robinhood Chain, so the ticker lives on that chain rather than somewhere else with better fees.

The tool is MIT and stays MIT — the token is not a licence, a key or a gate. It is where the people building around this project stand.

CA TBA
ticker
$DGEN
chain
Robinhood Chain
chain id
4663
license
MIT

The generated venue adapters are paper-mode stubs. There is no live order path in this repository, and nothing on this page is financial advice.

Safety

  • Generated adapters are paper-mode stubs: placeholder prices, fake fills, no sockets. The loop runs end to end and touches no exchange.
  • An adapter refuses to construct with live=True. The live order path is deliberately left to you to write and review.
  • No signing, no wallets, no key handling. A generated .env.example never names a private key, because a file that does teaches everyone who reads it the wrong habit.
  • risk.py enforces the caps from your spec and the generated tests assert those exact numbers. The caps are applied, not judged: nothing here decides your leverage is too high for you.
  • The generated bots place no trades. Every fill you will ever see from one is invented by the stub.