opencode-pyneruntime 6.6.4__py3-none-any.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.
- opencode_pyneruntime-6.6.4.dist-info/METADATA +281 -0
- opencode_pyneruntime-6.6.4.dist-info/RECORD +261 -0
- opencode_pyneruntime-6.6.4.dist-info/WHEEL +5 -0
- opencode_pyneruntime-6.6.4.dist-info/entry_points.txt +6 -0
- opencode_pyneruntime-6.6.4.dist-info/licenses/LICENSE +201 -0
- opencode_pyneruntime-6.6.4.dist-info/licenses/NOTICE +21 -0
- opencode_pyneruntime-6.6.4.dist-info/top_level.txt +1 -0
- pynecore/__init__.py +6 -0
- pynecore/cli/__init__.py +2 -0
- pynecore/cli/app.py +238 -0
- pynecore/cli/commands/__init__.py +343 -0
- pynecore/cli/commands/benchmark.py +186 -0
- pynecore/cli/commands/compile.py +198 -0
- pynecore/cli/commands/data.py +857 -0
- pynecore/cli/commands/debug.py +63 -0
- pynecore/cli/commands/optimize.py +956 -0
- pynecore/cli/commands/plugin.py +242 -0
- pynecore/cli/commands/run.py +2006 -0
- pynecore/cli/pluggable.py +132 -0
- pynecore/cli/utils/__init__.py +0 -0
- pynecore/cli/utils/api_error_handler.py +168 -0
- pynecore/cli/utils/broker_picker.py +330 -0
- pynecore/cli/utils/error_hook.py +28 -0
- pynecore/cli/utils/keyreader.py +178 -0
- pynecore/cli/utils/provider_picker.py +19 -0
- pynecore/cli/utils/symbol_browser.py +1149 -0
- pynecore/core/__init__.py +0 -0
- pynecore/core/aggregator.py +257 -0
- pynecore/core/bar_magnifier.py +168 -0
- pynecore/core/broker/__init__.py +64 -0
- pynecore/core/broker/defaults.py +113 -0
- pynecore/core/broker/disappearance.py +927 -0
- pynecore/core/broker/emulator.py +345 -0
- pynecore/core/broker/exceptions.py +346 -0
- pynecore/core/broker/idempotency.py +401 -0
- pynecore/core/broker/intent_builder.py +334 -0
- pynecore/core/broker/journal.py +1785 -0
- pynecore/core/broker/models.py +1600 -0
- pynecore/core/broker/native_failsafe_manager.py +1436 -0
- pynecore/core/broker/one_way_emulator.py +1128 -0
- pynecore/core/broker/position.py +787 -0
- pynecore/core/broker/run_identity.py +126 -0
- pynecore/core/broker/software_entry_stop_engine.py +351 -0
- pynecore/core/broker/software_partial_bracket_engine.py +1379 -0
- pynecore/core/broker/spot_inventory.py +1327 -0
- pynecore/core/broker/storage.py +2655 -0
- pynecore/core/broker/store_helpers.py +2161 -0
- pynecore/core/broker/sync_engine.py +16070 -0
- pynecore/core/broker/validation.py +382 -0
- pynecore/core/class_property.py +7 -0
- pynecore/core/config.py +392 -0
- pynecore/core/csv_file.py +547 -0
- pynecore/core/currency.py +262 -0
- pynecore/core/data_converter.py +1002 -0
- pynecore/core/datetime.py +296 -0
- pynecore/core/download_info.py +71 -0
- pynecore/core/download_runner.py +274 -0
- pynecore/core/htf_aggregator.py +181 -0
- pynecore/core/import_hook.py +358 -0
- pynecore/core/instance_state.py +494 -0
- pynecore/core/live_ltf_collector.py +442 -0
- pynecore/core/live_ltf_window.py +189 -0
- pynecore/core/live_runner.py +1347 -0
- pynecore/core/module_property.py +26 -0
- pynecore/core/ohlcv_file.py +1888 -0
- pynecore/core/overload.py +371 -0
- pynecore/core/pine_cast.py +113 -0
- pynecore/core/pine_export.py +95 -0
- pynecore/core/pine_method.py +244 -0
- pynecore/core/pine_range.py +86 -0
- pynecore/core/pine_udt.py +69 -0
- pynecore/core/plugin/__init__.py +394 -0
- pynecore/core/plugin/broker.py +781 -0
- pynecore/core/plugin/cli.py +96 -0
- pynecore/core/plugin/live_provider.py +208 -0
- pynecore/core/plugin/provider.py +331 -0
- pynecore/core/provider_string.py +148 -0
- pynecore/core/random.py +40 -0
- pynecore/core/resampler.py +686 -0
- pynecore/core/safe_convert.py +64 -0
- pynecore/core/script.py +1011 -0
- pynecore/core/script_runner.py +3202 -0
- pynecore/core/security.py +1749 -0
- pynecore/core/security_process.py +1253 -0
- pynecore/core/security_shm.py +456 -0
- pynecore/core/series.py +417 -0
- pynecore/core/strategy_stats.py +669 -0
- pynecore/core/symbol_map.py +134 -0
- pynecore/core/syminfo.py +505 -0
- pynecore/core/viz.py +591 -0
- pynecore/lib/__init__.py +1771 -0
- pynecore/lib/_fixnan.py +32 -0
- pynecore/lib/_math_stateful.py +202 -0
- pynecore/lib/_timeframe_change.py +101 -0
- pynecore/lib/adjustment.py +6 -0
- pynecore/lib/alert.py +39 -0
- pynecore/lib/alert.pyi +14 -0
- pynecore/lib/array.py +1051 -0
- pynecore/lib/barmerge.py +60 -0
- pynecore/lib/barstate.py +30 -0
- pynecore/lib/box.py +415 -0
- pynecore/lib/chart.py +128 -0
- pynecore/lib/color.py +152 -0
- pynecore/lib/color.pyi +50 -0
- pynecore/lib/currency.py +62 -0
- pynecore/lib/dayofweek.py +36 -0
- pynecore/lib/dayofweek.pyi +18 -0
- pynecore/lib/display.py +8 -0
- pynecore/lib/dividends.py +9 -0
- pynecore/lib/earnings.py +11 -0
- pynecore/lib/extend.py +6 -0
- pynecore/lib/font.py +5 -0
- pynecore/lib/footprint.py +79 -0
- pynecore/lib/format.py +11 -0
- pynecore/lib/hline.py +67 -0
- pynecore/lib/hline.pyi +24 -0
- pynecore/lib/label.py +409 -0
- pynecore/lib/line.py +433 -0
- pynecore/lib/linefill.py +93 -0
- pynecore/lib/location.py +11 -0
- pynecore/lib/log.py +362 -0
- pynecore/lib/map.py +150 -0
- pynecore/lib/math.py +385 -0
- pynecore/lib/matrix.py +708 -0
- pynecore/lib/order.py +8 -0
- pynecore/lib/pivotpointtype.py +8 -0
- pynecore/lib/plot.py +95 -0
- pynecore/lib/plot.pyi +33 -0
- pynecore/lib/polyline.py +91 -0
- pynecore/lib/position.py +15 -0
- pynecore/lib/request.py +281 -0
- pynecore/lib/runtime.py +5 -0
- pynecore/lib/scale.py +9 -0
- pynecore/lib/session.py +267 -0
- pynecore/lib/session.pyi +12 -0
- pynecore/lib/shape.py +18 -0
- pynecore/lib/size.py +12 -0
- pynecore/lib/splits.py +4 -0
- pynecore/lib/strategy/__init__.py +4778 -0
- pynecore/lib/strategy/closedtrades.py +347 -0
- pynecore/lib/strategy/closedtrades.pyi +53 -0
- pynecore/lib/strategy/commission.py +9 -0
- pynecore/lib/strategy/direction.py +9 -0
- pynecore/lib/strategy/oca.py +13 -0
- pynecore/lib/strategy/opentrades.py +281 -0
- pynecore/lib/strategy/opentrades.pyi +49 -0
- pynecore/lib/strategy/risk.py +109 -0
- pynecore/lib/string.py +649 -0
- pynecore/lib/syminfo.py +84 -0
- pynecore/lib/ta.py +2230 -0
- pynecore/lib/table.py +290 -0
- pynecore/lib/text.py +17 -0
- pynecore/lib/ticker.py +207 -0
- pynecore/lib/timeframe.py +293 -0
- pynecore/lib/volume_row.py +67 -0
- pynecore/lib/xloc.py +4 -0
- pynecore/lib/yloc.py +5 -0
- pynecore/providers/__init__.py +0 -0
- pynecore/providers/ccxt.py +664 -0
- pynecore/providers/replay.py +187 -0
- pynecore/pynesys/__init__.py +0 -0
- pynecore/pynesys/api.py +498 -0
- pynecore/pynesys/compiler.py +112 -0
- pynecore/standalone.py +99 -0
- pynecore/testing/__init__.py +1 -0
- pynecore/testing/broker_lab/__init__.py +41 -0
- pynecore/testing/broker_lab/__main__.py +5 -0
- pynecore/testing/broker_lab/cli.py +87 -0
- pynecore/testing/broker_lab/generate.py +47 -0
- pynecore/testing/broker_lab/model.py +84 -0
- pynecore/testing/broker_lab/reference.py +645 -0
- pynecore/testing/broker_lab/runner.py +372 -0
- pynecore/testing/broker_lab/scheduler.py +50 -0
- pynecore/testing/broker_lab/subprocess.py +73 -0
- pynecore/transformers/__init__.py +0 -0
- pynecore/transformers/builtin_shadow.py +136 -0
- pynecore/transformers/closure_arguments_transformer.py +428 -0
- pynecore/transformers/display_rewrite.py +140 -0
- pynecore/transformers/dynamic_default.py +147 -0
- pynecore/transformers/function_isolation.py +757 -0
- pynecore/transformers/import_lifter.py +61 -0
- pynecore/transformers/import_normalizer.py +328 -0
- pynecore/transformers/inline_series_hoist.py +178 -0
- pynecore/transformers/input_transformer.py +175 -0
- pynecore/transformers/lib_series.py +201 -0
- pynecore/transformers/locations.py +70 -0
- pynecore/transformers/module_properties.json +3387 -0
- pynecore/transformers/module_property.py +221 -0
- pynecore/transformers/ne_guard.py +70 -0
- pynecore/transformers/persistent.py +320 -0
- pynecore/transformers/persistent_series.py +76 -0
- pynecore/transformers/safe_convert_transformer.py +97 -0
- pynecore/transformers/safe_division_transformer.py +95 -0
- pynecore/transformers/script_requirements.py +308 -0
- pynecore/transformers/security.py +752 -0
- pynecore/transformers/security_instantiation.py +274 -0
- pynecore/transformers/series.py +275 -0
- pynecore/transformers/slot_layout.py +381 -0
- pynecore/transformers/type_checking_stripper.py +25 -0
- pynecore/transformers/unused_series_detector.py +267 -0
- pynecore/types/__init__.py +21 -0
- pynecore/types/alert.py +5 -0
- pynecore/types/barmerge.py +5 -0
- pynecore/types/base.py +39 -0
- pynecore/types/box.py +37 -0
- pynecore/types/chart.py +17 -0
- pynecore/types/color.py +107 -0
- pynecore/types/currency.py +5 -0
- pynecore/types/datetime.py +6 -0
- pynecore/types/display.py +5 -0
- pynecore/types/dividends.py +5 -0
- pynecore/types/earnings.py +5 -0
- pynecore/types/extend.py +5 -0
- pynecore/types/font.py +5 -0
- pynecore/types/footprint.py +41 -0
- pynecore/types/format.py +5 -0
- pynecore/types/hline.py +24 -0
- pynecore/types/ib_persistent.py +8 -0
- pynecore/types/ib_persistent.pyi +10 -0
- pynecore/types/label.py +35 -0
- pynecore/types/line.py +32 -0
- pynecore/types/linefill.py +13 -0
- pynecore/types/location.py +5 -0
- pynecore/types/matrix.py +999 -0
- pynecore/types/na.py +237 -0
- pynecore/types/na.pyi +83 -0
- pynecore/types/ohlcv.py +12 -0
- pynecore/types/order.py +5 -0
- pynecore/types/persistent.py +8 -0
- pynecore/types/persistent.pyi +13 -0
- pynecore/types/pine_types.py +11 -0
- pynecore/types/pine_types.pyi +15 -0
- pynecore/types/pivotpointtype.py +5 -0
- pynecore/types/plot.py +12 -0
- pynecore/types/plot_meta.py +60 -0
- pynecore/types/polyline.py +40 -0
- pynecore/types/position.py +5 -0
- pynecore/types/scale.py +5 -0
- pynecore/types/script_type.py +15 -0
- pynecore/types/series.py +23 -0
- pynecore/types/series.pyi +19 -0
- pynecore/types/session.py +35 -0
- pynecore/types/shape.py +5 -0
- pynecore/types/size.py +5 -0
- pynecore/types/source.py +33 -0
- pynecore/types/splits.py +5 -0
- pynecore/types/strategy.py +45 -0
- pynecore/types/table.py +87 -0
- pynecore/types/text.py +13 -0
- pynecore/types/type_checker.py +7 -0
- pynecore/types/type_checker.pyi +48 -0
- pynecore/types/volume_row.py +36 -0
- pynecore/types/weekdays.py +11 -0
- pynecore/types/xloc.py +5 -0
- pynecore/types/yloc.py +5 -0
- pynecore/utils/__init__.py +0 -0
- pynecore/utils/file_utils.py +50 -0
- pynecore/utils/rich/__init__.py +0 -0
- pynecore/utils/rich/date_column.py +25 -0
- pynecore/utils/sequence_view.py +92 -0
- pynecore/utils/stdlib_checker.py +17 -0
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
from typing import TYPE_CHECKING, Any
|
|
2
|
+
from collections.abc import Callable, Sequence
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
|
|
5
|
+
from . import Plugin, ConfigT
|
|
6
|
+
|
|
7
|
+
if TYPE_CHECKING:
|
|
8
|
+
import typer
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@dataclass(slots=True)
|
|
12
|
+
class CLIOption:
|
|
13
|
+
"""
|
|
14
|
+
Declarative description of a CLI option injected into a built-in command.
|
|
15
|
+
|
|
16
|
+
Deliberately backend-agnostic: the plugin describes the option, PyneCore
|
|
17
|
+
builds the concrete parser object. Plugins must not construct parser
|
|
18
|
+
objects themselves — Click is not a PyneCore dependency, and since Typer
|
|
19
|
+
0.26 it is not a Typer dependency either (Typer vendors a reduced fork of
|
|
20
|
+
it), so an externally built ``click.Option`` is not even parseable by the
|
|
21
|
+
CLI anymore.
|
|
22
|
+
|
|
23
|
+
:ivar decls: Option string, or a tuple of them (``"--live"``,
|
|
24
|
+
``("--output", "-o")``). The parameter name is derived from the first
|
|
25
|
+
long option, as usual.
|
|
26
|
+
:ivar help: Help text shown in ``--help``.
|
|
27
|
+
:ivar default: Value used when the option is not given on the command line.
|
|
28
|
+
:ivar is_flag: Boolean switch that takes no value.
|
|
29
|
+
:ivar type: Converter applied to the raw string value (``int``, ``float``,
|
|
30
|
+
``pathlib.Path``, or any single-argument callable). Ignored when
|
|
31
|
+
``choices`` is set.
|
|
32
|
+
:ivar choices: Restrict the value to this set.
|
|
33
|
+
:ivar metavar: Value placeholder in the help output.
|
|
34
|
+
:ivar required: Fail when the option is missing.
|
|
35
|
+
:ivar multiple: Allow the option to be repeated, collecting a tuple.
|
|
36
|
+
:ivar hidden: Keep the option out of the help output.
|
|
37
|
+
:ivar envvar: Environment variable to read the value from.
|
|
38
|
+
:ivar rich_help_panel: Help panel to group the option under.
|
|
39
|
+
"""
|
|
40
|
+
|
|
41
|
+
decls: str | tuple[str, ...]
|
|
42
|
+
help: str = ""
|
|
43
|
+
default: Any = None
|
|
44
|
+
is_flag: bool = False
|
|
45
|
+
type: Callable[[str], Any] | None = None
|
|
46
|
+
choices: Sequence[str] | None = None
|
|
47
|
+
metavar: str | None = None
|
|
48
|
+
required: bool = False
|
|
49
|
+
multiple: bool = False
|
|
50
|
+
hidden: bool = False
|
|
51
|
+
envvar: str | None = None
|
|
52
|
+
rich_help_panel: str | None = None
|
|
53
|
+
|
|
54
|
+
def __post_init__(self) -> None:
|
|
55
|
+
if isinstance(self.decls, str):
|
|
56
|
+
self.decls = (self.decls,)
|
|
57
|
+
else:
|
|
58
|
+
self.decls = tuple(self.decls)
|
|
59
|
+
if not self.decls:
|
|
60
|
+
raise ValueError("CLIOption needs at least one option string")
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
class CLIPlugin(Plugin[ConfigT]):
|
|
64
|
+
"""
|
|
65
|
+
Plugin that provides CLI commands and/or parameter hooks.
|
|
66
|
+
|
|
67
|
+
Override :meth:`cli` to add subcommands (``pyne <name> ...``).
|
|
68
|
+
Override :meth:`cli_params` to inject flags into existing commands.
|
|
69
|
+
"""
|
|
70
|
+
|
|
71
|
+
@staticmethod
|
|
72
|
+
def cli() -> 'typer.Typer | None':
|
|
73
|
+
"""
|
|
74
|
+
Return a Typer app for plugin subcommands.
|
|
75
|
+
|
|
76
|
+
Override to add commands like ``pyne <plugin_name> <subcommand>``.
|
|
77
|
+
|
|
78
|
+
:return: A Typer app, or ``None`` if the plugin has no CLI commands.
|
|
79
|
+
"""
|
|
80
|
+
return None
|
|
81
|
+
|
|
82
|
+
# noinspection PyUnusedLocal
|
|
83
|
+
@staticmethod
|
|
84
|
+
def cli_params(command_name: str) -> list[CLIOption]:
|
|
85
|
+
"""
|
|
86
|
+
Return extra parameters for an existing command.
|
|
87
|
+
|
|
88
|
+
Override to inject flags/options into commands like ``pyne run``.
|
|
89
|
+
Nested subcommands are addressed by their space-separated path, so
|
|
90
|
+
``pyne data download`` is matched as ``"data download"``.
|
|
91
|
+
|
|
92
|
+
:param command_name: The command to extend (e.g. ``"run"`` or
|
|
93
|
+
``"data download"``).
|
|
94
|
+
:return: List of option specs, or ``[]`` if no hooks for this command.
|
|
95
|
+
"""
|
|
96
|
+
return []
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
from abc import abstractmethod, ABCMeta
|
|
2
|
+
from dataclasses import dataclass, field
|
|
3
|
+
from datetime import datetime
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from typing import TYPE_CHECKING, TypeVar
|
|
6
|
+
|
|
7
|
+
from pynecore.types.ohlcv import OHLCV
|
|
8
|
+
|
|
9
|
+
from .provider import ProviderPlugin
|
|
10
|
+
|
|
11
|
+
if TYPE_CHECKING:
|
|
12
|
+
from ..syminfo import SymInfo
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@dataclass
|
|
16
|
+
class LiveProviderConfig:
|
|
17
|
+
"""Common base for all live-provider plugin configs.
|
|
18
|
+
|
|
19
|
+
Provides the symbol-translation map every live plugin shares so a Pine
|
|
20
|
+
script can keep using TradingView-style symbols (e.g. ``"FX:EURUSD"``)
|
|
21
|
+
even when the running provider exposes a different native identifier
|
|
22
|
+
(e.g. Capital.com's ``"EURUSD"`` epic). Subclasses inherit this field
|
|
23
|
+
via dataclass inheritance and add their own credentials/tunables on top.
|
|
24
|
+
|
|
25
|
+
Generated TOML example (a commented default appears in every live
|
|
26
|
+
plugin's config file)::
|
|
27
|
+
|
|
28
|
+
# Optional translation map. Keys are TradingView-style symbols as
|
|
29
|
+
# written in your Pine script (``request.security("FX:EURUSD", ...)``);
|
|
30
|
+
# values are the native identifier the plugin sends to the
|
|
31
|
+
# exchange API. Example for Capital.com::
|
|
32
|
+
#
|
|
33
|
+
# [symbol_map]
|
|
34
|
+
# "FX:EURUSD" = "EURUSD"
|
|
35
|
+
# "OANDA:XAUUSD" = "GOLD"
|
|
36
|
+
#
|
|
37
|
+
# When a Pine symbol is not in the map the plugin falls back to
|
|
38
|
+
# ``normalize_symbol()`` (provider-specific prefix-strip etc.).
|
|
39
|
+
#symbol_map = {}
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
symbol_map: dict[str, str] = field(default_factory=dict)
|
|
43
|
+
"""Optional translation map. Keys are TradingView-style symbols as
|
|
44
|
+
written in Pine scripts (e.g. ``"FX:EURUSD"``); values are the native
|
|
45
|
+
identifier the plugin sends to the exchange API (e.g. ``"EURUSD"``).
|
|
46
|
+
Missing keys fall back to ``ProviderPlugin.normalize_symbol``."""
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
LiveProviderConfigT = TypeVar('LiveProviderConfigT', bound=LiveProviderConfig)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
@dataclass(frozen=True)
|
|
53
|
+
class PluginSymbol:
|
|
54
|
+
"""Live-mode descriptor for a :func:`request.security` data source.
|
|
55
|
+
|
|
56
|
+
Replaces the historical ``.ohlcv`` file path in live runs: the security
|
|
57
|
+
subprocess instantiates ``provider_name``'s :class:`LiveProviderPlugin`
|
|
58
|
+
itself, downloads warmup history in-memory, then streams live bars.
|
|
59
|
+
No intermediate ``.ohlcv`` file is written.
|
|
60
|
+
|
|
61
|
+
:ivar provider_name: Entry-point name for :func:`load_plugin`
|
|
62
|
+
(e.g. ``"capitalcom"``).
|
|
63
|
+
:ivar symbol: Plugin-native symbol (e.g. ``"EURUSD"``); already passed
|
|
64
|
+
through :meth:`ProviderPlugin.resolve_symbol`.
|
|
65
|
+
:ivar timeframe: Timeframe in TradingView format (e.g. ``"1D"``).
|
|
66
|
+
:ivar config: Pre-loaded plugin config dataclass instance. The chart
|
|
67
|
+
process runs :func:`ensure_config` once and hands the
|
|
68
|
+
resulting instance to every subprocess via spawn args —
|
|
69
|
+
subprocesses must not touch the TOML file themselves
|
|
70
|
+
because :func:`ensure_config` is not process-safe.
|
|
71
|
+
:ivar time_from: Optional warmup-window start. Defaults to ``None``,
|
|
72
|
+
which lets the subprocess fall back to its built-in
|
|
73
|
+
``_DEFAULT_WARMUP_BARS`` heuristic; the chart passes
|
|
74
|
+
its own ``--from`` here so security contexts inherit
|
|
75
|
+
the same look-back range.
|
|
76
|
+
:ivar syminfo: Optional pre-fetched :class:`SymInfo`. The chart
|
|
77
|
+
process can call ``provider.update_symbol_info()`` once
|
|
78
|
+
and pass the result here, so the subprocess does not
|
|
79
|
+
have to repeat the REST round-trip on startup. ``None``
|
|
80
|
+
means the subprocess will fetch syminfo itself.
|
|
81
|
+
:ivar is_rate_source: When ``True`` the subprocess runs in a stripped
|
|
82
|
+
"close-only" loop instead of importing and executing the
|
|
83
|
+
Pine script. Used by the auto-spawned rate-source
|
|
84
|
+
contexts created from a ``request.security(..., currency=X)``
|
|
85
|
+
request when no explicit context already covers the
|
|
86
|
+
required currency pair.
|
|
87
|
+
:ivar ohlcv_dir: Optional path to the chart process's OHLCV data dir.
|
|
88
|
+
Forwarded to the subprocess so the provider can locate
|
|
89
|
+
workdir-side resources that live next to the data dir —
|
|
90
|
+
most notably per-exchange config overrides in
|
|
91
|
+
``<workdir>/config/plugins/<provider>.toml`` (e.g. the
|
|
92
|
+
``[binance]`` section of ``ccxt.toml``). Without it the
|
|
93
|
+
subprocess provider runs with default config while the
|
|
94
|
+
chart side runs with the override, breaking auth and
|
|
95
|
+
market-type selection for cross-symbol security feeds.
|
|
96
|
+
``None`` means the subprocess provider is constructed
|
|
97
|
+
without an OHLCV directory (no file is written either way).
|
|
98
|
+
"""
|
|
99
|
+
|
|
100
|
+
provider_name: str
|
|
101
|
+
symbol: str
|
|
102
|
+
timeframe: str
|
|
103
|
+
config: LiveProviderConfig | None = None
|
|
104
|
+
time_from: datetime | None = None
|
|
105
|
+
syminfo: 'SymInfo | None' = None
|
|
106
|
+
is_rate_source: bool = False
|
|
107
|
+
ohlcv_dir: Path | None = None
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
class LiveProviderPlugin(ProviderPlugin[LiveProviderConfigT], metaclass=ABCMeta):
|
|
111
|
+
"""
|
|
112
|
+
WebSocket/streaming data provider extending :class:`ProviderPlugin`.
|
|
113
|
+
|
|
114
|
+
Adds real-time data streaming to the offline OHLCV download capability.
|
|
115
|
+
Subclasses must implement connection lifecycle and data streaming methods
|
|
116
|
+
in addition to the :class:`ProviderPlugin` abstract methods.
|
|
117
|
+
|
|
118
|
+
The async methods run in a background thread; the framework bridges them
|
|
119
|
+
to the synchronous :class:`ScriptRunner` via a :class:`queue.Queue`.
|
|
120
|
+
"""
|
|
121
|
+
|
|
122
|
+
reconnect_delay: float = 1.0
|
|
123
|
+
"""Initial delay in seconds before a reconnection attempt. Doubles per
|
|
124
|
+
consecutive failure (exponential backoff) up to :attr:`max_reconnect_delay`."""
|
|
125
|
+
|
|
126
|
+
max_reconnect_delay: float = 60.0
|
|
127
|
+
"""Ceiling in seconds for the exponential reconnect backoff.
|
|
128
|
+
|
|
129
|
+
Reconnection is retried indefinitely — a live session must survive
|
|
130
|
+
arbitrarily long network / provider outages and resume on its own once
|
|
131
|
+
connectivity returns — so there is no attempt limit; the backoff simply
|
|
132
|
+
saturates at this delay.
|
|
133
|
+
"""
|
|
134
|
+
|
|
135
|
+
feed_timeout_bars: int | None = 3
|
|
136
|
+
"""Feed-liveness watchdog threshold, in timeframe periods.
|
|
137
|
+
|
|
138
|
+
When the provider reports a healthy connection but :meth:`watch_ohlcv`
|
|
139
|
+
has produced no update (closed bar or intra-bar) for this many timeframe
|
|
140
|
+
periods (floored at 90 s) during an open trading session, the framework
|
|
141
|
+
assumes the data feed is dead even though the transport looks alive
|
|
142
|
+
(half-open socket, lost server-side subscription) and drives a full
|
|
143
|
+
``disconnect()`` → ``connect()`` cycle. ``None`` disables the watchdog —
|
|
144
|
+
only justified for providers whose feed legitimately stays silent for
|
|
145
|
+
long in-session stretches AND whose own liveness machinery already
|
|
146
|
+
covers the dead-feed case.
|
|
147
|
+
"""
|
|
148
|
+
|
|
149
|
+
# --- Connection lifecycle ---
|
|
150
|
+
|
|
151
|
+
@abstractmethod
|
|
152
|
+
async def connect(self) -> None:
|
|
153
|
+
"""Establish connection to the data source.
|
|
154
|
+
|
|
155
|
+
Called for the initial connection AND again on every reconnect (the
|
|
156
|
+
framework drives ``disconnect()`` → ``connect()`` after a connection
|
|
157
|
+
error or a stale feed). Implementations must therefore fully
|
|
158
|
+
re-initialize every piece of connection-scoped state here —
|
|
159
|
+
authentication, server-side subscriptions, partially accumulated
|
|
160
|
+
quote state — rather than assuming a first-call-only environment.
|
|
161
|
+
"""
|
|
162
|
+
|
|
163
|
+
@abstractmethod
|
|
164
|
+
async def disconnect(self) -> None:
|
|
165
|
+
"""Close connection cleanly."""
|
|
166
|
+
|
|
167
|
+
@property
|
|
168
|
+
@abstractmethod
|
|
169
|
+
def is_connected(self) -> bool:
|
|
170
|
+
"""Whether the connection is currently active."""
|
|
171
|
+
|
|
172
|
+
# --- Data streaming ---
|
|
173
|
+
|
|
174
|
+
@abstractmethod
|
|
175
|
+
async def watch_ohlcv(self, symbol: str, timeframe: str) -> OHLCV:
|
|
176
|
+
"""
|
|
177
|
+
Wait for and return the next OHLCV update.
|
|
178
|
+
|
|
179
|
+
Called in a loop by the framework. Each call blocks (awaits)
|
|
180
|
+
until new data arrives from the data source.
|
|
181
|
+
|
|
182
|
+
:param symbol: The symbol in provider-specific format.
|
|
183
|
+
:param timeframe: Timeframe in TradingView format (e.g. ``"1D"``, ``"1"``, ``"4H"``).
|
|
184
|
+
:return: An :class:`OHLCV` with ``is_closed=True`` for a final bar, ``False`` for intra-bar updates.
|
|
185
|
+
"""
|
|
186
|
+
|
|
187
|
+
# --- Reconnection hooks (override for custom behavior) ---
|
|
188
|
+
|
|
189
|
+
async def on_disconnect(self) -> None:
|
|
190
|
+
"""Called when the connection is unexpectedly lost."""
|
|
191
|
+
|
|
192
|
+
async def on_reconnect(self) -> None:
|
|
193
|
+
"""Called after a successful reconnection."""
|
|
194
|
+
|
|
195
|
+
# --- Shutdown hooks ---
|
|
196
|
+
|
|
197
|
+
# noinspection PyMethodMayBeStatic
|
|
198
|
+
async def can_shutdown(self) -> bool:
|
|
199
|
+
"""
|
|
200
|
+
Whether the provider is ready to shut down.
|
|
201
|
+
|
|
202
|
+
Override to delay shutdown while cleanup is in progress
|
|
203
|
+
(e.g. waiting for open orders to fill or positions to close).
|
|
204
|
+
Called every second during the graceful shutdown phase.
|
|
205
|
+
|
|
206
|
+
:return: True if ready to shut down, False to keep waiting.
|
|
207
|
+
"""
|
|
208
|
+
return True
|
|
@@ -0,0 +1,331 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from typing import Callable, NamedTuple, TYPE_CHECKING
|
|
3
|
+
from abc import abstractmethod, ABCMeta
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from datetime import datetime
|
|
6
|
+
|
|
7
|
+
from pynecore.types.ohlcv import OHLCV
|
|
8
|
+
from pynecore.core.syminfo import SymInfo, default_mincontract
|
|
9
|
+
from pynecore.core.ohlcv_file import OHLCVWriter, OHLCVReader
|
|
10
|
+
|
|
11
|
+
from . import Plugin, ConfigT
|
|
12
|
+
|
|
13
|
+
if TYPE_CHECKING:
|
|
14
|
+
from pynecore.core.symbol_map import SymbolMap
|
|
15
|
+
|
|
16
|
+
logger = logging.getLogger(__name__)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class Broker(NamedTuple):
|
|
20
|
+
"""A selectable broker / exchange of a :attr:`~ProviderPlugin.multi_broker` provider.
|
|
21
|
+
|
|
22
|
+
:ivar id: The canonical selector used in the provider string and the saved
|
|
23
|
+
filename (e.g. ``"pepperstoneuk"``, ``"binance"``). Must be space-free.
|
|
24
|
+
:ivar name: A human-readable display name (e.g. ``"Pepperstone - Europe"``,
|
|
25
|
+
``"Binance"``), or ``""`` when the provider exposes none.
|
|
26
|
+
"""
|
|
27
|
+
id: str
|
|
28
|
+
name: str = ""
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class ProviderPlugin(Plugin[ConfigT], metaclass=ABCMeta):
|
|
32
|
+
"""
|
|
33
|
+
Base class for all data providers.
|
|
34
|
+
|
|
35
|
+
Subclasses must implement the abstract methods and define a ``Config``
|
|
36
|
+
dataclass for configuration (used by :func:`pynecore.core.config.ensure_config`).
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
timezone: str = 'UTC'
|
|
40
|
+
"""Default timezone of the provider."""
|
|
41
|
+
|
|
42
|
+
symbol: str | None = None
|
|
43
|
+
"""Symbol of the provider."""
|
|
44
|
+
|
|
45
|
+
timeframe: str | None = None
|
|
46
|
+
"""Timeframe of the provider."""
|
|
47
|
+
|
|
48
|
+
xchg_timeframe: str | None = None
|
|
49
|
+
"""Exchange-specific timeframe format."""
|
|
50
|
+
|
|
51
|
+
ohlcv_path: Path | None = None
|
|
52
|
+
"""Path to the OHLCV data file."""
|
|
53
|
+
|
|
54
|
+
syminfo: SymInfo | None = None
|
|
55
|
+
"""Symbol info pre-fetched by the chart side (security subprocesses store
|
|
56
|
+
it here instead of issuing a second REST round-trip)."""
|
|
57
|
+
|
|
58
|
+
global_symbol_map: 'SymbolMap | None' = None
|
|
59
|
+
"""Global workdir symbol map (``config/symbol_map.toml``), consulted by
|
|
60
|
+
:meth:`resolve_symbol` AFTER the plugin's own ``config.symbol_map``. Set by
|
|
61
|
+
the framework on the chart provider instance; ``None`` disables the fallback."""
|
|
62
|
+
|
|
63
|
+
provider_name: str | None = None
|
|
64
|
+
"""This provider's entry-point name, used to gate global-map entries to the
|
|
65
|
+
running provider (an entry naming a different provider is warned + skipped,
|
|
66
|
+
pending multi-provider support)."""
|
|
67
|
+
|
|
68
|
+
fetch_all_by_default: bool = False
|
|
69
|
+
"""If True, fetch all available data when no start date is given (instead of 1 year)."""
|
|
70
|
+
|
|
71
|
+
multi_broker: bool = False
|
|
72
|
+
"""If True, this provider serves many brokers/exchanges and the first segment
|
|
73
|
+
of the provider string after the provider name selects the broker
|
|
74
|
+
(e.g. ``ccxt:BYBIT:BTC/USDT:USDT`` → broker ``BYBIT``). Single-broker
|
|
75
|
+
providers leave this ``False`` and treat the whole string as the symbol."""
|
|
76
|
+
|
|
77
|
+
mincontract_estimated: bool = False
|
|
78
|
+
"""True when the last :meth:`get_symbol_info` fetch had to estimate
|
|
79
|
+
``mincontract`` because the provider returned no exchange value. The
|
|
80
|
+
download flow then refines the estimate from the downloaded volume data."""
|
|
81
|
+
|
|
82
|
+
@classmethod
|
|
83
|
+
@abstractmethod
|
|
84
|
+
def to_tradingview_timeframe(cls, timeframe: str) -> str:
|
|
85
|
+
"""
|
|
86
|
+
Convert timeframe to TradingView format.
|
|
87
|
+
|
|
88
|
+
:param timeframe: Timeframe in exchange format.
|
|
89
|
+
:return: Timeframe in TradingView format.
|
|
90
|
+
"""
|
|
91
|
+
|
|
92
|
+
@classmethod
|
|
93
|
+
@abstractmethod
|
|
94
|
+
def to_exchange_timeframe(cls, timeframe: str) -> str:
|
|
95
|
+
"""
|
|
96
|
+
Convert timeframe to exchange format.
|
|
97
|
+
|
|
98
|
+
:param timeframe: Timeframe in TradingView format.
|
|
99
|
+
:return: Timeframe in exchange format.
|
|
100
|
+
"""
|
|
101
|
+
|
|
102
|
+
@classmethod
|
|
103
|
+
def get_ohlcv_path(cls, symbol: str, timeframe: str, ohlcv_dir: Path,
|
|
104
|
+
provider_name: str | None = None) -> Path:
|
|
105
|
+
"""
|
|
106
|
+
Get the output path of the OHLCV data file.
|
|
107
|
+
|
|
108
|
+
:param symbol: Symbol name.
|
|
109
|
+
:param timeframe: Timeframe in TradingView format.
|
|
110
|
+
:param ohlcv_dir: Directory to save OHLCV data.
|
|
111
|
+
:param provider_name: Override provider name in filename.
|
|
112
|
+
:return: Path to the OHLCV file.
|
|
113
|
+
"""
|
|
114
|
+
return ohlcv_dir / (f"{provider_name or cls.__name__.lower().replace('provider', '').replace('plugin', '')}"
|
|
115
|
+
f"_{symbol.replace('/', '_').replace(':', '_').upper()}"
|
|
116
|
+
f"_{timeframe}.ohlcv")
|
|
117
|
+
|
|
118
|
+
def __init__(self, *, symbol: str | None = None, timeframe: str | None = None,
|
|
119
|
+
ohlcv_dir: Path | None = None, config: ConfigT | None = None):
|
|
120
|
+
"""
|
|
121
|
+
:param symbol: The symbol to get data for.
|
|
122
|
+
:param timeframe: The timeframe to get data for in TradingView format.
|
|
123
|
+
:param ohlcv_dir: The directory to save OHLCV data.
|
|
124
|
+
:param config: Pre-loaded config dataclass instance.
|
|
125
|
+
"""
|
|
126
|
+
self.symbol = symbol
|
|
127
|
+
self.timeframe = timeframe
|
|
128
|
+
self.xchg_timeframe = self.to_exchange_timeframe(timeframe) if timeframe else None
|
|
129
|
+
if ohlcv_dir:
|
|
130
|
+
assert symbol and timeframe
|
|
131
|
+
self.ohlcv_path = self.get_ohlcv_path(symbol, timeframe, ohlcv_dir)
|
|
132
|
+
else:
|
|
133
|
+
self.ohlcv_path = None
|
|
134
|
+
self.ohlcv_file = OHLCVWriter(self.ohlcv_path) if self.ohlcv_path else None
|
|
135
|
+
self.config: ConfigT | None = config
|
|
136
|
+
|
|
137
|
+
def normalize_symbol(self, symbol: str) -> str:
|
|
138
|
+
"""
|
|
139
|
+
Normalize a provider-format symbol to the exchange API format.
|
|
140
|
+
|
|
141
|
+
Called by the framework before passing ``symbol`` to :meth:`watch_ohlcv`
|
|
142
|
+
in the live runner. For historical methods (:meth:`download_ohlcv`,
|
|
143
|
+
:meth:`update_symbol_info`), providers use ``self.symbol`` directly —
|
|
144
|
+
handle any needed format conversion in ``__init__`` instead.
|
|
145
|
+
|
|
146
|
+
Override when the user-configured symbol includes prefixes or formatting
|
|
147
|
+
that the exchange API cannot accept
|
|
148
|
+
(e.g. stripping ``"binance:"`` from ``"binance:BTC/USDT"``).
|
|
149
|
+
|
|
150
|
+
:param symbol: Symbol as configured by the user.
|
|
151
|
+
:return: Symbol in the format the exchange API expects.
|
|
152
|
+
"""
|
|
153
|
+
return symbol
|
|
154
|
+
|
|
155
|
+
def resolve_symbol(self, pine_key: str) -> str:
|
|
156
|
+
"""
|
|
157
|
+
Translate a Pine-style symbol key to the plugin-native form.
|
|
158
|
+
|
|
159
|
+
Live ``request.security()`` calls hand the framework a TradingView-style
|
|
160
|
+
symbol (e.g. ``"FX:EURUSD"``). This method consults
|
|
161
|
+
``config.symbol_map`` first (the per-plugin TOML translation table),
|
|
162
|
+
then the global workdir ``symbol_map.toml`` (:attr:`global_symbol_map`,
|
|
163
|
+
only for entries whose provider matches :attr:`provider_name` — an entry
|
|
164
|
+
naming a different provider is warned + skipped); if the key is not
|
|
165
|
+
mapped the default fallback is the identity, i.e. the Pine key is
|
|
166
|
+
forwarded unchanged on the assumption that the user already wrote a
|
|
167
|
+
plugin-native symbol.
|
|
168
|
+
|
|
169
|
+
``normalize_symbol`` is deliberately **not** used as the fallback:
|
|
170
|
+
provider instances bind ``normalize_symbol`` to the chart's own
|
|
171
|
+
symbol (e.g. CCXT's returns ``self.symbol`` regardless of the
|
|
172
|
+
argument), so consulting it for a cross-symbol key would silently
|
|
173
|
+
resolve to the chart symbol and download wrong data.
|
|
174
|
+
|
|
175
|
+
Plugins that need real cross-symbol translation should override
|
|
176
|
+
:meth:`resolve_symbol` directly.
|
|
177
|
+
|
|
178
|
+
:param pine_key: Symbol as written in the Pine script.
|
|
179
|
+
:return: Symbol in the format the plugin's exchange API expects.
|
|
180
|
+
"""
|
|
181
|
+
sm = getattr(self.config, 'symbol_map', None)
|
|
182
|
+
if sm and pine_key in sm:
|
|
183
|
+
return sm[pine_key]
|
|
184
|
+
gm = self.global_symbol_map
|
|
185
|
+
if gm:
|
|
186
|
+
mapped = gm.resolve(pine_key)
|
|
187
|
+
if mapped is not None:
|
|
188
|
+
if self.provider_name is not None and mapped.provider != self.provider_name:
|
|
189
|
+
logger.warning(
|
|
190
|
+
"Skipping global symbol_map entry %r -> %r: it targets "
|
|
191
|
+
"provider %r but the running provider is %r "
|
|
192
|
+
"(multi-provider resolution is not supported yet).",
|
|
193
|
+
pine_key, f"{mapped.provider}:{mapped.native_symbol}",
|
|
194
|
+
mapped.provider, self.provider_name)
|
|
195
|
+
else:
|
|
196
|
+
return mapped.native_symbol
|
|
197
|
+
return pine_key
|
|
198
|
+
|
|
199
|
+
@classmethod
|
|
200
|
+
def construct_pair_symbol(cls, from_cur: str, to_cur: str) -> str:
|
|
201
|
+
"""
|
|
202
|
+
Build a Pine-style symbol for a currency pair.
|
|
203
|
+
|
|
204
|
+
Used by the auto-spawn rate-source path when a Pine script needs a
|
|
205
|
+
``(from_cur, to_cur)`` rate that is not already exposed by the chart
|
|
206
|
+
or by an explicit ``request.security()`` context. The default
|
|
207
|
+
concatenation (``"EUR" + "USD" -> "EURUSD"``) matches the most common
|
|
208
|
+
FX symbol convention; plugins whose API expects a different shape
|
|
209
|
+
(e.g. ``"EUR-USD"`` or ``"EUR/USD"``) can override.
|
|
210
|
+
|
|
211
|
+
The returned key is fed through :meth:`resolve_symbol`, so users can
|
|
212
|
+
still keep TradingView prefixes (``"FX:EURUSD"``) in their
|
|
213
|
+
``symbol_map`` instead of relying on the raw concatenation.
|
|
214
|
+
"""
|
|
215
|
+
return f"{from_cur}{to_cur}"
|
|
216
|
+
|
|
217
|
+
def __enter__(self) -> OHLCVWriter:
|
|
218
|
+
assert self.ohlcv_file is not None
|
|
219
|
+
return self.ohlcv_file.open()
|
|
220
|
+
|
|
221
|
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
222
|
+
assert self.ohlcv_file is not None
|
|
223
|
+
self.ohlcv_file.close()
|
|
224
|
+
|
|
225
|
+
@classmethod
|
|
226
|
+
def get_list_of_brokers(cls) -> list[Broker]:
|
|
227
|
+
"""
|
|
228
|
+
Get the list of brokers/exchanges this provider can serve.
|
|
229
|
+
|
|
230
|
+
Only meaningful for :attr:`multi_broker` providers. Optional — the
|
|
231
|
+
default raises :class:`NotImplementedError`, which the ``pyne data``
|
|
232
|
+
CLI catches and reports gracefully. Implemented as a classmethod so it
|
|
233
|
+
can answer ``--list-brokers`` without a symbol-bound instance.
|
|
234
|
+
|
|
235
|
+
:return: List of :class:`Broker` records (``id`` selector + optional
|
|
236
|
+
human-readable ``name``).
|
|
237
|
+
:raises NotImplementedError: If the provider does not enumerate brokers.
|
|
238
|
+
"""
|
|
239
|
+
raise NotImplementedError(
|
|
240
|
+
f"{cls.__name__} does not support listing brokers"
|
|
241
|
+
)
|
|
242
|
+
|
|
243
|
+
@abstractmethod
|
|
244
|
+
def get_list_of_symbols(self, *args, **kwargs) -> list[str]:
|
|
245
|
+
"""
|
|
246
|
+
Get list of available symbols.
|
|
247
|
+
|
|
248
|
+
:return: List of symbol names.
|
|
249
|
+
"""
|
|
250
|
+
|
|
251
|
+
@abstractmethod
|
|
252
|
+
def update_symbol_info(self) -> SymInfo:
|
|
253
|
+
"""
|
|
254
|
+
Fetch and return symbol info from the exchange.
|
|
255
|
+
|
|
256
|
+
This should include opening hours and session data.
|
|
257
|
+
|
|
258
|
+
:return: Symbol information.
|
|
259
|
+
"""
|
|
260
|
+
|
|
261
|
+
def is_symbol_info_exists(self) -> bool:
|
|
262
|
+
"""
|
|
263
|
+
Check if the symbol info TOML file exists.
|
|
264
|
+
|
|
265
|
+
:return: True if the file exists.
|
|
266
|
+
"""
|
|
267
|
+
assert self.ohlcv_path is not None
|
|
268
|
+
return self.ohlcv_path.with_suffix('.toml').exists()
|
|
269
|
+
|
|
270
|
+
def get_symbol_info(self, force_update=False) -> SymInfo:
|
|
271
|
+
"""
|
|
272
|
+
Get symbol info, loading from cache or fetching from exchange.
|
|
273
|
+
|
|
274
|
+
:param force_update: Force update from exchange even if cached.
|
|
275
|
+
:return: Symbol information.
|
|
276
|
+
"""
|
|
277
|
+
assert self.ohlcv_path is not None
|
|
278
|
+
toml_path = self.ohlcv_path.with_suffix('.toml')
|
|
279
|
+
if self.is_symbol_info_exists() and not force_update:
|
|
280
|
+
return SymInfo.load_toml(toml_path)
|
|
281
|
+
|
|
282
|
+
sym_info = self.update_symbol_info()
|
|
283
|
+
if sym_info.mincontract <= 0.0:
|
|
284
|
+
# No exchange value (providers signal that with 0.0): estimate it.
|
|
285
|
+
# The download flow refines the estimate from the downloaded
|
|
286
|
+
# volume data, see ``mincontract_estimated``.
|
|
287
|
+
sym_info.mincontract = default_mincontract(sym_info.type, sym_info.basecurrency)
|
|
288
|
+
self.mincontract_estimated = True
|
|
289
|
+
sym_info.save_toml(toml_path)
|
|
290
|
+
return sym_info
|
|
291
|
+
|
|
292
|
+
def save_ohlcv_data(self, data: OHLCV | list[OHLCV]):
|
|
293
|
+
"""
|
|
294
|
+
Save OHLCV data to the file.
|
|
295
|
+
|
|
296
|
+
:param data: Single OHLCV record or list of records.
|
|
297
|
+
"""
|
|
298
|
+
assert self.ohlcv_file is not None
|
|
299
|
+
if isinstance(data, OHLCV):
|
|
300
|
+
self.ohlcv_file.write(data)
|
|
301
|
+
else:
|
|
302
|
+
for candle in data:
|
|
303
|
+
self.ohlcv_file.write(candle)
|
|
304
|
+
|
|
305
|
+
@abstractmethod
|
|
306
|
+
def download_ohlcv(self, time_from: datetime, time_to: datetime,
|
|
307
|
+
on_progress: Callable[[datetime], None] | None = None,
|
|
308
|
+
limit: int | None = None, with_extra: bool = False):
|
|
309
|
+
"""
|
|
310
|
+
Download OHLCV data from the exchange.
|
|
311
|
+
|
|
312
|
+
Use :meth:`save_ohlcv_data` to write records to the data file.
|
|
313
|
+
|
|
314
|
+
:param time_from: The start time. Use ``datetime.fromtimestamp(0)`` to fetch all available data.
|
|
315
|
+
:param time_to: The end time.
|
|
316
|
+
:param on_progress: Optional progress callback.
|
|
317
|
+
:param limit: Override the automatic chunk size (number of bars per API request).
|
|
318
|
+
:param with_extra: When ``True``, also fetch and persist the provider's
|
|
319
|
+
extra per-bar fields (e.g. ask/bid/spread) to the ``.extra.csv``
|
|
320
|
+
sidecar. Off by default: the extra fields cost extra requests to
|
|
321
|
+
fetch and slow every later backtest that loads the sidecar, so they
|
|
322
|
+
are only produced on request. Providers without extra fields ignore it.
|
|
323
|
+
"""
|
|
324
|
+
|
|
325
|
+
def load_ohlcv_data(self) -> OHLCVReader:
|
|
326
|
+
"""
|
|
327
|
+
Load OHLCV data from the file.
|
|
328
|
+
|
|
329
|
+
:return: An OHLCVReader instance.
|
|
330
|
+
"""
|
|
331
|
+
return OHLCVReader(str(self.ohlcv_path))
|