reportify-sdk 0.2.0__tar.gz → 0.2.1__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.
- {reportify_sdk-0.2.0/reportify_sdk.egg-info → reportify_sdk-0.2.1}/PKG-INFO +1 -1
- {reportify_sdk-0.2.0 → reportify_sdk-0.2.1}/pyproject.toml +1 -1
- reportify_sdk-0.2.1/reportify_sdk/quant.py +404 -0
- {reportify_sdk-0.2.0 → reportify_sdk-0.2.1/reportify_sdk.egg-info}/PKG-INFO +1 -1
- reportify_sdk-0.2.0/reportify_sdk/quant.py +0 -371
- {reportify_sdk-0.2.0 → reportify_sdk-0.2.1}/LICENSE +0 -0
- {reportify_sdk-0.2.0 → reportify_sdk-0.2.1}/README.md +0 -0
- {reportify_sdk-0.2.0 → reportify_sdk-0.2.1}/reportify_sdk/__init__.py +0 -0
- {reportify_sdk-0.2.0 → reportify_sdk-0.2.1}/reportify_sdk/client.py +0 -0
- {reportify_sdk-0.2.0 → reportify_sdk-0.2.1}/reportify_sdk/docs.py +0 -0
- {reportify_sdk-0.2.0 → reportify_sdk-0.2.1}/reportify_sdk/exceptions.py +0 -0
- {reportify_sdk-0.2.0 → reportify_sdk-0.2.1}/reportify_sdk/kb.py +0 -0
- {reportify_sdk-0.2.0 → reportify_sdk-0.2.1}/reportify_sdk/stock.py +0 -0
- {reportify_sdk-0.2.0 → reportify_sdk-0.2.1}/reportify_sdk/timeline.py +0 -0
- {reportify_sdk-0.2.0 → reportify_sdk-0.2.1}/reportify_sdk.egg-info/SOURCES.txt +0 -0
- {reportify_sdk-0.2.0 → reportify_sdk-0.2.1}/reportify_sdk.egg-info/dependency_links.txt +0 -0
- {reportify_sdk-0.2.0 → reportify_sdk-0.2.1}/reportify_sdk.egg-info/requires.txt +0 -0
- {reportify_sdk-0.2.0 → reportify_sdk-0.2.1}/reportify_sdk.egg-info/top_level.txt +0 -0
- {reportify_sdk-0.2.0 → reportify_sdk-0.2.1}/setup.cfg +0 -0
|
@@ -0,0 +1,404 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Quant Module
|
|
3
|
+
|
|
4
|
+
Provides quantitative analysis tools including indicators, factors, quotes, and backtesting.
|
|
5
|
+
Based on Mai-language syntax compatible with TongDaXin/TongHuaShun.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from typing import Any, Literal
|
|
9
|
+
|
|
10
|
+
import pandas as pd
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
StockMarket = Literal["cn", "hk", "us"]
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class QuantModule:
|
|
17
|
+
"""
|
|
18
|
+
Quantitative analysis module
|
|
19
|
+
|
|
20
|
+
Access technical indicators, factors, OHLCV quotes, and backtesting functionality.
|
|
21
|
+
Uses Mai-language syntax for formulas.
|
|
22
|
+
|
|
23
|
+
Example:
|
|
24
|
+
>>> quant = client.quant
|
|
25
|
+
>>> # Compute RSI indicator
|
|
26
|
+
>>> df = quant.compute_indicators(["000001"], "RSI(14)")
|
|
27
|
+
>>> # Screen stocks by formula
|
|
28
|
+
>>> stocks = quant.screen(formula="RSI(14) < 30")
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
def __init__(self, client):
|
|
32
|
+
self._client = client
|
|
33
|
+
|
|
34
|
+
# -------------------------------------------------------------------------
|
|
35
|
+
# Indicators
|
|
36
|
+
# -------------------------------------------------------------------------
|
|
37
|
+
|
|
38
|
+
def list_indicators(self) -> list[dict[str, Any]]:
|
|
39
|
+
"""
|
|
40
|
+
Get list of available technical indicators
|
|
41
|
+
|
|
42
|
+
Returns:
|
|
43
|
+
List of indicator definitions with name, description, and fields
|
|
44
|
+
|
|
45
|
+
Example:
|
|
46
|
+
>>> indicators = client.quant.list_indicators()
|
|
47
|
+
>>> for ind in indicators:
|
|
48
|
+
... print(f"{ind['name']}: {ind['description']}")
|
|
49
|
+
... print(f" Fields: {ind['fields']}")
|
|
50
|
+
"""
|
|
51
|
+
return self._client._get("/v1/quant/indicators")
|
|
52
|
+
|
|
53
|
+
def compute_indicators(
|
|
54
|
+
self,
|
|
55
|
+
symbols: list[str],
|
|
56
|
+
formula: str,
|
|
57
|
+
*,
|
|
58
|
+
market: StockMarket = "cn",
|
|
59
|
+
start_date: str | None = None,
|
|
60
|
+
end_date: str | None = None,
|
|
61
|
+
) -> pd.DataFrame:
|
|
62
|
+
"""
|
|
63
|
+
Compute indicator values for given symbols
|
|
64
|
+
|
|
65
|
+
Args:
|
|
66
|
+
symbols: List of stock codes (e.g., ["000001", "600519"])
|
|
67
|
+
formula: Indicator formula (e.g., "RSI(14)", "MACD()", "MACD(12,26,9)")
|
|
68
|
+
market: Stock market ("cn", "hk", "us"), default "cn"
|
|
69
|
+
start_date: Start date (YYYY-MM-DD), default 3 months ago
|
|
70
|
+
end_date: End date (YYYY-MM-DD), default today
|
|
71
|
+
|
|
72
|
+
Returns:
|
|
73
|
+
DataFrame with indicator values
|
|
74
|
+
|
|
75
|
+
Example:
|
|
76
|
+
>>> # RSI indicator
|
|
77
|
+
>>> df = client.quant.compute_indicators(["000001"], "RSI(14)")
|
|
78
|
+
>>> print(df[["symbol", "date", "rsi"]])
|
|
79
|
+
|
|
80
|
+
>>> # MACD indicator
|
|
81
|
+
>>> df = client.quant.compute_indicators(["000001"], "MACD()")
|
|
82
|
+
>>> print(df[["symbol", "date", "dif", "dea", "macd"]])
|
|
83
|
+
|
|
84
|
+
>>> # KDJ indicator
|
|
85
|
+
>>> df = client.quant.compute_indicators(["000001"], "KDJ(9,3,3)")
|
|
86
|
+
>>> print(df[["symbol", "date", "k", "d", "j"]])
|
|
87
|
+
"""
|
|
88
|
+
data: dict[str, Any] = {
|
|
89
|
+
"symbols": symbols,
|
|
90
|
+
"formula": formula,
|
|
91
|
+
"market": market,
|
|
92
|
+
}
|
|
93
|
+
if start_date:
|
|
94
|
+
data["start_date"] = start_date
|
|
95
|
+
if end_date:
|
|
96
|
+
data["end_date"] = end_date
|
|
97
|
+
|
|
98
|
+
response = self._client._post("/v1/quant/indicators/compute", json=data)
|
|
99
|
+
return self._to_dataframe(response.get("datas", []))
|
|
100
|
+
|
|
101
|
+
# -------------------------------------------------------------------------
|
|
102
|
+
# Factors
|
|
103
|
+
# -------------------------------------------------------------------------
|
|
104
|
+
|
|
105
|
+
def list_factors(self) -> list[dict[str, Any]]:
|
|
106
|
+
"""
|
|
107
|
+
Get list of available factors (variables and functions)
|
|
108
|
+
|
|
109
|
+
Returns factors organized by level:
|
|
110
|
+
- Level 0 Variables: CLOSE, OPEN, HIGH, LOW, VOLUME
|
|
111
|
+
- Level 0 Functions: MA, EMA, REF, HHV, LLV, STD, etc.
|
|
112
|
+
- Level 1 Functions: CROSS, COUNT, EVERY, etc.
|
|
113
|
+
- Level 2 Functions: MACD, KDJ, RSI, BOLL, etc.
|
|
114
|
+
|
|
115
|
+
Returns:
|
|
116
|
+
List of factor definitions
|
|
117
|
+
|
|
118
|
+
Example:
|
|
119
|
+
>>> factors = client.quant.list_factors()
|
|
120
|
+
>>> for f in factors:
|
|
121
|
+
... print(f"{f['name']} ({f['type']}, level {f['level']})")
|
|
122
|
+
"""
|
|
123
|
+
return self._client._get("/v1/quant/factors")
|
|
124
|
+
|
|
125
|
+
def compute_factors(
|
|
126
|
+
self,
|
|
127
|
+
symbols: list[str],
|
|
128
|
+
formula: str,
|
|
129
|
+
*,
|
|
130
|
+
market: StockMarket = "cn",
|
|
131
|
+
start_date: str | None = None,
|
|
132
|
+
end_date: str | None = None,
|
|
133
|
+
) -> pd.DataFrame:
|
|
134
|
+
"""
|
|
135
|
+
Compute factor values for given symbols
|
|
136
|
+
|
|
137
|
+
Uses Mai-language syntax compatible with TongDaXin/TongHuaShun.
|
|
138
|
+
|
|
139
|
+
Args:
|
|
140
|
+
symbols: List of stock codes
|
|
141
|
+
formula: Factor formula using Mai-language syntax
|
|
142
|
+
market: Stock market ("cn", "hk", "us"), default "cn"
|
|
143
|
+
start_date: Start date (YYYY-MM-DD), default 3 months ago
|
|
144
|
+
end_date: End date (YYYY-MM-DD), default today
|
|
145
|
+
|
|
146
|
+
Returns:
|
|
147
|
+
DataFrame with factor values
|
|
148
|
+
|
|
149
|
+
Example:
|
|
150
|
+
>>> # Simple indicator
|
|
151
|
+
>>> df = client.quant.compute_factors(["000001"], "RSI(14)")
|
|
152
|
+
|
|
153
|
+
>>> # MACD DIF line
|
|
154
|
+
>>> df = client.quant.compute_factors(["000001"], "MACD().dif")
|
|
155
|
+
|
|
156
|
+
>>> # Close above 20-day MA (boolean)
|
|
157
|
+
>>> df = client.quant.compute_factors(["000001"], "CLOSE > MA(20)")
|
|
158
|
+
|
|
159
|
+
>>> # Deviation from MA20 in percent
|
|
160
|
+
>>> df = client.quant.compute_factors(["000001"], "(CLOSE - MA(20)) / MA(20) * 100")
|
|
161
|
+
|
|
162
|
+
Supported Operators:
|
|
163
|
+
- Comparison: >, <, >=, <=, ==, !=
|
|
164
|
+
- Logical AND: & (NOT "AND")
|
|
165
|
+
- Logical OR: | (NOT "OR")
|
|
166
|
+
- Arithmetic: +, -, *, /
|
|
167
|
+
|
|
168
|
+
Supported Variables:
|
|
169
|
+
- CLOSE, C: Close price
|
|
170
|
+
- OPEN, O: Open price
|
|
171
|
+
- HIGH, H: High price
|
|
172
|
+
- LOW, L: Low price
|
|
173
|
+
- VOLUME, V, VOL: Volume
|
|
174
|
+
"""
|
|
175
|
+
data: dict[str, Any] = {
|
|
176
|
+
"symbols": symbols,
|
|
177
|
+
"formula": formula,
|
|
178
|
+
"market": market,
|
|
179
|
+
}
|
|
180
|
+
if start_date:
|
|
181
|
+
data["start_date"] = start_date
|
|
182
|
+
if end_date:
|
|
183
|
+
data["end_date"] = end_date
|
|
184
|
+
|
|
185
|
+
response = self._client._post("/v1/quant/factors/compute", json=data)
|
|
186
|
+
return self._to_dataframe(response.get("datas", []))
|
|
187
|
+
|
|
188
|
+
def screen(
|
|
189
|
+
self,
|
|
190
|
+
formula: str,
|
|
191
|
+
*,
|
|
192
|
+
market: StockMarket = "cn",
|
|
193
|
+
check_date: str | None = None,
|
|
194
|
+
symbols: list[str] | None = None,
|
|
195
|
+
) -> pd.DataFrame:
|
|
196
|
+
"""
|
|
197
|
+
Screen stocks based on factor formula
|
|
198
|
+
|
|
199
|
+
Returns stocks where the formula evaluates to True (for boolean formulas)
|
|
200
|
+
or non-null (for numeric formulas).
|
|
201
|
+
|
|
202
|
+
Args:
|
|
203
|
+
formula: Screening formula using Mai-language syntax
|
|
204
|
+
market: Stock market ("cn", "hk", "us"), default "cn"
|
|
205
|
+
check_date: Check date (YYYY-MM-DD), default latest trading day
|
|
206
|
+
symbols: Stock codes to screen (None = full market)
|
|
207
|
+
|
|
208
|
+
Returns:
|
|
209
|
+
DataFrame with stocks that passed the filter
|
|
210
|
+
|
|
211
|
+
Example:
|
|
212
|
+
>>> # RSI oversold
|
|
213
|
+
>>> stocks = client.quant.screen(formula="RSI(14) < 30")
|
|
214
|
+
|
|
215
|
+
>>> # Golden cross
|
|
216
|
+
>>> stocks = client.quant.screen(formula="CROSS(MA(5), MA(10))")
|
|
217
|
+
|
|
218
|
+
>>> # Uptrend
|
|
219
|
+
>>> stocks = client.quant.screen(formula="(CLOSE > MA(20)) & (MA(20) > MA(60))")
|
|
220
|
+
|
|
221
|
+
>>> # Above upper Bollinger Band
|
|
222
|
+
>>> stocks = client.quant.screen(formula="CLOSE > BOLL(20, 2).upper")
|
|
223
|
+
|
|
224
|
+
>>> # Screen specific stocks
|
|
225
|
+
>>> stocks = client.quant.screen(
|
|
226
|
+
... formula="RSI(14) < 30",
|
|
227
|
+
... symbols=["000001", "600519", "000002"]
|
|
228
|
+
... )
|
|
229
|
+
"""
|
|
230
|
+
data: dict[str, Any] = {
|
|
231
|
+
"formula": formula,
|
|
232
|
+
"market": market,
|
|
233
|
+
}
|
|
234
|
+
if check_date:
|
|
235
|
+
data["check_date"] = check_date
|
|
236
|
+
if symbols:
|
|
237
|
+
data["symbols"] = symbols
|
|
238
|
+
|
|
239
|
+
response = self._client._post("/v1/quant/factors/screen", json=data)
|
|
240
|
+
return self._to_dataframe(response.get("datas", []))
|
|
241
|
+
|
|
242
|
+
# -------------------------------------------------------------------------
|
|
243
|
+
# Quotes
|
|
244
|
+
# -------------------------------------------------------------------------
|
|
245
|
+
|
|
246
|
+
def ohlcv(
|
|
247
|
+
self,
|
|
248
|
+
symbol: str,
|
|
249
|
+
*,
|
|
250
|
+
market: StockMarket = "cn",
|
|
251
|
+
start_date: str | None = None,
|
|
252
|
+
end_date: str | None = None,
|
|
253
|
+
) -> pd.DataFrame:
|
|
254
|
+
"""
|
|
255
|
+
Get OHLCV (Open, High, Low, Close, Volume) daily data for a single symbol
|
|
256
|
+
|
|
257
|
+
Args:
|
|
258
|
+
symbol: Stock code
|
|
259
|
+
market: Stock market ("cn", "hk", "us"), default "cn"
|
|
260
|
+
start_date: Start date (YYYY-MM-DD), default 1 month ago
|
|
261
|
+
end_date: End date (YYYY-MM-DD), default today
|
|
262
|
+
|
|
263
|
+
Returns:
|
|
264
|
+
DataFrame with OHLCV data
|
|
265
|
+
|
|
266
|
+
Example:
|
|
267
|
+
>>> df = client.quant.ohlcv("000001")
|
|
268
|
+
>>> print(df[["open", "high", "low", "close", "volume"]])
|
|
269
|
+
"""
|
|
270
|
+
params: dict[str, Any] = {
|
|
271
|
+
"symbol": symbol,
|
|
272
|
+
"market": market,
|
|
273
|
+
}
|
|
274
|
+
if start_date:
|
|
275
|
+
params["start_date"] = start_date
|
|
276
|
+
if end_date:
|
|
277
|
+
params["end_date"] = end_date
|
|
278
|
+
|
|
279
|
+
response = self._client._get("/v1/quant/quotes/ohlcv", params=params)
|
|
280
|
+
return self._to_dataframe(response.get("datas", []))
|
|
281
|
+
|
|
282
|
+
def ohlcv_batch(
|
|
283
|
+
self,
|
|
284
|
+
symbols: list[str],
|
|
285
|
+
*,
|
|
286
|
+
market: StockMarket = "cn",
|
|
287
|
+
start_date: str | None = None,
|
|
288
|
+
end_date: str | None = None,
|
|
289
|
+
) -> pd.DataFrame:
|
|
290
|
+
"""
|
|
291
|
+
Get OHLCV data for multiple symbols
|
|
292
|
+
|
|
293
|
+
Args:
|
|
294
|
+
symbols: List of stock codes
|
|
295
|
+
market: Stock market ("cn", "hk", "us"), default "cn"
|
|
296
|
+
start_date: Start date (YYYY-MM-DD), default 1 month ago
|
|
297
|
+
end_date: End date (YYYY-MM-DD), default today
|
|
298
|
+
|
|
299
|
+
Returns:
|
|
300
|
+
DataFrame with OHLCV data sorted by date (descending), then by symbol
|
|
301
|
+
|
|
302
|
+
Example:
|
|
303
|
+
>>> df = client.quant.ohlcv_batch(["000001", "600519"])
|
|
304
|
+
>>> print(df[["symbol", "date", "close", "volume"]])
|
|
305
|
+
"""
|
|
306
|
+
data: dict[str, Any] = {
|
|
307
|
+
"symbols": symbols,
|
|
308
|
+
"market": market,
|
|
309
|
+
}
|
|
310
|
+
if start_date:
|
|
311
|
+
data["start_date"] = start_date
|
|
312
|
+
if end_date:
|
|
313
|
+
data["end_date"] = end_date
|
|
314
|
+
|
|
315
|
+
response = self._client._post("/v1/quant/quotes/ohlcv/batch", json=data)
|
|
316
|
+
return self._to_dataframe(response.get("datas", []))
|
|
317
|
+
|
|
318
|
+
# -------------------------------------------------------------------------
|
|
319
|
+
# Backtest
|
|
320
|
+
# -------------------------------------------------------------------------
|
|
321
|
+
|
|
322
|
+
def backtest(
|
|
323
|
+
self,
|
|
324
|
+
symbol: str,
|
|
325
|
+
formula: str,
|
|
326
|
+
start_date: str,
|
|
327
|
+
end_date: str,
|
|
328
|
+
*,
|
|
329
|
+
market: StockMarket = "cn",
|
|
330
|
+
initial_cash: float = 100000.0,
|
|
331
|
+
commission: float = 0.0,
|
|
332
|
+
stop_loss: float = 0.0,
|
|
333
|
+
sizer_percent: int = 99,
|
|
334
|
+
auto_close: bool = True,
|
|
335
|
+
) -> dict[str, Any]:
|
|
336
|
+
"""
|
|
337
|
+
Execute strategy backtest
|
|
338
|
+
|
|
339
|
+
Args:
|
|
340
|
+
symbol: Stock code
|
|
341
|
+
formula: Trading strategy formula using Mai-language syntax
|
|
342
|
+
start_date: Backtest start date (YYYY-MM-DD)
|
|
343
|
+
end_date: Backtest end date (YYYY-MM-DD)
|
|
344
|
+
market: Stock market ("cn", "hk", "us"), default "cn"
|
|
345
|
+
initial_cash: Initial capital (default: 100000.0)
|
|
346
|
+
commission: Commission rate (default: 0.0)
|
|
347
|
+
stop_loss: Stop loss setting (default: 0.0, no stop loss)
|
|
348
|
+
sizer_percent: Position percentage (default: 99%)
|
|
349
|
+
auto_close: Auto close positions (default: True)
|
|
350
|
+
|
|
351
|
+
Returns:
|
|
352
|
+
Backtest results including:
|
|
353
|
+
- success: Whether backtest succeeded
|
|
354
|
+
- initial_cash: Initial capital
|
|
355
|
+
- final_cash: Final capital
|
|
356
|
+
- total_return: Total return
|
|
357
|
+
- total_return_pct: Total return percentage
|
|
358
|
+
- max_drawdown: Maximum drawdown
|
|
359
|
+
- profit_factor: Profit factor
|
|
360
|
+
- win_rate: Win rate
|
|
361
|
+
- total_trades: Total number of trades
|
|
362
|
+
- trades: List of trade details
|
|
363
|
+
|
|
364
|
+
Example:
|
|
365
|
+
>>> result = client.quant.backtest(
|
|
366
|
+
... symbol="000001",
|
|
367
|
+
... formula="CROSS(MA(5), MA(20))", # Golden cross buy signal
|
|
368
|
+
... start_date="2023-01-01",
|
|
369
|
+
... end_date="2024-01-01",
|
|
370
|
+
... initial_cash=100000
|
|
371
|
+
... )
|
|
372
|
+
>>> print(f"Total Return: {result['total_return_pct']:.2%}")
|
|
373
|
+
>>> print(f"Max Drawdown: {result['max_drawdown']:.2%}")
|
|
374
|
+
>>> print(f"Win Rate: {result['win_rate']:.2%}")
|
|
375
|
+
"""
|
|
376
|
+
data = {
|
|
377
|
+
"symbol": symbol,
|
|
378
|
+
"formula": formula,
|
|
379
|
+
"start_date": start_date,
|
|
380
|
+
"end_date": end_date,
|
|
381
|
+
"market": market,
|
|
382
|
+
"initial_cash": initial_cash,
|
|
383
|
+
"commission": commission,
|
|
384
|
+
"stop_loss": stop_loss,
|
|
385
|
+
"sizer_percent": sizer_percent,
|
|
386
|
+
"auto_close": auto_close,
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
return self._client._post("/v1/quant/backtest", json=data)
|
|
390
|
+
|
|
391
|
+
# -------------------------------------------------------------------------
|
|
392
|
+
# Helper Methods
|
|
393
|
+
# -------------------------------------------------------------------------
|
|
394
|
+
|
|
395
|
+
def _to_dataframe(self, data: list[dict[str, Any]]) -> pd.DataFrame:
|
|
396
|
+
"""Convert API response to DataFrame"""
|
|
397
|
+
if not data:
|
|
398
|
+
return pd.DataFrame()
|
|
399
|
+
df = pd.DataFrame(data)
|
|
400
|
+
# Convert date columns if present
|
|
401
|
+
for col in ["date", "check_date", "trade_date"]:
|
|
402
|
+
if col in df.columns:
|
|
403
|
+
df[col] = pd.to_datetime(df[col])
|
|
404
|
+
return df
|
|
@@ -1,371 +0,0 @@
|
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|