qka 2.5.2.dev2__tar.gz → 2.5.3.dev2__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.
- {qka-2.5.2.dev2 → qka-2.5.3.dev2}/PKG-INFO +1 -1
- {qka-2.5.2.dev2 → qka-2.5.3.dev2}/qka/core/data.py +21 -18
- {qka-2.5.2.dev2 → qka-2.5.3.dev2}/qka/core/indicator.py +140 -22
- {qka-2.5.2.dev2 → qka-2.5.3.dev2}/.github/workflows/release.yml +0 -0
- {qka-2.5.2.dev2 → qka-2.5.3.dev2}/.gitignore +0 -0
- {qka-2.5.2.dev2 → qka-2.5.3.dev2}/.vscode/settings.json +0 -0
- {qka-2.5.2.dev2 → qka-2.5.3.dev2}/CHANGELOG.md +0 -0
- {qka-2.5.2.dev2 → qka-2.5.3.dev2}/LICENSE +0 -0
- {qka-2.5.2.dev2 → qka-2.5.3.dev2}/README.md +0 -0
- {qka-2.5.2.dev2 → qka-2.5.3.dev2}/pyproject.toml +0 -0
- {qka-2.5.2.dev2 → qka-2.5.3.dev2}/qka/__init__.py +0 -0
- {qka-2.5.2.dev2 → qka-2.5.3.dev2}/qka/cli.py +0 -0
- {qka-2.5.2.dev2 → qka-2.5.3.dev2}/qka/core/__init__.py +0 -0
- {qka-2.5.2.dev2 → qka-2.5.3.dev2}/qka/core/accessor.py +0 -0
- {qka-2.5.2.dev2 → qka-2.5.3.dev2}/qka/core/analysis.py +0 -0
- {qka-2.5.2.dev2 → qka-2.5.3.dev2}/qka/core/backtest.py +0 -0
- {qka-2.5.2.dev2 → qka-2.5.3.dev2}/qka/core/broker.py +0 -0
- {qka-2.5.2.dev2 → qka-2.5.3.dev2}/qka/core/report.py +0 -0
- {qka-2.5.2.dev2 → qka-2.5.3.dev2}/qka/core/simulate.py +0 -0
- {qka-2.5.2.dev2 → qka-2.5.3.dev2}/qka/core/sizing.py +0 -0
- {qka-2.5.2.dev2 → qka-2.5.3.dev2}/qka/core/strategy.py +0 -0
- {qka-2.5.2.dev2 → qka-2.5.3.dev2}/qka/core/tdx.py +0 -0
- {qka-2.5.2.dev2 → qka-2.5.3.dev2}/qka/utils/__init__.py +0 -0
- {qka-2.5.2.dev2 → qka-2.5.3.dev2}/qka/utils/logger.py +0 -0
- {qka-2.5.2.dev2 → qka-2.5.3.dev2}/skills/qka/SKILL.md +0 -0
- {qka-2.5.2.dev2 → qka-2.5.3.dev2}/skills/qka/references/analysis.md +0 -0
- {qka-2.5.2.dev2 → qka-2.5.3.dev2}/skills/qka/references/backtest.md +0 -0
- {qka-2.5.2.dev2 → qka-2.5.3.dev2}/skills/qka/references/broker.md +0 -0
- {qka-2.5.2.dev2 → qka-2.5.3.dev2}/skills/qka/references/data.md +0 -0
- {qka-2.5.2.dev2 → qka-2.5.3.dev2}/skills/qka/references/sizing.md +0 -0
- {qka-2.5.2.dev2 → qka-2.5.3.dev2}/skills/qka/references/strategy.md +0 -0
- {qka-2.5.2.dev2 → qka-2.5.3.dev2}/tools/generate_api_ref.py +0 -0
|
@@ -1375,8 +1375,8 @@ class Data():
|
|
|
1375
1375
|
def _min_rows_for_factors(self):
|
|
1376
1376
|
"""计算所有因子所需的最小行数(含显式预热天数)。
|
|
1377
1377
|
|
|
1378
|
-
|
|
1379
|
-
|
|
1378
|
+
遍历所有因子条目的窗口参数(整数按交易日数、'1Y' 这类按日历折算成行数),
|
|
1379
|
+
取最大值作为窗口上限的保守估计,再与显式 warmup 取较大者。
|
|
1380
1380
|
"""
|
|
1381
1381
|
if not self._factors or not isinstance(self._factors, dict):
|
|
1382
1382
|
return self.warmup
|
|
@@ -1384,14 +1384,16 @@ class Data():
|
|
|
1384
1384
|
for spec in self._factors.values():
|
|
1385
1385
|
if callable(spec) or not isinstance(spec, (list, tuple)):
|
|
1386
1386
|
continue
|
|
1387
|
-
#
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
while idx < len(rest) and isinstance(rest[idx], str):
|
|
1391
|
-
idx += 1
|
|
1392
|
-
for v in rest[idx:]:
|
|
1387
|
+
# 窗口可能是整数(交易日数),也可能是 '1Y' / '3M' / '20D' 这样的日历写法。
|
|
1388
|
+
# 字符串要按交易日折算成行数,否则「数据够不够算」的门槛会漏掉它。
|
|
1389
|
+
for v in list(spec[1:]):
|
|
1393
1390
|
if isinstance(v, int):
|
|
1394
1391
|
max_window = max(max_window, v)
|
|
1392
|
+
elif isinstance(v, str) and v.strip() and v.strip()[-1].upper() in 'YMD':
|
|
1393
|
+
try:
|
|
1394
|
+
max_window = max(max_window, indicator.window_to_rows(v))
|
|
1395
|
+
except ValueError:
|
|
1396
|
+
pass # 不是窗口写法(如列名 'close_d'),忽略
|
|
1395
1397
|
return max(max_window, self.warmup)
|
|
1396
1398
|
|
|
1397
1399
|
# ── 因子缓存 ────────────────────────────────────────────────
|
|
@@ -1767,21 +1769,22 @@ class Data():
|
|
|
1767
1769
|
|
|
1768
1770
|
@staticmethod
|
|
1769
1771
|
def _spec_window(spec) -> int:
|
|
1770
|
-
"""
|
|
1772
|
+
"""从因子定义里取窗口,**统一换算成行数**;取不到返回 0。
|
|
1771
1773
|
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
+
整数原样;字符串('1Y' / '3M' / '20D')按交易日折算 —— 见
|
|
1775
|
+
`indicator.window_to_rows`。增量预热要用它算「往回留多少行」。
|
|
1774
1776
|
"""
|
|
1775
1777
|
if not isinstance(spec, (list, tuple)):
|
|
1776
1778
|
return 0
|
|
1777
|
-
rest = list(spec[1:])
|
|
1778
|
-
idx = 0
|
|
1779
|
-
while idx < len(rest) and isinstance(rest[idx], str):
|
|
1780
|
-
idx += 1
|
|
1781
1779
|
win = 0
|
|
1782
|
-
for v in
|
|
1783
|
-
if isinstance(v, int)
|
|
1784
|
-
win = v
|
|
1780
|
+
for v in list(spec[1:]):
|
|
1781
|
+
if isinstance(v, int):
|
|
1782
|
+
win = max(win, v)
|
|
1783
|
+
elif isinstance(v, str) and v.strip() and v.strip()[-1].upper() in 'YMD':
|
|
1784
|
+
try:
|
|
1785
|
+
win = max(win, indicator.window_to_rows(v))
|
|
1786
|
+
except ValueError:
|
|
1787
|
+
pass # 不是窗口写法(如列名),忽略
|
|
1785
1788
|
return win
|
|
1786
1789
|
|
|
1787
1790
|
@staticmethod
|
|
@@ -17,6 +17,105 @@ from statsmodels.tsa.filters.hp_filter import hpfilter
|
|
|
17
17
|
from qka.core.analysis import _zigzag_core
|
|
18
18
|
|
|
19
19
|
|
|
20
|
+
# ── 窗口参数:整数 = 行数,字符串 = 日历周期 ──────────────────────
|
|
21
|
+
# ('qka.alpha', 250) → 往前数 250 根 K 线(老写法,行为完全不变)
|
|
22
|
+
# ('qka.alpha', '1Y') → 过去一个日历年(约 243 个交易日,每年自动适配)
|
|
23
|
+
#
|
|
24
|
+
# 🚨 字符串**必须自己解析**,不能直接丢给 pandas:pandas 里小写 m 是「分钟」、
|
|
25
|
+
# 大写 M 是「月末」,`'12m'` 会被理解成 12 分钟还静默算出一堆垃圾。
|
|
26
|
+
# 规范写法大写(1Y / 3M / 20D),解析时大小写都认。
|
|
27
|
+
_WINDOW_UNITS = {'Y': 365.0, 'M': 30.0, 'D': 1.0}
|
|
28
|
+
|
|
29
|
+
# 一年按多少个交易日折算 —— 只用来定「窗口内至少要攒够几根 K 线」。
|
|
30
|
+
# 实测(600519 全历史,365 天窗口内行数):中位 243,最少 235,最多 250。
|
|
31
|
+
# 门槛取 233(比最少的 235 再低一点)—— 取 240 的话,遇上节假日特别密的年份
|
|
32
|
+
# 那几天会凑不满、因子留空,白丢数据。
|
|
33
|
+
_TRADING_DAYS_PER_YEAR = 233
|
|
34
|
+
|
|
35
|
+
# 真正折算「一个时间窗口里该有多少根 K 线」时用这个数(实际交易日的中位数)。
|
|
36
|
+
# 🚨 跟上面的门槛不是一回事:门槛是「够不够算」,这个数是「要留多少行当预热」。
|
|
37
|
+
# 用 233 去预热 10 年期会少 100 根样本,尾部算出来的值就是错的。
|
|
38
|
+
_TRADING_DAYS_ACTUAL = 243
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def resolve_window(window):
|
|
42
|
+
"""把窗口参数翻译成 (rolling 的窗口, 最小样本数)。
|
|
43
|
+
|
|
44
|
+
Args:
|
|
45
|
+
window: 整数(行数)或字符串('1Y' / '3M' / '20D',大小写都认)
|
|
46
|
+
|
|
47
|
+
Returns:
|
|
48
|
+
(win, min_periods)
|
|
49
|
+
- 整数:win 是 int,min_periods 等于它本身 —— 与旧行为逐位一致
|
|
50
|
+
- 字符串:win 是 pd.Timedelta,min_periods 是折合出来的交易日数
|
|
51
|
+
|
|
52
|
+
Raises:
|
|
53
|
+
ValueError: 写法认不出,或数值 <= 0
|
|
54
|
+
"""
|
|
55
|
+
if isinstance(window, str):
|
|
56
|
+
text = window.strip().upper()
|
|
57
|
+
if not text:
|
|
58
|
+
raise ValueError('窗口参数不能为空字符串')
|
|
59
|
+
unit = text[-1]
|
|
60
|
+
if unit not in _WINDOW_UNITS:
|
|
61
|
+
raise ValueError(
|
|
62
|
+
f"认不出的窗口单位 {window!r}:只支持 Y(年)/ M(月)/ D(天),如 '1Y'、'3M'、'20D'"
|
|
63
|
+
)
|
|
64
|
+
try:
|
|
65
|
+
num = float(text[:-1])
|
|
66
|
+
except ValueError:
|
|
67
|
+
raise ValueError(f"认不出的窗口写法 {window!r}:应为「数字+单位」,如 '1Y'") from None
|
|
68
|
+
if num <= 0:
|
|
69
|
+
raise ValueError(f'窗口必须大于 0:{window!r}')
|
|
70
|
+
days = num * _WINDOW_UNITS[unit]
|
|
71
|
+
min_periods = max(1, int(round(days * _TRADING_DAYS_PER_YEAR / 365.0)))
|
|
72
|
+
return pd.Timedelta(days=days), min_periods
|
|
73
|
+
|
|
74
|
+
try:
|
|
75
|
+
win = int(window)
|
|
76
|
+
except (TypeError, ValueError):
|
|
77
|
+
raise ValueError(f'窗口参数只能是整数或字符串,收到 {window!r}') from None
|
|
78
|
+
if win <= 0:
|
|
79
|
+
raise ValueError(f'窗口必须大于 0:{window!r}')
|
|
80
|
+
return win, win
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def window_to_rows(window) -> int:
|
|
84
|
+
"""这个窗口总共跨多少个交易日 —— 用来判断「数据够不够」和「预热要留多少行」。
|
|
85
|
+
|
|
86
|
+
- 整数:原样返回
|
|
87
|
+
- 字符串:按**实际交易日**折算('1Y' → 243、'10Y' → 2430)。
|
|
88
|
+
|
|
89
|
+
🚨 这里用实际交易日(243/年)而不是 `min_periods`(240/年):差的那 3 根在
|
|
90
|
+
10 年期会放大成 30 根,增量预热按 2400 行回溯的话,尾部算出来的值会比全量算的少
|
|
91
|
+
30 个样本 —— 结果不一致,而且不会报错。
|
|
92
|
+
"""
|
|
93
|
+
win, _min_periods = resolve_window(window)
|
|
94
|
+
if isinstance(win, int):
|
|
95
|
+
return win
|
|
96
|
+
days = win / pd.Timedelta(days=1)
|
|
97
|
+
return int(np.ceil(days * _TRADING_DAYS_ACTUAL / 365.0))
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def _window_starts(index, window):
|
|
101
|
+
"""算出「每个位置对应的窗口起点行号」,供手写循环的指标使用。
|
|
102
|
+
|
|
103
|
+
Returns:
|
|
104
|
+
(starts, min_periods):starts[i] 是第 i 行的窗口起始行号;
|
|
105
|
+
窗口还不够长时给 -1,调用方跳过即可。
|
|
106
|
+
"""
|
|
107
|
+
n = len(index)
|
|
108
|
+
if isinstance(window, str):
|
|
109
|
+
win, min_periods = resolve_window(window)
|
|
110
|
+
left = index - win # DatetimeIndex - Timedelta
|
|
111
|
+
starts = np.asarray(index.searchsorted(left, side='left'), dtype=np.int64)
|
|
112
|
+
return starts, min_periods
|
|
113
|
+
w = int(window)
|
|
114
|
+
starts = np.arange(n, dtype=np.int64) - w + 1
|
|
115
|
+
starts[starts < 0] = -1
|
|
116
|
+
return starts, w
|
|
117
|
+
|
|
118
|
+
|
|
20
119
|
def alpha(df: pd.DataFrame, symbol: str, window: int,
|
|
21
120
|
rf: float | pd.Series = 0.0, periods_per_year: int = 252) -> pd.Series:
|
|
22
121
|
"""滚动詹森 α(年化)。
|
|
@@ -27,7 +126,8 @@ def alpha(df: pd.DataFrame, symbol: str, window: int,
|
|
|
27
126
|
Args:
|
|
28
127
|
df: 含 {symbol}|returns 和 benchmark|returns 列的 DataFrame
|
|
29
128
|
symbol: 股票代码
|
|
30
|
-
window:
|
|
129
|
+
window: 滚动窗口。整数 = 交易日数(往前数多少根);
|
|
130
|
+
字符串 = 日历周期,如 '1Y'(一个日历年)/ '3M' / '20D',大小写都认
|
|
31
131
|
rf: 无风险利率(**年化**,小数)。两种写法:
|
|
32
132
|
- 标量:全程同一个数,如 0.02
|
|
33
133
|
- 序列(带日期索引):**取该窗口内的平均值**,见 _rf_window_mean
|
|
@@ -42,10 +142,11 @@ def alpha(df: pd.DataFrame, symbol: str, window: int,
|
|
|
42
142
|
"""
|
|
43
143
|
returns = df[f'{symbol}|returns']
|
|
44
144
|
bench = _get_benchmark(df)
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
145
|
+
win, mp = resolve_window(window)
|
|
146
|
+
rp = returns.rolling(win, min_periods=mp).mean() * periods_per_year
|
|
147
|
+
rm = bench.rolling(win, min_periods=mp).mean() * periods_per_year
|
|
148
|
+
cov = returns.rolling(win, min_periods=mp).cov(bench)
|
|
149
|
+
var = bench.rolling(win, min_periods=mp).var()
|
|
49
150
|
beta_series = cov / var
|
|
50
151
|
rf = _rf_window_mean(rf, df.index, window)
|
|
51
152
|
return rp - (rf + beta_series * (rm - rf))
|
|
@@ -59,15 +160,17 @@ def beta(df: pd.DataFrame, symbol: str, window: int) -> pd.Series:
|
|
|
59
160
|
Args:
|
|
60
161
|
df: 含 {symbol}|returns 和 benchmark|returns 列的 DataFrame
|
|
61
162
|
symbol: 股票代码
|
|
62
|
-
window:
|
|
163
|
+
window: 滚动窗口。整数 = 交易日数(往前数多少根);
|
|
164
|
+
字符串 = 日历周期,如 '1Y'(一个日历年)/ '3M' / '20D',大小写都认
|
|
63
165
|
|
|
64
166
|
Returns:
|
|
65
167
|
pd.Series: 滚动 β 值
|
|
66
168
|
"""
|
|
67
169
|
returns = df[f'{symbol}|returns']
|
|
68
170
|
bench = _get_benchmark(df)
|
|
69
|
-
|
|
70
|
-
|
|
171
|
+
win, mp = resolve_window(window)
|
|
172
|
+
cov = returns.rolling(win, min_periods=mp).cov(bench)
|
|
173
|
+
var = bench.rolling(win, min_periods=mp).var()
|
|
71
174
|
return cov / var
|
|
72
175
|
|
|
73
176
|
|
|
@@ -81,7 +184,8 @@ def sharpe(df: pd.DataFrame, symbol: str, window: int,
|
|
|
81
184
|
Args:
|
|
82
185
|
df: 含 {symbol}|returns 列的 DataFrame
|
|
83
186
|
symbol: 股票代码
|
|
84
|
-
window:
|
|
187
|
+
window: 滚动窗口。整数 = 交易日数(往前数多少根);
|
|
188
|
+
字符串 = 日历周期,如 '1Y'(一个日历年)/ '3M' / '20D',大小写都认
|
|
85
189
|
rf: 无风险利率(**年化**,小数)。标量 = 全程一个数;
|
|
86
190
|
序列 = 取该窗口内的平均值(见 _rf_window_mean)
|
|
87
191
|
periods_per_year: 每年交易日数,默认 252
|
|
@@ -90,8 +194,9 @@ def sharpe(df: pd.DataFrame, symbol: str, window: int,
|
|
|
90
194
|
pd.Series: 滚动夏普比率
|
|
91
195
|
"""
|
|
92
196
|
returns = df[f'{symbol}|returns']
|
|
93
|
-
|
|
94
|
-
|
|
197
|
+
win, mp = resolve_window(window)
|
|
198
|
+
rp = returns.rolling(win, min_periods=mp).mean() * periods_per_year
|
|
199
|
+
vol = returns.rolling(win, min_periods=mp).std() * np.sqrt(periods_per_year)
|
|
95
200
|
rf = _rf_window_mean(rf, df.index, window)
|
|
96
201
|
return (rp - rf) / vol
|
|
97
202
|
|
|
@@ -104,7 +209,8 @@ def max_drawdown(df: pd.DataFrame, symbol: str, window: int) -> pd.Series:
|
|
|
104
209
|
Args:
|
|
105
210
|
df: 含 {symbol}|returns 列的 DataFrame
|
|
106
211
|
symbol: 股票代码
|
|
107
|
-
window:
|
|
212
|
+
window: 滚动窗口。整数 = 交易日数(往前数多少根);
|
|
213
|
+
字符串 = 日历周期,如 '1Y'(一个日历年)/ '3M' / '20D',大小写都认
|
|
108
214
|
|
|
109
215
|
Returns:
|
|
110
216
|
pd.Series: 滚动最大回撤(负数)
|
|
@@ -113,8 +219,13 @@ def max_drawdown(df: pd.DataFrame, symbol: str, window: int) -> pd.Series:
|
|
|
113
219
|
cum = (1 + returns).cumprod()
|
|
114
220
|
cum_values = cum.values
|
|
115
221
|
result = np.full(len(cum_values), np.nan)
|
|
116
|
-
|
|
117
|
-
|
|
222
|
+
# 窗口起点按参数类型算:整数按行数、字符串按日历回溯(见 _window_starts)
|
|
223
|
+
starts, min_periods = _window_starts(cum.index, window)
|
|
224
|
+
for i in range(len(cum_values)):
|
|
225
|
+
s = starts[i]
|
|
226
|
+
if s < 0 or (i - s + 1) < min_periods:
|
|
227
|
+
continue
|
|
228
|
+
seg = cum_values[s : i + 1]
|
|
118
229
|
# 窗口内逐日刷新峰值,再取「任意时刻从峰到谷」最深的那一次。
|
|
119
230
|
# 旧实现只算 (末值 - 窗口峰值) / 窗口峰值 —— 那是「当前回撤」(现在离最高点多远),
|
|
120
231
|
# 会漏掉中间已经发生过的深坑:净值 1 → 1.2 → 0.6 → 1.3,旧算法给 0,
|
|
@@ -133,7 +244,8 @@ def information_ratio(df: pd.DataFrame, symbol: str, window: int,
|
|
|
133
244
|
Args:
|
|
134
245
|
df: 含 {symbol}|returns 和 benchmark|returns 列的 DataFrame
|
|
135
246
|
symbol: 股票代码
|
|
136
|
-
window:
|
|
247
|
+
window: 滚动窗口。整数 = 交易日数(往前数多少根);
|
|
248
|
+
字符串 = 日历周期,如 '1Y'(一个日历年)/ '3M' / '20D',大小写都认
|
|
137
249
|
periods_per_year: 每年交易日数,默认 252
|
|
138
250
|
|
|
139
251
|
Returns:
|
|
@@ -142,8 +254,9 @@ def information_ratio(df: pd.DataFrame, symbol: str, window: int,
|
|
|
142
254
|
returns = df[f'{symbol}|returns']
|
|
143
255
|
bench = _get_benchmark(df)
|
|
144
256
|
excess = returns - bench
|
|
145
|
-
|
|
146
|
-
|
|
257
|
+
win, mp = resolve_window(window)
|
|
258
|
+
mean_excess = excess.rolling(win, min_periods=mp).mean() * periods_per_year
|
|
259
|
+
vol_excess = excess.rolling(win, min_periods=mp).std() * np.sqrt(periods_per_year)
|
|
147
260
|
return mean_excess / vol_excess
|
|
148
261
|
|
|
149
262
|
|
|
@@ -157,7 +270,8 @@ def rolling_zigzag(df: pd.DataFrame, symbol: str, window: int,
|
|
|
157
270
|
Args:
|
|
158
271
|
df: 含 {symbol}|close 列的 DataFrame
|
|
159
272
|
symbol: 股票代码
|
|
160
|
-
window:
|
|
273
|
+
window: 滚动窗口。整数 = 交易日数(往前数多少根);
|
|
274
|
+
字符串 = 日历周期,如 '1Y'(一个日历年)/ '3M' / '20D',大小写都认,至少 10
|
|
161
275
|
threshold: 反转阈值,默认 0.3。峰顶回落 30% 确认顶
|
|
162
276
|
lamb: HP 滤波平滑参数,默认 1600(日线数据标准值)
|
|
163
277
|
|
|
@@ -173,11 +287,14 @@ def rolling_zigzag(df: pd.DataFrame, symbol: str, window: int,
|
|
|
173
287
|
idx = close.index
|
|
174
288
|
result = np.full(len(values), np.nan)
|
|
175
289
|
|
|
176
|
-
|
|
290
|
+
starts, min_periods = _window_starts(idx, window)
|
|
291
|
+
if len(values) < min_periods or min_periods < 10:
|
|
177
292
|
return pd.Series(result, index=idx)
|
|
178
293
|
|
|
179
|
-
for i in range(
|
|
180
|
-
win_start = i
|
|
294
|
+
for i in range(len(values)):
|
|
295
|
+
win_start = starts[i]
|
|
296
|
+
if win_start < 0 or (i - win_start + 1) < min_periods:
|
|
297
|
+
continue
|
|
181
298
|
win_vals = values[win_start : i + 1]
|
|
182
299
|
win_idx = idx[win_start : i + 1]
|
|
183
300
|
|
|
@@ -237,7 +354,8 @@ def _rf_window_mean(rf, index, window: int):
|
|
|
237
354
|
if index is not None and len(s) and isinstance(index, pd.DatetimeIndex):
|
|
238
355
|
s = s.reindex(s.index.union(index)).ffill().reindex(index)
|
|
239
356
|
|
|
240
|
-
|
|
357
|
+
win, min_periods = resolve_window(window)
|
|
358
|
+
return s.rolling(win, min_periods=min_periods).mean()
|
|
241
359
|
|
|
242
360
|
|
|
243
361
|
def _get_benchmark(df: pd.DataFrame) -> pd.Series:
|
|
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
|