I played around with the blocks and i think i have it correct now.
This commit is contained in:
parent
232d479827
commit
c7b7a47129
|
|
@ -294,7 +294,7 @@ class BrighterTrades:
|
||||||
# i_updates = self.indicators.update_indicators()
|
# i_updates = self.indicators.update_indicators()
|
||||||
state_changes = self.signals.process_all_signals(self.indicators)
|
state_changes = self.signals.process_all_signals(self.indicators)
|
||||||
trade_updates = self.trades.update(float(cdata['close']))
|
trade_updates = self.trades.update(float(cdata['close']))
|
||||||
stg_updates = self.strategies.update(self.signals)
|
# stg_updates = self.strategies.update()
|
||||||
|
|
||||||
updates = {}
|
updates = {}
|
||||||
# if i_updates:
|
# if i_updates:
|
||||||
|
|
@ -303,8 +303,8 @@ class BrighterTrades:
|
||||||
updates['s_updates'] = state_changes
|
updates['s_updates'] = state_changes
|
||||||
if trade_updates:
|
if trade_updates:
|
||||||
updates['trade_updts'] = trade_updates
|
updates['trade_updts'] = trade_updates
|
||||||
if stg_updates:
|
# if stg_updates:
|
||||||
updates['stg_updts'] = stg_updates
|
# updates['stg_updts'] = stg_updates
|
||||||
return updates
|
return updates
|
||||||
|
|
||||||
def received_new_signal(self, data: dict) -> str | dict:
|
def received_new_signal(self, data: dict) -> str | dict:
|
||||||
|
|
@ -706,7 +706,8 @@ class BrighterTrades:
|
||||||
|
|
||||||
# Processing commands
|
# Processing commands
|
||||||
if msg_type == 'delete_signal':
|
if msg_type == 'delete_signal':
|
||||||
self.delete_signal(msg_data)
|
pass
|
||||||
|
# self.delete_signal(msg_data)
|
||||||
|
|
||||||
if msg_type == 'delete_strategy':
|
if msg_type == 'delete_strategy':
|
||||||
result = self.delete_strategy(msg_data)
|
result = self.delete_strategy(msg_data)
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,12 @@ class Strategies:
|
||||||
columns=["id", "creator", "name", "workspace", "code", "stats", "public", "fee",
|
columns=["id", "creator", "name", "workspace", "code", "stats", "public", "fee",
|
||||||
"tbl_key", "strategy_components"])
|
"tbl_key", "strategy_components"])
|
||||||
|
|
||||||
|
# Initialize default settings (you can adjust these as needed)
|
||||||
|
self.default_timeframe = '5m'
|
||||||
|
self.default_exchange = 'Binance'
|
||||||
|
self.default_symbol = 'BTCUSD'
|
||||||
|
self.data_sources_used = set()
|
||||||
|
|
||||||
def new_strategy(self, data: dict) -> dict:
|
def new_strategy(self, data: dict) -> dict:
|
||||||
"""
|
"""
|
||||||
Add a new strategy to the cache and database.
|
Add a new strategy to the cache and database.
|
||||||
|
|
@ -328,19 +334,20 @@ class Strategies:
|
||||||
|
|
||||||
if node_type == 'trade_action':
|
if node_type == 'trade_action':
|
||||||
code_lines.extend(self.handle_trade_action(node, indent_level))
|
code_lines.extend(self.handle_trade_action(node, indent_level))
|
||||||
elif node_type in ['set_flag', 'notify_user']:
|
elif node_type in ['set_flag', 'notify_user', 'set_variable']:
|
||||||
# Handle actions that generate code lines
|
# Handle actions that generate code lines
|
||||||
if node_type == 'set_flag':
|
if node_type == 'set_flag':
|
||||||
code_lines.extend(self.handle_set_flag(node, indent_level))
|
code_lines.extend(self.handle_set_flag(node, indent_level))
|
||||||
elif node_type == 'notify_user':
|
elif node_type == 'notify_user':
|
||||||
code_lines.extend(self.handle_notify_user(node, indent_level))
|
code_lines.extend(self.handle_notify_user(node, indent_level))
|
||||||
elif node_type == 'conditional':
|
elif node_type == 'set_variable':
|
||||||
# Handle conditional statements
|
code_lines.extend(self.handle_set_variable(node, indent_level))
|
||||||
condition_node = node.get('condition')
|
elif node_type in ['comparison', 'logical_and', 'logical_or', 'math_operation']:
|
||||||
actions = node.get('actions', [])
|
# These are conditions that might be at the top level
|
||||||
condition_code = self.generate_condition_code(condition_node)
|
condition_code = self.generate_condition_code(node)
|
||||||
code_lines.append(f"{indent}if {condition_code}:")
|
code_lines.append(f"{indent}if {condition_code}:")
|
||||||
# Generate code for actions inside the condition
|
# Generate code for actions inside the condition
|
||||||
|
actions = node.get('statements', {}).get('DO', [])
|
||||||
code_lines.extend(self.generate_code_from_json(actions, indent_level + 1))
|
code_lines.extend(self.generate_code_from_json(actions, indent_level + 1))
|
||||||
else:
|
else:
|
||||||
# Handle other node types as needed
|
# Handle other node types as needed
|
||||||
|
|
@ -353,12 +360,13 @@ class Strategies:
|
||||||
if not node_type:
|
if not node_type:
|
||||||
return 'False' # Default to False if node type is missing
|
return 'False' # Default to False if node type is missing
|
||||||
|
|
||||||
|
# Allows for custom operator definitions represented in the JSON
|
||||||
if node_type == 'comparison':
|
if node_type == 'comparison':
|
||||||
operator = condition_node.get('operator')
|
operator = condition_node['fields'].get('operator')
|
||||||
left = condition_node.get('left')
|
left_node = condition_node['inputs'].get('left')
|
||||||
right = condition_node.get('right')
|
right_node = condition_node['inputs'].get('right')
|
||||||
left_expr = self.generate_condition_code(left)
|
left_expr = self.generate_condition_code(left_node)
|
||||||
right_expr = self.generate_condition_code(right)
|
right_expr = self.generate_condition_code(right_node)
|
||||||
operator_map = {
|
operator_map = {
|
||||||
'>': '>',
|
'>': '>',
|
||||||
'<': '<',
|
'<': '<',
|
||||||
|
|
@ -368,38 +376,48 @@ class Strategies:
|
||||||
'!=': '!='
|
'!=': '!='
|
||||||
}
|
}
|
||||||
return f"({left_expr} {operator_map.get(operator, operator)} {right_expr})"
|
return f"({left_expr} {operator_map.get(operator, operator)} {right_expr})"
|
||||||
|
|
||||||
elif node_type == 'logical_and':
|
elif node_type == 'logical_and':
|
||||||
conditions = condition_node.get('conditions', [])
|
conditions = condition_node.get('conditions', [])
|
||||||
condition_exprs = [self.generate_condition_code(cond) for cond in conditions]
|
condition_exprs = [self.generate_condition_code(cond) for cond in conditions]
|
||||||
return ' and '.join(condition_exprs)
|
return ' and '.join(condition_exprs)
|
||||||
|
|
||||||
elif node_type == 'logical_or':
|
elif node_type == 'logical_or':
|
||||||
conditions = condition_node.get('conditions', [])
|
conditions = condition_node.get('conditions', [])
|
||||||
condition_exprs = [self.generate_condition_code(cond) for cond in conditions]
|
condition_exprs = [self.generate_condition_code(cond) for cond in conditions]
|
||||||
return ' or '.join(condition_exprs)
|
return ' or '.join(condition_exprs)
|
||||||
|
|
||||||
elif node_type == 'is_false':
|
elif node_type == 'is_false':
|
||||||
condition = condition_node.get('condition')
|
condition = condition_node.get('condition')
|
||||||
condition_expr = self.generate_condition_code(condition)
|
condition_expr = self.generate_condition_code(condition)
|
||||||
return f"not ({condition_expr})"
|
return f"not ({condition_expr})"
|
||||||
|
|
||||||
elif node_type == 'flag_is_set':
|
elif node_type == 'flag_is_set':
|
||||||
flag_name = condition_node.get('flag_name')
|
flag_name = condition_node.get('flag_name')
|
||||||
self.flags_used.add(flag_name)
|
self.flags_used.add(flag_name)
|
||||||
return f"self.flags.get('{flag_name}', False)"
|
return f"self.flags.get('{flag_name}', False)"
|
||||||
|
|
||||||
elif node_type == 'strategy_profit_loss':
|
elif node_type == 'strategy_profit_loss':
|
||||||
metric = condition_node.get('metric')
|
metric = condition_node.get('metric')
|
||||||
if metric == 'profit':
|
if metric == 'profit':
|
||||||
return 'self.is_in_profit()'
|
return 'self.is_in_profit()'
|
||||||
elif metric == 'loss':
|
elif metric == 'loss':
|
||||||
return 'self.is_in_loss()'
|
return 'self.is_in_loss()'
|
||||||
|
|
||||||
elif node_type == 'active_trades':
|
elif node_type == 'active_trades':
|
||||||
return 'self.get_active_trades()'
|
return 'self.get_active_trades()'
|
||||||
|
|
||||||
elif node_type == 'current_balance':
|
elif node_type == 'current_balance':
|
||||||
return 'self.get_current_balance()'
|
return 'self.get_current_balance()'
|
||||||
|
|
||||||
elif node_type == 'starting_balance':
|
elif node_type == 'starting_balance':
|
||||||
return 'self.get_starting_balance()'
|
return 'self.get_starting_balance()'
|
||||||
|
|
||||||
elif node_type == 'value_input':
|
elif node_type == 'value_input':
|
||||||
value = condition_node.get('value', 0)
|
value = condition_node.get('value', 0)
|
||||||
return str(value)
|
return str(value)
|
||||||
elif node_type == 'arithmetic_operator':
|
|
||||||
|
elif node_type == 'math_operation':
|
||||||
operator = condition_node.get('operator')
|
operator = condition_node.get('operator')
|
||||||
operands = condition_node.get('operands', [])
|
operands = condition_node.get('operands', [])
|
||||||
if len(operands) == 2:
|
if len(operands) == 2:
|
||||||
|
|
@ -412,11 +430,15 @@ class Strategies:
|
||||||
'DIVIDE': '/'
|
'DIVIDE': '/'
|
||||||
}
|
}
|
||||||
return f"({left_expr} {operator_map.get(operator, operator)} {right_expr})"
|
return f"({left_expr} {operator_map.get(operator, operator)} {right_expr})"
|
||||||
|
else:
|
||||||
|
return '0' # Default or raise an error
|
||||||
|
|
||||||
elif node_type == 'last_candle_value':
|
elif node_type == 'last_candle_value':
|
||||||
candle_part = condition_node.get('candle_part')
|
candle_part = condition_node.get('candle_part')
|
||||||
source = condition_node.get('source', {})
|
source_node = condition_node.get('source', {})
|
||||||
data_feed = self.get_data_feed(source)
|
data_feed = self.get_data_feed(source_node)
|
||||||
return f"{data_feed}.{candle_part}[0]"
|
return f"{data_feed}.{candle_part}[0]"
|
||||||
|
|
||||||
elif node_type == 'indicator':
|
elif node_type == 'indicator':
|
||||||
indicator_name = condition_node.get('name')
|
indicator_name = condition_node.get('name')
|
||||||
output_field = condition_node.get('output')
|
output_field = condition_node.get('output')
|
||||||
|
|
@ -427,13 +449,15 @@ class Strategies:
|
||||||
})
|
})
|
||||||
# Generate code that calls process_indicator
|
# Generate code that calls process_indicator
|
||||||
return f"self.process_indicator('{indicator_name}', '{output_field}')"
|
return f"self.process_indicator('{indicator_name}', '{output_field}')"
|
||||||
|
|
||||||
elif node_type == 'number':
|
elif node_type == 'number':
|
||||||
# Handle numeric values
|
# Handle numeric values
|
||||||
return str(condition_node.get('value', 0))
|
return str(condition_node.get('value', 0))
|
||||||
|
|
||||||
elif node_type == 'string':
|
elif node_type == 'string':
|
||||||
# Handle string values
|
# Handle string values
|
||||||
return f"'{condition_node.get('value', '')}'"
|
return f"'{condition_node.get('value', '')}'"
|
||||||
# Handle other node types as needed
|
|
||||||
else:
|
else:
|
||||||
return 'False' # Default to False for unhandled types
|
return 'False' # Default to False for unhandled types
|
||||||
|
|
||||||
|
|
@ -441,12 +465,95 @@ class Strategies:
|
||||||
code_lines = []
|
code_lines = []
|
||||||
indent = ' ' * indent_level
|
indent = ' ' * indent_level
|
||||||
|
|
||||||
action = node.get('trade_type')
|
# Accessing fields and inputs
|
||||||
|
trade_type = node.get('trade_type', 'buy') # 'buy' or 'sell'
|
||||||
condition_node = node.get('condition')
|
condition_node = node.get('condition')
|
||||||
size = node.get('size', 1)
|
size = node.get('size', 1)
|
||||||
stop_loss = node.get('stop_loss')
|
|
||||||
take_profit = node.get('take_profit')
|
# Initialize defaults
|
||||||
trade_options = node.get('trade_options', [])
|
default_tif = 'gtc' # Good Till Canceled
|
||||||
|
default_timeframe = getattr(self, 'default_timeframe', '5m') # e.g., '5m'
|
||||||
|
default_exchange = getattr(self, 'default_exchange', 'Binance') # e.g., 'Binance'
|
||||||
|
default_symbol = getattr(self, 'default_symbol', 'BTCUSD') # e.g., 'BTCUSD'
|
||||||
|
|
||||||
|
# Set initial default values
|
||||||
|
tif = default_tif
|
||||||
|
timeframe = default_timeframe
|
||||||
|
exchange = default_exchange
|
||||||
|
symbol = default_symbol
|
||||||
|
stop_loss_type = None
|
||||||
|
stop_loss_value = None
|
||||||
|
stop_loss_trigger_price = None
|
||||||
|
stop_loss_limit_price = None
|
||||||
|
take_profit_type = None
|
||||||
|
take_profit_value = None
|
||||||
|
take_profit_trigger_price = None
|
||||||
|
take_profit_limit_price = None
|
||||||
|
price_type = 'market'
|
||||||
|
price_value = None
|
||||||
|
|
||||||
|
# Trade Options
|
||||||
|
trade_options_nodes = node.get('trade_options', [])
|
||||||
|
|
||||||
|
# Process trade options
|
||||||
|
for option in trade_options_nodes:
|
||||||
|
option_type = option.get('type')
|
||||||
|
|
||||||
|
if option_type == 'time_in_force':
|
||||||
|
# Extract Time in Force
|
||||||
|
tif = option.get('tif', default_tif)
|
||||||
|
|
||||||
|
elif option_type == 'target_market':
|
||||||
|
# Extract Target Market details
|
||||||
|
timeframe = option.get('timeframe', default_timeframe)
|
||||||
|
exchange = option.get('exchange', default_exchange)
|
||||||
|
symbol = option.get('symbol', default_symbol)
|
||||||
|
self.data_sources_used.add((exchange, symbol, timeframe))
|
||||||
|
|
||||||
|
elif option_type == 'stop_loss':
|
||||||
|
# Extract Stop Loss parameters
|
||||||
|
sl_type = option.get('stop_loss_type')
|
||||||
|
if sl_type == 'market':
|
||||||
|
sl_value = option.get('stop_loss_value')
|
||||||
|
if sl_value is not None:
|
||||||
|
stop_loss_type = sl_type
|
||||||
|
stop_loss_value = sl_value
|
||||||
|
elif sl_type == 'limit':
|
||||||
|
sl_trigger_price = option.get('trigger_price')
|
||||||
|
sl_limit_price = option.get('limit_price')
|
||||||
|
if sl_trigger_price is not None and sl_limit_price is not None:
|
||||||
|
stop_loss_type = sl_type
|
||||||
|
stop_loss_trigger_price = sl_trigger_price
|
||||||
|
stop_loss_limit_price = sl_limit_price
|
||||||
|
|
||||||
|
elif option_type == 'take_profit':
|
||||||
|
# Extract Take Profit parameters
|
||||||
|
tp_type = option.get('take_profit_type')
|
||||||
|
if tp_type == 'market':
|
||||||
|
tp_value = option.get('take_profit_value')
|
||||||
|
if tp_value is not None:
|
||||||
|
take_profit_type = tp_type
|
||||||
|
take_profit_value = tp_value
|
||||||
|
elif tp_type == 'limit':
|
||||||
|
tp_trigger_price = option.get('trigger_price')
|
||||||
|
tp_limit_price = option.get('limit_price')
|
||||||
|
if tp_trigger_price is not None and tp_limit_price is not None:
|
||||||
|
take_profit_type = tp_type
|
||||||
|
take_profit_trigger_price = tp_trigger_price
|
||||||
|
take_profit_limit_price = tp_limit_price
|
||||||
|
|
||||||
|
elif option_type == 'price':
|
||||||
|
# Extract Price parameters
|
||||||
|
p_type = option.get('price_type', 'market')
|
||||||
|
p_value = option.get('price_value', None)
|
||||||
|
if p_type and p_value is not None:
|
||||||
|
price_type = p_type
|
||||||
|
price_value = p_value
|
||||||
|
|
||||||
|
# Handle other trade options if added in the future
|
||||||
|
|
||||||
|
# Apply defaults if certain options are not provided
|
||||||
|
# Time in Force and Target Market already have defaults set above
|
||||||
|
|
||||||
# Generate code for the condition
|
# Generate code for the condition
|
||||||
if condition_node:
|
if condition_node:
|
||||||
|
|
@ -457,36 +564,96 @@ class Strategies:
|
||||||
action_indent = indent
|
action_indent = indent
|
||||||
|
|
||||||
# Prepare order parameters
|
# Prepare order parameters
|
||||||
order_params = [f"size={size}"]
|
# Handle 'size' being a single value or a list
|
||||||
if stop_loss is not None:
|
if isinstance(size, list):
|
||||||
order_params.append(f"stop_loss={stop_loss}")
|
# Iterate over each size and execute the trade
|
||||||
if take_profit is not None:
|
for idx, single_size in enumerate(size):
|
||||||
order_params.append(f"take_profit={take_profit}")
|
single_order_params = [f"size={single_size}", f"order_type='{trade_type}'"]
|
||||||
# Handle trade options
|
|
||||||
for option in trade_options:
|
|
||||||
if option.get('type') == 'order_type':
|
|
||||||
order_type = option.get('order_type', 'market')
|
|
||||||
order_params.append(f"order_type='{order_type}'")
|
|
||||||
if order_type == 'limit':
|
|
||||||
limit_price = option.get('limit_price')
|
|
||||||
if limit_price is not None:
|
|
||||||
order_params.append(f"price={limit_price}")
|
|
||||||
elif option.get('type') == 'time_in_force':
|
|
||||||
tif = option.get('tif')
|
|
||||||
if tif:
|
|
||||||
order_params.append(f"tif='{tif}'")
|
|
||||||
elif option.get('type') == 'target_market':
|
|
||||||
tf = option.get('timeframe')
|
|
||||||
exc = option.get('exchange')
|
|
||||||
sym = option.get('symbol')
|
|
||||||
order_params.append(f"timeframe='{tf}'")
|
|
||||||
order_params.append(f"exchange='{exc}'")
|
|
||||||
order_params.append(f"symbol='{sym}'")
|
|
||||||
self.data_sources_used.add((exc, sym, tf))
|
|
||||||
# Handle other trade options
|
|
||||||
|
|
||||||
params_str = ', '.join(order_params)
|
# Handle Price
|
||||||
code_lines.append(f"{action_indent}self.{action}({params_str})")
|
if price_type == 'limit' and price_value is not None:
|
||||||
|
single_order_params.append(f"limit_price={price_value}")
|
||||||
|
else:
|
||||||
|
single_order_params.append(f"limit_price=None")
|
||||||
|
|
||||||
|
# Handle Stop Loss
|
||||||
|
if stop_loss_type == 'market' and stop_loss_value is not None:
|
||||||
|
single_order_params.append(f"stop_loss_type='{stop_loss_type}'")
|
||||||
|
single_order_params.append(f"stop_loss={stop_loss_value}")
|
||||||
|
elif stop_loss_type == 'limit':
|
||||||
|
single_order_params.append(f"stop_loss_type='{stop_loss_type}'")
|
||||||
|
single_order_params.append(f"stop_loss_trigger_price={stop_loss_trigger_price}")
|
||||||
|
single_order_params.append(f"stop_loss_limit_price={stop_loss_limit_price}")
|
||||||
|
|
||||||
|
# Handle Take Profit
|
||||||
|
if take_profit_type == 'market' and take_profit_value is not None:
|
||||||
|
single_order_params.append(f"take_profit_type='{take_profit_type}'")
|
||||||
|
single_order_params.append(f"take_profit={take_profit_value}")
|
||||||
|
elif take_profit_type == 'limit':
|
||||||
|
single_order_params.append(f"take_profit_type='{take_profit_type}'")
|
||||||
|
single_order_params.append(f"take_profit_trigger_price={take_profit_trigger_price}")
|
||||||
|
single_order_params.append(f"take_profit_limit_price={take_profit_limit_price}")
|
||||||
|
|
||||||
|
# Always include Time in Force and Target Market parameters
|
||||||
|
single_order_params.append(f"tif='{tif}'")
|
||||||
|
single_order_params.append(f"timeframe='{timeframe}'")
|
||||||
|
single_order_params.append(f"exchange='{exchange}'")
|
||||||
|
single_order_params.append(f"symbol='{symbol}'")
|
||||||
|
|
||||||
|
# Include Price parameters if applicable
|
||||||
|
if price_type and price_value is not None:
|
||||||
|
single_order_params.append(f"price_type='{price_type}'")
|
||||||
|
single_order_params.append(f"price_value={price_value}")
|
||||||
|
|
||||||
|
# Convert parameters list to a comma-separated string
|
||||||
|
single_params_str = ', '.join(single_order_params)
|
||||||
|
|
||||||
|
# Generate the trade execution line
|
||||||
|
code_lines.append(f"{action_indent}self.{trade_type}({single_params_str})")
|
||||||
|
else:
|
||||||
|
# Single size value
|
||||||
|
order_params = [f"size={size}", f"order_type='{trade_type}'"]
|
||||||
|
|
||||||
|
# Handle Price
|
||||||
|
if price_type == 'limit' and price_value is not None:
|
||||||
|
order_params.append(f"limit_price={price_value}")
|
||||||
|
else:
|
||||||
|
order_params.append(f"limit_price=None")
|
||||||
|
|
||||||
|
# Handle Stop Loss
|
||||||
|
if stop_loss_type == 'market' and stop_loss_value is not None:
|
||||||
|
order_params.append(f"stop_loss_type='{stop_loss_type}'")
|
||||||
|
order_params.append(f"stop_loss={stop_loss_value}")
|
||||||
|
elif stop_loss_type == 'limit':
|
||||||
|
order_params.append(f"stop_loss_type='{stop_loss_type}'")
|
||||||
|
order_params.append(f"stop_loss_trigger_price={stop_loss_trigger_price}")
|
||||||
|
order_params.append(f"stop_loss_limit_price={stop_loss_limit_price}")
|
||||||
|
|
||||||
|
# Handle Take Profit
|
||||||
|
if take_profit_type == 'market' and take_profit_value is not None:
|
||||||
|
order_params.append(f"take_profit_type='{take_profit_type}'")
|
||||||
|
order_params.append(f"take_profit={take_profit_value}")
|
||||||
|
elif take_profit_type == 'limit':
|
||||||
|
order_params.append(f"take_profit_type='{take_profit_type}'")
|
||||||
|
order_params.append(f"take_profit_trigger_price={take_profit_trigger_price}")
|
||||||
|
order_params.append(f"take_profit_limit_price={take_profit_limit_price}")
|
||||||
|
|
||||||
|
# Always include Time in Force and Target Market parameters
|
||||||
|
order_params.append(f"tif='{tif}'")
|
||||||
|
order_params.append(f"timeframe='{timeframe}'")
|
||||||
|
order_params.append(f"exchange='{exchange}'")
|
||||||
|
order_params.append(f"symbol='{symbol}'")
|
||||||
|
|
||||||
|
# Include Price parameters if applicable
|
||||||
|
if price_type and price_value is not None:
|
||||||
|
order_params.append(f"price_type='{price_type}'")
|
||||||
|
order_params.append(f"price_value={price_value}")
|
||||||
|
|
||||||
|
# Convert parameters list to a comma-separated string
|
||||||
|
params_str = ', '.join(order_params)
|
||||||
|
|
||||||
|
# Generate the trade execution line
|
||||||
|
code_lines.append(f"{action_indent}self.{trade_type}({params_str})")
|
||||||
|
|
||||||
return code_lines
|
return code_lines
|
||||||
|
|
||||||
|
|
@ -494,9 +661,13 @@ class Strategies:
|
||||||
code_lines = []
|
code_lines = []
|
||||||
indent = ' ' * indent_level
|
indent = ' ' * indent_level
|
||||||
|
|
||||||
|
# Retrieve the condition directly from the node
|
||||||
condition_node = node.get('condition')
|
condition_node = node.get('condition')
|
||||||
|
if condition_node is None:
|
||||||
|
raise ValueError("Condition is missing in 'set_flag' node.")
|
||||||
|
|
||||||
flag_name = node.get('flag_name')
|
flag_name = node.get('flag_name')
|
||||||
flag_value = node.get('flag_value', 'True')
|
flag_value = node.get('flag_value', 'True') # Default to 'True' if not provided
|
||||||
|
|
||||||
condition_code = self.generate_condition_code(condition_node)
|
condition_code = self.generate_condition_code(condition_node)
|
||||||
code_lines.append(f"{indent}if {condition_code}:")
|
code_lines.append(f"{indent}if {condition_code}:")
|
||||||
|
|
@ -513,10 +684,22 @@ class Strategies:
|
||||||
|
|
||||||
return code_lines
|
return code_lines
|
||||||
|
|
||||||
def get_data_feed(self, source):
|
def handle_set_variable(self, node, indent_level):
|
||||||
timeframe = source.get('timeframe', 'default')
|
code_lines = []
|
||||||
exchange = source.get('exchange', 'default')
|
indent = ' ' * indent_level
|
||||||
symbol = source.get('symbol', 'default')
|
|
||||||
|
variable_name = node.get('variable_name')
|
||||||
|
value_node = node.get('value')
|
||||||
|
value_code = self.generate_condition_code(value_node) if value_node else '0'
|
||||||
|
|
||||||
|
code_lines.append(f"{indent}{variable_name} = {value_code}")
|
||||||
|
|
||||||
|
return code_lines
|
||||||
|
|
||||||
|
def get_data_feed(self, source_node):
|
||||||
|
timeframe = source_node.get('timeframe', 'default')
|
||||||
|
exchange = source_node.get('exchange', 'default')
|
||||||
|
symbol = source_node.get('symbol', 'default')
|
||||||
source_key = f"{exchange}_{symbol}_{timeframe}"
|
source_key = f"{exchange}_{symbol}_{timeframe}"
|
||||||
self.data_sources_used.add((exchange, symbol, timeframe))
|
self.data_sources_used.add((exchange, symbol, timeframe))
|
||||||
return f"self.datas['{source_key}']"
|
return f"self.datas['{source_key}']"
|
||||||
|
|
|
||||||
|
|
@ -307,7 +307,12 @@ class StratWorkspaceManager {
|
||||||
console.error("toolbox is not loaded.");
|
console.error("toolbox is not loaded.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* @override the toolbox zoom
|
||||||
|
*/
|
||||||
|
Blockly.VerticalFlyout.prototype.getFlyoutScale = function() {
|
||||||
|
return 1;
|
||||||
|
};
|
||||||
this.workspace = Blockly.inject('blocklyDiv', {
|
this.workspace = Blockly.inject('blocklyDiv', {
|
||||||
toolbox: toolboxElement,
|
toolbox: toolboxElement,
|
||||||
scrollbars: true,
|
scrollbars: true,
|
||||||
|
|
@ -393,51 +398,84 @@ class StratWorkspaceManager {
|
||||||
* @returns {Object} - The JSON representation of the block.
|
* @returns {Object} - The JSON representation of the block.
|
||||||
*/
|
*/
|
||||||
_blockToJson(block) {
|
_blockToJson(block) {
|
||||||
const json = {
|
if (!block) {
|
||||||
type: block.type,
|
return null;
|
||||||
fields: {},
|
|
||||||
inputs: {},
|
|
||||||
statements: {}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Capture all fields in the block
|
|
||||||
block.inputList.forEach(input => {
|
|
||||||
if (input.fieldRow) {
|
|
||||||
input.fieldRow.forEach(field => {
|
|
||||||
if (field.name && field.getValue) {
|
|
||||||
json.fields[field.name] = field.getValue();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Capture all connected blocks
|
|
||||||
block.inputList.forEach(input => {
|
|
||||||
if (input.connection && input.connection.targetBlock()) {
|
|
||||||
const targetBlock = input.connection.targetBlock();
|
|
||||||
if (input.type === Blockly.INPUT_VALUE) {
|
|
||||||
json.inputs[input.name] = this._blockToJson(targetBlock);
|
|
||||||
} else if (input.type === Blockly.NEXT_STATEMENT) {
|
|
||||||
// Handle multiple statement connections if applicable
|
|
||||||
const connectedBlocks = [];
|
|
||||||
let currentBlock = targetBlock;
|
|
||||||
while (currentBlock) {
|
|
||||||
connectedBlocks.push(this._blockToJson(currentBlock));
|
|
||||||
currentBlock = currentBlock.getNextBlock();
|
|
||||||
}
|
|
||||||
json.statements[input.name] = connectedBlocks;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Handle the next connected block at the same level
|
|
||||||
if (block.getNextBlock()) {
|
|
||||||
const nextBlock = this._blockToJson(block.getNextBlock());
|
|
||||||
// Assuming only one 'next' block; adjust if multiple are possible
|
|
||||||
json.next = nextBlock;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return json;
|
// Use the block's JSON generator if it exists
|
||||||
|
if (Blockly.JSON && Blockly.JSON[block.type]) {
|
||||||
|
// Generate JSON string using the block's JSON generator
|
||||||
|
const jsonString = Blockly.JSON[block.type](block);
|
||||||
|
// Parse the JSON string into an object
|
||||||
|
const json = JSON.parse(jsonString);
|
||||||
|
|
||||||
|
// Process inputs
|
||||||
|
block.inputList.forEach(input => {
|
||||||
|
if (input.connection && input.connection.targetBlock()) {
|
||||||
|
const targetBlock = input.connection.targetBlock();
|
||||||
|
const targetJson = this._blockToJson(targetBlock);
|
||||||
|
|
||||||
|
if (input.type === Blockly.INPUT_VALUE) {
|
||||||
|
if (!json.inputs) json.inputs = {};
|
||||||
|
json.inputs[input.name] = targetJson;
|
||||||
|
} else if (input.type === Blockly.NEXT_STATEMENT) {
|
||||||
|
if (!json.statements) json.statements = {};
|
||||||
|
if (!json.statements[input.name]) json.statements[input.name] = [];
|
||||||
|
json.statements[input.name].push(targetJson);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Handle the next connected block at the same level
|
||||||
|
if (block.getNextBlock()) {
|
||||||
|
const nextBlock = this._blockToJson(block.getNextBlock());
|
||||||
|
json.next = nextBlock;
|
||||||
|
}
|
||||||
|
|
||||||
|
return json;
|
||||||
|
} else {
|
||||||
|
// Fallback: Manually construct JSON if no JSON generator exists
|
||||||
|
const json = {
|
||||||
|
type: block.type,
|
||||||
|
fields: {},
|
||||||
|
inputs: {},
|
||||||
|
statements: {}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Capture all fields in the block
|
||||||
|
block.inputList.forEach(input => {
|
||||||
|
if (input.fieldRow) {
|
||||||
|
input.fieldRow.forEach(field => {
|
||||||
|
if (field.name && field.getValue) {
|
||||||
|
json.fields[field.name] = field.getValue();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Process inputs
|
||||||
|
block.inputList.forEach(input => {
|
||||||
|
if (input.connection && input.connection.targetBlock()) {
|
||||||
|
const targetBlock = input.connection.targetBlock();
|
||||||
|
const targetJson = this._blockToJson(targetBlock);
|
||||||
|
|
||||||
|
if (input.type === Blockly.INPUT_VALUE) {
|
||||||
|
json.inputs[input.name] = targetJson;
|
||||||
|
} else if (input.type === Blockly.NEXT_STATEMENT) {
|
||||||
|
if (!json.statements[input.name]) json.statements[input.name] = [];
|
||||||
|
json.statements[input.name].push(targetJson);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Handle the next connected block at the same level
|
||||||
|
if (block.getNextBlock()) {
|
||||||
|
const nextBlock = this._blockToJson(block.getNextBlock());
|
||||||
|
json.next = nextBlock;
|
||||||
|
}
|
||||||
|
|
||||||
|
return json;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -2,6 +2,13 @@
|
||||||
|
|
||||||
// Define Blockly blocks and their JSON generators dynamically based on indicators
|
// Define Blockly blocks and their JSON generators dynamically based on indicators
|
||||||
export function defineIndicatorBlocks() {
|
export function defineIndicatorBlocks() {
|
||||||
|
|
||||||
|
// Ensure Blockly.JSON is available
|
||||||
|
if (!Blockly.JSON) {
|
||||||
|
console.error('Blockly.JSON is not defined. Ensure json_generators.js is loaded before indicator_blocks.js.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Retrieve the indicator outputs configuration
|
// Retrieve the indicator outputs configuration
|
||||||
const indicatorOutputs = window.UI.indicators.getIndicatorOutputs();
|
const indicatorOutputs = window.UI.indicators.getIndicatorOutputs();
|
||||||
const toolboxCategory = document.querySelector('#toolbox category[name="Indicators"]');
|
const toolboxCategory = document.querySelector('#toolbox category[name="Indicators"]');
|
||||||
|
|
@ -14,9 +21,12 @@ export function defineIndicatorBlocks() {
|
||||||
for (let indicatorName in indicatorOutputs) {
|
for (let indicatorName in indicatorOutputs) {
|
||||||
const outputs = indicatorOutputs[indicatorName];
|
const outputs = indicatorOutputs[indicatorName];
|
||||||
|
|
||||||
|
// Create a unique block type for each indicator
|
||||||
|
const blockType = 'indicator_' + indicatorName;
|
||||||
|
|
||||||
// Define the block for this indicator
|
// Define the block for this indicator
|
||||||
Blockly.defineBlocksWithJsonArray([{
|
Blockly.defineBlocksWithJsonArray([{
|
||||||
"type": indicatorName,
|
"type": blockType,
|
||||||
"message0": `${indicatorName} Output %1`,
|
"message0": `${indicatorName} Output %1`,
|
||||||
"args0": [
|
"args0": [
|
||||||
{
|
{
|
||||||
|
|
@ -25,26 +35,30 @@ export function defineIndicatorBlocks() {
|
||||||
"options": outputs.map(output => [output, output])
|
"options": outputs.map(output => [output, output])
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"output": "Number",
|
"output": "dynamic_value",
|
||||||
"colour": 230,
|
"colour": 230,
|
||||||
"tooltip": `Select the ${indicatorName} output`,
|
"tooltip": `Select the ${indicatorName} output`,
|
||||||
"helpUrl": ""
|
"helpUrl": ""
|
||||||
}]);
|
}]);
|
||||||
|
|
||||||
// Define the JSON generator for this block
|
// Define the JSON generator for this block
|
||||||
Blockly.JSON[indicatorName] = function(block) {
|
Blockly.JSON[blockType] = function(block) {
|
||||||
const selectedOutput = block.getFieldValue('OUTPUT');
|
const selectedOutput = block.getFieldValue('OUTPUT');
|
||||||
const json = {
|
const json = {
|
||||||
type: 'indicator',
|
type: 'indicator',
|
||||||
name: indicatorName,
|
fields: {
|
||||||
output: selectedOutput
|
NAME: indicatorName,
|
||||||
|
OUTPUT: selectedOutput
|
||||||
|
},
|
||||||
|
inputs: {},
|
||||||
|
statements: {}
|
||||||
};
|
};
|
||||||
return JSON.stringify(json);
|
return JSON.stringify(json);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Append the newly created block to the Indicators category in the toolbox
|
// Append the newly created block to the Indicators category in the toolbox
|
||||||
const blockElement = document.createElement('block');
|
const blockElement = document.createElement('block');
|
||||||
blockElement.setAttribute('type', indicatorName);
|
blockElement.setAttribute('type', blockType);
|
||||||
toolboxCategory.appendChild(blockElement);
|
toolboxCategory.appendChild(blockElement);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,9 +6,71 @@ export function defineJsonGenerators() {
|
||||||
Blockly.JSON = new Blockly.Generator('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.
|
* Helper function to safely parse JSON strings.
|
||||||
* Returns an empty object if parsing fails.
|
* 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) {
|
function safeParse(jsonString) {
|
||||||
try {
|
try {
|
||||||
|
|
@ -19,175 +81,18 @@ export function defineJsonGenerators() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/************************************************
|
||||||
* Trade Action Block JSON Generator
|
* VALUE BLOCKS *
|
||||||
* Captures trade actions including conditions, trade types, sizes, stop loss, take profit, and trade options.
|
************************************************/
|
||||||
*/
|
|
||||||
Blockly.JSON['trade_action'] = function(block) {
|
|
||||||
const condition = Blockly.JSON.valueToCode(block, 'CONDITION', Blockly.JSON.ORDER_ATOMIC) || "False";
|
|
||||||
const tradeType = block.getFieldValue('TRADE_TYPE'); // e.g., 'buy' or 'sell'
|
|
||||||
const size = Blockly.JSON.valueToCode(block, 'SIZE', Blockly.JSON.ORDER_ATOMIC) || 1;
|
|
||||||
const stopLoss = Blockly.JSON.valueToCode(block, 'STOP_LOSS', Blockly.JSON.ORDER_ATOMIC) || null;
|
|
||||||
const takeProfit = Blockly.JSON.valueToCode(block, 'TAKE_PROFIT', Blockly.JSON.ORDER_ATOMIC) || null;
|
|
||||||
const tradeOptionsCode = Blockly.JSON.statementToCode(block, 'TRADE_OPTIONS').trim();
|
|
||||||
let tradeOptions = [];
|
|
||||||
|
|
||||||
if (tradeOptionsCode) {
|
|
||||||
tradeOptions = safeParse(tradeOptionsCode);
|
|
||||||
// Ensure tradeOptions is an array
|
|
||||||
if (!Array.isArray(tradeOptions)) {
|
|
||||||
tradeOptions = [tradeOptions];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const json = {
|
|
||||||
type: 'trade_action',
|
|
||||||
condition: condition,
|
|
||||||
trade_type: tradeType,
|
|
||||||
size: parseFloat(size),
|
|
||||||
stop_loss: stopLoss !== null ? parseFloat(stopLoss) : null,
|
|
||||||
take_profit: takeProfit !== null ? parseFloat(takeProfit) : null,
|
|
||||||
trade_options: tradeOptions
|
|
||||||
};
|
|
||||||
|
|
||||||
return JSON.stringify(json);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Trade Option Block JSON Generator
|
* last_candle_value
|
||||||
* Captures trade options like order type, limit price, and time in force.
|
* 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['trade_option'] = function(block) {
|
|
||||||
const orderType = block.getFieldValue('ORDER_TYPE'); // e.g., 'market', 'limit'
|
|
||||||
const limitPrice = Blockly.JSON.valueToCode(block, 'LIMIT_PRICE', Blockly.JSON.ORDER_ATOMIC) || null;
|
|
||||||
const timeInForce = block.getFieldValue('TIF'); // e.g., 'gtc', 'ioc'
|
|
||||||
|
|
||||||
const json = {
|
|
||||||
order_type: orderType,
|
|
||||||
limit_price: limitPrice !== null ? parseFloat(limitPrice) : null,
|
|
||||||
tif: timeInForce
|
|
||||||
};
|
|
||||||
|
|
||||||
return JSON.stringify(json);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* comparison JSON Generator
|
|
||||||
* Compares two numerical values, where one can be a strategy_profit_loss block.
|
|
||||||
*/
|
|
||||||
Blockly.JSON['comparison'] = function(block) {
|
|
||||||
const operator = block.getFieldValue('OPERATOR');
|
|
||||||
|
|
||||||
// Generate JSON for left operand
|
|
||||||
const leftBlock = block.getInputTargetBlock('LEFT');
|
|
||||||
let leftValue;
|
|
||||||
if (leftBlock && leftBlock.type === 'strategy_profit_loss') {
|
|
||||||
leftValue = JSON.parse(Blockly.JSON['strategy_profit_loss'](leftBlock));
|
|
||||||
} else {
|
|
||||||
leftValue = Blockly.JSON.valueToCode(block, 'LEFT', Blockly.JSON.ORDER_ATOMIC) || 0;
|
|
||||||
try {
|
|
||||||
leftValue = JSON.parse(leftValue);
|
|
||||||
} catch (e) {
|
|
||||||
leftValue = parseFloat(leftValue);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generate JSON for right operand
|
|
||||||
const rightBlock = block.getInputTargetBlock('RIGHT');
|
|
||||||
let rightValue;
|
|
||||||
if (rightBlock && rightBlock.type === 'strategy_profit_loss') {
|
|
||||||
rightValue = JSON.parse(Blockly.JSON['strategy_profit_loss'](rightBlock));
|
|
||||||
} else {
|
|
||||||
rightValue = Blockly.JSON.valueToCode(block, 'RIGHT', Blockly.JSON.ORDER_ATOMIC) || 0;
|
|
||||||
try {
|
|
||||||
rightValue = JSON.parse(rightValue);
|
|
||||||
} catch (e) {
|
|
||||||
rightValue = parseFloat(rightValue);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const json = {
|
|
||||||
type: 'comparison',
|
|
||||||
operator: operator,
|
|
||||||
left: leftValue,
|
|
||||||
right: rightValue
|
|
||||||
};
|
|
||||||
|
|
||||||
return JSON.stringify(json);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Logical AND Block JSON Generator
|
|
||||||
* Captures logical AND operations between two conditions.
|
|
||||||
*/
|
|
||||||
Blockly.JSON['logical_and'] = function(block) {
|
|
||||||
const condition1 = Blockly.JSON.valueToCode(block, 'CONDITION1', Blockly.JSON.ORDER_ATOMIC) || "False";
|
|
||||||
const condition2 = Blockly.JSON.valueToCode(block, 'CONDITION2', Blockly.JSON.ORDER_ATOMIC) || "False";
|
|
||||||
|
|
||||||
const json = {
|
|
||||||
type: 'logical_and',
|
|
||||||
conditions: [condition1, condition2]
|
|
||||||
};
|
|
||||||
|
|
||||||
return JSON.stringify(json);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Logical OR Block JSON Generator
|
|
||||||
* Captures logical OR operations between two conditions.
|
|
||||||
*/
|
|
||||||
Blockly.JSON['logical_or'] = function(block) {
|
|
||||||
const condition1 = Blockly.JSON.valueToCode(block, 'CONDITION1', Blockly.JSON.ORDER_ATOMIC) || "False";
|
|
||||||
const condition2 = Blockly.JSON.valueToCode(block, 'CONDITION2', Blockly.JSON.ORDER_ATOMIC) || "False";
|
|
||||||
|
|
||||||
const json = {
|
|
||||||
type: 'logical_or',
|
|
||||||
conditions: [condition1, condition2]
|
|
||||||
};
|
|
||||||
|
|
||||||
return JSON.stringify(json);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Is False Block JSON Generator
|
|
||||||
* Captures a condition that checks if another condition is false.
|
|
||||||
*/
|
|
||||||
Blockly.JSON['is_false'] = function(block) {
|
|
||||||
const condition = Blockly.JSON.valueToCode(block, 'CONDITION', Blockly.JSON.ORDER_ATOMIC) || "False";
|
|
||||||
|
|
||||||
const json = {
|
|
||||||
type: 'is_false',
|
|
||||||
condition: condition
|
|
||||||
};
|
|
||||||
|
|
||||||
return JSON.stringify(json);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Arithmetic Operator Block JSON Generator
|
|
||||||
* Captures arithmetic operations between two numerical values.
|
|
||||||
*/
|
|
||||||
Blockly.JSON['arithmetic_operator'] = function(block) {
|
|
||||||
const operand1 = Blockly.JSON.valueToCode(block, 'OPERAND1', Blockly.JSON.ORDER_ATOMIC) || "0";
|
|
||||||
const operand2 = Blockly.JSON.valueToCode(block, 'OPERAND2', Blockly.JSON.ORDER_ATOMIC) || "0";
|
|
||||||
const operator = block.getFieldValue('OPERATOR'); // e.g., '+', '-', '*', '/'
|
|
||||||
|
|
||||||
const json = {
|
|
||||||
type: 'arithmetic_operator',
|
|
||||||
operator: operator,
|
|
||||||
operands: [operand1, operand2]
|
|
||||||
};
|
|
||||||
|
|
||||||
return JSON.stringify(json);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Last Candle Value Block JSON Generator
|
|
||||||
* Captures the value part of the last candle from a specified data source.
|
|
||||||
*/
|
*/
|
||||||
Blockly.JSON['last_candle_value'] = function(block) {
|
Blockly.JSON['last_candle_value'] = function(block) {
|
||||||
const candlePart = block.getFieldValue('CANDLE_PART'); // e.g., 'open', 'high', 'low', 'close', 'volume'
|
const candlePart = block.getFieldValue('candle_part'); // e.g., 'open', 'high', 'low', 'close'
|
||||||
const sourceCode = Blockly.JSON.valueToCode(block, 'SOURCE', Blockly.JSON.ORDER_ATOMIC) || "{}";
|
const sourceCode = Blockly.JSON.valueToCode(block, 'source', Blockly.JSON.ORDER_ATOMIC) || "{}";
|
||||||
const source = safeParse(sourceCode);
|
const source = safeParse(sourceCode);
|
||||||
|
|
||||||
const json = {
|
const json = {
|
||||||
|
|
@ -200,8 +105,149 @@ export function defineJsonGenerators() {
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Source Block JSON Generator
|
* strategy_profit_loss
|
||||||
* Captures the data source details like timeframe, exchange, and symbol.
|
* Evaluates the total strategy's profit or loss.
|
||||||
|
* Generates a JSON object with type and metric.
|
||||||
|
* If 'NEXT' is connected, includes additional metrics.
|
||||||
|
*/
|
||||||
|
Blockly.JSON['strategy_profit_loss'] = function(block) {
|
||||||
|
const metric = block.getFieldValue('METRIC'); // 'profit' or 'loss'
|
||||||
|
const json = {
|
||||||
|
type: 'strategy_profit_loss',
|
||||||
|
metric: metric
|
||||||
|
};
|
||||||
|
|
||||||
|
const nextBlock = block.getInputTargetBlock('NEXT');
|
||||||
|
if (nextBlock) {
|
||||||
|
const nextCode = this.blockToCode(nextBlock);
|
||||||
|
const nextJson = safeParse(nextCode);
|
||||||
|
if (Array.isArray(json.metrics)) {
|
||||||
|
json.metrics.push(nextJson);
|
||||||
|
} else {
|
||||||
|
json.metrics = [json.metric, nextJson.metric];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
Blockly.JSON['source'] = function(block) {
|
||||||
const timeframe = block.getFieldValue('TF'); // e.g., '5m', '1h'
|
const timeframe = block.getFieldValue('TF'); // e.g., '5m', '1h'
|
||||||
|
|
@ -217,126 +263,141 @@ export function defineJsonGenerators() {
|
||||||
return JSON.stringify(json);
|
return JSON.stringify(json);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/************************************************
|
||||||
|
* LOGICAL BLOCKS *
|
||||||
|
************************************************/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Target Market Block JSON Generator
|
* logical_and
|
||||||
* Captures target market parameters for trading.
|
* Performs a logical AND operation between two Boolean conditions.
|
||||||
|
* Generates a JSON object with type and conditions.
|
||||||
*/
|
*/
|
||||||
Blockly.JSON['target_market'] = function(block) {
|
Blockly.JSON['logical_and'] = function(block) {
|
||||||
const timeframe = block.getFieldValue('TIMEFRAME'); // e.g., '5m', '1h'
|
const condition1Code = Blockly.JSON.valueToCode(block, 'left', Blockly.JSON.ORDER_ATOMIC) || "false";
|
||||||
const exchange = block.getFieldValue('EXCHANGE'); // e.g., 'Binance'
|
const condition2Code = Blockly.JSON.valueToCode(block, 'right', Blockly.JSON.ORDER_ATOMIC) || "false";
|
||||||
const symbol = block.getFieldValue('SYMBOL'); // e.g., 'BTCUSD'
|
|
||||||
|
const condition1 = safeParse(condition1Code) || condition1Code;
|
||||||
|
const condition2 = safeParse(condition2Code) || condition2Code;
|
||||||
|
|
||||||
const json = {
|
const json = {
|
||||||
timeframe: timeframe,
|
type: 'logical_and',
|
||||||
exchange: exchange,
|
conditions: [condition1, condition2]
|
||||||
symbol: symbol
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return JSON.stringify(json);
|
return JSON.stringify(json);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* strategy_profit_loss JSON Generator
|
* logical_or
|
||||||
* Outputs the metric (profit or loss) to evaluate.
|
* Performs a logical OR operation between two Boolean conditions.
|
||||||
|
* Generates a JSON object with type and conditions.
|
||||||
*/
|
*/
|
||||||
Blockly.JSON['strategy_profit_loss'] = function(block) {
|
Blockly.JSON['logical_or'] = function(block) {
|
||||||
const metric = block.getFieldValue('METRIC'); // 'profit' or 'loss'
|
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 = {
|
const json = {
|
||||||
type: 'strategy_profit_loss',
|
type: 'logical_or',
|
||||||
metric: metric
|
conditions: [condition1, condition2]
|
||||||
};
|
};
|
||||||
|
|
||||||
return JSON.stringify(json);
|
return JSON.stringify(json);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Current Balance Block JSON Generator
|
* is_false
|
||||||
* Captures the current balance of the account.
|
* Checks if a given Boolean condition is false.
|
||||||
|
* Generates a JSON object with type and condition.
|
||||||
*/
|
*/
|
||||||
Blockly.JSON['current_balance'] = function(block) {
|
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 = {
|
const json = {
|
||||||
type: 'current_balance'
|
type: 'is_false',
|
||||||
|
condition: condition
|
||||||
};
|
};
|
||||||
|
|
||||||
return JSON.stringify(json);
|
return JSON.stringify(json);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Starting Balance Block JSON Generator
|
* comparison
|
||||||
* Captures the starting balance of the account.
|
* Compares two numerical or dynamic values using operators like >, <, ==.
|
||||||
|
* Generates a JSON object with type, operator, left_operand, and right_operand.
|
||||||
*/
|
*/
|
||||||
Blockly.JSON['starting_balance'] = function(block) {
|
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 = {
|
const json = {
|
||||||
type: 'starting_balance'
|
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);
|
return JSON.stringify(json);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Active Trades Block JSON Generator
|
* time_in_force
|
||||||
* Captures the number of active trades.
|
* Sets the time in force for the order (GTC, FOK, IOC).
|
||||||
*/
|
* Generates a JSON object with type and tif.
|
||||||
Blockly.JSON['active_trades'] = function(block) {
|
|
||||||
const json = {
|
|
||||||
type: 'active_trades'
|
|
||||||
};
|
|
||||||
|
|
||||||
return JSON.stringify(json);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Flag Is Set Block JSON Generator
|
|
||||||
* Captures whether a specific flag is set.
|
|
||||||
*/
|
|
||||||
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 Block JSON Generator
|
|
||||||
* Captures the action to set a specific flag.
|
|
||||||
*/
|
|
||||||
Blockly.JSON['set_flag'] = function(block) {
|
|
||||||
const flagName = block.getFieldValue('FLAG_NAME'); // e.g., 'flag1'
|
|
||||||
|
|
||||||
const json = {
|
|
||||||
type: 'set_flag',
|
|
||||||
flag_name: flagName
|
|
||||||
};
|
|
||||||
|
|
||||||
return JSON.stringify(json);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Value Input Block JSON Generator
|
|
||||||
* Captures a numerical input value.
|
|
||||||
*/
|
|
||||||
Blockly.JSON['value_input'] = function(block) {
|
|
||||||
const value = Blockly.JSON.valueToCode(block, 'VALUE', Blockly.JSON.ORDER_ATOMIC) || "0";
|
|
||||||
|
|
||||||
const json = {
|
|
||||||
type: 'value_input',
|
|
||||||
value: parseFloat(value)
|
|
||||||
};
|
|
||||||
|
|
||||||
return JSON.stringify(json);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Time in Force Block JSON Generator
|
|
||||||
* Captures the time in force for an order.
|
|
||||||
*/
|
*/
|
||||||
Blockly.JSON['time_in_force'] = function(block) {
|
Blockly.JSON['time_in_force'] = function(block) {
|
||||||
const tif = block.getFieldValue('TIF'); // e.g., 'gtc', 'ioc'
|
const tif = block.getFieldValue('tif'); // 'gtc', 'fok', 'ioc'
|
||||||
|
|
||||||
const json = {
|
const json = {
|
||||||
type: 'time_in_force',
|
type: 'time_in_force',
|
||||||
|
|
@ -347,94 +408,129 @@ export function defineJsonGenerators() {
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Conditional Execution Block JSON Generator
|
* stop_loss
|
||||||
* Captures conditional statements within the strategy.
|
* Sets the Stop Loss parameter for a trade.
|
||||||
|
* Generates a JSON object with type and stop_loss.
|
||||||
*/
|
*/
|
||||||
Blockly.JSON['conditional_execution'] = function(block) {
|
Blockly.JSON['stop_loss'] = function(block) {
|
||||||
const conditionCode = Blockly.JSON.valueToCode(block, 'CONDITION', Blockly.JSON.ORDER_ATOMIC) || "False";
|
const stopLossCode = Blockly.JSON.valueToCode(block, 'stop_loss_input', Blockly.JSON.ORDER_ATOMIC) || "0";
|
||||||
const actionsCode = Blockly.JSON.statementToCode(block, 'ACTIONS').trim();
|
const stop_loss = safeParse(stopLossCode) || parseFloat(stopLossCode);
|
||||||
let actions = [];
|
|
||||||
|
|
||||||
if (actionsCode) {
|
|
||||||
actions = safeParse(actionsCode);
|
|
||||||
// Ensure actions is an array
|
|
||||||
if (!Array.isArray(actions)) {
|
|
||||||
actions = [actions];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const json = {
|
const json = {
|
||||||
type: 'conditional_execution',
|
type: 'stop_loss',
|
||||||
condition: conditionCode,
|
stop_loss: stop_loss
|
||||||
actions: actions
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return JSON.stringify(json);
|
return JSON.stringify(json);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Market Order Block JSON Generator
|
* take_profit
|
||||||
* Captures market order details.
|
* Sets the Take Profit parameter for a trade.
|
||||||
|
* Generates a JSON object with type and take_profit.
|
||||||
*/
|
*/
|
||||||
Blockly.JSON['market_order'] = function(block) {
|
Blockly.JSON['take_profit'] = function(block) {
|
||||||
const size = Blockly.JSON.valueToCode(block, 'SIZE', Blockly.JSON.ORDER_ATOMIC) || 0;
|
const takeProfitCode = Blockly.JSON.valueToCode(block, 'take_profit_input', Blockly.JSON.ORDER_ATOMIC) || "0";
|
||||||
|
const take_profit = safeParse(takeProfitCode) || parseFloat(takeProfitCode);
|
||||||
|
|
||||||
const json = {
|
const json = {
|
||||||
type: 'market_order',
|
type: 'take_profit',
|
||||||
size: parseFloat(size)
|
take_profit: take_profit
|
||||||
};
|
};
|
||||||
|
|
||||||
return JSON.stringify(json);
|
return JSON.stringify(json);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Limit Order Block JSON Generator
|
* limit
|
||||||
* Captures limit order details.
|
* Sets the Limit parameter for a trade order.
|
||||||
|
* Generates a JSON object with type and limit_price.
|
||||||
*/
|
*/
|
||||||
Blockly.JSON['limit_order'] = function(block) {
|
Blockly.JSON['limit'] = function(block) {
|
||||||
const size = Blockly.JSON.valueToCode(block, 'SIZE', Blockly.JSON.ORDER_ATOMIC) || 0;
|
const limitCode = Blockly.JSON.valueToCode(block, 'limit_input', Blockly.JSON.ORDER_ATOMIC) || "0";
|
||||||
const price = Blockly.JSON.valueToCode(block, 'PRICE', Blockly.JSON.ORDER_ATOMIC) || 0;
|
const limit_price = safeParse(limitCode) || parseFloat(limitCode);
|
||||||
const tif = block.getFieldValue('TIF'); // e.g., 'gtc', 'ioc'
|
|
||||||
|
|
||||||
const json = {
|
const json = {
|
||||||
type: 'limit_order',
|
type: 'limit',
|
||||||
size: parseFloat(size),
|
limit_price: limit_price
|
||||||
price: parseFloat(price),
|
|
||||||
tif: tif
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return JSON.stringify(json);
|
return JSON.stringify(json);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Entry Point Block JSON Generator
|
* target_market
|
||||||
* Captures the entry point of the strategy.
|
* 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['entry_point'] = function(block) {
|
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 = {
|
const json = {
|
||||||
type: 'entry_point'
|
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);
|
return JSON.stringify(json);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Exit Point Block JSON Generator
|
* strategy_exit
|
||||||
* Captures the exit point of the strategy.
|
* Overrides all, pauses the strategy, and exits trades based on configuration.
|
||||||
|
* Generates a JSON object with type, exit_condition, and exit_option.
|
||||||
*/
|
*/
|
||||||
Blockly.JSON['exit_point'] = function(block) {
|
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 = {
|
const json = {
|
||||||
type: 'exit_point'
|
type: 'strategy_exit',
|
||||||
|
exit_condition: exit_condition,
|
||||||
|
exit_option: exit_option
|
||||||
};
|
};
|
||||||
|
|
||||||
return JSON.stringify(json);
|
return JSON.stringify(json);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/************************************************
|
||||||
|
* INFO BLOCKS *
|
||||||
|
************************************************/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Notify User Block JSON Generator
|
* notify_user
|
||||||
* Captures a message to notify the user.
|
* Sends a notification message to the user.
|
||||||
|
* Generates a JSON object with type and message.
|
||||||
*/
|
*/
|
||||||
Blockly.JSON['notify_user'] = function(block) {
|
Blockly.JSON['notify_user'] = function(block) {
|
||||||
const message = Blockly.JSON.valueToCode(block, 'MESSAGE', Blockly.JSON.ORDER_ATOMIC) || "No message provided.";
|
const messageCode = Blockly.JSON.valueToCode(block, 'MESSAGE', Blockly.JSON.ORDER_ATOMIC) || '"Your message here"';
|
||||||
|
const message = safeParse(messageCode) || messageCode;
|
||||||
|
|
||||||
const json = {
|
const json = {
|
||||||
type: 'notify_user',
|
type: 'notify_user',
|
||||||
|
|
@ -444,5 +540,89 @@ export function defineJsonGenerators() {
|
||||||
return JSON.stringify(json);
|
return JSON.stringify(json);
|
||||||
};
|
};
|
||||||
|
|
||||||
console.log('All JSON generators have been defined successfully.');
|
/**
|
||||||
|
* 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.');
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -105,46 +105,59 @@
|
||||||
|
|
||||||
|
|
||||||
<xml id="toolbox" style="display: none">
|
<xml id="toolbox" style="display: none">
|
||||||
|
<!-- Indicators Category -->
|
||||||
<category name="Indicators" colour="230">
|
<category name="Indicators" colour="230">
|
||||||
<!-- Indicator blocks go here -->
|
<!-- Indicator blocks will be added here -->
|
||||||
</category>
|
</category>
|
||||||
|
<!-- Value Blocks Category -->
|
||||||
<category name="Values" colour="230">
|
<category name="Value" colour="#E69500">
|
||||||
<block type="last_candle_value"></block>
|
<block type="last_candle_value"></block>
|
||||||
<block type="value_input"></block>
|
<block type="strategy_profit_loss"></block>
|
||||||
<block type="source"></block>
|
|
||||||
<block type="current_balance"></block>
|
<block type="current_balance"></block>
|
||||||
<block type="starting_balance"></block>
|
<block type="starting_balance"></block>
|
||||||
<block type="strategy_profit_loss"></block>
|
|
||||||
<block type="active_trades"></block>
|
<block type="active_trades"></block>
|
||||||
|
<block type="math_operation"></block>
|
||||||
|
<block type="value_input"></block>
|
||||||
|
<block type="source"></block>
|
||||||
</category>
|
</category>
|
||||||
|
|
||||||
<category name="Logic" colour="210">
|
<!-- Logical Blocks Category -->
|
||||||
<block type="comparison"></block>
|
<category name="Logical" colour="#5C81A6">
|
||||||
<block type="logical_and"></block>
|
<block type="logical_and"></block>
|
||||||
<block type="logical_or"></block>
|
<block type="logical_or"></block>
|
||||||
<block type="arithmetic_operator"></block>
|
|
||||||
<block type="is_false"></block>
|
<block type="is_false"></block>
|
||||||
<block type="flag_is_set"></block>
|
<block type="comparison"></block> <!-- Added Comparison Block -->
|
||||||
<block type="set_flag"></block>
|
|
||||||
</category>
|
</category>
|
||||||
|
|
||||||
<!-- New category for Trading Actions -->
|
<!-- Trade Order Blocks Category -->
|
||||||
<category name="Trading" colour="230">
|
<category name="Trade Order" colour="#3366CC">
|
||||||
<block type="trade_action"></block>
|
<block type="trade_action"></block>
|
||||||
<block type="order_type"></block>
|
|
||||||
<block type="time_in_force"></block>
|
<block type="time_in_force"></block>
|
||||||
<block type="stop_loss"></block>
|
<block type="stop_loss"></block>
|
||||||
<block type="take_profit"></block>
|
<block type="take_profit"></block>
|
||||||
|
<block type="limit"></block>
|
||||||
<block type="target_market"></block>
|
<block type="target_market"></block>
|
||||||
</category>
|
</category>
|
||||||
<!-- New category for Control Blocks -->
|
|
||||||
<category name="Control" colour="120">
|
<!-- Control Blocks Category -->
|
||||||
<block type="entry_point"></block>
|
<category name="Control" colour="#FF9E00">
|
||||||
<block type="exit_point"></block>
|
<block type="strategy_pause"></block>
|
||||||
|
<block type="strategy_exit"></block>
|
||||||
|
<!-- Removed entry_point block as per your request -->
|
||||||
|
</category>
|
||||||
|
|
||||||
|
<!-- Info Blocks Category -->
|
||||||
|
<category name="Info" colour="#6C8EBF">
|
||||||
<block type="notify_user"></block>
|
<block type="notify_user"></block>
|
||||||
|
<block type="get_variable"></block>
|
||||||
|
<block type="set_variable"></block>
|
||||||
|
<block type="flag_is_set"></block>
|
||||||
|
<block type="set_flag"></block>
|
||||||
</category>
|
</category>
|
||||||
</xml>
|
</xml>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue