Skip to content

Commit

Permalink
feat(indicators): create rsi indicator
Browse files Browse the repository at this point in the history
  • Loading branch information
h-varmazyar committed Jan 11, 2024
1 parent eeac38a commit 8b93541
Show file tree
Hide file tree
Showing 6 changed files with 532 additions and 240 deletions.
170 changes: 170 additions & 0 deletions services/indicators/pkg/calculator/bollinger_bands.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
package calculator

//
//import (
// "context"
// "github.com/google/uuid"
// chipmunkApi "github.com/h-varmazyar/Gate/services/chipmunk/api/proto"
// "github.com/h-varmazyar/Gate/services/chipmunk/internal/pkg/buffer"
// "github.com/h-varmazyar/Gate/services/chipmunk/internal/pkg/entity"
// log "github.com/sirupsen/logrus"
// "math"
//)
//
//type BollingerBands struct {
// id uuid.UUID
// entity.BollingerBandsConfigs
// sma *movingAverage
//}
//
//type BollingerBandsValue struct {
// UpperBand float64
// LowerBand float64
// MA float64
//}
//
//func NewBollingerBands(configs *entity.BollingerBandsConfigs) (*BollingerBands, error) {
// if err := validateBollingerBandsConfigs(configs); err != nil {
// return nil, err
// }
// return &BollingerBands{
// id: id,
// BollingerBandsConfigs: *configs,
// sma: &movingAverage{
// id: id,
// MovingAverageConfigs: entity.MovingAverageConfigs{
// Length: configs.Length,
// Source: configs.Source,
// },
// },
// }, nil
//}
//
//func (conf *BollingerBands) Calculate(ctx context.Context, candles []*chipmunkApi.Candle, values []*BollingerBandsValue) error {
// _, err := conf.sma.sma(candles)
// if err != nil {
// return err
// }
// for i := conf.Length - 1; i < len(candles); i++ {
// conf.calculateBB(candles[1+i-conf.Length : i+1])
// //variance := float64(0)
// //ma := rateLimiters[i].MovingAverages[conf.id].Simple
// //for j := 1 + i - conf.Length; j <= i; j++ {
// // sum := float64(0)
// // switch conf.Source {
// // case chipmunkApi.Source_Open:
// // sum = rateLimiters[j].Open
// // case chipmunkApi.Source_High:
// // sum = rateLimiters[j].High
// // case chipmunkApi.Source_Low:
// // sum = rateLimiters[j].Low
// // case chipmunkApi.Source_Close:
// // sum = rateLimiters[j].Close
// // case chipmunkApi.Source_OHLC4:
// // sum = (rateLimiters[j].Open + rateLimiters[j].High + rateLimiters[j].Low + rateLimiters[j].Close) / 4
// // case chipmunkApi.Source_HLC3:
// // sum = (rateLimiters[j].Low + rateLimiters[j].High + rateLimiters[j].Close) / 3
// // case chipmunkApi.Source_HL2:
// // sum = (rateLimiters[j].Low + rateLimiters[j].High) / 2
// // }
// // variance += math.Pow(ma-sum, 2)
// //}
// //variance /= float64(conf.Length)
// //
// //if rateLimiters[i] == nil {
// // log.Errorf("nil candle")
// //}
// //
// //rateLimiters[i].BollingerBands[conf.id] = &entity.BollingerBandsValue{
// // UpperBand: ma + float64(conf.Deviation)*math.Sqrt(variance),
// // LowerBand: ma - float64(conf.Deviation)*math.Sqrt(variance),
// // MA: ma,
// //}
// }
// return nil
//}
//
//func (conf *BollingerBands) UpdateLast(ctx context.Context, candles []*entity.Candle, value *BollingerBandsValue) {
// if len(candles) == 0 {
// return
// }
// first := candles[0]
// start := buffer.CandleBuffer.Before(first.MarketID.String(), first.ResolutionID.String(), first.Time, conf.Length-1)
// if len(start) == 0 {
// return
// }
//
// internalCandles := append(start, candles...)
//
// _, err := conf.sma.sma(internalCandles)
// if err != nil {
// log.WithError(err).Error("failed to calculate sma for bollinger bands")
// return
// }
//
// for i := conf.Length - 1; i < len(internalCandles); i++ {
// conf.calculateBB(candles[1+i-conf.Length : i+1])
// //variance := float64(0)
// //ma := internalCandles[i].MovingAverages[conf.id].Simple
// //for j := 1 + i - conf.Length; j <= i; j++ {
// // sum := float64(0)
// // switch conf.Source {
// // case chipmunkApi.Source_Open:
// // sum = rateLimiters[j].Open
// // case chipmunkApi.Source_High:
// // sum = rateLimiters[j].High
// // case chipmunkApi.Source_Low:
// // sum = rateLimiters[j].Low
// // case chipmunkApi.Source_Close:
// // sum = rateLimiters[j].Close
// // case chipmunkApi.Source_OHLC4:
// // sum = (rateLimiters[j].Open + rateLimiters[j].High + rateLimiters[j].Low + rateLimiters[j].Close) / 4
// // case chipmunkApi.Source_HLC3:
// // sum = (rateLimiters[j].Low + rateLimiters[j].High + rateLimiters[j].Close) / 3
// // case chipmunkApi.Source_HL2:
// // sum = (rateLimiters[j].Low + rateLimiters[j].High) / 2
// // }
// // variance += math.Pow(ma-sum, 2)
// //}
// //variance /= float64(conf.Length)
// //
// //rateLimiters[i].BollingerBands[conf.id] = &entity.BollingerBandsValue{
// // UpperBand: ma + float64(conf.Deviation)*math.Sqrt(variance),
// // LowerBand: ma - float64(conf.Deviation)*math.Sqrt(variance),
// // MA: ma,
// //}
// }
//}
//
//func (conf *BollingerBands) calculateBB(candles []*entity.Candle) {
// variance := float64(0)
// index := len(candles) - 1
// ma := candles[index].MovingAverages[conf.id].Simple
// for j := 0; j < len(candles); j++ {
// sum := float64(0)
// switch conf.Source {
// case chipmunkApi.Source_Open:
// sum = candles[j].Open
// case chipmunkApi.Source_High:
// sum = candles[j].High
// case chipmunkApi.Source_Low:
// sum = candles[j].Low
// case chipmunkApi.Source_Close:
// sum = candles[j].Close
// case chipmunkApi.Source_OHLC4:
// sum = (candles[j].Open + candles[j].High + candles[j].Low + candles[j].Close) / 4
// case chipmunkApi.Source_HLC3:
// sum = (candles[j].Low + candles[j].High + candles[j].Close) / 3
// case chipmunkApi.Source_HL2:
// sum = (candles[j].Low + candles[j].High) / 2
// }
// variance += math.Pow(ma-sum, 2)
// }
// variance /= float64(conf.Length)
//
// candles[index].BollingerBands[conf.id] = &entity.BollingerBandsValue{
// UpperBand: ma + float64(conf.Deviation)*math.Sqrt(variance),
// LowerBand: ma - float64(conf.Deviation)*math.Sqrt(variance),
// MA: ma,
// }
//}
36 changes: 36 additions & 0 deletions services/indicators/pkg/calculator/helpers.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package calculator

import chipmunkAPI "github.com/h-varmazyar/Gate/services/chipmunk/api/proto"

func cloneCandle(from *chipmunkAPI.Candle) *chipmunkAPI.Candle {
if from == nil {
return nil
}

return &chipmunkAPI.Candle{
UpdatedAt: from.UpdatedAt,
CreatedAt: from.CreatedAt,
Volume: from.Volume,
Amount: from.Amount,
Close: from.Close,
Open: from.Open,
Time: from.Time,
High: from.High,
Low: from.Low,
ID: from.ID,
MarketID: from.MarketID,
ResolutionID: from.ResolutionID,
}
}

func cloneCandles(from []*chipmunkAPI.Candle) []*chipmunkAPI.Candle {
if from == nil {
return nil
}

to := make([]*chipmunkAPI.Candle, len(from))
for i, candle := range from {
to[i] = cloneCandle(candle)
}
return to
}
107 changes: 107 additions & 0 deletions services/indicators/pkg/calculator/rsi.go
Original file line number Diff line number Diff line change
@@ -1 +1,108 @@
package calculator

import (
"context"
"github.com/google/uuid"
chipmunkAPI "github.com/h-varmazyar/Gate/services/chipmunk/api/proto"
indicatorsAPI "github.com/h-varmazyar/Gate/services/indicators/api/proto"
"time"
)

type RSIValue struct {
gain float64
loss float64
Value *float64
TimeFrame time.Time
}

type RSI struct {
id uuid.UUID
Period int
Source indicatorsAPI.Source
lastValue RSIValue
lastCandle *chipmunkAPI.Candle
}

func NewRSI(period int, source indicatorsAPI.Source) (*RSI, error) {
return &RSI{
id: uuid.New(),
Period: period,
Source: source,
}, nil
}

func (conf *RSI) Calculate(_ context.Context, candles []*chipmunkAPI.Candle, values []*RSIValue) error {
{ //first RSI
gain := float64(0)
loss := float64(0)
for i := 1; i <= conf.Period; i++ {
if change := candles[i].Close - candles[i-1].Close; change > 0 {
gain += change
} else {
loss += change
}
}
loss *= -1
gain = gain / float64(conf.Period)
loss = loss / float64(conf.Period)
rs := gain / loss
rsiValue := 100 - (100 / (1 + rs))
values[conf.Period] = &RSIValue{
gain: gain,
loss: loss,
Value: &rsiValue,
TimeFrame: time.Unix(candles[conf.Period].Time, 0),
}
conf.lastValue = RSIValue{
gain: gain,
loss: loss,
Value: &rsiValue,
TimeFrame: time.Unix(candles[conf.Period].Time, 0),
}
}

for i := 0; i < conf.Period; i++ {
values[i] = &RSIValue{
gain: 0,
loss: 0,
Value: nil,
TimeFrame: time.Unix(candles[i].Time, 0),
}
}

for i := conf.Period + 1; i < len(candles); i++ {
conf.lastCandle = cloneCandle(candles[i-1])
values[i] = conf.calculateRSIValue(candles[i])

lastValue := values[i]
conf.lastValue = *lastValue
}
return nil
}

func (conf *RSI) UpdateLast(_ context.Context, candle *chipmunkAPI.Candle, value *RSIValue) {
value = conf.calculateRSIValue(candle)
lastValue := value

conf.lastCandle = cloneCandle(candle)
conf.lastValue = *lastValue
}

func (conf *RSI) calculateRSIValue(candle *chipmunkAPI.Candle) *RSIValue {
gain, loss := float64(0), float64(0)
if change := candle.Close - conf.lastCandle.Close; change > 0 {
gain = change
} else {
loss = change
}
avgGain := (conf.lastValue.gain*float64(conf.Period-1) + gain) / float64(conf.Period)
avgLoss := (conf.lastValue.loss*float64(conf.Period-1) - loss) / float64(conf.Period)
rs := avgGain / avgLoss
rsiValue := 100 - (100 / (1 + rs))

return &RSIValue{
gain: avgGain,
loss: avgLoss,
Value: &rsiValue,
}
}
Loading

0 comments on commit 8b93541

Please sign in to comment.