roboquant 2.2.0__tar.gz → 2.3.2__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.
Files changed (140) hide show
  1. {roboquant-2.2.0 → roboquant-2.3.2}/PKG-INFO +17 -17
  2. {roboquant-2.2.0 → roboquant-2.3.2}/pyproject.toml +7 -7
  3. {roboquant-2.2.0 → roboquant-2.3.2}/roboquant/__init__.py +0 -7
  4. {roboquant-2.2.0 → roboquant-2.3.2}/roboquant/feeds/feed.py +1 -1
  5. {roboquant-2.2.0 → roboquant-2.3.2}/roboquant/feeds/parquet.py +1 -0
  6. {roboquant-2.2.0 → roboquant-2.3.2}/roboquant/journals/metricsjournal.py +52 -33
  7. {roboquant-2.2.0 → roboquant-2.3.2}/roboquant/journals/scorecard.py +7 -5
  8. {roboquant-2.2.0 → roboquant-2.3.2}/roboquant/strategies/emacrossover.py +6 -7
  9. roboquant-2.2.0/.github/workflows/verify.yml +0 -46
  10. roboquant-2.2.0/.gitignore +0 -15
  11. roboquant-2.2.0/.vscode/settings.json +0 -39
  12. roboquant-2.2.0/.zed/settings.json +0 -21
  13. roboquant-2.2.0/.zed/tasks.json +0 -30
  14. roboquant-2.2.0/bin/local_install.sh +0 -19
  15. roboquant-2.2.0/bin/publish.sh +0 -24
  16. roboquant-2.2.0/bin/verify.sh +0 -13
  17. roboquant-2.2.0/docs/DESIGN.md +0 -85
  18. roboquant-2.2.0/docs/roboquant_header.png +0 -0
  19. roboquant-2.2.0/tests/__init__.py +0 -0
  20. roboquant-2.2.0/tests/common.py +0 -84
  21. roboquant-2.2.0/tests/data/stooq/5_min/ibm.us.txt +0 -2731
  22. roboquant-2.2.0/tests/data/stooq/daily/ibm.us.txt +0 -15626
  23. roboquant-2.2.0/tests/data/yahoo/AAPL.csv +0 -1219
  24. roboquant-2.2.0/tests/data/yahoo/AMZN.csv +0 -527
  25. roboquant-2.2.0/tests/data/yahoo/META.csv +0 -535
  26. roboquant-2.2.0/tests/data/yahoo/TSLA.csv +0 -6
  27. roboquant-2.2.0/tests/integration/__init__.py +0 -0
  28. roboquant-2.2.0/tests/integration/test_alpaca.py +0 -89
  29. roboquant-2.2.0/tests/integration/test_ccxt.py +0 -31
  30. roboquant-2.2.0/tests/integration/test_ibkr.py +0 -112
  31. roboquant-2.2.0/tests/integration/test_yahoo.py +0 -33
  32. roboquant-2.2.0/tests/performance/__init__.py +0 -0
  33. roboquant-2.2.0/tests/performance/test_bigfeed.py +0 -60
  34. roboquant-2.2.0/tests/performance/test_delay.py +0 -73
  35. roboquant-2.2.0/tests/performance/test_profiling.py +0 -43
  36. roboquant-2.2.0/tests/samples/__init__.py +0 -0
  37. roboquant-2.2.0/tests/samples/alpaca_forwardtest_bars.py +0 -30
  38. roboquant-2.2.0/tests/samples/alpaca_forwardtest_trades.py +0 -33
  39. roboquant-2.2.0/tests/samples/alpaca_live_feed.py +0 -25
  40. roboquant-2.2.0/tests/samples/alpaca_papertrade.py +0 -38
  41. roboquant-2.2.0/tests/samples/alpaca_record.py +0 -48
  42. roboquant-2.2.0/tests/samples/charts.py +0 -61
  43. roboquant-2.2.0/tests/samples/chronos_prediction.py +0 -87
  44. roboquant-2.2.0/tests/samples/crypto_backtest.py +0 -29
  45. roboquant-2.2.0/tests/samples/extra_logging.py +0 -18
  46. roboquant-2.2.0/tests/samples/ibkr_close_positions.py +0 -29
  47. roboquant-2.2.0/tests/samples/monetary.py +0 -53
  48. roboquant-2.2.0/tests/samples/pandas_dataframe.py +0 -21
  49. roboquant-2.2.0/tests/samples/parquet_csv.py +0 -24
  50. roboquant-2.2.0/tests/samples/parquet_csv_large.py +0 -17
  51. roboquant-2.2.0/tests/samples/parquet_yahoo.py +0 -11
  52. roboquant-2.2.0/tests/samples/sb3_strategy.py +0 -39
  53. roboquant-2.2.0/tests/samples/sb3_strategy_quotes.py +0 -45
  54. roboquant-2.2.0/tests/samples/talib_feature.py +0 -28
  55. roboquant-2.2.0/tests/samples/talib_strategy.py +0 -42
  56. roboquant-2.2.0/tests/samples/tensorboard_metrics.py +0 -21
  57. roboquant-2.2.0/tests/samples/torch_lstm.py +0 -73
  58. roboquant-2.2.0/tests/samples/torch_transformer.py +0 -105
  59. roboquant-2.2.0/tests/samples/yahoo_backtest.py +0 -17
  60. roboquant-2.2.0/tests/samples/yahoo_walkforward.py +0 -16
  61. roboquant-2.2.0/tests/samples/yahoo_walkforward_multiprocess.py +0 -50
  62. roboquant-2.2.0/tests/unit/__init__.py +0 -0
  63. roboquant-2.2.0/tests/unit/test_account.py +0 -42
  64. roboquant-2.2.0/tests/unit/test_asset.py +0 -74
  65. roboquant-2.2.0/tests/unit/test_buffer.py +0 -31
  66. roboquant-2.2.0/tests/unit/test_csvfeed.py +0 -43
  67. roboquant-2.2.0/tests/unit/test_features.py +0 -91
  68. roboquant-2.2.0/tests/unit/test_feedutil.py +0 -23
  69. roboquant-2.2.0/tests/unit/test_journal.py +0 -32
  70. roboquant-2.2.0/tests/unit/test_monetary.py +0 -92
  71. roboquant-2.2.0/tests/unit/test_order.py +0 -49
  72. roboquant-2.2.0/tests/unit/test_parquetfeed.py +0 -42
  73. roboquant-2.2.0/tests/unit/test_randomwalk.py +0 -23
  74. roboquant-2.2.0/tests/unit/test_rnn.py +0 -76
  75. roboquant-2.2.0/tests/unit/test_run.py +0 -37
  76. roboquant-2.2.0/tests/unit/test_signal.py +0 -30
  77. roboquant-2.2.0/tests/unit/test_simbroker.py +0 -69
  78. roboquant-2.2.0/tests/unit/test_sqlfeed.py +0 -31
  79. roboquant-2.2.0/tests/unit/test_strategy.py +0 -33
  80. roboquant-2.2.0/tests/unit/test_tastrategy.py +0 -30
  81. roboquant-2.2.0/tests/unit/test_tensorboard.py +0 -27
  82. roboquant-2.2.0/tests/unit/test_timeframe.py +0 -62
  83. roboquant-2.2.0/uv.lock +0 -3114
  84. {roboquant-2.2.0 → roboquant-2.3.2}/LICENSE +0 -0
  85. {roboquant-2.2.0 → roboquant-2.3.2}/README.md +0 -0
  86. {roboquant-2.2.0 → roboquant-2.3.2}/roboquant/account.py +0 -0
  87. {roboquant-2.2.0 → roboquant-2.3.2}/roboquant/ai/__init__.py +0 -0
  88. {roboquant-2.2.0 → roboquant-2.3.2}/roboquant/ai/features.py +0 -0
  89. {roboquant-2.2.0 → roboquant-2.3.2}/roboquant/ai/rl.py +0 -0
  90. {roboquant-2.2.0 → roboquant-2.3.2}/roboquant/ai/strategies.py +0 -0
  91. {roboquant-2.2.0 → roboquant-2.3.2}/roboquant/alpaca/__init__.py +0 -0
  92. {roboquant-2.2.0 → roboquant-2.3.2}/roboquant/alpaca/broker.py +0 -0
  93. {roboquant-2.2.0 → roboquant-2.3.2}/roboquant/alpaca/feed.py +0 -0
  94. {roboquant-2.2.0 → roboquant-2.3.2}/roboquant/asset.py +0 -0
  95. {roboquant-2.2.0 → roboquant-2.3.2}/roboquant/brokers/__init__.py +0 -0
  96. {roboquant-2.2.0 → roboquant-2.3.2}/roboquant/brokers/broker.py +0 -0
  97. {roboquant-2.2.0 → roboquant-2.3.2}/roboquant/brokers/simbroker.py +0 -0
  98. {roboquant-2.2.0 → roboquant-2.3.2}/roboquant/crypto/__init__.py +0 -0
  99. {roboquant-2.2.0 → roboquant-2.3.2}/roboquant/crypto/cryptobroker.py +0 -0
  100. {roboquant-2.2.0 → roboquant-2.3.2}/roboquant/crypto/cryptofeed.py +0 -0
  101. {roboquant-2.2.0 → roboquant-2.3.2}/roboquant/event.py +0 -0
  102. {roboquant-2.2.0 → roboquant-2.3.2}/roboquant/feeds/__init__.py +0 -0
  103. {roboquant-2.2.0 → roboquant-2.3.2}/roboquant/feeds/csvfeed.py +0 -0
  104. {roboquant-2.2.0 → roboquant-2.3.2}/roboquant/feeds/historic.py +0 -0
  105. {roboquant-2.2.0 → roboquant-2.3.2}/roboquant/feeds/live.py +0 -0
  106. {roboquant-2.2.0 → roboquant-2.3.2}/roboquant/feeds/randomwalk.py +0 -0
  107. {roboquant-2.2.0 → roboquant-2.3.2}/roboquant/feeds/resources/us10.parquet +0 -0
  108. {roboquant-2.2.0 → roboquant-2.3.2}/roboquant/feeds/sql.py +0 -0
  109. {roboquant-2.2.0 → roboquant-2.3.2}/roboquant/feeds/util.py +0 -0
  110. {roboquant-2.2.0 → roboquant-2.3.2}/roboquant/feeds/yahoo.py +0 -0
  111. {roboquant-2.2.0 → roboquant-2.3.2}/roboquant/ibkr/__init__.py +0 -0
  112. {roboquant-2.2.0 → roboquant-2.3.2}/roboquant/ibkr/broker.py +0 -0
  113. {roboquant-2.2.0 → roboquant-2.3.2}/roboquant/ibkr/types.py +0 -0
  114. {roboquant-2.2.0 → roboquant-2.3.2}/roboquant/journals/__init__.py +0 -0
  115. {roboquant-2.2.0 → roboquant-2.3.2}/roboquant/journals/alphabeta.py +0 -0
  116. {roboquant-2.2.0 → roboquant-2.3.2}/roboquant/journals/basicjournal.py +0 -0
  117. {roboquant-2.2.0 → roboquant-2.3.2}/roboquant/journals/feedmetric.py +0 -0
  118. {roboquant-2.2.0 → roboquant-2.3.2}/roboquant/journals/journal.py +0 -0
  119. {roboquant-2.2.0 → roboquant-2.3.2}/roboquant/journals/marketmetric.py +0 -0
  120. {roboquant-2.2.0 → roboquant-2.3.2}/roboquant/journals/metric.py +0 -0
  121. {roboquant-2.2.0 → roboquant-2.3.2}/roboquant/journals/pnlmetric.py +0 -0
  122. {roboquant-2.2.0 → roboquant-2.3.2}/roboquant/journals/pricemetric.py +0 -0
  123. {roboquant-2.2.0 → roboquant-2.3.2}/roboquant/journals/runmetric.py +0 -0
  124. {roboquant-2.2.0 → roboquant-2.3.2}/roboquant/journals/tensorboard.py +0 -0
  125. {roboquant-2.2.0 → roboquant-2.3.2}/roboquant/monetary.py +0 -0
  126. {roboquant-2.2.0 → roboquant-2.3.2}/roboquant/order.py +0 -0
  127. {roboquant-2.2.0 → roboquant-2.3.2}/roboquant/run.py +0 -0
  128. {roboquant-2.2.0 → roboquant-2.3.2}/roboquant/signal.py +0 -0
  129. {roboquant-2.2.0 → roboquant-2.3.2}/roboquant/strategies/__init__.py +0 -0
  130. {roboquant-2.2.0 → roboquant-2.3.2}/roboquant/strategies/buffer.py +0 -0
  131. {roboquant-2.2.0 → roboquant-2.3.2}/roboquant/strategies/cachedstrategy.py +0 -0
  132. {roboquant-2.2.0 → roboquant-2.3.2}/roboquant/strategies/ibsstrategy.py +0 -0
  133. {roboquant-2.2.0 → roboquant-2.3.2}/roboquant/strategies/multistrategy.py +0 -0
  134. {roboquant-2.2.0 → roboquant-2.3.2}/roboquant/strategies/strategy.py +0 -0
  135. {roboquant-2.2.0 → roboquant-2.3.2}/roboquant/strategies/tastrategy.py +0 -0
  136. {roboquant-2.2.0 → roboquant-2.3.2}/roboquant/ta/__init__.py +0 -0
  137. {roboquant-2.2.0 → roboquant-2.3.2}/roboquant/timeframe.py +0 -0
  138. {roboquant-2.2.0 → roboquant-2.3.2}/roboquant/traders/__init__.py +0 -0
  139. {roboquant-2.2.0 → roboquant-2.3.2}/roboquant/traders/flextrader.py +0 -0
  140. {roboquant-2.2.0 → roboquant-2.3.2}/roboquant/traders/trader.py +0 -0
@@ -1,43 +1,43 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: roboquant
3
- Version: 2.2.0
3
+ Version: 2.3.2
4
4
  Summary: A fast algorithmic trading platform with support for AI based strategies
5
- Project-URL: Homepage, https://roboquant.org
6
- Project-URL: Repository, https://github.com/neurallayer/roboquant.py.git
7
- Project-URL: Issues, https://github.com/neurallayer/roboquant.py/issues
5
+ Keywords: algorithmic,trading,machine learning,AI,investment,finance,crypto,stocks,exchange,forex
6
+ Author: roboquant team
8
7
  Author-email: roboquant team <info@roboquant.org>
9
8
  License-Expression: Apache-2.0
10
9
  License-File: LICENSE
11
- Keywords: AI,algorithmic,crypto,exchange,finance,forex,investment,machine learning,stocks,trading
12
10
  Classifier: Development Status :: 4 - Beta
13
11
  Classifier: Intended Audience :: Developers
14
- Classifier: License :: OSI Approved :: Apache Software License
15
- Classifier: Operating System :: OS Independent
16
12
  Classifier: Programming Language :: Python
17
13
  Classifier: Programming Language :: Python :: 3
18
14
  Classifier: Programming Language :: Python :: 3 :: Only
19
15
  Classifier: Programming Language :: Python :: 3.12
20
16
  Classifier: Programming Language :: Python :: 3.13
21
17
  Classifier: Programming Language :: Python :: 3.14
18
+ Classifier: Operating System :: OS Independent
22
19
  Classifier: Topic :: Office/Business :: Financial
23
20
  Classifier: Topic :: Office/Business :: Financial :: Investment
24
- Requires-Python: <3.15,>=3.12
25
21
  Requires-Dist: numpy>2.0.0
26
- Requires-Dist: pyarrow>24.0.0
27
22
  Requires-Dist: requests>2.30.0
28
- Requires-Dist: ta-lib==0.6.5
29
23
  Requires-Dist: yfinance>1.5.0
24
+ Requires-Dist: pyarrow>24.0.0
25
+ Requires-Dist: ta-lib==0.6.5
26
+ Requires-Dist: torch>=2.9.0 ; extra == 'ai'
27
+ Requires-Dist: tensorboard>=2.21.0 ; extra == 'ai'
28
+ Requires-Dist: stable-baselines3>=2.9.0 ; extra == 'ai'
29
+ Requires-Dist: sb3-contrib>=2.9.0 ; extra == 'ai'
30
+ Requires-Dist: alpaca-py>=0.43.5 ; extra == 'alpaca'
31
+ Requires-Dist: ccxt>=4.5.65 ; extra == 'crypto'
32
+ Requires-Dist: ibind[oauth]>=0.1.22 ; extra == 'ibkr'
33
+ Requires-Python: >=3.12, <3.15
34
+ Project-URL: Homepage, https://roboquant.org
35
+ Project-URL: Repository, https://github.com/neurallayer/roboquant.py.git
36
+ Project-URL: Issues, https://github.com/neurallayer/roboquant.py/issues
30
37
  Provides-Extra: ai
31
- Requires-Dist: sb3-contrib>=2.9.0; extra == 'ai'
32
- Requires-Dist: stable-baselines3>=2.9.0; extra == 'ai'
33
- Requires-Dist: tensorboard>=2.21.0; extra == 'ai'
34
- Requires-Dist: torch>=2.9.0; extra == 'ai'
35
38
  Provides-Extra: alpaca
36
- Requires-Dist: alpaca-py>=0.43.5; extra == 'alpaca'
37
39
  Provides-Extra: crypto
38
- Requires-Dist: ccxt>=4.5.65; extra == 'crypto'
39
40
  Provides-Extra: ibkr
40
- Requires-Dist: ibind[oauth]>=0.1.22; extra == 'ibkr'
41
41
  Description-Content-Type: text/markdown
42
42
 
43
43
 
@@ -2,11 +2,8 @@
2
2
  line-length = 127
3
3
 
4
4
  [build-system]
5
- requires = ["hatchling"]
6
- build-backend = "hatchling.build"
7
-
8
- [tool.hatch.version]
9
- path = "roboquant/__init__.py"
5
+ requires = ["uv_build>=0.11.28,<0.12"]
6
+ build-backend = "uv_build"
10
7
 
11
8
  [tool.ruff.lint]
12
9
  select = ["E", "F", "W"]
@@ -16,9 +13,13 @@ typeCheckingMode = "standard"
16
13
  reportPrivateImportUsage = "none"
17
14
  # typeCheckingMode = "strict"
18
15
 
16
+ [tool.uv.build-backend]
17
+ module-root = ""
18
+ module-name = "roboquant"
19
+
19
20
  [project]
20
21
  name = "roboquant"
21
- dynamic = ["version"]
22
+ version = "2.3.2"
22
23
  authors = [{ name = "roboquant team", email = "info@roboquant.org" }]
23
24
  description = "A fast algorithmic trading platform with support for AI based strategies"
24
25
  readme = "README.md"
@@ -34,7 +35,6 @@ classifiers = [
34
35
  "Programming Language :: Python :: 3.12",
35
36
  "Programming Language :: Python :: 3.13",
36
37
  "Programming Language :: Python :: 3.14",
37
- "License :: OSI Approved :: Apache Software License",
38
38
  "Operating System :: OS Independent",
39
39
  "Topic :: Office/Business :: Financial",
40
40
  "Topic :: Office/Business :: Financial :: Investment",
@@ -3,10 +3,6 @@ The `roboquant` package contains the `run` method and a number of shared classes
3
3
  like `Account`, `Asset` and `Event`.
4
4
  """
5
5
 
6
- __version__ = "2.2.0"
7
-
8
- import logging
9
-
10
6
  from roboquant import brokers
11
7
  from roboquant import feeds
12
8
  from roboquant import journals
@@ -22,9 +18,6 @@ from .asset import Asset, Stock, Crypto, Forex, Option
22
18
  from .run import run
23
19
  from .timeframe import Timeframe, utcnow
24
20
 
25
- logger = logging.getLogger(__name__)
26
- logger.info("roboquant version=%s", __version__)
27
-
28
21
  __all__ = [
29
22
  "brokers",
30
23
  "feeds",
@@ -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 sentiment could
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
 
@@ -213,3 +213,4 @@ class ParquetFeed(Feed):
213
213
  if items:
214
214
  batch = pa.RecordBatch.from_pylist(items, schema=ParquetFeed.__schema)
215
215
  writer.write_batch(batch)
216
+
@@ -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) -> tuple[list[datetime], list[float]]:
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 a single asset
27
- - the orders for that asset
28
- - any metric that is provided
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(8.27, 11.69) # A4
76
+ fig.set_size_inches(size)
75
77
  fig.tight_layout()
76
78
 
77
79
  plot_nr = 0
@@ -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 exponential moving average.
8
+ """Trading strategy based on a fast and slow Exponential Moving Average.
11
9
 
12
- The strategy tracks each asset independently and emits a buy signal when
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. The constructor controls the fast and slow
16
- periods, smoothing factor, and the event price type used as input.
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] = []
@@ -1,46 +0,0 @@
1
- # This workflow will install Python dependencies, run tests and lint with a variety of Python versions
2
-
3
- name: Verify roboquant
4
-
5
- on:
6
- push:
7
- branches: [ "main" ]
8
- pull_request:
9
- branches: [ "main" ]
10
-
11
- env:
12
- PYRIGHT_PYTHON_FORCE_VERSION: latest
13
-
14
- jobs:
15
- verify:
16
-
17
- runs-on: ${{ matrix.os }}
18
- strategy:
19
- fail-fast: false
20
- matrix:
21
- os: [ubuntu-24.04, windows-latest, macos-latest]
22
- python-version: ["3.12", "3.13"]
23
-
24
- steps:
25
- - name: Checkout
26
- uses: actions/checkout@v7
27
-
28
- - name: Install uv
29
- uses: astral-sh/setup-uv@v7
30
- with:
31
- ignore-nothing-to-cache: true
32
- python-version: ${{ matrix.python-version }}
33
-
34
- - name: Install the project
35
- run: uv sync --all-extras --no-dev
36
-
37
- - name: Run ruff checking
38
- run: uvx ruff check
39
-
40
- - name: Run pyright
41
- run: uv run pyright tests roboquant
42
-
43
- - name: Run unit tests
44
- run: uv run --no-dev python -m unittest discover -s tests/unit
45
-
46
-
@@ -1,15 +0,0 @@
1
- __pycache__
2
- runs/
3
- .idea/
4
- *.db
5
- scratch/
6
- dist/
7
- roboquant.egg-info/
8
- build/
9
- .venv/
10
- .ipynb_checkpoints/
11
- *.ipynb
12
- .ruff_cache/
13
- .pytest_cache/
14
- .env
15
- .DS_Store
@@ -1,39 +0,0 @@
1
- {
2
- "cSpell.words": [
3
- "annualize",
4
- "dataframe",
5
- "dtype",
6
- "hstack",
7
- "ibkr",
8
- "ohlcv",
9
- "optim",
10
- "pnlmetric",
11
- "prettytable",
12
- "roboquant",
13
- "stooq",
14
- "Tensorboard",
15
- "Tiingo",
16
- "timeframes"
17
- ],
18
- "python.testing.unittestArgs": [
19
- "-v",
20
- "-p",
21
- "test_*.py"
22
- ],
23
- "__python.testing.unittestArgs": [
24
- "-v",
25
- "-s",
26
- "./tests/unit",
27
- "-p",
28
- "test_*.py"
29
- ],
30
- "__python.testing.pytestArgs": [
31
- "--ignore=tests/integration",
32
- "--ignore=tests/performance"
33
- ],
34
- "python.testing.pytestEnabled": false,
35
- "python.testing.unittestEnabled": true,
36
- "[python]": {
37
- "editor.defaultFormatter": "charliermarsh.ruff"
38
- }
39
- }
@@ -1,21 +0,0 @@
1
- {
2
- "languages": {
3
- "Python": {
4
- "language_servers": ["pyright"],
5
- "tasks": {
6
- "variables": {
7
- "TEST_RUNNER": "unittest",
8
- },
9
- },
10
- },
11
- },
12
- "lsp": {
13
- "pyright": {
14
- "settings": {
15
- "python": {
16
- "pythonPath": ".venv/bin/python",
17
- },
18
- },
19
- },
20
- },
21
- }
@@ -1,30 +0,0 @@
1
- // Project tasks configuration. See https://zed.dev/docs/tasks for documentation.
2
- //
3
- // Example:
4
- [
5
- {
6
- "label": "run unit test",
7
- "command": "uv run python -m unittest discover -s tests/unit",
8
- "show_command": false,
9
- },
10
- {
11
- "label": "run pyright",
12
- "command": "uv run pyright tests roboquant",
13
- "show_command": false,
14
- },
15
- {
16
- "label": "uv sync",
17
- "command": "uv sync --all-extras --dev",
18
- "show_command": false,
19
- },
20
- {
21
- "label": "uv ruff",
22
- "command": "uvx ruff check",
23
- "show_command": false,
24
- },
25
- {
26
- "label": "run pyright",
27
- "command": "uv run pyright tests roboquant",
28
- "show_command": false,
29
- },
30
- ]
@@ -1,19 +0,0 @@
1
-
2
- [[ ! -f "LICENSE" ]] && echo "run the script from the project root directory like this: ./bin/local_install.sh" && exit 1
3
- export PYRIGHT_PYTHON_FORCE_VERSION="latest"
4
-
5
- rm -rf ./runs
6
-
7
- uv sync --all-extras --dev
8
-
9
- # QA
10
- uvx ruff check || exit 1
11
- uv run python -m unittest discover -s tests/unit || exit 1
12
-
13
- # Build the package
14
- rm -rf dist
15
- uv build || exit 1
16
-
17
- # Install the just build package
18
- uv pip install dist/*.whl
19
-
@@ -1,24 +0,0 @@
1
-
2
- [[ ! -f "LICENSE" ]] && echo "run the script from the project root directory like this: ./bin/publish.sh" && exit 1
3
- export PYRIGHT_PYTHON_FORCE_VERSION="latest"
4
-
5
- rm -rf ./runs
6
-
7
- uv sync --all-extras --dev
8
-
9
- # QA
10
- uvx ruff check
11
- uv run pyright tests roboquant || exit 1
12
- uv run python -m unittest discover -s tests/unit || exit 1
13
-
14
- # Build
15
- rm -rf dist
16
- uv build || exit 1
17
-
18
- # Publish
19
- read -p "Publish (y/n)? " ANSWER
20
- if [ "$ANSWER" = "y" ]; then
21
- uv publish; exit 0
22
- else
23
- echo "Not published"; exit 1
24
- fi
@@ -1,13 +0,0 @@
1
- [[ ! -f "LICENSE" ]] && echo "run the script from the project root directory like this: ./bin/verify.sh" && exit 1
2
- export PYRIGHT_PYTHON_FORCE_VERSION="latest"
3
-
4
- uv sync --all-extras --dev
5
-
6
- # QA
7
- echo "Running ruff"
8
- uvx ruff check || exit 1
9
- uv run pyright tests roboquant || exit 1
10
- echo "Running unittest"
11
- uv run python -m unittest discover -s tests/unit || exit 1
12
-
13
- echo "All tests passed"
@@ -1,85 +0,0 @@
1
- # Design Principles
2
-
3
- ## Modular Pipeline Architecture
4
-
5
- The library is built around a clean separation of five orthogonal concerns:
6
-
7
- | Component | Responsibility | Has access to Account? |
8
- |-----------|---------------|----------------------|
9
- | **Feed** | Provides market data events | No |
10
- | **Strategy** | Generates trading signals from events | No |
11
- | **Trader** | Converts signals into orders (risk/sizing) | Yes |
12
- | **Broker** | Executes orders, maintains account state | Yes (owns Account) |
13
- | **Journal** | Logs/tracks every step (read-only) | Read-only snapshot |
14
-
15
- Each component is an abstract base class with pluggable implementations, making every part of the pipeline independently swappable.
16
-
17
- ## The Run Loop
18
-
19
- The core of the system is the `roboquant.run()` function, which connects all components in a streaming event loop:
20
-
21
- ```
22
- for each event in feed.play(timeframe):
23
- 1. broker.sync(event) — update account, execute fills
24
- 2. strategy.create_signals(event) — generate signals from market data
25
- 3. trader.create_orders(signals, ...) — apply risk rules, produce orders
26
- 4. broker.place_orders(orders) — submit orders to the broker
27
- 5. journal.track(event, account, ...) — record metrics (optional)
28
- ```
29
-
30
- ### Step-by-step
31
-
32
- 1. **`broker.sync(event)`** — Updates the account with the latest market data. Open orders from previous steps are tested against prices and executed if conditions are met. No look-ahead bias: orders placed at time `t` only execute at time `t+1`.
33
-
34
- 2. **`strategy.create_signals(event)`** — The strategy examines the event's price data and returns a list of `Signal` objects. Each signal has an asset, a rating (typically -1.0 to 1.0), and a type (`ENTRY`, `EXIT`, or `ENTRY_EXIT`). Strategies are **pure decision-makers** — they know nothing about cash, positions, or risk.
35
-
36
- 3. **`trader.create_orders(signals, event, account)`** — The trader applies risk management rules (position sizing, shorting constraints, order limits) and converts signals into concrete `Order` objects. Unlike strategies, traders **have full access to the Account** (cash, positions, buying power).
37
-
38
- 4. **`broker.place_orders(orders)`** — New orders are submitted to the broker. In `SimBroker`, they are stored and evaluated for execution when the next event arrives.
39
-
40
- 5. **`journal.track(...)`** — Optional logging and metrics collection. Journals are passive observers that never modify state.
41
-
42
- ## Minimal Backtest
43
-
44
- ```python
45
- import roboquant as rq
46
-
47
- feed = rq.feeds.YahooFeed("JPM", "IBM", start_date="2015-01-01")
48
- account = rq.run(feed, rq.strategies.EMACrossover())
49
- print(account)
50
- ```
51
-
52
- This works because `run()` provides sensible defaults: `SimBroker` (USD 1M deposit, 0% slippage) and `FlexTrader` (conservative position sizing).
53
-
54
- ## Custom Backtest
55
-
56
- ```python
57
- feed = rq.feeds.YahooFeed("AAPL", "MSFT", start_date="2020-01-01")
58
- strategy = rq.strategies.EMACrossover()
59
- trader = rq.traders.FlexTrader(max_order_perc=0.1, shorting=True)
60
- broker = rq.brokers.SimBroker(deposit=500_000)
61
- journal = rq.journals.MetricsJournal()
62
-
63
- account = rq.run(feed, strategy, trader=trader, broker=broker, journal=journal)
64
- print(account)
65
- print(journal.metrics())
66
- ```
67
-
68
- ## Strategy/Trader Separation
69
-
70
- This is the most important design choice in the library:
71
-
72
- - **Strategies** produce signals from market data only. They implement `create_signals(event) -> list[Signal]` and have no access to account state. This keeps them pure, testable, and reusable across any trader configuration.
73
- - **Traders** implement `create_orders(signals, event, account) -> list[Order]` and are responsible for risk management, position sizing, and order construction. They know nothing about indicators or market data beyond what is in the signal.
74
-
75
- Example: The same `EMACrossover` strategy can be used with a conservative trader (2% max order) in backtesting and a different trader (20% max order, shorting enabled) in live trading — without changing a line of strategy code.
76
-
77
- ## Key Principles
78
-
79
- - **Event-driven streaming** — Everything is built around `Event` objects produced lazily by feeds, supporting both backtesting and live trading with the same pipeline.
80
- - **Broker owns the Account** — The Account is never modified directly by user code; it is always the broker's canonical view returned by `sync()`.
81
- - **Default-everywhere** — `roboquant.run(feed, strategy)` works out of the box with sensible defaults, making the simplest case a one-liner.
82
- - **Immutable core types** — `Asset`, `Signal`, and `Event` are immutable; `Order` uses `cancel()`/`modify()` returning new objects.
83
- - **Strategy composition** — `MultiStrategy` combines multiple strategies with configurable conflict resolution (`first`, `last`, `mean`, `none`).
84
- - **Pluggable pricing** — Price type strings (`"OPEN"`, `"CLOSE"`, `"HIGH"`, `"LOW"`, `"DEFAULT"`) allow strategies and traders to choose which price to use for evaluation.
85
- - **Multi-currency** — Built-in support via `Amount`, `Wallet`, and pluggable `CurrencyConverter` (ECB, static, one-to-one).
Binary file
File without changes