agentictrading 0.1.0__py3-none-any.whl
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.
- agentictrading/__init__.py +43 -0
- agentictrading/__main__.py +8 -0
- agentictrading/cli.py +80 -0
- agentictrading/client.py +286 -0
- agentictrading-0.1.0.dist-info/METADATA +187 -0
- agentictrading-0.1.0.dist-info/RECORD +10 -0
- agentictrading-0.1.0.dist-info/WHEEL +5 -0
- agentictrading-0.1.0.dist-info/entry_points.txt +2 -0
- agentictrading-0.1.0.dist-info/licenses/LICENSE +51 -0
- agentictrading-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"""Agentic Trading Lab - lightweight Python client.
|
|
2
|
+
|
|
3
|
+
Agentic Trading Lab is an open-source experimental playground for LLM-powered
|
|
4
|
+
trading agents: prototype agents, run backtests and paper-trading simulations,
|
|
5
|
+
inspect reasoning and decision logs, and benchmark against market baselines.
|
|
6
|
+
|
|
7
|
+
This package provides a small, dependency-free client for the Agentic Trading
|
|
8
|
+
Lab REST API so you can drive backtests and read results from Python.
|
|
9
|
+
|
|
10
|
+
Links:
|
|
11
|
+
- Live demo: https://agentic-trading-lab.vercel.app/
|
|
12
|
+
- Docs: https://finagent-orchestration.readthedocs.io/
|
|
13
|
+
- Source: https://github.com/Allan-Feng/AgenticTrading
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
from __future__ import annotations
|
|
17
|
+
|
|
18
|
+
from .client import AgenticTradingClient, ApiError
|
|
19
|
+
|
|
20
|
+
__all__ = [
|
|
21
|
+
"AgenticTradingClient",
|
|
22
|
+
"ApiError",
|
|
23
|
+
"__version__",
|
|
24
|
+
"info",
|
|
25
|
+
]
|
|
26
|
+
|
|
27
|
+
__version__ = "0.1.0"
|
|
28
|
+
|
|
29
|
+
LIVE_DEMO_URL = "https://agentic-trading-lab.vercel.app/"
|
|
30
|
+
DOCS_URL = "https://finagent-orchestration.readthedocs.io/"
|
|
31
|
+
SOURCE_URL = "https://github.com/Allan-Feng/AgenticTrading"
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def info() -> dict:
|
|
35
|
+
"""Return basic package/project metadata as a dict."""
|
|
36
|
+
return {
|
|
37
|
+
"name": "agentictrading",
|
|
38
|
+
"version": __version__,
|
|
39
|
+
"summary": "Lightweight Python client for the Agentic Trading Lab REST API.",
|
|
40
|
+
"live_demo": LIVE_DEMO_URL,
|
|
41
|
+
"docs": DOCS_URL,
|
|
42
|
+
"source": SOURCE_URL,
|
|
43
|
+
}
|
agentictrading/cli.py
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
"""Command-line interface for the ``agentictrading`` package.
|
|
2
|
+
|
|
3
|
+
Run ``agentictrading`` for project info, or a subcommand to hit a live API:
|
|
4
|
+
|
|
5
|
+
agentictrading # show project info + links
|
|
6
|
+
agentictrading health --api URL
|
|
7
|
+
agentictrading leaderboard --api URL
|
|
8
|
+
agentictrading ticker AAPL,NVDA --api URL
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
import argparse
|
|
14
|
+
import json
|
|
15
|
+
import sys
|
|
16
|
+
|
|
17
|
+
from . import DOCS_URL, LIVE_DEMO_URL, SOURCE_URL, __version__, info
|
|
18
|
+
from .client import DEFAULT_BASE_URL, AgenticTradingClient, ApiError
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def _print_info() -> int:
|
|
22
|
+
data = info()
|
|
23
|
+
print(f"agentictrading {data['version']}")
|
|
24
|
+
print(data["summary"])
|
|
25
|
+
print()
|
|
26
|
+
print(f" Live demo : {LIVE_DEMO_URL}")
|
|
27
|
+
print(f" Docs : {DOCS_URL}")
|
|
28
|
+
print(f" Source : {SOURCE_URL}")
|
|
29
|
+
print()
|
|
30
|
+
print("Quickstart:")
|
|
31
|
+
print(" from agentictrading import AgenticTradingClient")
|
|
32
|
+
print(f" client = AgenticTradingClient({DEFAULT_BASE_URL!r})")
|
|
33
|
+
print(" print(client.health())")
|
|
34
|
+
return 0
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def main(argv: list | None = None) -> int:
|
|
38
|
+
parser = argparse.ArgumentParser(
|
|
39
|
+
prog="agentictrading",
|
|
40
|
+
description="Lightweight client for the Agentic Trading Lab REST API.",
|
|
41
|
+
)
|
|
42
|
+
parser.add_argument("--version", action="version", version=f"agentictrading {__version__}")
|
|
43
|
+
|
|
44
|
+
# Shared option so each subcommand accepts --api after the command name.
|
|
45
|
+
common = argparse.ArgumentParser(add_help=False)
|
|
46
|
+
common.add_argument("--api", default=DEFAULT_BASE_URL, help="API base URL")
|
|
47
|
+
|
|
48
|
+
sub = parser.add_subparsers(dest="command")
|
|
49
|
+
sub.add_parser("info", help="Show project info and links (default)", parents=[common])
|
|
50
|
+
sub.add_parser("health", help="Check API server health", parents=[common])
|
|
51
|
+
sub.add_parser("leaderboard", help="Show the agent leaderboard", parents=[common])
|
|
52
|
+
p_ticker = sub.add_parser("ticker", help="Show latest quotes", parents=[common])
|
|
53
|
+
p_ticker.add_argument("symbols", nargs="?", default="AAPL,NVDA,MSFT,BTC")
|
|
54
|
+
|
|
55
|
+
args = parser.parse_args(argv)
|
|
56
|
+
|
|
57
|
+
if args.command in (None, "info"):
|
|
58
|
+
return _print_info()
|
|
59
|
+
|
|
60
|
+
client = AgenticTradingClient(args.api)
|
|
61
|
+
try:
|
|
62
|
+
if args.command == "health":
|
|
63
|
+
result = client.health()
|
|
64
|
+
elif args.command == "leaderboard":
|
|
65
|
+
result = client.leaderboard()
|
|
66
|
+
elif args.command == "ticker":
|
|
67
|
+
result = client.ticker(args.symbols)
|
|
68
|
+
else: # pragma: no cover - argparse guards this
|
|
69
|
+
parser.error(f"unknown command: {args.command}")
|
|
70
|
+
return 2
|
|
71
|
+
except ApiError as exc:
|
|
72
|
+
print(f"Error: {exc}", file=sys.stderr)
|
|
73
|
+
return 1
|
|
74
|
+
|
|
75
|
+
print(json.dumps(result, indent=2))
|
|
76
|
+
return 0
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
if __name__ == "__main__": # pragma: no cover
|
|
80
|
+
sys.exit(main())
|
agentictrading/client.py
ADDED
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
"""A small, dependency-free client for the Agentic Trading Lab REST API.
|
|
2
|
+
|
|
3
|
+
The client uses only the Python standard library (``urllib``) so that
|
|
4
|
+
``pip install agentictrading`` stays lightweight and has no transitive
|
|
5
|
+
dependencies. It covers the public read endpoints (health, runs, leaderboard,
|
|
6
|
+
ticker, paper trading) and the agent-facing backtest workflow
|
|
7
|
+
(``/api/v1/backtest/...``).
|
|
8
|
+
|
|
9
|
+
Example
|
|
10
|
+
-------
|
|
11
|
+
>>> from agentictrading import AgenticTradingClient
|
|
12
|
+
>>> client = AgenticTradingClient("https://agentictrading.onrender.com")
|
|
13
|
+
>>> client.health()
|
|
14
|
+
{'status': 'ok', ...}
|
|
15
|
+
|
|
16
|
+
Driving a backtest with your own strategy::
|
|
17
|
+
|
|
18
|
+
client = AgenticTradingClient(api_key="ag_xxx")
|
|
19
|
+
|
|
20
|
+
def strategy(snapshot):
|
|
21
|
+
# return a list of action dicts, e.g.
|
|
22
|
+
return [{"action": "hold", "symbol": "AAPL", "confidence": 0.5,
|
|
23
|
+
"reasoning": "no signal", "position_size": 0}]
|
|
24
|
+
|
|
25
|
+
result = client.run_backtest("2026-04-15", "2026-04-16", strategy,
|
|
26
|
+
agent_name="my-agent", model_name="rule-based")
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
from __future__ import annotations
|
|
30
|
+
|
|
31
|
+
import json
|
|
32
|
+
import time
|
|
33
|
+
import urllib.error
|
|
34
|
+
import urllib.parse
|
|
35
|
+
import urllib.request
|
|
36
|
+
from typing import Any, Callable, Optional
|
|
37
|
+
|
|
38
|
+
DEFAULT_BASE_URL = "https://agentictrading.onrender.com"
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class ApiError(RuntimeError):
|
|
42
|
+
"""Raised when the API returns a non-2xx response."""
|
|
43
|
+
|
|
44
|
+
def __init__(self, status: int, url: str, detail: Any):
|
|
45
|
+
self.status = status
|
|
46
|
+
self.url = url
|
|
47
|
+
self.detail = detail
|
|
48
|
+
super().__init__(f"HTTP {status} {url}: {detail}")
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
# Strategy callback: receives a market snapshot dict, returns a list of actions.
|
|
52
|
+
Strategy = Callable[[dict], list]
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
class AgenticTradingClient:
|
|
56
|
+
"""Thin HTTP client for an Agentic Trading Lab API server.
|
|
57
|
+
|
|
58
|
+
Parameters
|
|
59
|
+
----------
|
|
60
|
+
base_url:
|
|
61
|
+
Base URL of the API server (default: the hosted demo backend).
|
|
62
|
+
api_key:
|
|
63
|
+
Registered agent API key (``ag_...``). When provided, :meth:`resolve`
|
|
64
|
+
is used to obtain the session id automatically.
|
|
65
|
+
session_id:
|
|
66
|
+
Dashboard session id, sent as the ``X-Session-Id`` header. Optional if
|
|
67
|
+
``api_key`` is supplied.
|
|
68
|
+
timeout:
|
|
69
|
+
Default per-request timeout in seconds.
|
|
70
|
+
"""
|
|
71
|
+
|
|
72
|
+
def __init__(
|
|
73
|
+
self,
|
|
74
|
+
base_url: str = DEFAULT_BASE_URL,
|
|
75
|
+
api_key: Optional[str] = None,
|
|
76
|
+
session_id: Optional[str] = None,
|
|
77
|
+
timeout: int = 120,
|
|
78
|
+
):
|
|
79
|
+
self.base_url = base_url.rstrip("/")
|
|
80
|
+
self.api_key = api_key
|
|
81
|
+
self.session_id = session_id
|
|
82
|
+
self.timeout = timeout
|
|
83
|
+
|
|
84
|
+
# -- low-level ---------------------------------------------------------
|
|
85
|
+
|
|
86
|
+
def _request(
|
|
87
|
+
self,
|
|
88
|
+
method: str,
|
|
89
|
+
path: str,
|
|
90
|
+
body: Optional[dict] = None,
|
|
91
|
+
params: Optional[dict] = None,
|
|
92
|
+
timeout: Optional[int] = None,
|
|
93
|
+
) -> Any:
|
|
94
|
+
url = f"{self.base_url}{path}"
|
|
95
|
+
if params:
|
|
96
|
+
clean = {k: v for k, v in params.items() if v is not None}
|
|
97
|
+
if clean:
|
|
98
|
+
url = f"{url}?{urllib.parse.urlencode(clean)}"
|
|
99
|
+
|
|
100
|
+
headers = {"Accept": "application/json"}
|
|
101
|
+
if self.session_id:
|
|
102
|
+
headers["X-Session-Id"] = self.session_id
|
|
103
|
+
if self.api_key:
|
|
104
|
+
headers["X-API-Key"] = self.api_key
|
|
105
|
+
|
|
106
|
+
data = None
|
|
107
|
+
if body is not None:
|
|
108
|
+
data = json.dumps(body).encode("utf-8")
|
|
109
|
+
headers["Content-Type"] = "application/json"
|
|
110
|
+
|
|
111
|
+
req = urllib.request.Request(url, data=data, headers=headers, method=method)
|
|
112
|
+
try:
|
|
113
|
+
with urllib.request.urlopen(req, timeout=timeout or self.timeout) as resp:
|
|
114
|
+
raw = resp.read().decode("utf-8")
|
|
115
|
+
return json.loads(raw) if raw else None
|
|
116
|
+
except urllib.error.HTTPError as exc:
|
|
117
|
+
detail = exc.read().decode("utf-8", errors="replace")
|
|
118
|
+
try:
|
|
119
|
+
parsed = json.loads(detail)
|
|
120
|
+
detail = parsed.get("detail", parsed) if isinstance(parsed, dict) else parsed
|
|
121
|
+
except json.JSONDecodeError:
|
|
122
|
+
pass
|
|
123
|
+
raise ApiError(exc.code, url, detail) from exc
|
|
124
|
+
except TimeoutError as exc:
|
|
125
|
+
raise ApiError(0, url, f"request timed out after {timeout or self.timeout}s") from exc
|
|
126
|
+
|
|
127
|
+
# -- public read endpoints --------------------------------------------
|
|
128
|
+
|
|
129
|
+
def health(self) -> dict:
|
|
130
|
+
"""Return server health status."""
|
|
131
|
+
return self._request("GET", "/health")
|
|
132
|
+
|
|
133
|
+
def config_defaults(self) -> dict:
|
|
134
|
+
"""Return default run ids and date range configured on the server."""
|
|
135
|
+
return self._request("GET", "/config/defaults")
|
|
136
|
+
|
|
137
|
+
def ticker(self, symbols: str = "AAPL,NVDA,MSFT,BTC") -> dict:
|
|
138
|
+
"""Return latest quotes for a comma-separated list of symbols."""
|
|
139
|
+
return self._request("GET", "/ticker", params={"symbols": symbols})
|
|
140
|
+
|
|
141
|
+
def runs(self, mode: Optional[str] = None) -> list:
|
|
142
|
+
"""List stored backtest runs (optionally filtered by ``mode``)."""
|
|
143
|
+
return self._request("GET", "/runs", params={"mode": mode})
|
|
144
|
+
|
|
145
|
+
def run(self, run_id: str) -> dict:
|
|
146
|
+
"""Return metadata for a single run."""
|
|
147
|
+
return self._request("GET", f"/runs/{run_id}")
|
|
148
|
+
|
|
149
|
+
def equity(self, run_id: str) -> dict:
|
|
150
|
+
"""Return the equity curve for a run."""
|
|
151
|
+
return self._request("GET", f"/runs/{run_id}/equity")
|
|
152
|
+
|
|
153
|
+
def compare(self, run_ids: str) -> dict:
|
|
154
|
+
"""Compare equity curves for a comma-separated list of run ids."""
|
|
155
|
+
return self._request("GET", "/compare", params={"run_ids": run_ids})
|
|
156
|
+
|
|
157
|
+
def leaderboard(self) -> Any:
|
|
158
|
+
"""Return the agent leaderboard."""
|
|
159
|
+
return self._request("GET", "/api/v1/leaderboard")
|
|
160
|
+
|
|
161
|
+
def paper_account(self) -> dict:
|
|
162
|
+
"""Return the paper-trading account summary."""
|
|
163
|
+
return self._request("GET", "/paper/account")
|
|
164
|
+
|
|
165
|
+
def paper_positions(self) -> Any:
|
|
166
|
+
"""Return current paper-trading positions."""
|
|
167
|
+
return self._request("GET", "/paper/positions")
|
|
168
|
+
|
|
169
|
+
def paper_trades(self, limit: int = 50) -> Any:
|
|
170
|
+
"""Return recent paper-trading trades."""
|
|
171
|
+
return self._request("GET", "/paper/trades", params={"limit": limit})
|
|
172
|
+
|
|
173
|
+
# -- agent auth --------------------------------------------------------
|
|
174
|
+
|
|
175
|
+
def resolve(self) -> dict:
|
|
176
|
+
"""Resolve an agent API key into a session.
|
|
177
|
+
|
|
178
|
+
Requires ``api_key``. On success, ``self.session_id`` is populated.
|
|
179
|
+
"""
|
|
180
|
+
if not self.api_key:
|
|
181
|
+
raise ValueError("resolve() requires an api_key")
|
|
182
|
+
resolved = self._request("GET", "/api/v1/agents/resolve", timeout=60)
|
|
183
|
+
if isinstance(resolved, dict) and resolved.get("session_id"):
|
|
184
|
+
self.session_id = resolved["session_id"]
|
|
185
|
+
return resolved
|
|
186
|
+
|
|
187
|
+
# -- backtest workflow -------------------------------------------------
|
|
188
|
+
|
|
189
|
+
def backtest_schema(self) -> dict:
|
|
190
|
+
"""Return the decision schema and timeouts for external backtests."""
|
|
191
|
+
return self._request("GET", "/api/v1/backtest/schema")
|
|
192
|
+
|
|
193
|
+
def start_backtest(
|
|
194
|
+
self,
|
|
195
|
+
start_date: str,
|
|
196
|
+
end_date: str,
|
|
197
|
+
agent_name: str = "external-agent",
|
|
198
|
+
model_name: str = "custom",
|
|
199
|
+
mode: str = "safe_trading",
|
|
200
|
+
) -> dict:
|
|
201
|
+
"""Start a new external backtest and return its descriptor."""
|
|
202
|
+
return self._request(
|
|
203
|
+
"POST",
|
|
204
|
+
"/api/v1/backtest/start",
|
|
205
|
+
body={
|
|
206
|
+
"start_date": start_date,
|
|
207
|
+
"end_date": end_date,
|
|
208
|
+
"agent_name": agent_name,
|
|
209
|
+
"model_name": model_name,
|
|
210
|
+
"mode": mode,
|
|
211
|
+
},
|
|
212
|
+
timeout=60,
|
|
213
|
+
)
|
|
214
|
+
|
|
215
|
+
def current_step(self, backtest_id: str) -> dict:
|
|
216
|
+
"""Return the current step / market context for a running backtest."""
|
|
217
|
+
return self._request("GET", f"/api/v1/backtest/{backtest_id}/steps/current")
|
|
218
|
+
|
|
219
|
+
def submit_decisions(self, backtest_id: str, actions: list) -> dict:
|
|
220
|
+
"""Submit trading decisions for the current step."""
|
|
221
|
+
return self._request(
|
|
222
|
+
"POST",
|
|
223
|
+
f"/api/v1/backtest/{backtest_id}/steps/current/decisions",
|
|
224
|
+
body={"actions": actions},
|
|
225
|
+
)
|
|
226
|
+
|
|
227
|
+
def backtest_status(self, backtest_id: str) -> dict:
|
|
228
|
+
"""Return the status of a backtest."""
|
|
229
|
+
return self._request("GET", f"/api/v1/backtest/{backtest_id}/status")
|
|
230
|
+
|
|
231
|
+
def run_result(self, run_id: str) -> dict:
|
|
232
|
+
"""Return the full result (trades + decisions) for a finished run."""
|
|
233
|
+
return self._request("GET", f"/api/v1/backtest/runs/{run_id}/result")
|
|
234
|
+
|
|
235
|
+
# -- high-level convenience -------------------------------------------
|
|
236
|
+
|
|
237
|
+
def run_backtest(
|
|
238
|
+
self,
|
|
239
|
+
start_date: str,
|
|
240
|
+
end_date: str,
|
|
241
|
+
strategy: Strategy,
|
|
242
|
+
agent_name: str = "external-agent",
|
|
243
|
+
model_name: str = "custom",
|
|
244
|
+
mode: str = "safe_trading",
|
|
245
|
+
poll_interval: float = 2.0,
|
|
246
|
+
on_step: Optional[Callable[[dict], None]] = None,
|
|
247
|
+
) -> dict:
|
|
248
|
+
"""Run a full backtest loop, calling ``strategy`` at each step.
|
|
249
|
+
|
|
250
|
+
``strategy`` receives the per-step market snapshot (a dict) and must
|
|
251
|
+
return a list of action dicts. The loop submits those decisions, polls
|
|
252
|
+
until the backtest completes, and returns the final step context.
|
|
253
|
+
|
|
254
|
+
If ``api_key`` is set but no session yet, this resolves it first.
|
|
255
|
+
"""
|
|
256
|
+
if self.api_key and not self.session_id:
|
|
257
|
+
self.resolve()
|
|
258
|
+
|
|
259
|
+
started = self.start_backtest(start_date, end_date, agent_name, model_name, mode)
|
|
260
|
+
backtest_id = started["backtest_id"]
|
|
261
|
+
|
|
262
|
+
while True:
|
|
263
|
+
ctx = self.current_step(backtest_id)
|
|
264
|
+
status = ctx.get("status")
|
|
265
|
+
|
|
266
|
+
if status == "loading":
|
|
267
|
+
time.sleep(poll_interval)
|
|
268
|
+
continue
|
|
269
|
+
if status == "completed":
|
|
270
|
+
return ctx
|
|
271
|
+
if status == "failed":
|
|
272
|
+
raise ApiError(0, f"/api/v1/backtest/{backtest_id}", ctx.get("error", "backtest failed"))
|
|
273
|
+
if status != "waiting_decision":
|
|
274
|
+
time.sleep(min(poll_interval, 0.5))
|
|
275
|
+
continue
|
|
276
|
+
|
|
277
|
+
if on_step is not None:
|
|
278
|
+
on_step(ctx)
|
|
279
|
+
|
|
280
|
+
actions = strategy(ctx.get("market_snapshot") or {})
|
|
281
|
+
result = self.submit_decisions(backtest_id, actions)
|
|
282
|
+
if isinstance(result, dict) and result.get("status") == "completed":
|
|
283
|
+
return result
|
|
284
|
+
|
|
285
|
+
def __repr__(self) -> str: # pragma: no cover - cosmetic
|
|
286
|
+
return f"AgenticTradingClient(base_url={self.base_url!r})"
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: agentictrading
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Lightweight Python client for the Agentic Trading Lab REST API (LLM-powered trading agents, backtests, paper trading).
|
|
5
|
+
Author: SecureFinAI Lab
|
|
6
|
+
Author-email: Chunlin Feng <chunlinfeng0920@gmail.com>
|
|
7
|
+
License: OpenMDW License Agreement, version 1.0 (OpenMDW-1.0)
|
|
8
|
+
|
|
9
|
+
Copyright (c) SecureFinAI Lab
|
|
10
|
+
|
|
11
|
+
By exercising rights granted to you under this agreement, you accept and agree
|
|
12
|
+
to its terms.
|
|
13
|
+
|
|
14
|
+
As used in this agreement, "Model Materials" means the materials provided to
|
|
15
|
+
you under this agreement, consisting of: (1) one or more machine learning
|
|
16
|
+
models (including architecture and parameters); and (2) all related artifacts
|
|
17
|
+
(including associated data, documentation and software) that are provided to
|
|
18
|
+
you hereunder.
|
|
19
|
+
|
|
20
|
+
Subject to your compliance with this agreement, permission is hereby granted,
|
|
21
|
+
free of charge, to deal in the Model Materials without restriction, including
|
|
22
|
+
under all copyright, patent, database, and trade secret rights included or
|
|
23
|
+
embodied therein.
|
|
24
|
+
|
|
25
|
+
If you distribute any portion of the Model Materials, you shall retain in your
|
|
26
|
+
distribution (1) a copy of this agreement, and (2) all copyright notices and
|
|
27
|
+
other notices of origin included in the Model Materials that are applicable to
|
|
28
|
+
your distribution.
|
|
29
|
+
|
|
30
|
+
If you file, maintain, or voluntarily participate in a lawsuit against any
|
|
31
|
+
person or entity asserting that the Model Materials directly or indirectly
|
|
32
|
+
infringe any patent, then all rights and grants made to you hereunder are
|
|
33
|
+
terminated, unless that lawsuit was in response to a corresponding lawsuit
|
|
34
|
+
first brought against you.
|
|
35
|
+
|
|
36
|
+
This agreement does not impose any restrictions or obligations with respect to
|
|
37
|
+
any use, modification, or sharing of any outputs generated by using the Model
|
|
38
|
+
Materials.
|
|
39
|
+
|
|
40
|
+
THE MODEL MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
|
41
|
+
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
42
|
+
FITNESS FOR A PARTICULAR PURPOSE, TITLE, NONINFRINGEMENT, ACCURACY, OR THE
|
|
43
|
+
ABSENCE OF LATENT OR OTHER DEFECTS OR ERRORS, WHETHER OR NOT DISCOVERABLE, ALL
|
|
44
|
+
TO THE GREATEST EXTENT PERMISSIBLE UNDER APPLICABLE LAW.
|
|
45
|
+
|
|
46
|
+
YOU ARE SOLELY RESPONSIBLE FOR (1) CLEARING RIGHTS OF OTHER PERSONS THAT MAY
|
|
47
|
+
APPLY TO THE MODEL MATERIALS OR ANY USE THEREOF, INCLUDING WITHOUT LIMITATION
|
|
48
|
+
ANY PERSON'S COPYRIGHTS OR OTHER RIGHTS INCLUDED OR EMBODIED IN THE MODEL
|
|
49
|
+
MATERIALS; (2) OBTAINING ANY NECESSARY CONSENTS, PERMISSIONS OR OTHER RIGHTS
|
|
50
|
+
REQUIRED FOR ANY USE OF THE MODEL MATERIALS; OR (3) PERFORMING ANY DUE
|
|
51
|
+
DILIGENCE OR UNDERTAKING ANY OTHER INVESTIGATIONS INTO THE MODEL MATERIALS OR
|
|
52
|
+
ANYTHING INCORPORATED OR EMBODIED THEREIN.
|
|
53
|
+
|
|
54
|
+
IN NO EVENT SHALL THE PROVIDERS OF THE MODEL MATERIALS BE LIABLE FOR ANY CLAIM,
|
|
55
|
+
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
|
56
|
+
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE MODEL MATERIALS, THE
|
|
57
|
+
USE THEREOF OR OTHER DEALINGS THEREIN.
|
|
58
|
+
|
|
59
|
+
Project-URL: Homepage, https://agentic-trading-lab.vercel.app/
|
|
60
|
+
Project-URL: Documentation, https://finagent-orchestration.readthedocs.io/
|
|
61
|
+
Project-URL: Source, https://github.com/Allan-Feng/AgenticTrading
|
|
62
|
+
Project-URL: Issues, https://github.com/Allan-Feng/AgenticTrading/issues
|
|
63
|
+
Keywords: trading,agents,llm,backtesting,finance,quant,paper-trading,agentic
|
|
64
|
+
Classifier: Development Status :: 3 - Alpha
|
|
65
|
+
Classifier: Intended Audience :: Developers
|
|
66
|
+
Classifier: Intended Audience :: Financial and Insurance Industry
|
|
67
|
+
Classifier: Intended Audience :: Science/Research
|
|
68
|
+
Classifier: Operating System :: OS Independent
|
|
69
|
+
Classifier: Programming Language :: Python :: 3
|
|
70
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
71
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
72
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
73
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
74
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
75
|
+
Classifier: Topic :: Office/Business :: Financial :: Investment
|
|
76
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
77
|
+
Requires-Python: >=3.9
|
|
78
|
+
Description-Content-Type: text/markdown
|
|
79
|
+
License-File: LICENSE
|
|
80
|
+
Dynamic: license-file
|
|
81
|
+
|
|
82
|
+
# agentictrading
|
|
83
|
+
|
|
84
|
+
**Lightweight Python client for [Agentic Trading Lab](https://agentic-trading-lab.vercel.app/)** — an open-source experimental playground for LLM-powered trading agents.
|
|
85
|
+
|
|
86
|
+
Agentic Trading Lab lets you turn trading ideas into traceable experiments: prototype agents, run backtests and paper-trading simulations, inspect reasoning and decision logs, benchmark against market baselines, and study how agents behave under realistic financial constraints.
|
|
87
|
+
|
|
88
|
+
This package provides a small, **dependency-free** client (standard library only) for the Agentic Trading Lab REST API, so you can drive backtests and read results directly from Python.
|
|
89
|
+
|
|
90
|
+
- **Live demo:** https://agentic-trading-lab.vercel.app/
|
|
91
|
+
- **Docs:** https://finagent-orchestration.readthedocs.io/
|
|
92
|
+
- **Source:** https://github.com/Allan-Feng/AgenticTrading
|
|
93
|
+
|
|
94
|
+
> **Status:** early release (`0.1.0`). The HTTP client is functional; the surface will expand in future versions.
|
|
95
|
+
|
|
96
|
+
## Install
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
pip install agentictrading
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## Quickstart
|
|
103
|
+
|
|
104
|
+
```python
|
|
105
|
+
from agentictrading import AgenticTradingClient
|
|
106
|
+
|
|
107
|
+
client = AgenticTradingClient("https://agentictrading.onrender.com")
|
|
108
|
+
|
|
109
|
+
print(client.health())
|
|
110
|
+
print(client.leaderboard())
|
|
111
|
+
print(client.ticker("AAPL,NVDA,MSFT,BTC"))
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### Run a backtest with your own strategy
|
|
115
|
+
|
|
116
|
+
Register an agent on the dashboard (My Agents) to get an API key, then:
|
|
117
|
+
|
|
118
|
+
```python
|
|
119
|
+
from agentictrading import AgenticTradingClient
|
|
120
|
+
|
|
121
|
+
client = AgenticTradingClient(
|
|
122
|
+
base_url="https://agentictrading.onrender.com",
|
|
123
|
+
api_key="ag_xxxxxxxx",
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
def strategy(snapshot: dict) -> list:
|
|
127
|
+
"""Return a list of action dicts for the current hour."""
|
|
128
|
+
actions = []
|
|
129
|
+
for symbol, sig in (snapshot.get("top_signals") or {}).items():
|
|
130
|
+
rsi = float(sig.get("rsi") or 50)
|
|
131
|
+
price = float(sig.get("price") or 0)
|
|
132
|
+
if price > 0 and rsi < 35:
|
|
133
|
+
actions.append({
|
|
134
|
+
"action": "buy",
|
|
135
|
+
"symbol": symbol,
|
|
136
|
+
"confidence": 0.75,
|
|
137
|
+
"reasoning": "RSI oversold entry",
|
|
138
|
+
"position_size": max(1, int(2000 / price)),
|
|
139
|
+
})
|
|
140
|
+
if not actions:
|
|
141
|
+
actions.append({"action": "hold", "symbol": "AAPL",
|
|
142
|
+
"confidence": 0.5, "reasoning": "no signal", "position_size": 0})
|
|
143
|
+
return actions
|
|
144
|
+
|
|
145
|
+
result = client.run_backtest(
|
|
146
|
+
start_date="2026-04-15",
|
|
147
|
+
end_date="2026-04-16",
|
|
148
|
+
strategy=strategy,
|
|
149
|
+
agent_name="my-agent",
|
|
150
|
+
model_name="rule-based",
|
|
151
|
+
)
|
|
152
|
+
print(result)
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
## Command line
|
|
156
|
+
|
|
157
|
+
```bash
|
|
158
|
+
agentictrading # project info + links
|
|
159
|
+
agentictrading health --api https://... # API health check
|
|
160
|
+
agentictrading leaderboard --api https://... # agent leaderboard
|
|
161
|
+
agentictrading ticker AAPL,NVDA --api https://... # latest quotes
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
## API surface
|
|
165
|
+
|
|
166
|
+
| Method | Endpoint |
|
|
167
|
+
| --- | --- |
|
|
168
|
+
| `health()` | `GET /health` |
|
|
169
|
+
| `config_defaults()` | `GET /config/defaults` |
|
|
170
|
+
| `ticker(symbols)` | `GET /ticker` |
|
|
171
|
+
| `runs(mode=None)` | `GET /runs` |
|
|
172
|
+
| `run(run_id)` | `GET /runs/{id}` |
|
|
173
|
+
| `equity(run_id)` | `GET /runs/{id}/equity` |
|
|
174
|
+
| `compare(run_ids)` | `GET /compare` |
|
|
175
|
+
| `leaderboard()` | `GET /api/v1/leaderboard` |
|
|
176
|
+
| `paper_account()` / `paper_positions()` / `paper_trades()` | `GET /paper/...` |
|
|
177
|
+
| `resolve()` | `GET /api/v1/agents/resolve` |
|
|
178
|
+
| `backtest_schema()` | `GET /api/v1/backtest/schema` |
|
|
179
|
+
| `start_backtest(...)` | `POST /api/v1/backtest/start` |
|
|
180
|
+
| `current_step(id)` | `GET /api/v1/backtest/{id}/steps/current` |
|
|
181
|
+
| `submit_decisions(id, actions)` | `POST /api/v1/backtest/{id}/steps/current/decisions` |
|
|
182
|
+
| `run_result(run_id)` | `GET /api/v1/backtest/runs/{id}/result` |
|
|
183
|
+
| `run_backtest(...)` | full loop helper |
|
|
184
|
+
|
|
185
|
+
## License
|
|
186
|
+
|
|
187
|
+
OpenMDW-1.0 — see [LICENSE](LICENSE). Copyright (c) SecureFinAI Lab.
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
agentictrading/__init__.py,sha256=lzDRgutk7Xag3QxixD_sEWHJYOL3xg72m6JXNcUqybk,1349
|
|
2
|
+
agentictrading/__main__.py,sha256=40c3JGxDOKeEEdfKw5o6A8E_rhK9fFv8AYlmd-ngtcI,119
|
|
3
|
+
agentictrading/cli.py,sha256=4lO-RkwHT9u_XGON_rq0fyfH95JaJfYzr4kAlK_eLpQ,2765
|
|
4
|
+
agentictrading/client.py,sha256=9tBHwVSmbFIFul0B6vbVj1Ih0P1heqfzmbRQ-pM64UU,10501
|
|
5
|
+
agentictrading-0.1.0.dist-info/licenses/LICENSE,sha256=pKSAEET7IPvPfh6nPM_Bqbnv6uwxD7ktEffPNAKtwLY,2637
|
|
6
|
+
agentictrading-0.1.0.dist-info/METADATA,sha256=P4iBSENwSja0d4pPsR5UpWUCfEsEo-4oAj4hesPa36o,8221
|
|
7
|
+
agentictrading-0.1.0.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
|
|
8
|
+
agentictrading-0.1.0.dist-info/entry_points.txt,sha256=ZWCNNCXu7-CuPg_JZuxlW6f4zQ7wFY-Usj3lIPv7cUM,59
|
|
9
|
+
agentictrading-0.1.0.dist-info/top_level.txt,sha256=hHc6j4ouLQLBUj4zs8qpQxLULW2pTgFDoJ6m6CjpDsI,15
|
|
10
|
+
agentictrading-0.1.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
OpenMDW License Agreement, version 1.0 (OpenMDW-1.0)
|
|
2
|
+
|
|
3
|
+
Copyright (c) SecureFinAI Lab
|
|
4
|
+
|
|
5
|
+
By exercising rights granted to you under this agreement, you accept and agree
|
|
6
|
+
to its terms.
|
|
7
|
+
|
|
8
|
+
As used in this agreement, "Model Materials" means the materials provided to
|
|
9
|
+
you under this agreement, consisting of: (1) one or more machine learning
|
|
10
|
+
models (including architecture and parameters); and (2) all related artifacts
|
|
11
|
+
(including associated data, documentation and software) that are provided to
|
|
12
|
+
you hereunder.
|
|
13
|
+
|
|
14
|
+
Subject to your compliance with this agreement, permission is hereby granted,
|
|
15
|
+
free of charge, to deal in the Model Materials without restriction, including
|
|
16
|
+
under all copyright, patent, database, and trade secret rights included or
|
|
17
|
+
embodied therein.
|
|
18
|
+
|
|
19
|
+
If you distribute any portion of the Model Materials, you shall retain in your
|
|
20
|
+
distribution (1) a copy of this agreement, and (2) all copyright notices and
|
|
21
|
+
other notices of origin included in the Model Materials that are applicable to
|
|
22
|
+
your distribution.
|
|
23
|
+
|
|
24
|
+
If you file, maintain, or voluntarily participate in a lawsuit against any
|
|
25
|
+
person or entity asserting that the Model Materials directly or indirectly
|
|
26
|
+
infringe any patent, then all rights and grants made to you hereunder are
|
|
27
|
+
terminated, unless that lawsuit was in response to a corresponding lawsuit
|
|
28
|
+
first brought against you.
|
|
29
|
+
|
|
30
|
+
This agreement does not impose any restrictions or obligations with respect to
|
|
31
|
+
any use, modification, or sharing of any outputs generated by using the Model
|
|
32
|
+
Materials.
|
|
33
|
+
|
|
34
|
+
THE MODEL MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
|
35
|
+
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
36
|
+
FITNESS FOR A PARTICULAR PURPOSE, TITLE, NONINFRINGEMENT, ACCURACY, OR THE
|
|
37
|
+
ABSENCE OF LATENT OR OTHER DEFECTS OR ERRORS, WHETHER OR NOT DISCOVERABLE, ALL
|
|
38
|
+
TO THE GREATEST EXTENT PERMISSIBLE UNDER APPLICABLE LAW.
|
|
39
|
+
|
|
40
|
+
YOU ARE SOLELY RESPONSIBLE FOR (1) CLEARING RIGHTS OF OTHER PERSONS THAT MAY
|
|
41
|
+
APPLY TO THE MODEL MATERIALS OR ANY USE THEREOF, INCLUDING WITHOUT LIMITATION
|
|
42
|
+
ANY PERSON'S COPYRIGHTS OR OTHER RIGHTS INCLUDED OR EMBODIED IN THE MODEL
|
|
43
|
+
MATERIALS; (2) OBTAINING ANY NECESSARY CONSENTS, PERMISSIONS OR OTHER RIGHTS
|
|
44
|
+
REQUIRED FOR ANY USE OF THE MODEL MATERIALS; OR (3) PERFORMING ANY DUE
|
|
45
|
+
DILIGENCE OR UNDERTAKING ANY OTHER INVESTIGATIONS INTO THE MODEL MATERIALS OR
|
|
46
|
+
ANYTHING INCORPORATED OR EMBODIED THEREIN.
|
|
47
|
+
|
|
48
|
+
IN NO EVENT SHALL THE PROVIDERS OF THE MODEL MATERIALS BE LIABLE FOR ANY CLAIM,
|
|
49
|
+
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
|
50
|
+
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE MODEL MATERIALS, THE
|
|
51
|
+
USE THEREOF OR OTHER DEALINGS THEREIN.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
agentictrading
|