brighter-trading/static/indicators.js

341 lines
15 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: 'volume_ps',
scaleMargins: {
top: 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){
console.log('indicators[68]: setLine takes:(name,data,value_name)')
console.log(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');
console.log('line data', data)
}
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.95, 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],'value');
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], 'value');
// 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() {
// Contains instantiated indicators.
this.i_objs = {};
}
addToCharts(charts, idata){
/*
Receives indicator data, creates and stores the indicator
objects, then inserts the data into the charts.
*/
this.create_indicators(idata.indicators, charts);
// Initialize each indicators with the data.
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.
if (!this.i_objs[name]){
console.log('could not load:', name);
continue;
}
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.
let n = document.getElementById("new_prop_name").value;
let v = document.getElementById("new_prop_value").value;
// Converts css color name to hex
if (n == 'color'){
// list of valid css colors
let colours = {
"aliceblue":"#f0f8ff", "antiquewhite":"#faebd7", "aqua":"#00ffff", "aquamarine":"#7fffd4", "azure":"#f0ffff", "beige":"#f5f5dc", "bisque":"#ffe4c4", "black":"#000000", "blanchedalmond":"#ffebcd", "blue":"#0000ff", "blueviolet":"#8a2be2", "brown":"#a52a2a", "burlywood":"#deb887", "cadetblue":"#5f9ea0", "chartreuse":"#7fff00", "chocolate":"#d2691e", "coral":"#ff7f50", "cornflowerblue":"#6495ed", "cornsilk":"#fff8dc", "crimson":"#dc143c", "cyan":"#00ffff", "darkblue":"#00008b", "darkcyan":"#008b8b", "darkgoldenrod":"#b8860b", "darkgray":"#a9a9a9", "darkgreen":"#006400", "darkkhaki":"#bdb76b", "darkmagenta":"#8b008b", "darkolivegreen":"#556b2f", "darkorange":"#ff8c00", "darkorchid":"#9932cc", "darkred":"#8b0000", "darksalmon":"#e9967a", "darkseagreen":"#8fbc8f", "darkslateblue":"#483d8b", "darkslategray":"#2f4f4f", "darkturquoise":"#00ced1", "darkviolet":"#9400d3", "deeppink":"#ff1493", "deepskyblue":"#00bfff", "dimgray":"#696969", "dodgerblue":"#1e90ff", "firebrick":"#b22222", "floralwhite":"#fffaf0", "forestgreen":"#228b22", "fuchsia":"#ff00ff", "gainsboro":"#dcdcdc", "ghostwhite":"#f8f8ff", "gold":"#ffd700", "goldenrod":"#daa520", "gray":"#808080", "green":"#008000", "greenyellow":"#adff2f",
"honeydew":"#f0fff0", "hotpink":"#ff69b4", "indianred ":"#cd5c5c", "indigo":"#4b0082", "ivory":"#fffff0", "khaki":"#f0e68c", "lavender":"#e6e6fa", "lavenderblush":"#fff0f5", "lawngreen":"#7cfc00", "lemonchiffon":"#fffacd", "lightblue":"#add8e6", "lightcoral":"#f08080", "lightcyan":"#e0ffff", "lightgoldenrodyellow":"#fafad2", "lightgrey":"#d3d3d3", "lightgreen":"#90ee90", "lightpink":"#ffb6c1", "lightsalmon":"#ffa07a", "lightseagreen":"#20b2aa", "lightskyblue":"#87cefa", "lightslategray":"#778899", "lightsteelblue":"#b0c4de", "lightyellow":"#ffffe0", "lime":"#00ff00", "limegreen":"#32cd32", "linen":"#faf0e6", "magenta":"#ff00ff", "maroon":"#800000", "mediumaquamarine":"#66cdaa", "mediumblue":"#0000cd", "mediumorchid":"#ba55d3", "mediumpurple":"#9370d8", "mediumseagreen":"#3cb371", "mediumslateblue":"#7b68ee", "mediumspringgreen":"#00fa9a", "mediumturquoise":"#48d1cc", "mediumvioletred":"#c71585", "midnightblue":"#191970", "mintcream":"#f5fffa", "mistyrose":"#ffe4e1", "moccasin":"#ffe4b5", "navajowhite":"#ffdead", "navy":"#000080", "oldlace":"#fdf5e6", "olive":"#808000", "olivedrab":"#6b8e23", "orange":"#ffa500", "orangered":"#ff4500", "orchid":"#da70d6", "palegoldenrod":"#eee8aa",
"palegreen":"#98fb98", "paleturquoise":"#afeeee", "palevioletred":"#d87093", "papayawhip":"#ffefd5", "peachpuff":"#ffdab9", "peru":"#cd853f", "pink":"#ffc0cb", "plum":"#dda0dd", "powderblue":"#b0e0e6", "purple":"#800080", "rebeccapurple":"#663399", "red":"#ff0000", "rosybrown":"#bc8f8f", "royalblue":"#4169e1", "saddlebrown":"#8b4513", "salmon":"#fa8072", "sandybrown":"#f4a460", "seagreen":"#2e8b57", "seashell":"#fff5ee", "sienna":"#a0522d", "silver":"#c0c0c0", "skyblue":"#87ceeb", "slateblue":"#6a5acd", "slategray":"#708090", "snow":"#fffafa", "springgreen":"#00ff7f", "steelblue":"#4682b4", "tan":"#d2b48c", "teal":"#008080", "thistle":"#d8bfd8", "tomato":"#ff6347", "turquoise":"#40e0d0", "violet":"#ee82ee", "wheat":"#f5deb3", "white":"#ffffff", "whitesmoke":"#f5f5f5", "yellow":"#ffff00", "yellowgreen":"#9acd32"
};
// if the value is in the list of colors convert it.
if (typeof colours[v.toLowerCase()] != 'undefined')
v = colours[v.toLowerCase()];
}
let 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.*/
let pl=document.getElementById("new_prop_list").innerHTML;
if(pl) { pl = '[' + pl + ']'; }
document.getElementById("new_prop_obj").value=pl;
document.getElementById("new_i_form").submit();
}
}