onesecondtrader 0.43.0__py3-none-any.whl → 0.45.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 (57) hide show
  1. onesecondtrader/__init__.py +0 -60
  2. onesecondtrader/events/__init__.py +7 -0
  3. onesecondtrader/events/base.py +19 -0
  4. onesecondtrader/models/__init__.py +10 -0
  5. onesecondtrader/models/bar_fields.py +23 -0
  6. onesecondtrader/models/bar_period.py +21 -0
  7. onesecondtrader/models/order_types.py +21 -0
  8. onesecondtrader/models/trade_sides.py +20 -0
  9. {onesecondtrader-0.43.0.dist-info → onesecondtrader-0.45.0.dist-info}/METADATA +2 -2
  10. onesecondtrader-0.45.0.dist-info/RECORD +12 -0
  11. onesecondtrader/connectors/__init__.py +0 -3
  12. onesecondtrader/connectors/brokers/__init__.py +0 -4
  13. onesecondtrader/connectors/brokers/ib.py +0 -418
  14. onesecondtrader/connectors/brokers/simulated.py +0 -349
  15. onesecondtrader/connectors/datafeeds/__init__.py +0 -4
  16. onesecondtrader/connectors/datafeeds/ib.py +0 -286
  17. onesecondtrader/connectors/datafeeds/simulated.py +0 -198
  18. onesecondtrader/connectors/gateways/__init__.py +0 -3
  19. onesecondtrader/connectors/gateways/ib.py +0 -314
  20. onesecondtrader/core/__init__.py +0 -7
  21. onesecondtrader/core/brokers/__init__.py +0 -3
  22. onesecondtrader/core/brokers/base.py +0 -46
  23. onesecondtrader/core/datafeeds/__init__.py +0 -3
  24. onesecondtrader/core/datafeeds/base.py +0 -32
  25. onesecondtrader/core/events/__init__.py +0 -33
  26. onesecondtrader/core/events/bases.py +0 -29
  27. onesecondtrader/core/events/market.py +0 -22
  28. onesecondtrader/core/events/requests.py +0 -33
  29. onesecondtrader/core/events/responses.py +0 -54
  30. onesecondtrader/core/indicators/__init__.py +0 -13
  31. onesecondtrader/core/indicators/averages.py +0 -56
  32. onesecondtrader/core/indicators/bar.py +0 -47
  33. onesecondtrader/core/indicators/base.py +0 -60
  34. onesecondtrader/core/messaging/__init__.py +0 -7
  35. onesecondtrader/core/messaging/eventbus.py +0 -47
  36. onesecondtrader/core/messaging/subscriber.py +0 -69
  37. onesecondtrader/core/models/__init__.py +0 -15
  38. onesecondtrader/core/models/data.py +0 -18
  39. onesecondtrader/core/models/orders.py +0 -27
  40. onesecondtrader/core/models/params.py +0 -21
  41. onesecondtrader/core/models/records.py +0 -34
  42. onesecondtrader/core/strategies/__init__.py +0 -7
  43. onesecondtrader/core/strategies/base.py +0 -331
  44. onesecondtrader/core/strategies/examples.py +0 -47
  45. onesecondtrader/dashboard/__init__.py +0 -3
  46. onesecondtrader/dashboard/app.py +0 -2972
  47. onesecondtrader/dashboard/registry.py +0 -100
  48. onesecondtrader/orchestrator/__init__.py +0 -7
  49. onesecondtrader/orchestrator/orchestrator.py +0 -105
  50. onesecondtrader/orchestrator/recorder.py +0 -199
  51. onesecondtrader/orchestrator/schema.sql +0 -212
  52. onesecondtrader/secmaster/__init__.py +0 -6
  53. onesecondtrader/secmaster/schema.sql +0 -740
  54. onesecondtrader/secmaster/utils.py +0 -737
  55. onesecondtrader-0.43.0.dist-info/RECORD +0 -49
  56. {onesecondtrader-0.43.0.dist-info → onesecondtrader-0.45.0.dist-info}/WHEEL +0 -0
  57. {onesecondtrader-0.43.0.dist-info → onesecondtrader-0.45.0.dist-info}/licenses/LICENSE +0 -0
@@ -1,22 +0,0 @@
1
- from __future__ import annotations
2
-
3
- import dataclasses
4
-
5
- from onesecondtrader.core import models
6
- from . import bases
7
-
8
-
9
- @dataclasses.dataclass(kw_only=True, frozen=True)
10
- class BarReceived(bases.MarketEvent):
11
- symbol: str
12
- bar_period: models.data.BarPeriod
13
- open: float
14
- high: float
15
- low: float
16
- close: float
17
- volume: int | None = None
18
-
19
-
20
- @dataclasses.dataclass(kw_only=True, frozen=True)
21
- class BarProcessed(BarReceived):
22
- indicators: dict[str, float] = dataclasses.field(default_factory=dict)
@@ -1,33 +0,0 @@
1
- from __future__ import annotations
2
-
3
- import dataclasses
4
- import uuid
5
-
6
- from onesecondtrader.core import models
7
- from . import bases
8
-
9
-
10
- @dataclasses.dataclass(kw_only=True, frozen=True)
11
- class OrderSubmission(bases.BrokerRequestEvent):
12
- system_order_id: uuid.UUID = dataclasses.field(default_factory=uuid.uuid4)
13
- symbol: str
14
- order_type: models.orders.OrderType
15
- side: models.orders.OrderSide
16
- quantity: float
17
- limit_price: float | None = None
18
- stop_price: float | None = None
19
- action: models.orders.ActionType | None = None
20
- signal: str | None = None
21
-
22
-
23
- @dataclasses.dataclass(kw_only=True, frozen=True)
24
- class OrderCancellation(bases.BrokerRequestEvent):
25
- symbol: str
26
-
27
-
28
- @dataclasses.dataclass(kw_only=True, frozen=True)
29
- class OrderModification(bases.BrokerRequestEvent):
30
- symbol: str
31
- quantity: float | None = None
32
- limit_price: float | None = None
33
- stop_price: float | None = None
@@ -1,54 +0,0 @@
1
- from __future__ import annotations
2
-
3
- import dataclasses
4
- import uuid
5
-
6
- from onesecondtrader.core import models
7
- from . import bases
8
-
9
-
10
- @dataclasses.dataclass(kw_only=True, frozen=True)
11
- class OrderSubmissionAccepted(bases.BrokerResponseEvent):
12
- broker_order_id: str | None = None
13
-
14
-
15
- @dataclasses.dataclass(kw_only=True, frozen=True)
16
- class OrderSubmissionRejected(bases.BrokerResponseEvent):
17
- reason: str | None = None
18
-
19
-
20
- @dataclasses.dataclass(kw_only=True, frozen=True)
21
- class OrderModificationAccepted(bases.BrokerResponseEvent):
22
- broker_order_id: str | None = None
23
-
24
-
25
- @dataclasses.dataclass(kw_only=True, frozen=True)
26
- class OrderModificationRejected(bases.BrokerResponseEvent):
27
- reason: str | None = None
28
-
29
-
30
- @dataclasses.dataclass(kw_only=True, frozen=True)
31
- class OrderCancellationAccepted(bases.BrokerResponseEvent):
32
- pass
33
-
34
-
35
- @dataclasses.dataclass(kw_only=True, frozen=True)
36
- class OrderCancellationRejected(bases.BrokerResponseEvent):
37
- reason: str | None = None
38
-
39
-
40
- @dataclasses.dataclass(kw_only=True, frozen=True)
41
- class OrderFilled(bases.BrokerResponseEvent):
42
- fill_id: uuid.UUID = dataclasses.field(default_factory=uuid.uuid4)
43
- broker_fill_id: str | None = None
44
- symbol: str
45
- side: models.orders.OrderSide
46
- quantity_filled: float
47
- fill_price: float
48
- commission: float
49
- exchange: str = "SIMULATED"
50
-
51
-
52
- @dataclasses.dataclass(kw_only=True, frozen=True)
53
- class OrderExpired(bases.BrokerResponseEvent):
54
- pass
@@ -1,13 +0,0 @@
1
- __all__ = [
2
- "Indicator",
3
- "Open",
4
- "High",
5
- "Low",
6
- "Close",
7
- "Volume",
8
- "SimpleMovingAverage",
9
- ]
10
-
11
- from .base import Indicator
12
- from .bar import Open, High, Low, Close, Volume
13
- from .averages import SimpleMovingAverage
@@ -1,56 +0,0 @@
1
- from __future__ import annotations
2
-
3
- import collections
4
-
5
- import numpy as np
6
-
7
- from onesecondtrader.core import events, models
8
- from .base import Indicator
9
-
10
-
11
- class SimpleMovingAverage(Indicator):
12
- def __init__(
13
- self,
14
- period: int = 200,
15
- max_history: int = 100,
16
- input_source: models.InputSource = models.InputSource.CLOSE,
17
- plot_at: int = 0,
18
- ) -> None:
19
- super().__init__(max_history=max_history, plot_at=plot_at)
20
- self.period: int = max(1, int(period))
21
- self.input_source: models.InputSource = input_source
22
- self._window: dict[str, collections.deque[float]] = {}
23
-
24
- @property
25
- def name(self) -> str:
26
- return f"SMA_{self.period}_{self.input_source.name}"
27
-
28
- def _compute_indicator(self, incoming_bar: events.BarReceived) -> float:
29
- symbol = incoming_bar.symbol
30
- if symbol not in self._window:
31
- self._window[symbol] = collections.deque(maxlen=self.period)
32
- window = self._window[symbol]
33
- value = self._extract_input(incoming_bar)
34
- window.append(value)
35
- if len(window) < self.period:
36
- return np.nan
37
- return sum(window) / self.period
38
-
39
- def _extract_input(self, incoming_bar: events.BarReceived) -> float:
40
- match self.input_source:
41
- case models.InputSource.OPEN:
42
- return incoming_bar.open
43
- case models.InputSource.HIGH:
44
- return incoming_bar.high
45
- case models.InputSource.LOW:
46
- return incoming_bar.low
47
- case models.InputSource.CLOSE:
48
- return incoming_bar.close
49
- case models.InputSource.VOLUME:
50
- return (
51
- float(incoming_bar.volume)
52
- if incoming_bar.volume is not None
53
- else np.nan
54
- )
55
- case _:
56
- return incoming_bar.close
@@ -1,47 +0,0 @@
1
- from onesecondtrader.core import events
2
- from .base import Indicator
3
-
4
-
5
- class Open(Indicator):
6
- @property
7
- def name(self) -> str:
8
- return "OPEN"
9
-
10
- def _compute_indicator(self, incoming_bar: events.BarReceived) -> float:
11
- return incoming_bar.open
12
-
13
-
14
- class High(Indicator):
15
- @property
16
- def name(self) -> str:
17
- return "HIGH"
18
-
19
- def _compute_indicator(self, incoming_bar: events.BarReceived) -> float:
20
- return incoming_bar.high
21
-
22
-
23
- class Low(Indicator):
24
- @property
25
- def name(self) -> str:
26
- return "LOW"
27
-
28
- def _compute_indicator(self, incoming_bar: events.BarReceived) -> float:
29
- return incoming_bar.low
30
-
31
-
32
- class Close(Indicator):
33
- @property
34
- def name(self) -> str:
35
- return "CLOSE"
36
-
37
- def _compute_indicator(self, incoming_bar: events.BarReceived) -> float:
38
- return incoming_bar.close
39
-
40
-
41
- class Volume(Indicator):
42
- @property
43
- def name(self) -> str:
44
- return "VOLUME"
45
-
46
- def _compute_indicator(self, incoming_bar: events.BarReceived) -> float:
47
- return float(incoming_bar.volume) if incoming_bar.volume is not None else 0.0
@@ -1,60 +0,0 @@
1
- from __future__ import annotations
2
-
3
- import abc
4
- import collections
5
- import threading
6
-
7
- import numpy as np
8
-
9
- from onesecondtrader.core import events
10
-
11
-
12
- class Indicator(abc.ABC):
13
- def __init__(self, max_history: int = 100, plot_at: int = 99) -> None:
14
- self._lock = threading.Lock()
15
- self._max_history = max(1, int(max_history))
16
- self._current_symbol: str = ""
17
- self._history_data: dict[str, collections.deque[float]] = {}
18
- self._plot_at = plot_at
19
-
20
- @property
21
- @abc.abstractmethod
22
- def name(self) -> str:
23
- pass
24
-
25
- @abc.abstractmethod
26
- def _compute_indicator(self, incoming_bar: events.BarReceived) -> float:
27
- pass
28
-
29
- def update(self, incoming_bar: events.BarReceived) -> None:
30
- symbol = incoming_bar.symbol
31
- self._current_symbol = symbol
32
- value = self._compute_indicator(incoming_bar)
33
- with self._lock:
34
- if symbol not in self._history_data:
35
- self._history_data[symbol] = collections.deque(maxlen=self._max_history)
36
- self._history_data[symbol].append(value)
37
-
38
- @property
39
- def latest(self) -> float:
40
- with self._lock:
41
- h = self._history_data.get(self._current_symbol, collections.deque())
42
- return h[-1] if h else np.nan
43
-
44
- @property
45
- def history(self) -> collections.deque[float]:
46
- with self._lock:
47
- h = self._history_data.get(self._current_symbol, collections.deque())
48
- return collections.deque(h, maxlen=self._max_history)
49
-
50
- def __getitem__(self, index: int) -> float:
51
- # Returns np.nan on out-of-bounds access. Since np.nan comparisons always
52
- # return False, strategies can skip explicit length checks.
53
- try:
54
- return self.history[index]
55
- except IndexError:
56
- return np.nan
57
-
58
- @property
59
- def plot_at(self) -> int:
60
- return self._plot_at
@@ -1,7 +0,0 @@
1
- __all__ = [
2
- "EventBus",
3
- "Subscriber",
4
- ]
5
-
6
- from .eventbus import EventBus
7
- from .subscriber import Subscriber
@@ -1,47 +0,0 @@
1
- from __future__ import annotations
2
-
3
- import collections
4
- import threading
5
- import typing
6
-
7
- from onesecondtrader.core import events
8
-
9
- if typing.TYPE_CHECKING:
10
- from .subscriber import Subscriber
11
-
12
-
13
- class EventBus:
14
- def __init__(self) -> None:
15
- self._per_event_subscriptions: collections.defaultdict[
16
- type[events.bases.EventBase], set[Subscriber]
17
- ] = collections.defaultdict(set)
18
- self._subscribers: set[Subscriber] = set()
19
- self._lock: threading.Lock = threading.Lock()
20
-
21
- def subscribe(
22
- self,
23
- subscriber: Subscriber,
24
- event_type: type[events.bases.EventBase],
25
- ) -> None:
26
- with self._lock:
27
- self._subscribers.add(subscriber)
28
- self._per_event_subscriptions[event_type].add(subscriber)
29
-
30
- def unsubscribe(self, subscriber: Subscriber) -> None:
31
- with self._lock:
32
- for set_of_event_subscribers in self._per_event_subscriptions.values():
33
- set_of_event_subscribers.discard(subscriber)
34
- self._subscribers.discard(subscriber)
35
-
36
- def publish(self, event: events.bases.EventBase) -> None:
37
- # Intentionally matches exact event types only, not parent classes
38
- with self._lock:
39
- subscribers = self._per_event_subscriptions[type(event)].copy()
40
- for subscriber in subscribers:
41
- subscriber.receive(event)
42
-
43
- def wait_until_system_idle(self) -> None:
44
- with self._lock:
45
- subscribers = self._subscribers.copy()
46
- for subscriber in subscribers:
47
- subscriber.wait_until_idle()
@@ -1,69 +0,0 @@
1
- import abc
2
- import queue
3
- import threading
4
-
5
- from onesecondtrader.core import events
6
- from .eventbus import EventBus
7
-
8
-
9
- class Subscriber(abc.ABC):
10
- def __init__(self, event_bus: EventBus) -> None:
11
- self._event_bus = event_bus
12
- self._queue: queue.Queue[events.bases.EventBase | None] = queue.Queue()
13
- self._running: threading.Event = threading.Event()
14
- self._running.set()
15
- self._thread = threading.Thread(
16
- target=self._event_loop, name=self.__class__.__name__
17
- )
18
- self._thread.start()
19
-
20
- def receive(self, event: events.bases.EventBase) -> None:
21
- if self._running.is_set():
22
- self._queue.put(event)
23
-
24
- def wait_until_idle(self) -> None:
25
- if not self._running.is_set():
26
- return
27
- self._queue.join()
28
-
29
- def shutdown(self) -> None:
30
- if not self._running.is_set():
31
- return
32
- self._event_bus.unsubscribe(self)
33
- self._running.clear()
34
- self._queue.put(None)
35
- if threading.current_thread() is not self._thread:
36
- self._thread.join()
37
-
38
- def _subscribe(self, *event_types: type[events.bases.EventBase]) -> None:
39
- for event_type in event_types:
40
- self._event_bus.subscribe(self, event_type)
41
-
42
- def _publish(self, event: events.bases.EventBase) -> None:
43
- self._event_bus.publish(event)
44
-
45
- def _event_loop(self) -> None:
46
- while True:
47
- event = self._queue.get()
48
- if event is None:
49
- self._queue.task_done()
50
- break
51
- try:
52
- self._on_event(event)
53
- except Exception as exc:
54
- self._on_exception(exc)
55
- finally:
56
- self._queue.task_done()
57
- self._cleanup()
58
-
59
- def _on_exception(self, exc: Exception) -> None:
60
- # Override in subclass to log or handle exceptions
61
- pass
62
-
63
- def _cleanup(self) -> None:
64
- pass
65
-
66
- @abc.abstractmethod
67
- def _on_event(self, event: events.bases.EventBase) -> None:
68
- # Must not block indefinitely; wait_until_idle() has no timeout
69
- ...
@@ -1,15 +0,0 @@
1
- __all__ = [
2
- "ActionType",
3
- "BarPeriod",
4
- "InputSource",
5
- "OrderSide",
6
- "OrderType",
7
- "OrderRecord",
8
- "FillRecord",
9
- "ParamSpec",
10
- ]
11
-
12
- from .data import BarPeriod, InputSource
13
- from .orders import ActionType, OrderSide, OrderType
14
- from .records import OrderRecord, FillRecord
15
- from .params import ParamSpec
@@ -1,18 +0,0 @@
1
- from __future__ import annotations
2
-
3
- import enum
4
-
5
-
6
- class BarPeriod(enum.Enum):
7
- SECOND = enum.auto()
8
- MINUTE = enum.auto()
9
- HOUR = enum.auto()
10
- DAY = enum.auto()
11
-
12
-
13
- class InputSource(enum.Enum):
14
- OPEN = enum.auto()
15
- HIGH = enum.auto()
16
- LOW = enum.auto()
17
- CLOSE = enum.auto()
18
- VOLUME = enum.auto()
@@ -1,27 +0,0 @@
1
- from __future__ import annotations
2
-
3
- import enum
4
-
5
-
6
- class OrderType(enum.Enum):
7
- LIMIT = enum.auto()
8
- MARKET = enum.auto()
9
- STOP = enum.auto()
10
- STOP_LIMIT = enum.auto()
11
-
12
-
13
- class OrderSide(enum.Enum):
14
- BUY = enum.auto()
15
- SELL = enum.auto()
16
-
17
-
18
- class ActionType(enum.Enum):
19
- ENTRY = enum.auto()
20
- ENTRY_LONG = enum.auto()
21
- ENTRY_SHORT = enum.auto()
22
- EXIT = enum.auto()
23
- EXIT_LONG = enum.auto()
24
- EXIT_SHORT = enum.auto()
25
- ADD = enum.auto()
26
- REDUCE = enum.auto()
27
- REVERSE = enum.auto()
@@ -1,21 +0,0 @@
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
@@ -1,34 +0,0 @@
1
- from __future__ import annotations
2
-
3
- import dataclasses
4
- import uuid
5
-
6
- import pandas as pd
7
-
8
- from . import orders
9
-
10
-
11
- @dataclasses.dataclass
12
- class OrderRecord:
13
- order_id: uuid.UUID
14
- symbol: str
15
- order_type: orders.OrderType
16
- side: orders.OrderSide
17
- quantity: float
18
- limit_price: float | None = None
19
- stop_price: float | None = None
20
- action: orders.ActionType | None = None
21
- signal: str | None = None
22
- filled_quantity: float = 0.0
23
-
24
-
25
- @dataclasses.dataclass
26
- class FillRecord:
27
- fill_id: uuid.UUID
28
- order_id: uuid.UUID
29
- symbol: str
30
- side: orders.OrderSide
31
- quantity: float
32
- price: float
33
- commission: float
34
- ts_event: pd.Timestamp
@@ -1,7 +0,0 @@
1
- __all__ = [
2
- "StrategyBase",
3
- "SMACrossover",
4
- ]
5
-
6
- from .base import StrategyBase
7
- from .examples import SMACrossover