Skip to content

feat / add adaptive grid trader agent - #179

Open
rapcmia wants to merge 6 commits into
mainfrom
feat/adaptive-grid-trader-agent
Open

feat / add adaptive grid trader agent#179
rapcmia wants to merge 6 commits into
mainfrom
feat/adaptive-grid-trader-agent

Conversation

@rapcmia

@rapcmia rapcmia commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds the Adaptive Grid Trader — an agent that runs a grid bot and re-decides, once an hour, which way that grid should lean.

A grid bot places a ladder of buy and sell orders across a price range and profits from price bouncing between them. The hard part is that a ladder built for yesterday's market is wrong today. This agent handles that: it reads the market, builds a grid whose range comes from measured volatility, then checks every hour whether that grid still makes sense.

Safety, before anything deploys: the range comes from ATR, never a hardcoded percentage. limit_price is the single exit and sits outside normal noise, so worst-case loss is a number you know before deploying rather than discover on trigger. Leverage is checked so the exchange's liquidation price can't fire ahead of your own exit. A stop loss closes a grid that's losing, and a time_limit acts as a dead-man's switch so an unsupervised grid closes itself. Two grids never open unless the account is confirmed to support it. If no grid can be built safely, the answer is HOLD — the agent never raises the budget to make one fit.

Before you run it

Close anything open on the connector, then ask any Condor agent with trading tools to set your position mode — the grid agent won't do it itself:

"Set binance_perpetual to HEDGE mode and SOL-USDT leverage to 3x on master_account"

  • One-way — for LONG or SHORT grids. Usually already set.
  • Hedge — only for two-sided grids.

What's in it

agents/adaptive_grid_trader/
├── AGENT.md                                       domain identity + risk policy
├── routines/baseline_7d.py                        entry signal — 7d trend
├── routines/hourly_mtf_check.py                   per-tick prices + manage signal
├── routines/position_mode_check.py                account capability gate
├── skills/liquidation_guard/SKILL.md              gates the execution path
└── strategies/
    ├── btc_usdt_adaptive_grid/strategy.md         per-tick procedure ($60, bitget)
    └── sol_usdt_adaptive_grid/strategy.md         per-tick procedure ($100, binance)

The decision cases

Every tick lands in exactly one case. This is the whole control loop — there's no other path to a deploy or a teardown.

Case Situation Action
A No grid running — starting fresh, or just went flat Follow the 7-day trend: up → LONG, down → SHORT. No clear trend → try the ladder below.
B The grid closed itself If the 4-hour and daily charts agree on a direction, go that way. If not, use the 7-day trend.
C Grid running, market still agrees with it Nothing.
D Grid running, market unclear or mixed Nothing — the grid keeps working.
E Grid running, both charts turn against it Close it, then open one the other way.
F Two-sided grid running, market picks a side Close both sides, then open one.

Before it flips, two things must be true: both the 4-hour and daily charts agree on the new direction, and the grid has been open at least 3 hours. Together they stop it from flip-flopping. Most hours land on C or D — nothing to do.

Three checks run before that keep/flip decision, in this order:

  1. Stale — the grid stopped filling → close it and rebuild around the current price.
  2. In profit — up 2% of its budget → close and bank it.
  3. Losing badly — losses deepening for several hours → treat that as a vote to flip, even if only one chart agrees.

A clear 7-day trend still wins. Losses can break a tie, but they can't override the trend.

The NEUTRAL ladder (Case A with no baseline direction) — try in order, stop at the first rung that passes the capability gate, sizing, and the liquidation guard:

  1. TWO_SIDED — needs two_sided_allowed: YES, TWO_SIDED in the strategy envelope, ≥2 executor slots, and each leg viable on half the budget.
  2. Best single side — any available lean: baseline sub-lean, else 4h direction, else price vs. EMA20/EMA50.
  3. HOLD — no lean, nothing viable.

Two rules that shape everything

The hourly check can never veto a first entry. With no grid running, direction comes from the baseline alone and hourly only supplies range prices. If hourly returns HOLD or NOT ACTIONABLE, the agent still deploys and computes the prices itself from ATR. Hourly holds authority only over a grid that already exists — because range-bound markets are exactly what make hourly say HOLD, and those are a grid bot's best conditions.

Two grids require proof, not assumption. A grid_executor has no position_mode field and never sets one, so it silently inherits whatever the account has. On a one-way account, a second grid nets against the first instead of holding independently — the level math and the liquidation guard would both be computing a position that doesn't exist. So TWO_SIDED is gated on a live read, and anything unreadable is treated as one-way.

Routines — the deterministic math

Routines are plain Python that fetch data and compute numbers, so the same input always gives the same answer.

Routine When What it returns
baseline_7d startup, then daily 7d ATR, high/low range, trend direction and strength, where price sits against its averages, and 48h price slope — the first-entry compass
hourly_mtf_check every tick (~1h) 1h/4h/1d trend, ATR, volatility and range position → concrete start_price, end_price, limit_price plus the manage-the-running-grid signal
position_mode_check before any TWO_SIDED path, and on first entry account position mode, whether the account is flat, and the one line the agent acts on: two_sided_allowed: YES/NO

position_mode_check always resolves to HEDGE or ONEWAY — an unreadable or unrecognised response defaults to ONEWAY and says so on a mode_read: line. It's read-only: it never changes the mode, never places orders. Condor's MCP layer exposes only a setter for position mode, so the routine reads it straight from HAPI through the client routines already hold.

Skill — the gate before deployment

skills/liquidation_guard/SKILL.md runs after prices are known and before any executor is created. It stops on the first failure:

  1. Order size — do the levels divide the budget into orders clearing both the user's minimum and the exchange's?
  2. Worst-case position — assume every level fills; compute total size and average entry.
  3. Worst-case liquidation price — from average entry, leverage, and the exchange's maintenance margin tier.
  4. The check — liquidation must sit beyond limit_price, so your exit fires first.
  5. If it fails — drop leverage a step and recompute; then narrow the range; then HOLD and report why.

Strategies — two samples to start from

Each strategy is one market with a fixed budget and risk envelope. Both are small on purpose, so you can watch a real grid run without much at risk.

  • btc_usdt_adaptive_grid — BTC-USDT on Bitget, 60 USDT. One-sided only.
  • sol_usdt_adaptive_grid — SOL-USDT on Binance, 100 USDT. Can go two-sided when the account allows it.

Copy either one, change the pair and budget, and you have a new strategy. The two differ mainly in budget, which is what decides whether two-sided grids fit at all.

Validation

  • ruff check agents/adaptive_grid_trader — all checks passed
  • python -m compileall -q agents/adaptive_grid_trader — clean
  • position_mode_check exercised against a mock client across 15 cases: both real modes, five response-shape variants, zero-size positions, and both failure paths. Every unreadable or failing case resolves to ONEWAY / two_sided_allowed: NO.

@rapcmia
rapcmia marked this pull request as draft July 28, 2026 15:43
Reworks the adaptive grid trader around a two-layer decision system:
baseline_7d decides the first grid, hourly_mtf_check (1h/4h/1d) manages
the running one behind an anti-flip rule.

- add skills/liquidation_guard: pre-deploy gate covering order sizing,
  worst-case fill position, and liquidation-vs-limit_price check
- drop routines/order_size_validator; sizing is now step 0 of the gate
- add btc_usdt_adaptive_grid and sol_usdt_adaptive_grid strategies with
  fixed risk envelopes and per-tick procedures
- rewrite both routines for ATR-derived ranges and EMA trend detection

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@rapcmia
rapcmia marked this pull request as ready for review July 30, 2026 16:21
rapcmia and others added 2 commits July 31, 2026 14:17
A grid_executor has no position_mode field and never sets one, so it
inherits whatever the account has. On a ONEWAY account a second grid
nets against the first instead of holding independently, which makes the
level math and the liquidation guard compute a position that does not
exist.

- add routines/position_mode_check: read-only capability probe that
  resolves to HEDGE or ONEWAY and reports two_sided_allowed. Unreadable
  or unrecognised responses default to ONEWAY (fail-closed)
- gate the NEUTRAL ladder's TWO_SIDED rung on two_sided_allowed
- keep first entry baseline-driven; the hourly check can no longer veto it
- note in setup that the user sets position mode, not the agent

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
_trend_direction bails out below slow + 2 = 23 candles, but the routine
fetched only 20 daily candles. The daily timeframe therefore returned
NEUTRAL unconditionally — proven with a synthetic 2%-per-candle uptrend,
which reads NEUTRAL at 20 candles and BULLISH at 23.

Because a direction change requires both 4h and 1d to agree, flips were
unreachable: cases E and F never fired, case B always fell through to the
baseline, and confidence could never exceed LOW.

- fetch 60 candles for both 4h and 1d, with the 23-candle floor documented
- drop stale "proxy for 6h / 12h" comments left from the previous design
- add triple_barrier stop_loss (0.10) so a losing grid closes on PnL
  instead of waiting for limit_price or the time limit; note that it
  measures filled-position PnL, not budget

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@rapcmia rapcmia changed the title feat(agents): add adaptive grid trader feat / add adaptive grid trader agent Jul 31, 2026
The baseline sat on NEUTRAL through obvious moves because it only looked
at EMA separation, and a running grid had no feedback path at all — it
was kept or flipped purely on chart agreement.

baseline_7d:
- EMA separation threshold 0.3 -> 0.15 ATR
- add a price-action override: price on one side of both EMAs with a 48h
  slope past +/-1% reads directional even while EMAs are still crossing
- add a soft nudge at +/-0.25% for the remaining NEUTRAL cases
- retune strength bands to 0.15 / 0.8, and report price-vs-EMAs and slope

Layer 2, checked before keep/flip in priority order:
- stale grids that stopped filling are torn down and rebuilt on a fresh
  range
- grids at >=2% unrealized profit of trade budget are banked
- sustained worsening PnL counts as a confirming vote, substituting for
  one missing timeframe; it never overrides a clear opposite baseline

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@carlito-hummingbot

Copy link
Copy Markdown

Verified working with binance_perpetual (live deploy, place + cancel confirmed).

Note:
Grid deployed at 20x despite the strategy's max_leverage cap of 5x. Leverage guard isn't enforced.

- set leverage in the executor payload; omitted, it falls back to a
  default well above the envelope cap
- leverage comes from the envelope, not agent discretion; ask the user
  once at session start
- stale detection no longer requires a grid older than its time_limit,
  which the executor never allows to exist

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants