roboquant 0.2.7__tar.gz → 0.2.8__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.
- {roboquant-0.2.7 → roboquant-0.2.8}/PKG-INFO +3 -2
- {roboquant-0.2.7 → roboquant-0.2.8}/pyproject.toml +12 -2
- {roboquant-0.2.7 → roboquant-0.2.8}/roboquant/__init__.py +1 -1
- {roboquant-0.2.7 → roboquant-0.2.8}/roboquant/brokers/ibkr.py +4 -1
- {roboquant-0.2.7 → roboquant-0.2.8}/roboquant/event.py +1 -1
- {roboquant-0.2.7 → roboquant-0.2.8}/roboquant/feeds/__init__.py +1 -0
- {roboquant-0.2.7 → roboquant-0.2.8}/roboquant/feeds/alpacafeed.py +11 -23
- {roboquant-0.2.7 → roboquant-0.2.8}/roboquant/feeds/csvfeed.py +13 -3
- {roboquant-0.2.7 → roboquant-0.2.8}/roboquant/feeds/feedutil.py +5 -2
- {roboquant-0.2.7 → roboquant-0.2.8}/roboquant/feeds/historic.py +1 -1
- roboquant-0.2.8/roboquant/feeds/live.py +35 -0
- roboquant-0.2.8/roboquant/feeds/sp500.json +2527 -0
- {roboquant-0.2.7 → roboquant-0.2.8}/roboquant/feeds/tiingo.py +6 -22
- {roboquant-0.2.7 → roboquant-0.2.8}/roboquant/feeds/yahoo.py +2 -0
- {roboquant-0.2.7 → roboquant-0.2.8}/roboquant/journals/basicjournal.py +6 -2
- {roboquant-0.2.7 → roboquant-0.2.8}/roboquant/ml/envs.py +89 -139
- {roboquant-0.2.7 → roboquant-0.2.8}/roboquant/ml/features.py +163 -36
- roboquant-0.2.8/roboquant/ml/strategies.py +59 -0
- {roboquant-0.2.7 → roboquant-0.2.8}/roboquant/ml/torch.py +23 -38
- {roboquant-0.2.7 → roboquant-0.2.8}/roboquant/run.py +2 -2
- {roboquant-0.2.7 → roboquant-0.2.8}/roboquant/strategies/__init__.py +0 -1
- {roboquant-0.2.7 → roboquant-0.2.8}/roboquant/strategies/barstrategy.py +6 -3
- {roboquant-0.2.7 → roboquant-0.2.8}/roboquant/strategies/emacrossover.py +5 -4
- {roboquant-0.2.7 → roboquant-0.2.8}/roboquant/traders/flextrader.py +40 -8
- {roboquant-0.2.7 → roboquant-0.2.8}/roboquant/traders/sizing.py +2 -2
- {roboquant-0.2.7 → roboquant-0.2.8}/roboquant.egg-info/PKG-INFO +3 -2
- {roboquant-0.2.7 → roboquant-0.2.8}/roboquant.egg-info/SOURCES.txt +2 -1
- {roboquant-0.2.7 → roboquant-0.2.8}/roboquant.egg-info/requires.txt +2 -1
- roboquant-0.2.8/roboquant.egg-info/top_level.txt +4 -0
- roboquant-0.2.7/roboquant/ml/strategies.py +0 -46
- roboquant-0.2.7/roboquant/strategies/nopstrategy.py +0 -12
- roboquant-0.2.7/roboquant.egg-info/top_level.txt +0 -1
- {roboquant-0.2.7 → roboquant-0.2.8}/LICENSE +0 -0
- {roboquant-0.2.7 → roboquant-0.2.8}/README.md +0 -0
- {roboquant-0.2.7 → roboquant-0.2.8}/roboquant/account.py +0 -0
- {roboquant-0.2.7 → roboquant-0.2.8}/roboquant/brokers/__init__.py +0 -0
- {roboquant-0.2.7 → roboquant-0.2.8}/roboquant/brokers/broker.py +0 -0
- {roboquant-0.2.7 → roboquant-0.2.8}/roboquant/brokers/simbroker.py +0 -0
- {roboquant-0.2.7 → roboquant-0.2.8}/roboquant/config.py +0 -0
- {roboquant-0.2.7 → roboquant-0.2.8}/roboquant/feeds/aggregate.py +0 -0
- {roboquant-0.2.7 → roboquant-0.2.8}/roboquant/feeds/eventchannel.py +0 -0
- {roboquant-0.2.7 → roboquant-0.2.8}/roboquant/feeds/feed.py +0 -0
- {roboquant-0.2.7 → roboquant-0.2.8}/roboquant/feeds/randomwalk.py +0 -0
- {roboquant-0.2.7 → roboquant-0.2.8}/roboquant/feeds/sqllitefeed.py +0 -0
- {roboquant-0.2.7 → roboquant-0.2.8}/roboquant/journals/__init__.py +0 -0
- {roboquant-0.2.7 → roboquant-0.2.8}/roboquant/journals/alphabeta.py +0 -0
- {roboquant-0.2.7 → roboquant-0.2.8}/roboquant/journals/feedmetric.py +0 -0
- {roboquant-0.2.7 → roboquant-0.2.8}/roboquant/journals/journal.py +0 -0
- {roboquant-0.2.7 → roboquant-0.2.8}/roboquant/journals/metric.py +0 -0
- {roboquant-0.2.7 → roboquant-0.2.8}/roboquant/journals/metricsjournal.py +0 -0
- {roboquant-0.2.7 → roboquant-0.2.8}/roboquant/journals/pnlmetric.py +0 -0
- {roboquant-0.2.7 → roboquant-0.2.8}/roboquant/journals/pricemetric.py +0 -0
- {roboquant-0.2.7 → roboquant-0.2.8}/roboquant/journals/runmetric.py +0 -0
- {roboquant-0.2.7 → roboquant-0.2.8}/roboquant/journals/tensorboardjournal.py +0 -0
- {roboquant-0.2.7 → roboquant-0.2.8}/roboquant/ml/__init__.py +0 -0
- {roboquant-0.2.7 → roboquant-0.2.8}/roboquant/order.py +0 -0
- {roboquant-0.2.7 → roboquant-0.2.8}/roboquant/signal.py +0 -0
- {roboquant-0.2.7 → roboquant-0.2.8}/roboquant/strategies/buffer.py +0 -0
- {roboquant-0.2.7 → roboquant-0.2.8}/roboquant/strategies/multistrategy.py +0 -0
- {roboquant-0.2.7 → roboquant-0.2.8}/roboquant/strategies/smacrossover.py +0 -0
- {roboquant-0.2.7 → roboquant-0.2.8}/roboquant/strategies/strategy.py +0 -0
- {roboquant-0.2.7 → roboquant-0.2.8}/roboquant/timeframe.py +0 -0
- {roboquant-0.2.7 → roboquant-0.2.8}/roboquant/traders/__init__.py +0 -0
- {roboquant-0.2.7 → roboquant-0.2.8}/roboquant/traders/trader.py +0 -0
- {roboquant-0.2.7 → roboquant-0.2.8}/roboquant.egg-info/dependency_links.txt +0 -0
- {roboquant-0.2.7 → roboquant-0.2.8}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: roboquant
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.8
|
|
4
4
|
Summary: A fast algo-trading platform
|
|
5
5
|
Author-email: roboquant team <info@roboquant.org>
|
|
6
6
|
Project-URL: Homepage, https://roboquant.org
|
|
@@ -28,7 +28,8 @@ Requires-Dist: requests>=2.31.0
|
|
|
28
28
|
Provides-Extra: torch
|
|
29
29
|
Requires-Dist: torch>=2.1.0; extra == "torch"
|
|
30
30
|
Requires-Dist: tensorboard>=2.15.2; extra == "torch"
|
|
31
|
-
Requires-Dist: stable-baselines3>=2.2.1; extra == "torch"
|
|
31
|
+
Requires-Dist: stable-baselines3[extra]>=2.2.1; extra == "torch"
|
|
32
|
+
Requires-Dist: sb3-contrib>=2.2.1; extra == "torch"
|
|
32
33
|
Provides-Extra: yahoo
|
|
33
34
|
Requires-Dist: yfinance~=0.2.36; extra == "yahoo"
|
|
34
35
|
Provides-Extra: ibkr
|
|
@@ -10,6 +10,10 @@ testpaths = [
|
|
|
10
10
|
|
|
11
11
|
[tool.pyright]
|
|
12
12
|
reportOptionalOperand = "none"
|
|
13
|
+
exclude = ["samples/*.py"]
|
|
14
|
+
|
|
15
|
+
[tool.pylint.MASTER]
|
|
16
|
+
ignore-paths = 'samples'
|
|
13
17
|
|
|
14
18
|
[tool.pylint.'MESSAGES CONTROL']
|
|
15
19
|
max-line-length = 127
|
|
@@ -22,6 +26,12 @@ max-attributes = 10
|
|
|
22
26
|
requires = ["setuptools>=61.0"]
|
|
23
27
|
build-backend = "setuptools.build_meta"
|
|
24
28
|
|
|
29
|
+
[tool.setuptools.packages.find]
|
|
30
|
+
exclude = ["docs*", "tests*", "samples*"]
|
|
31
|
+
|
|
32
|
+
[tool.setuptools.package-data]
|
|
33
|
+
"*" = ["*.json"]
|
|
34
|
+
|
|
25
35
|
[project]
|
|
26
36
|
name = "roboquant"
|
|
27
37
|
dynamic = ["version"]
|
|
@@ -54,11 +64,11 @@ dependencies = [
|
|
|
54
64
|
version = {attr = "roboquant.__version__"}
|
|
55
65
|
|
|
56
66
|
[project.optional-dependencies]
|
|
57
|
-
torch = ["torch>=2.1.0", "tensorboard>=2.15.2", "stable-baselines3>=2.2.1"]
|
|
67
|
+
torch = ["torch>=2.1.0", "tensorboard>=2.15.2", "stable-baselines3[extra]>=2.2.1", "sb3-contrib>=2.2.1"]
|
|
58
68
|
yahoo = ["yfinance~=0.2.36"]
|
|
59
69
|
ibkr = ["nautilus-ibapi~=10.19.2"]
|
|
60
70
|
alpaca = ["alpaca-py"]
|
|
61
|
-
all = [
|
|
71
|
+
all = ["roboquant[torch,yahoo,ibkr]"]
|
|
62
72
|
|
|
63
73
|
|
|
64
74
|
[project.urls]
|
|
@@ -14,7 +14,7 @@ from ibapi.wrapper import EWrapper
|
|
|
14
14
|
from roboquant.account import Account, Position
|
|
15
15
|
from roboquant.event import Event
|
|
16
16
|
from roboquant.order import Order, OrderStatus
|
|
17
|
-
from .broker import Broker, _update_positions
|
|
17
|
+
from roboquant.brokers.broker import Broker, _update_positions
|
|
18
18
|
|
|
19
19
|
assert VERSION["major"] == 10 and VERSION["minor"] == 19, "Wrong version of the IBAPI found"
|
|
20
20
|
|
|
@@ -144,6 +144,7 @@ class IBKRBroker(Broker):
|
|
|
144
144
|
self.__api = api
|
|
145
145
|
self.__has_new_orders_since_sync = False
|
|
146
146
|
self.price_type = "DEFAULT"
|
|
147
|
+
self.sleep_after_cancel = 0.0
|
|
147
148
|
|
|
148
149
|
# Start the handling in a thread
|
|
149
150
|
self.__api_thread = threading.Thread(target=api.run, daemon=True)
|
|
@@ -211,6 +212,8 @@ class IBKRBroker(Broker):
|
|
|
211
212
|
if order.size.is_zero():
|
|
212
213
|
assert order.id is not None, "can only cancel orders with an id"
|
|
213
214
|
self.__api.cancelOrder(int(order.id), "")
|
|
215
|
+
if self.sleep_after_cancel:
|
|
216
|
+
time.sleep(self.sleep_after_cancel)
|
|
214
217
|
else:
|
|
215
218
|
if order.id is None:
|
|
216
219
|
order.id = self.__api.get_next_order_id()
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
from array import array
|
|
2
2
|
from datetime import timedelta
|
|
3
3
|
import threading
|
|
4
|
-
import time
|
|
5
4
|
from typing import Literal
|
|
6
5
|
|
|
7
6
|
from alpaca.data import DataFeed
|
|
@@ -11,11 +10,10 @@ from alpaca.data.live.option import OptionDataStream
|
|
|
11
10
|
|
|
12
11
|
from roboquant.config import Config
|
|
13
12
|
from roboquant.event import Event, Quote, Trade, Bar
|
|
14
|
-
from roboquant.feeds.
|
|
15
|
-
from roboquant.feeds.feed import Feed
|
|
13
|
+
from roboquant.feeds.live import LiveFeed
|
|
16
14
|
|
|
17
15
|
|
|
18
|
-
class AlpacaLiveFeed(
|
|
16
|
+
class AlpacaLiveFeed(LiveFeed):
|
|
19
17
|
|
|
20
18
|
__one_minute = str(timedelta(minutes=1))
|
|
21
19
|
|
|
@@ -38,34 +36,24 @@ class AlpacaLiveFeed(Feed):
|
|
|
38
36
|
|
|
39
37
|
thread = threading.Thread(None, self.stream.run, daemon=True)
|
|
40
38
|
thread.start()
|
|
41
|
-
self._channel = None
|
|
42
|
-
|
|
43
|
-
def play(self, channel: EventChannel):
|
|
44
|
-
self._channel = channel
|
|
45
|
-
while not channel.is_closed:
|
|
46
|
-
time.sleep(1)
|
|
47
|
-
self._channel = None
|
|
48
39
|
|
|
49
40
|
async def close(self):
|
|
50
41
|
await self.stream.close()
|
|
51
42
|
|
|
52
43
|
async def __handle_trades(self, data):
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
self._channel.put(event)
|
|
44
|
+
item = Trade(data.symbol, data.price, data.size)
|
|
45
|
+
event = Event(data.timestamp, [item])
|
|
46
|
+
self.put(event)
|
|
57
47
|
|
|
58
48
|
async def __handle_bars(self, data):
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
self._channel.put(event)
|
|
49
|
+
item = Bar(data.symbol, array("f", [data.open, data.high, data.low, data.close, data.volume]), self.__one_minute)
|
|
50
|
+
event = Event(data.timestamp, [item])
|
|
51
|
+
self.put(event)
|
|
63
52
|
|
|
64
53
|
async def __handle_quotes(self, data):
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
self._channel.put(event)
|
|
54
|
+
item = Quote(data.symbol, array("f", [data.ask_price, data.ask_size, data.bid_price, data.bid_size]))
|
|
55
|
+
event = Event(data.timestamp, [item])
|
|
56
|
+
self.put(event)
|
|
69
57
|
|
|
70
58
|
def subscribe_trades(self, *symbols: str):
|
|
71
59
|
self.stream.subscribe_trades(self.__handle_trades, *symbols)
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import csv
|
|
2
|
+
import sys
|
|
2
3
|
import logging
|
|
3
4
|
import os
|
|
4
5
|
import pathlib
|
|
@@ -87,9 +88,18 @@ class CSVFeed(HistoricFeed):
|
|
|
87
88
|
|
|
88
89
|
class StooqCSVFeed(CSVFeed):
|
|
89
90
|
def __init__(self):
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
91
|
+
# from Python 3.11 onwards we can use the fast standard ISO parsing
|
|
92
|
+
if sys.version_info >= (3, 11):
|
|
93
|
+
super().__init__(path, columns=columns, time_offset="21:00:00+00:00", endswith=".txt", frequency="1d")
|
|
94
|
+
else:
|
|
95
|
+
super().__init__(
|
|
96
|
+
path,
|
|
97
|
+
columns=columns,
|
|
98
|
+
time_offset="21:00:00+00:00",
|
|
99
|
+
datetime_fmt="%Y%m%d",
|
|
100
|
+
endswith=".txt",
|
|
101
|
+
frequency="1d",
|
|
102
|
+
)
|
|
93
103
|
|
|
94
104
|
def _get_symbol(self, filename: str):
|
|
95
105
|
base = pathlib.Path(filename).stem.upper()
|
|
@@ -23,8 +23,11 @@ def get_symbol_prices(
|
|
|
23
23
|
return x, y
|
|
24
24
|
|
|
25
25
|
|
|
26
|
-
def
|
|
27
|
-
"""Get the OHLCV values for a
|
|
26
|
+
def get_ohlcv(feed: Feed, symbol: str, timeframe: Timeframe | None = None) -> dict[str, list]:
|
|
27
|
+
"""Get the OHLCV values for a symbol from a feed.
|
|
28
|
+
The returned value is a dict with the keys being "Date", "Open", "High", "Low", "Close", "Volume"
|
|
29
|
+
and the values a list.
|
|
30
|
+
"""
|
|
28
31
|
|
|
29
32
|
result = {column: [] for column in ["Date", "Open", "High", "Low", "Close", "Volume"]}
|
|
30
33
|
channel = feed.play_background(timeframe)
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
from datetime import datetime, timedelta
|
|
2
|
+
import time
|
|
3
|
+
|
|
4
|
+
from roboquant.event import Event
|
|
5
|
+
from .eventchannel import ChannelClosed, EventChannel
|
|
6
|
+
from .feed import Feed
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class LiveFeed(Feed):
|
|
10
|
+
"""
|
|
11
|
+
Abstract base class for feeds that produce live price-items. It will ensure that
|
|
12
|
+
events that are published are monotonic in time (so always increasing).
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
def __init__(self):
|
|
16
|
+
super().__init__()
|
|
17
|
+
self._channel = None
|
|
18
|
+
self._last_time = datetime.fromisoformat("1900-01-01T00:00:00+00:00")
|
|
19
|
+
self._min_change = timedelta(microseconds=1)
|
|
20
|
+
|
|
21
|
+
def play(self, channel: EventChannel):
|
|
22
|
+
self._channel = channel
|
|
23
|
+
while not channel.is_closed:
|
|
24
|
+
time.sleep(1)
|
|
25
|
+
self._channel = None
|
|
26
|
+
|
|
27
|
+
def put(self, event: Event):
|
|
28
|
+
if self._channel:
|
|
29
|
+
try:
|
|
30
|
+
if event.time <= self._last_time:
|
|
31
|
+
event.time = self._last_time + self._min_change
|
|
32
|
+
self._channel.put(event)
|
|
33
|
+
self._last_time = event.time
|
|
34
|
+
except ChannelClosed:
|
|
35
|
+
pass
|