lit-litelements 2.3.7 → 2.3.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/component/candlestickPatterns.d.ts +1 -0
- package/dist/component/candlestickPatterns.js +7 -0
- package/dist/component/chart.lit.d.ts +69 -0
- package/dist/component/chart.lit.js +531 -0
- package/dist/component/spinner.lit.d.ts +16 -0
- package/dist/component/spinner.lit.js +56 -0
- package/dist/main.d.ts +3 -0
- package/dist/main.js +549 -0
- package/dist/styles/spinner-styles.d.ts +14 -0
- package/dist/styles/spinner-styles.js +493 -0
- package/package.json +1 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function isDoji(d: any): boolean;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
// --- Candlestick Pattern Detection Helpers ---
|
|
2
|
+
export function isDoji(d) {
|
|
3
|
+
// const body = Math.abs(d.close - d.open);
|
|
4
|
+
// const range = d.high - d.low;
|
|
5
|
+
// return body / range < 0.1; // very small body relative to range
|
|
6
|
+
return (d === null || d === void 0 ? void 0 : d.close) === (d === null || d === void 0 ? void 0 : d.open);
|
|
7
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { LitElement } from "lit";
|
|
2
|
+
import Chart from "chart.js/auto";
|
|
3
|
+
interface StockData {
|
|
4
|
+
volume?: number;
|
|
5
|
+
open?: number;
|
|
6
|
+
high?: number;
|
|
7
|
+
low?: number;
|
|
8
|
+
date: string;
|
|
9
|
+
close: number;
|
|
10
|
+
MA20?: number;
|
|
11
|
+
MA5?: number;
|
|
12
|
+
MA9?: number;
|
|
13
|
+
MA15?: number;
|
|
14
|
+
MA10?: number;
|
|
15
|
+
MA50?: number;
|
|
16
|
+
MA200?: number;
|
|
17
|
+
MA100?: number;
|
|
18
|
+
RSI?: number;
|
|
19
|
+
MACDLine?: number;
|
|
20
|
+
SignalLine?: number;
|
|
21
|
+
divergence?: number;
|
|
22
|
+
StochRSI_K?: number;
|
|
23
|
+
StochRSI_D?: number;
|
|
24
|
+
angleMACDsignal?: number | null;
|
|
25
|
+
}
|
|
26
|
+
declare class ChartElement extends LitElement {
|
|
27
|
+
stockData: StockData[];
|
|
28
|
+
zoomEnabled: boolean;
|
|
29
|
+
chart: Chart | null;
|
|
30
|
+
firstUpdated(): void;
|
|
31
|
+
_resetZoom(): void;
|
|
32
|
+
_Zoomenalbe(): void;
|
|
33
|
+
willUpdate(changed: Map<string, any>): Promise<void>;
|
|
34
|
+
customLinePlugin: {
|
|
35
|
+
id: string;
|
|
36
|
+
afterDraw: (chart: any) => void;
|
|
37
|
+
};
|
|
38
|
+
volume_Doji_915_Plugin: {
|
|
39
|
+
id: string;
|
|
40
|
+
afterDraw: (chart: any) => void;
|
|
41
|
+
};
|
|
42
|
+
_getChartData(): {
|
|
43
|
+
dates: string[];
|
|
44
|
+
closePrices: number[];
|
|
45
|
+
rsi: number[];
|
|
46
|
+
stochRSI_K: number[];
|
|
47
|
+
stochRSI_D: number[];
|
|
48
|
+
angleMACDsignal: number[];
|
|
49
|
+
ma5: number[];
|
|
50
|
+
ma10: number[];
|
|
51
|
+
ma20: number[];
|
|
52
|
+
ma50: number[];
|
|
53
|
+
ma100: number[];
|
|
54
|
+
ma200: number[];
|
|
55
|
+
MACDLine: number[];
|
|
56
|
+
SignalLine: number[];
|
|
57
|
+
divergence: number[];
|
|
58
|
+
volumes: number[];
|
|
59
|
+
};
|
|
60
|
+
_updateChart(): void;
|
|
61
|
+
_createChart(): void;
|
|
62
|
+
render(): import("lit-html").TemplateResult<1>;
|
|
63
|
+
}
|
|
64
|
+
declare global {
|
|
65
|
+
interface HTMLElementTagNameMap {
|
|
66
|
+
"stock-chart-display": ChartElement;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
export {};
|
|
@@ -0,0 +1,531 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
8
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
9
|
+
};
|
|
10
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
11
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
12
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
13
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
14
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
15
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
16
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
17
|
+
});
|
|
18
|
+
};
|
|
19
|
+
import { LitElement, html } from "lit";
|
|
20
|
+
import Chart from "chart.js/auto";
|
|
21
|
+
import zoomPlugin from "chartjs-plugin-zoom";
|
|
22
|
+
import { customElement, property } from "lit/decorators.js";
|
|
23
|
+
import * as Patterns from "./candlestickPatterns";
|
|
24
|
+
let ChartElement = class ChartElement extends LitElement {
|
|
25
|
+
constructor() {
|
|
26
|
+
super(...arguments);
|
|
27
|
+
this.stockData = [];
|
|
28
|
+
this.zoomEnabled = false;
|
|
29
|
+
this.chart = null;
|
|
30
|
+
// --------------------------------------------------------------------
|
|
31
|
+
// CUSTOM SIGNAL PLUGIN (RSI + MA200 + SL/TP)
|
|
32
|
+
// --------------------------------------------------------------------
|
|
33
|
+
this.customLinePlugin = {
|
|
34
|
+
id: "customLinePlugin",
|
|
35
|
+
afterDraw: (chart) => {
|
|
36
|
+
const ctx = chart.ctx;
|
|
37
|
+
const { chartArea, scales, data } = chart;
|
|
38
|
+
const rsiDataset = chart.data.datasets.find((ds) => ds.label === "RSI");
|
|
39
|
+
const closeDataset = data.datasets.find((ds) => ds.label === "Close Price");
|
|
40
|
+
const ma200Dataset = data.datasets.find((ds) => ds.label === "MA200");
|
|
41
|
+
if (!rsiDataset || !closeDataset || !ma200Dataset)
|
|
42
|
+
return;
|
|
43
|
+
const rsiData = rsiDataset.data;
|
|
44
|
+
const closeData = closeDataset.data;
|
|
45
|
+
const ma200Data = ma200Dataset.data;
|
|
46
|
+
const RISK_PERCENT = 0.01;
|
|
47
|
+
const REWARD_RATIO = 2;
|
|
48
|
+
for (let i = 1; i < closeData.length; i++) {
|
|
49
|
+
//base on rsi
|
|
50
|
+
const rsiValue = rsiData[i];
|
|
51
|
+
if (rsiValue !== null && rsiValue < 30) {
|
|
52
|
+
const x = scales.x.getPixelForValue(i);
|
|
53
|
+
const y = scales.yRSI.getPixelForValue(rsiValue);
|
|
54
|
+
ctx.beginPath();
|
|
55
|
+
ctx.arc(x, y, 4, 0, 2 * Math.PI);
|
|
56
|
+
ctx.fillStyle = "green";
|
|
57
|
+
ctx.fill();
|
|
58
|
+
ctx.fillStyle = "green";
|
|
59
|
+
ctx.font = "bold 12px sans-serif";
|
|
60
|
+
ctx.fillText("Buy_rsi", x + 6, y - 6);
|
|
61
|
+
}
|
|
62
|
+
else if (rsiValue !== null && rsiValue > 70) {
|
|
63
|
+
const x = scales.x.getPixelForValue(i);
|
|
64
|
+
const y = scales.yRSI.getPixelForValue(rsiValue);
|
|
65
|
+
ctx.beginPath();
|
|
66
|
+
ctx.arc(x, y, 4, 0, 2 * Math.PI);
|
|
67
|
+
ctx.fillStyle = "red";
|
|
68
|
+
ctx.fill();
|
|
69
|
+
ctx.fillStyle = "red";
|
|
70
|
+
ctx.font = "bold 12px sans-serif";
|
|
71
|
+
ctx.fillText("Sell_rsi", x + 6, y - 6);
|
|
72
|
+
}
|
|
73
|
+
// Add MA200 bullish/bearish crossover signals
|
|
74
|
+
const prevClose = closeData[i - 1];
|
|
75
|
+
const prevMA200 = ma200Data[i - 1];
|
|
76
|
+
const currClose = closeData[i];
|
|
77
|
+
const currMA200 = ma200Data[i];
|
|
78
|
+
if (prevClose == null ||
|
|
79
|
+
prevMA200 == null ||
|
|
80
|
+
currClose == null ||
|
|
81
|
+
currMA200 == null)
|
|
82
|
+
continue;
|
|
83
|
+
const x = scales.x.getPixelForValue(i);
|
|
84
|
+
const y = scales.yPrice.getPixelForValue(currClose);
|
|
85
|
+
if (currClose > currMA200 && prevClose < prevMA200) {
|
|
86
|
+
ctx.beginPath();
|
|
87
|
+
ctx.arc(x, y, 4, 0, 2 * Math.PI);
|
|
88
|
+
ctx.fillStyle = "green";
|
|
89
|
+
ctx.fill();
|
|
90
|
+
ctx.fillStyle = "green";
|
|
91
|
+
ctx.font = "bold 12px sans-serif";
|
|
92
|
+
ctx.fillText("Bull", x + 6, y - 6);
|
|
93
|
+
const stop = currClose * (1 - RISK_PERCENT);
|
|
94
|
+
const target = currClose + (currClose - stop) * REWARD_RATIO;
|
|
95
|
+
const yStop = scales.yPrice.getPixelForValue(stop);
|
|
96
|
+
const yTarget = scales.yPrice.getPixelForValue(target);
|
|
97
|
+
// Stop-Loss (SL) line
|
|
98
|
+
ctx.fillStyle = "red";
|
|
99
|
+
ctx.fillRect(x, yStop, 100, 1); // 100px wide, 1px high
|
|
100
|
+
ctx.font = "bold 12px sans-serif";
|
|
101
|
+
ctx.fillText("SL " + stop.toFixed(2), x + 12, yStop - 4);
|
|
102
|
+
// Take-Profit (TP) line
|
|
103
|
+
ctx.fillStyle = "green";
|
|
104
|
+
ctx.fillRect(x, yTarget, 100, 1); // 100px wide, 1px high
|
|
105
|
+
ctx.fillText("TP " + target.toFixed(2), x + 12, yTarget - 4);
|
|
106
|
+
}
|
|
107
|
+
else if (currClose < currMA200 && prevClose > prevMA200) {
|
|
108
|
+
ctx.beginPath();
|
|
109
|
+
ctx.arc(x, y, 4, 0, 2 * Math.PI);
|
|
110
|
+
ctx.fillStyle = "purple";
|
|
111
|
+
ctx.fill();
|
|
112
|
+
ctx.fillStyle = "purple";
|
|
113
|
+
ctx.font = "bold 12px sans-serif";
|
|
114
|
+
ctx.fillText("Bear", x + 6, y - 6);
|
|
115
|
+
const stop = currClose * (1 + RISK_PERCENT);
|
|
116
|
+
const target = currClose - (stop - currClose) * REWARD_RATIO;
|
|
117
|
+
}
|
|
118
|
+
ctx.setLineDash([]);
|
|
119
|
+
}
|
|
120
|
+
},
|
|
121
|
+
};
|
|
122
|
+
// buy sell buy volume
|
|
123
|
+
this.volume_Doji_915_Plugin = {
|
|
124
|
+
id: "volumeSignalPlugin",
|
|
125
|
+
afterDraw: (chart) => {
|
|
126
|
+
const ctx = chart.ctx;
|
|
127
|
+
const { scales, data } = chart;
|
|
128
|
+
const closeDataset = data.datasets.find((ds) => ds.label === "Close Price");
|
|
129
|
+
const volumeDataset = data.datasets.find((ds) => ds.label === "Volume");
|
|
130
|
+
if (!closeDataset || !volumeDataset)
|
|
131
|
+
return;
|
|
132
|
+
const closeData = closeDataset.data;
|
|
133
|
+
const volumeData = volumeDataset.data;
|
|
134
|
+
for (let i = 5; i < closeData.length; i++) {
|
|
135
|
+
const candle = this.stockData[i];
|
|
136
|
+
const divergence = candle === null || candle === void 0 ? void 0 : candle.divergence;
|
|
137
|
+
const isDoji = Patterns.isDoji(candle);
|
|
138
|
+
const isBull = (candle === null || candle === void 0 ? void 0 : candle.close) > (candle === null || candle === void 0 ? void 0 : candle.open);
|
|
139
|
+
const isBear = (candle === null || candle === void 0 ? void 0 : candle.close) < (candle === null || candle === void 0 ? void 0 : candle.open);
|
|
140
|
+
const x = scales.x.getPixelForValue(i);
|
|
141
|
+
const y = scales.yPrice.getPixelForValue(candle === null || candle === void 0 ? void 0 : candle.close);
|
|
142
|
+
if (!candle)
|
|
143
|
+
continue;
|
|
144
|
+
const currentVolume = volumeData[i];
|
|
145
|
+
if (currentVolume == null)
|
|
146
|
+
continue;
|
|
147
|
+
// Average of previous 5 bars
|
|
148
|
+
const avgVolume =
|
|
149
|
+
// volumeData[i - 1]
|
|
150
|
+
(volumeData[i - 1] +
|
|
151
|
+
volumeData[i - 2] +
|
|
152
|
+
volumeData[i - 3] +
|
|
153
|
+
volumeData[i - 4] +
|
|
154
|
+
volumeData[i - 5]) / 5;
|
|
155
|
+
if (isDoji) {
|
|
156
|
+
ctx.beginPath();
|
|
157
|
+
ctx.arc(x, y, 4, 0, 2 * Math.PI);
|
|
158
|
+
ctx.fillStyle = "red";
|
|
159
|
+
ctx.fill();
|
|
160
|
+
ctx.fillStyle = "gray";
|
|
161
|
+
ctx.font = "10px sans-serif";
|
|
162
|
+
ctx.fillText("DOJI-REVERS", x + 6, y - 6);
|
|
163
|
+
}
|
|
164
|
+
if (currentVolume <= avgVolume * 1.5)
|
|
165
|
+
continue;
|
|
166
|
+
if (isBull &&
|
|
167
|
+
divergence != null &&
|
|
168
|
+
divergence > 0) {
|
|
169
|
+
ctx.beginPath();
|
|
170
|
+
ctx.arc(x, y, 4, 0, 2 * Math.PI);
|
|
171
|
+
ctx.fillStyle = "green";
|
|
172
|
+
ctx.fill();
|
|
173
|
+
ctx.fillStyle = "green";
|
|
174
|
+
ctx.font = "bold 12px sans-serif";
|
|
175
|
+
ctx.fillText("BUY_Vol", x + 6, y - 6);
|
|
176
|
+
}
|
|
177
|
+
if (isBear && divergence != null &&
|
|
178
|
+
divergence < 0) {
|
|
179
|
+
ctx.beginPath();
|
|
180
|
+
ctx.arc(x, y, 4, 0, 2 * Math.PI);
|
|
181
|
+
ctx.fillStyle = "red";
|
|
182
|
+
ctx.fill();
|
|
183
|
+
ctx.fillStyle = "red";
|
|
184
|
+
ctx.font = "bold 12px sans-serif";
|
|
185
|
+
ctx.fillText("SELL_Vol", x + 6, y - 6);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
},
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
firstUpdated() {
|
|
192
|
+
this._createChart();
|
|
193
|
+
}
|
|
194
|
+
_resetZoom() {
|
|
195
|
+
var _a;
|
|
196
|
+
(_a = this.chart) === null || _a === void 0 ? void 0 : _a.resetZoom();
|
|
197
|
+
}
|
|
198
|
+
_Zoomenalbe() {
|
|
199
|
+
var _a, _b, _c, _d;
|
|
200
|
+
if (!((_b = (_a = this.chart) === null || _a === void 0 ? void 0 : _a.options.plugins) === null || _b === void 0 ? void 0 : _b.zoom))
|
|
201
|
+
return;
|
|
202
|
+
const zoomOptions = this.chart.options.plugins.zoom;
|
|
203
|
+
const newState = !this.zoomEnabled;
|
|
204
|
+
if ((_c = zoomOptions.zoom) === null || _c === void 0 ? void 0 : _c.wheel)
|
|
205
|
+
zoomOptions.zoom.wheel.enabled = newState;
|
|
206
|
+
if ((_d = zoomOptions.zoom) === null || _d === void 0 ? void 0 : _d.pinch)
|
|
207
|
+
zoomOptions.zoom.pinch.enabled = newState;
|
|
208
|
+
if (zoomOptions.pan)
|
|
209
|
+
zoomOptions.pan.enabled = newState;
|
|
210
|
+
this.zoomEnabled = newState;
|
|
211
|
+
this.chart.update();
|
|
212
|
+
}
|
|
213
|
+
willUpdate(changed) {
|
|
214
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
215
|
+
if (changed.has("stockData")) {
|
|
216
|
+
this.chart ? this._updateChart() : this._createChart();
|
|
217
|
+
}
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
// --------------------------------------------------------------------
|
|
221
|
+
// Data extraction
|
|
222
|
+
// --------------------------------------------------------------------
|
|
223
|
+
_getChartData() {
|
|
224
|
+
return {
|
|
225
|
+
dates: this.stockData.map((d) => { var _a; return (_a = d.date) !== null && _a !== void 0 ? _a : null; }),
|
|
226
|
+
closePrices: this.stockData.map((d) => { var _a; return (_a = d.close) !== null && _a !== void 0 ? _a : null; }),
|
|
227
|
+
rsi: this.stockData.map((d) => { var _a; return (_a = d.RSI) !== null && _a !== void 0 ? _a : null; }),
|
|
228
|
+
// ✅ StochRSI
|
|
229
|
+
stochRSI_K: this.stockData.map((d) => { var _a; return (_a = d.StochRSI_K) !== null && _a !== void 0 ? _a : null; }),
|
|
230
|
+
stochRSI_D: this.stockData.map((d) => { var _a; return (_a = d.StochRSI_D) !== null && _a !== void 0 ? _a : null; }),
|
|
231
|
+
angleMACDsignal: this.stockData.map((d) => { var _a; return (_a = d.angleMACDsignal) !== null && _a !== void 0 ? _a : null; }),
|
|
232
|
+
ma5: this.stockData.map((d) => { var _a; return (_a = d.MA5) !== null && _a !== void 0 ? _a : null; }),
|
|
233
|
+
ma10: this.stockData.map((d) => { var _a; return (_a = d.MA10) !== null && _a !== void 0 ? _a : null; }),
|
|
234
|
+
ma20: this.stockData.map((d) => { var _a; return (_a = d.MA20) !== null && _a !== void 0 ? _a : null; }),
|
|
235
|
+
ma50: this.stockData.map((d) => { var _a; return (_a = d.MA50) !== null && _a !== void 0 ? _a : null; }),
|
|
236
|
+
ma100: this.stockData.map((d) => { var _a; return (_a = d.MA100) !== null && _a !== void 0 ? _a : null; }),
|
|
237
|
+
ma200: this.stockData.map((d) => { var _a; return (_a = d.MA200) !== null && _a !== void 0 ? _a : null; }),
|
|
238
|
+
MACDLine: this.stockData.map((d) => { var _a; return (_a = d.MACDLine) !== null && _a !== void 0 ? _a : null; }),
|
|
239
|
+
SignalLine: this.stockData.map((d) => { var _a; return (_a = d.SignalLine) !== null && _a !== void 0 ? _a : null; }),
|
|
240
|
+
divergence: this.stockData.map((d) => { var _a; return (_a = d.divergence) !== null && _a !== void 0 ? _a : null; }),
|
|
241
|
+
volumes: this.stockData.map((d) => { var _a; return (_a = d.volume) !== null && _a !== void 0 ? _a : 0; }),
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
// --------------------------------------------------------------------
|
|
245
|
+
// Update
|
|
246
|
+
// --------------------------------------------------------------------
|
|
247
|
+
_updateChart() {
|
|
248
|
+
if (!this.chart)
|
|
249
|
+
return;
|
|
250
|
+
const { dates, closePrices, rsi, ma5, ma10, ma20, ma50, ma100, ma200, MACDLine, SignalLine, divergence, volumes, stochRSI_K, stochRSI_D, angleMACDsignal, } = this._getChartData();
|
|
251
|
+
this.chart.data.labels = dates;
|
|
252
|
+
const ds = this.chart.data.datasets;
|
|
253
|
+
ds[0].data = rsi;
|
|
254
|
+
ds[1].data = divergence;
|
|
255
|
+
ds[2].data = MACDLine;
|
|
256
|
+
ds[3].data = SignalLine;
|
|
257
|
+
ds[4].data = closePrices;
|
|
258
|
+
ds[5].data = ma20;
|
|
259
|
+
ds[6].data = ma50;
|
|
260
|
+
ds[7].data = ma100;
|
|
261
|
+
ds[8].data = ma200;
|
|
262
|
+
ds[9].data = ma10;
|
|
263
|
+
ds[10].data = ma5;
|
|
264
|
+
ds[11].data = volumes;
|
|
265
|
+
// ✅ Update StochRSI datasets (just like MACDLine and SignalLine)
|
|
266
|
+
ds[12].data = stochRSI_K; // %K line
|
|
267
|
+
ds[13].data = stochRSI_D; // %D line
|
|
268
|
+
ds[14].data = angleMACDsignal;
|
|
269
|
+
this.chart.update();
|
|
270
|
+
}
|
|
271
|
+
// --------------------------------------------------------------------
|
|
272
|
+
// Create Chart
|
|
273
|
+
// --------------------------------------------------------------------
|
|
274
|
+
_createChart() {
|
|
275
|
+
var _a;
|
|
276
|
+
const { dates, closePrices, rsi, ma5, ma10, ma20, ma50, ma100, ma200, MACDLine, SignalLine, divergence, volumes, stochRSI_K, stochRSI_D, angleMACDsignal } = this._getChartData();
|
|
277
|
+
const canvas = (_a = this.shadowRoot) === null || _a === void 0 ? void 0 : _a.getElementById("stockChart");
|
|
278
|
+
if (!canvas)
|
|
279
|
+
return;
|
|
280
|
+
const ctx = canvas.getContext("2d");
|
|
281
|
+
if (this.chart)
|
|
282
|
+
this.chart.destroy();
|
|
283
|
+
// Register plugins
|
|
284
|
+
Chart.register(zoomPlugin);
|
|
285
|
+
this.chart = new Chart(ctx, {
|
|
286
|
+
type: "line",
|
|
287
|
+
data: {
|
|
288
|
+
labels: dates,
|
|
289
|
+
datasets: [
|
|
290
|
+
{
|
|
291
|
+
label: "RSI",
|
|
292
|
+
data: rsi,
|
|
293
|
+
yAxisID: "yRSI",
|
|
294
|
+
borderColor: "rgba(61, 58, 215, 0.2)",
|
|
295
|
+
borderWidth: 1,
|
|
296
|
+
pointRadius: 0,
|
|
297
|
+
hidden: true, // <- hidden initially
|
|
298
|
+
},
|
|
299
|
+
{
|
|
300
|
+
label: "MACD Histogram",
|
|
301
|
+
data: divergence,
|
|
302
|
+
type: "bar",
|
|
303
|
+
yAxisID: "yMACD",
|
|
304
|
+
backgroundColor: (ctx) => ctx.raw >= 0 ? "rgba(9, 225, 9, 0.4)" : "rgba(227, 5, 5, 0.4)",
|
|
305
|
+
},
|
|
306
|
+
{
|
|
307
|
+
label: "MACD Line",
|
|
308
|
+
data: MACDLine,
|
|
309
|
+
borderColor: "blue",
|
|
310
|
+
yAxisID: "yMACD",
|
|
311
|
+
borderWidth: 1.2,
|
|
312
|
+
fill: false,
|
|
313
|
+
pointRadius: 0,
|
|
314
|
+
},
|
|
315
|
+
{
|
|
316
|
+
label: "Signal Line",
|
|
317
|
+
data: SignalLine,
|
|
318
|
+
borderColor: "orange",
|
|
319
|
+
yAxisID: "yMACD",
|
|
320
|
+
borderWidth: 1.2,
|
|
321
|
+
fill: false,
|
|
322
|
+
pointRadius: 0,
|
|
323
|
+
},
|
|
324
|
+
// Price section (middle)
|
|
325
|
+
{
|
|
326
|
+
label: "Close Price",
|
|
327
|
+
data: closePrices,
|
|
328
|
+
yAxisID: "yPrice",
|
|
329
|
+
borderColor: "rgba(75,192,192,1)",
|
|
330
|
+
backgroundColor: "rgba(75,192,192,0.2)",
|
|
331
|
+
fill: true,
|
|
332
|
+
pointRadius: 0,
|
|
333
|
+
},
|
|
334
|
+
{
|
|
335
|
+
label: "MA20",
|
|
336
|
+
data: ma20,
|
|
337
|
+
yAxisID: "yPrice",
|
|
338
|
+
borderColor: "#ff6384",
|
|
339
|
+
borderWidth: 1.5,
|
|
340
|
+
pointRadius: 0,
|
|
341
|
+
},
|
|
342
|
+
{
|
|
343
|
+
label: "MA50",
|
|
344
|
+
data: ma50,
|
|
345
|
+
yAxisID: "yPrice",
|
|
346
|
+
borderColor: "green",
|
|
347
|
+
borderWidth: 4,
|
|
348
|
+
pointRadius: 0,
|
|
349
|
+
},
|
|
350
|
+
{
|
|
351
|
+
label: "MA100",
|
|
352
|
+
data: ma100,
|
|
353
|
+
yAxisID: "yPrice",
|
|
354
|
+
borderColor: "#996633",
|
|
355
|
+
borderWidth: 1.5,
|
|
356
|
+
pointRadius: 0,
|
|
357
|
+
},
|
|
358
|
+
{
|
|
359
|
+
label: "MA200",
|
|
360
|
+
data: ma200,
|
|
361
|
+
yAxisID: "yPrice",
|
|
362
|
+
borderColor: "purple",
|
|
363
|
+
borderWidth: 3,
|
|
364
|
+
pointRadius: 0,
|
|
365
|
+
},
|
|
366
|
+
{
|
|
367
|
+
label: "MA10",
|
|
368
|
+
data: ma10,
|
|
369
|
+
yAxisID: "yPrice",
|
|
370
|
+
borderColor: "#cc3333",
|
|
371
|
+
borderWidth: 1,
|
|
372
|
+
fill: false,
|
|
373
|
+
hidden: true, // <- hidden initially
|
|
374
|
+
},
|
|
375
|
+
{
|
|
376
|
+
label: "MA5",
|
|
377
|
+
data: ma5,
|
|
378
|
+
yAxisID: "yPrice",
|
|
379
|
+
borderColor: "#111111",
|
|
380
|
+
borderWidth: 1,
|
|
381
|
+
fill: false,
|
|
382
|
+
hidden: true, // <- hidden initially
|
|
383
|
+
},
|
|
384
|
+
// Volume (bottom)
|
|
385
|
+
{
|
|
386
|
+
label: "Volume",
|
|
387
|
+
data: volumes,
|
|
388
|
+
type: "bar",
|
|
389
|
+
yAxisID: "yVolume",
|
|
390
|
+
backgroundColor: (context) => {
|
|
391
|
+
const index = context.dataIndex;
|
|
392
|
+
const candle = this.stockData[index];
|
|
393
|
+
if (!candle)
|
|
394
|
+
return "rgba(128,128,128,0.5)";
|
|
395
|
+
const isBull = (candle === null || candle === void 0 ? void 0 : candle.close) > (candle === null || candle === void 0 ? void 0 : candle.open);
|
|
396
|
+
const isBear = (candle === null || candle === void 0 ? void 0 : candle.close) < (candle === null || candle === void 0 ? void 0 : candle.open);
|
|
397
|
+
if (isBull) {
|
|
398
|
+
return "rgba(0, 200, 0, 0.6)";
|
|
399
|
+
}
|
|
400
|
+
if (isBear) {
|
|
401
|
+
return "rgb(220, 0, 0)";
|
|
402
|
+
}
|
|
403
|
+
// Doji
|
|
404
|
+
return "black";
|
|
405
|
+
},
|
|
406
|
+
},
|
|
407
|
+
// StochRSI %K
|
|
408
|
+
{
|
|
409
|
+
label: "StochRSI %K",
|
|
410
|
+
data: stochRSI_K,
|
|
411
|
+
yAxisID: "yStochRSI",
|
|
412
|
+
borderColor: "#00c853", // Green color for %K
|
|
413
|
+
borderWidth: 1.5,
|
|
414
|
+
pointRadius: 0,
|
|
415
|
+
hidden: true, // <- hidden initially
|
|
416
|
+
},
|
|
417
|
+
// StochRSI %D
|
|
418
|
+
{
|
|
419
|
+
label: "StochRSI %D",
|
|
420
|
+
data: stochRSI_D,
|
|
421
|
+
yAxisID: "yStochRSI",
|
|
422
|
+
borderColor: "#ff5252", // Red color for %D
|
|
423
|
+
borderWidth: 1.5,
|
|
424
|
+
pointRadius: 0,
|
|
425
|
+
hidden: true, // <- hidden initially
|
|
426
|
+
},
|
|
427
|
+
// angleMACDsignal
|
|
428
|
+
{
|
|
429
|
+
label: "angleMACDsignal",
|
|
430
|
+
data: stochRSI_D,
|
|
431
|
+
yAxisID: "yMACD",
|
|
432
|
+
borderColor: "#ff5250", // Red color for %D
|
|
433
|
+
borderWidth: 1.5,
|
|
434
|
+
pointRadius: 0,
|
|
435
|
+
hidden: true, // <- hidden initially
|
|
436
|
+
},
|
|
437
|
+
],
|
|
438
|
+
},
|
|
439
|
+
options: {
|
|
440
|
+
responsive: true,
|
|
441
|
+
maintainAspectRatio: true,
|
|
442
|
+
interaction: { mode: "index", intersect: false },
|
|
443
|
+
plugins: {
|
|
444
|
+
legend: { position: "top" },
|
|
445
|
+
zoom: {
|
|
446
|
+
pan: { enabled: this.zoomEnabled, mode: "x" },
|
|
447
|
+
zoom: { wheel: { enabled: this.zoomEnabled }, mode: "x" },
|
|
448
|
+
},
|
|
449
|
+
tooltip: { mode: "index", intersect: false },
|
|
450
|
+
},
|
|
451
|
+
scales: {
|
|
452
|
+
x: {
|
|
453
|
+
type: "category",
|
|
454
|
+
ticks: {
|
|
455
|
+
color: (ctx) => {
|
|
456
|
+
const label = ctx.tick.label;
|
|
457
|
+
if (typeof label === "string") {
|
|
458
|
+
const labelDate = new Date(label.replace(" ", "T"));
|
|
459
|
+
if (isNaN(labelDate.getTime()))
|
|
460
|
+
return "black";
|
|
461
|
+
const todayStr = new Date().toISOString().split("T")[0];
|
|
462
|
+
const labelStr = labelDate.toISOString().split("T")[0];
|
|
463
|
+
return labelStr === todayStr ? "Salmon" : "black";
|
|
464
|
+
}
|
|
465
|
+
return "black";
|
|
466
|
+
},
|
|
467
|
+
},
|
|
468
|
+
},
|
|
469
|
+
yMACD: {
|
|
470
|
+
position: "right",
|
|
471
|
+
title: { display: true, text: "MACD" },
|
|
472
|
+
beginAtZero: false,
|
|
473
|
+
weight: 1,
|
|
474
|
+
},
|
|
475
|
+
yPrice: {
|
|
476
|
+
position: "left",
|
|
477
|
+
title: { display: true, text: "Price" },
|
|
478
|
+
beginAtZero: false,
|
|
479
|
+
weight: 3,
|
|
480
|
+
grid: { drawOnChartArea: true },
|
|
481
|
+
},
|
|
482
|
+
yVolume: {
|
|
483
|
+
position: "left",
|
|
484
|
+
title: { display: true, text: "Volume" },
|
|
485
|
+
beginAtZero: true,
|
|
486
|
+
weight: 1,
|
|
487
|
+
grid: { drawOnChartArea: false },
|
|
488
|
+
},
|
|
489
|
+
yStochRSI: {
|
|
490
|
+
position: "right",
|
|
491
|
+
min: -0.5,
|
|
492
|
+
max: 1.5, // you can adjust this for your scale, or 100 if you use the full range
|
|
493
|
+
title: {
|
|
494
|
+
display: true,
|
|
495
|
+
text: "StochRSI",
|
|
496
|
+
},
|
|
497
|
+
grid: {
|
|
498
|
+
drawOnChartArea: false,
|
|
499
|
+
},
|
|
500
|
+
},
|
|
501
|
+
},
|
|
502
|
+
},
|
|
503
|
+
plugins: [
|
|
504
|
+
// this.customLinePlugin,
|
|
505
|
+
this.volume_Doji_915_Plugin,
|
|
506
|
+
],
|
|
507
|
+
});
|
|
508
|
+
}
|
|
509
|
+
render() {
|
|
510
|
+
return html `
|
|
511
|
+
<button @click="${this._Zoomenalbe}">
|
|
512
|
+
${this.zoomEnabled ? "Disable Zoom" : "Enable Zoom"}
|
|
513
|
+
</button>
|
|
514
|
+
|
|
515
|
+
<button @click="${this._resetZoom}">Reset Zoom</button>
|
|
516
|
+
|
|
517
|
+
<canvas id="stockChart" style="width:100%;"></canvas>
|
|
518
|
+
`;
|
|
519
|
+
}
|
|
520
|
+
};
|
|
521
|
+
__decorate([
|
|
522
|
+
property({ type: Array }),
|
|
523
|
+
__metadata("design:type", Array)
|
|
524
|
+
], ChartElement.prototype, "stockData", void 0);
|
|
525
|
+
__decorate([
|
|
526
|
+
property({ type: Boolean }),
|
|
527
|
+
__metadata("design:type", Object)
|
|
528
|
+
], ChartElement.prototype, "zoomEnabled", void 0);
|
|
529
|
+
ChartElement = __decorate([
|
|
530
|
+
customElement("stock-chart-display")
|
|
531
|
+
], ChartElement);
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { LitElement } from "lit";
|
|
2
|
+
declare class MyElement extends LitElement {
|
|
3
|
+
name: string;
|
|
4
|
+
size: string;
|
|
5
|
+
backgroundColor: string;
|
|
6
|
+
center: boolean;
|
|
7
|
+
static styles: import("lit").CSSResult[];
|
|
8
|
+
updated(changedProperties: any): void;
|
|
9
|
+
render(): import("lit-html").TemplateResult<1>;
|
|
10
|
+
}
|
|
11
|
+
declare global {
|
|
12
|
+
interface HTMLElementTagNameMap {
|
|
13
|
+
"loading-spinner-element": MyElement;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
export {};
|