ardata-sdk 1.0.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- ardata_sdk-1.0.0/PKG-INFO +74 -0
- ardata_sdk-1.0.0/README.md +53 -0
- ardata_sdk-1.0.0/ardata_sdk/__init__.py +28 -0
- ardata_sdk-1.0.0/ardata_sdk/api/__init__.py +15 -0
- ardata_sdk-1.0.0/ardata_sdk/api/billboard.py +16 -0
- ardata_sdk-1.0.0/ardata_sdk/api/client.py +195 -0
- ardata_sdk-1.0.0/ardata_sdk/api/extras.py +31 -0
- ardata_sdk-1.0.0/ardata_sdk/api/factors.py +45 -0
- ardata_sdk-1.0.0/ardata_sdk/api/fundamentals.py +156 -0
- ardata_sdk-1.0.0/ardata_sdk/api/history.py +79 -0
- ardata_sdk-1.0.0/ardata_sdk/api/index_concept.py +34 -0
- ardata_sdk-1.0.0/ardata_sdk/api/industry.py +22 -0
- ardata_sdk-1.0.0/ardata_sdk/api/margin.py +11 -0
- ardata_sdk-1.0.0/ardata_sdk/api/money_flow.py +19 -0
- ardata_sdk-1.0.0/ardata_sdk/api/mtss.py +17 -0
- ardata_sdk-1.0.0/ardata_sdk/api/price.py +74 -0
- ardata_sdk-1.0.0/ardata_sdk/api/securities.py +24 -0
- ardata_sdk-1.0.0/ardata_sdk/api/ticks.py +50 -0
- ardata_sdk-1.0.0/ardata_sdk/api/trade_days.py +11 -0
- ardata_sdk-1.0.0/ardata_sdk/config.py +30 -0
- ardata_sdk-1.0.0/ardata_sdk/finance/__init__.py +55 -0
- ardata_sdk-1.0.0/ardata_sdk/finance/columns.py +58 -0
- ardata_sdk-1.0.0/ardata_sdk/finance/query.py +45 -0
- ardata_sdk-1.0.0/ardata_sdk/finance/run_query.py +10 -0
- ardata_sdk-1.0.0/ardata_sdk/finance/tables.py +262 -0
- ardata_sdk-1.0.0/ardata_sdk.egg-info/PKG-INFO +74 -0
- ardata_sdk-1.0.0/ardata_sdk.egg-info/SOURCES.txt +31 -0
- ardata_sdk-1.0.0/ardata_sdk.egg-info/dependency_links.txt +1 -0
- ardata_sdk-1.0.0/ardata_sdk.egg-info/requires.txt +2 -0
- ardata_sdk-1.0.0/ardata_sdk.egg-info/top_level.txt +1 -0
- ardata_sdk-1.0.0/pyproject.toml +33 -0
- ardata_sdk-1.0.0/setup.cfg +4 -0
- ardata_sdk-1.0.0/setup.py +15 -0
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: ardata-sdk
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: ARData Python SDK - A-share market data client
|
|
5
|
+
Author-email: ARQuant <otreewen2020@users.noreply.github.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Keywords: a-share,stock,quant,china-market
|
|
8
|
+
Classifier: Development Status :: 4 - Beta
|
|
9
|
+
Classifier: Intended Audience :: Developers
|
|
10
|
+
Classifier: Intended Audience :: Financial and Insurance Industry
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
16
|
+
Requires-Python: >=3.8
|
|
17
|
+
Description-Content-Type: text/markdown
|
|
18
|
+
Requires-Dist: requests>=2.28
|
|
19
|
+
Requires-Dist: pandas>=1.5
|
|
20
|
+
Dynamic: requires-python
|
|
21
|
+
|
|
22
|
+
# ARData SDK
|
|
23
|
+
|
|
24
|
+
Python SDK for ARQuant market data platform.
|
|
25
|
+
|
|
26
|
+
## Install
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
pip install ardata-sdk
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Quick Start
|
|
33
|
+
|
|
34
|
+
```python
|
|
35
|
+
import ardata_sdk as ardata
|
|
36
|
+
|
|
37
|
+
# Initialize with your API key and server URL
|
|
38
|
+
ardata.init(token="your-api-key", base_url="https://ardata.example.com")
|
|
39
|
+
|
|
40
|
+
# Get daily price data
|
|
41
|
+
df = ardata.get_price(["000001.XSHE", "600519.XSHG"],
|
|
42
|
+
start_date="2024-01-01",
|
|
43
|
+
end_date="2024-12-31",
|
|
44
|
+
frequency="daily",
|
|
45
|
+
fields=["open", "close", "high", "low", "volume"],
|
|
46
|
+
fq="pre")
|
|
47
|
+
|
|
48
|
+
# Get all A-share securities
|
|
49
|
+
securities = ardata.get_all_securities()
|
|
50
|
+
|
|
51
|
+
# Get trade days
|
|
52
|
+
trade_days = ardata.get_trade_days(start_date="2024-01-01", end_date="2024-12-31")
|
|
53
|
+
|
|
54
|
+
# Get industry info
|
|
55
|
+
industries = ardata.get_industry(["000001.XSHE"])
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## API Reference
|
|
59
|
+
|
|
60
|
+
| Function | Description |
|
|
61
|
+
|----------|-------------|
|
|
62
|
+
| `get_price()` | Historical OHLCV data |
|
|
63
|
+
| `get_all_securities()` | List all securities |
|
|
64
|
+
| `get_security_info()` | Security details |
|
|
65
|
+
| `get_trade_days()` | Trading calendar |
|
|
66
|
+
| `get_industry()` | Industry classification |
|
|
67
|
+
| `get_index_stocks()` | Index constituents |
|
|
68
|
+
| `get_fundamentals()` | Financial data (valuation, balance sheet, etc.) |
|
|
69
|
+
| `get_money_flow()` | Capital flow data |
|
|
70
|
+
| `get_billboard_list()` | Dragon-tiger board data |
|
|
71
|
+
| `get_factor_values()` | Alpha factors |
|
|
72
|
+
| `history()` / `attribute_history()` | JQData-compatible history APIs |
|
|
73
|
+
| `get_ticks()` | Tick-level data |
|
|
74
|
+
| `finance.run_query()` | Financial table query DSL |
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# ARData SDK
|
|
2
|
+
|
|
3
|
+
Python SDK for ARQuant market data platform.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pip install ardata-sdk
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Quick Start
|
|
12
|
+
|
|
13
|
+
```python
|
|
14
|
+
import ardata_sdk as ardata
|
|
15
|
+
|
|
16
|
+
# Initialize with your API key and server URL
|
|
17
|
+
ardata.init(token="your-api-key", base_url="https://ardata.example.com")
|
|
18
|
+
|
|
19
|
+
# Get daily price data
|
|
20
|
+
df = ardata.get_price(["000001.XSHE", "600519.XSHG"],
|
|
21
|
+
start_date="2024-01-01",
|
|
22
|
+
end_date="2024-12-31",
|
|
23
|
+
frequency="daily",
|
|
24
|
+
fields=["open", "close", "high", "low", "volume"],
|
|
25
|
+
fq="pre")
|
|
26
|
+
|
|
27
|
+
# Get all A-share securities
|
|
28
|
+
securities = ardata.get_all_securities()
|
|
29
|
+
|
|
30
|
+
# Get trade days
|
|
31
|
+
trade_days = ardata.get_trade_days(start_date="2024-01-01", end_date="2024-12-31")
|
|
32
|
+
|
|
33
|
+
# Get industry info
|
|
34
|
+
industries = ardata.get_industry(["000001.XSHE"])
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## API Reference
|
|
38
|
+
|
|
39
|
+
| Function | Description |
|
|
40
|
+
|----------|-------------|
|
|
41
|
+
| `get_price()` | Historical OHLCV data |
|
|
42
|
+
| `get_all_securities()` | List all securities |
|
|
43
|
+
| `get_security_info()` | Security details |
|
|
44
|
+
| `get_trade_days()` | Trading calendar |
|
|
45
|
+
| `get_industry()` | Industry classification |
|
|
46
|
+
| `get_index_stocks()` | Index constituents |
|
|
47
|
+
| `get_fundamentals()` | Financial data (valuation, balance sheet, etc.) |
|
|
48
|
+
| `get_money_flow()` | Capital flow data |
|
|
49
|
+
| `get_billboard_list()` | Dragon-tiger board data |
|
|
50
|
+
| `get_factor_values()` | Alpha factors |
|
|
51
|
+
| `history()` / `attribute_history()` | JQData-compatible history APIs |
|
|
52
|
+
| `get_ticks()` | Tick-level data |
|
|
53
|
+
| `finance.run_query()` | Financial table query DSL |
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
from .config import init, get_client
|
|
2
|
+
from .api.price import get_price, get_bars
|
|
3
|
+
from .api.securities import get_security_info, get_all_securities
|
|
4
|
+
from .api.index_concept import get_index_stocks, get_industry_stocks, get_concept_stocks, get_index_weights
|
|
5
|
+
from .api.industry import get_industry, get_industries, get_concepts
|
|
6
|
+
from .api.fundamentals import get_valuation, get_fundamentals, get_fundamentals_continuously, get_history_fundamentals
|
|
7
|
+
from .api.money_flow import get_money_flow
|
|
8
|
+
from .api.mtss import get_mtss
|
|
9
|
+
from .api.billboard import get_billboard_list
|
|
10
|
+
from .api.margin import get_margincash_stocks, get_marginsec_stocks
|
|
11
|
+
from .api.extras import get_extras
|
|
12
|
+
from .api.history import history, attribute_history
|
|
13
|
+
from .api.trade_days import get_trade_days
|
|
14
|
+
from .api.factors import get_all_factors, get_factor_values
|
|
15
|
+
from .api.ticks import get_ticks, get_call_auction
|
|
16
|
+
from . import finance
|
|
17
|
+
from .finance import query
|
|
18
|
+
|
|
19
|
+
__all__ = [
|
|
20
|
+
'init', 'get_price', 'get_bars', 'get_security_info', 'get_all_securities',
|
|
21
|
+
'get_index_stocks', 'get_industry_stocks', 'get_concept_stocks',
|
|
22
|
+
'get_industry', 'get_industries', 'get_concepts', 'get_index_weights', 'get_valuation', 'get_fundamentals', 'get_fundamentals_continuously', 'get_history_fundamentals',
|
|
23
|
+
'get_money_flow', 'get_mtss', 'get_billboard_list',
|
|
24
|
+
'get_margincash_stocks', 'get_marginsec_stocks', 'get_extras',
|
|
25
|
+
'history', 'attribute_history',
|
|
26
|
+
'get_trade_days', 'get_all_factors', 'get_factor_values', 'get_ticks', 'get_call_auction',
|
|
27
|
+
'finance', 'query',
|
|
28
|
+
]
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
from .price import get_price, get_bars
|
|
2
|
+
from .securities import get_security_info, get_all_securities
|
|
3
|
+
from .index_concept import get_index_stocks, get_industry_stocks, get_concept_stocks, get_index_weights
|
|
4
|
+
from .industry import get_industry, get_industries, get_concepts
|
|
5
|
+
from .fundamentals import get_valuation, get_fundamentals, get_fundamentals_continuously, get_history_fundamentals
|
|
6
|
+
from .money_flow import get_money_flow
|
|
7
|
+
from .mtss import get_mtss
|
|
8
|
+
from .billboard import get_billboard_list
|
|
9
|
+
from .margin import get_margincash_stocks, get_marginsec_stocks
|
|
10
|
+
from .extras import get_extras
|
|
11
|
+
from .trade_days import get_trade_days
|
|
12
|
+
from .factors import get_all_factors, get_factor_values
|
|
13
|
+
from .ticks import get_ticks, get_call_auction
|
|
14
|
+
from .history import history, attribute_history
|
|
15
|
+
from ..finance.run_query import run_query
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
from ..config import get_client
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def get_billboard_list(stock_list=None, start_date=None, end_date=None, count=None):
|
|
5
|
+
client = get_client()
|
|
6
|
+
params = {}
|
|
7
|
+
if stock_list:
|
|
8
|
+
params['stock_list'] = ','.join(stock_list) if isinstance(stock_list, list) else stock_list
|
|
9
|
+
if start_date:
|
|
10
|
+
params['start_date'] = start_date
|
|
11
|
+
if end_date:
|
|
12
|
+
params['end_date'] = end_date
|
|
13
|
+
if count:
|
|
14
|
+
params['count'] = count
|
|
15
|
+
data = client.get('/get_billboard_list', params=params)
|
|
16
|
+
return client.to_dataframe(data)
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
"""ARData Proxy HTTP Client"""
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
import requests
|
|
5
|
+
import pandas as pd
|
|
6
|
+
from datetime import datetime, date
|
|
7
|
+
from typing import Optional, List, Union
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class APIClient:
|
|
11
|
+
"""ARData Proxy HTTP Client (计划中名为 ARDataClient)"""
|
|
12
|
+
def __init__(self, host: str = "http://localhost:8000", token: str = ""):
|
|
13
|
+
self.host = host.rstrip("/")
|
|
14
|
+
self.token = token
|
|
15
|
+
self.session = requests.Session()
|
|
16
|
+
self.session.headers.update({"Authorization": f"Bearer {token}"})
|
|
17
|
+
|
|
18
|
+
def _prefix(self, path: str) -> str:
|
|
19
|
+
"""server 路由都挂在 /api/v1 下, client 自动补前缀 (已有则不重复)."""
|
|
20
|
+
if not path.startswith("/api/v1"):
|
|
21
|
+
return "/api/v1" + path
|
|
22
|
+
return path
|
|
23
|
+
|
|
24
|
+
def _get(self, path: str, params: dict = None) -> dict:
|
|
25
|
+
resp = self.session.get(f"{self.host}{self._prefix(path)}", params=params, timeout=120)
|
|
26
|
+
resp.raise_for_status()
|
|
27
|
+
return resp.json()
|
|
28
|
+
|
|
29
|
+
def get(self, path: str, params: dict = None) -> dict:
|
|
30
|
+
return self._get(path, params)
|
|
31
|
+
|
|
32
|
+
def to_dataframe(self, data):
|
|
33
|
+
import pandas as pd
|
|
34
|
+
if not data:
|
|
35
|
+
return pd.DataFrame()
|
|
36
|
+
return pd.DataFrame(data)
|
|
37
|
+
|
|
38
|
+
def _post(self, path: str, json: dict) -> dict:
|
|
39
|
+
resp = self.session.post(f"{self.host}{self._prefix(path)}", json=json, timeout=30)
|
|
40
|
+
resp.raise_for_status()
|
|
41
|
+
return resp.json()
|
|
42
|
+
|
|
43
|
+
def get_token(self, api_key: str, expire_days: int = 30) -> dict:
|
|
44
|
+
"""用 api_key 登录拿 JWT (server /auth/login 端点)."""
|
|
45
|
+
return self._post("/api/v1/auth/login", {
|
|
46
|
+
"api_key": api_key, "expire_days": expire_days,
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
def get_price(self, security, start_date=None, end_date=None, frequency="daily",
|
|
50
|
+
fields=None, skip_paused=False, fq="pre", count=None, panel=True):
|
|
51
|
+
codes = ",".join(security) if isinstance(security, list) else security
|
|
52
|
+
params = {"security": codes, "frequency": frequency, "fq": fq if fq is not None else '', "skip_paused": str(skip_paused).lower()}
|
|
53
|
+
if start_date:
|
|
54
|
+
params["start_date"] = str(start_date)
|
|
55
|
+
if end_date:
|
|
56
|
+
params["end_date"] = str(end_date)
|
|
57
|
+
if fields:
|
|
58
|
+
params["fields"] = ",".join(fields) if isinstance(fields, list) else fields
|
|
59
|
+
|
|
60
|
+
data = self._get("/get_price", params)
|
|
61
|
+
if not data:
|
|
62
|
+
return pd.DataFrame()
|
|
63
|
+
|
|
64
|
+
df = pd.DataFrame(data)
|
|
65
|
+
if "code" in df.columns:
|
|
66
|
+
df.set_index(["code", "time" if frequency == "1m" else "date"], inplace=True)
|
|
67
|
+
|
|
68
|
+
if fields:
|
|
69
|
+
df = df[[c for c in fields if c in df.columns]]
|
|
70
|
+
return df
|
|
71
|
+
|
|
72
|
+
def get_trade_days(self, start_date=None, end_date=None):
|
|
73
|
+
params = {}
|
|
74
|
+
if start_date:
|
|
75
|
+
params["start_date"] = str(start_date)
|
|
76
|
+
if end_date:
|
|
77
|
+
params["end_date"] = str(end_date)
|
|
78
|
+
return self._get("/get_trade_days", params)
|
|
79
|
+
|
|
80
|
+
def get_all_securities(self, types=None, date=None):
|
|
81
|
+
params = {}
|
|
82
|
+
if types:
|
|
83
|
+
params["types"] = ",".join(types) if isinstance(types, list) else types
|
|
84
|
+
if date:
|
|
85
|
+
params["date"] = str(date)
|
|
86
|
+
data = self._get("/get_all_securities", params)
|
|
87
|
+
df = pd.DataFrame(data)
|
|
88
|
+
if "code" in df.columns:
|
|
89
|
+
df.set_index("code", inplace=True)
|
|
90
|
+
return df
|
|
91
|
+
|
|
92
|
+
def get_security_info(self, code, date=None):
|
|
93
|
+
data = self._get("/get_security_info", {"code": code})
|
|
94
|
+
return data
|
|
95
|
+
|
|
96
|
+
def get_industry(self, security, date=None):
|
|
97
|
+
codes = ",".join(security) if isinstance(security, list) else security
|
|
98
|
+
params = {"security": codes}
|
|
99
|
+
if date:
|
|
100
|
+
params["date"] = str(date)
|
|
101
|
+
return self._get("/get_industry", params)
|
|
102
|
+
|
|
103
|
+
def get_concept(self):
|
|
104
|
+
return self._get("/get_concepts")
|
|
105
|
+
|
|
106
|
+
def get_index_constituents(self, index_code, date=None):
|
|
107
|
+
params = {"index_code": index_code}
|
|
108
|
+
if date:
|
|
109
|
+
params["date"] = str(date)
|
|
110
|
+
return self._get("/get_index_stocks", params)
|
|
111
|
+
|
|
112
|
+
def get_industry_constituents(self, industry_code, date=None):
|
|
113
|
+
params = {"industry_code": industry_code}
|
|
114
|
+
if date:
|
|
115
|
+
params["date"] = str(date)
|
|
116
|
+
return self._get("/get_industry_stocks", params)
|
|
117
|
+
|
|
118
|
+
def get_index_weights(self, index_code, date=None):
|
|
119
|
+
params = {"index_code": index_code}
|
|
120
|
+
if date:
|
|
121
|
+
params["date"] = str(date)
|
|
122
|
+
return self._get("/get_index_weights", params)
|
|
123
|
+
|
|
124
|
+
def get_margin_stocks(self, date=None):
|
|
125
|
+
params = {"date": str(date)} if date else {}
|
|
126
|
+
return self._get("/get_margincash_stocks", params)
|
|
127
|
+
|
|
128
|
+
def get_ST_stocks(self):
|
|
129
|
+
return self._get("/get_extras", {"info": "is_st", "security_list": "*", "df": "false"})
|
|
130
|
+
|
|
131
|
+
def get_billboard_list(self, stock_list=None, start_date=None, end_date=None, count=None):
|
|
132
|
+
params = {}
|
|
133
|
+
if stock_list:
|
|
134
|
+
params["stock_list"] = ",".join(stock_list) if isinstance(stock_list, list) else stock_list
|
|
135
|
+
if start_date:
|
|
136
|
+
params["start_date"] = str(start_date)
|
|
137
|
+
if end_date:
|
|
138
|
+
params["end_date"] = str(end_date)
|
|
139
|
+
if count is not None:
|
|
140
|
+
params["count"] = count
|
|
141
|
+
return self._get("/get_billboard_list", params)
|
|
142
|
+
|
|
143
|
+
def get_fundamentals(self, query_object, date=None, statDate=None):
|
|
144
|
+
from ardata_sdk.finance.query import Query
|
|
145
|
+
if not isinstance(query_object, Query):
|
|
146
|
+
q = Query(query_object)
|
|
147
|
+
else:
|
|
148
|
+
q = query_object
|
|
149
|
+
if date is not None:
|
|
150
|
+
q._filters.append({'col': 'day' if q._table == 'valuation' else 'pubDate', 'op': 'lte', 'val': str(date)})
|
|
151
|
+
if statDate is not None:
|
|
152
|
+
s = str(statDate)
|
|
153
|
+
if 'q' in s:
|
|
154
|
+
year, qtr = s.split('q', 1)
|
|
155
|
+
quarter_end = {'1': '03-31', '2': '06-30', '3': '09-30', '4': '12-31'}.get(qtr)
|
|
156
|
+
if quarter_end:
|
|
157
|
+
q._filters.append({'col': 'statDate', 'op': 'eq', 'val': f'{year}-{quarter_end}'})
|
|
158
|
+
else:
|
|
159
|
+
q._filters.append({'col': 'statDate', 'op': 'lte', 'val': f'{s}-12-31'})
|
|
160
|
+
return self._query_finance(q._table, self._q_to_params(q))
|
|
161
|
+
|
|
162
|
+
def _q_to_params(self, q):
|
|
163
|
+
params = {}
|
|
164
|
+
for f in q._filters:
|
|
165
|
+
key = f['col'] if f['op'] == 'eq' else f"{f['col']}__{f['op']}"
|
|
166
|
+
params[key] = f['val']
|
|
167
|
+
if q._limit_val:
|
|
168
|
+
params['_limit'] = q._limit_val
|
|
169
|
+
if q._offset_val:
|
|
170
|
+
params['_offset'] = q._offset_val
|
|
171
|
+
if q._order_by_cols:
|
|
172
|
+
params['_order_by'] = ','.join(q._order_by_cols)
|
|
173
|
+
return params
|
|
174
|
+
|
|
175
|
+
def _query_finance(self, table_name: str, params: dict = None) -> pd.DataFrame:
|
|
176
|
+
data = self._get(f"/table/{table_name}", params)
|
|
177
|
+
if not data:
|
|
178
|
+
return pd.DataFrame()
|
|
179
|
+
df = pd.DataFrame(data)
|
|
180
|
+
if "id" in df.columns:
|
|
181
|
+
df.drop(columns=["id"], inplace=True)
|
|
182
|
+
return df
|
|
183
|
+
|
|
184
|
+
def get_factor_data(self, codes, start_date, end_date, fields=None):
|
|
185
|
+
params = {"securities": ",".join(codes) if isinstance(codes, list) else codes}
|
|
186
|
+
if start_date:
|
|
187
|
+
params["start_date"] = str(start_date)
|
|
188
|
+
if end_date:
|
|
189
|
+
params["end_date"] = str(end_date)
|
|
190
|
+
if fields:
|
|
191
|
+
params["factors"] = ",".join(fields)
|
|
192
|
+
return self._get("/get_factor_values", params)
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
ARDataClient = APIClient
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
from ..config import get_client
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def get_extras(info='is_st', security_list=None, start_date=None, end_date=None, df=True):
|
|
5
|
+
"""spec: get_extras(info, security_list, start_date, end_date, df=True).
|
|
6
|
+
df=True -> pandas.DataFrame, 行=日期(datetime), 列=股票代码.
|
|
7
|
+
df=False -> dict {code: numpy.ndarray(bool)}.
|
|
8
|
+
"""
|
|
9
|
+
import pandas as pd
|
|
10
|
+
import numpy as np
|
|
11
|
+
if security_list is None:
|
|
12
|
+
return pd.DataFrame() if df else {}
|
|
13
|
+
client = get_client()
|
|
14
|
+
list_str = ','.join(security_list) if isinstance(security_list, list) else security_list
|
|
15
|
+
params = {'info': info, 'security_list': list_str, 'df': str(df).lower()}
|
|
16
|
+
if start_date:
|
|
17
|
+
params['start_date'] = start_date
|
|
18
|
+
if end_date:
|
|
19
|
+
params['end_date'] = end_date
|
|
20
|
+
raw = client.get('/get_extras', params=params)
|
|
21
|
+
if not raw:
|
|
22
|
+
return pd.DataFrame() if df else {}
|
|
23
|
+
if df:
|
|
24
|
+
# server 返回 {index, columns, data}; data 是 {code: {day: value}}
|
|
25
|
+
from datetime import date as _date
|
|
26
|
+
idx = [_date.fromisoformat(d) for d in raw['index']]
|
|
27
|
+
cols = raw['columns']
|
|
28
|
+
data = {c: [raw['data'].get(c, {}).get(d) for d in raw['index']] for c in cols}
|
|
29
|
+
return pd.DataFrame(data, index=pd.DatetimeIndex(idx), columns=cols)
|
|
30
|
+
else:
|
|
31
|
+
return {c: np.array(list(raw.get(c, [])), dtype=bool) for c in raw}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
from ..config import get_client
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def get_all_factors():
|
|
5
|
+
"""获取所有可用因子名. 返回 list[str] (spec)."""
|
|
6
|
+
client = get_client()
|
|
7
|
+
data = client.get('/get_all_factors')
|
|
8
|
+
if isinstance(data, list):
|
|
9
|
+
# server 返回 [{factor: name}, ...] 或 [name, ...], 统一为 list[str]
|
|
10
|
+
if data and isinstance(data[0], dict):
|
|
11
|
+
return [d.get('factor') for d in data if d.get('factor')]
|
|
12
|
+
return list(data)
|
|
13
|
+
return []
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def get_factor_values(securities, factors, *, start_date=None, end_date=None, count=1):
|
|
17
|
+
"""spec: get_factor_values(securities, factors, start_date, end_date, count)
|
|
18
|
+
后两个参数 keyword-only 避免与历史 positional 调用混淆.
|
|
19
|
+
返回: dict[factor_name -> DataFrame(date x code)]"""
|
|
20
|
+
client = get_client()
|
|
21
|
+
params = {
|
|
22
|
+
'securities': ','.join(securities) if isinstance(securities, list) else securities,
|
|
23
|
+
'factors': ','.join(factors) if isinstance(factors, list) else factors,
|
|
24
|
+
'count': count,
|
|
25
|
+
}
|
|
26
|
+
if start_date:
|
|
27
|
+
params['start_date'] = start_date
|
|
28
|
+
if end_date:
|
|
29
|
+
params['end_date'] = end_date
|
|
30
|
+
data = client.get('/get_factor_values', params=params)
|
|
31
|
+
|
|
32
|
+
import pandas as pd
|
|
33
|
+
result = {}
|
|
34
|
+
for date_str, stocks in data.items():
|
|
35
|
+
for code, values in stocks.items():
|
|
36
|
+
for fname, fval in values.items():
|
|
37
|
+
if fname not in result:
|
|
38
|
+
result[fname] = {}
|
|
39
|
+
if date_str not in result[fname]:
|
|
40
|
+
result[fname][date_str] = {}
|
|
41
|
+
result[fname][date_str][code] = fval
|
|
42
|
+
|
|
43
|
+
for fname in result:
|
|
44
|
+
result[fname] = pd.DataFrame(result[fname]).T
|
|
45
|
+
return result
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
from ..config import get_client
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
# spec 字段名 -> 本地 valuation 表列名 (因 ARData 用了 'total_market_cap' 而本地是 'market_cap')
|
|
5
|
+
_VALUATION_FIELD_ALIASES = {
|
|
6
|
+
"total_market_cap": "market_cap",
|
|
7
|
+
"total_cap": "market_cap",
|
|
8
|
+
"mkt_cap": "market_cap",
|
|
9
|
+
# 其它字段 1-to-1 (pe_ratio, pb_ratio, ps_ratio, pcf_ratio, turnover_ratio 等)
|
|
10
|
+
"pe_ratio_ttm": "pe_ratio_lyr", # 简化映射
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def _translate_valuation_fields(fields):
|
|
15
|
+
"""spec -> 本地 列名映射; 不识别的字段保持原样 (服务端会 400)."""
|
|
16
|
+
if fields is None:
|
|
17
|
+
return None
|
|
18
|
+
items = fields.split(",") if isinstance(fields, str) else list(fields)
|
|
19
|
+
out = [_VALUATION_FIELD_ALIASES.get(f.strip(), f.strip()) for f in items]
|
|
20
|
+
return ",".join(out) if isinstance(fields, str) else out
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def get_valuation(security, start_date=None, end_date=None, fields=None, count=None):
|
|
24
|
+
"""spec: get_valuation(security, start_date, end_date, fields, count) -> DataFrame (index=day).
|
|
25
|
+
count: take the most recent N rows (ORDER BY day DESC LIMIT N).
|
|
26
|
+
spec 字段名 (如 total_market_cap) 自动翻译成本地表列名 (market_cap)."""
|
|
27
|
+
client = get_client()
|
|
28
|
+
params = {"security": security}
|
|
29
|
+
if start_date:
|
|
30
|
+
params["start_date"] = start_date
|
|
31
|
+
if end_date:
|
|
32
|
+
params["end_date"] = end_date
|
|
33
|
+
if fields is not None:
|
|
34
|
+
params["fields"] = _translate_valuation_fields(fields)
|
|
35
|
+
if count is not None:
|
|
36
|
+
params["count"] = count
|
|
37
|
+
data = client.get("/get_valuation", params=params)
|
|
38
|
+
df = client.to_dataframe(data)
|
|
39
|
+
if not df.empty and "day" in df.columns:
|
|
40
|
+
df = df.set_index("day")
|
|
41
|
+
return df
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def get_fundamentals(query_object, date=None, statDate=None):
|
|
45
|
+
"""spec: get_fundamentals(query_object, date, statDate) -> DataFrame.
|
|
46
|
+
query_object accepts a Query instance (recommended) or a table name string (back-compat).
|
|
47
|
+
Routes through /table/{table} via finance.run_query, applying date/statDate as
|
|
48
|
+
additional filter params on the table.
|
|
49
|
+
|
|
50
|
+
date: 查询日期, 传 '2015-10-15' 格式, 过滤 day <= date 或 pubDate <= date
|
|
51
|
+
statDate: 财报统计季度/年份, 传 '2014q1' 或 '2014', 过滤 statDate = 对应季末
|
|
52
|
+
"""
|
|
53
|
+
from ..finance import run_query
|
|
54
|
+
from ..finance.query import Query
|
|
55
|
+
if isinstance(query_object, Query):
|
|
56
|
+
q = query_object
|
|
57
|
+
else:
|
|
58
|
+
q = Query(query_object)
|
|
59
|
+
|
|
60
|
+
if date is not None:
|
|
61
|
+
from datetime import date as _date
|
|
62
|
+
from ..finance.columns import Compare
|
|
63
|
+
try:
|
|
64
|
+
parsed = str(_date.fromisoformat(str(date).replace('/', '-')))
|
|
65
|
+
except ValueError:
|
|
66
|
+
parsed = str(date)
|
|
67
|
+
if q._table == 'valuation':
|
|
68
|
+
q._filters.append({'col': 'day', 'op': 'lte', 'val': parsed})
|
|
69
|
+
else:
|
|
70
|
+
q._filters.append({'col': 'pubDate', 'op': 'lte', 'val': parsed})
|
|
71
|
+
|
|
72
|
+
if statDate is not None:
|
|
73
|
+
s = str(statDate)
|
|
74
|
+
if 'q' in s:
|
|
75
|
+
year, qtr = s.split('q', 1)
|
|
76
|
+
quarter_end = {'1': '03-31', '2': '06-30', '3': '09-30', '4': '12-31'}.get(qtr)
|
|
77
|
+
if quarter_end:
|
|
78
|
+
from ..finance.columns import Compare
|
|
79
|
+
q._filters.append({'col': 'statDate', 'op': 'eq', 'val': f'{year}-{quarter_end}'})
|
|
80
|
+
else:
|
|
81
|
+
from ..finance.columns import Compare
|
|
82
|
+
q._filters.append({'col': 'statDate', 'op': 'lte', 'val': f'{s}-12-31'})
|
|
83
|
+
|
|
84
|
+
return run_query(q)
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def get_fundamentals_continuously(query_object, end_date=None, count=None, panel=True):
|
|
88
|
+
"""spec: get_fundamentals_continuously(query_object, end_date, count, panel).
|
|
89
|
+
对估值/财务时间序列表取 end_date 之前 count 条.
|
|
90
|
+
panel=True (默认) -> dict[code -> DataFrame] (pandas.Panel 已弃用, 用 dict 替代).
|
|
91
|
+
panel=False -> 原始 DataFrame."""
|
|
92
|
+
import pandas as pd
|
|
93
|
+
from ..finance.query import Query
|
|
94
|
+
|
|
95
|
+
def _table_name(obj):
|
|
96
|
+
if isinstance(obj, Query):
|
|
97
|
+
return obj._table
|
|
98
|
+
return obj
|
|
99
|
+
|
|
100
|
+
table = _table_name(query_object)
|
|
101
|
+
code = None
|
|
102
|
+
if isinstance(query_object, Query):
|
|
103
|
+
for f in query_object._filters:
|
|
104
|
+
if f.get('col') == 'code' and f.get('op') == 'eq':
|
|
105
|
+
code = f.get('val')
|
|
106
|
+
break
|
|
107
|
+
|
|
108
|
+
client = get_client()
|
|
109
|
+
params = {'table': table}
|
|
110
|
+
if code:
|
|
111
|
+
params['code'] = code
|
|
112
|
+
if end_date:
|
|
113
|
+
params['end_date'] = end_date
|
|
114
|
+
if count is not None:
|
|
115
|
+
params['count'] = count
|
|
116
|
+
params['panel'] = str(panel).lower()
|
|
117
|
+
|
|
118
|
+
rows = client.get('/get_fundamentals_continuously', params=params)
|
|
119
|
+
df = pd.DataFrame(rows)
|
|
120
|
+
if not panel or df.empty or 'code' not in df.columns:
|
|
121
|
+
return df
|
|
122
|
+
codes = df['code'].unique().tolist()
|
|
123
|
+
if len(codes) == 1:
|
|
124
|
+
return df.drop(columns=['code'])
|
|
125
|
+
return {c: df[df['code'] == c].drop(columns=['code']) for c in codes}
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def get_history_fundamentals(security, fields, watch_date=None, stat_date=None,
|
|
129
|
+
count=1, interval="1q", stat_by_year=False):
|
|
130
|
+
"""spec: get_history_fundamentals(security, fields, watch_date, stat_date, count, interval, stat_by_year).
|
|
131
|
+
返回 list[dict]: 历史多个报告期数据, 按 statDate 升序.
|
|
132
|
+
|
|
133
|
+
- watch_date: 仅返回 pubDate <= watch_date 的报告
|
|
134
|
+
- stat_date: 'YYYY' (年) 或 'YYYYqN' (季); 只取 <= stat_date 的报告期
|
|
135
|
+
- interval: '1q' / '1y' (本参数只用于对齐 spec, 本地实现统一按 statDate 排序)
|
|
136
|
+
- stat_by_year: True 时强制使用 indicator 表
|
|
137
|
+
"""
|
|
138
|
+
import pandas as pd
|
|
139
|
+
client = get_client()
|
|
140
|
+
table = "indicator" if stat_by_year else "valuation"
|
|
141
|
+
params = {
|
|
142
|
+
"table": table,
|
|
143
|
+
"count": count,
|
|
144
|
+
"interval": interval,
|
|
145
|
+
"stat_by_year": str(stat_by_year).lower(),
|
|
146
|
+
}
|
|
147
|
+
if security:
|
|
148
|
+
params["security"] = security
|
|
149
|
+
if fields:
|
|
150
|
+
params["fields"] = ",".join(fields) if isinstance(fields, list) else fields
|
|
151
|
+
if watch_date:
|
|
152
|
+
params["watch_date"] = watch_date
|
|
153
|
+
if stat_date:
|
|
154
|
+
params["stat_date"] = stat_date
|
|
155
|
+
rows = client.get("/get_history_fundamentals", params=params)
|
|
156
|
+
return pd.DataFrame(rows)
|