ardata-sdk 1.0.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.
ardata_sdk/__init__.py ADDED
@@ -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)
@@ -0,0 +1,79 @@
1
+ from ..config import get_client
2
+
3
+
4
+ def attribute_history(security, count, unit='1d', fields=None,
5
+ skip_paused=True, df=True, fq='pre'):
6
+ """spec: attribute_history(security, count, unit, fields, skip_paused, df, fq).
7
+ 获取单个标的多个字段的历史数据。
8
+ df=True (default) -> DataFrame; df=False -> numpy array.
9
+ """
10
+ import pandas as pd
11
+ import numpy as np
12
+ end_date = _today_str()
13
+ start_date = _compute_start(count, unit)
14
+ from .price import get_price
15
+ df_price = get_price(security, start_date=start_date, end_date=end_date,
16
+ frequency=_unit_to_freq(unit), fields=fields,
17
+ skip_paused=skip_paused, fq=fq)
18
+ if df_price is None or df_price.empty:
19
+ return pd.DataFrame() if df else np.array([])
20
+ df_price = df_price.tail(count)
21
+ if df:
22
+ return df_price
23
+ return df_price.to_records(index=False)
24
+
25
+
26
+ def history(count, unit='1d', field=None, security_list=None,
27
+ df=True, skip_paused=False, fq='pre'):
28
+ """spec: history(count, unit, field, security_list, df, skip_paused, fq).
29
+ 获取多个标的单个字段的历史数据。
30
+ df=True -> DataFrame (列=股票代码, 行=日期); df=False -> dict of numpy arrays.
31
+ """
32
+ import pandas as pd
33
+ if security_list is None:
34
+ return pd.DataFrame() if df else {}
35
+ end_date = _today_str()
36
+ start_date = _compute_start(count, unit)
37
+ from .price import get_price
38
+ fields = [field] if field else None
39
+ df_price = get_price(security_list, start_date=start_date, end_date=end_date,
40
+ frequency=_unit_to_freq(unit), fields=fields,
41
+ skip_paused=skip_paused, fq=fq)
42
+ if df_price is None or df_price.empty:
43
+ return pd.DataFrame() if df else {}
44
+
45
+ result = {}
46
+ if 'code' in df_price.columns:
47
+ for code in df_price['code'].unique():
48
+ subset = df_price[df_price['code'] == code].tail(count)
49
+ result[code] = subset[field].values if field else subset
50
+ else:
51
+ codes = security_list if isinstance(security_list, list) else [security_list]
52
+ for code in codes:
53
+ subset = df_price.tail(count)
54
+ result[code] = subset[field].values if field else subset
55
+
56
+ if df:
57
+ return pd.DataFrame(result)
58
+ return result
59
+
60
+
61
+ def _unit_to_freq(unit):
62
+ _MAP = {
63
+ '1d': 'daily', '1m': '1m', '5m': '1m', '15m': '1m',
64
+ '30m': '1m', '60m': '1m', '120m': '1m',
65
+ '1w': 'daily', '1M': 'daily',
66
+ }
67
+ return _MAP.get(unit, 'daily')
68
+
69
+
70
+ def _today_str():
71
+ from datetime import date
72
+ return date.today().isoformat()
73
+
74
+
75
+ def _compute_start(count, unit):
76
+ from datetime import date, timedelta
77
+ buf = {'1d': 3, '1m': 0, '5m': 0, '15m': 0, '30m': 0, '60m': 0, '120m': 0, '1w': 10, '1M': 40}
78
+ days_back = buf.get(unit, 3) * count + 30
79
+ return (date.today() - timedelta(days=days_back)).isoformat()
@@ -0,0 +1,34 @@
1
+ from ..config import get_client
2
+
3
+
4
+ def get_index_stocks(index_symbol, date=None):
5
+ client = get_client()
6
+ params = {'index_code': index_symbol}
7
+ if date:
8
+ params['date'] = date
9
+ return client.get('/get_index_stocks', params=params)
10
+
11
+
12
+ def get_industry_stocks(industry_code, date=None):
13
+ client = get_client()
14
+ params = {'industry_code': industry_code}
15
+ if date:
16
+ params['date'] = date
17
+ return client.get('/get_industry_stocks', params=params)
18
+
19
+
20
+ def get_concept_stocks(concept_code, date=None):
21
+ client = get_client()
22
+ params = {'concept_code': concept_code}
23
+ if date:
24
+ params['date'] = date
25
+ return client.get('/get_concept_stocks', params=params)
26
+
27
+
28
+ def get_index_weights(index_id, date=None):
29
+ """获取指数成分股权重. 返回: list[dict{index_code, stock_code, weight, date, display_name}]"""
30
+ client = get_client()
31
+ params = {'index_code': index_id}
32
+ if date:
33
+ params['date'] = date
34
+ return client.get('/get_index_weights', params=params)
@@ -0,0 +1,22 @@
1
+ from ..config import get_client
2
+
3
+
4
+ def get_industry(security, date=None):
5
+ client = get_client()
6
+ params = {'security': security}
7
+ if date:
8
+ params['date'] = date
9
+ return client.get('/get_industry', params=params)
10
+
11
+
12
+ def get_industries(industry_type='sw_l1'):
13
+ """获取行业列表. industry_type in {jq_l1, jq_l2, sw_l1, sw_l2, zjw}.
14
+ 返回: list[dict{industry_code, industry_name}]"""
15
+ client = get_client()
16
+ return client.get('/get_industries', params={'type': industry_type})
17
+
18
+
19
+ def get_concepts():
20
+ """获取所有概念列表. 返回: list[dict{code}]"""
21
+ client = get_client()
22
+ return client.get('/get_concepts')
@@ -0,0 +1,11 @@
1
+ from ..config import get_client
2
+
3
+
4
+ def get_margincash_stocks(date=None):
5
+ client = get_client()
6
+ return client.get('/get_margincash_stocks', params={'date': date} if date else {})
7
+
8
+
9
+ def get_marginsec_stocks(date=None):
10
+ client = get_client()
11
+ return client.get('/get_marginsec_stocks', params={'date': date} if date else {})
@@ -0,0 +1,19 @@
1
+ from ..config import get_client
2
+
3
+
4
+ def get_money_flow(security_list=None, start_date=None, end_date=None, fields=None, count=None, *, security=None):
5
+ if security_list is None and security is not None:
6
+ security_list = security
7
+ client = get_client()
8
+ list_str = ','.join(security_list) if isinstance(security_list, list) else security_list
9
+ params = {'security': list_str}
10
+ if start_date:
11
+ params['start_date'] = start_date
12
+ if end_date:
13
+ params['end_date'] = end_date
14
+ if fields:
15
+ params['fields'] = ','.join(fields) if isinstance(fields, list) else fields
16
+ if count:
17
+ params['count'] = count
18
+ data = client.get('/get_money_flow', params=params)
19
+ return client.to_dataframe(data)