// 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.'); }