ORUS
Documentation15 sectionsVersioned with the engine

How it works,
end to end.

What a strategy actually contains, how a screener page becomes a position, what closes it, and which limits bind before any capital moves. Every number here is quoted from the engine, and every field name is the strategy's own — so you can go from this page to the strategy form without a translation step.

01Start here

What the thing is, the objects you need to hold in your head, and how to get in.

What Orus is

An autonomous desk for Robinhood Chain, running in the open.

Orus discovers tokens across the chain's DEXes and launchpads, waits for one to fall far enough from its all-time high, buys the dip, and manages the position until a target, a stop or a time limit closes it. No part of that requires anyone to be at a screen.

It runs on Robinhood Chain — an Arbitrum Nitro L2, chain id 4663, gas paid in ETH. Two chain facts shape everything downstream. USDG, not WETH, is the dominant quote asset, so every price in the system is denominated in USD and a "price in native" is meaningless across venues. Blocks land in about 100 ms with first-come-first-served sequencing, so there is no priority-fee auction to win: the race is on data freshness, not on tips.

Every decision an agent makes — every candidate looked at, every rejection and the measurement behind it, every entry and exit — is written to an event log. The public desk reads that log. Refusals and losses are published on the same page as the wins.

Strategies and agents

A strategy is the rulebook. An agent is one copy of it, running on a clock.

A strategy is a row of parameters covering every step of the pipeline: what to screen for, which holder profiles to reject, how deep and how recent a dip has to be, how much to spend, and when to sell. It holds no wallet and takes no action on its own.

An agent binds one strategy to a cadence, an execution mode and, optionally, a wallet. The cron fires every minute; an agent only runs when its own frequency has elapsed, and each run is claimed before it starts — the last-run timestamp is bumped under an optimistic lock — so a run that overruns its interval is never started twice by two concurrent ticks.

Two agents on two strategy rows that share every entry rule and differ only in their exits attribute any difference in results to the exit policy alone. That is how the desk's strategies are compared.

Pausing an agent stops new entries. It does not stop the exits: an agent that is paused or stopped but still holds positions gets a monitor-only pass on its own cadence, so the stop loss, the hold limit and the take profit keep working on what is already held.

frequency_seconds
How often the agent runs. The cron ticks every minute; the agent runs when this has elapsed.
execution_mode
simulation, manual or automatic — see the next section.
status
running, paused or stopped. Paused and stopped agents still monitor what they hold.

Execution modes

Simulation, manual and automatic differ in exactly one thing: whether a transaction is sent.

Simulation needs no wallet. The agent runs the full pipeline, including the guard rails and the round-trip check, and books each position at the fill the real routers quote — tokens received, price implied — not at the oracle price. Nothing is broadcast. A simulated exit is quoted out through the same router and booked at the native it would return. It is how a strategy earns the right to hold money.

Manual runs the pipeline and stops at the signal. It stays pending until an operator executes it from the console, against the agent's wallet, and only while the dip it was raised on still exists.

Automatic does the whole thing: quotes, wraps ETH, approves the token if needed, swaps, and records the position from the fill it actually got.

simulation
No wallet. Positions booked at the routed quote, status simulated_open.
manual
Signal stays pending until executed from the console.
automatic
Quotes, wraps, approves, swaps, records the real fill.

Wallets and keys

One agent, at most one wallet, and a key that never leaves the server.

An agent's wallet is a single EVM address. Its private key is held encrypted — AES-256-GCM under a server-side key — and decrypted only inside the engine, for the moment a transaction is signed. The raw key is never written anywhere, the console never displays it, and the console's API never returns it.

The wallet's ETH balance is read straight from the chain RPC on each refresh rather than cached.

An agent with no wallet cannot spend, whatever its mode says. That is a property of the code path, not a setting. Wallets are provisioned by the desk operator today; attaching your own is part of the open release.

Access and the demo

The console is open to anyone, in read-only.

The console at /app sits behind a login page. Anyone can enter with the demo account — demo / demo, filled in for you — and read everything: the fleet, every agent's radar, signals, positions and history, the activity tape and the reports.

A demo session writes nothing. Every attempt — creating an agent, starting or pausing one, executing a signal, ignoring a token, asking for a report — is refused by the server before it reaches the engine, and the console says what is coming instead. The operator signs in on the same page with their own credentials.

Nothing on the desk is a mock. What a demo reads is the same database the engine writes to, on the same fifteen-second poll the operator sees.

/app
The console. Demo reads, operator writes.
/live
The public desk. No account needed.
/docs
This page.

Where the numbers come from

Four providers behind one market layer, so the engine never talks to a vendor directly.

The screener is Birdeye: it narrows the chain's token list under thresholds a server can enforce for you, and serves bulk prices. Token details — price, market cap, launch date, pool, all-time high, holder concentration, bundler and sniper shares, launchpad state and security flags — come from a single Mobula call per token, which also serves candles, earliest trades and swap routing. LI.FI routes a pair when Mobula cannot. Chain reads, signing and broadcasting go through viem against the chain RPC.

Every provider sits behind one market module. The engine imports that module and nothing else, so replacing a provider means reimplementing one function, and every filter is re-checked locally even when the screener already applied it — a provider change can never silently weaken a rule.

Every price is in USD. Every quote — Mobula's and LI.FI's report output in different units — is normalised to the same economics before the engine compares them: expected output in token units, USD in and out, price impact.

02The pipeline

Scan, filter, launch check, radar, dip detection, signal, execution. Every stage can reject, and every rejection is logged with the test that failed and how far off it was.

Scan

Narrow the chain to a few hundred candidates, then enrich them.

The screener asks Birdeye for the chain's token list under the thresholds it can enforce server-side — 24-hour volume, liquidity, market cap, trade count. That is the cheap pass, and it exists to keep the expensive one small. The native sentinel and the two quote assets, WETH and USDG, are dropped here: the screener lists them as tokens, but they are not candidates.

Each survivor gets one Mobula details call, which returns everything the filter and the chart analyser will need in a single response. Details are shared across the agents running in one process, so nine agents screening the same chain cost one call per token, not nine.

Output: up to 200 candidates per run.

min_volume
Minimum 24-hour volume in USD. Default 100,000. Rolling 24 hours, not volume since launch.
min_market_cap
Minimum market cap in USD. Default 20,000.
min_transactions_24h
Minimum 24-hour trade count. Default 200.
min_liquidity_at_dip
Also passed to the screener as its liquidity floor. Default 5,000.

Filters and risk

Twenty-two checks, applied locally to every candidate. Where most of them die.

Three checks are fixed and cannot be loosened: a token with no pool is out, a token that cannot be sold — a honeypot — is out, and a buy or sell tax above 10% is out. There is no mint or freeze authority on an ERC-20, so the Solana-era checks do not apply; taxes and honeypots are the equivalent traps, and they are rejected rather than scored.

The venue whitelist matches the venue key, its name or the launchpad the token graduated from, so a strategy that names a launchpad admits its graduated tokens on whichever DEX they now trade. Bonding state can be required either way.

The rest is activity and holder distribution. A token whose supply sits with bundler wallets, launch snipers, insiders or the deployer is a token whose price is somebody's decision rather than a market's. Each has its own ceiling, and a wash-trading floor compares organic volume against reported volume.

Missing data fails the check it is missing from. A strategy that sets a threshold is asking a question, and "unknown" is not the answer it wants: a token with no creation date fails an age-bounded strategy, one with no sniper figure fails the sniper ceiling. These rejections carry their own codes — missing_age, missing_snipers, missing_holders — so the tape shows what was absent rather than what was exceeded. A strategy that leaves a threshold unset asks nothing and is unaffected.

A candidate that fails a numeric test still produces an event with the value and the threshold. That distance is what the near-miss board on the activity page is built from.

min_age_hours / max_age_hours
Token age window. Defaults 5 minutes to 7 days.
min_buys / min_sells
Minimum 24-hour buy and sell counts. Defaults 100 each.
min_volume_liquidity_ratio
Floor on 24-hour volume over liquidity.
min_total_fees
Floor on cumulative pool fees in USD.
allowed_dexes
Venue whitelist. Matches venue key, venue name or launchpad. Empty means any.
require_bonded
true: graduated only. false: pre-graduation only. Unset: both.
website_regex
Pattern matched against the token's socials and description.
max_bundled_buy_pct
Ceiling on supply held by bundler wallets. Default 40.
max_snipers_pct
Ceiling on supply held by wallets that sniped the launch.
max_insiders_pct
Ceiling on supply held by insider wallets.
max_dev_holdings_pct
Ceiling on the deployer's own holdings.
max_top10_holdings_pct
Ceiling on the top ten holders combined.
min_organic_volume_ratio
Floor on organic volume over reported volume, 0 to 1.
min_holders
Minimum holder count.

Launch market cap and the radar

Survivors are tracked over time rather than judged in one frame.

A token's market cap one second after its first trade says something its current cap does not: a launch at a high cap is usually sniped or bundled, and the dip pattern is unreliable on it. The clock starts at the first trade, not at contract creation — on a launchpad the two coincide, on a DEX listing made after deployment they do not. Supply is read from the token. The value is cached, so the check costs one request per token, once.

What survives lands on the radar — the watchlist — where it is tracked across runs with its last known all-time high, the instant of that high, the current dip and the liquidity behind it. Nothing is bought from the radar; it is the pool the chart analyser reads every cycle.

A token leaves the radar for one of three reasons, and each is recorded: it dropped out of the scan, its entry window expired, or its launch cap exceeded the ceiling. It comes back on its own terms. One that dropped out returns when it reappears, keeping the dip it was tracking. One whose window expired, or that has already been signalled, returns only on a new all-time high — a new high starts a new dip — and only if nothing is still open on it. A launch-cap rejection, or a token hidden by hand, is permanent.

Eviction only happens on a scan that observed the market. When the screener returns nothing — an outage, a rate limit — nothing is evicted, because a blind scan is indistinguishable from an empty market.

max_launch_mcap
Ceiling on market cap one second after the first trade. Default 50,000. Unset skips the check.

Dip detection

The entry test, and the window it opens.

For each radar token the analyser takes the all-time high from the details it already has, pulls five-minute candles only when that high moved or no crossing is stored yet, and measures the current price against it.

Depth alone is not enough. A dip that happened in ninety seconds is a flash crash, and one that took two days is a dead token — so timing is bounded on both sides, measured from the high and, optionally, from launch. Liquidity at entry and, optionally, the market cap at the high have floors of their own.

The first-dip rule is what makes an entry expire. The analyser walks candles forward from the high to the first close below the threshold; that instant starts the clock. With a window set, the entry is valid for that many minutes after the crossing and then written off — which is why a token can be rejected for being too late on a dip that is still deep. A crossing belongs to its high: a new high clears it.

dip_threshold_pct
Minimum fall from the all-time high. Default 75.
min_dip_timing_minutes
Floor on time from high to dip. Default 5 — the flash-crash guard.
max_dip_timing_minutes
Ceiling on time from high to dip. Default 600.
min_dip_timing_launch_minutes / max_dip_timing_launch_minutes
The same bounds, measured from launch.
max_first_dip_minutes
How long the entry stays valid after the first crossing.
min_liquidity_at_dip
Minimum USD liquidity at entry. Default 5,000.
min_ath_market_cap
Minimum market cap at the all-time high, in USD.

Signals

One entry per dip regime. A regime starts at an all-time high.

A buy signal carries the price at signal, the all-time high it was measured from, the dip depth and duration, the liquidity behind it and the venue it would trade on.

An agent's latest signal on a token — executed, expired or dismissed — blocks a new one only while it was raised at or after the high this dip is measured from. A signal older than the high belongs to a previous regime: the token has since made a new high and dipped again, and the strategy that entered the first dip has the same case for entering this one. A held position blocks regardless. The lookups are batched, two per run rather than two per candidate.

Once the signal row exists the radar entry moves to signal-generated and the desk operator is notified on Telegram. Sell signals are not produced here; they come from the position monitor when an exit condition fires.

A pending signal does not live forever. It expires on the strategy's own window measured from the crossing, and on a global ceiling of sixty minutes regardless. And it is executed only while the dip still exists: every entry in a batch is re-priced from one refresh at execution time, and a token that has bounced back above the threshold expires its signal with a dip-recovered event instead of being bought.

Execution

Quote both ways, record before broadcasting, and read the fill rather than assume it.

Three limits are asserted first — see guard rails below. Then the token is quoted both ways: a buy of the strategy's size, and the sale of what that buy would return. No buy route, no sell route, or an immediate round trip losing more than 25% refuses the entry outright. The signal is retired, not retried, and the reason — no route, unsellable, round-trip loss — goes on the tape. This runs in every mode, so a simulation declines what a real agent would decline. The provider's honeypot and tax flags are absent on many tokens; a router quote measures the trap directly.

ETH is wrapped before a buy, because the router quotes ERC-20 legs only and rejects the native address. Selling an ERC-20 needs an approval first. Routing goes through Mobula, with LI.FI as the fallback; signing and broadcasting are done with viem. The quote is taken before the wrap, so a pair with no route costs nothing.

The position row is written before the transaction is broadcast, so a confirmed swap can never end up untracked; if the swap fails, the placeholder is deleted. The amount received is read from the transaction's transfer logs, not from the quote, and the entry price is the fill price in every mode — what was spent over what was received. The oracle price the signal was raised on stays on the signal. Multipliers, the stop and the target are all measured from the fill, so a fill a few percent above the oracle is drawdown the position carries from the start.

An exit sells what the position holds, capped at the wallet balance for fee-on-transfer shortfalls — never the wallet's whole balance, which may hold the same token for another agent or for the operator.

03Running it

Exits, the limits that bind before capital moves, and how to read the desk.

Exits and position monitoring

Three conditions, checked in order, first match wins — and none of them trusts a single number.

At the end of every run, each position that still holds tokens has its price, multiplier and P&L refreshed. A quote more than 100× the entry is a bad print: that tick is skipped and recorded rather than acted on.

Stop loss first. It fires when the drawdown reaches the strategy's stop — but only once a second feed agrees. The details provider is asked once, and the sell proceeds if it shows at least 80% of the way to the stop. Feeds differ by a few percent; a bad print differs by orders of magnitude. If the second feed cannot answer, the stop proceeds: capital protection does not wait on a vendor.

Maximum hold second. This is a clock, not a price: it fires even when the feed has no quote for the token — liquidity pulled, pool abandoned — at a secondary-feed price or, failing that, the last mark, and the event says which. It is the most common exit on the desk by a wide margin.

Take profit last, and for a real position the fill is checked before the sell: the router is asked what the exit would return, and the sell proceeds only if it delivers at least half the promised gain and never less than the stake. A fill that fails the check is neither an exit nor a failure; the position stays open and the next tick asks again.

Exits run one at a time per wallet — they share a nonce, so concurrent swaps would collide. Agents on different wallets run concurrently. A failed exit does not close the position: if the swap reverts or is never confirmed, the position is marked sell-failed with the error and an attempt counter, and retried on later runs up to three times. Closing it would report a realised P&L for tokens the wallet still holds.

When an exit settles, realised P&L is the native the swap returned against what was spent, less the gas both legs paid, including attempts that reverted. Not oracle prices. When a position closes on the hold limit with no usable quote at all, it closes with no P&L rather than a made-up one, and the desk skips it.

min_sell_multiplier
Take profit at N× the fill. Default 2.
stop_loss_pct
Sell on a fall of N% from the fill, confirmed by a second feed. Unset disables it.
stop_loss_max_hours
Sell after holding N hours, quote or no quote. Unset disables it.
slippage_pct
Tolerance passed to the router. Default 5.

Guard rails

Checked before any capital is committed, in every mode.

Three limits are asserted on every buy, before the quote and before the wrap. The buy size must be greater than zero — set it to zero and the agent produces signals and never spends. The open-position cap counts open, simulated-open and sell-failed positions together, so a stuck exit still occupies a slot rather than quietly freeing one. The daily spend cap is a rolling 24-hour window, not a calendar day.

They are strategy fields, so two agents on the same rules can carry different ceilings by running two strategy rows. Leaving one unset disables that particular limit, which is a choice worth making deliberately.

buy_amount_native
ETH per buy. 0 means signals only, no spending.
max_open_positions
Cap on simultaneous positions, sell-failed included.
max_daily_spend_native
Cap on ETH committed per rolling 24 hours.

Reading the public desk

Everything above, live, without an account — and how each number is made.

The desk publishes the book, the trades table, the strategy board with every rule each strategy runs on, the analyses, and the decision tape — one row per decision the engine made, including the rejections and the measurement behind each one. Consecutive decisions with the same reason collapse into one row that says how many it stands for. The tape polls every eight seconds; the aggregates are computed over the whole closed book and cached for forty-five seconds.

The book is an index. These trades never shared a wallet, so the line is what a single 12 ETH float would have done taking every one of them, indexed to 1,000 — 12 ETH because it is above the most the desk ever had deployed at once and above its deepest drawdown, so the floor holds and the slope is profit. Below 1,000 the desk is down.

A closed trade whose only exit price was a quote more than 100× its entry is withheld from the book and from the trades table, and the note under the ribbon says how many. A trade that closed on its hold limit with no usable quote counts as closed and carries no P&L. Headline figures come from the receipt when there is one; a simulated trade is marked at the routed quote.

Results are forward-tested, not backtested. A backtest with no stop loss and no maximum hold can only close a trade by hitting its target, so every closed trade is a winner by construction — which is why the numbers on the desk come from running it live instead.

Watch it run.

Every decision on the desk is published, refusals included — and the console is open as a read-only demo.