simudyne-pulse 0.7.0.dev16__tar.gz → 0.7.0.dev17__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.
- {simudyne_pulse-0.7.0.dev16/src/simudyne_pulse.egg-info → simudyne_pulse-0.7.0.dev17}/PKG-INFO +1 -1
- {simudyne_pulse-0.7.0.dev16 → simudyne_pulse-0.7.0.dev17}/pyproject.toml +1 -1
- {simudyne_pulse-0.7.0.dev16 → simudyne_pulse-0.7.0.dev17}/src/simudyne/client.py +2 -1
- simudyne_pulse-0.7.0.dev17/src/simudyne/resources/data.py +348 -0
- simudyne_pulse-0.7.0.dev17/src/simudyne/resources/fm.py +149 -0
- {simudyne_pulse-0.7.0.dev16 → simudyne_pulse-0.7.0.dev17}/src/simudyne/resources/simulation.py +190 -209
- {simudyne_pulse-0.7.0.dev16 → simudyne_pulse-0.7.0.dev17/src/simudyne_pulse.egg-info}/PKG-INFO +1 -1
- {simudyne_pulse-0.7.0.dev16 → simudyne_pulse-0.7.0.dev17}/tests/test_new_resources.py +88 -24
- simudyne_pulse-0.7.0.dev16/src/simudyne/resources/data.py +0 -109
- simudyne_pulse-0.7.0.dev16/src/simudyne/resources/fm.py +0 -477
- {simudyne_pulse-0.7.0.dev16 → simudyne_pulse-0.7.0.dev17}/LICENSE +0 -0
- {simudyne_pulse-0.7.0.dev16 → simudyne_pulse-0.7.0.dev17}/README.md +0 -0
- {simudyne_pulse-0.7.0.dev16 → simudyne_pulse-0.7.0.dev17}/setup.cfg +0 -0
- {simudyne_pulse-0.7.0.dev16 → simudyne_pulse-0.7.0.dev17}/src/simudyne/__init__.py +0 -0
- {simudyne_pulse-0.7.0.dev16 → simudyne_pulse-0.7.0.dev17}/src/simudyne/exceptions.py +0 -0
- {simudyne_pulse-0.7.0.dev16 → simudyne_pulse-0.7.0.dev17}/src/simudyne/resources/__init__.py +0 -0
- {simudyne_pulse-0.7.0.dev16 → simudyne_pulse-0.7.0.dev17}/src/simudyne/resources/api_keys.py +0 -0
- {simudyne_pulse-0.7.0.dev16 → simudyne_pulse-0.7.0.dev17}/src/simudyne/resources/fix.py +0 -0
- {simudyne_pulse-0.7.0.dev16 → simudyne_pulse-0.7.0.dev17}/src/simudyne/resources/profile.py +0 -0
- {simudyne_pulse-0.7.0.dev16 → simudyne_pulse-0.7.0.dev17}/src/simudyne/resources/simulator_gym.py +0 -0
- {simudyne_pulse-0.7.0.dev16 → simudyne_pulse-0.7.0.dev17}/src/simudyne/resources/validation.py +0 -0
- {simudyne_pulse-0.7.0.dev16 → simudyne_pulse-0.7.0.dev17}/src/simudyne_pulse.egg-info/SOURCES.txt +0 -0
- {simudyne_pulse-0.7.0.dev16 → simudyne_pulse-0.7.0.dev17}/src/simudyne_pulse.egg-info/dependency_links.txt +0 -0
- {simudyne_pulse-0.7.0.dev16 → simudyne_pulse-0.7.0.dev17}/src/simudyne_pulse.egg-info/requires.txt +0 -0
- {simudyne_pulse-0.7.0.dev16 → simudyne_pulse-0.7.0.dev17}/src/simudyne_pulse.egg-info/top_level.txt +0 -0
- {simudyne_pulse-0.7.0.dev16 → simudyne_pulse-0.7.0.dev17}/tests/test_simulator_gym.py +0 -0
- {simudyne_pulse-0.7.0.dev16 → simudyne_pulse-0.7.0.dev17}/tests/test_validation.py +0 -0
|
@@ -45,7 +45,8 @@ class PulseABM:
|
|
|
45
45
|
api_keys : ApiKeysResource
|
|
46
46
|
Create, list and revoke API keys.
|
|
47
47
|
data : DataResource
|
|
48
|
-
The
|
|
48
|
+
The market data catalogue: what exists, what is calibrated,
|
|
49
|
+
and calibrating a day.
|
|
49
50
|
simulation : SimulationResource
|
|
50
51
|
Submit agent-based simulations and retrieve their results.
|
|
51
52
|
validation : ValidationResource
|
|
@@ -0,0 +1,348 @@
|
|
|
1
|
+
"""Market data: what exists, what the agent-based model can simulate, and the
|
|
2
|
+
one action that moves a symbol-day from the first to the second.
|
|
3
|
+
|
|
4
|
+
Reached as ``client.data``. The three calls are one story:
|
|
5
|
+
|
|
6
|
+
available_data() every symbol-day in the registry
|
|
7
|
+
calibrate() calibrate one of them
|
|
8
|
+
calibrated_data() the ones the ABM can now simulate
|
|
9
|
+
|
|
10
|
+
A foundation-model run is prompted from anything in ``available_data``; an
|
|
11
|
+
agent-based run needs the day to appear in ``calibrated_data`` first.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
AVAILABLE_DATA_PATH = "/data/available-data"
|
|
15
|
+
CALIBRATED_DATA_PATH = "/data/calibrated-data"
|
|
16
|
+
CALIBRATE_PATH = "/data/calibrate"
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class DataResource:
|
|
20
|
+
"""Browse the market data catalogue and calibrate days in it.
|
|
21
|
+
|
|
22
|
+
Reached as ``client.data``. Every simulation is pinned to a symbol-day, so
|
|
23
|
+
this is the resource that tells you which ``provider`` / ``exchange`` /
|
|
24
|
+
``symbol`` / ``cal_date`` combinations exist and which are ready to run.
|
|
25
|
+
|
|
26
|
+
Examples
|
|
27
|
+
--------
|
|
28
|
+
>>> catalog = client.data.calibrated_data(exchange="hkex_securities")
|
|
29
|
+
>>> for instrument in catalog["symbols"][:3]:
|
|
30
|
+
... print(instrument["symbol"], instrument["available_dates"][:2])
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
def __init__(self, client):
|
|
34
|
+
self._client = client
|
|
35
|
+
|
|
36
|
+
def available_data(
|
|
37
|
+
self,
|
|
38
|
+
model_id: str | None = None,
|
|
39
|
+
symbol: str | None = None,
|
|
40
|
+
q: str | None = None,
|
|
41
|
+
provider: str | None = None,
|
|
42
|
+
exchange: str | None = None,
|
|
43
|
+
date: str | None = None,
|
|
44
|
+
include_calibration_state: bool = False,
|
|
45
|
+
limit: int | None = None,
|
|
46
|
+
offset: int | None = None,
|
|
47
|
+
):
|
|
48
|
+
"""Every symbol-day in the data registry: what EXISTS.
|
|
49
|
+
|
|
50
|
+
The raw-data catalogue, so it answers both "what can a foundation model
|
|
51
|
+
be prompted with?" and "what could I calibrate?". Its counterpart is
|
|
52
|
+
:meth:`calibrated_data`, which lists only the days the agent-based
|
|
53
|
+
pipeline has already processed.
|
|
54
|
+
|
|
55
|
+
Every filter is applied in the database. The catalogue holds the whole
|
|
56
|
+
vendor universe (~15k symbols) and is never returned whole, so page
|
|
57
|
+
through it with ``limit`` and ``offset`` rather than asking for all of
|
|
58
|
+
it.
|
|
59
|
+
|
|
60
|
+
Parameters
|
|
61
|
+
----------
|
|
62
|
+
model_id : str, optional
|
|
63
|
+
A model from :meth:`~simudyne.resources.fm.FmResource.models`.
|
|
64
|
+
Keeps only the markets that model's ``supported_data`` declares, so
|
|
65
|
+
what comes back is exactly what it will accept at
|
|
66
|
+
:meth:`~simudyne.resources.simulation.SimulationResource.run` with
|
|
67
|
+
``engine="fm"``. An unknown id raises.
|
|
68
|
+
symbol : str, optional
|
|
69
|
+
Exact symbol, e.g. "700" or "TSCO".
|
|
70
|
+
q : str, optional
|
|
71
|
+
Case-insensitive substring match over the symbol.
|
|
72
|
+
provider : str, optional
|
|
73
|
+
Data provider, e.g. "omd" or "bmll". Narrows further within
|
|
74
|
+
``model_id``'s markets when both are given.
|
|
75
|
+
exchange : str, optional
|
|
76
|
+
Exchange protocol, e.g. "lse" or "hkex_securities".
|
|
77
|
+
date : str, optional
|
|
78
|
+
Trading date "YYYY-MM-DD". Keeps only symbols with data on that
|
|
79
|
+
day, and returns only that day.
|
|
80
|
+
include_calibration_state : bool, default False
|
|
81
|
+
Also return ``calibrated_dates`` and ``calibrated_count`` per
|
|
82
|
+
symbol — which of its days the agent-based model has already
|
|
83
|
+
calibrated. Costs a second query, so it is off by default; it means
|
|
84
|
+
nothing to a foundation model.
|
|
85
|
+
limit : int, optional
|
|
86
|
+
Max symbols to return (1-200, default 50).
|
|
87
|
+
offset : int, optional
|
|
88
|
+
Symbols to skip, for paging alongside ``limit``.
|
|
89
|
+
|
|
90
|
+
Returns
|
|
91
|
+
-------
|
|
92
|
+
dict
|
|
93
|
+
``{total, limit, offset, symbols}``, paged by symbol identity
|
|
94
|
+
rather than by row so a symbol's dates are never truncated.
|
|
95
|
+
``total`` is the match count before paging. Each symbol has:
|
|
96
|
+
|
|
97
|
+
- symbol (str): the ticker, e.g. "700"
|
|
98
|
+
- provider (str), exchange (str): the market it came from
|
|
99
|
+
- dates (list of str): trading days present, "YYYY-MM-DD"
|
|
100
|
+
- calibrated_dates (list of str), calibrated_count (int): only
|
|
101
|
+
with ``include_calibration_state``
|
|
102
|
+
|
|
103
|
+
``dates`` is always a list of strings, with or without calibration
|
|
104
|
+
state, so a caller never branches on the shape of the response.
|
|
105
|
+
|
|
106
|
+
Raises
|
|
107
|
+
------
|
|
108
|
+
PulseAPIError
|
|
109
|
+
If the key is not pro or demo tier (403), ``model_id`` names a
|
|
110
|
+
model that is not registered (404), or ``date`` is not
|
|
111
|
+
``YYYY-MM-DD`` (422).
|
|
112
|
+
|
|
113
|
+
See Also
|
|
114
|
+
--------
|
|
115
|
+
calibrated_data : The subset the agent-based model can simulate.
|
|
116
|
+
calibrate : Move one symbol-day from here to there.
|
|
117
|
+
|
|
118
|
+
Examples
|
|
119
|
+
--------
|
|
120
|
+
Search the registry:
|
|
121
|
+
|
|
122
|
+
>>> page = client.data.available_data(q="tsc", exchange="lse")
|
|
123
|
+
>>> page["total"]
|
|
124
|
+
3
|
|
125
|
+
|
|
126
|
+
What one model can be prompted with, which is the usual pre-run call:
|
|
127
|
+
|
|
128
|
+
>>> page = client.data.available_data(model_id="pulse-lob-1", limit=20)
|
|
129
|
+
>>> first = page["symbols"][0]
|
|
130
|
+
>>> first["symbol"], first["dates"][:2]
|
|
131
|
+
('AZN', ['2026-06-22', '2026-06-23'])
|
|
132
|
+
|
|
133
|
+
Which of a symbol's days are already calibrated:
|
|
134
|
+
|
|
135
|
+
>>> page = client.data.available_data(
|
|
136
|
+
... symbol="700", include_calibration_state=True,
|
|
137
|
+
... )
|
|
138
|
+
>>> entry = page["symbols"][0]
|
|
139
|
+
>>> len(entry["dates"]), entry["calibrated_count"]
|
|
140
|
+
(60, 4)
|
|
141
|
+
"""
|
|
142
|
+
params = {
|
|
143
|
+
k: v for k, v in {
|
|
144
|
+
"model_id": model_id,
|
|
145
|
+
"symbol": symbol,
|
|
146
|
+
"q": q,
|
|
147
|
+
"provider": provider,
|
|
148
|
+
"exchange": exchange,
|
|
149
|
+
"date": date,
|
|
150
|
+
"include_calibration_state": include_calibration_state or None,
|
|
151
|
+
"limit": limit,
|
|
152
|
+
"offset": offset,
|
|
153
|
+
}.items() if v is not None
|
|
154
|
+
}
|
|
155
|
+
return self._client._request("GET", AVAILABLE_DATA_PATH, params=params or None)
|
|
156
|
+
|
|
157
|
+
def calibrated_data(
|
|
158
|
+
self,
|
|
159
|
+
symbol: str | None = None,
|
|
160
|
+
exchange: str | None = None,
|
|
161
|
+
provider: str | None = None,
|
|
162
|
+
date: str | None = None,
|
|
163
|
+
q: str | None = None,
|
|
164
|
+
limit: int | None = None,
|
|
165
|
+
offset: int | None = None,
|
|
166
|
+
):
|
|
167
|
+
"""Symbol-days the agent-based model can simulate: what is CALIBRATED.
|
|
168
|
+
|
|
169
|
+
Every entry is a valid ``symbol`` / ``provider`` / ``exchange`` /
|
|
170
|
+
``cal_date`` for
|
|
171
|
+
:meth:`~simudyne.resources.simulation.SimulationResource.run` with
|
|
172
|
+
``engine="abm"``. A day that exists but has never been calibrated
|
|
173
|
+
appears in :meth:`available_data` and not here; :meth:`calibrate` is
|
|
174
|
+
what moves it across.
|
|
175
|
+
|
|
176
|
+
Parameters
|
|
177
|
+
----------
|
|
178
|
+
symbol : str, optional
|
|
179
|
+
Exact symbol, e.g. "700.HK".
|
|
180
|
+
exchange : str, optional
|
|
181
|
+
Exchange protocol, e.g. "hkex_securities".
|
|
182
|
+
provider : str, optional
|
|
183
|
+
Data provider, e.g. "omd" or "bmll".
|
|
184
|
+
date : str, optional
|
|
185
|
+
Calibration date "YYYY-MM-DD". Keeps only instruments calibrated
|
|
186
|
+
on that date, and narrows each one's ``available_dates`` to it.
|
|
187
|
+
q : str, optional
|
|
188
|
+
Case-insensitive substring match over ticker and company name.
|
|
189
|
+
limit : int, optional
|
|
190
|
+
Max instruments to return. Omitted, the whole calibrated catalogue
|
|
191
|
+
comes back — it is far smaller than the registry.
|
|
192
|
+
offset : int, optional
|
|
193
|
+
Instruments to skip, for paging alongside ``limit``.
|
|
194
|
+
|
|
195
|
+
Returns
|
|
196
|
+
-------
|
|
197
|
+
dict
|
|
198
|
+
``{total, limit, offset, symbols}``, the same envelope as
|
|
199
|
+
:meth:`available_data`, paged by instrument. Each entry has:
|
|
200
|
+
|
|
201
|
+
- symbol (str): the ticker, e.g. "700.HK"
|
|
202
|
+
- company_name (str or None): the issuer's name where the vendor
|
|
203
|
+
reports one
|
|
204
|
+
- provider (str), exchange (str): the market it came from
|
|
205
|
+
- instrument_type, currency, tick_size, lot_size
|
|
206
|
+
- available_dates (list of dict): one per calibrated day, each with
|
|
207
|
+
``date``, ``status``, ``stage``, ``number_of_msgs``,
|
|
208
|
+
``volume_traded`` and ``reference_price``. Any ``date`` here is a
|
|
209
|
+
valid ``cal_date`` to run against
|
|
210
|
+
- avg_number_of_msgs, avg_volume_traded: averaged over the dates
|
|
211
|
+
returned, so a ``date`` filter narrows them too
|
|
212
|
+
|
|
213
|
+
Raises
|
|
214
|
+
------
|
|
215
|
+
PulseAPIError
|
|
216
|
+
If a filter is malformed — most often ``date`` not in
|
|
217
|
+
``YYYY-MM-DD`` form (422).
|
|
218
|
+
|
|
219
|
+
See Also
|
|
220
|
+
--------
|
|
221
|
+
available_data : Everything that exists, calibrated or not.
|
|
222
|
+
|
|
223
|
+
Examples
|
|
224
|
+
--------
|
|
225
|
+
The whole calibrated catalogue:
|
|
226
|
+
|
|
227
|
+
>>> everything = client.data.calibrated_data()
|
|
228
|
+
>>> everything["total"]
|
|
229
|
+
412
|
|
230
|
+
|
|
231
|
+
One instrument, to see which dates it can be run for:
|
|
232
|
+
|
|
233
|
+
>>> hits = client.data.calibrated_data(symbol="700.HK")
|
|
234
|
+
>>> [d["date"] for d in hits["symbols"][0]["available_dates"]]
|
|
235
|
+
['2025-09-01', '2025-09-02', '2025-09-03']
|
|
236
|
+
|
|
237
|
+
Everything calibrated on one date, the usual way to pick a date that is
|
|
238
|
+
valid across several instruments:
|
|
239
|
+
|
|
240
|
+
>>> on_date = client.data.calibrated_data(date="2025-09-01")
|
|
241
|
+
|
|
242
|
+
Free-text search over ticker and company name, paged:
|
|
243
|
+
|
|
244
|
+
>>> page = client.data.calibrated_data(q="tencent", limit=10)
|
|
245
|
+
"""
|
|
246
|
+
params = {
|
|
247
|
+
k: v for k, v in {
|
|
248
|
+
"symbol": symbol,
|
|
249
|
+
"exchange": exchange,
|
|
250
|
+
"provider": provider,
|
|
251
|
+
"date": date,
|
|
252
|
+
"q": q,
|
|
253
|
+
"limit": limit,
|
|
254
|
+
"offset": offset,
|
|
255
|
+
}.items() if v is not None
|
|
256
|
+
}
|
|
257
|
+
return self._client._request("GET", CALIBRATED_DATA_PATH, params=params or None)
|
|
258
|
+
|
|
259
|
+
def calibrate(
|
|
260
|
+
self,
|
|
261
|
+
symbol: str,
|
|
262
|
+
cal_date: str,
|
|
263
|
+
provider: str,
|
|
264
|
+
exchange: str,
|
|
265
|
+
simulations: int = None,
|
|
266
|
+
batch_size: int = None,
|
|
267
|
+
optimize_adj_params: bool = True,
|
|
268
|
+
):
|
|
269
|
+
"""Fit the agent-based model to one symbol-day.
|
|
270
|
+
|
|
271
|
+
Calibration is a data operation: its input is a day from
|
|
272
|
+
:meth:`available_data` and its output is an entry in
|
|
273
|
+
:meth:`calibrated_data`. It runs asynchronously — the call returns once
|
|
274
|
+
the job is queued, and the day appears in :meth:`calibrated_data` when
|
|
275
|
+
the pipeline finishes.
|
|
276
|
+
|
|
277
|
+
A symbol is identified by four fields: ``provider``, ``exchange``,
|
|
278
|
+
``symbol`` and ``cal_date``.
|
|
279
|
+
|
|
280
|
+
Parameters
|
|
281
|
+
----------
|
|
282
|
+
symbol : str
|
|
283
|
+
Trading symbol, e.g. "9999.HK".
|
|
284
|
+
cal_date : str
|
|
285
|
+
Trading day in YYYY-MM-DD format.
|
|
286
|
+
provider : str
|
|
287
|
+
Data provider the symbol is sourced from: "omd" or "bmll".
|
|
288
|
+
exchange : str
|
|
289
|
+
Exchange protocol name, e.g. "hkex_securities".
|
|
290
|
+
simulations : int, optional
|
|
291
|
+
Number of simulations to run during calibration.
|
|
292
|
+
batch_size : int, optional
|
|
293
|
+
Batch size for calibration runs.
|
|
294
|
+
optimize_adj_params : bool, default True
|
|
295
|
+
Whether to optimise adjustment parameters.
|
|
296
|
+
|
|
297
|
+
Returns
|
|
298
|
+
-------
|
|
299
|
+
dict
|
|
300
|
+
Calibration job submission result, containing the job identifier
|
|
301
|
+
and its accepted status.
|
|
302
|
+
|
|
303
|
+
Raises
|
|
304
|
+
------
|
|
305
|
+
PulseAPIError
|
|
306
|
+
If the key is not demo tier (403), or there is no raw market data
|
|
307
|
+
for the symbol on ``cal_date`` (400).
|
|
308
|
+
|
|
309
|
+
Notes
|
|
310
|
+
-----
|
|
311
|
+
Calibration is only needed for a symbol-day that is not already
|
|
312
|
+
calibrated, and it is far slower than running against an existing
|
|
313
|
+
calibration. Check :meth:`calibrated_data` first — most instruments are
|
|
314
|
+
calibrated already.
|
|
315
|
+
|
|
316
|
+
Examples
|
|
317
|
+
--------
|
|
318
|
+
>>> job = client.data.calibrate(
|
|
319
|
+
... symbol="9999.HK",
|
|
320
|
+
... cal_date="2025-09-01",
|
|
321
|
+
... provider="omd",
|
|
322
|
+
... exchange="hkex_securities",
|
|
323
|
+
... )
|
|
324
|
+
|
|
325
|
+
With an explicit budget, and adjustment-parameter optimisation off:
|
|
326
|
+
|
|
327
|
+
>>> job = client.data.calibrate(
|
|
328
|
+
... symbol="9999.HK",
|
|
329
|
+
... cal_date="2025-09-01",
|
|
330
|
+
... provider="omd",
|
|
331
|
+
... exchange="hkex_securities",
|
|
332
|
+
... simulations=500,
|
|
333
|
+
... batch_size=50,
|
|
334
|
+
... optimize_adj_params=False,
|
|
335
|
+
... )
|
|
336
|
+
"""
|
|
337
|
+
payload: dict = {
|
|
338
|
+
"symbol": symbol,
|
|
339
|
+
"cal_date": cal_date,
|
|
340
|
+
"provider": provider,
|
|
341
|
+
"exchange": exchange,
|
|
342
|
+
"optimize_adj_params": optimize_adj_params,
|
|
343
|
+
}
|
|
344
|
+
if simulations is not None:
|
|
345
|
+
payload["simulations"] = simulations
|
|
346
|
+
if batch_size is not None:
|
|
347
|
+
payload["batch_size"] = batch_size
|
|
348
|
+
return self._client._request("POST", CALIBRATE_PATH, json=payload)
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
"""The foundation-model registry: what can run here, and what each accepts.
|
|
2
|
+
|
|
3
|
+
RUNNING a foundation model is not here. ``client.simulation.run(engine="fm")``
|
|
4
|
+
submits every simulation, agent-based or foundation-model, and
|
|
5
|
+
``client.simulation`` polls and reads them both — an FM sim carries the same
|
|
6
|
+
canonical ``sim_id``, differing only in its ``gen_method`` field
|
|
7
|
+
(``FM-{model_id}_v{version}`` rather than ``ABM_v{version}``).
|
|
8
|
+
|
|
9
|
+
What remains here is the registry, which is a catalogue of engines rather than
|
|
10
|
+
a simulation, and the live streaming session, which has no job and no sim_id
|
|
11
|
+
and so shares no lifecycle with the rest.
|
|
12
|
+
|
|
13
|
+
Workflow:
|
|
14
|
+
1. ``models()`` — what can run here, and what each model accepts
|
|
15
|
+
2. ``client.data.available_data(model_id=...)`` — what it can be prompted
|
|
16
|
+
with
|
|
17
|
+
3. ``client.simulation.run(engine="fm", model_id=...)`` — submit; returns a
|
|
18
|
+
job_id and its sim_ids up front
|
|
19
|
+
4. ``client.simulation.get_job_status(job_id)`` until it is complete
|
|
20
|
+
5. ``client.simulation.get_sim_data(sim_id)`` — read the generated book
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
MODELS_PATH = "/fm/models"
|
|
24
|
+
LIVE_PATH = "/fm/live"
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class FmResource:
|
|
28
|
+
"""The foundation models registered in this environment.
|
|
29
|
+
|
|
30
|
+
Reached as ``client.fm``. Use it to discover what can run and what each
|
|
31
|
+
model accepts, then submit through ``client.simulation.run(engine="fm")``
|
|
32
|
+
like any other simulation.
|
|
33
|
+
|
|
34
|
+
Every call on this resource requires a pro-tier key.
|
|
35
|
+
|
|
36
|
+
Examples
|
|
37
|
+
--------
|
|
38
|
+
The full workflow, from picking a model to reading the generated book —
|
|
39
|
+
note that only the first step is on this resource:
|
|
40
|
+
|
|
41
|
+
>>> import time
|
|
42
|
+
>>> model = client.fm.models()["models"][0]
|
|
43
|
+
>>> job = client.simulation.run(
|
|
44
|
+
... engine="fm",
|
|
45
|
+
... model_id=model["model_id"],
|
|
46
|
+
... symbol="700",
|
|
47
|
+
... cal_date="2025-09-02",
|
|
48
|
+
... provider="bmll",
|
|
49
|
+
... exchange="hkex_securities",
|
|
50
|
+
... duration_minutes=30,
|
|
51
|
+
... )
|
|
52
|
+
>>> while not client.simulation.get_job_status(job["job_id"])["is_complete"]:
|
|
53
|
+
... time.sleep(30)
|
|
54
|
+
>>> book = client.simulation.get_sim_data(job["sim_ids"][0])
|
|
55
|
+
"""
|
|
56
|
+
|
|
57
|
+
def __init__(self, client):
|
|
58
|
+
self._client = client
|
|
59
|
+
|
|
60
|
+
def models(self):
|
|
61
|
+
"""Foundation models active in this environment.
|
|
62
|
+
|
|
63
|
+
Requires a pro-tier key.
|
|
64
|
+
|
|
65
|
+
Returns
|
|
66
|
+
-------
|
|
67
|
+
dict
|
|
68
|
+
Contains ``models``, a list of model dicts, each with:
|
|
69
|
+
|
|
70
|
+
- model_id (str): the id to pass to :meth:`run`
|
|
71
|
+
- version (str): the model build version
|
|
72
|
+
- production_name (str): branding name, empty for dev builds
|
|
73
|
+
- supported_data (list of dict): ``{provider, exchange}`` markets
|
|
74
|
+
the model accepts a prompt from; empty means any
|
|
75
|
+
- min_prompt_rows (int or None): context rows it needs to start
|
|
76
|
+
- model_args (dict): the knobs it accepts, each
|
|
77
|
+
``{type, default, min, max, description}``
|
|
78
|
+
- resources (dict): ``{cpu, memory, gpu}`` it is scheduled with
|
|
79
|
+
|
|
80
|
+
Raises
|
|
81
|
+
------
|
|
82
|
+
PulseAPIError
|
|
83
|
+
If the key is not pro tier — status 403.
|
|
84
|
+
|
|
85
|
+
Examples
|
|
86
|
+
--------
|
|
87
|
+
>>> for m in client.fm.models()["models"]:
|
|
88
|
+
... print(m["model_id"], m["version"], sorted(m["model_args"]))
|
|
89
|
+
|
|
90
|
+
Inspect one model's knobs before overriding them:
|
|
91
|
+
|
|
92
|
+
>>> models = client.fm.models()["models"]
|
|
93
|
+
>>> first = models[0]
|
|
94
|
+
>>> knobs = first["model_args"]
|
|
95
|
+
>>> knobs["temperature"]
|
|
96
|
+
{'type': 'float', 'default': 1.0, 'min': 0.1, 'max': 2.0, ...}
|
|
97
|
+
"""
|
|
98
|
+
return self._client._request("GET", MODELS_PATH)
|
|
99
|
+
|
|
100
|
+
def live(
|
|
101
|
+
self,
|
|
102
|
+
model_id: str,
|
|
103
|
+
horizon: int = None,
|
|
104
|
+
seed: int = 42,
|
|
105
|
+
model_args: dict = None,
|
|
106
|
+
):
|
|
107
|
+
"""Start a live streaming session instead of a batch job.
|
|
108
|
+
|
|
109
|
+
Returns the token a live chart client uses to connect. Requires a
|
|
110
|
+
pro-tier key.
|
|
111
|
+
|
|
112
|
+
Parameters
|
|
113
|
+
----------
|
|
114
|
+
model_id : str
|
|
115
|
+
A model from models().
|
|
116
|
+
horizon : int, optional
|
|
117
|
+
Frames to generate.
|
|
118
|
+
seed : int, default 42
|
|
119
|
+
Master random seed.
|
|
120
|
+
model_args : dict, optional
|
|
121
|
+
Overrides for the model's declared knobs.
|
|
122
|
+
|
|
123
|
+
Returns
|
|
124
|
+
-------
|
|
125
|
+
dict
|
|
126
|
+
Contains ``token`` — the connection token a live chart client
|
|
127
|
+
presents — plus ``job_id`` and ``model_id``.
|
|
128
|
+
|
|
129
|
+
Raises
|
|
130
|
+
------
|
|
131
|
+
PulseAPIError
|
|
132
|
+
If ``model_id`` is unknown (404), ``model_args`` holds an unknown
|
|
133
|
+
or out-of-range key (400), or the key is not pro tier (403).
|
|
134
|
+
|
|
135
|
+
Examples
|
|
136
|
+
--------
|
|
137
|
+
>>> session = client.fm.live(model_id="tradefm-hkex", horizon=5000)
|
|
138
|
+
>>> print(session["token"])
|
|
139
|
+
|
|
140
|
+
A live session is not a batch job: nothing is queued, no ``sim_id`` is
|
|
141
|
+
minted, and there is no status to poll. The token is the whole
|
|
142
|
+
interface.
|
|
143
|
+
"""
|
|
144
|
+
payload = {"model_id": model_id, "seed": seed}
|
|
145
|
+
if horizon is not None:
|
|
146
|
+
payload["horizon"] = horizon
|
|
147
|
+
if model_args:
|
|
148
|
+
payload["model_args"] = model_args
|
|
149
|
+
return self._client._request("POST", LIVE_PATH, json=payload)
|