Pipeline Visualization¶
SignalFlow v0.5.0 includes built-in pipeline visualization, inspired by tools like Kedro-Viz. You can render your backtest pipeline as an interactive DAG (directed acyclic graph) to understand the flow of data through detectors, features, and strategy components.
What you'll learn:
- Visualize a backtest pipeline as a DAG
- Generate Mermaid diagrams (text-based, renders on GitHub and MkDocs)
- Create interactive HTML visualizations with D3.js
- Visualize feature dependency graphs
- Use the CLI and local development server for visualization
1. Setup¶
from datetime import datetime
from pathlib import Path
import signalflow as sf
from signalflow.data import RawDataFactory
from signalflow.data.raw_store import DuckDbSpotStore
from signalflow.data.source import VirtualDataProvider
# Generate synthetic data
db_path = Path("/tmp/viz_demo.duckdb")
store = DuckDbSpotStore(db_path=db_path)
VirtualDataProvider(store=store, seed=42).download(
pairs=["BTCUSDT", "ETHUSDT"],
n_bars=10_000,
)
# Load data
raw_data = RawDataFactory.from_duckdb_spot_store(
spot_store_path=db_path,
pairs=["BTCUSDT", "ETHUSDT"],
start=datetime(2020, 1, 1),
end=datetime(2030, 1, 1),
)
print(f"Loaded {len(raw_data.pairs)} pairs, shape: {raw_data.get('spot').shape}")
2026-02-15 00:50:45.015 | INFO | signalflow.data.raw_store.duckdb_stores:_ensure_tables:153 - Database initialized: /tmp/viz_demo.duckdb (data_type=spot, timeframe=1m) 2026-02-15 00:50:45.101 | DEBUG | signalflow.data.raw_store.duckdb_stores:insert_klines:220 - Inserted 10,000 rows for BTCUSDT 2026-02-15 00:50:45.102 | INFO | signalflow.data.source.virtual:download:255 - VirtualDataProvider: generated 10000 bars for BTCUSDT 2026-02-15 00:50:45.169 | DEBUG | signalflow.data.raw_store.duckdb_stores:insert_klines:220 - Inserted 10,000 rows for ETHUSDT 2026-02-15 00:50:45.170 | INFO | signalflow.data.source.virtual:download:255 - VirtualDataProvider: generated 10000 bars for ETHUSDT 2026-02-15 00:50:45.178 | INFO | signalflow.data.raw_store.duckdb_stores:_ensure_tables:153 - Database initialized: /tmp/viz_demo.duckdb (data_type=spot, timeframe=1m)
Loaded 2 pairs, shape: (20000, 8)
2. Build a Pipeline¶
We configure a backtest pipeline using the fluent builder API but do not call .run(). The visualization works on the pipeline configuration itself -- no execution needed.
builder = (
sf.Backtest("demo_strategy")
.data(raw=raw_data)
.detector("example/sma_cross", fast_period=20, slow_period=50)
.exit(tp=0.03, sl=0.015)
.capital(50_000)
)
print("Pipeline configured (not yet executed)")
2026-02-15 00:50:45.215 | DEBUG | signalflow.core.registry:_discover_internal_packages:152 - autodiscover: failed to import signalflow.detector.adapter
Pipeline configured (not yet executed)
3. Mermaid Diagram¶
Mermaid is a text-based diagramming format that renders natively on GitHub, MkDocs, Notion, and many other tools. It is ideal for embedding pipeline diagrams in documentation or pull requests.
Use format="mermaid" and show=False to get the raw Mermaid code as a string:
mermaid_code = sf.viz.pipeline(builder, format="mermaid", show=False)
print(mermaid_code)
{% raw %}
graph LR
subgraph Data Sources
data_spot(["Spot"])
end
subgraph Features
feature_0["ExampleSmaFeature"]
feature_1["ExampleSmaFeature"]
end
subgraph Detector
detector{{"ExampleSmaCrossDetector"}}
end
subgraph Runner
runner[["BacktestRunner"]]
end
subgraph Exit Rules
exit_rule("Exit: TP 3.0%, SL 1.5%")
end
data_spot --> feature_0
feature_0 --> feature_1
feature_1 --> detector
detector --> runner
exit_rule --> runner
{% endraw %}
4. HTML Visualization¶
The HTML format produces a self-contained interactive visualization built with D3.js. Nodes are draggable, and you can hover over them to inspect component details. When show=True (the default), the HTML opens automatically in your browser.
html = sf.viz.pipeline(builder, format="html", show=False)
print(f"Generated HTML visualization: {len(html):,} characters")
print("Contains D3.js interactive DAG with draggable nodes")
Generated HTML visualization: 32,667 characters Contains D3.js interactive DAG with draggable nodes
5. Save to File¶
Use the output parameter to save the visualization directly to a file:
output_path = Path("/tmp/pipeline.html")
sf.viz.pipeline(builder, format="html", output=str(output_path), show=False)
print(f"Saved to {output_path}")
Saved to /tmp/pipeline.html
6. Visualize Feature Dependencies¶
If your pipeline includes features, you can visualize their dependency graph separately. This is useful for understanding which raw columns each feature requires and how features chain together.
from signalflow.feature import FeaturePipeline
from signalflow.feature.examples import ExampleRsiFeature, ExampleSmaFeature
pipe = FeaturePipeline(
features=[
ExampleRsiFeature(period=14),
ExampleSmaFeature(period=20),
ExampleSmaFeature(period=50),
]
)
feature_mermaid = sf.viz.features(pipe, format="mermaid", show=False)
print(feature_mermaid)
graph LR
subgraph Data Sources
raw_data(["Raw Data (spot)"])
end
subgraph Features
feature_0_ExampleRsiFeature["ExampleRsiFeature 14"]
feature_1_ExampleSmaFeature["ExampleSmaFeature 20"]
feature_2_ExampleSmaFeature["ExampleSmaFeature 50"]
end
raw_data -->|close| feature_0_ExampleRsiFeature
raw_data -->|close| feature_1_ExampleSmaFeature
raw_data -->|close| feature_2_ExampleSmaFeature
7. CLI Visualization¶
You can also visualize pipelines directly from the command line using a YAML configuration file:
# Generate a pipeline visualization from a YAML config
sf viz config.yaml
# Start the local visualization server
sf viz config.yaml --serve --port 4141
This is convenient for CI/CD pipelines or quick inspection without writing Python code.
8. Local Development Server¶
The sf.viz.serve() function starts a local HTTP server (similar to Kedro-Viz) that serves the interactive pipeline visualization:
# This would open http://localhost:4141 in your browser
# sf.viz.serve(builder, port=4141)
The server runs until you press Ctrl+C. Do not run this in a notebook -- it blocks the kernel. Use it in standalone scripts or from the CLI instead.
Cleanup¶
store.close()
db_path.unlink(missing_ok=True)
Path("/tmp/pipeline.html").unlink(missing_ok=True)
print("Done!")
Done!
Next Steps¶
- 01 - Quick Start: Run your first backtest in 5 minutes
- 02 - Custom Detector: Create your own signal detector
- 03 - Data Loading & Resampling: Work with multiple timeframes
- 05 - Advanced Strategies: Multi-detector ensembles
- SignalFlow Documentation: Full API reference and guides