better fib left start
This commit is contained in:
parent
5f39144dc8
commit
1c3a852a03
2 changed files with 135 additions and 29 deletions
|
|
@ -1216,3 +1216,6 @@ shares numbering, hide, delete and colour with other drawings, but it is
|
|||
not a confluence level. The chart draws native price lines at 0, 23.6,
|
||||
38.2, 50, 61.8, 78.6 and 100 between the two prices. 0% is the first
|
||||
click.
|
||||
|
||||
Levels no longer span the full chart: they start at the first click and run
|
||||
to the right, with a vertical rail at the origin so the swing is obvious.
|
||||
|
|
|
|||
161
static/chart.js
161
static/chart.js
|
|
@ -72,6 +72,120 @@ class RthLinesPrimitive {
|
|||
}
|
||||
}
|
||||
|
||||
class FibLevelsPrimitive {
|
||||
constructor(locateTime) {
|
||||
this._locate = locateTime;
|
||||
this._fibs = [];
|
||||
this._series = null;
|
||||
this._requestUpdate = null;
|
||||
this._items = [];
|
||||
this._view = {
|
||||
update: () => this._sync(),
|
||||
renderer: () => this._renderer,
|
||||
};
|
||||
this._renderer = { draw: target => this._draw(target) };
|
||||
}
|
||||
|
||||
attached(param) {
|
||||
this._series = param.series;
|
||||
this._requestUpdate = param.requestUpdate;
|
||||
}
|
||||
|
||||
detached() {
|
||||
this._series = null;
|
||||
this._requestUpdate = null;
|
||||
}
|
||||
|
||||
setFibs(fibs) {
|
||||
this._fibs = fibs || [];
|
||||
if (this._requestUpdate) this._requestUpdate();
|
||||
}
|
||||
|
||||
updateAllViews() {
|
||||
this._view.update();
|
||||
}
|
||||
|
||||
paneViews() {
|
||||
return [this._view];
|
||||
}
|
||||
|
||||
_sync() {
|
||||
const items = [];
|
||||
if (!this._series) {
|
||||
this._items = [];
|
||||
return;
|
||||
}
|
||||
for (const fib of this._fibs) {
|
||||
if (fib.hidden) continue;
|
||||
const x0 = this._locate(fib.anchor_t);
|
||||
const y0 = this._series.priceToCoordinate(Number(fib.anchor_p));
|
||||
const endP = Number(fib.anchor_p) + Number(fib.slope || 0)
|
||||
* (Number(fib.last_t) - Number(fib.anchor_t));
|
||||
const y1 = this._series.priceToCoordinate(endP);
|
||||
const width = Math.max(1, Number(fib.line_width) || 2);
|
||||
const color = fib.color || '#c8992f';
|
||||
if (x0 != null && y0 != null && y1 != null) {
|
||||
items.push({
|
||||
kind: 'rail', x: x0, y0, y1, color, width,
|
||||
});
|
||||
}
|
||||
for (const level of ConfluenceChart.fibPrices(fib)) {
|
||||
const y = this._series.priceToCoordinate(level.price);
|
||||
if (y == null) continue;
|
||||
const major = level.r === 0 || level.r === 0.5 || level.r === 0.618 || level.r === 1;
|
||||
items.push({
|
||||
kind: 'level',
|
||||
x0,
|
||||
y,
|
||||
color,
|
||||
width,
|
||||
dashed: !major,
|
||||
label: `${level.label} ${level.price.toFixed(2)}`,
|
||||
});
|
||||
}
|
||||
}
|
||||
this._items = items;
|
||||
}
|
||||
|
||||
_draw(target) {
|
||||
if (!this._items.length) return;
|
||||
target.useMediaCoordinateSpace(scope => {
|
||||
const ctx = scope.context;
|
||||
const right = scope.mediaSize.width;
|
||||
ctx.save();
|
||||
ctx.font = '10px "IBM Plex Mono", ui-monospace, monospace';
|
||||
for (const item of this._items) {
|
||||
if (item.kind === 'rail') {
|
||||
ctx.strokeStyle = item.color;
|
||||
ctx.lineWidth = item.width;
|
||||
ctx.setLineDash([]);
|
||||
ctx.globalAlpha = 0.85;
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(item.x + 0.5, item.y0);
|
||||
ctx.lineTo(item.x + 0.5, item.y1);
|
||||
ctx.stroke();
|
||||
continue;
|
||||
}
|
||||
const start = item.x0 == null ? 0 : Math.max(0, item.x0);
|
||||
if (start > right) continue;
|
||||
ctx.strokeStyle = item.color;
|
||||
ctx.fillStyle = item.color;
|
||||
ctx.lineWidth = item.width;
|
||||
ctx.setLineDash(item.dashed ? [5, 4] : []);
|
||||
ctx.globalAlpha = 0.9;
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(start, item.y + 0.5);
|
||||
ctx.lineTo(right, item.y + 0.5);
|
||||
ctx.stroke();
|
||||
ctx.setLineDash([]);
|
||||
ctx.globalAlpha = 1;
|
||||
ctx.fillText(item.label, right - ctx.measureText(item.label).width - 6, item.y - 4);
|
||||
}
|
||||
ctx.restore();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
class ConfluenceChart {
|
||||
constructor() {
|
||||
this.chart = null;
|
||||
|
|
@ -135,7 +249,7 @@ class ConfluenceChart {
|
|||
this.rthPrimitive = null;
|
||||
this.rthEnabled = true;
|
||||
this.fibs = [];
|
||||
this.fibLines = new Map();
|
||||
this.fibPrimitive = null;
|
||||
this.fibPreview = null;
|
||||
this.futureSpace = null;
|
||||
this.snapDot = null;
|
||||
|
|
@ -327,6 +441,8 @@ class ConfluenceChart {
|
|||
// something to label and so timeToCoordinate answers out there.
|
||||
this.rthPrimitive = new RthLinesPrimitive();
|
||||
this.candles.attachPrimitive(this.rthPrimitive);
|
||||
this.fibPrimitive = new FibLevelsPrimitive(time => this.coordinateAtTime(time));
|
||||
this.candles.attachPrimitive(this.fibPrimitive);
|
||||
this.futureSpace = this.chart.addSeries(LightweightCharts.LineSeries, {
|
||||
priceScaleId: 'right',
|
||||
color: 'rgba(0,0,0,0)',
|
||||
|
|
@ -706,33 +822,7 @@ class ConfluenceChart {
|
|||
}
|
||||
|
||||
syncFibLines() {
|
||||
if (!this.candles) return;
|
||||
const wanted = new Set();
|
||||
for (const fib of this.fibs) {
|
||||
if (fib.hidden) continue;
|
||||
for (const level of ConfluenceChart.fibPrices(fib)) {
|
||||
const id = `${fib.id}:${level.r}`;
|
||||
wanted.add(id);
|
||||
const width = Math.max(1, Number(fib.line_width) || 2);
|
||||
const major = level.r === 0 || level.r === 0.5 || level.r === 0.618 || level.r === 1;
|
||||
const options = {
|
||||
price: level.price,
|
||||
color: fib.color || '#c8992f',
|
||||
lineWidth: width,
|
||||
lineStyle: major ? LightweightCharts.LineStyle.Solid : LightweightCharts.LineStyle.Dashed,
|
||||
axisLabelVisible: true,
|
||||
title: `${level.label} ${level.price.toFixed(2)}`,
|
||||
};
|
||||
const existing = this.fibLines.get(id);
|
||||
if (existing) existing.applyOptions(options);
|
||||
else this.fibLines.set(id, this.candles.createPriceLine(options));
|
||||
}
|
||||
}
|
||||
for (const [id, line] of this.fibLines) {
|
||||
if (wanted.has(id)) continue;
|
||||
this.candles.removePriceLine(line);
|
||||
this.fibLines.delete(id);
|
||||
}
|
||||
if (this.fibPrimitive) this.fibPrimitive.setFibs(this.fibs.filter(fib => !fib.hidden));
|
||||
}
|
||||
|
||||
setComments(comments) {
|
||||
|
|
@ -1946,11 +2036,24 @@ class ConfluenceChart {
|
|||
this.fibPreview.removeAttribute('hidden');
|
||||
this.fibPreview.replaceChildren();
|
||||
const width = this.overlayLayer?.clientWidth || this.chartEl.clientWidth;
|
||||
const originX = start.t == null ? 0 : (this.coordinateAtTime(start.t) ?? 0);
|
||||
const yStart = this.candles.priceToCoordinate(start.p);
|
||||
const yEnd = this.candles.priceToCoordinate(end.p);
|
||||
if (yStart != null && yEnd != null) {
|
||||
const rail = document.createElementNS('http://www.w3.org/2000/svg', 'line');
|
||||
rail.setAttribute('x1', originX);
|
||||
rail.setAttribute('x2', originX);
|
||||
rail.setAttribute('y1', yStart);
|
||||
rail.setAttribute('y2', yEnd);
|
||||
rail.setAttribute('stroke', '#c8992f');
|
||||
rail.setAttribute('stroke-width', 2);
|
||||
this.fibPreview.appendChild(rail);
|
||||
}
|
||||
for (const item of ConfluenceChart.FIB_RATIOS) {
|
||||
const y = this.candles.priceToCoordinate(start.p + (end.p - start.p) * item.r);
|
||||
if (y == null) continue;
|
||||
const line = document.createElementNS('http://www.w3.org/2000/svg', 'line');
|
||||
line.setAttribute('x1', 0);
|
||||
line.setAttribute('x1', originX);
|
||||
line.setAttribute('x2', width);
|
||||
line.setAttribute('y1', y);
|
||||
line.setAttribute('y2', y);
|
||||
|
|
|
|||
Loading…
Reference in a new issue