forked from rerun-io/rerun
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
New standalone example showing blueprint configuration of some stock (r…
…erun-io#5603) ### What This is the example that the blueprint getting started guide / tutorials will be built off of. However, this is a slightly more full-featured example that can be used as a reference and standalone application. ![](https://static.rerun.io/blueprint_stocks/8bfe6f16963acdceb2debb9de9a206dc2eb9b280/1024w.png) ### Checklist * [x] I have read and agree to [Contributor Guide](https://github.com/rerun-io/rerun/blob/main/CONTRIBUTING.md) and the [Code of Conduct](https://github.com/rerun-io/rerun/blob/main/CODE_OF_CONDUCT.md) * [x] I've included a screenshot or gif (if applicable) * [x] I have tested the web demo (if applicable): * Using newly built examples: [app.rerun.io](https://app.rerun.io/pr/5603/index.html) * Using examples from latest `main` build: [app.rerun.io](https://app.rerun.io/pr/5603/index.html?manifest_url=https://app.rerun.io/version/main/examples_manifest.json) * Using full set of examples from `nightly` build: [app.rerun.io](https://app.rerun.io/pr/5603/index.html?manifest_url=https://app.rerun.io/version/nightly/examples_manifest.json) * [x] The PR title and labels are set such as to maximize their usefulness for the next release's CHANGELOG * [x] If applicable, add a new check to the [release checklist](https://github.com/rerun-io/rerun/blob/main/tests/python/release_checklist)! - [PR Build Summary](https://build.rerun.io/pr/5603) - [Docs preview](https://rerun.io/preview/588e1abcebb80a6b5c2974b3067cfcdb0f7986f9/docs) <!--DOCS-PREVIEW--> - [Examples preview](https://rerun.io/preview/588e1abcebb80a6b5c2974b3067cfcdb0f7986f9/examples) <!--EXAMPLES-PREVIEW--> - [Recent benchmark results](https://build.rerun.io/graphs/crates.html) - [Wasm size tracking](https://build.rerun.io/graphs/sizes.html) --------- Co-authored-by: Emil Ernerfeldt <[email protected]>
- Loading branch information
Showing
8 changed files
with
338 additions
and
26 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
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,38 @@ | ||
<!--[metadata] | ||
title = "Stock Charts" | ||
description = "Uses stock data as an example of how to leverage Rerun blueprints to control the layout and presentation of the viewer." | ||
tags = ["time-series", "blueprint"] | ||
thumbnail = "https://static.rerun.io/blueprint_stocks/8bfe6f16963acdceb2debb9de9a206dc2eb9b280/480w.png" | ||
thumbnail_dimensions = [480, 270] | ||
--> | ||
|
||
<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> | ||
|
||
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. | ||
|
||
```bash | ||
pip install -r examples/python/blueprint_stocks/requirements.txt | ||
python examples/python/blueprint_stocks/blueprint_main.py | ||
``` | ||
|
||
The different blueprints can be explored using the `--blueprint` flag. For example: | ||
|
||
``` | ||
python examples/python/blueprint_stocks/main.py --blueprint=one-stock | ||
``` | ||
|
||
Available choices are: | ||
|
||
- `auto`: Reset the blueprint to the auto layout used by the viewer. | ||
- `one-stock`: Uses a filter to show only a single chart. | ||
- `one-stock-with-info`: Uses a container to layout a chart and its info document | ||
- `one-stock-no-peaks`: Uses a filter to additionally remove some of the data from the chart. | ||
- `compare-two`: Adds data from multiple sources to a single chart. | ||
- `grid`: Shows all the charts in a grid layout. |
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,215 @@ | ||
#!/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. | ||
""" | ||
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.ViewportLike: | ||
"""A blueprint enabling auto space views, which matches the application default.""" | ||
return rrb.Viewport(auto_space_views=True, auto_layout=True) | ||
|
||
|
||
def one_stock(symbol: str) -> rrb.ViewportLike: | ||
"""Create a blueprint showing a single stock.""" | ||
return rrb.TimeSeriesView(name=f"{symbol}", origin=f"/stocks/{symbol}") | ||
|
||
|
||
def one_stock_with_info(symbol: str) -> rrb.ViewportLike: | ||
"""Create a blueprint showing a single stock with its info arranged vertically.""" | ||
return rrb.Vertical( | ||
rrb.TextDocumentView(name=f"{symbol}", origin=f"/stocks/{symbol}/info"), | ||
rrb.TimeSeriesView(name=f"{symbol}", origin=f"/stocks/{symbol}"), | ||
row_shares=[1, 4], | ||
) | ||
|
||
|
||
def compare_two(symbol1: str, symbol2: str, day: Any) -> rrb.ViewportLike: | ||
"""Create a blueprint comparing 2 stocks for a single day.""" | ||
return rrb.TimeSeriesView( | ||
name=f"{symbol1} vs {symbol2} ({day})", | ||
contents=[ | ||
f"+ /stocks/{symbol1}/{day}", | ||
f"+ /stocks/{symbol2}/{day}", | ||
], | ||
) | ||
|
||
|
||
def one_stock_no_peaks(symbol: str) -> rrb.ViewportLike: | ||
""" | ||
Create a blueprint showing a single stock without annotated peaks. | ||
This uses an exclusion pattern to hide the peaks. | ||
""" | ||
return rrb.TimeSeriesView( | ||
name=f"{symbol}", | ||
origin=f"/stocks/{symbol}", | ||
contents=[ | ||
"+ $origin/**", | ||
"- $origin/peaks/**", | ||
], | ||
) | ||
|
||
|
||
def stock_grid(symbols: list[str], dates: list[Any]) -> rrb.ViewportLike: | ||
"""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}/{day}") for day in dates], | ||
) | ||
for symbol in symbols | ||
] | ||
) | ||
|
||
|
||
def hide_panels(viewport: rrb.ViewportLike) -> rrb.BlueprintLike: | ||
"""Wrap a viewport in a blueprint that hides the time and selection panels.""" | ||
return rrb.Blueprint( | ||
viewport, | ||
rrb.TimePanel(expanded=False), | ||
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-stock", "one-stock-with-info", "compare-two", "one-stock-no-peaks", "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)])) | ||
|
||
blueprint: rrb.BlueprintLike | ||
|
||
if args.blueprint == "auto": | ||
blueprint = auto_blueprint() | ||
elif args.blueprint == "one-stock": | ||
blueprint = one_stock("AAPL") | ||
elif args.blueprint == "one-stock-with-info": | ||
blueprint = one_stock_with_info("AMZN") | ||
elif args.blueprint == "one-stock-no-peaks": | ||
blueprint = one_stock_no_peaks("GOOGL") | ||
elif args.blueprint == "compare-two": | ||
blueprint = compare_two("META", "MSFT", dates[-1]) | ||
elif args.blueprint == "grid": | ||
blueprint = stock_grid(symbols, dates) | ||
else: | ||
raise ValueError(f"Unknown blueprint: {args.blueprint}") | ||
|
||
if not args.show_panels: | ||
blueprint = hide_panels(blueprint) | ||
|
||
rr.init("rerun_example_blueprint_stocks", spawn=True, 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}/{day}", style_plot(symbol), timeless=True) | ||
rr.log(f"stocks/{symbol}/peaks/{day}", 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, | ||
) | ||
|
||
for day in dates: | ||
open_time = dt.datetime.combine(day, dt.time(9, 30)) | ||
close_time = dt.datetime.combine(day, dt.time(16, 00)) | ||
|
||
hist = stock.history(start=open_time, end=close_time, interval="5m") | ||
|
||
hist.index = hist.index - et_timezone.localize(open_time) | ||
peak = hist.High.idxmax() | ||
|
||
for row in hist.itertuples(): | ||
rr.set_time_seconds("time", row.Index.total_seconds()) | ||
rr.log(f"stocks/{symbol}/{day}", rr.Scalar(row.High)) | ||
if row.Index == peak: | ||
rr.log(f"stocks/{symbol}/peaks/{day}", 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,3 @@ | ||
humanize | ||
rerun-sdk | ||
yfinance |
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
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
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
Oops, something went wrong.