-
Notifications
You must be signed in to change notification settings - Fork 293
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
New blueprint_stocks example that uses visible time ranges
- Loading branch information
Showing
5 changed files
with
2,594 additions
and
2,285 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
<!--[metadata] | ||
title = "Stock chart (time range)" | ||
tags = ["Time series", "Blueprint"] | ||
thumbnail = "https://static.rerun.io/blueprint_stocks/8bfe6f16963acdceb2debb9de9a206dc2eb9b280/480w.png" | ||
thumbnail_dimensions = [480, 270] | ||
--> | ||
|
||
This example fetches the last 5 days of stock data for a few different stocks. | ||
We show how Rerun blueprints can then be used to present many different views of the same data. | ||
|
||
This is an alternative version of the blueprint_stocks example that uses time ranges to create the daily | ||
time series for each stock. This allows the underlying data to be stored on a single entity rather than | ||
splitting it across multiple entities for each day. | ||
|
||
<picture> | ||
<img src="https://static.rerun.io/blueprint_stocks/8bfe6f16963acdceb2debb9de9a206dc2eb9b280/full.png" alt=""> | ||
<source media="(max-width: 480px)" srcset="https://static.rerun.io/blueprint_stocks/8bfe6f16963acdceb2debb9de9a206dc2eb9b280/480w.png"> | ||
<source media="(max-width: 768px)" srcset="https://static.rerun.io/blueprint_stocks/8bfe6f16963acdceb2debb9de9a206dc2eb9b280/768w.png"> | ||
<source media="(max-width: 1024px)" srcset="https://static.rerun.io/blueprint_stocks/8bfe6f16963acdceb2debb9de9a206dc2eb9b280/1024w.png"> | ||
<source media="(max-width: 1200px)" srcset="https://static.rerun.io/blueprint_stocks/8bfe6f16963acdceb2debb9de9a206dc2eb9b280/1200w.png"> | ||
</picture> | ||
|
||
|
||
```bash | ||
pip install -e examples/python/blueprint_stocks_timerange | ||
``` | ||
|
||
```bash | ||
python -m blueprint_stocks_timerange | ||
``` | ||
|
||
The different blueprints can be explored using the `--blueprint` flag. For example: | ||
|
||
``` | ||
python -m blueprint_stocks_timerange --blueprint=compare | ||
``` | ||
|
||
Available choices are: | ||
|
||
- `one`: Uses a filter and time range to show only a single chart. | ||
- `compare`: Compares two stocks on several different days. | ||
- `grid`: Shows all the charts in a grid layout. |
231 changes: 231 additions & 0 deletions
231
examples/python/blueprint_stocks_timerange/blueprint_stocks_timerange.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,231 @@ | ||
#!/usr/bin/env python3 | ||
""" | ||
A simple application that fetches stock data from Yahoo Finance and visualizes it using the Rerun SDK. | ||
The main focus of this example is using blueprints to control how the data is displayed in the viewer. | ||
This is an alternative version of the blueprint_stocks example that uses time ranges to create the daily | ||
time series for each stock. This allows the underlying data to be stored on a single entity rather than | ||
splitting it across multiple entities for each day. | ||
""" | ||
|
||
from __future__ import annotations | ||
|
||
import argparse | ||
import datetime as dt | ||
from typing import Any | ||
|
||
import humanize | ||
import pytz | ||
import rerun as rr | ||
import rerun.blueprint as rrb | ||
import yfinance as yf | ||
|
||
################################################################################ | ||
# Helper functions to create blueprints | ||
################################################################################ | ||
|
||
|
||
def auto_blueprint() -> rrb.BlueprintLike: | ||
"""A blueprint enabling auto space views, which matches the application default.""" | ||
return rrb.Blueprint(auto_space_views=True, auto_layout=True) | ||
|
||
|
||
def time_ranges_for_day(day: dt.date) -> list[rrb.VisibleTimeRange]: | ||
"""Create a time range for a single day.""" | ||
open = dt.datetime.combine(day, dt.time(9, 30)).timestamp() | ||
close = dt.datetime.combine(day, dt.time(16, 0)).timestamp() | ||
return [ | ||
rrb.VisibleTimeRange( | ||
"time", | ||
start=rrb.TimeRangeBoundary.absolute(int(open * 1e9)), | ||
end=rrb.TimeRangeBoundary.absolute(int(close * 1e9)), | ||
) | ||
] | ||
|
||
|
||
def one_stock_on_day(symbol: str, day: dt.date) -> rrb.ContainerLike: | ||
"""Create a blueprint showing a single stock.""" | ||
return rrb.TimeSeriesView( | ||
name=f"{symbol}: {day}", | ||
origin=f"/stocks/{symbol}", | ||
time_ranges=time_ranges_for_day(day), | ||
) | ||
|
||
|
||
def compare_two_over_time(symbol1: str, symbol2: str, dates: list[dt.date]) -> rrb.ContainerLike: | ||
"""Create a blueprint comparing 2 stocks for a single day.""" | ||
return rrb.Vertical( | ||
name=f"{symbol1} vs {symbol2}", | ||
contents=[ | ||
rrb.TimeSeriesView( | ||
name=f"{day}", | ||
origin="/stocks", | ||
contents=[ | ||
f"+ $origin/{symbol1}", | ||
f"+ $origin/{symbol2}", | ||
], | ||
time_ranges=time_ranges_for_day(day), | ||
) | ||
for day in dates | ||
], | ||
) | ||
|
||
|
||
def stock_grid(symbols: list[str], dates: list[Any]) -> rrb.ContainerLike: | ||
"""Create a grid of stocks and their time series over all days.""" | ||
return rrb.Vertical( | ||
contents=[ | ||
rrb.Horizontal( | ||
contents=[rrb.TextDocumentView(name=f"{symbol}", origin=f"/stocks/{symbol}/info")] | ||
+ [ | ||
rrb.TimeSeriesView( | ||
name=f"{day}", | ||
origin=f"/stocks/{symbol}", | ||
time_ranges=time_ranges_for_day(day), | ||
) | ||
for day in dates | ||
], | ||
name=symbol, | ||
) | ||
for symbol in symbols | ||
] | ||
) | ||
|
||
|
||
def hide_panels(viewport: rrb.ContainerLike) -> rrb.BlueprintLike: | ||
"""Wrap a viewport in a blueprint that hides the time and selection panels.""" | ||
return rrb.Blueprint( | ||
viewport, | ||
rrb.TimePanel(expanded=True), | ||
rrb.SelectionPanel(expanded=False), | ||
) | ||
|
||
|
||
################################################################################ | ||
# Helper functions for styling | ||
################################################################################ | ||
|
||
brand_colors = { | ||
"AAPL": 0xA2AAADFF, | ||
"AMZN": 0xFF9900FF, | ||
"GOOGL": 0x34A853FF, | ||
"META": 0x0081FBFF, | ||
"MSFT": 0xF14F21FF, | ||
} | ||
|
||
|
||
def style_plot(symbol: str) -> rr.SeriesLine: | ||
return rr.SeriesLine( | ||
color=brand_colors[symbol], | ||
name=symbol, | ||
) | ||
|
||
|
||
def style_peak(symbol: str) -> rr.SeriesPoint: | ||
return rr.SeriesPoint( | ||
color=0xFF0000FF, | ||
name=f"{symbol} (peak)", | ||
marker="Up", | ||
) | ||
|
||
|
||
################################################################################ | ||
# Main script | ||
################################################################################ | ||
|
||
|
||
def main() -> None: | ||
parser = argparse.ArgumentParser(description="Visualize stock data using the Rerun SDK") | ||
parser.add_argument( | ||
"--blueprint", | ||
choices=["auto", "one", "compare", "grid"], | ||
default="grid", | ||
help="Select the blueprint to use", | ||
) | ||
parser.add_argument( | ||
"--show_panels", | ||
action="store_true", | ||
help="Show the time and selection panels", | ||
) | ||
|
||
rr.script_add_args(parser) | ||
args = parser.parse_args() | ||
|
||
et_timezone = pytz.timezone("America/New_York") | ||
current_date = dt.datetime.now(et_timezone).date() | ||
symbols = ["AAPL", "AMZN", "GOOGL", "META", "MSFT"] | ||
dates = list(filter(lambda x: x.weekday() < 5, [current_date - dt.timedelta(days=i) for i in range(7, 0, -1)])) | ||
|
||
if args.blueprint == "auto": | ||
blueprint = auto_blueprint() | ||
else: | ||
if args.blueprint == "one": | ||
viewport = one_stock_on_day("AAPL", dates[3]) | ||
elif args.blueprint == "compare": | ||
viewport = compare_two_over_time("META", "MSFT", dates) | ||
elif args.blueprint == "grid": | ||
viewport = stock_grid(symbols, dates) | ||
else: | ||
raise ValueError(f"Unknown blueprint: {args.blueprint}") | ||
|
||
if not args.show_panels: | ||
blueprint = hide_panels(viewport) | ||
else: | ||
blueprint = viewport | ||
|
||
rr.script_setup(args, "rerun_example_blueprint_stocks") | ||
rr.send_blueprint(blueprint) | ||
|
||
# In a future blueprint release, this can move into the blueprint as well | ||
for symbol in symbols: | ||
for day in dates: | ||
rr.log(f"stocks/{symbol}", style_plot(symbol), timeless=True) | ||
rr.log(f"stocks/{symbol}/daily_peaks", style_peak(symbol), timeless=True) | ||
|
||
for symbol in symbols: | ||
stock = yf.Ticker(symbol) | ||
|
||
name = stock.info["shortName"] | ||
industry = stock.info["industry"] | ||
marketCap = humanize.intword(stock.info["marketCap"]) | ||
revenue = humanize.intword(stock.info["totalRevenue"]) | ||
|
||
info_md = ( | ||
f"- **Name**: {name}\n" | ||
f"- **Industry**: {industry}\n" | ||
f"- **Market cap**: ${marketCap}\n" | ||
f"- **Total Revenue**: ${revenue}\n" | ||
) | ||
|
||
rr.log( | ||
f"stocks/{symbol}/info", | ||
rr.TextDocument(info_md, media_type=rr.MediaType.MARKDOWN), | ||
timeless=True, | ||
) | ||
|
||
min_time = dt.datetime.combine(dates[0], dt.time(0, 0)) | ||
max_time = dt.datetime.combine(dates[-1], dt.time(16, 00)) | ||
|
||
hist = stock.history(start=min_time, end=max_time, interval="5m") | ||
if len(hist.index) == 0: | ||
continue | ||
|
||
daily_peaks = [] | ||
|
||
for day in dates: | ||
open_time = et_timezone.localize(dt.datetime.combine(day, dt.time(9, 30))) | ||
close_time = et_timezone.localize(dt.datetime.combine(day, dt.time(16, 00))) | ||
daily_peaks.append(hist.loc[open_time:close_time].High.idxmax()) # type: ignore[misc] | ||
|
||
for row in hist.itertuples(): | ||
rr.set_time_seconds("time", row.Index.timestamp()) | ||
rr.log(f"stocks/{symbol}", rr.Scalar(row.High)) | ||
if row.Index in daily_peaks: | ||
rr.log(f"stocks/{symbol}/daily_peaks", rr.Scalar(row.High)) | ||
|
||
rr.script_teardown(args) | ||
|
||
|
||
if __name__ == "__main__": | ||
main() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
[build-system] | ||
requires = ["hatchling"] | ||
build-backend = "hatchling.build" | ||
|
||
[project] | ||
name = "blueprint_stocks_timerange" | ||
version = "0.1.0" | ||
requires-python = ">=3.8" | ||
readme = "README.md" | ||
dependencies = ["humanize", "rerun-sdk", "yfinance"] | ||
|
||
[project.scripts] | ||
blueprint_stocks_timerange = "blueprint_stocks_timerange:main" | ||
|
||
[tool.rerun-example] | ||
#skip = true |
Oops, something went wrong.