akshare-one 0.1.3__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.
- akshare_one/__init__.py +2 -3
- akshare_one/financial.py +7 -4
- akshare_one/insider.py +4 -5
- akshare_one/modules/financial/base.py +41 -0
- akshare_one/modules/financial/factory.py +44 -0
- akshare_one/{adapters → modules/financial}/sina.py +22 -203
- akshare_one/modules/historical/base.py +64 -0
- akshare_one/modules/historical/eastmoney.py +242 -0
- akshare_one/modules/historical/factory.py +46 -0
- akshare_one/modules/historical/sina.py +219 -0
- akshare_one/modules/insider/base.py +78 -0
- akshare_one/modules/insider/factory.py +44 -0
- akshare_one/{adapters → modules/insider}/xueqiu.py +17 -76
- akshare_one/modules/news/base.py +51 -0
- akshare_one/modules/news/eastmoney.py +48 -0
- akshare_one/modules/news/factory.py +44 -0
- akshare_one/modules/realtime/base.py +68 -0
- akshare_one/modules/realtime/eastmoney.py +58 -0
- akshare_one/modules/realtime/factory.py +46 -0
- akshare_one/modules/realtime/xueqiu.py +61 -0
- akshare_one/modules/utils.py +10 -0
- akshare_one/news.py +3 -4
- akshare_one/stock.py +16 -30
- {akshare_one-0.1.3.dist-info → akshare_one-0.2.0.dist-info}/METADATA +2 -2
- akshare_one-0.2.0.dist-info/RECORD +29 -0
- {akshare_one-0.1.3.dist-info → akshare_one-0.2.0.dist-info}/WHEEL +1 -1
- akshare_one/adapters/__init__.py +0 -7
- akshare_one/adapters/eastmoney.py +0 -353
- akshare_one-0.1.3.dist-info/RECORD +0 -15
- /akshare_one/{adapters/cache → modules}/cache.py +0 -0
- {akshare_one-0.1.3.dist-info → akshare_one-0.2.0.dist-info}/licenses/LICENSE +0 -0
- {akshare_one-0.1.3.dist-info → akshare_one-0.2.0.dist-info}/top_level.txt +0 -0
@@ -1,353 +0,0 @@
|
|
1
|
-
from typing import Optional
|
2
|
-
import pandas as pd
|
3
|
-
import akshare as ak
|
4
|
-
from cachetools import cached
|
5
|
-
from .cache.cache import CACHE_CONFIG
|
6
|
-
|
7
|
-
|
8
|
-
class EastMoneyAdapter:
|
9
|
-
"""Adapter for EastMoney historical stock data API"""
|
10
|
-
|
11
|
-
@cached(
|
12
|
-
CACHE_CONFIG["hist_data_cache"],
|
13
|
-
key=lambda self,
|
14
|
-
symbol,
|
15
|
-
interval,
|
16
|
-
interval_multiplier,
|
17
|
-
start_date,
|
18
|
-
end_date,
|
19
|
-
adjust: (
|
20
|
-
"eastmoney",
|
21
|
-
symbol,
|
22
|
-
interval,
|
23
|
-
interval_multiplier,
|
24
|
-
start_date,
|
25
|
-
end_date,
|
26
|
-
adjust,
|
27
|
-
),
|
28
|
-
)
|
29
|
-
def get_hist_data(
|
30
|
-
self,
|
31
|
-
symbol: str,
|
32
|
-
interval: str = "day",
|
33
|
-
interval_multiplier: int = 1,
|
34
|
-
start_date: str = "1970-01-01",
|
35
|
-
end_date: str = "2030-12-31",
|
36
|
-
adjust: str = "none",
|
37
|
-
) -> pd.DataFrame:
|
38
|
-
"""获取东方财富历史行情数据
|
39
|
-
|
40
|
-
Args:
|
41
|
-
symbol: Unified symbol format (e.g. '600000')
|
42
|
-
interval: Time granularity ('minute','hour','day','week','month','year')
|
43
|
-
interval_multiplier: Interval multiplier (e.g. 5 for 5 minutes)
|
44
|
-
start_date: Start date in YYYY-MM-DD format (will be converted to YYYYMMDD)
|
45
|
-
end_date: End date in YYYY-MM-DD format (will be converted to YYYYMMDD)
|
46
|
-
adjust: Adjustment type ('none','qfq','hfq')
|
47
|
-
|
48
|
-
Returns:
|
49
|
-
Standardized DataFrame with OHLCV data
|
50
|
-
"""
|
51
|
-
# Map standard interval to akshare supported periods
|
52
|
-
interval = interval.lower()
|
53
|
-
if interval == "minute":
|
54
|
-
if interval_multiplier < 1:
|
55
|
-
raise ValueError("Minute interval multiplier must be >= 1")
|
56
|
-
|
57
|
-
start_date = (
|
58
|
-
f"{start_date} 09:30:00" if " " not in start_date else start_date
|
59
|
-
)
|
60
|
-
end_date = f"{end_date} 15:00:00" if " " not in end_date else end_date
|
61
|
-
|
62
|
-
raw_df = ak.stock_zh_a_hist_min_em(
|
63
|
-
symbol=symbol,
|
64
|
-
period="1",
|
65
|
-
start_date=start_date,
|
66
|
-
end_date=end_date,
|
67
|
-
adjust=adjust if adjust != "none" else "",
|
68
|
-
)
|
69
|
-
# Resample the data to the desired minute interval
|
70
|
-
raw_df["时间"] = pd.to_datetime(raw_df["时间"])
|
71
|
-
raw_df = raw_df.set_index("时间")
|
72
|
-
resampled = raw_df.resample(f"{interval_multiplier}min").agg(
|
73
|
-
{
|
74
|
-
"开盘": "first",
|
75
|
-
"最高": "max",
|
76
|
-
"最低": "min",
|
77
|
-
"收盘": "last",
|
78
|
-
"成交量": "sum",
|
79
|
-
"成交额": "sum",
|
80
|
-
}
|
81
|
-
)
|
82
|
-
raw_df = resampled.reset_index()
|
83
|
-
return self._clean_minute_data(raw_df, str(interval_multiplier))
|
84
|
-
elif interval == "hour":
|
85
|
-
if interval_multiplier < 1:
|
86
|
-
raise ValueError("Hour interval multiplier must be >= 1")
|
87
|
-
|
88
|
-
start_date = (
|
89
|
-
f"{start_date} 09:30:00" if " " not in start_date else start_date
|
90
|
-
)
|
91
|
-
end_date = f"{end_date} 15:00:00" if " " not in end_date else end_date
|
92
|
-
|
93
|
-
raw_df = ak.stock_zh_a_hist_min_em(
|
94
|
-
symbol=symbol,
|
95
|
-
period="60",
|
96
|
-
start_date=start_date,
|
97
|
-
end_date=end_date,
|
98
|
-
adjust=adjust if adjust != "none" else "",
|
99
|
-
)
|
100
|
-
|
101
|
-
# Resample the data to the desired hour interval
|
102
|
-
raw_df["时间"] = pd.to_datetime(raw_df["时间"])
|
103
|
-
raw_df = raw_df.set_index("时间")
|
104
|
-
resampled = raw_df.resample(f"{interval_multiplier}h").agg(
|
105
|
-
{
|
106
|
-
"开盘": "first",
|
107
|
-
"最高": "max",
|
108
|
-
"最低": "min",
|
109
|
-
"收盘": "last",
|
110
|
-
"成交量": "sum",
|
111
|
-
"成交额": "sum",
|
112
|
-
}
|
113
|
-
)
|
114
|
-
raw_df = resampled.reset_index()
|
115
|
-
|
116
|
-
return self._clean_minute_data(raw_df, f"{interval_multiplier}H")
|
117
|
-
elif interval == "day":
|
118
|
-
period = "daily"
|
119
|
-
elif interval == "week":
|
120
|
-
period = "weekly"
|
121
|
-
elif interval == "month":
|
122
|
-
period = "monthly"
|
123
|
-
elif interval == "year":
|
124
|
-
period = "monthly" # use monthly for yearly data
|
125
|
-
interval_multiplier = 12 * interval_multiplier
|
126
|
-
else:
|
127
|
-
raise ValueError(f"Unsupported interval: {interval}")
|
128
|
-
|
129
|
-
# Convert date format from YYYY-MM-DD to YYYYMMDD if needed
|
130
|
-
start_date = start_date.replace("-", "") if "-" in start_date else start_date
|
131
|
-
end_date = end_date.replace("-", "") if "-" in end_date else end_date
|
132
|
-
|
133
|
-
# Fetch raw data from akshare
|
134
|
-
raw_df = ak.stock_zh_a_hist(
|
135
|
-
symbol=symbol,
|
136
|
-
period=period, # daily/weekly/monthly
|
137
|
-
start_date=start_date,
|
138
|
-
end_date=end_date,
|
139
|
-
adjust=adjust if adjust != "none" else "",
|
140
|
-
)
|
141
|
-
|
142
|
-
if interval_multiplier > 1:
|
143
|
-
raw_df = self._resample_data(raw_df, interval, interval_multiplier)
|
144
|
-
|
145
|
-
# Standardize the data format
|
146
|
-
return self._clean_data(raw_df)
|
147
|
-
|
148
|
-
@cached(
|
149
|
-
CACHE_CONFIG["realtime_cache"],
|
150
|
-
key=lambda self, symbol=None: f"eastmoney_{symbol if symbol else 'all'}",
|
151
|
-
)
|
152
|
-
def get_realtime_data(self, symbol: Optional[str] = None) -> pd.DataFrame:
|
153
|
-
"""获取沪深京A股实时行情数据"""
|
154
|
-
raw_df = ak.stock_zh_a_spot_em()
|
155
|
-
df = self._clean_spot_data(raw_df)
|
156
|
-
if symbol:
|
157
|
-
df = df[df["symbol"] == symbol].reset_index(drop=True)
|
158
|
-
return df
|
159
|
-
|
160
|
-
def _resample_data(
|
161
|
-
self, df: pd.DataFrame, interval: str, multiplier: int
|
162
|
-
) -> pd.DataFrame:
|
163
|
-
"""Resample the data based on the given interval and multiplier"""
|
164
|
-
if interval == "day":
|
165
|
-
freq = f"{multiplier}D"
|
166
|
-
elif interval == "week":
|
167
|
-
freq = f"{multiplier}W-MON"
|
168
|
-
elif interval == "month":
|
169
|
-
freq = f"{multiplier}MS"
|
170
|
-
elif interval == "year":
|
171
|
-
freq = f"{multiplier}AS-JAN"
|
172
|
-
|
173
|
-
df["日期"] = pd.to_datetime(df["日期"])
|
174
|
-
df = df.set_index("日期")
|
175
|
-
resampled = df.resample(freq).agg(
|
176
|
-
{
|
177
|
-
"开盘": "first",
|
178
|
-
"最高": "max",
|
179
|
-
"最低": "min",
|
180
|
-
"收盘": "last",
|
181
|
-
"成交量": "sum",
|
182
|
-
}
|
183
|
-
)
|
184
|
-
return resampled.reset_index()
|
185
|
-
|
186
|
-
def _clean_minute_data(self, raw_df: pd.DataFrame, period: str) -> pd.DataFrame:
|
187
|
-
if period == "1":
|
188
|
-
column_mapping = {
|
189
|
-
"时间": "timestamp",
|
190
|
-
"开盘": "open",
|
191
|
-
"收盘": "close",
|
192
|
-
"最高": "high",
|
193
|
-
"最低": "low",
|
194
|
-
"成交量": "volume",
|
195
|
-
"成交额": "amount",
|
196
|
-
"均价": "vwap",
|
197
|
-
}
|
198
|
-
else:
|
199
|
-
column_mapping = {
|
200
|
-
"时间": "timestamp",
|
201
|
-
"开盘": "open",
|
202
|
-
"收盘": "close",
|
203
|
-
"最高": "high",
|
204
|
-
"最低": "low",
|
205
|
-
"涨跌幅": "pct_change",
|
206
|
-
"涨跌额": "change",
|
207
|
-
"成交量": "volume",
|
208
|
-
"成交额": "amount",
|
209
|
-
"振幅": "amplitude",
|
210
|
-
"换手率": "turnover",
|
211
|
-
}
|
212
|
-
|
213
|
-
df = raw_df.rename(columns=column_mapping)
|
214
|
-
|
215
|
-
if "timestamp" in df.columns:
|
216
|
-
df["timestamp"] = (
|
217
|
-
pd.to_datetime(df["timestamp"])
|
218
|
-
.dt.tz_localize("Asia/Shanghai")
|
219
|
-
.dt.tz_convert("UTC")
|
220
|
-
)
|
221
|
-
standard_columns = [
|
222
|
-
"timestamp",
|
223
|
-
"open",
|
224
|
-
"high",
|
225
|
-
"low",
|
226
|
-
"close",
|
227
|
-
"volume",
|
228
|
-
]
|
229
|
-
return df[[col for col in standard_columns if col in df.columns]]
|
230
|
-
|
231
|
-
def _clean_data(self, raw_df: pd.DataFrame, adjust: str = "none") -> pd.DataFrame:
|
232
|
-
"""清理和标准化历史数据格式
|
233
|
-
|
234
|
-
Args:
|
235
|
-
raw_df: Raw DataFrame from EastMoney API
|
236
|
-
adjust: Adjustment type ('none','qfq','hfq')
|
237
|
-
|
238
|
-
Returns:
|
239
|
-
Standardized DataFrame with consistent columns
|
240
|
-
"""
|
241
|
-
# Check if required columns exist in raw data
|
242
|
-
required_columns = {
|
243
|
-
"日期": "timestamp",
|
244
|
-
"开盘": "open",
|
245
|
-
"收盘": "close",
|
246
|
-
"最高": "high",
|
247
|
-
"最低": "low",
|
248
|
-
"成交量": "volume",
|
249
|
-
}
|
250
|
-
|
251
|
-
# Find available columns in raw data
|
252
|
-
available_columns = {}
|
253
|
-
for src_col, target_col in required_columns.items():
|
254
|
-
if src_col in raw_df.columns:
|
255
|
-
available_columns[src_col] = target_col
|
256
|
-
|
257
|
-
if not available_columns:
|
258
|
-
raise ValueError("Raw data does not contain any expected columns")
|
259
|
-
|
260
|
-
# Rename available columns
|
261
|
-
df = raw_df.rename(columns=available_columns)
|
262
|
-
|
263
|
-
# Process timestamp if available
|
264
|
-
if "timestamp" in df.columns:
|
265
|
-
df = df.assign(
|
266
|
-
timestamp=lambda x: pd.to_datetime(x["timestamp"])
|
267
|
-
.dt.tz_localize("Asia/Shanghai")
|
268
|
-
.dt.tz_convert("UTC")
|
269
|
-
)
|
270
|
-
|
271
|
-
# Process volume if available
|
272
|
-
if "volume" in df.columns:
|
273
|
-
df = df.assign(volume=lambda x: x["volume"].astype("int64"))
|
274
|
-
|
275
|
-
# Process adjustment flag
|
276
|
-
if adjust != "none":
|
277
|
-
df = df.assign(is_adjusted=lambda x: x["adjust"] != "none")
|
278
|
-
else:
|
279
|
-
df = df.assign(is_adjusted=False)
|
280
|
-
|
281
|
-
# Select available standardized columns
|
282
|
-
standard_columns = [
|
283
|
-
"timestamp",
|
284
|
-
"open",
|
285
|
-
"high",
|
286
|
-
"low",
|
287
|
-
"close",
|
288
|
-
"volume",
|
289
|
-
]
|
290
|
-
return df[[col for col in standard_columns if col in df.columns]]
|
291
|
-
|
292
|
-
@cached(CACHE_CONFIG["news_cache"], key=lambda self, symbol: f"eastmoney_{symbol}")
|
293
|
-
def get_news_data(self, symbol: str) -> pd.DataFrame:
|
294
|
-
"""获取东方财富个股新闻数据"""
|
295
|
-
raw_df = ak.stock_news_em(symbol=symbol)
|
296
|
-
|
297
|
-
column_mapping = {
|
298
|
-
"关键词": "keyword",
|
299
|
-
"新闻标题": "title",
|
300
|
-
"新闻内容": "content",
|
301
|
-
"发布时间": "publish_time",
|
302
|
-
"文章来源": "source",
|
303
|
-
"新闻链接": "url",
|
304
|
-
}
|
305
|
-
|
306
|
-
df = raw_df.rename(columns=column_mapping)
|
307
|
-
|
308
|
-
if "publish_time" in df.columns:
|
309
|
-
df = df.assign(
|
310
|
-
publish_time=lambda x: pd.to_datetime(x["publish_time"])
|
311
|
-
.dt.tz_localize("Asia/Shanghai")
|
312
|
-
.dt.tz_convert("UTC")
|
313
|
-
)
|
314
|
-
|
315
|
-
return df
|
316
|
-
|
317
|
-
def _clean_spot_data(self, raw_df: pd.DataFrame) -> pd.DataFrame:
|
318
|
-
"""清理和标准化实时行情数据"""
|
319
|
-
|
320
|
-
column_mapping = {
|
321
|
-
"代码": "symbol",
|
322
|
-
"最新价": "price",
|
323
|
-
"涨跌额": "change",
|
324
|
-
"涨跌幅": "pct_change",
|
325
|
-
"成交量": "volume",
|
326
|
-
"成交额": "amount",
|
327
|
-
"今开": "open",
|
328
|
-
"最高": "high",
|
329
|
-
"最低": "low",
|
330
|
-
"昨收": "prev_close",
|
331
|
-
}
|
332
|
-
|
333
|
-
df = raw_df.rename(columns=column_mapping)
|
334
|
-
|
335
|
-
# Change time to UTC
|
336
|
-
df = df.assign(
|
337
|
-
timestamp=lambda x: pd.Timestamp.now(tz="Asia/Shanghai").tz_convert("UTC")
|
338
|
-
)
|
339
|
-
|
340
|
-
required_columns = [
|
341
|
-
"symbol",
|
342
|
-
"price",
|
343
|
-
"change",
|
344
|
-
"pct_change",
|
345
|
-
"timestamp",
|
346
|
-
"volume",
|
347
|
-
"amount",
|
348
|
-
"open",
|
349
|
-
"high",
|
350
|
-
"low",
|
351
|
-
"prev_close",
|
352
|
-
]
|
353
|
-
return df[required_columns]
|
@@ -1,15 +0,0 @@
|
|
1
|
-
akshare_one/__init__.py,sha256=xWUyTnh1Tv-2mEPxDmk5giUPwhrsoAuZcyoW6mAbX4k,923
|
2
|
-
akshare_one/financial.py,sha256=fCi6FPtPc28id7nQf8b3i2bh5vSCpZTmnPQ9kKr2n8Y,1215
|
3
|
-
akshare_one/insider.py,sha256=Vz1yIvVlk_FVLhqFHdR-CCH5prfO12_CVzyAnrKgH4g,1199
|
4
|
-
akshare_one/news.py,sha256=qUeX_1SnViDXBAb4Gcl28ow1qvMugEEPL75JgJhLbCA,707
|
5
|
-
akshare_one/stock.py,sha256=w2gSjtbtcwyJ-qFpo0BJO7gxVrDur6Sun8en-A6LMb4,2727
|
6
|
-
akshare_one/adapters/__init__.py,sha256=0AVeL7uukxjmkBn4xmq_IGKrZo3LfAWRWYKagSMgD5s,252
|
7
|
-
akshare_one/adapters/eastmoney.py,sha256=nRs61bR_C12gOnqNBpo5-_Vqamt70-lMPr6vlxwPwuo,11780
|
8
|
-
akshare_one/adapters/sina.py,sha256=wD67NLtqDwNzmPuxnqhrRFCyTkhSuiXXuP4a8t1BsBU,16789
|
9
|
-
akshare_one/adapters/xueqiu.py,sha256=u_r3UtlykIZLa86flcqlc4xT8swFN_xLVkRbceNSciA,6073
|
10
|
-
akshare_one/adapters/cache/cache.py,sha256=47A80Xtfr4gkKvEfBQrT8Dz8hNFR769rBa2gU6ew25s,373
|
11
|
-
akshare_one-0.1.3.dist-info/licenses/LICENSE,sha256=Gg6A1GNSJCZWQ73aHJ7TXOa0i8RQ3FejZCTZ6Db07cU,1066
|
12
|
-
akshare_one-0.1.3.dist-info/METADATA,sha256=jEtaMyAF4HllS5DZFdwcYIzT1C6eRkNOvBbfTqvTJX8,1865
|
13
|
-
akshare_one-0.1.3.dist-info/WHEEL,sha256=DnLRTWE75wApRYVsjgc6wsVswC54sMSJhAEd4xhDpBk,91
|
14
|
-
akshare_one-0.1.3.dist-info/top_level.txt,sha256=kNiucyLVAGa89wmUSpXbBLWD7pF_RuahuiaOfLHZSyw,12
|
15
|
-
akshare_one-0.1.3.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|