Visualization with Kino TheoryCraft

Kino TheoryCraft is a visualization library for TheoryCraft that renders interactive trading charts in Livebook notebooks. It is built on TradingView Lightweight Charts, a JavaScript library for financial charting.

This library is a visualization tool. It displays market data (bars, indicators) produced by TheoryCraft pipelines. It does not execute strategies or manage portfolios.

⚠️ Kino TheoryCraft is under active development. The API may change.

Installation

Add to your Livebook notebook:

Mix.install([
  {:kino_theory_craft, github: "theorycraft-trading/kino_theory_craft"},
  {:theory_craft, github: "theorycraft-trading/theory_craft"},
  # Also your DataFeed if needed
  {:dukascopy, github: "theorycraft-trading/dukascopy"}
])

Modules

The library provides two main modules:

ModulePurpose
KinoTheoryCraft.ChartBuild chart configurations with series and options
KinoTheoryCraft.LiveChartRender charts with real-time update capabilities

Chart Module

KinoTheoryCraft.Chart provides a fluent API for building chart configurations.

Creating a Chart

alias KinoTheoryCraft.Chart

chart = Chart.new(width: :auto, height: 600)

The new/1 function creates a chart struct with optional width and height.

Attaching a Data Source

Use set_source/2 to attach a MarketSource or any enumerable:

chart = Chart.set_source(chart, market_source)

The source provides data that series can reference by name.

Adding Series

The add_data_from_source/4 function adds a series that loads data lazily from the attached source:

chart =
  chart
  |> Chart.add_data_from_source("EURUSD", :candlestick)
  |> Chart.add_data_from_source("SMA_20", :line)
  |> Chart.add_data_from_source("RSI", :line, pane: 1, options: %{color: "#2962ff"})

Parameters:

  • First argument: the data name in the source
  • Second argument: the series type
  • Options: pane index and options for series styling

For pre-computed data, use add_data/5:

data = [
  %{time: ~U[2024-01-01 00:00:00Z], open: 1.10, high: 1.11, low: 1.09, close: 1.105},
  %{time: ~U[2024-01-01 00:05:00Z], open: 1.105, high: 1.12, low: 1.10, close: 1.115}
]
chart = Chart.add_data(chart, "EURUSD", :candlestick, data)

For empty series that will receive live updates, use add_series/4:

chart = Chart.add_series(chart, "EURUSD", :candlestick)

Series Types

TypeDescriptionData Format
:candlestickOHLC candlestick bars%{time, open, high, low, close}
:barOHLC bar chart%{time, open, high, low, close}
:lineSimple line%{time, value}
:areaFilled area under line%{time, value}
:histogramVertical bars%{time, value}
:baselineLine with fill above/below baseline%{time, value}

Styling

Chart and series options follow the Lightweight Charts API. Pass chart_options to Chart.new/1 and options when adding series.

See examples.livemd for practical usage examples.

candlestick_options = %{
  up_color: "#00bfff",
  down_color: "#683ab6",
  border_up_color: "#0288d1",
  border_down_color: "#4527a0",
  wick_up_color: "#0288d1",
  wick_down_color: "#4527a0"
}

chart_options = %{
  time_scale: %{time_visible: true, seconds_visible: true}
}

Chart.new(width: :auto, height: 400, chart_options: chart_options)
|> Chart.set_source(market_source)
|> Chart.add_data_from_source("data_m5", :candlestick, options: candlestick_options)

Multi-Pane Layout

The pane option organizes series into separate chart sections:

chart =
  Chart.new(width: :auto, height: 600)
  |> Chart.set_source(market_source)
  |> Chart.add_data_from_source("data_m5", :candlestick, pane: 0)
  |> Chart.add_data_from_source("sma_20", :line, pane: 0)
  |> Chart.add_data_from_source("rsi", :line, pane: 1)
  |> Chart.add_data_from_source("volume", :histogram, pane: 2)

Pane indices start at 0. Series in the same pane share the same price scale.

Rendering Static Charts

Charts implement the Kino.Render protocol. Simply return a chart from a Livebook cell:

chart

Or explicitly render:

Kino.render(chart)

LiveChart Module

KinoTheoryCraft.LiveChart renders charts that can receive real-time updates.

Creating a Live Chart

alias KinoTheoryCraft.{Chart, LiveChart}

chart =
  Chart.new(width: :auto, height: 600)
  |> Chart.add_series("EURUSD", :candlestick)

kino = LiveChart.render(chart)
Kino.render(kino)

Updating Data

Replace all data for series:

LiveChart.set_data(kino, %{
  "EURUSD" => [
    %{time: ~U[2024-01-01 00:00:00Z], open: 1.10, high: 1.11, low: 1.09, close: 1.105},
    %{time: ~U[2024-01-01 00:05:00Z], open: 1.105, high: 1.12, low: 1.10, close: 1.115}
  ]
})

Append or update individual points:

LiveChart.update(kino, %{
  "EURUSD" => %{time: ~U[2024-01-01 00:10:00Z], open: 1.115, high: 1.12, low: 1.11, close: 1.118}
})

The update/2 function also accepts MarketEvent structs from TheoryCraft pipelines.

Control Functions

# Clear all series data
LiveChart.clear(kino)

# Fit chart viewport to visible data
LiveChart.fit_content(kino)

Lightweight Charts

Kino TheoryCraft uses TradingView Lightweight Charts for rendering. This JavaScript library provides:

  • Performant rendering of financial time series
  • Multiple series types (candlestick, line, area, histogram, bar, baseline)
  • Price and time scale management
  • Crosshair and tooltip functionality

The library handles the JavaScript integration internally. You work with Elixir data structures; the library serializes them to the format Lightweight Charts expects.

Time values are converted from Elixir DateTime structs to Unix timestamps (seconds with millisecond precision).

Livebook Integration

Kino TheoryCraft is built on Kino, the Livebook component framework. It uses:

  • Kino.JS for static chart rendering
  • Kino.JS.Live for real-time updates

Charts render automatically in Livebook cells. The JavaScript assets are bundled with the library.

Use Cases Examples

Backtesting Inspection

After running a backtest, visualize the price action:

alias KinoTheoryCraft.Chart
alias TheoryCraft.MarketSource

feed_options = [
  instrument: "EUR/USD",
  from: ~D[2025-01-01],
  to: ~D[2025-01-02]
]

market_source =
  %MarketSource{}
  |> MarketSource.add_data({Dukascopy.DataFeed, feed_options}, name: "eurusd")
  |> MarketSource.resample("m5", bar_only: true, data: "eurusd", name: "eurusd_m5")

Chart.new(width: :auto, height: 400)
|> Chart.set_source(market_source)
|> Chart.add_data_from_source("eurusd_m5", :candlestick)

Debugging Strategies

Use visualization to understand why a strategy behaved unexpectedly. Overlay entry/exit signals, indicator values, and price action to identify issues.

Data Exploration

Before building strategies, explore market data visually. Check data quality, identify patterns, and understand market behavior.

Next Steps