Working and relatively glitch free. Classes mostly implemented in javascript files. Python is basically one big class with no separation.
This commit is contained in:
parent
5b2cfe31e9
commit
626fa9a1a6
|
|
@ -1,5 +1,5 @@
|
|||
chart_configuration:
|
||||
chart_interval: 4h
|
||||
chart_interval: 1m
|
||||
trading_pair: BTCUSDT
|
||||
indicator_list:
|
||||
ATR:
|
||||
|
|
@ -39,7 +39,7 @@ indicator_list:
|
|||
period: 100
|
||||
type: LREG
|
||||
value: '38139.51'
|
||||
visible: 'True'
|
||||
visible: true
|
||||
MACD:
|
||||
color_1: '#50d617'
|
||||
color_2: '#94f657'
|
||||
|
|
|
|||
130
static/chart.js
130
static/chart.js
|
|
@ -1,130 +0,0 @@
|
|||
var app_con;
|
||||
|
||||
//*******************Chart*********************
|
||||
//Reference the target div for the chart. Div was defined in index.html
|
||||
var container = document.getElementById('chart');
|
||||
//Create a chart object
|
||||
var chart = LightweightCharts.createChart(container, {
|
||||
width: 1000,
|
||||
height: 500,
|
||||
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
|
||||
});
|
||||
bind_charts(chart);
|
||||
chart.applyOptions({
|
||||
watermark: {visible: true,
|
||||
color: '#DBC29E',
|
||||
text: bt_data['trading_pair'],
|
||||
fontSize: 30,
|
||||
fontFamily: 'Roboto',
|
||||
fontStyle: 'bold',
|
||||
vertAlign: 'center'
|
||||
}
|
||||
});
|
||||
// - Create the candle stick series for our chart
|
||||
var candleSeries = chart.addCandlestickSeries();
|
||||
|
||||
//Fetch price history
|
||||
var price_history = fetch('http://localhost:5000/history')
|
||||
.then((r) => r.json())
|
||||
.then((response) => {
|
||||
return response;
|
||||
})
|
||||
|
||||
//Initialise the candlestick series
|
||||
price_history.then((ph) => {
|
||||
//Initialise the candle data
|
||||
candleSeries.setData(ph);
|
||||
//Initialise indicators
|
||||
indicator_init();
|
||||
})
|
||||
|
||||
/* Place functions here that need to
|
||||
be run everytime a new msg is received */
|
||||
function update_on_msg(new_candle){
|
||||
// Update candlestick series
|
||||
candleSeries.update(new_candle);
|
||||
// Update javascript coded indicators
|
||||
indicator_update_msg_received(new_candle);
|
||||
// Send a copy of the data to the server
|
||||
app_con.send( JSON.stringify({ message_type: "candle_data", data :new_candle }));
|
||||
}
|
||||
|
||||
/* Place functions that here that need to
|
||||
be run everytime a candle is closed */
|
||||
function update_on_candle_close(new_candle){
|
||||
// Send a copy of the data to the server
|
||||
app_con.send( JSON.stringify({ message_type: "candle_data", data :new_candle }));
|
||||
}
|
||||
|
||||
// Create a web socket connection to the exchange
|
||||
function set_websocket(interval){
|
||||
// Connect to our app
|
||||
app_con = new WebSocket('ws://localhost:5000/ws');
|
||||
app_con.onopen = () => app_con.send("Connection OK");
|
||||
|
||||
app_con.addEventListener('message', ev => {
|
||||
if(ev.data){
|
||||
// Get the message received from server
|
||||
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);
|
||||
indicator_update(msg.data)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
var ws = "wss://stream.binance.com:9443/ws/btcusdt@kline_" + interval;
|
||||
var binanceSocket = new WebSocket(ws);
|
||||
|
||||
// Set the on-message call-back for the socket
|
||||
binanceSocket.onmessage = function (event) {
|
||||
// Convert message to json obj
|
||||
var message = JSON.parse(event.data);
|
||||
// Isolate the candle data from message
|
||||
var candlestick = message.k;
|
||||
//console.log(message.k)
|
||||
// Reformat data for lightweight charts
|
||||
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
|
||||
update_on_msg(new_candle);
|
||||
// Only call if the new candle received a new time stamp.
|
||||
// Update the price history and per candle updated objects.
|
||||
price_history.then((ph) => {
|
||||
if ( new_candle.time > ph[ph.length-1].time) {
|
||||
ph.push(new_candle);
|
||||
update_on_candle_close(new_candle);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -9,9 +9,8 @@ class Charts {
|
|||
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=[];
|
||||
number of charts with their position and zoom factors bound.*/
|
||||
this.bound_charts=[];
|
||||
// Only the main chart is created by default.
|
||||
this.create_main_chart();
|
||||
}
|
||||
|
|
@ -36,6 +35,10 @@ class Charts {
|
|||
this.bind_charts(this.chart_1);
|
||||
}
|
||||
|
||||
update_main_chart(new_candle){
|
||||
// Update candlestick series
|
||||
this.candleSeries.update(new_candle);
|
||||
}
|
||||
|
||||
create_RSI_chart(){
|
||||
this.chart2 = this.create_chart(this.chart2_id, 100);
|
||||
|
|
@ -111,7 +114,7 @@ class Charts {
|
|||
// 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;
|
||||
let bcl = Object.keys(this.bound_charts).length;
|
||||
// if bound_charts has two element in it bind them
|
||||
if (bcl == 2) { this.bind2charts(); }
|
||||
|
||||
|
|
@ -122,65 +125,66 @@ class Charts {
|
|||
}
|
||||
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);
|
||||
if ( !this.bound_charts.includes(chart) ){
|
||||
this.bound_charts.push(chart);
|
||||
}
|
||||
}
|
||||
|
||||
bind2charts(){
|
||||
//On change in chart 1 change chart 2
|
||||
window.bound_charts[0].timeScale().subscribeVisibleTimeRangeChange(syncHandler1);
|
||||
function syncHandler1(e) {
|
||||
let 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();
|
||||
let barSpacing1 = this.bound_charts[0].timeScale().getBarSpacing();
|
||||
let scrollPosition1 = this.bound_charts[0].timeScale().scrollPosition();
|
||||
// Apply barSpacing(zoom) and position to 2nd chart.
|
||||
window.bound_charts[1].timeScale().applyOptions({ rightOffset: scrollPosition1, barSpacing: barSpacing1 });
|
||||
this.bound_charts[1].timeScale().applyOptions({ rightOffset: scrollPosition1, barSpacing: barSpacing1 });
|
||||
}
|
||||
this.bound_charts[0].timeScale().subscribeVisibleTimeRangeChange(syncHandler1);
|
||||
|
||||
//On change in chart 2 change chart 1
|
||||
window.bound_charts[1].timeScale().subscribeVisibleTimeRangeChange(syncHandler2);
|
||||
function syncHandler2(e) {
|
||||
let 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();
|
||||
let barSpacing2 = this.bound_charts[1].timeScale().getBarSpacing();
|
||||
let scrollPosition2 = this.bound_charts[1].timeScale().scrollPosition();
|
||||
// Apply barSpacing(zoom) and position to chart 1
|
||||
window.bound_charts[0].timeScale().applyOptions({ rightOffset: scrollPosition2, barSpacing: barSpacing2 });
|
||||
this.bound_charts[0].timeScale().applyOptions({ rightOffset: scrollPosition2, barSpacing: barSpacing2 });
|
||||
}
|
||||
this.bound_charts[1].timeScale().subscribeVisibleTimeRangeChange(syncHandler2);
|
||||
}
|
||||
bind3charts(){
|
||||
|
||||
//On change to chart 1 change chart 2 and 3
|
||||
window.bound_charts[0].timeScale().subscribeVisibleTimeRangeChange(syncHandler);
|
||||
function syncHandler(e) {
|
||||
let 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();
|
||||
let barSpacing1 = this.bound_charts[0].timeScale().getBarSpacing();
|
||||
let scrollPosition1 = this.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 });
|
||||
this.bound_charts[1].timeScale().applyOptions({ rightOffset: scrollPosition1, barSpacing: barSpacing1 });
|
||||
this.bound_charts[2].timeScale().applyOptions({ rightOffset: scrollPosition1, barSpacing: barSpacing1 });
|
||||
}
|
||||
this.bound_charts[0].timeScale().subscribeVisibleTimeRangeChange(syncHandler);
|
||||
|
||||
//On change to chart 2 change chart 1 and 3
|
||||
window.bound_charts[1].timeScale().subscribeVisibleTimeRangeChange(syncHandler2);
|
||||
function syncHandler2(e) {
|
||||
let 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();
|
||||
let barSpacing2 = this.bound_charts[1].timeScale().getBarSpacing();
|
||||
let scrollPosition2 = this.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 });
|
||||
this.bound_charts[0].timeScale().applyOptions({ rightOffset: scrollPosition2, barSpacing: barSpacing2 });
|
||||
this.bound_charts[2].timeScale().applyOptions({ rightOffset: scrollPosition2, barSpacing: barSpacing2 });
|
||||
}
|
||||
this.bound_charts[1].timeScale().subscribeVisibleTimeRangeChange(syncHandler2);
|
||||
|
||||
//On change to chart 3 change chart 1 and 2
|
||||
window.bound_charts[2].timeScale().subscribeVisibleTimeRangeChange(syncHandler3);
|
||||
function syncHandler3(e) {
|
||||
let 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();
|
||||
let barSpacing2 = this.bound_charts[2].timeScale().getBarSpacing();
|
||||
let scrollPosition2 = this.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 });
|
||||
this.bound_charts[0].timeScale().applyOptions({ rightOffset: scrollPosition2, barSpacing: barSpacing2 });
|
||||
this.bound_charts[1].timeScale().applyOptions({ rightOffset: scrollPosition2, barSpacing: barSpacing2 });
|
||||
}
|
||||
this.bound_charts[2].timeScale().subscribeVisibleTimeRangeChange(syncHandler3);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,26 +1,35 @@
|
|||
class Communication {
|
||||
constructor(interval, ocu, occ, oiu) {
|
||||
class Comms {
|
||||
constructor(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){
|
||||
// Call the callback provided.
|
||||
this.on_candle_update(new_candle);
|
||||
}
|
||||
|
||||
candle_close(new_candle){
|
||||
// Send a copy of the data to the server
|
||||
// Forward a copy of the new candle to the server.
|
||||
this.app_con.send( JSON.stringify({ message_type: "candle_data", data :new_candle }));
|
||||
// Call the callback provided.
|
||||
this.on_candle_close(new_candle);
|
||||
}
|
||||
|
||||
indicator_update(data){
|
||||
this.this.on_indctr_update(data);
|
||||
// Call the callback provided.
|
||||
this.on_indctr_update(data);
|
||||
}
|
||||
|
||||
getPriceHistory(){
|
||||
let ph = fetch('http://localhost:5000/history').then((r) => r.json()).then( (data) => { return data; } );
|
||||
return ph;
|
||||
}
|
||||
getIndicatorData(){
|
||||
let id = fetch('http://localhost:5000/indicator_init').then((r) => r.json()).then( (data) => { return data; } );
|
||||
return id;
|
||||
}
|
||||
|
||||
set_app_con(){
|
||||
|
|
@ -42,7 +51,6 @@ class Communication {
|
|||
if (msg.reply) {
|
||||
// Handle indicator updates
|
||||
if (msg.reply == 'i_updates'){
|
||||
// console.log(msg.data);
|
||||
this.indicator_update(msg.data)
|
||||
}
|
||||
}
|
||||
|
|
@ -60,7 +68,6 @@ class Communication {
|
|||
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,
|
||||
|
|
@ -73,7 +80,7 @@ class Communication {
|
|||
// Update frequently updated objects
|
||||
this.candle_update(new_candle);
|
||||
// Update less frequently updated objects.
|
||||
if (candlestick.x == true) { this.candle.close(new_candle); }
|
||||
if (candlestick.x == true) { this.candle_close(new_candle); }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
class Controls {
|
||||
constructor() {
|
||||
this.name = 'controls';
|
||||
}
|
||||
showAtPos(event, id) {
|
||||
// Function to display popup output at curser.
|
||||
// Used in the chart controls to enable/disable indicators.
|
||||
var el, x, y;
|
||||
|
||||
el = document.getElementById(id);
|
||||
if (window.event) {
|
||||
x = window.event.clientX + document.documentElement.scrollLeft
|
||||
+ document.body.scrollLeft;
|
||||
y = window.event.clientY + document.documentElement.scrollTop +
|
||||
+ document.body.scrollTop;
|
||||
}
|
||||
else {
|
||||
x = event.clientX + window.scrollX;
|
||||
y = event.clientY + window.scrollY;
|
||||
}
|
||||
// The popup hides on mouse out so the element is moved back a little to
|
||||
// ensure the mouse is still hovering over it when it gets displayed
|
||||
y = y - (10);
|
||||
x = x - (15);
|
||||
el.style.left = x + "px";
|
||||
el.style.top = y + "px";
|
||||
el.style.display = "block";
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,22 +1,55 @@
|
|||
class Data {
|
||||
constructor() {
|
||||
// Set the id's of the HTML elements that contain each section of the user interface.
|
||||
|
||||
// The id's of the HTML elements that contain sections 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.
|
||||
|
||||
/* Set into memory configuration data from the server. */
|
||||
// The assets being traded.
|
||||
this.trading_pair = bt_data.trading_pair;
|
||||
// The time period of the charts.
|
||||
this.interval = bt_data.interval;
|
||||
this.price_history = fetch('http://localhost:5000/history').then((r) => r.json()).then( (data) => { return data; } );
|
||||
// All the indicators available.
|
||||
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');
|
||||
|
||||
/* Comms handles communication with the servers. Pass it
|
||||
a list of callbacks to handle various incoming messages.*/
|
||||
this.comms = new Comms(this.candle_update, this.candle_close, this.indicator_update);
|
||||
// Open the connection to our local server.
|
||||
this.comms.set_app_con();
|
||||
/* Open connection for streaming candle data wth the exchange.
|
||||
Pass it the time period of candles to stream. */
|
||||
this.comms.set_exchange_con(this.interval);
|
||||
|
||||
//Request historical price data from the server.
|
||||
this.price_history = this.comms.getPriceHistory();
|
||||
|
||||
// Request from the server initialization data for the indicators.
|
||||
this.indicator_data = this.comms.getIndicatorData();
|
||||
|
||||
// Call back for indicator updates.
|
||||
this.i_updates = null;
|
||||
}
|
||||
|
||||
candle_close(){
|
||||
console.log('candle close');
|
||||
candle_update(new_candle){
|
||||
// This is called everytime a candle update comes from the local server.
|
||||
window.UI.charts.update_main_chart(new_candle);
|
||||
//console.log('Candle update:');
|
||||
//console.log(new_candle);
|
||||
}
|
||||
set_i_updates(call_back){
|
||||
this.i_updates = call_back;
|
||||
}
|
||||
indicator_update(data){
|
||||
// This is called everytime an indicator update come in.
|
||||
window.UI.data.i_updates(data);
|
||||
}
|
||||
|
||||
candle_close(new_candle){
|
||||
// This is called everytime a candle closes.
|
||||
//console.log('Candle close:');
|
||||
//console.log(new_candle);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,13 +10,6 @@
|
|||
// this.height = height;
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//class Controls {
|
||||
// constructor() {
|
||||
// this.height = height;
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//class Strategies {
|
||||
// constructor() {
|
||||
// this.height = height;
|
||||
|
|
@ -53,19 +46,14 @@
|
|||
// }
|
||||
//}
|
||||
//
|
||||
//class Indicator_Output {
|
||||
// constructor() {
|
||||
// this.height = height;
|
||||
// }
|
||||
//}
|
||||
|
||||
|
||||
class User_Interface{
|
||||
/* This contains the entire User interface.*/
|
||||
/* This contains all the code for our User interface.
|
||||
The code is separated into classes that maintain and
|
||||
provide the data and functionality of each panel of
|
||||
the UI. */
|
||||
constructor() {
|
||||
/* Create the objects that contain all the
|
||||
data and scripts required for each section of
|
||||
the User interface. */
|
||||
|
||||
/* Data object is responsible for fetching and maintaining
|
||||
up-to-date configurable and variable data for the UI */
|
||||
|
|
@ -82,9 +70,8 @@ class User_Interface{
|
|||
}
|
||||
this.charts = new Charts(chart_init_data);
|
||||
|
||||
/* The Indicators object is responsible for maintaining and
|
||||
interacting with the indicator section. As well as
|
||||
updating the display on the charts.*/
|
||||
/* The Indicators object is responsible for interactions with
|
||||
the edit indicator menu and updating the display on the charts.*/
|
||||
let ind_init_data = {
|
||||
indicators: this.data.indicators,
|
||||
indicator_data: this.data.indicator_data
|
||||
|
|
@ -92,12 +79,9 @@ class User_Interface{
|
|||
/* Pass the initialization for the indicators and a reference to
|
||||
the charts object so the indicators can update it directly.*/
|
||||
this.indicators = new Indicators(this.charts, ind_init_data);
|
||||
|
||||
this.communicate = new Communication(
|
||||
this.data.interval,
|
||||
this.data.candle_update,
|
||||
this.data.candle_close,
|
||||
this.indicators.update);
|
||||
this.data.set_i_updates(this.indicators.update);
|
||||
/* The object that handles the interface controls.*/
|
||||
this.controls = new Controls();
|
||||
}
|
||||
}
|
||||
UI = new User_Interface();
|
||||
|
|
|
|||
|
|
@ -1,3 +1,34 @@
|
|||
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.
|
||||
|
|
@ -29,6 +60,9 @@ class Indicator {
|
|||
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.
|
||||
|
|
@ -36,13 +70,13 @@ class Indicator {
|
|||
// 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;
|
||||
//Initialise the legend todo fix this
|
||||
//set_legend_text(rounded_value, i);
|
||||
}
|
||||
setHist(name, data){
|
||||
this.hist[name].setData(data);
|
||||
|
|
@ -50,8 +84,10 @@ class Indicator {
|
|||
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_name);
|
||||
this.updateDisplay(name, data.value, value_name);
|
||||
}
|
||||
updateHist(name,data){
|
||||
this.hist[name].update(data);
|
||||
|
|
@ -66,14 +102,12 @@ class SMA extends Indicator{
|
|||
// 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');
|
||||
this.updateLine('line', data[0], 'value');
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -87,20 +121,17 @@ 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 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);
|
||||
|
||||
// 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');
|
||||
this.updateLine('line', data[0], 'value');
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -108,7 +139,7 @@ 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 the chart doesn't exist create one.
|
||||
if( !charts.hasOwnProperty('chart3') ) { charts.create_MACD_chart(); }
|
||||
let chart = charts.chart3;
|
||||
|
||||
|
|
@ -116,19 +147,18 @@ class MACD extends Indicator{
|
|||
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]);
|
||||
this.updateLine('line_m', data[0][0], 'macd');
|
||||
this.updateLine('line_s', data[1][0], 'signal');
|
||||
this.updateHist(name, data[2][0]);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -137,7 +167,7 @@ class ATR extends Indicator{
|
|||
this.updateDisplay(this.name, data.at(-1).value, 'value');
|
||||
}
|
||||
update(data) {
|
||||
this.updateDisplay(this.name, data.value, 'value');
|
||||
this.updateDisplay(this.name, data[0].value, 'value');
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -153,7 +183,7 @@ class Volume extends Indicator{
|
|||
this.setHist(this.name, data);
|
||||
}
|
||||
update(data){
|
||||
this.updateHist(this.name, data);
|
||||
this.updateHist(this.name, data[0]);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -177,11 +207,11 @@ class Bolenger extends Indicator{
|
|||
|
||||
update(data){
|
||||
// Update the line-set data in the chart
|
||||
this.line_u.update(data[0], 'value1');
|
||||
this.updateLine('line_u', data[0][0], 'value1');
|
||||
// Update the line-set data in the chart
|
||||
this.line_m.update(data[1]), 'value2';
|
||||
this.updateLine('line_m', data[1][0], 'value2');
|
||||
// Update the line-set data in the chart
|
||||
this.line_l.update(data[2], 'value3');
|
||||
this.updateLine('line_l', data[2][0], 'value3');
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -189,11 +219,11 @@ class Bolenger extends Indicator{
|
|||
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
|
||||
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 an initialize function for the indicators.
|
||||
// Pass the initial indicator data to a function to initialize the indicators.
|
||||
idata.indicator_data.then( (data) => { this.init_indicators(data); } );
|
||||
|
||||
|
||||
|
|
@ -213,36 +243,36 @@ class Indicators {
|
|||
if (i_type == 'SMA') {
|
||||
// The color of the line
|
||||
let color = indicators[name].color;
|
||||
this.indicators[name] = new SMA(name, charts.chart_1, 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.indicators[name] = new Bolenger(name, charts.chart_1, color_u, color_m, color_l);
|
||||
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.indicators[name] = new MACD(name, charts, color_m, color_s);
|
||||
this.i_objs[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 == '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.indicators[name] = new Linear_Regression(name, charts.chart_1, color);
|
||||
this.i_objs[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);
|
||||
this.i_objs[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);
|
||||
this.i_objs[name] = new EMA(name, charts.chart_1, color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -250,9 +280,36 @@ class Indicators {
|
|||
// Loop through all the indicators.
|
||||
for (name in data){
|
||||
// Call the initialization function for each indicator.
|
||||
this.indicators[name].init(data[name]['data']);
|
||||
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();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,405 @@
|
|||
<html>
|
||||
<head>
|
||||
<!-- Load style sheets and set the title -->
|
||||
<link rel= "stylesheet" type= "text/css" href= "{{ url_for('static',filename='brighterStyles.css') }}">
|
||||
<title>{{ title }}</title>
|
||||
<!-- Load lightweight charts -->
|
||||
<script src="{{ url_for('static', filename='lightweight-charts.standalone.production.js') }}"></script>
|
||||
<!-- The server places initiation data in the HTML. This loads it into the DOM. -->
|
||||
<script type="text/javascript">
|
||||
function get_init_data(vars) {return vars}
|
||||
bt_data = get_init_data({{js_data|tojson}});
|
||||
</script>
|
||||
<!-- Load javascript -->
|
||||
<script src="{{ url_for('static', filename='data.js') }}"></script>
|
||||
<script src="{{ url_for('static', filename='indicators.js') }}"></script>
|
||||
<script src="{{ url_for('static', filename='charts.js') }}"></script>
|
||||
<script src="{{ url_for('static', filename='communication.js') }}"></script>
|
||||
<script src="{{ url_for('static', filename='controls.js') }}"></script>
|
||||
|
||||
<script>function fill_prop(){let a=1;}</script>
|
||||
|
||||
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<!-- Pop up forms for interface -->
|
||||
<div class="form-popup" id="new_sig_form">
|
||||
<form action="/new_signal" class="form-container">
|
||||
<!-- Panel 1 of 3 (5 rows, 2 columns) -->
|
||||
<div id="panel_1" class="form_panels" style="grid-template-columns:repeat(2,1fr);grid-template-rows: repeat(5,1fr);">
|
||||
<!-- Panel title (row 1/5)-->
|
||||
<h1 style="grid-column: 1 / span 2; grid-row: 1;">Add New Signal</h1>
|
||||
<!-- Name Input field (row 2/5)-->
|
||||
<div id = "SigName_div" style="grid-column: 1 / span 2; grid_row:2;">
|
||||
<label for='signal_name' >Signal Name:</label>
|
||||
<input type="text" id="signal_name" name="signal_name" />
|
||||
</div>
|
||||
<!-- Source Input field (row 3/5)-->
|
||||
<label for="sig_source" style="grid-column: 1; grid-row: 3;"><b>Signal source</b></label>
|
||||
<select name="sig_source" id="sig_source" style="grid-column: 2; grid-row: 3;" onchange= "fill_prop('sig_prop', this.value)">
|
||||
<!-- Jinja2 loop through and populate the options -->
|
||||
<!-- Uses namespace to set a var in global scope, so we can pass info to javascript later. -->
|
||||
{% set ns = namespace(optonVal=0) %}
|
||||
{% for each in indicator_list %}
|
||||
<option>{{each}}</option>
|
||||
{% if loop.index0 == 0 %}
|
||||
{% set ns.optonVal = each %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</select>
|
||||
<!-- Property Input field (row 4/5)-->
|
||||
<label style="grid-column: 1; grid-row: 4;" for="sig_prop"><b>Property</b></label>
|
||||
<select style="grid-column: 2; grid-row: 4;" id="sig_prop" name="sig_prop" >
|
||||
</select>
|
||||
<script>fill_prop('sig_prop', '{{ns.optonVal}}')</script>
|
||||
<!-- Input controls (row 5/5)-->
|
||||
<div style="grid-column: 1 / span 2; grid-row: 5;">
|
||||
<button type="button" class="btn cancel" onclick="close_signal_Form()">Close</button>
|
||||
<button type="button" class="btn next" onclick="ns_next(1)">Next</button>
|
||||
</div>
|
||||
</div><!----End panel 1--------->
|
||||
<!-- Panel 2 of 3 (6 rows, 2 columns) -->
|
||||
<div id="panel_2" class="form_panels" style="grid-template-columns:repeat(2,1fr);grid-template-rows: 1fr 35px 35px repeat(3,1fr);">
|
||||
<!-- Panel title(Signal_type) (row 1/6)-->
|
||||
<h1 style="grid-column: 1 / span 2; grid-row: 1;">Signal Type</h1>
|
||||
<!-- Type Input field (row 2/6) -->
|
||||
<div id = "Signal_type" style="grid-column: 1 / span 2; grid_row:2;">
|
||||
<label for="signal_type"><b>Signal Type:</b></label>
|
||||
<select name="signal_type" id="select_s_type" onchange="hideIfTrue(this.value,'Value','subpanel_1');hideIfTrue(this.value,'Comparison','subpanel_2');">
|
||||
<option>Value</option>
|
||||
<option>Comparison</option>
|
||||
</select>
|
||||
</div>
|
||||
<!-- display signal (row 3/6) -->
|
||||
<span id="sig_display" style="grid-column: 1 / span 2; grid_row: 3;">name:{property}(operator)name:{property}</span>
|
||||
<!-- Operator field (row 4/6) -->
|
||||
<div id="sig_operator" style="grid-column: 1 / span 2; grid_row: 4;">
|
||||
|
||||
<input type = "radio" id="gt" name="Operator" value=">">
|
||||
<label for = "gt">Greater than</label>
|
||||
|
||||
<input type="radio" id="lt" name="Operator" value="<">
|
||||
<label for = "lt">lessthan</label><br>
|
||||
|
||||
<input type = "radio" id="eq" name="Operator" value="=" checked="checked">
|
||||
<label for = "eq">Equals</label>
|
||||
|
||||
<input type="radio" id="within" name="Operator" value="+/-">
|
||||
<label for = "within">Within range</label>
|
||||
<input type="range" id="rangeSlider" min="0" max="1000" value="100" onchange="document.getElementById('rangeVal').value=this.value;">
|
||||
<input type="text" id="rangeVal" value="100" onchange="document.getElementById('rangeSlider').value=this.value;"/>
|
||||
</div>
|
||||
<!-- Compare sub-panel (row 5,6) -->
|
||||
<div id="subpanel_2" style="grid-column: 1 / span 2; grid_row: 5 / span 3;">
|
||||
<label for="value">Value:</label>
|
||||
<input id="value" name="value" type="number" value="100"/>
|
||||
</div>
|
||||
|
||||
<div id="subpanel_1" style="grid-column: 1 / span 2; grid_row: 5 / span 3;">
|
||||
<!-- Source Input field -->
|
||||
<label for="sig2_source" ><b>Signal source</b></label>
|
||||
<select name="sig2_source" id="sig2_source" onchange= "fill_prop('sig2_prop', this.value)">
|
||||
<!-- Jinja2 loop through and populate the options -->
|
||||
<!-- Uses namespace to set a var in global scope, so we can pass info to javascript later. -->
|
||||
{% set ns = namespace(optonVal=0) %}
|
||||
{% for each in indicator_list %}
|
||||
<option>{{each}}</option>
|
||||
{% if loop.index0 == 0 %}
|
||||
{% set ns.optonVal = each %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</select><br>
|
||||
<!-- Property Input field -->
|
||||
<label for="sig2_prop"><b>Property</b></label>
|
||||
<select id="sig2_prop" name="sig2_prop" >
|
||||
</select>
|
||||
<script>fill_prop('sig2_prop', '{{ns.optonVal}}')</script>
|
||||
</div>
|
||||
<!-- Input controls (row 6/6) -->
|
||||
<div class="padDiv" style="grid-column: 1/3; grid-row: 6;">
|
||||
<button type="button" class="btn" onclick="switch_panel('panel_2','panel_1')">Back</button>
|
||||
<button type="button" class="btn submit" onclick="ns_next(2)">Next</button>
|
||||
</div>
|
||||
</div><!----End panel 2--------->
|
||||
<!-- Panel 3 of 3 (6 rows, 2 columns) -->
|
||||
<div id="panel_3" class="form_panels" style="grid-template-columns:repeat(2,1fr);grid-template-rows: 1fr 35px 35px repeat(3,1fr);">
|
||||
<!-- Panel title(Create Signal) (row 1/6)-->
|
||||
<h1 style="grid-column: 1 / span 2; grid-row: 1;">Create Signal</h1>
|
||||
<!-- display signal (row 2/6) -->
|
||||
<span id="sig_display2" style="grid-column: 1 / span 2; grid_row: 2;"></span>
|
||||
<!-- display current Values (row 3/6) -->
|
||||
<span style="grid-column: 1 / span 2; grid_row: 3;">Current values</span>
|
||||
<!-- display realtime values (row 4/6) -->
|
||||
<span id="sig_realtime" style="grid-column: 1 / span 2; grid_row: 4;"></span>
|
||||
<!-- display current evaluation (row 4/6) -->
|
||||
<span style="grid-column: 1; grid_row: 4;">Currently evaluates to :</span>
|
||||
<span id="sig_eval" style="grid-column: 2; grid_row: 4;"></span>
|
||||
|
||||
<!-- Input controls (row 6/6) -->
|
||||
<div class="padDiv" style="grid-column: 1/3; grid-row: 6;">
|
||||
<button type="button" class="btn" onclick="switch_panel('panel_3','panel_2')">Back</button>
|
||||
<button type="button" class="btn submit" onclick="submitNewSignal()">Next</button>
|
||||
</div>
|
||||
</div><!----End panel 3--------->
|
||||
</form>
|
||||
</div>
|
||||
<!-- End pop up -->
|
||||
<!-- Hidden Div elements containing markup for popup context menus.-->
|
||||
<div id='indicators' onmouseleave="document.getElementById('indicators').style.display = 'none' ">
|
||||
<form action="/settings" method="post">
|
||||
<input type="hidden" name="setting" value="toggle_indicator"/>
|
||||
{% for indicator in indicator_list %}
|
||||
<input type="checkbox" id="{{ indicator }}" name="{{ indicator }}" value="indicator"{% if indicator in checked %} checked{%endif%}>
|
||||
<label for = "{{ indicator }}">{{ indicator }}</label><br>
|
||||
{% endfor %}
|
||||
<input type="submit" value="Submit Changes">
|
||||
</form>
|
||||
</div>
|
||||
<!-- Container for the whole user interface -->
|
||||
<div id="master_panel" class="master_panel">
|
||||
<!-- Chart Header -->
|
||||
<div id="app_header" name="app_header">
|
||||
<H1 id="app_title">{{ title }}</H1>
|
||||
</div><!-- end Chart Header -->
|
||||
|
||||
<!-- Container for the javascript chart -->
|
||||
<div id="chart">
|
||||
<div class="a1" >
|
||||
<!-- Chart specific controls -->
|
||||
<div id="chart_controls">
|
||||
<!-- Container target for any indicator output -->
|
||||
<div id="indicator_output" ></div>
|
||||
<!-- Time interval selector -->
|
||||
<form action="/settings" method="post">
|
||||
<input type="hidden" name="setting" value="interval" />
|
||||
<select id="timeframe" name="timeframe" selected="{{interval_state}}" onchange="this.form.submit()">
|
||||
{% for time_frame in intervals %}
|
||||
<option {% if time_frame == interval_state %} selected="selected" {%endif%}>{{ time_frame }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</form>
|
||||
<!-- Toggle On/Off indicators-->
|
||||
<button id="enable_indicators" type="button" onclick="UI.controls.showAtPos(event,'indicators')">Indicators</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Indicator charts -->
|
||||
<div id="chart2"></div>
|
||||
<div id="chart3"></div>
|
||||
|
||||
<!-- Right Panel -->
|
||||
<div id="right_panel">
|
||||
|
||||
<button class="collapsible bg_red" id="alerts_btn">Alerts</button>
|
||||
<div class="content">
|
||||
<p>No Alerts</p>
|
||||
</div>
|
||||
<button class="collapsible bg_blue">Exchange Info</button>
|
||||
<div id="bal_content" class="content">
|
||||
<div>
|
||||
<h3>Status</h3>
|
||||
<span id="exc_status">Not connected</span>
|
||||
</div>
|
||||
<div>
|
||||
<h3>Balances</h3>
|
||||
<div id="balances">
|
||||
<div id="balances_tbl">
|
||||
<table style="width:25%;">
|
||||
{% for balance in my_balances %}
|
||||
<tr><td>Asset</td><td>Balance</td><td>Profit&Loss</td></tr>
|
||||
<tr>
|
||||
<td>
|
||||
{{ balance['asset'] }}
|
||||
</td>
|
||||
<td>
|
||||
{{ balance['crossWalletBalance'] }}
|
||||
</td>
|
||||
<td>
|
||||
{{ balance['crossUnPnl'] }}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<h3>Active Tades</h3>
|
||||
<span>None</span>
|
||||
</div>
|
||||
<div>
|
||||
<h3>Open orders</h3>
|
||||
<span>None</span>
|
||||
</div>
|
||||
</div>
|
||||
<button class="collapsible bg_blue">Signals</button>
|
||||
<div class="content">
|
||||
<button class="new_btn" id="new_signal" onclick="open_signal_Form()">New Signal</button>
|
||||
<hr>
|
||||
<h3>Signals</h3>
|
||||
</div>
|
||||
<button class="collapsible bg_blue">Strategies</button>
|
||||
<div class="content">
|
||||
<button class="new_btn">New Strategy</button>
|
||||
<hr>
|
||||
<h3>Strategies</h3>
|
||||
</div>
|
||||
<button class="collapsible bg_blue">Statistics</button>
|
||||
<div class="content">
|
||||
<h3>Statistics</h3>
|
||||
</div>
|
||||
<button class="collapsible bg_blue">Backtesting</button>
|
||||
<div class="content">
|
||||
<h3>Back Testing</h3>
|
||||
</div>
|
||||
<button class="collapsible bg_blue">Trade</button>
|
||||
<div class="content">
|
||||
<div id="trade_content" class="cp_content">
|
||||
<form action="/trade" method="post">
|
||||
<input type="text" id="quantity" name="quantity" placeholder="eg. 0.001" />
|
||||
<select id="symbol" name="symbol">
|
||||
{% for symbol in symbols %}
|
||||
<option>{{ symbol['symbol'] }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
<input type="submit" name="buy" value="buy" />
|
||||
<input type="submit" name="sell" value="sell" />
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
var coll = document.getElementsByClassName("collapsible");
|
||||
var i;
|
||||
|
||||
for (i = 0; i < coll.length; i++) {
|
||||
coll[i].addEventListener("click", function() {
|
||||
this.classList.toggle("active");
|
||||
var content = this.nextElementSibling;
|
||||
if (content.style.maxHeight){
|
||||
content.style.maxHeight = null;
|
||||
} else {
|
||||
content.style.maxHeight = content.scrollHeight + "px";
|
||||
}
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
</div> <!-- End RightPanel -->
|
||||
|
||||
<!-- Edit Indicator Panel -->
|
||||
<div id="edit_indcr_panel">
|
||||
<!-- Edit Indicator Heading -->
|
||||
<div id="edit_indcr_head">
|
||||
<div id="h_name">Indicator Name</div>
|
||||
<div id="h_properties">Properties</div>
|
||||
</div>
|
||||
<hr>
|
||||
<!-- Edit Indicator Rows of individual forms to edit each indicator --!>
|
||||
{% for indicator in indicator_list %}
|
||||
<form action="/settings" method="post" >
|
||||
<input type="hidden" name="setting" value="edit_indicator"/>
|
||||
<div class="ierow">
|
||||
<div id="edit_indctr_controls">
|
||||
<button type="submit" name="delete" class="e_btn" value="{{indicator}}">✘</button>
|
||||
<button type="submit" name="submit" style ="color:darkgreen;" class="e_btn" value="{{indicator}}">✔</button>
|
||||
</div>
|
||||
<div class="iename">{{indicator}}</div>
|
||||
<div id="ieprop_container">
|
||||
{% for property in indicator_list[indicator] %}
|
||||
|
||||
{% set list1 = property.split('_') %}
|
||||
<div class="ieprop" >
|
||||
<label class="ietextbox" for="{{indicator}}_{{property}}">{{property}}</label>
|
||||
{% if property=='type' %}
|
||||
<select class="ietextbox" id="{{indicator}}_{{property}}" name="{{property}}">
|
||||
{% for i_type in indicator_types['simple_indicators'] %}
|
||||
<option value="{{i_type}}" {% if indicator_list[indicator][property] == i_type %} selected="selected"{%endif%}>{{i_type}}</option>
|
||||
{% endfor %}
|
||||
{% for i_type in indicator_types['other'] %}
|
||||
<option value="{{i_type}}" {% if indicator_list[indicator][property] == i_type %} selected="selected"{%endif%}>{{i_type}}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
{% elif property=='ma' %}
|
||||
<select class="ietextbox" id="{{indicator}}_{{property}}" name="{{property}}">
|
||||
{% for ma_val in ma_vals %}
|
||||
<option value="{{ma_vals[ma_val]}}" {% if indicator_list[indicator][property] == ma_vals[ma_val] %} selected="selected"{%endif%}>{{ma_val}}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
{% elif property=='color' or list1[0]=='color' %}
|
||||
<input class="ietextbox" type="color" id="{{indicator}}_{{property}}"
|
||||
value="{{indicator_list[indicator][property]}}"
|
||||
name="{{property}}">
|
||||
{% elif property=='period' %}
|
||||
<input class="ietextbox" type="number" id="{{indicator}}_{{property}}"
|
||||
value="{{indicator_list[indicator][property]}}"
|
||||
name="{{property}}">
|
||||
{% elif property=='visible' %}
|
||||
<input class="ietextbox" type="checkbox" id="{{indicator}}_{{property}}"
|
||||
value="{{indicator_list[indicator][property]}}"
|
||||
name="{{property}}"
|
||||
{% if indicator in checked %} checked{%endif%}>
|
||||
{% elif property=='value' %}
|
||||
<input class="ie_value" type="number" id="{{indicator}}_{{property}}"
|
||||
value="{{indicator_list[indicator][property]}}"
|
||||
name="{{property}}" readonly>
|
||||
{% else %}
|
||||
<input class="ietextbox" type="text" id="{{indicator}}_{{property}}"
|
||||
value="{{indicator_list[indicator][property]}}"
|
||||
name="{{property}}">
|
||||
{%endif%}
|
||||
</div><!-- end row of properties --!>
|
||||
{% endfor %}
|
||||
</div><!-- end row of property container --!>
|
||||
</div><!-- end of row container --!>
|
||||
</form>
|
||||
{% endfor %}
|
||||
<!-- End of Rows of individual forms to edit each indicator --!>
|
||||
<form action="/settings" method="post" id="new_i_form">
|
||||
<input type="hidden" name="setting" value="new_indicator"/>
|
||||
<hr>
|
||||
<!-- Create indicator div container --!>
|
||||
<div id="create_indcr_container">
|
||||
<h1>Add Indicator</h1>
|
||||
<label for "newi_name">Indicator Name</label><input type ="text" name="newi_name" value="New Indicator">
|
||||
<label for "newi_type">Type</label>
|
||||
<select id="newi_type" name="newi_type">
|
||||
{% for i_type in indicator_types['simple_indicators'] %}
|
||||
<option value="{{i_type}}">{{i_type}}</option>
|
||||
{% endfor %}
|
||||
{% for i_type in indicator_types['other'] %}
|
||||
<option value="{{i_type}}">{{i_type}}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
<br><br>
|
||||
<span>Properties: <span id="new_prop_list"></span></span>
|
||||
<button name="add_prop_clear" id="add_prop_clear" style="color:darkred;" type="button" value="add_prop_clear" onclick="document.getElementById('new_prop_list').innerHTML=''">✘</button>
|
||||
|
||||
<br><br>
|
||||
<div id="add_prop_container" name="add_prop_container">
|
||||
<label for "new_prop_name">Property Name</label>
|
||||
<input type="text" id="new_prop_name" name="new_prop_name" value="name">
|
||||
<br>
|
||||
<label for "new_prop_value">Property Value</label>
|
||||
<input type="text" id="new_prop_value" name="new_prop_value" value="value">
|
||||
<button name="add_prop" id="add_prop" type="button" value="add_prop" style="color:darkgreen;" onclick="UI.indicators.add_to_list()">✙</button>
|
||||
<label for "add_prop">Add Property</label>
|
||||
|
||||
</div>
|
||||
<br>
|
||||
<button type="button" onclick="UI.indicators.submit_new_i()" name="create_indicator" id="create_indicator" >Create New Indicator</button>
|
||||
<input type="hidden" id="new_prop_obj" name="new_prop_obj" value="">
|
||||
</div> <!-- End of Create indicator div container --!>
|
||||
</form>
|
||||
</div><!-- End of Edit Indicator Panel -->
|
||||
</div><!-- End Master Panel --->
|
||||
|
||||
<!-- TODO ANY REASON THIS IS IN LINE WITH HTML?
|
||||
<script src="{{ url_for('static', filename='chart.js') }}"></script>
|
||||
<script type="module" >set_websocket( "{{interval_state}}" );</script>-->
|
||||
<script src="{{ url_for('static', filename='general.js') }}"></script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
Loading…
Reference in New Issue