Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 66 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,69 @@
## 2.4.0

### A chart that sits still

- **New `scrollEnabled` and `zoomEnabled` turn the chart's own gestures off**,
for a chart that is meant to show one fixed stretch rather than be navigated
— an intraday session, a thumbnail, a printed figure. With `scrollEnabled`
off a drag neither slides the window nor flings it, and `onLoadMore` is never
asked for more candles, since no edge is ever reached. With `zoomEnabled` off
a pinch does nothing, and the zoom slider — only ever shown on the web and on
desktop, where there is no pinch — is left off too.
- They are worth turning off together. Zooming out makes the candles narrower,
which leaves the window room to scroll into, so a chart that only had
`scrollEnabled` off could be pinched back into a scrollable one.
- Both hold the user back and leave your own code alone, the way
`priceScaleDrag` already did: `zoomIn`, `zoomOut`, `setChartScale`,
`goToIndex`, `fitAll` and the rest of `KChartController` still work.
- For a chart drawn at a fixed position, give `ChartStyle.pointWidth` roughly
the chart's width divided by the number of candles. Once the whole series
fits, there is nowhere to scroll to even before the flag.

### Price axis

- **New `lockPriceScale` keeps the axis still while the chart scrolls.** The
axis fits the candles in the window, so scrolling rescaled it: dragging back
through a trend changed every number on it. Locked, it holds the range it was
already showing and the candles move under a scale that stays where it is —
which is what reading a level off the axis while scrolling needs, and what
paging in history through `onLoadMore` needs in order not to jump. It locks
onto what is already on screen, so turning it on does not move the chart, and
`resetPriceScale` hands the axis back: it refits to the window and holds there
afresh.
- Only the scale is held. The window's own high and low are still measured, so
their markers keep pointing at the candles that set them, and a locked axis
can still be dragged and zoomed — from the range it is held at rather than
the window's. The range is held until it is reset, so a chart that switches to
another instrument should reset it; paging in candles and live ticks need
nothing, which is the point.
- **New `ChartStyle.priceAxisWidth` holds a gutter back for the labels.** It is
taken off whichever side `verticalTextAlignment` puts them on, and the
candles, the grid, the indicator panes and the date axis all stop short of it,
so the labels sit in the gutter on their own instead of candles sliding under
the numbers. The plot is clipped to its own bounds, so nothing spills into the
gutter, and pressing the labels grabs the scale the way pressing the axis
strip always has. Left at 0, the default, nothing changes: the labels are
drawn over the candles exactly as before.

### Fixed

- **`onLoadMore` is called again.** The callback was declared, documented and
accepted, but nothing in the package ever invoked it, so paging in older
candles could not work however it was wired up. It is now asked at both
places the scroll is clamped — dragging and flinging — with the edge latched,
so a drag held against the edge asks once when it arrives rather than on
every frame, and asks again after coming away and going back. The flag is
`true` at the newest candle and `false` at the oldest, as documented.

- **The long-press readout can be turned off and on again.** It listened to a
single-subscription stream from a subtree that is only mounted while
`showInfoDialog` is set. Setting it back to `true` made a second listen on a
stream already listened to, which threw `Bad state: Stream has already been
listened to` as the readout remounted — and because that throw landed while
the enclosing `Stack` was mounting its children, what callers actually saw was
`LateInitializationError: Field '_children' has not been initialized`. The
controller is a broadcast one now.

## 2.3.1

### Performance
Expand Down
10 changes: 7 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ Named for the open-high-low-close-volume bars it renders.
- **Sub-charts** — `MACD`, `KDJ`, `RSI`, `WR`, `CCI`, `ATR`, `OBV`, `MFI`, `DMI`, Aroon, Stochastic RSI, `ROC`, `TRIX`, a volume average and the Awesome oscillator, each in its own stacked pane — resizable and reorderable by dragging.
- **Linear, logarithmic, percentage or indexed-to-100 price axis**, so a decade of compounding reads as well as an afternoon — invertible, with the window's high, low and average close markable on it.
- **A price scale you can drag** — pull the labels to stretch or flatten the candles, drag the chart to slide the window, double-tap to fit it back; from code as well, through the controller.
- **A price axis that can be pinned** — `lockPriceScale` holds it at one range instead of refitting it to whatever is on screen, so scrolling moves the candles under a scale that stays put rather than rescaling every number on the axis; and `priceAxisWidth` holds a gutter back for the labels, so the candles stop short of them instead of sliding underneath.
- **Axes that read as round numbers** — both axes choose their values first and are ruled where the labels fall, so a price scale steps `69000, 69500, 70000` and an intraday date axis lands on the hour, never on whatever value happened to fall on an evenly spaced pixel.
- **29 drawing tools** — levels and rays, trend lines, arrows, extended lines, boxes, ellipses, triangles, parallel channels, pitchforks, Gann fans and boxes, four Fibonacci tools, a regression trend with its bands, XABCD patterns, multi-leg paths, price and date brackets, a measuring tool, planned positions with their risk-to-reward, notes, callouts, flags and freehand strokes; each placed by tap or drag, then dragged, locked, hidden and removed.
- **A line editor** that opens on selection: colour, opacity, thickness, solid/dashed/dotted stroke, fill, label text and visibility, alerts, lock and delete — every option list, control and pixel of it configurable through `DrawingStyle`.
Expand All @@ -54,6 +55,7 @@ Named for the open-high-low-close-volume bars it renders.
- **Higher timeframes** — a daily moving average on a fifteen-minute chart, through `TimeframeIndicator`; each candle reads the last higher-timeframe bar that had closed when it opened, so the line never repaints and never shows a value from its own future.
- **A right-click menu** on the chart and on any drawing — coordinates, duplicate, restack, lock, hide, alert, delete; paste, select all, fit the scale, undo — with your own items added through one builder.
- **Crosshair on hover** and an **OHLC legend** above the chart, which is how a chart reads on a desktop.
- **Or no interaction at all** — `scrollEnabled` and `zoomEnabled` turn the chart's own gestures off, for an intraday session, a thumbnail or a figure that is meant to sit still rather than be navigated; your own code can still drive it.
- **Driven from your own code** — `KChartController` zooms, scrolls back to the live candle and hands you the chart as a PNG.
- **The visible window, read and set** — which candles are on screen, go-to-date, fit-to-screen, and a callback whenever it moves.
- **An overview strip** — `ChartOverview` draws the whole history under the chart with the visible window lit; drag it to scrub, drag its edges to zoom, tap to jump.
Expand All @@ -76,11 +78,13 @@ Named for the open-high-low-close-volume bars it renders.

![Two linked charts sharing one crosshair](https://raw.githubusercontent.com/CtrlAltDevelop/ohlcv_chart/main/screenshots/linked-charts.png)

![Bar replay stepping the market forward a candle at a time](https://raw.githubusercontent.com/CtrlAltDevelop/ohlcv_chart/main/screenshots/bar-replay.gif)

## Install

```yaml
dependencies:
ohlcv_chart: ^2.2.0
ohlcv_chart: ^2.4.0
```

## Quick start
Expand Down Expand Up @@ -117,7 +121,7 @@ One page per feature, in [`doc/`](doc/README.md):
| [Indicators](doc/indicators.md) | 31 of them as instances, the catalogue, pane scales, chaining, higher timeframes, alerts |
| [Comparing a second instrument](doc/comparison.md) | Other series over the same window, rebased or at their own prices |
| [Chart types](doc/chart-types.md) | Eight ways to draw a series, and six transforms of the candles |
| [Price axis](doc/price-axis.md) | Linear, log, percentage or indexed to 100; inverting and dragging it |
| [Price axis](doc/price-axis.md) | Linear, log, percentage or indexed to 100; inverting, dragging, locking it, and its gutter |
| [The date axis](doc/date-axis.md) | Round time values, and taking the axis over yourself |
| [The legend and the crosshair](doc/legend-and-crosshair.md) | The OHLC row, and the crosshair on hover |
| [The long-press readout](doc/readout.md) | The card over a held candle, and your own builder |
Expand All @@ -126,7 +130,7 @@ One page per feature, in [`doc/`](doc/README.md):
| [Orders and positions](doc/orders-and-positions.md) | Live lines from your venue, tagged and draggable |
| [Event marks](doc/event-marks.md) | Earnings, dividends, splits and news under the candles |
| [Sessions and time zones](doc/sessions.md) | Day dividers, display time zone, extended hours, per-bar colour |
| [Driving the chart](doc/driving-the-chart.md) | `KChartController`, and the visible window |
| [Driving the chart](doc/driving-the-chart.md) | `KChartController`, the visible window, and a chart with no gestures |
| [Panes](doc/panes.md) | Stacking, resizing and reordering the indicator panes |
| [Bar replay](doc/bar-replay.md) | Rewind, then step or play the market forward |
| [Sizing](doc/sizing.md) | How the candle area and the panes divide the height |
Expand Down
8 changes: 5 additions & 3 deletions doc/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,9 @@ For installation, the feature list and support, see the
## Axes and reading the chart

- **[Price axis](price-axis.md)** — linear, logarithmic, percentage or indexed
to 100; inverting it, marking the window's high, low and average close, and
dragging the scale by hand.
to 100; inverting it, marking the window's high, low and average close,
dragging the scale by hand, locking it so scrolling cannot rescale it, and
holding a gutter back for its labels.
- **[The date axis](date-axis.md)** — round time values, the formats it picks
between, and taking it over with `dateFormatter`.
- **[The legend and the crosshair](legend-and-crosshair.md)** — the OHLC row
Expand Down Expand Up @@ -55,7 +56,8 @@ For installation, the feature list and support, see the
## Driving it from your code

- **[Driving the chart](driving-the-chart.md)** — `KChartController` for zoom,
scroll and a PNG of the chart, plus reading and setting the visible window.
scroll and a PNG of the chart, reading and setting the visible window, and
turning the chart's own gestures off for one that should sit still.
- **[Panes](panes.md)** — stacking, resizing and reordering the indicator panes.
- **[Bar replay](bar-replay.md)** — rewind to any candle and step or play the
market forward, with the indicators only knowing what has arrived.
Expand Down
4 changes: 4 additions & 0 deletions doc/bar-replay.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ the chart at a candle in the past: everything after it — the candles, the
indicators computed from them, the now-price line and the legend — is as it was
at that moment, so a setup can be studied without the answer already on screen.

![The replay running, a candle at a time](https://raw.githubusercontent.com/CtrlAltDevelop/ohlcv_chart/main/screenshots/bar-replay.gif)

```dart
final replay = ChartReplayController(interval: const Duration(milliseconds: 300));

Expand All @@ -31,6 +33,8 @@ candle having been picked first, and it gives up on its own at the newest one
drive a transport bar from. It is a `ChangeNotifier`, so those buttons rebuild
themselves; dispose it with the widget that owns it.

![The chart held at the 150th candle of 420, under a transport bar](https://raw.githubusercontent.com/CtrlAltDevelop/ohlcv_chart/main/screenshots/bar-replay.png)

Nothing is thrown away while a replay runs: the candle list is untouched and
the drawings stay where they were placed, including any on candles still to
arrive.
Expand Down
8 changes: 8 additions & 0 deletions doc/candlestick-chart.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# Candlestick chart

![Candles with moving averages, volume and MACD](https://raw.githubusercontent.com/CtrlAltDevelop/ohlcv_chart/main/screenshots/candles.png)

Feed it a `List<KLineEntity>`. Indicator values are computed in place by
`DataUtil.calculate` before the first paint, and again whenever new candles arrive:

Expand All @@ -26,6 +28,12 @@ KChartWidget(
`KLineEntity.fromJson` accepts the usual OHLCV shape (`open`, `high`, `low`,
`close`, `vol`, `time`/`id`), or build the entity directly.

`onLoadMore` fires when the scroll lands on an edge — `false` at the oldest
candle, `true` at the newest — once when it arrives rather than on every frame
the drag spends there, and again if the user comes away and goes back. Prepend
the older candles you fetch and hand the chart the longer list; it keeps its
place in the data, so the window does not jump.

---

[← All docs](README.md) · [Package README](../README.md)
4 changes: 3 additions & 1 deletion doc/date-axis.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ ends and the next begins. Labels that would crowd into each other are dropped
rather than printed over one another, and the boundaries follow the clock the
chart prints: a `timeZoneOffset` of half an hour still labels round local times.

![The axis the chart picks, and the same candles under a dateFormatter](https://raw.githubusercontent.com/CtrlAltDevelop/ohlcv_chart/main/screenshots/date-axis.png)

`ChartStyle.gridColumns` sets the density, read like `gridRows`. Formatting can
be taken over completely — `ChartStyle.dateTimeFormat` for a fixed pattern, or
`dateFormatter` for full control, which is handed each candle along with a flag
Expand All @@ -21,7 +23,7 @@ KChartWidget(
timeFrame: const Duration(minutes: 15),
dateFormatter: (candle, longForm) => DateFormat(
longForm ? 'EEE d MMM HH:mm' : 'HH:mm',
).format(DateTime.fromMillisecondsSinceEpoch(candle.time!)),
).format(candle.dateTime!),
xFrontPadding: 120,
);
```
Expand Down
71 changes: 70 additions & 1 deletion doc/driving-the-chart.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,73 @@ KChartWidget(
`indexRangeCovering` and `indexNearest` are exported for working out either
from a list of candles without a chart in hand.

## Turning the gestures off

Some charts are not meant to be navigated: an intraday session, a thumbnail in
a list, a figure in a report. `scrollEnabled` and `zoomEnabled` take the
chart's own gestures away.

```dart
KChartWidget(
sessionCandles,
ChartColors(),
isTrendLine: false,
watermarkAssetPath: 'assets/logo.svg',
timeFrame: const Duration(minutes: 5),
chartType: ChartType.area,
scrollEnabled: false,
zoomEnabled: false,
// 78 candles in a box about 400 wide: 400 / 78 ≈ 5
chartStyle: const ChartStyle(pointWidth: 5),
xFrontPadding: 0,
volHidden: true,
hideGrid: true,
showNowPrice: false,
showInfoDialog: false,
crosshairOnHover: false,
showContextMenu: false,
showScrollToNowButton: false,
priceScaleDrag: false,
);
```

With `scrollEnabled` off a drag neither slides the window nor flings it, and
[`onLoadMore`](candlestick-chart.md) is never asked for more candles — no edge
is ever reached to ask at. With `zoomEnabled` off a pinch does nothing, and the
zoom slider is left off too: that slider only ever appears on the web and on
desktop, standing in for the pinch those platforms do not have.

Turn the two off together. Zooming out makes the candles narrower, which leaves
the window room to scroll into, so a chart with only `scrollEnabled` off can be
pinched back into a scrollable one.

The two flags hold the *user* back and leave your own code alone, the way
`priceScaleDrag` does — so a chart nobody can drag can still be moved from a
toolbar, or fitted once at startup:

```dart
chart.fitAll(); // the whole history in the box
chart.goToIndex(0); // or somewhere particular
```

### Filling the width

`scrollEnabled: false` freezes the window wherever it happens to be, which is
usually at the newest candle with the rest off to the left. For a chart that
shows one fixed stretch, make the candles fit instead: `ChartStyle.pointWidth`
is the space each candle takes — 8 by default — so roughly the chart's width
divided by the number of candles puts the whole series on screen.

```dart
chartStyle: ChartStyle(pointWidth: width / candles.length),
```

Once the series fits there is nowhere to scroll to at all, flag or no flag —
the scroll clamps to zero. `xFrontPadding: 0` gives up the gap the chart
otherwise leaves to the right of the newest candle, so the candles reach the
edge. `fitAll()` is the other way there, and works on any width without the
arithmetic.

## Keeping charts in step

`ChartLink` holds several charts on the same window. Add each one's controller
Expand Down Expand Up @@ -156,7 +223,9 @@ chart.hideCrosshair(); // take it down

The price axis is readable and settable the same way — `priceZoom` and
`pricePan` for its stretch and shift, `setPriceZoom`, `setPricePan` and
`resetPriceScale` to move it.
`resetPriceScale` to move it. `resetPriceScale` is also what hands a
[locked axis](price-axis.md#keeping-it-still-while-the-chart-scrolls) back to the
chart, refitting it to the window and holding it there afresh.

`onCrosshairChanged` reports where it moved to, on the same terms as
`onVisibleRangeChanged`: after the frame that moved it, and only when the
Expand Down
2 changes: 2 additions & 0 deletions doc/legend-and-crosshair.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# The legend and the crosshair

![The OHLC legend above the chart, reading from the crosshair](https://raw.githubusercontent.com/CtrlAltDevelop/ohlcv_chart/main/screenshots/legend-and-crosshair.png)

With a mouse, the crosshair follows the pointer without waiting for a press —
that is `crosshairOnHover`, on by default and irrelevant to a touch screen, which
has nothing that hovers. The values then belong above the chart rather than in a
Expand Down
2 changes: 2 additions & 0 deletions doc/line-editor.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# Customising the line editor

![The line editor open on a selected line](https://raw.githubusercontent.com/CtrlAltDevelop/ohlcv_chart/main/screenshots/line-editor.png)

`DrawingStyle` decides what the toolbar offers, how it looks, and how close a tap
has to land to count. Everything is optional:

Expand Down
2 changes: 2 additions & 0 deletions doc/panes.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# Panes

![Three ATR panes stacked under the candles](https://raw.githubusercontent.com/CtrlAltDevelop/ohlcv_chart/main/screenshots/panes.png)

An indicator pane can be made taller by dragging its lower edge, and moved up or
down the stack by dragging its legend row:

Expand Down
Loading
Loading