Create Custom Blocks in Groovy (Strategy Designer)
expert level
If blocks provided by Strategy Designer out-of-the-box are not enough, you can define your own using Groovy.
Create Groovy Library
Follow these steps:
Create a Groovy Library and name it for example ‘StrategyDesignerCustomBlocks’ (write down this name for later).
In the library, create the first element and name it Meta. Set its Display Mode to Everywhere. More on the Meta element later.
Create other elements (at least one) which will contain the functions your custom blocks will call.
Meta Element
The Meta element must return an array of objects, each object representing a block you want to create in Strategy Designer.
Each block has a label and can contain parameters (block inputs where other blocks can be plugged in). The block will then call a specified custom Groovy function located in another element of this library, passing the values of the user-provided parameters to it.
The block object has the following structure:
Property | Type | Required? | Description |
---|---|---|---|
| String | Required | Location of the Groovy function which the block calls. |
| String | Required | Name of the function in the element specified by the |
| List | Optional | List of parameter objects specifying the inputs of the block. Each parameter object has the following properties:
|
| String | Required | Label rendered on the block. It can contain parameter placeholders like If no placeholders are specified, the parameters will be all added at the end of the block. |
| Integer or String | Optional | Color of the block. It can be either an integer value (0 to 360) representing HSV hue value, or a #rrggbb String representing a web color. If no color is specified, the block will have the default color of the Functions toolbox category. |
| String | Optional | Tooltip of the block which is displayed when the user hovers the pointer over the block. If empty, no tooltip will be displayed. |
| String, either number or string | Required | Return type of the block. It should correspond with the type of the return value from the function defined by the |
Examples of Custom Blocks
Arithmetic Block
Let’s say we want to create an arithmetic block for calculating a percentage mark-up that will look like this:
Create the Meta element and return a list with a single block configuration object from it, like this:
return [ [ "element" : "Math", "function": "markupPct", "label" : "{1} x (1 + {2})", "params" : [ ["name": "base", "type": "number"], ["name": "markup", "type": "number", "default": 0] ], "returns" : "number" ] ]
Notice the label containing the placeholders that make the inputs render at the correct places.
Create a Math element and in it create a function called markupPct with two parameters, like this:
BigDecimal markupPct(BigDecimal base, BigDecimal markup) { if (base == null) { throw new Error("The base cannot be empty"); } if (markup == null) { return base; } return base * (1 + markup) }
You can name the element and function whatever you want as long as it is the same name as specified in the
element
andfunction
properties in the block configuration object.
If you have multiple custom blocks, return all of them in the list returned by the Meta element.
You can have as many or as few elements with your functions as you want. A good practice is to group your functions in elements with a similar purpose, like Math, Text or Competition Data.
Block with Drop-down
Let’s say we want to create a block that will retrieve an aggregation of prices of a selected competitor. The block should look like this:
Create the Meta element and return a list with a single block configuration object from it (or append the object to the existing list of objects), like this:
def competitorsIt = api.stream("PCOMP", "competitor", ["competitor"], true) def competitors = competitorsIt.collect { it.competitor } competitorsIt?.close(); def operatorOptions = [ "MAX" : "highest", "MIN" : "lowest", "AVG" : "average", "SUM" : "total", "FIRST": "oldest", "LAST" : "latest" ] return [ [ "element" : "Competition", "function": "competitorPrice", "status" : "active", "label" : "Get {1} price of competitor {2}", "params" : [ ["name": "operator", "type": "option", "options": operatorOptions], ["name": "competitor", "type": "option", "options": competitors] ] ] ]
Notice how we first retrieved the available options from the PCOMP table (line 1) and mapped it into an array of competitor names (line 2). We used this list of competitors in the
competitor
input parameter (line 22). Instead of a list, we could also feed it a map where the key is a competitor ID and the value is the competitor’s name, if necessary. We took this approach with theoperatorOptions
(lines 5 and 22).Create a Competition element and in it create a function called competitorPrice with two parameters, like this:
Update Configuration
When your Groovy library is configured, you just need to tell Strategy Designer that it should read it and create blocks from the definitions. You can do it by specifying the customBlocksGroovyLibraryName
option and setting it to your library’s name.
For example:
When you reload the Strategy Designer, it will show all the defined blocks in the Functions category in the Strategies toolbox: