# User defined analytics

## Objective

The analytics processor provides a rich set of features that enable developers to define and execute analytics. This feature is idea for executing an event based analytic expression using attributes present within the event, linked reference data and  passed variables.

{% hint style="info" %}
Currently only Javascript ECMAScript 2024 and Python 3.11 are supported
{% endhint %}

## Processor architecture

The key analytic processor objective is to provide analytic deployment flexibility for the developer. The ambition is to enable a quick test, learn and refine cycle that reduces time to production deployment.

<figure><img src="https://3062398388-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FUU6FZlV07ZD90OzbzGww%2Fuploads%2FJCBsEYw6bfouaOobRQ4w%2Fanalytic_processor.png?alt=media&#x26;token=7625e663-83f1-4cc2-a095-99659d5c685c" alt=""><figcaption><p>Processor overview</p></figcaption></figure>

## Execution models

This section covers methods for executing expressions in stream processing, focusing on three key approaches:

1. [<mark style="color:green;">**Expression only**</mark>](#expression-only)\
   Execute independent event-based calculations using simple mathematical formulas by defining core attributes.
2. [<mark style="color:green;">**Expression and state**</mark>](#expression-and-state)\
   Incorporate constants and manage state to initialise calculations and update them with new data.
3. [<mark style="color:green;">**Expression and script**</mark>](#expression-and-script)\
   Leverage pre-existing scripts alongside expressions for more complex calculations.

See User Defined Scripts [documentation](https://docs.fractalworks.io/joule/components/analytics/analytic-tools/user-defined-analytics/user-defined-scripts) for script based execution models.

### Expression only

When you only want to execute an expression that can be defined as a mathematical formula use this method to execute independent event based calculations provide the expression along with the required core attributes.

```yaml
analytic:
  expression: (bid - ask) / scalar
  assign to: new_event_variable
```

### Expression and state

This example demonstrates how you would use provided constants for a calculation. This same method can be used to prime a calculation with a starting value for the initial calculation and then being it replaced with updated values by using the same `assign to` variable.

See Stateful variables for further [documentation](#stateful-variables).

```yaml
analytic:
  expression: "(ask + (bid - ask) / 2.0) / scaled_price"
  assign to: scaled_price
  variables:
      scaled_price : 120.21
  stateful:
      assign to: scaled_price
      memory capacity: 1
```

### Expression and script

Use this option when you have pre-existing scripts which you want to leverage within a stream processing context.

```yaml
analytic:
  script: ./scripts/js/preExistingFunctions.js
  expression: bid - squareRoot(`${ask}` / `${bid}`)
  assign to: new_event_variable
```

**Javascript function**

The example will apply the `bid` and `ask` event attributes to the expression.

Note the current implementation requires the script to be provided using the `js` extension due to the way the expression is defined.&#x20;

{% tabs %}
{% tab title="Javascript" %}

```javascript
export function squareRoot(num) {
    if (num < 0) return NaN;
    return Math.sqrt(num);
}
```

{% endtab %}

{% tab title="Python" %}

```python
import math

def square_root(num):
    if num < 0:
        return float('nan')  # Return NaN for negative input
    return math.sqrt(num)  # Return the square root for non-negative input

```

{% endtab %}
{% endtabs %}

## Variables

Variables are provided as a map of key and numerical values.

Variables and the event are passed in to the execution context per event, including stateful variables. This example demonstrates how to apply a constant as a scaling factor for a calculation.&#x20;

```yaml
analytic:  
  expression: (highest_price - lowest_price) / scaling_factor
  assign to: scaled_price
  variables:
      scaling_factor : 120.21 
```

## Stateful variables

Stateful variables provide a key function whereby the previous value(s) can be used within the current calculation context.&#x20;

The example primes the first calculation with a starting value for the initial calculation and thereafter replaced with the computed values from the stateful memory. This is achieved using the same `assign to` variable set within the `variables` section.

```yaml
analytic: 
  expression: (ask + (bid - ask) / 2.0) / Math.avg(scaled_price)
  assign to: scaled_price
  variables:
      scaled_price : 100.00
  stateful:
      assign to: scaled_price
      memory capacity: 25  
```

## Attributes schema

The follow attributes are used for expression and scripting.

<table><thead><tr><th width="136">Attribute</th><th width="365">Description</th><th width="153">Data Type</th><th data-type="checkbox">Required</th></tr></thead><tbody><tr><td>assign to</td><td>Assignment variable for the  evaluated expression result</td><td>String<br>Default: result</td><td>true</td></tr><tr><td>variables</td><td>Map of constants and seed values for the expression to use. This is an optional variable. </td><td>Map&#x3C;String, Number></td><td>false</td></tr><tr><td>stateful</td><td>Store previous computed values for next computation cycle</td><td>See <a href="#stateful-attributes-schema">stateful section</a></td><td>false</td></tr><tr><td>assignment datatype</td><td>Data type to cast too for the assignment variable. See scripting supported data types section</td><td>DataType<br>Default: Double</td><td>false</td></tr></tbody></table>

### Execution attributes schema

These attributes define what is need to execute analytical functions over a stream events.&#x20;

<table><thead><tr><th width="144">Attribute</th><th width="497">Description</th><th width="291">Data Type</th></tr></thead><tbody><tr><td>language</td><td>Language runtime to use to execute required execution definitions. Currently Javascript (javascript or js) and Python (python or py) are supported  </td><td>String<br>Default: js</td></tr><tr><td>expression</td><td>Math expression without an assignment variable. Required if method has not been provided.</td><td>String</td></tr><tr><td>script</td><td>Path of the script to loaded within the Joule processing context</td><td>String</td></tr><tr><td>function</td><td>Function to execute within the provided script using a StreamEvent as a parameter</td><td>String</td></tr><tr><td>expand all</td><td>Add all StreamEvent attributes to the processing context as independ variables</td><td>Boolean<br>Default: true</td></tr><tr><td>virtualEnv</td><td>Python only setting which set the virtual environment path to execute with the script of python expression within. </td><td>String</td></tr></tbody></table>

### Stateful attributes schema <a href="#stateful-attributes-schema" id="stateful-attributes-schema"></a>

Store previous computed values for next computation cycle. Honours the groupby definition. The previous computed value can be used on the next calculation.

<table><thead><tr><th width="144">Attribute</th><th width="353">Description</th><th width="150">Data Type</th><th data-type="checkbox">Required</th></tr></thead><tbody><tr><td>assign to</td><td>Assignment variable for the result</td><td>String</td><td>true</td></tr><tr><td>memory capacity</td><td>Number of rolling elements to store within a FIFO array</td><td>Integer<br>Default: 10</td><td>false</td></tr><tr><td>attach memory</td><td>Attach a copy of the array contents to the computed event</td><td>Boolean<br>Default: false</td><td>false</td></tr></tbody></table>
