onesecondtrader 0.22.0__py3-none-any.whl → 0.49.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 (38) hide show
  1. onesecondtrader/events/__init__.py +8 -0
  2. onesecondtrader/events/base.py +21 -0
  3. onesecondtrader/events/market/__init__.py +7 -0
  4. onesecondtrader/events/market/bar_processed.py +29 -0
  5. onesecondtrader/events/market/bar_received.py +34 -0
  6. onesecondtrader/events/orders/__init__.py +9 -0
  7. onesecondtrader/events/orders/base.py +30 -0
  8. onesecondtrader/events/orders/expirations.py +23 -0
  9. onesecondtrader/events/orders/fills.py +41 -0
  10. onesecondtrader/events/requests/__init__.py +11 -0
  11. onesecondtrader/events/requests/base.py +25 -0
  12. onesecondtrader/events/requests/order_cancellation.py +21 -0
  13. onesecondtrader/events/requests/order_modification.py +26 -0
  14. onesecondtrader/events/requests/order_submission.py +35 -0
  15. onesecondtrader/events/responses/__init__.py +14 -0
  16. onesecondtrader/events/responses/base.py +26 -0
  17. onesecondtrader/events/responses/cancellations.py +42 -0
  18. onesecondtrader/events/responses/modifications.py +43 -0
  19. onesecondtrader/events/responses/orders.py +42 -0
  20. onesecondtrader/indicators/__init__.py +17 -0
  21. onesecondtrader/indicators/base.py +142 -0
  22. onesecondtrader/indicators/market_fields.py +166 -0
  23. onesecondtrader/indicators/moving_averages.py +104 -0
  24. onesecondtrader/models/__init__.py +23 -0
  25. onesecondtrader/models/bar_fields.py +23 -0
  26. onesecondtrader/models/bar_period.py +21 -0
  27. onesecondtrader/models/order_types.py +21 -0
  28. onesecondtrader/models/rejection_reasons.py +48 -0
  29. onesecondtrader/models/trade_sides.py +20 -0
  30. {onesecondtrader-0.22.0.dist-info → onesecondtrader-0.49.0.dist-info}/METADATA +8 -2
  31. onesecondtrader-0.49.0.dist-info/RECORD +34 -0
  32. {onesecondtrader-0.22.0.dist-info → onesecondtrader-0.49.0.dist-info}/WHEEL +1 -1
  33. onesecondtrader/brokers.py +0 -92
  34. onesecondtrader/core.py +0 -260
  35. onesecondtrader/datafeeds.py +0 -173
  36. onesecondtrader/indicators.py +0 -106
  37. onesecondtrader-0.22.0.dist-info/RECORD +0 -9
  38. {onesecondtrader-0.22.0.dist-info → onesecondtrader-0.49.0.dist-info}/licenses/LICENSE +0 -0
@@ -1,92 +0,0 @@
1
- import abc
2
- import uuid
3
-
4
- from onesecondtrader.core import BaseConsumer, Events, event_bus
5
-
6
-
7
- class BaseBroker(BaseConsumer):
8
- """
9
- Base class for all brokers.
10
- """
11
-
12
- def __init__(self) -> None:
13
- super().__init__()
14
- event_bus.subscribe(self, Events.SubmitOrder)
15
- event_bus.subscribe(self, Events.CancelOrder)
16
- event_bus.subscribe(self, Events.ModifyOrder)
17
-
18
- def on_event(self, event) -> None:
19
- match event:
20
- case Events.SubmitOrder():
21
- self.on_submit_order(event)
22
- case Events.CancelOrder():
23
- self.on_cancel_order(event)
24
- case Events.ModifyOrder():
25
- self.on_modify_order(event)
26
-
27
- @abc.abstractmethod
28
- def on_submit_order(self, event: Events.SubmitOrder) -> None:
29
- pass
30
-
31
- @abc.abstractmethod
32
- def on_cancel_order(self, event: Events.CancelOrder) -> None:
33
- pass
34
-
35
- @abc.abstractmethod
36
- def on_modify_order(self, event: Events.ModifyOrder) -> None:
37
- pass
38
-
39
-
40
- class SimulatedBroker(BaseBroker):
41
- """
42
- Simulated broker for backtesting.
43
- """
44
-
45
- def __init__(self) -> None:
46
- super().__init__()
47
- event_bus.subscribe(self, Events.IncomingBar)
48
-
49
- self._pending_market_orders: dict[str, dict[uuid.UUID, Events.SubmitOrder]] = {}
50
- self._pending_limit_orders: dict[str, dict[uuid.UUID, Events.SubmitOrder]] = {}
51
- self._pending_stop_orders: dict[str, dict[uuid.UUID, Events.SubmitOrder]] = {}
52
- self._pending_stop_limit_orders: dict[
53
- str, dict[uuid.UUID, Events.SubmitOrder]
54
- ] = {}
55
-
56
- def on_event(self, event) -> None:
57
- match event:
58
- case Events.SubmitOrder():
59
- self.on_submit_order(event)
60
- case Events.CancelOrder():
61
- self.on_cancel_order(event)
62
- case Events.ModifyOrder():
63
- self.on_modify_order(event)
64
- case Events.IncomingBar():
65
- self.on_incoming_bar(event)
66
-
67
- def on_submit_order(self, event: Events.SubmitOrder) -> None:
68
- pass
69
-
70
- def on_cancel_order(self, event: Events.CancelOrder) -> None:
71
- pass
72
-
73
- def on_modify_order(self, event: Events.ModifyOrder) -> None:
74
- pass
75
-
76
- def on_incoming_bar(self, event: Events.IncomingBar) -> None:
77
- self._process_pending_orders(event)
78
-
79
- bar_ready = Events.BarReady(
80
- ts_event=event.ts_event,
81
- symbol=event.symbol,
82
- record_type=event.record_type,
83
- open=event.open,
84
- high=event.high,
85
- low=event.low,
86
- close=event.close,
87
- volume=event.volume,
88
- )
89
- event_bus.publish(bar_ready)
90
-
91
- def _process_pending_orders(self, event: Events.IncomingBar) -> None:
92
- pass
onesecondtrader/core.py DELETED
@@ -1,260 +0,0 @@
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 logging
9
- import pandas as pd
10
- import queue
11
- import threading
12
- import uuid
13
-
14
- from collections import defaultdict
15
-
16
-
17
- logging.basicConfig(
18
- level=logging.DEBUG,
19
- format="%(asctime)s - %(levelname)s - %(threadName)s - %(message)s",
20
- )
21
- logger = logging.getLogger("onesecondtrader")
22
-
23
-
24
- class Models:
25
- """
26
- Namespace for all models.
27
- """
28
-
29
- class RecordType(enum.Enum):
30
- OHLCV_1S = 32
31
- OHLCV_1M = 33
32
- OHLCV_1H = 34
33
- OHLCV_1D = 35
34
-
35
- class OrderSide(enum.Enum):
36
- BUY = enum.auto()
37
- SELL = enum.auto()
38
-
39
- class OrderType(enum.Enum):
40
- MARKET = enum.auto()
41
- LIMIT = enum.auto()
42
- STOP = enum.auto()
43
- STOP_LIMIT = enum.auto()
44
-
45
- class RejectionReason(enum.Enum):
46
- ORDER_ALREADY_FILLED = enum.auto()
47
- ORDER_ALREADY_CANCELLED = enum.auto()
48
- ORDER_PENDING_EXECUTION = enum.auto()
49
- INSUFFICIENT_FUNDS = enum.auto()
50
- MARKET_CLOSED = enum.auto()
51
- UNKNOWN = enum.auto()
52
-
53
- class TimeInForce(enum.Enum):
54
- GTC = enum.auto()
55
- DAY = enum.auto()
56
- IOC = enum.auto()
57
- FOK = enum.auto()
58
-
59
-
60
- class Events:
61
- """
62
- Namespace for all events.
63
- """
64
-
65
- # BASE EVENT
66
- @dataclasses.dataclass(kw_only=True, frozen=True)
67
- class BaseEvent:
68
- ts_event: pd.Timestamp = dataclasses.field(
69
- default_factory=lambda: pd.Timestamp.now(tz="UTC")
70
- )
71
-
72
- # SYSTEM EVENTS
73
- @dataclasses.dataclass(kw_only=True, frozen=True)
74
- class SystemEvent(BaseEvent):
75
- pass
76
-
77
- @dataclasses.dataclass(kw_only=True, frozen=True)
78
- class SystemShutdown(SystemEvent):
79
- pass
80
-
81
- # MARKET EVENTS
82
- @dataclasses.dataclass(kw_only=True, frozen=True)
83
- class MarketEvent(BaseEvent):
84
- pass
85
-
86
- @dataclasses.dataclass(kw_only=True, frozen=True)
87
- class IncomingBar(MarketEvent):
88
- ts_event: pd.Timestamp
89
- symbol: str
90
- record_type: Models.RecordType
91
- open: float
92
- high: float
93
- low: float
94
- close: float
95
- volume: int | None = None
96
-
97
- @dataclasses.dataclass(kw_only=True, frozen=True)
98
- class BarReady(MarketEvent):
99
- ts_event: pd.Timestamp
100
- symbol: str
101
- record_type: Models.RecordType
102
- open: float
103
- high: float
104
- low: float
105
- close: float
106
- volume: int | None = None
107
-
108
- # BROKER REQUESTS EVENTS
109
- @dataclasses.dataclass(kw_only=True, frozen=True)
110
- class BrokerRequestEvent(BaseEvent):
111
- pass
112
-
113
- @dataclasses.dataclass(kw_only=True, frozen=True)
114
- class SubmitOrder(BrokerRequestEvent):
115
- order_id: uuid.UUID = dataclasses.field(default_factory=uuid.uuid4)
116
- symbol: str
117
- order_type: Models.OrderType
118
- side: Models.OrderSide
119
- quantity: float
120
- limit_price: float | None = None
121
- stop_price: float | None = None
122
- time_in_force: Models.TimeInForce = Models.TimeInForce.GTC
123
-
124
- @dataclasses.dataclass(kw_only=True, frozen=True)
125
- class ModifyOrder(BrokerRequestEvent):
126
- symbol: str
127
- order_id: uuid.UUID
128
- quantity: float | None = None
129
- limit_price: float | None = None
130
- stop_price: float | None = None
131
-
132
- @dataclasses.dataclass(kw_only=True, frozen=True)
133
- class CancelOrder(BrokerRequestEvent):
134
- symbol: str
135
- order_id: uuid.UUID
136
-
137
- # BROKER RESPONSE EVENTS
138
- @dataclasses.dataclass(kw_only=True, frozen=True)
139
- class BrokerResponseEvent(BaseEvent):
140
- ts_broker: pd.Timestamp
141
-
142
- @dataclasses.dataclass(kw_only=True, frozen=True)
143
- class OrderSubmitted(BrokerResponseEvent):
144
- order_id: uuid.UUID
145
- broker_order_id: str | None = None
146
-
147
- @dataclasses.dataclass(kw_only=True, frozen=True)
148
- class OrderModified(BrokerResponseEvent):
149
- order_id: uuid.UUID
150
- broker_order_id: str | None = None
151
-
152
- @dataclasses.dataclass(kw_only=True, frozen=True)
153
- class Fill(BrokerResponseEvent):
154
- fill_id: uuid.UUID = dataclasses.field(default_factory=uuid.uuid4)
155
- broker_fill_id: str | None = None
156
- associated_order_id: uuid.UUID
157
- symbol: str
158
- side: Models.OrderSide
159
- quantity_filled: float
160
- fill_price: float
161
- commission: float
162
- exchange: str = "SIMULATED"
163
-
164
- @dataclasses.dataclass(kw_only=True, frozen=True)
165
- class OrderRejected(BrokerResponseEvent):
166
- order_id: uuid.UUID
167
- reason: Models.RejectionReason
168
-
169
- @dataclasses.dataclass(kw_only=True, frozen=True)
170
- class OrderCancelled(BrokerResponseEvent):
171
- order_id: uuid.UUID
172
-
173
- @dataclasses.dataclass(kw_only=True, frozen=True)
174
- class OrderExpired(BrokerResponseEvent):
175
- order_id: uuid.UUID
176
-
177
- @dataclasses.dataclass(kw_only=True, frozen=True)
178
- class CancelRejected(BrokerResponseEvent):
179
- order_id: uuid.UUID
180
- reason: Models.RejectionReason
181
-
182
- @dataclasses.dataclass(kw_only=True, frozen=True)
183
- class ModifyRejected(BrokerResponseEvent):
184
- order_id: uuid.UUID
185
- reason: Models.RejectionReason
186
-
187
-
188
- class BaseConsumer(abc.ABC):
189
- """
190
- Base class for all consumers.
191
- """
192
-
193
- def __init__(self) -> None:
194
- self.queue: queue.Queue[Events.BaseEvent] = queue.Queue()
195
- self._thread = threading.Thread(
196
- target=self._consume, name=self.__class__.__name__, daemon=True
197
- )
198
- self._thread.start()
199
-
200
- @abc.abstractmethod
201
- def on_event(self, event: Events.BaseEvent) -> None:
202
- pass
203
-
204
- def receive(self, event: Events.BaseEvent) -> None:
205
- self.queue.put(event)
206
-
207
- def _consume(self) -> None:
208
- while True:
209
- event = self.queue.get()
210
- if isinstance(event, Events.SystemShutdown):
211
- self.queue.task_done()
212
- break
213
- self.on_event(event)
214
- self.queue.task_done()
215
-
216
-
217
- class EventBus:
218
- """
219
- Event bus for publishing events to the consumers subscribed to them.
220
- """
221
-
222
- def __init__(self) -> None:
223
- self._subscriptions: defaultdict[type[Events.BaseEvent], list[BaseConsumer]] = (
224
- defaultdict(list)
225
- )
226
- self._consumers: set[BaseConsumer] = set()
227
- self._lock: threading.Lock = threading.Lock()
228
-
229
- def subscribe(self, subscriber: BaseConsumer, event_type: type[Events.BaseEvent]):
230
- with self._lock:
231
- self._consumers.add(subscriber)
232
- if subscriber not in self._subscriptions[event_type]:
233
- self._subscriptions[event_type].append(subscriber)
234
-
235
- def unsubscribe(self, subscriber: BaseConsumer):
236
- with self._lock:
237
- for consumer_list in self._subscriptions.values():
238
- if subscriber in consumer_list:
239
- consumer_list.remove(subscriber)
240
- if not any(subscriber in cl for cl in self._subscriptions.values()):
241
- self._consumers.discard(subscriber)
242
-
243
- def publish(self, event: Events.BaseEvent) -> None:
244
- with self._lock:
245
- consumers = list(self._subscriptions[type(event)])
246
- for consumer in consumers:
247
- consumer.receive(event)
248
-
249
- # Enable synchronous execution via wait_until_idle()
250
- def wait_until_idle(self) -> None:
251
- with self._lock:
252
- consumers = list(self._consumers)
253
- for consumer in consumers:
254
- consumer.queue.join()
255
-
256
-
257
- event_bus = EventBus()
258
- """
259
- Global instance of `EventBus`.
260
- """
@@ -1,173 +0,0 @@
1
- import abc
2
- import pandas as pd
3
- import threading
4
-
5
- from pathlib import Path
6
- from onesecondtrader.core import Events, Models, event_bus, logger
7
-
8
-
9
- class DatafeedBase(abc.ABC):
10
- """
11
- Base class for all datafeeds.
12
- """
13
-
14
- def __init__(self) -> None:
15
- self._is_connected: bool = False
16
- self._watched_symbols: set[tuple[str, Models.RecordType]] = set()
17
- self._lock: threading.Lock = threading.Lock()
18
-
19
- @abc.abstractmethod
20
- def watch(self, symbols: list[tuple[str, Models.RecordType]]) -> bool:
21
- pass
22
-
23
- @abc.abstractmethod
24
- def unwatch(self, symbols: list[str]) -> None:
25
- pass
26
-
27
-
28
- class SimulatedDatafeedCSV(DatafeedBase):
29
- """
30
- CSV-based simulated datafeed for backtesting.
31
- """
32
-
33
- csv_path: str | Path = ""
34
- artificial_delay: float = 0.0
35
-
36
- def __init__(self) -> None:
37
- super().__init__()
38
- self._stop_event = threading.Event()
39
- self._streaming_thread: threading.Thread | None = None
40
- self._data_iterator: pd.io.parsers.readers.TextFileReader | None = None
41
- self._connected_path: str | Path = ""
42
-
43
- def watch(self, symbols: list[tuple[str, Models.RecordType]]) -> bool:
44
- with self._lock:
45
- if not self._is_connected:
46
- try:
47
- self._data_iterator = pd.read_csv(
48
- Path(self.csv_path),
49
- usecols=[
50
- "ts_event",
51
- "rtype",
52
- "open",
53
- "high",
54
- "low",
55
- "close",
56
- "volume",
57
- "symbol",
58
- ],
59
- dtype={
60
- "ts_event": int,
61
- "rtype": int,
62
- "open": int,
63
- "high": int,
64
- "low": int,
65
- "close": int,
66
- "volume": int,
67
- "symbol": str,
68
- },
69
- chunksize=1,
70
- )
71
- self._is_connected = True
72
- self._connected_path = self.csv_path
73
- logger.info(
74
- f"{self.__class__.__name__} connected to {self.csv_path}"
75
- )
76
- except Exception as e:
77
- logger.error(f"{self.__class__.__name__} failed to connect: {e}")
78
- self._data_iterator = None
79
- self._is_connected = False
80
- return False
81
- elif self._connected_path != self.csv_path:
82
- logger.warning(
83
- "csv_path changed while connected; unwatch all symbols first"
84
- )
85
-
86
- self._watched_symbols.update(symbols)
87
- formatted = ", ".join(f"{s} ({r.name})" for s, r in symbols)
88
- logger.info(f"{self.__class__.__name__} watching {formatted}")
89
-
90
- if not self._streaming_thread or not self._streaming_thread.is_alive():
91
- self._stop_event.clear()
92
- self._streaming_thread = threading.Thread(
93
- target=self._stream, name="CSVDatafeedStreaming", daemon=False
94
- )
95
- self._streaming_thread.start()
96
-
97
- return True
98
-
99
- def unwatch(self, symbols: list[str]) -> None:
100
- thread_to_join = None
101
- with self._lock:
102
- symbols_set = set(symbols)
103
- self._watched_symbols.difference_update(
104
- {
105
- (symbol, rtype)
106
- for (symbol, rtype) in self._watched_symbols
107
- if symbol in symbols_set
108
- }
109
- )
110
- logger.info(f"{self.__class__.__name__} unwatched {', '.join(symbols)}")
111
- if not self._watched_symbols:
112
- self._stop_event.set()
113
- thread_to_join = self._streaming_thread
114
- self._streaming_thread = None
115
-
116
- if thread_to_join and thread_to_join.is_alive():
117
- thread_to_join.join(timeout=5.0)
118
- if thread_to_join.is_alive():
119
- logger.warning("Streaming thread did not terminate within timeout")
120
- else:
121
- logger.info(f"{self.__class__.__name__} disconnected")
122
-
123
- def _stream(self) -> None:
124
- if self._data_iterator is None:
125
- logger.error("_stream called with no data iterator")
126
- return
127
- should_delay = self.artificial_delay > 0
128
- delay_time = self.artificial_delay
129
- while not self._stop_event.is_set():
130
- try:
131
- chunk = next(self._data_iterator)
132
- row = chunk.iloc[0]
133
-
134
- symbol = row["symbol"]
135
- record_type = Models.RecordType(row["rtype"])
136
- symbol_key = (symbol, record_type)
137
-
138
- with self._lock:
139
- if symbol_key not in self._watched_symbols:
140
- continue
141
-
142
- bar_event = Events.IncomingBar(
143
- ts_event=pd.Timestamp(row["ts_event"], unit="ns", tz="UTC"),
144
- symbol=symbol,
145
- record_type=record_type,
146
- open=row["open"] / 1e9,
147
- high=row["high"] / 1e9,
148
- low=row["low"] / 1e9,
149
- close=row["close"] / 1e9,
150
- volume=row["volume"],
151
- )
152
-
153
- event_bus.publish(bar_event)
154
- event_bus.wait_until_idle()
155
-
156
- if should_delay and self._stop_event.wait(delay_time):
157
- break
158
- except StopIteration:
159
- logger.info("CSV datafeed reached end of file")
160
- break
161
- except Exception as e:
162
- logger.error(f"CSV datafeed error reading data: {e}")
163
- break
164
-
165
- with self._lock:
166
- self._data_iterator = None
167
- self._is_connected = False
168
-
169
-
170
- simulated_datafeed_csv = SimulatedDatafeedCSV()
171
- """
172
- Global instance of `SimulatedDatafeedCSV`.
173
- """
@@ -1,106 +0,0 @@
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,9 +0,0 @@
1
- onesecondtrader/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
- onesecondtrader/brokers.py,sha256=fDElmF0y6KD6NIVBX0lo8Z-nQzpe-Rp2J2C75TrbSNg,2779
3
- onesecondtrader/core.py,sha256=5H-JydCnMOedLw-GrSB1aooDj6s0R1gpVwI_JgxZpzA,7495
4
- onesecondtrader/datafeeds.py,sha256=IlMipy1OFLx8aMBa46d74Mm3rhhb-uR905odhVP_uX0,6149
5
- onesecondtrader/indicators.py,sha256=wGn-5v8L1gepMP45KcVrEo-f2ReOCD3r8lva9aEIUnY,3199
6
- onesecondtrader-0.22.0.dist-info/METADATA,sha256=lp8FV-Gaqm8IH36ubGu0EPzCXyPog5PfOVYRv3LBJZc,9682
7
- onesecondtrader-0.22.0.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
8
- onesecondtrader-0.22.0.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
9
- onesecondtrader-0.22.0.dist-info/RECORD,,