The generator definitions seem to work now.
This commit is contained in:
parent
bb7fdbf8f9
commit
50bcfa05cd
|
|
@ -1,27 +1,222 @@
|
|||
"""
|
||||
|
||||
{
|
||||
"type": "strategy",
|
||||
"statements": [
|
||||
{
|
||||
"type": "trade_action",
|
||||
"trade_type": "buy",
|
||||
"inputs": {
|
||||
"size": {
|
||||
"type": "sqrt",
|
||||
"inputs": {
|
||||
"number": {
|
||||
"type": "power",
|
||||
"inputs": {
|
||||
"base": 2,
|
||||
"exponent": 3
|
||||
"name": "fff",
|
||||
"strategy_json": {
|
||||
"type": "strategy",
|
||||
"statements": [
|
||||
{
|
||||
"type": "execute_if",
|
||||
"inputs": {
|
||||
"CONDITION": {
|
||||
"type": "comparison",
|
||||
"operator": "<",
|
||||
"inputs": {
|
||||
"LEFT": {
|
||||
"type": "risk_ratio",
|
||||
"inputs": {}
|
||||
},
|
||||
"RIGHT": {
|
||||
"type": "dynamic_value",
|
||||
"values": [
|
||||
{
|
||||
"type": "current_balance",
|
||||
"inputs": {}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"statements": {
|
||||
"DO": [
|
||||
{
|
||||
"type": "max_position_size",
|
||||
"inputs": {
|
||||
"MAX_SIZE": {
|
||||
"type": "available_balance",
|
||||
"inputs": {}
|
||||
}
|
||||
},
|
||||
"next": {
|
||||
"type": "execute_if",
|
||||
"inputs": {
|
||||
"CONDITION": {
|
||||
"type": "is_false",
|
||||
"inputs": {
|
||||
"condition": {
|
||||
"type": "order_status",
|
||||
"inputs": {
|
||||
"order_name": "order_name",
|
||||
"status": "filled"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"statements": {
|
||||
"DO": [
|
||||
{
|
||||
"type": "execute_if",
|
||||
"inputs": {
|
||||
"CONDITION": {
|
||||
"type": "comparison",
|
||||
"operator": ">",
|
||||
"inputs": {
|
||||
"LEFT": {
|
||||
"type": "dynamic_value",
|
||||
"values": [
|
||||
{
|
||||
"type": "last_candle_value",
|
||||
"inputs": {
|
||||
"source": {},
|
||||
"candle_part": "open"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"RIGHT": {
|
||||
"type": "dynamic_value",
|
||||
"values": [
|
||||
44
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"statements": {
|
||||
"DO": [
|
||||
{
|
||||
"type": "trade_action",
|
||||
"trade_type": "buy",
|
||||
"inputs": {
|
||||
"size": {
|
||||
"type": "math_operation",
|
||||
"inputs": {
|
||||
"operator": "divide",
|
||||
"left_operand": {
|
||||
"type": "starting_balance",
|
||||
"inputs": {}
|
||||
},
|
||||
"right_operand": 15
|
||||
}
|
||||
}
|
||||
},
|
||||
"trade_options": [
|
||||
{
|
||||
"type": "name_order",
|
||||
"inputs": {
|
||||
"order_name": "order_name"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "target_market",
|
||||
"inputs": {
|
||||
"time_frame": "15m",
|
||||
"exchange": "binance",
|
||||
"symbol": "ETH/BTC"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "execute_if",
|
||||
"inputs": {
|
||||
"CONDITION": {
|
||||
"type": "is_false",
|
||||
"inputs": {
|
||||
"condition": {
|
||||
"type": "order_status",
|
||||
"inputs": {
|
||||
"order_name": "order_name",
|
||||
"status": "filled"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"statements": {
|
||||
"DO": [
|
||||
{
|
||||
"type": "execute_if",
|
||||
"inputs": {
|
||||
"CONDITION": {
|
||||
"type": "comparison",
|
||||
"operator": ">",
|
||||
"inputs": {
|
||||
"LEFT": {
|
||||
"type": "dynamic_value",
|
||||
"values": [
|
||||
{
|
||||
"type": "last_candle_value",
|
||||
"inputs": {
|
||||
"source": {},
|
||||
"candle_part": "open"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"RIGHT": {
|
||||
"type": "dynamic_value",
|
||||
"values": [
|
||||
44
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"statements": {
|
||||
"DO": [
|
||||
{
|
||||
"type": "trade_action",
|
||||
"trade_type": "buy",
|
||||
"inputs": {
|
||||
"size": {
|
||||
"type": "math_operation",
|
||||
"inputs": {
|
||||
"operator": "divide",
|
||||
"left_operand": {
|
||||
"type": "starting_balance",
|
||||
"inputs": {}
|
||||
},
|
||||
"right_operand": 15
|
||||
}
|
||||
}
|
||||
},
|
||||
"trade_options": [
|
||||
{
|
||||
"type": "name_order",
|
||||
"inputs": {
|
||||
"order_name": "order_name"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "target_market",
|
||||
"inputs": {
|
||||
"time_frame": "15m",
|
||||
"exchange": "binance",
|
||||
"symbol": "ETH/BTC"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"workspace": "<xml xmlns=\"https://developers.google.com/blockly/xml\"><block type=\"execute_if\" id=\"MsS{sEm446Tq-9cZ?LEM\" x=\"50\" y=\"110\"><comment pinned=\"false\" h=\"80\" w=\"160\">Execute the enclosed statements if the condition is true.</comment><value name=\"CONDITION\"><block type=\"comparison\" id=\"o$er@YT|44pRfD2*;kC%\"><field name=\"OPERATOR\"><</field><comment pinned=\"false\" h=\"80\" w=\"160\">Compare two values using operators like >, <, ==.</comment><value name=\"LEFT\"><block type=\"risk_ratio\" id=\"-{)}8vrsu.uQ`,}~MEf-\"><comment pinned=\"false\" h=\"80\" w=\"160\">Calculate and retrieve the current risk ratio.</comment></block></value><value name=\"RIGHT\"><block type=\"current_balance\" id=\"rG=}HE!*$H|BWca!-G+%\"><comment pinned=\"false\" h=\"80\" w=\"160\">Retrieve the current balance of the strategy.</comment></block></value></block></value><statement name=\"DO\"><block type=\"max_position_size\" id=\"p{Do;wN=r3%T.7QD!]H@\"><comment pinned=\"false\" h=\"80\" w=\"160\">Set a maximum limit on the number of positions the strategy can hold.</comment><value name=\"MAX_SIZE\"><block type=\"available_balance\" id=\"H;h(SZy-/g/;;|S_]YQb\"><comment pinned=\"false\" h=\"80\" w=\"160\">Retrieve the overall available (liquid) balance of the user.</comment></block></value><next><block type=\"execute_if\" id=\"85pyvJR)Fr*LmqgKnIUm\"><comment pinned=\"false\" h=\"80\" w=\"160\">Execute the enclosed statements if the condition is true.</comment><value name=\"CONDITION\"><block type=\"is_false\" id=\"(vRWbh:/a=bX}${W4(/t\"><comment pinned=\"false\" h=\"80\" w=\"160\">Check if a condition is false.</comment><value name=\"condition\"><block type=\"order_status\" id=\"v#j1BnPG5pKC$M?csI)8\"><field name=\"ORDER_NAME\">order_name</field><field name=\"order_status\">partial</field><comment pinned=\"false\" h=\"80\" w=\"160\">Get the status of a named order.</comment></block></value></block></value><statement name=\"DO\"><block type=\"execute_if\" id=\"+dPkaDvC^21ylWseCZV;\"><comment pinned=\"false\" h=\"80\" w=\"160\">Execute the enclosed statements if the condition is true.</comment><value name=\"CONDITION\"><block type=\"comparison\" id=\"YTu*wGkeOU0k4]D|(j*:\"><field name=\"OPERATOR\">></field><comment pinned=\"false\" h=\"80\" w=\"160\">Compare two values using operators like >, <, ==.</comment><value name=\"LEFT\"><block type=\"last_candle_value\" id=\"8h3nZn?#t3UXcuU}icCC\"><field name=\"candle_part\">open</field><comment pinned=\"false\" h=\"80\" w=\"160\">Retrieve a specific part (Open, High, Low, Close) of the last candle from a given source.</comment></block></value><value name=\"RIGHT\"><block type=\"value_input\" id=\"i|%1Z,IK5~WCR5t~[T=J\"><field name=\"VALUE\">44</field><comment pinned=\"false\" h=\"80\" w=\"160\">Enter a numerical value. Chain multiple for a list.</comment></block></value></block></value><statement name=\"DO\"><block type=\"trade_action\" id=\"CYMHOlTFGfsK|l:ce{bX\"><field name=\"tradeType\">buy</field><comment pinned=\"false\" h=\"80\" w=\"160\">Execute a Buy/Sell trade based on a condition with specified size and options.</comment><value name=\"size\"><block type=\"math_operation\" id=\"^+dd,6d[Yg#w+bS|C{{S\"><field name=\"operator\">DIVIDE</field><comment pinned=\"false\" h=\"80\" w=\"160\">Perform basic arithmetic operations between two values.</comment><value name=\"LEFT\"><block type=\"starting_balance\" id=\"Bi5*6JEj#xP`DK9M1-p^\"><comment pinned=\"false\" h=\"80\" w=\"160\">Retrieve the starting balance of the strategy.</comment></block></value><value name=\"RIGHT\"><block type=\"value_input\" id=\".kd4Xeke%72-6/tdATz(\"><field name=\"VALUE\">15</field><comment pinned=\"false\" h=\"80\" w=\"160\">Enter a numerical value. Chain multiple for a list.</comment></block></value></block></value><statement name=\"trade_options\"><block type=\"name_order\" id=\"c;$K`P/c1nf2Ut)yJf=]\"><field name=\"order_name\">order_name</field><comment pinned=\"false\" h=\"80\" w=\"160\">Assign a custom name to the current order.</comment><next><block type=\"target_market\" id=\"FsX9_==AQf?DKtj]1C`]\"><field name=\"TF\">15m</field><field name=\"EXC\">binance</field><field name=\"SYM\">ETH/BTC</field><comment pinned=\"false\" h=\"80\" w=\"160\">Choose the target market for posting orders.</comment></block></next></block></statement></block></statement></block></statement></block></next></block></statement></block></xml>"
|
||||
}
|
||||
|
||||
"""
|
||||
|
|
@ -0,0 +1,120 @@
|
|||
### general:
|
||||
```plantuml
|
||||
@startuml
|
||||
start
|
||||
:User creates blocks in Blockly workspace;
|
||||
:User triggers code generation;
|
||||
:Blockly.JSON.blockToCode(block) is called;
|
||||
if (Generator function exists for block type?) then (Yes)
|
||||
:Call the generator function for the block type;
|
||||
if (Valid JSON object returned?) then (Yes)
|
||||
:Proceed to process inputs and statements;
|
||||
else (No)
|
||||
:Log error about invalid JSON object;
|
||||
stop
|
||||
endif
|
||||
else (No)
|
||||
:Log error about missing generator function;
|
||||
stop
|
||||
endif
|
||||
:Process inputs and statements recursively;
|
||||
:Handle 'next' blocks recursively;
|
||||
:Assemble final JSON representation;
|
||||
stop
|
||||
@enduml
|
||||
|
||||
|
||||
```
|
||||
|
||||
### blockToCode:
|
||||
```plantuml
|
||||
@startuml
|
||||
start
|
||||
:Input block;
|
||||
if (Generator function exists for block.type?) then (Yes)
|
||||
:Call generator function to get JSON;
|
||||
if (JSON is valid?) then (Yes)
|
||||
if (skipAdditionalParsing flag is set?) then (Yes)
|
||||
:Return JSON as is;
|
||||
else (No)
|
||||
:Call _blockToJson for further processing;
|
||||
endif
|
||||
else (No)
|
||||
:Log error about invalid JSON;
|
||||
stop
|
||||
endif
|
||||
else (No)
|
||||
:Log error about missing generator function;
|
||||
stop
|
||||
endif
|
||||
stop
|
||||
@enduml
|
||||
|
||||
```
|
||||
### _blockToJson:
|
||||
```plantuml
|
||||
@startuml
|
||||
start
|
||||
:Check if block is null;
|
||||
if (block is null?) then (Yes)
|
||||
:Return null;
|
||||
stop
|
||||
endif
|
||||
|
||||
:Call _calculateNestingDepth(block, currentDepth);
|
||||
if (Depth exceeds maxDepth?) then (Yes)
|
||||
:Throw Error "Maximum block nesting depth exceeded.";
|
||||
stop
|
||||
endif
|
||||
|
||||
if (Custom JSON generator exists for block.type?) then (Yes)
|
||||
:Call custom generator to get json;
|
||||
if (json is not valid?) then (Yes)
|
||||
:Log error;
|
||||
:Return null;
|
||||
stop
|
||||
endif
|
||||
|
||||
if (json.skipAdditionalParsing is true?) then (Yes)
|
||||
:Remove skipAdditionalParsing from json;
|
||||
if (json.type is not defined?) then (Yes)
|
||||
:Return null;
|
||||
else
|
||||
:Return json;
|
||||
stop
|
||||
endif
|
||||
endif
|
||||
else (No)
|
||||
:Create default json object with type, fields, inputs, statements;
|
||||
:Capture all fields in the block;
|
||||
endif
|
||||
|
||||
:Initialize json.inputs and json.statements if not already present;
|
||||
:Initialize index = 0;
|
||||
while (index < length of block.inputList) is (Yes)
|
||||
:input = block.inputList[index];
|
||||
if (input.connection and input.connection.targetBlock() exist?) then (Yes)
|
||||
:Get targetBlock and targetJson;
|
||||
:Call _blockToJson(targetBlock, currentDepth + 1);
|
||||
if (input.type == INPUT_VALUE) then (Yes)
|
||||
:Add targetJson to json.inputs[input.name];
|
||||
else if (input.type == NEXT_STATEMENT) then (Yes)
|
||||
:Add targetJson to json.statements[input.name] array;
|
||||
endif
|
||||
endif
|
||||
:Increment index;
|
||||
endwhile
|
||||
|
||||
:Handle 'next' connection;
|
||||
if (block has next block?) then (Yes)
|
||||
:Call _blockToJson(block.getNextBlock(), currentDepth);
|
||||
:Assign result to json.next;
|
||||
endif
|
||||
|
||||
:Return json;
|
||||
stop
|
||||
@enduml
|
||||
|
||||
|
||||
|
||||
```
|
||||
|
|
@ -0,0 +1,254 @@
|
|||
### StratWorkspaceManager:
|
||||
```plantuml
|
||||
@startuml
|
||||
start
|
||||
:Initialize StratWorkspaceManager;
|
||||
note right
|
||||
- workspace = null
|
||||
- blocksDefined = false
|
||||
- MAX_TOP_LEVEL_BLOCKS = 10
|
||||
end note
|
||||
|
||||
if (blocklyDiv exists?) then (yes)
|
||||
if (workspace exists?) then (yes)
|
||||
:Dispose of existing workspace;
|
||||
endif
|
||||
|
||||
:Load Modules and Init Workspace;
|
||||
note right
|
||||
Calls _loadModulesAndInitWorkspace()
|
||||
end note
|
||||
|
||||
:Initialize Blockly Workspace;
|
||||
note right
|
||||
- Set blocksDefined = true
|
||||
- Inject workspace into blocklyDiv
|
||||
end note
|
||||
else (no)
|
||||
:Log error "blocklyDiv is not loaded.";
|
||||
endif
|
||||
|
||||
:Adjust Workspace;
|
||||
note right
|
||||
Calls adjustWorkspace()
|
||||
end note
|
||||
|
||||
stop
|
||||
@enduml
|
||||
|
||||
```
|
||||
### initWorkspace:
|
||||
```plantuml
|
||||
@startuml
|
||||
participant "StratWorkspaceManager" as SWM
|
||||
participant "Document" as Doc
|
||||
participant "Blockly"
|
||||
|
||||
SWM -> Doc: getElementById('blocklyDiv')
|
||||
alt blocklyDiv exists
|
||||
SWM -> SWM: workspace.dispose() [if workspace exists]
|
||||
SWM -> SWM: _loadModulesAndInitWorkspace()
|
||||
SWM -> SWM: blocksDefined = true
|
||||
SWM -> Blockly: inject('blocklyDiv', options)
|
||||
else
|
||||
SWM -> SWM: console.error("blocklyDiv is not loaded.")
|
||||
end
|
||||
@enduml
|
||||
|
||||
```
|
||||
|
||||
### _loadModulesAndInitWorkspace:
|
||||
```plantuml
|
||||
@startuml
|
||||
participant "StratWorkspaceManager" as SWM
|
||||
participant "Document" as Doc
|
||||
participant "Blockly"
|
||||
@startuml
|
||||
participant "StratWorkspaceManager" as SWM
|
||||
participant "Custom Blocks Modules" as Modules
|
||||
participant "Document" as Doc
|
||||
participant "Blockly"
|
||||
|
||||
SWM -> SWM: Check blocksDefined
|
||||
alt blocksDefined is false
|
||||
SWM -> Modules: Import and define modules
|
||||
SWM -> SWM: blocksDefined = true
|
||||
else
|
||||
SWM -> SWM: Skip module loading
|
||||
end
|
||||
|
||||
SWM -> Doc: getElementById('toolbox_advanced')
|
||||
alt toolbox exists
|
||||
SWM -> Blockly: inject('blocklyDiv', options)
|
||||
SWM -> SWM: Workspace initialized
|
||||
else
|
||||
SWM -> SWM: console.error("toolbox is not loaded.")
|
||||
end
|
||||
@enduml
|
||||
|
||||
SWM -> Doc: getElementById('blocklyDiv')
|
||||
alt blocklyDiv exists
|
||||
SWM -> SWM: workspace.dispose() [if workspace exists]
|
||||
SWM -> SWM: _loadModulesAndInitWorkspace()
|
||||
SWM -> SWM: blocksDefined = true
|
||||
SWM -> Blockly: inject('blocklyDiv', options)
|
||||
else
|
||||
SWM -> SWM: console.error("blocklyDiv is not loaded.")
|
||||
end
|
||||
@enduml
|
||||
|
||||
```
|
||||
### adjustWorkspace:
|
||||
```plantuml
|
||||
@startuml
|
||||
participant "StratWorkspaceManager" as SWM
|
||||
participant "Document" as Doc
|
||||
participant "Blockly"
|
||||
|
||||
SWM -> Doc: getElementById('blocklyDiv')
|
||||
alt blocklyDiv and workspace exist
|
||||
SWM -> Blockly: svgResize(workspace)
|
||||
else
|
||||
SWM -> SWM: console.error("Cannot resize workspace.")
|
||||
end
|
||||
@enduml
|
||||
|
||||
```
|
||||
### compileStrategyJson:
|
||||
```plantuml
|
||||
@startuml
|
||||
start
|
||||
:Check if workspace is available;
|
||||
if (workspace exists?) then (yes)
|
||||
:Get strategy name from name_box;
|
||||
:Generate strategy JSON;
|
||||
note right
|
||||
Calls _generateStrategyJsonFromWorkspace()
|
||||
end note
|
||||
if (strategyJson is null?) then (yes)
|
||||
:Log error "Failed to generate strategy JSON.";
|
||||
stop
|
||||
endif
|
||||
:Serialize workspace to XML;
|
||||
:Return JSON string containing strategy data;
|
||||
else (no)
|
||||
:Log error "Workspace is not available.";
|
||||
stop
|
||||
endif
|
||||
@enduml
|
||||
|
||||
|
||||
```
|
||||
### _generateStrategyJsonFromWorkspace:
|
||||
```plantuml
|
||||
@startuml
|
||||
start
|
||||
:Categorize orphaned blocks;
|
||||
note right
|
||||
Calls _categorizeOrphanedBlocks(workspace)
|
||||
end note
|
||||
|
||||
:Calculate totalTopLevelBlocks;
|
||||
|
||||
if (totalTopLevelBlocks > MAX_TOP_LEVEL_BLOCKS?) then (yes)
|
||||
:Log error "Too many top-level blocks.";
|
||||
:Alert user;
|
||||
:Return null;
|
||||
stop
|
||||
else (no)
|
||||
:Create strategy JSON;
|
||||
note right
|
||||
Calls _createStrategyJson(initializationBlocks, actionBlocks)
|
||||
end note
|
||||
:Return strategy JSON;
|
||||
stop
|
||||
endif
|
||||
@enduml
|
||||
|
||||
```
|
||||
|
||||
### _categorizeOrphanedBlocks:
|
||||
```plantuml
|
||||
@startuml
|
||||
start
|
||||
:Get top-level blocks from workspace;
|
||||
|
||||
:Initialize initializationBlocks and actionBlocks arrays;
|
||||
|
||||
:Initialize index = 0;
|
||||
|
||||
while (index < total number of blocks?) is (yes)
|
||||
:Get block at index;
|
||||
if (block.type is initialization type?) then (yes)
|
||||
:Add block to initializationBlocks;
|
||||
else if (block.type is action type?) then (yes)
|
||||
:Add block to actionBlocks;
|
||||
else
|
||||
:Log warning "Block type not allowed as top-level block.";
|
||||
endif
|
||||
:Increment index;
|
||||
endwhile
|
||||
|
||||
:Return { initializationBlocks, actionBlocks };
|
||||
stop
|
||||
@enduml
|
||||
|
||||
|
||||
|
||||
|
||||
```
|
||||
|
||||
### _createStrategyJson:
|
||||
```plantuml
|
||||
@startuml
|
||||
start
|
||||
:Initialize empty statements array;
|
||||
|
||||
:Initialize index = 0;
|
||||
|
||||
while (index < number of initializationBlocks?) is (yes)
|
||||
:Get block at index from initializationBlocks;
|
||||
:Convert block to JSON;
|
||||
:Add to statements array;
|
||||
:Increment index;
|
||||
endwhile
|
||||
|
||||
:Reset index = 0;
|
||||
|
||||
while (index < number of actionBlocks?) is (yes)
|
||||
:Get block at index from actionBlocks;
|
||||
:Convert block to JSON;
|
||||
:Add to statements array;
|
||||
:Increment index;
|
||||
endwhile
|
||||
|
||||
:Create strategy JSON with type 'strategy' and statements;
|
||||
:Return strategy JSON;
|
||||
stop
|
||||
@enduml
|
||||
```
|
||||
|
||||
### _loadWorkspaceFromXml:
|
||||
```plantuml
|
||||
@startuml
|
||||
participant "StratWorkspaceManager" as SWM
|
||||
participant "Blockly"
|
||||
participant "Workspace" as WS
|
||||
|
||||
SWM -> SWM: Check if workspace is initialized
|
||||
alt workspace exists
|
||||
SWM -> Blockly: textToDom(workspaceXmlText)
|
||||
alt workspaceXml is valid
|
||||
SWM -> WS: clear()
|
||||
SWM -> Blockly: domToWorkspace(workspaceXml, workspace)
|
||||
else
|
||||
SWM -> SWM: console.error('Invalid workspace XML.')
|
||||
SWM -> User: alert('Invalid workspace data.')
|
||||
end
|
||||
else
|
||||
SWM -> SWM: console.error('Workspace is not initialized.')
|
||||
end
|
||||
@enduml
|
||||
|
||||
|
||||
```
|
||||
|
|
@ -0,0 +1,74 @@
|
|||
### Strategies:
|
||||
```plantuml
|
||||
@startuml
|
||||
class StratUIManager {
|
||||
- workspaceManager
|
||||
- targetEl
|
||||
- formElement
|
||||
--
|
||||
+ initUI(targetId, formElId)
|
||||
+ displayForm(action, strategyData = null)
|
||||
+ hideForm()
|
||||
+ updateStrategiesHtml(strategies)
|
||||
+ toggleFeeInput()
|
||||
+ registerDeleteStrategyCallback(callback)
|
||||
}
|
||||
|
||||
class StratDataManager {
|
||||
- strategies
|
||||
--
|
||||
+ fetchSavedStrategies(comms, data)
|
||||
+ addNewStrategy(data)
|
||||
+ updateStrategyData(data)
|
||||
+ removeStrategy(name)
|
||||
+ applyBatchUpdates(data)
|
||||
+ getAllStrategies()
|
||||
}
|
||||
|
||||
class StratWorkspaceManager {
|
||||
- workspace
|
||||
- blocksDefined
|
||||
- MAX_TOP_LEVEL_BLOCKS
|
||||
--
|
||||
+ initWorkspace()
|
||||
- loadModulesAndInitWorkspace()
|
||||
+ adjustWorkspace()
|
||||
+ compileStrategyJson()
|
||||
- generateStrategyJsonFromWorkspace()
|
||||
- categorizeOrphanedBlocks(workspace)
|
||||
- createStrategyJson(initializationBlocks, actionBlocks)
|
||||
+ loadWorkspaceFromXml(workspaceXmlText)
|
||||
}
|
||||
|
||||
class Strategies {
|
||||
- dataManager
|
||||
- workspaceManager
|
||||
- uiManager
|
||||
- comms
|
||||
- data
|
||||
- _initialized
|
||||
--
|
||||
+ initialize(targetId, formElId, data)
|
||||
+ handleStrategyError(errorData)
|
||||
+ handleStrategies(data)
|
||||
+ handleStrategyCreated(data)
|
||||
+ handleStrategyUpdated(data)
|
||||
+ handleStrategyDeleted(data)
|
||||
+ handleUpdates(data)
|
||||
+ resizeWorkspace()
|
||||
+ generateStrategyJson()
|
||||
+ restoreWorkspaceFromXml(workspaceXmlText)
|
||||
+ submitStrategy(action)
|
||||
+ deleteStrategy(name)
|
||||
}
|
||||
|
||||
' Relationships
|
||||
Strategies --> StratDataManager
|
||||
Strategies --> StratWorkspaceManager
|
||||
Strategies --> StratUIManager
|
||||
StratUIManager --> StratWorkspaceManager
|
||||
|
||||
@enduml
|
||||
|
||||
|
||||
...
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
class StratUIManager {
|
||||
constructor() {
|
||||
constructor(workspaceManager) {
|
||||
this.workspaceManager = workspaceManager;
|
||||
this.targetEl = null;
|
||||
this.formElement = null;
|
||||
}
|
||||
|
|
@ -26,7 +27,7 @@ class StratUIManager {
|
|||
/**
|
||||
* Displays the form for creating or editing a strategy.
|
||||
* @param {string} action - The action to perform ('new' or 'edit').
|
||||
* @param {string|null} strategyData - The data of the strategy to edit (only applicable for 'edit' action).
|
||||
* @param {object|null} strategyData - The data of the strategy to edit (only applicable for 'edit' action).
|
||||
*/
|
||||
async displayForm(action, strategyData = null) {
|
||||
console.log(`Opening form for action: ${action}, strategy: ${strategyData?.name}`);
|
||||
|
|
@ -64,9 +65,9 @@ class StratUIManager {
|
|||
this.formElement.style.display = "grid";
|
||||
|
||||
// Initialize Blockly workspace after the form becomes visible
|
||||
if (UI.strats && UI.strats.workspaceManager) {
|
||||
if (UI.strats && this.workspaceManager) {
|
||||
try {
|
||||
await UI.strats.workspaceManager.initWorkspace();
|
||||
await this.workspaceManager.initWorkspace();
|
||||
console.log("Blockly workspace initialized.");
|
||||
} catch (error) {
|
||||
console.error("Failed to initialize Blockly workspace:", error);
|
||||
|
|
@ -121,8 +122,11 @@ class StratUIManager {
|
|||
// Strategy icon
|
||||
const strategyIcon = document.createElement('div');
|
||||
strategyIcon.className = 'strategy-icon';
|
||||
// Open the form with strategy data when clicked
|
||||
strategyIcon.addEventListener('click', () => {
|
||||
this.displayForm('edit', strat); // Open the form with strategy data when clicked
|
||||
this.displayForm('edit', strat).catch(error => {
|
||||
console.error('Error displaying form:', error);
|
||||
});
|
||||
});
|
||||
|
||||
// Strategy name
|
||||
|
|
@ -150,6 +154,7 @@ class StratUIManager {
|
|||
* Toggles the fee input field based on the state of the public checkbox.
|
||||
*/
|
||||
toggleFeeInput() {
|
||||
/** @type {HTMLInputElement} */
|
||||
const publicCheckbox = document.getElementById('public_checkbox');
|
||||
const feeBox = document.getElementById('fee_box');
|
||||
|
||||
|
|
@ -223,7 +228,7 @@ class StratDataManager {
|
|||
|
||||
/**
|
||||
* Handles the deletion of a strategy.
|
||||
* @param {Object} data - The name for the deleted strategy.
|
||||
* @param {Object} name - The name for the deleted strategy.
|
||||
*/
|
||||
removeStrategy(name) {
|
||||
try {
|
||||
|
|
@ -259,7 +264,7 @@ class StratWorkspaceManager {
|
|||
this.workspace = null;
|
||||
this.blocksDefined = false;
|
||||
this.MAX_TOP_LEVEL_BLOCKS = 10; // Set your desired limit
|
||||
this.DEFAULT_MAX_NESTING_DEPTH = 5; // Set your desired default max depth
|
||||
this.MAX_DEPTH = 10; // Set your desired limit
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -280,6 +285,9 @@ class StratWorkspaceManager {
|
|||
|
||||
// Initialize custom blocks and Blockly workspace
|
||||
await this._loadModulesAndInitWorkspace();
|
||||
|
||||
// Set the maximum allowed nesting depth
|
||||
Blockly.JSON.maxDepth = this.MAX_DEPTH; // or any desired value
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -291,7 +299,7 @@ class StratWorkspaceManager {
|
|||
jsonBaseModule.defineJsonBaseGenerator();
|
||||
console.log('Defined defineJsonBaseGenerator from json_base_generator.js');
|
||||
|
||||
// Define lists of generator and block files with their corresponding define functions
|
||||
// Map generator and block files to the functions that define them.
|
||||
const generatorModules = [
|
||||
{ file: 'balances_generators.js', defineFunc: 'defineBalancesGenerators' },
|
||||
{ file: 'order_metrics_generators.js', defineFunc: 'defineOrderMetricsGenerators' },
|
||||
|
|
@ -408,7 +416,7 @@ class StratWorkspaceManager {
|
|||
console.error("Workspace is not available.");
|
||||
return null;
|
||||
}
|
||||
|
||||
/** @type {HTMLInputElement} */
|
||||
const nameElement = document.getElementById('name_box');
|
||||
if (!nameElement) {
|
||||
console.error("Name input element (name_box) is not available.");
|
||||
|
|
@ -416,9 +424,6 @@ class StratWorkspaceManager {
|
|||
}
|
||||
const strategyName = nameElement.value;
|
||||
|
||||
// Remove or comment out the following line as 'init' is undefined
|
||||
// Blockly.JSON.init(this.workspace);
|
||||
|
||||
// Generate code and data representations
|
||||
const strategyJson = this._generateStrategyJsonFromWorkspace();
|
||||
|
||||
|
|
@ -442,7 +447,7 @@ class StratWorkspaceManager {
|
|||
/**
|
||||
* Generates a JSON representation of the strategy from the workspace.
|
||||
* @private
|
||||
* @returns {Object[]} - An array of JSON objects representing the top-level blocks.
|
||||
* @returns {Object} - An array of JSON objects representing the top-level blocks.
|
||||
*/
|
||||
_generateStrategyJsonFromWorkspace() {
|
||||
const { initializationBlocks, actionBlocks } = this._categorizeOrphanedBlocks(this.workspace);
|
||||
|
|
@ -456,15 +461,13 @@ class StratWorkspaceManager {
|
|||
}
|
||||
|
||||
// Proceed with strategy JSON creation
|
||||
const strategyJson = this._createStrategyJson(initializationBlocks, actionBlocks);
|
||||
|
||||
return strategyJson;
|
||||
return this._createStrategyJson(initializationBlocks, actionBlocks);
|
||||
}
|
||||
|
||||
/**
|
||||
* Identify all orphaned blocks on the workspace. Categorize them into initialization blocks and action blocks.
|
||||
* @private
|
||||
* @returns {Object[]} - an object containing an array of initializationBlocks and actionBlocks.
|
||||
* @returns {Object} - an object containing an array of initializationBlocks and actionBlocks.
|
||||
*/
|
||||
_categorizeOrphanedBlocks(workspace) {
|
||||
const blocks = workspace.getTopBlocks(true);
|
||||
|
|
@ -481,6 +484,7 @@ class StratWorkspaceManager {
|
|||
case 'set_variable':
|
||||
case 'set_flag':
|
||||
case 'set_leverage':
|
||||
case 'max_position_size':
|
||||
case 'pause_strategy':
|
||||
case 'strategy_resume':
|
||||
case 'strategy_exit':
|
||||
|
|
@ -525,12 +529,10 @@ class StratWorkspaceManager {
|
|||
});
|
||||
|
||||
// Create the root strategy block
|
||||
const strategyJson = {
|
||||
type: 'strategy',
|
||||
statements: statements
|
||||
return {
|
||||
type: 'strategy',
|
||||
statements: statements
|
||||
};
|
||||
|
||||
return strategyJson;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -567,9 +569,9 @@ class StratWorkspaceManager {
|
|||
|
||||
class Strategies {
|
||||
constructor() {
|
||||
this.uiManager = new StratUIManager();
|
||||
this.dataManager = new StratDataManager();
|
||||
this.workspaceManager = new StratWorkspaceManager();
|
||||
this.uiManager = new StratUIManager(this.workspaceManager);
|
||||
this.comms = null;
|
||||
this.data = null;
|
||||
this._initialized = false;
|
||||
|
|
@ -593,17 +595,20 @@ class Strategies {
|
|||
this.uiManager.initUI(targetId, formElId);
|
||||
|
||||
if (!data || typeof data !== 'object') {
|
||||
throw new Error("Invalid data object provided for initialization.");
|
||||
console.error("Invalid data provided for initialization.");
|
||||
return;
|
||||
}
|
||||
this.data = data;
|
||||
|
||||
if (!this.data.user_name || typeof this.data.user_name !== 'string') {
|
||||
throw new Error("Invalid user_name provided in data object.");
|
||||
console.error("Invalid user_name provided in data object.");
|
||||
return;
|
||||
}
|
||||
|
||||
this.comms = this.data?.comms;
|
||||
if (!this.comms) {
|
||||
throw new Error('Communications instance not provided in data.');
|
||||
console.error("Communications instance not provided in data.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Register handlers with Comms for specific message types
|
||||
|
|
@ -704,14 +709,6 @@ class Strategies {
|
|||
this.uiManager.updateStrategiesHtml(this.dataManager.getAllStrategies());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the Blockly workspace using StratWorkspaceManager.
|
||||
* @async
|
||||
*/
|
||||
async createWorkspace() {
|
||||
await this.workspaceManager.initializeWorkspace();
|
||||
}
|
||||
|
||||
/**
|
||||
* Resizes the Blockly workspace using StratWorkspaceManager.
|
||||
*/
|
||||
|
|
@ -740,8 +737,11 @@ class Strategies {
|
|||
* @param {string} action - Action type, either 'new' or 'edit'.
|
||||
*/
|
||||
submitStrategy(action) {
|
||||
/** @type {HTMLInputElement} */
|
||||
const feeBox = document.getElementById('fee_box');
|
||||
/** @type {HTMLInputElement} */
|
||||
const nameBox = document.getElementById('name_box');
|
||||
/** @type {HTMLInputElement} */
|
||||
const publicCheckbox = document.getElementById('public_checkbox');
|
||||
|
||||
if (!feeBox || !nameBox || !publicCheckbox) {
|
||||
|
|
|
|||
|
|
@ -67,13 +67,13 @@ export function defineLogicalBlocks() {
|
|||
"args0": [
|
||||
{
|
||||
"type": "input_value",
|
||||
"name": "left",
|
||||
"name": "LEFT",
|
||||
"check": "Boolean",
|
||||
"align": "RIGHT"
|
||||
},
|
||||
{
|
||||
"type": "input_value",
|
||||
"name": "right",
|
||||
"name": "RIGHT",
|
||||
"check": "Boolean",
|
||||
"align": "LEFT"
|
||||
}
|
||||
|
|
@ -98,13 +98,13 @@ export function defineLogicalBlocks() {
|
|||
"args0": [
|
||||
{
|
||||
"type": "input_value",
|
||||
"name": "left",
|
||||
"name": "LEFT",
|
||||
"check": "Boolean",
|
||||
"align": "RIGHT"
|
||||
},
|
||||
{
|
||||
"type": "input_value",
|
||||
"name": "right",
|
||||
"name": "RIGHT",
|
||||
"check": "Boolean",
|
||||
"align": "LEFT"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -166,7 +166,7 @@ export function defineMarketDataBlocks() {
|
|||
},
|
||||
{
|
||||
"type": "input_value",
|
||||
"name": "NEXT",
|
||||
"name": "VALUES",
|
||||
"check": "dynamic_value",
|
||||
"align": "RIGHT"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ export function defineOrderMetricsBlocks() {
|
|||
},
|
||||
{
|
||||
"type": "input_value",
|
||||
"name": "NEXT",
|
||||
"name": "VALUES",
|
||||
"check": "dynamic_value",
|
||||
"align": "LEFT"
|
||||
}
|
||||
|
|
@ -60,7 +60,7 @@ export function defineOrderMetricsBlocks() {
|
|||
"args0": [
|
||||
{
|
||||
"type": "input_value",
|
||||
"name": "NEXT",
|
||||
"name": "VALUES",
|
||||
"check": "dynamic_value",
|
||||
"align": "LEFT"
|
||||
}
|
||||
|
|
@ -84,7 +84,7 @@ export function defineOrderMetricsBlocks() {
|
|||
"args0": [
|
||||
{
|
||||
"type": "input_value",
|
||||
"name": "NEXT",
|
||||
"name": "VALUES",
|
||||
"check": "dynamic_value",
|
||||
"align": "LEFT"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ export function defineTimeMetricsBlocks() {
|
|||
},
|
||||
{
|
||||
"type": "input_value",
|
||||
"name": "NEXT",
|
||||
"name": "VALUES",
|
||||
"check": "dynamic_value",
|
||||
"align": "LEFT"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ export function defineTradeMetricsBlocks() {
|
|||
"args0": [
|
||||
{
|
||||
"type": "input_value",
|
||||
"name": "NEXT",
|
||||
"name": "VALUES",
|
||||
"check": "dynamic_value",
|
||||
"align": "LEFT"
|
||||
}
|
||||
|
|
@ -49,7 +49,7 @@ export function defineTradeMetricsBlocks() {
|
|||
"args0": [
|
||||
{
|
||||
"type": "input_value",
|
||||
"name": "NEXT",
|
||||
"name": "VALUES",
|
||||
"check": "dynamic_value",
|
||||
"align": "LEFT"
|
||||
}
|
||||
|
|
@ -82,7 +82,7 @@ export function defineTradeMetricsBlocks() {
|
|||
},
|
||||
{
|
||||
"type": "input_value",
|
||||
"name": "NEXT",
|
||||
"name": "VALUES",
|
||||
"check": "dynamic_value",
|
||||
"align": "LEFT"
|
||||
}
|
||||
|
|
@ -105,7 +105,7 @@ export function defineTradeMetricsBlocks() {
|
|||
"args0": [
|
||||
{
|
||||
"type": "input_value",
|
||||
"name": "NEXT",
|
||||
"name": "VALUES",
|
||||
"check": "dynamic_value",
|
||||
"align": "LEFT"
|
||||
}
|
||||
|
|
@ -128,7 +128,7 @@ export function defineTradeMetricsBlocks() {
|
|||
"args0": [
|
||||
{
|
||||
"type": "input_value",
|
||||
"name": "NEXT",
|
||||
"name": "VALUES",
|
||||
"check": "dynamic_value",
|
||||
"align": "LEFT"
|
||||
}
|
||||
|
|
@ -152,7 +152,7 @@ export function defineTradeMetricsBlocks() {
|
|||
"args0": [
|
||||
{
|
||||
"type": "input_value",
|
||||
"name": "NEXT",
|
||||
"name": "VALUES",
|
||||
"check": "dynamic_value"
|
||||
}
|
||||
],
|
||||
|
|
|
|||
|
|
@ -15,39 +15,11 @@ export function defineAdvancedMathGenerators() {
|
|||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper Function: processValuesInput
|
||||
* Processes the 'VALUES' input and returns an array of values.
|
||||
*
|
||||
* @param {Blockly.Block} block - The block containing the 'VALUES' input.
|
||||
* @returns {Array} - An array of values from the 'VALUES' input.
|
||||
*/
|
||||
function processValuesInput(block) {
|
||||
const valuesInputBlock = block.getInputTargetBlock('VALUES');
|
||||
let values = [];
|
||||
|
||||
if (valuesInputBlock) {
|
||||
const valuesJson = Blockly.JSON.blockToCode(valuesInputBlock);
|
||||
|
||||
// Check if valuesJson is a 'dynamic_value' object and has an array of values
|
||||
if (valuesJson && valuesJson.type === 'dynamic_value' && Array.isArray(valuesJson.values)) {
|
||||
values = valuesJson.values; // Use the values array directly from dynamic_value
|
||||
} else if (Array.isArray(valuesJson)) {
|
||||
values = valuesJson; // Use valuesJson as-is if it's already an array
|
||||
} else {
|
||||
values = [valuesJson]; // Wrap non-array values in a list
|
||||
}
|
||||
}
|
||||
|
||||
return values;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Generator for 'power' Block
|
||||
*/
|
||||
Blockly.JSON['power'] = function(block) {
|
||||
const valuesArray = processValuesInput(block);
|
||||
const valuesArray = Blockly.JSON.processValuesInput(block);
|
||||
|
||||
const base = valuesArray[0] !== undefined ? valuesArray[0] : 0;
|
||||
const exponent = valuesArray[1] !== undefined ? valuesArray[1] : 1;
|
||||
|
|
@ -75,7 +47,7 @@ export function defineAdvancedMathGenerators() {
|
|||
* Generator for 'modulo' Block
|
||||
*/
|
||||
Blockly.JSON['modulo'] = function(block) {
|
||||
const valuesArray = processValuesInput(block);
|
||||
const valuesArray = Blockly.JSON.processValuesInput(block);
|
||||
|
||||
const dividend = valuesArray[0] !== undefined ? valuesArray[0] : 0;
|
||||
const divisor = valuesArray[1] !== undefined ? valuesArray[1] : 1;
|
||||
|
|
@ -103,7 +75,7 @@ export function defineAdvancedMathGenerators() {
|
|||
* Generator for 'sqrt' Block
|
||||
*/
|
||||
Blockly.JSON['sqrt'] = function(block) {
|
||||
const valuesArray = processValuesInput(block);
|
||||
const valuesArray = Blockly.JSON.processValuesInput(block);
|
||||
|
||||
const number = valuesArray[0] !== undefined ? valuesArray[0] : 0;
|
||||
|
||||
|
|
@ -129,7 +101,7 @@ export function defineAdvancedMathGenerators() {
|
|||
* Generator for 'abs' Block
|
||||
*/
|
||||
Blockly.JSON['abs'] = function(block) {
|
||||
const valuesArray = processValuesInput(block);
|
||||
const valuesArray = Blockly.JSON.processValuesInput(block);
|
||||
|
||||
const number = valuesArray[0] !== undefined ? valuesArray[0] : 0;
|
||||
|
||||
|
|
@ -155,7 +127,7 @@ export function defineAdvancedMathGenerators() {
|
|||
* Generator for 'max' Block
|
||||
*/
|
||||
Blockly.JSON['max'] = function(block) {
|
||||
const valuesArray = processValuesInput(block);
|
||||
const valuesArray = Blockly.JSON.processValuesInput(block);
|
||||
|
||||
// Create operation object
|
||||
const operationObject = Blockly.JSON.createOperationObject('max', {
|
||||
|
|
@ -173,7 +145,7 @@ export function defineAdvancedMathGenerators() {
|
|||
* Generator for 'min' Block
|
||||
*/
|
||||
Blockly.JSON['min'] = function(block) {
|
||||
const valuesArray = processValuesInput(block);
|
||||
const valuesArray = Blockly.JSON.processValuesInput(block);
|
||||
|
||||
// Create operation object
|
||||
const operationObject = Blockly.JSON.createOperationObject('min', {
|
||||
|
|
@ -191,7 +163,7 @@ export function defineAdvancedMathGenerators() {
|
|||
* Generator for 'factorial' Block
|
||||
*/
|
||||
Blockly.JSON['factorial'] = function(block) {
|
||||
const valuesArray = processValuesInput(block);
|
||||
const valuesArray = Blockly.JSON.processValuesInput(block);
|
||||
|
||||
const number = valuesArray[0] !== undefined ? valuesArray[0] : 1;
|
||||
|
||||
|
|
@ -217,7 +189,7 @@ export function defineAdvancedMathGenerators() {
|
|||
* Generator for 'log' Block
|
||||
*/
|
||||
Blockly.JSON['log'] = function(block) {
|
||||
const valuesArray = processValuesInput(block);
|
||||
const valuesArray = Blockly.JSON.processValuesInput(block);
|
||||
|
||||
const number = valuesArray[0] !== undefined ? valuesArray[0] : 1;
|
||||
|
||||
|
|
@ -243,7 +215,7 @@ export function defineAdvancedMathGenerators() {
|
|||
* Generator for 'ln' Block
|
||||
*/
|
||||
Blockly.JSON['ln'] = function(block) {
|
||||
const valuesArray = processValuesInput(block);
|
||||
const valuesArray = Blockly.JSON.processValuesInput(block);
|
||||
|
||||
const number = valuesArray[0] !== undefined ? valuesArray[0] : 1;
|
||||
|
||||
|
|
@ -270,7 +242,7 @@ export function defineAdvancedMathGenerators() {
|
|||
*/
|
||||
Blockly.JSON['trig'] = function(block) {
|
||||
const operator = block.getFieldValue('operator'); // 'sin', 'cos', 'tan'
|
||||
const valuesArray = processValuesInput(block);
|
||||
const valuesArray = Blockly.JSON.processValuesInput(block);
|
||||
|
||||
const angle = valuesArray[0] !== undefined ? valuesArray[0] : 0;
|
||||
|
||||
|
|
@ -304,7 +276,7 @@ export function defineAdvancedMathGenerators() {
|
|||
* Generator for 'mean' Block
|
||||
*/
|
||||
Blockly.JSON['mean'] = function(block) {
|
||||
const valuesArray = processValuesInput(block);
|
||||
const valuesArray = Blockly.JSON.processValuesInput(block);
|
||||
|
||||
// Create operation object
|
||||
const operationObject = Blockly.JSON.createOperationObject('mean', {
|
||||
|
|
@ -322,7 +294,7 @@ export function defineAdvancedMathGenerators() {
|
|||
* Generator for 'median' Block
|
||||
*/
|
||||
Blockly.JSON['median'] = function(block) {
|
||||
const valuesArray = processValuesInput(block);
|
||||
const valuesArray = Blockly.JSON.processValuesInput(block);
|
||||
|
||||
// Create operation object
|
||||
const operationObject = Blockly.JSON.createOperationObject('median', {
|
||||
|
|
@ -340,7 +312,7 @@ export function defineAdvancedMathGenerators() {
|
|||
* Generator for 'std_dev' Block
|
||||
*/
|
||||
Blockly.JSON['std_dev'] = function(block) {
|
||||
const valuesArray = processValuesInput(block);
|
||||
const valuesArray = Blockly.JSON.processValuesInput(block);
|
||||
|
||||
// Create operation object
|
||||
const operationObject = Blockly.JSON.createOperationObject('std_dev', {
|
||||
|
|
@ -358,7 +330,7 @@ export function defineAdvancedMathGenerators() {
|
|||
* Generator for 'round' Block
|
||||
*/
|
||||
Blockly.JSON['round'] = function(block) {
|
||||
const valuesArray = processValuesInput(block);
|
||||
const valuesArray = Blockly.JSON.processValuesInput(block);
|
||||
|
||||
const number = valuesArray[0] !== undefined ? valuesArray[0] : 0;
|
||||
const decimals = valuesArray[1] !== undefined ? valuesArray[1] : 0;
|
||||
|
|
@ -389,7 +361,7 @@ export function defineAdvancedMathGenerators() {
|
|||
* Generator for 'floor' Block
|
||||
*/
|
||||
Blockly.JSON['floor'] = function(block) {
|
||||
const valuesArray = processValuesInput(block);
|
||||
const valuesArray = Blockly.JSON.processValuesInput(block);
|
||||
|
||||
const number = valuesArray[0] !== undefined ? valuesArray[0] : 0;
|
||||
|
||||
|
|
@ -415,7 +387,7 @@ export function defineAdvancedMathGenerators() {
|
|||
* Generator for 'ceil' Block
|
||||
*/
|
||||
Blockly.JSON['ceil'] = function(block) {
|
||||
const valuesArray = processValuesInput(block);
|
||||
const valuesArray = Blockly.JSON.processValuesInput(block);
|
||||
|
||||
const number = valuesArray[0] !== undefined ? valuesArray[0] : 0;
|
||||
|
||||
|
|
@ -441,7 +413,7 @@ export function defineAdvancedMathGenerators() {
|
|||
* Generator for 'random' Block
|
||||
*/
|
||||
Blockly.JSON['random'] = function(block) {
|
||||
const valuesArray = processValuesInput(block);
|
||||
const valuesArray = Blockly.JSON.processValuesInput(block);
|
||||
|
||||
let min, max;
|
||||
if (valuesArray.length >= 2) {
|
||||
|
|
@ -482,7 +454,7 @@ export function defineAdvancedMathGenerators() {
|
|||
* Generator for 'clamp' Block
|
||||
*/
|
||||
Blockly.JSON['clamp'] = function(block) {
|
||||
const valuesArray = processValuesInput(block);
|
||||
const valuesArray = Blockly.JSON.processValuesInput(block);
|
||||
|
||||
const number = valuesArray[0] !== undefined ? valuesArray[0] : 0;
|
||||
const min = valuesArray[1] !== undefined ? valuesArray[1] : null;
|
||||
|
|
@ -516,36 +488,50 @@ export function defineAdvancedMathGenerators() {
|
|||
* Generator for 'math_operation' Block
|
||||
*/
|
||||
Blockly.JSON['math_operation'] = function(block) {
|
||||
/**
|
||||
* Helper Function: _extractValues
|
||||
* Extracts values from a given input block, handling 'dynamic_value' objects, arrays, or single values.
|
||||
*
|
||||
* @param {Blockly.Block} inputBlock - The block to extract values from.
|
||||
* @returns {Array} - An array of values extracted from the input block.
|
||||
*/
|
||||
Blockly.JSON._extractValues = function(inputBlock) {
|
||||
if (!inputBlock) return [];
|
||||
const inputJson = Blockly.JSON.blockToCode(inputBlock);
|
||||
if (inputJson?.type === 'dynamic_value' && Array.isArray(inputJson.values)) {
|
||||
return inputJson.values; // Use values directly if it's a 'dynamic_value' array
|
||||
}
|
||||
return Array.isArray(inputJson) ? inputJson : [inputJson]; // Wrap in array if not already an array
|
||||
};
|
||||
|
||||
const operator = block.getFieldValue('operator'); // 'ADD', 'SUBTRACT', 'MULTIPLY', 'DIVIDE'
|
||||
const valuesArray = processValuesInput(block);
|
||||
const leftValues = Blockly.JSON._extractValues(block.getInputTargetBlock('LEFT'));
|
||||
const rightValues = Blockly.JSON._extractValues(block.getInputTargetBlock('RIGHT'));
|
||||
|
||||
const leftOperand = valuesArray[0] !== undefined ? valuesArray[0] : 0;
|
||||
const rightOperand = valuesArray[1] !== undefined ? valuesArray[1] : 0;
|
||||
// Use the first values or default to 0 if not available
|
||||
const leftOperand = leftValues[0] ?? 0;
|
||||
const rightOperand = rightValues[0] ?? 0;
|
||||
|
||||
// Validate operator
|
||||
// Validate and format the operator
|
||||
const validOperators = ['ADD', 'SUBTRACT', 'MULTIPLY', 'DIVIDE'];
|
||||
const validatedOperator = validOperators.includes(operator) ? operator.toLowerCase() : 'add';
|
||||
if (!validOperators.includes(operator)) {
|
||||
console.error(`Invalid operator '${operator}' in 'math_operation' block. Defaulting to 'ADD'.`);
|
||||
}
|
||||
const validatedOperator = validOperators.includes(operator) ? operator : 'ADD';
|
||||
|
||||
// Create operation object
|
||||
// Create the primary operation object
|
||||
const operationObject = Blockly.JSON.createOperationObject('math_operation', {
|
||||
operator: validatedOperator.toLowerCase(), // Assuming server expects lowercase operators
|
||||
operator: validatedOperator,
|
||||
left_operand: leftOperand,
|
||||
right_operand: rightOperand
|
||||
});
|
||||
|
||||
// Include unused values if any
|
||||
const outputValues = [operationObject];
|
||||
if (valuesArray.length > 2) {
|
||||
outputValues.push(...valuesArray.slice(2));
|
||||
}
|
||||
|
||||
// Output as dynamic_value
|
||||
return {
|
||||
type: 'dynamic_value',
|
||||
values: outputValues
|
||||
values: [operationObject]
|
||||
};
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -120,26 +120,15 @@ export function defineBalancesGenerators() {
|
|||
* This is a statement block, so it generates an action object and handles 'NEXT' connections.
|
||||
*/
|
||||
Blockly.JSON['set_available_strategy_balance'] = function(block) {
|
||||
// Retrieve the 'BALANCE' input block
|
||||
const balanceBlock = block.getInputTargetBlock('BALANCE');
|
||||
let balance = 0;
|
||||
|
||||
if (balanceBlock) {
|
||||
const balanceJson = Blockly.JSON.blockToCode(balanceBlock);
|
||||
if (typeof balanceJson === 'number') {
|
||||
balance = balanceJson;
|
||||
} else {
|
||||
console.error("Invalid balance value in 'set_available_strategy_balance' block. Expected a number.");
|
||||
}
|
||||
} else {
|
||||
console.warn("No balance provided in 'set_available_strategy_balance' block. Using default value 0.");
|
||||
}
|
||||
const values = Blockly.JSON.processNumericInput(block,'BALANCE',0);
|
||||
|
||||
const balance = values !== undefined ? values : 0;
|
||||
|
||||
const json = {
|
||||
type: 'set_available_strategy_balance',
|
||||
inputs: {
|
||||
balance: balance
|
||||
}
|
||||
inputs: { BALANCE: balance },
|
||||
// skipAdditionalParsing: true
|
||||
};
|
||||
|
||||
// No need to handle 'NEXT' input; sequencing is managed by _blockToJson
|
||||
|
|
|
|||
|
|
@ -4,12 +4,6 @@ export function defineJsonBaseGenerator() {
|
|||
if (!Blockly.JSON) {
|
||||
Blockly.JSON = new Blockly.Generator('JSON');
|
||||
|
||||
/**
|
||||
* List of trade_option block types to exclude 'next' processing.
|
||||
*/
|
||||
const TRADE_OPTION_TYPES = ['time_in_force', 'limit', "trailing_limit", 'target_market',
|
||||
"stop_loss", "trailing_stop", "take_profit", "name_order"];
|
||||
|
||||
// Define operator precedence constants
|
||||
Blockly.JSON.ORDER_ATOMIC = 0; // 0 "" ...
|
||||
Blockly.JSON.ORDER_NONE = 99; // (no precedence)
|
||||
|
|
@ -89,134 +83,124 @@ export function defineJsonBaseGenerator() {
|
|||
};
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Helper Function: _blockToJson
|
||||
* Recursively converts a block and its connected blocks into JSON.
|
||||
* Converts a Blockly block into a JSON representation.
|
||||
* Handles both custom generators and default JSON object creation.
|
||||
*
|
||||
* @param {Blockly.Block} block - The block to convert.
|
||||
* @param {number} currentDepth - The current recursion depth.
|
||||
* @returns {Object} - The JSON representation of the block.
|
||||
* @param {Block} block - The block to convert.
|
||||
* @param {number} currentDepth - The current depth of the block in the workspace (for handling nesting).
|
||||
* @returns {object|null} - The JSON representation of the block, or null if conversion fails.
|
||||
*/
|
||||
Blockly.JSON._blockToJson = function(block, currentDepth = 0) {
|
||||
// If the block is null, return null
|
||||
if (!block) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Calculate nesting depth and enforce limits
|
||||
// Handle situations where recursion exceeds the maximum allowed depth.
|
||||
try {
|
||||
Blockly.JSON._calculateNestingDepth(block, currentDepth);
|
||||
} catch (error) {
|
||||
console.error(error.message);
|
||||
alert(error.message);
|
||||
throw error; // Stop processing if limit exceeded
|
||||
throw error;
|
||||
}
|
||||
|
||||
// Use the block's JSON generator if it exists
|
||||
let json;
|
||||
|
||||
if (Blockly.JSON && Blockly.JSON[block.type]) {
|
||||
// Generate JSON object using the block's JSON generator
|
||||
const json = Blockly.JSON[block.type](block);
|
||||
// Custom generator exists
|
||||
json = Blockly.JSON[block.type](block);
|
||||
|
||||
if (!json || typeof json !== 'object') {
|
||||
console.error(`Generator for block type '${block.type}' did not return a valid JSON object.`);
|
||||
return null;
|
||||
}
|
||||
|
||||
// **Check for the skipAdditionalParsing flag**
|
||||
// Skip additional parsing if specified
|
||||
if (json.skipAdditionalParsing) {
|
||||
// Remove the flag to prevent it from being included in the final JSON
|
||||
delete json.skipAdditionalParsing;
|
||||
|
||||
// **Decide whether to include the block based on the presence of meaningful content**
|
||||
if (!json.type) {
|
||||
// If 'type' is undefined or null, exclude the block
|
||||
return null;
|
||||
}
|
||||
|
||||
// Return the JSON as it has been fully handled by the generator
|
||||
return json;
|
||||
}
|
||||
|
||||
// **Proceed with existing parsing logic only if skipAdditionalParsing is not set**
|
||||
block.inputList.forEach(input => {
|
||||
if (input.type === Blockly.INPUT_VALUE && input.connection && input.connection.targetBlock()) {
|
||||
|
||||
const targetBlock = input.connection.targetBlock();
|
||||
const targetJson = Blockly.JSON._blockToJson(targetBlock, currentDepth + 1); // Increment currentDepth
|
||||
|
||||
if (!json.inputs) json.inputs = {};
|
||||
json.inputs[input.name] = targetJson;
|
||||
}
|
||||
});
|
||||
|
||||
// **Process Statement Inputs**
|
||||
block.inputList.forEach(input => {
|
||||
if (input.type === Blockly.NEXT_STATEMENT && input.connection && input.connection.targetBlock()) {
|
||||
|
||||
const targetBlock = input.connection.targetBlock();
|
||||
const targetJson = Blockly.JSON._blockToJson(targetBlock, currentDepth + 1); // Increment currentDepth
|
||||
|
||||
if (!json.statements) json.statements = {};
|
||||
if (!json.statements[input.name]) json.statements[input.name] = [];
|
||||
json.statements[input.name].push(targetJson);
|
||||
}
|
||||
});
|
||||
|
||||
// **Handle 'next' connections**
|
||||
if (block.getNextBlock()) {
|
||||
const nextBlock = Blockly.JSON._blockToJson(block.getNextBlock(), currentDepth); // Same depth
|
||||
json.next = nextBlock;
|
||||
}
|
||||
|
||||
return json;
|
||||
} else {
|
||||
// Fallback: Manually construct JSON if no JSON generator exists
|
||||
const json = {
|
||||
type: block.type,
|
||||
fields: {},
|
||||
inputs: {},
|
||||
statements: {}
|
||||
// No custom generator, create a default JSON object
|
||||
json = {
|
||||
type: block.type
|
||||
};
|
||||
|
||||
// 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 fields and process inputs
|
||||
Blockly.JSON._captureFields(block, json);
|
||||
Blockly.JSON._processInputs(block, json, currentDepth);
|
||||
|
||||
// Process inputs
|
||||
block.inputList.forEach(input => {
|
||||
if (input.type === Blockly.INPUT_VALUE && input.connection && input.connection.targetBlock()) {
|
||||
const targetBlock = input.connection.targetBlock();
|
||||
const targetJson = Blockly.JSON._blockToJson(targetBlock, currentDepth + 1);
|
||||
|
||||
json.inputs[input.name] = targetJson;
|
||||
} else if (input.type === Blockly.NEXT_STATEMENT && input.connection && input.connection.targetBlock()) {
|
||||
|
||||
const targetBlock = input.connection.targetBlock();
|
||||
const targetJson = Blockly.JSON._blockToJson(targetBlock, currentDepth + 1);
|
||||
|
||||
if (!json.statements) json.statements = {};
|
||||
if (!json.statements[input.name]) json.statements[input.name] = [];
|
||||
json.statements[input.name].push(targetJson);
|
||||
}
|
||||
});
|
||||
|
||||
// **Handle 'next' connections**
|
||||
if (block.getNextBlock()) {
|
||||
const nextBlock = Blockly.JSON._blockToJson(block.getNextBlock(), currentDepth);
|
||||
json.next = nextBlock;
|
||||
}
|
||||
|
||||
return json;
|
||||
}
|
||||
// Handle 'next' connections recursively
|
||||
if (block.getNextBlock()) {
|
||||
json.next = Blockly.JSON._blockToJson(block.getNextBlock(), currentDepth);
|
||||
}
|
||||
|
||||
return json;
|
||||
};
|
||||
|
||||
/**
|
||||
* Processes the inputs of a block and adds them to the JSON representation.
|
||||
* Handles both value and statement inputs.
|
||||
*
|
||||
* @param {Blockly.Block} block - The block whose inputs are to be processed.
|
||||
* @param {object} json - The JSON object to populate with input data.
|
||||
* @param {number} currentDepth - The current depth of the block in the workspace.
|
||||
*/
|
||||
Blockly.JSON._processInputs = function(block, json, currentDepth) {
|
||||
// Iterate over each input in the block's input list
|
||||
block.inputList.forEach(input => {
|
||||
// If the input does not have a connection to another block, skip to the next input
|
||||
if (!input.connection || !input.connection.targetBlock()) return;
|
||||
|
||||
// Get and convert the connected block to JSON, increasing the depth
|
||||
const targetBlock = input.connection.targetBlock();
|
||||
const targetJson = Blockly.JSON._blockToJson(targetBlock, currentDepth + 1);
|
||||
|
||||
// If the input is a value input, add it to the 'inputs' section of the json
|
||||
if (input.type === Blockly.INPUT_VALUE) {
|
||||
if (!json.inputs) json.inputs = {};
|
||||
json.inputs[input.name] = targetJson;
|
||||
}
|
||||
// If the input is a statement input, add it to the 'statements' section of the json
|
||||
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);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Captures the fields of a block and adds them to the JSON representation.
|
||||
*
|
||||
* @param {Blockly.Block} block - The block whose fields are to be captured.
|
||||
* @param {object} json - The JSON object to populate with field data.
|
||||
*/
|
||||
Blockly.JSON._captureFields = function(block, json) {
|
||||
// Iterate over each input in the block's input list
|
||||
block.inputList.forEach(input => {
|
||||
// If the input doesn't contain a fieldRow, skip to the next input.
|
||||
if (!input.fieldRow) return;
|
||||
|
||||
// Iterate over each field in the fieldRow
|
||||
input.fieldRow.forEach(field => {
|
||||
// If the field has no name or method to get its value, skip to the next field.
|
||||
if (!field.name || !field.getValue) return;
|
||||
|
||||
// Initialize the fields object in JSON if it doesn't exist
|
||||
if (!json.fields) json.fields = {};
|
||||
// Add the field name and its value to the JSON fields object
|
||||
json.fields[field.name] = field.getValue();
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Helper Function: _calculateNestingDepth
|
||||
* Calculates the nesting depth of a block and throws an error if it exceeds the maximum allowed depth.
|
||||
|
|
@ -225,43 +209,56 @@ export function defineJsonBaseGenerator() {
|
|||
* @param {number} currentDepth - The current recursion depth.
|
||||
*/
|
||||
Blockly.JSON._calculateNestingDepth = function(block, currentDepth) {
|
||||
const MAX_DEPTH = 10;
|
||||
if (currentDepth > MAX_DEPTH) {
|
||||
throw new Error(`Maximum block nesting depth of ${MAX_DEPTH} exceeded.`);
|
||||
if (currentDepth > Blockly.JSON.maxDepth) {
|
||||
throw new Error(`Maximum block nesting depth of ${Blockly.JSON.maxDepth} exceeded.`);
|
||||
}
|
||||
// Add any additional depth calculations or checks if necessary
|
||||
};
|
||||
|
||||
/**
|
||||
* Helper Function: processNumericInput
|
||||
* Processes a numeric input.
|
||||
* Helper Function: extractValues
|
||||
* Extracts values from a block's input, handling dynamic_value arrays or single values.
|
||||
*
|
||||
* @param {Blockly.Block} block - The block containing the input.
|
||||
* @param {string} inputName - The name of the input field.
|
||||
* @param {number} defaultValue - The default value to use if input is invalid.
|
||||
* @returns {number} - The validated input.
|
||||
* @param {number | null} defaultValue - Default value for single extractions (null for array extraction).
|
||||
* @returns {Array | number} - An array of values if defaultValue is null; otherwise, the first value or the default.
|
||||
*/
|
||||
Blockly.JSON.processNumericInput = function (block, inputName, defaultValue) {
|
||||
Blockly.JSON.extractValues = function(block, inputName, defaultValue = null) {
|
||||
const inputBlock = block.getInputTargetBlock(inputName);
|
||||
|
||||
// Return default value if input block is missing
|
||||
if (!inputBlock) {
|
||||
console.warn(`No block connected to '${inputName}' input of '${block.type}' block. Using default value ${defaultValue}.`);
|
||||
return defaultValue;
|
||||
if (defaultValue !== null) {
|
||||
console.warn(`No block connected to '${inputName}' input of '${block.type}' block. Using default value ${defaultValue}.`);
|
||||
return defaultValue;
|
||||
}
|
||||
return []; // Return empty array for multi-value cases
|
||||
}
|
||||
|
||||
const inputJson = Blockly.JSON.blockToCode(inputBlock);
|
||||
|
||||
// Check if inputJson is correctly structured
|
||||
if (!inputJson || inputJson.type !== 'dynamic_value' || !Array.isArray(inputJson.values) || inputJson.values.length === 0) {
|
||||
if (inputJson?.type === 'dynamic_value' && Array.isArray(inputJson.values)) {
|
||||
return defaultValue !== null ? inputJson.values[0] : inputJson.values;
|
||||
}
|
||||
|
||||
if (defaultValue !== null) {
|
||||
console.error(`Invalid JSON structure for '${inputName}' in '${block.type}' block. Expected a 'dynamic_value' with at least one value.`);
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
const extractedValue = inputJson.values[0];
|
||||
return extractedValue;
|
||||
return Array.isArray(inputJson) ? inputJson : [inputJson];
|
||||
};
|
||||
|
||||
// Usage in processValuesInput (array case)
|
||||
Blockly.JSON.processValuesInput = function(block) {
|
||||
return Blockly.JSON.extractValues(block, 'VALUES');
|
||||
};
|
||||
|
||||
// Usage in processNumericInput (single numeric case)
|
||||
Blockly.JSON.processNumericInput = function(block, inputName, defaultValue) {
|
||||
return Blockly.JSON.extractValues(block, inputName, defaultValue);
|
||||
};
|
||||
|
||||
|
||||
console.log('Blockly.JSON generator initialized with helper functions: createOperationObject, _blockToJson, collectTradeOptions.');
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -48,75 +48,77 @@ export function defineLogicalGenerators() {
|
|||
return { left, right };
|
||||
}
|
||||
|
||||
/**
|
||||
* Generator for 'comparison' Block
|
||||
*
|
||||
* Generates a JSON object representing a comparison operation between two values
|
||||
* using operators like '>', '<', '=='. Outputs a Boolean value based on the comparison result.
|
||||
*/
|
||||
Blockly.JSON['comparison'] = function(block) {
|
||||
/**
|
||||
* Helper Function: processTwoValueInput
|
||||
* Processes 'LEFT' and 'RIGHT' inputs and returns their JSON representations.
|
||||
* Generator for 'comparison' Block
|
||||
*
|
||||
* @param {Blockly.Block} block - The block containing the 'LEFT' and 'RIGHT' inputs.
|
||||
* @returns {Object} - An object with 'LEFT' and 'RIGHT' properties.
|
||||
* Generates a JSON object representing a comparison operation between two values
|
||||
* using operators like '>', '<', '=='. Outputs a Boolean value based on the comparison result.
|
||||
*/
|
||||
function processTwoValueInput(block) {
|
||||
const leftBlock = block.getInputTargetBlock('LEFT');
|
||||
const rightBlock = block.getInputTargetBlock('RIGHT');
|
||||
Blockly.JSON['comparison'] = function(block) {
|
||||
/**
|
||||
* Helper Function: processTwoValueInput
|
||||
* Processes 'LEFT' and 'RIGHT' inputs and returns their JSON representations.
|
||||
*
|
||||
* @param {Blockly.Block} block - The block containing the 'LEFT' and 'RIGHT' inputs.
|
||||
* @returns {Object} - An object with 'LEFT' and 'RIGHT' properties.
|
||||
*/
|
||||
function processTwoValueInput(block) {
|
||||
const leftBlock = block.getInputTargetBlock('LEFT');
|
||||
const rightBlock = block.getInputTargetBlock('RIGHT');
|
||||
|
||||
let left = null;
|
||||
let right = null;
|
||||
let left = null;
|
||||
let right = null;
|
||||
|
||||
if (leftBlock) {
|
||||
left = Blockly.JSON.blockToCode(leftBlock);
|
||||
if (typeof left !== 'object' || left === null) {
|
||||
console.error(`Invalid 'LEFT' input in 'comparison' block. Expected an object.`);
|
||||
left = 0;
|
||||
if (leftBlock) {
|
||||
left = Blockly.JSON.blockToCode(leftBlock);
|
||||
if (typeof left !== 'object' || left === null) {
|
||||
console.error(`Invalid 'LEFT' input in 'comparison' block. Expected an object.`);
|
||||
left = 0;
|
||||
}
|
||||
} else {
|
||||
console.warn(`No block connected to 'LEFT' input of 'comparison' block. Using default value 0.`);
|
||||
left = 0; // Assign a default value if necessary
|
||||
}
|
||||
} else {
|
||||
console.warn(`No block connected to 'LEFT' input of 'comparison' block. Using default value 0.`);
|
||||
left = 0; // Assign a default value if necessary
|
||||
}
|
||||
|
||||
if (rightBlock) {
|
||||
right = Blockly.JSON.blockToCode(rightBlock);
|
||||
if (typeof right !== 'object' || right === null) {
|
||||
console.error(`Invalid 'RIGHT' input in 'comparison' block. Expected an object.`);
|
||||
right = 0;
|
||||
if (rightBlock) {
|
||||
right = Blockly.JSON.blockToCode(rightBlock);
|
||||
if (typeof right !== 'object' || right === null) {
|
||||
console.error(`Invalid 'RIGHT' input in 'comparison' block. Expected an object.`);
|
||||
right = 0;
|
||||
}
|
||||
} else {
|
||||
console.warn(`No block connected to 'RIGHT' input of 'comparison' block. Using default value 0.`);
|
||||
right = 0; // Assign a default value if necessary
|
||||
}
|
||||
} else {
|
||||
console.warn(`No block connected to 'RIGHT' input of 'comparison' block. Using default value 0.`);
|
||||
right = 0; // Assign a default value if necessary
|
||||
|
||||
return { LEFT: left, RIGHT: right };
|
||||
}
|
||||
|
||||
return { LEFT: left, RIGHT: right };
|
||||
}
|
||||
const { LEFT, RIGHT } = processTwoValueInput(block);
|
||||
|
||||
const { LEFT, RIGHT } = processTwoValueInput(block);
|
||||
|
||||
// Retrieve the selected operator
|
||||
const operator = block.getFieldValue('OPERATOR'); // Must match the block definition
|
||||
const validOperators = ['>', '<', '==', '!=', '>=', '<='];
|
||||
if (!validOperators.includes(operator)) {
|
||||
console.error(`Invalid operator '${operator}' in 'comparison' block. Defaulting to '=='`);
|
||||
}
|
||||
const validatedOperator = validOperators.includes(operator) ? operator : '==';
|
||||
|
||||
// Create operation object with 'inputs'
|
||||
const operationObject = {
|
||||
type: 'comparison',
|
||||
operator: validatedOperator,
|
||||
inputs: {
|
||||
LEFT: LEFT !== null ? LEFT : { type: 'value_input', values: [0] },
|
||||
RIGHT: RIGHT !== null ? RIGHT : { type: 'value_input', values: [0] }
|
||||
// Retrieve the selected operator
|
||||
const operator = block.getFieldValue('OPERATOR'); // Must match the block definition
|
||||
const validOperators = ['>', '<', '==', '!=', '>=', '<='];
|
||||
if (!validOperators.includes(operator)) {
|
||||
console.error(`Invalid operator '${operator}' in 'comparison' block. Defaulting to '=='`);
|
||||
}
|
||||
const validatedOperator = validOperators.includes(operator) ? operator : '==';
|
||||
|
||||
// Create operation object with 'inputs'
|
||||
const operationObject = {
|
||||
type: 'comparison',
|
||||
operator: validatedOperator,
|
||||
inputs: {
|
||||
LEFT: LEFT !== null ? LEFT : { type: 'dynamic_value', values: [0] },
|
||||
RIGHT: RIGHT !== null ? RIGHT : { type: 'dynamic_value', values: [0] }
|
||||
},
|
||||
skipAdditionalParsing: true
|
||||
|
||||
};
|
||||
|
||||
return operationObject;
|
||||
};
|
||||
|
||||
return operationObject;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Generator for 'logical_and' Block
|
||||
|
|
@ -124,101 +126,142 @@ Blockly.JSON['comparison'] = function(block) {
|
|||
* Generates a JSON object representing a logical AND operation between two conditions.
|
||||
*/
|
||||
Blockly.JSON['logical_and'] = function(block) {
|
||||
const { left, right } = processTwoValueInput(block);
|
||||
// Get the connected blocks
|
||||
const leftBlock = block.getInputTargetBlock('LEFT');
|
||||
const rightBlock = block.getInputTargetBlock('RIGHT');
|
||||
|
||||
// Default to false if any condition is null
|
||||
const leftCondition = typeof left === 'boolean' ? left : false;
|
||||
const rightCondition = typeof right === 'boolean' ? right : false;
|
||||
let leftValue = null;
|
||||
let rightValue = null;
|
||||
|
||||
// Create operation object
|
||||
const operationObject = Blockly.JSON.createOperationObject('logical_and', {
|
||||
left: leftCondition,
|
||||
right: rightCondition
|
||||
});
|
||||
// Process the left input
|
||||
if (leftBlock) {
|
||||
const leftGenerator = Blockly.JSON[leftBlock.type];
|
||||
if (typeof leftGenerator === 'function') {
|
||||
leftValue = leftGenerator(leftBlock);
|
||||
} else {
|
||||
console.warn(`No generator found for block type "${leftBlock.type}". Defaulting left input to false.`);
|
||||
leftValue = false;
|
||||
}
|
||||
} else {
|
||||
console.warn("No block connected to 'LEFT' input of 'logical_and' block. Defaulting to false.");
|
||||
leftValue = false;
|
||||
}
|
||||
|
||||
// Output as dynamic_value
|
||||
return operationObject;
|
||||
// Process the right input
|
||||
if (rightBlock) {
|
||||
const rightGenerator = Blockly.JSON[rightBlock.type];
|
||||
if (typeof rightGenerator === 'function') {
|
||||
rightValue = rightGenerator(rightBlock);
|
||||
} else {
|
||||
console.warn(`No generator found for block type "${rightBlock.type}". Defaulting right input to false.`);
|
||||
rightValue = false;
|
||||
}
|
||||
} else {
|
||||
console.warn("No block connected to 'RIGHT' input of 'logical_and' block. Defaulting to false.");
|
||||
rightValue = false;
|
||||
}
|
||||
|
||||
// Create the logical_and operation object
|
||||
const json = {
|
||||
type: 'logical_and',
|
||||
inputs: {
|
||||
left: leftValue,
|
||||
right: rightValue
|
||||
}
|
||||
};
|
||||
|
||||
return json;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Generator for 'logical_or' Block
|
||||
*
|
||||
* Generates a JSON object representing a logical OR operation between two conditions.
|
||||
*/
|
||||
Blockly.JSON['logical_or'] = function(block) {
|
||||
const { left, right } = processTwoValueInput(block);
|
||||
// Get the connected blocks
|
||||
const leftBlock = block.getInputTargetBlock('LEFT');
|
||||
const rightBlock = block.getInputTargetBlock('RIGHT');
|
||||
|
||||
// Default to false if any condition is null
|
||||
const leftCondition = typeof left === 'boolean' ? left : false;
|
||||
const rightCondition = typeof right === 'boolean' ? right : false;
|
||||
let leftValue = null;
|
||||
let rightValue = null;
|
||||
|
||||
// Create operation object
|
||||
const operationObject = Blockly.JSON.createOperationObject('logical_or', {
|
||||
left: leftCondition,
|
||||
right: rightCondition
|
||||
});
|
||||
// Process the left input
|
||||
if (leftBlock) {
|
||||
const leftGenerator = Blockly.JSON[leftBlock.type];
|
||||
if (typeof leftGenerator === 'function') {
|
||||
leftValue = leftGenerator(leftBlock);
|
||||
} else {
|
||||
console.warn(`No generator found for block type "${leftBlock.type}". Defaulting left input to false.`);
|
||||
leftValue = false;
|
||||
}
|
||||
} else {
|
||||
console.warn("No block connected to 'LEFT' input of 'logical_or' block. Defaulting to false.");
|
||||
leftValue = false;
|
||||
}
|
||||
|
||||
// Output as dynamic_value
|
||||
return operationObject;
|
||||
// Process the right input
|
||||
if (rightBlock) {
|
||||
const rightGenerator = Blockly.JSON[rightBlock.type];
|
||||
if (typeof rightGenerator === 'function') {
|
||||
rightValue = rightGenerator(rightBlock);
|
||||
} else {
|
||||
console.warn(`No generator found for block type "${rightBlock.type}". Defaulting right input to false.`);
|
||||
rightValue = false;
|
||||
}
|
||||
} else {
|
||||
console.warn("No block connected to 'RIGHT' input of 'logical_or' block. Defaulting to false.");
|
||||
rightValue = false;
|
||||
}
|
||||
|
||||
// Create the logical_or operation object
|
||||
const json = {
|
||||
type: 'logical_or',
|
||||
inputs: {
|
||||
left: leftValue,
|
||||
right: rightValue
|
||||
}
|
||||
};
|
||||
|
||||
return json;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Generator for 'is_false' Block
|
||||
*
|
||||
* Generates a JSON object to check if a given condition is false.
|
||||
*/
|
||||
Blockly.JSON['is_false'] = function(block) {
|
||||
// Retrieve and parse the 'CONDITION' input
|
||||
const conditionBlock = block.getInputTargetBlock('CONDITION');
|
||||
let condition = false;
|
||||
// Get the connected block
|
||||
const conditionBlock = block.getInputTargetBlock('condition');
|
||||
|
||||
let conditionValue = null;
|
||||
|
||||
// Process the condition input
|
||||
if (conditionBlock) {
|
||||
const conditionJson = Blockly.JSON.blockToCode(conditionBlock);
|
||||
if (typeof conditionJson === 'boolean') {
|
||||
condition = conditionJson;
|
||||
const conditionGenerator = Blockly.JSON[conditionBlock.type];
|
||||
if (typeof conditionGenerator === 'function') {
|
||||
conditionValue = conditionGenerator(conditionBlock);
|
||||
} else {
|
||||
console.error("Invalid condition value in 'is_false' block. Expected a Boolean.");
|
||||
console.warn(`No generator found for block type "${conditionBlock.type}". Defaulting condition to false.`);
|
||||
conditionValue = false;
|
||||
}
|
||||
} else {
|
||||
console.warn("No condition provided in 'is_false' block. Using default value 'false'.");
|
||||
console.warn("No block connected to 'condition' input of 'is_false' block. Defaulting to false.");
|
||||
conditionValue = false;
|
||||
}
|
||||
|
||||
// Create operation object
|
||||
const operationObject = Blockly.JSON.createOperationObject('is_false', {
|
||||
condition: condition
|
||||
});
|
||||
|
||||
// Output as dynamic_value
|
||||
return operationObject;
|
||||
};
|
||||
|
||||
/**
|
||||
* Generator for 'is_true' Block
|
||||
*
|
||||
* Generates a JSON object to check if a given condition is true.
|
||||
*/
|
||||
Blockly.JSON['is_true'] = function(block) {
|
||||
// Retrieve and parse the 'CONDITION' input
|
||||
const conditionBlock = block.getInputTargetBlock('CONDITION');
|
||||
let condition = false;
|
||||
|
||||
if (conditionBlock) {
|
||||
const conditionJson = Blockly.JSON.blockToCode(conditionBlock);
|
||||
if (typeof conditionJson === 'boolean') {
|
||||
condition = conditionJson;
|
||||
} else {
|
||||
console.error("Invalid condition value in 'is_true' block. Expected a Boolean.");
|
||||
// Create the is_false operation object
|
||||
const json = {
|
||||
type: 'is_false',
|
||||
inputs: {
|
||||
condition: conditionValue
|
||||
}
|
||||
} else {
|
||||
console.warn("No condition provided in 'is_true' block. Using default value 'false'.");
|
||||
}
|
||||
};
|
||||
|
||||
// Create operation object
|
||||
const operationObject = Blockly.JSON.createOperationObject('is_true', {
|
||||
condition: condition
|
||||
});
|
||||
|
||||
// Output as dynamic_value
|
||||
return operationObject;
|
||||
return json;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,72 +16,70 @@ export function defineMarketDataGenerators() {
|
|||
|
||||
/**
|
||||
* Helper Function: processSourceInput
|
||||
* Processes the 'SOURCE' input and returns its value.
|
||||
* Processes the 'source' input, ensuring it is a valid source block and returning its value if connected.
|
||||
*
|
||||
* @param {Blockly.Block} block - The block containing the 'SOURCE' input.
|
||||
* @returns {Object} - The source object or a default empty object.
|
||||
* @param {Blockly.Block} block - The block containing the 'source' input.
|
||||
* @returns {Object} - The source object or a default empty object if validation fails.
|
||||
*/
|
||||
function processSourceInput(block) {
|
||||
const sourceBlock = block.getInputTargetBlock('SOURCE');
|
||||
let source = {};
|
||||
|
||||
if (sourceBlock) {
|
||||
const sourceJson = Blockly.JSON.blockToCode(sourceBlock);
|
||||
if (typeof sourceJson === 'object' && sourceJson !== null) {
|
||||
source = sourceJson;
|
||||
} else {
|
||||
console.error(`Invalid source object in '${block.type}' block. Expected an object.`);
|
||||
}
|
||||
} else {
|
||||
console.warn(`No source connected to 'SOURCE' input of '${block.type}' block. Using default empty object.`);
|
||||
const sourceBlock = block.getInputTargetBlock('source');
|
||||
if (sourceBlock && sourceBlock.type === 'source') {
|
||||
return Blockly.JSON.blockToCode(sourceBlock); // Process source configuration
|
||||
}
|
||||
|
||||
return source;
|
||||
console.warn(`Invalid or missing source block connected to 'source' input of '${block.type}' block. Using default configuration.`);
|
||||
return {}; // Default empty object if validation fails
|
||||
}
|
||||
|
||||
/**
|
||||
* General function to generate market data blocks, handling additional values.
|
||||
* Uses extractValues to pull optional extra values connected to 'source'.
|
||||
*/
|
||||
function generateMarketDataBlock(block, operationType) {
|
||||
// Process the 'source' input (optional) and additional values
|
||||
const source = processSourceInput(block);
|
||||
const operationObject = Blockly.JSON.createOperationObject(operationType, { source: source.values ? source.values[0] : source });
|
||||
|
||||
// Initialize output values array
|
||||
const outputValues = [operationObject];
|
||||
|
||||
// Add the remaining values from source if it contains a dynamic_value structure
|
||||
if (source?.type === 'dynamic_value' && Array.isArray(source.values)) {
|
||||
outputValues.push(...source.values.slice(1));
|
||||
}
|
||||
|
||||
// Use extractValues to capture additional values beyond the primary source
|
||||
const additionalValues = Blockly.JSON.extractValues(block, 'source'); // Extract additional values if any
|
||||
if (additionalValues.length > 0) {
|
||||
outputValues.push(...additionalValues);
|
||||
}
|
||||
|
||||
return {
|
||||
type: 'dynamic_value',
|
||||
values: outputValues
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Generator for 'current_price' Block
|
||||
*/
|
||||
Blockly.JSON['current_price'] = function(block) {
|
||||
const source = processSourceInput(block);
|
||||
|
||||
// Create operation object
|
||||
const operationObject = Blockly.JSON.createOperationObject('current_price', {
|
||||
source: source
|
||||
});
|
||||
|
||||
// Output as dynamic_value
|
||||
return operationObject;
|
||||
return generateMarketDataBlock(block, 'current_price');
|
||||
};
|
||||
|
||||
/**
|
||||
* Generator for 'bid_price' Block
|
||||
*/
|
||||
Blockly.JSON['bid_price'] = function(block) {
|
||||
const source = processSourceInput(block);
|
||||
|
||||
// Create operation object
|
||||
const operationObject = Blockly.JSON.createOperationObject('bid_price', {
|
||||
source: source
|
||||
});
|
||||
|
||||
// Output as dynamic_value
|
||||
return operationObject;
|
||||
return generateMarketDataBlock(block, 'bid_price');
|
||||
};
|
||||
|
||||
/**
|
||||
* Generator for 'ask_price' Block
|
||||
*/
|
||||
Blockly.JSON['ask_price'] = function(block) {
|
||||
const source = processSourceInput(block);
|
||||
|
||||
// Create operation object
|
||||
const operationObject = Blockly.JSON.createOperationObject('ask_price', {
|
||||
source: source
|
||||
});
|
||||
|
||||
// Output as dynamic_value
|
||||
return operationObject;
|
||||
return generateMarketDataBlock(block, 'ask_price');
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -89,49 +87,51 @@ export function defineMarketDataGenerators() {
|
|||
*/
|
||||
Blockly.JSON['last_candle_value'] = function(block) {
|
||||
const source = processSourceInput(block);
|
||||
|
||||
// Retrieve the 'CANDLE_PART' field
|
||||
const candlePart = block.getFieldValue('CANDLE_PART') || 'close';
|
||||
const validCandleParts = ['open', 'high', 'low', 'close', 'volume'];
|
||||
if (!validCandleParts.includes(candlePart)) {
|
||||
console.error(`Invalid candle part '${candlePart}' in 'last_candle_value' block. Defaulting to 'close'.`);
|
||||
}
|
||||
const candlePart = block.getFieldValue('candle_part') || 'close';
|
||||
const validCandleParts = ['open', 'high', 'low', 'close'];
|
||||
const validatedCandlePart = validCandleParts.includes(candlePart) ? candlePart : 'close';
|
||||
|
||||
// Create operation object
|
||||
const operationObject = Blockly.JSON.createOperationObject('last_candle_value', {
|
||||
source: source,
|
||||
source,
|
||||
candle_part: validatedCandlePart
|
||||
});
|
||||
|
||||
// Output as dynamic_value
|
||||
return operationObject;
|
||||
const outputValues = [operationObject];
|
||||
const additionalValues = Blockly.JSON.extractValues(block, 'source'); // Use extractValues for any additional values
|
||||
if (additionalValues.length > 0) {
|
||||
outputValues.push(...additionalValues);
|
||||
}
|
||||
|
||||
return {
|
||||
type: 'dynamic_value',
|
||||
values: outputValues
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Generator for 'source' Block
|
||||
*
|
||||
* Generates a source object defining the data source based on Time Frame (TF),
|
||||
* Exchange (EXC), and Symbol (SYM).
|
||||
*/
|
||||
Blockly.JSON['source'] = function(block) {
|
||||
// Retrieve dropdown values
|
||||
const timeFrame = block.getFieldValue('TF') || '1m'; // Default to '1m' if not set
|
||||
const exchange = block.getFieldValue('EXC') || 'Binance'; // Default to 'Binance'
|
||||
const symbol = block.getFieldValue('SYM') || 'BTCUSDT'; // Default to 'BTCUSDT'
|
||||
|
||||
// Validate fields if necessary
|
||||
// Example: Ensure timeFrame matches expected patterns
|
||||
// Skipping detailed validation for brevity
|
||||
const timeFrame = block.getFieldValue('TF') || '1m';
|
||||
const exchange = block.getFieldValue('EXC') || 'Binance';
|
||||
const symbol = block.getFieldValue('SYM') || 'BTCUSDT';
|
||||
|
||||
// Create source object
|
||||
const sourceObject = {
|
||||
const operationObject = Blockly.JSON.createOperationObject('source', {
|
||||
time_frame: timeFrame,
|
||||
exchange: exchange,
|
||||
symbol: symbol
|
||||
};
|
||||
symbol: symbol});
|
||||
|
||||
// Output as source object
|
||||
return sourceObject;
|
||||
// Prepare output, handling any additional values
|
||||
const outputValues = [operationObject];
|
||||
const valuesArray = Blockly.JSON.processValuesInput(block);
|
||||
if (valuesArray.length > 0) {
|
||||
outputValues.push(...valuesArray);
|
||||
}
|
||||
|
||||
return {
|
||||
type: 'dynamic_value',
|
||||
values: outputValues
|
||||
};
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,39 +34,60 @@ export function defineOrderMetricsGenerators() {
|
|||
* Generator for 'order_volume' Block
|
||||
*/
|
||||
Blockly.JSON['order_volume'] = function(block) {
|
||||
// Retrieve the 'ORDER_TYPE' from the dropdown
|
||||
const orderType = block.getFieldValue('ORDER_TYPE'); // 'filled' or 'unfilled'
|
||||
const orderType = block.getFieldValue('ORDER_TYPE');
|
||||
const validatedOrderType = validateOrderType(orderType);
|
||||
|
||||
// Create operation object
|
||||
const operationObject = Blockly.JSON.createOperationObject('order_volume', {
|
||||
order_type: validatedOrderType
|
||||
});
|
||||
|
||||
// Output as dynamic_value
|
||||
return operationObject;
|
||||
// Prepare output, handling any additional values
|
||||
const outputValues = [operationObject];
|
||||
const valuesArray = Blockly.JSON.processValuesInput(block);
|
||||
if (valuesArray.length > 0) {
|
||||
outputValues.push(...valuesArray);
|
||||
}
|
||||
|
||||
return {
|
||||
type: 'dynamic_value',
|
||||
values: outputValues
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Generator for 'filled_orders' Block
|
||||
*/
|
||||
Blockly.JSON['filled_orders'] = function(block) {
|
||||
// Create operation object
|
||||
const operationObject = Blockly.JSON.createOperationObject('filled_orders', {});
|
||||
|
||||
// Output as dynamic_value
|
||||
return operationObject;
|
||||
const outputValues = [operationObject];
|
||||
const valuesArray = Blockly.JSON.processValuesInput(block);
|
||||
if (valuesArray.length > 0) {
|
||||
outputValues.push(...valuesArray);
|
||||
}
|
||||
|
||||
return {
|
||||
type: 'dynamic_value',
|
||||
values: outputValues
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Generator for 'unfilled_orders' Block
|
||||
*/
|
||||
Blockly.JSON['unfilled_orders'] = function(block) {
|
||||
// Create operation object
|
||||
const operationObject = Blockly.JSON.createOperationObject('unfilled_orders', {});
|
||||
|
||||
// Output as dynamic_value
|
||||
return operationObject;
|
||||
const outputValues = [operationObject];
|
||||
const valuesArray = Blockly.JSON.processValuesInput(block);
|
||||
if (valuesArray.length > 0) {
|
||||
outputValues.push(...valuesArray);
|
||||
}
|
||||
|
||||
return {
|
||||
type: 'dynamic_value',
|
||||
values: outputValues
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -74,11 +95,11 @@ export function defineOrderMetricsGenerators() {
|
|||
*/
|
||||
Blockly.JSON['order_status'] = function(block) {
|
||||
// Retrieve the 'ORDER_NAME' field input
|
||||
const orderName = (block.getFieldValue('ORDER_NAME') || 'undefined_order').trim();
|
||||
const orderName = (block.getFieldValue('ORDER_NAME') || 'last_order').trim();
|
||||
|
||||
// Validate order name
|
||||
if (!orderName) {
|
||||
console.warn("Empty 'ORDER_NAME' in 'order_status' block. Using 'undefined_order'.");
|
||||
console.warn("Empty 'ORDER_NAME' in 'order_status' block. Using 'last_order'.");
|
||||
}
|
||||
|
||||
// Retrieve the selected order status from the dropdown
|
||||
|
|
@ -92,7 +113,7 @@ export function defineOrderMetricsGenerators() {
|
|||
|
||||
// Create operation object
|
||||
const operationObject = Blockly.JSON.createOperationObject('order_status', {
|
||||
order_name: orderName || 'undefined_order',
|
||||
order_name: orderName || 'last_order',
|
||||
status: validatedStatus
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -8,63 +8,89 @@
|
|||
|
||||
export function defineRiskManagementGenerators() {
|
||||
|
||||
// Ensure the base generator is initialized and 'createOperationObject' is available
|
||||
if (!Blockly.JSON || !Blockly.JSON.createOperationObject) {
|
||||
console.error("Blockly.JSON or createOperationObject not initialized. Please ensure 'json_base_generator.js' is loaded first.");
|
||||
// Ensure the base generator is initialized
|
||||
if (!Blockly.JSON) {
|
||||
console.error("Blockly.JSON not initialized.");
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper Function: processNumericInput
|
||||
* Processes a numeric input and ensures it is a valid number.
|
||||
* Processes a numeric input and returns the value.
|
||||
*
|
||||
* @param {Blockly.Block} block - The block containing the input.
|
||||
* @param {string} inputName - The name of the input field.
|
||||
* @param {number} defaultValue - The default value to use if input is invalid.
|
||||
* @param {number} minValue - The minimum allowable value (inclusive).
|
||||
* @param {number} maxValue - The maximum allowable value (inclusive).
|
||||
* @returns {number} - The validated numeric input.
|
||||
* @param {*} defaultValue - The default value to use if the input is invalid.
|
||||
* @returns {*} - The value from the input.
|
||||
*/
|
||||
function processNumericInput(block, inputName, defaultValue, minValue = Number.NEGATIVE_INFINITY, maxValue = Number.POSITIVE_INFINITY) {
|
||||
Blockly.JSON.processNumericInput = function(block, inputName, defaultValue) {
|
||||
const inputBlock = block.getInputTargetBlock(inputName);
|
||||
let value = defaultValue;
|
||||
|
||||
if (inputBlock) {
|
||||
const inputJson = Blockly.JSON.blockToCode(inputBlock);
|
||||
if (typeof inputJson === 'number') {
|
||||
value = inputJson;
|
||||
const generator = Blockly.JSON[inputBlock.type];
|
||||
if (typeof generator === 'function') {
|
||||
const inputJson = generator(inputBlock);
|
||||
|
||||
// Handle dynamic_value
|
||||
if (inputJson && inputJson.type === 'dynamic_value' && Array.isArray(inputJson.values)) {
|
||||
const firstValue = inputJson.values[0];
|
||||
if (typeof firstValue === 'number' || typeof firstValue === 'object') {
|
||||
return firstValue;
|
||||
} else {
|
||||
console.warn(`Invalid value in '${inputName}'. Using default value ${defaultValue}.`);
|
||||
return defaultValue;
|
||||
}
|
||||
} else if (typeof inputJson === 'number' || typeof inputJson === 'object') {
|
||||
return inputJson;
|
||||
} else {
|
||||
console.warn(`Invalid input for '${inputName}'. Using default value ${defaultValue}.`);
|
||||
return defaultValue;
|
||||
}
|
||||
} else {
|
||||
console.error(`Invalid value for '${inputName}' in '${block.type}' block. Expected a number.`);
|
||||
console.warn(`No generator found for block type '${inputBlock.type}'. Using default value ${defaultValue}.`);
|
||||
return defaultValue;
|
||||
}
|
||||
} else {
|
||||
console.warn(`No block connected to '${inputName}' input of '${block.type}' block. Using default value ${defaultValue}.`);
|
||||
console.warn(`No block connected to '${inputName}'. Using default value ${defaultValue}.`);
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
// Enforce limits
|
||||
if (value < minValue) {
|
||||
console.warn(`Value for '${inputName}' in '${block.type}' block is below minimum (${minValue}). Clamping to ${minValue}.`);
|
||||
value = minValue;
|
||||
} else if (value > maxValue) {
|
||||
console.warn(`Value for '${inputName}' in '${block.type}' block is above maximum (${maxValue}). Clamping to ${maxValue}.`);
|
||||
value = maxValue;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Generator for 'set_leverage' Block
|
||||
*/
|
||||
Blockly.JSON['set_leverage'] = function(block) {
|
||||
// Process the 'LEVERAGE' input
|
||||
const leverage = processNumericInput(block, 'LEVERAGE', 1, 1, 100); // Leverage between 1 and 100
|
||||
// Process the 'LEVERAGE' input using processNumericInput
|
||||
const leverageValue = Blockly.JSON.processNumericInput(block, 'LEVERAGE', 1);
|
||||
|
||||
// Validate the leverage value
|
||||
let leverage = leverageValue;
|
||||
|
||||
if (typeof leverageValue === 'number') {
|
||||
// Enforce limits (e.g., leverage between 1 and 100)
|
||||
if (leverageValue < 1) {
|
||||
console.warn(`Leverage value too low. Clamping to 1.`);
|
||||
leverage = 1;
|
||||
} else if (leverageValue > 100) {
|
||||
console.warn(`Leverage value too high. Clamping to 100.`);
|
||||
leverage = 100;
|
||||
}
|
||||
} else if (typeof leverageValue === 'object' && leverageValue !== null) {
|
||||
// Include the object directly (e.g., if it's an operation)
|
||||
leverage = leverageValue;
|
||||
} else {
|
||||
console.warn(`Invalid leverage value. Defaulting to 1.`);
|
||||
leverage = 1;
|
||||
}
|
||||
|
||||
// Create operation object
|
||||
const json = Blockly.JSON.createOperationObject('set_leverage', {
|
||||
leverage: leverage
|
||||
});
|
||||
const json = {
|
||||
type: 'set_leverage',
|
||||
inputs: {
|
||||
LEVERAGE: leverage
|
||||
}
|
||||
};
|
||||
|
||||
// Output as dynamic_value
|
||||
// Return the operation object directly
|
||||
return json;
|
||||
};
|
||||
|
||||
|
|
@ -73,10 +99,13 @@ export function defineRiskManagementGenerators() {
|
|||
*/
|
||||
Blockly.JSON['current_margin'] = function(block) {
|
||||
// Create operation object
|
||||
const operationObject = Blockly.JSON.createOperationObject('current_margin', {});
|
||||
const json = {
|
||||
type: 'current_margin',
|
||||
inputs: {}
|
||||
};
|
||||
|
||||
// Output as dynamic_value
|
||||
return operationObject;
|
||||
// Return the operation object directly
|
||||
return json;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -84,25 +113,47 @@ export function defineRiskManagementGenerators() {
|
|||
*/
|
||||
Blockly.JSON['risk_ratio'] = function(block) {
|
||||
// Create operation object
|
||||
const operationObject = Blockly.JSON.createOperationObject('risk_ratio', {});
|
||||
const json = {
|
||||
type: 'risk_ratio',
|
||||
inputs: {}
|
||||
};
|
||||
|
||||
// Output as dynamic_value
|
||||
return operationObject;
|
||||
// Return the operation object directly
|
||||
return json;
|
||||
};
|
||||
|
||||
/**
|
||||
* Generator for 'max_position_size' Block
|
||||
*/
|
||||
Blockly.JSON['max_position_size'] = function(block) {
|
||||
// Process the 'MAX_SIZE' input
|
||||
const maxSize = processNumericInput(block, 'MAX_SIZE', 1, 1); // Minimum of 1
|
||||
// Process the 'MAX_SIZE' input using processNumericInput
|
||||
const maxSizeValue = Blockly.JSON.processNumericInput(block, 'MAX_SIZE', 1);
|
||||
|
||||
let maxSize = maxSizeValue;
|
||||
|
||||
if (typeof maxSizeValue === 'number') {
|
||||
// Enforce limits (e.g., minimum of 1)
|
||||
if (maxSizeValue < 1) {
|
||||
console.warn(`Max position size too low. Clamping to 1.`);
|
||||
maxSize = 1;
|
||||
}
|
||||
} else if (typeof maxSizeValue === 'object' && maxSizeValue !== null) {
|
||||
// Include the object directly (e.g., if it's an operation)
|
||||
maxSize = maxSizeValue;
|
||||
} else {
|
||||
console.warn(`Invalid max position size value. Defaulting to 1.`);
|
||||
maxSize = 1;
|
||||
}
|
||||
|
||||
// Create operation object
|
||||
const json = Blockly.JSON.createOperationObject('max_position_size', {
|
||||
max_size: maxSize
|
||||
});
|
||||
const json = {
|
||||
type: 'max_position_size',
|
||||
inputs: {
|
||||
MAX_SIZE: maxSize
|
||||
}
|
||||
};
|
||||
|
||||
// Output as dynamic_value
|
||||
// Return the action object directly
|
||||
return json;
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,16 +34,21 @@ export function defineTimeMetricsGenerators() {
|
|||
* Generator for 'time_since_start' Block
|
||||
*/
|
||||
Blockly.JSON['time_since_start'] = function(block) {
|
||||
// Retrieve the selected time unit from the dropdown
|
||||
const timeUnit = block.getFieldValue('TIME_UNIT') || 'seconds';
|
||||
const validatedTimeUnit = validateTimeUnit(timeUnit);
|
||||
|
||||
// Create operation object
|
||||
const operationObject = Blockly.JSON.createOperationObject('time_since_start', {
|
||||
unit: validatedTimeUnit
|
||||
});
|
||||
const operationObject = Blockly.JSON.createOperationObject('time_since_start', {unit: validatedTimeUnit} );
|
||||
|
||||
// Output as dynamic_value
|
||||
return operationObject;
|
||||
// Prepare output, handling any additional values
|
||||
const outputValues = [operationObject];
|
||||
const valuesArray = Blockly.JSON.processValuesInput(block);
|
||||
if (valuesArray.length > 0) {
|
||||
outputValues.push(...valuesArray);
|
||||
}
|
||||
|
||||
return {
|
||||
type: 'dynamic_value',
|
||||
values: outputValues
|
||||
};
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,74 +18,119 @@ export function defineTradeMetricsGenerators() {
|
|||
* Generator for 'active_trades' Block
|
||||
*/
|
||||
Blockly.JSON['active_trades'] = function(block) {
|
||||
// Create operation object
|
||||
const operationObject = Blockly.JSON.createOperationObject('active_trades', {});
|
||||
|
||||
// Output as dynamic_value
|
||||
return JSON.stringify(operationObject);
|
||||
// Handle additional values
|
||||
const outputValues = [operationObject];
|
||||
const valuesArray = Blockly.JSON.processValuesInput(block);
|
||||
if (valuesArray.length > 0) {
|
||||
outputValues.push(...valuesArray);
|
||||
}
|
||||
|
||||
return {
|
||||
type: 'dynamic_value',
|
||||
values: outputValues
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Generator for 'total_trades' Block
|
||||
*/
|
||||
Blockly.JSON['total_trades'] = function(block) {
|
||||
// Create operation object
|
||||
const operationObject = Blockly.JSON.createOperationObject('total_trades', {});
|
||||
|
||||
// Output as dynamic_value
|
||||
return JSON.stringify(operationObject);
|
||||
// Handle additional values
|
||||
const outputValues = [operationObject];
|
||||
const valuesArray = Blockly.JSON.processValuesInput(block);
|
||||
if (valuesArray.length > 0) {
|
||||
outputValues.push(...valuesArray);
|
||||
}
|
||||
|
||||
return {
|
||||
type: 'dynamic_value',
|
||||
values: outputValues
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Generator for 'last_trade_details' Block
|
||||
*/
|
||||
Blockly.JSON['last_trade_details'] = function(block) {
|
||||
// Retrieve the 'DETAILS' field input
|
||||
const details = block.getFieldValue('DETAILS'); // 'price', 'volume', 'direction'
|
||||
|
||||
// Validate the details selection
|
||||
const details = block.getFieldValue('DETAILS');
|
||||
const validDetails = ['price', 'volume', 'direction'];
|
||||
const validatedDetails = validDetails.includes(details) ? details : 'price';
|
||||
|
||||
// Create operation object
|
||||
const operationObject = Blockly.JSON.createOperationObject('last_trade_details', {
|
||||
details: validatedDetails
|
||||
});
|
||||
|
||||
// Output as dynamic_value
|
||||
return JSON.stringify(operationObject);
|
||||
// Handle additional values
|
||||
const outputValues = [operationObject];
|
||||
const valuesArray = Blockly.JSON.processValuesInput(block);
|
||||
if (valuesArray.length > 0) {
|
||||
outputValues.push(...valuesArray);
|
||||
}
|
||||
|
||||
return {
|
||||
type: 'dynamic_value',
|
||||
values: outputValues
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Generator for 'average_entry_price' Block
|
||||
*/
|
||||
Blockly.JSON['average_entry_price'] = function(block) {
|
||||
// Create operation object
|
||||
const operationObject = Blockly.JSON.createOperationObject('average_entry_price', {});
|
||||
|
||||
// Output as dynamic_value
|
||||
return JSON.stringify(operationObject);
|
||||
// Handle additional values
|
||||
const outputValues = [operationObject];
|
||||
const valuesArray = Blockly.JSON.processValuesInput(block);
|
||||
if (valuesArray.length > 0) {
|
||||
outputValues.push(...valuesArray);
|
||||
}
|
||||
|
||||
return {
|
||||
type: 'dynamic_value',
|
||||
values: outputValues
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Generator for 'unrealized_profit_loss' Block
|
||||
*/
|
||||
Blockly.JSON['unrealized_profit_loss'] = function(block) {
|
||||
// Create operation object
|
||||
const operationObject = Blockly.JSON.createOperationObject('unrealized_profit_loss', {});
|
||||
|
||||
// Output as dynamic_value
|
||||
return JSON.stringify(operationObject);
|
||||
// Handle additional values
|
||||
const outputValues = [operationObject];
|
||||
const valuesArray = Blockly.JSON.processValuesInput(block);
|
||||
if (valuesArray.length > 0) {
|
||||
outputValues.push(...valuesArray);
|
||||
}
|
||||
|
||||
return {
|
||||
type: 'dynamic_value',
|
||||
values: outputValues
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Generator for 'user_active_trades' Block
|
||||
*/
|
||||
Blockly.JSON['user_active_trades'] = function(block) {
|
||||
// Create operation object
|
||||
const operationObject = Blockly.JSON.createOperationObject('user_active_trades', {});
|
||||
|
||||
// Output as dynamic_value
|
||||
return JSON.stringify(operationObject);
|
||||
// Handle additional values
|
||||
const outputValues = [operationObject];
|
||||
const valuesArray = Blockly.JSON.processValuesInput(block);
|
||||
if (valuesArray.length > 0) {
|
||||
outputValues.push(...valuesArray);
|
||||
}
|
||||
|
||||
return {
|
||||
type: 'dynamic_value',
|
||||
values: outputValues
|
||||
};
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,22 +35,13 @@ export function defineVAFGenerators() {
|
|||
};
|
||||
return json;
|
||||
};
|
||||
/**
|
||||
* Generator for 'value_input' Block
|
||||
*
|
||||
* Generates a JSON object representing a list of numerical values or nested objects.
|
||||
* Handles 'VALUE' and 'NEXT' inputs separately to support dynamic block chaining.
|
||||
*
|
||||
* @param {Block} block - The current block to generate JSON for.
|
||||
* @returns {Object} The generated JSON object.
|
||||
*/
|
||||
Blockly.JSON['value_input'] = function(block) {
|
||||
const values = []; // Initialize the values array
|
||||
|
||||
/**
|
||||
* Recursively processes each block in the chain.
|
||||
*
|
||||
* @param {Block} currentBlock - The current block to process.
|
||||
* @param {Blockly.Block} currentBlock - The current block to process.
|
||||
* @param {number} currentDepth - The current depth in the recursion.
|
||||
*/
|
||||
function processBlock(currentBlock, currentDepth) {
|
||||
|
|
@ -63,17 +54,38 @@ export function defineVAFGenerators() {
|
|||
|
||||
// Process 'VALUE' and add to values
|
||||
const valueJson = processConnectedBlock(currentBlock, 'VALUE');
|
||||
if (valueJson !== null) values.push(valueJson);
|
||||
if (valueJson !== null) {
|
||||
// Flatten any nested dynamic_value objects
|
||||
if (valueJson && valueJson.type === 'dynamic_value' && Array.isArray(valueJson.values)) {
|
||||
values.push(...valueJson.values);
|
||||
} else {
|
||||
values.push(valueJson);
|
||||
}
|
||||
}
|
||||
|
||||
// Process 'NEXT' block in the chain
|
||||
const nextBlock = currentBlock.getInputTargetBlock('NEXT');
|
||||
if (nextBlock) processBlock(nextBlock, currentDepth + 1);
|
||||
if (nextBlock) {
|
||||
const generator = Blockly.JSON[nextBlock.type];
|
||||
if (typeof generator === 'function') {
|
||||
const generatedValue = generator(nextBlock);
|
||||
|
||||
// Flatten any nested dynamic_value objects
|
||||
if (generatedValue && generatedValue.type === 'dynamic_value' && Array.isArray(generatedValue.values)) {
|
||||
values.push(...generatedValue.values);
|
||||
} else {
|
||||
values.push(generatedValue);
|
||||
}
|
||||
} else {
|
||||
console.warn(`No generator found for block type "${nextBlock.type}". Skipping this block.`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes a connected block and retrieves its generated JSON or field value.
|
||||
*
|
||||
* @param {Block} block - The current block to process.
|
||||
* @param {Blockly.Block} block - The current block to process.
|
||||
* @param {string} inputName - The input name to retrieve.
|
||||
* @returns {*} The processed value or null if invalid.
|
||||
*/
|
||||
|
|
@ -83,29 +95,31 @@ export function defineVAFGenerators() {
|
|||
const generator = Blockly.JSON[connectedBlock.type];
|
||||
if (typeof generator === 'function') {
|
||||
const generatedValue = generator(connectedBlock);
|
||||
return generatedValue.type === 'dynamic_value' && Array.isArray(generatedValue.values)
|
||||
? generatedValue.values
|
||||
: generatedValue;
|
||||
|
||||
// Flatten any nested dynamic_value objects
|
||||
if (generatedValue && generatedValue.type === 'dynamic_value' && Array.isArray(generatedValue.values)) {
|
||||
return generatedValue.values; // Return the values array directly
|
||||
}
|
||||
return generatedValue;
|
||||
}
|
||||
console.warn(`No generator found for block type "${connectedBlock.type}". Skipping this block.`);
|
||||
return null;
|
||||
}
|
||||
|
||||
// If no connected block, parse field value as float
|
||||
const fieldValue = parseFloat(block.getFieldValue(inputName) || NaN);
|
||||
let fieldValue = parseFloat(block.getFieldValue(inputName) || NaN);
|
||||
if (isNaN(fieldValue)) {
|
||||
console.warn(`Invalid VALUE "${block.getFieldValue(inputName)}" in value_input block. Skipping this block.`);
|
||||
return null;
|
||||
fieldValue = block.getFieldValue(inputName);
|
||||
}
|
||||
return fieldValue;
|
||||
}
|
||||
|
||||
processBlock(block, 1); // Start processing from the initial block
|
||||
|
||||
// Build the JSON object without the skipAdditionalParsing flag
|
||||
const json = {
|
||||
type: 'dynamic_value',
|
||||
values: values.flat(), // Flatten values array to handle nested arrays
|
||||
skipAdditionalParsing: true // Flag to skip further parsing
|
||||
values: values.flat() // Flatten values array to handle nested arrays
|
||||
};
|
||||
|
||||
console.log(`Generated JSON for 'value_input' block:`, json);
|
||||
|
|
@ -114,7 +128,6 @@ export function defineVAFGenerators() {
|
|||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Generator for 'get_variable' Block
|
||||
*
|
||||
|
|
|
|||
|
|
@ -49,11 +49,13 @@ export function defineIndicatorBlocks() {
|
|||
fields: {
|
||||
NAME: indicatorName,
|
||||
OUTPUT: selectedOutput
|
||||
},
|
||||
inputs: {},
|
||||
statements: {}
|
||||
}
|
||||
};
|
||||
// Output as dynamic_value
|
||||
return {
|
||||
type: 'dynamic_value',
|
||||
values: [json]
|
||||
};
|
||||
return json;
|
||||
};
|
||||
|
||||
// Append the newly created block to the Indicators category in the toolbox
|
||||
|
|
|
|||
Loading…
Reference in New Issue