Skip to content

Commit 4a90506

Browse files
jzbtc101claude
andcommitted
Add the Oreka desk's three read-only Orakle skills
oreka_desk, oreka_signal and oreka_preflight are maintained in the Oreka repository and copied here, so they version with the engine they wrap. This is a first install, not a refresh: Ainara had no copies of them at all. All three are read-only. None can place, cancel or modify an order, and oreka_signal does not expose dry_run as a parameter, so there is no value a model can pass to reach an order path. Live trading stays on Oreka's CLI. Ainara's own carry_engine.py, portfolio.py and executor_client.py are a separate pre-extraction implementation and are left untouched, which is why these files carry the oreka_ prefix. The skills return a clear "Oreka is not installed" result until Oreka is importable by Orakle. It is not installable into this venv today: dydx-v4-client 1.1.6 needs httpx>=0.27,<0.28 and solana 0.36.10 needs httpx>=0.28. See OREKA_SKILLS.md. Records the Oreka version and commit each copy was taken from, because version: "1.0" on every copy could not distinguish a fresh one from a stale one. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
1 parent 5fb4a01 commit 4a90506

7 files changed

Lines changed: 527 additions & 0 deletions

File tree

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
# The `oreka_*` skills are copies. Do not edit them here.
2+
3+
`oreka_desk`, `oreka_signal` and `oreka_preflight` are maintained in the **Oreka**
4+
repository at `integrations/ainara/`, so they version with the engine they wrap.
5+
The files in this directory are snapshots taken by `cp`.
6+
7+
| | |
8+
|---|---|
9+
| Source | `<oreka>/integrations/ainara/` |
10+
| Oreka version | `0.1.0` |
11+
| Oreka commit | `7e485b3` |
12+
| Copied on | 2026-08-27 |
13+
14+
The three `.py` files are **content-identical** to their source, so a diff
15+
against Oreka is the staleness check:
16+
17+
```bash
18+
diff --strip-trailing-cr <oreka>/integrations/ainara/oreka_desk.py oreka_desk.py
19+
```
20+
21+
Use `--strip-trailing-cr` (or `git diff --no-index`): git checks these out with
22+
CRLF endings on Windows while the Oreka working copy has LF, so a plain `diff`
23+
reports every line as changed and tells you nothing. The `.SKILL.md` files differ only by the provenance keys in their
24+
frontmatter (`version`, `oreka_version`, `oreka_commit`, `copied_on`), which
25+
exist because a bare `version: "1.0"` could not tell a fresh copy from an old
26+
one.
27+
28+
## Refreshing
29+
30+
```bash
31+
cp <oreka>/integrations/ainara/oreka_*.py ainara/orakle/skills/trading/
32+
cp <oreka>/integrations/ainara/oreka_*.SKILL.md ainara/orakle/skills/trading/
33+
```
34+
35+
Then re-apply the provenance keys above with the new commit, and reinstall Oreka
36+
into the environment Orakle runs in — the package changes, not just the skills.
37+
Diff before copying: a local edit here was made for a reason.
38+
39+
## They need Oreka importable by Orakle
40+
41+
If `import oreka` fails, each skill returns a clear `{"installed": false,
42+
"error": ...}` rather than a stack trace. That is deliberate — do not replace it
43+
with a silent fallback. See `<oreka>/integrations/ainara/README.md` §3 for why
44+
none of these can place an order, and §4 for the open import-versus-proxy
45+
question.
46+
47+
## Not related to `carry_engine.py` / `portfolio.py` / `executor_client.py`
48+
49+
Those are Ainara's own pre-extraction carry skills and are a separate, older
50+
implementation of the same strategy. They are untouched by this add-on, which is
51+
why these files carry the `oreka_` prefix. Whether they are retired is the
52+
owner's call.
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
---
2+
name: "trading_oreka_desk"
3+
version: "0.1.0+7e485b3"
4+
oreka_version: "0.1.0"
5+
oreka_commit: "7e485b3"
6+
copied_on: "2026-08-27"
7+
description: "Read the Oreka delta-neutral desk: open hedges across Hyperliquid and dYdX, hedge health, funding earned, closed round trips, predicted vs realized"
8+
category: "trading"
9+
---
10+
11+
# Oreka Desk
12+
13+
## Description
14+
15+
Reports the book of the **Oreka** delta-neutral funding-carry desk — a strategy
16+
that holds equal-and-opposite perpetual positions on Hyperliquid and dYdX v4,
17+
price-neutral by construction, and collects the funding *differential* between
18+
the two venues.
19+
20+
Three views. `status` is what is open right now, per coin, with both legs, hedge
21+
health, net delta, liquidation distance and the funding each leg pays or
22+
receives. `review` reconstructs closed round trips from venue history.
23+
`analytics` compares each recorded trade's predicted edge against what it
24+
realized — the view that answers whether the model is right, as distinct from
25+
whether the plumbing works.
26+
27+
**Read-only.** It uses no signing key and cannot place, cancel or modify an
28+
order. A failed read is reported as an error and never as an empty book, because
29+
"I could not see your positions" and "you have no positions" must not look alike.
30+
31+
## Trigger Conditions
32+
33+
Use when the user asks how their book, positions, hedges or carry desk are doing;
34+
what a position has earned; whether a hedge is balanced; how close a leg is to
35+
liquidation; or whether the strategy earned what it forecast.
36+
37+
Do not use it to open or close anything — it cannot.
38+
39+
## Parameters
40+
41+
| Name | Type | Required | Default | Description |
42+
|------|------|----------|---------|-------------|
43+
| action | Literal["status","review","analytics"] | no | "status" | Open now / closed round trips / predicted vs realized |
44+
| coin | str | no | "ALL" | Whole book by default. Pass one symbol only if the user named one |
45+
| lookback_days | float | no | derived | For `review`: how far back to reconstruct. Unset derives it from `expected_hold_days` (>= 90 days) |
46+
47+
## Returns
48+
49+
The portfolio's JSON report. For `status`: a `summary` (open count, worst health,
50+
net funding per day, combined unrealized PnL) and a `positions` list, each with
51+
both legs, `net_delta`, `health`, liquidation distance and funding economics.
52+
53+
A dYdX leg may report `liquidation_px: null` with the note *"not liquidatable by
54+
price alone (equity exceeds notional)"* — that is benign and means there is
55+
genuinely no liquidation price. A leg reporting liquidation **unknown** is not
56+
benign: it is unmonitored, and usually means two coins share a dYdX subaccount.
57+
58+
For `review`: a round trip with `status: "incomplete_window"` is NOT an open
59+
position. It means the lookback caught the trade's close but not its open, so it
60+
could not be reconstructed; both venues are flat. Say so, and offer a longer
61+
`lookback_days` rather than reporting a live position.
62+
63+
For `analytics`: check `data_quality_ok` on a trade and `summary.data_quality`
64+
before quoting any figure. A trade marked faulted either has numbers a
65+
delta-neutral hedge cannot produce, or was computed from fills that do not
66+
round-trip the position — which can look like a perfectly clean $0.00. Either
67+
way `benchmark` carries an `error` instead of a `verdict`: report the fault,
68+
never the number. `cross_check` says whether the same trades reconstructed
69+
independently from venue history agree.
70+
71+
On failure: `{"error": "..."}`.
72+
73+
## Configuration
74+
75+
Reads Oreka's own config (`OREKA_CONFIG`, else the platform default), not
76+
`ainara.yaml`. Oreka must be importable by Orakle; if it is not, the skill
77+
returns `{"installed": false, "error": "..."}`.
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
"""Orakle skill — read the Oreka desk's book.
2+
3+
Drops into `ainara/orakle/skills/trading/`. Read-only: it reports positions and
4+
history and cannot place, cancel or modify an order. See the README beside this
5+
file for why the add-on draws that line where it does.
6+
"""
7+
8+
import logging
9+
from typing import Annotated, Any, Dict, Literal, Optional
10+
11+
from ainara.framework.skill import Skill
12+
13+
logger = logging.getLogger(__name__)
14+
15+
16+
class TradingOrekaDesk(Skill):
17+
"""Live and historical view of the Oreka delta-neutral desk."""
18+
19+
matcher_info = (
20+
"Use this skill to report on the Oreka delta-neutral funding-carry desk:"
21+
" open hedged positions across Hyperliquid and dYdX, hedge health,"
22+
" liquidation distance, funding earned, closed round trips, and whether"
23+
" the strategy realized the edge it predicted. Read-only; it never places"
24+
" an order. Keywords: my book, my positions, delta neutral, carry desk,"
25+
" hedge health, funding earned, realized pnl, how is the desk doing."
26+
)
27+
28+
def __init__(self):
29+
super().__init__()
30+
self.name = "oreka_desk"
31+
self.logger = logging.getLogger(__name__)
32+
33+
def run(
34+
self,
35+
action: Annotated[
36+
Literal["status", "review", "analytics"],
37+
"'status' = positions open right now, with hedge health, liquidation"
38+
" distance and the funding each leg is paying or receiving."
39+
" 'review' = closed round trips reconstructed from venue history."
40+
" 'analytics' = each recorded trade's PREDICTED edge against what it"
41+
" actually realized, which is the one that answers whether the model"
42+
" is right rather than whether the plumbing works.",
43+
] = "status",
44+
coin: Annotated[
45+
str,
46+
"Which asset(s). Default 'ALL' — the whole book at once; use it"
47+
" whenever the user does not name one specific asset. Pass a single"
48+
" symbol (BTC, ETH, SOL, ...) ONLY when they explicitly ask about that"
49+
" one. Do NOT default to BTC.",
50+
] = "ALL",
51+
lookback_days: Annotated[
52+
Optional[float],
53+
"For 'review': how far back to reconstruct closed trades. Leave unset"
54+
" unless the user names a period - the default is derived from the"
55+
" strategy's own expected hold (at least 90 days), and a shorter"
56+
" window cannot see a completed trade at all.",
57+
] = None,
58+
) -> Dict[str, Any]:
59+
"""Report the desk's book. Places no orders and signs nothing."""
60+
try:
61+
from oreka.portfolio import TradingPortfolio
62+
except ImportError as e:
63+
return _not_installed(e)
64+
65+
try:
66+
return TradingPortfolio().run(
67+
action=action, coin=coin, lookback_days=lookback_days)
68+
except Exception as e:
69+
# A read that failed is reported as a failure. It must never come
70+
# back looking like an empty book, which reads as "you hold nothing".
71+
self.logger.warning("oreka_desk %s failed: %s", action, e)
72+
return {"error": f"could not read the desk: {type(e).__name__}: {e}",
73+
"action": action, "coin": coin}
74+
75+
76+
def _not_installed(exc):
77+
return {
78+
"error": "Oreka is not installed in this environment, so the desk cannot"
79+
" be read. Install it into the environment Orakle runs in"
80+
f" (pip install -e <oreka>). Import failed with: {exc}",
81+
"installed": False,
82+
}
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
---
2+
name: "trading_oreka_preflight"
3+
version: "0.1.0+7e485b3"
4+
oreka_version: "0.1.0"
5+
oreka_commit: "7e485b3"
6+
copied_on: "2026-08-27"
7+
description: "Preflight the Oreka desk: config, venue credentials verified at the venue, gates, size caps, daemon and watchdog liveness"
8+
category: "trading"
9+
---
10+
11+
# Oreka Preflight
12+
13+
## Description
14+
15+
Runs the **Oreka** desk's preflight and summarises it: which config file was
16+
actually resolved, whether each venue's credentials are accepted *by that venue*,
17+
which network each one is really on, the state of the dry-run and jurisdiction
18+
gates, whether both size caps are set, whether the dYdX authenticator's scope
19+
covers the coins configured, and whether the daemon and watchdog are alive.
20+
21+
Its guiding rule is worth knowing when reading the result: it prefers what the
22+
**running daemon** reports over what the YAML says. Services read their config
23+
once at startup, so a file edited afterwards is a file the daemon is not using.
24+
"I set it to mainnet" and "it is on mainnet" are different claims.
25+
26+
**Read-only.** Every check is a read; nothing here can place, cancel or modify an
27+
order.
28+
29+
## Trigger Conditions
30+
31+
Use when the user asks whether their desk is healthy, safe to run, or correctly
32+
configured; whether the watchdog is alive; whether an alarm is being raised; or
33+
after they have changed configuration and want to know it took effect.
34+
35+
## Parameters
36+
37+
| Name | Type | Required | Default | Description |
38+
|------|------|----------|---------|-------------|
39+
| verbose | bool | no | false | true returns every check; false returns only failures and warnings plus counts |
40+
41+
## Returns
42+
43+
```jsonc
44+
{
45+
"healthy": true, // false on ANY failure; null if preflight could not run
46+
"failures": 0,
47+
"warnings": 3,
48+
"summary": "no failures, 3 warning(s)",
49+
"needs_attention": [ { "check": "...", "status": "warn", "detail": "..." } ],
50+
"checks": null // the full list, when verbose
51+
}
52+
```
53+
54+
**Warnings are frequently intended.** Running on mainnet, a jurisdiction gate the
55+
operator opened on purpose, and `dry_run` set false all warn by design — they are
56+
states to be aware of, not faults. Report what they say rather than counting
57+
them. A failure is different and blocks running.
58+
59+
`healthy` is `null`, never `false`, when the preflight itself could not run — an
60+
unknown and a known-bad must not read alike.
61+
62+
## Configuration
63+
64+
Reads Oreka's own config, not `ainara.yaml`. Requires Oreka importable by Orakle;
65+
otherwise returns `{"installed": false, "healthy": null, "error": "..."}`.
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
"""Orakle skill — the Oreka desk's preflight.
2+
3+
Drops into `ainara/orakle/skills/trading/`. Every check is read-only; nothing
4+
here can place, cancel or modify an order.
5+
6+
Worth knowing when reading the result: preflight prefers what the RUNNING daemon
7+
reports over what the YAML says, because services read their config once at
8+
startup. "I set it to mainnet" and "it is on mainnet" are different claims, and
9+
the gap between them is what this catches.
10+
"""
11+
12+
import logging
13+
from typing import Annotated, Any, Dict
14+
15+
from ainara.framework.skill import Skill
16+
17+
logger = logging.getLogger(__name__)
18+
19+
20+
class TradingOrekaPreflight(Skill):
21+
"""Is the desk configured, credentialled and guarded the way you think?"""
22+
23+
matcher_info = (
24+
"Use this skill to check whether the Oreka delta-neutral desk is healthy"
25+
" and safe to run: config resolution, venue credentials verified at the"
26+
" venue, which network each venue is really on, the dry-run and"
27+
" jurisdiction gates, size caps, the daemon, and whether the watchdog is"
28+
" alive and raising anything. Read-only. Keywords: is my desk ok, desk"
29+
" health, preflight, doctor, is the watchdog running, am I safe to trade,"
30+
" check my trading setup."
31+
)
32+
33+
def __init__(self):
34+
super().__init__()
35+
self.name = "oreka_preflight"
36+
self.logger = logging.getLogger(__name__)
37+
38+
def run(
39+
self,
40+
verbose: Annotated[
41+
bool,
42+
"True returns every check. False (default) returns only failures and"
43+
" warnings plus the counts, which is what someone asking 'is it ok?'"
44+
" actually wants.",
45+
] = False,
46+
) -> Dict[str, Any]:
47+
"""Run the preflight and summarise it. Touches no order path."""
48+
try:
49+
from oreka import doctor
50+
except ImportError as e:
51+
return _not_installed(e)
52+
53+
try:
54+
checks = doctor.run_checks()
55+
except Exception as e:
56+
self.logger.warning("oreka_preflight failed: %s", e)
57+
return {"error": f"preflight could not run: {type(e).__name__}: {e}",
58+
"healthy": None}
59+
60+
rows = [{"check": c.name, "status": c.status, "detail": c.detail}
61+
for c in checks]
62+
failed = [r for r in rows if r["status"] == doctor.FAIL]
63+
warned = [r for r in rows if r["status"] == doctor.WARN]
64+
65+
# `healthy` is False on any failure, never None-as-false: an unknown and a
66+
# known-bad must not read alike to whatever renders this.
67+
return {
68+
"healthy": not failed,
69+
"failures": len(failed),
70+
"warnings": len(warned),
71+
"summary": (
72+
f"{len(failed)} failed, {len(warned)} warning(s)" if failed
73+
else f"no failures, {len(warned)} warning(s)" if warned
74+
else "all checks passed"),
75+
"needs_attention": failed + warned,
76+
"checks": rows if verbose else None,
77+
"note": ("Warnings are often states you intended - mainnet, a gate you"
78+
" opened on purpose. Read them rather than counting them."),
79+
}
80+
81+
82+
def _not_installed(exc):
83+
return {
84+
"error": "Oreka is not installed in this environment, so the preflight"
85+
" cannot run. Install it into the environment Orakle runs in"
86+
f" (pip install -e <oreka>). Import failed with: {exc}",
87+
"installed": False,
88+
"healthy": None,
89+
}

0 commit comments

Comments
 (0)