diff --git a/.gitignore b/.gitignore index a6c57f5..66dc707 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,3 @@ *.json +*.random_base_data +stocks.gob \ No newline at end of file diff --git a/api.go b/api.go index 7e11cca..ee940bd 100644 --- a/api.go +++ b/api.go @@ -12,10 +12,10 @@ func GetFromAPI() { for _, code := range GetStocks() { - if code.MCAP >= 200*100000000 { + if code.MCAP >= 100*100000000 { pcode.StringSet(code.CODE) start.StringSet("20150101") - end.StringSet("20220622") + end.StringSet("20220701") resp, err := tp.Execute() if err != nil { diff --git a/base.go b/base.go new file mode 100644 index 0000000..05217fe --- /dev/null +++ b/base.go @@ -0,0 +1,71 @@ +package moneymoney + +import ( + "context" + "encoding/gob" + "os" + + "github.com/474420502/structure/compare" + "github.com/474420502/structure/tree/treelist" + "go.mongodb.org/mongo-driver/bson" +) + +func GetAll() (result *treelist.Tree[int64]) { + + result = treelist.New[int64](compare.Any[int64]) + var stocks []*Stock + + cur, err := cstock.Find(context.TODO(), bson.M{}) + if err != nil { + panic(err) + } + + f, err := os.Open("./stocks.gob") + if err == nil { + err = gob.NewDecoder(f).Decode(&stocks) + if err != nil { + panic(err) + } + } else { + for cur.Next(context.TODO()) { + var s Stock + err := cur.Decode(&s) + if err != nil { + panic(err) + } + + stocks = append(stocks, &s) + // result = append(result, &s) + } + + f, err = os.OpenFile("./stocks.gob", os.O_CREATE|os.O_TRUNC|os.O_RDWR, 0644) + if err != nil { + panic(err) + } + + err = gob.NewEncoder(f).Encode(stocks) + if err != nil { + panic(err) + } + } + + for _, s := range stocks { + var daymap *treelist.Tree[int64] + idaymap, ok := result.Get(s.Date.Unix()) + + // daymap, ok := DateStocks[s.Date.Unix()] + if !ok { + idaymap = treelist.New(compare.Any[int64]) + result.Put(s.Date.Unix(), idaymap) + // DateStocks[s.Date.Unix()] = daymap + } + daymap = idaymap.(*treelist.Tree[int64]) + daymap.Put(s.Code, s) + } + + return +} + +func AggregateNewField(result []*Stock) { + +} diff --git a/go.mod b/go.mod index 98d21dd..02c85d0 100644 --- a/go.mod +++ b/go.mod @@ -9,6 +9,8 @@ require ( ) require ( + github.com/474420502/classify v1.3.1 + github.com/474420502/structure v1.0.1 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 diff --git a/go.sum b/go.sum index 1a42724..ce4f005 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,12 @@ +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/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/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 a91d57b..647bc55 100644 --- a/main.go +++ b/main.go @@ -16,6 +16,7 @@ import ( "time" "github.com/474420502/gcurl" + "github.com/474420502/structure/tree/treelist" "github.com/go-rod/rod" "github.com/go-rod/rod/lib/devices" "github.com/go-rod/rod/lib/launcher" @@ -28,55 +29,6 @@ import ( "golang.org/x/text/transform" ) -type Stock struct { - Date string `json:"日期" bson:"日期"` - CodeStr string `json:"股票代码" bson:"股票代码"` - Name string `json:"名称" bson:"名称"` - ClosingPrice float64 `json:"收盘价" bson:"收盘价"` - MaxPrice float64 `json:"最高价" bson:"最高价"` - MinPrice float64 `json:"最低价" bson:"最低价"` - OpeningPrice float64 `json:"开盘价" bson:"开盘价"` - PreviousClosingPrice float64 `json:"前收盘" bson:"前收盘"` - UpsDowns float64 `json:"涨跌额" bson:"涨跌额"` - UpsDownsRatio float64 `json:"涨跌幅" bson:"涨跌幅"` - TurnoverRate float64 `json:"换手率" bson:"换手率"` - Volume float64 `json:"成交量" bson:"成交量"` - Turnover float64 `json:"成交金额" bson:"成交金额"` - MarketValue float64 `json:"总市值" bson:"总市值"` - CirculatingMarketValue float64 `json:"流通市值" bson:"流通市值"` - - Code int64 `json:"股票数字代码" bson:"股票数字代码"` -} - -type StockBase struct { - // CodeStr string // 代地区码 - // Code string // 不带地区码 - - CODE string `json:"CODE"` - FIVE_MINUTE float64 `json:"FIVE_MINUTE"` - HIGH float64 `json:"HIGH"` - HS float64 `json:"HS"` - LB float64 `json:"LB"` - LOW float64 `json:"LOW"` - MCAP float64 `json:"MCAP"` - MFSUM float64 `json:"MFSUM"` - NAME string `json:"NAME"` - OPEN float64 `json:"OPEN"` - PE float64 `json:"PE"` - PERCENT float64 `json:"PERCENT"` - PRICE float64 `json:"PRICE"` - SNAME string `json:"SNAME"` - SYMBOL string `json:"SYMBOL"` - TCAP float64 `json:"TCAP"` - TURNOVER float64 `json:"TURNOVER"` - UPDOWN float64 `json:"UPDOWN"` - VOLUME float64 `json:"VOLUME"` - WB float64 `json:"WB"` - YESTCLOSE float64 `json:"YESTCLOSE"` - ZF float64 `json:"ZF"` - NO float64 `json:"NO"` -} - var DefaultPage *rod.Page func GetDefaultPage() *rod.Page { @@ -118,6 +70,8 @@ func GetDefaultPage() *rod.Page { } var client *mongo.Client +var cstock *mongo.Collection +var DateStocks *treelist.Tree[int64] var err error func init() { @@ -127,6 +81,8 @@ func init() { if err != nil { panic(err) } + cstock = client.Database("money").Collection("stock") + DateStocks = GetAll() } func main() { @@ -323,8 +279,13 @@ func DownloadDataFromCode(code *StockBase) { panic(err) } + ts, err := time.ParseInLocation("2006-01-02", fields[0], time.Local) + if err != nil { + panic(err) + } + s := &Stock{ - Date: fields[0], + Date: ts, CodeStr: fields[1], Name: fields[2], ClosingPrice: ToFloat(fields[3]), @@ -386,8 +347,13 @@ func SaveFromCSV(downloaddata []byte) { panic(err) } + ts, err := time.ParseInLocation("2006-01-02", fields[0], time.Local) + if err != nil { + panic(err) + } + s := &Stock{ - Date: fields[0], + Date: ts, CodeStr: fields[1], Name: fields[2], ClosingPrice: ToFloat(fields[3]), diff --git a/main_test.go b/main_test.go index 97092d0..e1f976d 100644 --- a/main_test.go +++ b/main_test.go @@ -1,9 +1,194 @@ package moneymoney -import "testing" +import ( + "log" + "sort" + "testing" + "time" + + "github.com/474420502/structure/tree/treelist" +) + +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 +} + +var CurrentDay = "2017-03-15" + +func GetStocksByCondition(cday time.Time) []*Stock { + var stocks []*Stock + + // cday := GetDate(CurrentDay) + start := cday.Add(-time.Hour * 24 * 14) + log.Println(start.Local(), cday.Local()) + + // cur, err := cstock.Find(context.TODO(), bson.M{ + // "日期": bson.M{"$gte": start, "$lte": cday}, + // // "涨跌幅": bson.M{"$gte": -15.0, "$lte": -2.0}, + // // // "换手率": bson.M{"$gte": 1.0}, + // // "流通市值": bson.M{"$gte": 100 * 亿}, + // }) + // if err != nil { + // panic(err) + // } + // log.Println(cur.Current) + + // // var total = 0.0 + // var stocks []*Stock + // for cur.Next(context.TODO()) { + // var s Stock + // err := cur.Decode(&s) + // if err != nil { + // panic(err) + // } + // // clf.Add(&s) + + // var daymap *treelist.Tree[int64] + // idaymap, ok := DateStocks.Get(s.Date.Unix()) + + // // daymap, ok := DateStocks[s.Date.Unix()] + // if !ok { + // idaymap = treelist.New(compare.Any[int64]) + // DateStocks.Put(s.Date.Unix(), idaymap) + // // DateStocks[s.Date.Unix()] = daymap + // } + // daymap = idaymap.(*treelist.Tree[int64]) + // daymap.Put(s.Code, &s) + // } + + // 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()) + + 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 = 0.0 + cday := GetDate("2017-05-15") + + for money < 1.0 { + + selectStocks := GetStocksByCondition(cday) + log.Println(len(selectStocks)) + + for _, s := range selectStocks[0:15] { + log.Printf("%.4f%% %s %s %f %f", s.UpsDownsRatio*100.0, s.Date.Local().Format("2006-01-02"), s.Name, s.MinPrice, s.MaxPrice) + // 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()) + + for { + var total float64 = 0.0 + iter.Next() + 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) + } + } + + total = total / float64(len(selectStocks)) + + log.Printf("%.2f%%, %s", total*100, cs.Date.Local()) + if total >= 0.06 { + money += total + cday = cs.Date + break + } + } + } +} + +func TestCase3(t *testing.T) { + +} func TestCase2(t *testing.T) { - GetFromAPI() + GetFromAPI() // 获取基础数据 } func TestCase1(t *testing.T) { diff --git a/unity.go b/unity.go new file mode 100644 index 0000000..9b18bd7 --- /dev/null +++ b/unity.go @@ -0,0 +1,67 @@ +package moneymoney + +import "time" + +type Stock struct { + // 日期 + Date time.Time `json:"日期" bson:"日期"` + // 股票代码 + CodeStr string `json:"股票代码" bson:"股票代码"` + // 名称 + Name string `json:"名称" bson:"名称"` + // 收盘价 + ClosingPrice float64 `json:"收盘价" bson:"收盘价"` + // 最高价 + MaxPrice float64 `json:"最高价" bson:"最高价"` + // 最低价 + MinPrice float64 `json:"最低价" bson:"最低价"` + // 开盘价 + OpeningPrice float64 `json:"开盘价" bson:"开盘价"` + // 前收盘 + PreviousClosingPrice float64 `json:"前收盘" bson:"前收盘"` + // 涨跌额 + UpsDowns float64 `json:"涨跌额" bson:"涨跌额"` + // 涨跌幅 + UpsDownsRatio float64 `json:"涨跌幅" bson:"涨跌幅"` + // 换手率 + TurnoverRate float64 `json:"换手率" bson:"换手率"` + // 成交量 + Volume float64 `json:"成交量" bson:"成交量"` + // 成交金额 + Turnover float64 `json:"成交金额" bson:"成交金额"` + // 总市值 + MarketValue float64 `json:"总市值" bson:"总市值"` + // 流通市值 + CirculatingMarketValue float64 `json:"流通市值" bson:"流通市值"` + // 股票数字代码 + Code int64 `json:"股票数字代码" bson:"股票数字代码"` +} + +type StockBase struct { + // CodeStr string // 代地区码 + // Code string // 不带地区码 + + CODE string `json:"CODE"` + FIVE_MINUTE float64 `json:"FIVE_MINUTE"` + HIGH float64 `json:"HIGH"` + HS float64 `json:"HS"` + LB float64 `json:"LB"` + LOW float64 `json:"LOW"` + MCAP float64 `json:"MCAP"` + MFSUM float64 `json:"MFSUM"` + NAME string `json:"NAME"` + OPEN float64 `json:"OPEN"` + PE float64 `json:"PE"` + PERCENT float64 `json:"PERCENT"` + PRICE float64 `json:"PRICE"` + SNAME string `json:"SNAME"` + SYMBOL string `json:"SYMBOL"` + TCAP float64 `json:"TCAP"` + TURNOVER float64 `json:"TURNOVER"` + UPDOWN float64 `json:"UPDOWN"` + VOLUME float64 `json:"VOLUME"` + WB float64 `json:"WB"` + YESTCLOSE float64 `json:"YESTCLOSE"` + ZF float64 `json:"ZF"` + NO float64 `json:"NO"` +}