diff --git a/base.go b/base.go index 710e162..a8c9f04 100644 --- a/base.go +++ b/base.go @@ -3,23 +3,33 @@ package moneymoney import ( "context" "encoding/gob" + "log" "os" + "time" + "github.com/474420502/random" "github.com/474420502/structure/compare" "github.com/474420502/structure/tree/treelist" "github.com/klauspost/compress/zstd" "go.mongodb.org/mongo-driver/bson" ) -func GetAll() (result *treelist.Tree[int64]) { +const 亿 = 100000000 - result = treelist.New[int64](compare.Any[int64]) - var stocks []*Stock - - cur, err := cstock.Find(context.TODO(), bson.M{}) +func GetDate(date string) time.Time { + ts, err := time.ParseInLocation("2006-01-02", date, time.Local) if err != nil { panic(err) } + return ts +} + +type StockTree *treelist.Tree[int64, *Stock] + +func GetAll() (result *treelist.Tree[int64, *treelist.Tree[int64, *Stock]]) { + + result = treelist.New[int64, *treelist.Tree[int64, *Stock]](compare.Any[int64]) + var stocks []*Stock f, err := os.Open("./stocks.gob") if err == nil { @@ -36,6 +46,11 @@ func GetAll() (result *treelist.Tree[int64]) { defer f.Close() } else { + cur, err := cstock.Find(cxt, bson.M{}) + if err != nil { + log.Println(err) + } + for cur.Next(context.TODO()) { var s Stock err := cur.Decode(&s) @@ -66,16 +81,16 @@ func GetAll() (result *treelist.Tree[int64]) { } for _, s := range stocks { - var daymap *treelist.Tree[int64] + var daymap *treelist.Tree[int64, *Stock] idaymap, ok := result.Get(s.Date.Unix()) // daymap, ok := DateStocks[s.Date.Unix()] if !ok { - idaymap = treelist.New(compare.Any[int64]) + idaymap = treelist.New[int64, *Stock](compare.Any[int64]) result.Put(s.Date.Unix(), idaymap) // DateStocks[s.Date.Unix()] = daymap } - daymap = idaymap.(*treelist.Tree[int64]) + daymap = idaymap daymap.Put(s.Code, s) } @@ -85,3 +100,18 @@ func GetAll() (result *treelist.Tree[int64]) { func AggregateNewField(result []*Stock) { } + +func ShowSelectStocks(selectStocks []*Stock) { + for _, s := range selectStocks { + log.Printf("%s %s 当前价格:%f", s.Date.Local().Format("2006-01-02"), s.Name, s.ClosingPrice) + // log.Println(s.UpsDownsRatio, s) + } +} + +func RandomRange[T float32 | float64 | int | int64 | uint32 | uint64](rd *random.Random, rv *RangeValue[T]) { + rv.Min = rv.MinLimt + T(rd.Int63n(int64((rv.MaxLimit-rv.MinLimt)/rv.Step)))*rv.Step + rv.Max = rv.MinLimt + T(rd.Int63n(int64((rv.MaxLimit-rv.MinLimt)/rv.Step)))*rv.Step + if rv.Min > rv.Max { + rv.Min, rv.Max = rv.Max, rv.Min + } +} diff --git a/go.mod b/go.mod index 02c85d0..16c37ea 100644 --- a/go.mod +++ b/go.mod @@ -9,11 +9,10 @@ require ( ) require ( - github.com/474420502/classify v1.3.1 - github.com/474420502/structure v1.0.1 + github.com/474420502/structure v1.0.4 github.com/go-stack/stack v1.8.0 // indirect github.com/golang/snappy v0.0.1 // indirect - github.com/klauspost/compress v1.13.6 // indirect + github.com/klauspost/compress v1.13.6 github.com/pkg/errors v0.9.1 // indirect github.com/xdg-go/pbkdf2 v1.0.0 // indirect github.com/xdg-go/scram v1.0.2 // indirect @@ -28,7 +27,9 @@ require ( require ( github.com/474420502/gcurl v0.5.1 + github.com/474420502/random v0.5.2-0.20220222044003-09d6ed40ca23 github.com/474420502/requests v1.11.1 // indirect + github.com/davecgh/go-spew v1.1.1 github.com/tidwall/match v1.1.1 // indirect github.com/tidwall/pretty v1.2.0 // indirect golang.org/x/net v0.0.0-20211105192438-b53810dc28af // indirect diff --git a/go.sum b/go.sum index ce4f005..9c3a853 100644 --- a/go.sum +++ b/go.sum @@ -1,12 +1,11 @@ -github.com/474420502/classify v1.3.1 h1:AakdyoS97lx54uiPjbtTFCyeYr16qebE+ScDvtZmXDA= -github.com/474420502/classify v1.3.1/go.mod h1:Z2nPKjdgsxx7P1UfJZOiQdvZqlxlxGYgNswdVz1LYsM= github.com/474420502/gcurl v0.5.1 h1:Rpl0qdffIwgzOivc4BAn8biOpoGIFhOBAFe8vogswIk= github.com/474420502/gcurl v0.5.1/go.mod h1:bYc/86ynsx28WNFd6MDt/8gy0xRvuqhspmEqEm7Sybo= github.com/474420502/random v0.5.2-0.20220222044003-09d6ed40ca23 h1:ZO9oDeD8EOHiHbFLPlZ5WyfF0uBoYfRD0/NoEIFUeAQ= +github.com/474420502/random v0.5.2-0.20220222044003-09d6ed40ca23/go.mod h1:51QVTqbWTDUBTA+4phZZjjVsJXWz0zayPTdDEBu+2sA= github.com/474420502/requests v1.11.1 h1:BLI95klIGRjTLeQ7U7CJLkkniVsL2tK0PLJnUTSTyfY= github.com/474420502/requests v1.11.1/go.mod h1:epMXb90Z7hmiOu+hMLFj8eFbkqOXWThNDYHuh2ThTlE= -github.com/474420502/structure v1.0.1 h1:X8hM0m6CA+13HZI2/Uv3pO+Kce3cm/ZGfXVeE+S4uN8= -github.com/474420502/structure v1.0.1/go.mod h1:rND3ZSiZH84JKV43+bR0/iUHvLlhkcBmW9hyUNkr3bU= +github.com/474420502/structure v1.0.4 h1:sHu7Z+4tu2gvPE/ZgYb258/sVOZ1g2jHI0M8TO/DCAk= +github.com/474420502/structure v1.0.4/go.mod h1:rND3ZSiZH84JKV43+bR0/iUHvLlhkcBmW9hyUNkr3bU= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= diff --git a/main.go b/main.go index 72ee1d1..84f2e2e 100644 --- a/main.go +++ b/main.go @@ -25,6 +25,7 @@ import ( "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/mongo" "go.mongodb.org/mongo-driver/mongo/options" + "go.mongodb.org/mongo-driver/mongo/readpref" "golang.org/x/text/encoding/simplifiedchinese" "golang.org/x/text/transform" ) @@ -71,17 +72,27 @@ func GetDefaultPage() *rod.Page { var client *mongo.Client var cstock *mongo.Collection -var DateStocks *treelist.Tree[int64] +var DateStocks *treelist.Tree[int64, *treelist.Tree[int64, *Stock]] var CountedDays map[int]bool var err error +var cxt context.Context +var cancel context.CancelFunc + func init() { log.SetFlags(log.Llongfile | log.LstdFlags) - client, err = mongo.Connect(context.TODO(), options.Client().ApplyURI("mongodb://localhost:27017")) + cxt, cancel = context.WithTimeout(context.TODO(), time.Second*10) + defer cancel() + client, err = mongo.Connect(cxt, options.Client().ApplyURI("mongodb://localhost:27017")) if err != nil { panic(err) } + client.Ping(cxt, &readpref.ReadPref{}) + if err != nil { + panic(err) + } + cstock = client.Database("money").Collection("stock") DateStocks = GetAll() CountedDays = map[int]bool{} diff --git a/main_test.go b/main_test.go index 8229fa7..4d3f588 100644 --- a/main_test.go +++ b/main_test.go @@ -2,220 +2,92 @@ package moneymoney import ( "encoding/gob" - "log" "os" - "sort" "testing" - "time" "github.com/474420502/structure/tree/treelist" "github.com/klauspost/compress/zstd" ) -const 亿 = 100000000 - -func GetDate(date string) time.Time { - ts, err := time.ParseInLocation("2006-01-02", date, time.Local) - if err != nil { - panic(err) - } - return ts -} - -func GetStocksByCondition(cday time.Time) []*Stock { - var stocks []*Stock - - // cday := GetDate(CurrentDay) - start := cday.Add(-time.Hour * 24 * 14) - log.Println("重新从", start.Local().Format("2006-01-02"), cday.Local().Format("2006-01-02"), "策略选股") - - // istartStock, _ := DateStocks.Get(start.Unix()) - siter := DateStocks.Iterator() - siter.SeekGE(start.Unix()) - siter.Valid() - { - ssiter := siter.Value().(*treelist.Tree[int64]).Iterator() - ssiter.SeekToFirst() - // ss := ssiter.Value().(*Stock) - // log.Printf("%s", ss.Date.Local().Format("2006-01-02")) - } - - iter := DateStocks.Iterator() - - // log.Println(iter.SeekLE(cday.Unix()), iter.Valid()) - iter.SeekLE(cday.Unix()) - endStock := iter.Value().(*treelist.Tree[int64]) - - // log.Println(DateStocks.Size(), endStock.Size()) - - endStock.Traverse(func(s *treelist.Slice[int64]) bool { - var ok bool - citer := siter.Clone() - - stock := s.Value.(*Stock) - if stock.MarketValue <= 100*亿 { - return true - } - - startStock := citer.Value().(*treelist.Tree[int64]) - - var istock any - for { - istock, ok = startStock.Get(stock.Code) - if ok { - break - } - citer.Next() - if !citer.Valid() { - return true - } - } - - fstock := istock.(*Stock) - if fstock.ClosingPrice == 0 { - return true - } - if stock.ClosingPrice == 0 { - return true - } - - stock.MinPrice = fstock.ClosingPrice - stock.MaxPrice = stock.ClosingPrice - - stock.UpsDownsRatio = ((stock.ClosingPrice - fstock.ClosingPrice) / fstock.ClosingPrice) - // log.Println(stock.UpsDownsRatio, s) - - stocks = append(stocks, stock) - return true - }) - - sort.Slice(stocks, func(i, j int) bool { - return stocks[i].UpsDownsRatio < stocks[j].UpsDownsRatio - }) - - return stocks -} - func TestMoney(t *testing.T) { - - var money float64 = 1000000.0 - cday := GetDate("2017-04-01") - -ALL_LOOP: - for money < 1000000*3 { - - selectStocks := GetStocksByCondition(cday) - // log.Println(len(selectStocks)) - - for _, s := range selectStocks[0:15] { - log.Printf("%.4f%% %s %s %f", s.UpsDownsRatio*100.0, s.Date.Local().Format("2006-01-02"), s.Name, s.ClosingPrice) - // log.Println(s.UpsDownsRatio, s) - } - - selectStocks = selectStocks[0:15] - - start := (len(selectStocks) - 10) / 2 - selectStocks = selectStocks[start : start+10] - // for _, s := range selectStocks { - // log.Printf("%.4f%% %s", s.UpsDownsRatio*100.0, s.Name) - // } - // TODO: 测试 收益 - iter := DateStocks.Iterator() - iter.SeekGT(cday.Unix()) - if !iter.Valid() { - break - } - - for { - var total float64 = 0.0 - iter.Next() - - if !iter.Valid() { - break ALL_LOOP - } - cstocks := iter.Value().(*treelist.Tree[int64]) - - var cs *Stock - for _, s := range selectStocks { - itf, ok := cstocks.Get(s.Code) - if ok { - cs = itf.(*Stock) - total += (cs.ClosingPrice - s.ClosingPrice) / s.ClosingPrice - // log.Printf("%.2f", (cs.ClosingPrice-s.ClosingPrice)/s.ClosingPrice) - } - } - - csdate := cs.Date.Local().Format("2006-01-02") - if csdate == "2017-12-07" { - for _, s := range selectStocks { - log.Printf("%.4f%% %s %s %f %f %f", s.UpsDownsRatio*100.0, s.Date.Local().Format("2006-01-02"), s.Name, s.ClosingPrice, s.MinPrice, s.MaxPrice) - // log.Println(s.UpsDownsRatio, s) - } - log.Println() - } - - total = total / float64(len(selectStocks)) - - log.Printf("%s 总收益:%.2f%%", csdate, total*100) - if total >= 0.10 || total <= -0.20 { - money = money + money*total - cday = cs.Date - log.Printf("最后总财富 1000000 -> money: %f", money) - break - } - } - } - - log.Printf("最后总财富 1000000 -> money: %f", money) - + HistoryRun() } +// 统计历史关系参数. func TestCase3(t *testing.T) { - cday := GetDate("2017-04-01") + cday := GetDate("2017-04-15") citer := DateStocks.Iterator() citer.SeekToFirst() citer.SeekGE(cday.Unix()) for citer.Valid() { - stocks := citer.Value().(*treelist.Tree[int64]) - - todayIter := stocks.Iterator() + stocks := citer.Value() + todayIter := stocks.Iterator() // 获取当天 所有股票的迭代器 cmpiter := citer.Clone() var i = 0 - // 向前移动的天数 + // 向后移动的天数 for cmpiter.Valid() { todayIter.SeekToFirst() - cmpday := cmpiter.Value().(*treelist.Tree[int64]) + cmpday := cmpiter.Value() - for todayIter.Valid() { + stocks.Traverse(func(s *treelist.Slice[int64, *Stock]) bool { - s := todayIter.Value().(*Stock) + todaystock := s.Value + if todaystock.Extend == nil { + todaystock.Extend = &StockExtend{ + MaxPriceDays: 0, + MinPriceDays: 100000000000, + } + todaystock.Extend.UpsDownsRatioDays = append(todaystock.Extend.UpsDownsRatioDays, &UpsDownsDays{}) + } - ic, ok := cmpday.Get(s.Code) + // 根据股票代码 获取对比的股票 + c, ok := cmpday.Get(todaystock.Code) if ok { - c := ic.(*Stock) - if c.Extend == nil { - c.Extend = &StockExtend{} - } - // 满足相邻天数的处理 - if len(c.Extend.UpsDownsRatioDays) == 0 { - if _, ok := CountedDays[i]; ok { - UpsDownsRatioDays := ((c.ClosingPrice - s.ClosingPrice) / c.ClosingPrice) - c.Extend.UpsDownsRatioDays = append(c.Extend.UpsDownsRatioDays, UpsDownsRatioDays) - } + + // 对比的历史前移时间股票 + var ClosingPrice float64 = 0 + + if c.ClosingPrice == 0 { + ClosingPrice = c.PreviousClosingPrice + } else { + ClosingPrice = c.ClosingPrice } - // TODO: 其他的统计处理 + if ClosingPrice != 0 { + if ClosingPrice > todaystock.Extend.MaxPriceDays { + todaystock.Extend.MaxPriceDays = ClosingPrice + } + + if ClosingPrice < todaystock.Extend.MinPriceDays { + todaystock.Extend.MinPriceDays = ClosingPrice + } + + // 满足相邻天数的处理 + + // TODO: 其他的统计处理 + } + + last := todaystock.Extend.UpsDownsRatioDays[len(todaystock.Extend.UpsDownsRatioDays)-1] + last.DownsCount++ + last.UpsCount++ + if _, ok := CountedDays[i]; ok { + last.Ratio = ((ClosingPrice - todaystock.ClosingPrice) / ClosingPrice) + todaystock.Extend.UpsDownsRatioDays = append(todaystock.Extend.UpsDownsRatioDays, &UpsDownsDays{ + UpsCount: last.UpsCount, + DownsCount: last.DownsCount, + }) + } } - todayIter.Next() - } + return true + }) if i >= 1<<7 { break @@ -224,21 +96,33 @@ func TestCase3(t *testing.T) { cmpiter.Prev() i++ } + + todayIter.SeekToFirst() + for todayIter.Valid() { + s := todayIter.Value() + if s.Extend != nil { + // if len(s.Extend.UpsDownsRatioDays) <= 1 { + // log.Println(s.Name, s.Date.Local().String(), s.Code) + // } + s.Extend.UpsDownsRatioDays = s.Extend.UpsDownsRatioDays[0 : len(s.Extend.UpsDownsRatioDays)-1] + } + todayIter.Next() + } + citer.Next() } var stocks []*Stock - DateStocks.Traverse(func(s *treelist.Slice[int64]) bool { - s.Value.(*treelist.Tree[int64]).Traverse(func(s *treelist.Slice[int64]) bool { - stocks = append(stocks, s.Value.(*Stock)) + + DateStocks.Traverse(func(s *treelist.Slice[int64, *treelist.Tree[int64, *Stock]]) bool { + s.Value.Traverse(func(s *treelist.Slice[int64, *Stock]) bool { + stocks = append(stocks, s.Value) return true }) - return true }) if stocks != nil { - f, err := os.OpenFile("./stocks1.gob", os.O_CREATE|os.O_TRUNC|os.O_RDWR, 0644) if err != nil { panic(err) diff --git a/test_histroy.go b/test_histroy.go new file mode 100644 index 0000000..84d0383 --- /dev/null +++ b/test_histroy.go @@ -0,0 +1,225 @@ +package moneymoney + +import ( + "hash" + "log" + "sort" + "time" + + "github.com/474420502/random" + "github.com/474420502/structure/tree/treelist" + "github.com/davecgh/go-spew/spew" +) + +func HistoryRun() { + + rd := random.New() + + var money float64 = 1000000.0 + cday := GetDate("2017-05-01") + + p := &Policy{ + MarketValue: &RangeValue[float64]{MinLimt: 10, MaxLimit: 20000, Step: 1}, + UpsDownsRatioDays: []*RangeValue[float64]{{MinLimt: 0, MaxLimit: 0.2, Step: 0.01}, {MinLimt: 0, MaxLimit: 0.3, Step: 0.01}, {MinLimt: 0, MaxLimit: 0.5, Step: 0.01}, {MinLimt: 0, MaxLimit: 0.5, Step: 0.01}}, + UpsCount: &RangeValue[int]{MinLimt: 0, MaxLimit: 100, Step: 1}, + DownsCount: &RangeValue[int]{MinLimt: 0, MaxLimit: 100, Step: 1}, + MaxPriceRatio: &RangeValue[float64]{MinLimt: 0, MaxLimit: 1, Step: 0.01}, + MinPriceRatio: &RangeValue[float64]{MinLimt: 0, MaxLimit: 1, Step: 0.01}, + } + +RESET: + for { + + RandomRange(rd, p.MarketValue) + for _, rv := range p.UpsDownsRatioDays { + RandomRange(rd, rv) + } + RandomRange(rd, p.UpsCount) + RandomRange(rd, p.DownsCount) + RandomRange(rd, p.MaxPriceRatio) + RandomRange(rd, p.MinPriceRatio) + spew.Println(p) + + ALL_LOOP: + for { + // log.Println(p) + + selectStocks := GetStocksByCondition(cday, p) + // log.Println(len(selectStocks)) + + if len(selectStocks) == 0 { + cday = cday.Add(time.Hour * 24) + if cday.After(GetDate("2022-07-02")) { + cday = GetDate("2017-05-01") + money = 1000000.0 + continue RESET + } + continue + } + + if len(selectStocks) >= 15 { + selectStocks = selectStocks[3:13] + } + + // ShowSelectStocks(selectStocks) + + // TODO: 测试 收益 + iter := DateStocks.Iterator() + iter.SeekGT(cday.Unix()) + if !iter.Valid() { + break + } + + for { + var total float64 = 0.0 + iter.Next() + + if !iter.Valid() { + break ALL_LOOP + } + cstocks := iter.Value() // 当天的股票数据 + + // 当前股票 + var cs *Stock + + for _, s := range selectStocks { + itf, ok := cstocks.Get(s.Code) + if ok { + cs = itf + var ClosePrice float64 + if cs.ClosingPrice == 0 { + ClosePrice = cs.PreviousClosingPrice + } else { + ClosePrice = cs.ClosingPrice + } + total += (ClosePrice - s.ClosingPrice) / s.ClosingPrice + // log.Printf("%.2f", (cs.ClosingPrice-s.ClosingPrice)/s.ClosingPrice) + } + } + + total = total / float64(len(selectStocks)) + + // log.Printf("%s 总收益: money: %f, 涨跌幅:%.2f%%", cs.Date.Local().Format("2006-01-02"), money+money*total, total*100) + if total >= 0.08 || total <= -0.08 { + + for _, s := range selectStocks { + show, ok := cstocks.Get(s.Code) // 当前股票 + if ok { + log.Printf("%s %s 买入:%f 当前:%f", s.Date.Local().Format("2006-01-02"), s.Name, s.ClosingPrice, show.ClosingPrice) + } + } + + money = money + money*total + cday = cs.Date + log.Printf("最后总财富 1000000 -> money: %f, 涨跌幅: %.2f%%", money, (money-1000000)/1000000*100) + break + } + } + } + + log.Printf("最后总财富 本金:1000000 -> money: %f, 涨跌幅: %.2f%%", money, (money-1000000)/1000000*100) + } +} + +type RangeValue[T float32 | float64 | int | int64 | uint32 | uint64] struct { + Min T + MinLimt T + Max T + MaxLimit T + Step T +} + +func (rv *RangeValue[T]) In(v T) bool { + return v >= rv.Min && v <= rv.Max +} + +type Policy struct { + Hash hash.Hash + MarketValue *RangeValue[float64] + UpsDownsRatioDays []*RangeValue[float64] + UpsCount *RangeValue[int] + DownsCount *RangeValue[int] + MaxPriceRatio *RangeValue[float64] + MinPriceRatio *RangeValue[float64] +} + +func GetStocksByCondition(cday time.Time, policy *Policy) []*Stock { + var stocks []*Stock + + // cday := GetDate(CurrentDay) + + // log.Println("重新从", cday.Local().Format("2006-01-02"), "策略选股") + + iter := DateStocks.Iterator() + + // log.Println(iter.SeekLE(cday.Unix()), iter.Valid()) + iter.SeekGE(cday.Unix()) + if !iter.Valid() { + return nil + } + endStock := iter.Value() + + // log.Println(DateStocks.Size(), endStock.Size()) + + endStock.Traverse(func(s *treelist.Slice[int64, *Stock]) bool { + + stock := s.Value + if stock.Name[0] == '*' { + return true + } + + if len(stock.Extend.UpsDownsRatioDays) < 4 { + return true + } + + // if !(stock.MarketValue >= 50*亿) { + // return true + // } + + if !(policy.MarketValue.In(stock.MarketValue / 亿)) { + return true + } + + udDays := stock.Extend.UpsDownsRatioDays + // if !(udDays[0].Ratio < -0.02 && udDays[3].Ratio > 0.03) { + // return true + // } + + for i, ratio := range policy.UpsDownsRatioDays { + if !ratio.In(udDays[i].Ratio) { + return true + } + if !policy.UpsCount.In(udDays[i].UpsCount) { + return true + } + if !policy.DownsCount.In(udDays[i].DownsCount) { + return true + } + } + + maxRatio := (stock.Extend.MaxPriceDays - stock.ClosingPrice) / stock.ClosingPrice + if !policy.MaxPriceRatio.In(maxRatio) { + return true + } + + minRatio := (stock.ClosingPrice - stock.Extend.MinPriceDays) / stock.ClosingPrice + if !policy.MaxPriceRatio.In(minRatio) { + return true + } + + if stock.ClosingPrice == 0 { + return true + } + + // log.Println(stock.UpsDownsRatio, s) + + stocks = append(stocks, stock) + return true + }) + + sort.Slice(stocks, func(i, j int) bool { + return stocks[i].UpsDownsRatio < stocks[j].UpsDownsRatio + }) + + return stocks +} diff --git a/unity.go b/unity.go index 8d590f0..e1969ac 100644 --- a/unity.go +++ b/unity.go @@ -39,10 +39,16 @@ type Stock struct { Extend *StockExtend `json:"Extend" bson:"Extend"` } +type UpsDownsDays struct { + Ratio float64 `json:"Ratio" bson:"Ratio"` + UpsCount int `json:"UpCount" bson:"UpsCount"` + DownsCount int `json:"DownCount" bson:"DownsCount"` +} + type StockExtend struct { - UpsDownsRatioDays []float64 `json:"UpsDownsRatioDays" bson:"UpsDownsRatioDays"` - MaxPriceDays float64 `json:"MaxPriceDays" bson:"MaxPriceDays"` - MinPriceDay float64 `json:"MinPriceDay" bson:"MinPriceDay"` + UpsDownsRatioDays []*UpsDownsDays `json:"UpsDownsRatioDays" bson:"UpsDownsRatioDays"` + MaxPriceDays float64 `json:"MaxPriceDays" bson:"MaxPriceDays"` + MinPriceDays float64 `json:"MinPriceDays" bson:"MinPriceDays"` } type StockBase struct {