Skip to content

Repository files navigation

wickplot

Maven Central

Candlestick / trading charts for Compose Multiplatform, drawn directly on a native Compose Canvas — no WebView, no JS bridge, no platform chart library underneath.

A wick is the thin line above or below a candle's body, marking the bar's high and low.

wickplot dark sample

Features

  • Candlesticks + volume band from any List<Candle> — implement the small Candle interface on your own bar type (zero-copy), or use the bundled OhlcvCandle
  • Line overlays for host-computed indicators — VWAP, moving averages, band edges (overlays: List<LineOverlay>; per-line colour, stroke width, and optional legend label)
  • Exact-price trade entry/exit markers (PriceMarker diamonds)
  • Crosshair with OHLCV legend, price axis, adaptive time axis (intraday HH:MM / daily MM-DD)
  • Cursor-anchored scroll zoom and drag pan, clamped to the data
  • ChartColors.Dark / ChartColors.Light presets, fully customizable
  • Trade-centric initial framing via ChartInitialView (calendar or intraday modes)
  • Pure, unit-tested pan/zoom/viewport math; the renderer is exercised by a Skia screenshot harness

wickplot renders what you give it — the Lightweight-Charts philosophy. Indicator values (VWAP, moving averages, …) are computed by your app and passed in as plain LineOverlays; the library never computes market math.

Targets: JVM/Desktop, iOS (arm64, simulatorArm64), wasmJs. License: Apache-2.0.

Install

commonMain.dependencies {
    implementation("io.github.earlisreal:wickplot:0.1.2")
}

Quick start

val bars: List<Candle> = listOf(
    OhlcvCandle(LocalDateTime(2026, 7, 1, 9, 30), open = 100.0, high = 103.5, low = 99.2, close = 102.8, volume = 120_000),
    // ...
)

CandlestickCanvasChart(
    bars = bars,
    markers = listOf(PriceMarker(barIndex = 12, price = 101.25, isBuy = true)),
    title = "ACME · D",
    colors = ChartColors.Light,  // default; or ChartColors.Dark
    overlays = emptyList(),      // indicator lines — see "Drawing indicators" below
    intraday = false,            // switches the time-axis format
)

Drag to pan, scroll to zoom (anchored at the cursor), hover for a crosshair with the bar's OHLCV.

Preview the chart locally

The repository includes an interactive desktop preview with the same sample data and gestures as the chart. From the repository root, run:

./gradlew.bat runSample                 # light theme (default)
./gradlew.bat runSample -Ptheme=dark    # dark theme

The theme property accepts only light or dark. The published CandlestickCanvasChart API also defaults to ChartColors.Light; pass colors = ChartColors.Dark when an application needs the dark palette.

Light theme:

wickplot light sample

Customizing the crosshair date tag

The highlighted date tag will follow the selected candle on the bottom axis. Its default format is Sun, Sep 20, 2026 18:56:40, using English names and a 24-hour clock. When every supplied candle is exactly midnight, the default omits the time: Sun, Sep 20, 2026. Otherwise, when every candle has zero seconds and nanoseconds, it omits seconds: Sun, Sep 20, 2026 18:56. If any candle has nonzero seconds or nanoseconds, all date tags include seconds (including :00); fractional seconds are not printed. Detection uses the entire supplied dataset, so hovering or panning does not switch precision.

Pass crosshairTimeFormatter to customize the full tag text. For example, this callback always shows 2026-09-20 18:56, regardless of the data's seconds:

CandlestickCanvasChart(
    bars = bars,
    markers = emptyList(),
    title = "ACME",
    crosshairTimeFormatter = { timestamp ->
        val hour = timestamp.hour.toString().padStart(2, '0')
        val minute = timestamp.minute.toString().padStart(2, '0')
        "${timestamp.date} $hour:$minute"
    },
)

The optional parameter is crosshairTimeFormatter: ((LocalDateTime) -> String)? = null on both CandlestickCanvasChart and drawCandlestickChart. Omit it or pass null to use the default. The callback receives the selected candle's kotlinx.datetime.LocalDateTime as supplied, with no timezone conversion, and controls only the crosshair date tag. Regular axis ticks still use intraday. The tag stays within the plot edges and uses a single-line ellipsis if it cannot fit.

LocalDateTime cannot distinguish omitted seconds from explicit :00. A dataset containing only minute-aligned timestamps therefore omits seconds by default, even if the host considers it a 10s timeframe. The same limitation applies to midnight-only data: it is treated as date-only. Use the callback when the host needs a fixed precision or a different locale.

Drawing indicators

Your app computes the values, wickplot draws the lines: anything of shape List<Candle> → List<LinePoint> plugs straight into overlays.

// A simple moving average — a few lines of your code.
fun sma(bars: List<Candle>, period: Int): List<LinePoint> =
    ((period - 1) until bars.size).map { i ->
        LinePoint(barIndex = i, value = ((i - period + 1)..i).sumOf { bars[it].close } / period)
    }

// Session VWAP: cumulative typical price × volume, reset at each new trading day.
fun sessionVwap(bars: List<Candle>): List<LinePoint> {
    var tpv = 0.0
    var vol = 0.0
    var day: LocalDate? = null
    return bars.mapIndexedNotNull { i, b ->
        if (b.timestamp.date != day) { tpv = 0.0; vol = 0.0; day = b.timestamp.date }
        tpv += (b.high + b.low + b.close) / 3.0 * b.volume
        vol += b.volume
        if (vol > 0.0) LinePoint(barIndex = i, value = tpv / vol) else null
    }
}

CandlestickCanvasChart(
    bars = bars,
    markers = emptyList(),
    title = "ACME · 1m",
    overlays = listOf(
        LineOverlay(points = sessionVwap(bars), label = "VWAP"),          // no color → ChartColors.overlay
        LineOverlay(points = sma(bars, 20), label = "SMA 20", color = Color(0xFF64B5F6)),
        LineOverlay(points = sma(bars, 50), color = Color(0xFFFFB74D), strokeWidth = 2f),
    ),
    intraday = true,
)

Overlays with a label show their value at the hovered bar in the crosshair legend, in the overlay's colour; unlabeled overlays are drawn but stay out of the legend. Multi-line indicators (Bollinger, Keltner) are just several overlays — one per band edge.

Why not Vico?

Vico is a great general-purpose Compose chart engine and has a candlestick layer, but no built-in trading overlays. wickplot is purpose-built for trade analysis: volume, indicator line overlays, and exact-price trade markers out of the box, with indicator panes on the roadmap.

Roadmap

  • Stacked sub-panes sharing the X axis (for host-computed RSI / MACD / volume studies)
  • Android target

License

Apache License 2.0

About

Candlestick & trading charts for Compose Multiplatform, drawn natively on Canvas - no WebView, no JS bridge.

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages