Qubx 0.1.83__tar.gz → 0.1.85__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.1.83 → qubx-0.1.85}/PKG-INFO +7 -3
- {qubx-0.1.83 → qubx-0.1.85}/README.md +6 -2
- {qubx-0.1.83 → qubx-0.1.85}/build.py +24 -16
- {qubx-0.1.83 → qubx-0.1.85}/pyproject.toml +2 -2
- {qubx-0.1.83 → qubx-0.1.85}/src/qubx/__init__.py +62 -32
- {qubx-0.1.83 → qubx-0.1.85}/src/qubx/_nb_magic.py +14 -8
- {qubx-0.1.83 → qubx-0.1.85}/src/qubx/core/series.pxd +7 -3
- {qubx-0.1.83 → qubx-0.1.85}/src/qubx/core/series.pyx +18 -1
- {qubx-0.1.83 → qubx-0.1.85}/src/qubx/core/utils.pyx +1 -1
- {qubx-0.1.83 → qubx-0.1.85}/src/qubx/data/readers.py +71 -60
- qubx-0.1.85/src/qubx/math/__init__.py +1 -0
- {qubx-0.1.83 → qubx-0.1.85}/src/qubx/math/stats.py +21 -4
- {qubx-0.1.83 → qubx-0.1.85}/src/qubx/pandaz/ta.py +438 -376
- qubx-0.1.85/src/qubx/ta/indicators.pyx +680 -0
- {qubx-0.1.83 → qubx-0.1.85}/src/qubx/utils/charting/mpl_helpers.py +304 -243
- {qubx-0.1.83 → qubx-0.1.85}/src/qubx/utils/misc.py +70 -60
- qubx-0.1.83/src/qubx/math/__init__.py +0 -1
- qubx-0.1.83/src/qubx/ta/indicators.pyx +0 -258
- {qubx-0.1.83 → qubx-0.1.85}/src/qubx/core/__init__.py +0 -0
- {qubx-0.1.83 → qubx-0.1.85}/src/qubx/core/account.py +0 -0
- {qubx-0.1.83 → qubx-0.1.85}/src/qubx/core/basics.py +0 -0
- {qubx-0.1.83 → qubx-0.1.85}/src/qubx/core/helpers.py +0 -0
- {qubx-0.1.83 → qubx-0.1.85}/src/qubx/core/loggers.py +0 -0
- {qubx-0.1.83 → qubx-0.1.85}/src/qubx/core/lookups.py +0 -0
- {qubx-0.1.83 → qubx-0.1.85}/src/qubx/core/strategy.py +0 -0
- {qubx-0.1.83 → qubx-0.1.85}/src/qubx/impl/ccxt_connector.py +0 -0
- {qubx-0.1.83 → qubx-0.1.85}/src/qubx/impl/ccxt_customizations.py +0 -0
- {qubx-0.1.83 → qubx-0.1.85}/src/qubx/impl/ccxt_trading.py +0 -0
- {qubx-0.1.83 → qubx-0.1.85}/src/qubx/impl/ccxt_utils.py +0 -0
- {qubx-0.1.83 → qubx-0.1.85}/src/qubx/pandaz/__init__.py +0 -0
- {qubx-0.1.83 → qubx-0.1.85}/src/qubx/pandaz/utils.py +0 -0
- {qubx-0.1.83 → qubx-0.1.85}/src/qubx/ta/__init__.py +0 -0
- {qubx-0.1.83 → qubx-0.1.85}/src/qubx/trackers/__init__.py +0 -0
- {qubx-0.1.83 → qubx-0.1.85}/src/qubx/trackers/rebalancers.py +0 -0
- {qubx-0.1.83 → qubx-0.1.85}/src/qubx/utils/__init__.py +0 -0
- {qubx-0.1.83 → qubx-0.1.85}/src/qubx/utils/_pyxreloader.py +0 -0
- {qubx-0.1.83 → qubx-0.1.85}/src/qubx/utils/marketdata/binance.py +0 -0
- {qubx-0.1.83 → qubx-0.1.85}/src/qubx/utils/runner.py +0 -0
- {qubx-0.1.83 → qubx-0.1.85}/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.
|
|
3
|
+
Version: 0.1.85
|
|
4
4
|
Summary: Qubx - quantitative trading framework
|
|
5
5
|
Home-page: https://github.com/dmarienko/Qubx
|
|
6
6
|
Author: Dmitry Marienko
|
|
@@ -48,7 +48,11 @@ Description-Content-Type: text/markdown
|
|
|
48
48
|
⠀⠀⠱⣜⣀⣀⣈⣦⠃⠀⠀⠀
|
|
49
49
|
|
|
50
50
|
```
|
|
51
|
-
|
|
51
|
+
|
|
52
|
+
## Installation
|
|
53
|
+
> pip install qubx
|
|
54
|
+
|
|
55
|
+
## How to run live trading (Only Binance spot tested)
|
|
52
56
|
1. cd experiments/
|
|
53
57
|
2. Edit strategy config file (zero_test.yaml). Testing strategy is just doing flip / flop trading once per minute (trading_allowed should be set for trading)
|
|
54
58
|
3. Modify accounts config file under ./configs/.env and provide your API binance credentials (see example in example-accounts.cfg):
|
|
@@ -61,6 +65,6 @@ base_currency = USDT
|
|
|
61
65
|
4. Run in console (-j key if want to run under jupyter console)
|
|
62
66
|
|
|
63
67
|
```
|
|
64
|
-
> python
|
|
68
|
+
> python ..\src\qubx\utils\runner.py configs\zero_test.yaml -a configs\.env -j
|
|
65
69
|
```
|
|
66
70
|
|
|
@@ -8,7 +8,11 @@
|
|
|
8
8
|
⠀⠀⠱⣜⣀⣀⣈⣦⠃⠀⠀⠀
|
|
9
9
|
|
|
10
10
|
```
|
|
11
|
-
|
|
11
|
+
|
|
12
|
+
## Installation
|
|
13
|
+
> pip install qubx
|
|
14
|
+
|
|
15
|
+
## How to run live trading (Only Binance spot tested)
|
|
12
16
|
1. cd experiments/
|
|
13
17
|
2. Edit strategy config file (zero_test.yaml). Testing strategy is just doing flip / flop trading once per minute (trading_allowed should be set for trading)
|
|
14
18
|
3. Modify accounts config file under ./configs/.env and provide your API binance credentials (see example in example-accounts.cfg):
|
|
@@ -21,5 +25,5 @@ base_currency = USDT
|
|
|
21
25
|
4. Run in console (-j key if want to run under jupyter console)
|
|
22
26
|
|
|
23
27
|
```
|
|
24
|
-
> python
|
|
28
|
+
> python ..\src\qubx\utils\runner.py configs\zero_test.yaml -a configs\.env -j
|
|
25
29
|
```
|
|
@@ -103,12 +103,13 @@ def _build_extensions() -> list[Extension]:
|
|
|
103
103
|
Extension(
|
|
104
104
|
name=str(pyx.relative_to(".")).replace(os.path.sep, ".")[:-4],
|
|
105
105
|
sources=[str(pyx)],
|
|
106
|
-
include_dirs=[np.get_include()],
|
|
106
|
+
include_dirs=[np.get_include()], # , *RUST_INCLUDES],
|
|
107
107
|
define_macros=define_macros,
|
|
108
108
|
language="c",
|
|
109
109
|
extra_link_args=extra_link_args,
|
|
110
110
|
extra_compile_args=extra_compile_args,
|
|
111
|
-
)
|
|
111
|
+
)
|
|
112
|
+
for pyx in itertools.chain(Path("src/qubx").rglob("*.pyx"))
|
|
112
113
|
]
|
|
113
114
|
|
|
114
115
|
|
|
@@ -159,7 +160,9 @@ def _strip_unneeded_symbols() -> None:
|
|
|
159
160
|
elif platform.system() == "Darwin":
|
|
160
161
|
strip_cmd = ["strip", "-x", so]
|
|
161
162
|
else:
|
|
162
|
-
raise RuntimeError(
|
|
163
|
+
raise RuntimeError(
|
|
164
|
+
f"Cannot strip symbols for platform {platform.system()}"
|
|
165
|
+
)
|
|
163
166
|
subprocess.run(
|
|
164
167
|
strip_cmd, # type: ignore [arg-type] # noqa
|
|
165
168
|
check=True,
|
|
@@ -176,7 +179,7 @@ def build() -> None:
|
|
|
176
179
|
# _build_rust_libs()
|
|
177
180
|
# _copy_rust_dylibs_to_project()
|
|
178
181
|
|
|
179
|
-
if True:
|
|
182
|
+
if True: # not PYO3_ONLY:
|
|
180
183
|
# Create C Extensions to feed into cythonize()
|
|
181
184
|
extensions = _build_extensions()
|
|
182
185
|
distribution = _build_distribution(extensions)
|
|
@@ -185,7 +188,7 @@ def build() -> None:
|
|
|
185
188
|
print("Compiling C extension modules...")
|
|
186
189
|
cmd: build_ext = build_ext(distribution)
|
|
187
190
|
# if PARALLEL_BUILD:
|
|
188
|
-
|
|
191
|
+
# cmd.parallel = os.cpu_count()
|
|
189
192
|
cmd.ensure_finalized()
|
|
190
193
|
cmd.run()
|
|
191
194
|
|
|
@@ -198,18 +201,21 @@ def build() -> None:
|
|
|
198
201
|
_strip_unneeded_symbols()
|
|
199
202
|
|
|
200
203
|
|
|
204
|
+
RED, BLUE, GREEN, YLW, RES = "\033[31m", "\033[36m", "\033[32m", "\033[33m", "\033[0m"
|
|
201
205
|
if __name__ == "__main__":
|
|
202
206
|
qubx_platform = toml.load("pyproject.toml")["tool"]["poetry"]["version"]
|
|
203
|
-
print(
|
|
207
|
+
print(BLUE)
|
|
204
208
|
print("=====================================================================")
|
|
205
209
|
print(f"Qubx Builder {qubx_platform}")
|
|
206
|
-
print(
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
print(f"
|
|
211
|
-
print(f"
|
|
212
|
-
print(f"
|
|
210
|
+
print(
|
|
211
|
+
"=====================================================================\033[0m"
|
|
212
|
+
)
|
|
213
|
+
print(f"System: {GREEN}{platform.system()} {platform.machine()}{RES}")
|
|
214
|
+
# print(f"Clang: {GREEN}{_get_clang_version()}{RES}")
|
|
215
|
+
# print(f"Rust: {GREEN}{_get_rustc_version()}{RES}")
|
|
216
|
+
print(f"Python: {GREEN}{platform.python_version()}{RES}")
|
|
217
|
+
print(f"Cython: {GREEN}{cython_compiler_version}{RES}")
|
|
218
|
+
print(f"NumPy: {GREEN}{np.__version__}{RES}\n")
|
|
213
219
|
|
|
214
220
|
print(f"BUILD_MODE={BUILD_MODE}")
|
|
215
221
|
print(f"BUILD_DIR={BUILD_DIR}")
|
|
@@ -222,8 +228,10 @@ if __name__ == "__main__":
|
|
|
222
228
|
print("Starting build...")
|
|
223
229
|
ts_start = datetime.datetime.now(datetime.timezone.utc)
|
|
224
230
|
build()
|
|
225
|
-
print(
|
|
226
|
-
|
|
231
|
+
print(
|
|
232
|
+
f"Build time: {YLW}{datetime.datetime.now(datetime.timezone.utc) - ts_start}{RES}"
|
|
233
|
+
)
|
|
234
|
+
print(GREEN + "Build completed" + RES)
|
|
227
235
|
|
|
228
236
|
# # See if Cython is installed
|
|
229
237
|
# try:
|
|
@@ -262,4 +270,4 @@ if __name__ == "__main__":
|
|
|
262
270
|
# include_path=[np.get_include()]
|
|
263
271
|
# ),
|
|
264
272
|
# 'cmdclass': {'build_ext': build_ext}
|
|
265
|
-
# })
|
|
273
|
+
# })
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "Qubx"
|
|
3
|
-
version = "0.1.
|
|
3
|
+
version = "0.1.85"
|
|
4
4
|
description = "Qubx - quantitative trading framework"
|
|
5
5
|
authors = ["Dmitry Marienko <dmitry@gmail.com>"]
|
|
6
6
|
readme = "README.md"
|
|
@@ -52,7 +52,7 @@ pytest = "^7.1.3"
|
|
|
52
52
|
ipykernel = "^6.29.4"
|
|
53
53
|
|
|
54
54
|
[build-system]
|
|
55
|
-
requires = ["poetry-core", "setuptools", "numpy>=1.26.3", "cython==3.0.8", "toml>=0.10.2"
|
|
55
|
+
requires = ["poetry-core", "setuptools", "numpy>=1.26.3", "cython==3.0.8", "toml>=0.10.2"]
|
|
56
56
|
build-backend = "poetry.core.masonry.api"
|
|
57
57
|
|
|
58
58
|
[tool.poetry.build]
|
|
@@ -10,13 +10,20 @@ def formatter(record):
|
|
|
10
10
|
end = record["extra"].get("end", "\n")
|
|
11
11
|
fmt = "<lvl>{message}</lvl>%s" % end
|
|
12
12
|
if record["level"].name in {"WARNING", "SNAKY"}:
|
|
13
|
-
fmt =
|
|
13
|
+
fmt = (
|
|
14
|
+
"<cyan>{name}</cyan>:<cyan>{function}</cyan>:<cyan>{line}</cyan> - %s" % fmt
|
|
15
|
+
)
|
|
14
16
|
|
|
15
|
-
prefix =
|
|
17
|
+
prefix = (
|
|
18
|
+
"<green>{time:YYYY-MM-DD HH:mm:ss.SSS}</green> [ <level>%s</level> ] "
|
|
19
|
+
% record["level"].icon
|
|
20
|
+
)
|
|
16
21
|
|
|
17
22
|
if record["exception"] is not None:
|
|
18
23
|
# stackprinter.set_excepthook(style='darkbg2')
|
|
19
|
-
record["extra"]["stack"] = stackprinter.format(
|
|
24
|
+
record["extra"]["stack"] = stackprinter.format(
|
|
25
|
+
record["exception"], style="darkbg"
|
|
26
|
+
)
|
|
20
27
|
fmt += "\n{extra[stack]}\n"
|
|
21
28
|
|
|
22
29
|
if record["level"].name in {"TEXT"}:
|
|
@@ -25,24 +32,43 @@ def formatter(record):
|
|
|
25
32
|
return prefix + fmt
|
|
26
33
|
|
|
27
34
|
|
|
28
|
-
|
|
29
|
-
"handlers": [ {"sink": sys.stdout, "format": "{time} - {message}"}, ],
|
|
30
|
-
"extra": {"user": "someone"},
|
|
31
|
-
}
|
|
35
|
+
class QubxLogConfig:
|
|
32
36
|
|
|
37
|
+
@staticmethod
|
|
38
|
+
def get_log_level():
|
|
39
|
+
return os.getenv("QUBX_LOG_LEVEL", "DEBUG")
|
|
40
|
+
|
|
41
|
+
@staticmethod
|
|
42
|
+
def set_log_level(level: str):
|
|
43
|
+
os.environ["QUBX_LOG_LEVEL"] = level
|
|
44
|
+
QubxLogConfig.setup_logger(level)
|
|
45
|
+
|
|
46
|
+
@staticmethod
|
|
47
|
+
def setup_logger(level: str | None = None):
|
|
48
|
+
global logger
|
|
49
|
+
config = {
|
|
50
|
+
"handlers": [
|
|
51
|
+
{"sink": sys.stdout, "format": "{time} - {message}"},
|
|
52
|
+
],
|
|
53
|
+
"extra": {"user": "someone"},
|
|
54
|
+
}
|
|
55
|
+
logger.configure(**config)
|
|
56
|
+
logger.remove(None)
|
|
57
|
+
level = level or QubxLogConfig.get_log_level()
|
|
58
|
+
logger.add(sys.stdout, format=formatter, colorize=True, level=level)
|
|
59
|
+
logger = logger.opt(colors=True)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
QubxLogConfig.setup_logger()
|
|
33
63
|
|
|
34
|
-
logger.configure(**config)
|
|
35
|
-
logger.remove(None)
|
|
36
|
-
logger.add(sys.stdout, format=formatter, colorize=True)
|
|
37
|
-
logger = logger.opt(colors=True)
|
|
38
64
|
|
|
39
65
|
# - global lookup helper
|
|
40
66
|
lookup = GlobalLookup(InstrumentsLookup(), FeesLookup())
|
|
41
67
|
|
|
42
68
|
|
|
43
69
|
# registering magic for jupyter notebook
|
|
44
|
-
if runtime_env() in [
|
|
45
|
-
from IPython.core.magic import
|
|
70
|
+
if runtime_env() in ["notebook", "shell"]:
|
|
71
|
+
from IPython.core.magic import Magics, magics_class, line_magic, line_cell_magic
|
|
46
72
|
from IPython import get_ipython
|
|
47
73
|
|
|
48
74
|
@magics_class
|
|
@@ -52,11 +78,11 @@ if runtime_env() in ['notebook', 'shell']:
|
|
|
52
78
|
|
|
53
79
|
@line_magic
|
|
54
80
|
def qubxd(self, line: str):
|
|
55
|
-
self.qubx_setup(
|
|
81
|
+
self.qubx_setup("dark" + " " + line)
|
|
56
82
|
|
|
57
83
|
@line_magic
|
|
58
84
|
def qubxl(self, line: str):
|
|
59
|
-
self.qubx_setup(
|
|
85
|
+
self.qubx_setup("light" + " " + line)
|
|
60
86
|
|
|
61
87
|
@line_magic
|
|
62
88
|
def qubx_setup(self, line: str):
|
|
@@ -64,25 +90,26 @@ if runtime_env() in ['notebook', 'shell']:
|
|
|
64
90
|
QUBX framework initialization
|
|
65
91
|
"""
|
|
66
92
|
import os
|
|
67
|
-
|
|
68
|
-
|
|
93
|
+
|
|
94
|
+
args = [x.strip() for x in line.split(" ")]
|
|
95
|
+
|
|
69
96
|
# setup cython dev hooks - only if 'dev' is passed as argument
|
|
70
|
-
if line and
|
|
97
|
+
if line and "dev" in args:
|
|
71
98
|
install_pyx_recompiler_for_dev()
|
|
72
99
|
|
|
73
100
|
tpl_path = os.path.join(os.path.dirname(__file__), "_nb_magic.py")
|
|
74
|
-
with open(tpl_path,
|
|
101
|
+
with open(tpl_path, "r", encoding="utf8") as myfile:
|
|
75
102
|
s = myfile.read()
|
|
76
103
|
|
|
77
104
|
exec(s, self.shell.user_ns)
|
|
78
105
|
|
|
79
106
|
# setup more funcy mpl theme instead of ugly default
|
|
80
107
|
if line:
|
|
81
|
-
if
|
|
82
|
-
set_mpl_theme(
|
|
108
|
+
if "dark" in line.lower():
|
|
109
|
+
set_mpl_theme("dark")
|
|
83
110
|
|
|
84
|
-
elif
|
|
85
|
-
set_mpl_theme(
|
|
111
|
+
elif "light" in line.lower():
|
|
112
|
+
set_mpl_theme("light")
|
|
86
113
|
|
|
87
114
|
# install additional plotly helpers
|
|
88
115
|
# from qube.charting.plot_helpers import install_plotly_helpers
|
|
@@ -91,6 +118,7 @@ if runtime_env() in ['notebook', 'shell']:
|
|
|
91
118
|
def _get_manager(self):
|
|
92
119
|
if self.__manager is None:
|
|
93
120
|
import multiprocessing as m
|
|
121
|
+
|
|
94
122
|
self.__manager = m.Manager()
|
|
95
123
|
return self.__manager
|
|
96
124
|
|
|
@@ -102,7 +130,7 @@ if runtime_env() in ['notebook', 'shell']:
|
|
|
102
130
|
>>> %%proc x, y as MyProc1
|
|
103
131
|
>>> x.set('Hello')
|
|
104
132
|
>>> y.set([1,2,3,4])
|
|
105
|
-
|
|
133
|
+
|
|
106
134
|
"""
|
|
107
135
|
import multiprocessing as m
|
|
108
136
|
import time, re
|
|
@@ -111,8 +139,8 @@ if runtime_env() in ['notebook', 'shell']:
|
|
|
111
139
|
name = None
|
|
112
140
|
if line:
|
|
113
141
|
# check if custom process name was provided
|
|
114
|
-
if
|
|
115
|
-
line, name = line.split(
|
|
142
|
+
if " as " in line:
|
|
143
|
+
line, name = line.split("as")
|
|
116
144
|
if not name.isspace():
|
|
117
145
|
name = name.strip()
|
|
118
146
|
else:
|
|
@@ -120,11 +148,11 @@ if runtime_env() in ['notebook', 'shell']:
|
|
|
120
148
|
return
|
|
121
149
|
|
|
122
150
|
ipy = get_ipython()
|
|
123
|
-
for a in [x for x in re.split(
|
|
151
|
+
for a in [x for x in re.split("[\ ,;]", line.strip()) if x]:
|
|
124
152
|
ipy.push({a: self._get_manager().Value(None, None)})
|
|
125
153
|
|
|
126
154
|
# code to run
|
|
127
|
-
lines =
|
|
155
|
+
lines = "\n".join([" %s" % x for x in cell.split("\n")])
|
|
128
156
|
|
|
129
157
|
def fn():
|
|
130
158
|
result = get_ipython().run_cell(lines)
|
|
@@ -136,17 +164,18 @@ if runtime_env() in ['notebook', 'shell']:
|
|
|
136
164
|
if result.error_in_exec:
|
|
137
165
|
raise result.error_in_exec
|
|
138
166
|
|
|
139
|
-
t_start = str(time.time()).replace(
|
|
140
|
-
f_id = f
|
|
167
|
+
t_start = str(time.time()).replace(".", "_")
|
|
168
|
+
f_id = f"proc_{t_start}" if name is None else name
|
|
141
169
|
if self._is_task_name_already_used(f_id):
|
|
142
170
|
f_id = f"{f_id}_{t_start}"
|
|
143
171
|
|
|
144
172
|
task = m.Process(target=fn, name=f_id)
|
|
145
173
|
task.start()
|
|
146
|
-
print(
|
|
174
|
+
print(" -> Task %s is started" % f_id)
|
|
147
175
|
|
|
148
176
|
def _is_task_name_already_used(self, name):
|
|
149
177
|
import multiprocessing as m
|
|
178
|
+
|
|
150
179
|
for p in m.active_children():
|
|
151
180
|
if p.name == name:
|
|
152
181
|
return True
|
|
@@ -155,16 +184,17 @@ if runtime_env() in ['notebook', 'shell']:
|
|
|
155
184
|
@line_magic
|
|
156
185
|
def list_proc(self, line):
|
|
157
186
|
import multiprocessing as m
|
|
187
|
+
|
|
158
188
|
for p in m.active_children():
|
|
159
189
|
print(p.name)
|
|
160
190
|
|
|
161
191
|
@line_magic
|
|
162
192
|
def kill_proc(self, line):
|
|
163
193
|
import multiprocessing as m
|
|
194
|
+
|
|
164
195
|
for p in m.active_children():
|
|
165
196
|
if line and p.name.startswith(line):
|
|
166
197
|
p.terminate()
|
|
167
198
|
|
|
168
|
-
|
|
169
199
|
# - registering magic here
|
|
170
200
|
get_ipython().register_magics(QubxMagics)
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
""""
|
|
2
2
|
Here stuff we want to have in every Jupyter notebook after calling %qube magic
|
|
3
3
|
"""
|
|
4
|
-
import importlib_metadata
|
|
5
4
|
|
|
6
5
|
import qubx
|
|
7
6
|
from qubx.utils import runtime_env
|
|
@@ -15,11 +14,19 @@ def np_fmt_short():
|
|
|
15
14
|
|
|
16
15
|
def np_fmt_reset():
|
|
17
16
|
# reset default np printing options
|
|
18
|
-
np.set_printoptions(
|
|
19
|
-
|
|
17
|
+
np.set_printoptions(
|
|
18
|
+
edgeitems=3,
|
|
19
|
+
infstr="inf",
|
|
20
|
+
linewidth=75,
|
|
21
|
+
nanstr="nan",
|
|
22
|
+
precision=8,
|
|
23
|
+
suppress=False,
|
|
24
|
+
threshold=1000,
|
|
25
|
+
formatter=None,
|
|
26
|
+
)
|
|
20
27
|
|
|
21
28
|
|
|
22
|
-
if runtime_env() in [
|
|
29
|
+
if runtime_env() in ["notebook", "shell"]:
|
|
23
30
|
|
|
24
31
|
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
25
32
|
# -- all imports below will appear in notebook after calling %%alphalab magic ---
|
|
@@ -39,19 +46,18 @@ if runtime_env() in ['notebook', 'shell']:
|
|
|
39
46
|
# - - - - Learn stuff - - - -
|
|
40
47
|
# - - - - Charting stuff - - - -
|
|
41
48
|
from matplotlib import pyplot as plt
|
|
42
|
-
from qubx.utils.charting.mpl_helpers import fig, subplot, sbp
|
|
49
|
+
from qubx.utils.charting.mpl_helpers import fig, subplot, sbp, plot_trends, ohlc_plot
|
|
43
50
|
|
|
44
51
|
# - - - - Utils - - - -
|
|
45
52
|
from qubx.pandaz.utils import scols, srows, ohlc_resample, continuous_periods, generate_equal_date_ranges
|
|
46
53
|
|
|
47
54
|
# - setup short numpy output format
|
|
48
55
|
np_fmt_short()
|
|
49
|
-
|
|
56
|
+
|
|
50
57
|
# - add project home to system path
|
|
51
58
|
add_project_to_system_path()
|
|
52
59
|
|
|
53
60
|
# show logo first time
|
|
54
|
-
if not hasattr(qubx.QubxMagics,
|
|
61
|
+
if not hasattr(qubx.QubxMagics, "__already_initialized__"):
|
|
55
62
|
setattr(qubx.QubxMagics, "__already_initialized__", True)
|
|
56
63
|
logo()
|
|
57
|
-
|
|
@@ -15,7 +15,7 @@ cdef class TimeSeries:
|
|
|
15
15
|
cdef public long long timeframe
|
|
16
16
|
cdef public Indexed times
|
|
17
17
|
cdef public Indexed values
|
|
18
|
-
cdef float max_series_length
|
|
18
|
+
cdef public float max_series_length
|
|
19
19
|
cdef unsigned short _is_new_item
|
|
20
20
|
cdef public str name
|
|
21
21
|
cdef dict indicators # it's used for indicators caching
|
|
@@ -28,8 +28,12 @@ cdef class TimeSeries:
|
|
|
28
28
|
|
|
29
29
|
|
|
30
30
|
cdef class Indicator(TimeSeries):
|
|
31
|
-
cdef TimeSeries series
|
|
32
|
-
cdef TimeSeries parent
|
|
31
|
+
cdef public TimeSeries series
|
|
32
|
+
cdef public TimeSeries parent
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
cdef class IndicatorOHLC(Indicator):
|
|
36
|
+
pass
|
|
33
37
|
|
|
34
38
|
|
|
35
39
|
cdef class RollingSum:
|
|
@@ -300,6 +300,9 @@ def _wrap_indicator(series: TimeSeries, clz, *args, **kwargs):
|
|
|
300
300
|
|
|
301
301
|
|
|
302
302
|
cdef class Indicator(TimeSeries):
|
|
303
|
+
"""
|
|
304
|
+
Basic class for indicator that can be attached to TimeSeries
|
|
305
|
+
"""
|
|
303
306
|
|
|
304
307
|
def __init__(self, str name, TimeSeries series):
|
|
305
308
|
if not name:
|
|
@@ -309,7 +312,7 @@ cdef class Indicator(TimeSeries):
|
|
|
309
312
|
self.name = name
|
|
310
313
|
|
|
311
314
|
# - we need to make a empty copy and fill it
|
|
312
|
-
self.series =
|
|
315
|
+
self.series = self._instantiate_base_series(series.name, series.timeframe, series.max_series_length)
|
|
313
316
|
self.parent = series
|
|
314
317
|
|
|
315
318
|
# - notify the parent series that indicator has been attached
|
|
@@ -318,6 +321,9 @@ cdef class Indicator(TimeSeries):
|
|
|
318
321
|
# - recalculate indicator on data as if it would being streamed
|
|
319
322
|
self._initial_data_recalculate(series)
|
|
320
323
|
|
|
324
|
+
def _instantiate_base_series(self, str name, long long timeframe, float max_series_length):
|
|
325
|
+
return TimeSeries(name, timeframe, max_series_length)
|
|
326
|
+
|
|
321
327
|
def _on_attach_indicator(self, indicator: Indicator, indicator_input: TimeSeries):
|
|
322
328
|
self.parent._on_attach_indicator(indicator, indicator_input)
|
|
323
329
|
|
|
@@ -345,6 +351,17 @@ cdef class Indicator(TimeSeries):
|
|
|
345
351
|
return _wrap_indicator(series, clz, *args, **kwargs)
|
|
346
352
|
|
|
347
353
|
|
|
354
|
+
cdef class IndicatorOHLC(Indicator):
|
|
355
|
+
"""
|
|
356
|
+
Extension of indicator class to be used for OHLCV series
|
|
357
|
+
"""
|
|
358
|
+
def _instantiate_base_series(self, str name, long long timeframe, float max_series_length):
|
|
359
|
+
return OHLCV(name, timeframe, max_series_length)
|
|
360
|
+
|
|
361
|
+
def calculate(self, long long time, Bar value, short new_item_started) -> object:
|
|
362
|
+
raise ValueError("Indicator must implement calculate() method")
|
|
363
|
+
|
|
364
|
+
|
|
348
365
|
cdef class Lag(Indicator):
|
|
349
366
|
cdef int period
|
|
350
367
|
|