onesecondtrader 0.23.0__py3-none-any.whl → 0.24.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.
Files changed (31) hide show
  1. onesecondtrader/analyst/__init__.py +0 -0
  2. onesecondtrader/analyst/charting.py +0 -0
  3. onesecondtrader/components/__init__.py +0 -0
  4. onesecondtrader/components/broker_base.py +0 -0
  5. onesecondtrader/components/datafeed_base.py +0 -0
  6. onesecondtrader/components/indicator_base.py +0 -0
  7. onesecondtrader/components/strategy_base.py +0 -0
  8. onesecondtrader/connectors/__init__.py +0 -0
  9. onesecondtrader/connectors/ib.py +0 -0
  10. onesecondtrader/connectors/mt5.py +0 -0
  11. onesecondtrader/connectors/simulated.py +0 -0
  12. onesecondtrader/core/__init__.py +47 -0
  13. onesecondtrader/core/domain_models.py +30 -0
  14. onesecondtrader/core/event_bus.py +60 -0
  15. onesecondtrader/core/event_messages.py +128 -0
  16. onesecondtrader/core/event_publisher.py +23 -0
  17. onesecondtrader/core/event_subscriber.py +76 -0
  18. onesecondtrader/libraries/__init__.py +0 -0
  19. onesecondtrader/libraries/indicators.py +0 -0
  20. onesecondtrader/libraries/strategies.py +0 -0
  21. onesecondtrader/observers/__init__.py +0 -0
  22. onesecondtrader/observers/csvbookkeeper.py +0 -0
  23. {onesecondtrader-0.23.0.dist-info → onesecondtrader-0.24.0.dist-info}/METADATA +1 -1
  24. onesecondtrader-0.24.0.dist-info/RECORD +27 -0
  25. onesecondtrader/core/component.py +0 -119
  26. onesecondtrader/core/eventbus.py +0 -85
  27. onesecondtrader/core/events.py +0 -224
  28. onesecondtrader/core/models.py +0 -70
  29. onesecondtrader-0.23.0.dist-info/RECORD +0 -10
  30. {onesecondtrader-0.23.0.dist-info → onesecondtrader-0.24.0.dist-info}/WHEEL +0 -0
  31. {onesecondtrader-0.23.0.dist-info → onesecondtrader-0.24.0.dist-info}/licenses/LICENSE +0 -0
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
@@ -0,0 +1,47 @@
1
+ from .domain_models import BarPeriod, OrderSide, OrderType
2
+ from .event_bus import EventBus
3
+ from .event_messages import (
4
+ AcceptedOrderCancellation,
5
+ AcceptedOrderModification,
6
+ AcceptedOrderSubmission,
7
+ BrokerRequestEventBase,
8
+ BrokerResponseEventBase,
9
+ ConfirmedOrderExpired,
10
+ ConfirmedOrderFilled,
11
+ EventBase,
12
+ MarketEventBase,
13
+ NewBar,
14
+ RejectedOrderCancellation,
15
+ RejectedOrderModification,
16
+ RejectedOrderSubmission,
17
+ RequestOrderCancellation,
18
+ RequestOrderModification,
19
+ RequestOrderSubmission,
20
+ )
21
+ from .event_publisher import EventPublisher
22
+ from .event_subscriber import EventSubscriber
23
+
24
+ __all__ = [
25
+ "BarPeriod",
26
+ "OrderSide",
27
+ "OrderType",
28
+ "EventBus",
29
+ "AcceptedOrderCancellation",
30
+ "AcceptedOrderModification",
31
+ "AcceptedOrderSubmission",
32
+ "BrokerRequestEventBase",
33
+ "BrokerResponseEventBase",
34
+ "ConfirmedOrderExpired",
35
+ "ConfirmedOrderFilled",
36
+ "EventBase",
37
+ "MarketEventBase",
38
+ "NewBar",
39
+ "RejectedOrderCancellation",
40
+ "RejectedOrderModification",
41
+ "RejectedOrderSubmission",
42
+ "RequestOrderCancellation",
43
+ "RequestOrderModification",
44
+ "RequestOrderSubmission",
45
+ "EventPublisher",
46
+ "EventSubscriber",
47
+ ]
@@ -0,0 +1,30 @@
1
+ """
2
+ ---
3
+ This module defines the core domain models used throughout the system.
4
+
5
+ Domain models are enumerations that define the shared vocabulary of the trading system.
6
+ They provide a fixed set of valid values for core concepts and are used across all
7
+ system components to ensure consistency and type safety.
8
+ ---
9
+ """
10
+
11
+ import enum
12
+
13
+
14
+ class OrderType(enum.Enum):
15
+ LIMIT = enum.auto()
16
+ MARKET = enum.auto()
17
+ STOP = enum.auto()
18
+ STOP_LIMIT = enum.auto()
19
+
20
+
21
+ class OrderSide(enum.Enum):
22
+ BUY = enum.auto()
23
+ SELL = enum.auto()
24
+
25
+
26
+ class BarPeriod(enum.Enum):
27
+ SECOND = 32
28
+ MINUTE = 33
29
+ HOUR = 34
30
+ DAY = 35
@@ -0,0 +1,60 @@
1
+ """
2
+ Read first: [Event Messages](./event_messages.md).
3
+
4
+ ---
5
+ This module defines the central event bus for publish-subscribe communication.
6
+
7
+ EventBus is the core messaging infrastructure that routes events between system
8
+ components.
9
+ Subscribers register interest in specific event types and receive events when publishers
10
+ dispatch them.
11
+ Event routing uses exact type matching, so subscribing to a base class will not receive
12
+ events of derived types.
13
+ ---
14
+ """
15
+
16
+ import collections
17
+ import threading
18
+ import typing
19
+
20
+ from .event_messages import EventBase
21
+
22
+
23
+ @typing.runtime_checkable
24
+ class EventSubscriberLike(typing.Protocol):
25
+ def receive(self, event: EventBase) -> None: ...
26
+ def wait_until_idle(self) -> None: ...
27
+
28
+
29
+ class EventBus:
30
+ def __init__(self) -> None:
31
+ self._per_event_subscriptions: collections.defaultdict[
32
+ type[EventBase], set[EventSubscriberLike]
33
+ ] = collections.defaultdict(set)
34
+ self._subscribers: set[EventSubscriberLike] = set()
35
+ self._lock: threading.Lock = threading.Lock()
36
+
37
+ def subscribe(
38
+ self, subscriber: EventSubscriberLike, event_type: type[EventBase]
39
+ ) -> None:
40
+ with self._lock:
41
+ self._subscribers.add(subscriber)
42
+ self._per_event_subscriptions[event_type].add(subscriber)
43
+
44
+ def unsubscribe(self, subscriber: EventSubscriberLike) -> None:
45
+ with self._lock:
46
+ for set_of_event_subscribers in self._per_event_subscriptions.values():
47
+ set_of_event_subscribers.discard(subscriber)
48
+ self._subscribers.discard(subscriber)
49
+
50
+ def publish(self, event: EventBase) -> None:
51
+ with self._lock:
52
+ subscribers = self._per_event_subscriptions[type(event)].copy()
53
+ for subscriber in subscribers:
54
+ subscriber.receive(event)
55
+
56
+ def wait_until_system_idle(self) -> None:
57
+ with self._lock:
58
+ subscribers = self._subscribers.copy()
59
+ for subscriber in subscribers:
60
+ subscriber.wait_until_idle()
@@ -0,0 +1,128 @@
1
+ """
2
+ Read first: [Domain Models](./domain_models.md).
3
+
4
+ ---
5
+ This module defines the event messages used throughout the system.
6
+
7
+ Event messages are immutable dataclasses used for communication between system
8
+ components
9
+ They are semantically grouped into market, broker request, and broker
10
+ response events via inheritance from dedicated base classes.
11
+ ---
12
+ """
13
+
14
+ import dataclasses
15
+ import pandas as pd
16
+ import uuid
17
+
18
+ from .domain_models import BarPeriod, OrderSide, OrderType
19
+
20
+
21
+ @dataclasses.dataclass(kw_only=True, frozen=True)
22
+ class EventBase:
23
+ ts_event: pd.Timestamp
24
+ ts_created: pd.Timestamp = dataclasses.field(
25
+ default_factory=lambda: pd.Timestamp.now(tz="UTC")
26
+ )
27
+
28
+
29
+ @dataclasses.dataclass(kw_only=True, frozen=True)
30
+ class MarketEventBase(EventBase):
31
+ pass
32
+
33
+
34
+ @dataclasses.dataclass(kw_only=True, frozen=True)
35
+ class BrokerRequestEventBase(EventBase):
36
+ pass
37
+
38
+
39
+ @dataclasses.dataclass(kw_only=True, frozen=True)
40
+ class BrokerResponseEventBase(EventBase):
41
+ ts_broker: pd.Timestamp
42
+
43
+
44
+ @dataclasses.dataclass(kw_only=True, frozen=True)
45
+ class NewBar(MarketEventBase):
46
+ symbol: str
47
+ bar_period: BarPeriod
48
+ open: float
49
+ high: float
50
+ low: float
51
+ close: float
52
+ volume: int | None = None
53
+
54
+
55
+ @dataclasses.dataclass(kw_only=True, frozen=True)
56
+ class RequestOrderSubmission(BrokerRequestEventBase):
57
+ order_id: uuid.UUID = dataclasses.field(default_factory=uuid.uuid4)
58
+ symbol: str
59
+ order_type: OrderType
60
+ side: OrderSide
61
+ quantity: float
62
+ limit_price: float | None = None
63
+ stop_price: float | None = None
64
+
65
+
66
+ @dataclasses.dataclass(kw_only=True, frozen=True)
67
+ class RequestOrderCancellation(BrokerRequestEventBase):
68
+ symbol: str
69
+ order_id: uuid.UUID
70
+
71
+
72
+ @dataclasses.dataclass(kw_only=True, frozen=True)
73
+ class RequestOrderModification(BrokerRequestEventBase):
74
+ symbol: str
75
+ order_id: uuid.UUID
76
+ quantity: float | None = None
77
+ limit_price: float | None = None
78
+ stop_price: float | None = None
79
+
80
+
81
+ @dataclasses.dataclass(kw_only=True, frozen=True)
82
+ class AcceptedOrderSubmission(BrokerResponseEventBase):
83
+ order_id: uuid.UUID
84
+ broker_order_id: str | None = None
85
+
86
+
87
+ @dataclasses.dataclass(kw_only=True, frozen=True)
88
+ class AcceptedOrderModification(BrokerResponseEventBase):
89
+ order_id: uuid.UUID
90
+ broker_order_id: str | None = None
91
+
92
+
93
+ @dataclasses.dataclass(kw_only=True, frozen=True)
94
+ class AcceptedOrderCancellation(BrokerResponseEventBase):
95
+ order_id: uuid.UUID
96
+
97
+
98
+ @dataclasses.dataclass(kw_only=True, frozen=True)
99
+ class RejectedOrderSubmission(BrokerResponseEventBase):
100
+ order_id: uuid.UUID
101
+
102
+
103
+ @dataclasses.dataclass(kw_only=True, frozen=True)
104
+ class RejectedOrderModification(BrokerResponseEventBase):
105
+ order_id: uuid.UUID
106
+
107
+
108
+ @dataclasses.dataclass(kw_only=True, frozen=True)
109
+ class RejectedOrderCancellation(BrokerResponseEventBase):
110
+ order_id: uuid.UUID
111
+
112
+
113
+ @dataclasses.dataclass(kw_only=True, frozen=True)
114
+ class ConfirmedOrderFilled(BrokerResponseEventBase):
115
+ fill_id: uuid.UUID = dataclasses.field(default_factory=uuid.uuid4)
116
+ broker_fill_id: str | None = None
117
+ associated_order_id: uuid.UUID
118
+ symbol: str
119
+ side: OrderSide
120
+ quantity_filled: float
121
+ fill_price: float
122
+ commission: float
123
+ exchange: str = "SIMULATED"
124
+
125
+
126
+ @dataclasses.dataclass(kw_only=True, frozen=True)
127
+ class ConfirmedOrderExpired(BrokerResponseEventBase):
128
+ order_id: uuid.UUID
@@ -0,0 +1,23 @@
1
+ """
2
+ Read first: [Event Bus](./event_bus.md), [Event Messages](./event_messages.md).
3
+
4
+ ---
5
+ This module defines the base class for components that publish events.
6
+
7
+ EventPublisher provides a minimal interface for publishing events to the event bus.
8
+ Components that only publish events inherit from this class directly.
9
+ Components that both subscribe and publish inherit from both EventSubscriber and
10
+ EventPublisher.
11
+ ---
12
+ """
13
+
14
+ from .event_bus import EventBus
15
+ from .event_messages import EventBase
16
+
17
+
18
+ class EventPublisher:
19
+ def __init__(self, event_bus: EventBus) -> None:
20
+ self._event_bus = event_bus
21
+
22
+ def _publish(self, event: EventBase) -> None:
23
+ self._event_bus.publish(event)
@@ -0,0 +1,76 @@
1
+ """
2
+ Read first: [Event Bus](./event_bus.md), [Event Messages](./event_messages.md).
3
+
4
+ ---
5
+ This module defines the base class for components that subscribe to events.
6
+
7
+ EventSubscriber provides a threaded event loop that receives events from the event bus.
8
+ Each subscriber runs in its own thread and processes events from its internal queue.
9
+ Components that only subscribe inherit from this class directly.
10
+ Components that both subscribe and publish inherit from both EventSubscriber and
11
+ EventPublisher.
12
+ ---
13
+ """
14
+
15
+ import abc
16
+ import queue
17
+ import threading
18
+
19
+ from .event_bus import EventBus
20
+ from .event_messages import EventBase
21
+
22
+
23
+ class EventSubscriber(abc.ABC):
24
+ def __init__(self, event_bus: EventBus) -> None:
25
+ self._event_bus = event_bus
26
+ self._queue: queue.Queue[EventBase | None] = queue.Queue()
27
+ self._running = True
28
+ self._thread = threading.Thread(
29
+ target=self._event_loop, name=self.__class__.__name__
30
+ )
31
+ self._thread.start()
32
+
33
+ def receive(self, event: EventBase) -> None:
34
+ if self._running:
35
+ self._queue.put(event)
36
+
37
+ def wait_until_idle(self) -> None:
38
+ if not self._running:
39
+ return
40
+ self._queue.join()
41
+
42
+ def shutdown(self) -> None:
43
+ if not self._running:
44
+ return
45
+ self._running = False
46
+ self._event_bus.unsubscribe(self)
47
+ self._queue.put(None)
48
+ if threading.current_thread() is not self._thread:
49
+ self._thread.join()
50
+
51
+ def _subscribe(self, *event_types: type[EventBase]) -> None:
52
+ for event_type in event_types:
53
+ self._event_bus.subscribe(self, event_type)
54
+
55
+ def _event_loop(self) -> None:
56
+ while True:
57
+ event = self._queue.get()
58
+ if event is None:
59
+ self._queue.task_done()
60
+ break
61
+ try:
62
+ self._on_event(event)
63
+ except Exception:
64
+ self._on_exception()
65
+ finally:
66
+ self._queue.task_done()
67
+ self._cleanup()
68
+
69
+ def _on_exception(self) -> None:
70
+ pass
71
+
72
+ def _cleanup(self) -> None:
73
+ pass
74
+
75
+ @abc.abstractmethod
76
+ def _on_event(self, event: EventBase) -> None: ...
File without changes
File without changes
File without changes
File without changes
File without changes
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: onesecondtrader
3
- Version: 0.23.0
3
+ Version: 0.24.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
@@ -0,0 +1,27 @@
1
+ onesecondtrader/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
+ onesecondtrader/analyst/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
+ onesecondtrader/analyst/charting.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
+ onesecondtrader/components/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
+ onesecondtrader/components/broker_base.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
+ onesecondtrader/components/datafeed_base.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
7
+ onesecondtrader/components/indicator_base.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
+ onesecondtrader/components/strategy_base.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
9
+ onesecondtrader/connectors/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10
+ onesecondtrader/connectors/ib.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
11
+ onesecondtrader/connectors/mt5.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
12
+ onesecondtrader/connectors/simulated.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
13
+ onesecondtrader/core/__init__.py,sha256=sbxGdriACYOgBBTJluaAF9W11RWfhCDgzKP5JtwN-sI,1228
14
+ onesecondtrader/core/domain_models.py,sha256=G-DxruNArRAKSFZOWFKPYMMeJNKx9L7HD_e98TZBxYo,624
15
+ onesecondtrader/core/event_bus.py,sha256=uxLIfWqnHM8KTuKThiySgZPe616y6IGNUhTb4CmoE90,2009
16
+ onesecondtrader/core/event_messages.py,sha256=N6hbTPEQevGkHKlvW3IoWGbHIzs7ySRSD6v24rynG0c,3348
17
+ onesecondtrader/core/event_publisher.py,sha256=XjRcoOIo5-RSxJ_aazTgFd2ypoP-HM9hfiGBtiGojFo,694
18
+ onesecondtrader/core/event_subscriber.py,sha256=HqSxOjToXT_YF03W4jjlEayZ82e1RJ-qqY9P-HP_Xns,2220
19
+ onesecondtrader/libraries/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
20
+ onesecondtrader/libraries/indicators.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
21
+ onesecondtrader/libraries/strategies.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
22
+ onesecondtrader/observers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
23
+ onesecondtrader/observers/csvbookkeeper.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
24
+ onesecondtrader-0.24.0.dist-info/METADATA,sha256=WGHyqYgRJA4EK9i5yFPYwpCJjHK8mNk_ZOagQsNMRPs,9682
25
+ onesecondtrader-0.24.0.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
26
+ onesecondtrader-0.24.0.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
27
+ onesecondtrader-0.24.0.dist-info/RECORD,,
@@ -1,119 +0,0 @@
1
- """
2
- Read first: [`events.py`](./events.md), [`eventbus.py`](./eventbus.md).
3
-
4
- ---
5
- This module defines the `Component` class.
6
-
7
- In an event-driven system, components interact exclusively via event messages.
8
- The `Component` class is an abstract base class that defines a common interface for
9
- publishing and receiving such messages and is intended to be subclassed by all system
10
- components.
11
- ---
12
- """
13
-
14
- import abc
15
- import queue
16
- import threading
17
-
18
- from .events import Event
19
- from .eventbus import EventBus
20
-
21
-
22
- class Component(abc.ABC):
23
- """
24
- Abstract base class for all system components.
25
-
26
- Incoming events are delivered via `receive()` and stored in an internal queue.
27
- A dedicated thread runs an event loop that consumes queued events and dispatches
28
- them to `_process()`, which subclasses must implement.
29
- For publication, `publish()` forwards events to the event bus.
30
- The `join_queue()` method is used by the event bus to block until all queued events
31
- have been processed, which is useful for deterministic, stepwise execution in
32
- backtesting.
33
- """
34
-
35
- def __init__(self, eventbus: EventBus) -> None:
36
- """
37
- Initialize the component and start its event-loop thread.
38
-
39
- Stores a reference to the event bus, creates an internal queue for incoming
40
- events, and starts a non-daemon thread for the event loop that runs until
41
- shutdown.
42
- """
43
- self._event_bus = eventbus
44
- self._incoming_event_queue: queue.Queue[Event | None] = queue.Queue()
45
- self._thread: threading.Thread = threading.Thread(
46
- target=self._event_loop,
47
- name=self.__class__.__name__,
48
- daemon=False,
49
- )
50
- self._thread.start()
51
-
52
- def publish(self, event: Event) -> None:
53
- """
54
- Wrapper for the event bus's `publish()` method.
55
-
56
- For convenience, this method can be used by subclasses to publish events via the
57
- event bus's publication mechanism.
58
- """
59
- self._event_bus.publish(event)
60
-
61
- def receive(self, event: Event) -> None:
62
- """
63
- Store incoming events in the component's queue.
64
-
65
- This method is typically called by the event bus when an event is published that
66
- the component has subscribed to.
67
- It simply puts the event in the queue for processing by the subscribed
68
- component.
69
- """
70
- self._incoming_event_queue.put(event)
71
-
72
- def join_queue(self) -> None:
73
- """
74
- Block until all queued events have been processed.
75
-
76
- This method is used by the event bus to block until all queued events have been
77
- processed.
78
- This is useful for deterministic, stepwise execution in backtesting.
79
- """
80
- self._incoming_event_queue.join()
81
-
82
- def _event_loop(self) -> None:
83
- """
84
- Event-processing loop executed in the component’s dedicated thread.
85
-
86
- The loop continuously retrieves events from the `incoming_event_queue`,
87
- processes each event via `_process()`, and signals completion with
88
- `task_done()`.
89
- Execution terminates upon receipt of a `None` sentinel, at which point
90
- `_shutdown()` is invoked before exiting the thread to ensure proper cleanup.
91
- """
92
- while True:
93
- incoming_event = self._incoming_event_queue.get()
94
- if incoming_event is None:
95
- self._shutdown()
96
- self._incoming_event_queue.task_done()
97
- break
98
- self._process(incoming_event)
99
- self._incoming_event_queue.task_done()
100
-
101
- @abc.abstractmethod
102
- def _process(self, event: Event) -> None:
103
- """
104
- Process a single event.
105
-
106
- This method is invoked by the event loop for each incoming event.
107
- Subclasses must implement it to define event-handling behavior.
108
- """
109
- pass
110
-
111
- def _shutdown(self) -> None:
112
- """
113
- Perform cleanup when the component is shutting down.
114
-
115
- Called by `_event_loop()` when a `None` sentinel is received.
116
- Subclasses may override this method to perform cleanup tasks such as closing
117
- files or connections before the thread exits.
118
- """
119
- pass
@@ -1,85 +0,0 @@
1
- """
2
- Read first: [`events.py`](./events.md), [`component.py`](./component.md).
3
-
4
- ---
5
- This module defines the `EventBus` class.
6
-
7
- The event bus provides a publish–subscribe mechanism for event-driven systems.
8
- It maintains subscriptions between event types and components and delivers
9
- published events to all subscribed components in a thread-safe manner.
10
- ---
11
- """
12
-
13
- import collections
14
- import threading
15
-
16
- from .component import Component
17
- from .events import Event
18
-
19
-
20
- class EventBus:
21
- """
22
- Central event-dispatch mechanism for the system.
23
-
24
- The event bus manages subscriptions between event types and components and
25
- forwards published events to all subscribed components.
26
- """
27
-
28
- def __init__(self) -> None:
29
- """
30
- Initialize the event bus.
31
-
32
- Sets up internal data structures for managing subscriptions and registered
33
- components and initializes a lock to ensure thread-safe access.
34
- """
35
- self._subscriptions: collections.defaultdict[type[Event], set[Component]] = (
36
- collections.defaultdict(set)
37
- )
38
- self._components: set[Component] = set()
39
- self._lock: threading.Lock = threading.Lock()
40
-
41
- def subscribe(self, subscriber: Component, *event_types: type[Event]) -> None:
42
- """
43
- Subscribe a component to one or more event types.
44
-
45
- The subscriber will receive all future events whose type matches one of the
46
- specified event types.
47
- """
48
- with self._lock:
49
- self._components.add(subscriber)
50
- for event_type in event_types:
51
- self._subscriptions[event_type].add(subscriber)
52
-
53
- def unsubscribe(self, subscriber: Component) -> None:
54
- """
55
- Remove a component from all event subscriptions.
56
-
57
- After unsubscription, the component will no longer receive published events.
58
- """
59
- with self._lock:
60
- for component_set in self._subscriptions.values():
61
- component_set.discard(subscriber)
62
- self._components.discard(subscriber)
63
-
64
- def publish(self, event: Event) -> None:
65
- """
66
- Publish an event to all subscribed components.
67
-
68
- The event is delivered to each subscriber by invoking its `receive()` method.
69
- """
70
- with self._lock:
71
- components = self._subscriptions[type(event)].copy()
72
- for component in components:
73
- component.receive(event)
74
-
75
- def wait_until_idle(self) -> None:
76
- """
77
- Block until all components have processed their queued events.
78
-
79
- This method is primarily used in backtesting to enforce deterministic,
80
- system-wide synchronization.
81
- """
82
- with self._lock:
83
- components = self._components.copy()
84
- for component in components:
85
- component.join_queue()
@@ -1,224 +0,0 @@
1
- """
2
- Read first: [`models.py`](./models.md).
3
-
4
- ---
5
- This module provides all event message classes used in the system.
6
-
7
- In an event-driven system, components communicate by sending event messages to each
8
- other. An event message is an immutable object that contains all information relevant
9
- to a specific occurrence, such as incoming market data, order submissions, fills, or
10
- other domain-specific events.
11
- ---
12
- """
13
-
14
- import dataclasses
15
- import uuid
16
-
17
- import pandas as pd
18
-
19
- from .models import BarPeriod, OrderType, OrderSide
20
-
21
-
22
- @dataclasses.dataclass(kw_only=True, frozen=True)
23
- class Event:
24
- """
25
- Base class for all event messages in the system.
26
-
27
- All events include a timestamp indicating when the event was created or received.
28
- Subclasses define specific event types with additional fields relevant to that
29
- event.
30
- """
31
-
32
- ts_event: pd.Timestamp = dataclasses.field(
33
- default_factory=lambda: pd.Timestamp.now(tz="UTC")
34
- )
35
-
36
-
37
- @dataclasses.dataclass(kw_only=True, frozen=True)
38
- class ReceivedNewBar(Event):
39
- """
40
- Event indicating a new bar of market data has been received.
41
-
42
- Contains OHLCV data for a specific symbol and bar period. This event is typically
43
- emitted by data feed components when new market data arrives.
44
- """
45
-
46
- ts_event: pd.Timestamp
47
- symbol: str
48
- bar_period: BarPeriod
49
- open: float
50
- high: float
51
- low: float
52
- close: float
53
- volume: int | None = None
54
-
55
-
56
- @dataclasses.dataclass(kw_only=True, frozen=True)
57
- class ProcessedBar(Event):
58
- """
59
- Event indicating a bar has been processed and enriched with indicator values.
60
-
61
- Contains the same OHLCV data as ReceivedNewBar, plus a dictionary of computed
62
- indicator values. This event is typically emitted after strategies or other
63
- components have calculated technical indicators for the bar.
64
- """
65
-
66
- ts_event: pd.Timestamp
67
- symbol: str
68
- bar_period: BarPeriod
69
- open: float
70
- high: float
71
- low: float
72
- close: float
73
- volume: int | None = None
74
- indicators: dict[str, float] = dataclasses.field(default_factory=dict)
75
-
76
-
77
- @dataclasses.dataclass(kw_only=True, frozen=True)
78
- class RequestOrderSubmission(Event):
79
- """
80
- Event requesting submission of a new order with the broker.
81
-
82
- Contains all parameters needed to submit an order, including symbol, order type,
83
- side, quantity, and optional limit/stop prices. A unique order_id is automatically
84
- generated if not provided.
85
- """
86
-
87
- order_id: uuid.UUID = dataclasses.field(default_factory=uuid.uuid4)
88
- symbol: str
89
- order_type: OrderType
90
- side: OrderSide
91
- quantity: float
92
- limit_price: float | None = None
93
- stop_price: float | None = None
94
-
95
-
96
- @dataclasses.dataclass(kw_only=True, frozen=True)
97
- class RequestOrderModification(Event):
98
- """
99
- Event requesting modification of an existing order.
100
-
101
- Identifies the order to modify by order_id and symbol. Any of quantity, limit_price,
102
- or stop_price can be updated by providing new values.
103
- """
104
-
105
- symbol: str
106
- order_id: uuid.UUID
107
- quantity: float | None = None
108
- limit_price: float | None = None
109
- stop_price: float | None = None
110
-
111
-
112
- @dataclasses.dataclass(kw_only=True, frozen=True)
113
- class RequestOrderCancellation(Event):
114
- """
115
- Event requesting cancellation of an existing order.
116
-
117
- Identifies the order to cancel by order_id and symbol.
118
- """
119
-
120
- symbol: str
121
- order_id: uuid.UUID
122
-
123
-
124
- @dataclasses.dataclass(kw_only=True, frozen=True)
125
- class AcceptedOrderSubmission(Event):
126
- """
127
- Event indicating the broker has accepted an order submission.
128
-
129
- Contains the system's order_id and optionally the broker's order_id if provided
130
- by the broker.
131
- """
132
-
133
- order_id: uuid.UUID
134
- broker_order_id: str | None = None
135
-
136
-
137
- @dataclasses.dataclass(kw_only=True, frozen=True)
138
- class AcceptedOrderModification(Event):
139
- """
140
- Event indicating the broker has accepted an order modification.
141
-
142
- Contains the system's order_id and optionally the broker's order_id if provided
143
- by the broker.
144
- """
145
-
146
- order_id: uuid.UUID
147
- broker_order_id: str | None = None
148
-
149
-
150
- @dataclasses.dataclass(kw_only=True, frozen=True)
151
- class AcceptedOrderCancellation(Event):
152
- """
153
- Event indicating the broker has accepted an order cancellation.
154
-
155
- Contains the system's order_id for the cancelled order.
156
- """
157
-
158
- order_id: uuid.UUID
159
-
160
-
161
- @dataclasses.dataclass(kw_only=True, frozen=True)
162
- class RejectedOrderSubmission(Event):
163
- """
164
- Event indicating the broker has rejected an order submission.
165
-
166
- Contains the system's order_id for the rejected order.
167
- """
168
-
169
- order_id: uuid.UUID
170
-
171
-
172
- @dataclasses.dataclass(kw_only=True, frozen=True)
173
- class RejectedOrderModification(Event):
174
- """
175
- Event indicating the broker has rejected an order modification.
176
-
177
- Contains the system's order_id for the order that could not be modified.
178
- """
179
-
180
- order_id: uuid.UUID
181
-
182
-
183
- @dataclasses.dataclass(kw_only=True, frozen=True)
184
- class RejectedOrderCancellation(Event):
185
- """
186
- Event indicating the broker has rejected an order cancellation.
187
-
188
- Contains the system's order_id for the order that could not be cancelled.
189
- """
190
-
191
- order_id: uuid.UUID
192
-
193
-
194
- @dataclasses.dataclass(kw_only=True, frozen=True)
195
- class OrderFilled(Event):
196
- """
197
- Event indicating an order has been filled (fully or partially).
198
-
199
- Contains details about the fill including quantity, price, commission, and the
200
- associated order. A unique fill_id is automatically generated if not provided.
201
- The broker may optionally provide a broker_fill_id.
202
- """
203
-
204
- fill_id: uuid.UUID = dataclasses.field(default_factory=uuid.uuid4)
205
- broker_fill_id: str | None = None
206
- associated_order_id: uuid.UUID
207
- symbol: str
208
- side: OrderSide
209
- quantity_filled: float
210
- fill_price: float
211
- commission: float
212
- exchange: str = "SIMULATED"
213
-
214
-
215
- @dataclasses.dataclass(kw_only=True, frozen=True)
216
- class OrderExpired(Event):
217
- """
218
- Event indicating an order has expired without being filled.
219
-
220
- Contains the system's order_id for the expired order. This typically occurs for
221
- time-limited orders that were not filled before their expiration time.
222
- """
223
-
224
- order_id: uuid.UUID
@@ -1,70 +0,0 @@
1
- """
2
- ---
3
- This module provides core domain models used throughout the system.
4
-
5
- Domain models are enumerations that define the vocabulary and valid values for key
6
- concepts in the trading system. These models are used across all components to ensure
7
- type safety and consistency when representing domain-specific concepts.
8
- ---
9
- """
10
-
11
- import enum
12
-
13
-
14
- class BarPeriod(enum.Enum):
15
- """
16
- Time period for aggregating market data into bars.
17
-
18
- Defines the standard time intervals used for OHLCV bar data.
19
- Each bar represents market activity aggregated over the specified period.
20
- """
21
-
22
- SECOND = 32
23
- MINUTE = 33
24
- HOUR = 34
25
- DAY = 35
26
-
27
-
28
- class OrderType(enum.Enum):
29
- """
30
- Type of order to submit to the broker.
31
-
32
- Defines the execution behavior for an order.
33
- Market orders execute immediately at current market price.
34
- Limit orders execute only at a specified price or better.
35
- Stop orders become market orders when a trigger price is reached.
36
- Stop-limit orders become limit orders when a trigger price is reached.
37
- """
38
-
39
- MARKET = enum.auto()
40
- LIMIT = enum.auto()
41
- STOP = enum.auto()
42
- STOP_LIMIT = enum.auto()
43
-
44
-
45
- class OrderSide(enum.Enum):
46
- """
47
- Direction of an order or position.
48
-
49
- Indicates whether an order is buying or selling a security.
50
- """
51
-
52
- BUY = enum.auto()
53
- SELL = enum.auto()
54
-
55
-
56
- class InputSource(enum.Enum):
57
- """
58
- Source field from bar data to use as input for indicators.
59
-
60
- Specifies which component of OHLCV bar data should be used when calculating
61
- technical indicators.
62
- For example, many indicators use CLOSE prices per default, but could also be
63
- calculated based on other fields.
64
- """
65
-
66
- OPEN = enum.auto()
67
- HIGH = enum.auto()
68
- LOW = enum.auto()
69
- CLOSE = enum.auto()
70
- VOLUME = enum.auto()
@@ -1,10 +0,0 @@
1
- onesecondtrader/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
- onesecondtrader/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
- onesecondtrader/core/component.py,sha256=I1pO48qT6yAuSQMAbte9YubGxK1FVzv0HJ-qsIWyanU,4193
4
- onesecondtrader/core/eventbus.py,sha256=o4cQ6FR61TXdDYC3K_tdKCWYbK5UOPfHSYflNfsCANQ,2818
5
- onesecondtrader/core/events.py,sha256=tRtanpMVcBEfcl7Wcw9CLR-gd6wFf7yABajnnXVGXJM,6167
6
- onesecondtrader/core/models.py,sha256=wdp3bFlvu21o5vsEHT2Lk_3kb2Vk5N0RmgcnZXelAxo,1806
7
- onesecondtrader-0.23.0.dist-info/METADATA,sha256=LlsTFMLyQYjSY2yec848Y5cln9x4ADwxuhFUiMzttxc,9682
8
- onesecondtrader-0.23.0.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
9
- onesecondtrader-0.23.0.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
10
- onesecondtrader-0.23.0.dist-info/RECORD,,