Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Request to Add Envelope Indicator #228

Closed
chentiangang opened this issue Oct 9, 2024 · 10 comments · Fixed by #233
Closed

Request to Add Envelope Indicator #228

chentiangang opened this issue Oct 9, 2024 · 10 comments · Fixed by #233

Comments

@chentiangang
Copy link

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
Copy link
Owner

cinar commented Oct 9, 2024 via email

@chentiangang
Copy link
Author

chentiangang commented Oct 9, 2024

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
Copy link
Owner

cinar commented Oct 10, 2024

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
Copy link
Owner

cinar commented Oct 10, 2024

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
Copy link
Author

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
Copy link
Author

"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
Copy link
Author

chentiangang commented Oct 10, 2024

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
Copy link
Owner

cinar commented Oct 11, 2024

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
Copy link
Author

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 added a commit that referenced this issue Oct 12, 2024
# Describe Request

Envelope trend indicator and Envelope strategy are added.

Fixed #228 

# Change Type

New feature.

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

- **New Features**
- Introduced a new `Envelope` indicator and multiple new strategies,
enhancing the library's capabilities.
- Added dedicated test data in CSV format for better validation of
indicators and strategies.

- **Documentation**
- Added detailed documentation for the new `Envelope` type and its
methods.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
@cinar
Copy link
Owner

cinar commented Oct 12, 2024

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
2 participants