From 92e35084c66e1a5aa05c96fb64bdb61046e180d3 Mon Sep 17 00:00:00 2001 From: Peter Dekkers Date: Sun, 25 Feb 2024 20:43:50 +0100 Subject: [PATCH] tracker => journal --- roboquant/__init__.py | 2 +- roboquant/feeds/candlefeed.py | 2 +- roboquant/journals/__init__.py | 9 +++++++++ roboquant/{trackers => journals}/alphabeta.py | 0 .../basictracker.py => journals/basicjournal.py} | 6 +++--- roboquant/{trackers => journals}/equitymetric.py | 2 +- roboquant/{trackers => journals}/feedmetric.py | 2 +- .../{trackers/tracker.py => journals/journal.py} | 6 +++--- roboquant/{trackers => journals}/metric.py | 0 .../metricsjournal.py} | 6 +++--- roboquant/{trackers => journals}/pricemetric.py | 2 +- roboquant/{trackers => journals}/runmetric.py | 6 ++---- .../tensorboardjournal.py} | 6 +++--- roboquant/roboquant.py | 10 +++++----- roboquant/trackers/__init__.py | 9 --------- tests/performance/bigcsvfeed_test.py | 8 ++++---- tests/performance/profiling_test.py | 12 ++++++------ tests/performance/tiingodelay_test.py | 4 +++- tests/samples/papertrade_tiingo_ibkr.py | 6 +++--- tests/samples/tensorboard_metrics.py | 6 +++--- tests/samples/walkforward.py | 15 +++++++-------- tests/unit/test_flextrader.py | 14 +++++++------- tests/unit/test_rnnstrategy.py | 10 +++++----- ..._tensorboardtracker.py => test_tensorboard.py} | 10 +++++----- 24 files changed, 76 insertions(+), 77 deletions(-) create mode 100644 roboquant/journals/__init__.py rename roboquant/{trackers => journals}/alphabeta.py (100%) rename roboquant/{trackers/basictracker.py => journals/basicjournal.py} (87%) rename roboquant/{trackers => journals}/equitymetric.py (97%) rename roboquant/{trackers => journals}/feedmetric.py (94%) rename roboquant/{trackers/tracker.py => journals/journal.py} (67%) rename roboquant/{trackers => journals}/metric.py (100%) rename roboquant/{trackers/metricstracker.py => journals/metricsjournal.py} (91%) rename roboquant/{trackers => journals}/pricemetric.py (94%) rename roboquant/{trackers => journals}/runmetric.py (78%) rename roboquant/{trackers/tensorboardtracker.py => journals/tensorboardjournal.py} (87%) delete mode 100644 roboquant/trackers/__init__.py rename tests/unit/{test_tensorboardtracker.py => test_tensorboard.py} (64%) diff --git a/roboquant/__init__.py b/roboquant/__init__.py index 938070d..43031ce 100644 --- a/roboquant/__init__.py +++ b/roboquant/__init__.py @@ -10,6 +10,6 @@ from roboquant import brokers from roboquant import traders -from roboquant import trackers +from roboquant import journals from roboquant import strategies from roboquant import feeds diff --git a/roboquant/feeds/candlefeed.py b/roboquant/feeds/candlefeed.py index c32514c..30ea783 100644 --- a/roboquant/feeds/candlefeed.py +++ b/roboquant/feeds/candlefeed.py @@ -8,7 +8,7 @@ class CandleFeed(Feed): - """Aggregates Trades of another feed into Candles""" + """Aggregates Trades of another feed into candles""" def __init__(self, feed: Feed, frequency: timedelta, send_remaining=False): super().__init__() diff --git a/roboquant/journals/__init__.py b/roboquant/journals/__init__.py new file mode 100644 index 0000000..bface5e --- /dev/null +++ b/roboquant/journals/__init__.py @@ -0,0 +1,9 @@ +from roboquant.journals.journal import Journal +from roboquant.journals.alphabeta import AlphaBeta +from roboquant.journals.basicjournal import BasicJournal +from roboquant.journals.tensorboardjournal import TensorboardJournal +from roboquant.journals.runmetric import RunMetric +from roboquant.journals.equitymetric import EquityMetric +from roboquant.journals.metric import Metric +from roboquant.journals.feedmetric import FeedMetric +from roboquant.journals.pricemetric import PriceItemMetric diff --git a/roboquant/trackers/alphabeta.py b/roboquant/journals/alphabeta.py similarity index 100% rename from roboquant/trackers/alphabeta.py rename to roboquant/journals/alphabeta.py diff --git a/roboquant/trackers/basictracker.py b/roboquant/journals/basicjournal.py similarity index 87% rename from roboquant/trackers/basictracker.py rename to roboquant/journals/basicjournal.py index 75d1285..e283e23 100644 --- a/roboquant/trackers/basictracker.py +++ b/roboquant/journals/basicjournal.py @@ -1,13 +1,13 @@ -from roboquant.trackers.tracker import Tracker +from roboquant.journals.journal import Journal import inspect -class BasicTracker(Tracker): +class BasicJournal(Journal): """Tracks a number of basic metrics: - total number of events, items, signals and orders until that time - total pnl percentage - This tracker adds little overhead to a run, both CPU and memory wise. + This journal adds little overhead to a run, both CPU and memory wise. """ def __init__(self): diff --git a/roboquant/trackers/equitymetric.py b/roboquant/journals/equitymetric.py similarity index 97% rename from roboquant/trackers/equitymetric.py rename to roboquant/journals/equitymetric.py index 9b673f6..752e32a 100644 --- a/roboquant/trackers/equitymetric.py +++ b/roboquant/journals/equitymetric.py @@ -1,4 +1,4 @@ -from roboquant.trackers.metric import Metric +from roboquant.journals.metric import Metric class EquityMetric(Metric): diff --git a/roboquant/trackers/feedmetric.py b/roboquant/journals/feedmetric.py similarity index 94% rename from roboquant/trackers/feedmetric.py rename to roboquant/journals/feedmetric.py index 0c830cb..c792207 100644 --- a/roboquant/trackers/feedmetric.py +++ b/roboquant/journals/feedmetric.py @@ -1,4 +1,4 @@ -from roboquant.trackers.metric import Metric +from roboquant.journals.metric import Metric class FeedMetric(Metric): diff --git a/roboquant/trackers/tracker.py b/roboquant/journals/journal.py similarity index 67% rename from roboquant/trackers/tracker.py rename to roboquant/journals/journal.py index 42cbd29..047661c 100644 --- a/roboquant/trackers/tracker.py +++ b/roboquant/journals/journal.py @@ -6,12 +6,12 @@ from roboquant.signal import Signal -class Tracker(Protocol): +class Journal(Protocol): """ - A tracker allows for the tracking and/or logging of one or more metrics during a run. + A journal allows for the tracking and/or logging of one or more metrics during a run. """ def track(self, event: Event, account: Account, signals: dict[str, Signal], orders: list[Order]): - """invoked at each step of a run that provides the tracker with the opportunity to + """invoked at each step of a run that provides the journal with the opportunity to track and log various metrics.""" ... diff --git a/roboquant/trackers/metric.py b/roboquant/journals/metric.py similarity index 100% rename from roboquant/trackers/metric.py rename to roboquant/journals/metric.py diff --git a/roboquant/trackers/metricstracker.py b/roboquant/journals/metricsjournal.py similarity index 91% rename from roboquant/trackers/metricstracker.py rename to roboquant/journals/metricsjournal.py index 8682a87..ccdf24c 100644 --- a/roboquant/trackers/metricstracker.py +++ b/roboquant/journals/metricsjournal.py @@ -1,9 +1,9 @@ -from roboquant.trackers.metric import Metric -from roboquant.trackers.tracker import Tracker +from roboquant.journals.metric import Metric +from roboquant.journals.journal import Journal from datetime import datetime -class MetricsTracker(Tracker): +class MetricsJournal(Journal): """ A journal that allows for metrics to be added and calculated at each step. It will store the results in memory. diff --git a/roboquant/trackers/pricemetric.py b/roboquant/journals/pricemetric.py similarity index 94% rename from roboquant/trackers/pricemetric.py rename to roboquant/journals/pricemetric.py index 4edaec3..173a6a8 100644 --- a/roboquant/trackers/pricemetric.py +++ b/roboquant/journals/pricemetric.py @@ -1,4 +1,4 @@ -from roboquant.trackers.metric import Metric +from roboquant.journals.metric import Metric class PriceItemMetric(Metric): diff --git a/roboquant/trackers/runmetric.py b/roboquant/journals/runmetric.py similarity index 78% rename from roboquant/trackers/runmetric.py rename to roboquant/journals/runmetric.py index f9d50fd..e3854af 100644 --- a/roboquant/trackers/runmetric.py +++ b/roboquant/journals/runmetric.py @@ -1,11 +1,9 @@ -from roboquant.trackers.metric import Metric +from roboquant.journals.metric import Metric class RunMetric(Metric): - """Tracks a number of basic progress metrics: + """Calculates a number of basic metrics during a run: - total number of events, items, signals and orders - - This tracker adds little overhead to a run, both CPU and memory wise. """ def __init__(self): diff --git a/roboquant/trackers/tensorboardtracker.py b/roboquant/journals/tensorboardjournal.py similarity index 87% rename from roboquant/trackers/tensorboardtracker.py rename to roboquant/journals/tensorboardjournal.py index 654c2c3..7644a9d 100644 --- a/roboquant/trackers/tensorboardtracker.py +++ b/roboquant/journals/tensorboardjournal.py @@ -1,8 +1,8 @@ -from roboquant.trackers import Tracker -from roboquant.trackers.metric import Metric +from roboquant.journals import Journal +from roboquant.journals.metric import Metric -class TensorboardTracker(Tracker): +class TensorboardJournal(Journal): """Record metrics to a Tensorboard compatible file. This can be used outside the realm of machine learning, but requires tensorboard to be installed. diff --git a/roboquant/roboquant.py b/roboquant/roboquant.py index ad8b6be..944d58d 100644 --- a/roboquant/roboquant.py +++ b/roboquant/roboquant.py @@ -3,7 +3,7 @@ from roboquant.brokers.broker import Broker from roboquant.feeds.feed import Feed from roboquant.traders.trader import Trader -from roboquant.trackers.tracker import Tracker +from roboquant.journals.journal import Journal from roboquant.feeds.eventchannel import EventChannel from .timeframe import Timeframe from roboquant.brokers.simbroker import SimBroker @@ -39,7 +39,7 @@ def __init__( def run( self, feed: Feed, - tracker: Tracker | None = None, + journal: Journal | None = None, timeframe: Timeframe | None = None, capacity: int = 10, heartbeat_timeout: float | None = None, @@ -49,7 +49,7 @@ def run( Args: - feed: the feed to use for this run. This is the only mandatory argument. - - tracker: tracker to use to log and/or store progress and metrics, default is None. + - journal: journal to use to log and/or store progress and metrics, default is None. - timeframe: optionally limit the run to events within this timeframe. The default is None, resulting in all events in the feed being delivered. - capacity: the buffer capacity of the event channel before it starts blocking new events. Default is 10 events. @@ -65,7 +65,7 @@ def run( account = self.broker.sync(event) orders = self.trader.create_orders(signals, event, account) self.broker.place_orders(*orders) - if tracker: - tracker.track(event, account, signals, orders) + if journal: + journal.track(event, account, signals, orders) return self.broker.sync() diff --git a/roboquant/trackers/__init__.py b/roboquant/trackers/__init__.py deleted file mode 100644 index 0b1102f..0000000 --- a/roboquant/trackers/__init__.py +++ /dev/null @@ -1,9 +0,0 @@ -from roboquant.trackers.tracker import Tracker -from roboquant.trackers.alphabeta import AlphaBeta -from roboquant.trackers.basictracker import BasicTracker -from roboquant.trackers.tensorboardtracker import TensorboardTracker -from roboquant.trackers.runmetric import RunMetric -from roboquant.trackers.equitymetric import EquityMetric -from roboquant.trackers.metric import Metric -from roboquant.trackers.feedmetric import FeedMetric -from roboquant.trackers.pricemetric import PriceItemMetric diff --git a/tests/performance/bigcsvfeed_test.py b/tests/performance/bigcsvfeed_test.py index 3980c3a..919cbd4 100644 --- a/tests/performance/bigcsvfeed_test.py +++ b/tests/performance/bigcsvfeed_test.py @@ -9,13 +9,13 @@ feed = rq.feeds.CSVFeed.stooq_us_daily(path) loadtime = time.time() - start roboquant = rq.Roboquant(rq.strategies.EMACrossover(13, 26)) - tracker = rq.trackers.BasicTracker() + journal = rq.journals.BasicJournal() start = time.time() - account = roboquant.run(feed, tracker=tracker) + account = roboquant.run(feed, journal=journal) runtime = time.time() - start print(account) - print(tracker) + print(journal) # Print statistics print() @@ -23,7 +23,7 @@ print("files =", len(feed.symbols)) print(f"throughput = {len(feed.symbols) / loadtime:.0f} files/s") print(f"run time = {runtime:.1f}s") - candles = tracker.items + candles = journal.items print(f"candles = {(candles / 1_000_000):.1f}M") throughput = candles / (runtime * 1_000_000) print(f"throughput = {throughput:.1f}M candles/s") diff --git a/tests/performance/profiling_test.py b/tests/performance/profiling_test.py index 16f073f..de6ef39 100644 --- a/tests/performance/profiling_test.py +++ b/tests/performance/profiling_test.py @@ -1,18 +1,18 @@ from cProfile import Profile import os from pstats import Stats, SortKey +import roboquant as rq -from roboquant import Roboquant, CSVFeed, BasicTracker, EMACrossover if __name__ == "__main__": path = os.path.expanduser("~/data/nasdaq_stocks/1") - feed = CSVFeed.stooq_us_daily(path) + feed = rq.feeds.CSVFeed.stooq_us_daily(path) print("timeframe =", feed.timeframe(), " symbols =", len(feed.symbols)) - rq = Roboquant(EMACrossover(13, 26)) - tracker = BasicTracker() + roboquant = rq.Roboquant(rq.strategies.EMACrossover(13, 26)) + journal = rq.journals.BasicJournal() # Profile the run to detect bottlenecks with Profile() as profile: - rq.run(feed, tracker=tracker) - print(tracker) + roboquant.run(feed, journal) + print(f"\n{journal}") Stats(profile).sort_stats(SortKey.TIME).print_stats() diff --git a/tests/performance/tiingodelay_test.py b/tests/performance/tiingodelay_test.py index 5263a20..cc96821 100644 --- a/tests/performance/tiingodelay_test.py +++ b/tests/performance/tiingodelay_test.py @@ -10,9 +10,11 @@ import logging import time -from roboquant import EventChannel, Timeframe, TiingoLiveFeed, feedutil +from roboquant import Timeframe +from roboquant.feeds import EventChannel, TiingoLiveFeed, feedutil from statistics import mean, stdev + if __name__ == "__main__": logging.basicConfig(level=logging.INFO) diff --git a/tests/samples/papertrade_tiingo_ibkr.py b/tests/samples/papertrade_tiingo_ibkr.py index abded03..3819e41 100644 --- a/tests/samples/papertrade_tiingo_ibkr.py +++ b/tests/samples/papertrade_tiingo_ibkr.py @@ -21,10 +21,10 @@ # Lets run our EMACrossover strategy for 15 minutes roboquant = rq.Roboquant(rq.strategies.EMACrossover(3, 5), broker=ibkr) timeframe = rq.Timeframe.next(minutes=15) - tracker = rq.trackers.BasicTracker() - account = roboquant.run(feed, tracker, timeframe) + journal = rq.journals.BasicJournal() + account = roboquant.run(feed, journal, timeframe) src_feed.close() print(account) - print(tracker) + print(journal) sys.exit(0) diff --git a/tests/samples/tensorboard_metrics.py b/tests/samples/tensorboard_metrics.py index 16d3d73..63c7ef5 100644 --- a/tests/samples/tensorboard_metrics.py +++ b/tests/samples/tensorboard_metrics.py @@ -1,7 +1,7 @@ from roboquant import Roboquant from roboquant.feeds import YahooFeed from roboquant.strategies import EMACrossover -from roboquant.trackers import TensorboardTracker, EquityMetric, RunMetric, FeedMetric, PriceItemMetric, AlphaBeta +from roboquant.journals import TensorboardJournal, EquityMetric, RunMetric, FeedMetric, PriceItemMetric, AlphaBeta from tensorboard.summary import Writer if __name__ == "__main__": @@ -15,6 +15,6 @@ rq = Roboquant(EMACrossover(p1, p2)) log_dir = f"""runs/ema_{p1}_{p2}""" writer = Writer(log_dir) - tracker = TensorboardTracker(writer, EquityMetric(), RunMetric(), FeedMetric(), PriceItemMetric("JPM"), AlphaBeta(200)) - account = rq.run(feed, tracker) + journal = TensorboardJournal(writer, EquityMetric(), RunMetric(), FeedMetric(), PriceItemMetric("JPM"), AlphaBeta(200)) + account = rq.run(feed, journal) writer.close() diff --git a/tests/samples/walkforward.py b/tests/samples/walkforward.py index 55caf5d..ab502b1 100644 --- a/tests/samples/walkforward.py +++ b/tests/samples/walkforward.py @@ -1,17 +1,16 @@ -from roboquant import Roboquant, EMACrossover, EquityTracker, YahooFeed +import roboquant as rq if __name__ == "__main__": - feed = YahooFeed("JPM", "IBM", "F", start_date="2000-01-01") + feed = rq.feeds.YahooFeed("JPM", "IBM", "F", start_date="2000-01-01") # split the feed timeframe in 4 equal parts timeframes = feed.timeframe().split(4) # run a back-test on each timeframe for timeframe in timeframes: - rq = Roboquant(EMACrossover(13, 26)) - tracker = EquityTracker() - rq.run(feed, tracker, timeframe) - pnl = tracker.pnl() * 100 - mdd = tracker.max_drawdown() * 100 - print(f"{timeframe} pnl={pnl:5.2f}% mdd={mdd:5.2f}%") + roboquant = rq.Roboquant(rq.strategies.EMACrossover(13, 26)) + journal = rq.journals.BasicJournal() + roboquant.run(feed, journal, timeframe) + pnl = journal.pnl * 100.0 + print(f"{timeframe} pnl={pnl:5.2f}%") diff --git a/tests/unit/test_flextrader.py b/tests/unit/test_flextrader.py index efb89ab..07d6106 100644 --- a/tests/unit/test_flextrader.py +++ b/tests/unit/test_flextrader.py @@ -3,7 +3,7 @@ from roboquant import Roboquant, Order, PriceItem from roboquant.strategies import EMACrossover -from roboquant.trackers import BasicTracker +from roboquant.journals import BasicJournal from roboquant.traders import FlexTrader from tests.common import get_feed @@ -24,16 +24,16 @@ class TestFlexTrader(unittest.TestCase): def test_default_flextrader(self): feed = get_feed() rq = Roboquant(EMACrossover(13, 26), trader=FlexTrader()) - tracker = BasicTracker() - rq.run(feed, tracker=tracker) - self.assertGreater(tracker.orders, 0) + journal = BasicJournal() + rq.run(feed, journal=journal) + self.assertGreater(journal.orders, 0) def test_custom_flextrader(self): feed = get_feed() rq = Roboquant(EMACrossover(13, 26), trader=_MyTrader()) - tracker = BasicTracker() - rq.run(feed, tracker=tracker) - self.assertGreater(tracker.orders, 0) + journal = BasicJournal() + rq.run(feed, journal=journal) + self.assertGreater(journal.orders, 0) if __name__ == "__main__": diff --git a/tests/unit/test_rnnstrategy.py b/tests/unit/test_rnnstrategy.py index 6372d1a..31d5702 100644 --- a/tests/unit/test_rnnstrategy.py +++ b/tests/unit/test_rnnstrategy.py @@ -1,7 +1,7 @@ import unittest from roboquant import Roboquant, Timeframe from roboquant.traders import FlexTrader -from roboquant.trackers import BasicTracker +from roboquant.journals import BasicJournal from roboquant.strategies.rnnstrategy import RNNStrategy import torch.nn as nn @@ -43,10 +43,10 @@ def test_lstm_model(self): # Run the trained model with the last 4 years of data rq = Roboquant(strategy, FlexTrader(max_order_perc=0.8)) tf = Timeframe.fromisoformat("2020-01-01", "2023-12-31") - tracker = BasicTracker() - rq.run(feed, tracker, tf) - self.assertGreater(tracker.signals, 0) - # print(tracker) + journal = BasicJournal() + rq.run(feed, journal, tf) + self.assertGreater(journal.signals, 0) + # print(journal) if __name__ == "__main__": diff --git a/tests/unit/test_tensorboardtracker.py b/tests/unit/test_tensorboard.py similarity index 64% rename from tests/unit/test_tensorboardtracker.py rename to tests/unit/test_tensorboard.py index e5486f2..fddf87d 100644 --- a/tests/unit/test_tensorboardtracker.py +++ b/tests/unit/test_tensorboard.py @@ -3,14 +3,14 @@ import unittest from roboquant import Roboquant from roboquant.strategies import EMACrossover -from roboquant.trackers import RunMetric, EquityMetric, TensorboardTracker +from roboquant.journals import RunMetric, EquityMetric, TensorboardJournal from tests.common import get_feed from tensorboard.summary import Writer -class TestTensorboardTracker(unittest.TestCase): +class TestTensorboard(unittest.TestCase): - def test_tensorboardtracker(self): + def test_tensorboard_journal(self): rq = Roboquant(EMACrossover()) feed = get_feed() @@ -18,8 +18,8 @@ def test_tensorboardtracker(self): output = Path(tmpdir).joinpath("runs") writer = Writer(str(output)) - tracker = TensorboardTracker(writer, RunMetric(), EquityMetric()) - rq.run(feed, tracker=tracker) + journal = TensorboardJournal(writer, RunMetric(), EquityMetric()) + rq.run(feed, journal=journal) writer.close()