Qubx 0.0.1__cp311-cp311-manylinux_2_35_x86_64.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.

Potentially problematic release.


This version of Qubx might be problematic. Click here for more details.

qubx/ta/indicators.pyx ADDED
@@ -0,0 +1,258 @@
1
+ import numpy as np
2
+ cimport numpy as np
3
+ from collections import deque
4
+
5
+ from qubx.core.series cimport TimeSeries, Indicator, RollingSum, nans
6
+
7
+
8
+ cdef extern from "math.h":
9
+ float INFINITY
10
+
11
+
12
+ cdef class Sma(Indicator):
13
+ """
14
+ Simple Moving Average indicator
15
+ """
16
+ cdef unsigned int period
17
+ cdef RollingSum summator
18
+
19
+ """
20
+ Simple moving average
21
+ """
22
+ def __init__(self, str name, TimeSeries series, int period):
23
+ self.period = period
24
+ self.summator = RollingSum(period)
25
+ super().__init__(name, series)
26
+
27
+ cpdef double calculate(self, long long time, double value, short new_item_started):
28
+ cdef double r = self.summator.update(value, new_item_started)
29
+ return np.nan if self.summator.is_init_stage else r / self.period
30
+
31
+
32
+ def sma(series:TimeSeries, period: int):
33
+ return Sma.wrap(series, period)
34
+
35
+
36
+ cdef class Ema(Indicator):
37
+ """
38
+ Exponential moving average
39
+ """
40
+ cdef int period
41
+ cdef np.ndarray __s
42
+ cdef int __i
43
+ cdef double alpha
44
+ cdef double alpha_1
45
+ cdef unsigned short init_mean
46
+ cdef unsigned short _init_stage
47
+
48
+ def __init__(self, str name, TimeSeries series, int period, init_mean=True):
49
+ self.period = period
50
+
51
+ # when it's required to initialize this ema by mean on first period
52
+ self.init_mean = init_mean
53
+ if init_mean:
54
+ self.__s = nans(period)
55
+ self.__i = 0
56
+
57
+ self._init_stage = 1
58
+ self.alpha = 2.0 / (1.0 + period)
59
+ self.alpha_1 = (1 - self.alpha)
60
+ super().__init__(name, series)
61
+
62
+ cpdef double calculate(self, long long time, double value, short new_item_started):
63
+ cdef int prev_bar_idx = 0 if new_item_started else 1
64
+
65
+ if self._init_stage:
66
+ if np.isnan(value): return np.nan
67
+
68
+ if new_item_started:
69
+ self.__i += 1
70
+ if self.__i > self.period - 1:
71
+ self._init_stage = False
72
+ return self.alpha * value + self.alpha_1 * self[prev_bar_idx]
73
+
74
+ if self.__i == self.period - 1:
75
+ self.__s[self.__i] = value
76
+ return np.nansum(self.__s) / self.period
77
+
78
+ self.__s[self.__i] = value
79
+ return np.nan
80
+
81
+ if len(self) == 0:
82
+ return value
83
+
84
+ return self.alpha * value + self.alpha_1 * self[prev_bar_idx]
85
+
86
+
87
+ def ema(series:TimeSeries, period: int, init_mean: bool = True):
88
+ return Ema.wrap(series, period, init_mean=init_mean)
89
+
90
+
91
+ cdef class Tema(Indicator):
92
+ cdef int period
93
+ cdef unsigned short init_mean
94
+ cdef TimeSeries ser0
95
+ cdef Ema ema1
96
+ cdef Ema ema2
97
+ cdef Ema ema3
98
+
99
+ def __init__(self, str name, TimeSeries series, int period, init_mean=True):
100
+ self.period = period
101
+ self.init_mean = init_mean
102
+ self.ser0 = TimeSeries('ser0', series.timeframe, series.max_series_length)
103
+ self.ema1 = ema(self.ser0, period, init_mean)
104
+ self.ema2 = ema(self.ema1, period, init_mean)
105
+ self.ema3 = ema(self.ema2, period, init_mean)
106
+ super().__init__(name, series)
107
+
108
+ cpdef double calculate(self, long long time, double value, short new_item_started):
109
+ self.ser0.update(time, value)
110
+ return 3 * self.ema1[0] - 3 * self.ema2[0] + self.ema3[0]
111
+
112
+
113
+ def tema(series:TimeSeries, period: int, init_mean: bool = True):
114
+ return Tema.wrap(series, period, init_mean=init_mean)
115
+
116
+
117
+ cdef class Dema(Indicator):
118
+ cdef int period
119
+ cdef unsigned short init_mean
120
+ cdef TimeSeries ser0
121
+ cdef Ema ema1
122
+ cdef Ema ema2
123
+
124
+ def __init__(self, str name, TimeSeries series, int period, init_mean=True):
125
+ self.period = period
126
+ self.init_mean = init_mean
127
+ self.ser0 = TimeSeries('ser0', series.timeframe, series.max_series_length)
128
+ self.ema1 = ema(self.ser0, period, init_mean)
129
+ self.ema2 = ema(self.ema1, period, init_mean)
130
+ super().__init__(name, series)
131
+
132
+ cpdef double calculate(self, long long time, double value, short new_item_started):
133
+ self.ser0.update(time, value)
134
+ return 2 * self.ema1[0] - self.ema2[0]
135
+
136
+
137
+ def dema(series:TimeSeries, period: int, init_mean: bool = True):
138
+ return Dema.wrap(series, period, init_mean=init_mean)
139
+
140
+
141
+ cdef class Kama(Indicator):
142
+ cdef int period
143
+ cdef int fast_span
144
+ cdef int slow_span
145
+ cdef double _S1
146
+ cdef double _K1
147
+ cdef _x_past
148
+ cdef RollingSum summator
149
+
150
+ def __init__(self, str name, TimeSeries series, int period, int fast_span=2, int slow_span=30):
151
+ self.period = period
152
+ self.fast_span = fast_span
153
+ self.slow_span = slow_span
154
+ self._S1 = 2.0 / (slow_span + 1)
155
+ self._K1 = 2.0 / (fast_span + 1) - self._S1
156
+ self._x_past = deque(nans(period+1), period+1)
157
+ self.summator = RollingSum(period)
158
+ super().__init__(name, series)
159
+
160
+ cpdef double calculate(self, long long time, double value, short new_item_started):
161
+ if new_item_started:
162
+ self._x_past.append(value)
163
+ else:
164
+ self._x_past[-1] = value
165
+
166
+ cdef double rs = self.summator.update(abs(value - self._x_past[-2]), new_item_started)
167
+ cdef double er = abs(value - self._x_past[0]) / rs
168
+ cdef double sc = (er * self._K1 + self._S1) ** 2
169
+
170
+ if self.summator.is_init_stage:
171
+ if not np.isnan(self._x_past[1]):
172
+ return value
173
+ return np.nan
174
+
175
+ return sc * value + (1 - sc) * self[0 if new_item_started else 1]
176
+
177
+
178
+ def kama(series:TimeSeries, period: int, fast_span:int=2, slow_span:int=30):
179
+ return Kama.wrap(series, period, fast_span, slow_span)
180
+
181
+
182
+ cdef class Highest(Indicator):
183
+ cdef int period
184
+ cdef queue
185
+
186
+ def __init__(self, str name, TimeSeries series, int period):
187
+ self.period = period
188
+ self.queue = deque([np.nan] * period, maxlen=period)
189
+ super().__init__(name, series)
190
+
191
+ cpdef double calculate(self, long long time, double value, short new_item_started):
192
+ """
193
+ Not a most effictive algo but simplest and can handle updated last value
194
+ """
195
+ cdef float r = np.nan
196
+
197
+ if not np.isnan(value):
198
+ if new_item_started:
199
+ self.queue.append(value)
200
+ else:
201
+ self.queue[-1] = value
202
+
203
+ if not np.isnan(self.queue[0]):
204
+ r = max(self.queue)
205
+
206
+ return r
207
+
208
+
209
+ def highest(series:TimeSeries, period:int):
210
+ return Highest.wrap(series, period)
211
+
212
+
213
+ cdef class Lowest(Indicator):
214
+ cdef int period
215
+ cdef queue
216
+
217
+ def __init__(self, str name, TimeSeries series, int period):
218
+ self.period = period
219
+ self.queue = deque([np.nan] * period, maxlen=period)
220
+ super().__init__(name, series)
221
+
222
+ cpdef double calculate(self, long long time, double value, short new_item_started):
223
+ """
224
+ Not a most effictive algo but simplest and can handle updated last value
225
+ """
226
+ cdef float r = np.nan
227
+
228
+ if not np.isnan(value):
229
+ if new_item_started:
230
+ self.queue.append(value)
231
+ else:
232
+ self.queue[-1] = value
233
+
234
+ if not np.isnan(self.queue[0]):
235
+ r = min(self.queue)
236
+
237
+ return r
238
+
239
+
240
+ def lowest(series:TimeSeries, period:int):
241
+ return Lowest.wrap(series, period)
242
+
243
+
244
+ # - - - - TODO !!!!!!!
245
+ cdef class Std(Indicator):
246
+ cdef int period
247
+
248
+ def __init__(self, str name, TimeSeries series, int period):
249
+ self.period = period
250
+ super().__init__(name, series)
251
+
252
+ cpdef double calculate(self, long long time, double value, short new_item_started):
253
+ pass
254
+
255
+
256
+ def std(series:TimeSeries, period:int, mean=0):
257
+ return Std.wrap(series, period)
258
+ # - - - - TODO !!!!!!!
qubx/utils/__init__.py ADDED
@@ -0,0 +1,3 @@
1
+ from .time import convert_seconds_to_str, convert_tf_str_td64, floor_t64, time_to_str, infer_series_frequency
2
+ from .misc import install_pyx_recompiler_for_dev, runtime_env, version, Struct
3
+ from .charting.mpl_helpers import set_mpl_theme, fig, sbp, vline, hline, ellips, set_mpl_theme
@@ -0,0 +1,271 @@
1
+ import importlib, glob, imp, os, sys
2
+ from importlib.abc import MetaPathFinder
3
+ from importlib.util import spec_from_file_location
4
+ from importlib.machinery import ExtensionFileLoader, SourceFileLoader
5
+ from typing import List
6
+
7
+ PYX_EXT = ".pyx"
8
+ PYXDEP_EXT = ".pyxdep"
9
+ PYXBLD_EXT = ".pyxbld"
10
+
11
+
12
+ def handle_dependencies(pyxfilename):
13
+ testing = '_test_files' in globals()
14
+ dependfile = os.path.splitext(pyxfilename)[0] + PYXDEP_EXT
15
+
16
+ # by default let distutils decide whether to rebuild on its own
17
+ # (it has a better idea of what the output file will be)
18
+
19
+ # but we know more about dependencies so force a rebuild if
20
+ # some of the dependencies are newer than the pyxfile.
21
+ if os.path.exists(dependfile):
22
+ with open(dependfile) as fid:
23
+ depends = fid.readlines()
24
+ depends = [depend.strip() for depend in depends]
25
+
26
+ # gather dependencies in the "files" variable
27
+ # the dependency file is itself a dependency
28
+ files = [dependfile]
29
+ for depend in depends:
30
+ fullpath = os.path.join(os.path.dirname(dependfile),
31
+ depend)
32
+ files.extend(glob.glob(fullpath))
33
+
34
+ # if any file that the pyxfile depends upon is newer than
35
+ # the pyx file, 'touch' the pyx file so that distutils will
36
+ # be tricked into rebuilding it.
37
+ for file in files:
38
+ from distutils.dep_util import newer
39
+ if newer(file, pyxfilename):
40
+ print("Rebuilding %s because of %s", pyxfilename, file)
41
+ filetime = os.path.getmtime(file)
42
+ os.utime(pyxfilename, (filetime, filetime))
43
+
44
+
45
+ def handle_special_build(modname, pyxfilename):
46
+ special_build = os.path.splitext(pyxfilename)[0] + PYXBLD_EXT
47
+ ext = None
48
+ setup_args={}
49
+ if os.path.exists(special_build):
50
+ # globls = {}
51
+ # locs = {}
52
+ # execfile(special_build, globls, locs)
53
+ # ext = locs["make_ext"](modname, pyxfilename)
54
+ with open(special_build) as fid:
55
+ mod = imp.load_source("XXXX", special_build, fid)
56
+ make_ext = getattr(mod,'make_ext',None)
57
+ if make_ext:
58
+ ext = make_ext(modname, pyxfilename)
59
+ assert ext and ext.sources, "make_ext in %s did not return Extension" % special_build
60
+ make_setup_args = getattr(mod, 'make_setup_args',None)
61
+ if make_setup_args:
62
+ setup_args = make_setup_args()
63
+ assert isinstance(setup_args,dict), ("make_setup_args in %s did not return a dict"
64
+ % special_build)
65
+ assert set or setup_args, ("neither make_ext nor make_setup_args %s" % special_build)
66
+ ext.sources = [os.path.join(os.path.dirname(special_build), source) for source in ext.sources]
67
+ return ext, setup_args
68
+
69
+
70
+ def get_distutils_extension(modname, pyxfilename, language_level=None):
71
+ extension_mod, setup_args = handle_special_build(modname, pyxfilename)
72
+ if not extension_mod:
73
+ if not isinstance(pyxfilename, str):
74
+ # distutils is stupid in Py2 and requires exactly 'str'
75
+ # => encode accidentally coerced unicode strings back to str
76
+ pyxfilename = pyxfilename.encode(sys.getfilesystemencoding())
77
+ from distutils.extension import Extension
78
+ extension_mod = Extension(name = modname, sources=[pyxfilename])
79
+ if language_level is not None:
80
+ extension_mod.cython_directives = {'language_level': language_level}
81
+ return extension_mod, setup_args
82
+
83
+
84
+ def build_module(name, pyxfilename, user_setup_args, pyxbuild_dir=None, inplace=False, language_level=None,
85
+ build_in_temp=False, reload_support=True):
86
+ assert os.path.exists(pyxfilename), "Path does not exist: %s" % pyxfilename
87
+ handle_dependencies(pyxfilename)
88
+
89
+ extension_mod, setup_args = get_distutils_extension(name, pyxfilename, language_level)
90
+ build_in_temp = True
91
+ sargs = user_setup_args.copy() if user_setup_args else dict()
92
+ sargs.update(setup_args)
93
+ build_in_temp = sargs.pop('build_in_temp',build_in_temp)
94
+
95
+ from pyximport import pyxbuild
96
+ olddir = os.getcwd()
97
+ common = ''
98
+ if pyxbuild_dir:
99
+ # Windows concatenates the pyxbuild_dir to the pyxfilename when
100
+ # compiling, and then complains that the filename is too long
101
+ common = os.path.commonprefix([pyxbuild_dir, pyxfilename])
102
+ if len(common) > 30:
103
+ pyxfilename = os.path.relpath(pyxfilename)
104
+ pyxbuild_dir = os.path.relpath(pyxbuild_dir)
105
+ os.chdir(common)
106
+ try:
107
+ so_path = pyxbuild.pyx_to_dll(pyxfilename, extension_mod,
108
+ force_rebuild=1,
109
+ build_in_temp=build_in_temp,
110
+ pyxbuild_dir=pyxbuild_dir,
111
+ setup_args=sargs,
112
+ inplace=inplace,
113
+ reload_support=reload_support)
114
+ finally:
115
+ os.chdir(olddir)
116
+ so_path = os.path.join(common, so_path)
117
+ assert os.path.exists(so_path), "Cannot find: %s" % so_path
118
+
119
+ junkpath = os.path.join(os.path.dirname(so_path), name+"_*") #very dangerous with --inplace ? yes, indeed, trying to eat my files ;)
120
+ junkstuff = glob.glob(junkpath)
121
+ for path in junkstuff:
122
+ if path != so_path:
123
+ try:
124
+ os.remove(path)
125
+ except IOError:
126
+ print("Couldn't remove %s", path)
127
+
128
+ return so_path
129
+
130
+
131
+ def load_module(name, pyxfilename, pyxbuild_dir=None, is_package=False, build_inplace=False, language_level=None, so_path=None):
132
+ try:
133
+ if so_path is None:
134
+ if is_package:
135
+ module_name = name + '.__init__'
136
+ else:
137
+ module_name = name
138
+ so_path = build_module(module_name, pyxfilename, pyxbuild_dir, inplace=build_inplace, language_level=language_level)
139
+ mod = imp.load_dynamic(name, so_path)
140
+ if is_package and not hasattr(mod, '__path__'):
141
+ mod.__path__ = [os.path.dirname(so_path)]
142
+ assert mod.__file__ == so_path, (mod.__file__, so_path)
143
+ except Exception as failure_exc:
144
+ print("Failed to load extension module: %r" % failure_exc)
145
+ # if pyxargs.load_py_module_on_import_failure and pyxfilename.endswith('.py'):
146
+ if False and pyxfilename.endswith('.py'):
147
+ # try to fall back to normal import
148
+ mod = imp.load_source(name, pyxfilename)
149
+ assert mod.__file__ in (pyxfilename, pyxfilename+'c', pyxfilename+'o'), (mod.__file__, pyxfilename)
150
+ else:
151
+ tb = sys.exc_info()[2]
152
+ import traceback
153
+ exc = ImportError("Building module %s failed: %s" % (name, traceback.format_exception_only(*sys.exc_info()[:2])))
154
+ if sys.version_info[0] >= 3:
155
+ raise exc.with_traceback(tb)
156
+ else:
157
+ exec("raise exc, None, tb", {'exc': exc, 'tb': tb})
158
+ return mod
159
+
160
+
161
+ class PyxImportLoader(ExtensionFileLoader):
162
+
163
+ def __init__(self, filename, setup_args, pyxbuild_dir, inplace, language_level, reload_support):
164
+ module_name = os.path.splitext(os.path.basename(filename))[0]
165
+ super().__init__(module_name, filename)
166
+ self._pyxbuild_dir = pyxbuild_dir
167
+ self._inplace = inplace
168
+ self._language_level = language_level
169
+ self._setup_args = setup_args
170
+ self._reload_support = reload_support
171
+
172
+ def create_module(self, spec):
173
+ try:
174
+ # print(f"CREATING MODULE: {spec.name} -> {spec.origin}")
175
+ so_path = build_module(spec.name, pyxfilename=spec.origin, user_setup_args=self._setup_args, pyxbuild_dir=self._pyxbuild_dir,
176
+ inplace=self._inplace, language_level=self._language_level, reload_support=self._reload_support)
177
+ self.path = so_path
178
+ spec.origin = so_path
179
+ return super().create_module(spec)
180
+ except Exception as failure_exc:
181
+ # print("LOADING on FAILURE MODULE")
182
+ # if pyxargs.load_py_module_on_import_failure and spec.origin.endswith('.pyx'):
183
+ if False and spec.origin.endswith(PYX_EXT):
184
+ spec = importlib.util.spec_from_file_location(spec.name, spec.origin,
185
+ loader=SourceFileLoader(spec.name, spec.origin))
186
+ mod = importlib.util.module_from_spec(spec)
187
+ assert mod.__file__ in (spec.origin, spec.origin + 'c', spec.origin + 'o'), (mod.__file__, spec.origin)
188
+ return mod
189
+ else:
190
+ tb = sys.exc_info()[2]
191
+ import traceback
192
+ exc = ImportError("Building module %s failed: %s" % (
193
+ spec.name, traceback.format_exception_only(*sys.exc_info()[:2])))
194
+ raise exc.with_traceback(tb)
195
+
196
+ def exec_module(self, module):
197
+ try:
198
+ # print(f"EXEC MODULE: {module}")
199
+ return super().exec_module(module)
200
+ except Exception as failure_exc:
201
+ import traceback
202
+ print("Failed to load extension module: %r" % failure_exc)
203
+ raise ImportError("Executing module %s failed %s" % (
204
+ module.__file__, traceback.format_exception_only(*sys.exc_info()[:2])))
205
+
206
+
207
+ class CustomPyxImportMetaFinder(MetaPathFinder):
208
+
209
+ def __init__(self, modules_to_check: List[str], extension=PYX_EXT, setup_args=None, pyxbuild_dir=None, inplace=False, language_level=None, reload_support=True):
210
+ self.valid_modules = modules_to_check
211
+ self.pyxbuild_dir = pyxbuild_dir
212
+ self.inplace = inplace
213
+ self.language_level = language_level
214
+ self.extension = extension
215
+ self.setup_args = setup_args if setup_args else dict()
216
+ self.reload_support = reload_support
217
+
218
+ def find_spec(self, fullname, path, target=None):
219
+ def _is_valid(module):
220
+ if not self.valid_modules:
221
+ return True
222
+ for m in self.valid_modules:
223
+ if module.startswith(m):
224
+ return True
225
+ return False
226
+
227
+ if not path:
228
+ path = [os.getcwd()] # top level import --
229
+ if "." in fullname:
230
+ *parents, name = fullname.split(".")
231
+ else:
232
+ name = fullname
233
+ for entry in path:
234
+ if os.path.isdir(os.path.join(entry, name)):
235
+ # this module has child modules
236
+ filename = os.path.join(entry, name, "__init__" + self.extension)
237
+ submodule_locations = [os.path.join(entry, name)]
238
+ else:
239
+ filename = os.path.join(entry, name + self.extension)
240
+ submodule_locations = None
241
+ if not os.path.exists(filename):
242
+ continue
243
+
244
+ if not _is_valid(fullname):
245
+ continue
246
+
247
+ return spec_from_file_location(
248
+ fullname, filename,
249
+ loader=PyxImportLoader(filename, self.setup_args, self.pyxbuild_dir, self.inplace, self.language_level, self.reload_support),
250
+ submodule_search_locations=submodule_locations)
251
+
252
+ return None # we don't know how to import this
253
+
254
+
255
+ __pyx_finder_installed = False
256
+
257
+ def pyx_install_loader(modules_to_check: List[str]):
258
+ import numpy as np
259
+ import pyximport
260
+ global __pyx_finder_installed
261
+
262
+ if not __pyx_finder_installed:
263
+ build_dir = os.path.expanduser("~/.pyxbld")
264
+ setup_args = {'include_dirs': np.get_include()}
265
+ sys.meta_path.insert(0, CustomPyxImportMetaFinder(
266
+ modules_to_check,
267
+ PYX_EXT, setup_args=setup_args, pyxbuild_dir=build_dir,
268
+ language_level=3, reload_support=True
269
+ ))
270
+ pyximport.install(setup_args=setup_args, build_dir=build_dir, reload_support=True, language_level=3)
271
+