Working and relatively glitch free. Classes are being implemented in javascript files. Python is basically one big class with no separation.
This commit is contained in:
parent
42da478944
commit
b6d9bccaaf
|
|
@ -0,0 +1,187 @@
|
|||
class Charts {
|
||||
|
||||
|
||||
constructor(idata) {
|
||||
// Unpack the initialization data.
|
||||
this.chart1_id = idata.chart1_id;
|
||||
this.chart2_id = idata.chart2_id;
|
||||
this.chart3_id = idata.chart3_id;
|
||||
this.trading_pair = idata.trading_pair;
|
||||
this.price_history = idata.price_history;
|
||||
/* A list of bound charts this is necessary for maintaining a dynamic
|
||||
number of charts with their position and zoom factors bound. I had to
|
||||
declare it global as it is accessed through a callback.*/
|
||||
window.bound_charts=[];
|
||||
// Only the main chart is created by default.
|
||||
this.create_main_chart();
|
||||
}
|
||||
|
||||
|
||||
create_main_chart() {
|
||||
// Pass the id of the element to create the main chart in.
|
||||
// This function returns the main chart object.
|
||||
this.chart_1 = this.create_chart(this.chart1_id);
|
||||
|
||||
// Display the trading pair as a watermark overlaying the chart.
|
||||
this.addWatermark(this.chart_1, this.trading_pair);
|
||||
|
||||
// - Create the candle stick series for our chart
|
||||
this.candleSeries = this.chart_1.addCandlestickSeries();
|
||||
|
||||
//Initialise the candlestick series
|
||||
this.price_history.then((ph) => {
|
||||
//Initialise the candle data
|
||||
this.candleSeries.setData(ph);
|
||||
})
|
||||
this.bind_charts(this.chart_1);
|
||||
}
|
||||
|
||||
|
||||
create_RSI_chart(){
|
||||
this.chart2 = this.create_chart(this.chart2_id, 100);
|
||||
this.set_priceScale(this.chart2, 0.3, 0.0);
|
||||
// Put the name of the chart in a watermark in the chart.
|
||||
this.addWatermark(this.chart2, 'RSI');
|
||||
// Todo: Not sure how to set this
|
||||
//this.chart2.applyOptions({ priceRange: {minValue:0,maxValue:100} });
|
||||
this.bind_charts(this.chart2);
|
||||
}
|
||||
|
||||
|
||||
create_MACD_chart(){
|
||||
this.chart3 = this.create_chart(this.chart3_id, 100);
|
||||
this.addWatermark(this.chart3, 'MACD');
|
||||
this.bind_charts(this.chart3);
|
||||
|
||||
}
|
||||
|
||||
|
||||
create_chart(target_id, height=500){
|
||||
// Accepts a target element to place the chart in.
|
||||
// Returns the chart object.
|
||||
let container = document.getElementById(target_id);
|
||||
//Create a lightweight chart object.
|
||||
let chart = LightweightCharts.createChart(container, {
|
||||
width: 1000,
|
||||
height: height,
|
||||
crosshair: {
|
||||
mode: LightweightCharts.CrosshairMode.Normal,
|
||||
},
|
||||
priceScale: {
|
||||
borderColor: 'rgba(197, 203, 206, 0.8)',
|
||||
},
|
||||
timeScale: {
|
||||
borderColor: 'rgba(197, 203, 206, 0.8)',
|
||||
timeVisible: true,
|
||||
secondsVisible: false,
|
||||
barSpacing: 6
|
||||
},
|
||||
handleScroll: true
|
||||
});
|
||||
return chart;
|
||||
}
|
||||
|
||||
|
||||
set_priceScale(chart, top, bottom){
|
||||
chart.priceScale('right').applyOptions({
|
||||
scaleMargins: {
|
||||
top: top,
|
||||
bottom: bottom,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
addWatermark(chart,text){
|
||||
chart.applyOptions({
|
||||
watermark: {visible: true,
|
||||
color: '#DBC29E',
|
||||
text: text,
|
||||
fontSize: 30,
|
||||
fontFamily: 'Roboto',
|
||||
fontStyle: 'bold',
|
||||
vertAlign: 'center'
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
bind_charts(chart){
|
||||
// keep a list of charts and bind all their position and spacing.
|
||||
// Add (arg1) to bound_charts
|
||||
this.add_to_list(chart);
|
||||
// Get the number of objects in bound_charts
|
||||
let bcl = Object.keys(window.bound_charts).length;
|
||||
// if bound_charts has two element in it bind them
|
||||
if (bcl == 2) { this.bind2charts(); }
|
||||
|
||||
// if bound_charts has two element in it bind them
|
||||
if (bcl == 3) { this.bind3charts(); }
|
||||
|
||||
return;
|
||||
}
|
||||
add_to_list(chart){
|
||||
// If the chart isn't already included in the list, add it.
|
||||
if ( !window.bound_charts.includes(chart) ){
|
||||
window.bound_charts.push(chart);
|
||||
}
|
||||
}
|
||||
|
||||
bind2charts(){
|
||||
//On change in chart 1 change chart 2
|
||||
window.bound_charts[0].timeScale().subscribeVisibleTimeRangeChange(syncHandler1);
|
||||
function syncHandler1(e) {
|
||||
// Get the barSpacing(zoom) and position of 1st chart.
|
||||
let barSpacing1 = window.bound_charts[0].timeScale().getBarSpacing();
|
||||
let scrollPosition1 = window.bound_charts[0].timeScale().scrollPosition();
|
||||
// Apply barSpacing(zoom) and position to 2nd chart.
|
||||
window.bound_charts[1].timeScale().applyOptions({ rightOffset: scrollPosition1, barSpacing: barSpacing1 });
|
||||
}
|
||||
//On change in chart 2 change chart 1
|
||||
window.bound_charts[1].timeScale().subscribeVisibleTimeRangeChange(syncHandler2);
|
||||
function syncHandler2(e) {
|
||||
// Get the barSpacing(zoom) and position of chart 2
|
||||
let barSpacing2 = window.bound_charts[1].timeScale().getBarSpacing();
|
||||
let scrollPosition2 = window.bound_charts[1].timeScale().scrollPosition();
|
||||
// Apply barSpacing(zoom) and position to chart 1
|
||||
window.bound_charts[0].timeScale().applyOptions({ rightOffset: scrollPosition2, barSpacing: barSpacing2 });
|
||||
}
|
||||
}
|
||||
bind3charts(){
|
||||
|
||||
//On change to chart 1 change chart 2 and 3
|
||||
window.bound_charts[0].timeScale().subscribeVisibleTimeRangeChange(syncHandler);
|
||||
function syncHandler(e) {
|
||||
// Get the barSpacing(zoom) and position of chart 1
|
||||
let barSpacing1 = window.bound_charts[0].timeScale().getBarSpacing();
|
||||
let scrollPosition1 = window.bound_charts[0].timeScale().scrollPosition();
|
||||
// Apply barSpacing(zoom) and position to new chart
|
||||
window.bound_charts[1].timeScale().applyOptions({ rightOffset: scrollPosition1, barSpacing: barSpacing1 });
|
||||
window.bound_charts[2].timeScale().applyOptions({ rightOffset: scrollPosition1, barSpacing: barSpacing1 });
|
||||
}
|
||||
|
||||
//On change to chart 2 change chart 1 and 3
|
||||
window.bound_charts[1].timeScale().subscribeVisibleTimeRangeChange(syncHandler2);
|
||||
function syncHandler2(e) {
|
||||
// Get the barSpacing(zoom) and position of chart 2
|
||||
let barSpacing2 = window.bound_charts[1].timeScale().getBarSpacing();
|
||||
let scrollPosition2 = window.bound_charts[1].timeScale().scrollPosition();
|
||||
// Apply barSpacing(zoom) and position to chart 1 and 3
|
||||
window.bound_charts[0].timeScale().applyOptions({ rightOffset: scrollPosition2, barSpacing: barSpacing2 });
|
||||
window.bound_charts[2].timeScale().applyOptions({ rightOffset: scrollPosition2, barSpacing: barSpacing2 });
|
||||
}
|
||||
|
||||
//On change to chart 3 change chart 1 and 2
|
||||
window.bound_charts[2].timeScale().subscribeVisibleTimeRangeChange(syncHandler3);
|
||||
function syncHandler3(e) {
|
||||
// Get the barSpacing(zoom) and position of new chart
|
||||
let barSpacing2 = window.bound_charts[2].timeScale().getBarSpacing();
|
||||
let scrollPosition2 = window.bound_charts[2].timeScale().scrollPosition();
|
||||
// Apply barSpacing(zoom) and position to parent chart
|
||||
window.bound_charts[0].timeScale().applyOptions({ rightOffset: scrollPosition2, barSpacing: barSpacing2 });
|
||||
window.bound_charts[1].timeScale().applyOptions({ rightOffset: scrollPosition2, barSpacing: barSpacing2 });
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,79 @@
|
|||
class Communication {
|
||||
constructor(interval, ocu, occ, oiu) {
|
||||
// Register callbacks
|
||||
this.on_candle_update = ocu;
|
||||
this.on_candle_close = occ;
|
||||
this.on_indctr_update = oiu;
|
||||
// Open connections.
|
||||
this.set_app_con();
|
||||
this.set_exchange_con(interval);
|
||||
}
|
||||
|
||||
candle_update(new_candle){
|
||||
this.on_candle_update(new_candle);
|
||||
}
|
||||
|
||||
candle_close(new_candle){
|
||||
// Send a copy of the data to the server
|
||||
this.app_con.send( JSON.stringify({ message_type: "candle_data", data :new_candle }));
|
||||
this.on_candle_close(new_candle);
|
||||
}
|
||||
|
||||
indicator_update(data){
|
||||
this.this.on_indctr_update(data);
|
||||
}
|
||||
|
||||
set_app_con(){
|
||||
// Create a web socket connection to our app.
|
||||
this.app_con = new WebSocket('ws://localhost:5000/ws');
|
||||
this.app_con.onopen = () => this.app_con.send("Connection OK");
|
||||
|
||||
this.app_con.addEventListener('message', ev => {
|
||||
if(ev.data){
|
||||
// Get the message received from server
|
||||
let msg = JSON.parse(ev.data)
|
||||
// Handle a request from the server
|
||||
if (msg.request) {
|
||||
//handle request
|
||||
console.log('Received a request from the server');
|
||||
console.log(msg.request);
|
||||
}
|
||||
// Handle a reply from the server
|
||||
if (msg.reply) {
|
||||
// Handle indicator updates
|
||||
if (msg.reply == 'i_updates'){
|
||||
// console.log(msg.data);
|
||||
this.indicator_update(msg.data)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
set_exchange_con(interval){
|
||||
let ws = "wss://stream.binance.com:9443/ws/btcusdt@kline_" + interval;
|
||||
this.exchange_con = new WebSocket(ws);
|
||||
|
||||
// Set the on-message call-back for the socket
|
||||
this.exchange_con.onmessage = (event) => {
|
||||
// Convert message to json obj
|
||||
let message = JSON.parse(event.data);
|
||||
// Isolate the candle data from message
|
||||
let candlestick = message.k;
|
||||
//console.log(message.k)
|
||||
// Reformat data for lightweight charts
|
||||
let new_candle={
|
||||
time: candlestick.t / 1000,
|
||||
open: candlestick.o,
|
||||
high: candlestick.h,
|
||||
low: candlestick.l,
|
||||
close: candlestick.c,
|
||||
vol: candlestick.v
|
||||
};
|
||||
// Update frequently updated objects
|
||||
this.candle_update(new_candle);
|
||||
// Update less frequently updated objects.
|
||||
if (candlestick.x == true) { this.candle.close(new_candle); }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
class Data {
|
||||
constructor() {
|
||||
// Set the id's of the HTML elements that contain each section of the user interface.
|
||||
this.chart1_id = 'chart';
|
||||
this.chart2_id = 'chart2';
|
||||
this.chart3_id = 'chart3';
|
||||
// Get and set into memory configuration and historical data from the server.
|
||||
this.trading_pair = bt_data.trading_pair;
|
||||
this.interval = bt_data.interval;
|
||||
this.price_history = fetch('http://localhost:5000/history').then((r) => r.json()).then( (data) => { return data; } );
|
||||
this.indicators = bt_data.indicators;
|
||||
// Request initialization data for the indicators.
|
||||
this.indicator_data = fetch('http://localhost:5000/indicator_init').then((r) => r.json()).then( (data) => { return data; } );
|
||||
}
|
||||
candle_update(){
|
||||
console.log('candle update');
|
||||
}
|
||||
|
||||
candle_close(){
|
||||
console.log('candle close');
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,258 @@
|
|||
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
|
||||
});
|
||||
}
|
||||
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);
|
||||
}
|
||||
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;
|
||||
//Initialise the legend todo fix this
|
||||
//set_legend_text(rounded_value, i);
|
||||
}
|
||||
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 the data in the edit and view indicators panel
|
||||
this.updateDisplay(name, data, 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);
|
||||
|
||||
// todo: pass in indicator output or something? Regiyhhster crosshair movements to the indicators output panel
|
||||
//create_legend(name, chart);
|
||||
}
|
||||
init(data){
|
||||
this.setLine('line',data, 'value');
|
||||
}
|
||||
update(data){
|
||||
this.updateLine('line', data, '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 exsist 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);
|
||||
|
||||
// todo: pass in indicator output or somthing? Register crosshair movements to the indicators output panel
|
||||
//create_legend(name, chart);
|
||||
}
|
||||
init(data){
|
||||
this.setLine('line',data, 'value');
|
||||
}
|
||||
update(data){
|
||||
this.updateLine('line', data, '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 exsist 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);
|
||||
|
||||
// todo: pass in indicator output or somthing? Register crosshair movements to the indicators output panel
|
||||
//create_legend(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], 'macd');
|
||||
this.updateLine('line_s', data[1], 'signal');
|
||||
this.addHist(name, data[3]);
|
||||
}
|
||||
}
|
||||
|
||||
class ATR extends Indicator{
|
||||
init(data) {
|
||||
this.updateDisplay(this.name, data.at(-1).value, 'value');
|
||||
}
|
||||
update(data) {
|
||||
this.updateDisplay(this.name, data.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);
|
||||
}
|
||||
}
|
||||
|
||||
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.line_u.update(data[0], 'value1');
|
||||
// Update the line-set data in the chart
|
||||
this.line_m.update(data[1]), 'value2';
|
||||
// Update the line-set data in the chart
|
||||
this.line_l.update(data[2], 'value3');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class Indicators {
|
||||
constructor(charts, idata) {
|
||||
// Create an array to hold all the created indicators.
|
||||
this.indicators = [];
|
||||
// Pass a list of indicators from received from the server
|
||||
// to a function that will create them.
|
||||
this.create_indicators(idata.indicators, charts);
|
||||
// Pass the initial indicator data to an initialize function for 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.indicators[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.indicators[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.indicators[name] = new MACD(name, charts, color_m, color_s);
|
||||
}
|
||||
if (i_type == 'Volume') { this.indicators[name] = new Volume(name, charts.chart_1); }
|
||||
if (i_type == 'ATR') { this.indicators[name] = new ATR(name); }
|
||||
if (i_type == 'LREG') {
|
||||
// The color of the line
|
||||
let color = indicators[name].color;
|
||||
this.indicators[name] = new Linear_Regression(name, charts.chart_1, color);
|
||||
}
|
||||
if (i_type == 'RSI') {
|
||||
let color = indicators[name].color;
|
||||
this.indicators[name] = new RSI(name, charts, color);
|
||||
}
|
||||
if (i_type == 'EMA') {
|
||||
// The color of the line
|
||||
let color = indicators[name].color;
|
||||
this.indicators[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.indicators[name].init(data[name]['data']);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
Reference in New Issue