# Stateless analytics

## Objective

We will create a Bollinger band analytic function that calculated the upper, middle and lower pricing bands for a given variable.&#x20;

> Bollinger Bands are a type of price envelope developed by [John Bollinger](https://www.bollingerbands.com/). (Price envelopes define upper and lower price range levels.) Bollinger Bands are envelopes plotted at a standard deviation level above and below a simple moving average of the price. Because the distance of the bands is based on standard deviation, they adjust to volatility swings in the underlying price.
>
> [*Fidelity*](https://www.fidelity.com/learning-center/trading-investing/technical-analysis/technical-indicator-guide/bollinger-bands)

<div data-full-width="true"><figure><img src="https://3062398388-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FUU6FZlV07ZD90OzbzGww%2Fuploads%2FNll8FQMwAQRhTrxFolaI%2Fbolllingerbands.png?alt=media&#x26;token=b6b9bde3-1339-432b-97ad-389739896e00" alt=""><figcaption><p>Source: Fidelity Investments</p></figcaption></figure></div>

### Prerequisites

To get started building a custom processor ensure you have your development environment configured. Read the [environment setup](https://docs.fractalworks.io/joule/developer-guides/setting-up-developer-environment/environment-setup) documentation to get your environment ready to build.&#x20;

## Development steps <a href="#explaining-each-step" id="explaining-each-step"></a>

These instructions cover how to build, deploy a use the function on to the Joule Platform.&#x20;

{% stepper %}
{% step %}

### Create project using the template <a href="#step-1-create-the-destination-using-the-template" id="step-1-create-the-destination-using-the-template"></a>

We have provided a project template project to quick start development. The project can be found [here](https://gitlab.com/joule-platform/fractalworks-project-templates). Clone the template project and copy relevant code and structure to your own project.

```bash
git clone git@gitlab.com:joule-platform/fractalworks-project-templates.git
```

{% hint style="info" %}
Joule uses Gradle to manage Java dependencies. To add dependencies for your processor, manage them in the `build.gradle` file inside your processors project directory.
{% endhint %}
{% endstep %}

{% step %}

### Implement Bollinger bands function

Processors differ from connectors as they do not require, currently, a specification and builder classes. So jump right in and create and name a class that reflects the processing function.

&#x20;Joule provides the core logic such as batching, cloning, linking of data stores, and a unique processor UUID for event change lineage.

Key areas of implementation:

* Define analytic function DSL namespace
* Implement following:\
  @AnalyticsDefinition annotation, compute, setParameters and getVariablePostFixID methods
* Add the class definition to plugins.properties&#x20;
* Deploy and apply to a Joule runtime environment

#### Code implementation

```java
package com.fractalworks.examples.banking.analytics;

import com.fasterxml.jackson.annotation.JsonRootName;
import com.fractalworks.streams.core.annotations.AnalyticDefinition;
import com.fractalworks.streams.core.data.streams.Context;
import com.fractalworks.streams.sdk.analytics.AnalyticsFunction;

import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

/**
 * Bollinger bands analytic unction
 */
@AnalyticDefinition(
        id = "bollingerband",
        stateless = true,
        useRawColumn = true,
        description = "Bollinger Bands are a momentum indicator used in technical analysis."
)
@JsonRootName(value = "bollinger bands")
public class BollingerBands extends AnalyticsFunction<Map<String, Double>>  {

    private int deviations = 2;
    
    public BollingerBands(){
        super();
    }

    /**
    * Calculation code for a single attribute
    */
    @Override
    public Map<String, Double> compute(Number[] values, Number previousValue, Context context) {

        Double mean = Arrays.stream(values).mapToDouble(d-> (double) d).sum() / values.length;
        double sqrtmean = 0.0;
        for(int i=0; i<values.length; i++){
            double s =  values[i].doubleValue() - mean;
            sqrtmean += s * s;
        }

        sqrtmean = Math.sqrt( sqrtmean / values.length);
        double band = deviations * sqrtmean;

        Map<String, Double> results = new HashMap<>();
        results.put("upper", mean + band);
        results.put("middle", mean );
        results.put("lower", mean - band);
        return results;
    }

    /**
    * Function parameters are provided as a properties map 
    */
    @Override
    public void setParameters(Properties parameters) {
        if( parameters != null && parameters.containsKey("deviations")) {
            deviations = Integer.parseInt(parameters.get("deviations").toString());
        }
    }

    /**
    * This defines the resulting value attributes are returned
    * as <variable>_<calculated-attribute>_<postfixid>
    * i.e. ask_upper_bollingerband
    */
    @Override
    public String getVariablePostFixID() {
        return "bollingerband";
    }
}

```

{% endstep %}

{% step %}

### Add to plugins.properties

For Joule to load and initialised the component the processor must be defined within the `plugins.properties` file under the `META-INF/services` directory.

Add the below line in the `plugins.properties` file:

```properties
com.fractalworks.examples.banking.analytics.BollingerBands
```

{% endstep %}

{% step %}

### Build, test and package <a href="#step-1-create-the-destination-using-the-template" id="step-1-create-the-destination-using-the-template"></a>

The template project provides basic JUnit test to validate DSL. The project will execute these tests during the gradle build cycle and deploy to your local maven repository.&#x20;

```bash
gradle build publishToMavenLocal
```

{% endstep %}

{% step %}

### Deploy

Once your package has been successfully created you are ready to deploy to a Joule project.

The resulting jar from the build process needs copied to the `userlibs` directory under a Joule project directory. For example using the getting started project copy the file to `quickstart/userlibs` directory.&#x20;

```bash
cp build/libs/<your-analytics>.jar <location>/userlibs
```

{% endstep %}

{% step %}

### Now apply to a stream

Let's say, sometimes we do not get a bid value which is needed to trigger an alert.  So overcome a division by zero we provide a default value and use previous values when needed.

```yaml
stream:
  name: nasdaq_major_banks_bollinger_bands_stream
  eventTimeType: EVENT_TIME

  processing unit:
    pipeline:
        # Filter events by major banks to reduce number of enrichment queries
      - filter:
          expression: "(typeof industry !== 'undefined' && 
                        industry == 'Major Banks')"

      - user defined function:
          bollinger bands:
            parameters:
              deviations: 2
          fields: [ ask, bid ]
          event history: 20

  emit:
    select: "symbol, ask_upper_bollingerband, ask_middle_bollingerband, ask_lower_bollingerband"

  group by:
    - symbol
```

Follow the same steps used in the [getting started](https://docs.fractalworks.io/joule/tutorials/getting-started) documentation to apply this script.
{% endstep %}
{% endstepper %}

## What we have learnt

As a first process we have covered a number of key features:

* <mark style="color:green;">**Build a custom analytic**</mark>\
  Used the provided template project to quick start development and add custom code within key analytic methods.
* <mark style="color:green;">**Built the jar**</mark>\
  Used gradle build tool to build, test and deploy to local maven repo.
* <mark style="color:green;">**Deploy the jar to a Joule runtime environment**</mark>\
  Copied the Jar to an existing local Joule runtime environment&#x20;
* <mark style="color:green;">**Apply the custom analytic within a use case**</mark>\
  Apply the analytic within a use case to provide Bollinger bands for ask and bid prices.
