akshare-one 0.3.6__tar.gz → 0.3.7__tar.gz
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-0.3.6 → akshare_one-0.3.7}/PKG-INFO +2 -2
- akshare_one-0.3.7/akshare_one/modules/indicators/simple.py +384 -0
- {akshare_one-0.3.6 → akshare_one-0.3.7}/akshare_one.egg-info/PKG-INFO +2 -2
- {akshare_one-0.3.6 → akshare_one-0.3.7}/akshare_one.egg-info/requires.txt +1 -1
- {akshare_one-0.3.6 → akshare_one-0.3.7}/pyproject.toml +2 -2
- {akshare_one-0.3.6 → akshare_one-0.3.7}/tests/test_indicators.py +189 -0
- akshare_one-0.3.6/akshare_one/modules/indicators/simple.py +0 -230
- {akshare_one-0.3.6 → akshare_one-0.3.7}/LICENSE +0 -0
- {akshare_one-0.3.6 → akshare_one-0.3.7}/README.md +0 -0
- {akshare_one-0.3.6 → akshare_one-0.3.7}/akshare_one/__init__.py +0 -0
- {akshare_one-0.3.6 → akshare_one-0.3.7}/akshare_one/eastmoney/client.py +0 -0
- {akshare_one-0.3.6 → akshare_one-0.3.7}/akshare_one/eastmoney/utils.py +0 -0
- {akshare_one-0.3.6 → akshare_one-0.3.7}/akshare_one/indicators.py +0 -0
- {akshare_one-0.3.6 → akshare_one-0.3.7}/akshare_one/modules/cache.py +0 -0
- {akshare_one-0.3.6 → akshare_one-0.3.7}/akshare_one/modules/financial/base.py +0 -0
- {akshare_one-0.3.6 → akshare_one-0.3.7}/akshare_one/modules/financial/eastmoney_direct.py +0 -0
- {akshare_one-0.3.6 → akshare_one-0.3.7}/akshare_one/modules/financial/factory.py +0 -0
- {akshare_one-0.3.6 → akshare_one-0.3.7}/akshare_one/modules/financial/sina.py +0 -0
- {akshare_one-0.3.6 → akshare_one-0.3.7}/akshare_one/modules/historical/base.py +0 -0
- {akshare_one-0.3.6 → akshare_one-0.3.7}/akshare_one/modules/historical/eastmoney.py +0 -0
- {akshare_one-0.3.6 → akshare_one-0.3.7}/akshare_one/modules/historical/eastmoney_direct.py +0 -0
- {akshare_one-0.3.6 → akshare_one-0.3.7}/akshare_one/modules/historical/factory.py +0 -0
- {akshare_one-0.3.6 → akshare_one-0.3.7}/akshare_one/modules/historical/sina.py +0 -0
- {akshare_one-0.3.6 → akshare_one-0.3.7}/akshare_one/modules/indicators/__init__.py +0 -0
- {akshare_one-0.3.6 → akshare_one-0.3.7}/akshare_one/modules/indicators/base.py +0 -0
- {akshare_one-0.3.6 → akshare_one-0.3.7}/akshare_one/modules/indicators/factory.py +0 -0
- {akshare_one-0.3.6 → akshare_one-0.3.7}/akshare_one/modules/indicators/talib.py +0 -0
- {akshare_one-0.3.6 → akshare_one-0.3.7}/akshare_one/modules/info/base.py +0 -0
- {akshare_one-0.3.6 → akshare_one-0.3.7}/akshare_one/modules/info/eastmoney.py +0 -0
- {akshare_one-0.3.6 → akshare_one-0.3.7}/akshare_one/modules/info/factory.py +0 -0
- {akshare_one-0.3.6 → akshare_one-0.3.7}/akshare_one/modules/insider/base.py +0 -0
- {akshare_one-0.3.6 → akshare_one-0.3.7}/akshare_one/modules/insider/factory.py +0 -0
- {akshare_one-0.3.6 → akshare_one-0.3.7}/akshare_one/modules/insider/xueqiu.py +0 -0
- {akshare_one-0.3.6 → akshare_one-0.3.7}/akshare_one/modules/news/base.py +0 -0
- {akshare_one-0.3.6 → akshare_one-0.3.7}/akshare_one/modules/news/eastmoney.py +0 -0
- {akshare_one-0.3.6 → akshare_one-0.3.7}/akshare_one/modules/news/factory.py +0 -0
- {akshare_one-0.3.6 → akshare_one-0.3.7}/akshare_one/modules/realtime/base.py +0 -0
- {akshare_one-0.3.6 → akshare_one-0.3.7}/akshare_one/modules/realtime/eastmoney.py +0 -0
- {akshare_one-0.3.6 → akshare_one-0.3.7}/akshare_one/modules/realtime/eastmoney_direct.py +0 -0
- {akshare_one-0.3.6 → akshare_one-0.3.7}/akshare_one/modules/realtime/factory.py +0 -0
- {akshare_one-0.3.6 → akshare_one-0.3.7}/akshare_one/modules/realtime/xueqiu.py +0 -0
- {akshare_one-0.3.6 → akshare_one-0.3.7}/akshare_one/modules/utils.py +0 -0
- {akshare_one-0.3.6 → akshare_one-0.3.7}/akshare_one.egg-info/SOURCES.txt +0 -0
- {akshare_one-0.3.6 → akshare_one-0.3.7}/akshare_one.egg-info/dependency_links.txt +0 -0
- {akshare_one-0.3.6 → akshare_one-0.3.7}/akshare_one.egg-info/top_level.txt +0 -0
- {akshare_one-0.3.6 → akshare_one-0.3.7}/setup.cfg +0 -0
- {akshare_one-0.3.6 → akshare_one-0.3.7}/tests/test_financial.py +0 -0
- {akshare_one-0.3.6 → akshare_one-0.3.7}/tests/test_info.py +0 -0
- {akshare_one-0.3.6 → akshare_one-0.3.7}/tests/test_insider.py +0 -0
- {akshare_one-0.3.6 → akshare_one-0.3.7}/tests/test_news.py +0 -0
- {akshare_one-0.3.6 → akshare_one-0.3.7}/tests/test_stock.py +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: akshare-one
|
3
|
-
Version: 0.3.
|
3
|
+
Version: 0.3.7
|
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,7 +9,7 @@ Keywords: akshare,financial-data,stock-data,quant
|
|
9
9
|
Requires-Python: >=3.10
|
10
10
|
Description-Content-Type: text/markdown
|
11
11
|
License-File: LICENSE
|
12
|
-
Requires-Dist: akshare>=1.17.
|
12
|
+
Requires-Dist: akshare>=1.17.26
|
13
13
|
Requires-Dist: cachetools>=5.5.2
|
14
14
|
Provides-Extra: talib
|
15
15
|
Requires-Dist: ta-lib>=0.6.4; extra == "talib"
|
@@ -0,0 +1,384 @@
|
|
1
|
+
import pandas as pd
|
2
|
+
import numpy as np
|
3
|
+
from .base import BaseIndicatorCalculator
|
4
|
+
|
5
|
+
|
6
|
+
class SimpleIndicatorCalculator(BaseIndicatorCalculator):
|
7
|
+
"""Basic pandas-based indicator implementations"""
|
8
|
+
|
9
|
+
def _get_ma(self, series: pd.Series, window: int, ma_type: int) -> pd.Series:
|
10
|
+
if ma_type == 0:
|
11
|
+
return series.rolling(window=window, min_periods=window).mean()
|
12
|
+
elif ma_type == 1:
|
13
|
+
return series.ewm(span=window, adjust=False, min_periods=window).mean()
|
14
|
+
else:
|
15
|
+
raise ValueError(
|
16
|
+
f"Unsupported ma_type: {ma_type} in simple calculator. Only SMA (0) and EMA (1) are supported."
|
17
|
+
)
|
18
|
+
|
19
|
+
def _wilder_smooth(self, series: pd.Series, window: int) -> pd.Series:
|
20
|
+
return series.ewm(alpha=1 / window, adjust=False, min_periods=window).mean()
|
21
|
+
|
22
|
+
def calculate_sma(self, df: pd.DataFrame, window: int) -> pd.DataFrame:
|
23
|
+
return (
|
24
|
+
df["close"]
|
25
|
+
.rolling(window=window, min_periods=window)
|
26
|
+
.mean()
|
27
|
+
.to_frame("sma")
|
28
|
+
)
|
29
|
+
|
30
|
+
def calculate_ema(self, df: pd.DataFrame, window: int) -> pd.DataFrame:
|
31
|
+
return (
|
32
|
+
df["close"]
|
33
|
+
.ewm(span=window, adjust=False, min_periods=window)
|
34
|
+
.mean()
|
35
|
+
.to_frame("ema")
|
36
|
+
)
|
37
|
+
|
38
|
+
def calculate_rsi(self, df: pd.DataFrame, window: int) -> pd.DataFrame:
|
39
|
+
delta = df["close"].diff()
|
40
|
+
gain = delta.clip(lower=0)
|
41
|
+
loss = -delta.clip(upper=0)
|
42
|
+
|
43
|
+
avg_gain = gain.ewm(alpha=1 / window, min_periods=window, adjust=False).mean()
|
44
|
+
avg_loss = loss.ewm(alpha=1 / window, min_periods=window, adjust=False).mean()
|
45
|
+
|
46
|
+
rs = avg_gain / avg_loss
|
47
|
+
rsi = 100 - (100 / (1 + rs))
|
48
|
+
|
49
|
+
return rsi.clip(0, 100).to_frame("rsi")
|
50
|
+
|
51
|
+
def calculate_macd(
|
52
|
+
self, df: pd.DataFrame, fast: int, slow: int, signal: int
|
53
|
+
) -> pd.DataFrame:
|
54
|
+
close = df["close"]
|
55
|
+
ema_fast = close.ewm(span=fast, adjust=False, min_periods=fast).mean()
|
56
|
+
ema_slow = close.ewm(span=slow, adjust=False, min_periods=slow).mean()
|
57
|
+
|
58
|
+
macd_line = ema_fast - ema_slow
|
59
|
+
signal_line = macd_line.ewm(
|
60
|
+
span=signal, adjust=False, min_periods=signal
|
61
|
+
).mean()
|
62
|
+
|
63
|
+
return pd.DataFrame(
|
64
|
+
{
|
65
|
+
"macd": macd_line,
|
66
|
+
"signal": signal_line,
|
67
|
+
"histogram": macd_line - signal_line,
|
68
|
+
}
|
69
|
+
)
|
70
|
+
|
71
|
+
def calculate_bollinger_bands(
|
72
|
+
self, df: pd.DataFrame, window: int, std: int
|
73
|
+
) -> pd.DataFrame:
|
74
|
+
close = df["close"]
|
75
|
+
sma = close.rolling(window=window, min_periods=window).mean()
|
76
|
+
rolling_std = close.rolling(window=window, min_periods=window).std()
|
77
|
+
upper_band = sma + (rolling_std * std)
|
78
|
+
lower_band = sma - (rolling_std * std)
|
79
|
+
return pd.DataFrame(
|
80
|
+
{"upper_band": upper_band, "middle_band": sma, "lower_band": lower_band}
|
81
|
+
)
|
82
|
+
|
83
|
+
def calculate_stoch(
|
84
|
+
self, df: pd.DataFrame, window: int, smooth_d: int, smooth_k: int
|
85
|
+
) -> pd.DataFrame:
|
86
|
+
high = df["high"]
|
87
|
+
low = df["low"]
|
88
|
+
close = df["close"]
|
89
|
+
|
90
|
+
lowest_low = low.rolling(window=window).min()
|
91
|
+
highest_high = high.rolling(window=window).max()
|
92
|
+
|
93
|
+
k = 100 * (close - lowest_low) / (highest_high - lowest_low).replace(0, np.nan)
|
94
|
+
slow_k = k.rolling(window=smooth_k, min_periods=smooth_k).mean()
|
95
|
+
slow_d = slow_k.rolling(window=smooth_d, min_periods=smooth_d).mean()
|
96
|
+
|
97
|
+
return pd.DataFrame({"slow_k": slow_k, "slow_d": slow_d})
|
98
|
+
|
99
|
+
def calculate_atr(self, df: pd.DataFrame, window: int) -> pd.DataFrame:
|
100
|
+
high = df["high"]
|
101
|
+
low = df["low"]
|
102
|
+
close = df["close"]
|
103
|
+
|
104
|
+
tr1 = high - low
|
105
|
+
tr2 = abs(high - close.shift())
|
106
|
+
tr3 = abs(low - close.shift())
|
107
|
+
tr = pd.concat([tr1, tr2, tr3], axis=1).max(axis=1)
|
108
|
+
|
109
|
+
atr = self._wilder_smooth(tr, window)
|
110
|
+
return atr.to_frame("atr")
|
111
|
+
|
112
|
+
def calculate_cci(self, df: pd.DataFrame, window: int) -> pd.DataFrame:
|
113
|
+
high = df["high"]
|
114
|
+
low = df["low"]
|
115
|
+
close = df["close"]
|
116
|
+
|
117
|
+
tp = (high + low + close) / 3
|
118
|
+
tp_sma = tp.rolling(window=window, min_periods=window).mean()
|
119
|
+
mean_dev = tp.rolling(window=window, min_periods=window).apply(
|
120
|
+
lambda x: (x - x.mean()).abs().mean()
|
121
|
+
)
|
122
|
+
|
123
|
+
cci = (tp - tp_sma) / (0.015 * mean_dev)
|
124
|
+
return cci.to_frame("cci")
|
125
|
+
|
126
|
+
def calculate_adx(self, df: pd.DataFrame, window: int) -> pd.DataFrame:
|
127
|
+
dx = self.calculate_dx(df, window)["dx"]
|
128
|
+
adx = self._wilder_smooth(dx, window)
|
129
|
+
return adx.to_frame("adx")
|
130
|
+
|
131
|
+
def calculate_willr(self, df: pd.DataFrame, window: int) -> pd.DataFrame:
|
132
|
+
high = df["high"]
|
133
|
+
low = df["low"]
|
134
|
+
close = df["close"]
|
135
|
+
highest_high = high.rolling(window=window, min_periods=window).max()
|
136
|
+
lowest_low = low.rolling(window=window, min_periods=window).min()
|
137
|
+
willr = -100 * (highest_high - close) / (highest_high - lowest_low)
|
138
|
+
return willr.to_frame("willr")
|
139
|
+
|
140
|
+
def calculate_ad(self, df: pd.DataFrame) -> pd.DataFrame:
|
141
|
+
high = df["high"]
|
142
|
+
low = df["low"]
|
143
|
+
close = df["close"]
|
144
|
+
volume = df["volume"]
|
145
|
+
mfm = ((close - low) - (high - close)) / (high - low).replace(0, np.nan)
|
146
|
+
mfm = mfm.fillna(0)
|
147
|
+
mfv = mfm * volume
|
148
|
+
ad = mfv.cumsum()
|
149
|
+
return ad.to_frame("ad")
|
150
|
+
|
151
|
+
def calculate_adosc(
|
152
|
+
self, df: pd.DataFrame, fast_period: int, slow_period: int
|
153
|
+
) -> pd.DataFrame:
|
154
|
+
ad = self.calculate_ad(df)["ad"]
|
155
|
+
ema_fast = ad.ewm(span=fast_period, adjust=False).mean()
|
156
|
+
ema_slow = ad.ewm(span=slow_period, adjust=False).mean()
|
157
|
+
adosc = ema_fast - ema_slow
|
158
|
+
return adosc.to_frame("adosc")
|
159
|
+
|
160
|
+
def calculate_obv(self, df: pd.DataFrame) -> pd.DataFrame:
|
161
|
+
close = df["close"]
|
162
|
+
volume = df["volume"]
|
163
|
+
sign = (close > close.shift(1)).astype(int) - (close < close.shift(1)).astype(
|
164
|
+
int
|
165
|
+
)
|
166
|
+
obv = (volume * sign).cumsum()
|
167
|
+
return obv.to_frame("obv")
|
168
|
+
|
169
|
+
def calculate_mom(self, df: pd.DataFrame, window: int) -> pd.DataFrame:
|
170
|
+
close = df["close"]
|
171
|
+
mom = close.diff(periods=window)
|
172
|
+
return mom.to_frame("mom")
|
173
|
+
|
174
|
+
def calculate_sar(
|
175
|
+
self, df: pd.DataFrame, acceleration: float, maximum: float
|
176
|
+
) -> pd.DataFrame:
|
177
|
+
high, low = df["high"], df["low"]
|
178
|
+
sar = pd.Series(index=df.index, dtype=float)
|
179
|
+
uptrend = True
|
180
|
+
accel_factor = acceleration
|
181
|
+
extreme_point = high[0]
|
182
|
+
sar.iloc[0] = low[0]
|
183
|
+
|
184
|
+
for i in range(1, len(df)):
|
185
|
+
prev_sar = sar.iloc[i - 1]
|
186
|
+
|
187
|
+
if uptrend:
|
188
|
+
sar.iloc[i] = prev_sar + accel_factor * (extreme_point - prev_sar)
|
189
|
+
sar.iloc[i] = min(sar.iloc[i], low.iloc[i - 1])
|
190
|
+
if i > 1:
|
191
|
+
sar.iloc[i] = min(sar.iloc[i], low.iloc[i - 2])
|
192
|
+
|
193
|
+
if low[i] < sar.iloc[i]:
|
194
|
+
uptrend = False
|
195
|
+
sar.iloc[i] = extreme_point
|
196
|
+
extreme_point = low[i]
|
197
|
+
accel_factor = acceleration
|
198
|
+
else:
|
199
|
+
if high[i] > extreme_point:
|
200
|
+
extreme_point = high[i]
|
201
|
+
accel_factor = min(maximum, accel_factor + acceleration)
|
202
|
+
else:
|
203
|
+
sar.iloc[i] = prev_sar - accel_factor * (prev_sar - extreme_point)
|
204
|
+
sar.iloc[i] = max(sar.iloc[i], high.iloc[i - 1])
|
205
|
+
if i > 1:
|
206
|
+
sar.iloc[i] = max(sar.iloc[i], high.iloc[i - 2])
|
207
|
+
|
208
|
+
if high[i] > sar.iloc[i]:
|
209
|
+
uptrend = True
|
210
|
+
sar.iloc[i] = extreme_point
|
211
|
+
extreme_point = high[i]
|
212
|
+
accel_factor = acceleration
|
213
|
+
else:
|
214
|
+
if low[i] < extreme_point:
|
215
|
+
extreme_point = low[i]
|
216
|
+
accel_factor = min(maximum, accel_factor + acceleration)
|
217
|
+
|
218
|
+
return sar.to_frame("sar")
|
219
|
+
|
220
|
+
def calculate_tsf(self, df: pd.DataFrame, window: int) -> pd.DataFrame:
|
221
|
+
close = df["close"]
|
222
|
+
|
223
|
+
def linear_reg_forecast(y):
|
224
|
+
x = np.arange(1, len(y) + 1)
|
225
|
+
b_num = len(x) * np.sum(x * y) - np.sum(x) * np.sum(y)
|
226
|
+
b_den = len(x) * np.sum(x * x) - np.sum(x) ** 2
|
227
|
+
b = b_num / b_den if b_den != 0 else 0
|
228
|
+
a = np.mean(y) - b * np.mean(x)
|
229
|
+
return a + b * len(y)
|
230
|
+
|
231
|
+
tsf = close.rolling(window=window, min_periods=window).apply(
|
232
|
+
linear_reg_forecast, raw=True
|
233
|
+
)
|
234
|
+
return tsf.to_frame("tsf")
|
235
|
+
|
236
|
+
def calculate_apo(
|
237
|
+
self, df: pd.DataFrame, fast_period: int, slow_period: int, ma_type: int
|
238
|
+
) -> pd.DataFrame:
|
239
|
+
close = df["close"]
|
240
|
+
fast_ma = self._get_ma(close, fast_period, ma_type)
|
241
|
+
slow_ma = self._get_ma(close, slow_period, ma_type)
|
242
|
+
apo = fast_ma - slow_ma
|
243
|
+
return apo.to_frame("apo")
|
244
|
+
|
245
|
+
def calculate_aroon(self, df: pd.DataFrame, window: int) -> pd.DataFrame:
|
246
|
+
high = df["high"]
|
247
|
+
low = df["low"]
|
248
|
+
periods_since_high = high.rolling(window=window, min_periods=window).apply(
|
249
|
+
lambda x: len(x) - 1 - np.argmax(x), raw=True
|
250
|
+
)
|
251
|
+
periods_since_low = low.rolling(window=window, min_periods=window).apply(
|
252
|
+
lambda x: len(x) - 1 - np.argmin(x), raw=True
|
253
|
+
)
|
254
|
+
aroon_up = ((window - periods_since_high) / window) * 100
|
255
|
+
aroon_down = ((window - periods_since_low) / window) * 100
|
256
|
+
return pd.DataFrame({"aroon_up": aroon_up, "aroon_down": aroon_down})
|
257
|
+
|
258
|
+
def calculate_aroonosc(self, df: pd.DataFrame, window: int) -> pd.DataFrame:
|
259
|
+
aroon_df = self.calculate_aroon(df, window)
|
260
|
+
aroonosc = aroon_df["aroon_up"] - aroon_df["aroon_down"]
|
261
|
+
return aroonosc.to_frame("aroonosc")
|
262
|
+
|
263
|
+
def calculate_bop(self, df: pd.DataFrame) -> pd.DataFrame:
|
264
|
+
bop = (df["close"] - df["open"]) / (df["high"] - df["low"]).replace(0, np.nan)
|
265
|
+
bop = bop.fillna(0)
|
266
|
+
return bop.to_frame("bop")
|
267
|
+
|
268
|
+
def calculate_cmo(self, df: pd.DataFrame, window: int) -> pd.DataFrame:
|
269
|
+
close_diff = df["close"].diff(1)
|
270
|
+
sum_up = close_diff.where(close_diff > 0, 0).rolling(window=window).sum()
|
271
|
+
sum_down = -close_diff.where(close_diff < 0, 0).rolling(window=window).sum()
|
272
|
+
cmo = 100 * (sum_up - sum_down) / (sum_up + sum_down).replace(0, np.nan)
|
273
|
+
cmo = cmo.fillna(0)
|
274
|
+
return cmo.to_frame("cmo")
|
275
|
+
|
276
|
+
def calculate_dx(self, df: pd.DataFrame, window: int) -> pd.DataFrame:
|
277
|
+
plus_di = self.calculate_plus_di(df, window)["plus_di"]
|
278
|
+
minus_di = self.calculate_minus_di(df, window)["minus_di"]
|
279
|
+
dx = 100 * abs(plus_di - minus_di) / (plus_di + minus_di).replace(0, np.nan)
|
280
|
+
dx = dx.fillna(0)
|
281
|
+
return dx.to_frame("dx")
|
282
|
+
|
283
|
+
def calculate_mfi(self, df: pd.DataFrame, window: int) -> pd.DataFrame:
|
284
|
+
typical_price = (df["high"] + df["low"] + df["close"]) / 3
|
285
|
+
money_flow = typical_price * df["volume"]
|
286
|
+
price_diff = typical_price.diff()
|
287
|
+
positive_mf = money_flow.where(price_diff > 0, 0)
|
288
|
+
negative_mf = money_flow.where(price_diff < 0, 0)
|
289
|
+
positive_mf_sum = positive_mf.rolling(window=window).sum()
|
290
|
+
negative_mf_sum = negative_mf.rolling(window=window).sum()
|
291
|
+
money_ratio = positive_mf_sum / negative_mf_sum.replace(0, np.nan)
|
292
|
+
money_ratio = money_ratio.fillna(0)
|
293
|
+
mfi = 100 - (100 / (1 + money_ratio))
|
294
|
+
return mfi.to_frame("mfi")
|
295
|
+
|
296
|
+
def calculate_minus_di(self, df: pd.DataFrame, window: int) -> pd.DataFrame:
|
297
|
+
atr = self.calculate_atr(df, window)["atr"]
|
298
|
+
minus_dm = self.calculate_minus_dm(df, window)["minus_dm"]
|
299
|
+
minus_di = 100 * (minus_dm / atr)
|
300
|
+
return minus_di.to_frame("minus_di")
|
301
|
+
|
302
|
+
def calculate_minus_dm(self, df: pd.DataFrame, window: int) -> pd.DataFrame:
|
303
|
+
high = df["high"]
|
304
|
+
low = df["low"]
|
305
|
+
up_move = high.diff()
|
306
|
+
down_move = -low.diff()
|
307
|
+
minus_dm = down_move.where((down_move > up_move) & (down_move > 0), 0)
|
308
|
+
smoothed_minus_dm = self._wilder_smooth(minus_dm, window)
|
309
|
+
return smoothed_minus_dm.to_frame("minus_dm")
|
310
|
+
|
311
|
+
def calculate_plus_di(self, df: pd.DataFrame, window: int) -> pd.DataFrame:
|
312
|
+
atr = self.calculate_atr(df, window)["atr"]
|
313
|
+
plus_dm = self.calculate_plus_dm(df, window)["plus_dm"]
|
314
|
+
plus_di = 100 * (plus_dm / atr)
|
315
|
+
return plus_di.to_frame("plus_di")
|
316
|
+
|
317
|
+
def calculate_plus_dm(self, df: pd.DataFrame, window: int) -> pd.DataFrame:
|
318
|
+
high = df["high"]
|
319
|
+
low = df["low"]
|
320
|
+
up_move = high.diff()
|
321
|
+
down_move = -low.diff()
|
322
|
+
plus_dm = up_move.where((up_move > down_move) & (up_move > 0), 0)
|
323
|
+
smoothed_plus_dm = self._wilder_smooth(plus_dm, window)
|
324
|
+
return smoothed_plus_dm.to_frame("plus_dm")
|
325
|
+
|
326
|
+
def calculate_ppo(
|
327
|
+
self, df: pd.DataFrame, fast_period: int, slow_period: int, ma_type: int
|
328
|
+
) -> pd.DataFrame:
|
329
|
+
close = df["close"]
|
330
|
+
fast_ma = self._get_ma(close, fast_period, ma_type)
|
331
|
+
slow_ma = self._get_ma(close, slow_period, ma_type)
|
332
|
+
ppo = ((fast_ma - slow_ma) / slow_ma) * 100
|
333
|
+
return ppo.to_frame("ppo")
|
334
|
+
|
335
|
+
def calculate_roc(self, df: pd.DataFrame, window: int) -> pd.DataFrame:
|
336
|
+
close = df["close"]
|
337
|
+
roc = (close.diff(window) / close.shift(window)) * 100
|
338
|
+
return roc.to_frame("roc")
|
339
|
+
|
340
|
+
def calculate_rocp(self, df: pd.DataFrame, window: int) -> pd.DataFrame:
|
341
|
+
close = df["close"]
|
342
|
+
rocp = close.diff(window) / close.shift(window)
|
343
|
+
return rocp.to_frame("rocp")
|
344
|
+
|
345
|
+
def calculate_rocr(self, df: pd.DataFrame, window: int) -> pd.DataFrame:
|
346
|
+
close = df["close"]
|
347
|
+
rocr = close / close.shift(window)
|
348
|
+
return rocr.to_frame("rocr")
|
349
|
+
|
350
|
+
def calculate_rocr100(self, df: pd.DataFrame, window: int) -> pd.DataFrame:
|
351
|
+
close = df["close"]
|
352
|
+
rocr100 = (close / close.shift(window)) * 100
|
353
|
+
return rocr100.to_frame("rocr100")
|
354
|
+
|
355
|
+
def calculate_trix(self, df: pd.DataFrame, window: int) -> pd.DataFrame:
|
356
|
+
close = df["close"]
|
357
|
+
ema1 = close.ewm(span=window, adjust=False).mean()
|
358
|
+
ema2 = ema1.ewm(span=window, adjust=False).mean()
|
359
|
+
ema3 = ema2.ewm(span=window, adjust=False).mean()
|
360
|
+
trix = 100 * ema3.diff(1) / ema3.shift(1)
|
361
|
+
return trix.to_frame("trix")
|
362
|
+
|
363
|
+
def calculate_ultosc(
|
364
|
+
self, df: pd.DataFrame, window1: int, window2: int, window3: int
|
365
|
+
) -> pd.DataFrame:
|
366
|
+
low = df["low"]
|
367
|
+
high = df["high"]
|
368
|
+
close = df["close"]
|
369
|
+
close_prev = close.shift(1)
|
370
|
+
true_low = pd.concat([low, close_prev], axis=1).min(axis=1)
|
371
|
+
true_high = pd.concat([high, close_prev], axis=1).max(axis=1)
|
372
|
+
bp = close - true_low
|
373
|
+
tr = true_high - true_low
|
374
|
+
tr_sum1 = tr.rolling(window=window1).sum()
|
375
|
+
tr_sum2 = tr.rolling(window=window2).sum()
|
376
|
+
tr_sum3 = tr.rolling(window=window3).sum()
|
377
|
+
avg1 = bp.rolling(window=window1).sum() / tr_sum1.replace(0, np.nan)
|
378
|
+
avg2 = bp.rolling(window=window2).sum() / tr_sum2.replace(0, np.nan)
|
379
|
+
avg3 = bp.rolling(window=window3).sum() / tr_sum3.replace(0, np.nan)
|
380
|
+
avg1 = avg1.fillna(0)
|
381
|
+
avg2 = avg2.fillna(0)
|
382
|
+
avg3 = avg3.fillna(0)
|
383
|
+
ultosc = 100 * (4 * avg1 + 2 * avg2 + 1 * avg3) / (4 + 2 + 1)
|
384
|
+
return ultosc.to_frame("ultosc")
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: akshare-one
|
3
|
-
Version: 0.3.
|
3
|
+
Version: 0.3.7
|
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,7 +9,7 @@ Keywords: akshare,financial-data,stock-data,quant
|
|
9
9
|
Requires-Python: >=3.10
|
10
10
|
Description-Content-Type: text/markdown
|
11
11
|
License-File: LICENSE
|
12
|
-
Requires-Dist: akshare>=1.17.
|
12
|
+
Requires-Dist: akshare>=1.17.26
|
13
13
|
Requires-Dist: cachetools>=5.5.2
|
14
14
|
Provides-Extra: talib
|
15
15
|
Requires-Dist: ta-lib>=0.6.4; extra == "talib"
|
@@ -1,11 +1,11 @@
|
|
1
1
|
[project]
|
2
2
|
name = "akshare-one"
|
3
|
-
version = "0.3.
|
3
|
+
version = "0.3.7"
|
4
4
|
description = "Standardized interface for Chinese financial market data, built on AKShare with unified data formats and simplified APIs"
|
5
5
|
readme = "README.md"
|
6
6
|
requires-python = ">=3.10"
|
7
7
|
dependencies = [
|
8
|
-
"akshare>=1.17.
|
8
|
+
"akshare>=1.17.26",
|
9
9
|
"cachetools>=5.5.2",
|
10
10
|
]
|
11
11
|
license = "MIT"
|
@@ -94,12 +94,30 @@ class TestIndicators(unittest.TestCase):
|
|
94
94
|
result = get_stoch(self.df, 14, 3, 3, calculator_type="simple")
|
95
95
|
self.assertEqual(len(result), len(self.df))
|
96
96
|
self.assertEqual(set(result.columns), {"slow_k", "slow_d"})
|
97
|
+
# Test handling of zero range (high == low)
|
98
|
+
df_zero_range = self.df.copy()
|
99
|
+
df_zero_range["high"] = df_zero_range["low"]
|
100
|
+
result_zero = get_stoch(df_zero_range, 14, 3, 3, calculator_type="simple")
|
101
|
+
self.assertEqual(len(result_zero), len(df_zero_range))
|
102
|
+
self.assertTrue(
|
103
|
+
not result_zero["slow_k"].isna().all()
|
104
|
+
) # Should handle zero range
|
97
105
|
|
98
106
|
def test_simple_atr(self):
|
99
107
|
result = get_atr(self.df, 14, calculator_type="simple")
|
100
108
|
self.assertEqual(len(result), len(self.df))
|
101
109
|
self.assertTrue("atr" in result.columns)
|
102
110
|
|
111
|
+
def test_simple_willr(self):
|
112
|
+
result = get_willr(self.df, 14, calculator_type="simple")
|
113
|
+
self.assertEqual(len(result), len(self.df))
|
114
|
+
self.assertTrue("willr" in result.columns)
|
115
|
+
# Test handling of zero range (high == low)
|
116
|
+
df_zero_range = self.df.copy()
|
117
|
+
df_zero_range["high"] = df_zero_range["low"]
|
118
|
+
result_zero = get_willr(df_zero_range, 14, calculator_type="simple")
|
119
|
+
self.assertEqual(len(result_zero), len(df_zero_range))
|
120
|
+
|
103
121
|
def test_simple_cci(self):
|
104
122
|
result = get_cci(self.df, 14, calculator_type="simple")
|
105
123
|
self.assertEqual(len(result), len(self.df))
|
@@ -110,6 +128,17 @@ class TestIndicators(unittest.TestCase):
|
|
110
128
|
self.assertEqual(len(result), len(self.df))
|
111
129
|
self.assertTrue("adx" in result.columns)
|
112
130
|
|
131
|
+
def test_simple_ad(self):
|
132
|
+
result = get_ad(self.df, calculator_type="simple")
|
133
|
+
self.assertEqual(len(result), len(self.df))
|
134
|
+
self.assertTrue("ad" in result.columns)
|
135
|
+
# Test handling of zero range (high == low)
|
136
|
+
df_zero_range = self.df.copy()
|
137
|
+
df_zero_range["high"] = df_zero_range["low"]
|
138
|
+
result_zero = get_ad(df_zero_range, calculator_type="simple")
|
139
|
+
self.assertEqual(len(result_zero), len(df_zero_range))
|
140
|
+
self.assertTrue(not result_zero["ad"].isna().all()) # Should handle zero range
|
141
|
+
|
113
142
|
# Explicitly test talib implementation
|
114
143
|
@unittest.skipUnless(TALIB_AVAILABLE, "talib not installed")
|
115
144
|
def test_talib_sma(self):
|
@@ -329,3 +358,163 @@ class TestIndicators(unittest.TestCase):
|
|
329
358
|
result = get_ultosc(self.df, 7, 14, 28)
|
330
359
|
self.assertEqual(len(result), len(self.df))
|
331
360
|
self.assertTrue("ultosc" in result.columns)
|
361
|
+
|
362
|
+
# Additional tests for edge cases in simple implementation
|
363
|
+
def test_simple_bop_edge_case(self):
|
364
|
+
result = get_bop(self.df, calculator_type="simple")
|
365
|
+
self.assertEqual(len(result), len(self.df))
|
366
|
+
self.assertTrue("bop" in result.columns)
|
367
|
+
# Test handling of zero range (high == low)
|
368
|
+
df_zero_range = self.df.copy()
|
369
|
+
df_zero_range["high"] = df_zero_range["low"]
|
370
|
+
result_zero = get_bop(df_zero_range, calculator_type="simple")
|
371
|
+
self.assertEqual(len(result_zero), len(df_zero_range))
|
372
|
+
self.assertTrue(not result_zero["bop"].isna().all()) # Should handle zero range
|
373
|
+
|
374
|
+
def test_simple_mfi_edge_case(self):
|
375
|
+
result = get_mfi(self.df, 14, calculator_type="simple")
|
376
|
+
self.assertEqual(len(result), len(self.df))
|
377
|
+
self.assertTrue("mfi" in result.columns)
|
378
|
+
# Test handling of zero negative money flow
|
379
|
+
df_constant_price = self.df.copy()
|
380
|
+
df_constant_price["close"] = 100 # All prices are the same
|
381
|
+
result_zero = get_mfi(df_constant_price, 14, calculator_type="simple")
|
382
|
+
self.assertEqual(len(result_zero), len(df_constant_price))
|
383
|
+
self.assertTrue(
|
384
|
+
not result_zero["mfi"].isna().all()
|
385
|
+
) # Should handle zero negative flow
|
386
|
+
|
387
|
+
def test_simple_dx_edge_case(self):
|
388
|
+
result = get_dx(self.df, 14, calculator_type="simple")
|
389
|
+
self.assertEqual(len(result), len(self.df))
|
390
|
+
self.assertTrue("dx" in result.columns)
|
391
|
+
# Test handling of zero DI sum
|
392
|
+
df_flat = self.df.copy()
|
393
|
+
df_flat["high"] = df_flat["low"] # No directional movement
|
394
|
+
result_zero = get_dx(df_flat, 14, calculator_type="simple")
|
395
|
+
self.assertEqual(len(result_zero), len(df_flat))
|
396
|
+
self.assertTrue(not result_zero["dx"].isna().all()) # Should handle zero DI sum
|
397
|
+
|
398
|
+
def test_simple_cmo_edge_case(self):
|
399
|
+
result = get_cmo(self.df, 14, calculator_type="simple")
|
400
|
+
self.assertEqual(len(result), len(self.df))
|
401
|
+
self.assertTrue("cmo" in result.columns)
|
402
|
+
# Test handling of zero price movement
|
403
|
+
df_flat = self.df.copy()
|
404
|
+
df_flat["close"] = 100 # No price movement
|
405
|
+
result_zero = get_cmo(df_flat, 14, calculator_type="simple")
|
406
|
+
self.assertEqual(len(result_zero), len(df_flat))
|
407
|
+
self.assertTrue(
|
408
|
+
not result_zero["cmo"].isna().all()
|
409
|
+
) # Should handle zero movement
|
410
|
+
|
411
|
+
def test_simple_ultosc_edge_case(self):
|
412
|
+
result = get_ultosc(self.df, 7, 14, 28, calculator_type="simple")
|
413
|
+
self.assertEqual(len(result), len(self.df))
|
414
|
+
self.assertTrue("ultosc" in result.columns)
|
415
|
+
# Test handling of zero true range
|
416
|
+
df_flat = self.df.copy()
|
417
|
+
df_flat["high"] = df_flat["low"] # Zero true range
|
418
|
+
result_zero = get_ultosc(df_flat, 7, 14, 28, calculator_type="simple")
|
419
|
+
self.assertEqual(len(result_zero), len(df_flat))
|
420
|
+
self.assertTrue(not result_zero["ultosc"].isna().all()) # Should handle zero TR
|
421
|
+
|
422
|
+
def test_simple_sar_edge_case(self):
|
423
|
+
result = get_sar(self.df, 0.02, 0.2, calculator_type="simple")
|
424
|
+
self.assertEqual(len(result), len(self.df))
|
425
|
+
self.assertTrue("sar" in result.columns)
|
426
|
+
# Test handling of flat market
|
427
|
+
df_flat = self.df.copy()
|
428
|
+
df_flat["high"] = df_flat["low"] = df_flat["close"] # Flat market
|
429
|
+
result_zero = get_sar(df_flat, 0.02, 0.2, calculator_type="simple")
|
430
|
+
self.assertEqual(len(result_zero), len(df_flat))
|
431
|
+
self.assertTrue(
|
432
|
+
not result_zero["sar"].isna().all()
|
433
|
+
) # Should handle flat market
|
434
|
+
|
435
|
+
# Tests for other simple implementation functions not covered above
|
436
|
+
def test_simple_adosc(self):
|
437
|
+
result = get_adosc(self.df, 3, 10, calculator_type="simple")
|
438
|
+
self.assertEqual(len(result), len(self.df))
|
439
|
+
self.assertTrue("adosc" in result.columns)
|
440
|
+
|
441
|
+
def test_simple_obv(self):
|
442
|
+
result = get_obv(self.df, calculator_type="simple")
|
443
|
+
self.assertEqual(len(result), len(self.df))
|
444
|
+
self.assertTrue("obv" in result.columns)
|
445
|
+
|
446
|
+
def test_simple_mom(self):
|
447
|
+
result = get_mom(self.df, 10, calculator_type="simple")
|
448
|
+
self.assertEqual(len(result), len(self.df))
|
449
|
+
self.assertTrue("mom" in result.columns)
|
450
|
+
|
451
|
+
def test_simple_tsf(self):
|
452
|
+
result = get_tsf(self.df, 14, calculator_type="simple")
|
453
|
+
self.assertEqual(len(result), len(self.df))
|
454
|
+
self.assertTrue("tsf" in result.columns)
|
455
|
+
|
456
|
+
def test_simple_apo(self):
|
457
|
+
result = get_apo(self.df, 12, 26, 0, calculator_type="simple")
|
458
|
+
self.assertEqual(len(result), len(self.df))
|
459
|
+
self.assertTrue("apo" in result.columns)
|
460
|
+
|
461
|
+
def test_simple_aroon(self):
|
462
|
+
result = get_aroon(self.df, 14, calculator_type="simple")
|
463
|
+
self.assertEqual(len(result), len(self.df))
|
464
|
+
self.assertTrue("aroon_up" in result.columns)
|
465
|
+
self.assertTrue("aroon_down" in result.columns)
|
466
|
+
|
467
|
+
def test_simple_aroonosc(self):
|
468
|
+
result = get_aroonosc(self.df, 14, calculator_type="simple")
|
469
|
+
self.assertEqual(len(result), len(self.df))
|
470
|
+
self.assertTrue("aroonosc" in result.columns)
|
471
|
+
|
472
|
+
def test_simple_ppo(self):
|
473
|
+
result = get_ppo(self.df, 12, 26, 0, calculator_type="simple")
|
474
|
+
self.assertEqual(len(result), len(self.df))
|
475
|
+
self.assertTrue("ppo" in result.columns)
|
476
|
+
|
477
|
+
def test_simple_roc(self):
|
478
|
+
result = get_roc(self.df, 10, calculator_type="simple")
|
479
|
+
self.assertEqual(len(result), len(self.df))
|
480
|
+
self.assertTrue("roc" in result.columns)
|
481
|
+
|
482
|
+
def test_simple_rocp(self):
|
483
|
+
result = get_rocp(self.df, 10, calculator_type="simple")
|
484
|
+
self.assertEqual(len(result), len(self.df))
|
485
|
+
self.assertTrue("rocp" in result.columns)
|
486
|
+
|
487
|
+
def test_simple_rocr(self):
|
488
|
+
result = get_rocr(self.df, 10, calculator_type="simple")
|
489
|
+
self.assertEqual(len(result), len(self.df))
|
490
|
+
self.assertTrue("rocr" in result.columns)
|
491
|
+
|
492
|
+
def test_simple_rocr100(self):
|
493
|
+
result = get_rocr100(self.df, 10, calculator_type="simple")
|
494
|
+
self.assertEqual(len(result), len(self.df))
|
495
|
+
self.assertTrue("rocr100" in result.columns)
|
496
|
+
|
497
|
+
def test_simple_trix(self):
|
498
|
+
result = get_trix(self.df, 30, calculator_type="simple")
|
499
|
+
self.assertEqual(len(result), len(self.df))
|
500
|
+
self.assertTrue("trix" in result.columns)
|
501
|
+
|
502
|
+
def test_simple_minus_di(self):
|
503
|
+
result = get_minus_di(self.df, 14, calculator_type="simple")
|
504
|
+
self.assertEqual(len(result), len(self.df))
|
505
|
+
self.assertTrue("minus_di" in result.columns)
|
506
|
+
|
507
|
+
def test_simple_minus_dm(self):
|
508
|
+
result = get_minus_dm(self.df, 14, calculator_type="simple")
|
509
|
+
self.assertEqual(len(result), len(self.df))
|
510
|
+
self.assertTrue("minus_dm" in result.columns)
|
511
|
+
|
512
|
+
def test_simple_plus_di(self):
|
513
|
+
result = get_plus_di(self.df, 14, calculator_type="simple")
|
514
|
+
self.assertEqual(len(result), len(self.df))
|
515
|
+
self.assertTrue("plus_di" in result.columns)
|
516
|
+
|
517
|
+
def test_simple_plus_dm(self):
|
518
|
+
result = get_plus_dm(self.df, 14, calculator_type="simple")
|
519
|
+
self.assertEqual(len(result), len(self.df))
|
520
|
+
self.assertTrue("plus_dm" in result.columns)
|
@@ -1,230 +0,0 @@
|
|
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")
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|