Qubx 0.1.86__tar.gz → 0.1.88__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.

Potentially problematic release.


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

Files changed (37) hide show
  1. {qubx-0.1.86 → qubx-0.1.88}/PKG-INFO +1 -1
  2. {qubx-0.1.86 → qubx-0.1.88}/build.py +12 -17
  3. {qubx-0.1.86 → qubx-0.1.88}/pyproject.toml +2 -2
  4. {qubx-0.1.86 → qubx-0.1.88}/src/qubx/ta/indicators.pyx +85 -55
  5. {qubx-0.1.86 → qubx-0.1.88}/README.md +0 -0
  6. {qubx-0.1.86 → qubx-0.1.88}/src/qubx/__init__.py +0 -0
  7. {qubx-0.1.86 → qubx-0.1.88}/src/qubx/_nb_magic.py +0 -0
  8. {qubx-0.1.86 → qubx-0.1.88}/src/qubx/core/__init__.py +0 -0
  9. {qubx-0.1.86 → qubx-0.1.88}/src/qubx/core/account.py +0 -0
  10. {qubx-0.1.86 → qubx-0.1.88}/src/qubx/core/basics.py +0 -0
  11. {qubx-0.1.86 → qubx-0.1.88}/src/qubx/core/helpers.py +0 -0
  12. {qubx-0.1.86 → qubx-0.1.88}/src/qubx/core/loggers.py +0 -0
  13. {qubx-0.1.86 → qubx-0.1.88}/src/qubx/core/lookups.py +0 -0
  14. {qubx-0.1.86 → qubx-0.1.88}/src/qubx/core/series.pxd +0 -0
  15. {qubx-0.1.86 → qubx-0.1.88}/src/qubx/core/series.pyx +0 -0
  16. {qubx-0.1.86 → qubx-0.1.88}/src/qubx/core/strategy.py +0 -0
  17. {qubx-0.1.86 → qubx-0.1.88}/src/qubx/core/utils.pyx +0 -0
  18. {qubx-0.1.86 → qubx-0.1.88}/src/qubx/data/readers.py +0 -0
  19. {qubx-0.1.86 → qubx-0.1.88}/src/qubx/impl/ccxt_connector.py +0 -0
  20. {qubx-0.1.86 → qubx-0.1.88}/src/qubx/impl/ccxt_customizations.py +0 -0
  21. {qubx-0.1.86 → qubx-0.1.88}/src/qubx/impl/ccxt_trading.py +0 -0
  22. {qubx-0.1.86 → qubx-0.1.88}/src/qubx/impl/ccxt_utils.py +0 -0
  23. {qubx-0.1.86 → qubx-0.1.88}/src/qubx/math/__init__.py +0 -0
  24. {qubx-0.1.86 → qubx-0.1.88}/src/qubx/math/stats.py +0 -0
  25. {qubx-0.1.86 → qubx-0.1.88}/src/qubx/pandaz/__init__.py +0 -0
  26. {qubx-0.1.86 → qubx-0.1.88}/src/qubx/pandaz/ta.py +0 -0
  27. {qubx-0.1.86 → qubx-0.1.88}/src/qubx/pandaz/utils.py +0 -0
  28. {qubx-0.1.86 → qubx-0.1.88}/src/qubx/ta/__init__.py +0 -0
  29. {qubx-0.1.86 → qubx-0.1.88}/src/qubx/trackers/__init__.py +0 -0
  30. {qubx-0.1.86 → qubx-0.1.88}/src/qubx/trackers/rebalancers.py +0 -0
  31. {qubx-0.1.86 → qubx-0.1.88}/src/qubx/utils/__init__.py +0 -0
  32. {qubx-0.1.86 → qubx-0.1.88}/src/qubx/utils/_pyxreloader.py +0 -0
  33. {qubx-0.1.86 → qubx-0.1.88}/src/qubx/utils/charting/mpl_helpers.py +0 -0
  34. {qubx-0.1.86 → qubx-0.1.88}/src/qubx/utils/marketdata/binance.py +0 -0
  35. {qubx-0.1.86 → qubx-0.1.88}/src/qubx/utils/misc.py +0 -0
  36. {qubx-0.1.86 → qubx-0.1.88}/src/qubx/utils/runner.py +0 -0
  37. {qubx-0.1.86 → qubx-0.1.88}/src/qubx/utils/time.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: Qubx
3
- Version: 0.1.86
3
+ Version: 0.1.88
4
4
  Summary: Qubx - quantitative trading framework
5
5
  Home-page: https://github.com/dmarienko/Qubx
6
6
  Author: Dmitry Marienko
@@ -17,6 +17,8 @@ from Cython.Compiler.Version import version as cython_compiler_version
17
17
  from setuptools import Distribution
18
18
  from setuptools import Extension
19
19
 
20
+ RED, BLUE, GREEN, YLW, RES = "\033[31m", "\033[36m", "\033[32m", "\033[33m", "\033[0m"
21
+
20
22
 
21
23
  BUILD_MODE = os.getenv("BUILD_MODE", "release")
22
24
  PROFILE_MODE = bool(os.getenv("PROFILE_MODE", ""))
@@ -95,13 +97,13 @@ def _build_extensions() -> list[Extension]:
95
97
  "WS2_32.Lib",
96
98
  ]
97
99
 
98
- print("Creating C extension modules...")
100
+ print(f">> {GREEN} Creating C extension modules...{RES}")
99
101
  print(f"define_macros={define_macros}")
100
102
  print(f"extra_compile_args={extra_compile_args}")
101
103
 
102
104
  return [
103
105
  Extension(
104
- name=str(pyx.relative_to(".")).replace(os.path.sep, ".")[:-4],
106
+ name=str(pyx.relative_to("src")).replace(os.path.sep, ".")[:-4],
105
107
  sources=[str(pyx)],
106
108
  include_dirs=[np.get_include()], # , *RUST_INCLUDES],
107
109
  define_macros=define_macros,
@@ -138,7 +140,7 @@ def _build_distribution(extensions: list[Extension]) -> Distribution:
138
140
  def _copy_build_dir_to_project(cmd: build_ext) -> None:
139
141
  # Copy built extensions back to the project tree
140
142
  for output in cmd.get_outputs():
141
- relative_extension = Path(output).relative_to(cmd.build_lib)
143
+ relative_extension = Path("src") / Path(output).relative_to(cmd.build_lib)
142
144
  if not Path(output).exists():
143
145
  continue
144
146
 
@@ -148,21 +150,19 @@ def _copy_build_dir_to_project(cmd: build_ext) -> None:
148
150
  mode |= (mode & 0o444) >> 2
149
151
  relative_extension.chmod(mode)
150
152
 
151
- print("Copied all compiled dynamic library files into source")
153
+ print(f" >> {GREEN}Copied all compiled dynamic library files into source{RES}")
152
154
 
153
155
 
154
156
  def _strip_unneeded_symbols() -> None:
155
157
  try:
156
- print("Stripping unneeded symbols from binaries...")
158
+ print(f" >> {YLW}Stripping unneeded symbols from binaries...{RES}")
157
159
  for so in itertools.chain(Path("src/qubx").rglob("*.so")):
158
160
  if platform.system() == "Linux":
159
161
  strip_cmd = ["strip", "--strip-unneeded", so]
160
162
  elif platform.system() == "Darwin":
161
163
  strip_cmd = ["strip", "-x", so]
162
164
  else:
163
- raise RuntimeError(
164
- f"Cannot strip symbols for platform {platform.system()}"
165
- )
165
+ raise RuntimeError(f"Cannot strip symbols for platform {platform.system()}")
166
166
  subprocess.run(
167
167
  strip_cmd, # type: ignore [arg-type] # noqa
168
168
  check=True,
@@ -185,7 +185,7 @@ def build() -> None:
185
185
  distribution = _build_distribution(extensions)
186
186
 
187
187
  # Build and run the command
188
- print("Compiling C extension modules...")
188
+ print(f">> {GREEN} Compiling C extension modules...{RES}")
189
189
  cmd: build_ext = build_ext(distribution)
190
190
  # if PARALLEL_BUILD:
191
191
  # cmd.parallel = os.cpu_count()
@@ -201,15 +201,12 @@ def build() -> None:
201
201
  _strip_unneeded_symbols()
202
202
 
203
203
 
204
- RED, BLUE, GREEN, YLW, RES = "\033[31m", "\033[36m", "\033[32m", "\033[33m", "\033[0m"
205
204
  if __name__ == "__main__":
206
205
  qubx_platform = toml.load("pyproject.toml")["tool"]["poetry"]["version"]
207
206
  print(BLUE)
208
207
  print("=====================================================================")
209
208
  print(f"Qubx Builder {qubx_platform}")
210
- print(
211
- "=====================================================================\033[0m"
212
- )
209
+ print("=====================================================================\033[0m")
213
210
  print(f"System: {GREEN}{platform.system()} {platform.machine()}{RES}")
214
211
  # print(f"Clang: {GREEN}{_get_clang_version()}{RES}")
215
212
  # print(f"Rust: {GREEN}{_get_rustc_version()}{RES}")
@@ -225,12 +222,10 @@ if __name__ == "__main__":
225
222
  print(f"COPY_TO_SOURCE={COPY_TO_SOURCE}")
226
223
  # print(f"PYO3_ONLY={PYO3_ONLY}\n")
227
224
 
228
- print("Starting build...")
225
+ print(f">> {GREEN}Starting build...{RES}")
229
226
  ts_start = datetime.datetime.now(datetime.timezone.utc)
230
227
  build()
231
- print(
232
- f"Build time: {YLW}{datetime.datetime.now(datetime.timezone.utc) - ts_start}{RES}"
233
- )
228
+ print(f"Build time: {YLW}{datetime.datetime.now(datetime.timezone.utc) - ts_start}{RES}")
234
229
  print(GREEN + "Build completed" + RES)
235
230
 
236
231
  # # See if Cython is installed
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "Qubx"
3
- version = "0.1.86"
3
+ version = "0.1.88"
4
4
  description = "Qubx - quantitative trading framework"
5
5
  authors = ["Dmitry Marienko <dmitry@gmail.com>"]
6
6
  readme = "README.md"
@@ -66,4 +66,4 @@ pytest-mock = "*"
66
66
 
67
67
 
68
68
  [tool.pytest.ini_options]
69
- pythonpath = ["src"]
69
+ pythonpath = ["src"]
@@ -274,7 +274,8 @@ cdef class Pewma(Indicator):
274
274
  cdef double alpha, beta
275
275
  cdef int T
276
276
 
277
- cdef double _mean, _std, _var
277
+ cdef double _mean, _vstd, _var
278
+ cdef double mean, vstd, var
278
279
  cdef long _i
279
280
 
280
281
  def __init__(self, str name, TimeSeries series, double alpha, double beta, int T):
@@ -287,34 +288,51 @@ cdef class Pewma(Indicator):
287
288
  self.std = TimeSeries('std', series.timeframe, series.max_series_length)
288
289
  super().__init__(name, series)
289
290
 
291
+ def _store(self):
292
+ self.mean = self._mean
293
+ self.vstd = self._vstd
294
+ self.var = self._var
295
+
296
+ def _restore(self):
297
+ self._mean = self.mean
298
+ self._vstd = self.vstd
299
+ self._var = self.var
300
+
301
+ def _get_alpha(self, p_t):
302
+ if self._i - 1 > self.T:
303
+ return self.alpha * (1.0 - self.beta * p_t)
304
+ return 1.0 - 1.0 / self._i
305
+
290
306
  cpdef double calculate(self, long long time, double x, short new_item_started):
291
- cdef double diff, p, a_t, incr, v
307
+ cdef double diff, p_t, a_t, incr
292
308
 
293
- if self._i < 1:
309
+ if len(self.series) <= 1:
294
310
  self._mean = x
295
- self._std = 0.0
311
+ self._vstd = 0.0
296
312
  self._var = 0.0
297
- incr = 0.0
298
- v = 0.0
299
- else:
300
- diff = x - self._mean
301
- # prob of observing diff
302
- p = norm_pdf(diff / self._std) if self._std != 0.0 else 0.0
303
-
304
- # weight to give to this point
305
- a_t = self.alpha * (1 - self.beta * p) if self._i > self.T else (1.0 - 1.0 / self._i)
306
- incr = (1.0 - a_t) * diff
307
- v = a_t * (self._var + diff * incr)
308
- self.std.update(time, np.sqrt(v))
313
+ self._store()
314
+ self.std.update(time, self.vstd)
315
+ return self.mean
309
316
 
310
317
  if new_item_started:
311
- self._mean += incr
312
318
  self._i += 1
313
- self._var = v
314
- self._std = np.sqrt(v)
315
- self.std.update(time, self._std)
319
+ self._restore()
320
+ else:
321
+ self._store()
322
+
323
+ diff = x - self.mean
324
+ # prob of observing diff
325
+ p_t = norm_pdf(diff / self.vstd) if self.vstd != 0.0 else 0.0
326
+
327
+ # weight to give to this point
328
+ a_t = self._get_alpha(p_t)
329
+ incr = (1.0 - a_t) * diff
330
+ self.mean += incr
331
+ self.var = a_t * (self.var + diff * incr)
332
+ self.vstd = np.sqrt(self.var)
333
+ self.std.update(time, self.vstd)
316
334
 
317
- return self._mean
335
+ return self.mean
318
336
 
319
337
 
320
338
  def pewma(series:TimeSeries, alpha: float, beta: float, T:int=30):
@@ -326,14 +344,13 @@ def pewma(series:TimeSeries, alpha: float, beta: float, T:int=30):
326
344
 
327
345
 
328
346
  cdef class PewmaOutliersDetector(Indicator):
329
- cdef public TimeSeries upper
330
- cdef public TimeSeries lower
331
- cdef public TimeSeries outliers
347
+ cdef public TimeSeries upper, lower, outliers, std
332
348
  cdef double alpha, beta, threshold
333
349
  cdef int T
334
350
 
335
351
  cdef long _i
336
- cdef double _z_thr, _mean, _variance,_std
352
+ cdef double mean, vstd, variance
353
+ cdef double _mean, _vstd, _variance, _z_thr
337
354
 
338
355
  def __init__(self, str name, TimeSeries series, double alpha, double beta, int T, double threshold):
339
356
  self.alpha = alpha
@@ -344,44 +361,43 @@ cdef class PewmaOutliersDetector(Indicator):
344
361
  # - series
345
362
  self.upper = TimeSeries('uba', series.timeframe, series.max_series_length)
346
363
  self.lower = TimeSeries('lba', series.timeframe, series.max_series_length)
364
+ self.std = TimeSeries('std', series.timeframe, series.max_series_length)
347
365
  self.outliers = TimeSeries('outliers', series.timeframe, series.max_series_length)
348
366
 
349
367
  # - local variables
350
368
  self._i = 0
351
369
  self._z_thr = ndtri(1 - threshold / 2)
352
370
 
353
- self._mean = 0.0
354
- self._variance = 0.0
355
- self._std = 0.0
356
371
  super().__init__(name, series)
357
372
 
358
- cdef double _get_alpha(self, double p_t):
359
- if self._i == 0:
360
- return 0.0
373
+ def _store(self):
374
+ self.mean = self._mean
375
+ self.vstd = self._vstd
376
+ self.variance = self._variance
361
377
 
362
- if self._i < self.T:
363
- return 1.0 - 1.0 / self._i
378
+ def _restore(self):
379
+ self._mean = self.mean
380
+ self._vstd = self.vstd
381
+ self._variance = self.variance
364
382
 
365
- return self.alpha * (1.0 - self.beta * p_t)
383
+ cdef double _get_alpha(self, double p_t):
384
+ if self._i + 1 >= self.T:
385
+ return self.alpha * (1.0 - self.beta * p_t)
386
+ return 1.0 - 1.0 / (self._i + 1.0)
366
387
 
367
388
  cdef double _get_mean(self, double x, double alpha_t):
368
- return alpha_t * self._mean + (1.0 - alpha_t) * x
389
+ return alpha_t * self.mean + (1.0 - alpha_t) * x
369
390
 
370
391
  cdef double _get_variance(self, double x, double alpha_t):
371
- return alpha_t * self._variance + (1.0 - alpha_t) * np.square(x)
392
+ return alpha_t * self.variance + (1.0 - alpha_t) * np.square(x)
372
393
 
373
394
  cdef double _get_std(self, double variance, double mean):
374
395
  return np.sqrt(max(variance - np.square(mean), 0.0))
375
396
 
376
397
  cdef double _get_p(self, double x):
377
- cdef double z_t = 0.0
378
- cdef double p_t
379
-
380
- if self._i != 1:
381
- z_t = ((x - self._mean) / self._std) if (self._std != 0 and not np.isnan(x)) else 0.0
382
-
398
+ cdef double z_t = ((x - self.mean) / self.vstd) if (self.vstd != 0 and not np.isnan(x)) else 0.0
383
399
  # if self.dist == 'normal':
384
- p_t = norm_pdf(z_t)
400
+ # p_t = norm_pdf(z_t)
385
401
  # elif self.dist == 'cauchy':
386
402
  # p_t = (1 / (np.pi * (1 + np.square(z_t))))
387
403
  # elif self.dist == 'student_t':
@@ -389,31 +405,45 @@ cdef class PewmaOutliersDetector(Indicator):
389
405
  # (np.sqrt(self.count - 1) * np.sqrt(np.pi) * np.exp(np.math.lgamma(0.5 * (self.count - 1))))
390
406
  # else:
391
407
  # raise ValueError('Invalid distribution type')
392
- return p_t
408
+ return norm_pdf(z_t)
393
409
 
394
410
  cpdef double calculate(self, long long time, double x, short new_item_started):
411
+ # - first bar - just use it as initial value
412
+ if len(self.series) <= 1:
413
+ self._mean = x
414
+ self._variance = x ** 2
415
+ self._vstd = 0.0
416
+ self._store()
417
+ self.std.update(time, self.vstd)
418
+ self.upper.update(time, x)
419
+ self.lower.update(time, x)
420
+ return self._mean
421
+
422
+ # - new bar is started use n-1 values for calculate innovations
423
+ if new_item_started:
424
+ self._i += 1
425
+ self._restore()
426
+ else:
427
+ self._store()
428
+
395
429
  cdef double p_t = self._get_p(x)
396
430
  cdef double alpha_t = self._get_alpha(p_t)
397
- cdef double mean = self._get_mean(x, alpha_t)
398
- cdef double variance = self._get_variance(x, alpha_t)
399
- cdef double std = self._get_std(variance, mean)
400
- cdef double ub = mean + self._z_thr * std
401
- cdef double lb = mean - self._z_thr * std
431
+ self.mean = self._get_mean(x, alpha_t)
432
+ self.variance = self._get_variance(x, alpha_t)
433
+ self.vstd = self._get_std(self.variance, self.mean)
434
+ cdef double ub = self.mean + self._z_thr * self.vstd
435
+ cdef double lb = self.mean - self._z_thr * self.vstd
402
436
 
403
437
  self.upper.update(time, ub)
404
438
  self.lower.update(time, lb)
405
- if new_item_started:
406
- self._mean = mean
407
- self._i += 1
408
- self._variance = variance
409
- self._std = std
439
+ self.std.update(time, self.vstd)
410
440
 
411
441
  # - check if it's outlier
412
442
  if p_t < self.threshold:
413
443
  self.outliers.update(time, x)
414
444
  else:
415
445
  self.outliers.update(time, np.nan)
416
- return mean
446
+ return self.mean
417
447
 
418
448
 
419
449
  def pewma_outliers_detector(series:TimeSeries, alpha: float, beta: float, T:int=30, threshold=0.05):
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