onesecondtrader 0.15.0__py3-none-any.whl → 0.16.0__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.
@@ -1,12 +0,0 @@
1
- """
2
- The Trading Infrastructure Toolkit for Python.
3
-
4
- Research, simulate, and deploy algorithmic trading strategies — all in one place.
5
- """
6
-
7
- from .monitoring.console import logger
8
-
9
-
10
- __all__ = [
11
- "logger",
12
- ]
@@ -0,0 +1,102 @@
1
+ """
2
+ OneSecondTrader's library of pre-built indicators.
3
+ """
4
+
5
+ import abc
6
+ import enum
7
+ import numpy as np
8
+ import threading
9
+
10
+ from collections import deque
11
+ from onesecondtrader.ontology import Bar
12
+
13
+
14
+ class BaseIndicator(abc.ABC):
15
+ """
16
+ Base class for indicators. Subclasses must set the `name` property and implement
17
+ the `_compute_indicator()` method. See `SimpleMovingAverage` for an example.
18
+ """
19
+
20
+ def __init__(self, max_history: int = 100) -> None:
21
+ self._lock = threading.Lock()
22
+ self._history: deque[float] = deque(maxlen=max(1, int(max_history)))
23
+
24
+ @property
25
+ @abc.abstractmethod
26
+ def name(self) -> str:
27
+ pass
28
+
29
+ def update(self, incoming_bar: Bar) -> None:
30
+ _latest_value: float = self._compute_indicator(incoming_bar)
31
+ with self._lock:
32
+ self._history.append(_latest_value)
33
+
34
+ @abc.abstractmethod
35
+ def _compute_indicator(self, incoming_bar: Bar) -> float:
36
+ pass
37
+
38
+ @property
39
+ def latest(self) -> float:
40
+ with self._lock:
41
+ return self._history[-1] if self._history else np.nan
42
+
43
+ @property
44
+ def history(self) -> deque[float]:
45
+ return self._history
46
+
47
+
48
+ class InputSource(enum.Enum):
49
+ """
50
+ Enum of supported input sources for indicators. Indicators with a `input_source`
51
+ parameter can be configured to use one of these sources for their calculations.
52
+ """
53
+
54
+ OPEN = enum.auto()
55
+ HIGH = enum.auto()
56
+ LOW = enum.auto()
57
+ CLOSE = enum.auto()
58
+ VOLUME = enum.auto()
59
+
60
+
61
+ class SimpleMovingAverage(BaseIndicator):
62
+ """
63
+ Simple Moving Average (SMA) indicator. Can be configured to use different input
64
+ sources (see `InputSource` enum, default is `InputSource.CLOSE`).
65
+ """
66
+
67
+ def __init__(
68
+ self,
69
+ period: int = 200,
70
+ max_history: int = 100,
71
+ input_source: InputSource = InputSource.CLOSE,
72
+ ) -> None:
73
+ super().__init__(max_history=max_history)
74
+ self.period: int = max(1, int(period))
75
+ self.input_source: InputSource = input_source
76
+ self._window: deque[float] = deque(maxlen=self.period)
77
+
78
+ @property
79
+ def name(self) -> str:
80
+ return f"SMA_{self.period}_{self.input_source.name}"
81
+
82
+ def _compute_indicator(self, bar: Bar) -> float:
83
+ value: float = self._extract_input(bar)
84
+ self._window.append(value)
85
+ if len(self._window) < self.period:
86
+ return np.nan
87
+ return sum(self._window) / self.period
88
+
89
+ def _extract_input(self, bar: Bar) -> float:
90
+ match self.input_source:
91
+ case InputSource.OPEN:
92
+ return float(bar.open)
93
+ case InputSource.HIGH:
94
+ return float(bar.high)
95
+ case InputSource.LOW:
96
+ return float(bar.low)
97
+ case InputSource.CLOSE:
98
+ return float(bar.close)
99
+ case InputSource.VOLUME:
100
+ return float(bar.volume)
101
+ case _:
102
+ return float(bar.close)
@@ -0,0 +1,18 @@
1
+ """
2
+ Domain-specific data models that are used system-wide.
3
+ """
4
+
5
+ import dataclasses
6
+
7
+
8
+ @dataclasses.dataclass(slots=True)
9
+ class Bar:
10
+ """
11
+ Data model for OHLCV bar data.
12
+ """
13
+
14
+ open: float
15
+ high: float
16
+ low: float
17
+ close: float
18
+ volume: int
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: onesecondtrader
3
- Version: 0.15.0
3
+ Version: 0.16.0
4
4
  Summary: The Trading Infrastructure Toolkit for Python. Research, simulate, and deploy algorithmic trading strategies — all in one place.
5
5
  License-File: LICENSE
6
6
  Author: Nils P. Kujath
@@ -11,6 +11,7 @@ Classifier: Programming Language :: Python :: 3.11
11
11
  Classifier: Programming Language :: Python :: 3.12
12
12
  Classifier: Programming Language :: Python :: 3.13
13
13
  Classifier: Programming Language :: Python :: 3.14
14
+ Requires-Dist: matplotlib (>=3.10.7,<4.0.0)
14
15
  Requires-Dist: pandas (>=2.3.1,<3.0.0)
15
16
  Requires-Dist: python-dotenv (>=1.0.0,<2.0.0)
16
17
  Description-Content-Type: text/markdown
@@ -0,0 +1,7 @@
1
+ onesecondtrader/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
+ onesecondtrader/indicators.py,sha256=nV7EFlOnNWxdU6-lb4rxvzjfdRIXkS7P-tyH08cSrKM,2967
3
+ onesecondtrader/ontology.py,sha256=z1fipmt6Rh8nS1Z-M3ln197TWPWSvt6rkBQXkdXhekM,263
4
+ onesecondtrader-0.16.0.dist-info/METADATA,sha256=oU16PF9yR3neuhhQNZbE64ZcSdBAnr4j1-_wecDwDkQ,9682
5
+ onesecondtrader-0.16.0.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
6
+ onesecondtrader-0.16.0.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
7
+ onesecondtrader-0.16.0.dist-info/RECORD,,
File without changes
@@ -1,99 +0,0 @@
1
- import abc
2
- from onesecondtrader import messaging
3
- from onesecondtrader.messaging import events
4
-
5
-
6
- class BaseBroker(abc.ABC):
7
- def __init__(self, event_bus: messaging.EventBus | None = None):
8
- self.event_bus: messaging.EventBus = (
9
- event_bus if event_bus else messaging.system_event_bus
10
- )
11
- self._is_connected: bool = False
12
-
13
- def connect(self) -> bool:
14
- if self._is_connected:
15
- return True
16
- self._subscribe_to_events()
17
- self._is_connected = True
18
- return True
19
-
20
- def disconnect(self) -> None:
21
- if not self._is_connected:
22
- return
23
- try:
24
- self.event_bus.unsubscribe(events.System.Shutdown, self.on_system_shutdown)
25
- finally:
26
- self._is_connected = False
27
-
28
- @property
29
- def is_connected(self) -> bool:
30
- return self._is_connected
31
-
32
- def _subscribe_to_events(self) -> None:
33
- """
34
- Subscribe to relevant events from the event bus.
35
- """
36
- self.event_bus.subscribe(events.System.Shutdown, self.on_system_shutdown)
37
- self.event_bus.subscribe(events.Strategy.SymbolRelease, self.on_symbol_release)
38
-
39
- def on_system_shutdown(self, event: events.Base.Event) -> None:
40
- """
41
- Handle system shutdown events (ignore unrelated events).
42
- """
43
- if not isinstance(event, events.System.Shutdown):
44
- return
45
- # Default no-op
46
- return
47
-
48
- def on_symbol_release(self, event: events.Base.Event) -> None:
49
- """
50
- Handle portfolio symbol release events (ignore unrelated events).
51
- Intended for brokers to perform any symbol-specific cleanup if necessary.
52
- Default implementation is a no-op.
53
- """
54
- if not isinstance(event, events.Strategy.SymbolRelease):
55
- return
56
- # Default no-op
57
- return
58
-
59
- def on_request_market_order(self, event: events.Request.MarketOrder) -> None:
60
- """
61
- Handle market order requests.
62
- """
63
- pass
64
-
65
- def on_request_limit_order(self, event: events.Request.LimitOrder) -> None:
66
- """
67
- Handle limit order requests.
68
- """
69
- pass
70
-
71
- def on_request_stop_order(self, event: events.Request.StopOrder) -> None:
72
- """
73
- Handle stop order requests.
74
- """
75
- pass
76
-
77
- def on_request_stop_limit_order(self, event: events.Request.StopLimitOrder) -> None:
78
- """
79
- Handle stop limit order requests.
80
- """
81
- pass
82
-
83
- def on_request_cancel_order(self, event: events.Request.CancelOrder) -> None:
84
- """
85
- Handle cancel order requests.
86
- """
87
- pass
88
-
89
- def on_request_flush_symbol(self, event: events.Request.FlushSymbol) -> None:
90
- """
91
- Handle flush symbol requests.
92
- """
93
- pass
94
-
95
- def on_request_flush_all(self, event: events.Request.FlushAll) -> None:
96
- """
97
- Handle flush all requests.
98
- """
99
- pass
@@ -1,10 +0,0 @@
1
- from onesecondtrader.brokers import base_broker
2
-
3
-
4
- class SimulatedBroker(base_broker.BaseBroker):
5
- """
6
- Simple simulated broker used as a safe default.
7
- """
8
-
9
- def __init__(self, event_bus=None):
10
- super().__init__(event_bus)
File without changes
@@ -1,204 +0,0 @@
1
- from dataclasses import dataclass
2
- import enum
3
-
4
-
5
- @dataclass(frozen=True, slots=True)
6
- class Bar:
7
- """
8
- Class for representing a OHLC(V) bar of market data.
9
-
10
- Attributes:
11
- open (float): Open price
12
- high (float): High price
13
- low (float): Low price
14
- close (float): Close price
15
- volume (int | None): Volume
16
- """
17
-
18
- open: float
19
- high: float
20
- low: float
21
- close: float
22
- volume: int | None = None
23
-
24
-
25
- class Side(enum.Enum):
26
- """
27
- Enum for order sides.
28
- """
29
-
30
- BUY = enum.auto()
31
- SELL = enum.auto()
32
-
33
-
34
- class TimeInForce(enum.Enum):
35
- """
36
- Order time-in-force specifications.
37
-
38
- **Attributes:**
39
-
40
- | Enum | Value | Description |
41
- |------|-------|-------------|
42
- | `DAY` | `enum.auto()` | Valid until end of trading day |
43
- | `FOK` | `enum.auto()` | Fill entire order immediately or cancel (Fill-or-Kill) |
44
- | `GTC` | `enum.auto()` | Active until explicitly cancelled (Good-Till-Cancelled) |
45
- | `GTD` | `enum.auto()` | Active until specified date (Good-Till-Date) |
46
- | `IOC` | `enum.auto()` | Execute available quantity immediately, cancel rest (Immediate-or-Cancel) |
47
- """
48
-
49
- DAY = enum.auto()
50
- FOK = enum.auto()
51
- GTC = enum.auto()
52
- GTD = enum.auto()
53
- IOC = enum.auto()
54
-
55
-
56
- class OrderType(enum.Enum):
57
- """
58
- Enum for order types.
59
-
60
- **Attributes:**
61
-
62
- | Enum | Value | Description |
63
- |------|-------|-------------|
64
- | `MARKET` | `enum.auto()` | Market order |
65
- | `LIMIT` | `enum.auto()` | Limit order |
66
- | `STOP` | `enum.auto()` | Stop order |
67
- | `STOP_LIMIT` | `enum.auto()` | Stop-limit order |
68
- """
69
-
70
- MARKET = enum.auto()
71
- LIMIT = enum.auto()
72
- STOP = enum.auto()
73
- STOP_LIMIT = enum.auto()
74
-
75
-
76
- class OrderLifecycleState(enum.Enum):
77
- """
78
- Enum for order lifecycle states.
79
-
80
- **Attributes:**
81
-
82
- | Enum | Value | Description |
83
- |------|-------|-------------|
84
- | `PENDING` | `enum.auto()` | Order has been submitted, but not yet acknowledged by the brokers |
85
- | `OPEN` | `enum.auto()` | Order has been acknowledged by the brokers, but not yet filled or cancelled |
86
- | `FILLED` | `enum.auto()` | Order has been filled |
87
- | `CANCELLED` | `enum.auto()` | Order has been cancelled |
88
- """
89
-
90
- PENDING = enum.auto()
91
- OPEN = enum.auto()
92
- PARTIALLY_FILLED = enum.auto()
93
- FILLED = enum.auto()
94
- CANCELLED = enum.auto()
95
-
96
-
97
- class OrderRejectionReason(enum.Enum):
98
- """
99
- Enum for order rejection reasons.
100
-
101
- **Attributes:**
102
-
103
- | Enum | Value | Description |
104
- |------|-------|-------------|
105
- | `UNKNOWN` | `enum.auto()` | Unknown reason |
106
- | `NEGATIVE_QUANTITY` | `enum.auto()` | Negative quantity |
107
- """
108
-
109
- UNKNOWN = enum.auto()
110
- NEGATIVE_QUANTITY = enum.auto()
111
-
112
-
113
- class RecordType(enum.Enum):
114
- """
115
- Enum for Databento record types.
116
-
117
- **Attributes:**
118
-
119
- | Enum | Value | Description |
120
- |------|-------|-------------|
121
- | `OHLCV_1S` | `32` | 1-second bars |
122
- | `OHLCV_1M` | `33` | 1-minute bars |
123
- | `OHLCV_1H` | `34` | 1-hour bars |
124
- | `OHLCV_1D` | `35` | 1-day bars |
125
- """
126
-
127
- OHLCV_1S = 32
128
- OHLCV_1M = 33
129
- OHLCV_1H = 34
130
- OHLCV_1D = 35
131
-
132
- @classmethod
133
- def to_string(cls, rtype: int) -> str:
134
- match rtype:
135
- case cls.OHLCV_1S.value:
136
- return "1-second bars"
137
- case cls.OHLCV_1M.value:
138
- return "1-minute bars"
139
- case cls.OHLCV_1H.value:
140
- return "1-hour bars"
141
- case cls.OHLCV_1D.value:
142
- return "daily bars"
143
- case _:
144
- return f"unknown ({rtype})"
145
-
146
-
147
- class XMAMode(enum.Enum):
148
- """
149
- Enum for moving average modes.
150
-
151
- **Attributes:**
152
-
153
- | Enum | Value | Description |
154
- |------|-------|-------------|
155
- | `OPEN` | `enum.auto()` | Open price |
156
- | `HIGH` | `enum.auto()` | High price |
157
- | `LOW` | `enum.auto()` | Low price |
158
- | `CLOSE` | `enum.auto()` | Close price |
159
- | `TYPICAL_PRICE` | `enum.auto()` | Typical price ((H+ L + C) / 3) |
160
- | `WEIGHTED_CLOSE` | `enum.auto()` | Weighted close price ((H + L + 2*C) / 4) |
161
- """
162
-
163
- OPEN = enum.auto()
164
- HIGH = enum.auto()
165
- LOW = enum.auto()
166
- CLOSE = enum.auto()
167
- TYPICAL_PRICE = enum.auto()
168
- WEIGHTED_CLOSE = enum.auto()
169
-
170
-
171
- class Position:
172
- pass
173
-
174
-
175
- class StrategyShutdownMode(enum.Enum):
176
- """
177
- Enum for strategy shutdown modes.
178
-
179
- **Attributes:**
180
-
181
- | Enum | Value | Description |
182
- |------|-------|-------------|
183
- | `SOFT` | `enum.auto()` | Do not open new positions; wait until current positions close naturally |
184
- | `HARD` | `enum.auto()` | Close all positions immediately with market orders |
185
- """
186
-
187
- SOFT = enum.auto()
188
- HARD = enum.auto()
189
-
190
-
191
- class SymbolShutdownMode(enum.Enum):
192
- """
193
- Enum for symbol shutdown modes.
194
-
195
- **Attributes:**
196
-
197
- | Enum | Value | Description |
198
- |------|-------|-------------|
199
- | `SOFT` | `enum.auto()` | Do not open new positions; wait until current positions close naturally |
200
- | `HARD` | `enum.auto()` | Close all positions immediately with market orders |
201
- """
202
-
203
- SOFT = enum.auto()
204
- HARD = enum.auto()
@@ -1,177 +0,0 @@
1
- import threading
2
-
3
- from onesecondtrader import messaging
4
- from onesecondtrader.messaging import events
5
- from onesecondtrader.strategies import base_strategy
6
- from onesecondtrader.monitoring import console
7
- from onesecondtrader.core import models
8
-
9
-
10
- class Portfolio:
11
- def __init__(self, event_bus: messaging.EventBus | None = None):
12
- """
13
- Initialize the Portfolio class and subscribe to events.
14
- Most importantly, the `symbol_to_strategy` registry is initialized,
15
- which keeps track of which symbols are currently assigned to which strategy
16
- in order to enforce exclusive symbol ownership.
17
-
18
- Args:
19
- event_bus (messaging.EventBus | None): Event bus to use; defaults to
20
- messaging.system_event_bus when None.
21
-
22
- Attributes:
23
- self._lock (threading.Lock): Lock for thread-safe operations.
24
- self.event_bus (messaging.EventBus): Event bus used for communication
25
- between the trading infrastructure's components.
26
- self.symbols_to_strategy (dict[str, base_strategy.Strategy]): Registry of
27
- symbols to strategies.
28
- """
29
- # ------------------------------------------------------------------------------
30
- # INITIALIZE LOCK FOR THREAD-SAFE OPERATIONS
31
- self._lock: threading.Lock = threading.Lock()
32
-
33
- # ------------------------------------------------------------------------------
34
- # INITIALIZE EVENT BUS AND SUBSCRIBE TO EVENTS
35
- self.event_bus: messaging.EventBus = (
36
- event_bus if event_bus else messaging.system_event_bus
37
- )
38
- self.event_bus.subscribe(events.Strategy.SymbolRelease, self.on_symbol_release)
39
-
40
- # ------------------------------------------------------------------------------
41
- # INITIALIZE SYMBOLS TO STRATEGY REGISTRY
42
- self.symbols_to_strategy: dict[str, base_strategy.Strategy] = {}
43
-
44
- def on_symbol_release(self, event: messaging.events.Base.Event) -> None:
45
- """
46
- Event handler for symbol release events (`events.Strategy.SymbolRelease`).
47
- The symbol is removed from the `symbols_to_strategy` registry.
48
-
49
- Args:
50
- event (messaging.events.Base.Event): Symbol release event.
51
- """
52
- # ------------------------------------------------------------------------------
53
- # IGNORE UNRELATED EVENT TYPES
54
- if not isinstance(event, events.Strategy.SymbolRelease):
55
- return
56
-
57
- # ------------------------------------------------------------------------------
58
- # RELEASE SYMBOL FROM STRATEGY
59
- symbol = event.symbol
60
- with self._lock:
61
- if symbol in self.symbols_to_strategy:
62
- del self.symbols_to_strategy[symbol]
63
- console.logger.info(
64
- f"on_symbol_release: symbol {symbol} released from "
65
- f"{getattr(event.strategy, 'name', type(event.strategy).__name__)}"
66
- )
67
- else:
68
- console.logger.warning(
69
- f"on_symbol_release: symbol {symbol} not owned by "
70
- f"{getattr(event.strategy, 'name', type(event.strategy).__name__)}"
71
- )
72
-
73
- def assign_symbols(
74
- self, strategy_instance: base_strategy.Strategy, symbols: list[str]
75
- ) -> bool:
76
- """
77
- Assign a list of symbols to a strategy if no conflicts exist and notify the
78
- strategy of the assignment.
79
-
80
- Args:
81
- strategy_instance (base_strategy.Strategy): Strategy instance to assign
82
- symbols to.
83
- symbols (list[str]): List of symbols to assign.
84
- """
85
- # ------------------------------------------------------------------------------
86
- # VALIDATE THAT INSTANCE IS A SUBCLASS OF base_strategy.Strategy
87
- if not isinstance(strategy_instance, base_strategy.Strategy):
88
- console.logger.error("assign_symbols: strategy must inherit from Strategy")
89
- return False
90
-
91
- # ------------------------------------------------------------------------------
92
- # CHECK FOR CONFLICTS
93
- non_conflicting: list[str] = []
94
- conflicting: list[str] = []
95
- with self._lock:
96
- for symbol in symbols:
97
- owner = self.symbols_to_strategy.get(symbol)
98
- if owner is None:
99
- non_conflicting.append(symbol)
100
- else:
101
- conflicting.append(symbol)
102
- if conflicting:
103
- console.logger.warning(
104
- "assign_symbols: symbols not assigned due to conflicts; "
105
- "use Portfolio.assign_symbols(...) after resolving. "
106
- f"non_conflicting={non_conflicting}, conflicts={conflicting}"
107
- )
108
- return False
109
- else:
110
- # --------------------------------------------------------------------------
111
- # ASSIGN SYMBOLS TO REGISTRY
112
- for symbol in symbols:
113
- self.symbols_to_strategy[symbol] = strategy_instance
114
-
115
- # --------------------------------------------------------------------------
116
- # PUBLISH SYMBOL ASSIGNMENT EVENT
117
- # noinspection PyArgumentList
118
- self.event_bus.publish(
119
- events.Strategy.SymbolAssignment(
120
- strategy=strategy_instance,
121
- symbol_list=symbols,
122
- )
123
- )
124
- return True
125
-
126
- def unassign_symbols(
127
- self,
128
- symbols: list[str],
129
- shutdown_mode: models.SymbolShutdownMode = models.SymbolShutdownMode.SOFT,
130
- ) -> bool:
131
- """
132
- Unassign a list of symbols from their owning strategy if all of them have
133
- previously been assigned to a strategy.
134
- Calling this methods will request the owning strategy to stop trading the symbol
135
- in the manner dictated via the `shutdown_mode` argument (default to soft
136
- shutdown, i.e. wait for open positions to close naturally and release symbols
137
- once they are flat).
138
- After the owning strategy has released the symbol, the symbol is unassigned from
139
- the portfolio via the `on_symbol_release` event handler.
140
-
141
- Args:
142
- symbols (list[str]): List of symbols to unassign.
143
- shutdown_mode (models.SymbolShutdownMode): Shutdown mode to use. Defaults
144
- to `models.SymbolShutdownMode.SOFT`.
145
- """
146
- # ------------------------------------------------------------------------------
147
- # CHECK THAT SYMBOLS ARE REGISTERED
148
- conflicting: list[str] = []
149
- with self._lock:
150
- for symbol in symbols:
151
- if symbol not in self.symbols_to_strategy:
152
- conflicting.append(symbol)
153
- if conflicting:
154
- console.logger.warning(
155
- "unassign_symbols: symbols not unassigned due to conflicts; "
156
- f"conflicts={conflicting}. "
157
- f"Use Portfolio.unassign_symbols(...) after resolving."
158
- )
159
- return False
160
- else:
161
- # ----------------------------------------------------------------------
162
- # PUBLISH STOP TRADING SYMBOL EVENT FOR EACH SYMBOL
163
- for symbol in symbols:
164
- # noinspection PyArgumentList
165
- self.event_bus.publish(
166
- events.Strategy.StopTradingSymbol(
167
- strategy=self.symbols_to_strategy[symbol],
168
- symbol=symbol,
169
- shutdown_mode=shutdown_mode,
170
- )
171
- )
172
- console.logger.info(
173
- f"unassign_symbols: trading stop for {symbol} trading strategy "
174
- f"{self.symbols_to_strategy[symbol]} requested with shutdown"
175
- f"mode {shutdown_mode.name}"
176
- )
177
- return True
File without changes
File without changes