Skip to content

feat(options-gps): autonomous execution — CLI trades options on Deribit/Aevo (closes #26)#42

Merged
e35ventura merged 8 commits intoentrius:mainfrom
MkDev11:feature/issue-26-autonomous-execution
Mar 14, 2026
Merged

feat(options-gps): autonomous execution — CLI trades options on Deribit/Aevo (closes #26)#42
e35ventura merged 8 commits intoentrius:mainfrom
MkDev11:feature/issue-26-autonomous-execution

Conversation

@MkDev11
Copy link
Contributor

@MkDev11 MkDev11 commented Mar 13, 2026

Summary

Options GPS can now autonomously trade options on Deribit and Aevo directly from the CLI. This adds a full execution engine with order placement, order lifecycle management (place → monitor → cancel), slippage protection, position sizing, and safety guardrails — transforming the tool from analysis-only to end-to-end autonomous execution.

Before: CLI showed recommended venue/price per leg but could not place orders. No --execute or Screen 5.

After: --dry-run simulates execution (no credentials needed); --execute best (or safer/upside) submits live orders to Deribit/Aevo. Each leg is auto-routed to its best venue. Orders are monitored with configurable timeout, protected by slippage limits, and partially-filled multi-leg strategies are automatically unwound on failure.

Related Issues

Closes #26

Type of Change

  • Bug fix
  • Improvement to existing tool
  • Documentation
  • Other (describe below)

What Changed

executor.py — New execution engine (719 lines)

Data model:

  • OrderRequest / OrderResult / ExecutionPlan / ExecutionReport dataclasses
  • OrderResult tracks timestamp (ISO 8601), slippage_pct, and latency_ms per fill
  • ExecutionPlan supports max_slippage_pct, timeout_seconds, quantity_override
  • ExecutionReport includes started_at, finished_at, cancelled_orders

Executors (abstract BaseExecutor with full order lifecycle):

  • DryRunExecutor — simulates fills from quote data; tracks orders for status/cancel
  • DeribitExecutor — POST + JSON-RPC 2.0 with OAuth token caching, USD↔BTC price conversion, live order book pricing, tick-size alignment
  • AevoExecutor — REST with EIP-712 L2 order signing (Ethereum private key), instrument ID resolution via /markets, 6-decimal price/amount scaling

Order lifecycle: place_order()get_order_status()cancel_order() on all executors

Execution engine:

  • build_execution_plan() — auto-routes per leg via leg_divergences, builds exchange-specific instrument names (Deribit: BTC-27MAR26-71000-C, Aevo: BTC-71000-C), applies quantity override
  • validate_plan() — pre-flight checks (instruments, prices, quantities, actions)
  • execute_plan() — sequential execution with:
    • Order monitoring: polls get_order_status() until filled or timeout, then cancels
    • Slippage protection: rejects fills exceeding --max-slippage threshold
    • Auto-cancel on partial failure: cancels already-filled legs when a later leg fails
  • get_executor() factory — reads credentials from env vars, supports testnet

main.py — CLI integration

New flags:

Flag Purpose
--execute best|safer|upside Which strategy card to trade
--dry-run Simulate without placing real orders
--force Override no-trade guardrail for live execution
--exchange deribit|aevo Force all legs to one venue (default: auto-route)
--max-slippage N Max allowed slippage % per fill (0=off)
--quantity N Override contract quantity for all legs (0=default)
--timeout N Seconds to wait for order fill before cancel (0=fire-and-forget)
--screen none Skip analysis screens 1-4, show only execution (Screen 5)

Screen 5 (Execution): order plan display, live confirmation prompt, per-leg results with slippage/latency metrics, auto-cancelled orders, execution timestamps.

Decision log: execution block now includes started_at, finished_at, per-fill slippage_pct/latency_ms/timestamp, cancelled_orders, max_slippage_pct, timeout_seconds, quantity_override.

README.md — Updated documentation

  • Exchange integration architecture (Synth → pipeline → quotes → routing → execution → lifecycle → safety)
  • All CLI flags and env vars documented
  • Test coverage description updated

Testing

  • Tested against Synth API
  • Manually tested
  • Tests added/updated

Test breakdown

test_executor.py — 54 tests:

  • TestInstrumentNames (7) — Deribit/Aevo name builders, roundtrip parsing, edge cases
  • TestBuildPlan (6) — single/multi-leg, exchange override, aevo names, auto-route, estimated cost
  • TestValidatePlan (5) — valid plan, empty orders, zero price, zero quantity, empty instrument
  • TestDryRunExecutor (12) — authenticate, buy/sell fills, missing strike, no-match fallback, get_order_status, status not found, cancel_order, cancel not found, timestamp on result, slippage tracked
  • TestExecuteFlow (7) — single/multi-leg, net cost, auto-routing factory, summary message, report timestamps
  • TestGetExecutor (5) — dry-run, dry-run ignores exchange, missing creds (Deribit/Aevo), unknown exchange
  • TestSlippage (8) — buy worse/better, sell worse/better, zero expected, slippage protection rejects, slippage protection allows
  • TestQuantityOverride (3) — default quantity, override applied (single-leg), override applied (multi-leg)

test_executor_e2e.py — 8 tests:

  • Full pipeline → dry-run execution
  • Guardrail: refuse live when no-trade, allow with force, allow dry-run, allow when no guardrail
  • Multi-leg execution pipeline (spread → rank → build → dry-run → verify all legs + timestamps)
  • Non-crypto symbol skips execution (SPY returns no exchange quotes)
  • Slippage protection E2E (full pipeline with slippage guard → all pass)

Verify: python3 -m pytest tools/options-gps/tests/ -v178 passed (119 existing + 59 new).

Edge Cases Handled

# Scenario Behavior
E1 No API credentials get_executor raises with clear env var message
E2 Non-crypto symbol + execute Exit 1: "Execution only supported for crypto assets"
E3 No exchange quotes available Exit 1: "exchange data not available"
E4 Multi-leg partial failure Auto-cancel filled legs; report which were cancelled
E5 Instrument names per exchange Deribit: BTC-27MAR26-71000-C; Aevo: BTC-71000-C
E6 No-trade guardrail active Refuse live execution unless --force; dry-run always allowed
E7 Dry-run mode No HTTP calls to exchange; simulates from quote data
E8 Default CLI (no execute flags) Unchanged — analysis screens only
E9 Slippage exceeds threshold Order rejected + cancelled; partial fills unwound
E10 Order timeout Polls status; cancels if still open after deadline
E11 Negative sleep on monitor deadline Clamped to 0 — no ValueError crash
E12 Quantity override = 0 Uses strategy default leg quantities

Environment Variables (live execution)

Variable Purpose
DERIBIT_CLIENT_ID / DERIBIT_CLIENT_SECRET Deribit API credentials
DERIBIT_TESTNET=1 Use Deribit testnet
AEVO_API_KEY / AEVO_API_SECRET Aevo REST API credentials
AEVO_SIGNING_KEY Ethereum private key for EIP-712 order signing
AEVO_WALLET_ADDRESS Maker wallet address (Ethereum)
AEVO_TESTNET=1 Use Aevo testnet

Aevo L2 Signing — Live Testnet Verification

EIP-712 signing implementation verified end-to-end on Aevo testnet with real credentials.

Full order lifecycle test

Step Action Result
1 Resolve instrument ID (BTC-71000-P) 121777 via /markets fuzzy match
2 Place limit sell order (EIP-712 signed) opened — order_id 0x8c0a...3ace
3 Get order status opened — instrument BTC-15MAR26-71000-P
4 Cancel order True

Latency: 1108ms (testnet)

EIP-712 encoding verification

Domain separator, struct hash, and final digest verified byte-for-byte against the official Aevo Python SDK:

Component Match
Domain Separator
Order Struct Hash
Signable Bytes
Final Digest

Checklist

  • Code follows project style guidelines
  • Self-review completed
  • Changes are documented (if applicable)

Demo Video

https://screenrec.com/share/KnbYFEMVvT

…ibit/Aevo (closes entrius#26)

- executor.py: Deribit via POST+JSON-RPC, Aevo HMAC, DryRun, auto-route per leg
- main.py: --execute best|safer|upside, --dry-run, --force, --exchange; Screen 5; guardrail refusal
- README: exchange integration architecture, Execution section, env vars
- 37 tests: instrument names, plan build/validate, dry-run, factory, guardrail, E2E
@MkDev11 MkDev11 force-pushed the feature/issue-26-autonomous-execution branch from d87e1b3 to b1eafb4 Compare March 13, 2026 17:38
@eureka928
Copy link
Contributor

eureka928 commented Mar 13, 2026

Hi @MkDev11 please leave gps part to me, please do not copy my approach from previous commit, this can be penalized...

@MkDev11
Copy link
Contributor Author

MkDev11 commented Mar 13, 2026

@eureka928 I implemented the issue using my own approach, not yours. Also, this is a public issue, not a private one, so anyone is free to work on it and open their own PRs.

@MkDev11
Copy link
Contributor Author

MkDev11 commented Mar 13, 2026

don’t make my PR messy. my approach is my own, and it doesn’t mean I used yours. I already spent enough time working on this issue.

- Deribit: use contracts not amount for options order size
- Retry transient errors (timeout, 5xx) in _deribit_rpc and AevoExecutor
- Add execution.testnet to decision log from env
- DeribitExecutor: cache token, skip re-auth for multi-leg same exchange
- Remove unreachable code in _deribit_rpc
@eureka928
Copy link
Contributor

don’t make my PR messy. my approach is my own, and it doesn’t mean I used yours. I already spent enough time working on this issue.

Okay, but just don't copy others... just use your own judgement

MkDev11 added 2 commits March 13, 2026 20:15
…ride, bug fixes

- Add get_order_status/cancel_order to all executors (DryRun, Deribit, Aevo)
- Add execution timestamps, slippage %, latency ms to OrderResult
- Add slippage protection (--max-slippage): reject fills exceeding threshold
- Add quantity override (--quantity): override contract count for all legs
- Add order monitoring (--timeout): poll until filled or cancel on timeout
- Add auto-cancel of filled legs on partial multi-leg failure
- Fix Aevo HMAC signing to include HTTP method + path in signature
- Fix quantity_override not applied during plan build (was set post-build)
- Fix _monitor_order negative sleep ValueError on deadline overrun
- Enhanced Screen 5 output with slippage/latency/timestamps
- Enhanced decision log JSON with per-fill metrics and cancelled orders
- Add 21 new tests: lifecycle, slippage, timestamps, quantity override,
  multi-leg E2E, non-crypto skip, slippage E2E (177 total, all passing)
- Update README with new CLI flags and test coverage
… valid expiry

- DeribitExecutor.place_order: use 'amount' param (not 'contracts') for options
- Add _get_index_price: fetch/cache underlying price for USD→BTC conversion
- Add _get_book_price: fetch live order book ask/bid with mark_price fallback
- Snap prices to tick size (0.0005) to avoid Deribit tick conformance rejection
- Convert BTC fill prices back to USD for pipeline consistency
- Update mock data expiry from 2026-02-26 to 2026-03-27 (valid on testnet)
- Update deribit_BTC.json instrument names to match (27MAR26)
- Verified: live testnet order placement, monitoring, timeout+cancel all working
- 177 tests passing
@MkDev11
Copy link
Contributor Author

MkDev11 commented Mar 13, 2026

https://screenrec.com/share/xM8Ibk2COs
@e35ventura I updated the demo video. please review it and let me know your feedback.

MkDev11 added 2 commits March 13, 2026 21:17
… demo fills

- Update option_pricing/BTC.json: strikes 68000-74000 centered on $71,181
- Update exchange mock data with full strike coverage (old 65000-68500 + new 69000-74000)
- Scale prediction_percentiles BTC_24h/BTC_1h to center on new price
- Ensures: live testnet orders fill (near-ATM liquidity), guardrail triggers
  (fusion='unclear'), and all 177 tests pass
- Demo shows real 2-leg bull put credit spread filled on Deribit testnet
Addresses owner feedback: demo should focus on trade execution, not
unchanged analysis screens. Usage: --screen none --execute best
@MkDev11
Copy link
Contributor Author

MkDev11 commented Mar 13, 2026

13.03.2026_13.23.08_REC.mp4

@MkDev11
Copy link
Contributor Author

MkDev11 commented Mar 13, 2026

@e35ventura I just updated the demo video

@ventura-oss
Copy link

Testing live execution on Aevo currently fails with a 401 Unauthorized error. The implementation only uses the basic REST API key headers for authentication, but placing orders on Aevo requires Layer 2 (L2) cryptographic signing, which is completely missing from this execution engine.

- Replace broken HMAC-SHA256 header signing with proper EIP-712 typed
  data signing using eth_account (Ethereum private key)
- Add instrument ID resolution via /markets endpoint (Aevo uses numeric IDs)
- Add 6-decimal price/amount scaling per Aevo protocol
- Fix REST headers: AEVO-KEY + AEVO-SECRET (no HMAC timestamp/signature)
- New env vars: AEVO_SIGNING_KEY, AEVO_WALLET_ADDRESS
- Improve Deribit JSON-RPC error parsing (extract message from 400 responses)
- Remove unused hashlib/hmac imports
- 178 tests passing (+1 new test for L2 signing credential validation)
@MkDev11
Copy link
Contributor Author

MkDev11 commented Mar 14, 2026

Aevo Testnet Live Test Log

=== Step 1: Resolve instrument ===
BTC-71000-P -> id: 121777

=== Step 2: Place limit sell (far from market) ===
status:     opened
order_id:   0x8c0a427855d865c8f041a841ee4fd3ebbae8add7f680375cdf7abb3f46b93ace
error:      None
latency_ms: 1108

=== Step 3: Get order status ===
status: opened
instrument: BTC-15MAR26-71000-P

=== Step 4: Cancel order ===
cancelled: True

EIP-712 encoding verified byte-for-byte against Aevo Python SDK:

=== Domain Separator ===
SDK:  30c073629d8bf03b436295d4ded682a22940796d7206014ffb285f106019df8d
Ours: 30c073629d8bf03b436295d4ded682a22940796d7206014ffb285f106019df8d
Match: True

=== Order Struct Hash ===
SDK:  1d33e00349511ce0ecf9530d6510fe5e87ceaf48c7d568d2edebe101b5212535
Ours: 1d33e00349511ce0ecf9530d6510fe5e87ceaf48c7d568d2edebe101b5212535
Match: True

=== Signable bytes ===
Match: True

=== Final digest ===
Match: True

…quirements.txt

- DeribitExecutor.get_order_status now converts fill_price from BTC to
  USD via index price, matching place_order behavior
- Add eth-account, eth-abi, eth-hash to requirements.txt for Aevo
  EIP-712 signing to work out of the box
@e35ventura e35ventura merged commit 72bd8d1 into entrius:main Mar 14, 2026
1 check passed
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.

Options GPS: Autonomous Execution

4 participants