onesecondtrader 0.15.0__py3-none-any.whl → 0.17.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,105 @@
1
+ """
2
+ Core module containing the backbone of OneSecondTrader's event-driven architecture.
3
+ """
4
+
5
+ import abc
6
+ import dataclasses
7
+ import enum
8
+ import pandas as pd
9
+ import queue
10
+ import threading
11
+
12
+ from collections import defaultdict
13
+
14
+
15
+ class Models:
16
+ """
17
+ Namespace for all models.
18
+ """
19
+
20
+ class RecordType(enum.Enum):
21
+ OHLCV_1S = 32
22
+ OHLCV_1M = 33
23
+ OHLCV_1H = 34
24
+ OHLCV_1D = 35
25
+
26
+
27
+ class Events:
28
+ """
29
+ Namespace for all events.
30
+ """
31
+
32
+ @dataclasses.dataclass(kw_only=True, frozen=True)
33
+ class BaseEvent:
34
+ ts_event: pd.Timestamp = dataclasses.field(
35
+ default_factory=lambda: pd.Timestamp.now(tz="UTC")
36
+ )
37
+
38
+ @dataclasses.dataclass(kw_only=True, frozen=True)
39
+ class SystemShutdown(BaseEvent):
40
+ pass
41
+
42
+ @dataclasses.dataclass(kw_only=True, frozen=True)
43
+ class IncomingBar(BaseEvent):
44
+ ts_event: pd.Timestamp
45
+ symbol: str
46
+ record_type: Models.RecordType
47
+ open: float
48
+ high: float
49
+ low: float
50
+ close: float
51
+ volume: int | None = None
52
+
53
+
54
+ class BaseConsumer(abc.ABC):
55
+ """
56
+ Base class for all consumers.
57
+ """
58
+
59
+ def __init__(self) -> None:
60
+ self._queue: queue.Queue[Events.BaseEvent] = queue.Queue()
61
+ self._thread = threading.Thread(target=self._consume, daemon=True)
62
+ self._thread.start()
63
+
64
+ @abc.abstractmethod
65
+ def on_event(self, event: Events.BaseEvent) -> None:
66
+ pass
67
+
68
+ def receive(self, event: Events.BaseEvent) -> None:
69
+ self._queue.put(event)
70
+
71
+ def _consume(self) -> None:
72
+ while True:
73
+ event = self._queue.get()
74
+ if isinstance(event, Events.SystemShutdown):
75
+ break
76
+ self.on_event(event)
77
+
78
+
79
+ class EventBus:
80
+ """
81
+ Event bus for publishing events to the consumers subscribed to them.
82
+ """
83
+
84
+ def __init__(self) -> None:
85
+ self._subscriptions: defaultdict[type[Events.BaseEvent], list[BaseConsumer]] = (
86
+ defaultdict(list)
87
+ )
88
+ self._lock: threading.Lock = threading.Lock()
89
+
90
+ def subscribe(self, subscriber: BaseConsumer, event_type: type[Events.BaseEvent]):
91
+ with self._lock:
92
+ if subscriber not in self._subscriptions[event_type]:
93
+ self._subscriptions[event_type].append(subscriber)
94
+
95
+ def unsubscribe(self, subscriber: BaseConsumer):
96
+ with self._lock:
97
+ for consumer_list in self._subscriptions.values():
98
+ if subscriber in consumer_list:
99
+ consumer_list.remove(subscriber)
100
+
101
+ def publish(self, event: Events.BaseEvent) -> None:
102
+ with self._lock:
103
+ consumers = list(self._subscriptions[type(event)])
104
+ for consumer in consumers:
105
+ consumer.receive(event)
@@ -0,0 +1,106 @@
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.core import Events
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: Events.IncomingBar) -> 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: Events.IncomingBar) -> 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, incoming_bar: Events.IncomingBar) -> float:
83
+ value: float = self._extract_input(incoming_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, incoming_bar: Events.IncomingBar) -> float:
90
+ match self.input_source:
91
+ case InputSource.OPEN:
92
+ return incoming_bar.open
93
+ case InputSource.HIGH:
94
+ return incoming_bar.high
95
+ case InputSource.LOW:
96
+ return incoming_bar.low
97
+ case InputSource.CLOSE:
98
+ return incoming_bar.close
99
+ case InputSource.VOLUME:
100
+ return (
101
+ float(incoming_bar.volume)
102
+ if incoming_bar.volume is not None
103
+ else np.nan
104
+ )
105
+ case _:
106
+ return incoming_bar.close
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: onesecondtrader
3
- Version: 0.15.0
3
+ Version: 0.17.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/core.py,sha256=_ww01j3PQ0bCRkkTDxDD-cNtD4dkeaIFKwBn2ro0Y5M,2766
3
+ onesecondtrader/indicators.py,sha256=wGn-5v8L1gepMP45KcVrEo-f2ReOCD3r8lva9aEIUnY,3199
4
+ onesecondtrader-0.17.0.dist-info/METADATA,sha256=Y-rLsvsHp0Vo-6lG0FqXE0Abm6YxaduSWkNb5D0Zv7g,9682
5
+ onesecondtrader-0.17.0.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
6
+ onesecondtrader-0.17.0.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
7
+ onesecondtrader-0.17.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()