Qubx 0.0.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.
Potentially problematic release.
This version of Qubx might be problematic. Click here for more details.
- qubx-0.0.1/PKG-INFO +39 -0
- qubx-0.0.1/README.md +10 -0
- qubx-0.0.1/build.py +265 -0
- qubx-0.0.1/pyproject.toml +56 -0
- qubx-0.0.1/src/qubx/__init__.py +164 -0
- qubx-0.0.1/src/qubx/_nb_magic.py +69 -0
- qubx-0.0.1/src/qubx/core/__init__.py +0 -0
- qubx-0.0.1/src/qubx/core/basics.py +224 -0
- qubx-0.0.1/src/qubx/core/lookups.py +152 -0
- qubx-0.0.1/src/qubx/core/series.pxd +94 -0
- qubx-0.0.1/src/qubx/core/series.pyx +763 -0
- qubx-0.0.1/src/qubx/core/strategy.py +89 -0
- qubx-0.0.1/src/qubx/core/utils.pyx +54 -0
- qubx-0.0.1/src/qubx/data/readers.py +387 -0
- qubx-0.0.1/src/qubx/math/__init__.py +1 -0
- qubx-0.0.1/src/qubx/math/stats.py +42 -0
- qubx-0.0.1/src/qubx/ta/__init__.py +0 -0
- qubx-0.0.1/src/qubx/ta/indicators.pyx +258 -0
- qubx-0.0.1/src/qubx/utils/__init__.py +3 -0
- qubx-0.0.1/src/qubx/utils/_pyxreloader.py +271 -0
- qubx-0.0.1/src/qubx/utils/charting/mpl_helpers.py +182 -0
- qubx-0.0.1/src/qubx/utils/marketdata/binance.py +212 -0
- qubx-0.0.1/src/qubx/utils/misc.py +234 -0
- qubx-0.0.1/src/qubx/utils/pandas.py +206 -0
- qubx-0.0.1/src/qubx/utils/time.py +145 -0
qubx-0.0.1/PKG-INFO
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: Qubx
|
|
3
|
+
Version: 0.0.1
|
|
4
|
+
Summary: Qubx - quantitative trading framework
|
|
5
|
+
Home-page: https://github.com/dmarienko/Qubx
|
|
6
|
+
Author: Dmitry Marienko
|
|
7
|
+
Author-email: dmitry@gmail.com
|
|
8
|
+
Requires-Python: >=3.9,<4.0
|
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
14
|
+
Requires-Dist: cython (==3.0.8)
|
|
15
|
+
Requires-Dist: loguru (>=0.7.2,<0.8.0)
|
|
16
|
+
Requires-Dist: ntplib (>=0.4.0,<0.5.0)
|
|
17
|
+
Requires-Dist: numpy (>=1.26.3,<2.0.0)
|
|
18
|
+
Requires-Dist: pyarrow (>=15.0.0,<16.0.0)
|
|
19
|
+
Requires-Dist: pydantic (>=1.10.2,<2.0.0)
|
|
20
|
+
Requires-Dist: pymongo (>=4.6.1,<5.0.0)
|
|
21
|
+
Requires-Dist: pytest[lazyfixture] (>=7.2.0,<8.0.0)
|
|
22
|
+
Requires-Dist: python-binance (>=1.0.19,<2.0.0)
|
|
23
|
+
Requires-Dist: python-dotenv (>=1.0.0,<2.0.0)
|
|
24
|
+
Requires-Dist: scipy (>=1.12.0,<2.0.0)
|
|
25
|
+
Requires-Dist: stackprinter (>=0.2.10,<0.3.0)
|
|
26
|
+
Project-URL: Repository, https://github.com/dmarienko/Qubx
|
|
27
|
+
Description-Content-Type: text/markdown
|
|
28
|
+
|
|
29
|
+
# Qubx
|
|
30
|
+
|
|
31
|
+
### Next generation of Qube quantitative backtesting framework (QUBX)
|
|
32
|
+
```
|
|
33
|
+
/////\
|
|
34
|
+
///// \
|
|
35
|
+
\\\\\ /
|
|
36
|
+
\\\\\/ (c) 2024, by M.D.E
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
|
qubx-0.0.1/README.md
ADDED
qubx-0.0.1/build.py
ADDED
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
import datetime
|
|
2
|
+
import itertools
|
|
3
|
+
import os
|
|
4
|
+
import platform
|
|
5
|
+
import shutil
|
|
6
|
+
import subprocess
|
|
7
|
+
import sysconfig
|
|
8
|
+
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
import os
|
|
11
|
+
import numpy as np
|
|
12
|
+
import toml
|
|
13
|
+
from Cython.Build import build_ext
|
|
14
|
+
from Cython.Build import cythonize
|
|
15
|
+
from Cython.Compiler import Options
|
|
16
|
+
from Cython.Compiler.Version import version as cython_compiler_version
|
|
17
|
+
from setuptools import Distribution
|
|
18
|
+
from setuptools import Extension
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
BUILD_MODE = os.getenv("BUILD_MODE", "release")
|
|
22
|
+
PROFILE_MODE = bool(os.getenv("PROFILE_MODE", ""))
|
|
23
|
+
ANNOTATION_MODE = bool(os.getenv("ANNOTATION_MODE", ""))
|
|
24
|
+
BUILD_DIR = "build/optimized"
|
|
25
|
+
COPY_TO_SOURCE = os.getenv("COPY_TO_SOURCE", "true") == "true"
|
|
26
|
+
|
|
27
|
+
################################################################################
|
|
28
|
+
# CYTHON BUILD
|
|
29
|
+
################################################################################
|
|
30
|
+
# https://cython.readthedocs.io/en/latest/src/userguide/source_files_and_compilation.html
|
|
31
|
+
|
|
32
|
+
Options.docstrings = True # Include docstrings in modules
|
|
33
|
+
Options.fast_fail = True # Abort the compilation on the first error occurred
|
|
34
|
+
Options.annotate = ANNOTATION_MODE # Create annotated HTML files for each .pyx
|
|
35
|
+
if ANNOTATION_MODE:
|
|
36
|
+
Options.annotate_coverage_xml = "coverage.xml"
|
|
37
|
+
Options.fast_fail = True # Abort compilation on first error
|
|
38
|
+
Options.warning_errors = True # Treat compiler warnings as errors
|
|
39
|
+
Options.extra_warnings = True
|
|
40
|
+
|
|
41
|
+
CYTHON_COMPILER_DIRECTIVES = {
|
|
42
|
+
"language_level": "3",
|
|
43
|
+
"cdivision": True, # If division is as per C with no check for zero (35% speed up)
|
|
44
|
+
"nonecheck": True, # Insert extra check for field access on C extensions
|
|
45
|
+
"embedsignature": True, # If docstrings should be embedded into C signatures
|
|
46
|
+
"profile": PROFILE_MODE, # If we're debugging or profiling
|
|
47
|
+
"linetrace": PROFILE_MODE, # If we're debugging or profiling
|
|
48
|
+
"warn.maybe_uninitialized": True,
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def _build_extensions() -> list[Extension]:
|
|
53
|
+
# Regarding the compiler warning: #warning "Using deprecated NumPy API,
|
|
54
|
+
# disable it with " "#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION"
|
|
55
|
+
# https://stackoverflow.com/questions/52749662/using-deprecated-numpy-api
|
|
56
|
+
# From the Cython docs: "For the time being, it is just a warning that you can ignore."
|
|
57
|
+
define_macros: list[tuple[str, str | None]] = [
|
|
58
|
+
("NPY_NO_DEPRECATED_API", "NPY_1_7_API_VERSION"),
|
|
59
|
+
]
|
|
60
|
+
if PROFILE_MODE or ANNOTATION_MODE:
|
|
61
|
+
# Profiling requires special macro directives
|
|
62
|
+
define_macros.append(("CYTHON_TRACE", "1"))
|
|
63
|
+
|
|
64
|
+
extra_compile_args = []
|
|
65
|
+
extra_link_args = []
|
|
66
|
+
# extra_link_args = RUST_LIBS
|
|
67
|
+
|
|
68
|
+
if platform.system() != "Windows":
|
|
69
|
+
# Suppress warnings produced by Cython boilerplate
|
|
70
|
+
extra_compile_args.append("-Wno-unreachable-code")
|
|
71
|
+
if BUILD_MODE == "release":
|
|
72
|
+
extra_compile_args.append("-O2")
|
|
73
|
+
extra_compile_args.append("-pipe")
|
|
74
|
+
|
|
75
|
+
if platform.system() == "Windows":
|
|
76
|
+
extra_link_args += [
|
|
77
|
+
"AdvAPI32.Lib",
|
|
78
|
+
"bcrypt.lib",
|
|
79
|
+
"Crypt32.lib",
|
|
80
|
+
"Iphlpapi.lib",
|
|
81
|
+
"Kernel32.lib",
|
|
82
|
+
"ncrypt.lib",
|
|
83
|
+
"Netapi32.lib",
|
|
84
|
+
"ntdll.lib",
|
|
85
|
+
"Ole32.lib",
|
|
86
|
+
"OleAut32.lib",
|
|
87
|
+
"Pdh.lib",
|
|
88
|
+
"PowrProf.lib",
|
|
89
|
+
"Psapi.lib",
|
|
90
|
+
"schannel.lib",
|
|
91
|
+
"secur32.lib",
|
|
92
|
+
"Shell32.lib",
|
|
93
|
+
"User32.Lib",
|
|
94
|
+
"UserEnv.Lib",
|
|
95
|
+
"WS2_32.Lib",
|
|
96
|
+
]
|
|
97
|
+
|
|
98
|
+
print("Creating C extension modules...")
|
|
99
|
+
print(f"define_macros={define_macros}")
|
|
100
|
+
print(f"extra_compile_args={extra_compile_args}")
|
|
101
|
+
|
|
102
|
+
return [
|
|
103
|
+
Extension(
|
|
104
|
+
name=str(pyx.relative_to(".")).replace(os.path.sep, ".")[:-4],
|
|
105
|
+
sources=[str(pyx)],
|
|
106
|
+
include_dirs=[np.get_include()], #, *RUST_INCLUDES],
|
|
107
|
+
define_macros=define_macros,
|
|
108
|
+
language="c",
|
|
109
|
+
extra_link_args=extra_link_args,
|
|
110
|
+
extra_compile_args=extra_compile_args,
|
|
111
|
+
) for pyx in itertools.chain(Path("src/qubx").rglob("*.pyx"))
|
|
112
|
+
]
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def _build_distribution(extensions: list[Extension]) -> Distribution:
|
|
116
|
+
nthreads = os.cpu_count() or 1
|
|
117
|
+
if platform.system() == "Windows":
|
|
118
|
+
nthreads = min(nthreads, 60)
|
|
119
|
+
print(f"nthreads={nthreads}")
|
|
120
|
+
|
|
121
|
+
distribution = Distribution(
|
|
122
|
+
{
|
|
123
|
+
"name": "qubx",
|
|
124
|
+
"ext_modules": cythonize(
|
|
125
|
+
module_list=extensions,
|
|
126
|
+
compiler_directives=CYTHON_COMPILER_DIRECTIVES,
|
|
127
|
+
nthreads=nthreads,
|
|
128
|
+
build_dir=BUILD_DIR,
|
|
129
|
+
gdb_debug=PROFILE_MODE,
|
|
130
|
+
),
|
|
131
|
+
"zip_safe": False,
|
|
132
|
+
},
|
|
133
|
+
)
|
|
134
|
+
return distribution
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def _copy_build_dir_to_project(cmd: build_ext) -> None:
|
|
138
|
+
# Copy built extensions back to the project tree
|
|
139
|
+
for output in cmd.get_outputs():
|
|
140
|
+
relative_extension = Path(output).relative_to(cmd.build_lib)
|
|
141
|
+
if not Path(output).exists():
|
|
142
|
+
continue
|
|
143
|
+
|
|
144
|
+
# Copy the file and set permissions
|
|
145
|
+
shutil.copyfile(output, relative_extension)
|
|
146
|
+
mode = relative_extension.stat().st_mode
|
|
147
|
+
mode |= (mode & 0o444) >> 2
|
|
148
|
+
relative_extension.chmod(mode)
|
|
149
|
+
|
|
150
|
+
print("Copied all compiled dynamic library files into source")
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
def _strip_unneeded_symbols() -> None:
|
|
154
|
+
try:
|
|
155
|
+
print("Stripping unneeded symbols from binaries...")
|
|
156
|
+
for so in itertools.chain(Path("nautilus_trader").rglob("*.so")):
|
|
157
|
+
if platform.system() == "Linux":
|
|
158
|
+
strip_cmd = ["strip", "--strip-unneeded", so]
|
|
159
|
+
elif platform.system() == "Darwin":
|
|
160
|
+
strip_cmd = ["strip", "-x", so]
|
|
161
|
+
else:
|
|
162
|
+
raise RuntimeError(f"Cannot strip symbols for platform {platform.system()}")
|
|
163
|
+
subprocess.run(
|
|
164
|
+
strip_cmd, # type: ignore [arg-type] # noqa
|
|
165
|
+
check=True,
|
|
166
|
+
capture_output=True,
|
|
167
|
+
)
|
|
168
|
+
except subprocess.CalledProcessError as e:
|
|
169
|
+
raise RuntimeError(f"Error when stripping symbols.\n{e}") from e
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
def build() -> None:
|
|
173
|
+
"""
|
|
174
|
+
Construct the extensions and distribution.
|
|
175
|
+
"""
|
|
176
|
+
# _build_rust_libs()
|
|
177
|
+
# _copy_rust_dylibs_to_project()
|
|
178
|
+
|
|
179
|
+
if True: #not PYO3_ONLY:
|
|
180
|
+
# Create C Extensions to feed into cythonize()
|
|
181
|
+
extensions = _build_extensions()
|
|
182
|
+
distribution = _build_distribution(extensions)
|
|
183
|
+
|
|
184
|
+
# Build and run the command
|
|
185
|
+
print("Compiling C extension modules...")
|
|
186
|
+
cmd: build_ext = build_ext(distribution)
|
|
187
|
+
# if PARALLEL_BUILD:
|
|
188
|
+
# cmd.parallel = os.cpu_count()
|
|
189
|
+
cmd.ensure_finalized()
|
|
190
|
+
cmd.run()
|
|
191
|
+
|
|
192
|
+
if COPY_TO_SOURCE:
|
|
193
|
+
# Copy the build back into the source tree for development and wheel packaging
|
|
194
|
+
_copy_build_dir_to_project(cmd)
|
|
195
|
+
|
|
196
|
+
if BUILD_MODE == "release" and platform.system() in ("Linux", "Darwin"):
|
|
197
|
+
# Only strip symbols for release builds
|
|
198
|
+
_strip_unneeded_symbols()
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
if __name__ == "__main__":
|
|
202
|
+
qubx_platform = toml.load("pyproject.toml")["tool"]["poetry"]["version"]
|
|
203
|
+
print("\033[36m")
|
|
204
|
+
print("=====================================================================")
|
|
205
|
+
print(f"Qubx Builder {qubx_platform}")
|
|
206
|
+
print("=====================================================================\033[0m")
|
|
207
|
+
print(f"System: {platform.system()} {platform.machine()}")
|
|
208
|
+
# print(f"Clang: {_get_clang_version()}")
|
|
209
|
+
# print(f"Rust: {_get_rustc_version()}")
|
|
210
|
+
print(f"Python: {platform.python_version()}")
|
|
211
|
+
print(f"Cython: {cython_compiler_version}")
|
|
212
|
+
print(f"NumPy: {np.__version__}\n")
|
|
213
|
+
|
|
214
|
+
print(f"BUILD_MODE={BUILD_MODE}")
|
|
215
|
+
print(f"BUILD_DIR={BUILD_DIR}")
|
|
216
|
+
print(f"PROFILE_MODE={PROFILE_MODE}")
|
|
217
|
+
print(f"ANNOTATION_MODE={ANNOTATION_MODE}")
|
|
218
|
+
# print(f"PARALLEL_BUILD={PARALLEL_BUILD}")
|
|
219
|
+
print(f"COPY_TO_SOURCE={COPY_TO_SOURCE}")
|
|
220
|
+
# print(f"PYO3_ONLY={PYO3_ONLY}\n")
|
|
221
|
+
|
|
222
|
+
print("Starting build...")
|
|
223
|
+
ts_start = datetime.datetime.now(datetime.timezone.utc)
|
|
224
|
+
build()
|
|
225
|
+
print(f"Build time: {datetime.datetime.now(datetime.timezone.utc) - ts_start}")
|
|
226
|
+
print("\033[32m" + "Build completed" + "\033[0m")
|
|
227
|
+
|
|
228
|
+
# # See if Cython is installed
|
|
229
|
+
# try:
|
|
230
|
+
# from Cython.Build import cythonize
|
|
231
|
+
# # Do nothing if Cython is not available
|
|
232
|
+
# except ImportError:
|
|
233
|
+
# # Got to provide this function. Otherwise, poetry will fail
|
|
234
|
+
# def build(setup_kwargs):
|
|
235
|
+
# pass
|
|
236
|
+
|
|
237
|
+
# # Cython is installed. Compile
|
|
238
|
+
# else:
|
|
239
|
+
# from setuptools import Extension
|
|
240
|
+
# from setuptools.dist import Distribution
|
|
241
|
+
# from distutils.command.build_ext import build_ext
|
|
242
|
+
|
|
243
|
+
# # This function will be executed in setup.py:
|
|
244
|
+
# def build(setup_kwargs):
|
|
245
|
+
# # The file you want to compile
|
|
246
|
+
# extensions = [
|
|
247
|
+
# "src/qubx/core/series.pyx",
|
|
248
|
+
# "src/qubx/core/utils.pyx",
|
|
249
|
+
# "src/qubx/ta/indicators.pyx",
|
|
250
|
+
# ]
|
|
251
|
+
|
|
252
|
+
# # gcc arguments hack: enable optimizations
|
|
253
|
+
# os.environ['CFLAGS'] = '-O3'
|
|
254
|
+
|
|
255
|
+
# # Build
|
|
256
|
+
# import numpy as np
|
|
257
|
+
# setup_kwargs.update({
|
|
258
|
+
# 'ext_modules': cythonize(
|
|
259
|
+
# extensions,
|
|
260
|
+
# language_level=3,
|
|
261
|
+
# compiler_directives={'linetrace': True},
|
|
262
|
+
# include_path=[np.get_include()]
|
|
263
|
+
# ),
|
|
264
|
+
# 'cmdclass': {'build_ext': build_ext}
|
|
265
|
+
# })
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
[tool.poetry]
|
|
2
|
+
name = "Qubx"
|
|
3
|
+
version = "0.0.1"
|
|
4
|
+
description = "Qubx - quantitative trading framework"
|
|
5
|
+
authors = ["Dmitry Marienko <dmitry@gmail.com>"]
|
|
6
|
+
readme = "README.md"
|
|
7
|
+
packages = [
|
|
8
|
+
{ include = "qubx", from = "src" },
|
|
9
|
+
]
|
|
10
|
+
repository = "https://github.com/dmarienko/Qubx"
|
|
11
|
+
include = [
|
|
12
|
+
# Compiled extensions must be included in the wheel distributions
|
|
13
|
+
{ path = "src/**/*.so", format = "wheel" },
|
|
14
|
+
{ path = "src/**/*.pyd", format = "wheel" },
|
|
15
|
+
]
|
|
16
|
+
|
|
17
|
+
[tool.poetry.dependencies]
|
|
18
|
+
python = ">=3.9,<4.0"
|
|
19
|
+
pytest = {extras = ["lazyfixture"], version = "^7.2.0"}
|
|
20
|
+
numpy = "^1.26.3"
|
|
21
|
+
ntplib = "^0.4.0"
|
|
22
|
+
loguru = "^0.7.2"
|
|
23
|
+
stackprinter = "^0.2.10"
|
|
24
|
+
#websocket-client = "^1.6.3"
|
|
25
|
+
#websockets = "^11.0.3"
|
|
26
|
+
pymongo = "^4.6.1"
|
|
27
|
+
pydantic = "^1.10.2"
|
|
28
|
+
python-dotenv = "^1.0.0"
|
|
29
|
+
python-binance = "^1.0.19"
|
|
30
|
+
pyarrow = "^15.0.0"
|
|
31
|
+
scipy = "^1.12.0"
|
|
32
|
+
cython = "3.0.8"
|
|
33
|
+
|
|
34
|
+
[tool.poetry.group.dev.dependencies]
|
|
35
|
+
pre-commit = "^2.20.0"
|
|
36
|
+
pytest = "^7.1.3"
|
|
37
|
+
|
|
38
|
+
#[project.optional-dependencies]
|
|
39
|
+
#numba = "^0.57.1"
|
|
40
|
+
|
|
41
|
+
[build-system]
|
|
42
|
+
requires = ["poetry-core", "setuptools", "numpy>=1.26.3", "cython==3.0.8", "toml>=0.10.2",]
|
|
43
|
+
build-backend = "poetry.core.masonry.api"
|
|
44
|
+
|
|
45
|
+
[tool.poetry.build]
|
|
46
|
+
script = "build.py"
|
|
47
|
+
generate-setup-file = false
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
[tool.poetry.group.test.dependencies]
|
|
51
|
+
pytest = "^7.1.3"
|
|
52
|
+
pytest-mock = "*"
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
[tool.pytest.ini_options]
|
|
56
|
+
pythonpath = ["src"]
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
from qubx.utils import set_mpl_theme, runtime_env, install_pyx_recompiler_for_dev
|
|
2
|
+
install_pyx_recompiler_for_dev()
|
|
3
|
+
|
|
4
|
+
from loguru import logger
|
|
5
|
+
import os, sys, stackprinter
|
|
6
|
+
from qubx.core.lookups import InstrumentsLookup
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def formatter(record):
|
|
10
|
+
end = record["extra"].get("end", "\n")
|
|
11
|
+
fmt = "<lvl>{message}</lvl>%s" % end
|
|
12
|
+
if record["level"].name in {"WARNING", "SNAKY"}:
|
|
13
|
+
fmt = "<cyan>{name}</cyan>:<cyan>{function}</cyan>:<cyan>{line}</cyan> - %s" % fmt
|
|
14
|
+
|
|
15
|
+
prefix = "<green>{time:YYYY-MM-DD HH:mm:ss.SSS}</green> [ <level>%s</level> ] " % record["level"].icon
|
|
16
|
+
|
|
17
|
+
if record["exception"] is not None:
|
|
18
|
+
# stackprinter.set_excepthook(style='darkbg2')
|
|
19
|
+
record["extra"]["stack"] = stackprinter.format(record["exception"], style="darkbg")
|
|
20
|
+
fmt += "\n{extra[stack]}\n"
|
|
21
|
+
|
|
22
|
+
if record["level"].name in {"TEXT"}:
|
|
23
|
+
prefix = ""
|
|
24
|
+
|
|
25
|
+
return prefix + fmt
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
config = {
|
|
29
|
+
"handlers": [ {"sink": sys.stdout, "format": "{time} - {message}"}, ],
|
|
30
|
+
"extra": {"user": "someone"},
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
logger.configure(**config)
|
|
35
|
+
logger.remove(None)
|
|
36
|
+
logger.add(sys.stdout, format=formatter, colorize=True)
|
|
37
|
+
logger = logger.opt(colors=True)
|
|
38
|
+
|
|
39
|
+
lookup = InstrumentsLookup()
|
|
40
|
+
|
|
41
|
+
# registering magic for jupyter notebook
|
|
42
|
+
if runtime_env() in ['notebook', 'shell']:
|
|
43
|
+
from IPython.core.magic import (Magics, magics_class, line_magic, line_cell_magic)
|
|
44
|
+
from IPython import get_ipython
|
|
45
|
+
|
|
46
|
+
@magics_class
|
|
47
|
+
class QubxMagics(Magics):
|
|
48
|
+
# process data manager
|
|
49
|
+
__manager = None
|
|
50
|
+
|
|
51
|
+
@line_magic
|
|
52
|
+
def qubxd(self, line: str):
|
|
53
|
+
self.qubx_setup('dark')
|
|
54
|
+
|
|
55
|
+
@line_magic
|
|
56
|
+
def qubxl(self, line: str):
|
|
57
|
+
self.qubx_setup('light')
|
|
58
|
+
|
|
59
|
+
@line_magic
|
|
60
|
+
def qubx_setup(self, line: str):
|
|
61
|
+
"""
|
|
62
|
+
QUBE framework initialization
|
|
63
|
+
"""
|
|
64
|
+
import os
|
|
65
|
+
|
|
66
|
+
tpl_path = os.path.join(os.path.dirname(__file__), "_nb_magic.py")
|
|
67
|
+
# print("TPL:", tpl_path)
|
|
68
|
+
with open(tpl_path, 'r', encoding="utf8") as myfile:
|
|
69
|
+
s = myfile.read()
|
|
70
|
+
|
|
71
|
+
exec(s, self.shell.user_ns)
|
|
72
|
+
|
|
73
|
+
# setup more funcy mpl theme instead of ugly default
|
|
74
|
+
if line:
|
|
75
|
+
if 'dark' in line.lower():
|
|
76
|
+
set_mpl_theme('dark')
|
|
77
|
+
|
|
78
|
+
elif 'light' in line.lower():
|
|
79
|
+
set_mpl_theme('light')
|
|
80
|
+
|
|
81
|
+
# install additional plotly helpers
|
|
82
|
+
# from qube.charting.plot_helpers import install_plotly_helpers
|
|
83
|
+
# install_plotly_helpers()
|
|
84
|
+
|
|
85
|
+
def _get_manager(self):
|
|
86
|
+
if self.__manager is None:
|
|
87
|
+
import multiprocessing as m
|
|
88
|
+
self.__manager = m.Manager()
|
|
89
|
+
return self.__manager
|
|
90
|
+
|
|
91
|
+
@line_cell_magic
|
|
92
|
+
def proc(self, line, cell=None):
|
|
93
|
+
"""
|
|
94
|
+
Run cell in separate process
|
|
95
|
+
|
|
96
|
+
>>> %%proc x, y as MyProc1
|
|
97
|
+
>>> x.set('Hello')
|
|
98
|
+
>>> y.set([1,2,3,4])
|
|
99
|
+
|
|
100
|
+
"""
|
|
101
|
+
import multiprocessing as m
|
|
102
|
+
import time, re
|
|
103
|
+
|
|
104
|
+
# create ext args
|
|
105
|
+
name = None
|
|
106
|
+
if line:
|
|
107
|
+
# check if custom process name was provided
|
|
108
|
+
if ' as ' in line:
|
|
109
|
+
line, name = line.split('as')
|
|
110
|
+
if not name.isspace():
|
|
111
|
+
name = name.strip()
|
|
112
|
+
else:
|
|
113
|
+
print('>>> Process name must be specified afer "as" keyword !')
|
|
114
|
+
return
|
|
115
|
+
|
|
116
|
+
ipy = get_ipython()
|
|
117
|
+
for a in [x for x in re.split('[\ ,;]', line.strip()) if x]:
|
|
118
|
+
ipy.push({a: self._get_manager().Value(None, None)})
|
|
119
|
+
|
|
120
|
+
# code to run
|
|
121
|
+
lines = '\n'.join([' %s' % x for x in cell.split('\n')])
|
|
122
|
+
|
|
123
|
+
def fn():
|
|
124
|
+
result = get_ipython().run_cell(lines)
|
|
125
|
+
|
|
126
|
+
# send errors to parent
|
|
127
|
+
if result.error_before_exec:
|
|
128
|
+
raise result.error_before_exec
|
|
129
|
+
|
|
130
|
+
if result.error_in_exec:
|
|
131
|
+
raise result.error_in_exec
|
|
132
|
+
|
|
133
|
+
t_start = str(time.time()).replace('.', '_')
|
|
134
|
+
f_id = f'proc_{t_start}' if name is None else name
|
|
135
|
+
if self._is_task_name_already_used(f_id):
|
|
136
|
+
f_id = f"{f_id}_{t_start}"
|
|
137
|
+
|
|
138
|
+
task = m.Process(target=fn, name=f_id)
|
|
139
|
+
task.start()
|
|
140
|
+
print(' -> Task %s is started' % f_id)
|
|
141
|
+
|
|
142
|
+
def _is_task_name_already_used(self, name):
|
|
143
|
+
import multiprocessing as m
|
|
144
|
+
for p in m.active_children():
|
|
145
|
+
if p.name == name:
|
|
146
|
+
return True
|
|
147
|
+
return False
|
|
148
|
+
|
|
149
|
+
@line_magic
|
|
150
|
+
def list_proc(self, line):
|
|
151
|
+
import multiprocessing as m
|
|
152
|
+
for p in m.active_children():
|
|
153
|
+
print(p.name)
|
|
154
|
+
|
|
155
|
+
@line_magic
|
|
156
|
+
def kill_proc(self, line):
|
|
157
|
+
import multiprocessing as m
|
|
158
|
+
for p in m.active_children():
|
|
159
|
+
if line and p.name.startswith(line):
|
|
160
|
+
p.terminate()
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
# - registering magic here
|
|
164
|
+
get_ipython().register_magics(QubxMagics)
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
""""
|
|
2
|
+
Here stuff we want to have in every Jupyter notebook after calling %qube magic
|
|
3
|
+
"""
|
|
4
|
+
import importlib_metadata
|
|
5
|
+
|
|
6
|
+
import qubx
|
|
7
|
+
from qubx.utils import runtime_env
|
|
8
|
+
from qubx.utils.misc import add_project_to_system_path
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def np_fmt_short():
|
|
12
|
+
# default np output is 75 columns so extend it a bit and suppress scientific fmt for small floats
|
|
13
|
+
np.set_printoptions(linewidth=240, suppress=True)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def np_fmt_reset():
|
|
17
|
+
# reset default np printing options
|
|
18
|
+
np.set_printoptions(edgeitems=3, infstr='inf', linewidth=75, nanstr='nan', precision=8,
|
|
19
|
+
suppress=False, threshold=1000, formatter=None)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
if runtime_env() in ['notebook', 'shell']:
|
|
23
|
+
|
|
24
|
+
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
25
|
+
# -- all imports below will appear in notebook after calling %%alphalab magic ---
|
|
26
|
+
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
27
|
+
|
|
28
|
+
# - - - - Common stuff - - - -
|
|
29
|
+
import numpy as np
|
|
30
|
+
import pandas as pd
|
|
31
|
+
from datetime import time, timedelta
|
|
32
|
+
from tqdm.auto import tqdm
|
|
33
|
+
|
|
34
|
+
# - - - - TA stuff and indicators - - - -
|
|
35
|
+
# - - - - Portfolio analysis - - - -
|
|
36
|
+
# - - - - Simulator stuff - - - -
|
|
37
|
+
# - - - - Learn stuff - - - -
|
|
38
|
+
# - - - - Charting stuff - - - -
|
|
39
|
+
from matplotlib import pyplot as plt
|
|
40
|
+
from qubx.utils.charting.mpl_helpers import fig, subplot, sbp
|
|
41
|
+
# - - - - Utils - - - -
|
|
42
|
+
|
|
43
|
+
# - setup short numpy output format
|
|
44
|
+
np_fmt_short()
|
|
45
|
+
|
|
46
|
+
# - add project home to system path
|
|
47
|
+
add_project_to_system_path()
|
|
48
|
+
|
|
49
|
+
# - check current version
|
|
50
|
+
try:
|
|
51
|
+
version = importlib_metadata.version('qube2')
|
|
52
|
+
except:
|
|
53
|
+
version = 'Dev'
|
|
54
|
+
|
|
55
|
+
# some new logo
|
|
56
|
+
if not hasattr(qubx.QubxMagics, '__already_initialized__'):
|
|
57
|
+
from qubx.utils.misc import (green, yellow, cyan, magenta, white, blue, red)
|
|
58
|
+
|
|
59
|
+
print(
|
|
60
|
+
f"""
|
|
61
|
+
{red("╻")}
|
|
62
|
+
{green("┏┓ ╻ ")} {red("┃")} {yellow("┏┓")} {cyan("Quantitative Backtesting Environment")}
|
|
63
|
+
{green("┃┃ ┓┏ ┣┓ ┏┓")} {red("┃")} {yellow("┏┛")}
|
|
64
|
+
{green("┗┻ ┗┻ ┗┛ ┗ ")} {red("┃")} {yellow("┗━")} (c) 2024, ver. {magenta(version.rstrip())}
|
|
65
|
+
{red("╹")}
|
|
66
|
+
"""
|
|
67
|
+
)
|
|
68
|
+
qubx.QubxMagics.__already_initialized__ = True
|
|
69
|
+
|
|
File without changes
|