644 lines
22 KiB
JavaScript
644 lines
22 KiB
JavaScript
// client/json_generators.js
|
|
|
|
export function defineJsonGenerators() {
|
|
// Initialize the JSON generator if not already initialized
|
|
if (!Blockly.JSON) {
|
|
Blockly.JSON = new Blockly.Generator('JSON');
|
|
}
|
|
|
|
// Define order constants needed for the JSON generator
|
|
Blockly.JSON.ORDER_ATOMIC = 0; // Highest precedence
|
|
Blockly.JSON.ORDER_NONE = 99; // Lowest precedence
|
|
|
|
/**
|
|
* Override the default blockToCode method to generate JSON.
|
|
*/
|
|
Blockly.JSON.blockToCode = function(block) {
|
|
if (!block) {
|
|
return '';
|
|
}
|
|
var func = this[block.type];
|
|
if (typeof func !== 'function') {
|
|
throw Error('JSON generator does not know how to generate code for block type "' + block.type + '".');
|
|
}
|
|
var code = func.call(this, block);
|
|
return code;
|
|
};
|
|
|
|
/**
|
|
* Generate JSON for a value input.
|
|
* @param {Blockly.Block} block Block to generate code from.
|
|
* @param {string} name Name of the input.
|
|
* @param {number} outerOrder The current operation order.
|
|
* @returns {string} JSON string of the input value.
|
|
*/
|
|
Blockly.JSON.valueToCode = function(block, name, outerOrder) {
|
|
if (isNaN(outerOrder)) {
|
|
throw Error('Expecting valid order from block: ' + block);
|
|
}
|
|
var targetBlock = block.getInputTargetBlock(name);
|
|
if (!targetBlock) {
|
|
return '';
|
|
}
|
|
var code = this.blockToCode(targetBlock);
|
|
if (code === '') {
|
|
return '';
|
|
}
|
|
return code;
|
|
};
|
|
|
|
/**
|
|
* Generate JSON for a statement input.
|
|
* @param {Blockly.Block} block Block to generate code from.
|
|
* @param {string} name Name of the input.
|
|
* @returns {string} JSON string of the statement.
|
|
*/
|
|
Blockly.JSON.statementToCode = function(block, name) {
|
|
var targetBlock = block.getInputTargetBlock(name);
|
|
var code = '';
|
|
while (targetBlock) {
|
|
var line = this.blockToCode(targetBlock);
|
|
if (line) {
|
|
code += line;
|
|
}
|
|
targetBlock = targetBlock.getNextBlock();
|
|
}
|
|
return code;
|
|
};
|
|
|
|
/**
|
|
* Helper function to safely parse JSON strings.
|
|
* Returns an empty object if parsing fails.
|
|
* @param {string} jsonString The JSON string to parse.
|
|
* @returns {Object} Parsed JSON object or empty object on failure.
|
|
*/
|
|
function safeParse(jsonString) {
|
|
try {
|
|
return JSON.parse(jsonString);
|
|
} catch (e) {
|
|
console.error("JSON Parsing Error:", e);
|
|
return {};
|
|
}
|
|
}
|
|
|
|
/************************************************
|
|
* VALUE BLOCKS *
|
|
************************************************/
|
|
|
|
/**
|
|
* last_candle_value
|
|
* Retrieves a specific part of the last candle (Open, High, Low, Close) from a given source.
|
|
* Generates a JSON object with type, candle_part, and source.
|
|
*/
|
|
Blockly.JSON['last_candle_value'] = function(block) {
|
|
const candlePart = block.getFieldValue('candle_part'); // e.g., 'open', 'high', 'low', 'close'
|
|
const provided_source = Blockly.JSON.valueToCode(block, 'source', Blockly.JSON.ORDER_ATOMIC) || null;
|
|
|
|
let defined_source;
|
|
if (!provided_source){
|
|
const symbolElement = document.getElementById('symbol');
|
|
const timeframeElement = document.getElementById('timeframe');
|
|
const exchangeElement = document.getElementById('exchange_name');
|
|
|
|
if (symbolElement && timeframeElement && exchangeElement) {
|
|
defined_source = {
|
|
symbol: symbolElement.value,
|
|
timeframe: timeframeElement.value,
|
|
exchange_name: exchangeElement.value
|
|
};
|
|
} else {
|
|
console.error('One or more source elements are missing in the DOM.');
|
|
defined_source = {};
|
|
}
|
|
} else {
|
|
// Assume `provided_source` is a JSON string or JavaScript code that represents a source object
|
|
defined_source = safeParse(provided_source);
|
|
}
|
|
|
|
const json = {
|
|
type: 'last_candle_value',
|
|
candle_part: candlePart,
|
|
source: defined_source
|
|
};
|
|
|
|
return JSON.stringify(json); // Ensure that Blockly expects a JSON string
|
|
};
|
|
|
|
|
|
/**
|
|
* strategy_profit_loss
|
|
* Evaluates the total strategy's profit or loss with comparison operators.
|
|
* Generates a JSON object with type, metric, operator, and value.
|
|
*/
|
|
Blockly.JSON['strategy_profit_loss'] = function(block) {
|
|
const metric = block.getFieldValue('METRIC'); // 'profit' or 'loss'
|
|
const operator = block.getFieldValue('OPERATOR'); // '>', '<', '>=', '<=', '==', '!='
|
|
const valueCode = Blockly.JSON.valueToCode(block, 'VALUE', Blockly.JSON.ORDER_ATOMIC) || "0";
|
|
const value = safeParse(valueCode) || parseFloat(valueCode);
|
|
|
|
const json = {
|
|
type: 'strategy_profit_loss',
|
|
metric: metric,
|
|
operator: operator,
|
|
value: value
|
|
};
|
|
|
|
return JSON.stringify(json);
|
|
};
|
|
|
|
/**
|
|
* current_balance
|
|
* Retrieves the current balance of the strategy.
|
|
* Generates a JSON object with type.
|
|
* If 'NEXT' is connected, includes additional balance information.
|
|
*/
|
|
Blockly.JSON['current_balance'] = function(block) {
|
|
const json = {
|
|
type: 'current_balance'
|
|
};
|
|
|
|
const nextBlock = block.getInputTargetBlock('NEXT');
|
|
if (nextBlock) {
|
|
const nextCode = this.blockToCode(nextBlock);
|
|
const nextJson = safeParse(nextCode);
|
|
json.next = nextJson;
|
|
}
|
|
|
|
return JSON.stringify(json);
|
|
};
|
|
|
|
/**
|
|
* starting_balance
|
|
* Retrieves the starting balance of the strategy.
|
|
* Generates a JSON object with type.
|
|
* If 'NEXT' is connected, includes additional starting balance information.
|
|
*/
|
|
Blockly.JSON['starting_balance'] = function(block) {
|
|
const json = {
|
|
type: 'starting_balance'
|
|
};
|
|
|
|
const nextBlock = block.getInputTargetBlock('NEXT');
|
|
if (nextBlock) {
|
|
const nextCode = this.blockToCode(nextBlock);
|
|
const nextJson = safeParse(nextCode);
|
|
json.next = nextJson;
|
|
}
|
|
|
|
return JSON.stringify(json);
|
|
};
|
|
|
|
/**
|
|
* active_trades
|
|
* Gets the number of active trades currently open.
|
|
* Generates a JSON object with type.
|
|
* If 'NEXT' is connected, includes additional active trades information.
|
|
*/
|
|
Blockly.JSON['active_trades'] = function(block) {
|
|
const json = {
|
|
type: 'active_trades'
|
|
};
|
|
|
|
const nextBlock = block.getInputTargetBlock('NEXT');
|
|
if (nextBlock) {
|
|
const nextCode = this.blockToCode(nextBlock);
|
|
const nextJson = safeParse(nextCode);
|
|
json.next = nextJson;
|
|
}
|
|
|
|
return JSON.stringify(json);
|
|
};
|
|
|
|
/**
|
|
* math_operation
|
|
* Performs basic arithmetic operations between two values.
|
|
* Generates a JSON object with type, operator, left_operand, and right_operand.
|
|
*/
|
|
Blockly.JSON['math_operation'] = function(block) {
|
|
const operator = block.getFieldValue('operator'); // '+', '-', '*', '/'
|
|
const leftCode = Blockly.JSON.valueToCode(block, 'left', Blockly.JSON.ORDER_ATOMIC) || "0";
|
|
const rightCode = Blockly.JSON.valueToCode(block, 'right', Blockly.JSON.ORDER_ATOMIC) || "0";
|
|
|
|
const leftValue = safeParse(leftCode) || leftCode;
|
|
const rightValue = safeParse(rightCode) || rightCode;
|
|
|
|
const json = {
|
|
type: 'math_operation',
|
|
operator: operator,
|
|
left_operand: leftValue,
|
|
right_operand: rightValue
|
|
};
|
|
|
|
return JSON.stringify(json);
|
|
};
|
|
|
|
/**
|
|
* value_input
|
|
* Allows users to input numerical values and chain multiple values for list creation.
|
|
* Generates a JSON object with type and value.
|
|
* If 'NEXT' is connected, includes additional values.
|
|
*/
|
|
Blockly.JSON['value_input'] = function(block) {
|
|
const value = parseFloat(block.getFieldValue('VALUE')) || 0;
|
|
const json = {
|
|
type: 'value_input',
|
|
value: value
|
|
};
|
|
|
|
const nextBlock = block.getInputTargetBlock('NEXT');
|
|
if (nextBlock) {
|
|
const nextCode = this.blockToCode(nextBlock);
|
|
const nextJson = safeParse(nextCode);
|
|
if (Array.isArray(json.next_values)) {
|
|
json.next_values.push(nextJson);
|
|
} else {
|
|
json.next_values = [json.value, nextJson.value];
|
|
}
|
|
}
|
|
|
|
return JSON.stringify(json);
|
|
};
|
|
|
|
/**
|
|
* source
|
|
* Defines the data source with Time Frame (TF), Exchange (Ex), and Symbol (Sym).
|
|
* Generates a JSON object with timeframe, exchange, and symbol.
|
|
*/
|
|
Blockly.JSON['source'] = function(block) {
|
|
const timeframe = block.getFieldValue('TF'); // e.g., '5m', '1h'
|
|
const exchange = block.getFieldValue('EXC'); // e.g., 'Binance'
|
|
const symbol = block.getFieldValue('SYM'); // e.g., 'BTCUSD'
|
|
|
|
const json = {
|
|
timeframe: timeframe,
|
|
exchange: exchange,
|
|
symbol: symbol
|
|
};
|
|
|
|
return JSON.stringify(json);
|
|
};
|
|
|
|
/************************************************
|
|
* LOGICAL BLOCKS *
|
|
************************************************/
|
|
|
|
/**
|
|
* logical_and
|
|
* Performs a logical AND operation between two Boolean conditions.
|
|
* Generates a JSON object with type and conditions.
|
|
*/
|
|
Blockly.JSON['logical_and'] = function(block) {
|
|
const condition1Code = Blockly.JSON.valueToCode(block, 'left', Blockly.JSON.ORDER_ATOMIC) || "false";
|
|
const condition2Code = Blockly.JSON.valueToCode(block, 'right', Blockly.JSON.ORDER_ATOMIC) || "false";
|
|
|
|
const condition1 = safeParse(condition1Code) || condition1Code;
|
|
const condition2 = safeParse(condition2Code) || condition2Code;
|
|
|
|
const json = {
|
|
type: 'logical_and',
|
|
conditions: [condition1, condition2]
|
|
};
|
|
|
|
return JSON.stringify(json);
|
|
};
|
|
|
|
/**
|
|
* logical_or
|
|
* Performs a logical OR operation between two Boolean conditions.
|
|
* Generates a JSON object with type and conditions.
|
|
*/
|
|
Blockly.JSON['logical_or'] = function(block) {
|
|
const condition1Code = Blockly.JSON.valueToCode(block, 'left', Blockly.JSON.ORDER_ATOMIC) || "false";
|
|
const condition2Code = Blockly.JSON.valueToCode(block, 'right', Blockly.JSON.ORDER_ATOMIC) || "false";
|
|
|
|
const condition1 = safeParse(condition1Code) || condition1Code;
|
|
const condition2 = safeParse(condition2Code) || condition2Code;
|
|
|
|
const json = {
|
|
type: 'logical_or',
|
|
conditions: [condition1, condition2]
|
|
};
|
|
|
|
return JSON.stringify(json);
|
|
};
|
|
|
|
/**
|
|
* is_false
|
|
* Checks if a given Boolean condition is false.
|
|
* Generates a JSON object with type and condition.
|
|
*/
|
|
Blockly.JSON['is_false'] = function(block) {
|
|
const conditionCode = Blockly.JSON.valueToCode(block, 'condition', Blockly.JSON.ORDER_ATOMIC) || "false";
|
|
const condition = safeParse(conditionCode) || conditionCode;
|
|
|
|
const json = {
|
|
type: 'is_false',
|
|
condition: condition
|
|
};
|
|
|
|
return JSON.stringify(json);
|
|
};
|
|
|
|
/**
|
|
* comparison
|
|
* Compares two numerical or dynamic values using operators like >, <, ==.
|
|
* Generates a JSON object with type, operator, left_operand, and right_operand.
|
|
*/
|
|
Blockly.JSON['comparison'] = function(block) {
|
|
const operator = block.getFieldValue('operator'); // '>', '<', '=='
|
|
const leftCode = Blockly.JSON.valueToCode(block, 'left', Blockly.JSON.ORDER_ATOMIC) || "0";
|
|
const rightCode = Blockly.JSON.valueToCode(block, 'right', Blockly.JSON.ORDER_ATOMIC) || "0";
|
|
|
|
const leftValue = safeParse(leftCode) || leftCode;
|
|
const rightValue = safeParse(rightCode) || rightCode;
|
|
|
|
const json = {
|
|
type: 'comparison',
|
|
operator: operator,
|
|
left_operand: leftValue,
|
|
right_operand: rightValue
|
|
};
|
|
|
|
return JSON.stringify(json);
|
|
};
|
|
|
|
/************************************************
|
|
* TRADE ORDER BLOCKS *
|
|
************************************************/
|
|
|
|
/**
|
|
* trade_action
|
|
* Executes a trade action (Buy/Sell) based on a Boolean condition, specifying the amount and optional trade options.
|
|
* Generates a JSON object with type, condition, trade_type, size, and trade_options.
|
|
*/
|
|
Blockly.JSON['trade_action'] = function(block) {
|
|
const conditionCode = Blockly.JSON.valueToCode(block, 'condition', Blockly.JSON.ORDER_ATOMIC) || "false";
|
|
const condition = safeParse(conditionCode) || conditionCode;
|
|
|
|
const tradeType = block.getFieldValue('tradeType'); // 'buy' or 'sell'
|
|
|
|
const sizeCode = Blockly.JSON.valueToCode(block, 'size', Blockly.JSON.ORDER_ATOMIC) || "0";
|
|
const size = safeParse(sizeCode) || parseFloat(sizeCode);
|
|
|
|
const tradeOptionsCode = Blockly.JSON.statementToCode(block, 'trade_options').trim();
|
|
let tradeOptions = [];
|
|
|
|
if (tradeOptionsCode) {
|
|
try {
|
|
tradeOptions = JSON.parse(tradeOptionsCode);
|
|
if (!Array.isArray(tradeOptions)) {
|
|
tradeOptions = [tradeOptions];
|
|
}
|
|
} catch (e) {
|
|
console.error("Trade Options Parsing Error:", e);
|
|
tradeOptions = [];
|
|
}
|
|
}
|
|
|
|
const json = {
|
|
type: 'trade_action',
|
|
condition: condition,
|
|
trade_type: tradeType,
|
|
size: size,
|
|
trade_options: tradeOptions
|
|
};
|
|
|
|
return JSON.stringify(json);
|
|
};
|
|
|
|
/**
|
|
* time_in_force
|
|
* Sets the time in force for the order (GTC, FOK, IOC).
|
|
* Generates a JSON object with type and tif.
|
|
*/
|
|
Blockly.JSON['time_in_force'] = function(block) {
|
|
const tif = block.getFieldValue('tif'); // 'gtc', 'fok', 'ioc'
|
|
|
|
const json = {
|
|
type: 'time_in_force',
|
|
tif: tif
|
|
};
|
|
|
|
return JSON.stringify(json);
|
|
};
|
|
|
|
/**
|
|
* stop_loss
|
|
* Sets the Stop Loss parameter for a trade.
|
|
* Generates a JSON object with type and stop_loss.
|
|
*/
|
|
Blockly.JSON['stop_loss'] = function(block) {
|
|
const stopLossCode = Blockly.JSON.valueToCode(block, 'stop_loss_input', Blockly.JSON.ORDER_ATOMIC) || "0";
|
|
const stop_loss = safeParse(stopLossCode) || parseFloat(stopLossCode);
|
|
|
|
const json = {
|
|
type: 'stop_loss',
|
|
stop_loss: stop_loss
|
|
};
|
|
|
|
return JSON.stringify(json);
|
|
};
|
|
|
|
/**
|
|
* take_profit
|
|
* Sets the Take Profit parameter for a trade.
|
|
* Generates a JSON object with type and take_profit.
|
|
*/
|
|
Blockly.JSON['take_profit'] = function(block) {
|
|
const takeProfitCode = Blockly.JSON.valueToCode(block, 'take_profit_input', Blockly.JSON.ORDER_ATOMIC) || "0";
|
|
const take_profit = safeParse(takeProfitCode) || parseFloat(takeProfitCode);
|
|
|
|
const json = {
|
|
type: 'take_profit',
|
|
take_profit: take_profit
|
|
};
|
|
|
|
return JSON.stringify(json);
|
|
};
|
|
|
|
/**
|
|
* limit
|
|
* Sets the Limit parameter for a trade order.
|
|
* Generates a JSON object with type and limit_price.
|
|
*/
|
|
Blockly.JSON['limit'] = function(block) {
|
|
const limitCode = Blockly.JSON.valueToCode(block, 'limit_input', Blockly.JSON.ORDER_ATOMIC) || "0";
|
|
const limit_price = safeParse(limitCode) || parseFloat(limitCode);
|
|
|
|
const json = {
|
|
type: 'limit',
|
|
limit_price: limit_price
|
|
};
|
|
|
|
return JSON.stringify(json);
|
|
};
|
|
|
|
/**
|
|
* target_market
|
|
* Defines the target market for executing trades with specified Time Frame, Exchange, and Symbol.
|
|
* Generates a JSON object with type, timeframe, exchange, and symbol.
|
|
*/
|
|
Blockly.JSON['target_market'] = function(block) {
|
|
const timeframe = block.getFieldValue('TF'); // e.g., '5m', '1h'
|
|
const exchange = block.getFieldValue('EXC'); // e.g., 'Binance'
|
|
const symbol = block.getFieldValue('SYM'); // e.g., 'BTCUSD'
|
|
|
|
const json = {
|
|
type: 'target_market',
|
|
timeframe: timeframe,
|
|
exchange: exchange,
|
|
symbol: symbol
|
|
};
|
|
|
|
return JSON.stringify(json);
|
|
};
|
|
|
|
/************************************************
|
|
* CONTROL BLOCKS *
|
|
************************************************/
|
|
|
|
/**
|
|
* strategy_pause
|
|
* Overrides all and pauses the strategy. No new orders will be placed while the condition is true.
|
|
* Generates a JSON object with type and pause_condition.
|
|
*/
|
|
Blockly.JSON['strategy_pause'] = function(block) {
|
|
const conditionCode = Blockly.JSON.valueToCode(block, 'condition', Blockly.JSON.ORDER_ATOMIC) || "false";
|
|
const pause_condition = safeParse(conditionCode) || conditionCode;
|
|
|
|
const json = {
|
|
type: 'strategy_pause',
|
|
pause_condition: pause_condition
|
|
};
|
|
|
|
return JSON.stringify(json);
|
|
};
|
|
|
|
/**
|
|
* strategy_exit
|
|
* Overrides all, pauses the strategy, and exits trades based on configuration.
|
|
* Generates a JSON object with type, exit_condition, and exit_option.
|
|
*/
|
|
Blockly.JSON['strategy_exit'] = function(block) {
|
|
const conditionCode = Blockly.JSON.valueToCode(block, 'condition', Blockly.JSON.ORDER_ATOMIC) || "false";
|
|
const exit_condition = safeParse(conditionCode) || conditionCode;
|
|
|
|
const exit_option = block.getFieldValue('option_value'); // 'all', 'in_profit', 'in_loss'
|
|
|
|
const json = {
|
|
type: 'strategy_exit',
|
|
exit_condition: exit_condition,
|
|
exit_option: exit_option
|
|
};
|
|
|
|
return JSON.stringify(json);
|
|
};
|
|
|
|
/************************************************
|
|
* INFO BLOCKS *
|
|
************************************************/
|
|
|
|
/**
|
|
* notify_user
|
|
* Sends a notification message to the user.
|
|
* Generates a JSON object with type and message.
|
|
*/
|
|
Blockly.JSON['notify_user'] = function(block) {
|
|
const messageCode = Blockly.JSON.valueToCode(block, 'MESSAGE', Blockly.JSON.ORDER_ATOMIC) || '"Your message here"';
|
|
const message = safeParse(messageCode) || messageCode;
|
|
|
|
const json = {
|
|
type: 'notify_user',
|
|
message: message
|
|
};
|
|
|
|
return JSON.stringify(json);
|
|
};
|
|
|
|
/**
|
|
* get_variable
|
|
* Retrieves the value of a specified variable.
|
|
* Generates a JSON object with type, variable_name, and next_value.
|
|
*/
|
|
Blockly.JSON['get_variable'] = function(block) {
|
|
const variableName = block.getFieldValue('variable_name'); // e.g., 'my_var'
|
|
const json = {
|
|
type: 'get_variable',
|
|
variable_name: variableName
|
|
};
|
|
|
|
const nextBlock = block.getInputTargetBlock('NEXT');
|
|
if (nextBlock) {
|
|
const nextCode = this.blockToCode(nextBlock);
|
|
const nextJson = safeParse(nextCode);
|
|
json.next_value = nextJson;
|
|
}
|
|
|
|
return JSON.stringify(json);
|
|
};
|
|
|
|
/**
|
|
* set_variable
|
|
* Sets a specified variable to a given value.
|
|
* Generates a JSON object with type, variable_name, and value.
|
|
*/
|
|
Blockly.JSON['set_variable'] = function(block) {
|
|
const variableName = block.getFieldValue('variable_name'); // e.g., 'my_var'
|
|
const valueCode = Blockly.JSON.valueToCode(block, 'value', Blockly.JSON.ORDER_ATOMIC) || "0";
|
|
const value = safeParse(valueCode) || parseFloat(valueCode);
|
|
|
|
const json = {
|
|
type: 'set_variable',
|
|
variable_name: variableName,
|
|
value: value
|
|
};
|
|
|
|
return JSON.stringify(json);
|
|
};
|
|
|
|
/**
|
|
* flag_is_set
|
|
* Checks if a specified flag is set to True.
|
|
* Generates a JSON object with type and flag_name.
|
|
*/
|
|
Blockly.JSON['flag_is_set'] = function(block) {
|
|
const flagName = block.getFieldValue('flag_name'); // e.g., 'flag1'
|
|
|
|
const json = {
|
|
type: 'flag_is_set',
|
|
flag_name: flagName
|
|
};
|
|
|
|
return JSON.stringify(json);
|
|
};
|
|
|
|
/**
|
|
* set_flag
|
|
* Sets a specified flag to True or False based on a Boolean condition.
|
|
* Generates a JSON object with type, condition, flag_name, and flag_value.
|
|
*/
|
|
Blockly.JSON['set_flag'] = function(block) {
|
|
const conditionCode = Blockly.JSON.valueToCode(block, 'condition', Blockly.JSON.ORDER_ATOMIC) || "false";
|
|
const condition = safeParse(conditionCode) || conditionCode;
|
|
|
|
const flagName = block.getFieldValue('flag_name'); // e.g., 'flag1'
|
|
const flagValue = block.getFieldValue('flag_value'); // 'True' or 'False'
|
|
const flag_value_bool = flagValue === 'True' ? true : false;
|
|
|
|
const json = {
|
|
type: 'set_flag',
|
|
condition: condition,
|
|
flag_name: flagName,
|
|
flag_value: flag_value_bool
|
|
};
|
|
|
|
return JSON.stringify(json);
|
|
};
|
|
|
|
/************************************************
|
|
* END OF json_generators.js *
|
|
************************************************/
|
|
|
|
console.log('Custom JSON generators defined successfully.');
|
|
}
|