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