roboquant 2.3.1__tar.gz → 2.3.3__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-2.3.1 → roboquant-2.3.3}/PKG-INFO +2 -2
- {roboquant-2.3.1 → roboquant-2.3.3}/README.md +1 -1
- {roboquant-2.3.1 → roboquant-2.3.3}/pyproject.toml +1 -1
- {roboquant-2.3.1 → roboquant-2.3.3}/roboquant/ai/features.py +3 -3
- {roboquant-2.3.1 → roboquant-2.3.3}/roboquant/feeds/feed.py +1 -1
- {roboquant-2.3.1 → roboquant-2.3.3}/roboquant/feeds/parquet.py +1 -0
- {roboquant-2.3.1 → roboquant-2.3.3}/roboquant/journals/metricsjournal.py +52 -33
- {roboquant-2.3.1 → roboquant-2.3.3}/roboquant/journals/scorecard.py +7 -5
- {roboquant-2.3.1 → roboquant-2.3.3}/roboquant/signal.py +1 -2
- {roboquant-2.3.1 → roboquant-2.3.3}/roboquant/strategies/emacrossover.py +6 -7
- {roboquant-2.3.1 → roboquant-2.3.3}/LICENSE +0 -0
- {roboquant-2.3.1 → roboquant-2.3.3}/roboquant/__init__.py +0 -0
- {roboquant-2.3.1 → roboquant-2.3.3}/roboquant/account.py +0 -0
- {roboquant-2.3.1 → roboquant-2.3.3}/roboquant/ai/__init__.py +0 -0
- {roboquant-2.3.1 → roboquant-2.3.3}/roboquant/ai/rl.py +0 -0
- {roboquant-2.3.1 → roboquant-2.3.3}/roboquant/ai/strategies.py +0 -0
- {roboquant-2.3.1 → roboquant-2.3.3}/roboquant/alpaca/__init__.py +0 -0
- {roboquant-2.3.1 → roboquant-2.3.3}/roboquant/alpaca/broker.py +0 -0
- {roboquant-2.3.1 → roboquant-2.3.3}/roboquant/alpaca/feed.py +0 -0
- {roboquant-2.3.1 → roboquant-2.3.3}/roboquant/asset.py +0 -0
- {roboquant-2.3.1 → roboquant-2.3.3}/roboquant/brokers/__init__.py +0 -0
- {roboquant-2.3.1 → roboquant-2.3.3}/roboquant/brokers/broker.py +0 -0
- {roboquant-2.3.1 → roboquant-2.3.3}/roboquant/brokers/simbroker.py +0 -0
- {roboquant-2.3.1 → roboquant-2.3.3}/roboquant/crypto/__init__.py +0 -0
- {roboquant-2.3.1 → roboquant-2.3.3}/roboquant/crypto/cryptobroker.py +0 -0
- {roboquant-2.3.1 → roboquant-2.3.3}/roboquant/crypto/cryptofeed.py +0 -0
- {roboquant-2.3.1 → roboquant-2.3.3}/roboquant/event.py +0 -0
- {roboquant-2.3.1 → roboquant-2.3.3}/roboquant/feeds/__init__.py +0 -0
- {roboquant-2.3.1 → roboquant-2.3.3}/roboquant/feeds/csvfeed.py +0 -0
- {roboquant-2.3.1 → roboquant-2.3.3}/roboquant/feeds/historic.py +0 -0
- {roboquant-2.3.1 → roboquant-2.3.3}/roboquant/feeds/live.py +0 -0
- {roboquant-2.3.1 → roboquant-2.3.3}/roboquant/feeds/randomwalk.py +0 -0
- {roboquant-2.3.1 → roboquant-2.3.3}/roboquant/feeds/resources/us10.parquet +0 -0
- {roboquant-2.3.1 → roboquant-2.3.3}/roboquant/feeds/sql.py +0 -0
- {roboquant-2.3.1 → roboquant-2.3.3}/roboquant/feeds/util.py +0 -0
- {roboquant-2.3.1 → roboquant-2.3.3}/roboquant/feeds/yahoo.py +0 -0
- {roboquant-2.3.1 → roboquant-2.3.3}/roboquant/ibkr/__init__.py +0 -0
- {roboquant-2.3.1 → roboquant-2.3.3}/roboquant/ibkr/broker.py +0 -0
- {roboquant-2.3.1 → roboquant-2.3.3}/roboquant/ibkr/types.py +0 -0
- {roboquant-2.3.1 → roboquant-2.3.3}/roboquant/journals/__init__.py +0 -0
- {roboquant-2.3.1 → roboquant-2.3.3}/roboquant/journals/alphabeta.py +0 -0
- {roboquant-2.3.1 → roboquant-2.3.3}/roboquant/journals/basicjournal.py +0 -0
- {roboquant-2.3.1 → roboquant-2.3.3}/roboquant/journals/feedmetric.py +0 -0
- {roboquant-2.3.1 → roboquant-2.3.3}/roboquant/journals/journal.py +0 -0
- {roboquant-2.3.1 → roboquant-2.3.3}/roboquant/journals/marketmetric.py +0 -0
- {roboquant-2.3.1 → roboquant-2.3.3}/roboquant/journals/metric.py +0 -0
- {roboquant-2.3.1 → roboquant-2.3.3}/roboquant/journals/pnlmetric.py +0 -0
- {roboquant-2.3.1 → roboquant-2.3.3}/roboquant/journals/pricemetric.py +0 -0
- {roboquant-2.3.1 → roboquant-2.3.3}/roboquant/journals/runmetric.py +0 -0
- {roboquant-2.3.1 → roboquant-2.3.3}/roboquant/journals/tensorboard.py +0 -0
- {roboquant-2.3.1 → roboquant-2.3.3}/roboquant/monetary.py +0 -0
- {roboquant-2.3.1 → roboquant-2.3.3}/roboquant/order.py +0 -0
- {roboquant-2.3.1 → roboquant-2.3.3}/roboquant/run.py +0 -0
- {roboquant-2.3.1 → roboquant-2.3.3}/roboquant/strategies/__init__.py +0 -0
- {roboquant-2.3.1 → roboquant-2.3.3}/roboquant/strategies/buffer.py +0 -0
- {roboquant-2.3.1 → roboquant-2.3.3}/roboquant/strategies/cachedstrategy.py +0 -0
- {roboquant-2.3.1 → roboquant-2.3.3}/roboquant/strategies/ibsstrategy.py +0 -0
- {roboquant-2.3.1 → roboquant-2.3.3}/roboquant/strategies/multistrategy.py +0 -0
- {roboquant-2.3.1 → roboquant-2.3.3}/roboquant/strategies/strategy.py +0 -0
- {roboquant-2.3.1 → roboquant-2.3.3}/roboquant/strategies/tastrategy.py +0 -0
- {roboquant-2.3.1 → roboquant-2.3.3}/roboquant/ta/__init__.py +0 -0
- {roboquant-2.3.1 → roboquant-2.3.3}/roboquant/timeframe.py +0 -0
- {roboquant-2.3.1 → roboquant-2.3.3}/roboquant/traders/__init__.py +0 -0
- {roboquant-2.3.1 → roboquant-2.3.3}/roboquant/traders/flextrader.py +0 -0
- {roboquant-2.3.1 → roboquant-2.3.3}/roboquant/traders/trader.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: roboquant
|
|
3
|
-
Version: 2.3.
|
|
3
|
+
Version: 2.3.3
|
|
4
4
|
Summary: A fast algorithmic trading platform with support for AI based strategies
|
|
5
5
|
Keywords: algorithmic,trading,machine learning,AI,investment,finance,crypto,stocks,exchange,forex
|
|
6
6
|
Author: roboquant team
|
|
@@ -77,7 +77,7 @@ python3 -m pip install --upgrade roboquant
|
|
|
77
77
|
The core of roboquant limits the number of dependencies. But you can install roboquant including one or more of the optional dependencies if you require certain additional functionality:
|
|
78
78
|
|
|
79
79
|
```shell
|
|
80
|
-
# AI based strategies using
|
|
80
|
+
# AI based strategies using PyTorch and SB3
|
|
81
81
|
python3 -m pip install --upgrade "roboquant[ai]"
|
|
82
82
|
|
|
83
83
|
# Integration with Interactive Brokers
|
|
@@ -35,7 +35,7 @@ python3 -m pip install --upgrade roboquant
|
|
|
35
35
|
The core of roboquant limits the number of dependencies. But you can install roboquant including one or more of the optional dependencies if you require certain additional functionality:
|
|
36
36
|
|
|
37
37
|
```shell
|
|
38
|
-
# AI based strategies using
|
|
38
|
+
# AI based strategies using PyTorch and SB3
|
|
39
39
|
python3 -m pip install --upgrade "roboquant[ai]"
|
|
40
40
|
|
|
41
41
|
# Integration with Interactive Brokers
|
|
@@ -19,7 +19,7 @@ module-name = "roboquant"
|
|
|
19
19
|
|
|
20
20
|
[project]
|
|
21
21
|
name = "roboquant"
|
|
22
|
-
version = "2.3.
|
|
22
|
+
version = "2.3.3"
|
|
23
23
|
authors = [{ name = "roboquant team", email = "info@roboquant.org" }]
|
|
24
24
|
description = "A fast algorithmic trading platform with support for AI based strategies"
|
|
25
25
|
readme = "README.md"
|
|
@@ -244,7 +244,7 @@ class ReturnFeature(Feature[T]):
|
|
|
244
244
|
class LongReturnsFeature(Feature[T]):
|
|
245
245
|
def __init__(self, feature: Feature[T], period: int) -> None:
|
|
246
246
|
super().__init__()
|
|
247
|
-
self.history = deque(maxlen=period)
|
|
247
|
+
self.history: deque[FloatArray] = deque(maxlen=period)
|
|
248
248
|
self.feature: Feature = feature
|
|
249
249
|
|
|
250
250
|
def calc(self, value: T) -> FloatArray:
|
|
@@ -275,7 +275,7 @@ class MaxReturnFeature(Feature[T]):
|
|
|
275
275
|
def __init__(self, feature: Feature[T], period: int) -> None:
|
|
276
276
|
super().__init__()
|
|
277
277
|
assert feature.size() == 1
|
|
278
|
-
self.history = deque(maxlen=period)
|
|
278
|
+
self.history: deque[FloatArray] = deque(maxlen=period)
|
|
279
279
|
self.feature: Feature = feature
|
|
280
280
|
|
|
281
281
|
def calc(self, value: T) -> FloatArray:
|
|
@@ -304,7 +304,7 @@ class MinReturnFeature(Feature[T]):
|
|
|
304
304
|
|
|
305
305
|
def __init__(self, feature: Feature[T], period: int) -> None:
|
|
306
306
|
super().__init__()
|
|
307
|
-
self.history = deque(maxlen=period)
|
|
307
|
+
self.history: deque[FloatArray] = deque(maxlen=period)
|
|
308
308
|
self.feature: Feature = feature
|
|
309
309
|
|
|
310
310
|
def calc(self, value: T) -> FloatArray:
|
|
@@ -13,7 +13,7 @@ class Feed(ABC):
|
|
|
13
13
|
It provides methods for playing the events and some smaller utility methods, like plotting prices.
|
|
14
14
|
|
|
15
15
|
Although the most common type of events are those containing market data, other types of
|
|
16
|
-
events are also possible. For example, events containing news items or social media
|
|
16
|
+
events are also possible. For example, events containing news items or social media posts could
|
|
17
17
|
also be represented as a feed.
|
|
18
18
|
"""
|
|
19
19
|
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
1
2
|
from datetime import datetime
|
|
2
3
|
|
|
3
4
|
from roboquant.journals.journal import Journal
|
|
@@ -9,6 +10,55 @@ from roboquant.event import Event
|
|
|
9
10
|
from roboquant.order import Order
|
|
10
11
|
from typing import Dict, List
|
|
11
12
|
|
|
13
|
+
from roboquant.timeframe import Timeframe
|
|
14
|
+
|
|
15
|
+
@dataclass
|
|
16
|
+
class TimeSeries:
|
|
17
|
+
name: str
|
|
18
|
+
timeline: list[datetime]
|
|
19
|
+
values: list[float]
|
|
20
|
+
|
|
21
|
+
def __post_init__(self):
|
|
22
|
+
if len(self.timeline) != len(self.values):
|
|
23
|
+
raise ValueError("Timeline and values must have the same length")
|
|
24
|
+
|
|
25
|
+
def __len__(self):
|
|
26
|
+
return len(self.timeline)
|
|
27
|
+
|
|
28
|
+
def timeframe(self) -> Timeframe:
|
|
29
|
+
"""Return the timeframe of the time series. If the time series is empty,
|
|
30
|
+
an empty timeframe will be returned."""
|
|
31
|
+
return Timeframe(self.timeline[0], self.timeline[-1], True) if len(self) > 0 else Timeframe.EMPTY
|
|
32
|
+
|
|
33
|
+
def plot(self, plot_x: bool = True, ax = None, **kwargs):
|
|
34
|
+
"""Plot one of the metrics. Optional a `matplotlib.axes.Axes` can be provided
|
|
35
|
+
This requires matplotlib to be installed."""
|
|
36
|
+
if not ax:
|
|
37
|
+
from matplotlib import pyplot as plt
|
|
38
|
+
_, ax = plt.subplots()
|
|
39
|
+
|
|
40
|
+
if plot_x:
|
|
41
|
+
result = ax.plot(self.timeline, self.values, **kwargs) # type: ignore
|
|
42
|
+
else:
|
|
43
|
+
result = ax.plot(self.values, **kwargs)
|
|
44
|
+
|
|
45
|
+
ax.set_title(self.name)
|
|
46
|
+
return result
|
|
47
|
+
|
|
48
|
+
def to_dataframe(self, time_index: bool = False):
|
|
49
|
+
"""Return the timeseries as a Pandas dataframe optionally with the time being the index
|
|
50
|
+
and the value being the column.
|
|
51
|
+
"""
|
|
52
|
+
import pandas as pd
|
|
53
|
+
|
|
54
|
+
d = {
|
|
55
|
+
"time": self.timeline,
|
|
56
|
+
"value": self.values
|
|
57
|
+
}
|
|
58
|
+
df = pd.DataFrame.from_dict(d, orient="columns")
|
|
59
|
+
df.Name = self.name
|
|
60
|
+
return df.set_index("time") if time_index else df # type: ignore
|
|
61
|
+
|
|
12
62
|
|
|
13
63
|
class MetricsJournal(Journal):
|
|
14
64
|
"""
|
|
@@ -36,7 +86,7 @@ class MetricsJournal(Journal):
|
|
|
36
86
|
|
|
37
87
|
self._history.append((event.time, result))
|
|
38
88
|
|
|
39
|
-
def get_metric(self, metric_name: str) ->
|
|
89
|
+
def get_metric(self, metric_name: str) -> TimeSeries:
|
|
40
90
|
"""Return the calculated values of a metric as tuple of date-times and float values"""
|
|
41
91
|
timeline: list[datetime] = []
|
|
42
92
|
values: list[float] = []
|
|
@@ -44,38 +94,7 @@ class MetricsJournal(Journal):
|
|
|
44
94
|
if metric_name in metrics:
|
|
45
95
|
timeline.append(time)
|
|
46
96
|
values.append(metrics[metric_name])
|
|
47
|
-
return timeline, values
|
|
48
|
-
|
|
49
|
-
def to_dataframe(self, metric_name: str, time_index: bool = False):
|
|
50
|
-
"""Return the metric as a Pandas dataframe optionally with the time being the index
|
|
51
|
-
and the value being the column.
|
|
52
|
-
"""
|
|
53
|
-
import pandas as pd
|
|
54
|
-
|
|
55
|
-
times, values = self.get_metric(metric_name)
|
|
56
|
-
d = {
|
|
57
|
-
"time": times,
|
|
58
|
-
"value": values
|
|
59
|
-
}
|
|
60
|
-
df = pd.DataFrame.from_dict(d, orient="columns")
|
|
61
|
-
return df.set_index("time") if time_index else df # type: ignore
|
|
62
|
-
|
|
63
|
-
def plot(self, metric_name: str, plot_x: bool = True, ax = None, **kwargs):
|
|
64
|
-
"""Plot one of the metrics. Optional a `matplotlib.axes.Axes` can be provided
|
|
65
|
-
This requires matplotlib to be installed."""
|
|
66
|
-
if not ax:
|
|
67
|
-
from matplotlib import pyplot as plt
|
|
68
|
-
_, ax = plt.subplots()
|
|
69
|
-
|
|
70
|
-
x, y = self.get_metric(metric_name)
|
|
71
|
-
|
|
72
|
-
if plot_x:
|
|
73
|
-
result = ax.plot(x, y, **kwargs) # type: ignore
|
|
74
|
-
else:
|
|
75
|
-
result = ax.plot(y, **kwargs)
|
|
76
|
-
|
|
77
|
-
ax.set_title(metric_name)
|
|
78
|
-
return result
|
|
97
|
+
return TimeSeries(metric_name, timeline, values)
|
|
79
98
|
|
|
80
99
|
def get_metric_names(self) -> list[str]:
|
|
81
100
|
"""Return a list of the recorded metric names"""
|
|
@@ -23,9 +23,11 @@ class _Timeseries:
|
|
|
23
23
|
class ScoreCard(Journal):
|
|
24
24
|
"""Tracks progress of a run so it can be plotted using matplotlib charts afterwards.
|
|
25
25
|
It will track the following aspects:
|
|
26
|
-
- the price of
|
|
27
|
-
- the orders for that asset
|
|
28
|
-
- any metric that
|
|
26
|
+
- the price of the assets
|
|
27
|
+
- the orders for that asset as markers on the price chart
|
|
28
|
+
- any additional metric that has been provided
|
|
29
|
+
|
|
30
|
+
This works best on smaller runs with a limited number of assets and orders.
|
|
29
31
|
"""
|
|
30
32
|
|
|
31
33
|
def __init__(self, *metrics: Metric, include_prices: bool = True, price_type: str = "DEFAULT"):
|
|
@@ -56,7 +58,7 @@ class ScoreCard(Journal):
|
|
|
56
58
|
for name, value in result.items():
|
|
57
59
|
self._metric_results[name].add(time, value)
|
|
58
60
|
|
|
59
|
-
def plot(self, **kwargs):
|
|
61
|
+
def plot(self, size: tuple[float, float] = (8.27, 11.69), **kwargs):
|
|
60
62
|
"""Plot a chart with the following sub-charts:
|
|
61
63
|
- prices of the configured asset. Orders als small green up (BUY) and red down (SELL) triangles.
|
|
62
64
|
- metrics that have been configured, each in their own chart.
|
|
@@ -71,7 +73,7 @@ class ScoreCard(Journal):
|
|
|
71
73
|
if not hasattr(axes, "__getitem__"):
|
|
72
74
|
axes = [axes]
|
|
73
75
|
|
|
74
|
-
fig.set_size_inches(
|
|
76
|
+
fig.set_size_inches(size)
|
|
75
77
|
fig.tight_layout()
|
|
76
78
|
|
|
77
79
|
plot_nr = 0
|
|
@@ -22,8 +22,7 @@ class SignalType(Flag):
|
|
|
22
22
|
"""Indicates that this signal can be used for both entering and exiting a position size"""
|
|
23
23
|
|
|
24
24
|
def __repr__(self):
|
|
25
|
-
return self.name.split(".")[-1]
|
|
26
|
-
|
|
25
|
+
return self.name.split(".")[-1] if self.name else super().__repr__()
|
|
27
26
|
|
|
28
27
|
@dataclass(slots=True, frozen=True)
|
|
29
28
|
class Signal:
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
from datetime import timedelta
|
|
2
|
-
|
|
3
1
|
from roboquant.signal import Signal
|
|
4
2
|
from roboquant.asset import Asset
|
|
5
3
|
from roboquant.event import Event
|
|
@@ -7,13 +5,15 @@ from roboquant.strategies.strategy import Strategy
|
|
|
7
5
|
|
|
8
6
|
|
|
9
7
|
class EMACrossover(Strategy):
|
|
10
|
-
"""Trading strategy based on a fast and slow
|
|
8
|
+
"""Trading strategy based on a fast and slow Exponential Moving Average.
|
|
11
9
|
|
|
12
|
-
|
|
10
|
+
This strategy tracks each asset independently and emits a buy signal when
|
|
13
11
|
the fast EMA crosses above the slow EMA, and a sell signal when it crosses
|
|
14
12
|
below. Signals are only generated after enough prices have been observed to
|
|
15
|
-
cover the longer EMA period.
|
|
16
|
-
|
|
13
|
+
cover the longer EMA period.
|
|
14
|
+
|
|
15
|
+
The constructor controls the fast and slow periods, smoothing factor,
|
|
16
|
+
and the event price type used as input.
|
|
17
17
|
"""
|
|
18
18
|
|
|
19
19
|
def __init__(self, fast_period: int = 13, slow_period: int = 26, smoothing: float = 2.0, price_type: str = "DEFAULT"):
|
|
@@ -23,7 +23,6 @@ class EMACrossover(Strategy):
|
|
|
23
23
|
self.slow = 1.0 - (smoothing / (slow_period + 1))
|
|
24
24
|
self.price_type = price_type
|
|
25
25
|
self.min_steps = max(fast_period, slow_period)
|
|
26
|
-
self.cancel_orders_older_than = timedelta(days=5)
|
|
27
26
|
|
|
28
27
|
def create_signals(self, event: Event) -> list[Signal]:
|
|
29
28
|
result : list[Signal] = []
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|