All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.roboquant.ta.TaLibSignalStrategy.kt Maven / Gradle / Ivy

/*
 * Copyright 2020-2023 Neural Layer
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

@file:Suppress("WildcardImport")

package org.roboquant.ta

import org.roboquant.common.Asset
import org.roboquant.common.addNotNull
import org.roboquant.feeds.Event
import org.roboquant.feeds.PriceBar
import org.roboquant.strategies.Rating
import org.roboquant.strategies.Signal
import org.roboquant.strategies.SignalType
import org.roboquant.strategies.Strategy
import java.lang.Integer.max

/**
 * This strategy that makes it easy to implement different types strategies based on technical analysis indicators from
 * the TaLib library.
 *
 * This strategy requires [PriceBar] data and common use cases are candlestick patterns and moving average strategies.
 *
 * It is important that the strategy is initialized with a large enough [history] window to support the underlying
 * technical indicators you want to use. If the [history] is too small, it will lead to a runtime exception.
 *
 * @property history the history to keep track of
 * @property block the logic
 *
 */
class TaLibSignalStrategy(
    private val history: Int = 15,
    private var block: TaLib.(asset: Asset, series: PriceBarSerie) -> Signal?
) : Strategy {

    private val buffers = mutableMapOf()

    /**
     * Underlying [TaLib] instance to use when executing this strategy.
     */
    val taLib = TaLib()

    /**
     * Some default TA strategies that are based off TaLibSignalStrategy
     */
    companion object {

        /**
         * Breakout strategy that supports different entry and exit periods
         */
        fun breakout(entryPeriod: Int = 100, exitPeriod: Int = 50): TaLibSignalStrategy {
            val maxPeriod = max(entryPeriod, exitPeriod)
            return TaLibSignalStrategy(maxPeriod) { asset, series ->
                when {
                    recordHigh(series.high, entryPeriod) -> Signal(asset, Rating.BUY, SignalType.BOTH)
                    recordLow(series.low, entryPeriod) -> Signal(asset, Rating.SELL, SignalType.BOTH)
                    recordLow(series.low, exitPeriod) -> Signal(asset, Rating.SELL, SignalType.EXIT)
                    recordHigh(series.high, exitPeriod) -> Signal(asset, Rating.BUY, SignalType.EXIT)
                    else -> null
                }
            }

        }

        /**
         * MACD strategy
         */
        fun macd(): TaLibSignalStrategy {

            val strategy = TaLibSignalStrategy(35) { asset, prices ->
                val (_, _, diff) = macd(prices, 12, 26, 9)
                val (_, _, diff2) = macd(prices, 12, 26, 9, 1)
                when {
                    diff < 0.0 && diff2 >= 0.0 -> Signal(asset, Rating.BUY)
                    diff > 0.0 && diff2 <= 0.0 -> Signal(asset, Rating.SELL)
                    else -> null
                }
            }

            return strategy
        }

    }

    /**
     * Based on a [event], return zero or more signals. Typically, they are for the assets in the event,
     * but this is not a strict requirement.
     *
     * @see Strategy.generate
     *
     */
    override fun generate(event: Event): List {
        val signals = mutableListOf()
        for (priceAction in event.prices.values.filterIsInstance()) {
            val asset = priceAction.asset
            val buffer = buffers.getOrPut(asset) { PriceBarSerie(history) }
            if (buffer.add(priceAction)) {
                val signal = block.invoke(taLib, asset, buffer)
                signals.addNotNull(signal)
            }
        }
        return signals
    }

    /**
     * Reset all state
     */
    override fun reset() {
        buffers.clear()
    }

    /**
     * @suppress
     */
    override fun toString() = "TaLibSignalStrategy history=$history"

}





© 2015 - 2025 Weber Informatics LLC | Privacy Policy