Working and relatively glitch free. Classes implemented in javascript. Python is basically one big class with no separation.

This commit is contained in:
Rob 2022-05-12 11:56:05 -03:00
parent 626fa9a1a6
commit 958ad4d4a5
7 changed files with 240 additions and 58 deletions

View File

@ -1,5 +1,5 @@
chart_configuration: chart_configuration:
chart_interval: 1m chart_interval: 4h
trading_pair: BTCUSDT trading_pair: BTCUSDT
indicator_list: indicator_list:
ATR: ATR:
@ -45,13 +45,13 @@ indicator_list:
color_2: '#94f657' color_2: '#94f657'
fast_p: 12 fast_p: 12
hist: 0 hist: 0
macd: 0 macd: '-2540.72'
signal: 0 signal: '-1775.26'
signal_p: 9 signal_p: 9
slow_p: 26 slow_p: 26
type: MACD type: MACD
value: 0 value: 0
visible: true visible: 'False'
New Indicator: New Indicator:
color: '#d5ed5e' color: '#d5ed5e'
period: 20 period: 20
@ -76,6 +76,12 @@ indicator_list:
type: RSI type: RSI
value: 0 value: 0
visible: true visible: true
RSI_21:
color: '#ea3ea2'
period: 21
type: RSI
value: '29.78'
visible: 'True'
SMA 200: SMA 200:
color: '#1d545c' color: '#1d545c'
period: 200 period: 200

View File

@ -791,7 +791,7 @@ class BrighterData:
def received_new_signal(self, data): def received_new_signal(self, data):
# Check the data. # Check the data.
if 'sigName' not in data: if 'sigName' not in data:
return 'No name provided' return 'data.py:received_new_signal() - The new signal has no name. '
Signal Signal
print(data) print(data)

View File

@ -31,7 +31,9 @@ class Comms {
let id = fetch('http://localhost:5000/indicator_init').then((r) => r.json()).then( (data) => { return data; } ); let id = fetch('http://localhost:5000/indicator_init').then((r) => r.json()).then( (data) => { return data; } );
return id; return id;
} }
send_to_app(message_type, data){
this.app_con.send( JSON.stringify({ message_type: message_type, data : data }));
}
set_app_con(){ set_app_con(){
// Create a web socket connection to our app. // Create a web socket connection to our app.
this.app_con = new WebSocket('ws://localhost:5000/ws'); this.app_con = new WebSocket('ws://localhost:5000/ws');

View File

@ -15,13 +15,6 @@
// this.height = height; // this.height = height;
// } // }
//} //}
//
//class Signals {
// constructor() {
// this.height = height;
// }
//}
//
//class Exchange_Info { //class Exchange_Info {
// constructor() { // constructor() {
// this.height = height; // this.height = height;
@ -59,29 +52,37 @@ class User_Interface{
up-to-date configurable and variable data for the UI */ up-to-date configurable and variable data for the UI */
this.data = new Data(); this.data = new Data();
/* These classes interact with HTML elements that need to be parsed first */
window.addEventListener('load', function () {
/* Charts object is responsible for maintaining the /* Charts object is responsible for maintaining the
data visualisation area in the UI. */ data visualisation area in the UI. */
let chart_init_data = { let chart_init_data = {
chart1_id : this.data.chart1_id, chart1_id : window.UI.data.chart1_id,
chart2_id : this.data.chart2_id, chart2_id : window.UI.data.chart2_id,
chart3_id : this.data.chart3_id, chart3_id : window.UI.data.chart3_id,
trading_pair : this.data.trading_pair, trading_pair : window.UI.data.trading_pair,
price_history : this.data.price_history price_history : window.UI.data.price_history
} }
this.charts = new Charts(chart_init_data); window.UI.charts = new Charts(chart_init_data);
/* The Indicators object is responsible for interactions with /* The Indicators object is responsible for interactions with
the edit indicator menu and updating the display on the charts.*/ the edit indicator menu and updating the display on the charts.*/
let ind_init_data = { let ind_init_data = {
indicators: this.data.indicators, indicators: window.UI.data.indicators,
indicator_data: this.data.indicator_data indicator_data: window.UI.data.indicator_data
} }
/* Pass the initialization for the indicators and a reference to /* Pass the initialization for the indicators and a reference to
the charts object so the indicators can update it directly.*/ the charts object so the indicators can update it directly.*/
this.indicators = new Indicators(this.charts, ind_init_data); window.UI.indicators = new Indicators(window.UI.charts, ind_init_data);
this.data.set_i_updates(this.indicators.update); /* Point the callback fired when indicator updates are received
to the indicator class object.*/
window.UI.data.set_i_updates(window.UI.indicators.update);
});
/* The object that handles the interface controls.*/ /* The object that handles the interface controls.*/
this.controls = new Controls(); this.controls = new Controls();
/* The object that handles the signals interface.*/
this.signals = new Signals(this.data.indicators);
} }
} }
UI = new User_Interface(); UI = new User_Interface();

View File

@ -285,6 +285,7 @@ class Indicators {
} }
update(updates){ update(updates){
console.log(updates);
for (name in updates){ for (name in updates){
window.UI.indicators.i_objs[name].update(updates[name].data); window.UI.indicators.i_objs[name].update(updates[name].data);
} }
@ -293,9 +294,9 @@ class Indicators {
add_to_list(){ add_to_list(){
// Adds user input to a list and displays it in a HTML element. // Adds user input to a list and displays it in a HTML element.
// Used in the Create indicator panel (!)Called from inline html. // Used in the Create indicator panel (!)Called from inline html.
var n = document.getElementById("new_prop_name").value; let n = document.getElementById("new_prop_name").value;
var v = document.getElementById("new_prop_value").value; let v = document.getElementById("new_prop_value").value;
p={}; let p={};
p[n] = v; p[n] = v;
if (document.getElementById("new_prop_list").innerHTML ==""){ if (document.getElementById("new_prop_list").innerHTML ==""){
document.getElementById("new_prop_list").insertAdjacentHTML('beforeend', JSON.stringify(p)); document.getElementById("new_prop_list").insertAdjacentHTML('beforeend', JSON.stringify(p));
@ -305,7 +306,7 @@ class Indicators {
submit_new_i(){ submit_new_i(){
/* Populates a hidden <input> with a value from another element then submits the form /* Populates a hidden <input> with a value from another element then submits the form
Used in the create indicator panel.*/ Used in the create indicator panel.*/
pl=document.getElementById("new_prop_list").innerHTML; let pl=document.getElementById("new_prop_list").innerHTML;
if(pl) { pl = '[' + pl + ']'; } if(pl) { pl = '[' + pl + ']'; }
document.getElementById("new_prop_obj").value=pl; document.getElementById("new_prop_obj").value=pl;
document.getElementById("new_i_form").submit(); document.getElementById("new_i_form").submit();

179
static/signals.js Normal file
View File

@ -0,0 +1,179 @@
class Signals {
constructor(indicators) {
this.indicators = indicators;
}
// Call to display Create new signal dialog.
open_signal_Form() { document.getElementById("new_sig_form").style.display = "grid"; }
// Call to hide Create new signal dialog.
close_signal_Form() { document.getElementById("new_sig_form").style.display = "none"; }
fill_prop(target_id, indctr){
// arg1: Id of of a selection element.
// arg2: Name of an indicator
// Replace the options of an HTML select elements
// with the properties an indicator object.
// Fetch the objects using name and id received.
var target = document.getElementById(target_id);
var properties = window.UI.data.indicators[indctr];
// Remove any previous options in the select tag.
removeOptions(target);
// Loop through each property in the object.
// Create an option element for each one.
// Append it to the selection element.
for(let prop in properties)
{
if (prop =='type'|| prop == 'visible' || prop == 'period'){continue;}
if (prop.substring(0,5) == 'color'){continue;}
var opt = document.createElement("option");
opt.innerHTML = prop;
target.appendChild(opt);
}
return;
function removeOptions(selectElement) {
var i, L = selectElement.options.length - 1;
for(i = L; i >= 0; i--) {
selectElement.remove(i);
}
}
}
switch_panel(p1,p2){
// Panel switcher for multi page forms
// arg1 = target from id
// arg2 = next target id
// This function is used in the New Signal dialog in signals
document.getElementById(p1).style.display='none';
document.getElementById(p2).style.display='grid';
}
hideIfTrue(firstValue, scndValue, id){
// Compare first two args and hides an element if they are equal.
// This function is used in the New Signal dialog in signals
if( firstValue == scndValue){
document.getElementById(id).style.display='none';
}else{
document.getElementById(id).style.display='block'
}
}
ns_next(n){
// This function is used in the New Signal dialog in signals
if (n==1){
// Check input fields.
let sigName = document.getElementById('signal_name').value;
let sigSource = document.getElementById('sig_source').value;
let sigProp = document.getElementById('sig_prop').value;
if (sigName == '' ) { alert('Please give the signal a name.'); return; }
// Populate sig_display
document.getElementById('sig_display').innerHTML = (sigName + ': {' + sigSource + ':' + sigProp +'}');
// Popilate Value input
let indctrVal = document.getElementById(sigSource + '_' + sigProp).value;
document.getElementById('value').value = indctrVal;
this.switch_panel('panel_1','panel_2');
}
if (n==2){
// Collect all the input fields.
let sigName = document.getElementById('signal_name').value; // The name of the New Signal.
let sigSource = document.getElementById('sig_source').value; // The source(indicator) of the signal.
let sigProp = document.getElementById('sig_prop').value; // The property to evaluate.
let sig2Source = document.getElementById('sig2_source').value; // The second source if selected.
let sig2Prop = document.getElementById('sig2_prop').value; // The second property to evaluate.
let operator = document.querySelector('input[name="Operator"]:checked').value; // The operator this evaluation will use.
let range = document.getElementById('rangeVal').value; // The value of any range being evaluated.
let sigType = document.getElementById('select_s_type').value; // The type of signal value or indicator comparison.
let value = document.getElementById('value').value; // The input value if it is a value comparison.
// Create a string to define the signal.
// Include the first indicator source.
var sig1 = `${sigSource} : ${sigProp}`;
// If it is a comparison signal include the second indicator source.
if (sigType == 'Comparison') {
var sig2 = `${sig2Source} : ${sig2Prop}`;
}
// If it is a value signal include the value.
if (sigType == 'Value') {var sig2 = value;}
// If the operator is set to range, include the range value in the string.
if (operator == '+/-') {
var operatorStr = `${operator} ${range}`;
} else{
var operatorStr = operator;
}
let sigDisplayStr = `(${sigName}) (${sig1}) (${operatorStr}) (${sig2})`;
// Get the current realtime values of the sources.
let sig1_realtime = document.getElementById(sigSource + '_' + sigProp).value;
if (sigType == 'Comparison') {
// If its a comparison get the second value from the second source.
var sig2_realtime = document.getElementById(sig2Source + '_' + sig2Prop).value;
}else {
// If not the second realtime value is literally the value.
var sig2_realtime = sig2;
}
// Populate the signal display field with the string.
document.getElementById('sig_display2').innerHTML = sigDisplayStr;
// Populate the realtime values display.
let realtime_Str = `(${sigProp} : ${sig1_realtime}) (${operatorStr}) (${sig2_realtime})`;
document.getElementById('sig_realtime').innerHTML = realtime_Str;
// Evaluate the signal
var evalStr;
if (operator == '=') {evalStr = (sig1_realtime == sig2_realtime);console.log([sig1_realtime, sig2_realtime, operator,evalStr]);}
if (operator == '>') {evalStr = (sig1_realtime > sig2_realtime);}
if (operator == '<') {evalStr = (sig1_realtime < sig2_realtime);}
if (operator == '+/-') {
evalStr = (Math.abs(sig1_realtime - sig2_realtime) <= range);
}
// Populate the signal eval field with the string.
document.getElementById('sig_eval').innerHTML = evalStr;
// Show the panel
this.switch_panel('panel_2','panel_3');
}
}
submitNewSignal(){
// Collect all the input fields.
var sigName = document.getElementById('signal_name').value; // The name of the New Signal.
var sigSource = document.getElementById('sig_source').value; // The source(indicator) of the signal.
var sigProp = document.getElementById('sig_prop').value; // The property to evaluate.
var sig2Source = document.getElementById('sig2_source').value; // The second source if selected.
var sig2Prop = document.getElementById('sig2_prop').value; // The second property to evaluate.
var operator = document.querySelector('input[name="Operator"]:checked').value; // The operator this evaluation will use.
var range = document.getElementById('rangeVal').value; // The value of any range being evaluated.
var sigType = document.getElementById('select_s_type').value; // The type of signal value or indicator comparison.
var value = document.getElementById('value').value; // The input value if it is a value comparison.
var sigName = {sigName : sigName}; // Name_of_signal
var sigSource = {sigSource : sigSource}; // First_signal_indicator.
var sigProp = {sigProp : sigProp}; // First_signal_property.
var operator = {operator : operator}; // Operator.
if (sigType == 'Comparison'){
var sig2Source = {sig2Source: sig2Source};
var sig2Prop = {sig2Prop : sig2Prop};
}else{
var sig2Source = {sig2Source: value};
var sig2Prop = {value: value};
}
if (operator == "'operator' : '+/-'" ){
var range = {range : range};
var data = [sigName, sigSource, sigProp, operator, sig2Source, sig2Prop, range];
}else{
var data = [sigName, sigSource, sigProp, operator, sig2Source, sig2Prop];
}
/* It may be more maintainable to configure the connection inside the different classes
than passing functions, references and callbacks around. */
window.UI.data.comms.send_to_app( "new_signal", data);
}
}

View File

@ -16,10 +16,8 @@
<script src="{{ url_for('static', filename='charts.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='communication.js') }}"></script>
<script src="{{ url_for('static', filename='controls.js') }}"></script> <script src="{{ url_for('static', filename='controls.js') }}"></script>
<script src="{{ url_for('static', filename='signals.js') }}"></script>
<script>function fill_prop(){let a=1;}</script> <script src="{{ url_for('static', filename='general.js') }}"></script>
</head> </head>
<body> <body>
@ -37,7 +35,7 @@
</div> </div>
<!-- Source Input field (row 3/5)--> <!-- Source Input field (row 3/5)-->
<label for="sig_source" style="grid-column: 1; grid-row: 3;"><b>Signal source</b></label> <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)"> <select name="sig_source" id="sig_source" style="grid-column: 2; grid-row: 3;" onchange= "UI.signals.fill_prop('sig_prop', this.value)">
<!-- Jinja2 loop through and populate the options --> <!-- Jinja2 loop through and populate the options -->
<!-- Uses namespace to set a var in global scope, so we can pass info to javascript later. --> <!-- Uses namespace to set a var in global scope, so we can pass info to javascript later. -->
{% set ns = namespace(optonVal=0) %} {% set ns = namespace(optonVal=0) %}
@ -52,11 +50,11 @@
<label style="grid-column: 1; grid-row: 4;" for="sig_prop"><b>Property</b></label> <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 style="grid-column: 2; grid-row: 4;" id="sig_prop" name="sig_prop" >
</select> </select>
<script>fill_prop('sig_prop', '{{ns.optonVal}}')</script> <script>UI.signals.fill_prop('sig_prop', '{{ns.optonVal}}')</script>
<!-- Input controls (row 5/5)--> <!-- Input controls (row 5/5)-->
<div style="grid-column: 1 / span 2; grid-row: 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 cancel" onclick="UI.signals.close_signal_Form()">Close</button>
<button type="button" class="btn next" onclick="ns_next(1)">Next</button> <button type="button" class="btn next" onclick="UI.signals.ns_next(1)">Next</button>
</div> </div>
</div><!----End panel 1---------> </div><!----End panel 1--------->
<!-- Panel 2 of 3 (6 rows, 2 columns) --> <!-- Panel 2 of 3 (6 rows, 2 columns) -->
@ -66,7 +64,7 @@
<!-- Type Input field (row 2/6) --> <!-- Type Input field (row 2/6) -->
<div id = "Signal_type" style="grid-column: 1 / span 2; grid_row:2;"> <div id = "Signal_type" style="grid-column: 1 / span 2; grid_row:2;">
<label for="signal_type"><b>Signal Type:</b></label> <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');"> <select name="signal_type" id="select_s_type" onchange="UI.signals.hideIfTrue(this.value,'Value','subpanel_1');UI.signals.hideIfTrue(this.value,'Comparison','subpanel_2');">
<option>Value</option> <option>Value</option>
<option>Comparison</option> <option>Comparison</option>
</select> </select>
@ -99,7 +97,7 @@
<div id="subpanel_1" style="grid-column: 1 / span 2; grid_row: 5 / span 3;"> <div id="subpanel_1" style="grid-column: 1 / span 2; grid_row: 5 / span 3;">
<!-- Source Input field --> <!-- Source Input field -->
<label for="sig2_source" ><b>Signal source</b></label> <label for="sig2_source" ><b>Signal source</b></label>
<select name="sig2_source" id="sig2_source" onchange= "fill_prop('sig2_prop', this.value)"> <select name="sig2_source" id="sig2_source" onchange= "UI.signals.fill_prop('sig2_prop', this.value)">
<!-- Jinja2 loop through and populate the options --> <!-- Jinja2 loop through and populate the options -->
<!-- Uses namespace to set a var in global scope, so we can pass info to javascript later. --> <!-- Uses namespace to set a var in global scope, so we can pass info to javascript later. -->
{% set ns = namespace(optonVal=0) %} {% set ns = namespace(optonVal=0) %}
@ -114,12 +112,12 @@
<label for="sig2_prop"><b>Property</b></label> <label for="sig2_prop"><b>Property</b></label>
<select id="sig2_prop" name="sig2_prop" > <select id="sig2_prop" name="sig2_prop" >
</select> </select>
<script>fill_prop('sig2_prop', '{{ns.optonVal}}')</script> <script>UI.signals.fill_prop('sig2_prop', '{{ns.optonVal}}')</script>
</div> </div>
<!-- Input controls (row 6/6) --> <!-- Input controls (row 6/6) -->
<div class="padDiv" style="grid-column: 1/3; grid-row: 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" onclick="UI.signals.switch_panel('panel_2','panel_1')">Back</button>
<button type="button" class="btn submit" onclick="ns_next(2)">Next</button> <button type="button" class="btn submit" onclick="UI.signals.ns_next(2)">Next</button>
</div> </div>
</div><!----End panel 2---------> </div><!----End panel 2--------->
<!-- Panel 3 of 3 (6 rows, 2 columns) --> <!-- Panel 3 of 3 (6 rows, 2 columns) -->
@ -138,8 +136,8 @@
<!-- Input controls (row 6/6) --> <!-- Input controls (row 6/6) -->
<div class="padDiv" style="grid-column: 1/3; grid-row: 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" onclick="UI.signals.switch_panel('panel_3','panel_2')">Back</button>
<button type="button" class="btn submit" onclick="submitNewSignal()">Next</button> <button type="button" class="btn submit" onclick="UI.signals.submitNewSignal()">Next</button>
</div> </div>
</div><!----End panel 3---------> </div><!----End panel 3--------->
</form> </form>
@ -236,7 +234,7 @@
</div> </div>
<button class="collapsible bg_blue">Signals</button> <button class="collapsible bg_blue">Signals</button>
<div class="content"> <div class="content">
<button class="new_btn" id="new_signal" onclick="open_signal_Form()">New Signal</button> <button class="new_btn" id="new_signal" onclick="UI.signals.open_signal_Form()">New Signal</button>
<hr> <hr>
<h3>Signals</h3> <h3>Signals</h3>
</div> </div>
@ -396,10 +394,5 @@
</div><!-- End of Edit Indicator Panel --> </div><!-- End of Edit Indicator Panel -->
</div><!-- End Master 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> </body>
</html> </html>