akshare-one 0.2.3__py3-none-any.whl → 0.3.1__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/indicators.py +395 -0
- akshare_one/modules/eastmoney/client.py +2 -2
- akshare_one/modules/historical/base.py +8 -0
- akshare_one/modules/historical/sina.py +36 -0
- akshare_one/modules/indicators/__init__.py +0 -0
- akshare_one/modules/indicators/base.py +158 -0
- akshare_one/modules/indicators/factory.py +33 -0
- akshare_one/modules/indicators/simple.py +230 -0
- akshare_one/modules/indicators/talib.py +263 -0
- akshare_one/modules/insider/xueqiu.py +1 -1
- {akshare_one-0.2.3.dist-info → akshare_one-0.3.1.dist-info}/METADATA +10 -6
- {akshare_one-0.2.3.dist-info → akshare_one-0.3.1.dist-info}/RECORD +15 -9
- {akshare_one-0.2.3.dist-info → akshare_one-0.3.1.dist-info}/WHEEL +0 -0
- {akshare_one-0.2.3.dist-info → akshare_one-0.3.1.dist-info}/licenses/LICENSE +0 -0
- {akshare_one-0.2.3.dist-info → akshare_one-0.3.1.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,395 @@
|
|
1
|
+
"""Technical indicators module
|
2
|
+
|
3
|
+
Provides common technical analysis indicators like:
|
4
|
+
- Simple Moving Average (SMA)
|
5
|
+
- Exponential Moving Average (EMA)
|
6
|
+
- Relative Strength Index (RSI)
|
7
|
+
- Moving Average Convergence Divergence (MACD)
|
8
|
+
- Bollinger Bands (BBANDS)
|
9
|
+
- Stochastic Oscillator (STOCH)
|
10
|
+
- Average True Range (ATR)
|
11
|
+
- Commodity Channel Index (CCI)
|
12
|
+
- Average Directional Index (ADX)
|
13
|
+
- Williams' %R (WILLR)
|
14
|
+
- Chaikin A/D Line (AD)
|
15
|
+
- Chaikin A/D Oscillator (ADOSC)
|
16
|
+
- On Balance Volume (OBV)
|
17
|
+
- Momentum (MOM)
|
18
|
+
- Parabolic SAR (SAR)
|
19
|
+
- Time Series Forecast (TSF)
|
20
|
+
- Absolute Price Oscillator (APO)
|
21
|
+
- Aroon (AROON)
|
22
|
+
- Aroon Oscillator (AROONOSC)
|
23
|
+
- Balance of Power (BOP)
|
24
|
+
- Chande Momentum Oscillator (CMO)
|
25
|
+
- Directional Movement Index (DX)
|
26
|
+
- Money Flow Index (MFI)
|
27
|
+
- Minus Directional Indicator (MINUS_DI)
|
28
|
+
- Minus Directional Movement (MINUS_DM)
|
29
|
+
- Plus Directional Indicator (PLUS_DI)
|
30
|
+
- Plus Directional Movement (PLUS_DM)
|
31
|
+
- Percentage Price Oscillator (PPO)
|
32
|
+
- Rate of change (ROC)
|
33
|
+
- Rate of change Percentage (ROCP)
|
34
|
+
- Rate of change ratio (ROCR)
|
35
|
+
- Rate of change ratio 100 scale (ROCR100)
|
36
|
+
- 1-day Rate of Change (ROC) of a Triple Smooth EMA (TRIX)
|
37
|
+
- Ultimate Oscillator (ULTOSC)
|
38
|
+
"""
|
39
|
+
|
40
|
+
import pandas as pd
|
41
|
+
from .modules.indicators.factory import IndicatorFactory
|
42
|
+
|
43
|
+
|
44
|
+
def get_sma(
|
45
|
+
df: pd.DataFrame, window: int = 20, calculator_type: str = "talib"
|
46
|
+
) -> pd.DataFrame:
|
47
|
+
"""Calculate Simple Moving Average
|
48
|
+
|
49
|
+
Args:
|
50
|
+
df: DataFrame with 'close' column
|
51
|
+
window: Lookback window size
|
52
|
+
calculator_type: ('talib', 'simple')
|
53
|
+
"""
|
54
|
+
calculator = IndicatorFactory.get_calculator(calculator_type)
|
55
|
+
return calculator.calculate_sma(df, window)
|
56
|
+
|
57
|
+
|
58
|
+
def get_ema(
|
59
|
+
df: pd.DataFrame, window: int = 20, calculator_type: str = "talib"
|
60
|
+
) -> pd.DataFrame:
|
61
|
+
"""Calculate Exponential Moving Average
|
62
|
+
|
63
|
+
Args:
|
64
|
+
df: DataFrame with 'close' column
|
65
|
+
window: Lookback window size
|
66
|
+
calculator_type: ('talib', 'simple')
|
67
|
+
"""
|
68
|
+
calculator = IndicatorFactory.get_calculator(calculator_type)
|
69
|
+
return calculator.calculate_ema(df, window)
|
70
|
+
|
71
|
+
|
72
|
+
def get_rsi(
|
73
|
+
df: pd.DataFrame, window: int = 14, calculator_type: str = "talib"
|
74
|
+
) -> pd.DataFrame:
|
75
|
+
"""Calculate Relative Strength Index
|
76
|
+
|
77
|
+
Args:
|
78
|
+
df: DataFrame with 'close' column
|
79
|
+
window: Lookback window size
|
80
|
+
calculator_type: ('talib', 'simple')
|
81
|
+
"""
|
82
|
+
calculator = IndicatorFactory.get_calculator(calculator_type)
|
83
|
+
return calculator.calculate_rsi(df, window)
|
84
|
+
|
85
|
+
|
86
|
+
def get_macd(
|
87
|
+
df: pd.DataFrame,
|
88
|
+
fast: int = 12,
|
89
|
+
slow: int = 26,
|
90
|
+
signal: int = 9,
|
91
|
+
calculator_type: str = "talib",
|
92
|
+
) -> pd.DataFrame:
|
93
|
+
"""Calculate MACD
|
94
|
+
|
95
|
+
Args:
|
96
|
+
df: DataFrame with 'close' column
|
97
|
+
fast: Fast period
|
98
|
+
slow: Slow period
|
99
|
+
signal: Signal period
|
100
|
+
calculator_type: ('talib', 'simple')
|
101
|
+
"""
|
102
|
+
calculator = IndicatorFactory.get_calculator(calculator_type)
|
103
|
+
return calculator.calculate_macd(df, fast, slow, signal)
|
104
|
+
|
105
|
+
|
106
|
+
def get_bollinger_bands(
|
107
|
+
df: pd.DataFrame,
|
108
|
+
window: int = 20,
|
109
|
+
std: int = 2,
|
110
|
+
calculator_type: str = "talib",
|
111
|
+
) -> pd.DataFrame:
|
112
|
+
"""Calculate Bollinger Bands
|
113
|
+
|
114
|
+
Args:
|
115
|
+
df: DataFrame with 'close' column
|
116
|
+
window: Lookback window size
|
117
|
+
std: Standard deviation
|
118
|
+
calculator_type: ('talib', 'simple')
|
119
|
+
"""
|
120
|
+
calculator = IndicatorFactory.get_calculator(calculator_type)
|
121
|
+
return calculator.calculate_bollinger_bands(df, window, std)
|
122
|
+
|
123
|
+
|
124
|
+
def get_stoch(
|
125
|
+
df: pd.DataFrame,
|
126
|
+
window: int = 14,
|
127
|
+
smooth_d: int = 3,
|
128
|
+
smooth_k: int = 3,
|
129
|
+
calculator_type: str = "talib",
|
130
|
+
) -> pd.DataFrame:
|
131
|
+
"""Calculate Stochastic Oscillator
|
132
|
+
|
133
|
+
Args:
|
134
|
+
df: DataFrame with 'high', 'low', 'close' columns
|
135
|
+
window: Lookback window size
|
136
|
+
smooth_d: Smoothing for D line
|
137
|
+
smooth_k: Smoothing for K line
|
138
|
+
calculator_type: ('talib', 'simple')
|
139
|
+
"""
|
140
|
+
calculator = IndicatorFactory.get_calculator(calculator_type)
|
141
|
+
return calculator.calculate_stoch(df, window, smooth_d, smooth_k)
|
142
|
+
|
143
|
+
|
144
|
+
def get_atr(
|
145
|
+
df: pd.DataFrame, window: int = 14, calculator_type: str = "talib"
|
146
|
+
) -> pd.DataFrame:
|
147
|
+
"""Calculate Average True Range
|
148
|
+
|
149
|
+
Args:
|
150
|
+
df: DataFrame with 'high', 'low', 'close' columns
|
151
|
+
window: Lookback window size
|
152
|
+
calculator_type: ('talib', 'simple')
|
153
|
+
"""
|
154
|
+
calculator = IndicatorFactory.get_calculator(calculator_type)
|
155
|
+
return calculator.calculate_atr(df, window)
|
156
|
+
|
157
|
+
|
158
|
+
def get_cci(
|
159
|
+
df: pd.DataFrame, window: int = 14, calculator_type: str = "talib"
|
160
|
+
) -> pd.DataFrame:
|
161
|
+
"""Calculate Commodity Channel Index
|
162
|
+
|
163
|
+
Args:
|
164
|
+
df: DataFrame with 'high', 'low', 'close' columns
|
165
|
+
window: Lookback window size
|
166
|
+
calculator_type: ('talib', 'simple')
|
167
|
+
"""
|
168
|
+
calculator = IndicatorFactory.get_calculator(calculator_type)
|
169
|
+
return calculator.calculate_cci(df, window)
|
170
|
+
|
171
|
+
|
172
|
+
def get_adx(
|
173
|
+
df: pd.DataFrame, window: int = 14, calculator_type: str = "talib"
|
174
|
+
) -> pd.DataFrame:
|
175
|
+
"""Calculate Average Directional Index
|
176
|
+
|
177
|
+
Args:
|
178
|
+
df: DataFrame with 'high', 'low', 'close' columns
|
179
|
+
window: Lookback window size
|
180
|
+
calculator_type: ('talib', 'simple')
|
181
|
+
"""
|
182
|
+
calculator = IndicatorFactory.get_calculator(calculator_type)
|
183
|
+
return calculator.calculate_adx(df, window)
|
184
|
+
|
185
|
+
|
186
|
+
def get_willr(
|
187
|
+
df: pd.DataFrame, window: int = 14, calculator_type: str = "talib"
|
188
|
+
) -> pd.DataFrame:
|
189
|
+
"""Calculate Williams' %R"""
|
190
|
+
calculator = IndicatorFactory.get_calculator(calculator_type)
|
191
|
+
return calculator.calculate_willr(df, window)
|
192
|
+
|
193
|
+
|
194
|
+
def get_ad(df: pd.DataFrame, calculator_type: str = "talib") -> pd.DataFrame:
|
195
|
+
"""Calculate Chaikin A/D Line"""
|
196
|
+
calculator = IndicatorFactory.get_calculator(calculator_type)
|
197
|
+
return calculator.calculate_ad(df)
|
198
|
+
|
199
|
+
|
200
|
+
def get_adosc(
|
201
|
+
df: pd.DataFrame,
|
202
|
+
fast_period: int = 3,
|
203
|
+
slow_period: int = 10,
|
204
|
+
calculator_type: str = "talib",
|
205
|
+
) -> pd.DataFrame:
|
206
|
+
"""Calculate Chaikin A/D Oscillator"""
|
207
|
+
calculator = IndicatorFactory.get_calculator(calculator_type)
|
208
|
+
return calculator.calculate_adosc(df, fast_period, slow_period)
|
209
|
+
|
210
|
+
|
211
|
+
def get_obv(df: pd.DataFrame, calculator_type: str = "talib") -> pd.DataFrame:
|
212
|
+
"""Calculate On Balance Volume"""
|
213
|
+
calculator = IndicatorFactory.get_calculator(calculator_type)
|
214
|
+
return calculator.calculate_obv(df)
|
215
|
+
|
216
|
+
|
217
|
+
def get_mom(
|
218
|
+
df: pd.DataFrame, window: int = 10, calculator_type: str = "talib"
|
219
|
+
) -> pd.DataFrame:
|
220
|
+
"""Calculate Momentum"""
|
221
|
+
calculator = IndicatorFactory.get_calculator(calculator_type)
|
222
|
+
return calculator.calculate_mom(df, window)
|
223
|
+
|
224
|
+
|
225
|
+
def get_sar(
|
226
|
+
df: pd.DataFrame,
|
227
|
+
acceleration: float = 0.02,
|
228
|
+
maximum: float = 0.2,
|
229
|
+
calculator_type: str = "talib",
|
230
|
+
) -> pd.DataFrame:
|
231
|
+
"""Calculate Parabolic SAR"""
|
232
|
+
calculator = IndicatorFactory.get_calculator(calculator_type)
|
233
|
+
return calculator.calculate_sar(df, acceleration, maximum)
|
234
|
+
|
235
|
+
|
236
|
+
def get_tsf(
|
237
|
+
df: pd.DataFrame, window: int = 14, calculator_type: str = "talib"
|
238
|
+
) -> pd.DataFrame:
|
239
|
+
"""Calculate Time Series Forecast"""
|
240
|
+
calculator = IndicatorFactory.get_calculator(calculator_type)
|
241
|
+
return calculator.calculate_tsf(df, window)
|
242
|
+
|
243
|
+
|
244
|
+
def get_apo(
|
245
|
+
df: pd.DataFrame,
|
246
|
+
fast_period: int = 12,
|
247
|
+
slow_period: int = 26,
|
248
|
+
ma_type: int = 0,
|
249
|
+
calculator_type: str = "talib",
|
250
|
+
) -> pd.DataFrame:
|
251
|
+
"""Calculate Absolute Price Oscillator"""
|
252
|
+
calculator = IndicatorFactory.get_calculator(calculator_type)
|
253
|
+
return calculator.calculate_apo(df, fast_period, slow_period, ma_type)
|
254
|
+
|
255
|
+
|
256
|
+
def get_aroon(
|
257
|
+
df: pd.DataFrame, window: int = 14, calculator_type: str = "talib"
|
258
|
+
) -> pd.DataFrame:
|
259
|
+
"""Calculate Aroon"""
|
260
|
+
calculator = IndicatorFactory.get_calculator(calculator_type)
|
261
|
+
return calculator.calculate_aroon(df, window)
|
262
|
+
|
263
|
+
|
264
|
+
def get_aroonosc(
|
265
|
+
df: pd.DataFrame, window: int = 14, calculator_type: str = "talib"
|
266
|
+
) -> pd.DataFrame:
|
267
|
+
"""Calculate Aroon Oscillator"""
|
268
|
+
calculator = IndicatorFactory.get_calculator(calculator_type)
|
269
|
+
return calculator.calculate_aroonosc(df, window)
|
270
|
+
|
271
|
+
|
272
|
+
def get_bop(df: pd.DataFrame, calculator_type: str = "talib") -> pd.DataFrame:
|
273
|
+
"""Calculate Balance of Power"""
|
274
|
+
calculator = IndicatorFactory.get_calculator(calculator_type)
|
275
|
+
return calculator.calculate_bop(df)
|
276
|
+
|
277
|
+
|
278
|
+
def get_cmo(
|
279
|
+
df: pd.DataFrame, window: int = 14, calculator_type: str = "talib"
|
280
|
+
) -> pd.DataFrame:
|
281
|
+
"""Calculate Chande Momentum Oscillator"""
|
282
|
+
calculator = IndicatorFactory.get_calculator(calculator_type)
|
283
|
+
return calculator.calculate_cmo(df, window)
|
284
|
+
|
285
|
+
|
286
|
+
def get_dx(
|
287
|
+
df: pd.DataFrame, window: int = 14, calculator_type: str = "talib"
|
288
|
+
) -> pd.DataFrame:
|
289
|
+
"""Calculate Directional Movement Index"""
|
290
|
+
calculator = IndicatorFactory.get_calculator(calculator_type)
|
291
|
+
return calculator.calculate_dx(df, window)
|
292
|
+
|
293
|
+
|
294
|
+
def get_mfi(
|
295
|
+
df: pd.DataFrame, window: int = 14, calculator_type: str = "talib"
|
296
|
+
) -> pd.DataFrame:
|
297
|
+
"""Calculate Money Flow Index"""
|
298
|
+
calculator = IndicatorFactory.get_calculator(calculator_type)
|
299
|
+
return calculator.calculate_mfi(df, window)
|
300
|
+
|
301
|
+
|
302
|
+
def get_minus_di(
|
303
|
+
df: pd.DataFrame, window: int = 14, calculator_type: str = "talib"
|
304
|
+
) -> pd.DataFrame:
|
305
|
+
"""Calculate Minus Directional Indicator"""
|
306
|
+
calculator = IndicatorFactory.get_calculator(calculator_type)
|
307
|
+
return calculator.calculate_minus_di(df, window)
|
308
|
+
|
309
|
+
|
310
|
+
def get_minus_dm(
|
311
|
+
df: pd.DataFrame, window: int = 14, calculator_type: str = "talib"
|
312
|
+
) -> pd.DataFrame:
|
313
|
+
"""Calculate Minus Directional Movement"""
|
314
|
+
calculator = IndicatorFactory.get_calculator(calculator_type)
|
315
|
+
return calculator.calculate_minus_dm(df, window)
|
316
|
+
|
317
|
+
|
318
|
+
def get_plus_di(
|
319
|
+
df: pd.DataFrame, window: int = 14, calculator_type: str = "talib"
|
320
|
+
) -> pd.DataFrame:
|
321
|
+
"""Calculate Plus Directional Indicator"""
|
322
|
+
calculator = IndicatorFactory.get_calculator(calculator_type)
|
323
|
+
return calculator.calculate_plus_di(df, window)
|
324
|
+
|
325
|
+
|
326
|
+
def get_plus_dm(
|
327
|
+
df: pd.DataFrame, window: int = 14, calculator_type: str = "talib"
|
328
|
+
) -> pd.DataFrame:
|
329
|
+
"""Calculate Plus Directional Movement"""
|
330
|
+
calculator = IndicatorFactory.get_calculator(calculator_type)
|
331
|
+
return calculator.calculate_plus_dm(df, window)
|
332
|
+
|
333
|
+
|
334
|
+
def get_ppo(
|
335
|
+
df: pd.DataFrame,
|
336
|
+
fast_period: int = 12,
|
337
|
+
slow_period: int = 26,
|
338
|
+
ma_type: int = 0,
|
339
|
+
calculator_type: str = "talib",
|
340
|
+
) -> pd.DataFrame:
|
341
|
+
"""Calculate Percentage Price Oscillator"""
|
342
|
+
calculator = IndicatorFactory.get_calculator(calculator_type)
|
343
|
+
return calculator.calculate_ppo(df, fast_period, slow_period, ma_type)
|
344
|
+
|
345
|
+
|
346
|
+
def get_roc(
|
347
|
+
df: pd.DataFrame, window: int = 10, calculator_type: str = "talib"
|
348
|
+
) -> pd.DataFrame:
|
349
|
+
"""Calculate Rate of change"""
|
350
|
+
calculator = IndicatorFactory.get_calculator(calculator_type)
|
351
|
+
return calculator.calculate_roc(df, window)
|
352
|
+
|
353
|
+
|
354
|
+
def get_rocp(
|
355
|
+
df: pd.DataFrame, window: int = 10, calculator_type: str = "talib"
|
356
|
+
) -> pd.DataFrame:
|
357
|
+
"""Calculate Rate of change Percentage"""
|
358
|
+
calculator = IndicatorFactory.get_calculator(calculator_type)
|
359
|
+
return calculator.calculate_rocp(df, window)
|
360
|
+
|
361
|
+
|
362
|
+
def get_rocr(
|
363
|
+
df: pd.DataFrame, window: int = 10, calculator_type: str = "talib"
|
364
|
+
) -> pd.DataFrame:
|
365
|
+
"""Calculate Rate of change ratio"""
|
366
|
+
calculator = IndicatorFactory.get_calculator(calculator_type)
|
367
|
+
return calculator.calculate_rocr(df, window)
|
368
|
+
|
369
|
+
|
370
|
+
def get_rocr100(
|
371
|
+
df: pd.DataFrame, window: int = 10, calculator_type: str = "talib"
|
372
|
+
) -> pd.DataFrame:
|
373
|
+
"""Calculate Rate of change ratio 100 scale"""
|
374
|
+
calculator = IndicatorFactory.get_calculator(calculator_type)
|
375
|
+
return calculator.calculate_rocr100(df, window)
|
376
|
+
|
377
|
+
|
378
|
+
def get_trix(
|
379
|
+
df: pd.DataFrame, window: int = 30, calculator_type: str = "talib"
|
380
|
+
) -> pd.DataFrame:
|
381
|
+
"""Calculate 1-day Rate of Change (ROC) of a Triple Smooth EMA"""
|
382
|
+
calculator = IndicatorFactory.get_calculator(calculator_type)
|
383
|
+
return calculator.calculate_trix(df, window)
|
384
|
+
|
385
|
+
|
386
|
+
def get_ultosc(
|
387
|
+
df: pd.DataFrame,
|
388
|
+
window1: int = 7,
|
389
|
+
window2: int = 14,
|
390
|
+
window3: int = 28,
|
391
|
+
calculator_type: str = "talib",
|
392
|
+
) -> pd.DataFrame:
|
393
|
+
"""Calculate Ultimate Oscillator"""
|
394
|
+
calculator = IndicatorFactory.get_calculator(calculator_type)
|
395
|
+
return calculator.calculate_ultosc(df, window1, window2, window3)
|
@@ -35,9 +35,9 @@ class EastMoneyClient:
|
|
35
35
|
market = "116"
|
36
36
|
code = symbol[2:]
|
37
37
|
elif len(symbol) == 6:
|
38
|
-
if symbol.startswith(("000", "001", "002", "003", "300")):
|
38
|
+
if symbol.startswith(("000", "001", "002", "003", "300", "200")):
|
39
39
|
market = "0"
|
40
|
-
elif symbol.startswith(("600", "601", "603", "605", "688")):
|
40
|
+
elif symbol.startswith(("600", "601", "603", "605", "688", "900")):
|
41
41
|
market = "1"
|
42
42
|
else:
|
43
43
|
market = "0" # Default to SZ for ambiguity
|
@@ -18,6 +18,14 @@ class HistoricalDataProvider(ABC):
|
|
18
18
|
self.start_date = start_date
|
19
19
|
self.end_date = end_date
|
20
20
|
self.adjust = adjust
|
21
|
+
self._validate_dates()
|
22
|
+
|
23
|
+
def _validate_dates(self):
|
24
|
+
try:
|
25
|
+
pd.to_datetime(self.start_date)
|
26
|
+
pd.to_datetime(self.end_date)
|
27
|
+
except ValueError:
|
28
|
+
raise ValueError("Invalid date format. Please use YYYY-MM-DD.")
|
21
29
|
|
22
30
|
@classmethod
|
23
31
|
def get_supported_intervals(cls):
|
@@ -98,8 +98,44 @@ class SinaHistorical(HistoricalDataProvider):
|
|
98
98
|
)
|
99
99
|
return self._clean_minute_data(raw_df)
|
100
100
|
|
101
|
+
def _get_b_share_data(self, stock: str) -> pd.DataFrame:
|
102
|
+
"""Fetches B-share historical data"""
|
103
|
+
start_date = self._convert_date_format(self.start_date)
|
104
|
+
end_date = self._convert_date_format(self.end_date)
|
105
|
+
|
106
|
+
if self.interval in ["minute", "hour"]:
|
107
|
+
period = "1" if self.interval == "minute" else "60"
|
108
|
+
raw_df = ak.stock_zh_b_minute(
|
109
|
+
symbol=stock,
|
110
|
+
period=period,
|
111
|
+
adjust=self._map_adjust_param(self.adjust),
|
112
|
+
)
|
113
|
+
# Rename 'day' to 'date' for consistency
|
114
|
+
raw_df = raw_df.rename(columns={"day": "date"})
|
115
|
+
|
116
|
+
if self.interval_multiplier > 1:
|
117
|
+
freq = f"{self.interval_multiplier}{'min' if self.interval == 'minute' else 'h'}"
|
118
|
+
raw_df = self._resample_intraday_data(raw_df, freq)
|
119
|
+
else:
|
120
|
+
raw_df = ak.stock_zh_b_daily(
|
121
|
+
symbol=stock,
|
122
|
+
start_date=start_date,
|
123
|
+
end_date=end_date,
|
124
|
+
adjust=self._map_adjust_param(self.adjust),
|
125
|
+
)
|
126
|
+
if self.interval_multiplier > 1:
|
127
|
+
raw_df = self._resample_data(
|
128
|
+
raw_df, self.interval, self.interval_multiplier
|
129
|
+
)
|
130
|
+
|
131
|
+
return self._clean_data(raw_df)
|
132
|
+
|
101
133
|
def _get_daily_plus_data(self, stock: str) -> pd.DataFrame:
|
102
134
|
"""Fetches daily and higher-level data (day/week/month/year)"""
|
135
|
+
# Check if it's a B-share symbol
|
136
|
+
if stock.startswith(("sh9", "sz2")):
|
137
|
+
return self._get_b_share_data(stock)
|
138
|
+
|
103
139
|
start_date = self._convert_date_format(self.start_date)
|
104
140
|
end_date = self._convert_date_format(self.end_date)
|
105
141
|
|
File without changes
|
@@ -0,0 +1,158 @@
|
|
1
|
+
from abc import ABC, abstractmethod
|
2
|
+
import pandas as pd
|
3
|
+
|
4
|
+
|
5
|
+
class BaseIndicatorCalculator(ABC):
|
6
|
+
"""Base class for indicator calculators"""
|
7
|
+
|
8
|
+
@abstractmethod
|
9
|
+
def calculate_sma(self, df: pd.DataFrame, window: int) -> pd.DataFrame:
|
10
|
+
pass
|
11
|
+
|
12
|
+
@abstractmethod
|
13
|
+
def calculate_ema(self, df: pd.DataFrame, window: int) -> pd.DataFrame:
|
14
|
+
pass
|
15
|
+
|
16
|
+
@abstractmethod
|
17
|
+
def calculate_rsi(self, df: pd.DataFrame, window: int) -> pd.DataFrame:
|
18
|
+
pass
|
19
|
+
|
20
|
+
@abstractmethod
|
21
|
+
def calculate_macd(
|
22
|
+
self, df: pd.DataFrame, fast: int, slow: int, signal: int
|
23
|
+
) -> pd.DataFrame:
|
24
|
+
pass
|
25
|
+
|
26
|
+
@abstractmethod
|
27
|
+
def calculate_bollinger_bands(
|
28
|
+
self, df: pd.DataFrame, window: int, std: int
|
29
|
+
) -> pd.DataFrame:
|
30
|
+
pass
|
31
|
+
|
32
|
+
@abstractmethod
|
33
|
+
def calculate_stoch(
|
34
|
+
self, df: pd.DataFrame, window: int, smooth_d: int, smooth_k: int
|
35
|
+
) -> pd.DataFrame:
|
36
|
+
pass
|
37
|
+
|
38
|
+
@abstractmethod
|
39
|
+
def calculate_atr(self, df: pd.DataFrame, window: int) -> pd.DataFrame:
|
40
|
+
pass
|
41
|
+
|
42
|
+
@abstractmethod
|
43
|
+
def calculate_cci(self, df: pd.DataFrame, window: int) -> pd.DataFrame:
|
44
|
+
pass
|
45
|
+
|
46
|
+
@abstractmethod
|
47
|
+
def calculate_adx(self, df: pd.DataFrame, window: int) -> pd.DataFrame:
|
48
|
+
pass
|
49
|
+
|
50
|
+
@abstractmethod
|
51
|
+
def calculate_willr(self, df: pd.DataFrame, window: int) -> pd.DataFrame:
|
52
|
+
pass
|
53
|
+
|
54
|
+
@abstractmethod
|
55
|
+
def calculate_ad(self, df: pd.DataFrame) -> pd.DataFrame:
|
56
|
+
pass
|
57
|
+
|
58
|
+
@abstractmethod
|
59
|
+
def calculate_adosc(
|
60
|
+
self, df: pd.DataFrame, fast_period: int, slow_period: int
|
61
|
+
) -> pd.DataFrame:
|
62
|
+
pass
|
63
|
+
|
64
|
+
@abstractmethod
|
65
|
+
def calculate_obv(self, df: pd.DataFrame) -> pd.DataFrame:
|
66
|
+
pass
|
67
|
+
|
68
|
+
@abstractmethod
|
69
|
+
def calculate_mom(self, df: pd.DataFrame, window: int) -> pd.DataFrame:
|
70
|
+
pass
|
71
|
+
|
72
|
+
@abstractmethod
|
73
|
+
def calculate_sar(
|
74
|
+
self, df: pd.DataFrame, acceleration: float, maximum: float
|
75
|
+
) -> pd.DataFrame:
|
76
|
+
pass
|
77
|
+
|
78
|
+
@abstractmethod
|
79
|
+
def calculate_tsf(self, df: pd.DataFrame, window: int) -> pd.DataFrame:
|
80
|
+
pass
|
81
|
+
|
82
|
+
@abstractmethod
|
83
|
+
def calculate_apo(
|
84
|
+
self, df: pd.DataFrame, fast_period: int, slow_period: int, ma_type: int
|
85
|
+
) -> pd.DataFrame:
|
86
|
+
pass
|
87
|
+
|
88
|
+
@abstractmethod
|
89
|
+
def calculate_aroon(self, df: pd.DataFrame, window: int) -> pd.DataFrame:
|
90
|
+
pass
|
91
|
+
|
92
|
+
@abstractmethod
|
93
|
+
def calculate_aroonosc(self, df: pd.DataFrame, window: int) -> pd.DataFrame:
|
94
|
+
pass
|
95
|
+
|
96
|
+
@abstractmethod
|
97
|
+
def calculate_bop(self, df: pd.DataFrame) -> pd.DataFrame:
|
98
|
+
pass
|
99
|
+
|
100
|
+
@abstractmethod
|
101
|
+
def calculate_cmo(self, df: pd.DataFrame, window: int) -> pd.DataFrame:
|
102
|
+
pass
|
103
|
+
|
104
|
+
@abstractmethod
|
105
|
+
def calculate_dx(self, df: pd.DataFrame, window: int) -> pd.DataFrame:
|
106
|
+
pass
|
107
|
+
|
108
|
+
@abstractmethod
|
109
|
+
def calculate_mfi(self, df: pd.DataFrame, window: int) -> pd.DataFrame:
|
110
|
+
pass
|
111
|
+
|
112
|
+
@abstractmethod
|
113
|
+
def calculate_minus_di(self, df: pd.DataFrame, window: int) -> pd.DataFrame:
|
114
|
+
pass
|
115
|
+
|
116
|
+
@abstractmethod
|
117
|
+
def calculate_minus_dm(self, df: pd.DataFrame, window: int) -> pd.DataFrame:
|
118
|
+
pass
|
119
|
+
|
120
|
+
@abstractmethod
|
121
|
+
def calculate_plus_di(self, df: pd.DataFrame, window: int) -> pd.DataFrame:
|
122
|
+
pass
|
123
|
+
|
124
|
+
@abstractmethod
|
125
|
+
def calculate_plus_dm(self, df: pd.DataFrame, window: int) -> pd.DataFrame:
|
126
|
+
pass
|
127
|
+
|
128
|
+
@abstractmethod
|
129
|
+
def calculate_ppo(
|
130
|
+
self, df: pd.DataFrame, fast_period: int, slow_period: int, ma_type: int
|
131
|
+
) -> pd.DataFrame:
|
132
|
+
pass
|
133
|
+
|
134
|
+
@abstractmethod
|
135
|
+
def calculate_roc(self, df: pd.DataFrame, window: int) -> pd.DataFrame:
|
136
|
+
pass
|
137
|
+
|
138
|
+
@abstractmethod
|
139
|
+
def calculate_rocp(self, df: pd.DataFrame, window: int) -> pd.DataFrame:
|
140
|
+
pass
|
141
|
+
|
142
|
+
@abstractmethod
|
143
|
+
def calculate_rocr(self, df: pd.DataFrame, window: int) -> pd.DataFrame:
|
144
|
+
pass
|
145
|
+
|
146
|
+
@abstractmethod
|
147
|
+
def calculate_rocr100(self, df: pd.DataFrame, window: int) -> pd.DataFrame:
|
148
|
+
pass
|
149
|
+
|
150
|
+
@abstractmethod
|
151
|
+
def calculate_trix(self, df: pd.DataFrame, window: int) -> pd.DataFrame:
|
152
|
+
pass
|
153
|
+
|
154
|
+
@abstractmethod
|
155
|
+
def calculate_ultosc(
|
156
|
+
self, df: pd.DataFrame, window1: int, window2: int, window3: int
|
157
|
+
) -> pd.DataFrame:
|
158
|
+
pass
|
@@ -0,0 +1,33 @@
|
|
1
|
+
from .base import BaseIndicatorCalculator
|
2
|
+
from .simple import SimpleIndicatorCalculator
|
3
|
+
|
4
|
+
_calculators = {
|
5
|
+
"simple": SimpleIndicatorCalculator,
|
6
|
+
}
|
7
|
+
TALIB_AVAILABLE = False
|
8
|
+
try:
|
9
|
+
from .talib import TalibIndicatorCalculator
|
10
|
+
|
11
|
+
_calculators["talib"] = TalibIndicatorCalculator
|
12
|
+
TALIB_AVAILABLE = True
|
13
|
+
except ImportError:
|
14
|
+
# talib is optional
|
15
|
+
pass
|
16
|
+
|
17
|
+
|
18
|
+
class IndicatorFactory:
|
19
|
+
"""Factory for indicator calculators"""
|
20
|
+
|
21
|
+
@classmethod
|
22
|
+
def get_calculator(cls, calculator_type: str = "talib") -> BaseIndicatorCalculator:
|
23
|
+
"""Get indicator calculator instance
|
24
|
+
|
25
|
+
If talib is not installed, it will fall back to the simple implementation.
|
26
|
+
"""
|
27
|
+
if calculator_type == "talib" and not TALIB_AVAILABLE:
|
28
|
+
calculator_type = "simple"
|
29
|
+
|
30
|
+
calculator_class = _calculators.get(calculator_type)
|
31
|
+
if not calculator_class:
|
32
|
+
raise ValueError(f"Unsupported calculator type: {calculator_type}")
|
33
|
+
return calculator_class()
|
@@ -0,0 +1,230 @@
|
|
1
|
+
import pandas as pd
|
2
|
+
from .base import BaseIndicatorCalculator
|
3
|
+
|
4
|
+
|
5
|
+
class SimpleIndicatorCalculator(BaseIndicatorCalculator):
|
6
|
+
"""Basic pandas-based indicator implementations"""
|
7
|
+
|
8
|
+
def calculate_sma(self, df: pd.DataFrame, window: int) -> pd.DataFrame:
|
9
|
+
return (
|
10
|
+
df["close"]
|
11
|
+
.rolling(window=window, min_periods=window)
|
12
|
+
.mean()
|
13
|
+
.to_frame("sma")
|
14
|
+
)
|
15
|
+
|
16
|
+
def calculate_ema(self, df: pd.DataFrame, window: int) -> pd.DataFrame:
|
17
|
+
return (
|
18
|
+
df["close"]
|
19
|
+
.ewm(span=window, adjust=False, min_periods=window)
|
20
|
+
.mean()
|
21
|
+
.to_frame("ema")
|
22
|
+
)
|
23
|
+
|
24
|
+
def calculate_rsi(self, df: pd.DataFrame, window: int) -> pd.DataFrame:
|
25
|
+
delta = df["close"].diff()
|
26
|
+
gain = delta.clip(lower=0)
|
27
|
+
loss = -delta.clip(upper=0)
|
28
|
+
|
29
|
+
avg_gain = gain.ewm(alpha=1 / window, min_periods=window, adjust=False).mean()
|
30
|
+
avg_loss = loss.ewm(alpha=1 / window, min_periods=window, adjust=False).mean()
|
31
|
+
|
32
|
+
rs = avg_gain / avg_loss
|
33
|
+
rsi = 100 - (100 / (1 + rs))
|
34
|
+
|
35
|
+
return rsi.clip(0, 100).to_frame("rsi")
|
36
|
+
|
37
|
+
def calculate_macd(
|
38
|
+
self, df: pd.DataFrame, fast: int, slow: int, signal: int
|
39
|
+
) -> pd.DataFrame:
|
40
|
+
close = df["close"]
|
41
|
+
ema_fast = close.ewm(span=fast, adjust=False, min_periods=fast).mean()
|
42
|
+
ema_slow = close.ewm(span=slow, adjust=False, min_periods=slow).mean()
|
43
|
+
|
44
|
+
macd_line = ema_fast - ema_slow
|
45
|
+
signal_line = macd_line.ewm(
|
46
|
+
span=signal, adjust=False, min_periods=signal
|
47
|
+
).mean()
|
48
|
+
|
49
|
+
return pd.DataFrame(
|
50
|
+
{
|
51
|
+
"macd": macd_line,
|
52
|
+
"signal": signal_line,
|
53
|
+
"histogram": macd_line - signal_line,
|
54
|
+
}
|
55
|
+
)
|
56
|
+
|
57
|
+
def calculate_bollinger_bands(
|
58
|
+
self, df: pd.DataFrame, window: int, std: int
|
59
|
+
) -> pd.DataFrame:
|
60
|
+
close = df["close"]
|
61
|
+
sma = close.rolling(window=window, min_periods=window).mean()
|
62
|
+
rolling_std = close.rolling(window=window, min_periods=window).std()
|
63
|
+
upper_band = sma + (rolling_std * std)
|
64
|
+
lower_band = sma - (rolling_std * std)
|
65
|
+
return pd.DataFrame(
|
66
|
+
{"upper_band": upper_band, "middle_band": sma, "lower_band": lower_band}
|
67
|
+
)
|
68
|
+
|
69
|
+
def calculate_stoch(
|
70
|
+
self, df: pd.DataFrame, window: int, smooth_d: int, smooth_k: int
|
71
|
+
) -> pd.DataFrame:
|
72
|
+
high = df["high"]
|
73
|
+
low = df["low"]
|
74
|
+
close = df["close"]
|
75
|
+
|
76
|
+
lowest_low = low.rolling(window=window).min()
|
77
|
+
highest_high = high.rolling(window=window).max()
|
78
|
+
|
79
|
+
k = 100 * (close - lowest_low) / (highest_high - lowest_low)
|
80
|
+
slow_k = k.rolling(window=smooth_k).mean()
|
81
|
+
slow_d = slow_k.rolling(window=smooth_d).mean()
|
82
|
+
|
83
|
+
return pd.DataFrame({"slow_k": slow_k, "slow_d": slow_d})
|
84
|
+
|
85
|
+
def calculate_atr(self, df: pd.DataFrame, window: int) -> pd.DataFrame:
|
86
|
+
high = df["high"]
|
87
|
+
low = df["low"]
|
88
|
+
close = df["close"]
|
89
|
+
|
90
|
+
tr1 = high - low
|
91
|
+
tr2 = abs(high - close.shift())
|
92
|
+
tr3 = abs(low - close.shift())
|
93
|
+
tr = pd.concat([tr1, tr2, tr3], axis=1).max(axis=1)
|
94
|
+
|
95
|
+
atr = tr.ewm(alpha=1 / window, adjust=False, min_periods=window).mean()
|
96
|
+
return atr.to_frame("atr")
|
97
|
+
|
98
|
+
def calculate_cci(self, df: pd.DataFrame, window: int) -> pd.DataFrame:
|
99
|
+
high = df["high"]
|
100
|
+
low = df["low"]
|
101
|
+
close = df["close"]
|
102
|
+
|
103
|
+
tp = (high + low + close) / 3
|
104
|
+
tp_sma = tp.rolling(window=window, min_periods=window).mean()
|
105
|
+
mean_dev = tp.rolling(window=window, min_periods=window).apply(
|
106
|
+
lambda x: (x - x.mean()).abs().mean()
|
107
|
+
)
|
108
|
+
|
109
|
+
cci = (tp - tp_sma) / (0.015 * mean_dev)
|
110
|
+
return cci.to_frame("cci")
|
111
|
+
|
112
|
+
def calculate_adx(self, df: pd.DataFrame, window: int) -> pd.DataFrame:
|
113
|
+
high = df["high"]
|
114
|
+
low = df["low"]
|
115
|
+
close = df["close"]
|
116
|
+
|
117
|
+
# Calculate +DM, -DM and TR
|
118
|
+
move_up = high.diff()
|
119
|
+
move_down = low.diff().apply(abs)
|
120
|
+
|
121
|
+
plus_dm = pd.Series((move_up > move_down) & (move_up > 0)).astype(int) * move_up
|
122
|
+
minus_dm = (
|
123
|
+
pd.Series((move_down > move_up) & (move_down > 0)).astype(int) * move_down
|
124
|
+
)
|
125
|
+
|
126
|
+
tr1 = high - low
|
127
|
+
tr2 = abs(high - close.shift())
|
128
|
+
tr3 = abs(low - close.shift())
|
129
|
+
tr = pd.concat([tr1, tr2, tr3], axis=1).max(axis=1)
|
130
|
+
|
131
|
+
# Smooth +DM, -DM and TR
|
132
|
+
atr = tr.ewm(alpha=1 / window, adjust=False, min_periods=window).mean()
|
133
|
+
plus_di = 100 * (
|
134
|
+
plus_dm.ewm(alpha=1 / window, adjust=False, min_periods=window).mean() / atr
|
135
|
+
)
|
136
|
+
minus_di = 100 * (
|
137
|
+
minus_dm.ewm(alpha=1 / window, adjust=False, min_periods=window).mean()
|
138
|
+
/ atr
|
139
|
+
)
|
140
|
+
|
141
|
+
# Calculate ADX
|
142
|
+
dx = 100 * (abs(plus_di - minus_di) / (plus_di + minus_di))
|
143
|
+
adx = dx.ewm(alpha=1 / window, adjust=False, min_periods=window).mean()
|
144
|
+
|
145
|
+
return adx.to_frame("adx")
|
146
|
+
|
147
|
+
def calculate_willr(self, df: pd.DataFrame, window: int) -> pd.DataFrame:
|
148
|
+
raise NotImplementedError("WILLR not implemented in simple calculator")
|
149
|
+
|
150
|
+
def calculate_ad(self, df: pd.DataFrame) -> pd.DataFrame:
|
151
|
+
raise NotImplementedError("AD not implemented in simple calculator")
|
152
|
+
|
153
|
+
def calculate_adosc(
|
154
|
+
self, df: pd.DataFrame, fast_period: int, slow_period: int
|
155
|
+
) -> pd.DataFrame:
|
156
|
+
raise NotImplementedError("ADOSC not implemented in simple calculator")
|
157
|
+
|
158
|
+
def calculate_obv(self, df: pd.DataFrame) -> pd.DataFrame:
|
159
|
+
raise NotImplementedError("OBV not implemented in simple calculator")
|
160
|
+
|
161
|
+
def calculate_mom(self, df: pd.DataFrame, window: int) -> pd.DataFrame:
|
162
|
+
raise NotImplementedError("MOM not implemented in simple calculator")
|
163
|
+
|
164
|
+
def calculate_sar(
|
165
|
+
self, df: pd.DataFrame, acceleration: float, maximum: float
|
166
|
+
) -> pd.DataFrame:
|
167
|
+
raise NotImplementedError("SAR not implemented in simple calculator")
|
168
|
+
|
169
|
+
def calculate_tsf(self, df: pd.DataFrame, window: int) -> pd.DataFrame:
|
170
|
+
raise NotImplementedError("TSF not implemented in simple calculator")
|
171
|
+
|
172
|
+
def calculate_apo(
|
173
|
+
self, df: pd.DataFrame, fast_period: int, slow_period: int, ma_type: int
|
174
|
+
) -> pd.DataFrame:
|
175
|
+
raise NotImplementedError("APO not implemented in simple calculator")
|
176
|
+
|
177
|
+
def calculate_aroon(self, df: pd.DataFrame, window: int) -> pd.DataFrame:
|
178
|
+
raise NotImplementedError("AROON not implemented in simple calculator")
|
179
|
+
|
180
|
+
def calculate_aroonosc(self, df: pd.DataFrame, window: int) -> pd.DataFrame:
|
181
|
+
raise NotImplementedError("AROONOSC not implemented in simple calculator")
|
182
|
+
|
183
|
+
def calculate_bop(self, df: pd.DataFrame) -> pd.DataFrame:
|
184
|
+
raise NotImplementedError("BOP not implemented in simple calculator")
|
185
|
+
|
186
|
+
def calculate_cmo(self, df: pd.DataFrame, window: int) -> pd.DataFrame:
|
187
|
+
raise NotImplementedError("CMO not implemented in simple calculator")
|
188
|
+
|
189
|
+
def calculate_dx(self, df: pd.DataFrame, window: int) -> pd.DataFrame:
|
190
|
+
raise NotImplementedError("DX not implemented in simple calculator")
|
191
|
+
|
192
|
+
def calculate_mfi(self, df: pd.DataFrame, window: int) -> pd.DataFrame:
|
193
|
+
raise NotImplementedError("MFI not implemented in simple calculator")
|
194
|
+
|
195
|
+
def calculate_minus_di(self, df: pd.DataFrame, window: int) -> pd.DataFrame:
|
196
|
+
raise NotImplementedError("MINUS_DI not implemented in simple calculator")
|
197
|
+
|
198
|
+
def calculate_minus_dm(self, df: pd.DataFrame, window: int) -> pd.DataFrame:
|
199
|
+
raise NotImplementedError("MINUS_DM not implemented in simple calculator")
|
200
|
+
|
201
|
+
def calculate_plus_di(self, df: pd.DataFrame, window: int) -> pd.DataFrame:
|
202
|
+
raise NotImplementedError("PLUS_DI not implemented in simple calculator")
|
203
|
+
|
204
|
+
def calculate_plus_dm(self, df: pd.DataFrame, window: int) -> pd.DataFrame:
|
205
|
+
raise NotImplementedError("PLUS_DM not implemented in simple calculator")
|
206
|
+
|
207
|
+
def calculate_ppo(
|
208
|
+
self, df: pd.DataFrame, fast_period: int, slow_period: int, ma_type: int
|
209
|
+
) -> pd.DataFrame:
|
210
|
+
raise NotImplementedError("PPO not implemented in simple calculator")
|
211
|
+
|
212
|
+
def calculate_roc(self, df: pd.DataFrame, window: int) -> pd.DataFrame:
|
213
|
+
raise NotImplementedError("ROC not implemented in simple calculator")
|
214
|
+
|
215
|
+
def calculate_rocp(self, df: pd.DataFrame, window: int) -> pd.DataFrame:
|
216
|
+
raise NotImplementedError("ROCP not implemented in simple calculator")
|
217
|
+
|
218
|
+
def calculate_rocr(self, df: pd.DataFrame, window: int) -> pd.DataFrame:
|
219
|
+
raise NotImplementedError("ROCR not implemented in simple calculator")
|
220
|
+
|
221
|
+
def calculate_rocr100(self, df: pd.DataFrame, window: int) -> pd.DataFrame:
|
222
|
+
raise NotImplementedError("ROCR100 not implemented in simple calculator")
|
223
|
+
|
224
|
+
def calculate_trix(self, df: pd.DataFrame, window: int) -> pd.DataFrame:
|
225
|
+
raise NotImplementedError("TRIX not implemented in simple calculator")
|
226
|
+
|
227
|
+
def calculate_ultosc(
|
228
|
+
self, df: pd.DataFrame, window1: int, window2: int, window3: int
|
229
|
+
) -> pd.DataFrame:
|
230
|
+
raise NotImplementedError("ULTOSC not implemented in simple calculator")
|
@@ -0,0 +1,263 @@
|
|
1
|
+
import talib
|
2
|
+
import pandas as pd
|
3
|
+
from .base import BaseIndicatorCalculator
|
4
|
+
|
5
|
+
|
6
|
+
class TalibIndicatorCalculator(BaseIndicatorCalculator):
|
7
|
+
"""TA-Lib based indicator implementations"""
|
8
|
+
|
9
|
+
def calculate_sma(self, df: pd.DataFrame, window: int) -> pd.DataFrame:
|
10
|
+
close = df["close"].values
|
11
|
+
sma = talib.SMA(close, timeperiod=window)
|
12
|
+
return pd.DataFrame({"sma": sma}, index=df.index)
|
13
|
+
|
14
|
+
def calculate_ema(self, df: pd.DataFrame, window: int) -> pd.DataFrame:
|
15
|
+
close = df["close"].values
|
16
|
+
ema = talib.EMA(close, timeperiod=window)
|
17
|
+
return pd.DataFrame({"ema": ema}, index=df.index)
|
18
|
+
|
19
|
+
def calculate_rsi(self, df: pd.DataFrame, window: int) -> pd.DataFrame:
|
20
|
+
close = df["close"].values
|
21
|
+
rsi = talib.RSI(close, timeperiod=window)
|
22
|
+
return pd.DataFrame({"rsi": rsi}, index=df.index)
|
23
|
+
|
24
|
+
def calculate_macd(
|
25
|
+
self, df: pd.DataFrame, fast: int, slow: int, signal: int
|
26
|
+
) -> pd.DataFrame:
|
27
|
+
close = df["close"].values
|
28
|
+
macd, signal_line, histogram = talib.MACD(
|
29
|
+
close, fastperiod=fast, slowperiod=slow, signalperiod=signal
|
30
|
+
)
|
31
|
+
return pd.DataFrame(
|
32
|
+
{"macd": macd, "signal": signal_line, "histogram": histogram},
|
33
|
+
index=df.index,
|
34
|
+
)
|
35
|
+
|
36
|
+
def calculate_bollinger_bands(
|
37
|
+
self, df: pd.DataFrame, window: int, std: int
|
38
|
+
) -> pd.DataFrame:
|
39
|
+
close = df["close"].values
|
40
|
+
upper, middle, lower = talib.BBANDS(
|
41
|
+
close, timeperiod=window, nbdevup=std, nbdevdn=std, matype=talib.MA_Type.SMA
|
42
|
+
)
|
43
|
+
return pd.DataFrame(
|
44
|
+
{"upper_band": upper, "middle_band": middle, "lower_band": lower},
|
45
|
+
index=df.index,
|
46
|
+
)
|
47
|
+
|
48
|
+
def calculate_stoch(
|
49
|
+
self, df: pd.DataFrame, window: int, smooth_d: int, smooth_k: int
|
50
|
+
) -> pd.DataFrame:
|
51
|
+
high = df["high"].values
|
52
|
+
low = df["low"].values
|
53
|
+
close = df["close"].values
|
54
|
+
slow_k, slow_d = talib.STOCH(
|
55
|
+
high,
|
56
|
+
low,
|
57
|
+
close,
|
58
|
+
fastk_period=window,
|
59
|
+
slowk_period=smooth_k,
|
60
|
+
slowk_matype=talib.MA_Type.SMA,
|
61
|
+
slowd_period=smooth_d,
|
62
|
+
slowd_matype=talib.MA_Type.SMA,
|
63
|
+
)
|
64
|
+
return pd.DataFrame({"slow_k": slow_k, "slow_d": slow_d}, index=df.index)
|
65
|
+
|
66
|
+
def calculate_atr(self, df: pd.DataFrame, window: int) -> pd.DataFrame:
|
67
|
+
high = df["high"].values
|
68
|
+
low = df["low"].values
|
69
|
+
close = df["close"].values
|
70
|
+
atr = talib.ATR(high, low, close, timeperiod=window)
|
71
|
+
return pd.DataFrame({"atr": atr}, index=df.index)
|
72
|
+
|
73
|
+
def calculate_cci(self, df: pd.DataFrame, window: int) -> pd.DataFrame:
|
74
|
+
high = df["high"].values
|
75
|
+
low = df["low"].values
|
76
|
+
close = df["close"].values
|
77
|
+
cci = talib.CCI(high, low, close, timeperiod=window)
|
78
|
+
return pd.DataFrame({"cci": cci}, index=df.index)
|
79
|
+
|
80
|
+
def calculate_adx(self, df: pd.DataFrame, window: int) -> pd.DataFrame:
|
81
|
+
high = df["high"].values
|
82
|
+
low = df["low"].values
|
83
|
+
close = df["close"].values
|
84
|
+
adx = talib.ADX(high, low, close, timeperiod=window)
|
85
|
+
return pd.DataFrame({"adx": adx}, index=df.index)
|
86
|
+
|
87
|
+
def calculate_willr(self, df: pd.DataFrame, window: int) -> pd.DataFrame:
|
88
|
+
high = df["high"].values
|
89
|
+
low = df["low"].values
|
90
|
+
close = df["close"].values
|
91
|
+
willr = talib.WILLR(high, low, close, timeperiod=window)
|
92
|
+
return pd.DataFrame({"willr": willr}, index=df.index)
|
93
|
+
|
94
|
+
def calculate_ad(self, df: pd.DataFrame) -> pd.DataFrame:
|
95
|
+
high = df["high"].values
|
96
|
+
low = df["low"].values
|
97
|
+
close = df["close"].values
|
98
|
+
volume = df["volume"].values.astype(float)
|
99
|
+
ad = talib.AD(high, low, close, volume)
|
100
|
+
return pd.DataFrame({"ad": ad}, index=df.index)
|
101
|
+
|
102
|
+
def calculate_adosc(
|
103
|
+
self, df: pd.DataFrame, fast_period: int, slow_period: int
|
104
|
+
) -> pd.DataFrame:
|
105
|
+
high = df["high"].values
|
106
|
+
low = df["low"].values
|
107
|
+
close = df["close"].values
|
108
|
+
volume = df["volume"].values.astype(float)
|
109
|
+
adosc = talib.ADOSC(
|
110
|
+
high, low, close, volume, fastperiod=fast_period, slowperiod=slow_period
|
111
|
+
)
|
112
|
+
return pd.DataFrame({"adosc": adosc}, index=df.index)
|
113
|
+
|
114
|
+
def calculate_obv(self, df: pd.DataFrame) -> pd.DataFrame:
|
115
|
+
close = df["close"].values
|
116
|
+
volume = df["volume"].values.astype(float)
|
117
|
+
obv = talib.OBV(close, volume)
|
118
|
+
return pd.DataFrame({"obv": obv}, index=df.index)
|
119
|
+
|
120
|
+
def calculate_mom(self, df: pd.DataFrame, window: int) -> pd.DataFrame:
|
121
|
+
close = df["close"].values
|
122
|
+
mom = talib.MOM(close, timeperiod=window)
|
123
|
+
return pd.DataFrame({"mom": mom}, index=df.index)
|
124
|
+
|
125
|
+
def calculate_sar(
|
126
|
+
self, df: pd.DataFrame, acceleration: float, maximum: float
|
127
|
+
) -> pd.DataFrame:
|
128
|
+
high = df["high"].values
|
129
|
+
low = df["low"].values
|
130
|
+
sar = talib.SAR(high, low, acceleration=acceleration, maximum=maximum)
|
131
|
+
return pd.DataFrame({"sar": sar}, index=df.index)
|
132
|
+
|
133
|
+
def calculate_tsf(self, df: pd.DataFrame, window: int) -> pd.DataFrame:
|
134
|
+
close = df["close"].values
|
135
|
+
tsf = talib.TSF(close, timeperiod=window)
|
136
|
+
return pd.DataFrame({"tsf": tsf}, index=df.index)
|
137
|
+
|
138
|
+
def calculate_apo(
|
139
|
+
self, df: pd.DataFrame, fast_period: int, slow_period: int, ma_type
|
140
|
+
) -> pd.DataFrame:
|
141
|
+
close = df["close"].values
|
142
|
+
apo = talib.APO(
|
143
|
+
close, fastperiod=fast_period, slowperiod=slow_period, matype=ma_type
|
144
|
+
)
|
145
|
+
return pd.DataFrame({"apo": apo}, index=df.index)
|
146
|
+
|
147
|
+
def calculate_aroon(self, df: pd.DataFrame, window: int) -> pd.DataFrame:
|
148
|
+
high = df["high"].values
|
149
|
+
low = df["low"].values
|
150
|
+
aroon_down, aroon_up = talib.AROON(high, low, timeperiod=window)
|
151
|
+
return pd.DataFrame(
|
152
|
+
{"aroon_down": aroon_down, "aroon_up": aroon_up}, index=df.index
|
153
|
+
)
|
154
|
+
|
155
|
+
def calculate_aroonosc(self, df: pd.DataFrame, window: int) -> pd.DataFrame:
|
156
|
+
high = df["high"].values
|
157
|
+
low = df["low"].values
|
158
|
+
aroonosc = talib.AROONOSC(high, low, timeperiod=window)
|
159
|
+
return pd.DataFrame({"aroonosc": aroonosc}, index=df.index)
|
160
|
+
|
161
|
+
def calculate_bop(self, df: pd.DataFrame) -> pd.DataFrame:
|
162
|
+
open_ = df["open"].values
|
163
|
+
high = df["high"].values
|
164
|
+
low = df["low"].values
|
165
|
+
close = df["close"].values
|
166
|
+
bop = talib.BOP(open_, high, low, close)
|
167
|
+
return pd.DataFrame({"bop": bop}, index=df.index)
|
168
|
+
|
169
|
+
def calculate_cmo(self, df: pd.DataFrame, window: int) -> pd.DataFrame:
|
170
|
+
close = df["close"].values
|
171
|
+
cmo = talib.CMO(close, timeperiod=window)
|
172
|
+
return pd.DataFrame({"cmo": cmo}, index=df.index)
|
173
|
+
|
174
|
+
def calculate_dx(self, df: pd.DataFrame, window: int) -> pd.DataFrame:
|
175
|
+
high = df["high"].values
|
176
|
+
low = df["low"].values
|
177
|
+
close = df["close"].values
|
178
|
+
dx = talib.DX(high, low, close, timeperiod=window)
|
179
|
+
return pd.DataFrame({"dx": dx}, index=df.index)
|
180
|
+
|
181
|
+
def calculate_mfi(self, df: pd.DataFrame, window: int) -> pd.DataFrame:
|
182
|
+
high = df["high"].values
|
183
|
+
low = df["low"].values
|
184
|
+
close = df["close"].values
|
185
|
+
volume = df["volume"].values.astype(float)
|
186
|
+
mfi = talib.MFI(high, low, close, volume, timeperiod=window)
|
187
|
+
return pd.DataFrame({"mfi": mfi}, index=df.index)
|
188
|
+
|
189
|
+
def calculate_minus_di(self, df: pd.DataFrame, window: int) -> pd.DataFrame:
|
190
|
+
high = df["high"].values
|
191
|
+
low = df["low"].values
|
192
|
+
close = df["close"].values
|
193
|
+
minus_di = talib.MINUS_DI(high, low, close, timeperiod=window)
|
194
|
+
return pd.DataFrame({"minus_di": minus_di}, index=df.index)
|
195
|
+
|
196
|
+
def calculate_minus_dm(self, df: pd.DataFrame, window: int) -> pd.DataFrame:
|
197
|
+
high = df["high"].values
|
198
|
+
low = df["low"].values
|
199
|
+
minus_dm = talib.MINUS_DM(high, low, timeperiod=window)
|
200
|
+
return pd.DataFrame({"minus_dm": minus_dm}, index=df.index)
|
201
|
+
|
202
|
+
def calculate_plus_di(self, df: pd.DataFrame, window: int) -> pd.DataFrame:
|
203
|
+
high = df["high"].values
|
204
|
+
low = df["low"].values
|
205
|
+
close = df["close"].values
|
206
|
+
plus_di = talib.PLUS_DI(high, low, close, timeperiod=window)
|
207
|
+
return pd.DataFrame({"plus_di": plus_di}, index=df.index)
|
208
|
+
|
209
|
+
def calculate_plus_dm(self, df: pd.DataFrame, window: int) -> pd.DataFrame:
|
210
|
+
high = df["high"].values
|
211
|
+
low = df["low"].values
|
212
|
+
plus_dm = talib.PLUS_DM(high, low, timeperiod=window)
|
213
|
+
return pd.DataFrame({"plus_dm": plus_dm}, index=df.index)
|
214
|
+
|
215
|
+
def calculate_ppo(
|
216
|
+
self, df: pd.DataFrame, fast_period: int, slow_period: int, ma_type
|
217
|
+
) -> pd.DataFrame:
|
218
|
+
close = df["close"].values
|
219
|
+
ppo = talib.PPO(
|
220
|
+
close, fastperiod=fast_period, slowperiod=slow_period, matype=ma_type
|
221
|
+
)
|
222
|
+
return pd.DataFrame({"ppo": ppo}, index=df.index)
|
223
|
+
|
224
|
+
def calculate_roc(self, df: pd.DataFrame, window: int) -> pd.DataFrame:
|
225
|
+
close = df["close"].values
|
226
|
+
roc = talib.ROC(close, timeperiod=window)
|
227
|
+
return pd.DataFrame({"roc": roc}, index=df.index)
|
228
|
+
|
229
|
+
def calculate_rocp(self, df: pd.DataFrame, window: int) -> pd.DataFrame:
|
230
|
+
close = df["close"].values
|
231
|
+
rocp = talib.ROCP(close, timeperiod=window)
|
232
|
+
return pd.DataFrame({"rocp": rocp}, index=df.index)
|
233
|
+
|
234
|
+
def calculate_rocr(self, df: pd.DataFrame, window: int) -> pd.DataFrame:
|
235
|
+
close = df["close"].values
|
236
|
+
rocr = talib.ROCR(close, timeperiod=window)
|
237
|
+
return pd.DataFrame({"rocr": rocr}, index=df.index)
|
238
|
+
|
239
|
+
def calculate_rocr100(self, df: pd.DataFrame, window: int) -> pd.DataFrame:
|
240
|
+
close = df["close"].values
|
241
|
+
rocr100 = talib.ROCR100(close, timeperiod=window)
|
242
|
+
return pd.DataFrame({"rocr100": rocr100}, index=df.index)
|
243
|
+
|
244
|
+
def calculate_trix(self, df: pd.DataFrame, window: int) -> pd.DataFrame:
|
245
|
+
close = df["close"].values
|
246
|
+
trix = talib.TRIX(close, timeperiod=window)
|
247
|
+
return pd.DataFrame({"trix": trix}, index=df.index)
|
248
|
+
|
249
|
+
def calculate_ultosc(
|
250
|
+
self, df: pd.DataFrame, window1: int, window2: int, window3: int
|
251
|
+
) -> pd.DataFrame:
|
252
|
+
high = df["high"].values
|
253
|
+
low = df["low"].values
|
254
|
+
close = df["close"].values
|
255
|
+
ultosc = talib.ULTOSC(
|
256
|
+
high,
|
257
|
+
low,
|
258
|
+
close,
|
259
|
+
timeperiod1=window1,
|
260
|
+
timeperiod2=window2,
|
261
|
+
timeperiod3=window3,
|
262
|
+
)
|
263
|
+
return pd.DataFrame({"ultosc": ultosc}, index=df.index)
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: akshare-one
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.3.1
|
4
4
|
Summary: Standardized interface for Chinese financial market data, built on AKShare with unified data formats and simplified APIs
|
5
5
|
License-Expression: MIT
|
6
6
|
Project-URL: Homepage, https://github.com/zwldarren/akshare-one
|
@@ -9,8 +9,10 @@ Keywords: akshare,financial-data,stock-data,quant
|
|
9
9
|
Requires-Python: >=3.12
|
10
10
|
Description-Content-Type: text/markdown
|
11
11
|
License-File: LICENSE
|
12
|
-
Requires-Dist: akshare>=1.17.
|
12
|
+
Requires-Dist: akshare>=1.17.15
|
13
13
|
Requires-Dist: cachetools>=6.1.0
|
14
|
+
Provides-Extra: talib
|
15
|
+
Requires-Dist: ta-lib>=0.6.4; extra == "talib"
|
14
16
|
Dynamic: license-file
|
15
17
|
|
16
18
|
<div align="center">
|
@@ -38,6 +40,7 @@ Dynamic: license-file
|
|
38
40
|
| Stock news | `get_news_data` |
|
39
41
|
| Financial data | `get_balance_sheet`/`get_income_statement`/`get_cash_flow` |
|
40
42
|
| Internal transactions | `get_inner_trade_data` |
|
43
|
+
| Technical indicators | See [indicators.py](akshare_one/indicators.py) |
|
41
44
|
|
42
45
|
## 📦 Quick Installation
|
43
46
|
|
@@ -48,17 +51,18 @@ pip install akshare-one
|
|
48
51
|
## 💻 Usage Example
|
49
52
|
|
50
53
|
```python
|
51
|
-
from akshare_one import get_hist_data
|
54
|
+
from akshare_one import get_hist_data
|
55
|
+
from akshare_one.indicators import get_sma
|
52
56
|
|
53
57
|
# Get historical data
|
54
|
-
|
58
|
+
df = get_hist_data(
|
55
59
|
symbol="600000",
|
56
60
|
interval="day",
|
57
61
|
adjust="hfq"
|
58
62
|
)
|
59
63
|
|
60
|
-
#
|
61
|
-
|
64
|
+
# Calculate 20-day Simple Moving Average
|
65
|
+
df_sma = get_sma(df, window=20)
|
62
66
|
```
|
63
67
|
|
64
68
|
## 📚 Documentation
|
@@ -1,23 +1,29 @@
|
|
1
1
|
akshare_one/__init__.py,sha256=M4eXCnBzGqa5FihT-q7DHaluTvidnqwVF7AgPgCikKU,878
|
2
2
|
akshare_one/financial.py,sha256=XAsonRzGK8akKtW2Q7LUrew4OFRnRAfZm0nw0JY73Jc,1426
|
3
|
+
akshare_one/indicators.py,sha256=CltxhMoy6qbiQKIq2veTQ6xsf4_q7Ir3Zw8IfHyr4FI,12138
|
3
4
|
akshare_one/insider.py,sha256=fM6wvlLSGm7a2NkQFvxEK2PkhN5WudrO-V0BboVz2Bo,1092
|
4
5
|
akshare_one/news.py,sha256=yrYeCaKTgCGP-TSyOfOou9gMw8185qiWrg380fD9-f8,669
|
5
6
|
akshare_one/stock.py,sha256=kldPmfggrgUH8pEcU8HGR5ofsxzYXJiUqj8GXCM5pBk,2266
|
6
7
|
akshare_one/modules/cache.py,sha256=47A80Xtfr4gkKvEfBQrT8Dz8hNFR769rBa2gU6ew25s,373
|
7
8
|
akshare_one/modules/utils.py,sha256=H4nrGf8m4_ezTiW5-OcNPxpV-neTYfffEfaOLDFLY9Y,323
|
8
|
-
akshare_one/modules/eastmoney/client.py,sha256=
|
9
|
+
akshare_one/modules/eastmoney/client.py,sha256=AprWA4SxX_B4RzvG6B0_ciOMYaoRFcmIwHGRkEtvfHQ,3118
|
9
10
|
akshare_one/modules/eastmoney/utils.py,sha256=KzT0x6dF8y6wgc9Wzewr450NCee1i_dK2EhocBJQc5g,2896
|
10
11
|
akshare_one/modules/financial/base.py,sha256=qj_XeujG7Tn2oO1niuIG4s7lMH3xg5zERDbcj75DUnU,536
|
11
12
|
akshare_one/modules/financial/factory.py,sha256=GqzFp6LoHWj7t5VwtZJkLFxBuaAW_0b__bduBrmlcOg,1301
|
12
13
|
akshare_one/modules/financial/sina.py,sha256=Hf6fVeN0gClS9GU2N9Hg6QJ1f8X4mecpEA6KWVgiwTc,10914
|
13
|
-
akshare_one/modules/historical/base.py,sha256=
|
14
|
+
akshare_one/modules/historical/base.py,sha256=EkSNFk9bV1zbbuuIvI5Yk3r6s0EsomZJ4VFROYe4prU,1245
|
14
15
|
akshare_one/modules/historical/eastmoney.py,sha256=2bYXobIwlrd5vG8pHlUZVE6edjQqn8AEGCVFGcWCCKk,8248
|
15
16
|
akshare_one/modules/historical/eastmoney_direct.py,sha256=QRgGrY6NhAB2NAWCh_H3G0q2wKnSEnYQBRjMq-GjXGM,2814
|
16
17
|
akshare_one/modules/historical/factory.py,sha256=K1JwhScNPvuP-wR33C5VnosJYXJ3u1RykJuaMU6-3ls,1500
|
17
|
-
akshare_one/modules/historical/sina.py,sha256=
|
18
|
+
akshare_one/modules/historical/sina.py,sha256=D6xYpwoV5aNDsTLqUphnXye_2k7TVZZTdfvoLFu70Ds,8685
|
19
|
+
akshare_one/modules/indicators/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
20
|
+
akshare_one/modules/indicators/base.py,sha256=oNfP2p1ZO-jdV9Ay7CECO7LrFCGs9y3Xxg7ewg9uAZ0,4218
|
21
|
+
akshare_one/modules/indicators/factory.py,sha256=FDERSNI7r22qN06PYzyzkB5fyhXa2f_v1Yj-A_e6dFc,988
|
22
|
+
akshare_one/modules/indicators/simple.py,sha256=8GdDteLchQv-ON6lEYlq57u3ryPaHAOo-RksmLcze3s,9112
|
23
|
+
akshare_one/modules/indicators/talib.py,sha256=w7C-21TZfiLcVA_WgE7aQ1nYVzbyhb99r5rzKS7a6aU,10510
|
18
24
|
akshare_one/modules/insider/base.py,sha256=xOhBDzMtZd1DutsHaJWe931P17fgW7dlivo1vz5ZflI,949
|
19
25
|
akshare_one/modules/insider/factory.py,sha256=om51vX-GTXknpptZGvmDD9m10FfgEVmy402xZ3bBsNM,1280
|
20
|
-
akshare_one/modules/insider/xueqiu.py,sha256=
|
26
|
+
akshare_one/modules/insider/xueqiu.py,sha256=UnvyLyfpz_OJCcPjGEaD_tm8YEpOag7H1cpel0rVhEU,3994
|
21
27
|
akshare_one/modules/news/base.py,sha256=yrBZf1q1QwJkCXygNmeLIEjvcqm1iti63tjb3s40L_4,559
|
22
28
|
akshare_one/modules/news/eastmoney.py,sha256=efVT4plk03s8TbSfhAhRLWgMLlR5G_2G0xGZTQgSxmY,1333
|
23
29
|
akshare_one/modules/news/factory.py,sha256=Em6c7m46K760iwIX3GZ15HdFu7pXT2w-n4QsjwHezjY,1264
|
@@ -26,8 +32,8 @@ akshare_one/modules/realtime/eastmoney.py,sha256=7IR35dwPr5xj0ErdBajYwyBivEoi55w
|
|
26
32
|
akshare_one/modules/realtime/eastmoney_direct.py,sha256=yFH1fNcZRieyxFN5YzX7Gcw5-CR-dUEgUHSeR0NPD0g,1238
|
27
33
|
akshare_one/modules/realtime/factory.py,sha256=qHdCDY6dA7ck4JgK3yUDqcne9WrruAA6XzbRT69kT2w,1480
|
28
34
|
akshare_one/modules/realtime/xueqiu.py,sha256=LKu0fW0EMt3c2m1w2kqBbEt9s2TcHUeBFpbFtueJgbU,2241
|
29
|
-
akshare_one-0.
|
30
|
-
akshare_one-0.
|
31
|
-
akshare_one-0.
|
32
|
-
akshare_one-0.
|
33
|
-
akshare_one-0.
|
35
|
+
akshare_one-0.3.1.dist-info/licenses/LICENSE,sha256=Gg6A1GNSJCZWQ73aHJ7TXOa0i8RQ3FejZCTZ6Db07cU,1066
|
36
|
+
akshare_one-0.3.1.dist-info/METADATA,sha256=rJ0LO-mZZcvL78bqVVVhL7U8FAS7nSeNn-V60FuDYRg,2062
|
37
|
+
akshare_one-0.3.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
38
|
+
akshare_one-0.3.1.dist-info/top_level.txt,sha256=kNiucyLVAGa89wmUSpXbBLWD7pF_RuahuiaOfLHZSyw,12
|
39
|
+
akshare_one-0.3.1.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|