Qubx 0.2.78__cp311-cp311-manylinux_2_35_x86_64.whl → 0.2.80__cp311-cp311-manylinux_2_35_x86_64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- qubx/__init__.py +4 -2
- qubx/_nb_magic.py +1 -0
- qubx/backtester/simulator.py +19 -1
- qubx/core/context.py +45 -24
- qubx/core/metrics.py +1 -1
- qubx/core/series.cpython-311-x86_64-linux-gnu.so +0 -0
- qubx/core/series.pyi +1 -0
- qubx/core/strategy.py +113 -15
- qubx/core/utils.cpython-311-x86_64-linux-gnu.so +0 -0
- qubx/data/__init__.py +2 -0
- qubx/data/helpers.py +386 -21
- qubx/data/readers.py +58 -2
- qubx/math/stats.py +47 -0
- qubx/pandaz/utils.py +75 -2
- qubx/ta/indicators.cpython-311-x86_64-linux-gnu.so +0 -0
- qubx/ta/indicators.pxd +7 -0
- qubx/ta/indicators.pyi +7 -0
- qubx/ta/indicators.pyx +47 -4
- qubx/utils/misc.py +4 -4
- {qubx-0.2.78.dist-info → qubx-0.2.80.dist-info}/METADATA +1 -1
- {qubx-0.2.78.dist-info → qubx-0.2.80.dist-info}/RECORD +22 -22
- {qubx-0.2.78.dist-info → qubx-0.2.80.dist-info}/WHEEL +0 -0
qubx/__init__.py
CHANGED
|
@@ -6,6 +6,8 @@ from loguru import logger
|
|
|
6
6
|
import os, sys, stackprinter
|
|
7
7
|
from qubx.core.lookups import FeesLookup, GlobalLookup, InstrumentsLookup
|
|
8
8
|
|
|
9
|
+
# - TODO: import some main methods from packages
|
|
10
|
+
|
|
9
11
|
|
|
10
12
|
def formatter(record):
|
|
11
13
|
end = record["extra"].get("end", "\n")
|
|
@@ -63,7 +65,7 @@ lookup = GlobalLookup(InstrumentsLookup(), FeesLookup())
|
|
|
63
65
|
# registering magic for jupyter notebook
|
|
64
66
|
if runtime_env() in ["notebook", "shell"]:
|
|
65
67
|
from IPython.core.magic import Magics, magics_class, line_magic, line_cell_magic
|
|
66
|
-
from IPython import get_ipython
|
|
68
|
+
from IPython.core.getipython import get_ipython
|
|
67
69
|
|
|
68
70
|
@magics_class
|
|
69
71
|
class QubxMagics(Magics):
|
|
@@ -195,4 +197,4 @@ if runtime_env() in ["notebook", "shell"]:
|
|
|
195
197
|
p.terminate()
|
|
196
198
|
|
|
197
199
|
# - registering magic here
|
|
198
|
-
get_ipython().register_magics(QubxMagics)
|
|
200
|
+
get_ipython().register_magics(QubxMagics) # type: ignore
|
qubx/_nb_magic.py
CHANGED
qubx/backtester/simulator.py
CHANGED
|
@@ -37,6 +37,7 @@ from qubx.core.strategy import (
|
|
|
37
37
|
from qubx.core.context import StrategyContextImpl
|
|
38
38
|
from qubx.backtester.ome import OrdersManagementEngine, OmeReport
|
|
39
39
|
|
|
40
|
+
from qubx.data.helpers import InMemoryCachedReader, TimeGuardedWrapper
|
|
40
41
|
from qubx.data.readers import (
|
|
41
42
|
AsTrades,
|
|
42
43
|
DataReader,
|
|
@@ -428,7 +429,9 @@ class SimulatedExchange(IBrokerServiceProvider):
|
|
|
428
429
|
units = kwargs.get("timestamp_units", "ns")
|
|
429
430
|
|
|
430
431
|
for instr in instruments:
|
|
431
|
-
logger.debug(
|
|
432
|
+
logger.debug(
|
|
433
|
+
f"SimulatedExchangeService :: subscribe :: {instr.symbol}({subscription_type}, {nback}, {timeframe})"
|
|
434
|
+
)
|
|
432
435
|
self._symbol_to_instrument[instr.symbol] = instr
|
|
433
436
|
|
|
434
437
|
_params: Dict[str, Any] = dict(
|
|
@@ -731,6 +734,7 @@ def simulate(
|
|
|
731
734
|
silent: bool = False,
|
|
732
735
|
enable_event_batching: bool = True,
|
|
733
736
|
accurate_stop_orders_execution: bool = False,
|
|
737
|
+
aux_data: DataReader | None = None,
|
|
734
738
|
debug: Literal["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"] | None = "WARNING",
|
|
735
739
|
) -> list[TradingSessionResult]:
|
|
736
740
|
"""
|
|
@@ -773,6 +777,8 @@ def simulate(
|
|
|
773
777
|
If True, enables event batching for optimization.
|
|
774
778
|
accurate_stop_orders_execution (bool):
|
|
775
779
|
If True, enables more accurate stop order execution simulation.
|
|
780
|
+
aux_data (DataReader | None):
|
|
781
|
+
Auxiliary data provider (default is None).
|
|
776
782
|
debug (Literal["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"] | None):
|
|
777
783
|
Logging level for debugging.
|
|
778
784
|
|
|
@@ -842,6 +848,7 @@ def simulate(
|
|
|
842
848
|
silent=silent,
|
|
843
849
|
enable_event_batching=enable_event_batching,
|
|
844
850
|
accurate_stop_orders_execution=accurate_stop_orders_execution,
|
|
851
|
+
aux_data=aux_data,
|
|
845
852
|
)
|
|
846
853
|
|
|
847
854
|
|
|
@@ -906,6 +913,7 @@ def _run_setups(
|
|
|
906
913
|
silent: bool = False,
|
|
907
914
|
enable_event_batching: bool = True,
|
|
908
915
|
accurate_stop_orders_execution: bool = False,
|
|
916
|
+
aux_data: DataReader | None = None,
|
|
909
917
|
) -> List[TradingSessionResult]:
|
|
910
918
|
# loggers don't work well with joblib and multiprocessing in general because they contain
|
|
911
919
|
# open file handlers that cannot be pickled. I found a solution which requires the usage of enqueue=True
|
|
@@ -928,6 +936,7 @@ def _run_setups(
|
|
|
928
936
|
silent=silent,
|
|
929
937
|
enable_event_batching=enable_event_batching,
|
|
930
938
|
accurate_stop_orders_execution=accurate_stop_orders_execution,
|
|
939
|
+
aux_data_provider=aux_data,
|
|
931
940
|
)
|
|
932
941
|
for id, s in enumerate(setups)
|
|
933
942
|
)
|
|
@@ -946,6 +955,7 @@ def _run_setup(
|
|
|
946
955
|
silent: bool = False,
|
|
947
956
|
enable_event_batching: bool = True,
|
|
948
957
|
accurate_stop_orders_execution: bool = False,
|
|
958
|
+
aux_data_provider: InMemoryCachedReader | None = None,
|
|
949
959
|
) -> TradingSessionResult:
|
|
950
960
|
_trigger = trigger
|
|
951
961
|
_stop = stop
|
|
@@ -996,6 +1006,13 @@ def _run_setup(
|
|
|
996
1006
|
case _:
|
|
997
1007
|
raise ValueError(f"Unsupported setup type: {setup.setup_type} !")
|
|
998
1008
|
|
|
1009
|
+
# - check aux data provider
|
|
1010
|
+
_aux_data = None
|
|
1011
|
+
if aux_data_provider is not None:
|
|
1012
|
+
if not isinstance(aux_data_provider, InMemoryCachedReader):
|
|
1013
|
+
logger.error("Aux data provider should be an instance of InMemoryCachedReader! Skipping it.")
|
|
1014
|
+
_aux_data = TimeGuardedWrapper(aux_data_provider, broker)
|
|
1015
|
+
|
|
999
1016
|
ctx = StrategyContextImpl(
|
|
1000
1017
|
strategy=strat, # type: ignore
|
|
1001
1018
|
config=None, # TODO: need to think how we could pass altered parameters here (from variating etc)
|
|
@@ -1007,6 +1024,7 @@ def _run_setup(
|
|
|
1007
1024
|
trigger_spec=_trigger,
|
|
1008
1025
|
fit_spec=fit,
|
|
1009
1026
|
logs_writer=logs_writer,
|
|
1027
|
+
aux_data_provider=_aux_data,
|
|
1010
1028
|
)
|
|
1011
1029
|
ctx.start()
|
|
1012
1030
|
|
qubx/core/context.py
CHANGED
|
@@ -36,6 +36,7 @@ from qubx.core.strategy import (
|
|
|
36
36
|
SubscriptionType,
|
|
37
37
|
)
|
|
38
38
|
from qubx.core.series import Trade, Quote, Bar, OHLCV
|
|
39
|
+
from qubx.data.readers import DataReader
|
|
39
40
|
from qubx.gathering.simplest import SimplePositionGatherer
|
|
40
41
|
from qubx.trackers.sizers import FixedSizer
|
|
41
42
|
from qubx.utils.misc import Stopwatch
|
|
@@ -74,6 +75,9 @@ class StrategyContextImpl(StrategyContext):
|
|
|
74
75
|
_cache: CachedMarketDataHolder # market data cache
|
|
75
76
|
_scheduler: BasicScheduler
|
|
76
77
|
|
|
78
|
+
# - aux data provider
|
|
79
|
+
_aux_data_provider: DataReader | None # auxiliary data provider
|
|
80
|
+
|
|
77
81
|
# - configuration
|
|
78
82
|
_market_data_subcription_type: str = "unknown"
|
|
79
83
|
_market_data_subcription_params: dict = dict()
|
|
@@ -126,6 +130,9 @@ class StrategyContextImpl(StrategyContext):
|
|
|
126
130
|
# - - - - - - - - - - - - - - - - - - - - -
|
|
127
131
|
# - signals executor configuration - - - -
|
|
128
132
|
position_gathering: IPositionGathering | None = None,
|
|
133
|
+
# - - - - - - - - - - - - - - - - - - - - -
|
|
134
|
+
# - aux data provider - - - - - - - - - - -
|
|
135
|
+
aux_data_provider: DataReader | None = None,
|
|
129
136
|
) -> None:
|
|
130
137
|
# - initialization
|
|
131
138
|
self.broker_provider = broker_connector
|
|
@@ -146,6 +153,7 @@ class StrategyContextImpl(StrategyContext):
|
|
|
146
153
|
self.__init_fit_was_called = False
|
|
147
154
|
self.__pool = None
|
|
148
155
|
self.__fails_counter = 0
|
|
156
|
+
self._aux_data_provider = aux_data_provider
|
|
149
157
|
|
|
150
158
|
# - for fast access to instrument by it's symbol
|
|
151
159
|
self._symb_to_instr = {i.symbol: i for i in instruments}
|
|
@@ -751,36 +759,12 @@ class StrategyContextImpl(StrategyContext):
|
|
|
751
759
|
if order_id:
|
|
752
760
|
self.trading_service.cancel_order(order_id)
|
|
753
761
|
|
|
754
|
-
def quote(self, symbol: str) -> Quote | None:
|
|
755
|
-
return self.broker_provider.get_quote(symbol)
|
|
756
|
-
|
|
757
762
|
def get_capital(self) -> float:
|
|
758
763
|
return self.trading_service.get_capital()
|
|
759
764
|
|
|
760
765
|
def get_reserved(self, instrument: Instrument) -> float:
|
|
761
766
|
return self.trading_service.get_account().get_reserved(instrument)
|
|
762
767
|
|
|
763
|
-
@_SW.watch("StrategyContext")
|
|
764
|
-
def get_historical_ohlcs(self, instrument: Instrument | str, timeframe: str, length: int) -> OHLCV | None:
|
|
765
|
-
"""
|
|
766
|
-
Helper for historical ohlc data
|
|
767
|
-
"""
|
|
768
|
-
instr = self._symb_to_instr.get(instrument) if isinstance(instrument, str) else instrument
|
|
769
|
-
|
|
770
|
-
if instr is None:
|
|
771
|
-
logger.warning(f"Can't find instrument for {instrument} symbol !")
|
|
772
|
-
return None
|
|
773
|
-
|
|
774
|
-
# - first check if we can use cached series
|
|
775
|
-
rc = self.ohlc(instr, timeframe)
|
|
776
|
-
if len(rc) >= length:
|
|
777
|
-
return rc
|
|
778
|
-
|
|
779
|
-
# - send request for historical data
|
|
780
|
-
bars = self.broker_provider.get_historical_ohlcs(instr.symbol, timeframe, length)
|
|
781
|
-
r = self._cache.update_by_bars(instr.symbol, timeframe, bars)
|
|
782
|
-
return r
|
|
783
|
-
|
|
784
768
|
@_SW.watch("StrategyContext")
|
|
785
769
|
def set_universe(self, instruments: list[Instrument]) -> None:
|
|
786
770
|
for instr in instruments:
|
|
@@ -901,3 +885,40 @@ class StrategyContextImpl(StrategyContext):
|
|
|
901
885
|
if self.__pool is None:
|
|
902
886
|
self.__pool = ThreadPool(2)
|
|
903
887
|
self.__pool.apply_async(func, args)
|
|
888
|
+
|
|
889
|
+
# - IMarketDataProvider methods implementation -
|
|
890
|
+
|
|
891
|
+
@_SW.watch("StrategyContext")
|
|
892
|
+
def get_historical_ohlcs(self, instrument: Instrument | str, timeframe: str, length: int) -> OHLCV | None:
|
|
893
|
+
"""
|
|
894
|
+
Helper for historical ohlc data
|
|
895
|
+
"""
|
|
896
|
+
instr = self._symb_to_instr.get(instrument) if isinstance(instrument, str) else instrument
|
|
897
|
+
|
|
898
|
+
if instr is None:
|
|
899
|
+
logger.warning(f"Can't find instrument for {instrument} symbol !")
|
|
900
|
+
return None
|
|
901
|
+
|
|
902
|
+
# - first check if we can use cached series
|
|
903
|
+
rc = self.ohlc(instr, timeframe)
|
|
904
|
+
if len(rc) >= length:
|
|
905
|
+
return rc
|
|
906
|
+
|
|
907
|
+
# - send request for historical data
|
|
908
|
+
bars = self.broker_provider.get_historical_ohlcs(instr.symbol, timeframe, length)
|
|
909
|
+
r = self._cache.update_by_bars(instr.symbol, timeframe, bars)
|
|
910
|
+
return r
|
|
911
|
+
|
|
912
|
+
def get_aux_data(self, data_id: str, **parameters) -> pd.DataFrame | None:
|
|
913
|
+
if self._aux_data_provider:
|
|
914
|
+
return self._aux_data_provider.get_aux_data(data_id, **parameters)
|
|
915
|
+
return None
|
|
916
|
+
|
|
917
|
+
def get_instruments(self) -> List[Instrument]:
|
|
918
|
+
return self.instruments
|
|
919
|
+
|
|
920
|
+
def quote(self, symbol: str) -> Quote | None:
|
|
921
|
+
return self.broker_provider.get_quote(symbol)
|
|
922
|
+
|
|
923
|
+
def get_instrument(self, symbol: str) -> Instrument | None:
|
|
924
|
+
return self._symb_to_instr.get(symbol)
|
qubx/core/metrics.py
CHANGED
|
@@ -340,7 +340,7 @@ def aggregate_returns(returns: pd.Series, convert_to: str) -> pd.DataFrame | pd.
|
|
|
340
340
|
str_check = convert_to.lower()
|
|
341
341
|
resample_mod = None
|
|
342
342
|
if str_check in ["a", "annual", "y", "yearly"]:
|
|
343
|
-
resample_mod = "
|
|
343
|
+
resample_mod = "YE"
|
|
344
344
|
elif str_check in ["m", "monthly", "mon"]:
|
|
345
345
|
resample_mod = "ME"
|
|
346
346
|
elif str_check in ["w", "weekly"]:
|
|
Binary file
|
qubx/core/series.pyi
CHANGED
qubx/core/strategy.py
CHANGED
|
@@ -3,11 +3,6 @@
|
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
5
|
from typing import Any, Callable, Dict, List, Optional, Tuple, Union
|
|
6
|
-
from types import FunctionType
|
|
7
|
-
from collections import defaultdict
|
|
8
|
-
from dataclasses import dataclass
|
|
9
|
-
from threading import Thread
|
|
10
|
-
from multiprocessing.pool import ThreadPool
|
|
11
6
|
import traceback
|
|
12
7
|
|
|
13
8
|
import pandas as pd
|
|
@@ -30,28 +25,58 @@ from qubx.core.basics import (
|
|
|
30
25
|
)
|
|
31
26
|
from qubx.core.series import Trade, Quote, Bar, OHLCV
|
|
32
27
|
from qubx.utils.misc import Stopwatch
|
|
33
|
-
from qubx.utils.time import convert_seconds_to_str
|
|
34
28
|
|
|
35
29
|
|
|
36
30
|
_SW = Stopwatch()
|
|
37
31
|
|
|
38
32
|
|
|
39
33
|
class ITradingServiceProvider(ITimeProvider, IComminucationManager):
|
|
34
|
+
"""
|
|
35
|
+
Trading service provider interface that manages account operations, order placement, and position tracking.
|
|
36
|
+
"""
|
|
37
|
+
|
|
40
38
|
acc: AccountProcessor
|
|
41
39
|
|
|
42
40
|
def set_account(self, account: AccountProcessor):
|
|
41
|
+
"""
|
|
42
|
+
Set the account processor for the trading service provider.
|
|
43
|
+
|
|
44
|
+
:param account: The AccountProcessor object to be set.
|
|
45
|
+
"""
|
|
43
46
|
self.acc = account
|
|
44
47
|
|
|
45
48
|
def get_account(self) -> AccountProcessor:
|
|
49
|
+
"""
|
|
50
|
+
Retrieve the current account processor.
|
|
51
|
+
|
|
52
|
+
:return: The current AccountProcessor object.
|
|
53
|
+
"""
|
|
46
54
|
return self.acc
|
|
47
55
|
|
|
48
56
|
def get_name(self) -> str:
|
|
57
|
+
"""
|
|
58
|
+
Get the name of the trading service provider.
|
|
59
|
+
|
|
60
|
+
:return: The name of the trading service provider as a string.
|
|
61
|
+
:raises NotImplementedError: If the method is not implemented by the subclass.
|
|
62
|
+
"""
|
|
49
63
|
raise NotImplementedError("get_name is not implemented")
|
|
50
64
|
|
|
51
65
|
def get_account_id(self) -> str:
|
|
66
|
+
"""
|
|
67
|
+
Get the account ID associated with the trading service provider.
|
|
68
|
+
|
|
69
|
+
:return: The account ID as a string.
|
|
70
|
+
:raises NotImplementedError: If the method is not implemented by the subclass.
|
|
71
|
+
"""
|
|
52
72
|
raise NotImplementedError("get_account_id is not implemented")
|
|
53
73
|
|
|
54
74
|
def get_capital(self) -> float:
|
|
75
|
+
"""
|
|
76
|
+
Get the available capital in the account.
|
|
77
|
+
|
|
78
|
+
:return: The free capital as a float.
|
|
79
|
+
"""
|
|
55
80
|
return self.acc.get_free_capital()
|
|
56
81
|
|
|
57
82
|
def send_order(
|
|
@@ -65,25 +90,81 @@ class ITradingServiceProvider(ITimeProvider, IComminucationManager):
|
|
|
65
90
|
time_in_force: str = "gtc",
|
|
66
91
|
**optional,
|
|
67
92
|
) -> Order:
|
|
93
|
+
"""
|
|
94
|
+
Send an order to the trading service.
|
|
95
|
+
|
|
96
|
+
:param instrument: The instrument to trade.
|
|
97
|
+
:param order_side: The side of the order (e.g., "buy" or "sell").
|
|
98
|
+
:param order_type: The type of the order (e.g., "market" or "limit").
|
|
99
|
+
:param amount: The amount of the instrument to trade.
|
|
100
|
+
:param price: The price for limit orders (optional).
|
|
101
|
+
:param client_id: A client-specified ID for the order (optional).
|
|
102
|
+
:param time_in_force: The time in force for the order (default is "gtc" - good till cancelled).
|
|
103
|
+
:param optional: Additional optional parameters for the order.
|
|
104
|
+
:return: An Order object representing the sent order.
|
|
105
|
+
:raises NotImplementedError: If the method is not implemented by the subclass.
|
|
106
|
+
"""
|
|
68
107
|
raise NotImplementedError("send_order is not implemented")
|
|
69
108
|
|
|
70
109
|
def cancel_order(self, order_id: str) -> Order | None:
|
|
110
|
+
"""
|
|
111
|
+
Cancel an existing order.
|
|
112
|
+
|
|
113
|
+
:param order_id: The ID of the order to cancel.
|
|
114
|
+
:return: The cancelled Order object if successful, None otherwise.
|
|
115
|
+
:raises NotImplementedError: If the method is not implemented by the subclass.
|
|
116
|
+
"""
|
|
71
117
|
raise NotImplementedError("cancel_order is not implemented")
|
|
72
118
|
|
|
73
119
|
def get_orders(self, symbol: str | None = None) -> List[Order]:
|
|
120
|
+
"""
|
|
121
|
+
Get a list of current orders, optionally filtered by symbol.
|
|
122
|
+
|
|
123
|
+
:param symbol: The symbol to filter orders by (optional).
|
|
124
|
+
:return: A list of Order objects.
|
|
125
|
+
:raises NotImplementedError: If the method is not implemented by the subclass.
|
|
126
|
+
"""
|
|
74
127
|
raise NotImplementedError("get_orders is not implemented")
|
|
75
128
|
|
|
76
129
|
def get_position(self, instrument: Instrument | str) -> Position:
|
|
130
|
+
"""
|
|
131
|
+
Get the current position for a given instrument.
|
|
132
|
+
|
|
133
|
+
:param instrument: The instrument or symbol to get the position for.
|
|
134
|
+
:return: A Position object representing the current position.
|
|
135
|
+
:raises NotImplementedError: If the method is not implemented by the subclass.
|
|
136
|
+
"""
|
|
77
137
|
raise NotImplementedError("get_position is not implemented")
|
|
78
138
|
|
|
79
139
|
def get_base_currency(self) -> str:
|
|
140
|
+
"""
|
|
141
|
+
Get the base currency for the account.
|
|
142
|
+
|
|
143
|
+
:return: The base currency as a string.
|
|
144
|
+
:raises NotImplementedError: If the method is not implemented by the subclass.
|
|
145
|
+
"""
|
|
80
146
|
raise NotImplementedError("get_basic_currency is not implemented")
|
|
81
147
|
|
|
82
148
|
def process_execution_report(self, symbol: str, report: Dict[str, Any]) -> Tuple[Order, List[Deal]]:
|
|
149
|
+
"""
|
|
150
|
+
Process an execution report for a given symbol.
|
|
151
|
+
|
|
152
|
+
:param symbol: The symbol the execution report is for.
|
|
153
|
+
:param report: A dictionary containing the execution report details.
|
|
154
|
+
:return: A tuple containing the updated Order and a list of Deal objects.
|
|
155
|
+
:raises NotImplementedError: If the method is not implemented by the subclass.
|
|
156
|
+
"""
|
|
83
157
|
raise NotImplementedError("process_execution_report is not implemented")
|
|
84
158
|
|
|
85
159
|
@staticmethod
|
|
86
160
|
def _extract_price(update: float | Quote | Trade | Bar) -> float:
|
|
161
|
+
"""
|
|
162
|
+
Extract the price from various types of market data updates.
|
|
163
|
+
|
|
164
|
+
:param update: The market data update, which can be a float, Quote, Trade, or Bar.
|
|
165
|
+
:return: The extracted price as a float.
|
|
166
|
+
:raises ValueError: If the update type is unknown.
|
|
167
|
+
"""
|
|
87
168
|
if isinstance(update, float):
|
|
88
169
|
return update
|
|
89
170
|
elif isinstance(update, Quote):
|
|
@@ -96,6 +177,13 @@ class ITradingServiceProvider(ITimeProvider, IComminucationManager):
|
|
|
96
177
|
raise ValueError(f"Unknown update type: {type(update)}")
|
|
97
178
|
|
|
98
179
|
def update_position_price(self, symbol: str, timestamp: dt_64, update: float | Quote | Trade | Bar):
|
|
180
|
+
"""
|
|
181
|
+
Update the price of a position for a given symbol.
|
|
182
|
+
|
|
183
|
+
:param symbol: The symbol of the position to update.
|
|
184
|
+
:param timestamp: The timestamp of the update.
|
|
185
|
+
:param update: The price update, which can be a float, Quote, Trade, or Bar.
|
|
186
|
+
"""
|
|
99
187
|
self.acc.update_position_price(timestamp, symbol, ITradingServiceProvider._extract_price(update))
|
|
100
188
|
|
|
101
189
|
|
|
@@ -146,7 +234,25 @@ class SubscriptionType:
|
|
|
146
234
|
OHLC = "ohlc"
|
|
147
235
|
|
|
148
236
|
|
|
149
|
-
class
|
|
237
|
+
class IMarketDataProvider(ITimeProvider):
|
|
238
|
+
"""
|
|
239
|
+
Interface for market data providing class
|
|
240
|
+
"""
|
|
241
|
+
|
|
242
|
+
def ohlc(self, instrument: Instrument | str, timeframe: str) -> OHLCV: ...
|
|
243
|
+
|
|
244
|
+
def quote(self, symbol: str) -> Quote | None: ...
|
|
245
|
+
|
|
246
|
+
def get_historical_ohlcs(self, instrument: Instrument | str, timeframe: str, length: int) -> OHLCV | None: ...
|
|
247
|
+
|
|
248
|
+
def get_aux_data(self, data_id: str, **parametes) -> pd.DataFrame | None: ...
|
|
249
|
+
|
|
250
|
+
def get_instruments(self) -> List[Instrument]: ...
|
|
251
|
+
|
|
252
|
+
def get_instrument(self, symbol: str) -> Instrument | None: ...
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
class StrategyContext(IMarketDataProvider):
|
|
150
256
|
"""
|
|
151
257
|
Strategy context interface
|
|
152
258
|
"""
|
|
@@ -158,14 +264,10 @@ class StrategyContext(ITimeProvider):
|
|
|
158
264
|
|
|
159
265
|
def process_data(self, symbol: str, d_type: str, data: Any) -> bool: ...
|
|
160
266
|
|
|
161
|
-
def ohlc(self, instrument: str | Instrument, timeframe: str) -> OHLCV: ...
|
|
162
|
-
|
|
163
267
|
def start(self, blocking: bool = False): ...
|
|
164
268
|
|
|
165
269
|
def stop(self): ...
|
|
166
270
|
|
|
167
|
-
def time(self) -> dt_64: ...
|
|
168
|
-
|
|
169
271
|
def trade(
|
|
170
272
|
self,
|
|
171
273
|
instr_or_symbol: Instrument | str,
|
|
@@ -179,14 +281,10 @@ class StrategyContext(ITimeProvider):
|
|
|
179
281
|
|
|
180
282
|
def cancel_order(self, order_id: str): ...
|
|
181
283
|
|
|
182
|
-
def quote(self, symbol: str) -> Quote | None: ...
|
|
183
|
-
|
|
184
284
|
def get_capital(self) -> float: ...
|
|
185
285
|
|
|
186
286
|
def get_reserved(self, instrument: Instrument) -> float: ...
|
|
187
287
|
|
|
188
|
-
def get_historical_ohlcs(self, instrument: Instrument | str, timeframe: str, length: int) -> OHLCV | None: ...
|
|
189
|
-
|
|
190
288
|
def set_universe(self, instruments: list[Instrument]): ...
|
|
191
289
|
|
|
192
290
|
def subscribe(self, subscription_type: str, instr_or_symbol: Instrument | str, **kwargs) -> bool: ...
|
|
Binary file
|