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.
@@ -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)
@@ -112,4 +112,4 @@ class XueQiuInsider(InsiderDataProvider):
112
112
  if col in df.columns:
113
113
  df[col] = pd.to_numeric(df[col], errors="coerce")
114
114
 
115
- return df
115
+ return df.reset_index(drop=True)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: akshare-one
3
- Version: 0.2.3
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.5
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, get_realtime_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
- df_hist = get_hist_data(
58
+ df = get_hist_data(
55
59
  symbol="600000",
56
60
  interval="day",
57
61
  adjust="hfq"
58
62
  )
59
63
 
60
- # Get real-time data
61
- df_realtime = get_realtime_data(symbol="600000")
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=_fOXtvAbs65WHY3s2Ge1bsCTpVDbfkNj9yaVErF6GoU,3104
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=UeKfMU_k8SgazXn27M1FVNaYJ5vBNgUB0jVBDpgXnz8,980
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=R-5hBOP5zslDuC0qfzw5zNhQyWmbmkmqB2sI2WhKVgI,7249
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=2gmSrcer8qw2Jb80kYT3tHNJ3U88hxd0dJSfoh846EY,3971
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.2.3.dist-info/licenses/LICENSE,sha256=Gg6A1GNSJCZWQ73aHJ7TXOa0i8RQ3FejZCTZ6Db07cU,1066
30
- akshare_one-0.2.3.dist-info/METADATA,sha256=jDbWls7NZNuJd6jOpSl8oGguxl4NLBCLGo16aL89VKw,1896
31
- akshare_one-0.2.3.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
32
- akshare_one-0.2.3.dist-info/top_level.txt,sha256=kNiucyLVAGa89wmUSpXbBLWD7pF_RuahuiaOfLHZSyw,12
33
- akshare_one-0.2.3.dist-info/RECORD,,
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,,