onesecondtrader 0.40.0__tar.gz → 0.41.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.
- {onesecondtrader-0.40.0 → onesecondtrader-0.41.0}/PKG-INFO +3 -1
- {onesecondtrader-0.40.0 → onesecondtrader-0.41.0}/pyproject.toml +3 -1
- {onesecondtrader-0.40.0 → onesecondtrader-0.41.0}/src/onesecondtrader/__init__.py +2 -0
- {onesecondtrader-0.40.0 → onesecondtrader-0.41.0}/src/onesecondtrader/connectors/datafeeds/simulated.py +21 -6
- {onesecondtrader-0.40.0 → onesecondtrader-0.41.0}/src/onesecondtrader/core/datafeeds/base.py +3 -0
- {onesecondtrader-0.40.0 → onesecondtrader-0.41.0}/src/onesecondtrader/core/models/__init__.py +2 -0
- onesecondtrader-0.41.0/src/onesecondtrader/core/models/params.py +21 -0
- {onesecondtrader-0.40.0 → onesecondtrader-0.41.0}/src/onesecondtrader/core/strategies/base.py +9 -3
- {onesecondtrader-0.40.0 → onesecondtrader-0.41.0}/src/onesecondtrader/core/strategies/examples.py +15 -7
- onesecondtrader-0.41.0/src/onesecondtrader/dashboard/__init__.py +3 -0
- onesecondtrader-0.41.0/src/onesecondtrader/dashboard/app.py +1677 -0
- onesecondtrader-0.41.0/src/onesecondtrader/dashboard/registry.py +100 -0
- onesecondtrader-0.41.0/src/onesecondtrader/orchestrator/__init__.py +7 -0
- onesecondtrader-0.41.0/src/onesecondtrader/orchestrator/orchestrator.py +105 -0
- onesecondtrader-0.41.0/src/onesecondtrader/orchestrator/recorder.py +196 -0
- onesecondtrader-0.41.0/src/onesecondtrader/orchestrator/schema.sql +208 -0
- {onesecondtrader-0.40.0 → onesecondtrader-0.41.0}/src/onesecondtrader/secmaster/schema.sql +48 -0
- {onesecondtrader-0.40.0 → onesecondtrader-0.41.0}/src/onesecondtrader/secmaster/utils.py +90 -0
- onesecondtrader-0.40.0/src/onesecondtrader/dashboard/__init__.py +0 -0
- {onesecondtrader-0.40.0 → onesecondtrader-0.41.0}/LICENSE +0 -0
- {onesecondtrader-0.40.0 → onesecondtrader-0.41.0}/README.md +0 -0
- {onesecondtrader-0.40.0 → onesecondtrader-0.41.0}/src/onesecondtrader/connectors/__init__.py +0 -0
- {onesecondtrader-0.40.0 → onesecondtrader-0.41.0}/src/onesecondtrader/connectors/brokers/__init__.py +0 -0
- {onesecondtrader-0.40.0 → onesecondtrader-0.41.0}/src/onesecondtrader/connectors/brokers/ib.py +0 -0
- {onesecondtrader-0.40.0 → onesecondtrader-0.41.0}/src/onesecondtrader/connectors/brokers/simulated.py +0 -0
- {onesecondtrader-0.40.0 → onesecondtrader-0.41.0}/src/onesecondtrader/connectors/datafeeds/__init__.py +0 -0
- {onesecondtrader-0.40.0 → onesecondtrader-0.41.0}/src/onesecondtrader/connectors/datafeeds/ib.py +0 -0
- {onesecondtrader-0.40.0 → onesecondtrader-0.41.0}/src/onesecondtrader/connectors/gateways/__init__.py +0 -0
- {onesecondtrader-0.40.0 → onesecondtrader-0.41.0}/src/onesecondtrader/connectors/gateways/ib.py +0 -0
- {onesecondtrader-0.40.0 → onesecondtrader-0.41.0}/src/onesecondtrader/core/__init__.py +0 -0
- {onesecondtrader-0.40.0 → onesecondtrader-0.41.0}/src/onesecondtrader/core/brokers/__init__.py +0 -0
- {onesecondtrader-0.40.0 → onesecondtrader-0.41.0}/src/onesecondtrader/core/brokers/base.py +0 -0
- {onesecondtrader-0.40.0 → onesecondtrader-0.41.0}/src/onesecondtrader/core/datafeeds/__init__.py +0 -0
- {onesecondtrader-0.40.0 → onesecondtrader-0.41.0}/src/onesecondtrader/core/events/__init__.py +0 -0
- {onesecondtrader-0.40.0 → onesecondtrader-0.41.0}/src/onesecondtrader/core/events/bases.py +0 -0
- {onesecondtrader-0.40.0 → onesecondtrader-0.41.0}/src/onesecondtrader/core/events/market.py +0 -0
- {onesecondtrader-0.40.0 → onesecondtrader-0.41.0}/src/onesecondtrader/core/events/requests.py +0 -0
- {onesecondtrader-0.40.0 → onesecondtrader-0.41.0}/src/onesecondtrader/core/events/responses.py +0 -0
- {onesecondtrader-0.40.0 → onesecondtrader-0.41.0}/src/onesecondtrader/core/indicators/__init__.py +0 -0
- {onesecondtrader-0.40.0 → onesecondtrader-0.41.0}/src/onesecondtrader/core/indicators/averages.py +0 -0
- {onesecondtrader-0.40.0 → onesecondtrader-0.41.0}/src/onesecondtrader/core/indicators/bar.py +0 -0
- {onesecondtrader-0.40.0 → onesecondtrader-0.41.0}/src/onesecondtrader/core/indicators/base.py +0 -0
- {onesecondtrader-0.40.0 → onesecondtrader-0.41.0}/src/onesecondtrader/core/messaging/__init__.py +0 -0
- {onesecondtrader-0.40.0 → onesecondtrader-0.41.0}/src/onesecondtrader/core/messaging/eventbus.py +0 -0
- {onesecondtrader-0.40.0 → onesecondtrader-0.41.0}/src/onesecondtrader/core/messaging/subscriber.py +0 -0
- {onesecondtrader-0.40.0 → onesecondtrader-0.41.0}/src/onesecondtrader/core/models/data.py +0 -0
- {onesecondtrader-0.40.0 → onesecondtrader-0.41.0}/src/onesecondtrader/core/models/orders.py +0 -0
- {onesecondtrader-0.40.0 → onesecondtrader-0.41.0}/src/onesecondtrader/core/models/records.py +0 -0
- {onesecondtrader-0.40.0 → onesecondtrader-0.41.0}/src/onesecondtrader/core/strategies/__init__.py +0 -0
- {onesecondtrader-0.40.0 → onesecondtrader-0.41.0}/src/onesecondtrader/secmaster/__init__.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: onesecondtrader
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.41.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
|
|
@@ -12,12 +12,14 @@ Classifier: Programming Language :: Python :: 3.12
|
|
|
12
12
|
Classifier: Programming Language :: Python :: 3.13
|
|
13
13
|
Classifier: Programming Language :: Python :: 3.14
|
|
14
14
|
Requires-Dist: databento (>=0.69.0,<0.70.0)
|
|
15
|
+
Requires-Dist: fastapi (>=0.128.0,<0.129.0)
|
|
15
16
|
Requires-Dist: ib-async (>=2.1.0,<3.0.0)
|
|
16
17
|
Requires-Dist: matplotlib (>=3.10.7,<4.0.0)
|
|
17
18
|
Requires-Dist: mplfinance (>=0.12.10b0,<0.13.0)
|
|
18
19
|
Requires-Dist: pandas (>=2.3.1,<3.0.0)
|
|
19
20
|
Requires-Dist: python-dotenv (>=1.0.0,<2.0.0)
|
|
20
21
|
Requires-Dist: tqdm (>=4.67.1,<5.0.0)
|
|
22
|
+
Requires-Dist: uvicorn (>=0.40.0,<0.41.0)
|
|
21
23
|
Description-Content-Type: text/markdown
|
|
22
24
|
|
|
23
25
|
# OneSecondTrader
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "onesecondtrader"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.41.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"}
|
|
@@ -15,6 +15,8 @@ dependencies = [
|
|
|
15
15
|
"tqdm (>=4.67.1,<5.0.0)",
|
|
16
16
|
"mplfinance (>=0.12.10b0,<0.13.0)",
|
|
17
17
|
"ib-async (>=2.1.0,<3.0.0)",
|
|
18
|
+
"fastapi (>=0.128.0,<0.129.0)",
|
|
19
|
+
"uvicorn (>=0.40.0,<0.41.0)",
|
|
18
20
|
]
|
|
19
21
|
|
|
20
22
|
[tool.poetry]
|
|
@@ -18,6 +18,7 @@ __all__ = [
|
|
|
18
18
|
"OrderSide",
|
|
19
19
|
"OrderSubmission",
|
|
20
20
|
"OrderType",
|
|
21
|
+
"ParamSpec",
|
|
21
22
|
"SimulatedBroker",
|
|
22
23
|
"SimulatedDatafeed",
|
|
23
24
|
"SimpleMovingAverage",
|
|
@@ -52,5 +53,6 @@ from onesecondtrader.core.models import (
|
|
|
52
53
|
OrderRecord,
|
|
53
54
|
OrderSide,
|
|
54
55
|
OrderType,
|
|
56
|
+
ParamSpec,
|
|
55
57
|
)
|
|
56
58
|
from onesecondtrader.core.strategies import SMACrossover, StrategyBase
|
|
@@ -21,6 +21,8 @@ _PRICE_SCALE = 1e9
|
|
|
21
21
|
|
|
22
22
|
class SimulatedDatafeed(DatafeedBase):
|
|
23
23
|
db_path: str = ""
|
|
24
|
+
start_ts: int | None = None
|
|
25
|
+
end_ts: int | None = None
|
|
24
26
|
|
|
25
27
|
def __init__(self, event_bus: messaging.EventBus) -> None:
|
|
26
28
|
super().__init__(event_bus)
|
|
@@ -51,9 +53,14 @@ class SimulatedDatafeed(DatafeedBase):
|
|
|
51
53
|
self._connected = False
|
|
52
54
|
|
|
53
55
|
def subscribe(self, symbol: str, bar_period: models.BarPeriod) -> None:
|
|
54
|
-
if (symbol, bar_period) in self._subscriptions:
|
|
55
|
-
return
|
|
56
56
|
self._subscriptions.add((symbol, bar_period))
|
|
57
|
+
|
|
58
|
+
def unsubscribe(self, symbol: str, bar_period: models.BarPeriod) -> None:
|
|
59
|
+
self._subscriptions.discard((symbol, bar_period))
|
|
60
|
+
|
|
61
|
+
def wait_until_complete(self) -> None:
|
|
62
|
+
if not self._subscriptions:
|
|
63
|
+
return
|
|
57
64
|
if self._thread is None or not self._thread.is_alive():
|
|
58
65
|
self._stop_event.clear()
|
|
59
66
|
self._thread = threading.Thread(
|
|
@@ -62,9 +69,7 @@ class SimulatedDatafeed(DatafeedBase):
|
|
|
62
69
|
daemon=False,
|
|
63
70
|
)
|
|
64
71
|
self._thread.start()
|
|
65
|
-
|
|
66
|
-
def unsubscribe(self, symbol: str, bar_period: models.BarPeriod) -> None:
|
|
67
|
-
self._subscriptions.discard((symbol, bar_period))
|
|
72
|
+
self._thread.join()
|
|
68
73
|
|
|
69
74
|
def _stream(self) -> None:
|
|
70
75
|
if not self._connection:
|
|
@@ -94,15 +99,25 @@ class SimulatedDatafeed(DatafeedBase):
|
|
|
94
99
|
placeholders_ids = ",".join("?" * len(id_list))
|
|
95
100
|
placeholders_rtypes = ",".join("?" * len(rtype_list))
|
|
96
101
|
|
|
102
|
+
date_filter = ""
|
|
103
|
+
params = id_list + rtype_list
|
|
104
|
+
if self.start_ts is not None:
|
|
105
|
+
date_filter += " AND ts_event >= ?"
|
|
106
|
+
params.append(self.start_ts)
|
|
107
|
+
if self.end_ts is not None:
|
|
108
|
+
date_filter += " AND ts_event <= ?"
|
|
109
|
+
params.append(self.end_ts)
|
|
110
|
+
|
|
97
111
|
query = f"""
|
|
98
112
|
SELECT instrument_id, rtype, ts_event, open, high, low, close, volume
|
|
99
113
|
FROM ohlcv
|
|
100
114
|
WHERE instrument_id IN ({placeholders_ids})
|
|
101
115
|
AND rtype IN ({placeholders_rtypes})
|
|
116
|
+
{date_filter}
|
|
102
117
|
ORDER BY ts_event
|
|
103
118
|
"""
|
|
104
119
|
|
|
105
|
-
cursor.execute(query,
|
|
120
|
+
cursor.execute(query, params)
|
|
106
121
|
|
|
107
122
|
while True:
|
|
108
123
|
if self._stop_event.is_set():
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import dataclasses
|
|
4
|
+
import enum
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
@dataclasses.dataclass
|
|
8
|
+
class ParamSpec:
|
|
9
|
+
default: int | float | str | bool | enum.Enum
|
|
10
|
+
min: int | float | None = None
|
|
11
|
+
max: int | float | None = None
|
|
12
|
+
step: int | float | None = None
|
|
13
|
+
choices: list | None = None
|
|
14
|
+
|
|
15
|
+
@property
|
|
16
|
+
def resolved_choices(self) -> list | None:
|
|
17
|
+
if self.choices is not None:
|
|
18
|
+
return self.choices
|
|
19
|
+
if isinstance(self.default, enum.Enum):
|
|
20
|
+
return list(type(self.default))
|
|
21
|
+
return None
|
{onesecondtrader-0.40.0 → onesecondtrader-0.41.0}/src/onesecondtrader/core/strategies/base.py
RENAMED
|
@@ -10,11 +10,17 @@ from onesecondtrader.core import events, indicators, messaging, models
|
|
|
10
10
|
|
|
11
11
|
|
|
12
12
|
class StrategyBase(messaging.Subscriber, abc.ABC):
|
|
13
|
+
name: str = ""
|
|
13
14
|
symbols: list[str] = []
|
|
14
|
-
|
|
15
|
+
parameters: dict[str, models.ParamSpec] = {}
|
|
15
16
|
|
|
16
|
-
def __init__(self, event_bus: messaging.EventBus) -> None:
|
|
17
|
+
def __init__(self, event_bus: messaging.EventBus, **overrides) -> None:
|
|
17
18
|
super().__init__(event_bus)
|
|
19
|
+
|
|
20
|
+
for name, spec in self.parameters.items():
|
|
21
|
+
value = overrides.get(name, spec.default)
|
|
22
|
+
setattr(self, name, value)
|
|
23
|
+
|
|
18
24
|
self._subscribe(
|
|
19
25
|
events.BarReceived,
|
|
20
26
|
events.OrderSubmissionAccepted,
|
|
@@ -179,7 +185,7 @@ class StrategyBase(messaging.Subscriber, abc.ABC):
|
|
|
179
185
|
def _on_bar_received(self, event: events.BarReceived) -> None:
|
|
180
186
|
if event.symbol not in self.symbols:
|
|
181
187
|
return
|
|
182
|
-
if event.bar_period != self.bar_period:
|
|
188
|
+
if event.bar_period != self.bar_period: # type: ignore[attr-defined]
|
|
183
189
|
return
|
|
184
190
|
|
|
185
191
|
self._current_symbol = event.symbol
|
{onesecondtrader-0.40.0 → onesecondtrader-0.41.0}/src/onesecondtrader/core/strategies/examples.py
RENAMED
|
@@ -3,16 +3,20 @@ from .base import StrategyBase
|
|
|
3
3
|
|
|
4
4
|
|
|
5
5
|
class SMACrossover(StrategyBase):
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
name = "SMA Crossover"
|
|
7
|
+
parameters = {
|
|
8
|
+
"bar_period": models.ParamSpec(default=models.BarPeriod.SECOND),
|
|
9
|
+
"fast_period": models.ParamSpec(default=20, min=5, max=100, step=1),
|
|
10
|
+
"slow_period": models.ParamSpec(default=100, min=10, max=500, step=1),
|
|
11
|
+
"quantity": models.ParamSpec(default=1.0, min=0.1, max=100.0, step=0.1),
|
|
12
|
+
}
|
|
9
13
|
|
|
10
14
|
def setup(self) -> None:
|
|
11
15
|
self.fast_sma = self.add_indicator(
|
|
12
|
-
indicators.SimpleMovingAverage(period=self.fast_period)
|
|
16
|
+
indicators.SimpleMovingAverage(period=self.fast_period) # type: ignore[attr-defined]
|
|
13
17
|
)
|
|
14
18
|
self.slow_sma = self.add_indicator(
|
|
15
|
-
indicators.SimpleMovingAverage(period=self.slow_period)
|
|
19
|
+
indicators.SimpleMovingAverage(period=self.slow_period) # type: ignore[attr-defined]
|
|
16
20
|
)
|
|
17
21
|
|
|
18
22
|
def on_bar(self, event: events.BarReceived) -> None:
|
|
@@ -22,7 +26,9 @@ class SMACrossover(StrategyBase):
|
|
|
22
26
|
and self.position <= 0
|
|
23
27
|
):
|
|
24
28
|
self.submit_order(
|
|
25
|
-
models.OrderType.MARKET,
|
|
29
|
+
models.OrderType.MARKET,
|
|
30
|
+
models.OrderSide.BUY,
|
|
31
|
+
self.quantity, # type: ignore[attr-defined]
|
|
26
32
|
)
|
|
27
33
|
|
|
28
34
|
if (
|
|
@@ -31,5 +37,7 @@ class SMACrossover(StrategyBase):
|
|
|
31
37
|
and self.position >= 0
|
|
32
38
|
):
|
|
33
39
|
self.submit_order(
|
|
34
|
-
models.OrderType.MARKET,
|
|
40
|
+
models.OrderType.MARKET,
|
|
41
|
+
models.OrderSide.SELL,
|
|
42
|
+
self.quantity, # type: ignore[attr-defined]
|
|
35
43
|
)
|