diff --git a/cmd/indicators/funcs.go b/cmd/indicators/funcs.go new file mode 100644 index 0000000..ce8cdf8 --- /dev/null +++ b/cmd/indicators/funcs.go @@ -0,0 +1,98 @@ +package indicators + +import ( + "fmt" + + "github.com/yasseldg/go-simple/logs/sLog" + "github.com/yasseldg/go-simple/repositorys/rMongo" + "github.com/yasseldg/go-simple/trading/tCandle" + "github.com/yasseldg/go-simple/trading/tIndicator" + "github.com/yasseldg/go-simple/trading/tInterval" +) + +func Run(mongo *rMongo.Manager) { + indicator := get("SuperTrend") + + run(indicator, mongo, "BYBIT_WLDUSDT", tInterval.Interval_1h) +} + +func get(indicator string) Indicator { + switch indicator { + case "RSI": + return tIndicator.NewRSIcandle(14) + + case "BBands": + return tIndicator.NewBBcandle(30, 2) + + case "AvgATR": + return tIndicator.NewAvgATR(14) + + case "SmATR": + return tIndicator.NewSmATR(14) + + case "ADX": + return tIndicator.NewADX(14) + + case "SuperTrend": + return tIndicator.NewSuperTrend(15, 5, false) + + default: + return nil + } +} + +// private vars + +var ( + _coll rMongo.Collection + + _iter tCandle.InterIter +) + +type Indicator interface { + Log() + Add(candle tCandle.Inter) +} + +// private methods + +func run(indicator Indicator, mongo *rMongo.Manager, symbol string, interval tInterval.Interval) { + if indicator == nil { + sLog.Info("Indicator is nil") + return + } + + err := config(mongo, symbol, interval) + if err != nil { + sLog.Info("repos.Run(): config(): %s", err) + return + } + + indicator.Log() + + for _iter.Next() { + indicator.Add(_iter.Item()) + + indicator.Log() + } +} + +func config(_mongo *rMongo.Manager, symbol string, interval tInterval.Interval) error { + + coll_name := fmt.Sprintf("%s_%s", "historic_prices", interval.String()) + + var err error + _coll, err = _mongo.GetColl("", "PP_Historic_Trades_R", symbol, coll_name) + if err != nil { + return fmt.Errorf("GetColl(): %s", err) + } + _coll.Log() + + _iter, err = tCandle.NewIter(rMongo.NewFilter(), _coll) + if err != nil { + return fmt.Errorf("tCandle.NewIter(): %s", err) + } + _iter.Log("Candles") + + return nil +} diff --git a/cmd/main.go b/cmd/main.go new file mode 100644 index 0000000..ddaf58c --- /dev/null +++ b/cmd/main.go @@ -0,0 +1,39 @@ +package main + +import ( + "github.com/yasseldg/go-simple/cmd/indicators" + "github.com/yasseldg/go-simple/cmd/repos" + + "github.com/yasseldg/go-simple/logs/sLog" + "github.com/yasseldg/go-simple/repositorys/rMongo" + "github.com/yasseldg/go-simple/types/sTime" +) + +var ( + _mongo *rMongo.Manager +) + +func Init() { + mongo := rMongo.NewManager() + _mongo = &mongo +} + +func main() { + + clean := sLog.SetByName(sLog.Zap, sLog.LevelInfo, "") + defer clean() + + Init() + + sLog.Info("Starting...") + + sTime.TimeControl(testIndicators, "Indicators") +} + +func testModel() { + repos.Run(_mongo) +} + +func testIndicators() { + indicators.Run(_mongo) +} diff --git a/cmd/repos/funcs.go b/cmd/repos/funcs.go new file mode 100644 index 0000000..cd4023e --- /dev/null +++ b/cmd/repos/funcs.go @@ -0,0 +1,95 @@ +package repos + +import ( + "fmt" + + "github.com/yasseldg/go-simple/logs/sLog" + "github.com/yasseldg/go-simple/repositorys/rMongo" + "github.com/yasseldg/go-simple/trading/tSide" +) + +var ( + _coll rMongo.Collection +) + +func Run(mongo *rMongo.Manager) { + err := config(mongo) + if err != nil { + sLog.Error("repos.Run(): config(): %s", err) + return + } + + strategie := Model{ + Uuid: "uuid", + Name: "name", + Code: "code", + Symbol: "symbol", + Side: tSide.Buy, + } + + err = load(strategie) + if err != nil { + sLog.Error("load(): %s", err) + } +} + +func config(_mongo *rMongo.Manager) error { + var err error + _coll, err = _mongo.GetColl("strategies", "WRITE", "bot", "strategies", Indexes()...) + if err != nil { + return fmt.Errorf("GetColl(): %s", err) + } + _coll.Log() + + return nil +} + +func load(strategie Model) error { + + doc, err := find(strategie) + if err != nil { + sLog.Error("find(): %s", err) + return create(strategie) + } + + sLog.Warn("strategie already exists: %s", doc.String()) + + return nil +} + +func find(strategie Model) (*Model, error) { + + filter := NewFilter().Code(strategie.Code).Symbol(strategie.Symbol).State("active").Side(strategie.Side) + + sLog.Warn("find strategie: %s", filter.String()) + + var doc Model + err := _coll.Filters(filter.Filters).FindOne(&doc) + if err != nil { + return nil, fmt.Errorf("coll.FindOne(): %s", err) + } + + sLog.Warn("found strategie: %s", doc.String()) + + return &doc, nil +} + +func create(strategie Model) error { + + sLog.Warn("create strategie: %s", strategie.String()) + + strategie.SetState("active") + + strategie.Log() + + _coll.Log() + + err := _coll.Create(&strategie) + if err != nil { + return fmt.Errorf("coll.Create(): %s", err) + } + + strategie.Log() + + return nil +} diff --git a/cmd/repos/model.go b/cmd/repos/model.go new file mode 100644 index 0000000..b32450e --- /dev/null +++ b/cmd/repos/model.go @@ -0,0 +1,87 @@ +package repos + +import ( + "fmt" + + "github.com/yasseldg/go-simple/logs/sLog" + "github.com/yasseldg/go-simple/repositorys/rFilter" + "github.com/yasseldg/go-simple/repositorys/rMongo" + "github.com/yasseldg/go-simple/repositorys/rSort" + "github.com/yasseldg/go-simple/trading/tSide" + + "github.com/yasseldg/mgm/v4" + + "go.mongodb.org/mongo-driver/bson" +) + +type Model struct { + mgm.DefaultModelDateState `bson:",inline"` + + Uuid string `bson:"uuid" json:"uuid"` + Name string `bson:"name" json:"name"` + Code string `bson:"code" json:"code"` + Symbol string `bson:"symbol" json:"symbol"` + Side tSide.Side `bson:"sd" json:"sd"` +} +type Models []Model + +// filters + +type Filters struct{ rFilter.Filters } + +func NewFilter() *Filters { + return &Filters{Filters: rMongo.NewFilter()} +} + +func (f *Filters) Uuid(uuid string) *Filters { f.Append("uuid", uuid); return f } + +func (f *Filters) Code(code string) *Filters { f.Append("code", code); return f } + +func (f *Filters) Side(side tSide.Side) *Filters { f.Int("sd", int(side)); return f } + +func (f *Filters) State(state string) *Filters { f.Append("st", state); return f } + +func (f *Filters) Symbol(symbol string) *Filters { f.Append("symbol", symbol); return f } + +// sorts + +type Sorts struct{ rSort.Sorts } + +func NewSort() *Sorts { + return &Sorts{Sorts: rMongo.NewSort()} +} + +func (s *Sorts) Fields() bson.D { + return rMongo.GetFields(NewSort().Indexes().Sorts) +} + +func (s *Sorts) UuidAsc() *Sorts { s.Asc("uuid"); return s } + +func (s *Sorts) CodeAsc() *Sorts { s.Asc("code"); return s } + +func (s *Sorts) SymbolAsc() *Sorts { s.Asc("symbol"); return s } + +func (s *Sorts) SideAsc() *Sorts { s.Asc("sd"); return s } + +func (s *Sorts) Indexes() *Sorts { + s.CodeAsc().SymbolAsc().SideAsc().UuidAsc() + return s +} + +// indexes + +func Indexes() rMongo.Indexes { + return rMongo.Indexes{rMongo.Index{Fields: NewSort().Indexes().Fields(), Unique: true}} +} + +// model methods + +// TODO + +func (m *Model) String() string { + return fmt.Sprintf("uuid: %s .. name: %s .. code: %s .. symbol: %s .. side: %s .. state: %s", m.Uuid, m.Name, m.Code, m.Symbol, m.Side, m.State) +} + +func (m *Model) Log() { + sLog.Info("Model: %s", m.String()) +} diff --git a/data/dAccu/base.go b/data/dAccu/base.go new file mode 100644 index 0000000..0709002 --- /dev/null +++ b/data/dAccu/base.go @@ -0,0 +1,67 @@ +package dAccu + +import ( + "fmt" + + "github.com/yasseldg/go-simple/logs/sLog" +) + +type Base struct { + limit int + + count int + + empty bool + err error + + save func() error +} + +func New(limit int, save func() error) *Base { + return &Base{ + limit: limit, + save: save, + } +} + +func (a *Base) String(name string) string { + return fmt.Sprintf("Accu ( %s ): count: %d .. limit: %d", name, a.count, a.limit) +} + +func (a *Base) Log(name string) { + sLog.Info(a.String(name)) +} + +func (a *Base) SetError(e error) { + a.err = e +} + +func (a Base) Error() error { + return a.err +} + +func (a *Base) Empty() bool { + return a.empty +} + +func (a *Base) Increase() { + a.count++ +} + +func (a *Base) Limit() int { + return a.limit +} + +func (a *Base) Count() int { + return a.count +} + +func (a *Base) Save() { + err := a.save() + if err != nil { + a.SetError(err) + return + } + + a.empty = true +} diff --git a/data/dAccu/inter.go b/data/dAccu/inter.go new file mode 100644 index 0000000..7a3ca26 --- /dev/null +++ b/data/dAccu/inter.go @@ -0,0 +1,16 @@ +package dAccu + +type Inter interface { + String(name string) string + Log(name string) + + SetError(e error) + Error() error + + Increase() + Limit() int + Count() int + Empty() bool + + Save() +} diff --git a/data/dIter/base.go b/data/dIter/base.go new file mode 100644 index 0000000..26773b4 --- /dev/null +++ b/data/dIter/base.go @@ -0,0 +1,52 @@ +package dIter + +import ( + "fmt" + + "github.com/yasseldg/go-simple/logs/sLog" +) + +type Base struct { + empty bool + err error +} + +func New() *Base { + return &Base{} +} + +func (a *Base) String(name string) string { + return fmt.Sprintf("Iter ( %s ): ", name) +} + +func (a *Base) Log(name string) { + sLog.Info(a.String(name)) +} + +func (b *Base) SetError(e error) { + b.err = e +} + +func (b *Base) Error() error { + return b.err +} + +func (b *Base) SetEmpty(e bool) { + b.empty = e +} + +func (b *Base) Empty() bool { + return b.empty +} + +func (b *Base) Next() bool { + if b.empty { + return false + } + + if b.err != nil { + return false + } + + return true +} diff --git a/data/dIter/inter.go b/data/dIter/inter.go new file mode 100644 index 0000000..d296fe4 --- /dev/null +++ b/data/dIter/inter.go @@ -0,0 +1,14 @@ +package dIter + +type Inter interface { + String(name string) string + Log(name string) + + SetError(e error) + Error() error + + SetEmpty(e bool) + Empty() bool + + Next() bool +} diff --git a/files/fAccu/base.go b/files/fAccu/base.go new file mode 100644 index 0000000..ba44dc1 --- /dev/null +++ b/files/fAccu/base.go @@ -0,0 +1,55 @@ +package fAccu + +import ( + "fmt" + "os" + + "github.com/yasseldg/go-simple/data/dAccu" + "github.com/yasseldg/go-simple/files/sFile" +) + +type Base struct { + dAccu.Inter + + file_path string + + is_new bool +} + +func New(file_path string, delete bool, limit int, save func() error) (*Base, error) { + if delete { + err := sFile.DeletePath(file_path) + if err != nil { + return nil, err + } + } + + b := &Base{ + file_path: file_path, + is_new: delete, + } + + b.Inter = dAccu.New(limit, save) + + return b, nil +} + +func (a *Base) FilePath() string { + return a.file_path +} + +func (a *Base) IsNew() bool { + return a.is_new +} + +func (a *Base) SetNew(b bool) { + a.is_new = b +} + +func (a *Base) OpenFile() (*os.File, error) { + f, err := os.OpenFile(a.file_path, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) + if err != nil { + return nil, fmt.Errorf("os.OpenFile( %s ): %s", a.file_path, err) + } + return f, err +} diff --git a/files/fAccu/inter.go b/files/fAccu/inter.go new file mode 100644 index 0000000..5e8b298 --- /dev/null +++ b/files/fAccu/inter.go @@ -0,0 +1,17 @@ +package fAccu + +import ( + "os" + + "github.com/yasseldg/go-simple/data/dAccu" +) + +type Inter interface { + dAccu.Inter + + FilePath() string + IsNew() bool + SetNew(bool) + + OpenFile() (*os.File, error) +} diff --git a/files/fCsv/accu.go b/files/fCsv/accu.go new file mode 100644 index 0000000..958dc7a --- /dev/null +++ b/files/fCsv/accu.go @@ -0,0 +1,99 @@ +package fCsv + +import ( + "encoding/csv" + "fmt" + "sync" + + "github.com/yasseldg/go-simple/files/fAccu" + "github.com/yasseldg/go-simple/logs/sLog" +) + +type InterAccu interface { + fAccu.Inter + + AddHeader([]string) + AddData([]string) +} + +type Accu struct { + fAccu.Inter + + mu sync.Mutex + + data [][]string +} + +func NewAccu(file_path string, delete bool, limit int) (*Accu, error) { + + accu := new(Accu) + + inter, err := fAccu.New(file_path, delete, limit, accu.save) + if err != nil { + return nil, err + } + + accu.Inter = inter + + return accu, nil +} + +func (a *Accu) AddHeader(header []string) { + a.mu.Lock() + defer a.mu.Unlock() + + if a.IsNew() { + a.data = append([][]string{header}, a.data...) + + a.SetNew(false) + } +} + +func (a *Accu) AddData(d []string) { + a.mu.Lock() + defer a.mu.Unlock() + + a.data = append(a.data, d) + + a.Increase() + + if len(a.data) >= a.Limit() { + a.Save() + } +} + +// private methods + +func (a *Accu) save() error { + if len(a.data) == 0 { + return nil + } + + err := a.write() + if err != nil { + return err + } + + a.data = [][]string{} + + return nil +} + +func (a *Accu) write() error { + f, err := a.OpenFile() + if err != nil { + return err + } + defer f.Close() + + writer := csv.NewWriter(f) + defer writer.Flush() + + err = writer.WriteAll(a.data) + if err != nil { + return fmt.Errorf("csvWriter.WriteAll(data) in ( %s ): %s", a.FilePath(), err) + } + + sLog.Info("Accu Csv: %d lines written successfully in ( %s )", len(a.data), a.FilePath()) + return nil +} diff --git a/files/fCsv/iter.go b/files/fCsv/iter.go index 5b03b53..6f1857d 100644 --- a/files/fCsv/iter.go +++ b/files/fCsv/iter.go @@ -7,8 +7,12 @@ import ( "github.com/yasseldg/go-simple/files/fIter" ) +type InterIter interface { + fIter.Inter +} + type Iter struct { - fIter.Iter + fIter.Inter reader *csv.Reader comma rune @@ -18,12 +22,14 @@ type Iter struct { } func NewIter(file_path string, limit int, comma rune) (Iter, error) { - - return Iter{Iter: fIter.NewIter(file_path, limit), comma: comma}, nil + return Iter{ + Inter: fIter.New(file_path, limit), + comma: comma, + }, nil } func (iter *Iter) Next() bool { - if !iter.Iter.Next() { + if !iter.Inter.Next() { return false } @@ -40,7 +46,7 @@ func (iter *Iter) Next() bool { } if len(items) == 0 { - iter.Iter.SetEmpty(true) + iter.Inter.SetEmpty(true) iter.CloseFile() return false } diff --git a/files/fIter/base.go b/files/fIter/base.go new file mode 100644 index 0000000..5ca7db5 --- /dev/null +++ b/files/fIter/base.go @@ -0,0 +1,53 @@ +package fIter + +import ( + "fmt" + "os" + + "github.com/yasseldg/go-simple/data/dIter" + "github.com/yasseldg/go-simple/logs/sLog" +) + +// Base + +type Base struct { + dIter.Inter + + file *os.File + + file_path string + limit int +} + +func New(file_path string, limit int) *Base { + return &Base{ + Inter: dIter.New(), + file_path: file_path, + limit: limit, + } +} + +func (i Base) File() *os.File { + return i.file +} + +func (iter *Base) OpenFile() error { + file, err := os.Open(iter.file_path) + if err != nil { + return fmt.Errorf("os.Open( %s ): %s", iter.file_path, err) + } + + sLog.Info("OpenFile: %s", iter.file_path) + + iter.file = file + return nil +} + +func (iter *Base) CloseFile() { + sLog.Info("CloseFile: %s ", iter.file_path) + iter.file.Close() +} + +func (iter *Base) Limit() int { + return iter.limit +} diff --git a/files/fIter/inter.go b/files/fIter/inter.go new file mode 100644 index 0000000..11a30c8 --- /dev/null +++ b/files/fIter/inter.go @@ -0,0 +1,16 @@ +package fIter + +import ( + "os" + + "github.com/yasseldg/go-simple/data/dIter" +) + +type Inter interface { + dIter.Inter + + File() *os.File + Limit() int + OpenFile() error + CloseFile() +} diff --git a/files/fIter/iter.go b/files/fIter/iter.go deleted file mode 100644 index 4a4678c..0000000 --- a/files/fIter/iter.go +++ /dev/null @@ -1,77 +0,0 @@ -package fIter - -import ( - "fmt" - "os" - - "github.com/yasseldg/go-simple/logs/sLog" -) - -// Iter - -type Iter struct { - file *os.File - - file_path string - limit int - - empty bool - err error -} - -func NewIter(file_path string, limit int) Iter { - return Iter{file_path: file_path, limit: limit} -} - -func (i Iter) Next() bool { - if i.empty { - return false - } - - if i.err != nil { - return false - } - - return true -} - -func (i *Iter) SetError(e error) { - i.err = e -} - -func (i Iter) Error() error { - return i.err -} - -func (i *Iter) SetEmpty(e bool) { - i.empty = e -} - -func (i Iter) Empty() bool { - return i.empty -} - -func (i Iter) File() *os.File { - return i.file -} - -func (iter *Iter) OpenFile() error { - file, err := os.Open(iter.file_path) - if err != nil { - return fmt.Errorf("os.Open( %s ): %s", iter.file_path, err) - } - - sLog.Info("OpenFile: %s", iter.file_path) - - iter.file = file - return nil -} - -func (iter *Iter) CloseFile() { - sLog.Info("CloseFile: %s ", iter.file_path) - iter.file.Close() -} - -func (iter *Iter) Limit() int { - return iter.limit -} diff --git a/files/fJson/accu.go b/files/fJson/accu.go new file mode 100644 index 0000000..1fcfdfd --- /dev/null +++ b/files/fJson/accu.go @@ -0,0 +1,77 @@ +package fJson + +import ( + "encoding/json" + "fmt" + + "github.com/yasseldg/go-simple/files/fAccu" + "github.com/yasseldg/go-simple/logs/sLog" +) + +type InterAccu interface { + fAccu.Inter +} + +type Accu struct { + fAccu.Inter + + data func() any +} + +func NewAccu(file_path string, delete bool, limit int, data func() any) (*Accu, error) { + + accu := new(Accu) + + inter, err := fAccu.New(file_path, delete, limit, accu.save) + if err != nil { + return nil, err + } + + accu.Inter = inter + accu.data = data + + return accu, nil +} + +func (a *Accu) save() error { + if a.data == nil { + return nil + } + + err := a.write() + if err != nil { + return err + } + + // FIX + // a.data = nil + + return nil +} + +func (a *Accu) write() error { + data, err := json.MarshalIndent(a.data(), "", " ") + if err != nil { + return fmt.Errorf("json.MarshalIndent(objects): %s", err) + } + + file, err := a.OpenFile() + if err != nil { + return err + } + defer file.Close() + + b, err := file.Write(data) + if err != nil { + return fmt.Errorf("file.Write(data) in ( %s ): %s", a.FilePath(), err) + } + + bb, err := file.WriteString("\n") + if err != nil { + return fmt.Errorf("ExportJson: file.WriteString(): path: %s : %s", a.FilePath(), err) + + } + + sLog.Info("Accu Json: %d bytes written successfully in ( %s )", (b + bb), a.FilePath()) + return nil +} diff --git a/files/sFile/file.go b/files/sFile/file.go new file mode 100644 index 0000000..c5dd5c3 --- /dev/null +++ b/files/sFile/file.go @@ -0,0 +1,59 @@ +package sFile + +import ( + "fmt" + "os" +) + +func DeletePath(file_path string) error { + + exist, err := ExistingPath(file_path) + if err != nil { + return err + } + + if !exist { + return nil + } + + err = os.Remove(file_path) + if err != nil { + err = fmt.Errorf("os.Remove( %s ): %s", file_path, err) + } + + return err +} + +func ExistingPath(file_path string) (bool, error) { + + _, err := os.Stat(file_path) + if err == nil { + return true, nil + } + + if os.IsNotExist(err) { + return false, nil + } + + return false, err +} + +func GetDir(dir_path string) (err error) { + _, err = os.Stat(dir_path) + if err == nil { + // Directory exists + return nil + } + + if os.IsNotExist(err) { + // File or directory does not exist + return os.MkdirAll(dir_path, mode(0755, os.ModeDir)) + } + + return fmt.Errorf("os.Stat( %q ): %s", dir_path, err) +} + +// mode returns the file mode masked by the umask +func mode(mode, umask os.FileMode) os.FileMode { + return mode & ^umask +} diff --git a/repositorys/rAccu/accu.go b/repositorys/rAccu/accu.go deleted file mode 100644 index e30a023..0000000 --- a/repositorys/rAccu/accu.go +++ /dev/null @@ -1,90 +0,0 @@ -package rAccu - -import ( - "github.com/yasseldg/go-simple/logs/sLog" - "github.com/yasseldg/go-simple/repositorys/rMongo" - "github.com/yasseldg/mgm/v4" -) - -// Accu - -type Accu struct { - coll rMongo.Collection - limit int - - items []mgm.Model - - count int - - empty bool - err error -} - -func New(coll rMongo.Collection, limit int) Accu { - return Accu{ - coll: coll, - limit: limit, - } -} - -func (iter *Accu) Log(name string) { - sLog.Info("Accu ( %s ): %d", name, iter.Count()) -} - -func (iter *Accu) Add(model mgm.Model) { - if model == nil { - return - } - - iter.items = append(iter.items, model) - - if len(iter.items) >= iter.Limit() { - iter.save() - } -} - -func (iter *Accu) Save() { - iter.save() -} - -func (i *Accu) SetError(e error) { - i.err = e -} - -func (i Accu) Error() error { - return i.err -} - -func (i *Accu) SetEmpty(e bool) { - i.empty = e -} - -func (i Accu) Empty() bool { - return i.empty -} - -func (i Accu) Limit() int { - return i.limit -} - -func (i Accu) Count() int { - return i.count -} - -// private methods - -func (iter *Accu) save() { - if len(iter.items) == 0 { - return - } - - err := rMongo.CreateMany(iter.items, iter.coll) - if err != nil { - iter.SetError(err) - return - } - - iter.count += len(iter.items) - - iter.items = []mgm.Model{} -} diff --git a/repositorys/rAccu/base.go b/repositorys/rAccu/base.go new file mode 100644 index 0000000..256bf2f --- /dev/null +++ b/repositorys/rAccu/base.go @@ -0,0 +1,72 @@ +package rAccu + +import ( + "fmt" + + "github.com/yasseldg/go-simple/data/dAccu" + "github.com/yasseldg/go-simple/logs/sLog" + "github.com/yasseldg/go-simple/repositorys/rMongo" + + "github.com/yasseldg/mgm/v4" +) + +type Base struct { + dAccu.Inter + + coll rMongo.Collection + + items []mgm.Model +} + +func New(coll rMongo.Collection, limit int) *Base { + + b := &Base{ + coll: coll, + } + + b.Inter = dAccu.New(limit, b.save) + + return b +} + +func (a *Base) String(name string) string { + return fmt.Sprintf("%s .. items: %d .. %s", a.Inter.String(name), len(a.items), a.coll.String()) +} + +func (a *Base) Log(name string) { + sLog.Info(a.String(name)) +} + +func (a *Base) Clone() Inter { + return New(a.coll, a.Limit()) +} + +func (a *Base) Add(model mgm.Model) { + if model == nil { + return + } + + a.items = append(a.items, model) + a.Increase() + + if len(a.items) >= a.Limit() { + a.Save() + } +} + +// private methods + +func (a *Base) save() error { + if len(a.items) == 0 { + return nil + } + + err := rMongo.CreateMany(a.items, a.coll) + if err != nil { + return fmt.Errorf("rMongo.CreateMany(): %s", err) + } + + a.items = []mgm.Model{} + + return nil +} diff --git a/repositorys/rAccu/inter.go b/repositorys/rAccu/inter.go new file mode 100644 index 0000000..3a69b78 --- /dev/null +++ b/repositorys/rAccu/inter.go @@ -0,0 +1,17 @@ +package rAccu + +import ( + "github.com/yasseldg/go-simple/data/dAccu" + + "github.com/yasseldg/mgm/v4" +) + +type Inter interface { + dAccu.Inter + + Log(name string) + + Clone() Inter + + Add(model mgm.Model) +} diff --git a/repositorys/rFilter/inter.go b/repositorys/rFilter/inter.go index 9206945..efa1fd4 100644 --- a/repositorys/rFilter/inter.go +++ b/repositorys/rFilter/inter.go @@ -1,12 +1,16 @@ package rFilter type Inter interface { + InterComp + Clone() Inter String() string Log(msg string) Append(key string, value interface{}) +} +type InterComp interface { In(key string, values ...interface{}) Nin(key string, values ...interface{}) diff --git a/repositorys/rFilter/objectId.go b/repositorys/rFilter/objectId.go index d7ccbfe..49e5c76 100644 --- a/repositorys/rFilter/objectId.go +++ b/repositorys/rFilter/objectId.go @@ -16,3 +16,8 @@ func (f *Filters) ObjectId_in(field string, values ...sId.ObjectId) *Filters { f.Inter.In(field, values) return f } + +func (f *Filters) ObjectId_gt(field string, value interface{}) *Filters { + f.Inter.Gt(field, value) + return f +} diff --git a/repositorys/rIter/base.go b/repositorys/rIter/base.go new file mode 100644 index 0000000..a524ca5 --- /dev/null +++ b/repositorys/rIter/base.go @@ -0,0 +1,42 @@ +package rIter + +import ( + "fmt" + + "github.com/yasseldg/go-simple/data/dIter" + "github.com/yasseldg/go-simple/logs/sLog" + "github.com/yasseldg/go-simple/repositorys/rFilter" + "github.com/yasseldg/go-simple/repositorys/rMongo" +) + +// Base +type Base struct { + dIter.Inter + + coll rMongo.Collection + filter rFilter.Filters +} + +func New(filter rFilter.Filters, coll rMongo.Collection) *Base { + return &Base{ + Inter: dIter.New(), + coll: coll, + filter: filter, + } +} + +func (i *Base) String(name string) string { + return fmt.Sprintf("%s coll: %s .. filter: %s", i.Inter.String(name), i.coll.String(), i.filter.String()) +} + +func (i *Base) Log(name string) { + sLog.Info(i.String(name)) +} + +func (i *Base) Coll() *rMongo.Collection { + return &i.coll +} + +func (i *Base) Filter() rFilter.Filters { + return *i.filter.Clone() +} diff --git a/repositorys/rIter/inter.go b/repositorys/rIter/inter.go new file mode 100644 index 0000000..4729c01 --- /dev/null +++ b/repositorys/rIter/inter.go @@ -0,0 +1,14 @@ +package rIter + +import ( + "github.com/yasseldg/go-simple/data/dIter" + "github.com/yasseldg/go-simple/repositorys/rFilter" + "github.com/yasseldg/go-simple/repositorys/rMongo" +) + +type Inter interface { + dIter.Inter + + Coll() *rMongo.Collection + Filter() rFilter.Filters +} diff --git a/repositorys/rIter/iter.go b/repositorys/rIter/iter.go deleted file mode 100644 index d898855..0000000 --- a/repositorys/rIter/iter.go +++ /dev/null @@ -1,59 +0,0 @@ -package rIter - -import ( - "github.com/yasseldg/go-simple/repositorys/rFilter" - "github.com/yasseldg/go-simple/repositorys/rMongo" -) - -// Iter - -type Iter struct { - coll rMongo.Collection - filter rFilter.Filters - - empty bool - err error -} - -func New(filter rFilter.Filters, coll rMongo.Collection) Iter { - return Iter{ - coll: coll, - filter: filter, - } -} - -func (i Iter) Coll() *rMongo.Collection { - return &i.coll -} - -func (i Iter) Next() bool { - if i.empty { - return false - } - - if i.err != nil { - return false - } - - return true -} - -func (i *Iter) SetError(e error) { - i.err = e -} - -func (i Iter) Error() error { - return i.err -} - -func (i *Iter) SetEmpty(e bool) { - i.empty = e -} - -func (i Iter) Empty() bool { - return i.empty -} - -func (i Iter) Filter() rFilter.Filters { - return *i.filter.Clone() -} diff --git a/repositorys/rMongo/collection.go b/repositorys/rMongo/collection.go index 485265b..e57e7f9 100644 --- a/repositorys/rMongo/collection.go +++ b/repositorys/rMongo/collection.go @@ -2,6 +2,7 @@ package rMongo import ( "context" + "fmt" "github.com/yasseldg/go-simple/logs/sLog" "github.com/yasseldg/go-simple/repositorys/rFilter" @@ -39,8 +40,12 @@ func (c Collection) Mgm() *mgm.Collection { return c.collection } +func (c Collection) String() string { + return fmt.Sprintf("coll: %s .. %s", c.conn, c.prefix) +} + func (c Collection) Log() { - sLog.Info("Mongo Collection: %s .. %s \n", c.conn, c.prefix) + sLog.Info("Mongo: %s", c.String()) } func (c *Collection) Drop(indexes ...Index) error { diff --git a/repositorys/rSort/sorts.go b/repositorys/rSort/sorts.go index 3969dca..73effbe 100644 --- a/repositorys/rSort/sorts.go +++ b/repositorys/rSort/sorts.go @@ -12,6 +12,11 @@ func (f *Sorts) Clone() *Sorts { return &Sorts{Inter: f.Inter.Clone()} } +func (s *Sorts) IdAsc() *Sorts { + s.Inter.Asc("_id") + return s +} + func (s *Sorts) TsAsc() *Sorts { s.Inter.Asc("ts") return s diff --git a/trading/tCandle/accu.go b/trading/tCandle/accu.go index 819310a..9933788 100644 --- a/trading/tCandle/accu.go +++ b/trading/tCandle/accu.go @@ -8,7 +8,7 @@ import ( ) type Accu struct { - rAccu.Accu + rAccu.Inter } type mgmCandle struct { @@ -19,9 +19,11 @@ type mgmCandle struct { func NewAccu(coll rMongo.Collection, limit int) (Accu, error) { - return Accu{Accu: rAccu.New(coll, limit)}, nil + return Accu{ + Inter: rAccu.New(coll, limit), + }, nil } func (iter *Accu) Add(candle *Candle) { - iter.Accu.Add(&mgmCandle{Candle: *candle}) + iter.Inter.Add(&mgmCandle{Candle: *candle}) } diff --git a/trading/tCandle/base.go b/trading/tCandle/base.go new file mode 100644 index 0000000..9a191cf --- /dev/null +++ b/trading/tCandle/base.go @@ -0,0 +1,88 @@ +package tCandle + +import ( + "fmt" + "math" + + "github.com/yasseldg/go-simple/logs/sLog" + "github.com/yasseldg/go-simple/types/sFloats" + "github.com/yasseldg/go-simple/types/sTime" +) + +type OHLC struct { + M_open float64 `bson:"o" json:"o"` + M_high float64 `bson:"h" json:"h"` + M_low float64 `bson:"l" json:"l"` + M_close float64 `bson:"c" json:"c"` +} + +type OHLCV struct { + OHLC `bson:",inline"` + M_volume float64 `bson:"v" json:"v"` +} + +type Candle struct { + OHLCV `bson:",inline"` + M_ts int64 `bson:"ts" json:"ts"` +} +type Candles []*Candle + +func New(ts int64, open, high, low, close, volume float64) *Candle { + return &Candle{ + M_ts: ts, + OHLCV: OHLCV{ + OHLC: OHLC{ + M_open: open, + M_high: high, + M_low: low, + M_close: close}, + M_volume: volume}, + } +} + +func (b *OHLC) String(prec int) string { + return fmt.Sprintf("o: %.*f .. h: %.*f .. l: %.*f .. c: %.*f", prec, b.M_open, prec, b.M_high, prec, b.M_low, prec, b.M_close) +} + +func (b *OHLCV) String(prec int) string { + return fmt.Sprintf("%s .. v: %.*f", b.OHLC.String(prec), prec, b.M_volume) +} + +func (b *Candle) String(prec int) string { + return fmt.Sprintf("%s .. %s", sTime.ForLog(b.M_ts, 0), b.OHLCV.String(prec)) +} + +func (b *Candle) Log(prec int) { + sLog.Info("Candle: %s", b.String(prec)) +} + +func (b *Candle) Ts() int64 { + return b.M_ts +} + +func (b *Candle) Open() float64 { + return b.M_open +} + +func (b *Candle) High() float64 { + return b.M_high +} + +func (b *Candle) Low() float64 { + return b.M_low +} + +func (b *Candle) Close() float64 { + return b.M_close +} + +func (b *Candle) Volume() float64 { + return b.M_volume +} + +func (b *Candle) LogReturn() float64 { + if b.Close() == 0 || b.Open() == 0 { + return 0 + } + return sFloats.GetValid(math.Log(b.Close() / b.Open())) +} diff --git a/trading/tCandle/fanout.go b/trading/tCandle/fanout.go index b2dc9ed..df3b72c 100644 --- a/trading/tCandle/fanout.go +++ b/trading/tCandle/fanout.go @@ -8,18 +8,18 @@ import ( ) type FanOut struct { - in <-chan Candle - outs map[string]chan<- Candle + in <-chan Inter + outs map[string]chan<- Inter active bool done chan bool } type FanOuts []*FanOut -func NewFanOut(in <-chan Candle) FanOut { +func NewFanOut(in <-chan Inter) FanOut { return FanOut{ in: in, - outs: make(map[string]chan<- Candle), + outs: make(map[string]chan<- Inter), done: make(chan bool), } } @@ -31,7 +31,7 @@ func (f *FanOut) Log() { } } -func (f *FanOut) Add(topic string, out chan<- Candle) error { +func (f *FanOut) Add(topic string, out chan<- Inter) error { if _, ok := f.outs[topic]; ok { return fmt.Errorf("topic %s already exists", topic) } diff --git a/trading/tCandle/inter.go b/trading/tCandle/inter.go new file mode 100644 index 0000000..5e6b4a5 --- /dev/null +++ b/trading/tCandle/inter.go @@ -0,0 +1,15 @@ +package tCandle + +type Inter interface { + String(prec int) string + Log(prec int) + + Ts() int64 + Open() float64 + High() float64 + Low() float64 + Close() float64 + Volume() float64 + + LogReturn() float64 +} diff --git a/trading/tCandle/iter.go b/trading/tCandle/iter.go index b559ef2..9924e5e 100644 --- a/trading/tCandle/iter.go +++ b/trading/tCandle/iter.go @@ -3,31 +3,47 @@ package tCandle import ( "fmt" + "github.com/yasseldg/go-simple/logs/sLog" "github.com/yasseldg/go-simple/repositorys/rFilter" "github.com/yasseldg/go-simple/repositorys/rIter" "github.com/yasseldg/go-simple/repositorys/rMongo" ) +type InterIter interface { + rIter.Inter + + Item() Inter + SetNextTs(int64) +} + type Iter struct { - rIter.Iter + rIter.Inter next_ts int64 - item Candle + item Inter items Candles } -func NewIter(filter rFilter.Filters, coll rMongo.Collection) (Iter, error) { +func NewIter(filter rFilter.Filters, coll rMongo.Collection) (*Iter, error) { + + return &Iter{Inter: rIter.New(filter, coll)}, nil +} + +func (iter *Iter) String(name string) string { + return fmt.Sprintf("%s next_ts: %d", iter.Inter.String(name), iter.next_ts) +} - return Iter{Iter: rIter.New(filter, coll)}, nil +func (iter *Iter) Log(name string) { + sLog.Info(iter.String(name)) } -func (iter *Iter) Item() Candle { +func (iter *Iter) Item() Inter { return iter.item } func (iter *Iter) Next() bool { - if !iter.Iter.Next() { + if !iter.Inter.Next() { return false } @@ -57,7 +73,7 @@ func (iter *Iter) Next() bool { // sLog.Warn("next: items: %d", items[0].UnixTs) iter.items = items - iter.next_ts = items[len(items)-1].Ts + 1 + iter.next_ts = items[len(items)-1].Ts() + 1 return iter.Next() } diff --git a/trading/tCandle/methods.go b/trading/tCandle/methods.go deleted file mode 100644 index 7f56b84..0000000 --- a/trading/tCandle/methods.go +++ /dev/null @@ -1,28 +0,0 @@ -package tCandle - -import ( - "fmt" - "math" - - "github.com/yasseldg/go-simple/types/sFloats" - "github.com/yasseldg/go-simple/types/sTime" -) - -func (c Candle) LogReturn() float64 { - if c.Close == 0 || c.Open == 0 { - return 0 - } - return sFloats.GetValid(math.Log(c.Close / c.Open)) -} - -func (o OHLC) String(prec int) string { - return fmt.Sprintf("o: %.*f .. h: %.*f .. l: %.*f .. c: %.*f", prec, o.Open, prec, o.High, prec, o.Low, prec, o.Close) -} - -func (o OHLCV) String(prec int) string { - return fmt.Sprintf("%s .. v: %.*f", o.OHLC.String(prec), prec, o.Volume) -} - -func (c Candle) String(prec int) string { - return fmt.Sprintf("Candle: %s .. %s", sTime.ForLog(c.Ts, 0), c.OHLCV.String(prec)) -} diff --git a/trading/tCandle/methods_test.go b/trading/tCandle/methods_test.go deleted file mode 100644 index 8bb035d..0000000 --- a/trading/tCandle/methods_test.go +++ /dev/null @@ -1,82 +0,0 @@ -package tCandle - -import ( - "math" - "testing" -) - -func TestCandle_LogReturn(t *testing.T) { - tests := []struct { - name string - c Candle - want float64 - }{ - {"Zero Open and Close", Candle{OHLCV: OHLCV{OHLC: OHLC{Open: 0, Close: 0}}}, 0}, - {"Non-Zero Open and Close", Candle{OHLCV: OHLCV{OHLC: OHLC{Open: 1, Close: 2}}}, 0.693147}, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if got := tt.c.LogReturn(); math.Abs(got-tt.want) > 1e-6 { - t.Errorf("LogReturn() = %v, want %v", got, tt.want) - } - }) - } -} - -func TestOHLC_String(t *testing.T) { - tests := []struct { - name string - o OHLC - prec int - want string - }{ - {"Test OHLC String", OHLC{Open: 1.23, High: 2.34, Low: 0.12, Close: 1.23}, 2, "o: 1.23 .. h: 2.34 .. l: 0.12 .. c: 1.23"}, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if got := tt.o.String(tt.prec); got != tt.want { - t.Errorf("String() = %v, want %v", got, tt.want) - } - }) - } -} - -func TestOHLCV_String(t *testing.T) { - tests := []struct { - name string - o OHLCV - prec int - want string - }{ - {"Test OHLCV String", OHLCV{OHLC: OHLC{Open: 1.23, High: 2.34, Low: 0.12, Close: 1.23}, Volume: 123.45}, 2, "o: 1.23 .. h: 2.34 .. l: 0.12 .. c: 1.23 .. v: 123.45"}, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if got := tt.o.String(tt.prec); got != tt.want { - t.Errorf("String() = %v, want %v", got, tt.want) - } - }) - } -} - -func TestCandle_String(t *testing.T) { - tests := []struct { - name string - c Candle - prec int - want string - }{ - {"Test Candle String", Candle{Ts: 1625140800, OHLCV: OHLCV{OHLC: OHLC{Open: 1.23, High: 2.34, Low: 0.12, Close: 1.23}, Volume: 123.45}}, 2, "Candle: 1625140800 ( 2021.07.01 12:00:00 ) .. o: 1.23 .. h: 2.34 .. l: 0.12 .. c: 1.23 .. v: 123.45"}, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if got := tt.c.String(tt.prec); got != tt.want { - t.Errorf("String() = %v, want %v", got, tt.want) - } - }) - } -} diff --git a/trading/tCandle/models.go b/trading/tCandle/models.go deleted file mode 100644 index d012042..0000000 --- a/trading/tCandle/models.go +++ /dev/null @@ -1,44 +0,0 @@ -package tCandle - -import ( - "github.com/yasseldg/go-simple/logs/sLog" - "github.com/yasseldg/go-simple/types/sTime" -) - -type OHLC struct { - Open float64 `bson:"o" json:"o"` - High float64 `bson:"h" json:"h"` - Low float64 `bson:"l" json:"l"` - Close float64 `bson:"c" json:"c"` -} -type OHLCs []OHLC - -type OHLCV struct { - OHLC `bson:",inline"` - Volume float64 `bson:"v" json:"v"` -} -type OHLCVs []OHLCV - -type Candle struct { - Ts int64 `bson:"ts" json:"ts"` - OHLCV `bson:",inline"` -} -type Candles []Candle - -func New(ts int64, open, high, low, close, volume float64) *Candle { - return &Candle{ - Ts: ts, - OHLCV: OHLCV{ - OHLC: OHLC{ - Open: open, - High: high, - Low: low, - Close: close}, - Volume: volume}, - } -} - -func (c Candle) Log() { - sLog.Info("Candle: %s .. o: %f .. h: %f .. l: %f .. c: %f .. v: %f", - sTime.ForLog(c.Ts, 0), c.Open, c.High, c.Low, c.Close, c.Volume) -} diff --git a/trading/tCandle/models_test.go b/trading/tCandle/models_test.go deleted file mode 100644 index c248b43..0000000 --- a/trading/tCandle/models_test.go +++ /dev/null @@ -1,41 +0,0 @@ -package tCandle - -import ( - "testing" -) - -func TestOHLC(t *testing.T) { - ohlc := OHLC{Open: 1.0, High: 2.0, Low: 0.5, Close: 1.5} - - if ohlc.Open != 1.0 { - t.Errorf("Expected Open to be 1.0, got %f", ohlc.Open) - } - - if ohlc.High != 2.0 { - t.Errorf("Expected High to be 2.0, got %f", ohlc.High) - } - - if ohlc.Low != 0.5 { - t.Errorf("Expected Low to be 0.5, got %f", ohlc.Low) - } - - if ohlc.Close != 1.5 { - t.Errorf("Expected Close to be 1.5, got %f", ohlc.Close) - } -} - -func TestOHLCV(t *testing.T) { - ohlcv := OHLCV{OHLC: OHLC{Open: 1.0, High: 2.0, Low: 0.5, Close: 1.5}, Volume: 1000.0} - - if ohlcv.Volume != 1000.0 { - t.Errorf("Expected Volume to be 1000.0, got %f", ohlcv.Volume) - } -} - -func TestCandle(t *testing.T) { - candle := Candle{Ts: 123456789, OHLCV: OHLCV{OHLC: OHLC{Open: 1.0, High: 2.0, Low: 0.5, Close: 1.5}, Volume: 1000.0}} - - if candle.Ts != 123456789 { - t.Errorf("Expected Ts to be 123456789, got %d", candle.Ts) - } -} diff --git a/trading/tIndicator/adx.go b/trading/tIndicator/adx.go index cb9bcf2..aa83a5e 100644 --- a/trading/tIndicator/adx.go +++ b/trading/tIndicator/adx.go @@ -1,18 +1,25 @@ package tIndicator import ( + "fmt" "math" + "sync" "github.com/yasseldg/go-simple/logs/sLog" "github.com/yasseldg/go-simple/trading/tCandle" "github.com/yasseldg/go-simple/types/sFloats" - "github.com/yasseldg/go-simple/types/sTime" ) +type InterADX interface { + InterATR +} + // El Índice de Movimiento Direccional Promedio (ADX, por sus siglas en inglés) type ADX struct { - ATR + InterATR + + mu sync.Mutex plusDMs sFloats.SmoothedAverage minusDMs sFloats.SmoothedAverage @@ -23,7 +30,7 @@ type ADX struct { func NewADX(period int) *ADX { return &ADX{ - ATR: *NewATR(period), + InterATR: NewSmATR(period), plusDMs: *sFloats.NewSmoothedAverage(period), minusDMs: *sFloats.NewSmoothedAverage(period), @@ -33,26 +40,32 @@ func NewADX(period int) *ADX { } } +func (adx *ADX) String() string { + return fmt.Sprintf("%s .. pDMs: %f .. mDMs: %f .. adx: %f .. %s", + adx.InterATR.String(), adx.plusDMs.Value(), adx.minusDMs.Value(), adx.value.Value(), adx.historic.String()) +} + func (adx *ADX) Log() { adx.mu.Lock() defer adx.mu.Unlock() - sLog.Info("ADX %d: %s .. TRs: %f .. pDMs: %f .. mDMs: %f .. adx: %f .. %s", adx.c, sTime.ForLog(adx.prev.Ts, 0), adx.trs.Value(), adx.plusDMs.Value(), adx.minusDMs.Value(), adx.value.Value(), adx.historic.String()) + sLog.Info("ADX: %s", adx.String()) } -func (adx *ADX) Add(candle tCandle.Candle) { +func (adx *ADX) Add(candle tCandle.Inter) { adx.mu.Lock() defer adx.mu.Unlock() - if adx.prev.Close > 0 { - adx.calcDMs(candle) + if adx.Prev().Close() <= 0 { + adx.InterATR.Add(candle) + return + } - adx.ATR.add(candle) + adx.calcDMs(candle) - adx.calcDIs() - } + adx.InterATR.Add(candle) - adx.prev = candle + adx.calcDIs() } func (adx *ADX) Value() float64 { @@ -69,10 +82,10 @@ func (adx *ADX) Historic() float64 { return adx.historic.Calc() } -func (adx *ADX) calcDMs(candle tCandle.Candle) { +func (adx *ADX) calcDMs(candle tCandle.Inter) { - plusDM := candle.High - adx.prev.High - minusDM := adx.prev.Low - candle.Low + plusDM := candle.High() - adx.Prev().High() + minusDM := adx.Prev().Low() - candle.Low() if plusDM <= minusDM || plusDM <= 0 { plusDM = 0 @@ -87,12 +100,12 @@ func (adx *ADX) calcDMs(candle tCandle.Candle) { func (adx *ADX) calcDIs() { - if !adx.trs.Filled() || !adx.plusDMs.Filled() || !adx.minusDMs.Filled() { + if !adx.InterATR.Filled() || !adx.plusDMs.Filled() || !adx.minusDMs.Filled() { return } - plusDi := (adx.plusDMs.Value() / adx.trs.Value()) * 100 - minusDi := (adx.minusDMs.Value() / adx.trs.Value()) * 100 + plusDi := (adx.plusDMs.Value() / adx.InterATR.Get()) * 100 + minusDi := (adx.minusDMs.Value() / adx.InterATR.Get()) * 100 dx := (math.Abs(plusDi-minusDi) / math.Abs(plusDi+minusDi)) * 100 diff --git a/trading/tIndicator/atr.go b/trading/tIndicator/atr.go index 64dadd3..09731c7 100644 --- a/trading/tIndicator/atr.go +++ b/trading/tIndicator/atr.go @@ -1,63 +1,58 @@ package tIndicator import ( - "math" + "fmt" "sync" - "github.com/yasseldg/go-simple/logs/sLog" "github.com/yasseldg/go-simple/trading/tCandle" - "github.com/yasseldg/go-simple/types/sFloats" "github.com/yasseldg/go-simple/types/sTime" ) // ATR (Average True Range) -type ATR struct { - mu sync.Mutex +type InterATR interface { + String() string + Log() + + Count() int + Periods() int + Filled() bool + Get() float64 + Prev() tCandle.Inter - prev tCandle.Candle + Add(candle tCandle.Inter) +} - trs sFloats.SmoothedAverage +type BaseATR struct { + mu sync.Mutex + + prev tCandle.Inter c int } -func NewATR(period int) *ATR { - return &ATR{ +func newBaseATR() *BaseATR { + return &BaseATR{ mu: sync.Mutex{}, - trs: *sFloats.NewSmoothedAverage(period), + prev: new(tCandle.Candle), } } -func (atr *ATR) Log() { - atr.mu.Lock() - defer atr.mu.Unlock() - - sLog.Info("ATR %d: %s .. %f", atr.c, sTime.ForLog(atr.prev.Ts, 0), atr.trs.Value()) +func (atr *BaseATR) String() string { + return fmt.Sprintf("%d: %s", atr.c, sTime.ForLog(atr.prev.Ts(), 0)) } -// Add adds a candle to the ATR -func (atr *ATR) Add(candle tCandle.Candle) { +func (atr *BaseATR) Count() int { atr.mu.Lock() defer atr.mu.Unlock() - atr.add(candle) -} - -func (atr *ATR) add(candle tCandle.Candle) { - atr.c++ - - if atr.prev.Close > 0 { - atr.trs.AddPos(max(candle.High-candle.Low, math.Abs(candle.High-atr.prev.Close), math.Abs(candle.Low-atr.prev.Close))) - } - - atr.prev = candle + return atr.c } -func (atr *ATR) Get() float64 { +func (atr *BaseATR) Prev() tCandle.Inter { atr.mu.Lock() defer atr.mu.Unlock() - return atr.trs.Value() + return atr.prev } diff --git a/trading/tIndicator/atrAvg.go b/trading/tIndicator/atrAvg.go new file mode 100644 index 0000000..ff4889a --- /dev/null +++ b/trading/tIndicator/atrAvg.go @@ -0,0 +1,73 @@ +package tIndicator + +import ( + "fmt" + "math" + + "github.com/yasseldg/go-simple/logs/sLog" + "github.com/yasseldg/go-simple/trading/tCandle" + "github.com/yasseldg/go-simple/types/sFloats" +) + +type AvgATR struct { + BaseATR + + trs sFloats.InterPeriodValues +} + +func NewAvgATR(periods int) *AvgATR { + return &AvgATR{ + BaseATR: *newBaseATR(), + trs: sFloats.NewPeriodValues(periods), + } +} + +func (atr *AvgATR) String() string { + return fmt.Sprintf("%s .. atr: %f", atr.BaseATR.String(), atr.get()) +} + +func (atr *AvgATR) Log() { + atr.mu.Lock() + defer atr.mu.Unlock() + + sLog.Info("AvgATR: %s", atr.String()) +} + +func (atr *AvgATR) Periods() int { + return atr.trs.Periods() +} + +func (atr *AvgATR) Filled() bool { + return atr.trs.Filled() +} + +// Add adds a candle to the AvgATR +func (atr *AvgATR) Add(candle tCandle.Inter) { + atr.mu.Lock() + defer atr.mu.Unlock() + + atr.add(candle) +} + +func (atr *AvgATR) Get() float64 { + atr.mu.Lock() + defer atr.mu.Unlock() + + return atr.get() +} + +// private methods + +func (atr *AvgATR) add(candle tCandle.Inter) { + atr.c++ + + if atr.prev.Close() > 0 { + atr.trs.Add(max(candle.High()-candle.Low(), math.Abs(candle.High()-atr.prev.Close()), math.Abs(candle.Low()-atr.prev.Close()))) + } + + atr.prev = candle +} + +func (atr *AvgATR) get() float64 { + return atr.trs.Mean() +} diff --git a/trading/tIndicator/atrSmoothed.go b/trading/tIndicator/atrSmoothed.go new file mode 100644 index 0000000..b1c007d --- /dev/null +++ b/trading/tIndicator/atrSmoothed.go @@ -0,0 +1,73 @@ +package tIndicator + +import ( + "fmt" + "math" + + "github.com/yasseldg/go-simple/logs/sLog" + "github.com/yasseldg/go-simple/trading/tCandle" + "github.com/yasseldg/go-simple/types/sFloats" +) + +type SmATR struct { + BaseATR + + trs sFloats.SmoothedAverage +} + +func NewSmATR(periods int) *SmATR { + return &SmATR{ + BaseATR: *newBaseATR(), + trs: *sFloats.NewSmoothedAverage(periods), + } +} + +func (atr *SmATR) String() string { + return fmt.Sprintf("%s .. %f", atr.BaseATR.String(), atr.get()) +} + +func (atr *SmATR) Log() { + atr.mu.Lock() + defer atr.mu.Unlock() + + sLog.Info("SmATR: %s", atr.String()) +} + +func (atr *SmATR) Periods() int { + return atr.trs.Periods() +} + +func (atr *SmATR) Filled() bool { + return atr.trs.Filled() +} + +// Add adds a candle to the SmATR +func (atr *SmATR) Add(candle tCandle.Inter) { + atr.mu.Lock() + defer atr.mu.Unlock() + + atr.add(candle) +} + +func (atr *SmATR) Get() float64 { + atr.mu.Lock() + defer atr.mu.Unlock() + + return atr.get() +} + +// private methods + +func (atr *SmATR) add(candle tCandle.Inter) { + atr.c++ + + if atr.prev.Close() > 0 { + atr.trs.AddPos(max(candle.High()-candle.Low(), math.Abs(candle.High()-atr.prev.Close()), math.Abs(candle.Low()-atr.prev.Close()))) + } + + atr.prev = candle +} + +func (atr *SmATR) get() float64 { + return atr.trs.Value() +} diff --git a/trading/tIndicator/bbands.go b/trading/tIndicator/bbands.go index 7985d3f..dda616a 100644 --- a/trading/tIndicator/bbands.go +++ b/trading/tIndicator/bbands.go @@ -1,21 +1,38 @@ package tIndicator import ( + "fmt" "sync" "github.com/yasseldg/go-simple/logs/sLog" - "github.com/yasseldg/go-simple/trading/tCandle" "github.com/yasseldg/go-simple/types/sFloats" - "github.com/yasseldg/go-simple/types/sTime" ) // BBands (Bollinger Bands) +type interBBands interface { + String() string + Log() + + Periods() int + Deviations() float64 + + Filled() bool + Get() (mean, upper, lower float64) + Calc(deviations float64) (mean, upper, lower float64) +} + +type InterBBands interface { + interBBands + + Add(close float64) +} + type BBands struct { mu sync.Mutex deviations float64 - closes sFloats.PeriodValues + closes sFloats.InterPeriodValues mean float64 std float64 @@ -26,19 +43,31 @@ func NewBBands(period int, deviations float64) *BBands { mu: sync.Mutex{}, deviations: deviations, - closes: *sFloats.NewPeriodValues(period), + closes: sFloats.NewPeriodValues(period), } } -func (bb *BBands) Add(close float64) { +func (bb *BBands) String() string { + + mean, upper, lower := bb.get() + + return fmt.Sprintf("deviations: %f .. periods: %d .. std: %f .. upper: %f .. mean: %f .. lower: %f", + bb.deviations, bb.closes.Periods(), bb.std, upper, mean, lower) +} + +func (bb *BBands) Log() { bb.mu.Lock() defer bb.mu.Unlock() - if close == 0 { - return - } + sLog.Info("BBands: %s", bb.String()) +} - bb.add(close) +func (bb *BBands) Deviations() float64 { + return bb.deviations +} + +func (bb *BBands) Periods() int { + return bb.closes.Periods() } func (bb *BBands) Filled() bool { @@ -52,7 +81,7 @@ func (bb *BBands) Get() (mean, upper, lower float64) { bb.mu.Lock() defer bb.mu.Unlock() - return bb.calc(bb.deviations) + return bb.get() } func (bb *BBands) Calc(deviations float64) (mean, upper, lower float64) { @@ -62,6 +91,17 @@ func (bb *BBands) Calc(deviations float64) (mean, upper, lower float64) { return bb.calc(deviations) } +func (bb *BBands) Add(close float64) { + bb.mu.Lock() + defer bb.mu.Unlock() + + if close == 0 { + return + } + + bb.add(close) +} + func (bb *BBands) add(close float64) { bb.closes.Add(close) @@ -72,6 +112,10 @@ func (bb *BBands) add(close float64) { bb.mean, bb.std = bb.closes.MeanStdDev() } +func (bb *BBands) get() (mean, upper, lower float64) { + return bb.calc(bb.deviations) +} + func (bb *BBands) calc(deviations float64) (mean, upper, lower float64) { if !bb.closes.Filled() { return 0, 0, 0 @@ -82,43 +126,3 @@ func (bb *BBands) calc(deviations float64) (mean, upper, lower float64) { lower = bb.mean - (bb.std * deviations) return } - -// BBcandle is a BBands indicator for candles - -type BBcandle struct { - BBands - - c int - prev tCandle.Candle -} - -func NewBBcandle(period int, deviations float64) *BBcandle { - return &BBcandle{ - BBands: *NewBBands(period, deviations), - } -} - -func (bb *BBcandle) Log() { - bb.mu.Lock() - defer bb.mu.Unlock() - - sLog.Info("BBands: %d: %s .. mean: %f .. std: %f ", bb.c, sTime.ForLog(bb.prev.Ts, 0), bb.mean, bb.std) -} - -func (bb *BBcandle) Add(candle tCandle.Candle) { - bb.mu.Lock() - defer bb.mu.Unlock() - - if candle.Close == 0 { - return - } - - bb.add(candle.Close) - - bb.c++ - bb.prev = candle -} - -func (bb *BBcandle) Filled() bool { - return bb.BBands.Filled() -} diff --git a/trading/tIndicator/bbandsCandle.go b/trading/tIndicator/bbandsCandle.go new file mode 100644 index 0000000..34bd694 --- /dev/null +++ b/trading/tIndicator/bbandsCandle.go @@ -0,0 +1,57 @@ +package tIndicator + +import ( + "fmt" + + "github.com/yasseldg/go-simple/logs/sLog" + "github.com/yasseldg/go-simple/trading/tCandle" + "github.com/yasseldg/go-simple/types/sTime" +) + +// BBcandle is a BBands indicator for candles + +type InterBBcandle interface { + interBBands + + Candle() tCandle.Inter + Add(candle tCandle.Inter) +} + +type BBcandle struct { + BBands + + c int + prev tCandle.Inter +} + +func NewBBcandle(period int, deviations float64) *BBcandle { + return &BBcandle{ + BBands: *NewBBands(period, deviations), + prev: new(tCandle.Candle), + } +} + +func (bb *BBcandle) String() string { + return fmt.Sprintf("c: %d: %s .. %s", bb.c, sTime.ForLog(bb.prev.Ts(), 0), bb.BBands.String()) +} + +func (bb *BBcandle) Log() { + bb.mu.Lock() + defer bb.mu.Unlock() + + sLog.Info("BBands: %s", bb.String()) +} + +func (bb *BBcandle) Add(candle tCandle.Inter) { + bb.mu.Lock() + defer bb.mu.Unlock() + + if candle.Close() == 0 { + return + } + + bb.add(candle.Close()) + + bb.c++ + bb.prev = candle +} diff --git a/trading/tIndicator/rsi.go b/trading/tIndicator/rsi.go index 6713305..8946d88 100644 --- a/trading/tIndicator/rsi.go +++ b/trading/tIndicator/rsi.go @@ -5,11 +5,25 @@ import ( "sync" "github.com/yasseldg/go-simple/logs/sLog" - "github.com/yasseldg/go-simple/trading/tCandle" "github.com/yasseldg/go-simple/types/sFloats" - "github.com/yasseldg/go-simple/types/sTime" ) +type interRSI interface { + String() string + Log() + + Periods() int + + Filled() bool + Get() float64 +} + +type InterRSI interface { + interRSI + + Add(close float64) +} + // RSI (Relative Strength Index) type RSI struct { @@ -30,14 +44,6 @@ func NewRSI(period int) *RSI { } } -func (rsi *RSI) Period() int { - return rsi.gain.Period() -} - -func (rsi *RSI) Filled() bool { - return rsi.gain.Filled() -} - func (rsi *RSI) String() string { return fmt.Sprintf("calc: %f .. gain: %f .. loss: %f", rsi.calc(), rsi.gain.Value(), rsi.loss.Value()) } @@ -49,15 +55,12 @@ func (rsi *RSI) Log() { sLog.Info("RSI: %s", rsi.String()) } -func (rsi *RSI) Add(close float64) { - rsi.mu.Lock() - defer rsi.mu.Unlock() - - if close == 0 { - return - } +func (rsi *RSI) Periods() int { + return rsi.gain.Periods() +} - rsi.add(close) +func (rsi *RSI) Filled() bool { + return rsi.gain.Filled() } func (rsi *RSI) Get() float64 { @@ -71,6 +74,17 @@ func (rsi *RSI) Get() float64 { return rsi.calc() } +func (rsi *RSI) Add(close float64) { + rsi.mu.Lock() + defer rsi.mu.Unlock() + + if close == 0 { + return + } + + rsi.add(close) +} + func (rsi *RSI) add(close float64) { if rsi.close == 0 { @@ -100,50 +114,3 @@ func (rsi *RSI) calc() float64 { return 0 } - -// RSIcandle is a RSI indicator for candles - -type RSIcandle struct { - RSI - - c int - candle tCandle.Candle -} - -func NewRSIcandle(period int) *RSIcandle { - return &RSIcandle{ - RSI: *NewRSI(period), - } -} - -func (rsi *RSIcandle) String() string { - return fmt.Sprintf("c: %d: %s .. %s", rsi.c, sTime.ForLog(rsi.candle.Ts, 0), rsi.RSI.String()) -} - -func (rsi *RSIcandle) Log() { - rsi.mu.Lock() - defer rsi.mu.Unlock() - - sLog.Info("RSI: %s", rsi.String()) -} - -func (rsi *RSIcandle) Candle() tCandle.Candle { - rsi.mu.Lock() - defer rsi.mu.Unlock() - - return rsi.candle -} - -func (rsi *RSIcandle) Add(candle tCandle.Candle) { - rsi.mu.Lock() - defer rsi.mu.Unlock() - - if candle.Close == 0 { - return - } - - rsi.add(candle.Close) - - rsi.c++ - rsi.candle = candle -} diff --git a/trading/tIndicator/rsiCandle.go b/trading/tIndicator/rsiCandle.go new file mode 100644 index 0000000..0d33b4d --- /dev/null +++ b/trading/tIndicator/rsiCandle.go @@ -0,0 +1,64 @@ +package tIndicator + +import ( + "fmt" + + "github.com/yasseldg/go-simple/logs/sLog" + "github.com/yasseldg/go-simple/trading/tCandle" + "github.com/yasseldg/go-simple/types/sTime" +) + +type InterRSIcandle interface { + interRSI + + Candle() tCandle.Inter + Add(candle tCandle.Inter) +} + +// RSIcandle is a RSI indicator for candles + +type RSIcandle struct { + RSI + + c int + candle tCandle.Inter +} + +func NewRSIcandle(period int) *RSIcandle { + return &RSIcandle{ + RSI: *NewRSI(period), + candle: new(tCandle.Candle), + } +} + +func (rsi *RSIcandle) String() string { + return fmt.Sprintf("c: %d: %s .. %s", rsi.c, sTime.ForLog(rsi.candle.Ts(), 0), rsi.RSI.String()) +} + +func (rsi *RSIcandle) Log() { + rsi.mu.Lock() + defer rsi.mu.Unlock() + + sLog.Info("RSI: %s ", rsi.String()) +} + +func (rsi *RSIcandle) Candle() tCandle.Inter { + rsi.mu.Lock() + defer rsi.mu.Unlock() + + return rsi.candle +} + +func (rsi *RSIcandle) Add(candle tCandle.Inter) { + rsi.mu.Lock() + defer rsi.mu.Unlock() + + if candle.Close() == 0 { + return + } + + rsi.add(candle.Close()) + + rsi.c++ + rsi.candle = candle +} diff --git a/trading/tIndicator/superTrend.go b/trading/tIndicator/superTrend.go new file mode 100644 index 0000000..25ac55a --- /dev/null +++ b/trading/tIndicator/superTrend.go @@ -0,0 +1,172 @@ +package tIndicator + +import ( + "fmt" + "sync" + + "github.com/yasseldg/go-simple/logs/sLog" + "github.com/yasseldg/go-simple/trading/tCandle" + "github.com/yasseldg/go-simple/types/sStrings" + "github.com/yasseldg/go-simple/types/sTime" +) + +// SuperTrend like TradingView + +type InterSuperTrend interface { + InterATR + + String() string + + Config() string + Multiplier() float64 + IsUptrend() bool + Add(candle tCandle.Inter) + Get() float64 + + Periods() int +} + +type SuperTrend struct { + InterATR + + mu sync.Mutex + + multiplier float64 + + prev_close float64 + prev_upper float64 + prev_value float64 + + upper float64 + lower float64 + + value float64 +} + +func NewSuperTrend(period int, multiplier float64, smoothed bool) *SuperTrend { + supertrend := &SuperTrend{ + multiplier: multiplier, + } + + if smoothed { + supertrend.InterATR = NewSmATR(period) + } else { + supertrend.InterATR = NewAvgATR(period) + } + + return supertrend +} + +func (st *SuperTrend) Config() string { + return fmt.Sprintf("period: %d .. multiplier: %.2f", st.Periods(), st.multiplier) +} + +func (st *SuperTrend) String() string { + v := "" + if st.IsUptrend() { + v = sStrings.Colored(sStrings.Green, fmt.Sprintf("%f", st.value)) + } else { + v = sStrings.Colored(sStrings.Red, fmt.Sprintf("%f", st.value)) + } + + return fmt.Sprintf("SuperTrend %d: period: %d .. multiplier: %.2f .. %s .. %s", + st.InterATR.Count(), st.Periods(), st.multiplier, sTime.ForLog(st.InterATR.Prev().Ts(), 0), v) +} + +func (st *SuperTrend) Log() { + st.mu.Lock() + defer st.mu.Unlock() + + sLog.Info(st.String()) +} + +func (st *SuperTrend) Multiplier() float64 { + return st.multiplier +} + +func (st *SuperTrend) IsUptrend() bool { + return st.value == st.lower +} + +func (st *SuperTrend) Add(candle tCandle.Inter) { + st.mu.Lock() + defer st.mu.Unlock() + + st.InterATR.Add(candle) + + if st.InterATR.Get() == 0 { + return + } + + hl2 := (candle.High() + candle.Low()) / 2 + + multAtr := st.multiplier * st.InterATR.Get() + + basicUpper := hl2 + multAtr + basicLower := hl2 - multAtr + + // sLog.Debug("SuperTrend %d: basicUpper: %f .. uper: %f .. basicLower: %f .. lower: %f .. close: %f .. prev_close: %f .. trend: %t .. value: %f", + // st.ATR.c, basicUpper, st.upper, basicLower, st.lower, candle.Close, st.prev_close, st.IsUptrend(), st.value) + + // upperBand = basicUpperBand < prev upperBand or prev close > prev upperBand ? basicUpperBand : prev upperBand + if st.upper == 0 || basicUpper < st.upper || st.prev_close > st.upper { + st.upper = basicUpper + } + + // lowerBand = basicLowerBand > prev lowerBand or prev close < prev lowerBand ? basicLowerBand : prev lowerBand + if st.lower == 0 || basicLower > st.lower || st.prev_close < st.lower { + st.lower = basicLower + } + + // hasta acá parece estar todo bien + + // if prev superTrend == prev upperBand + if st.prev_value == st.prev_upper { + // trendDirection := close > upperBand ? isUpTrend : isDownTrend + if candle.Close() > st.upper { + st.value = st.lower + } else { + st.value = st.upper + } + } else { + // trendDirection := close < lowerBand ? isDownTrend : isUpTrend + if candle.Close() < st.lower { + st.value = st.upper + } else { + st.value = st.lower + } + } + + st.prev_close = candle.Close() + st.prev_upper = st.upper + st.prev_value = st.value +} + +func (st *SuperTrend) Get() float64 { + st.mu.Lock() + defer st.mu.Unlock() + + return st.value +} + +// https://es.tradingview.com/support/solutions/43000634738/ + +// Para calcular las bandas de supertendencia, hay que utilizar las siguientes fórmulas: + +// hl2 = (high + low) / 2 +// basicUpperBand = hl2 + (multiplier × ATR) +// basicLowerBand = hl2 - (multiplier × ATR) + +// upperBand = basicUpperBand < prev upperBand or +// prev close > prev upperBand ? basicUpperBand : prev upperBand +// lowerBand = basicLowerBand > prev lowerBand or +// prev close < prev lowerBand ? basicLowerBand : prev lowerBand + +// superTrend = trendDirection == isUpTrend ? lowerBand : upperBand +// El parámetro trendDirection se determina en función de que se cumplan las siguientes condiciones: + +// Until the ATR value is calculated trendDirection = isDownTrend +// else if prev superTrend == prev upperBand +// trendDirection := close > upperBand ? isUpTrend : isDownTrend +// else +// trendDirection := close < lowerBand ? isDownTrend : isUpTrend diff --git a/types/sFloats/average.go b/types/sFloats/average.go index 4fad62a..268f6d8 100644 --- a/types/sFloats/average.go +++ b/types/sFloats/average.go @@ -5,6 +5,16 @@ import ( "sync" ) +type InterAverage interface { + String() string + + Add(float64) + Calc() float64 + Value() float64 + Qty() int + Reset() +} + type Average struct { mu sync.Mutex @@ -26,7 +36,7 @@ func (a *Average) String() string { } func (a *Average) calc() float64 { - return a.value / float64(a.qty) + return GetValid(a.value / float64(a.qty)) } func (a *Average) Add(v float64) { diff --git a/types/sFloats/periodValues.go b/types/sFloats/periodValues.go index 91fb12e..219f0f2 100644 --- a/types/sFloats/periodValues.go +++ b/types/sFloats/periodValues.go @@ -10,28 +10,43 @@ import ( // ---- PeriodValues +type InterPeriodValues interface { + Log(qty int) + + Filled() bool + ToFill() int + Values() []float64 + Periods() int + + Add(value float64) + + Fill(value float64) bool + // DeepCopy() InterPeriodValues + Mean() float64 + MeanStdDev() (mean, std float64) + Sum() float64 +} + type PeriodValues struct { mu sync.Mutex - period int - values []float64 + periods int + values []float64 filled bool } -func NewPeriodValues(period int) *PeriodValues { +func NewPeriodValues(periods int) *PeriodValues { return &PeriodValues{ - mu: sync.Mutex{}, - period: period, - values: make([]float64, 0, period), + mu: sync.Mutex{}, + + periods: periods, + values: make([]float64, 0, periods), } } -func (pv *PeriodValues) Period() int { - pv.mu.Lock() - defer pv.mu.Unlock() - - return pv.period +func (pv *PeriodValues) Periods() int { + return pv.periods } func (pv *PeriodValues) Values() []float64 { @@ -52,7 +67,7 @@ func (pv *PeriodValues) ToFill() int { pv.mu.Lock() defer pv.mu.Unlock() - return pv.period - len(pv.values) + return pv.periods - len(pv.values) } func (pv *PeriodValues) Log(qty int) { @@ -60,14 +75,14 @@ func (pv *PeriodValues) Log(qty int) { defer pv.mu.Unlock() if len(pv.values) < 1 { - sLog.Info("PeriodValues.Log: periods: %-5d .. len: %-5d", pv.period, len(pv.values)) + sLog.Info("PeriodValues.Log: periods: %-5d .. len: %-5d", pv.periods, len(pv.values)) return } if len(pv.values) < qty { - sLog.Info("PeriodValues.Log: periods: %-5d .. len: %-5d .. values: %v ", pv.period, len(pv.values), pv.values) + sLog.Info("PeriodValues.Log: periods: %-5d .. len: %-5d .. values: %v ", pv.periods, len(pv.values), pv.values) return } - sLog.Info("PeriodValues.Log: periods: %-5d .. len: %-5d .. firsts: %v .. lasts: %v", pv.period, len(pv.values), pv.values[:3], pv.values[len(pv.values)-3:]) + sLog.Info("PeriodValues.Log: periods: %-5d .. len: %-5d .. firsts: %v .. lasts: %v", pv.periods, len(pv.values), pv.values[:3], pv.values[len(pv.values)-3:]) } func (pv *PeriodValues) Add(value float64) { @@ -85,7 +100,7 @@ func (pv *PeriodValues) Add(value float64) { func (pv *PeriodValues) add(value float64) { pv.values = append(pv.values, value) - if len(pv.values) >= pv.period { + if len(pv.values) >= pv.periods { pv.filled = true } } @@ -105,7 +120,7 @@ func (pv *PeriodValues) fill(value float64) bool { return false } - if len(pv.values) >= pv.period { + if len(pv.values) >= pv.periods { pv.filled = true return false } @@ -127,9 +142,9 @@ func (pv *PeriodValues) deepCopy() *PeriodValues { copy(newValues, pv.values) return &PeriodValues{ - mu: sync.Mutex{}, - period: pv.period, // Los valores de tipo int se copian por valor - values: newValues, + mu: sync.Mutex{}, + periods: pv.periods, // Los valores de tipo int se copian por valor + values: newValues, } } diff --git a/types/sFloats/periodValuesMinMax.go b/types/sFloats/periodValuesMinMax.go index 9208d1c..fe9fd29 100644 --- a/types/sFloats/periodValuesMinMax.go +++ b/types/sFloats/periodValuesMinMax.go @@ -15,9 +15,9 @@ type PeriodValuesMinMax struct { max float64 } -func NewPeriodValuesMinMax(period int) *PeriodValuesMinMax { +func NewPeriodValuesMinMax(periods int) *PeriodValuesMinMax { return &PeriodValuesMinMax{ - PeriodValues: NewPeriodValues(period), + PeriodValues: NewPeriodValues(periods), } } @@ -36,7 +36,7 @@ func (pv *PeriodValuesMinMax) Max() float64 { } func (pv *PeriodValuesMinMax) Log(qty int) { - msg := fmt.Sprintf("PeriodValues.Log: periods: %-5d .. len: %-5d .. min: %f .. max: %f", pv.period, len(pv.values), pv.min, pv.max) + msg := fmt.Sprintf("PeriodValues.Log: periods: %-5d .. len: %-5d .. min: %f .. max: %f", pv.periods, len(pv.values), pv.min, pv.max) if len(pv.values) < 1 { sLog.Warn(msg) return @@ -45,7 +45,7 @@ func (pv *PeriodValuesMinMax) Log(qty int) { sLog.Warn("%s .. values: %v ", msg, pv.values) return } - sLog.Warn("PeriodValues.Log: periods: %-5d .. len: %-5d .. firsts: %v .. lasts: %v", pv.period, len(pv.values), pv.values[:3], pv.values[len(pv.values)-3:]) + sLog.Warn("PeriodValues.Log: periods: %-5d .. len: %-5d .. firsts: %v .. lasts: %v", pv.periods, len(pv.values), pv.values[:3], pv.values[len(pv.values)-3:]) } func (pv *PeriodValuesMinMax) Add(value float64) { diff --git a/types/sFloats/smoothedAvg.go b/types/sFloats/smoothedAvg.go index 673360b..a252982 100644 --- a/types/sFloats/smoothedAvg.go +++ b/types/sFloats/smoothedAvg.go @@ -7,26 +7,38 @@ import ( "github.com/yasseldg/go-simple/logs/sLog" ) +type InterSmoothedAverage interface { + String() string + Log() + + Filled() bool + Value() float64 + Periods() int + + AddPos(v float64) + AddNeg(v float64) +} + type SmoothedAverage struct { mu sync.Mutex - period int - value float64 + periods int + value float64 filled int } -func NewSmoothedAverage(period int) *SmoothedAverage { +func NewSmoothedAverage(periods int) *SmoothedAverage { return &SmoothedAverage{ mu: sync.Mutex{}, - period: period, - filled: period, + periods: periods, + filled: periods, } } func (sa *SmoothedAverage) String() string { - return fmt.Sprintf("period: %d .. value: %f .. filled: %d", sa.period, sa.value, sa.filled) + return fmt.Sprintf("periods: %d .. value: %f .. filled: %d", sa.periods, sa.value, sa.filled) } func (sa *SmoothedAverage) Log() { @@ -46,9 +58,9 @@ func (sa *SmoothedAverage) AddPos(v float64) { return } - sa.value *= float64(sa.period - 1) + sa.value *= float64(sa.periods - 1) sa.value += v - sa.value /= float64(sa.period) + sa.value /= float64(sa.periods) } // AddNeg subtracts a value from the average @@ -61,9 +73,9 @@ func (sa *SmoothedAverage) AddNeg(v float64) { return } - sa.value *= float64(sa.period - 1) + sa.value *= float64(sa.periods - 1) sa.value -= v - sa.value /= float64(sa.period) + sa.value /= float64(sa.periods) } func (sa *SmoothedAverage) fillPos(v float64) { @@ -80,7 +92,7 @@ func (sa *SmoothedAverage) fill() { sa.filled-- if sa.filled == 0 { - sa.value /= float64(sa.period) + sa.value /= float64(sa.periods) } } @@ -102,9 +114,9 @@ func (sa *SmoothedAverage) Value() float64 { return sa.value } -func (sa *SmoothedAverage) Period() int { +func (sa *SmoothedAverage) Periods() int { sa.mu.Lock() defer sa.mu.Unlock() - return sa.period + return sa.periods } diff --git a/types/sStrings/colors.go b/types/sStrings/colors.go new file mode 100644 index 0000000..675b6f7 --- /dev/null +++ b/types/sStrings/colors.go @@ -0,0 +1,18 @@ +package sStrings + +import "fmt" + +type Color int + +const ( + Black Color = iota + 30 + Red + Green + Yellow + Blue + Magenta +) + +func Colored(color Color, msg string) string { + return fmt.Sprintf("\033[%dm%s\033[0m", color, msg) +} diff --git a/types/sTime/funcs.go b/types/sTime/funcs.go index 15433a9..b1f8fce 100644 --- a/types/sTime/funcs.go +++ b/types/sTime/funcs.go @@ -3,6 +3,8 @@ package sTime import ( "fmt" "time" + + "github.com/yasseldg/go-simple/logs/sLog" ) func Since(duration time.Duration) string { @@ -34,3 +36,17 @@ func Since(duration time.Duration) string { return s } + +func TimeControl(f func(), name string) { + t := time.Now() + + msg := fmt.Sprintf("Time Control ( %s )", name) + + sLog.Info(msg) + + f() + + fmt.Println() + + sLog.Info(fmt.Sprintf("%s elapsed %s \n", msg, Since(time.Since(t)))) +}