roboquant avatar

Technical Analysis

Introduction

Technical analysis is based on information coming directly from the financial markets and exchanges. This includes attributes like:

  • Asset prices, including quotes, trades and aggregates like price-bars

  • Volumes of trades and aggregates there of

  • Open interest (only for certain types of asset classes)

  • Shorting percentage

  • Spread (difference between ask and bid prices)

Since many strategies use technical analysis as a basis for decision-making, roboquant comes out-of-the-box with support for two of the most popular Java libraries:

In order to use this, you’ll need to include to module roboquant-ta in your build tool of choice.

This page goes into more details on how to use the TaLib library. For Ta4j, please check their website.

Basic usage

TaLib is a very fast and powerful library that can be used for calculating many different types of indicators. It is a pure Java library, but using an API that might be confusing.

In order to make it more convenient to work with, roboquant comes with a wrapper for it. The following example shows how to use it:

val taLib = TaLib()
val asset = Asset("TEST")

// Keep maximum 30 price-bars in history
val serie = PriceBarSeries(30)
repeat(30) {
    serie.add(PriceBar(asset, 100.0, 101.0, 99.0, 100.0, 1000))
}

// Calculate EMA over the last 10 days using the default price-type (close)
val ema10: Double = taLib.ema(serie, 10)

// Calculate EMA over the last 5 days using the low-prices of the price-bars
val ema5: Double = taLib.ema(serie.low, 5)

// Calculate RSI over the default period (14) using the default price-type (close)
val rsi: Double = taLib.rsi(serie)

// Did we detect the Two Crow candlestick pattern today?
val found: Boolean = taLib.cdl2Crows(serie)

// Did we detect the Two Crow candlestick pattern 4 days ago?
val found2: Boolean = taLib.cdl2Crows(serie, 4)

// Some indicators return multiple double values, like the Bollinger Bands
val (high, mid, low) = taLib.bbands(serie)

If you invoke a taLib-indicator and don’t have enough data to perform the calculations, an InsufficientData exception will be thrown. You can either increase the initial capacity of the PriceBarSerie, or use this exception to increase the historic data capacity:

if (series.isFull()) {
    try {
        taLib.sma(series, 30)
    } catch (err: InsufficientData) {
        // The error contains the minimum required size
        series.increaseCapacity(err.minSize)
    }
}

TaLibStrategy

If you use TaLibStrategy as a foundation for your strategy, much of the boilerplate code is already taken care of. All you have to do is to define the sell and buy rules.

Each rule should return true or false. In case you return true, a corresponding Signal will be created. So if you return true from the buy-rule, a buy signal will be created.

If you don’t implement a rule, it will by default always return false.

You can provide the amount of history to keep track of when initializing the TaLibStrategy. But if you don’t, the history will grow automatically until the rules can be successfully executed.

val strategy = TaLibStrategy()

// Define the buy rule
strategy.buy {
    cdl3StarsInSouth(it) || cdl3WhiteSoldiers(it)
}

// Define the sell rule
strategy.sell {
    ema(it.close, 5) < ema(it.close, 10)
}

You can also write your own TaLib indicators and use them:

fun TaLib.myIndicator(period: Int = 10, series: PriceBarSeries): Double {
    val x1 = sma(series, period)
    val x2 = ema(series, period)
    return x1 - x2
}

val s = TaLibStrategy()
s.buy {
    myIndicator(10, it) > 0
}

If you want to run different indicators over different resolutions, that is also possible:

fun TaLib.myIndicator(serie1M: PriceBarSeries): Double {
    val series5M = serie1M.aggregate(5)
    val x1 = sma(serie1M, 20)
    val x2 = ema(series5M, 2)
    return x1 - x2
}

// You have to specify enough history for both 1M and 5M
val s = TaLibStrategy(21)
s.buy {
    myIndicator(it) > 0
}

TaLibMetric & TaLibIndicator

Similar to the TaLibStrategy, the TaLibMetric makes it convenient to turn TaLib indicators into roboquant metrics.

The following example demonstrates how to create a metric for the three values of the Bollinger Band indicator.

val metric = TaLibMetric {
    val (h, m, l) = bbands(it)
    mapOf(
        "bbands.low" to l,
        "bbands.mid" to m,
        "bbands.high" to h,
    )
}

The final names of the metrics will be in the form of "<metric name>.<symbol>".

The TaLibIndicator works almost exactly the same, but as usual, an indicator only works on a single price-series.

The following indicator flags when it detects a 3-white-soldiers candlestick pattern:

val metric = TaLibIndicator {
    val value = if (cdl3WhiteSoldiers(it)) 1.0 else 0.0
    mapOf(
        "threewhitesoldiers" to value,
    )
}