TODO: 做hash持久化和搜索出正确的策略

This commit is contained in:
eson 2022-07-10 05:19:48 +08:00
parent a333376c57
commit 5e85a3fe2d
7 changed files with 361 additions and 205 deletions

46
base.go
View File

@ -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
}
}

7
go.mod
View File

@ -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

7
go.sum
View File

@ -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=

15
main.go
View File

@ -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{}

View File

@ -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{}
// 对比的历史前移时间股票
var ClosingPrice float64 = 0
if c.ClosingPrice == 0 {
ClosingPrice = c.PreviousClosingPrice
} else {
ClosingPrice = c.ClosingPrice
}
if ClosingPrice != 0 {
if ClosingPrice > todaystock.Extend.MaxPriceDays {
todaystock.Extend.MaxPriceDays = ClosingPrice
}
if ClosingPrice < todaystock.Extend.MinPriceDays {
todaystock.Extend.MinPriceDays = ClosingPrice
}
// 满足相邻天数的处理
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)
}
}
// 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)

225
test_histroy.go Normal file
View File

@ -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
}

View File

@ -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"`
UpsDownsRatioDays []*UpsDownsDays `json:"UpsDownsRatioDays" bson:"UpsDownsRatioDays"`
MaxPriceDays float64 `json:"MaxPriceDays" bson:"MaxPriceDays"`
MinPriceDay float64 `json:"MinPriceDay" bson:"MinPriceDay"`
MinPriceDays float64 `json:"MinPriceDays" bson:"MinPriceDays"`
}
type StockBase struct {