onesecondtrader 0.16.0__tar.gz → 0.17.0__tar.gz

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,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: onesecondtrader
3
- Version: 0.16.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
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "onesecondtrader"
3
- version = "0.16.0"
3
+ version = "0.17.0"
4
4
  description = "The Trading Infrastructure Toolkit for Python. Research, simulate, and deploy algorithmic trading strategies — all in one place."
5
5
  authors = [
6
6
  {name = "Nils P. Kujath",email = "63961429+NilsKujath@users.noreply.github.com"}
@@ -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)
@@ -8,7 +8,7 @@ import numpy as np
8
8
  import threading
9
9
 
10
10
  from collections import deque
11
- from onesecondtrader.ontology import Bar
11
+ from onesecondtrader.core import Events
12
12
 
13
13
 
14
14
  class BaseIndicator(abc.ABC):
@@ -26,13 +26,13 @@ class BaseIndicator(abc.ABC):
26
26
  def name(self) -> str:
27
27
  pass
28
28
 
29
- def update(self, incoming_bar: Bar) -> None:
29
+ def update(self, incoming_bar: Events.IncomingBar) -> None:
30
30
  _latest_value: float = self._compute_indicator(incoming_bar)
31
31
  with self._lock:
32
32
  self._history.append(_latest_value)
33
33
 
34
34
  @abc.abstractmethod
35
- def _compute_indicator(self, incoming_bar: Bar) -> float:
35
+ def _compute_indicator(self, incoming_bar: Events.IncomingBar) -> float:
36
36
  pass
37
37
 
38
38
  @property
@@ -79,24 +79,28 @@ class SimpleMovingAverage(BaseIndicator):
79
79
  def name(self) -> str:
80
80
  return f"SMA_{self.period}_{self.input_source.name}"
81
81
 
82
- def _compute_indicator(self, bar: Bar) -> float:
83
- value: float = self._extract_input(bar)
82
+ def _compute_indicator(self, incoming_bar: Events.IncomingBar) -> float:
83
+ value: float = self._extract_input(incoming_bar)
84
84
  self._window.append(value)
85
85
  if len(self._window) < self.period:
86
86
  return np.nan
87
87
  return sum(self._window) / self.period
88
88
 
89
- def _extract_input(self, bar: Bar) -> float:
89
+ def _extract_input(self, incoming_bar: Events.IncomingBar) -> float:
90
90
  match self.input_source:
91
91
  case InputSource.OPEN:
92
- return float(bar.open)
92
+ return incoming_bar.open
93
93
  case InputSource.HIGH:
94
- return float(bar.high)
94
+ return incoming_bar.high
95
95
  case InputSource.LOW:
96
- return float(bar.low)
96
+ return incoming_bar.low
97
97
  case InputSource.CLOSE:
98
- return float(bar.close)
98
+ return incoming_bar.close
99
99
  case InputSource.VOLUME:
100
- return float(bar.volume)
100
+ return (
101
+ float(incoming_bar.volume)
102
+ if incoming_bar.volume is not None
103
+ else np.nan
104
+ )
101
105
  case _:
102
- return float(bar.close)
106
+ return incoming_bar.close
@@ -1,18 +0,0 @@
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