The strategy section is generating code. Implemented code is mostly functional.
This commit is contained in:
parent
38de03c022
commit
034b8ab925
|
|
@ -267,7 +267,7 @@ class UserAccountManagement(BaseUser):
|
|||
username = f'guest_{suffix}'
|
||||
|
||||
# Check if the username already exists in the database
|
||||
if not self.data.get_rows_from_datacache(cache_name='users', filter_vals=[('user_name', username)]):
|
||||
if self.data.get_rows_from_datacache(cache_name='users', filter_vals=[('user_name', username)]).empty:
|
||||
return username
|
||||
|
||||
attempts += 1
|
||||
|
|
|
|||
|
|
@ -1,379 +1,519 @@
|
|||
|
||||
class Strategies {
|
||||
constructor(target_id) {
|
||||
// The list of strategies.
|
||||
this.strategies = [];
|
||||
// The html element id that displays the strategies.
|
||||
// The HTML element id that displays the strategies.
|
||||
this.target_id = target_id;
|
||||
// The html element that displays the the strategies.
|
||||
// The HTML element that displays the strategies.
|
||||
this.target = null;
|
||||
this.workspace = null;
|
||||
|
||||
}
|
||||
// Create the Blockly workspace and define custom blocks
|
||||
createWorkspace() {
|
||||
// Dispose of the existing workspace before creating a new one
|
||||
if (this.workspace) {this.workspace.dispose();}
|
||||
if (this.workspace) {
|
||||
this.workspace.dispose();
|
||||
}
|
||||
|
||||
// Define custom blocks before initializing the workspace
|
||||
this.defineCustomBlocks();
|
||||
this.defineIndicatorBlocks();
|
||||
|
||||
// Initialize Blockly workspace
|
||||
this.workspace = Blockly.inject('blocklyDiv', {
|
||||
toolbox: document.getElementById('toolbox'),
|
||||
scrollbars: true,
|
||||
trashcan: true,
|
||||
});
|
||||
|
||||
// Define Python generators after workspace initialization
|
||||
this.definePythonGenerators();
|
||||
}
|
||||
// Generate Python code from the Blockly workspace and return as JSON
|
||||
generateStrategyJson() {
|
||||
var code = Blockly.JavaScript.workspaceToCode(this.workspace);
|
||||
var json = {
|
||||
strategy: code
|
||||
// Initialize Python generator with the current workspace
|
||||
Blockly.Python.init(this.workspace);
|
||||
|
||||
// Generate Python code from the Blockly workspace
|
||||
const pythonCode = Blockly.Python.workspaceToCode(this.workspace);
|
||||
|
||||
// Create a strategy object with generated code
|
||||
const json = {
|
||||
name: document.getElementById('name_box').value,
|
||||
code: pythonCode // This is the generated Python code
|
||||
};
|
||||
|
||||
return JSON.stringify(json);
|
||||
}
|
||||
// Call to display Create new signal dialog.
|
||||
open_form() { document.getElementById("new_strat_form").style.display = "grid"; }
|
||||
// Call to hide Create new signal dialog.
|
||||
close_form() { document.getElementById("new_strat_form").style.display = "none"; }
|
||||
|
||||
// Show the "Create New Strategy" form (open the workspace)
|
||||
open_form() {
|
||||
const formElement = document.getElementById("new_strat_form");
|
||||
|
||||
// Check if the form element exists
|
||||
if (formElement) {
|
||||
formElement.style.display = "grid"; // Open the form
|
||||
} else {
|
||||
console.error('Form element "new_strat_form" not found.');
|
||||
}
|
||||
}
|
||||
|
||||
// Hide the "Create New Strategy" form
|
||||
close_form() {
|
||||
const formElement = document.getElementById("new_strat_form");
|
||||
|
||||
if (formElement) {
|
||||
formElement.style.display = "none"; // Close the form
|
||||
} else {
|
||||
console.error('Form element "new_strat_form" not found.');
|
||||
}
|
||||
}
|
||||
|
||||
// Submit the strategy and send it to the server
|
||||
submit() {
|
||||
/*
|
||||
- Collect the data from the form fields and
|
||||
create a json object representing a strategy.
|
||||
- Append the strategy to a local list of strategies.
|
||||
- Update the display output.
|
||||
- Send the strategy info to the server.
|
||||
*/
|
||||
let strat = {};
|
||||
strat.name = document.getElementById('stg_name').value;
|
||||
if (strat.name == ''){
|
||||
alert('Please provide a name.');
|
||||
return;
|
||||
}
|
||||
// The type of strategy will determine underlying feature of its execution.
|
||||
strat.type = document.getElementById('strat_type').value;
|
||||
// Whether the strategy is bullish or bearish.
|
||||
strat.side = document.getElementById('trade_in_side').value;
|
||||
// Position size for each trade the strategy executes.
|
||||
strat.trade_amount = document.getElementById('trade_amount').value;
|
||||
// The maximum combined position allowed.
|
||||
strat.max_position = document.getElementById('strgy_total').value;
|
||||
// The fee the exchange charges per trade.
|
||||
strat.trading_fee = document.getElementById('fee').value;
|
||||
// The maximum allowable loss the strategy can endure in combined trades.
|
||||
// Before trading out and terminating execution.
|
||||
strat.max_loss = document.getElementById('max_loss').value;
|
||||
// The trading pair being exchanged.
|
||||
strat.symbol = window.UI.data.trading_pair;
|
||||
// The combined profit or loss including trading fees.
|
||||
strat.net_pl = 0;
|
||||
// The combined profit or loss.
|
||||
strat.gross_pl = 0;
|
||||
// The quantity of the traded assets being held.
|
||||
strat.combined_position = 0;
|
||||
// Opening value of the asset.
|
||||
strat.opening_value = 0;
|
||||
// The current value of the asset.
|
||||
strat.current_value = 0;
|
||||
// Whether or not the strategy has begun trading.
|
||||
strat.active = false;
|
||||
// The conditions that must be met before trading in.
|
||||
strat.trd_in_conds = {};
|
||||
let conds = Array.from(document.querySelectorAll('#trade_in_cond>li'));
|
||||
for (let cond of conds){
|
||||
let json_obj = JSON.parse(cond.innerHTML);
|
||||
strat.trd_in_conds[json_obj.Trigger] = json_obj.Value;
|
||||
}
|
||||
// The conditions that must be met before taking profit.
|
||||
let take_profit = {};
|
||||
take_profit.typ = document.getElementById('prof_typ').value;
|
||||
if (take_profit.typ == 'conditional'){
|
||||
take_profit.trig = document.getElementById('prof_trig').value;
|
||||
take_profit.val = document.getElementById('prof_trigVal').value;
|
||||
}else{
|
||||
take_profit.val = document.getElementById('prof_val').value;
|
||||
}
|
||||
strat.take_profit = take_profit;
|
||||
// Generate the Python code as JSON
|
||||
const strategyJson = this.generateStrategyJson();
|
||||
|
||||
// The conditions that must be met before taking a loss.
|
||||
let stop_loss = {};
|
||||
stop_loss.typ = document.getElementById('loss_typ').value;
|
||||
if ( stop_loss.typ == 'conditional' ){
|
||||
stop_loss.trig = document.getElementById('loss_trig').value;
|
||||
stop_loss.val = document.getElementById('loss_trigVal').value;
|
||||
}else{
|
||||
stop_loss.val = document.getElementById('loss_val').value;
|
||||
// Send the strategy to the server
|
||||
fetch('/new_strategy', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: strategyJson
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => console.log('Strategy submitted successfully:', data))
|
||||
.catch(error => console.error('Error submitting strategy:', error));
|
||||
}
|
||||
strat.stop_loss = stop_loss;
|
||||
|
||||
// Add the strategy to the instance list.
|
||||
this.strategies.push(strat);
|
||||
// Update the display of the created strategies
|
||||
update_html() {
|
||||
let stratsHtml = '';
|
||||
const onClick = "window.UI.strats.del(this.value);";
|
||||
for (let strat of this.strategies) {
|
||||
let deleteButton = `<button type='button' name='delete' class='e_btn' value='${strat.name}' onclick='${onClick}'>✘</button>`;
|
||||
stratsHtml += `<li id='${strat.name}_item'>${deleteButton}<pre>${JSON.stringify(strat, null, 2)}</pre></li>`;
|
||||
}
|
||||
document.getElementById(this.target_id).innerHTML = stratsHtml;
|
||||
}
|
||||
|
||||
// Add the strategy to display.
|
||||
this.update_html();
|
||||
|
||||
// Send the new strategy to the server.
|
||||
window.UI.data.comms.sendToApp( "new_strategy", strat);
|
||||
|
||||
// Close the html form.
|
||||
this.close_form();
|
||||
}
|
||||
update_received(stg_updts){
|
||||
if ( 'cmd' in stg_updts) {
|
||||
let alert =
|
||||
window.UI.alerts.publish_alerts('strategy', stg_updts);
|
||||
this.executeCmd(stg_updts.cmd);
|
||||
}
|
||||
console.log('recieved stategy update.');
|
||||
console.log(stg_updts);
|
||||
}
|
||||
set_data(strats){
|
||||
for (let strat of strats){
|
||||
// Add the strategy to the instance list.
|
||||
this.strategies.push(JSON.parse(strat));
|
||||
}
|
||||
// Add the strategy to display.
|
||||
this.update_html();
|
||||
}
|
||||
// Open the form and create the Blockly workspace
|
||||
open_stg_form() {
|
||||
this.open_form();
|
||||
this.createWorkspace()
|
||||
this.createWorkspace();
|
||||
}
|
||||
clear_innerHTML(el){
|
||||
el.innerHTML="";
|
||||
}
|
||||
dropDown(name, label, parent, options){
|
||||
/* Create a html selection element and append it to a parent element.
|
||||
name: name and id of the element.
|
||||
label: text displayed beside the selection list element.
|
||||
parent: the html element to append to.
|
||||
options: An array of selection options.
|
||||
*/
|
||||
let lbl = document.createElement("label");
|
||||
lbl.for = name;
|
||||
lbl.id = name + '_lbl';
|
||||
lbl.innerHTML = label;
|
||||
let select = document.createElement("select");
|
||||
select.id = name;
|
||||
select.name = name;
|
||||
for(let option of options){
|
||||
select.innerHTML += '<option>' + option + '</option>';
|
||||
}
|
||||
parent.appendChild(lbl);
|
||||
parent.appendChild(select);
|
||||
}
|
||||
val_input(name, label, parent, min, max, i_val, style = null){
|
||||
/* Create an input element.
|
||||
name: name and id of the element.
|
||||
label: text displayed beside the element.
|
||||
parent: the html element to append to.
|
||||
min, max, i_val: Range and initialization values.
|
||||
*/
|
||||
let lbl = document.createElement("label");
|
||||
lbl.for = name;
|
||||
lbl.id = name + '_lbl';
|
||||
lbl.innerHTML = label;
|
||||
let input = document.createElement("input");
|
||||
input.name = name;
|
||||
input.id = name;
|
||||
input.type = 'number';
|
||||
input.min = min;
|
||||
if (max) {input.max = max;}
|
||||
input.value = i_val;
|
||||
if (style){
|
||||
input.style = style;
|
||||
}
|
||||
parent.appendChild(lbl);
|
||||
parent.appendChild(input);
|
||||
|
||||
}
|
||||
hideShowTags(elements, cmd){
|
||||
/* Hides or shows 1 or many elements.
|
||||
Receives either string or array of string.
|
||||
*/
|
||||
if (typeof(elements) == 'string'){
|
||||
elements = [elements];
|
||||
}
|
||||
for(let el of elements){
|
||||
if(cmd== 'hide'){document.getElementById(el).style.display = 'none';}
|
||||
if(cmd== 'show'){document.getElementById(el).style.display = 'inline-block';}
|
||||
}
|
||||
}
|
||||
fill_field(field, value){
|
||||
if (field == 'strat_opt'){
|
||||
let options = document.getElementById('strat_opt');
|
||||
|
||||
if (value == 'in-out'){
|
||||
// Clear previous content.
|
||||
this.clear_innerHTML(options);
|
||||
|
||||
// Add a horizontal rule.
|
||||
options.appendChild(document.createElement('hr'));
|
||||
|
||||
//Create a drop down for buy/sell option
|
||||
this.dropDown('trade_in_side','Side:', options, ['buy','sell']);
|
||||
|
||||
//Create an input for the margin. (1-100)
|
||||
this.val_input('margin_select', 'Margin:', options, 1,100, 50);
|
||||
|
||||
// Add a line break.
|
||||
options.appendChild( document.createElement('br') );
|
||||
|
||||
//Create an input for the amount. (1-*)
|
||||
this.val_input('trade_amount', 'Trade amount:', options, 1, 0, 1,'width: 54px;');
|
||||
|
||||
//Create an input for the amount. (1-*)
|
||||
this.val_input('strgy_total', 'Strategy total:', options, 1, 0, 10, 'width: 54px;');
|
||||
|
||||
// Add a line break.
|
||||
options.appendChild( document.createElement('br') );
|
||||
|
||||
//Create an input for the fee. (0.01-1)
|
||||
this.val_input('fee', 'Trading Fee:', options, 0.001, 1, 0.025);
|
||||
|
||||
// Create a un ordered list to hold the conditions of trading in.
|
||||
let ul_in_cond = document.createElement('ul');
|
||||
ul_in_cond.id ='trade_in_cond';
|
||||
|
||||
// Create a submit button for the conditions.
|
||||
let add_cond_btn = document.createElement('button');
|
||||
add_cond_btn.setAttribute('id','add_cond_btn');
|
||||
add_cond_btn.setAttribute('type','button');
|
||||
add_cond_btn.innerHTML = "Add Condition";
|
||||
add_cond_btn.onclick = function () {
|
||||
let li = document.createElement('li');
|
||||
li.innerHTML = JSON.stringify({ Trigger: trigger.value, Value: trigVal.value });
|
||||
ul_in_cond.appendChild(li);
|
||||
};
|
||||
|
||||
// Add a horizontal rule.
|
||||
options.appendChild(document.createElement('hr'));
|
||||
|
||||
//Create a drop down for trigger options
|
||||
// Get the signal options from the signal instance.
|
||||
let ops = [];
|
||||
for (let signal of window.UI.signals.signals) {ops.push(signal.name);}
|
||||
this.dropDown('trigger','Trigger Signal:', options, ops);
|
||||
|
||||
|
||||
//Create a drop down for trigger value.
|
||||
this.dropDown('trigVal','Trigger Value:', options, ['true', 'false', 'changed']);
|
||||
|
||||
// Add the submit btn and the list to the dom.
|
||||
options.appendChild(add_cond_btn);
|
||||
options.appendChild(ul_in_cond);
|
||||
|
||||
// Add a horizontal rule.
|
||||
options.appendChild(document.createElement('hr'));
|
||||
|
||||
//Create a drop down for take profit type.
|
||||
this.dropDown('prof_typ','Profit type:', options, ['value', 'conditional']);
|
||||
// Add and onchange function to show and hide some options.
|
||||
let that = this;
|
||||
document.getElementById('prof_typ').onchange= function(){
|
||||
if (this.value == 'value'){
|
||||
that.hideShowTags(['prof_val_lbl','prof_val'], 'show');
|
||||
that.hideShowTags(['prof_trig_lbl', 'prof_trig', 'prof_trigVal_lbl', 'prof_trigVal'], 'hide');
|
||||
}
|
||||
else if (this.value == 'conditional'){
|
||||
that.hideShowTags(['prof_val_lbl','prof_val'], 'hide');
|
||||
that.hideShowTags(['prof_trig_lbl', 'prof_trig', 'prof_trigVal_lbl', 'prof_trigVal'], 'show');
|
||||
}
|
||||
};
|
||||
|
||||
// Add a line break.
|
||||
options.appendChild(document.createElement('br'));
|
||||
|
||||
// Create an input for take profit value.
|
||||
this.val_input('prof_val', 'Profit %:', options, 1,100, 50);
|
||||
// Set display to visible.
|
||||
this.hideShowTags(['prof_val_lbl','prof_val'], 'show');
|
||||
|
||||
// Create an input for take profit signal trigger.
|
||||
// Get the signal options from the signal instance.
|
||||
ops = []; for (let signal of window.UI.signals.signals) {ops.push(signal.name);}
|
||||
this.dropDown('prof_trig','Profit trigger:', options, ops);
|
||||
// Set display to hidden.
|
||||
this.hideShowTags(['prof_trig_lbl','prof_trig'], 'hide');
|
||||
|
||||
//Create a drop down for take profit trigger value.
|
||||
this.dropDown('prof_trigVal','Trigger Value:', options, ['true', 'false', 'changed']);
|
||||
// Set display to hidden.
|
||||
this.hideShowTags(['prof_trigVal_lbl','prof_trigVal'], 'hide');
|
||||
|
||||
// Add a line break.
|
||||
options.appendChild(document.createElement('br'));
|
||||
// Add a horizontal rule.
|
||||
options.appendChild(document.createElement('hr'));
|
||||
|
||||
// Create an input for tolerable loss.
|
||||
this.val_input('max_loss', 'Max loss :', options, 1,100, 50);
|
||||
// Add a line break.
|
||||
options.appendChild(document.createElement('br'));
|
||||
|
||||
//Create a drop down for Stop Loss type.
|
||||
this.dropDown('loss_typ','Stop-Loss type:', options, ['value', 'conditional']);
|
||||
// Add and onchange function to show and hide some options.
|
||||
document.getElementById('loss_typ').onchange= function(){
|
||||
if (this.value == 'value'){
|
||||
that.hideShowTags(['loss_val_lbl','loss_val'], 'show');
|
||||
that.hideShowTags(['loss_trig_lbl', 'loss_trig', 'loss_trigVal_lbl', 'loss_trigVal'], 'hide');
|
||||
}
|
||||
else if (this.value == 'conditional'){
|
||||
that.hideShowTags(['loss_val_lbl','loss_val'], 'hide');
|
||||
that.hideShowTags(['loss_trig_lbl', 'loss_trig', 'loss_trigVal_lbl', 'loss_trigVal'], 'show');
|
||||
}
|
||||
};
|
||||
|
||||
// Add a line break.
|
||||
options.appendChild( document.createElement('br') );
|
||||
|
||||
// Create an input for stop loss value.
|
||||
this.val_input('loss_val', 'Loss %:', options, 1,100, 50);
|
||||
// Set display to visible.
|
||||
this.hideShowTags(['loss_val_lbl','loss_val'], 'show');
|
||||
|
||||
// Create an input for take profit signal trigger.
|
||||
// Get the signal options from the signal instance.
|
||||
ops = []; for (let signal of window.UI.signals.signals) {ops.push(signal.name);}
|
||||
this.dropDown('loss_trig','Stop-Loss trigger:', options, ops);
|
||||
// Set display to hidden.
|
||||
this.hideShowTags(['loss_trig_lbl','loss_trig'], 'hide');
|
||||
|
||||
//Create a drop down for take profit trigger value.
|
||||
this.dropDown('loss_trigVal','Loss Value:', options, ['true', 'false', 'changed']);
|
||||
// Set display to hidden.
|
||||
this.hideShowTags(['loss_trigVal_lbl','loss_trigVal'], 'hide');
|
||||
|
||||
// Add a line break.
|
||||
options.appendChild(document.createElement('br'));
|
||||
// Add a horizontal rule.
|
||||
options.appendChild(document.createElement('hr'));
|
||||
|
||||
}
|
||||
if (value == 'incremental_profits'){
|
||||
options.innerHTML="Incremental_profits -> not done.";
|
||||
}
|
||||
if (value == 'swing'){
|
||||
options.innerHTML="swing -> not done.";
|
||||
}
|
||||
}
|
||||
}
|
||||
initialize(){
|
||||
// This is called after the html document has been parsed.
|
||||
this.target = document.getElementById(this.target_id);
|
||||
// Send a request to the server for any loaded data.
|
||||
window.UI.data.comms.sendToApp('request', 'strategies');
|
||||
}
|
||||
// Delete a strategy by its name
|
||||
del(name) {
|
||||
window.UI.data.comms.sendToApp('delete_strategy', name);
|
||||
// Get the child element node
|
||||
// Remove the strategy from the UI
|
||||
let child = document.getElementById(name + '_item');
|
||||
// Remove the child element from the document
|
||||
child.parentNode.removeChild(child);
|
||||
}
|
||||
update_html(){
|
||||
let strats ='';
|
||||
let on_click = " window.UI.strats.del(this.value);";
|
||||
for (let strat of this.strategies){
|
||||
let button ="<button type='button' name='delete' class='e_btn' value='" + strat.name + "' onclick='" + on_click + "'>✘</button>";
|
||||
strats += "<li id='" + strat.name + "_item'>" + button + "<pre>" + JSON.stringify(strat) + "</pre></li>";
|
||||
|
||||
// Initialize the Strategies UI component
|
||||
initialize() {
|
||||
this.target = document.getElementById(this.target_id);
|
||||
if (!this.target) {
|
||||
console.error('Target element', this.target_id, 'not found.');
|
||||
}
|
||||
}
|
||||
// Define Blockly blocks dynamically based on indicators
|
||||
defineIndicatorBlocks() {
|
||||
const indicatorOutputs = window.UI.indicators.getIndicatorOutputs();
|
||||
const toolboxCategory = document.querySelector('#toolbox category[name="Indicators"]');
|
||||
|
||||
for (let indicatorName in indicatorOutputs) {
|
||||
const outputs = indicatorOutputs[indicatorName];
|
||||
|
||||
// Define the block for this indicator
|
||||
Blockly.defineBlocksWithJsonArray([{
|
||||
"type": indicatorName,
|
||||
"message0": `${indicatorName} %1`,
|
||||
"args0": [
|
||||
{
|
||||
"type": "field_dropdown",
|
||||
"name": "OUTPUT",
|
||||
"options": outputs.map(output => [output, output])
|
||||
}
|
||||
],
|
||||
"output": "Number",
|
||||
"colour": 230,
|
||||
"tooltip": `Select the ${indicatorName} output`,
|
||||
"helpUrl": ""
|
||||
}]);
|
||||
|
||||
// Define how this block will generate Python code
|
||||
Blockly.Python[indicatorName] = Blockly.Python.forBlock[indicatorName] = function(block) {
|
||||
const selectedOutput = block.getFieldValue('OUTPUT');
|
||||
const code = `get_${indicatorName.toLowerCase()}_value('${selectedOutput}')`;
|
||||
return [code, Blockly.Python.ORDER_ATOMIC];
|
||||
};
|
||||
|
||||
// Append dynamically created blocks to the Indicators category in the toolbox
|
||||
const blockElement = document.createElement('block');
|
||||
blockElement.setAttribute('type', indicatorName);
|
||||
toolboxCategory.appendChild(blockElement);
|
||||
}
|
||||
this.target.innerHTML = strats;
|
||||
}
|
||||
|
||||
// Define custom Blockly blocks and Python code generation
|
||||
defineCustomBlocks() {
|
||||
// Custom block for retrieving last candle values
|
||||
Blockly.defineBlocksWithJsonArray([
|
||||
{
|
||||
"type": "last_candle_value",
|
||||
"message0": "Last candle %1 value",
|
||||
"args0": [
|
||||
{
|
||||
"type": "field_dropdown",
|
||||
"name": "CANDLE_PART",
|
||||
"options": [
|
||||
["Open", "open"],
|
||||
["High", "high"],
|
||||
["Low", "low"],
|
||||
["Close", "close"]
|
||||
]
|
||||
}
|
||||
],
|
||||
"output": "Number",
|
||||
"colour": 230,
|
||||
"tooltip": "Get the value of the last candle.",
|
||||
"helpUrl": ""
|
||||
}
|
||||
]);
|
||||
|
||||
// Comparison Block (for Candle Close > Candle Open, etc.)
|
||||
Blockly.defineBlocksWithJsonArray([
|
||||
{
|
||||
"type": "comparison",
|
||||
"message0": "%1 %2 %3",
|
||||
"args0": [
|
||||
{
|
||||
"type": "input_value",
|
||||
"name": "LEFT"
|
||||
},
|
||||
{
|
||||
"type": "field_dropdown",
|
||||
"name": "OPERATOR",
|
||||
"options": [
|
||||
[">", ">"],
|
||||
["<", "<"],
|
||||
["==", "=="]
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "input_value",
|
||||
"name": "RIGHT"
|
||||
}
|
||||
],
|
||||
"inputsInline": true,
|
||||
"output": "Boolean",
|
||||
"colour": 160,
|
||||
"tooltip": "Compare two values.",
|
||||
"helpUrl": ""
|
||||
}
|
||||
]);
|
||||
|
||||
Blockly.defineBlocksWithJsonArray([{
|
||||
"type": "trade_action",
|
||||
"message0": "if %1 then %2 with Stop Loss %3 and Take Profit %4 (Options) %5",
|
||||
"args0": [
|
||||
{
|
||||
"type": "input_value",
|
||||
"name": "CONDITION",
|
||||
"check": "Boolean"
|
||||
},
|
||||
{
|
||||
"type": "field_dropdown",
|
||||
"name": "TRADE_TYPE",
|
||||
"options": [
|
||||
["Buy", "buy"],
|
||||
["Sell", "sell"]
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "input_value",
|
||||
"name": "STOP_LOSS",
|
||||
"check": "Number"
|
||||
},
|
||||
{
|
||||
"type": "input_value",
|
||||
"name": "TAKE_PROFIT",
|
||||
"check": "Number"
|
||||
},
|
||||
{
|
||||
"type": "input_statement",
|
||||
"name": "TRADE_OPTIONS",
|
||||
"check": "trade_option" // This will accept the chain of trade options
|
||||
}
|
||||
],
|
||||
"previousStatement": null,
|
||||
"nextStatement": null,
|
||||
"colour": 230,
|
||||
"tooltip": "Executes a trade with optional stop loss, take profit, and trade options",
|
||||
"helpUrl": ""
|
||||
}]);
|
||||
|
||||
|
||||
// Stop Loss Block
|
||||
Blockly.defineBlocksWithJsonArray([{
|
||||
"type": "stop_loss",
|
||||
"message0": "Stop Loss %1",
|
||||
"args0": [
|
||||
{
|
||||
"type": "input_value",
|
||||
"name": "STOP_LOSS",
|
||||
"check": "Number"
|
||||
}
|
||||
],
|
||||
"output": "Number",
|
||||
"colour": 230,
|
||||
"tooltip": "Sets a stop loss value",
|
||||
"helpUrl": ""
|
||||
}]);
|
||||
|
||||
// Take Profit Block
|
||||
Blockly.defineBlocksWithJsonArray([{
|
||||
"type": "take_profit",
|
||||
"message0": "Take Profit %1",
|
||||
"args0": [
|
||||
{
|
||||
"type": "input_value",
|
||||
"name": "TAKE_PROFIT",
|
||||
"check": "Number"
|
||||
}
|
||||
],
|
||||
"output": "Number",
|
||||
"colour": 230,
|
||||
"tooltip": "Sets a take profit value",
|
||||
"helpUrl": ""
|
||||
}]);
|
||||
// Logical AND Block
|
||||
Blockly.defineBlocksWithJsonArray([{
|
||||
"type": "logical_and",
|
||||
"message0": "%1 AND %2",
|
||||
"args0": [
|
||||
{
|
||||
"type": "input_value",
|
||||
"name": "LEFT",
|
||||
"check": "Boolean"
|
||||
},
|
||||
{
|
||||
"type": "input_value",
|
||||
"name": "RIGHT",
|
||||
"check": "Boolean"
|
||||
}
|
||||
],
|
||||
"inputsInline": true,
|
||||
"output": "Boolean",
|
||||
"colour": 210,
|
||||
"tooltip": "Logical AND of two conditions",
|
||||
"helpUrl": ""
|
||||
}]);
|
||||
// Logical OR Block
|
||||
Blockly.defineBlocksWithJsonArray([{
|
||||
"type": "logical_or",
|
||||
"message0": "%1 OR %2",
|
||||
"args0": [
|
||||
{
|
||||
"type": "input_value",
|
||||
"name": "LEFT",
|
||||
"check": "Boolean"
|
||||
},
|
||||
{
|
||||
"type": "input_value",
|
||||
"name": "RIGHT",
|
||||
"check": "Boolean"
|
||||
}
|
||||
],
|
||||
"inputsInline": true,
|
||||
"output": "Boolean",
|
||||
"colour": 210,
|
||||
"tooltip": "Logical OR of two conditions",
|
||||
"helpUrl": ""
|
||||
}]);
|
||||
// "is" Block
|
||||
Blockly.defineBlocksWithJsonArray([{
|
||||
"type": "is_true",
|
||||
"message0": "%1 is true",
|
||||
"args0": [
|
||||
{
|
||||
"type": "input_value",
|
||||
"name": "CONDITION",
|
||||
"check": "Boolean"
|
||||
}
|
||||
],
|
||||
"output": "Boolean",
|
||||
"colour": 160,
|
||||
"tooltip": "Checks if the condition is true",
|
||||
"helpUrl": ""
|
||||
}]);
|
||||
// Order Type Block with Limit Price
|
||||
Blockly.defineBlocksWithJsonArray([{
|
||||
"type": "order_type",
|
||||
"message0": "Order Type %1 %2",
|
||||
"args0": [
|
||||
{
|
||||
"type": "field_dropdown",
|
||||
"name": "ORDER_TYPE",
|
||||
"options": [
|
||||
["Market", "market"],
|
||||
["Limit", "limit"]
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "input_value",
|
||||
"name": "LIMIT_PRICE", // Input for limit price when Limit order is selected
|
||||
"check": "Number"
|
||||
}
|
||||
],
|
||||
"previousStatement": "trade_option",
|
||||
"nextStatement": "trade_option",
|
||||
"colour": 230,
|
||||
"tooltip": "Select order type (Market or Limit) with optional limit price",
|
||||
"helpUrl": ""
|
||||
}]);
|
||||
Blockly.defineBlocksWithJsonArray([{
|
||||
"type": "value_input",
|
||||
"message0": "Value %1",
|
||||
"args0": [
|
||||
{
|
||||
"type": "field_number",
|
||||
"name": "VALUE",
|
||||
"value": 0,
|
||||
"min": 0
|
||||
}
|
||||
],
|
||||
"output": "Number",
|
||||
"colour": 230,
|
||||
"tooltip": "Enter a numerical value",
|
||||
"helpUrl": ""
|
||||
}]);
|
||||
// Time In Force (TIF) Block
|
||||
Blockly.defineBlocksWithJsonArray([{
|
||||
"type": "time_in_force",
|
||||
"message0": "Time in Force %1",
|
||||
"args0": [
|
||||
{
|
||||
"type": "field_dropdown",
|
||||
"name": "TIF",
|
||||
"options": [
|
||||
["GTC (Good Till Canceled)", "gtc"],
|
||||
["FOK (Fill or Kill)", "fok"],
|
||||
["IOC (Immediate or Cancel)", "ioc"]
|
||||
]
|
||||
}
|
||||
],
|
||||
"previousStatement": "trade_option",
|
||||
"nextStatement": "trade_option",
|
||||
"colour": 230,
|
||||
"tooltip": "Select time in force for the order",
|
||||
"helpUrl": ""
|
||||
}]);
|
||||
console.log('Custom blocks defined');
|
||||
|
||||
}
|
||||
|
||||
// Define Python generators for custom blocks
|
||||
definePythonGenerators() {
|
||||
// Last candle value to Python code
|
||||
Blockly.Python['last_candle_value'] = Blockly.Python.forBlock['last_candle_value'] = function(block) {
|
||||
var candlePart = block.getFieldValue('CANDLE_PART');
|
||||
var code = `market.get_last_candle_value('${candlePart}')`;
|
||||
return [code, Blockly.Python.ORDER_ATOMIC];
|
||||
};
|
||||
|
||||
// Comparison block to Python code
|
||||
Blockly.Python['comparison'] = Blockly.Python.forBlock['comparison'] = function(block) {
|
||||
const left = Blockly.Python.valueToCode(block, 'LEFT', Blockly.Python.ORDER_ATOMIC);
|
||||
const right = Blockly.Python.valueToCode(block, 'RIGHT', Blockly.Python.ORDER_ATOMIC);
|
||||
const operator = block.getFieldValue('OPERATOR');
|
||||
return [left + ' ' + operator + ' ' + right, Blockly.Python.ORDER_ATOMIC];
|
||||
};
|
||||
|
||||
// Logical OR block to Python code
|
||||
Blockly.Python['logical_or'] = Blockly.Python.forBlock['logical_or'] = function(block) {
|
||||
var left = Blockly.Python.valueToCode(block, 'LEFT', Blockly.Python.ORDER_ATOMIC);
|
||||
var right = Blockly.Python.valueToCode(block, 'RIGHT', Blockly.Python.ORDER_ATOMIC);
|
||||
var code = `${left} or ${right}`;
|
||||
return [code, Blockly.Python.ORDER_ATOMIC];
|
||||
};
|
||||
|
||||
// Logical AND block to Python code
|
||||
Blockly.Python['logical_and'] = Blockly.Python.forBlock['logical_and'] = function(block) {
|
||||
var left = Blockly.Python.valueToCode(block, 'LEFT', Blockly.Python.ORDER_ATOMIC);
|
||||
var right = Blockly.Python.valueToCode(block, 'RIGHT', Blockly.Python.ORDER_ATOMIC);
|
||||
var code = `${left} and ${right}`;
|
||||
return [code, Blockly.Python.ORDER_ATOMIC];
|
||||
};
|
||||
|
||||
// Stop Loss block to Python code
|
||||
Blockly.Python['stop_loss'] = Blockly.Python.forBlock['stop_loss'] = function(block) {
|
||||
var stopLoss = Blockly.Python.valueToCode(block, 'STOP_LOSS', Blockly.Python.ORDER_ATOMIC);
|
||||
return [stopLoss, Blockly.Python.ORDER_ATOMIC];
|
||||
};
|
||||
|
||||
// Take Profit block to Python code
|
||||
Blockly.Python['take_profit'] = Blockly.Python.forBlock['take_profit'] = function(block) {
|
||||
var takeProfit = Blockly.Python.valueToCode(block, 'TAKE_PROFIT', Blockly.Python.ORDER_ATOMIC);
|
||||
return [takeProfit, Blockly.Python.ORDER_ATOMIC];
|
||||
};
|
||||
|
||||
// Is True block to Python code
|
||||
Blockly.Python['is_true'] = Blockly.Python.forBlock['is_true'] = function(block) {
|
||||
var condition = Blockly.Python.valueToCode(block, 'CONDITION', Blockly.Python.ORDER_ATOMIC);
|
||||
var code = `${condition}`;
|
||||
return [code, Blockly.Python.ORDER_ATOMIC];
|
||||
};
|
||||
|
||||
// Trade Action block to Python code
|
||||
Blockly.Python['trade_action'] = Blockly.Python.forBlock['trade_action'] = function(block) {
|
||||
alert('Generating Python for trade_action block'); // Debugging alert
|
||||
|
||||
var condition = Blockly.Python.valueToCode(block, 'CONDITION', Blockly.Python.ORDER_ATOMIC);
|
||||
var tradeType = block.getFieldValue('TRADE_TYPE');
|
||||
var stopLoss = Blockly.Python.valueToCode(block, 'STOP_LOSS', Blockly.Python.ORDER_ATOMIC) || 'None';
|
||||
var takeProfit = Blockly.Python.valueToCode(block, 'TAKE_PROFIT', Blockly.Python.ORDER_ATOMIC) || 'None';
|
||||
var tradeOptions = Blockly.Python.statementToCode(block, 'TRADE_OPTIONS').trim();
|
||||
|
||||
var code = `if ${condition}:\n`;
|
||||
code += ` ${tradeType}_order(stop_loss=${stopLoss}, take_profit=${takeProfit}`;
|
||||
|
||||
// Include trade options if they are set
|
||||
if (tradeOptions) {
|
||||
code += `, ${tradeOptions}`;
|
||||
}
|
||||
|
||||
code += `)\n`;
|
||||
return code;
|
||||
};
|
||||
|
||||
// Order Type block to Python code
|
||||
Blockly.Python['order_type'] = Blockly.Python.forBlock['order_type'] = function(block) {
|
||||
var orderType = block.getFieldValue('ORDER_TYPE');
|
||||
var limitPrice = Blockly.Python.valueToCode(block, 'LIMIT_PRICE', Blockly.Python.ORDER_ATOMIC) || 'None';
|
||||
|
||||
// If it's a limit order, include the limit price in the output
|
||||
if (orderType === 'limit') {
|
||||
return `order_type='limit', limit_price=${limitPrice}`;
|
||||
} else {
|
||||
return `order_type='market'`;
|
||||
}
|
||||
};
|
||||
|
||||
// Value Input block to Python code
|
||||
Blockly.Python['value_input'] = Blockly.Python.forBlock['value_input'] = function(block) {
|
||||
var value = block.getFieldValue('VALUE');
|
||||
return [value.toString(), Blockly.Python.ORDER_ATOMIC]; // Returning both value and precedence
|
||||
};
|
||||
|
||||
// Time in Force block to Python code
|
||||
Blockly.Python['time_in_force'] = Blockly.Python.forBlock['time_in_force'] = function(block) {
|
||||
var tif = block.getFieldValue('TIF');
|
||||
return `tif='${tif}'`;
|
||||
};
|
||||
|
||||
console.log('Python generators defined');
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,84 +1,59 @@
|
|||
|
||||
class 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() {
|
||||
// Initialize all components needed by the user interface
|
||||
this.strats = new Strategies('strats_display'); // Handles strategies display and interaction
|
||||
this.exchanges = new Exchanges(); // Manages exchange-related data
|
||||
this.data = new Data(); // Stores and maintains application-wide data
|
||||
this.controls = new Controls(); // Handles user controls
|
||||
this.signals = new Signals(this.data.indicators); // Manages signals used in strategies
|
||||
this.alerts = new Alerts("alert_list"); // Manages the alerts system
|
||||
this.trade = new Trade(); // Manages trade-related data and operations
|
||||
this.users = new Users(); // Handles user login and account details
|
||||
this.indicators = new Indicators(this.data.comms); // Manages indicators and interaction with charts
|
||||
|
||||
/*The Exchanges class is is responsible for maintaining
|
||||
and retrieving exchanges data from the server. Also
|
||||
issuing exchange related commands to the server.*/
|
||||
this.exchanges = new Exchanges();
|
||||
|
||||
/* Data object is responsible for fetching and maintaining
|
||||
up-to-date configurable and variable data for the UI */
|
||||
this.data = new Data();
|
||||
|
||||
/* The object that handles the interface controls.*/
|
||||
this.controls = new Controls();
|
||||
|
||||
/* The object that handles the signals interface.*/
|
||||
this.signals = new Signals(this.data.indicators);
|
||||
|
||||
/* The object that handles alerts. Pass in the html
|
||||
element that will hold the list of alerts*/
|
||||
this.alerts = new Alerts("alert_list");
|
||||
|
||||
/* The object that handles alerts. Pass in the html
|
||||
element that will hold the strategies interface.*/
|
||||
this.strats = new Strategies("strats_display");
|
||||
|
||||
/* The object that handles trades. Pass in the html
|
||||
element that will hold the trade interface.*/
|
||||
this.trade = new Trade();
|
||||
|
||||
/* The object that handles user log in */
|
||||
this.users = new Users();
|
||||
|
||||
/* Indicators contains methods that interact with indicator
|
||||
related menus and update indicator output in charts and panels. */
|
||||
this.indicators = new Indicators(this.data.comms);
|
||||
|
||||
/* Register an Indicator callback method to receive updates from the data class. */
|
||||
// Register a callback function for when indicator updates are received from the data object
|
||||
this.data.registerCallback_i_updates(this.indicators.update);
|
||||
|
||||
window.addEventListener('load', function () {
|
||||
/* This javascript files are loaded before the entire
|
||||
HTML document gets parsed. Place initializers here
|
||||
to be executed after the document is loaded. */
|
||||
// Initialize all components after the page has loaded and Blockly is ready
|
||||
this.initializeAll();
|
||||
}
|
||||
|
||||
/* Charts object is responsible for maintaining the
|
||||
data visualisation area in the UI. */
|
||||
/**
|
||||
* Initializes all components after the DOM has loaded.
|
||||
* This includes initializing charts, signals, strategies, and other interface components.
|
||||
* Components that rely on external libraries like Blockly are initialized after they are ready.
|
||||
*/
|
||||
initializeAll() {
|
||||
// Use an arrow function to preserve the value of 'this' (User_Interface instance)
|
||||
window.addEventListener('load', () => {
|
||||
// Initialize the Charts component with the required data
|
||||
let chart_init_data = {
|
||||
chart1_id : window.UI.data.chart1_id,
|
||||
chart2_id : window.UI.data.chart2_id,
|
||||
chart3_id : window.UI.data.chart3_id,
|
||||
trading_pair : window.UI.data.trading_pair,
|
||||
price_history : window.UI.data.price_history
|
||||
}
|
||||
window.UI.charts = new Charts(chart_init_data);
|
||||
|
||||
/* Pass the initialization data to indicators. */
|
||||
let ind_init_data = {
|
||||
// A list of indicator object available for display.
|
||||
indicators: window.UI.data.indicators,
|
||||
// Output data - indicator specific format. Example: list of indexed value, bools, or colours codes.
|
||||
indicator_data: window.UI.data.indicator_data
|
||||
chart1_id: this.data.chart1_id, // ID of the first chart element
|
||||
chart2_id: this.data.chart2_id, // ID of the second chart element
|
||||
chart3_id: this.data.chart3_id, // ID of the third chart element
|
||||
trading_pair: this.data.trading_pair, // Active trading pair
|
||||
price_history: this.data.price_history // Historical price data
|
||||
};
|
||||
// Pass in a reference to Charts so the indicators can update them directly
|
||||
window.UI.indicators.addToCharts(window.UI.charts, ind_init_data);
|
||||
this.charts = new Charts(chart_init_data); // Initialize the charts
|
||||
|
||||
/* Request Initialization data from the server. All incoming
|
||||
data is forwarded to any registered callback functions. */
|
||||
window.UI.signals.request_signals();
|
||||
window.UI.alerts.set_target();
|
||||
window.UI.strats.initialize();
|
||||
window.UI.controls.init_TP_selector();
|
||||
window.UI.trade.initialize();
|
||||
window.UI.exchanges.initialize();
|
||||
// Initialize indicators and link them to the charts
|
||||
let ind_init_data = {
|
||||
indicators: this.data.indicators, // List of indicators
|
||||
indicator_data: this.data.indicator_data // Data for rendering indicators
|
||||
};
|
||||
this.indicators.addToCharts(this.charts, ind_init_data); // Add indicators to the charts
|
||||
|
||||
// Initialize various components that don't depend on external libraries
|
||||
this.signals.request_signals(); // Request and initialize trading signals
|
||||
this.alerts.set_target(); // Set up alert notifications
|
||||
this.controls.init_TP_selector(); // Initialize trade parameter selectors
|
||||
this.trade.initialize(); // Initialize trade-related data and UI elements
|
||||
this.exchanges.initialize(); // Initialize exchange-related data
|
||||
|
||||
this.strats.initialize(); // Initialize strategies once Blockly is ready
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
UI = new User_Interface();
|
||||
// Instantiate the User_Interface class and assign it to a global variable for access in other parts of the app
|
||||
window.UI = new User_Interface();
|
||||
|
|
|
|||
|
|
@ -47,6 +47,7 @@ class Indicator {
|
|||
this.name = name;
|
||||
this.lines = [];
|
||||
this.hist = [];
|
||||
this.outputs = [];
|
||||
}
|
||||
|
||||
static getIndicatorConfig() {
|
||||
|
|
@ -162,6 +163,7 @@ class SMA extends Indicator {
|
|||
constructor(name, chart, color, lineWidth = 2) {
|
||||
super(name);
|
||||
this.addLine('line', chart, color, lineWidth);
|
||||
this.outputs = ['value'];
|
||||
}
|
||||
|
||||
static getIndicatorConfig() {
|
||||
|
|
@ -201,6 +203,7 @@ class RSI extends Indicator {
|
|||
}
|
||||
let chart = charts.chart2;
|
||||
this.addLine('line', chart, color, lineWidth);
|
||||
this.outputs = ['value'];
|
||||
}
|
||||
|
||||
static getIndicatorConfig() {
|
||||
|
|
@ -231,6 +234,7 @@ class MACD extends Indicator {
|
|||
this.addLine('line_m', chart, color_1, lineWidth);
|
||||
this.addLine('line_s', chart, color_2, lineWidth);
|
||||
this.addHist(name, chart);
|
||||
this.outputs = ['macd', 'signal', 'hist'];
|
||||
}
|
||||
|
||||
static getIndicatorConfig() {
|
||||
|
|
@ -286,6 +290,7 @@ class ATR extends Indicator {
|
|||
|
||||
init(data) {
|
||||
this.updateDisplay(this.name, data.at(-1).value, 'value');
|
||||
this.outputs = ['value'];
|
||||
}
|
||||
|
||||
update(data) {
|
||||
|
|
@ -300,6 +305,7 @@ class Volume extends Indicator {
|
|||
super(name);
|
||||
this.addHist(name, chart);
|
||||
this.hist[name].applyOptions({ scaleMargins: { top: 0.95, bottom: 0.0 } });
|
||||
this.outputs = ['value'];
|
||||
}
|
||||
|
||||
static getIndicatorConfig() {
|
||||
|
|
@ -325,6 +331,7 @@ class Bolenger extends Indicator {
|
|||
this.addLine('line_u', chart, color_1, lineWidth);
|
||||
this.addLine('line_m', chart, color_2, lineWidth);
|
||||
this.addLine('line_l', chart, color_3, lineWidth);
|
||||
this.outputs = ['upper', 'middle', 'lower'];
|
||||
}
|
||||
|
||||
static getIndicatorConfig() {
|
||||
|
|
@ -396,6 +403,19 @@ class Indicators {
|
|||
}
|
||||
}
|
||||
|
||||
// Method to retrieve outputs for each indicator
|
||||
getIndicatorOutputs() {
|
||||
let indicatorOutputs = {};
|
||||
for (let name in this.i_objs) {
|
||||
if (this.i_objs[name].outputs) {
|
||||
indicatorOutputs[name] = this.i_objs[name].outputs;
|
||||
} else {
|
||||
indicatorOutputs[name] = ['value']; // Default output if not defined
|
||||
}
|
||||
}
|
||||
return indicatorOutputs;
|
||||
}
|
||||
|
||||
addToCharts(charts, idata){
|
||||
/*
|
||||
Receives indicator data, creates and stores the indicator
|
||||
|
|
|
|||
|
|
@ -8,7 +8,10 @@
|
|||
<!-- Load lightweight charts -->
|
||||
<script src="{{ url_for('static', filename='lightweight-charts.standalone.production.js') }}"></script>
|
||||
<!-- Load Blockly -->
|
||||
<script src="https://unpkg.com/blockly/blockly.min.js"></script>
|
||||
<script src="{{ url_for('static', filename='blockly/blockly-develop/dist/blockly_compressed.js') }}"></script>
|
||||
<script src="{{ url_for('static', filename='blockly/blockly-develop/dist/blocks_compressed.js') }}"></script>
|
||||
<script src="{{ url_for('static', filename='blockly/blockly-develop/dist/python_compressed.js') }}"></script>
|
||||
|
||||
<!-- The server passed initiation data to the HTML. This loads it into the DOM. -->
|
||||
<script type="text/javascript">
|
||||
function get_init_data(vars) {return vars}
|
||||
|
|
|
|||
|
|
@ -1,59 +1,50 @@
|
|||
<div class="form-popup" id="new_strat_form" style="overflow: auto;">
|
||||
<form action="/new_strategy" class="form-container" style="display: grid; grid-template-columns: 1fr; grid-template-rows: auto;">
|
||||
<!-- Panel 1 of 1 (5 rows, 2 columns) -->
|
||||
<div id="strat_pan_1" class="form_panels" style="display: grid; grid-template-columns:repeat(2,1fr); grid-template-rows: repeat(5,1fr); gap: 10px;">
|
||||
|
||||
<!-- Panel title (row 1/5)-->
|
||||
<!-- Strategy Form Popup -->
|
||||
<div class="form-popup" id="new_strat_form" style="display: none; overflow: auto;">
|
||||
<form class="form-container" style="display: grid; grid-template-columns: 1fr; grid-template-rows: auto;">
|
||||
<!-- Panel 1 of 1 -->
|
||||
<div id="strat_pan_1" class="form_panels" style="display: grid; grid-template-columns:repeat(2,1fr); grid-template-rows: repeat(6,1fr); gap: 10px;">
|
||||
<!-- Panel title -->
|
||||
<h1 style="grid-column: 1 / span 2; grid-row: 1;">Create New Strategy</h1>
|
||||
<div style="grid-column: 1 / span 2; grid-row: 2;">Name: <input class="ietextbox" type="text" id="name_box" name="name_box" value="Indicator name"></div>
|
||||
|
||||
<!-- Workspace (row 2-4/5)-->
|
||||
<div id="blocklyDiv" style="grid-column: 1 / span 2; grid-row: 2 / span 3; height: 300px; width: 100%;"></div>
|
||||
<!-- Blockly workspace -->
|
||||
<div id="blocklyDiv" style="grid-column: 1 / span 2; grid-row: 3 / span 3; height: 300px; width: 100%;"></div>
|
||||
|
||||
<!-- Buttons (row 5/5)-->
|
||||
<div style="grid-column: 1 / span 2; grid-row: 5; text-align: center;">
|
||||
<!-- Buttons -->
|
||||
<div style="grid-column: 1 / span 2; grid-row: 6; text-align: center;">
|
||||
<button type="button" class="btn cancel" onclick="UI.strats.close_form()">Close</button>
|
||||
<button type="button" class="btn next" onclick="UI.strats.submit()">Create Strategy</button>
|
||||
</div>
|
||||
|
||||
</div><!----End panel 1--------->
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<xml id="toolbox" style="display: none">
|
||||
<!-- Define your custom blocks here -->
|
||||
<category name="Indicators" colour="230">
|
||||
<!-- Indicator blocks go here -->
|
||||
</category>
|
||||
|
||||
<category name="Values" colour="230">
|
||||
<block type="last_candle_value"></block>
|
||||
<block type="value_input"></block>
|
||||
</category>
|
||||
<category name="Logic" colour="210">
|
||||
<block type="controls_if"></block>
|
||||
<block type="logic_compare"></block>
|
||||
<block type="comparison"></block>
|
||||
<block type="logical_and"></block>
|
||||
<block type="logical_or"></block>
|
||||
<block type="is_true"></block>
|
||||
</category>
|
||||
<category name="Math" colour="230">
|
||||
<block type="math_number"></block>
|
||||
<block type="math_arithmetic"></block>
|
||||
|
||||
<!-- New category for Trading Actions -->
|
||||
<category name="Trading" colour="230">
|
||||
<block type="trade_action"></block>
|
||||
<block type="order_type"></block>
|
||||
<block type="time_in_force"></block>
|
||||
<block type="stop_loss"></block>
|
||||
<block type="take_profit"></block>
|
||||
</category>
|
||||
<!-- Add more blocks as needed -->
|
||||
</xml>
|
||||
|
||||
<!-- <form action="/new_strategy" class="form-container">-->
|
||||
<!-- <!– Panel 1 of 1 (5 rows, 2 columns) –>-->
|
||||
<!-- <div id="strat_pan_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;">Create New Strategy</h1>-->
|
||||
<!-- <!– Name Input field (row 2/5)–>-->
|
||||
<!-- <div id = "strat_name_div" style="grid-column: 1 / span 2; grid_row:2;">-->
|
||||
<!-- <label for='stg_name' >Strategy Name:</label>-->
|
||||
<!-- <input type="text" id="stg_name" name="strat_name" />-->
|
||||
<!-- </div>-->
|
||||
<!-- <!– Source Input field (row 3/5)–>-->
|
||||
<!-- <label for="strat_type" style="grid-column: 1; grid-row: 3;"><b>Strategy type</b></label>-->
|
||||
<!-- <select name="strat_type" id="strat_type" style="grid-column: 2; grid-row: 3;" onchange= "UI.strats.fill_field('strat_opt', this.value)">-->
|
||||
<!-- <option>in-out</option>-->
|
||||
<!-- <option>incremental_profits</option>-->
|
||||
<!-- <option>swing</option>-->
|
||||
<!-- </select>-->
|
||||
<!-- <div id="strat_opt"></div>-->
|
||||
<!-- <div style="grid-column: 1 / span 2; grid-row: 5;">-->
|
||||
<!-- <button type="button" class="btn cancel" onclick="UI.strats.close_form()">Close</button>-->
|
||||
<!-- <button type="button" class="btn next" onclick="UI.strats.submit()">Create Strategy</button>-->
|
||||
<!-- </div>-->
|
||||
|
||||
<!-- </div><!–--End panel 1-------–>-->
|
||||
<!-- </form>-->
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue