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:

  1. Create a Groovy Library and name it for example ‘StrategyDesignerCustomBlocks’ (write down this name for later).

  2. In the library, create the first element and name it Meta. Set its Display Mode to Everywhere. More on the Meta element later.

  3. 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

Property

Type

Required?

Description

element

String

Required

Location of the Groovy function which the block calls.

function

String

Required

Name of the function in the element specified by the element property, which the block calls.

params

List

Optional

List of parameter objects specifying the inputs of the block.

Each parameter object has the following properties:

  • name (String) – Unique name of the parameter.

  • type (String) – Type of the parameter, either number, string, or option.

  • default – Default value supplied if the user does not provide the input; its type must correspond with the type property.

  • options – Specified only if the type is option. It is either a list of values, or a map where the key is the option’s value and value is the option’s label.

label

String

Required

Label rendered on the block. It can contain parameter placeholders like {1}, {2}, etc. If params are specified, each parameter will be rendered at the position of the corresponding placeholder, so the first parameter will be rendered at the {1} placeholder, the second at the {2} placeholder, and so on.

If no placeholders are specified, the parameters will be all added at the end of the block.

color

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.

tooltip

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.

returns

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 element and function parameters.

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:

  1. 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.

  2. 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 and function 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:

  1. 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 the operatorOptions (lines 5 and 22).

  2. 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: