onesecondtrader 0.34.0__py3-none-any.whl → 0.36.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.
- onesecondtrader/connectors/__init__.py +2 -0
- onesecondtrader/connectors/brokers/__init__.py +3 -0
- onesecondtrader/connectors/brokers/simulated.py +346 -0
- onesecondtrader/connectors/datafeeds/__init__.py +4 -0
- onesecondtrader/connectors/datafeeds/base.py +19 -0
- onesecondtrader/connectors/datafeeds/simulated.py +99 -0
- onesecondtrader/core/__init__.py +6 -0
- onesecondtrader/core/brokers/__init__.py +3 -0
- onesecondtrader/core/brokers/base.py +39 -0
- onesecondtrader/core/events/__init__.py +33 -0
- onesecondtrader/core/events/bases.py +29 -0
- onesecondtrader/core/events/market.py +22 -0
- onesecondtrader/core/events/requests.py +31 -0
- onesecondtrader/core/events/responses.py +54 -0
- onesecondtrader/core/indicators/__init__.py +13 -0
- onesecondtrader/core/indicators/averages.py +56 -0
- onesecondtrader/core/indicators/bar.py +47 -0
- onesecondtrader/core/indicators/base.py +60 -0
- onesecondtrader/core/messaging/__init__.py +7 -0
- onesecondtrader/core/messaging/eventbus.py +47 -0
- onesecondtrader/core/messaging/subscriber.py +69 -0
- onesecondtrader/core/models/__init__.py +12 -0
- onesecondtrader/core/models/data.py +18 -0
- onesecondtrader/core/models/orders.py +15 -0
- onesecondtrader/core/models/records.py +32 -0
- onesecondtrader/core/strategies/__init__.py +7 -0
- onesecondtrader/core/strategies/base.py +318 -0
- onesecondtrader/core/strategies/examples.py +35 -0
- onesecondtrader/dashboard/__init__.py +0 -0
- onesecondtrader/secmaster/__init__.py +5 -0
- onesecondtrader/secmaster/schema.sql +46 -0
- onesecondtrader/secmaster/utils.py +26 -0
- {onesecondtrader-0.34.0.dist-info → onesecondtrader-0.36.0.dist-info}/METADATA +1 -1
- onesecondtrader-0.36.0.dist-info/RECORD +62 -0
- onesecondtrader-0.34.0.dist-info/RECORD +0 -30
- {onesecondtrader-0.34.0.dist-info → onesecondtrader-0.36.0.dist-info}/WHEEL +0 -0
- {onesecondtrader-0.34.0.dist-info → onesecondtrader-0.36.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,318 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import abc
|
|
4
|
+
import uuid
|
|
5
|
+
from types import SimpleNamespace
|
|
6
|
+
|
|
7
|
+
import pandas as pd
|
|
8
|
+
|
|
9
|
+
from onesecondtrader.core import events, indicators, messaging, models
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class StrategyBase(messaging.Subscriber, abc.ABC):
|
|
13
|
+
symbols: list[str] = []
|
|
14
|
+
bar_period: models.BarPeriod = models.BarPeriod.SECOND
|
|
15
|
+
|
|
16
|
+
def __init__(self, event_bus: messaging.EventBus) -> None:
|
|
17
|
+
super().__init__(event_bus)
|
|
18
|
+
self._subscribe(
|
|
19
|
+
events.BarReceived,
|
|
20
|
+
events.OrderSubmissionAccepted,
|
|
21
|
+
events.OrderModificationAccepted,
|
|
22
|
+
events.OrderCancellationAccepted,
|
|
23
|
+
events.OrderSubmissionRejected,
|
|
24
|
+
events.OrderModificationRejected,
|
|
25
|
+
events.OrderCancellationRejected,
|
|
26
|
+
events.OrderFilled,
|
|
27
|
+
events.OrderExpired,
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
self._current_symbol: str = ""
|
|
31
|
+
self._current_ts: pd.Timestamp = pd.Timestamp.now(tz="UTC")
|
|
32
|
+
self._indicators: list[indicators.Indicator] = []
|
|
33
|
+
|
|
34
|
+
self._fills: dict[str, list[models.FillRecord]] = {}
|
|
35
|
+
self._positions: dict[str, float] = {}
|
|
36
|
+
self._avg_prices: dict[str, float] = {}
|
|
37
|
+
self._pending_orders: dict[uuid.UUID, models.OrderRecord] = {}
|
|
38
|
+
self._submitted_orders: dict[uuid.UUID, models.OrderRecord] = {}
|
|
39
|
+
self._submitted_modifications: dict[uuid.UUID, models.OrderRecord] = {}
|
|
40
|
+
self._submitted_cancellations: dict[uuid.UUID, models.OrderRecord] = {}
|
|
41
|
+
|
|
42
|
+
# OHLCV as indicators for history access: self.bar.close.history
|
|
43
|
+
self.bar = SimpleNamespace(
|
|
44
|
+
open=self.add_indicator(indicators.Open()),
|
|
45
|
+
high=self.add_indicator(indicators.High()),
|
|
46
|
+
low=self.add_indicator(indicators.Low()),
|
|
47
|
+
close=self.add_indicator(indicators.Close()),
|
|
48
|
+
volume=self.add_indicator(indicators.Volume()),
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
# Hook for subclasses to register indicators without overriding __init__
|
|
52
|
+
self.setup()
|
|
53
|
+
|
|
54
|
+
def add_indicator(self, ind: indicators.Indicator) -> indicators.Indicator:
|
|
55
|
+
self._indicators.append(ind)
|
|
56
|
+
return ind
|
|
57
|
+
|
|
58
|
+
@property
|
|
59
|
+
def position(self) -> float:
|
|
60
|
+
return self._positions.get(self._current_symbol, 0.0)
|
|
61
|
+
|
|
62
|
+
@property
|
|
63
|
+
def avg_price(self) -> float:
|
|
64
|
+
return self._avg_prices.get(self._current_symbol, 0.0)
|
|
65
|
+
|
|
66
|
+
def submit_order(
|
|
67
|
+
self,
|
|
68
|
+
order_type: models.OrderType,
|
|
69
|
+
side: models.OrderSide,
|
|
70
|
+
quantity: float,
|
|
71
|
+
limit_price: float | None = None,
|
|
72
|
+
stop_price: float | None = None,
|
|
73
|
+
) -> uuid.UUID:
|
|
74
|
+
# Uses bar timestamp for backtest compatibility; ts_created tracks real wall-clock time
|
|
75
|
+
order_id = uuid.uuid4()
|
|
76
|
+
|
|
77
|
+
event = events.OrderSubmission(
|
|
78
|
+
ts_event=self._current_ts,
|
|
79
|
+
system_order_id=order_id,
|
|
80
|
+
symbol=self._current_symbol,
|
|
81
|
+
order_type=order_type,
|
|
82
|
+
side=side,
|
|
83
|
+
quantity=quantity,
|
|
84
|
+
limit_price=limit_price,
|
|
85
|
+
stop_price=stop_price,
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
order = models.OrderRecord(
|
|
89
|
+
order_id=order_id,
|
|
90
|
+
symbol=self._current_symbol,
|
|
91
|
+
order_type=order_type,
|
|
92
|
+
side=side,
|
|
93
|
+
quantity=quantity,
|
|
94
|
+
limit_price=limit_price,
|
|
95
|
+
stop_price=stop_price,
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
self._submitted_orders[order_id] = order
|
|
99
|
+
self._publish(event)
|
|
100
|
+
return order_id
|
|
101
|
+
|
|
102
|
+
def submit_modification(
|
|
103
|
+
self,
|
|
104
|
+
order_id: uuid.UUID,
|
|
105
|
+
quantity: float | None = None,
|
|
106
|
+
limit_price: float | None = None,
|
|
107
|
+
stop_price: float | None = None,
|
|
108
|
+
) -> bool:
|
|
109
|
+
original_order = self._pending_orders.get(order_id)
|
|
110
|
+
if original_order is None:
|
|
111
|
+
return False
|
|
112
|
+
|
|
113
|
+
event = events.OrderModification(
|
|
114
|
+
ts_event=self._current_ts,
|
|
115
|
+
system_order_id=order_id,
|
|
116
|
+
symbol=original_order.symbol,
|
|
117
|
+
quantity=quantity,
|
|
118
|
+
limit_price=limit_price,
|
|
119
|
+
stop_price=stop_price,
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
modified_order = models.OrderRecord(
|
|
123
|
+
order_id=order_id,
|
|
124
|
+
symbol=original_order.symbol,
|
|
125
|
+
order_type=original_order.order_type,
|
|
126
|
+
side=original_order.side,
|
|
127
|
+
quantity=quantity if quantity is not None else original_order.quantity,
|
|
128
|
+
limit_price=(
|
|
129
|
+
limit_price if limit_price is not None else original_order.limit_price
|
|
130
|
+
),
|
|
131
|
+
stop_price=(
|
|
132
|
+
stop_price if stop_price is not None else original_order.stop_price
|
|
133
|
+
),
|
|
134
|
+
filled_quantity=original_order.filled_quantity,
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
self._submitted_modifications[order_id] = modified_order
|
|
138
|
+
self._publish(event)
|
|
139
|
+
return True
|
|
140
|
+
|
|
141
|
+
def submit_cancellation(self, order_id: uuid.UUID) -> bool:
|
|
142
|
+
original_order = self._pending_orders.get(order_id)
|
|
143
|
+
if original_order is None:
|
|
144
|
+
return False
|
|
145
|
+
|
|
146
|
+
event = events.OrderCancellation(
|
|
147
|
+
ts_event=self._current_ts,
|
|
148
|
+
system_order_id=order_id,
|
|
149
|
+
symbol=original_order.symbol,
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
self._submitted_cancellations[order_id] = original_order
|
|
153
|
+
self._publish(event)
|
|
154
|
+
return True
|
|
155
|
+
|
|
156
|
+
def _on_event(self, event: events.EventBase) -> None:
|
|
157
|
+
match event:
|
|
158
|
+
case events.BarReceived() as bar_event:
|
|
159
|
+
self._on_bar_received(bar_event)
|
|
160
|
+
case events.OrderSubmissionAccepted() as accepted:
|
|
161
|
+
self._on_order_submission_accepted(accepted)
|
|
162
|
+
case events.OrderModificationAccepted() as accepted:
|
|
163
|
+
self._on_order_modification_accepted(accepted)
|
|
164
|
+
case events.OrderCancellationAccepted() as accepted:
|
|
165
|
+
self._on_order_cancellation_accepted(accepted)
|
|
166
|
+
case events.OrderSubmissionRejected() as rejected:
|
|
167
|
+
self._on_order_submission_rejected(rejected)
|
|
168
|
+
case events.OrderModificationRejected() as rejected:
|
|
169
|
+
self._on_order_modification_rejected(rejected)
|
|
170
|
+
case events.OrderCancellationRejected() as rejected:
|
|
171
|
+
self._on_order_cancellation_rejected(rejected)
|
|
172
|
+
case events.OrderFilled() as filled:
|
|
173
|
+
self._on_order_filled(filled)
|
|
174
|
+
case events.OrderExpired() as expired:
|
|
175
|
+
self._on_order_expired(expired)
|
|
176
|
+
case _:
|
|
177
|
+
return
|
|
178
|
+
|
|
179
|
+
def _on_bar_received(self, event: events.BarReceived) -> None:
|
|
180
|
+
if event.symbol not in self.symbols:
|
|
181
|
+
return
|
|
182
|
+
if event.bar_period != self.bar_period:
|
|
183
|
+
return
|
|
184
|
+
|
|
185
|
+
self._current_symbol = event.symbol
|
|
186
|
+
self._current_ts = event.ts_event
|
|
187
|
+
|
|
188
|
+
for ind in self._indicators:
|
|
189
|
+
ind.update(event)
|
|
190
|
+
|
|
191
|
+
self._emit_processed_bar(event)
|
|
192
|
+
self.on_bar(event)
|
|
193
|
+
|
|
194
|
+
def _emit_processed_bar(self, event: events.BarReceived) -> None:
|
|
195
|
+
ohlcv_names = {"OPEN", "HIGH", "LOW", "CLOSE", "VOLUME"}
|
|
196
|
+
|
|
197
|
+
indicator_values = {
|
|
198
|
+
f"{ind.plot_at:02d}_{ind.name}": ind.latest
|
|
199
|
+
for ind in self._indicators
|
|
200
|
+
if ind.name not in ohlcv_names
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
processed_bar = events.BarProcessed(
|
|
204
|
+
ts_event=event.ts_event,
|
|
205
|
+
symbol=event.symbol,
|
|
206
|
+
bar_period=event.bar_period,
|
|
207
|
+
open=event.open,
|
|
208
|
+
high=event.high,
|
|
209
|
+
low=event.low,
|
|
210
|
+
close=event.close,
|
|
211
|
+
volume=event.volume,
|
|
212
|
+
indicators=indicator_values,
|
|
213
|
+
)
|
|
214
|
+
|
|
215
|
+
self._publish(processed_bar)
|
|
216
|
+
|
|
217
|
+
def _on_order_submission_accepted(
|
|
218
|
+
self, event: events.OrderSubmissionAccepted
|
|
219
|
+
) -> None:
|
|
220
|
+
order = self._submitted_orders.pop(event.associated_order_id, None)
|
|
221
|
+
if order is not None:
|
|
222
|
+
self._pending_orders[event.associated_order_id] = order
|
|
223
|
+
|
|
224
|
+
def _on_order_modification_accepted(
|
|
225
|
+
self, event: events.OrderModificationAccepted
|
|
226
|
+
) -> None:
|
|
227
|
+
modified_order = self._submitted_modifications.pop(
|
|
228
|
+
event.associated_order_id, None
|
|
229
|
+
)
|
|
230
|
+
if modified_order is not None:
|
|
231
|
+
self._pending_orders[event.associated_order_id] = modified_order
|
|
232
|
+
|
|
233
|
+
def _on_order_cancellation_accepted(
|
|
234
|
+
self, event: events.OrderCancellationAccepted
|
|
235
|
+
) -> None:
|
|
236
|
+
self._submitted_cancellations.pop(event.associated_order_id, None)
|
|
237
|
+
self._pending_orders.pop(event.associated_order_id, None)
|
|
238
|
+
|
|
239
|
+
def _on_order_submission_rejected(
|
|
240
|
+
self, event: events.OrderSubmissionRejected
|
|
241
|
+
) -> None:
|
|
242
|
+
self._submitted_orders.pop(event.associated_order_id, None)
|
|
243
|
+
|
|
244
|
+
def _on_order_modification_rejected(
|
|
245
|
+
self, event: events.OrderModificationRejected
|
|
246
|
+
) -> None:
|
|
247
|
+
self._submitted_modifications.pop(event.associated_order_id, None)
|
|
248
|
+
|
|
249
|
+
def _on_order_cancellation_rejected(
|
|
250
|
+
self, event: events.OrderCancellationRejected
|
|
251
|
+
) -> None:
|
|
252
|
+
self._submitted_cancellations.pop(event.associated_order_id, None)
|
|
253
|
+
|
|
254
|
+
def _on_order_filled(self, event: events.OrderFilled) -> None:
|
|
255
|
+
# Track partial fills: only remove order when fully filled
|
|
256
|
+
order = self._pending_orders.get(event.associated_order_id)
|
|
257
|
+
if order:
|
|
258
|
+
order.filled_quantity += event.quantity_filled
|
|
259
|
+
if order.filled_quantity >= order.quantity:
|
|
260
|
+
self._pending_orders.pop(event.associated_order_id)
|
|
261
|
+
|
|
262
|
+
fill = models.FillRecord(
|
|
263
|
+
fill_id=event.fill_id,
|
|
264
|
+
order_id=event.associated_order_id,
|
|
265
|
+
symbol=event.symbol,
|
|
266
|
+
side=event.side,
|
|
267
|
+
quantity=event.quantity_filled,
|
|
268
|
+
price=event.fill_price,
|
|
269
|
+
commission=event.commission,
|
|
270
|
+
ts_event=event.ts_event,
|
|
271
|
+
)
|
|
272
|
+
|
|
273
|
+
self._fills.setdefault(event.symbol, []).append(fill)
|
|
274
|
+
self._update_position(event)
|
|
275
|
+
|
|
276
|
+
def _update_position(self, event: events.OrderFilled) -> None:
|
|
277
|
+
symbol = event.symbol
|
|
278
|
+
fill_qty = event.quantity_filled
|
|
279
|
+
fill_price = event.fill_price
|
|
280
|
+
|
|
281
|
+
signed_qty = 0.0
|
|
282
|
+
match event.side:
|
|
283
|
+
case models.OrderSide.BUY:
|
|
284
|
+
signed_qty = fill_qty
|
|
285
|
+
case models.OrderSide.SELL:
|
|
286
|
+
signed_qty = -fill_qty
|
|
287
|
+
|
|
288
|
+
old_pos = self._positions.get(symbol, 0.0)
|
|
289
|
+
old_avg = self._avg_prices.get(symbol, 0.0)
|
|
290
|
+
new_pos = old_pos + signed_qty
|
|
291
|
+
|
|
292
|
+
if new_pos == 0.0:
|
|
293
|
+
new_avg = 0.0
|
|
294
|
+
elif old_pos == 0.0:
|
|
295
|
+
new_avg = fill_price
|
|
296
|
+
elif (old_pos > 0 and signed_qty > 0) or (old_pos < 0 and signed_qty < 0):
|
|
297
|
+
new_avg = (old_avg * abs(old_pos) + fill_price * abs(signed_qty)) / abs(
|
|
298
|
+
new_pos
|
|
299
|
+
)
|
|
300
|
+
else:
|
|
301
|
+
if abs(new_pos) <= abs(old_pos):
|
|
302
|
+
new_avg = old_avg
|
|
303
|
+
else:
|
|
304
|
+
new_avg = fill_price
|
|
305
|
+
|
|
306
|
+
self._positions[symbol] = new_pos
|
|
307
|
+
self._avg_prices[symbol] = new_avg
|
|
308
|
+
|
|
309
|
+
def _on_order_expired(self, event: events.OrderExpired) -> None:
|
|
310
|
+
self._pending_orders.pop(event.associated_order_id, None)
|
|
311
|
+
|
|
312
|
+
# Override to register indicators. Called at end of __init__.
|
|
313
|
+
def setup(self) -> None:
|
|
314
|
+
pass
|
|
315
|
+
|
|
316
|
+
@abc.abstractmethod
|
|
317
|
+
def on_bar(self, event: events.BarReceived) -> None:
|
|
318
|
+
pass
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
from onesecondtrader.core import events, indicators, models
|
|
2
|
+
from .base import StrategyBase
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class SMACrossover(StrategyBase):
|
|
6
|
+
fast_period: int = 20
|
|
7
|
+
slow_period: int = 100
|
|
8
|
+
quantity: float = 1.0
|
|
9
|
+
|
|
10
|
+
def setup(self) -> None:
|
|
11
|
+
self.fast_sma = self.add_indicator(
|
|
12
|
+
indicators.SimpleMovingAverage(period=self.fast_period)
|
|
13
|
+
)
|
|
14
|
+
self.slow_sma = self.add_indicator(
|
|
15
|
+
indicators.SimpleMovingAverage(period=self.slow_period)
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
def on_bar(self, event: events.BarReceived) -> None:
|
|
19
|
+
if (
|
|
20
|
+
self.fast_sma[-2] <= self.slow_sma[-2]
|
|
21
|
+
and self.fast_sma.latest > self.slow_sma.latest
|
|
22
|
+
and self.position <= 0
|
|
23
|
+
):
|
|
24
|
+
self.submit_order(
|
|
25
|
+
models.OrderType.MARKET, models.OrderSide.BUY, self.quantity
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
if (
|
|
29
|
+
self.fast_sma[-2] >= self.slow_sma[-2]
|
|
30
|
+
and self.fast_sma.latest < self.slow_sma.latest
|
|
31
|
+
and self.position >= 0
|
|
32
|
+
):
|
|
33
|
+
self.submit_order(
|
|
34
|
+
models.OrderType.MARKET, models.OrderSide.SELL, self.quantity
|
|
35
|
+
)
|
|
File without changes
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
-- Security Master Database Schema
|
|
2
|
+
--
|
|
3
|
+
-- Stores instrument metadata and OHLCV market data. Prices are stored as
|
|
4
|
+
-- fixed-point integers (scale factor $10^9$) to avoid floating-point errors.
|
|
5
|
+
-- Timestamps are nanoseconds since Unix epoch.
|
|
6
|
+
|
|
7
|
+
-- Data providers. Separated from instruments because the same symbol (e.g.,
|
|
8
|
+
-- `ESH5`) may exist across multiple vendors with different `instrument_id`s.
|
|
9
|
+
CREATE TABLE publishers (
|
|
10
|
+
publisher_id INTEGER PRIMARY KEY,
|
|
11
|
+
name TEXT NOT NULL UNIQUE,
|
|
12
|
+
dataset TEXT NOT NULL,
|
|
13
|
+
venue TEXT
|
|
14
|
+
);
|
|
15
|
+
|
|
16
|
+
-- Security/instrument metadata. The UNIQUE constraint on (`publisher_id`, `raw_symbol`)
|
|
17
|
+
-- allows the same symbol from different vendors while preventing duplicates.
|
|
18
|
+
CREATE TABLE instruments (
|
|
19
|
+
instrument_id INTEGER PRIMARY KEY,
|
|
20
|
+
publisher_id INTEGER NOT NULL,
|
|
21
|
+
raw_symbol TEXT NOT NULL,
|
|
22
|
+
instrument_class TEXT NOT NULL DEFAULT 'K',
|
|
23
|
+
exchange TEXT,
|
|
24
|
+
currency TEXT DEFAULT 'USD',
|
|
25
|
+
min_price_increment INTEGER,
|
|
26
|
+
ts_recv INTEGER NOT NULL,
|
|
27
|
+
FOREIGN KEY (publisher_id) REFERENCES publishers(publisher_id),
|
|
28
|
+
UNIQUE(publisher_id, raw_symbol)
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
-- OHLCV bar data. The primary key order (`instrument_id`, `rtype`, `ts_event`) ensures
|
|
32
|
+
-- bars for the same instrument/timeframe are contiguous on disk, making range
|
|
33
|
+
-- queries fast. `WITHOUT ROWID` stores data directly in the primary key B-tree,
|
|
34
|
+
-- eliminating the indirection of a separate rowid lookup.
|
|
35
|
+
CREATE TABLE ohlcv (
|
|
36
|
+
instrument_id INTEGER NOT NULL,
|
|
37
|
+
rtype INTEGER NOT NULL,
|
|
38
|
+
ts_event INTEGER NOT NULL,
|
|
39
|
+
open INTEGER NOT NULL,
|
|
40
|
+
high INTEGER NOT NULL,
|
|
41
|
+
low INTEGER NOT NULL,
|
|
42
|
+
close INTEGER NOT NULL,
|
|
43
|
+
volume INTEGER NOT NULL,
|
|
44
|
+
FOREIGN KEY (instrument_id) REFERENCES instruments(instrument_id),
|
|
45
|
+
PRIMARY KEY (instrument_id, rtype, ts_event)
|
|
46
|
+
) WITHOUT ROWID;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import pathlib
|
|
4
|
+
import sqlite3
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def init_secmaster(db_path: pathlib.Path) -> None:
|
|
8
|
+
"""Initialize a new secmaster database at the specified path.
|
|
9
|
+
|
|
10
|
+
Creates the database file with the schema defined in schema.sql (publishers, instruments,
|
|
11
|
+
and ohlcv tables) but does not populate any data.
|
|
12
|
+
|
|
13
|
+
Args:
|
|
14
|
+
db_path: Path where the database file will be created.
|
|
15
|
+
|
|
16
|
+
Raises:
|
|
17
|
+
FileExistsError: If a database already exists at the path.
|
|
18
|
+
"""
|
|
19
|
+
if db_path.exists():
|
|
20
|
+
raise FileExistsError(f"Database already exists: {db_path}")
|
|
21
|
+
db_path.parent.mkdir(parents=True, exist_ok=True)
|
|
22
|
+
schema_path = pathlib.Path(__file__).parent / "schema.sql"
|
|
23
|
+
conn = sqlite3.connect(str(db_path))
|
|
24
|
+
conn.executescript(schema_path.read_text())
|
|
25
|
+
conn.commit()
|
|
26
|
+
conn.close()
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: onesecondtrader
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.36.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,62 @@
|
|
|
1
|
+
onesecondtrader/__init__.py,sha256=bzV-SaWcDnq7os8pUtsaq8GAsBWjxBDhkFvRs3Z9FJw,1008
|
|
2
|
+
onesecondtrader/brokers/__init__.py,sha256=YofzD0qlrfo_BzL6gwiHb9by7Webp36TQ_1O5EV0uzY,124
|
|
3
|
+
onesecondtrader/brokers/base.py,sha256=b6Xq2gBUdy3RTpnUfpUiNSXwbuq_bRIjXJ-s89Xu7nE,1345
|
|
4
|
+
onesecondtrader/brokers/simulated.py,sha256=pJvZ7b76xAs-NBbOX_v78IJgVrdnuTLCadqj8kYQ5sg,12694
|
|
5
|
+
onesecondtrader/connectors/__init__.py,sha256=TDV1mXCkNVuMS8FQWRts_6FDwBEeNciMqYtZb8HNl2w,120
|
|
6
|
+
onesecondtrader/connectors/brokers/__init__.py,sha256=O67ETIic6yrGDQp_wJSNTsjZ4Tt_3NGO3v4RJf3bm7Q,70
|
|
7
|
+
onesecondtrader/connectors/brokers/simulated.py,sha256=ck0gWdz88KSyEMkiHW2CP0vuLrBELBTz3KveYIwbLuI,12722
|
|
8
|
+
onesecondtrader/connectors/datafeeds/__init__.py,sha256=OzrPQcmdwzI6Fo8BONoWeJQtp0ttQ6TMYL6dUvvery4,113
|
|
9
|
+
onesecondtrader/connectors/datafeeds/base.py,sha256=hpffDlv5Z92OZnL_B3vs_rRmq7uRU4HCf8N6IZkRxmM,482
|
|
10
|
+
onesecondtrader/connectors/datafeeds/simulated.py,sha256=aMLSDu2CVaQcxxeWOcznFrfPgXq3icEB2rRe4KNK0Wg,3011
|
|
11
|
+
onesecondtrader/core/__init__.py,sha256=5T2eAX4I_yo2ku9zywhFvl628kPTINxoLY92u7x_gy0,324
|
|
12
|
+
onesecondtrader/core/brokers/__init__.py,sha256=mC-nNPdaT17oy-rjluwNvbKzxi5q8xxZ4mAkU7aJU0Y,55
|
|
13
|
+
onesecondtrader/core/brokers/base.py,sha256=n82DHGvqkP9c4ATW8qT9yosPRoRQGUrs1oveGASdhlY,1350
|
|
14
|
+
onesecondtrader/core/events/__init__.py,sha256=IOlFRdiOXz93SpvkpKL8wr1954d_8olKSB1n9mlTL3Y,898
|
|
15
|
+
onesecondtrader/core/events/bases.py,sha256=g-ykq2jgcitIAueRurUlqAq0jINQwuhSWi_khAniPHw,662
|
|
16
|
+
onesecondtrader/core/events/market.py,sha256=UY04TH6G-98NgYPEvBQSY92_ddeiT4yNwaUXnHdongQ,514
|
|
17
|
+
onesecondtrader/core/events/requests.py,sha256=7cnt7TiuE7yl9ezFruIixY0v5A-mzZDWdoTBkAcVff0,836
|
|
18
|
+
onesecondtrader/core/events/responses.py,sha256=biHIFa6usnsgSEX9bh2zOs1zB00I7HVSS01Dh_pZISE,1441
|
|
19
|
+
onesecondtrader/core/indicators/__init__.py,sha256=hRg3FCP1FT7LYOLzztybWn48gTR5QvewzzdELPYbdoY,239
|
|
20
|
+
onesecondtrader/core/indicators/averages.py,sha256=sCAIJrOYhtkwO5MkS2vx-4CfzHIVLJZpwYDzwv1GZ7Y,1905
|
|
21
|
+
onesecondtrader/core/indicators/bar.py,sha256=9NUckrLO1AhyO3uTJlNCL2_Pqge58x5S_TcSz4qrk-c,1120
|
|
22
|
+
onesecondtrader/core/indicators/base.py,sha256=_2pJ7PS0MRUz85UT0YpvzJ5V6ce60koT8MbjoerZ9eM,1894
|
|
23
|
+
onesecondtrader/core/messaging/__init__.py,sha256=vMRDabHBgse_vZRTRFtnU8M8v2sY_o4pHjGzgu3hp3E,115
|
|
24
|
+
onesecondtrader/core/messaging/eventbus.py,sha256=SaXLRoqz9tWBBlyEo0ry1HHr_8azj2U2dEaQEnr3PNo,1581
|
|
25
|
+
onesecondtrader/core/messaging/subscriber.py,sha256=Sp77pB6bGyW57FlV0rdzAWy1Jv_nHA77YEtN64spZjs,2111
|
|
26
|
+
onesecondtrader/core/models/__init__.py,sha256=7amHCQ6BAhHKps0ke63E-zh8IJNmkdDogZq-PfBukMs,249
|
|
27
|
+
onesecondtrader/core/models/data.py,sha256=fBmddVl6EXYC5u2UnvQ59DXAXeZeIb48KP1ZdeTL52A,322
|
|
28
|
+
onesecondtrader/core/models/orders.py,sha256=y6Ar-6fMqaOd_hRnRGvfWUF0Z13H_2hfTOW3ROOk0A8,254
|
|
29
|
+
onesecondtrader/core/models/records.py,sha256=vdCWBtoDQs5R4iB_8_3fXkxWEvoCxOssk9XBnS4l7Vk,599
|
|
30
|
+
onesecondtrader/core/strategies/__init__.py,sha256=Nq1n6HCdZ-GKkpn4WAlKxconPnOmeVKo0nhTk0J1ybA,121
|
|
31
|
+
onesecondtrader/core/strategies/base.py,sha256=wZMjPu4sSMclvoyMptIZKllaqFkRrA06CZ5EG7uQ-Uo,11211
|
|
32
|
+
onesecondtrader/core/strategies/examples.py,sha256=j4K092_qt0bcvajp-i4ugrGqzbaFebYX6rBbtZawtvE,1124
|
|
33
|
+
onesecondtrader/dashboard/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
34
|
+
onesecondtrader/datafeeds/__init__.py,sha256=zSj1cQhBS-Z5271ICsmGc_zq-1fOd8yLQ7c4F0oJIVk,124
|
|
35
|
+
onesecondtrader/datafeeds/base.py,sha256=bSIHBlZ8kbJ4rTZrUMiMgl8_j0Kqe6Eh2hNX5E3DhHU,477
|
|
36
|
+
onesecondtrader/datafeeds/simulated.py,sha256=mbnEh0jjgZWRDhRLIASMbUpV1ybQktBA8opTv6nj33s,3006
|
|
37
|
+
onesecondtrader/events/__init__.py,sha256=IOlFRdiOXz93SpvkpKL8wr1954d_8olKSB1n9mlTL3Y,898
|
|
38
|
+
onesecondtrader/events/bases.py,sha256=g-ykq2jgcitIAueRurUlqAq0jINQwuhSWi_khAniPHw,662
|
|
39
|
+
onesecondtrader/events/market.py,sha256=IfHuIGfp_IUiw-dFay4c4RYmkoNzshxbhuWTglBqfN0,509
|
|
40
|
+
onesecondtrader/events/requests.py,sha256=2KXwSckiar9-fy8wkN3vcSIeOkeBfeo_XhUhrNKEd2Q,831
|
|
41
|
+
onesecondtrader/events/responses.py,sha256=w_BH1nkkPyxQjh30EXEVFcUGDoMprFc2PuAaqpVrQ8A,1436
|
|
42
|
+
onesecondtrader/indicators/__init__.py,sha256=hRg3FCP1FT7LYOLzztybWn48gTR5QvewzzdELPYbdoY,239
|
|
43
|
+
onesecondtrader/indicators/averages.py,sha256=DpRRdY5G5ze3jwNOV19PPjV6slA0IEeOOla67yChi2Y,1900
|
|
44
|
+
onesecondtrader/indicators/bar.py,sha256=0H07mKNiUx5cE1fQvx1oPVY0R_MXcmAAgsLek175vTk,1115
|
|
45
|
+
onesecondtrader/indicators/base.py,sha256=64HJD8JWBnUdR7PLd02N3hbNrRfKMjWHEKGeSyMRms8,1889
|
|
46
|
+
onesecondtrader/messaging/__init__.py,sha256=vMRDabHBgse_vZRTRFtnU8M8v2sY_o4pHjGzgu3hp3E,115
|
|
47
|
+
onesecondtrader/messaging/eventbus.py,sha256=Y8VbDZlEz8Q6KcCkfXRKsVIixsctBMRW1a5ANw297Ls,1576
|
|
48
|
+
onesecondtrader/messaging/subscriber.py,sha256=ImpFmu5IstLXLoKVMaebmLp5MXN6225vHLdTL1ZOPvw,2106
|
|
49
|
+
onesecondtrader/models/__init__.py,sha256=7amHCQ6BAhHKps0ke63E-zh8IJNmkdDogZq-PfBukMs,249
|
|
50
|
+
onesecondtrader/models/data.py,sha256=fBmddVl6EXYC5u2UnvQ59DXAXeZeIb48KP1ZdeTL52A,322
|
|
51
|
+
onesecondtrader/models/orders.py,sha256=y6Ar-6fMqaOd_hRnRGvfWUF0Z13H_2hfTOW3ROOk0A8,254
|
|
52
|
+
onesecondtrader/models/records.py,sha256=vdCWBtoDQs5R4iB_8_3fXkxWEvoCxOssk9XBnS4l7Vk,599
|
|
53
|
+
onesecondtrader/secmaster/__init__.py,sha256=HUpfkdWpz6t5YO0QgiEyM5MU32RQNGv9lfsvdE8HHZQ,96
|
|
54
|
+
onesecondtrader/secmaster/schema.sql,sha256=NHHBb86cwZYtSZbOWlBgXd8dQsi5B26KZC-OR5sytAU,1802
|
|
55
|
+
onesecondtrader/secmaster/utils.py,sha256=82DUBbzdGbNmQHewhC2qmcbbarnrr4g9PhEsucxbT4Y,831
|
|
56
|
+
onesecondtrader/strategies/__init__.py,sha256=5TlEckz3RnwZTs1Isj0wJ_9Og5R9MMXBL90Vu9b45io,126
|
|
57
|
+
onesecondtrader/strategies/base.py,sha256=kIi6by4Y8YuB9gPMPMr2Unm5_i9SGAANyiW2UaHiRO0,11206
|
|
58
|
+
onesecondtrader/strategies/sma_crossover.py,sha256=s2u_uL_D5CrZTACiAbojnrLrLf4jqIPdfOCiNDEIsDA,1119
|
|
59
|
+
onesecondtrader-0.36.0.dist-info/METADATA,sha256=BaNIEbmpAsU8S94iLlVRUiabpgeTIJ8p6BwBcyTQ78s,9682
|
|
60
|
+
onesecondtrader-0.36.0.dist-info/WHEEL,sha256=3ny-bZhpXrU6vSQ1UPG34FoxZBp3lVcvK0LkgUz6VLk,88
|
|
61
|
+
onesecondtrader-0.36.0.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
|
|
62
|
+
onesecondtrader-0.36.0.dist-info/RECORD,,
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
onesecondtrader/__init__.py,sha256=bzV-SaWcDnq7os8pUtsaq8GAsBWjxBDhkFvRs3Z9FJw,1008
|
|
2
|
-
onesecondtrader/brokers/__init__.py,sha256=YofzD0qlrfo_BzL6gwiHb9by7Webp36TQ_1O5EV0uzY,124
|
|
3
|
-
onesecondtrader/brokers/base.py,sha256=b6Xq2gBUdy3RTpnUfpUiNSXwbuq_bRIjXJ-s89Xu7nE,1345
|
|
4
|
-
onesecondtrader/brokers/simulated.py,sha256=pJvZ7b76xAs-NBbOX_v78IJgVrdnuTLCadqj8kYQ5sg,12694
|
|
5
|
-
onesecondtrader/datafeeds/__init__.py,sha256=zSj1cQhBS-Z5271ICsmGc_zq-1fOd8yLQ7c4F0oJIVk,124
|
|
6
|
-
onesecondtrader/datafeeds/base.py,sha256=bSIHBlZ8kbJ4rTZrUMiMgl8_j0Kqe6Eh2hNX5E3DhHU,477
|
|
7
|
-
onesecondtrader/datafeeds/simulated.py,sha256=mbnEh0jjgZWRDhRLIASMbUpV1ybQktBA8opTv6nj33s,3006
|
|
8
|
-
onesecondtrader/events/__init__.py,sha256=IOlFRdiOXz93SpvkpKL8wr1954d_8olKSB1n9mlTL3Y,898
|
|
9
|
-
onesecondtrader/events/bases.py,sha256=g-ykq2jgcitIAueRurUlqAq0jINQwuhSWi_khAniPHw,662
|
|
10
|
-
onesecondtrader/events/market.py,sha256=IfHuIGfp_IUiw-dFay4c4RYmkoNzshxbhuWTglBqfN0,509
|
|
11
|
-
onesecondtrader/events/requests.py,sha256=2KXwSckiar9-fy8wkN3vcSIeOkeBfeo_XhUhrNKEd2Q,831
|
|
12
|
-
onesecondtrader/events/responses.py,sha256=w_BH1nkkPyxQjh30EXEVFcUGDoMprFc2PuAaqpVrQ8A,1436
|
|
13
|
-
onesecondtrader/indicators/__init__.py,sha256=hRg3FCP1FT7LYOLzztybWn48gTR5QvewzzdELPYbdoY,239
|
|
14
|
-
onesecondtrader/indicators/averages.py,sha256=DpRRdY5G5ze3jwNOV19PPjV6slA0IEeOOla67yChi2Y,1900
|
|
15
|
-
onesecondtrader/indicators/bar.py,sha256=0H07mKNiUx5cE1fQvx1oPVY0R_MXcmAAgsLek175vTk,1115
|
|
16
|
-
onesecondtrader/indicators/base.py,sha256=64HJD8JWBnUdR7PLd02N3hbNrRfKMjWHEKGeSyMRms8,1889
|
|
17
|
-
onesecondtrader/messaging/__init__.py,sha256=vMRDabHBgse_vZRTRFtnU8M8v2sY_o4pHjGzgu3hp3E,115
|
|
18
|
-
onesecondtrader/messaging/eventbus.py,sha256=Y8VbDZlEz8Q6KcCkfXRKsVIixsctBMRW1a5ANw297Ls,1576
|
|
19
|
-
onesecondtrader/messaging/subscriber.py,sha256=ImpFmu5IstLXLoKVMaebmLp5MXN6225vHLdTL1ZOPvw,2106
|
|
20
|
-
onesecondtrader/models/__init__.py,sha256=7amHCQ6BAhHKps0ke63E-zh8IJNmkdDogZq-PfBukMs,249
|
|
21
|
-
onesecondtrader/models/data.py,sha256=fBmddVl6EXYC5u2UnvQ59DXAXeZeIb48KP1ZdeTL52A,322
|
|
22
|
-
onesecondtrader/models/orders.py,sha256=y6Ar-6fMqaOd_hRnRGvfWUF0Z13H_2hfTOW3ROOk0A8,254
|
|
23
|
-
onesecondtrader/models/records.py,sha256=vdCWBtoDQs5R4iB_8_3fXkxWEvoCxOssk9XBnS4l7Vk,599
|
|
24
|
-
onesecondtrader/strategies/__init__.py,sha256=5TlEckz3RnwZTs1Isj0wJ_9Og5R9MMXBL90Vu9b45io,126
|
|
25
|
-
onesecondtrader/strategies/base.py,sha256=kIi6by4Y8YuB9gPMPMr2Unm5_i9SGAANyiW2UaHiRO0,11206
|
|
26
|
-
onesecondtrader/strategies/sma_crossover.py,sha256=s2u_uL_D5CrZTACiAbojnrLrLf4jqIPdfOCiNDEIsDA,1119
|
|
27
|
-
onesecondtrader-0.34.0.dist-info/METADATA,sha256=N-0PPAPIFmnllV7fvuNLRNJSrRfyWoHT3SIVDOSPOU8,9682
|
|
28
|
-
onesecondtrader-0.34.0.dist-info/WHEEL,sha256=3ny-bZhpXrU6vSQ1UPG34FoxZBp3lVcvK0LkgUz6VLk,88
|
|
29
|
-
onesecondtrader-0.34.0.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
|
|
30
|
-
onesecondtrader-0.34.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|