316 lines
11 KiB
JavaScript
316 lines
11 KiB
JavaScript
class Indicator_Output {
|
|
constructor(name) {
|
|
this.legend={};
|
|
}
|
|
create_legend(name, chart, lineSeries){
|
|
// Create legend div and append it to the output element
|
|
let target_div = document.getElementById('indicator_output');
|
|
this.legend[name] = document.createElement('div');
|
|
this.legend[name].className = 'legend';
|
|
target_div.appendChild(this.legend[name]);
|
|
this.legend[name].style.display = 'block';
|
|
this.legend[name].style.left = 3 + 'px';
|
|
this.legend[name].style.top = 3 + 'px';
|
|
// subscribe set legend text to crosshair moves
|
|
chart.subscribeCrosshairMove((param) => {
|
|
this.set_legend_text(param.seriesPrices.get(lineSeries),name);
|
|
});
|
|
}
|
|
|
|
|
|
set_legend_text(priceValue,name) {
|
|
// Callback assigned to fire on crosshair movements.
|
|
let val = 'n/a';
|
|
if (priceValue !== undefined) {
|
|
val = (Math.round(priceValue * 100) / 100).toFixed(2);
|
|
}
|
|
this.legend[name].innerHTML = name + ' <span style="color:rgba(4, 111, 232, 1)">' + val + '</span>';
|
|
}
|
|
}
|
|
iOutput = new Indicator_Output();
|
|
|
|
class Indicator {
|
|
constructor(name) {
|
|
// The name of the indicator.
|
|
this.name = name;
|
|
this.lines=[];
|
|
this.hist=[];
|
|
}
|
|
init(data){
|
|
console.log(this.name + ': init() unimplemented.');
|
|
}
|
|
update(data){
|
|
console.log(this.name + ': update() unimplemented.');
|
|
}
|
|
addHist(name, chart, color='#26a69a'){
|
|
this.hist[name] = chart.addHistogramSeries({
|
|
color: color,
|
|
priceFormat: {
|
|
type: 'price',
|
|
},
|
|
priceScaleId: '',
|
|
scaleMargins: {
|
|
top: 0.0,
|
|
bottom: 0,
|
|
},
|
|
});
|
|
}
|
|
addLine(name, chart, color, lineWidth){
|
|
this.lines[name] = chart.addLineSeries({
|
|
color: color,
|
|
lineWidth: lineWidth
|
|
});
|
|
//Initialise the crosshair legend for the charts.
|
|
iOutput.create_legend(this.name, chart, this.lines[name]);
|
|
|
|
}
|
|
setLine(name, data, value_name){
|
|
// Initialize the data with the data object provided.
|
|
this.lines[name].setData(data);
|
|
// Isolate the last value provided and round to 2 decimals places.
|
|
let priceValue = data.at(-1).value;
|
|
this.updateDisplay(name, priceValue, value_name);
|
|
// Update indicator output/crosshair legend.
|
|
iOutput.set_legend_text(data.at(-1).value, this.name);
|
|
}
|
|
updateDisplay(name, priceValue, value_name){
|
|
let rounded_value = (Math.round(priceValue * 100) / 100).toFixed(2);
|
|
// Update the data in the edit and view indicators panel
|
|
document.getElementById(this.name + '_' + value_name).value = rounded_value;
|
|
}
|
|
setHist(name, data){
|
|
this.hist[name].setData(data);
|
|
}
|
|
updateLine(name, data, value_name){
|
|
// Update the line-set data in the chart
|
|
this.lines[name].update(data);
|
|
// Update indicator output/crosshair legend.
|
|
iOutput.set_legend_text(data.value, this.name);
|
|
// Update the data in the edit and view indicators panel
|
|
this.updateDisplay(name, data.value, value_name);
|
|
}
|
|
updateHist(name,data){
|
|
this.hist[name].update(data);
|
|
}
|
|
}
|
|
|
|
class SMA extends Indicator{
|
|
constructor(name, chart, color, lineWidth = 2) {
|
|
// Call the inherited constructor.
|
|
super(name);
|
|
|
|
// Create a line series and append to the appropriate chart.
|
|
this.addLine('line', chart, color, lineWidth);
|
|
|
|
}
|
|
init(data){
|
|
this.setLine('line',data, 'value');
|
|
}
|
|
update(data){
|
|
this.updateLine('line', data[0], 'value');
|
|
}
|
|
}
|
|
|
|
class Linear_Regression extends SMA{
|
|
}
|
|
|
|
class EMA extends SMA{
|
|
}
|
|
|
|
class RSI extends Indicator{
|
|
constructor(name, charts, color, lineWidth = 2) {
|
|
// Call the inherited constructor.
|
|
super(name);
|
|
// If the chart doesn't exist create one.
|
|
if( !charts.hasOwnProperty('chart2') ) { charts.create_RSI_chart(); }
|
|
let chart = charts.chart2;
|
|
// Create a line series and append to the appropriate chart.
|
|
this.addLine('line', chart, color, lineWidth);
|
|
}
|
|
init(data){
|
|
this.setLine('line',data, 'value');
|
|
}
|
|
update(data){
|
|
this.updateLine('line', data[0], 'value');
|
|
}
|
|
}
|
|
|
|
class MACD extends Indicator{
|
|
constructor(name, charts, color_m, color_s, lineWidth = 2) {
|
|
// Call the inherited constructor.
|
|
super(name);
|
|
// If the chart doesn't exist create one.
|
|
if( !charts.hasOwnProperty('chart3') ) { charts.create_MACD_chart(); }
|
|
let chart = charts.chart3;
|
|
|
|
// Create two line series and append to the chart.
|
|
this.addLine('line_m', chart, color_m, lineWidth);
|
|
this.addLine('line_s', chart, color_s, lineWidth);
|
|
this.addHist(name, chart);
|
|
}
|
|
|
|
init(data){
|
|
this.setLine('line_m',data[0], 'macd');
|
|
this.setLine('line_s',data[1], 'signal');
|
|
this.setHist(name, data[2]);
|
|
}
|
|
|
|
update(data){
|
|
this.updateLine('line_m', data[0][0], 'macd');
|
|
this.updateLine('line_s', data[1][0], 'signal');
|
|
this.updateHist(name, data[2][0]);
|
|
}
|
|
}
|
|
|
|
class ATR extends Indicator{
|
|
init(data) {
|
|
this.updateDisplay(this.name, data.at(-1).value, 'value');
|
|
}
|
|
update(data) {
|
|
this.updateDisplay(this.name, data[0].value, 'value');
|
|
|
|
}
|
|
}
|
|
|
|
class Volume extends Indicator{
|
|
constructor(name, chart) {
|
|
// Call the inherited constructor.
|
|
super(name);
|
|
this.addHist(name, chart);
|
|
this.hist[name].applyOptions( { scaleMargins: { top: 0.8, bottom: 0.0} } );
|
|
}
|
|
init(data){
|
|
this.setHist(this.name, data);
|
|
}
|
|
update(data){
|
|
this.updateHist(this.name, data[0]);
|
|
}
|
|
}
|
|
|
|
class Bolenger extends Indicator{
|
|
constructor(name, chart, color_u, color_m, color_l, lineWidth = 2) {
|
|
// Call the inherited constructor.
|
|
super(name);
|
|
|
|
// Create three line series and append to the chart.
|
|
this.addLine('line_u', chart, color_u, lineWidth);
|
|
this.addLine('line_m', chart, color_u, lineWidth);
|
|
this.addLine('line_l', chart, color_u, lineWidth);
|
|
}
|
|
|
|
init(data){
|
|
// Initialize the data with the data object provided.
|
|
this.setLine('line_u',data[0],'value1');
|
|
this.setLine('line_m',data[1],'value2');
|
|
this.setLine('line_l',data[2],'value3');
|
|
}
|
|
|
|
update(data){
|
|
// Update the line-set data in the chart
|
|
this.updateLine('line_u', data[0][0], 'value1');
|
|
// Update the line-set data in the chart
|
|
this.updateLine('line_m', data[1][0], 'value2');
|
|
// Update the line-set data in the chart
|
|
this.updateLine('line_l', data[2][0], 'value3');
|
|
}
|
|
|
|
}
|
|
|
|
class Indicators {
|
|
constructor(charts, idata) {
|
|
// Create an array to hold all the created indicators.
|
|
this.i_objs = {};
|
|
// Pass a list of indicators that was received from the server
|
|
// to a function that will create them.
|
|
this.create_indicators(idata.indicators, charts);
|
|
// Pass the initial indicator data to a function to initialize the indicators.
|
|
idata.indicator_data.then( (data) => { this.init_indicators(data); } );
|
|
|
|
|
|
}
|
|
create_indicators(indicators, charts){
|
|
// loop through all the indicators received from the
|
|
// server and if the are enabled and create them.
|
|
for (let name in indicators) {
|
|
|
|
// If this indicator is hidden skip to the next one
|
|
if (!indicators[name].visible) {continue;}
|
|
|
|
// Get the type of indicator
|
|
let i_type = indicators[name].type;
|
|
|
|
// Call the indicator creation function
|
|
if (i_type == 'SMA') {
|
|
// The color of the line
|
|
let color = indicators[name].color;
|
|
this.i_objs[name] = new SMA(name, charts.chart_1, color);
|
|
}
|
|
if (i_type == 'BOLBands') {
|
|
// The color of three lines
|
|
let color_u = bt_data.indicators[name].color_1;
|
|
let color_m = bt_data.indicators[name].color_2;
|
|
let color_l = bt_data.indicators[name].color_3;
|
|
this.i_objs[name] = new Bolenger(name, charts.chart_1, color_u, color_m, color_l);
|
|
}
|
|
if (i_type == 'MACD') {
|
|
// The color of two lines
|
|
let color_m = bt_data.indicators[name].color_1;
|
|
let color_s = bt_data.indicators[name].color_2;
|
|
this.i_objs[name] = new MACD(name, charts, color_m, color_s);
|
|
}
|
|
if (i_type == 'Volume') { this.i_objs[name] = new Volume(name, charts.chart_1); }
|
|
if (i_type == 'ATR') { this.i_objs[name] = new ATR(name); }
|
|
if (i_type == 'LREG') {
|
|
// The color of the line
|
|
let color = indicators[name].color;
|
|
this.i_objs[name] = new Linear_Regression(name, charts.chart_1, color);
|
|
}
|
|
if (i_type == 'RSI') {
|
|
let color = indicators[name].color;
|
|
this.i_objs[name] = new RSI(name, charts, color);
|
|
}
|
|
if (i_type == 'EMA') {
|
|
// The color of the line
|
|
let color = indicators[name].color;
|
|
this.i_objs[name] = new EMA(name, charts.chart_1, color);
|
|
}
|
|
}
|
|
}
|
|
init_indicators(data){
|
|
// Loop through all the indicators.
|
|
for (name in data){
|
|
// Call the initialization function for each indicator.
|
|
this.i_objs[name].init(data[name]['data']);
|
|
}
|
|
}
|
|
|
|
update(updates){
|
|
for (name in updates){
|
|
window.UI.indicators.i_objs[name].update(updates[name].data);
|
|
}
|
|
}
|
|
|
|
add_to_list(){
|
|
// Adds user input to a list and displays it in a HTML element.
|
|
// Used in the Create indicator panel (!)Called from inline html.
|
|
var n = document.getElementById("new_prop_name").value;
|
|
var v = document.getElementById("new_prop_value").value;
|
|
p={};
|
|
p[n] = v;
|
|
if (document.getElementById("new_prop_list").innerHTML ==""){
|
|
document.getElementById("new_prop_list").insertAdjacentHTML('beforeend', JSON.stringify(p));
|
|
}else{
|
|
document.getElementById("new_prop_list").insertAdjacentHTML('beforeend', ',' + JSON.stringify(p)); }
|
|
}
|
|
submit_new_i(){
|
|
/* Populates a hidden <input> with a value from another element then submits the form
|
|
Used in the create indicator panel.*/
|
|
pl=document.getElementById("new_prop_list").innerHTML;
|
|
if(pl) { pl = '[' + pl + ']'; }
|
|
document.getElementById("new_prop_obj").value=pl;
|
|
document.getElementById("new_i_form").submit();
|
|
}
|
|
|
|
|
|
}
|