cinar / indicator

Indicator is a Golang module that provides a rich set of technical analysis indicators, strategies, and a framework for backtesting.
GNU Affero General Public License v3.0
524 stars 103 forks source link

Request to Add Envelope Indicator #228

Closed chentiangang closed 1 month ago

chentiangang commented 1 month ago

Hello,  I am a user of the github.com/cinar/indicator package and I greatly appreciate the work you've done so far. I would like to request the addition of a new indicator, the Envelope Indicator.  The Envelope Indicator is a technical analysis tool that consists of two moving averages—one shifted upward and one shifted downward by a fixed percentage. It is used to identify overbought and oversold conditions as well as to signal potential trend reversals.  I believe adding this indicator would be a valuable enhancement to the package.  Thank you for considering my request!  Best regards,

cinar commented 1 month ago

Thank you very much for the feature request! It definitely sounds like a good one to add. I will read more and implement it. I will very much appreciate if I can use your help to review and test it.

On Tue, Oct 8, 2024, 9:57 PM 超级大黄蜂 @.***> wrote:

Hello,  I am a user of the github.com/cinar/indicator package and I greatly appreciate the work you've done so far. I would like to request the addition of a new indicator, the Envelope Indicator.  The Envelope Indicator is a technical analysis tool that consists of two moving averages—one shifted upward and one shifted downward by a fixed percentage. It is used to identify overbought and oversold conditions as well as to signal potential trend reversals.  I believe adding this indicator would be a valuable enhancement to the package.  Thank you for considering my request!  Best regards,

— Reply to this email directly, view it on GitHub https://github.com/cinar/indicator/issues/228, or unsubscribe https://github.com/notifications/unsubscribe-auth/AANMH3FQHTHUHSLEJCT25FTZ2SZUZAVCNFSM6AAAAABPTU7UZ2VHI2DSMVQWIX3LMV43ASLTON2WKOZSGU3TINZWGE3DQMQ . You are receiving this because you are subscribed to this thread.Message ID: @.***>

chentiangang commented 1 month ago

Preliminary Implementation of Envelope Indicator

 Hello,  Thank you for your positive response to the feature request. I’ve taken the liberty to write a preliminary implementation of the Envelope Indicator as a reference. Below is the code I’ve written: 

import (
    "github.com/cinar/indicator/v2/helper"
    "github.com/cinar/indicator/v2/trend"
)

// 包络线的默认偏移百分比
const DefaultEnvelopePercentage = 10

// 包络线的默认周期
const DefaultEnvelopePeriod = 20

// Envelope 结构体表示包络线指标,它可以选择使用简单移动平均线(SMA)或指数移动平均线(EMA),并基于百分比计算上下轨。
type Envelope[T helper.Number] struct {
    // Ema 是用于计算中轨道的指数移动平均线(EMA)实例
    Ema *trend.Ema[T]

    // Sma 是用于计算中轨道的简单移动平均线(SMA)实例
    Sma *trend.Sma[T]

    // 使用 EMA 还是 SMA
    UseEma bool

    // Percentage 是上轨和下轨相对于均线的偏移百分比
    Percentage T
}

// NewEnvelope 初始化一个默认参数的包络线实例,基于TradingView默认值
func NewEnvelope[T helper.Number](useEma bool) *Envelope[T] {
    return NewEnvelopeWithPeriodAndPercentage[T](DefaultEnvelopePeriod, DefaultEnvelopePercentage, useEma)
}

// NewEnvelopeWithPeriodAndPercentage 初始化一个带有指定周期和偏移百分比的包络线实例,并选择是否使用EMA
func NewEnvelopeWithPeriodAndPercentage[T helper.Number](period int, percentage float64, useEma bool) *Envelope[T] {
    return &Envelope[T]{
        Ema:        trend.NewEmaWithPeriod[T](period),
        Sma:        trend.NewSmaWithPeriod[T](period),
        UseEma:     useEma,
        Percentage: T(percentage / 100.0), // 百分比转换为小数
    }
}

// Compute 函数接受一个价格通道,计算包络线的上下轨和中轨。
//
// 返回值:
// - 上轨道通道
// - 中轨道 (SMA 或 EMA)
// - 下轨道通道
func (e *Envelope[T]) Compute(c <-chan T) (<-chan T, <-chan T, <-chan T) {
    // 复制收盘价数据,分别用于计算上轨和下轨
    //closings := helper.Duplicate(c, 2)

    // 计算中轨: 根据 UseEma 判断使用 EMA 还是 SMA
    var middle <-chan T
    if e.UseEma {
        middle = e.Ema.Compute(c)
    } else {
        middle = e.Sma.Compute(c)
    }

    basis := helper.Duplicate(middle, 2)

    // 上边界:basis * (1 + percent / 100)
    upper := helper.MultiplyBy(basis[0], 1+e.Percentage)

    // 下边界:basis * (1 - percent / 100)
    lower := helper.MultiplyBy(basis[1], 1-e.Percentage)

    return upper, middle, lower
}

// IdlePeriod 返回包络线的初始空闲周期,在此周期内不会生成结果
func (e *Envelope[T]) IdlePeriod() int {
    if e.UseEma {
        return e.Ema.IdlePeriod()
    }
    return e.Sma.IdlePeriod()
}

  This is an initial version, and I would really appreciate any feedback or suggestions for improvement. It might also need further testing to ensure it works as expected across different datasets.  Thank you again for your support, and I look forward to collaborating on this!  Best regards, [chentiangang]

cinar commented 1 month ago

Excellent, this is great, let me move it to a branch quickly. We can also abstract out the SMA and EMA piece using the Ma interface. I'll make some quick modifications and share it with you.

cinar commented 1 month ago

I did some modifications and put the Envelope indicator to a new branch:

https://github.com/cinar/indicator/tree/issue-228

I also added tests for both SMA and EMA based Envelope also. Could you take a look and see if it is working as expected?

By the way, what strategy we can develop based on this? Something like sell when above upper, and buy when below lower?

chentiangang commented 1 month ago

Thank you for the update! I’m currently at work and a bit busy, so I will need some time before I can review whether it's working as expected. I’ll take a look as soon as I can and get back to you.  I am now preparing to monitor this indicator and send alert messages to a communication bot. I believe it can help identify overbought or oversold conditions during rapid upward or downward market trends. In the case of rapid upward trends, it can help us be more alert to potential risks. For rapid downward trends, it can help us consider potential opportunities. However, creating a fixed strategy based solely on this indicator might be challenging.

chentiangang commented 1 month ago

"Something like sell when above upper, and buy when below lower?"

You're absolutely right. At the moment, my approach is to be cautious of risk when the price is above the upper band and to consider opportunities when it's below the lower band.

chentiangang commented 1 month ago

After testing, I found the results to be as expected. Would it be possible to add an option to create an Envelope instance based on a custom percentage? Of course, I can also achieve this by assigning values to variables myself.

cinar commented 1 month ago

Absolutely, I've simplified things. There are two approaches:

You can begin by using the SMA and EMA convenience constructor functions:

envelope := trend.NewEnvelopeWithEma[float64]()
envelope.Percentage = 40

If you want to control both the period and the percentage, you can also do this:

envelope := trend.NewEnvelope(
    trend.NewSmaWithPeriod[float64](10),
   40,
)

Please let me know if this will work for your use case?

chentiangang commented 1 month ago

Thank you for the clarification and for simplifying the approach. Both methods seem very practical. I’ve already implemented one of them in my program, and it works great!  By the way, your code looks extremely elegant, and I have a lot to learn from it. I really appreciate everything you’ve done. Wishing you all the best!

cinar commented 1 month ago

You're welcome! I'm glad you find this module helpful. If you have any suggestions for improvements, please let me know. I've recently added the EnvelopeStrategy and merged the branch. A new release will be available soon.