reportify-sdk 0.1.0__py3-none-any.whl → 0.2.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.
- reportify_sdk/client.py +9 -0
- reportify_sdk/quant.py +371 -0
- {reportify_sdk-0.1.0.dist-info → reportify_sdk-0.2.0.dist-info}/METADATA +1 -1
- {reportify_sdk-0.1.0.dist-info → reportify_sdk-0.2.0.dist-info}/RECORD +7 -6
- {reportify_sdk-0.1.0.dist-info → reportify_sdk-0.2.0.dist-info}/WHEEL +0 -0
- {reportify_sdk-0.1.0.dist-info → reportify_sdk-0.2.0.dist-info}/licenses/LICENSE +0 -0
- {reportify_sdk-0.1.0.dist-info → reportify_sdk-0.2.0.dist-info}/top_level.txt +0 -0
reportify_sdk/client.py
CHANGED
|
@@ -61,6 +61,7 @@ class Reportify:
|
|
|
61
61
|
self._kb = None
|
|
62
62
|
self._docs = None
|
|
63
63
|
self._tools = None
|
|
64
|
+
self._quant = None
|
|
64
65
|
|
|
65
66
|
def _get_headers(self) -> dict[str, str]:
|
|
66
67
|
"""Get default headers for API requests"""
|
|
@@ -351,6 +352,14 @@ class Reportify:
|
|
|
351
352
|
self._docs = DocsModule(self)
|
|
352
353
|
return self._docs
|
|
353
354
|
|
|
355
|
+
@property
|
|
356
|
+
def quant(self):
|
|
357
|
+
"""Quant module for indicators, factors, quotes, and backtesting"""
|
|
358
|
+
if self._quant is None:
|
|
359
|
+
from reportify_sdk.quant import QuantModule
|
|
360
|
+
self._quant = QuantModule(self)
|
|
361
|
+
return self._quant
|
|
362
|
+
|
|
354
363
|
def close(self):
|
|
355
364
|
"""Close the HTTP client"""
|
|
356
365
|
self._client.close()
|
reportify_sdk/quant.py
ADDED
|
@@ -0,0 +1,371 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Quant Module
|
|
3
|
+
|
|
4
|
+
Provides quantitative analysis tools including indicators, factors, quotes, and backtesting.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from typing import Any
|
|
8
|
+
|
|
9
|
+
import pandas as pd
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class QuantModule:
|
|
13
|
+
"""
|
|
14
|
+
Quantitative analysis module
|
|
15
|
+
|
|
16
|
+
Access technical indicators, factors, quotes, and backtesting functionality.
|
|
17
|
+
|
|
18
|
+
Example:
|
|
19
|
+
>>> quant = client.quant
|
|
20
|
+
>>> indicators = quant.indicators(symbols=["US:AAPL"], indicators=["ma", "rsi"])
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
def __init__(self, client):
|
|
24
|
+
self._client = client
|
|
25
|
+
|
|
26
|
+
# -------------------------------------------------------------------------
|
|
27
|
+
# Indicators
|
|
28
|
+
# -------------------------------------------------------------------------
|
|
29
|
+
|
|
30
|
+
def indicators(
|
|
31
|
+
self,
|
|
32
|
+
symbols: list[str],
|
|
33
|
+
indicators: list[str],
|
|
34
|
+
*,
|
|
35
|
+
start_date: str | None = None,
|
|
36
|
+
end_date: str | None = None,
|
|
37
|
+
interval: str = "1d",
|
|
38
|
+
params: dict[str, Any] | None = None,
|
|
39
|
+
) -> pd.DataFrame:
|
|
40
|
+
"""
|
|
41
|
+
Get technical indicators for given symbols
|
|
42
|
+
|
|
43
|
+
Args:
|
|
44
|
+
symbols: List of stock symbols (e.g., ["US:AAPL", "US:MSFT"])
|
|
45
|
+
indicators: List of indicator names (e.g., ["ma", "rsi", "macd", "bollinger"])
|
|
46
|
+
start_date: Start date (YYYY-MM-DD)
|
|
47
|
+
end_date: End date (YYYY-MM-DD)
|
|
48
|
+
interval: Data interval ("1d", "1w", "1m")
|
|
49
|
+
params: Additional indicator parameters (e.g., {"ma_period": 20})
|
|
50
|
+
|
|
51
|
+
Returns:
|
|
52
|
+
DataFrame with indicator values
|
|
53
|
+
|
|
54
|
+
Example:
|
|
55
|
+
>>> df = client.quant.indicators(
|
|
56
|
+
... symbols=["US:AAPL"],
|
|
57
|
+
... indicators=["ma", "rsi"],
|
|
58
|
+
... params={"ma_period": 20, "rsi_period": 14}
|
|
59
|
+
... )
|
|
60
|
+
>>> print(df[["close", "ma_20", "rsi_14"]])
|
|
61
|
+
"""
|
|
62
|
+
data: dict[str, Any] = {
|
|
63
|
+
"symbols": symbols,
|
|
64
|
+
"indicators": indicators,
|
|
65
|
+
"interval": interval,
|
|
66
|
+
}
|
|
67
|
+
if start_date:
|
|
68
|
+
data["start_date"] = start_date
|
|
69
|
+
if end_date:
|
|
70
|
+
data["end_date"] = end_date
|
|
71
|
+
if params:
|
|
72
|
+
data["params"] = params
|
|
73
|
+
|
|
74
|
+
response = self._client._post("/v2/quant/indicators", json=data)
|
|
75
|
+
return self._to_dataframe(response.get("data", []))
|
|
76
|
+
|
|
77
|
+
def indicator_list(self) -> list[dict[str, Any]]:
|
|
78
|
+
"""
|
|
79
|
+
Get list of available technical indicators
|
|
80
|
+
|
|
81
|
+
Returns:
|
|
82
|
+
List of indicator definitions with name, description, and parameters
|
|
83
|
+
|
|
84
|
+
Example:
|
|
85
|
+
>>> indicators = client.quant.indicator_list()
|
|
86
|
+
>>> for ind in indicators:
|
|
87
|
+
... print(f"{ind['name']}: {ind['description']}")
|
|
88
|
+
"""
|
|
89
|
+
response = self._client._get("/v2/quant/indicators")
|
|
90
|
+
return response.get("indicators", [])
|
|
91
|
+
|
|
92
|
+
# -------------------------------------------------------------------------
|
|
93
|
+
# Factors
|
|
94
|
+
# -------------------------------------------------------------------------
|
|
95
|
+
|
|
96
|
+
def factors(
|
|
97
|
+
self,
|
|
98
|
+
symbols: list[str],
|
|
99
|
+
factors: list[str],
|
|
100
|
+
*,
|
|
101
|
+
start_date: str | None = None,
|
|
102
|
+
end_date: str | None = None,
|
|
103
|
+
frequency: str = "daily",
|
|
104
|
+
) -> pd.DataFrame:
|
|
105
|
+
"""
|
|
106
|
+
Get factor data for given symbols
|
|
107
|
+
|
|
108
|
+
Args:
|
|
109
|
+
symbols: List of stock symbols
|
|
110
|
+
factors: List of factor names (e.g., ["momentum", "value", "quality", "size"])
|
|
111
|
+
start_date: Start date (YYYY-MM-DD)
|
|
112
|
+
end_date: End date (YYYY-MM-DD)
|
|
113
|
+
frequency: Data frequency ("daily", "weekly", "monthly")
|
|
114
|
+
|
|
115
|
+
Returns:
|
|
116
|
+
DataFrame with factor values
|
|
117
|
+
|
|
118
|
+
Example:
|
|
119
|
+
>>> df = client.quant.factors(
|
|
120
|
+
... symbols=["US:AAPL", "US:MSFT"],
|
|
121
|
+
... factors=["momentum", "value"],
|
|
122
|
+
... frequency="monthly"
|
|
123
|
+
... )
|
|
124
|
+
"""
|
|
125
|
+
data: dict[str, Any] = {
|
|
126
|
+
"symbols": symbols,
|
|
127
|
+
"factors": factors,
|
|
128
|
+
"frequency": frequency,
|
|
129
|
+
}
|
|
130
|
+
if start_date:
|
|
131
|
+
data["start_date"] = start_date
|
|
132
|
+
if end_date:
|
|
133
|
+
data["end_date"] = end_date
|
|
134
|
+
|
|
135
|
+
response = self._client._post("/v2/quant/factors", json=data)
|
|
136
|
+
return self._to_dataframe(response.get("data", []))
|
|
137
|
+
|
|
138
|
+
def factor_list(self) -> list[dict[str, Any]]:
|
|
139
|
+
"""
|
|
140
|
+
Get list of available factors
|
|
141
|
+
|
|
142
|
+
Returns:
|
|
143
|
+
List of factor definitions
|
|
144
|
+
|
|
145
|
+
Example:
|
|
146
|
+
>>> factors = client.quant.factor_list()
|
|
147
|
+
>>> for f in factors:
|
|
148
|
+
... print(f"{f['name']}: {f['category']}")
|
|
149
|
+
"""
|
|
150
|
+
response = self._client._get("/v2/quant/factors")
|
|
151
|
+
return response.get("factors", [])
|
|
152
|
+
|
|
153
|
+
def factor_exposure(
|
|
154
|
+
self,
|
|
155
|
+
symbols: list[str],
|
|
156
|
+
*,
|
|
157
|
+
date: str | None = None,
|
|
158
|
+
) -> pd.DataFrame:
|
|
159
|
+
"""
|
|
160
|
+
Get factor exposure for given symbols
|
|
161
|
+
|
|
162
|
+
Args:
|
|
163
|
+
symbols: List of stock symbols
|
|
164
|
+
date: Specific date (YYYY-MM-DD), defaults to latest
|
|
165
|
+
|
|
166
|
+
Returns:
|
|
167
|
+
DataFrame with factor exposures for each symbol
|
|
168
|
+
|
|
169
|
+
Example:
|
|
170
|
+
>>> exposure = client.quant.factor_exposure(["US:AAPL", "US:GOOGL"])
|
|
171
|
+
>>> print(exposure[["symbol", "momentum", "value", "quality"]])
|
|
172
|
+
"""
|
|
173
|
+
data: dict[str, Any] = {"symbols": symbols}
|
|
174
|
+
if date:
|
|
175
|
+
data["date"] = date
|
|
176
|
+
|
|
177
|
+
response = self._client._post("/v2/quant/factors/exposure", json=data)
|
|
178
|
+
return self._to_dataframe(response.get("data", []))
|
|
179
|
+
|
|
180
|
+
# -------------------------------------------------------------------------
|
|
181
|
+
# Quotes
|
|
182
|
+
# -------------------------------------------------------------------------
|
|
183
|
+
|
|
184
|
+
def quotes(
|
|
185
|
+
self,
|
|
186
|
+
symbols: list[str],
|
|
187
|
+
*,
|
|
188
|
+
fields: list[str] | None = None,
|
|
189
|
+
) -> pd.DataFrame:
|
|
190
|
+
"""
|
|
191
|
+
Get real-time quotes for given symbols
|
|
192
|
+
|
|
193
|
+
Args:
|
|
194
|
+
symbols: List of stock symbols
|
|
195
|
+
fields: Specific fields to return (default: all)
|
|
196
|
+
|
|
197
|
+
Returns:
|
|
198
|
+
DataFrame with quote data
|
|
199
|
+
|
|
200
|
+
Example:
|
|
201
|
+
>>> quotes = client.quant.quotes(["US:AAPL", "US:MSFT"])
|
|
202
|
+
>>> print(quotes[["symbol", "price", "change", "volume"]])
|
|
203
|
+
"""
|
|
204
|
+
data: dict[str, Any] = {"symbols": symbols}
|
|
205
|
+
if fields:
|
|
206
|
+
data["fields"] = fields
|
|
207
|
+
|
|
208
|
+
response = self._client._post("/v2/quant/quotes", json=data)
|
|
209
|
+
return self._to_dataframe(response.get("data", []))
|
|
210
|
+
|
|
211
|
+
def quotes_history(
|
|
212
|
+
self,
|
|
213
|
+
symbols: list[str],
|
|
214
|
+
*,
|
|
215
|
+
start_date: str | None = None,
|
|
216
|
+
end_date: str | None = None,
|
|
217
|
+
interval: str = "1d",
|
|
218
|
+
adjust: str = "forward",
|
|
219
|
+
limit: int = 100,
|
|
220
|
+
) -> pd.DataFrame:
|
|
221
|
+
"""
|
|
222
|
+
Get historical quotes data
|
|
223
|
+
|
|
224
|
+
Args:
|
|
225
|
+
symbols: List of stock symbols
|
|
226
|
+
start_date: Start date (YYYY-MM-DD)
|
|
227
|
+
end_date: End date (YYYY-MM-DD)
|
|
228
|
+
interval: Data interval ("1d", "1w", "1m")
|
|
229
|
+
adjust: Adjustment type ("forward", "backward", "none")
|
|
230
|
+
limit: Maximum records per symbol
|
|
231
|
+
|
|
232
|
+
Returns:
|
|
233
|
+
DataFrame with historical OHLCV data
|
|
234
|
+
|
|
235
|
+
Example:
|
|
236
|
+
>>> history = client.quant.quotes_history(
|
|
237
|
+
... ["US:AAPL"],
|
|
238
|
+
... start_date="2024-01-01",
|
|
239
|
+
... interval="1d"
|
|
240
|
+
... )
|
|
241
|
+
"""
|
|
242
|
+
data: dict[str, Any] = {
|
|
243
|
+
"symbols": symbols,
|
|
244
|
+
"interval": interval,
|
|
245
|
+
"adjust": adjust,
|
|
246
|
+
"limit": limit,
|
|
247
|
+
}
|
|
248
|
+
if start_date:
|
|
249
|
+
data["start_date"] = start_date
|
|
250
|
+
if end_date:
|
|
251
|
+
data["end_date"] = end_date
|
|
252
|
+
|
|
253
|
+
response = self._client._post("/v2/quant/quotes/history", json=data)
|
|
254
|
+
return self._to_dataframe(response.get("data", []))
|
|
255
|
+
|
|
256
|
+
# -------------------------------------------------------------------------
|
|
257
|
+
# Backtest
|
|
258
|
+
# -------------------------------------------------------------------------
|
|
259
|
+
|
|
260
|
+
def backtest(
|
|
261
|
+
self,
|
|
262
|
+
strategy: dict[str, Any],
|
|
263
|
+
*,
|
|
264
|
+
start_date: str,
|
|
265
|
+
end_date: str,
|
|
266
|
+
initial_capital: float = 1000000.0,
|
|
267
|
+
benchmark: str | None = None,
|
|
268
|
+
) -> dict[str, Any]:
|
|
269
|
+
"""
|
|
270
|
+
Run a backtest for a given strategy
|
|
271
|
+
|
|
272
|
+
Args:
|
|
273
|
+
strategy: Strategy definition dict
|
|
274
|
+
start_date: Backtest start date (YYYY-MM-DD)
|
|
275
|
+
end_date: Backtest end date (YYYY-MM-DD)
|
|
276
|
+
initial_capital: Starting capital (default: 1,000,000)
|
|
277
|
+
benchmark: Benchmark symbol for comparison (e.g., "US:SPY")
|
|
278
|
+
|
|
279
|
+
Returns:
|
|
280
|
+
Backtest results including returns, metrics, and trades
|
|
281
|
+
|
|
282
|
+
Example:
|
|
283
|
+
>>> result = client.quant.backtest(
|
|
284
|
+
... strategy={
|
|
285
|
+
... "type": "momentum",
|
|
286
|
+
... "universe": ["US:AAPL", "US:MSFT", "US:GOOGL"],
|
|
287
|
+
... "rebalance": "monthly",
|
|
288
|
+
... "top_n": 2
|
|
289
|
+
... },
|
|
290
|
+
... start_date="2020-01-01",
|
|
291
|
+
... end_date="2024-01-01",
|
|
292
|
+
... benchmark="US:SPY"
|
|
293
|
+
... )
|
|
294
|
+
>>> print(f"Total Return: {result['metrics']['total_return']:.2%}")
|
|
295
|
+
"""
|
|
296
|
+
data = {
|
|
297
|
+
"strategy": strategy,
|
|
298
|
+
"start_date": start_date,
|
|
299
|
+
"end_date": end_date,
|
|
300
|
+
"initial_capital": initial_capital,
|
|
301
|
+
}
|
|
302
|
+
if benchmark:
|
|
303
|
+
data["benchmark"] = benchmark
|
|
304
|
+
|
|
305
|
+
return self._client._post("/v2/quant/backtest", json=data)
|
|
306
|
+
|
|
307
|
+
def backtest_result(self, backtest_id: str) -> dict[str, Any]:
|
|
308
|
+
"""
|
|
309
|
+
Get backtest result by ID
|
|
310
|
+
|
|
311
|
+
Args:
|
|
312
|
+
backtest_id: Backtest job ID
|
|
313
|
+
|
|
314
|
+
Returns:
|
|
315
|
+
Backtest results
|
|
316
|
+
|
|
317
|
+
Example:
|
|
318
|
+
>>> result = client.quant.backtest_result("bt_abc123")
|
|
319
|
+
"""
|
|
320
|
+
return self._client._get(f"/v2/quant/backtest/{backtest_id}")
|
|
321
|
+
|
|
322
|
+
def backtest_returns(self, backtest_id: str) -> pd.DataFrame:
|
|
323
|
+
"""
|
|
324
|
+
Get backtest return series
|
|
325
|
+
|
|
326
|
+
Args:
|
|
327
|
+
backtest_id: Backtest job ID
|
|
328
|
+
|
|
329
|
+
Returns:
|
|
330
|
+
DataFrame with daily returns
|
|
331
|
+
|
|
332
|
+
Example:
|
|
333
|
+
>>> returns = client.quant.backtest_returns("bt_abc123")
|
|
334
|
+
>>> print(returns[["date", "portfolio_value", "daily_return"]])
|
|
335
|
+
"""
|
|
336
|
+
response = self._client._get(f"/v2/quant/backtest/{backtest_id}/returns")
|
|
337
|
+
return self._to_dataframe(response.get("data", []))
|
|
338
|
+
|
|
339
|
+
def backtest_trades(self, backtest_id: str) -> pd.DataFrame:
|
|
340
|
+
"""
|
|
341
|
+
Get backtest trade history
|
|
342
|
+
|
|
343
|
+
Args:
|
|
344
|
+
backtest_id: Backtest job ID
|
|
345
|
+
|
|
346
|
+
Returns:
|
|
347
|
+
DataFrame with all trades
|
|
348
|
+
|
|
349
|
+
Example:
|
|
350
|
+
>>> trades = client.quant.backtest_trades("bt_abc123")
|
|
351
|
+
>>> print(trades[["date", "symbol", "action", "quantity", "price"]])
|
|
352
|
+
"""
|
|
353
|
+
response = self._client._get(f"/v2/quant/backtest/{backtest_id}/trades")
|
|
354
|
+
return self._to_dataframe(response.get("data", []))
|
|
355
|
+
|
|
356
|
+
# -------------------------------------------------------------------------
|
|
357
|
+
# Helper Methods
|
|
358
|
+
# -------------------------------------------------------------------------
|
|
359
|
+
|
|
360
|
+
def _to_dataframe(self, data: list[dict[str, Any]]) -> pd.DataFrame:
|
|
361
|
+
"""Convert API response to DataFrame"""
|
|
362
|
+
if not data:
|
|
363
|
+
return pd.DataFrame()
|
|
364
|
+
df = pd.DataFrame(data)
|
|
365
|
+
# Convert date columns if present
|
|
366
|
+
for col in ["date", "timestamp", "trade_date"]:
|
|
367
|
+
if col in df.columns:
|
|
368
|
+
df[col] = pd.to_datetime(df[col])
|
|
369
|
+
df = df.set_index(col)
|
|
370
|
+
break
|
|
371
|
+
return df
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
reportify_sdk/__init__.py,sha256=f2bq8Exeuf5g7hAhEQ3qrS7wMVqZRXP7xmQEYA3Nvso,662
|
|
2
|
-
reportify_sdk/client.py,sha256=
|
|
2
|
+
reportify_sdk/client.py,sha256=P56qBme48UPkZhMb9BlDnLMLXwHK-DjU7qDhGFFxMI0,11019
|
|
3
3
|
reportify_sdk/docs.py,sha256=O30I1J4qTC8zpkVP_qu1cgT8nv6ysXNClqvaiRb0lhY,4957
|
|
4
4
|
reportify_sdk/exceptions.py,sha256=r2_C_kTh6tCrQnfA3UozSqMMA-2OBnoP3pGpgYeqcdU,1049
|
|
5
5
|
reportify_sdk/kb.py,sha256=-4UHWtudFncGRBB42B4YEoMPsEHr4QOtY55_8hKyogE,2240
|
|
6
|
+
reportify_sdk/quant.py,sha256=xXh4mz4B_iUh4PRbc0KI5e2FVtO0Hawvw_TMP_5rji4,11658
|
|
6
7
|
reportify_sdk/stock.py,sha256=NcEhV9JfIQm2BsfYM1k-JVcxHhnnil32dp-bedGG4BU,13513
|
|
7
8
|
reportify_sdk/timeline.py,sha256=b5Zj5SjXT4gP0dvEl8gXCWDZJHemyU7NSYahet2nHhc,4075
|
|
8
|
-
reportify_sdk-0.
|
|
9
|
-
reportify_sdk-0.
|
|
10
|
-
reportify_sdk-0.
|
|
11
|
-
reportify_sdk-0.
|
|
12
|
-
reportify_sdk-0.
|
|
9
|
+
reportify_sdk-0.2.0.dist-info/licenses/LICENSE,sha256=zBUq4DL4lE-fZU_PMkr0gnxkYS1LhdRHFw8_LmCb-ek,1066
|
|
10
|
+
reportify_sdk-0.2.0.dist-info/METADATA,sha256=kmE-VtAC7lDLy6E4X8mWMVcq8gBpv-ukuFKple3J0F8,4311
|
|
11
|
+
reportify_sdk-0.2.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
12
|
+
reportify_sdk-0.2.0.dist-info/top_level.txt,sha256=tc_dzCSWIDsNbHSi-FlyEEX8xwinhN9gl-CwyLRE4B0,14
|
|
13
|
+
reportify_sdk-0.2.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|