onesecondtrader 0.16.0__py3-none-any.whl → 0.18.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.
@@ -0,0 +1,147 @@
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
+ import uuid
12
+
13
+ from collections import defaultdict
14
+
15
+
16
+ class Models:
17
+ """
18
+ Namespace for all models.
19
+ """
20
+
21
+ class RecordType(enum.Enum):
22
+ OHLCV_1S = 32
23
+ OHLCV_1M = 33
24
+ OHLCV_1H = 34
25
+ OHLCV_1D = 35
26
+
27
+ class OrderSide(enum.Enum):
28
+ BUY = enum.auto()
29
+ SELL = enum.auto()
30
+
31
+ class OrderType(enum.Enum):
32
+ MARKET = enum.auto()
33
+ LIMIT = enum.auto()
34
+ STOP = enum.auto()
35
+ STOP_LIMIT = enum.auto()
36
+
37
+
38
+ class Events:
39
+ """
40
+ Namespace for all events.
41
+ """
42
+
43
+ @dataclasses.dataclass(kw_only=True, frozen=True)
44
+ class BaseEvent:
45
+ ts_event: pd.Timestamp = dataclasses.field(
46
+ default_factory=lambda: pd.Timestamp.now(tz="UTC")
47
+ )
48
+
49
+ # SYSTEM EVENTS
50
+ @dataclasses.dataclass(kw_only=True, frozen=True)
51
+ class SystemShutdown(BaseEvent):
52
+ pass
53
+
54
+ # MARKET EVENTS
55
+ @dataclasses.dataclass(kw_only=True, frozen=True)
56
+ class IncomingBar(BaseEvent):
57
+ ts_event: pd.Timestamp
58
+ symbol: str
59
+ record_type: Models.RecordType
60
+ open: float
61
+ high: float
62
+ low: float
63
+ close: float
64
+ volume: int | None = None
65
+
66
+ # BROKER REQUEST EVENTS
67
+ @dataclasses.dataclass(kw_only=True, frozen=True)
68
+ class Order(BaseEvent):
69
+ order_id: uuid.UUID = dataclasses.field(default_factory=lambda: uuid.uuid4())
70
+ symbol: str
71
+ order_type: Models.OrderType
72
+ side: Models.OrderSide
73
+ quantity: float
74
+ limit_price: float | None = None
75
+ stop_price: float | None = None
76
+
77
+ # BROKER RESPONSE EVENTS
78
+ @dataclasses.dataclass(kw_only=True, frozen=True)
79
+ class Fill(BaseEvent):
80
+ fill_id: uuid.UUID = dataclasses.field(default_factory=uuid.uuid4)
81
+ broker_fill_id: str | None = None
82
+ associated_order_id: uuid.UUID
83
+ side: Models.OrderSide
84
+ quantity_filled: float
85
+ fill_price: float
86
+ commission: float
87
+ exchange: str = "SIMULATED"
88
+
89
+
90
+ class BaseConsumer(abc.ABC):
91
+ """
92
+ Base class for all consumers.
93
+ """
94
+
95
+ def __init__(self) -> None:
96
+ self._queue: queue.Queue[Events.BaseEvent] = queue.Queue()
97
+ self._thread = threading.Thread(target=self._consume, daemon=True)
98
+ self._thread.start()
99
+
100
+ @abc.abstractmethod
101
+ def on_event(self, event: Events.BaseEvent) -> None:
102
+ pass
103
+
104
+ def receive(self, event: Events.BaseEvent) -> None:
105
+ self._queue.put(event)
106
+
107
+ def _consume(self) -> None:
108
+ while True:
109
+ event = self._queue.get()
110
+ if isinstance(event, Events.SystemShutdown):
111
+ break
112
+ self.on_event(event)
113
+
114
+
115
+ class EventBus:
116
+ """
117
+ Event bus for publishing events to the consumers subscribed to them.
118
+ """
119
+
120
+ def __init__(self) -> None:
121
+ self._subscriptions: defaultdict[type[Events.BaseEvent], list[BaseConsumer]] = (
122
+ defaultdict(list)
123
+ )
124
+ self._lock: threading.Lock = threading.Lock()
125
+
126
+ def subscribe(self, subscriber: BaseConsumer, event_type: type[Events.BaseEvent]):
127
+ with self._lock:
128
+ if subscriber not in self._subscriptions[event_type]:
129
+ self._subscriptions[event_type].append(subscriber)
130
+
131
+ def unsubscribe(self, subscriber: BaseConsumer):
132
+ with self._lock:
133
+ for consumer_list in self._subscriptions.values():
134
+ if subscriber in consumer_list:
135
+ consumer_list.remove(subscriber)
136
+
137
+ def publish(self, event: Events.BaseEvent) -> None:
138
+ with self._lock:
139
+ consumers = list(self._subscriptions[type(event)])
140
+ for consumer in consumers:
141
+ consumer.receive(event)
142
+
143
+
144
+ event_bus = EventBus()
145
+ """
146
+ Global event bus instance.
147
+ """
@@ -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,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: onesecondtrader
3
- Version: 0.16.0
3
+ Version: 0.18.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,7 @@
1
+ onesecondtrader/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
+ onesecondtrader/core.py,sha256=uMy2eYLtzfSPC_99kjft7L3DL4SRi1ZBnnSYyd_XrIo,3921
3
+ onesecondtrader/indicators.py,sha256=wGn-5v8L1gepMP45KcVrEo-f2ReOCD3r8lva9aEIUnY,3199
4
+ onesecondtrader-0.18.0.dist-info/METADATA,sha256=48Xk2OPiF57MkcpqFnyAG328SWljqyTEO5XmFS0VDrI,9682
5
+ onesecondtrader-0.18.0.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
6
+ onesecondtrader-0.18.0.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
7
+ onesecondtrader-0.18.0.dist-info/RECORD,,
@@ -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
@@ -1,7 +0,0 @@
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,,