Run your own bot
The starter bot is sample code you download, edit in one place (your strategy) and run on your own computer. It trades through Orblivion inside limits you set, and it starts as a dry run: nothing is sent until you switch it on. It comes in Python and TypeScript, and it uses only the published SDKs (orblivion 0.1.1 or later, @orblivion/sdk 0.1.1 or later).
Last updated: 12 October 2026
What it is#
A loop that runs every interval_s seconds:
- It reads the USD price of each configured token (Chainlink, read on chain by the SDK) and the wallet's balances.
- It asks your strategy what to trade:
decide(market, book, state)returns a list of orders. - It cuts each order to the bot's own limits: per trade, per rolling 24 hours, at most what the wallet holds, and nothing under $1.
- It trades, or in a dry run only checks what the trade would do, and logs everything to the console and
bot-log.jsonl.
It runs in one of two modes:
| Mode | Which key the bot holds | What holds it back |
|---|---|---|
session (the default) | an executor key, which can trade only inside a session the wallet signed | the bot's limits, the SDK's checks, and the session's caps, which Orblivion's co-signer and the chain enforce |
direct | the wallet's own key | the bot's limits and the SDK's checks |
It runs only while your computer does. For an agent that keeps trading with your computer off, see Hosted agents.
Download#
Put each version in a folder of its own.
| Python | TypeScript | What it is |
|---|---|---|
| strategy.py | strategy.ts | your logic, and the only file you need to edit |
| config.toml | config.json | the settings: tokens, interval, dry run, mode, limits, the session's caps |
| bot.py | bot.ts | the loop |
| setup_wallet.py | setup-wallet.ts | one-time setup, status, and the off switches, revoke and reset-code |
| README.txt | README.txt | a short how-to |
| package.json, tsconfig.json | the SDK, viem and tsx, and the npm run scripts |
The code is under the MIT License (LICENSE.txt). Read it before you run it: it's short on purpose.
Quick start#
Python 3.10 or later, or Node.js 20 or later.
pip install "orblivion>=0.1.1"
python setup_wallet.py # keys, funding, the session
python bot.py # a dry runnpm install
npm run setup # keys, funding, the session
npm run bot # a dry runThe setup walks through four steps, and each can be run on its own (keys, fund, session; npm run setup -- keys in TypeScript):
- Keys. It makes the wallet's key and, in session mode, the executor's, in
~/.orblivion-bot(key_dir), outside the bot's folder. They are never printed. - Funding. It prints both addresses and waits until they're funded. Send the wallet what the bot trades, plus about $1 of ETH for its own transactions. In session mode, send the executor about $2 of ETH: it pays the gas of every trade, about 2 cents each.
- The session (session mode). The wallet grants the executor a session from config's
[session]caps: the tokens, the most per trade and per day, the number of trades, and how many hours it lasts (at most 30 days). - Its setup. After you type
SEND, the wallet sends the session's setup: on a plain wallet, the smart-account code sessions run on (Sessions), and for each token it sells, a capped Permit2 approval. Nothing is sent on chain withoutSEND.
Then python bot.py (npm run bot) runs a dry run. Stop it with Ctrl+C, or by creating a file named STOP in its folder.
Your strategy#
Your logic goes in one function. Every tick the bot calls it with fresh data, and it returns the orders it wants, or none:
from dataclasses import dataclass
@dataclass
class Order:
sell: str # a token from config's tokens, e.g. "ETH"
buy: str # e.g. "USDG"
amount_usd: float # dollars of `sell` to trade
reason: str = "" # shown in the log
def decide(market, book, state) -> list[Order]:
...export type Order = { sell: string; buy: string; amountUsd: number; reason?: string }
export function decide(market: Market, book: Book, state: State): Order[] {
// ...
}| Argument | What it holds |
|---|---|
market | each token's USD price, read from Chainlink on chain: {"ETH": 2512.37, "USDG": 1.0}. A token without a feed is left out. |
book | the wallet's balances: {"balances": {"ETH": {"units": 0.0398, "usd": 100.0}, ...}, "total_usd": 202.5} (totalUsd in TypeScript) |
state | a dict (an object in TypeScript) that persists between ticks and between runs, saved in bot-state.json |
The strategy shipped with the bot keeps the wallet half in ETH and half in USDG, and trades back when ETH drifts more than 5 points either way. A second example in the same file buys a dip, and the Strategy cookbook has five more to drop in. Your own can be as short as this:
def decide(market, book, state):
eth = market.get("ETH")
if eth is None:
return []
last = state.get("last")
state["last"] = eth
if last and eth < last * 0.98: # down 2% since the last tick
return [Order(sell="USDG", buy="ETH", amount_usd=5, reason=f"ETH fell to ${eth:,.2f}")]
return []Keep the strategy free of keys, network calls and sending: the bot does those, inside your limits. You don't need to size orders carefully either, since the bot cuts every order to fit.
How it keeps you safe#
| Layer | What it does | Enforced by |
|---|---|---|
| Dry run, on by default | each order is checked, as a quote in session mode or as a full plan in direct mode, and nothing is signed or sent | the bot |
| The bot's limits | max_trade_usd per trade, max_day_usd per rolling 24 hours (kept in bot-ledger.json), at most what the wallet holds, nothing under $1 | the bot |
| The SDK's checks | every transaction is decoded and checked before it's signed: route, recipient, minimum output, fee; and no trade above the per-trade limit | the SDK, on your computer |
| The session's caps | the tokens, the most per trade, per 24 hours and in trades, the ERC-20 budget, the expiry | Orblivion's co-signer, and the chain |
| The off switch | revoke ends the session | the chain, from the record in session.json, then Orblivion |
| The emergency switch | reset-code makes the wallet a plain account: no session can trade | the chain |
A dry run never uses the session's limits#
Orblivion counts every trade its co-signer signs against the session's day cap, sent or not (Sessions). A session trade asked for as a dry run is still co-signed, so it would use the session's day. The bot never asks for one: its dry run is a quote (session mode) or a plan (direct mode), and neither is co-signed.
The bot keeps dry runs and live trades apart in its own ledger too, so a dry run shows what its limits would let through, and going live starts with a clean day.
The off switch#
python setup_wallet.py revokenpm run revokeTransactions from the wallet end the session on chain and set its approvals back to 0, and Orblivion stops co-signing for it (Revoking). That works whatever the bot is doing, and wherever it runs. To stop only the bot, press Ctrl+C or create the STOP file.
session.json keeps the session's revocation record (no secret: the delegation the wallet signed, its signature and what the setup approved). With it, revoke builds those transactions on your computer and sends them straight to Robinhood Chain, even when Orblivion doesn't answer, then reads them back and says whether the chain confirms them (The revocation record). It says the bot can't trade in the session only when the chain confirms it ended, or Orblivion revoked it; otherwise run revoke again. When only Orblivion's answer stands in the way (it names a root session.json doesn't hold, and none your wallet signed), a rerun won't change that: revoke --chain-only ends the session from session.json alone and asks Orblivion nothing. When Orblivion's answer carries a root your wallet signed that still reads live, session.json is most likely an older session's record: revoke says so and asks before disabling that root too. A session without a record ends on Orblivion's word, and a rerun can't check it better. Keep session.json where nobody else can edit it. That needs SDK 0.1.3 or later (Python and TypeScript); with an older SDK, or a session granted with one, revoke takes its on-chain steps from Orblivion's answer and says to update the SDK.
The emergency switch#
python setup_wallet.py reset-codenpm run reset-codeAfter you type SEND, the wallet sends one transaction to itself that removes its smart-account code, straight to Robinhood Chain. From then on no session can move anything from it, whatever Orblivion or the executor do, and your own transactions work as before. It doesn't revoke the session or reset its approvals, and setting the code again (setup, a new session, or any wallet app that delegates the account to EIP7702StatelessDeleGator again) brings the session back until it expires, so run revoke as well (what it does and doesn't undo).
Going live#
- Run the dry run for a while. Read
bot-log.jsonl: every tick, decision, order and dry run is there. - Check the setup.
python setup_wallet.py status(npm run status) shows the balances, the executor's gas, and what the session's caps have left. - Set
dry_run = falseinconfig.toml("dry_run": falseinconfig.json), and start the bot again. - Keep the caps small until the bot has traded the way you expect. A wallet that holds only what the bot trades limits the worst case too.
- Renew before the session ends. A session lasts at most 30 days. When it ends the bot stops and says so: run
revoke, thensession, for a new one.
FAQ#
Do I need an Orbio agent?#
No. Any wallet can trade through Orblivion. An Orbio agent matters only for the league: to enter the bot's wallet, see Enter your bot in the league.
Which tokens can it trade?#
The ones in tokens: core symbols such as ETH, USDG and cbBTC, or any token by its contract address. market holds prices only for tokens with a Chainlink feed, and a session that trades a token without one lasts at most 24 hours. Read Tokens and risks before you add one.
What does it cost?#
Orblivion's fee on each trade (Fees and gas), and gas: about 2 cents a trade in session mode, paid by the executor, or about a cent in direct mode, paid by the wallet.
What happens when a trade fails?#
The bot logs the error's code and message and carries on. When Orblivion asks it to wait (a rate limit), it waits. When the session can't trade any more (revoked, ended, out of trades), it stops and says what to run. A failed dry run, or a trade refused before anything was sent, doesn't count against the bot's day.
What if my computer sleeps?#
The bot trades only while it runs. Nothing queues up while it's asleep: the next tick reads fresh prices and balances.
Can I run the Python and the TypeScript bot together?#
They share the key folder and its files, so either can use the setup the other made. Don't run both on the same wallet at once: each keeps its own ledger, so together they could trade twice the bot's day limit (the session's caps still hold).
How do I change the session's caps?#
Edit [session] in the config, then revoke and session. The caps are signed into the session, so a new one is the only way to change them.
Why does the session ask for an ERC-20 budget?#
Selling a token such as USDG goes through Permit2, which the wallet approves once, up to setup_budget_usd, for the whole session. Selling ETH needs no approval.
Is this financial advice?#
No. It's example code: you bear the outcome of every trade it makes. See the Terms of Use.