- TypeScript 66.9%
- PLpgSQL 14.8%
- CSS 10.4%
- HTML 4.6%
- Shell 3.3%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
The page offers every pair that has candles behind it, which was the whole list until a product's contracts became pairs of their own: two hundred of them would be the menu, and nobody opens December 2019 gold by name. They carry their product in parent, so one condition leaves them out. The filter goes in AND over an OR, so the existing condition is now parenthesised: without that it would have offered the contracts that have ticks and hidden everything else. What belongs here is one series per product - the continuous one while there is nothing else, and the series stitched from the roll calendar once that exists. `ohlcdb pair list --contracts` is where a contract is visible, as it is for a pair that holds nothing. |
||
| build/alpine | ||
| public | ||
| src | ||
| .gitignore | ||
| api.sql | ||
| config.json | ||
| index.html | ||
| LICENSE | ||
| package-lock.json | ||
| package.json | ||
| postgrest.conf | ||
| README.md | ||
| THIRD-PARTY | ||
| tsconfig.json | ||
| vite.config.ts | ||
ohlcdb-web
A chart page for the candles ohlcdb collects: candlesticks, the trading sessions as boxes behind them, the previous day, week and month as lines, and a table of the newest sessions.
The page is static files. Its data comes from PostgREST over the api
schema that api.sql makes, and both the page and PostgREST are in this
package.
Requirements
- A database filled by ohlcdb, reachable from this host.
- A web server to publish
/usr/share/ohlcdb-weband to pass/apithrough to PostgREST under the same address as the page. Which web server is the host's business, not this package's.
Install
apk add ohlcdb-web
It brings its own PostgREST, creates the ohlcdb-web user, installs the
page in /usr/share/ohlcdb-web and two files in /etc/ohlcdb-web.
Setup
-
Make the API in the database, as a PostgreSQL superuser, after ohlcdb's
schema.sqlandlevels.sql, then give the web role a password:psql -d ohlcdb -f /usr/share/ohlcdb-web/api.sql psql -d ohlcdb -c '\password ohlcdb_web' -
Put that password in the
PASSWORD=line of/etc/ohlcdb-web/postgrest.conf, and the database's address in itsdb-uri.A
NAME=valueline there is not one of PostgREST's settings: the service expands$NAMEin the rest of the file and starts PostgREST on the result, written to/run/ohlcdb-web/postgrest.conf. So the password is in the one file that already holds secrets, and not on a command line or in the environment, wherepsand a strayenvwould show it.The value is whatever follows the
=to the end of the line, quotes and spaces and$included, so a password needs no escaping of its own. Thedb-urinames keywords and values rather than a URI for the same reason: no percent-encoding. What libpq does ask is that a'or a\inside the password be written\'and\\.The service will not start while a variable is empty, or while the file uses one that nothing sets: it says which, by name. Comments are left alone, so a
$NAMEwritten in one is only text. -
Start it:
rc-service ohlcdb-web startcurl -s localhost:3000/pairsthen answers with the pairs. -
Publish the page, and let
/apireach127.0.0.1:3000under the same address. Until that is arranged, the page can talk to PostgREST directly: setserver-hostinpostgrest.confto an address the browser can reach, andapiinconfig.jsontohttp://that-address:3000. PostgREST answers a request from another origin on its own.
Configuration
/etc/ohlcdb-web/config.json holds what the page starts with. Every key is
optional: the build carries a copy of these defaults, so a file left behind
by an upgrade, or a broken one, costs at most the settings it names. A
value that fails its check is reported in the browser's console and
ignored.
| Key | What it sets |
|---|---|
api |
where the API is; /api for a web server in front, a full address otherwise |
feed |
the source to open: broker, environment and account type joined by ` |
pair |
the pair to open, by the broker's name for it |
period |
the candle length to open |
barType |
time, tick, renko, range or heikin: what ends a bar |
tickCounts |
the ticks-per-bar the row offers |
tickCount |
the count to open with |
renkoSizes |
the brick sizes the row offers, in pips; each says what it is worth on the pair in view in its tooltip |
rangeSizes |
the range sizes it offers, in pips |
renkoSize |
the brick to open with |
rangeSize |
the range to open with |
barWindowDays |
how far back a renko or range load computes, from the bar asked for and not from today; a window that comes back nearly empty - a large size crosses its grid rarely - is asked again twice as wide, up to eight times that |
timeZone |
the zone the times are shown in |
timeZones |
the zones the selector offers |
candles |
green-red, hollow or blue-red |
theme |
auto, light or dark |
sessions |
which sessions start shown, by name: {"asia": true, ...} |
levels |
which of day, week and month start shown |
sessionColors |
a light and a dark colour per session name |
lots |
how many lots the ruler reckons a move in money for |
scrollMargin |
how far the chart can be dragged past its data, as a fraction of the width; 0.5 stops with the outermost bar in the middle |
refreshSeconds |
how often the page asks for what is new |
candlesPerLoad |
how many candles a load brings |
chartHeight |
how tall the chart starts, in pixels; 0 leaves it to the stylesheet |
What is chosen on the page outlives a reload in that browser and wins over the file; the "Reset" button clears those choices and goes back to it.
The default session colours pass the colour-blindness and contrast checks in both themes. Colours put in the file do not: they are used as written.
The page
- Controls, in three rows, with no captions: every control shows the
value it holds, and what it is for is in its tooltip. Anything written
there can be clicked.
- the pair, the time zone and the candle style; in the middle of the page the source behind the prices - the broker, whether the account is live or demo, its type; and at the far end the two arrows, the theme and reset, as symbols;
- what ends a bar - time, tick, renko, range or Heikin Ashi - and beside it what that kind is measured in: the periods for time and Heikin Ashi, the ticks per bar, or the size in pips. One block, the current one filled; and at the far end of that row the drawing tools;
- a chip per session, in the colour of its boxes, and one per level, showing the line it is drawn with. A chip is the key and the switch at once, so each session can be turned off on its own; and at the end of that row, above the price scale, the padlock and full screen.
- The menu names the instrument, not the symbol it was sold under:
XAUUSDfor a broker'sXAUUSD.rawandGCfor Databento'sGC.v.0, with the whole name in the tooltip. What follows the first dot belongs to the seller - an account type, or how a continuous contract rolls - and the source is named beside the menu anyway. Two pairs of one feed that would read alike keep their own names. - The pairs offered are those with something stored behind them: a pair
whose collection never ran has no kind of bar to draw and is left out of
the menu rather than shown and then drawn empty, which is the rule the
kinds of bar follow too.
ohlcdb pair liston the collector is where such a pair is still visible. - The source: with more than one, the broker plate opens a layer over the page listing them - broker, live or demo, account type - in alphabetical order, so the two accounts of one broker stand together. Choosing one narrows the pair menu to that source's pairs and keeps the instrument by name where it exists in both. With a single source there is nothing to choose and the plate is a plate.
- Candles, a thousand at a time, more as the chart scrolls back.
- Renko and range bars, at the size chosen in pips, built by the server from the M1 candles rather than stored: no size could be, since the reader picks it. The hourly candles are asked first which hours could hold a bar at that size, and only those have their minutes read, so a coarse size over years costs what a fine one over days does. A brick is a move of one size across a grid of prices; a range bar closes when it has travelled one size between its own high and low. Both are drawn on the same time axis as the candles, which is why they sit unevenly across it: a minute can hold twenty of them, and a quiet hour none.
- Tick bars, of as many ticks as chosen, counted the way cTrader counts them: the bid and the ask are one stream of events and the price at each is the bid, which is the rule that rebuilds its candles exactly. So a bar of 500 here is a bar of 500 there. They need the ticks ohlcdb downloads, and only reach as far back as those do; a stretch where no bid tick was downloaded draws nothing.
- Heikin Ashi, which is not a bar of its own but a reading of the candles - the close is the bar's average price and the open is where the last reading stood - so it keeps the periods, and the sessions and levels with them. Its highs and lows are not the real ones, so the previous day's and week's lines will not touch the wicks.
- Sessions as boxes from a session's low to its high, across its own hours on any period, dashed while the session is still under way. The sessions are whatever the database holds: one added there gets its chip without the page being rebuilt.
- Levels: the previous day, week and month, each with its own line style and its label on the price scale.
- A readout of open, high, low and close under the pointer, and a table of the newest sessions, which reads without hovering.
- Drawing, by hand: a horizontal line at a price, a vertical one at a
moment, or a line between two points. One button carries all three:
pressing it again moves to the next and, after the third, puts it down.
A tool is armed until it has been used, until
Esc, or until something outside the chart is clicked.- Click a drawing to take it: it thickens and shows its handles. Drag a
handle to move that end, or the line itself to move the whole of it.
Deleteremoves it, as does the bin; with nothing taken the bin offers to remove every drawing of the pair, and asks first. Ctrl+Zsteps back through the last twenty changes, which is what a line moved by accident needs.- A point takes the time of the candle under it, and its price when the
pointer is within a few pixels of that candle's open, high, low or
close, so a line off a high starts on the high.
Altwhile placing or dragging leaves the point where the pointer is. - The swatch beside the tools opens the eight colours; picking one sets what the next line is drawn in, and recolours the line in hand if there is one. A horizontal line writes its price on the price scale and a vertical one its time on the time scale, in its own colour, the way the day's and week's levels do.
- Drawings belong to the pair, not to the period: they are kept as a time and a price, so they hold their place across periods, across renko, range and tick bars, and across a reload. They live in the browser, and the two arrows beside the theme write them to a JSON file and read one back - the pairs a file names replace what is held for them, the rest are left alone.
- Click a drawing to take it: it thickens and shows its handles. Drag a
handle to move that end, or the line itself to move the whole of it.
- The ruler measures a move: press it and drag, or hold
Shiftand drag without pressing anything. It reads the move in pips, in the quote currency, as a percentage and - when the broker has said how many units a lot holds - in money for the lots named bylots; below that, how many bars were crossed and how long it took. The reading stays until the next thing is done, and is never stored: it is a question, not a drawing. - The arrows jump to the newest candle, or to the oldest one held - which is a thousand back, not the start of the history; arriving there asks for more, as scrolling there does.
- The padlock is the state of the price scale rather than a mode of its
own: shut, the prices follow the candles and the chart cannot be dragged
up or down; dragging the price scale unfits them and the padlock opens by
itself; shutting it again, with the button or
L, fits the candles back. - Full screen on the chart with the button or
F;Escleaves it. There the controls are out of sight, so the chart shows the session colours as a key in its corner, which it has no reason to elsewhere. - A limit to the dragging: the chart stops with the newest or oldest
bar in the middle of the screen rather than scrolling off into the empty
space,
scrollMarginbeing how much of that space it allows. - A height of its own: the strip under the time axis drags the chart
taller or shorter, and the height stays for that browser. With the strip
focused the arrows move it by 16 pixels and
PageUp/PageDownby 64;Home, or a double click, goes back to the heightchartHeightasks for.
Times are shown in the chosen zone but stay UTC inside the chart: shifting them would repeat an hour when daylight saving ends, which the chart refuses.
What to know about the derived bars, since nothing on screen says it:
- The size of a brick or a range is in pips, and what a pip is worth comes from the broker: ohlcdb stores the pip position the symbol declares. Until it has, the page takes a pip to be ten of the last digit, which is the usual convention and right for gold and for the rates.
- They come from the M1 candles, and within a minute the order of the high and the low is unknown. It is taken from the close, the usual reading: a minute that closed up is assumed to have made its low first. The ticks would settle it for the days that have them.
- A brick's closes sit on a grid of multiples of its size, so asking for more history does not redraw what is already on screen. The cost is that a reversal takes one brick and not the two of the classic rule, so price hovering over a grid line alternates. A range bar chain has no grid to hold on to, so scrolling far back can shift where its bars begin.
- A brick, a range bar and a tick bar exist once they are complete, so none of them has a bar under way: the chart's newest bar stands still between updates, where a candle grows. Heikin Ashi has one, being candles.
- A page of tick bars is bounded by the ticks it reads and not by time, so it costs the same at any size, and each page counts from its own end: the bars near a seam can hold different events than they would have in one long run.
- Volume is left out of them on purpose. What ohlcdb stores from cTrader is a count of price updates, and splitting a minute's count between the bars it spans would be inventing a number.
Development
The build needs Node; running the page needs nothing. In a container or
chroot with nodejs and npm:
npm ci
OHLCDB_API=http://localhost:3000 npm run dev
Vite serves the page and forwards /api to the PostgREST named by
OHLCDB_API, so a PostgREST started by hand against the database is enough
to work on the page. Started by hand it wants the expanded settings, which
the renderer writes anywhere:
/usr/lib/ohlcdb-web/render /etc/ohlcdb-web/postgrest.conf /tmp/postgrest.conf
npm run build writes dist, which is what the package installs, and
checks the types first.
License
GPL-3.0; see LICENSE.
The page is drawn with TradingView Lightweight Charts, under Apache-2.0,
whose code the build folds into the files in /usr/share/ohlcdb-web. Its
attribution notice is in THIRD-PARTY, installed beside them, since
minifying drops the one the library's own source carries and the page turns
the library's attribution logo off.