irl-sdk 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
irl_sdk-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,199 @@
1
+ Metadata-Version: 2.4
2
+ Name: irl-sdk
3
+ Version: 0.1.0
4
+ Summary: Python SDK for the IRL Engine — cryptographic pre-execution compliance rail
5
+ Author-email: MacroPulse <hello@macropulse.live>
6
+ License: MIT
7
+ Keywords: ai,compliance,crypto,fintech,trading
8
+ Classifier: Development Status :: 4 - Beta
9
+ Classifier: Intended Audience :: Developers
10
+ Classifier: License :: OSI Approved :: MIT License
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Programming Language :: Python :: 3.10
13
+ Classifier: Programming Language :: Python :: 3.11
14
+ Classifier: Programming Language :: Python :: 3.12
15
+ Requires-Python: >=3.10
16
+ Requires-Dist: cryptography>=42
17
+ Requires-Dist: httpx>=0.27
18
+ Provides-Extra: dev
19
+ Requires-Dist: pytest; extra == 'dev'
20
+ Requires-Dist: pytest-asyncio; extra == 'dev'
21
+ Requires-Dist: respx; extra == 'dev'
22
+ Description-Content-Type: text/markdown
23
+
24
+ # irl-sdk — Python SDK for the IRL Engine
25
+
26
+ Async Python client for the [IRL Engine](https://irl.macropulse.live) — the cryptographic
27
+ pre-execution compliance gateway for autonomous AI trading agents.
28
+
29
+ - Fetches a signed Layer 2 heartbeat from the MTA operator automatically
30
+ - Constructs and signs the authorize request
31
+ - Returns a sealed `trace_id` and `reasoning_hash` before any order reaches the exchange
32
+
33
+ ## Install
34
+
35
+ ```bash
36
+ pip install irl-sdk
37
+ ```
38
+
39
+ Requires Python 3.10+.
40
+
41
+ ## Quick Start
42
+
43
+ ```python
44
+ import asyncio
45
+ from irl_sdk import IRLClient, AuthorizeRequest, TradeAction, OrderType
46
+
47
+ IRL_URL = "https://irl.macropulse.live"
48
+ MTA_URL = "https://api.macropulse.live"
49
+ API_TOKEN = "your-irl-api-token"
50
+ AGENT_ID = "your-agent-uuid" # from POST /irl/agents
51
+ MODEL_HASH = "your-model-sha256-hex" # 64-char hex
52
+
53
+ async def main():
54
+ async with IRLClient(IRL_URL, API_TOKEN, MTA_URL) as client:
55
+ req = AuthorizeRequest(
56
+ agent_id=AGENT_ID,
57
+ model_id="my-model-v1",
58
+ model_hash_hex=MODEL_HASH,
59
+ action=TradeAction.LONG,
60
+ asset="BTC-USD",
61
+ order_type=OrderType.MARKET,
62
+ venue_id="coinbase",
63
+ quantity=0.1,
64
+ notional=6500.0,
65
+ notional_currency="USD",
66
+ )
67
+ result = await client.authorize(req)
68
+
69
+ if result.authorized:
70
+ print(f"AUTHORIZED trace_id={result.trace_id}")
71
+ print(f"reasoning_hash={result.reasoning_hash[:24]}...")
72
+ # embed trace_id in your exchange order metadata, then place the order
73
+ else:
74
+ print("HALTED — IRL blocked the trade")
75
+
76
+ asyncio.run(main())
77
+ ```
78
+
79
+ ## End-to-End Demo
80
+
81
+ The demo registers nothing — it uses a pre-seeded demo agent in the public sandbox:
82
+
83
+ ```bash
84
+ cd examples
85
+ pip install -e ..
86
+ python demo_e2e.py
87
+ ```
88
+
89
+ Expected output:
90
+ ```
91
+ IRL Engine : https://irl.macropulse.live
92
+ MTA : https://api.macropulse.live
93
+ Agent ID : 00000000-0000-4000-a000-000000000001
94
+
95
+ Fetching heartbeat and submitting authorize request...
96
+
97
+ AUTHORIZED
98
+ trace_id : <uuid>
99
+ reasoning_hash: <first 24 chars>...
100
+ shadow_blocked: False
101
+ ```
102
+
103
+ ## API Reference
104
+
105
+ ### `IRLClient(irl_url, api_token, mta_url)`
106
+
107
+ Async context manager. All parameters are positional.
108
+
109
+ | Parameter | Description |
110
+ |-----------|-------------|
111
+ | `irl_url` | IRL Engine base URL |
112
+ | `api_token` | Bearer token (from `IRL_API_TOKENS` env on the engine) |
113
+ | `mta_url` | MTA operator URL for heartbeat fetch. Pass empty string `""` when `LAYER2_ENABLED=false`. |
114
+
115
+ ### `client.authorize(req: AuthorizeRequest) → AuthorizeResult`
116
+
117
+ 1. Fetches a fresh signed heartbeat from `{mta_url}/v1/irl/heartbeat`
118
+ 2. POSTs to `{irl_url}/irl/authorize` with the heartbeat and request payload
119
+ 3. Returns `AuthorizeResult`
120
+
121
+ ### `AuthorizeRequest` fields
122
+
123
+ | Field | Type | Required | Notes |
124
+ |-------|------|----------|-------|
125
+ | `agent_id` | str | yes | UUID registered via `POST /irl/agents` |
126
+ | `model_id` | str | yes | Human-readable model name |
127
+ | `model_hash_hex` | str | yes | SHA-256 of model config (64 hex chars) |
128
+ | `action` | TradeAction | yes | `LONG` / `SHORT` / `NEUTRAL` |
129
+ | `asset` | str | yes | e.g. `"BTC-USD"`, `"SPY"` |
130
+ | `order_type` | OrderType | yes | `MARKET` / `LIMIT` / `STOP` / `TWAP` / `VWAP` |
131
+ | `venue_id` | str | yes | Exchange identifier |
132
+ | `quantity` | float | yes | Order size |
133
+ | `notional` | float | yes | Notional value |
134
+ | `notional_currency` | str | yes | e.g. `"USD"` |
135
+ | `client_order_id` | str | no | Your internal order reference |
136
+ | `agent_valid_time` | int | no | Model inference timestamp (ms). Defaults to now. |
137
+
138
+ ### `AuthorizeResult` fields
139
+
140
+ | Field | Type | Notes |
141
+ |-------|------|-------|
142
+ | `trace_id` | str | UUID — embed in exchange order metadata |
143
+ | `reasoning_hash` | str | SHA-256 seal of the full cognitive snapshot |
144
+ | `authorized` | bool | `True` = proceed; `False` = halted by policy |
145
+ | `shadow_blocked` | bool | `True` = would have been blocked; only set when `SHADOW_MODE=true` on the engine |
146
+
147
+ ### `TradeAction`
148
+
149
+ ```python
150
+ from irl_sdk import TradeAction
151
+
152
+ TradeAction.LONG # "Long"
153
+ TradeAction.SHORT # "Short"
154
+ TradeAction.NEUTRAL # "Neutral"
155
+ ```
156
+
157
+ ### `OrderType`
158
+
159
+ ```python
160
+ from irl_sdk import OrderType
161
+
162
+ OrderType.MARKET # "MARKET"
163
+ OrderType.LIMIT # "LIMIT"
164
+ OrderType.STOP # "STOP"
165
+ OrderType.TWAP # "TWAP"
166
+ OrderType.VWAP # "VWAP"
167
+ ```
168
+
169
+ ## Error Handling
170
+
171
+ The SDK raises `httpx.HTTPStatusError` on non-2xx responses. The response body
172
+ contains `{"error": "ERROR_CODE", "message": "..."}`. Common codes:
173
+
174
+ | Code | Meaning |
175
+ |------|---------|
176
+ | `HEARTBEAT_DRIFT_EXCEEDED` | Heartbeat too old — clock skew or slow network |
177
+ | `HEARTBEAT_MTA_REF_MISMATCH` | MTA hash mismatch — regime changed between heartbeat and authorize |
178
+ | `REGIME_VIOLATION` | Action not permitted in current regime |
179
+ | `NOTIONAL_EXCEEDS_LIMIT` | Exceeds agent notional cap × regime scale |
180
+ | `MODEL_HASH_MISMATCH` | Provided hash ≠ registered hash |
181
+ | `AGENT_NOT_FOUND` | Register the agent first via `POST /irl/agents` |
182
+
183
+ ## Layer 2 (Heartbeat) Details
184
+
185
+ When `LAYER2_ENABLED=true` on the engine (default), every authorize request must
186
+ carry a `SignedHeartbeat`. The SDK fetches this automatically from `{mta_url}/v1/irl/heartbeat`.
187
+
188
+ The heartbeat binds each request to a specific MTA broadcast:
189
+ - `sequence_id` — strictly monotone (anti-replay)
190
+ - `timestamp_ms` — must be within `MAX_HEARTBEAT_DRIFT_MS` of `txn_time`
191
+ - `mta_ref` — SHA-256 of the raw `/v1/regime/current` HTTP response body
192
+ - `signature` — Ed25519 signature by the MTA operator
193
+
194
+ For local dev with `LAYER2_ENABLED=false`, pass `mta_url=""`. The engine
195
+ substitutes a zero heartbeat internally.
196
+
197
+ ## License
198
+
199
+ MIT
@@ -0,0 +1,176 @@
1
+ # irl-sdk — Python SDK for the IRL Engine
2
+
3
+ Async Python client for the [IRL Engine](https://irl.macropulse.live) — the cryptographic
4
+ pre-execution compliance gateway for autonomous AI trading agents.
5
+
6
+ - Fetches a signed Layer 2 heartbeat from the MTA operator automatically
7
+ - Constructs and signs the authorize request
8
+ - Returns a sealed `trace_id` and `reasoning_hash` before any order reaches the exchange
9
+
10
+ ## Install
11
+
12
+ ```bash
13
+ pip install irl-sdk
14
+ ```
15
+
16
+ Requires Python 3.10+.
17
+
18
+ ## Quick Start
19
+
20
+ ```python
21
+ import asyncio
22
+ from irl_sdk import IRLClient, AuthorizeRequest, TradeAction, OrderType
23
+
24
+ IRL_URL = "https://irl.macropulse.live"
25
+ MTA_URL = "https://api.macropulse.live"
26
+ API_TOKEN = "your-irl-api-token"
27
+ AGENT_ID = "your-agent-uuid" # from POST /irl/agents
28
+ MODEL_HASH = "your-model-sha256-hex" # 64-char hex
29
+
30
+ async def main():
31
+ async with IRLClient(IRL_URL, API_TOKEN, MTA_URL) as client:
32
+ req = AuthorizeRequest(
33
+ agent_id=AGENT_ID,
34
+ model_id="my-model-v1",
35
+ model_hash_hex=MODEL_HASH,
36
+ action=TradeAction.LONG,
37
+ asset="BTC-USD",
38
+ order_type=OrderType.MARKET,
39
+ venue_id="coinbase",
40
+ quantity=0.1,
41
+ notional=6500.0,
42
+ notional_currency="USD",
43
+ )
44
+ result = await client.authorize(req)
45
+
46
+ if result.authorized:
47
+ print(f"AUTHORIZED trace_id={result.trace_id}")
48
+ print(f"reasoning_hash={result.reasoning_hash[:24]}...")
49
+ # embed trace_id in your exchange order metadata, then place the order
50
+ else:
51
+ print("HALTED — IRL blocked the trade")
52
+
53
+ asyncio.run(main())
54
+ ```
55
+
56
+ ## End-to-End Demo
57
+
58
+ The demo registers nothing — it uses a pre-seeded demo agent in the public sandbox:
59
+
60
+ ```bash
61
+ cd examples
62
+ pip install -e ..
63
+ python demo_e2e.py
64
+ ```
65
+
66
+ Expected output:
67
+ ```
68
+ IRL Engine : https://irl.macropulse.live
69
+ MTA : https://api.macropulse.live
70
+ Agent ID : 00000000-0000-4000-a000-000000000001
71
+
72
+ Fetching heartbeat and submitting authorize request...
73
+
74
+ AUTHORIZED
75
+ trace_id : <uuid>
76
+ reasoning_hash: <first 24 chars>...
77
+ shadow_blocked: False
78
+ ```
79
+
80
+ ## API Reference
81
+
82
+ ### `IRLClient(irl_url, api_token, mta_url)`
83
+
84
+ Async context manager. All parameters are positional.
85
+
86
+ | Parameter | Description |
87
+ |-----------|-------------|
88
+ | `irl_url` | IRL Engine base URL |
89
+ | `api_token` | Bearer token (from `IRL_API_TOKENS` env on the engine) |
90
+ | `mta_url` | MTA operator URL for heartbeat fetch. Pass empty string `""` when `LAYER2_ENABLED=false`. |
91
+
92
+ ### `client.authorize(req: AuthorizeRequest) → AuthorizeResult`
93
+
94
+ 1. Fetches a fresh signed heartbeat from `{mta_url}/v1/irl/heartbeat`
95
+ 2. POSTs to `{irl_url}/irl/authorize` with the heartbeat and request payload
96
+ 3. Returns `AuthorizeResult`
97
+
98
+ ### `AuthorizeRequest` fields
99
+
100
+ | Field | Type | Required | Notes |
101
+ |-------|------|----------|-------|
102
+ | `agent_id` | str | yes | UUID registered via `POST /irl/agents` |
103
+ | `model_id` | str | yes | Human-readable model name |
104
+ | `model_hash_hex` | str | yes | SHA-256 of model config (64 hex chars) |
105
+ | `action` | TradeAction | yes | `LONG` / `SHORT` / `NEUTRAL` |
106
+ | `asset` | str | yes | e.g. `"BTC-USD"`, `"SPY"` |
107
+ | `order_type` | OrderType | yes | `MARKET` / `LIMIT` / `STOP` / `TWAP` / `VWAP` |
108
+ | `venue_id` | str | yes | Exchange identifier |
109
+ | `quantity` | float | yes | Order size |
110
+ | `notional` | float | yes | Notional value |
111
+ | `notional_currency` | str | yes | e.g. `"USD"` |
112
+ | `client_order_id` | str | no | Your internal order reference |
113
+ | `agent_valid_time` | int | no | Model inference timestamp (ms). Defaults to now. |
114
+
115
+ ### `AuthorizeResult` fields
116
+
117
+ | Field | Type | Notes |
118
+ |-------|------|-------|
119
+ | `trace_id` | str | UUID — embed in exchange order metadata |
120
+ | `reasoning_hash` | str | SHA-256 seal of the full cognitive snapshot |
121
+ | `authorized` | bool | `True` = proceed; `False` = halted by policy |
122
+ | `shadow_blocked` | bool | `True` = would have been blocked; only set when `SHADOW_MODE=true` on the engine |
123
+
124
+ ### `TradeAction`
125
+
126
+ ```python
127
+ from irl_sdk import TradeAction
128
+
129
+ TradeAction.LONG # "Long"
130
+ TradeAction.SHORT # "Short"
131
+ TradeAction.NEUTRAL # "Neutral"
132
+ ```
133
+
134
+ ### `OrderType`
135
+
136
+ ```python
137
+ from irl_sdk import OrderType
138
+
139
+ OrderType.MARKET # "MARKET"
140
+ OrderType.LIMIT # "LIMIT"
141
+ OrderType.STOP # "STOP"
142
+ OrderType.TWAP # "TWAP"
143
+ OrderType.VWAP # "VWAP"
144
+ ```
145
+
146
+ ## Error Handling
147
+
148
+ The SDK raises `httpx.HTTPStatusError` on non-2xx responses. The response body
149
+ contains `{"error": "ERROR_CODE", "message": "..."}`. Common codes:
150
+
151
+ | Code | Meaning |
152
+ |------|---------|
153
+ | `HEARTBEAT_DRIFT_EXCEEDED` | Heartbeat too old — clock skew or slow network |
154
+ | `HEARTBEAT_MTA_REF_MISMATCH` | MTA hash mismatch — regime changed between heartbeat and authorize |
155
+ | `REGIME_VIOLATION` | Action not permitted in current regime |
156
+ | `NOTIONAL_EXCEEDS_LIMIT` | Exceeds agent notional cap × regime scale |
157
+ | `MODEL_HASH_MISMATCH` | Provided hash ≠ registered hash |
158
+ | `AGENT_NOT_FOUND` | Register the agent first via `POST /irl/agents` |
159
+
160
+ ## Layer 2 (Heartbeat) Details
161
+
162
+ When `LAYER2_ENABLED=true` on the engine (default), every authorize request must
163
+ carry a `SignedHeartbeat`. The SDK fetches this automatically from `{mta_url}/v1/irl/heartbeat`.
164
+
165
+ The heartbeat binds each request to a specific MTA broadcast:
166
+ - `sequence_id` — strictly monotone (anti-replay)
167
+ - `timestamp_ms` — must be within `MAX_HEARTBEAT_DRIFT_MS` of `txn_time`
168
+ - `mta_ref` — SHA-256 of the raw `/v1/regime/current` HTTP response body
169
+ - `signature` — Ed25519 signature by the MTA operator
170
+
171
+ For local dev with `LAYER2_ENABLED=false`, pass `mta_url=""`. The engine
172
+ substitutes a zero heartbeat internally.
173
+
174
+ ## License
175
+
176
+ MIT
@@ -0,0 +1,61 @@
1
+ """
2
+ IRL SDK — end-to-end demo.
3
+
4
+ Registers an agent in the IRL Engine MAR (if not already present) and
5
+ submits a test trade intent for authorization.
6
+
7
+ Usage:
8
+ pip install -e ..
9
+ python demo_e2e.py
10
+ """
11
+
12
+ import asyncio
13
+ import hashlib
14
+ import os
15
+
16
+ from irl_sdk import IRLClient, AuthorizeRequest, TradeAction, OrderType
17
+
18
+ IRL_URL = os.getenv("IRL_URL", "https://irl.macropulse.live")
19
+ MTA_URL = os.getenv("MTA_URL", "https://api.macropulse.live")
20
+ API_TOKEN = os.getenv("IRL_API_TOKEN", "demo-readonly-1a1bb53fb4bcb5f1ca2c2f48808a35ba")
21
+
22
+ # Stable demo agent — must be registered in the IRL Engine MAR.
23
+ AGENT_ID = "00000000-0000-4000-a000-000000000001" # demo-crypto-agent in MAR
24
+ MODEL_HASH = "cee60d4e409bd88b5e1998ef6ac078498491616a3a321bc76399b94784c4f283" # sha256(b"demo-crypto-agent-v1")
25
+
26
+
27
+ async def main() -> None:
28
+ print(f"IRL Engine : {IRL_URL}")
29
+ print(f"MTA : {MTA_URL}")
30
+ print(f"Agent ID : {AGENT_ID}")
31
+ print(f"Model hash : {MODEL_HASH[:16]}...")
32
+ print()
33
+
34
+ async with IRLClient(IRL_URL, API_TOKEN, MTA_URL) as client:
35
+ req = AuthorizeRequest(
36
+ agent_id=AGENT_ID,
37
+ model_id="demo-algo-v1",
38
+ model_hash_hex=MODEL_HASH,
39
+ action=TradeAction.LONG,
40
+ asset="BTC-USD",
41
+ order_type=OrderType.MARKET,
42
+ venue_id="coinbase",
43
+ quantity=0.01,
44
+ notional=650.0,
45
+ notional_currency="USD",
46
+ )
47
+
48
+ print("Fetching heartbeat and submitting authorize request...")
49
+ try:
50
+ result = await client.authorize(req)
51
+ print()
52
+ print("AUTHORIZED" if result.authorized else "HALTED")
53
+ print(f" trace_id : {result.trace_id}")
54
+ print(f" reasoning_hash: {result.reasoning_hash[:24]}...")
55
+ print(f" shadow_blocked: {result.shadow_blocked}")
56
+ except Exception as exc:
57
+ print(f"ERROR: {exc}")
58
+
59
+
60
+ if __name__ == "__main__":
61
+ asyncio.run(main())
@@ -0,0 +1,6 @@
1
+ """IRL SDK — Python client for the IRL Engine compliance rail."""
2
+
3
+ from .client import IRLClient
4
+ from .models import AuthorizeRequest, AuthorizeResult, TradeAction, OrderType
5
+
6
+ __all__ = ["IRLClient", "AuthorizeRequest", "AuthorizeResult", "TradeAction", "OrderType"]
@@ -0,0 +1,144 @@
1
+ """
2
+ IRLClient — send authorized trade intents to the IRL Engine.
3
+
4
+ Usage:
5
+ client = IRLClient(
6
+ irl_url="https://irl.macropulse.live",
7
+ api_token="your-token",
8
+ mta_url="https://api.macropulse.live",
9
+ )
10
+
11
+ result = await client.authorize(
12
+ AuthorizeRequest(
13
+ agent_id="550e8400-e29b-41d4-a716-446655440000",
14
+ model_id="my-algo-v1",
15
+ model_hash_hex="abc123...",
16
+ action=TradeAction.LONG,
17
+ asset="BTC-USD",
18
+ quantity=0.1,
19
+ notional=6500.0,
20
+ )
21
+ )
22
+
23
+ print(result.trace_id, result.authorized)
24
+ """
25
+
26
+ from __future__ import annotations
27
+
28
+ import time
29
+ from typing import Optional
30
+
31
+ import httpx
32
+
33
+ from .models import AuthorizeRequest, AuthorizeResult
34
+
35
+
36
+ class IRLClient:
37
+ """Async client for the IRL Engine /irl/authorize endpoint.
38
+
39
+ Fetches a fresh heartbeat from MacroPulse before each authorize call
40
+ and attaches it automatically. The heartbeat ensures L2 anti-replay
41
+ compliance — do not cache or reuse heartbeats across requests.
42
+ """
43
+
44
+ def __init__(
45
+ self,
46
+ irl_url: str,
47
+ api_token: str,
48
+ mta_url: str,
49
+ timeout: float = 5.0,
50
+ ) -> None:
51
+ self._irl_url = irl_url.rstrip("/")
52
+ self._mta_url = mta_url.rstrip("/")
53
+ self._headers = {"Authorization": f"Bearer {api_token}"}
54
+ self._http = httpx.AsyncClient(timeout=timeout)
55
+
56
+ async def authorize(self, req: AuthorizeRequest) -> AuthorizeResult:
57
+ """Fetch a fresh heartbeat and submit a trade intent for authorization.
58
+
59
+ Raises:
60
+ httpx.HTTPStatusError: on 4xx/5xx from IRL Engine
61
+ RuntimeError: if heartbeat fetch fails
62
+ """
63
+ hb = await self._fetch_heartbeat()
64
+
65
+ if req.agent_valid_time == 0:
66
+ req.agent_valid_time = int(time.time() * 1000)
67
+
68
+ body = self._build_body(req, hb)
69
+
70
+ resp = await self._http.post(
71
+ f"{self._irl_url}/irl/authorize",
72
+ json=body,
73
+ headers=self._headers,
74
+ )
75
+ resp.raise_for_status()
76
+ data = resp.json()
77
+
78
+ return AuthorizeResult(
79
+ trace_id=data["trace_id"],
80
+ reasoning_hash=data["reasoning_hash"],
81
+ authorized=data["authorized"],
82
+ shadow_blocked=data.get("shadow_blocked", False),
83
+ )
84
+
85
+ async def close(self) -> None:
86
+ await self._http.aclose()
87
+
88
+ async def __aenter__(self) -> "IRLClient":
89
+ return self
90
+
91
+ async def __aexit__(self, *_: object) -> None:
92
+ await self.close()
93
+
94
+ async def _fetch_heartbeat(self) -> dict:
95
+ resp = await self._http.get(f"{self._mta_url}/v1/irl/heartbeat")
96
+ if resp.status_code != 200:
97
+ raise RuntimeError(
98
+ f"Failed to fetch heartbeat: HTTP {resp.status_code} — {resp.text}"
99
+ )
100
+ return resp.json()
101
+
102
+ def _build_body(self, req: AuthorizeRequest, heartbeat: dict) -> dict:
103
+ # Match the AuthorizeRequest JSON schema expected by the IRL Engine.
104
+ # TradeAction variants are serialized as tagged JSON: {"Long": qty}
105
+ action_value: object
106
+ if req.action.value == "Long":
107
+ action_value = {"Long": req.quantity}
108
+ elif req.action.value == "Short":
109
+ action_value = {"Short": req.quantity}
110
+ elif req.action.value == "Neutral":
111
+ action_value = "Neutral"
112
+ else:
113
+ action_value = req.action.value
114
+
115
+ body: dict = {
116
+ "agent_id": req.agent_id,
117
+ "model_id": req.model_id,
118
+ "model_hash_hex": req.model_hash_hex,
119
+ "prompt_version": req.prompt_version,
120
+ "feature_schema_id": req.feature_schema_id,
121
+ "hyperparameter_checksum": req.hyperparameter_checksum,
122
+ "action": action_value,
123
+ "asset": req.asset,
124
+ "order_type": req.order_type.value,
125
+ "venue_id": req.venue_id,
126
+ "quantity": req.quantity,
127
+ "notional": req.notional,
128
+ "notional_currency": req.notional_currency,
129
+ "multiplier": req.multiplier,
130
+ "reduce_only": req.reduce_only,
131
+ "agent_valid_time": req.agent_valid_time,
132
+ "heartbeat": heartbeat,
133
+ }
134
+
135
+ body["client_order_id"] = req.client_order_id or ""
136
+
137
+ if req.limit_price is not None:
138
+ body["limit_price"] = req.limit_price
139
+ if req.stop_price is not None:
140
+ body["stop_price"] = req.stop_price
141
+ if req.regulatory is not None:
142
+ body["regulatory"] = req.regulatory
143
+
144
+ return body
@@ -0,0 +1,61 @@
1
+ """Data models matching the IRL Engine wire format."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from dataclasses import dataclass, field
6
+ from enum import Enum
7
+ from typing import Optional
8
+
9
+
10
+ class TradeAction(str, Enum):
11
+ LONG = "Long"
12
+ SHORT = "Short"
13
+ NEUTRAL = "Neutral"
14
+
15
+
16
+ class OrderType(str, Enum):
17
+ MARKET = "MARKET"
18
+ LIMIT = "LIMIT"
19
+ STOP = "STOP"
20
+ STOP_LIMIT = "STOP_LIMIT"
21
+ TWAP = "TWAP"
22
+
23
+
24
+ @dataclass
25
+ class AuthorizeRequest:
26
+ # Agent identity
27
+ agent_id: str # UUID string — must be registered in the MAR
28
+ model_id: str # Human-readable model identifier
29
+ model_hash_hex: str # SHA-256 of the agent's model weights (hex)
30
+ prompt_version: str = "v1"
31
+ feature_schema_id: str = "default"
32
+ hyperparameter_checksum: str = "0" * 64
33
+
34
+ # Trade intent
35
+ action: TradeAction = TradeAction.NEUTRAL
36
+ asset: str = ""
37
+ order_type: OrderType = OrderType.MARKET
38
+ venue_id: str = ""
39
+ quantity: float = 0.0
40
+ notional: float = 0.0
41
+ notional_currency: str = "USD"
42
+ multiplier: float = 1.0
43
+ limit_price: Optional[float] = None
44
+ stop_price: Optional[float] = None
45
+ client_order_id: Optional[str] = None
46
+ reduce_only: bool = False
47
+
48
+ # Temporal context
49
+ agent_valid_time: int = 0 # Unix ms of the agent's decision time
50
+
51
+ # Filled automatically by IRLClient.authorize()
52
+ heartbeat: Optional[dict] = None
53
+ regulatory: Optional[dict] = None
54
+
55
+
56
+ @dataclass
57
+ class AuthorizeResult:
58
+ trace_id: str
59
+ reasoning_hash: str
60
+ authorized: bool
61
+ shadow_blocked: bool
@@ -0,0 +1,34 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "irl-sdk"
7
+ version = "0.1.0"
8
+ description = "Python SDK for the IRL Engine — cryptographic pre-execution compliance rail"
9
+ readme = "README.md"
10
+ requires-python = ">=3.10"
11
+ license = {text = "MIT"}
12
+ authors = [
13
+ {name = "MacroPulse", email = "hello@macropulse.live"},
14
+ ]
15
+ keywords = ["trading", "compliance", "ai", "crypto", "fintech"]
16
+ classifiers = [
17
+ "Development Status :: 4 - Beta",
18
+ "Intended Audience :: Developers",
19
+ "License :: OSI Approved :: MIT License",
20
+ "Programming Language :: Python :: 3",
21
+ "Programming Language :: Python :: 3.10",
22
+ "Programming Language :: Python :: 3.11",
23
+ "Programming Language :: Python :: 3.12",
24
+ ]
25
+ dependencies = [
26
+ "httpx>=0.27",
27
+ "cryptography>=42",
28
+ ]
29
+
30
+ [project.optional-dependencies]
31
+ dev = ["pytest", "pytest-asyncio", "respx"]
32
+
33
+ [tool.hatch.build.targets.wheel]
34
+ packages = ["irl_sdk"]