py-alpha-lib 0.1.0__cp311-abi3-macosx_11_0_arm64.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.
- alpha/__init__.py +8 -0
- alpha/algo/__init__.py +3 -0
- alpha/algo/_algo.abi3.so +0 -0
- alpha/algo/algo.md +98 -0
- alpha/algo/algo.py +29 -0
- alpha/algo/algo_gen.py +456 -0
- alpha/algo.md +30 -0
- alpha/lang/__init__.py +1 -0
- alpha/lang/__main__.py +17 -0
- alpha/lang/alpha.lark +49 -0
- alpha/lang/parser.py +3572 -0
- alpha/lang/to_python.py +239 -0
- py_alpha_lib-0.1.0.dist-info/METADATA +188 -0
- py_alpha_lib-0.1.0.dist-info/RECORD +16 -0
- py_alpha_lib-0.1.0.dist-info/WHEEL +4 -0
- py_alpha_lib-0.1.0.dist-info/licenses/LICENSE +22 -0
alpha/__init__.py
ADDED
alpha/algo/__init__.py
ADDED
alpha/algo/_algo.abi3.so
ADDED
|
Binary file
|
alpha/algo/algo.md
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# Algo
|
|
2
|
+
## barslast
|
|
3
|
+
Bars since last condition true
|
|
4
|
+
|
|
5
|
+
Ref: https://www.amibroker.com/guide/afl/barslast.html
|
|
6
|
+
|
|
7
|
+
## barssince
|
|
8
|
+
Bars since first condition true
|
|
9
|
+
|
|
10
|
+
Ref: https://www.amibroker.com/guide/afl/barssince.html
|
|
11
|
+
|
|
12
|
+
## count
|
|
13
|
+
Count periods where condition is true
|
|
14
|
+
|
|
15
|
+
Ref: https://www.amibroker.com/guide/afl/count.html
|
|
16
|
+
|
|
17
|
+
## cross
|
|
18
|
+
CROSS(A, B): Previous A < B, Current A >= B
|
|
19
|
+
|
|
20
|
+
Ref: https://www.amibroker.com/guide/afl/cross.html
|
|
21
|
+
|
|
22
|
+
## dma
|
|
23
|
+
Exponential Moving Average
|
|
24
|
+
|
|
25
|
+
https://en.wikipedia.org/wiki/Moving_average#Exponential_moving_average
|
|
26
|
+
|
|
27
|
+
current = alpha * current + (1 - alpha) * previous
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
## hhv
|
|
31
|
+
Highest High Value
|
|
32
|
+
|
|
33
|
+
Ref: https://www.amibroker.com/guide/afl/hhv.html
|
|
34
|
+
|
|
35
|
+
## hhvbars
|
|
36
|
+
Bars since Highest High Value
|
|
37
|
+
|
|
38
|
+
Ref: https://www.amibroker.com/guide/afl/hhvbars.html
|
|
39
|
+
|
|
40
|
+
## llv
|
|
41
|
+
Lowest Low Value
|
|
42
|
+
|
|
43
|
+
Ref: https://www.amibroker.com/guide/afl/llv.html
|
|
44
|
+
|
|
45
|
+
## llvbars
|
|
46
|
+
Bars since Lowest Low Value
|
|
47
|
+
|
|
48
|
+
Ref: https://www.amibroker.com/guide/afl/llvbars.html
|
|
49
|
+
|
|
50
|
+
## longcross
|
|
51
|
+
LONGCROSS(A,B,N): Previous N A < B, Current A >= B
|
|
52
|
+
|
|
53
|
+
## ma
|
|
54
|
+
Moving Average
|
|
55
|
+
|
|
56
|
+
https://en.wikipedia.org/wiki/Moving_average#Simple_moving_average
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
## rank
|
|
61
|
+
rank by group dim
|
|
62
|
+
|
|
63
|
+
## rcross
|
|
64
|
+
RCROSE(A, B): Previous A > B, Current A <= B
|
|
65
|
+
|
|
66
|
+
## ref
|
|
67
|
+
Reference to value N periods ago
|
|
68
|
+
|
|
69
|
+
Ref: https://www.amibroker.com/guide/afl/ref.html
|
|
70
|
+
|
|
71
|
+
## rlongcross
|
|
72
|
+
RLONGCROSS(A,B,N): Previous N A > B, Current A <= B
|
|
73
|
+
|
|
74
|
+
## sma
|
|
75
|
+
Exponential Moving Average (variant of EMA)
|
|
76
|
+
|
|
77
|
+
alpha = m / n
|
|
78
|
+
|
|
79
|
+
https://en.wikipedia.org/wiki/Moving_average#Exponential_moving_average
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
## sum
|
|
83
|
+
Sum of value N periods ago
|
|
84
|
+
|
|
85
|
+
If periods is 0, it calculates the cumulative sum from the first valid value.
|
|
86
|
+
|
|
87
|
+
Ref: https://www.amibroker.com/guide/afl/sum.html
|
|
88
|
+
|
|
89
|
+
## sumbars
|
|
90
|
+
Sums X backwards until the sum is greater than or equal to A
|
|
91
|
+
|
|
92
|
+
Returns the number of periods (bars) passed.
|
|
93
|
+
|
|
94
|
+
Ref: https://www.amibroker.com/guide/afl/sumbars.html
|
|
95
|
+
|
|
96
|
+
## ts_rank
|
|
97
|
+
rank by ts dim
|
|
98
|
+
|
alpha/algo/algo.py
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
from . import _algo
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def EMA(
|
|
6
|
+
input: np.ndarray | list[np.ndarray], period: int
|
|
7
|
+
) -> np.ndarray | list[np.ndarray]:
|
|
8
|
+
"""
|
|
9
|
+
Exponential Moving Average (variant of EMA)
|
|
10
|
+
|
|
11
|
+
alpha = 2 / (n + 1)
|
|
12
|
+
|
|
13
|
+
https://en.wikipedia.org/wiki/Moving_average#Exponential_moving_average
|
|
14
|
+
|
|
15
|
+
Args:
|
|
16
|
+
input: input array
|
|
17
|
+
period: period
|
|
18
|
+
|
|
19
|
+
Returns:
|
|
20
|
+
output array
|
|
21
|
+
"""
|
|
22
|
+
if isinstance(input, list):
|
|
23
|
+
r = [np.empty_like(x) for x in input]
|
|
24
|
+
_algo.ema(r, input, period)
|
|
25
|
+
return r
|
|
26
|
+
else:
|
|
27
|
+
r = np.empty_like(input)
|
|
28
|
+
_algo.ema(r, input, period)
|
|
29
|
+
return r
|
alpha/algo/algo_gen.py
ADDED
|
@@ -0,0 +1,456 @@
|
|
|
1
|
+
# THIS FILE IS AUTO-GENERATED, DO NOT EDIT
|
|
2
|
+
|
|
3
|
+
import numpy as np
|
|
4
|
+
from . import _algo
|
|
5
|
+
|
|
6
|
+
def BARSLAST(
|
|
7
|
+
input: np.ndarray | list[np.ndarray]
|
|
8
|
+
) -> np.ndarray | list[np.ndarray]:
|
|
9
|
+
"""
|
|
10
|
+
Calculate number of bars since last condition true
|
|
11
|
+
|
|
12
|
+
Ref: https://www.amibroker.com/guide/afl/barslast.html
|
|
13
|
+
"""
|
|
14
|
+
if isinstance(input, list):
|
|
15
|
+
r = [np.empty_like(x, dtype=float) for x in input]
|
|
16
|
+
input = [x.astype(bool) for x in input]
|
|
17
|
+
_algo.barslast(r, input)
|
|
18
|
+
return r
|
|
19
|
+
else:
|
|
20
|
+
r = np.empty_like(input, dtype=float)
|
|
21
|
+
input = input.astype(bool)
|
|
22
|
+
_algo.barslast(r, input)
|
|
23
|
+
return r
|
|
24
|
+
|
|
25
|
+
def BARSSINCE(
|
|
26
|
+
input: np.ndarray | list[np.ndarray]
|
|
27
|
+
) -> np.ndarray | list[np.ndarray]:
|
|
28
|
+
"""
|
|
29
|
+
Calculate number of bars since first condition true
|
|
30
|
+
|
|
31
|
+
Ref: https://www.amibroker.com/guide/afl/barssince.html
|
|
32
|
+
"""
|
|
33
|
+
if isinstance(input, list):
|
|
34
|
+
r = [np.empty_like(x, dtype=float) for x in input]
|
|
35
|
+
input = [x.astype(bool) for x in input]
|
|
36
|
+
_algo.barssince(r, input)
|
|
37
|
+
return r
|
|
38
|
+
else:
|
|
39
|
+
r = np.empty_like(input, dtype=float)
|
|
40
|
+
input = input.astype(bool)
|
|
41
|
+
_algo.barssince(r, input)
|
|
42
|
+
return r
|
|
43
|
+
|
|
44
|
+
def CORR(
|
|
45
|
+
x: np.ndarray | list[np.ndarray], y: np.ndarray | list[np.ndarray], periods: int
|
|
46
|
+
) -> np.ndarray | list[np.ndarray]:
|
|
47
|
+
"""
|
|
48
|
+
Calculate Correlation over a moving window
|
|
49
|
+
|
|
50
|
+
Correlation = Cov(X, Y) / (StdDev(X) * StdDev(Y))
|
|
51
|
+
"""
|
|
52
|
+
if isinstance(x, list) and isinstance(y, list):
|
|
53
|
+
r = [np.empty_like(x) for x in x]
|
|
54
|
+
x = [x.astype(float) for x in x]
|
|
55
|
+
y = [x.astype(float) for x in y]
|
|
56
|
+
_algo.corr(r, x, y, periods)
|
|
57
|
+
return r
|
|
58
|
+
else:
|
|
59
|
+
r = np.empty_like(x)
|
|
60
|
+
x = x.astype(float)
|
|
61
|
+
y = y.astype(float)
|
|
62
|
+
_algo.corr(r, x, y, periods)
|
|
63
|
+
return r
|
|
64
|
+
|
|
65
|
+
def COUNT(
|
|
66
|
+
input: np.ndarray | list[np.ndarray], periods: int
|
|
67
|
+
) -> np.ndarray | list[np.ndarray]:
|
|
68
|
+
"""
|
|
69
|
+
Calculate number of periods where condition is true in passed `periods` window
|
|
70
|
+
|
|
71
|
+
Ref: https://www.amibroker.com/guide/afl/count.html
|
|
72
|
+
"""
|
|
73
|
+
if isinstance(input, list):
|
|
74
|
+
r = [np.empty_like(x, dtype=float) for x in input]
|
|
75
|
+
input = [x.astype(bool) for x in input]
|
|
76
|
+
_algo.count(r, input, periods)
|
|
77
|
+
return r
|
|
78
|
+
else:
|
|
79
|
+
r = np.empty_like(input, dtype=float)
|
|
80
|
+
input = input.astype(bool)
|
|
81
|
+
_algo.count(r, input, periods)
|
|
82
|
+
return r
|
|
83
|
+
|
|
84
|
+
def COV(
|
|
85
|
+
x: np.ndarray | list[np.ndarray], y: np.ndarray | list[np.ndarray], periods: int
|
|
86
|
+
) -> np.ndarray | list[np.ndarray]:
|
|
87
|
+
"""
|
|
88
|
+
Calculate Covariance over a moving window
|
|
89
|
+
|
|
90
|
+
Covariance = (SumXY - (SumX * SumY) / N) / (N - 1)
|
|
91
|
+
"""
|
|
92
|
+
if isinstance(x, list) and isinstance(y, list):
|
|
93
|
+
r = [np.empty_like(x) for x in x]
|
|
94
|
+
x = [x.astype(float) for x in x]
|
|
95
|
+
y = [x.astype(float) for x in y]
|
|
96
|
+
_algo.cov(r, x, y, periods)
|
|
97
|
+
return r
|
|
98
|
+
else:
|
|
99
|
+
r = np.empty_like(x)
|
|
100
|
+
x = x.astype(float)
|
|
101
|
+
y = y.astype(float)
|
|
102
|
+
_algo.cov(r, x, y, periods)
|
|
103
|
+
return r
|
|
104
|
+
|
|
105
|
+
def CROSS(
|
|
106
|
+
a: np.ndarray | list[np.ndarray], b: np.ndarray | list[np.ndarray]
|
|
107
|
+
) -> np.ndarray | list[np.ndarray]:
|
|
108
|
+
"""
|
|
109
|
+
For 2 arrays A and B, return true if A[i-1] < B[i-1] and A[i] >= B[i]
|
|
110
|
+
alias: golden_cross, cross_ge
|
|
111
|
+
"""
|
|
112
|
+
if isinstance(a, list) and isinstance(b, list):
|
|
113
|
+
r = [np.empty_like(x, dtype=bool) for x in a]
|
|
114
|
+
a = [x.astype(float) for x in a]
|
|
115
|
+
b = [x.astype(float) for x in b]
|
|
116
|
+
_algo.cross(r, a, b)
|
|
117
|
+
return r
|
|
118
|
+
else:
|
|
119
|
+
r = np.empty_like(a, dtype=bool)
|
|
120
|
+
a = a.astype(float)
|
|
121
|
+
b = b.astype(float)
|
|
122
|
+
_algo.cross(r, a, b)
|
|
123
|
+
return r
|
|
124
|
+
|
|
125
|
+
def DMA(
|
|
126
|
+
input: np.ndarray | list[np.ndarray], weight: float
|
|
127
|
+
) -> np.ndarray | list[np.ndarray]:
|
|
128
|
+
"""
|
|
129
|
+
Exponential Moving Average
|
|
130
|
+
current = weight * current + (1 - weight) * previous
|
|
131
|
+
|
|
132
|
+
Ref: https://en.wikipedia.org/wiki/Moving_average#Exponential_moving_average
|
|
133
|
+
"""
|
|
134
|
+
if isinstance(input, list):
|
|
135
|
+
r = [np.empty_like(x) for x in input]
|
|
136
|
+
_algo.dma(r, input, weight)
|
|
137
|
+
return r
|
|
138
|
+
else:
|
|
139
|
+
r = np.empty_like(input)
|
|
140
|
+
_algo.dma(r, input, weight)
|
|
141
|
+
return r
|
|
142
|
+
|
|
143
|
+
def HHV(
|
|
144
|
+
input: np.ndarray | list[np.ndarray], periods: int
|
|
145
|
+
) -> np.ndarray | list[np.ndarray]:
|
|
146
|
+
"""
|
|
147
|
+
Find highest value in a preceding `periods` window
|
|
148
|
+
|
|
149
|
+
Ref: https://www.amibroker.com/guide/afl/hhv.html
|
|
150
|
+
"""
|
|
151
|
+
if isinstance(input, list):
|
|
152
|
+
r = [np.empty_like(x) for x in input]
|
|
153
|
+
_algo.hhv(r, input, periods)
|
|
154
|
+
return r
|
|
155
|
+
else:
|
|
156
|
+
r = np.empty_like(input)
|
|
157
|
+
_algo.hhv(r, input, periods)
|
|
158
|
+
return r
|
|
159
|
+
|
|
160
|
+
def HHVBARS(
|
|
161
|
+
input: np.ndarray | list[np.ndarray], periods: int
|
|
162
|
+
) -> np.ndarray | list[np.ndarray]:
|
|
163
|
+
"""
|
|
164
|
+
The number of periods that have passed since the array reached its `periods` period high
|
|
165
|
+
|
|
166
|
+
Ref: https://www.amibroker.com/guide/afl/hhvbars.html
|
|
167
|
+
"""
|
|
168
|
+
if isinstance(input, list):
|
|
169
|
+
r = [np.empty_like(x) for x in input]
|
|
170
|
+
_algo.hhvbars(r, input, periods)
|
|
171
|
+
return r
|
|
172
|
+
else:
|
|
173
|
+
r = np.empty_like(input)
|
|
174
|
+
_algo.hhvbars(r, input, periods)
|
|
175
|
+
return r
|
|
176
|
+
|
|
177
|
+
def LLV(
|
|
178
|
+
input: np.ndarray | list[np.ndarray], periods: int
|
|
179
|
+
) -> np.ndarray | list[np.ndarray]:
|
|
180
|
+
"""
|
|
181
|
+
Find lowest value in a preceding `periods` window
|
|
182
|
+
|
|
183
|
+
Ref: https://www.amibroker.com/guide/afl/llv.html
|
|
184
|
+
"""
|
|
185
|
+
if isinstance(input, list):
|
|
186
|
+
r = [np.empty_like(x) for x in input]
|
|
187
|
+
_algo.llv(r, input, periods)
|
|
188
|
+
return r
|
|
189
|
+
else:
|
|
190
|
+
r = np.empty_like(input)
|
|
191
|
+
_algo.llv(r, input, periods)
|
|
192
|
+
return r
|
|
193
|
+
|
|
194
|
+
def LLVBARS(
|
|
195
|
+
input: np.ndarray | list[np.ndarray], periods: int
|
|
196
|
+
) -> np.ndarray | list[np.ndarray]:
|
|
197
|
+
"""
|
|
198
|
+
The number of periods that have passed since the array reached its periods period low
|
|
199
|
+
|
|
200
|
+
Ref: https://www.amibroker.com/guide/afl/llvbars.html
|
|
201
|
+
"""
|
|
202
|
+
if isinstance(input, list):
|
|
203
|
+
r = [np.empty_like(x) for x in input]
|
|
204
|
+
_algo.llvbars(r, input, periods)
|
|
205
|
+
return r
|
|
206
|
+
else:
|
|
207
|
+
r = np.empty_like(input)
|
|
208
|
+
_algo.llvbars(r, input, periods)
|
|
209
|
+
return r
|
|
210
|
+
|
|
211
|
+
def LONGCROSS(
|
|
212
|
+
a: np.ndarray | list[np.ndarray], b: np.ndarray | list[np.ndarray], n: int
|
|
213
|
+
) -> np.ndarray | list[np.ndarray]:
|
|
214
|
+
"""
|
|
215
|
+
For 2 arrays A and B, return true if previous N periods A < B, Current A >= B
|
|
216
|
+
"""
|
|
217
|
+
if isinstance(a, list) and isinstance(b, list):
|
|
218
|
+
r = [np.empty_like(x, dtype=bool) for x in a]
|
|
219
|
+
a = [x.astype(float) for x in a]
|
|
220
|
+
b = [x.astype(float) for x in b]
|
|
221
|
+
_algo.longcross(r, a, b, n)
|
|
222
|
+
return r
|
|
223
|
+
else:
|
|
224
|
+
r = np.empty_like(a, dtype=bool)
|
|
225
|
+
a = a.astype(float)
|
|
226
|
+
b = b.astype(float)
|
|
227
|
+
_algo.longcross(r, a, b, n)
|
|
228
|
+
return r
|
|
229
|
+
|
|
230
|
+
def LWMA(
|
|
231
|
+
input: np.ndarray | list[np.ndarray], periods: int
|
|
232
|
+
) -> np.ndarray | list[np.ndarray]:
|
|
233
|
+
"""
|
|
234
|
+
Linear Weighted Moving Average
|
|
235
|
+
|
|
236
|
+
LWMA = SUM(Price * Weight) / SUM(Weight)
|
|
237
|
+
"""
|
|
238
|
+
if isinstance(input, list):
|
|
239
|
+
r = [np.empty_like(x) for x in input]
|
|
240
|
+
_algo.lwma(r, input, periods)
|
|
241
|
+
return r
|
|
242
|
+
else:
|
|
243
|
+
r = np.empty_like(input)
|
|
244
|
+
_algo.lwma(r, input, periods)
|
|
245
|
+
return r
|
|
246
|
+
|
|
247
|
+
def MA(
|
|
248
|
+
input: np.ndarray | list[np.ndarray], periods: int
|
|
249
|
+
) -> np.ndarray | list[np.ndarray]:
|
|
250
|
+
"""
|
|
251
|
+
Simple Moving Average, also known as arithmetic moving average
|
|
252
|
+
|
|
253
|
+
Ref: https://en.wikipedia.org/wiki/Moving_average#Simple_moving_average
|
|
254
|
+
"""
|
|
255
|
+
if isinstance(input, list):
|
|
256
|
+
r = [np.empty_like(x) for x in input]
|
|
257
|
+
_algo.ma(r, input, periods)
|
|
258
|
+
return r
|
|
259
|
+
else:
|
|
260
|
+
r = np.empty_like(input)
|
|
261
|
+
_algo.ma(r, input, periods)
|
|
262
|
+
return r
|
|
263
|
+
|
|
264
|
+
def PRODUCT(
|
|
265
|
+
input: np.ndarray | list[np.ndarray], periods: int
|
|
266
|
+
) -> np.ndarray | list[np.ndarray]:
|
|
267
|
+
"""
|
|
268
|
+
Calculate product of values in preceding `periods` window
|
|
269
|
+
|
|
270
|
+
If periods is 0, it calculates the cumulative product from the first valid value.
|
|
271
|
+
|
|
272
|
+
Ref: https://www.amibroker.com/guide/afl/product.html
|
|
273
|
+
"""
|
|
274
|
+
if isinstance(input, list):
|
|
275
|
+
r = [np.empty_like(x) for x in input]
|
|
276
|
+
_algo.product(r, input, periods)
|
|
277
|
+
return r
|
|
278
|
+
else:
|
|
279
|
+
r = np.empty_like(input)
|
|
280
|
+
_algo.product(r, input, periods)
|
|
281
|
+
return r
|
|
282
|
+
|
|
283
|
+
def RANK(
|
|
284
|
+
input: np.ndarray | list[np.ndarray]
|
|
285
|
+
) -> np.ndarray | list[np.ndarray]:
|
|
286
|
+
"""
|
|
287
|
+
Calculate rank percentage cross group dimension, the ctx.groups() is the number of groups
|
|
288
|
+
Same value are averaged
|
|
289
|
+
"""
|
|
290
|
+
if isinstance(input, list):
|
|
291
|
+
r = [np.empty_like(x) for x in input]
|
|
292
|
+
_algo.rank(r, input)
|
|
293
|
+
return r
|
|
294
|
+
else:
|
|
295
|
+
r = np.empty_like(input)
|
|
296
|
+
_algo.rank(r, input)
|
|
297
|
+
return r
|
|
298
|
+
|
|
299
|
+
def RCROSS(
|
|
300
|
+
a: np.ndarray | list[np.ndarray], b: np.ndarray | list[np.ndarray]
|
|
301
|
+
) -> np.ndarray | list[np.ndarray]:
|
|
302
|
+
"""
|
|
303
|
+
For 2 arrays A and B, return true if A[i-1] > B[i-1] and A[i] <= B[i]
|
|
304
|
+
alias: death_cross, cross_le
|
|
305
|
+
"""
|
|
306
|
+
if isinstance(a, list) and isinstance(b, list):
|
|
307
|
+
r = [np.empty_like(x, dtype=bool) for x in a]
|
|
308
|
+
a = [x.astype(float) for x in a]
|
|
309
|
+
b = [x.astype(float) for x in b]
|
|
310
|
+
_algo.rcross(r, a, b)
|
|
311
|
+
return r
|
|
312
|
+
else:
|
|
313
|
+
r = np.empty_like(a, dtype=bool)
|
|
314
|
+
a = a.astype(float)
|
|
315
|
+
b = b.astype(float)
|
|
316
|
+
_algo.rcross(r, a, b)
|
|
317
|
+
return r
|
|
318
|
+
|
|
319
|
+
def REF(
|
|
320
|
+
input: np.ndarray | list[np.ndarray], periods: int
|
|
321
|
+
) -> np.ndarray | list[np.ndarray]:
|
|
322
|
+
"""
|
|
323
|
+
Right shift input array by `periods`, r[i] = input[i - periods]
|
|
324
|
+
|
|
325
|
+
Ref: https://www.amibroker.com/guide/afl/ref.html
|
|
326
|
+
"""
|
|
327
|
+
if isinstance(input, list):
|
|
328
|
+
r = [np.empty_like(x) for x in input]
|
|
329
|
+
_algo.ref(r, input, periods)
|
|
330
|
+
return r
|
|
331
|
+
else:
|
|
332
|
+
r = np.empty_like(input)
|
|
333
|
+
_algo.ref(r, input, periods)
|
|
334
|
+
return r
|
|
335
|
+
|
|
336
|
+
def RLONGCROSS(
|
|
337
|
+
a: np.ndarray | list[np.ndarray], b: np.ndarray | list[np.ndarray], n: int
|
|
338
|
+
) -> np.ndarray | list[np.ndarray]:
|
|
339
|
+
"""
|
|
340
|
+
For 2 arrays A and B, return true if previous N periods A > B, Current A <= B
|
|
341
|
+
"""
|
|
342
|
+
if isinstance(a, list) and isinstance(b, list):
|
|
343
|
+
r = [np.empty_like(x, dtype=bool) for x in a]
|
|
344
|
+
a = [x.astype(float) for x in a]
|
|
345
|
+
b = [x.astype(float) for x in b]
|
|
346
|
+
_algo.rlongcross(r, a, b, n)
|
|
347
|
+
return r
|
|
348
|
+
else:
|
|
349
|
+
r = np.empty_like(a, dtype=bool)
|
|
350
|
+
a = a.astype(float)
|
|
351
|
+
b = b.astype(float)
|
|
352
|
+
_algo.rlongcross(r, a, b, n)
|
|
353
|
+
return r
|
|
354
|
+
|
|
355
|
+
def SMA(
|
|
356
|
+
input: np.ndarray | list[np.ndarray], n: int, m: int
|
|
357
|
+
) -> np.ndarray | list[np.ndarray]:
|
|
358
|
+
"""
|
|
359
|
+
Exponential Moving Average (variant of well-known EMA) weight = m / n
|
|
360
|
+
|
|
361
|
+
Ref: https://en.wikipedia.org/wiki/Moving_average#Exponential_moving_average
|
|
362
|
+
"""
|
|
363
|
+
if isinstance(input, list):
|
|
364
|
+
r = [np.empty_like(x) for x in input]
|
|
365
|
+
_algo.sma(r, input, n, m)
|
|
366
|
+
return r
|
|
367
|
+
else:
|
|
368
|
+
r = np.empty_like(input)
|
|
369
|
+
_algo.sma(r, input, n, m)
|
|
370
|
+
return r
|
|
371
|
+
|
|
372
|
+
def STDDEV(
|
|
373
|
+
input: np.ndarray | list[np.ndarray], periods: int
|
|
374
|
+
) -> np.ndarray | list[np.ndarray]:
|
|
375
|
+
"""
|
|
376
|
+
Calculate Standard Deviation over a moving window
|
|
377
|
+
|
|
378
|
+
Ref: https://en.wikipedia.org/wiki/Standard_deviation
|
|
379
|
+
"""
|
|
380
|
+
if isinstance(input, list):
|
|
381
|
+
r = [np.empty_like(x) for x in input]
|
|
382
|
+
_algo.stddev(r, input, periods)
|
|
383
|
+
return r
|
|
384
|
+
else:
|
|
385
|
+
r = np.empty_like(input)
|
|
386
|
+
_algo.stddev(r, input, periods)
|
|
387
|
+
return r
|
|
388
|
+
|
|
389
|
+
def SUM(
|
|
390
|
+
input: np.ndarray | list[np.ndarray], periods: int
|
|
391
|
+
) -> np.ndarray | list[np.ndarray]:
|
|
392
|
+
"""
|
|
393
|
+
Calculate sum of values in preceding `periods` window
|
|
394
|
+
|
|
395
|
+
If periods is 0, it calculates the cumulative sum from the first valid value.
|
|
396
|
+
|
|
397
|
+
Ref: https://www.amibroker.com/guide/afl/sum.html
|
|
398
|
+
"""
|
|
399
|
+
if isinstance(input, list):
|
|
400
|
+
r = [np.empty_like(x) for x in input]
|
|
401
|
+
_algo.sum(r, input, periods)
|
|
402
|
+
return r
|
|
403
|
+
else:
|
|
404
|
+
r = np.empty_like(input)
|
|
405
|
+
_algo.sum(r, input, periods)
|
|
406
|
+
return r
|
|
407
|
+
|
|
408
|
+
def SUMBARS(
|
|
409
|
+
input: np.ndarray | list[np.ndarray], amount: float
|
|
410
|
+
) -> np.ndarray | list[np.ndarray]:
|
|
411
|
+
"""
|
|
412
|
+
Calculate number of periods (bars) backwards until the sum of values is greater than or equal to `amount`
|
|
413
|
+
|
|
414
|
+
Ref: https://www.amibroker.com/guide/afl/sumbars.html
|
|
415
|
+
"""
|
|
416
|
+
if isinstance(input, list):
|
|
417
|
+
r = [np.empty_like(x) for x in input]
|
|
418
|
+
_algo.sumbars(r, input, amount)
|
|
419
|
+
return r
|
|
420
|
+
else:
|
|
421
|
+
r = np.empty_like(input)
|
|
422
|
+
_algo.sumbars(r, input, amount)
|
|
423
|
+
return r
|
|
424
|
+
|
|
425
|
+
def TS_RANK(
|
|
426
|
+
input: np.ndarray | list[np.ndarray], periods: int
|
|
427
|
+
) -> np.ndarray | list[np.ndarray]:
|
|
428
|
+
"""
|
|
429
|
+
Calculate rank in a sliding window with size `periods`
|
|
430
|
+
"""
|
|
431
|
+
if isinstance(input, list):
|
|
432
|
+
r = [np.empty_like(x) for x in input]
|
|
433
|
+
_algo.ts_rank(r, input, periods)
|
|
434
|
+
return r
|
|
435
|
+
else:
|
|
436
|
+
r = np.empty_like(input)
|
|
437
|
+
_algo.ts_rank(r, input, periods)
|
|
438
|
+
return r
|
|
439
|
+
|
|
440
|
+
def VAR(
|
|
441
|
+
input: np.ndarray | list[np.ndarray], periods: int
|
|
442
|
+
) -> np.ndarray | list[np.ndarray]:
|
|
443
|
+
"""
|
|
444
|
+
Calculate Variance over a moving window
|
|
445
|
+
|
|
446
|
+
Variance = (SumSq - (Sum^2)/N) / (N - 1)
|
|
447
|
+
"""
|
|
448
|
+
if isinstance(input, list):
|
|
449
|
+
r = [np.empty_like(x) for x in input]
|
|
450
|
+
_algo.var(r, input, periods)
|
|
451
|
+
return r
|
|
452
|
+
else:
|
|
453
|
+
r = np.empty_like(input)
|
|
454
|
+
_algo.var(r, input, periods)
|
|
455
|
+
return r
|
|
456
|
+
|
alpha/algo.md
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
List of available functions with python type hints:
|
|
2
|
+
|
|
3
|
+
the `np.ndarray` is `ndarray` type in `numpy` package
|
|
4
|
+
|
|
5
|
+
- BARSLAST(input: np.ndarray[bool]): Calculate number of bars since last condition true
|
|
6
|
+
- BARSSINCE(input: np.ndarray[bool]): Calculate number of bars since first condition true
|
|
7
|
+
- CORR(x: np.ndarray[float], y: np.ndarray[float], periods: int): Calculate Correlation over a moving window Correlation = Cov(X, Y) / (StdDev(X) * StdDev(Y))
|
|
8
|
+
- COUNT(input: np.ndarray[bool], periods: int): Calculate number of periods where condition is true in passed `periods` window
|
|
9
|
+
- COV(x: np.ndarray[float], y: np.ndarray[float], periods: int): Calculate Covariance over a moving window Covariance = (SumXY - (SumX * SumY) / N) / (N - 1)
|
|
10
|
+
- CROSS(a: np.ndarray[float], b: np.ndarray[float]): For 2 arrays A and B, return true if A[i-1] < B[i-1] and A[i] >= B[i] alias: golden_cross, cross_ge
|
|
11
|
+
- DMA(input: np.ndarray[float], weight: float): Exponential Moving Average current = weight * current + (1 - weight) * previous
|
|
12
|
+
- EMA(input: np.ndarray[float], periods: int): Exponential Moving Average (variant of well-known EMA) weight = 2 / (n + 1)
|
|
13
|
+
- HHV(input: np.ndarray[float], periods: int): Find highest value in a preceding `periods` window
|
|
14
|
+
- HHVBARS(input: np.ndarray[float], periods: int): The number of periods that have passed since the array reached its `periods` period high
|
|
15
|
+
- LLV(input: np.ndarray[float], periods: int): Find lowest value in a preceding `periods` window
|
|
16
|
+
- LLVBARS(input: np.ndarray[float], periods: int): The number of periods that have passed since the array reached its periods period low
|
|
17
|
+
- LONGCROSS(a: np.ndarray[float], b: np.ndarray[float], n: int): For 2 arrays A and B, return true if previous N periods A < B, Current A >= B
|
|
18
|
+
- LWMA(input: np.ndarray[float], periods: int): Linear Weighted Moving Average LWMA = SUM(Price * Weight) / SUM(Weight)
|
|
19
|
+
- MA(input: np.ndarray[float], periods: int): Simple Moving Average, also known as arithmetic moving average
|
|
20
|
+
- PRODUCT(input: np.ndarray[float], periods: int): Calculate product of values in preceding `periods` window If periods is 0, it calculates the cumulative product from the first valid value.
|
|
21
|
+
- RANK(input: np.ndarray[float]): Calculate rank percentage cross group dimension, the ctx.groups() is the number of groups Same value are averaged
|
|
22
|
+
- RCROSS(a: np.ndarray[float], b: np.ndarray[float]): For 2 arrays A and B, return true if A[i-1] > B[i-1] and A[i] <= B[i] alias: death_cross, cross_le
|
|
23
|
+
- REF(input: np.ndarray[float], periods: int): Right shift input array by `periods`, r[i] = input[i - periods]
|
|
24
|
+
- RLONGCROSS(a: np.ndarray[float], b: np.ndarray[float], n: int): For 2 arrays A and B, return true if previous N periods A > B, Current A <= B
|
|
25
|
+
- SMA(input: np.ndarray[float], n: int, m: int): Exponential Moving Average (variant of well-known EMA) weight = m / n
|
|
26
|
+
- STDDEV(input: np.ndarray[float], periods: int): Calculate Standard Deviation over a moving window
|
|
27
|
+
- SUM(input: np.ndarray[float], periods: int): Calculate sum of values in preceding `periods` window If periods is 0, it calculates the cumulative sum from the first valid value.
|
|
28
|
+
- SUMBARS(input: np.ndarray[float], amount: float): Calculate number of periods (bars) backwards until the sum of values is greater than or equal to `amount`
|
|
29
|
+
- TS_RANK(input: np.ndarray[float], periods: int): Calculate rank in a sliding window with size `periods`
|
|
30
|
+
- VAR(input: np.ndarray[float], periods: int): Calculate Variance over a moving window Variance = (SumSq - (Sum^2)/N) / (N - 1)
|
alpha/lang/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from .to_python import to_python, to_python_file
|
alpha/lang/__main__.py
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
from .to_python import to_python_file
|
|
2
|
+
|
|
3
|
+
if __name__ == "__main__":
|
|
4
|
+
import sys
|
|
5
|
+
import pathlib
|
|
6
|
+
|
|
7
|
+
if len(sys.argv) < 2:
|
|
8
|
+
print("Usage: python -m alpha.lang <file | code>")
|
|
9
|
+
sys.exit(1)
|
|
10
|
+
|
|
11
|
+
file_path = pathlib.Path(sys.argv[1])
|
|
12
|
+
if file_path.exists():
|
|
13
|
+
codes = file_path.read_text().splitlines()
|
|
14
|
+
to_python_file(codes, name_convertor=lambda x: x.upper())
|
|
15
|
+
else:
|
|
16
|
+
code = "\n".join(sys.argv[1:])
|
|
17
|
+
to_python_file([code], name_convertor=lambda x: x.upper())
|
alpha/lang/alpha.lark
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
start: expression
|
|
2
|
+
|
|
3
|
+
?expression: ternary_expr
|
|
4
|
+
|
|
5
|
+
?ternary_expr: logical_or_expr "?" ternary_expr ":" ternary_expr
|
|
6
|
+
| logical_or_expr
|
|
7
|
+
|
|
8
|
+
?logical_or_expr: logical_and_expr ("||" logical_and_expr)*
|
|
9
|
+
|
|
10
|
+
?logical_and_expr: comparison_expr ("&&" comparison_expr)*
|
|
11
|
+
|
|
12
|
+
?comparison_expr: sum "==" sum -> eq
|
|
13
|
+
| sum "!=" sum -> ne
|
|
14
|
+
| sum "<" sum -> lt
|
|
15
|
+
| sum ">" sum -> gt
|
|
16
|
+
| sum "<=" sum -> le
|
|
17
|
+
| sum ">=" sum -> ge
|
|
18
|
+
| sum
|
|
19
|
+
|
|
20
|
+
?sum: product (add_op product)*
|
|
21
|
+
|
|
22
|
+
?product: power (mul_op power)*
|
|
23
|
+
|
|
24
|
+
?power: atom (POWER atom)*
|
|
25
|
+
|
|
26
|
+
?atom: MINUS atom -> neg
|
|
27
|
+
| func_call
|
|
28
|
+
| NUMBER
|
|
29
|
+
| NAME
|
|
30
|
+
| NAME "." NAME -> dotted_name
|
|
31
|
+
| "(" expression ")"
|
|
32
|
+
|
|
33
|
+
!add_op: PLUS | MINUS
|
|
34
|
+
!mul_op: STAR | DIV
|
|
35
|
+
|
|
36
|
+
func_call: NAME "(" [arguments] ")"
|
|
37
|
+
arguments: expression ("," expression)*
|
|
38
|
+
|
|
39
|
+
// Terminals
|
|
40
|
+
PLUS: "+"
|
|
41
|
+
MINUS: "-"
|
|
42
|
+
STAR: "*"
|
|
43
|
+
DIV: "/"
|
|
44
|
+
POWER: "^"
|
|
45
|
+
NUMBER: /\d+(\.\d*)?|\.\d+/
|
|
46
|
+
NAME: /[a-zA-Z_]\w*/
|
|
47
|
+
|
|
48
|
+
%import common.WS
|
|
49
|
+
%ignore WS
|