py-alpha-lib 0.2.0__tar.gz → 0.2.1__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.
Files changed (76) hide show
  1. {py_alpha_lib-0.2.0 → py_alpha_lib-0.2.1}/CHANGELOG.md +9 -0
  2. {py_alpha_lib-0.2.0 → py_alpha_lib-0.2.1}/Cargo.lock +15 -42
  3. {py_alpha_lib-0.2.0 → py_alpha_lib-0.2.1}/Cargo.toml +3 -3
  4. {py_alpha_lib-0.2.0 → py_alpha_lib-0.2.1}/PKG-INFO +2 -2
  5. {py_alpha_lib-0.2.0 → py_alpha_lib-0.2.1}/README.md +1 -1
  6. {py_alpha_lib-0.2.0 → py_alpha_lib-0.2.1}/src/algo/backfill.rs +4 -6
  7. {py_alpha_lib-0.2.0 → py_alpha_lib-0.2.1}/src/algo/context.rs +4 -17
  8. {py_alpha_lib-0.2.0 → py_alpha_lib-0.2.1}/src/algo/cross.rs +37 -26
  9. {py_alpha_lib-0.2.0 → py_alpha_lib-0.2.1}/src/algo/ema.rs +3 -6
  10. {py_alpha_lib-0.2.0 → py_alpha_lib-0.2.1}/src/algo/entropy.rs +2 -3
  11. {py_alpha_lib-0.2.0 → py_alpha_lib-0.2.1}/src/algo/extremum.rs +4 -5
  12. {py_alpha_lib-0.2.0 → py_alpha_lib-0.2.1}/src/algo/group.rs +0 -12
  13. {py_alpha_lib-0.2.0 → py_alpha_lib-0.2.1}/src/algo/ma.rs +7 -9
  14. {py_alpha_lib-0.2.0 → py_alpha_lib-0.2.1}/src/algo/misc.rs +7 -10
  15. {py_alpha_lib-0.2.0 → py_alpha_lib-0.2.1}/src/algo/moments.rs +6 -8
  16. {py_alpha_lib-0.2.0 → py_alpha_lib-0.2.1}/src/algo/neutralize.rs +2 -7
  17. {py_alpha_lib-0.2.0 → py_alpha_lib-0.2.1}/src/algo/rank.rs +2 -11
  18. {py_alpha_lib-0.2.0 → py_alpha_lib-0.2.1}/src/algo/returns.rs +3 -10
  19. {py_alpha_lib-0.2.0 → py_alpha_lib-0.2.1}/src/algo/scan.rs +4 -8
  20. {py_alpha_lib-0.2.0 → py_alpha_lib-0.2.1}/src/algo/series.rs +10 -14
  21. {py_alpha_lib-0.2.0 → py_alpha_lib-0.2.1}/src/algo/slope.rs +2 -3
  22. {py_alpha_lib-0.2.0 → py_alpha_lib-0.2.1}/src/algo/stats.rs +15 -24
  23. {py_alpha_lib-0.2.0 → py_alpha_lib-0.2.1}/src/algo/stddev.rs +3 -4
  24. {py_alpha_lib-0.2.0 → py_alpha_lib-0.2.1}/src/algo/sum.rs +10 -14
  25. {py_alpha_lib-0.2.0 → py_alpha_lib-0.2.1}/src/algo/zscore.rs +3 -8
  26. {py_alpha_lib-0.2.0 → py_alpha_lib-0.2.1}/tests/usage.py +16 -17
  27. {py_alpha_lib-0.2.0 → py_alpha_lib-0.2.1}/.agent/skills/add_algo/SKILL.md +0 -0
  28. {py_alpha_lib-0.2.0 → py_alpha_lib-0.2.1}/.github/workflows/CI.yml +0 -0
  29. {py_alpha_lib-0.2.0 → py_alpha_lib-0.2.1}/.gitignore +0 -0
  30. {py_alpha_lib-0.2.0 → py_alpha_lib-0.2.1}/.nwa-config.yaml +0 -0
  31. {py_alpha_lib-0.2.0 → py_alpha_lib-0.2.1}/LICENSE +0 -0
  32. {py_alpha_lib-0.2.0 → py_alpha_lib-0.2.1}/articles/001.md +0 -0
  33. {py_alpha_lib-0.2.0 → py_alpha_lib-0.2.1}/articles/COMPARISON.md +0 -0
  34. {py_alpha_lib-0.2.0 → py_alpha_lib-0.2.1}/benchmarks/benchmark_alpha101.py +0 -0
  35. {py_alpha_lib-0.2.0 → py_alpha_lib-0.2.1}/benchmarks/benchmark_pandas.py +0 -0
  36. {py_alpha_lib-0.2.0 → py_alpha_lib-0.2.1}/benchmarks/benchmark_polars_ta.py +0 -0
  37. {py_alpha_lib-0.2.0 → py_alpha_lib-0.2.1}/build.rs +0 -0
  38. {py_alpha_lib-0.2.0 → py_alpha_lib-0.2.1}/examples/gtja191/al/__init__.py +0 -0
  39. {py_alpha_lib-0.2.0 → py_alpha_lib-0.2.1}/examples/gtja191/al/alpha191.py +0 -0
  40. {py_alpha_lib-0.2.0 → py_alpha_lib-0.2.1}/examples/gtja191/al/alpha191_context.py +0 -0
  41. {py_alpha_lib-0.2.0 → py_alpha_lib-0.2.1}/examples/gtja191/alpha191.txt +0 -0
  42. {py_alpha_lib-0.2.0 → py_alpha_lib-0.2.1}/examples/gtja191/main.py +0 -0
  43. {py_alpha_lib-0.2.0 → py_alpha_lib-0.2.1}/examples/wq101/al/__init__.py +0 -0
  44. {py_alpha_lib-0.2.0 → py_alpha_lib-0.2.1}/examples/wq101/al/alpha101.py +0 -0
  45. {py_alpha_lib-0.2.0 → py_alpha_lib-0.2.1}/examples/wq101/al/alpha101_context.py +0 -0
  46. {py_alpha_lib-0.2.0 → py_alpha_lib-0.2.1}/examples/wq101/alpha101.txt +0 -0
  47. {py_alpha_lib-0.2.0 → py_alpha_lib-0.2.1}/examples/wq101/main.py +0 -0
  48. {py_alpha_lib-0.2.0 → py_alpha_lib-0.2.1}/examples/wq101/pd_/__init__.py +0 -0
  49. {py_alpha_lib-0.2.0 → py_alpha_lib-0.2.1}/examples/wq101/pd_/alpha101_adjusted.py +0 -0
  50. {py_alpha_lib-0.2.0 → py_alpha_lib-0.2.1}/examples/wq101/pl_/__init__.py +0 -0
  51. {py_alpha_lib-0.2.0 → py_alpha_lib-0.2.1}/examples/wq101/pl_/alpha101_adjusted.py +0 -0
  52. {py_alpha_lib-0.2.0 → py_alpha_lib-0.2.1}/examples/wq101/result.md +0 -0
  53. {py_alpha_lib-0.2.0 → py_alpha_lib-0.2.1}/pyproject.toml +0 -0
  54. {py_alpha_lib-0.2.0 → py_alpha_lib-0.2.1}/python/alpha/__init__.py +0 -0
  55. {py_alpha_lib-0.2.0 → py_alpha_lib-0.2.1}/python/alpha/algo/__init__.py +0 -0
  56. {py_alpha_lib-0.2.0 → py_alpha_lib-0.2.1}/python/alpha/algo/algo.py +0 -0
  57. {py_alpha_lib-0.2.0 → py_alpha_lib-0.2.1}/python/alpha/algo/algo_gen.py +0 -0
  58. {py_alpha_lib-0.2.0 → py_alpha_lib-0.2.1}/python/alpha/algo.md +0 -0
  59. {py_alpha_lib-0.2.0 → py_alpha_lib-0.2.1}/python/alpha/context.py +0 -0
  60. {py_alpha_lib-0.2.0 → py_alpha_lib-0.2.1}/python/alpha/lang/__init__.py +0 -0
  61. {py_alpha_lib-0.2.0 → py_alpha_lib-0.2.1}/python/alpha/lang/__main__.py +0 -0
  62. {py_alpha_lib-0.2.0 → py_alpha_lib-0.2.1}/python/alpha/lang/alpha.lark +0 -0
  63. {py_alpha_lib-0.2.0 → py_alpha_lib-0.2.1}/python/alpha/lang/parser.py +0 -0
  64. {py_alpha_lib-0.2.0 → py_alpha_lib-0.2.1}/python/alpha/lang/to_python.py +0 -0
  65. {py_alpha_lib-0.2.0 → py_alpha_lib-0.2.1}/python/conftest.py +0 -0
  66. {py_alpha_lib-0.2.0 → py_alpha_lib-0.2.1}/python/tests/test_grammar.py +0 -0
  67. {py_alpha_lib-0.2.0 → py_alpha_lib-0.2.1}/python/tests/test_rank.py +0 -0
  68. {py_alpha_lib-0.2.0 → py_alpha_lib-0.2.1}/python/tests/test_talib.py +0 -0
  69. {py_alpha_lib-0.2.0 → py_alpha_lib-0.2.1}/python/tests/test_to_python.py +0 -0
  70. {py_alpha_lib-0.2.0 → py_alpha_lib-0.2.1}/rustfmt.toml +0 -0
  71. {py_alpha_lib-0.2.0 → py_alpha_lib-0.2.1}/src/algo/error.rs +0 -0
  72. {py_alpha_lib-0.2.0 → py_alpha_lib-0.2.1}/src/algo/mod.rs +0 -0
  73. {py_alpha_lib-0.2.0 → py_alpha_lib-0.2.1}/src/algo/skip_nan_window.rs +0 -0
  74. {py_alpha_lib-0.2.0 → py_alpha_lib-0.2.1}/src/lib.rs +0 -0
  75. {py_alpha_lib-0.2.0 → py_alpha_lib-0.2.1}/tests/rank.py +0 -0
  76. {py_alpha_lib-0.2.0 → py_alpha_lib-0.2.1}/tests/verify_sumif.py +0 -0
@@ -1,5 +1,14 @@
1
1
  # ChangeLog
2
2
 
3
+ ## [0.2.1] - 2026-04-06
4
+
5
+ ### Fix
6
+
7
+ - Fixed an internal logic bug in `Context::end` where `_end == 0` incorrectly returned `0` instead of yielding the expected `total` array dimension.
8
+ - Changed Context data truncation (`end` limit) to execute within the closure of each parallel grouped chunk (via `ctx.end()`) rather than globally shrinking arrays ahead of time (`align_end()`). This prevents group size corruption and misaligned cross-sectional indexing when `groups > 1`.
9
+ - Bound internal algorithm iterations and `SkipNanWindow` initializations dynamically to the `end` limit on a per-chunk basis while preserving absolute `start` index zero-alignments.
10
+ - Deprecated and removed legacy `align_end` and `align_end_mut` functions entirely to enforce correct boundary lifecycle patterns across algorithms.
11
+
3
12
 
4
13
  ## [0.2.0] - 2026-02-26
5
14
 
@@ -4,7 +4,7 @@ version = 4
4
4
 
5
5
  [[package]]
6
6
  name = "alpha"
7
- version = "0.2.0"
7
+ version = "0.2.1"
8
8
  dependencies = [
9
9
  "anyhow",
10
10
  "log",
@@ -74,15 +74,6 @@ version = "0.5.0"
74
74
  source = "registry+https://github.com/rust-lang/crates.io-index"
75
75
  checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
76
76
 
77
- [[package]]
78
- name = "indoc"
79
- version = "2.0.7"
80
- source = "registry+https://github.com/rust-lang/crates.io-index"
81
- checksum = "79cf5c93f93228cf8efb3ba362535fb11199ac548a09ce117c9b1adc3030d706"
82
- dependencies = [
83
- "rustversion",
84
- ]
85
-
86
77
  [[package]]
87
78
  name = "libc"
88
79
  version = "0.2.180"
@@ -105,15 +96,6 @@ dependencies = [
105
96
  "rawpointer",
106
97
  ]
107
98
 
108
- [[package]]
109
- name = "memoffset"
110
- version = "0.9.1"
111
- source = "registry+https://github.com/rust-lang/crates.io-index"
112
- checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a"
113
- dependencies = [
114
- "autocfg",
115
- ]
116
-
117
99
  [[package]]
118
100
  name = "ndarray"
119
101
  version = "0.17.2"
@@ -158,9 +140,9 @@ dependencies = [
158
140
 
159
141
  [[package]]
160
142
  name = "numpy"
161
- version = "0.27.1"
143
+ version = "0.28.0"
162
144
  source = "registry+https://github.com/rust-lang/crates.io-index"
163
- checksum = "7aac2e6a6e4468ffa092ad43c39b81c79196c2bb773b8db4085f695efe3bba17"
145
+ checksum = "778da78c64ddc928ebf5ad9df5edf0789410ff3bdbf3619aed51cd789a6af1e2"
164
146
  dependencies = [
165
147
  "libc",
166
148
  "ndarray",
@@ -204,35 +186,32 @@ dependencies = [
204
186
 
205
187
  [[package]]
206
188
  name = "pyo3"
207
- version = "0.27.2"
189
+ version = "0.28.2"
208
190
  source = "registry+https://github.com/rust-lang/crates.io-index"
209
- checksum = "ab53c047fcd1a1d2a8820fe84f05d6be69e9526be40cb03b73f86b6b03e6d87d"
191
+ checksum = "cf85e27e86080aafd5a22eae58a162e133a589551542b3e5cee4beb27e54f8e1"
210
192
  dependencies = [
211
- "indoc",
212
193
  "libc",
213
- "memoffset",
214
194
  "once_cell",
215
195
  "portable-atomic",
216
196
  "pyo3-build-config",
217
197
  "pyo3-ffi",
218
198
  "pyo3-macros",
219
- "unindent",
220
199
  ]
221
200
 
222
201
  [[package]]
223
202
  name = "pyo3-build-config"
224
- version = "0.27.2"
203
+ version = "0.28.2"
225
204
  source = "registry+https://github.com/rust-lang/crates.io-index"
226
- checksum = "b455933107de8642b4487ed26d912c2d899dec6114884214a0b3bb3be9261ea6"
205
+ checksum = "8bf94ee265674bf76c09fa430b0e99c26e319c945d96ca0d5a8215f31bf81cf7"
227
206
  dependencies = [
228
207
  "target-lexicon",
229
208
  ]
230
209
 
231
210
  [[package]]
232
211
  name = "pyo3-ffi"
233
- version = "0.27.2"
212
+ version = "0.28.2"
234
213
  source = "registry+https://github.com/rust-lang/crates.io-index"
235
- checksum = "1c85c9cbfaddf651b1221594209aed57e9e5cff63c4d11d1feead529b872a089"
214
+ checksum = "491aa5fc66d8059dd44a75f4580a2962c1862a1c2945359db36f6c2818b748dc"
236
215
  dependencies = [
237
216
  "libc",
238
217
  "pyo3-build-config",
@@ -240,9 +219,9 @@ dependencies = [
240
219
 
241
220
  [[package]]
242
221
  name = "pyo3-log"
243
- version = "0.13.2"
222
+ version = "0.13.3"
244
223
  source = "registry+https://github.com/rust-lang/crates.io-index"
245
- checksum = "2f8bae9ad5ba08b0b0ed2bb9c2bdbaeccc69cafca96d78cf0fbcea0d45d122bb"
224
+ checksum = "26c2ec80932c5c3b2d4fbc578c9b56b2d4502098587edb8bef5b6bfcad43682e"
246
225
  dependencies = [
247
226
  "arc-swap",
248
227
  "log",
@@ -251,9 +230,9 @@ dependencies = [
251
230
 
252
231
  [[package]]
253
232
  name = "pyo3-macros"
254
- version = "0.27.2"
233
+ version = "0.28.2"
255
234
  source = "registry+https://github.com/rust-lang/crates.io-index"
256
- checksum = "0a5b10c9bf9888125d917fb4d2ca2d25c8df94c7ab5a52e13313a07e050a3b02"
235
+ checksum = "f5d671734e9d7a43449f8480f8b38115df67bef8d21f76837fa75ee7aaa5e52e"
257
236
  dependencies = [
258
237
  "proc-macro2",
259
238
  "pyo3-macros-backend",
@@ -263,9 +242,9 @@ dependencies = [
263
242
 
264
243
  [[package]]
265
244
  name = "pyo3-macros-backend"
266
- version = "0.27.2"
245
+ version = "0.28.2"
267
246
  source = "registry+https://github.com/rust-lang/crates.io-index"
268
- checksum = "03b51720d314836e53327f5871d4c0cfb4fb37cc2c4a11cc71907a86342c40f9"
247
+ checksum = "22faaa1ce6c430a1f71658760497291065e6450d7b5dc2bcf254d49f66ee700a"
269
248
  dependencies = [
270
249
  "heck",
271
250
  "proc-macro2",
@@ -363,9 +342,3 @@ name = "unicode-ident"
363
342
  version = "1.0.22"
364
343
  source = "registry+https://github.com/rust-lang/crates.io-index"
365
344
  checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5"
366
-
367
- [[package]]
368
- name = "unindent"
369
- version = "0.2.4"
370
- source = "registry+https://github.com/rust-lang/crates.io-index"
371
- checksum = "7264e107f553ccae879d21fbea1d6724ac785e8c3bfc762137959b5802826ef3"
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "alpha"
3
- version = "0.2.0"
3
+ version = "0.2.1"
4
4
  edition = "2024"
5
5
  authors = ["ElseJJ"]
6
6
  readme = "README.md"
@@ -11,10 +11,10 @@ name = "alpha"
11
11
  crate-type = ["cdylib", "rlib"]
12
12
 
13
13
  [dependencies]
14
- pyo3 = { version = "0.27.0", features = ["abi3"] }
14
+ pyo3 = { version = "0.28", features = ["abi3"] }
15
15
  pyo3-log = "0.13"
16
16
  thiserror = "2"
17
- numpy = "0.27.0"
17
+ numpy = "0.28"
18
18
  num-traits = "0.2"
19
19
  rayon = "1.11"
20
20
  log = "0.4"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: py-alpha-lib
3
- Version: 0.2.0
3
+ Version: 0.2.1
4
4
  Classifier: Programming Language :: Rust
5
5
  Classifier: Programming Language :: Python :: Implementation :: CPython
6
6
  Classifier: Programming Language :: Python :: Implementation :: PyPy
@@ -71,7 +71,7 @@ Control computation behavior via `alpha.set_ctx()`:
71
71
  alpha.set_ctx(flags=alpha.FLAG_SKIP_NAN)
72
72
  data_nan = np.array([1, 2, np.nan, 4, 5, 6, 7, 8, 9, 10], dtype=np.float64)
73
73
  result = alpha.MA(data_nan, 3)
74
- # [1. 1.5 nan 3. 4.5 5. 6. 7. 8. 9.]
74
+ #[1. 1.5 nan 2.333 3.667 5. 6. 7. 8. 9. ]
75
75
  ```
76
76
 
77
77
 
@@ -56,7 +56,7 @@ Control computation behavior via `alpha.set_ctx()`:
56
56
  alpha.set_ctx(flags=alpha.FLAG_SKIP_NAN)
57
57
  data_nan = np.array([1, 2, np.nan, 4, 5, 6, 7, 8, 9, 10], dtype=np.float64)
58
58
  result = alpha.MA(data_nan, 3)
59
- # [1. 1.5 nan 3. 4.5 5. 6. 7. 8. 9.]
59
+ #[1. 1.5 nan 2.333 3.667 5. 6. 7. 8. 9. ]
60
60
  ```
61
61
 
62
62
 
@@ -19,18 +19,17 @@ pub fn ta_backfill<NumT: Float + Send + Sync>(
19
19
  return Err(Error::LengthMismatch(r.len(), input.len()));
20
20
  }
21
21
 
22
- let r = ctx.align_end_mut(r);
23
- let input = ctx.align_end(input);
24
22
 
25
23
  r.par_chunks_mut(ctx.chunk_size(r.len()))
26
24
  .zip(input.par_chunks(ctx.chunk_size(input.len())))
27
25
  .for_each(|(r, x)| {
28
26
  let start = ctx.start(r.len());
27
+ let end = ctx.end(r.len());
29
28
  r[..start].fill(NumT::nan());
30
29
 
31
30
  let mut last_valid = NumT::nan();
32
31
 
33
- for i in start..x.len() {
32
+ for i in start..end {
34
33
  let val = x[i];
35
34
  if is_normal(&val) {
36
35
  last_valid = val;
@@ -59,13 +58,12 @@ pub fn ta_count_nans<NumT: Float + Send + Sync>(
59
58
  return Err(Error::LengthMismatch(r.len(), input.len()));
60
59
  }
61
60
 
62
- let r = ctx.align_end_mut(r);
63
- let input = ctx.align_end(input);
64
61
 
65
62
  r.par_chunks_mut(ctx.chunk_size(r.len()))
66
63
  .zip(input.par_chunks(ctx.chunk_size(input.len())))
67
64
  .for_each(|(r, x)| {
68
65
  let start = ctx.start(r.len());
66
+ let end = ctx.end(r.len());
69
67
  r.fill(NumT::nan());
70
68
 
71
69
  let mut nan_count: usize = 0;
@@ -78,7 +76,7 @@ pub fn ta_count_nans<NumT: Float + Send + Sync>(
78
76
  }
79
77
  }
80
78
 
81
- for i in start..x.len() {
79
+ for i in start..end {
82
80
  let val = x[i];
83
81
  if !is_normal(&val) {
84
82
  nan_count += 1;
@@ -40,27 +40,14 @@ impl Context {
40
40
  }
41
41
  }
42
42
 
43
- pub fn align_end_mut<'a, T>(&'a self, r: &'a mut [T]) -> &'a mut [T] {
44
- if self._end == 0 {
45
- return r;
46
- }
47
- let end = self.end(r.len());
48
- &mut r[..end]
49
- }
50
-
51
- pub fn align_end<'a, T>(&'a self, r: &'a [T]) -> &'a [T] {
52
- if self._end == 0 {
53
- return r;
54
- }
55
- let end = self.end(r.len());
56
- &r[..end]
57
- }
58
-
59
43
  pub fn end(&self, total: usize) -> usize {
60
44
  if total == 0 {
61
45
  return 0;
62
46
  }
63
- if self._end >= 0 {
47
+ if self._end == 0 {
48
+ return total;
49
+ }
50
+ if self._end > 0 {
64
51
  (total).min(self._end as usize)
65
52
  } else {
66
53
  0.max(total as i32 + self._end) as usize
@@ -19,15 +19,12 @@ pub fn ta_cross<NumT: Float + Send + Sync>(
19
19
  return Err(Error::LengthMismatch(r.len(), a.len()));
20
20
  }
21
21
 
22
- let r = ctx.align_end_mut(r);
23
- let a = ctx.align_end(a);
24
- let b = ctx.align_end(b);
25
-
26
22
  r.par_chunks_mut(ctx.chunk_size(r.len()))
27
23
  .zip(a.par_chunks(ctx.chunk_size(a.len())))
28
24
  .zip(b.par_chunks(ctx.chunk_size(b.len())))
29
25
  .for_each(|((r, a), b)| {
30
26
  let start = ctx.start(r.len());
27
+ let end = ctx.end(r.len());
31
28
  r.fill(false);
32
29
 
33
30
  if ctx.is_skip_nan() {
@@ -41,7 +38,7 @@ pub fn ta_cross<NumT: Float + Send + Sync>(
41
38
 
42
39
  let mut was_less: Option<bool> = None;
43
40
 
44
- for i in start..a.len() {
41
+ for i in start..end {
45
42
  let val_a = a[i];
46
43
  let val_b = b[i];
47
44
 
@@ -60,7 +57,7 @@ pub fn ta_cross<NumT: Float + Send + Sync>(
60
57
  }
61
58
  } else {
62
59
  // Normal logic (look at i-1)
63
- for i in start..a.len() {
60
+ for i in start..end {
64
61
  if i == 0 {
65
62
  continue;
66
63
  }
@@ -94,21 +91,18 @@ pub fn ta_rcross<NumT: Float + Send + Sync>(
94
91
  return Err(Error::LengthMismatch(r.len(), a.len()));
95
92
  }
96
93
 
97
- let r = ctx.align_end_mut(r);
98
- let a = ctx.align_end(a);
99
- let b = ctx.align_end(b);
100
-
101
94
  r.par_chunks_mut(ctx.chunk_size(r.len()))
102
95
  .zip(a.par_chunks(ctx.chunk_size(a.len())))
103
96
  .zip(b.par_chunks(ctx.chunk_size(b.len())))
104
97
  .for_each(|((r, a), b)| {
105
98
  let start = ctx.start(r.len());
99
+ let end = ctx.end(r.len());
106
100
  r.fill(false);
107
101
 
108
102
  if ctx.is_skip_nan() {
109
103
  let mut was_greater: Option<bool> = None;
110
104
 
111
- for i in start..a.len() {
105
+ for i in start..end {
112
106
  let val_a = a[i];
113
107
  let val_b = b[i];
114
108
 
@@ -126,7 +120,7 @@ pub fn ta_rcross<NumT: Float + Send + Sync>(
126
120
  }
127
121
  }
128
122
  } else {
129
- for i in start..a.len() {
123
+ for i in start..end {
130
124
  if i == 0 {
131
125
  continue;
132
126
  }
@@ -160,15 +154,12 @@ pub fn ta_longcross<NumT: Float + Send + Sync>(
160
154
  return Err(Error::LengthMismatch(r.len(), a.len()));
161
155
  }
162
156
 
163
- let r = ctx.align_end_mut(r);
164
- let a = ctx.align_end(a);
165
- let b = ctx.align_end(b);
166
-
167
157
  r.par_chunks_mut(ctx.chunk_size(r.len()))
168
158
  .zip(a.par_chunks(ctx.chunk_size(a.len())))
169
159
  .zip(b.par_chunks(ctx.chunk_size(b.len())))
170
160
  .for_each(|((r, a), b)| {
171
161
  let start = ctx.start(r.len());
162
+ let end = ctx.end(r.len());
172
163
  r.fill(false);
173
164
 
174
165
  if n == 0 {
@@ -180,7 +171,7 @@ pub fn ta_longcross<NumT: Float + Send + Sync>(
180
171
  // Let's assume if N=0 it requires at least 1 previous period?
181
172
  // Usually N >= 1 for such funcs.
182
173
  // If N=0, let's treat as just A >= B
183
- for i in start..a.len() {
174
+ for i in start..end {
184
175
  if is_normal(&a[i]) && is_normal(&b[i]) {
185
176
  if a[i] >= b[i] {
186
177
  r[i] = true;
@@ -205,7 +196,7 @@ pub fn ta_longcross<NumT: Float + Send + Sync>(
205
196
 
206
197
  let mut less_count = 0;
207
198
 
208
- for i in start..a.len() {
199
+ for i in start..end {
209
200
  let val_a = a[i];
210
201
  let val_b = b[i];
211
202
 
@@ -240,7 +231,7 @@ pub fn ta_longcross<NumT: Float + Send + Sync>(
240
231
  }
241
232
  }
242
233
 
243
- for i in start..a.len() {
234
+ for i in start..end {
244
235
  let curr_a = a[i];
245
236
  let curr_b = b[i];
246
237
 
@@ -276,19 +267,16 @@ pub fn ta_rlongcross<NumT: Float + Send + Sync>(
276
267
  return Err(Error::LengthMismatch(r.len(), a.len()));
277
268
  }
278
269
 
279
- let r = ctx.align_end_mut(r);
280
- let a = ctx.align_end(a);
281
- let b = ctx.align_end(b);
282
-
283
270
  r.par_chunks_mut(ctx.chunk_size(r.len()))
284
271
  .zip(a.par_chunks(ctx.chunk_size(a.len())))
285
272
  .zip(b.par_chunks(ctx.chunk_size(b.len())))
286
273
  .for_each(|((r, a), b)| {
287
274
  let start = ctx.start(r.len());
275
+ let end = ctx.end(r.len());
288
276
  r.fill(false);
289
277
 
290
278
  if n == 0 {
291
- for i in start..a.len() {
279
+ for i in start..end {
292
280
  if is_normal(&a[i]) && is_normal(&b[i]) {
293
281
  if a[i] <= b[i] {
294
282
  r[i] = true;
@@ -301,7 +289,7 @@ pub fn ta_rlongcross<NumT: Float + Send + Sync>(
301
289
  if ctx.is_skip_nan() {
302
290
  let mut greater_count = 0;
303
291
 
304
- for i in start..a.len() {
292
+ for i in start..end {
305
293
  let val_a = a[i];
306
294
  let val_b = b[i];
307
295
 
@@ -329,7 +317,7 @@ pub fn ta_rlongcross<NumT: Float + Send + Sync>(
329
317
  }
330
318
  }
331
319
 
332
- for i in start..a.len() {
320
+ for i in start..end {
333
321
  let curr_a = a[i];
334
322
  let curr_b = b[i];
335
323
 
@@ -425,4 +413,27 @@ mod tests {
425
413
 
426
414
  assert_vec_eq_bool(&r, &vec![false, false, false, true]);
427
415
  }
416
+
417
+ #[test]
418
+ fn test_longcross_end_boundary() {
419
+ let a = vec![1.0, 2.0, 3.0, 6.0, 7.0, 1.0, 2.0, 3.0, 6.0, 7.0];
420
+ let b = vec![5.0, 5.0, 5.0, 5.0, 5.0, 5.0, 5.0, 5.0, 5.0, 5.0];
421
+
422
+ let mut r = vec![false; 10];
423
+ let mut ctx = Context::new(0, 2, 0);
424
+ for i in 4..=5 {
425
+ ctx._end = i;
426
+ ta_longcross(&ctx, &mut r, &a, &b, 2).unwrap();
427
+ // 0: cnt=1
428
+ // 1: cnt=2
429
+ // 2: 3<5. cnt=3 >= 2. -> T. cnt=0.
430
+
431
+ assert_vec_eq_bool(
432
+ &r,
433
+ &vec![
434
+ false, false, false, true, false, false, false, false, true, false,
435
+ ],
436
+ );
437
+ }
438
+ }
428
439
  }
@@ -64,8 +64,6 @@ pub fn ta_lwma<NumT: Float + Send + Sync>(
64
64
  return Err(Error::LengthMismatch(r.len(), input.len()));
65
65
  }
66
66
 
67
- let r = ctx.align_end_mut(r);
68
- let input = ctx.align_end(input);
69
67
 
70
68
  if periods == 1 {
71
69
  r.copy_from_slice(input);
@@ -79,10 +77,11 @@ pub fn ta_lwma<NumT: Float + Send + Sync>(
79
77
  .zip(input.par_chunks(ctx.chunk_size(input.len())))
80
78
  .for_each(|(r, x)| {
81
79
  let start = ctx.start(r.len());
80
+ let end = ctx.end(r.len());
82
81
  r.fill(NumT::nan());
83
82
 
84
83
  if ctx.is_skip_nan() {
85
- let iter = SkipNanWindow::new(x, periods, start);
84
+ let iter = SkipNanWindow::new(&x[..end], periods, start);
86
85
  let mut simple_sum = NumT::zero();
87
86
  let mut weighted_sum = NumT::zero();
88
87
 
@@ -155,7 +154,7 @@ pub fn ta_lwma<NumT: Float + Send + Sync>(
155
154
  }
156
155
  }
157
156
 
158
- for i in start..x.len() {
157
+ for i in start..end {
159
158
  let val = x[i];
160
159
 
161
160
  // Add new
@@ -220,8 +219,6 @@ pub fn ema_impl<NumT: Float + Send + Sync>(
220
219
  return Err(Error::LengthMismatch(r.len(), input.len()));
221
220
  }
222
221
 
223
- let r = ctx.align_end_mut(r);
224
- let input = ctx.align_end(input);
225
222
 
226
223
  if weight < NumT::zero() || weight > NumT::one() {
227
224
  return Err(Error::InvalidParameter(
@@ -23,8 +23,6 @@ pub fn ta_entropy<NumT: Float + Send + Sync>(
23
23
  return Err(Error::LengthMismatch(r.len(), input.len()));
24
24
  }
25
25
 
26
- let r = ctx.align_end_mut(r);
27
- let input = ctx.align_end(input);
28
26
 
29
27
  let bins = if bins == 0 { 10 } else { bins };
30
28
 
@@ -32,13 +30,14 @@ pub fn ta_entropy<NumT: Float + Send + Sync>(
32
30
  .zip(input.par_chunks(ctx.chunk_size(input.len())))
33
31
  .for_each(|(r, x)| {
34
32
  let start = ctx.start(r.len());
33
+ let end = ctx.end(r.len());
35
34
  r.fill(NumT::nan());
36
35
 
37
36
  // We need to collect window values to compute entropy,
38
37
  // since we need min/max for binning.
39
38
  let mut window: Vec<NumT> = Vec::with_capacity(periods);
40
39
 
41
- for i in start..x.len() {
40
+ for i in start..end {
42
41
  let val = x[i];
43
42
 
44
43
  // Add new value
@@ -87,13 +87,12 @@ where
87
87
  return Err(Error::LengthMismatch(r.len(), input.len()));
88
88
  }
89
89
 
90
- let r = ctx.align_end_mut(r);
91
- let input = ctx.align_end(input);
92
90
 
93
91
  r.par_chunks_mut(ctx.chunk_size(r.len()))
94
92
  .zip(input.par_chunks(ctx.chunk_size(input.len())))
95
93
  .for_each(|(r, x)| {
96
94
  let start = ctx.start(r.len());
95
+ let end = ctx.end(r.len());
97
96
  r.fill(NumT::nan());
98
97
 
99
98
  if periods == 0 {
@@ -102,7 +101,7 @@ where
102
101
  let mut best_idx: usize = 0;
103
102
  let mut has_nan_poison = false;
104
103
 
105
- for i in start..x.len() {
104
+ for i in start..end {
106
105
  let val = x[i];
107
106
  if !is_normal(&val) {
108
107
  if !ctx.is_skip_nan() {
@@ -141,7 +140,7 @@ where
141
140
  let mut deque: VecDeque<usize> = VecDeque::new();
142
141
 
143
142
  if ctx.is_skip_nan() {
144
- let iter = SkipNanWindow::new(x, periods, start);
143
+ let iter = SkipNanWindow::new(&x[..end], periods, start);
145
144
  for i in iter {
146
145
  let curr_val = x[i.end];
147
146
  if is_normal(&curr_val) {
@@ -205,7 +204,7 @@ where
205
204
  }
206
205
  }
207
206
 
208
- for i in pre_fill_start..x.len() {
207
+ for i in pre_fill_start..end {
209
208
  let val = x[i];
210
209
  let is_valid = is_normal(&val);
211
210
 
@@ -78,9 +78,6 @@ pub fn ta_group_rank<NumT: Float + Send + Sync + Debug>(
78
78
  return Err(Error::LengthMismatch(r.len(), input.len()));
79
79
  }
80
80
 
81
- let r = ctx.align_end_mut(r);
82
- let category = ctx.align_end(category);
83
- let input = ctx.align_end(input);
84
81
 
85
82
  let group_size = ctx.chunk_size(r.len()) as usize;
86
83
  let groups = ctx.groups() as usize;
@@ -89,9 +86,6 @@ pub fn ta_group_rank<NumT: Float + Send + Sync + Debug>(
89
86
  return Err(Error::LengthMismatch(r.len(), group_size * groups));
90
87
  }
91
88
 
92
- let r = ctx.align_end_mut(r);
93
- let category = ctx.align_end(category);
94
- let input = ctx.align_end(input);
95
89
 
96
90
  let r_ptr = UnsafePtr::new(r.as_mut_ptr(), r.len());
97
91
  (0..group_size).into_par_iter().for_each(|j| {
@@ -178,9 +172,6 @@ pub fn ta_group_zscore<NumT: Float + Send + Sync + Debug>(
178
172
  return Err(Error::LengthMismatch(r.len(), input.len()));
179
173
  }
180
174
 
181
- let r = ctx.align_end_mut(r);
182
- let category = ctx.align_end(category);
183
- let input = ctx.align_end(input);
184
175
 
185
176
  let group_size = ctx.chunk_size(r.len()) as usize;
186
177
  let groups = ctx.groups() as usize;
@@ -189,9 +180,6 @@ pub fn ta_group_zscore<NumT: Float + Send + Sync + Debug>(
189
180
  return Err(Error::LengthMismatch(r.len(), group_size * groups));
190
181
  }
191
182
 
192
- let r = ctx.align_end_mut(r);
193
- let category = ctx.align_end(category);
194
- let input = ctx.align_end(input);
195
183
 
196
184
  let r_ptr = UnsafePtr::new(r.as_mut_ptr(), r.len());
197
185
  (0..group_size).into_par_iter().for_each(|j| {
@@ -20,8 +20,6 @@ pub fn ta_ma<NumT: Float + Send + Sync>(
20
20
  return Err(Error::LengthMismatch(r.len(), input.len()));
21
21
  }
22
22
 
23
- let r = ctx.align_end_mut(r);
24
- let input = ctx.align_end(input);
25
23
 
26
24
  if periods == 1 {
27
25
  r.copy_from_slice(input);
@@ -32,9 +30,10 @@ pub fn ta_ma<NumT: Float + Send + Sync>(
32
30
  .zip(input.par_chunks(ctx.chunk_size(input.len())))
33
31
  .for_each(|(r, x)| {
34
32
  let start = ctx.start(r.len());
33
+ let end = ctx.end(r.len());
35
34
  r.fill(NumT::nan());
36
35
  if ctx.is_skip_nan() {
37
- let iter = SkipNanWindow::new(x, periods, start);
36
+ let iter = SkipNanWindow::new(&x[..end], periods, start);
38
37
  let mut sum = NumT::zero();
39
38
  for i in iter {
40
39
  let val = x[i.end];
@@ -78,7 +77,7 @@ pub fn ta_ma<NumT: Float + Send + Sync>(
78
77
  }
79
78
  }
80
79
 
81
- for i in start..x.len() {
80
+ for i in start..end {
82
81
  let val = x[i];
83
82
 
84
83
  // Add new value
@@ -142,13 +141,12 @@ pub fn ta_product<NumT: Float + Send + Sync>(
142
141
  return Err(Error::LengthMismatch(r.len(), input.len()));
143
142
  }
144
143
 
145
- let r = ctx.align_end_mut(r);
146
- let input = ctx.align_end(input);
147
144
 
148
145
  r.par_chunks_mut(ctx.chunk_size(r.len()))
149
146
  .zip(input.par_chunks(ctx.chunk_size(input.len())))
150
147
  .for_each(|(r, x)| {
151
148
  let start = ctx.start(r.len());
149
+ let end = ctx.end(r.len());
152
150
  r.fill(NumT::nan());
153
151
 
154
152
  if periods == 0 {
@@ -156,7 +154,7 @@ pub fn ta_product<NumT: Float + Send + Sync>(
156
154
  let mut prod = NumT::one();
157
155
  let mut found_valid = false;
158
156
 
159
- for i in start..x.len() {
157
+ for i in start..end {
160
158
  let val = x[i];
161
159
  if is_normal(&val) {
162
160
  prod = prod * val;
@@ -170,7 +168,7 @@ pub fn ta_product<NumT: Float + Send + Sync>(
170
168
  } else {
171
169
  // Sliding window
172
170
  if ctx.is_skip_nan() {
173
- let iter = SkipNanWindow::new(x, periods, start);
171
+ let iter = SkipNanWindow::new(&x[..end], periods, start);
174
172
  let mut prod_non_zero = NumT::one();
175
173
  let mut zero_count = 0;
176
174
 
@@ -235,7 +233,7 @@ pub fn ta_product<NumT: Float + Send + Sync>(
235
233
  }
236
234
  }
237
235
 
238
- for i in start..x.len() {
236
+ for i in start..end {
239
237
  let val = x[i];
240
238
 
241
239
  // Add new value