parent
5681ec638d
commit
86036645ee
@ -0,0 +1,43 @@ |
||||
package decode |
||||
|
||||
import ( |
||||
"easyquotation/stock" |
||||
"easyquotation/utils" |
||||
"fmt" |
||||
"reflect" |
||||
"strings" |
||||
) |
||||
|
||||
var str string = "var hq_str_sz161505=\"银河通利,0.000,1.258,0.000,0.000,0.000,1.239,1.280,0,0.000,3000,1.239,8100,1.234,1000,1.233,1000,1.198,900,1.188,5548,1.280,900,1.300,1000,1.314,1000,1.326,1600,1.330,2020-07-24,10:58:12,00\"" |
||||
|
||||
func Split(r rune) bool { |
||||
return r == ' ' || r == '_' || r == '=' |
||||
} |
||||
func DecodeMarket() { |
||||
l := utils.SplitString(str, Split) |
||||
symbol := l[3] |
||||
|
||||
market := l[4][1 : len(l[4])-1] |
||||
marketList := strings.Split(market, ",") |
||||
|
||||
a := stock.Market{} |
||||
eleVals := reflect.ValueOf(&a).Elem() |
||||
utils.DecodeStock(marketList[:10], eleVals) |
||||
for i := 0; i < 10; i += 2 { |
||||
bEntry := stock.Entry{} |
||||
vals := reflect.ValueOf(&bEntry).Elem() |
||||
utils.DecodeStock(marketList[10+i:10+i+2], vals) |
||||
a.BuyEntryList = append(a.BuyEntryList, bEntry) |
||||
} |
||||
for i := 0; i < 10; i += 2 { |
||||
bEntry := stock.Entry{} |
||||
vals := reflect.ValueOf(&bEntry).Elem() |
||||
utils.DecodeStock(marketList[20+i:20+i+2], vals) |
||||
a.SellEntryList = append(a.SellEntryList, bEntry) |
||||
} |
||||
a.Date = marketList[30] |
||||
a.Time = marketList[31] |
||||
a.Flag = marketList[32] |
||||
|
||||
fmt.Println(a) |
||||
} |
@ -0,0 +1,7 @@ |
||||
package decode |
||||
|
||||
import "testing" |
||||
|
||||
func TestDecodeMarket(t *testing.T) { |
||||
DecodeMarket() |
||||
} |
@ -0,0 +1,31 @@ |
||||
package easyquotation |
||||
|
||||
import ( |
||||
. "easyquotation/stock" |
||||
"easyquotation/utils" |
||||
"log" |
||||
"os" |
||||
) |
||||
|
||||
func Init() { |
||||
path, err := os.Getwd() |
||||
file := path + "/message" + ".txt" |
||||
logFile, err := os.OpenFile(file, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0766) |
||||
if err != nil { |
||||
panic(err) |
||||
} |
||||
log.SetOutput(logFile) // 将文件设置为log输出的文件
|
||||
log.SetPrefix("[quotation]") |
||||
log.SetFlags(log.LstdFlags | log.Lshortfile | log.LUTC) |
||||
|
||||
// 获取新浪股票代码信息
|
||||
utils.UpdateStockCodes() |
||||
//
|
||||
G_STOCK_MANAGER = &StockManager{ |
||||
make(map[string]*Stock), |
||||
make(map[string]*Stock), |
||||
make(map[string]*Stock), |
||||
make(map[string]*Stock), |
||||
} |
||||
G_STOCK_MANAGER.Load() |
||||
} |
@ -0,0 +1,26 @@ |
||||
package easyquotation |
||||
|
||||
import ( |
||||
"easyquotation/sina" |
||||
"easyquotation/stock" |
||||
"fmt" |
||||
"github.com/gocolly/colly" |
||||
"testing" |
||||
"time" |
||||
) |
||||
|
||||
func TestInit(t *testing.T) { |
||||
Init() |
||||
|
||||
c := colly.NewCollector() |
||||
SinaStock_spider := sina.NewSinaStock(c) |
||||
SinaStock_spider.Start() |
||||
|
||||
tt := time.NewTicker(time.Second * 3) |
||||
for { |
||||
select { |
||||
case <- tt.C: |
||||
fmt.Println(stock.G_STOCK_MANAGER.StockList["sh600000"]) |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,19 @@ |
||||
package sina |
||||
|
||||
import ( |
||||
"github.com/gocolly/colly" |
||||
"time" |
||||
) |
||||
|
||||
type ISpiderIntface interface { |
||||
Url() string |
||||
Collector(c *colly.Collector) |
||||
OnRequest(r *colly.Request) |
||||
OnResponse(res *colly.Response) |
||||
Start() |
||||
} |
||||
|
||||
type Spider struct { |
||||
C *colly.Collector |
||||
Timer *time.Ticker // 个股
|
||||
} |
@ -0,0 +1,63 @@ |
||||
package sina |
||||
|
||||
import ( |
||||
"easyquotation/stock" |
||||
"easyquotation/utils" |
||||
"fmt" |
||||
"github.com/gocolly/colly" |
||||
"log" |
||||
"time" |
||||
) |
||||
|
||||
type SinaIndex struct { |
||||
Spider |
||||
} |
||||
|
||||
func (s *SinaIndex) Url() string { |
||||
return fmt.Sprintf("http://hq.sinajs.cn/rn=%s&list=", utils.Random(5)) |
||||
} |
||||
|
||||
func NewSinaIndex(c *colly.Collector) *SinaIndex { |
||||
s := &SinaIndex{Spider{Timer:time.NewTicker(time.Second * 10)}} |
||||
s.Collector(c) |
||||
s.C.OnRequest(s.OnRequest) |
||||
s.C.OnResponse(s.OnResponse) |
||||
return s |
||||
} |
||||
func (s *SinaIndex) Collector(c *colly.Collector) { |
||||
s.Spider.C = c.Clone() |
||||
} |
||||
|
||||
func (s *SinaIndex) OnRequest(r *colly.Request) { |
||||
} |
||||
|
||||
func (s *SinaIndex) OnResponse(res *colly.Response) { |
||||
log.Println(string(res.Body)) |
||||
} |
||||
|
||||
func (s *SinaIndex) Start() { |
||||
go func() { |
||||
for { |
||||
select { |
||||
case <- s.Spider.Timer.C: |
||||
ipos := 1 |
||||
params := make([]string, 0) |
||||
param := "" |
||||
for k, _ := range stock.G_STOCK_MANAGER.StockList { |
||||
if ipos%800 == 0 { |
||||
params = append(params, param) |
||||
param = "" |
||||
} |
||||
param += k |
||||
param += "," |
||||
ipos++ |
||||
} |
||||
params = append(params, param) |
||||
for _, str := range params { |
||||
url := s.Url() + str |
||||
go s.C.Visit(url) |
||||
} |
||||
} |
||||
} |
||||
}() |
||||
} |
@ -0,0 +1,104 @@ |
||||
package sina |
||||
|
||||
import ( |
||||
"easyquotation/stock" |
||||
"easyquotation/utils" |
||||
"fmt" |
||||
"github.com/gocolly/colly" |
||||
"reflect" |
||||
"strings" |
||||
"time" |
||||
) |
||||
|
||||
type SinaStock struct { |
||||
Spider |
||||
} |
||||
|
||||
func NewSinaStock(c *colly.Collector) *SinaStock { |
||||
s := &SinaStock{Spider{Timer: time.NewTicker(time.Second * 3)}} |
||||
s.Collector(c) |
||||
s.C.OnRequest(s.OnRequest) |
||||
s.C.OnResponse(s.OnResponse) |
||||
return s |
||||
} |
||||
func (s *SinaStock) Collector(c *colly.Collector) { |
||||
s.Spider.C = c.Clone() |
||||
} |
||||
func (s *SinaStock) Url() string { |
||||
return fmt.Sprintf("http://hq.sinajs.cn/rn=%d&list=", time.Now().Unix()) |
||||
} |
||||
|
||||
func (s *SinaStock) OnRequest(r *colly.Request) { |
||||
// fmt.Println(r.URL)
|
||||
} |
||||
|
||||
func (s *SinaStock) Split(r rune) bool { |
||||
return r == ' ' || r == '_' || r == '=' |
||||
} |
||||
func (s *SinaStock) DecodeMarket(str string) { |
||||
l := utils.SplitString(str, s.Split) |
||||
if (len(l) < 5){ |
||||
return |
||||
} |
||||
symbol := l[3] // 代码
|
||||
market := l[4][1 : len(l[4])-1] // 去除左右" "
|
||||
marketList := strings.Split(market, ",") |
||||
if (len(marketList) < 33) { |
||||
return |
||||
} |
||||
a := stock.Market{} |
||||
eleVals := reflect.ValueOf(&a).Elem() |
||||
utils.DecodeStock(marketList[:10], eleVals) |
||||
// 买五档
|
||||
for i := 0; i < 10; i += 2 { |
||||
bEntry := stock.Entry{} |
||||
vals := reflect.ValueOf(&bEntry).Elem() |
||||
utils.DecodeStock(marketList[10+i:10+i+2], vals) |
||||
a.BuyEntryList = append(a.BuyEntryList, bEntry) |
||||
} |
||||
// 卖五档
|
||||
for i := 0; i < 10; i += 2 { |
||||
bEntry := stock.Entry{} |
||||
vals := reflect.ValueOf(&bEntry).Elem() |
||||
utils.DecodeStock(marketList[20+i:20+i+2], vals) |
||||
a.SellEntryList = append(a.SellEntryList, bEntry) |
||||
} |
||||
a.Date = marketList[30] |
||||
a.Time = marketList[31] |
||||
a.Flag = marketList[32] |
||||
stock.G_STOCK_MANAGER.StockList[symbol].Market = a |
||||
} |
||||
|
||||
func (s *SinaStock) OnResponse(res *colly.Response) { |
||||
l := strings.Split(string(res.Body), ";") |
||||
for _, str := range l { |
||||
s.DecodeMarket(str) |
||||
} |
||||
} |
||||
|
||||
func (s *SinaStock) Start() { |
||||
go func() { |
||||
for { |
||||
select { |
||||
case <-s.Spider.Timer.C: |
||||
ipos := 1 |
||||
params := make([]string, 0) |
||||
param := "" |
||||
for k, _ := range stock.G_STOCK_MANAGER.StockList { |
||||
if ipos%800 == 0 { |
||||
params = append(params, param) |
||||
param = "" |
||||
} |
||||
param += k |
||||
param += "," |
||||
ipos++ |
||||
} |
||||
params = append(params, param) |
||||
for _, str := range params { |
||||
url := s.Url() + str |
||||
go s.C.Visit(url) |
||||
} |
||||
} |
||||
} |
||||
}() |
||||
} |
@ -0,0 +1,2 @@ |
||||
package sina |
||||
|
@ -0,0 +1,47 @@ |
||||
package stock |
||||
|
||||
const ( |
||||
TICK_DAY_NUM = 4*60 + 2 // 分时数据量
|
||||
) |
||||
|
||||
type Base struct { |
||||
Symbol string // 代码
|
||||
Name string // 全称
|
||||
Short string // 简称
|
||||
} |
||||
|
||||
type Entry struct { |
||||
Qty float64 `json:"qty"` // 数量
|
||||
Price float64 `json:"price"` // 价格
|
||||
} |
||||
|
||||
type Tick struct { |
||||
Time string // 时间
|
||||
LastPrice float64 // 最新价
|
||||
AvgPrice float64 // 均价
|
||||
Volumn float64 // 成交量
|
||||
Value float64 // 成交额
|
||||
} |
||||
|
||||
type Market struct { |
||||
Name string `json:"name"` // 名称
|
||||
Open float64 `json:"open"` // 开盘
|
||||
PreClose float64 `json:"pre_close"` // 昨收
|
||||
LastPrice float64 `json:"last_price"` // 最新价
|
||||
High float64 `json:"high"` // 最高价
|
||||
Low float64 `json:"low"` // 最低价
|
||||
BidPice float64 `json:"bid_pice"` // 竞买价
|
||||
OfferPice float64 `json:"offer_pice"` // 竞卖价
|
||||
Volumn float64 `json:"volumn"` // 成交量(股)
|
||||
Value float64 `json:"value"` // 成交额(元)
|
||||
BuyEntryList []Entry `json:"buy_entry_list"` // 买5档
|
||||
SellEntryList []Entry `json:"sell_entry_list"` // 卖5档
|
||||
Date string `json:"date"` // 日期
|
||||
Time string `json:"time"` // 时间
|
||||
Flag string `json:"flag"` // 状态
|
||||
} |
||||
|
||||
type Stock struct { |
||||
Base |
||||
Market |
||||
} |
@ -0,0 +1,66 @@ |
||||
package stock |
||||
|
||||
import ( |
||||
"bufio" |
||||
"easyquotation/utils" |
||||
"fmt" |
||||
"os" |
||||
"reflect" |
||||
"strings" |
||||
) |
||||
|
||||
var G_STOCK_MANAGER *StockManager |
||||
|
||||
type StockManager struct { |
||||
StockList map[string]*Stock //个股
|
||||
IndexList map[string]*Stock //指数
|
||||
BKkList map[string]*Stock //板块
|
||||
ZZIndexList map[string]*Stock //中证指数
|
||||
} |
||||
|
||||
func (s *StockManager) SetBase(list []string) *Stock { |
||||
b := Base{} |
||||
eleVals := reflect.ValueOf(&b).Elem() |
||||
utils.DecodeStock(list, eleVals) |
||||
return &Stock{Base: b} |
||||
} |
||||
|
||||
// 加载股票信息
|
||||
func (s *StockManager) Load() { |
||||
path, err := os.Getwd() |
||||
if err != nil { |
||||
path = "./" |
||||
} |
||||
if file, err := os.Open(path + "/stock_codes"); err != nil { |
||||
panic(err) |
||||
} else { |
||||
scanner := bufio.NewScanner(file) |
||||
for scanner.Scan() { |
||||
line := scanner.Text() |
||||
eleList := strings.Split(line, " ") |
||||
if len(eleList) < 3 { |
||||
continue |
||||
} else { |
||||
if strings.HasPrefix(eleList[0], "zz") { |
||||
s.ZZIndexList[eleList[0]] = s.SetBase(eleList) |
||||
} else { |
||||
if utils.EndSwitch(eleList[1], []string{".沪指数", ".深指数"}) { |
||||
if !strings.HasPrefix(eleList[0], "sh") { |
||||
eleList[0] = fmt.Sprintf("sz%s", eleList[0]) |
||||
} |
||||
s.IndexList[eleList[0]] = s.SetBase(eleList) |
||||
} else if strings.HasSuffix(eleList[1], ".板块") { |
||||
s.BKkList[eleList[0]] = s.SetBase(eleList) |
||||
} else { |
||||
if utils.StartSwitch(eleList[0], []string{"5", "6", "9"}) { |
||||
eleList[0] = fmt.Sprintf("sh%s", eleList[0]) |
||||
} else { |
||||
eleList[0] = fmt.Sprintf("sz%s", eleList[0]) |
||||
} |
||||
s.StockList[eleList[0]] = s.SetBase(eleList) |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,6 @@ |
||||
package stock |
||||
|
||||
import "testing" |
||||
|
||||
func TestStockManager_Load(t *testing.T) { |
||||
} |
@ -0,0 +1,131 @@ |
||||
package utils |
||||
|
||||
import ( |
||||
"io/ioutil" |
||||
"math/rand" |
||||
"net/http" |
||||
"os" |
||||
"reflect" |
||||
"strconv" |
||||
"strings" |
||||
"time" |
||||
"unsafe" |
||||
"errors" |
||||
) |
||||
|
||||
func UpdateStockCodes() { |
||||
path, err := os.Getwd() |
||||
if err != nil { |
||||
path = "./" |
||||
} |
||||
req, err := http.NewRequest("GET", "http://www.shdjt.com/js/lib/astock.js", nil) |
||||
if err != nil { |
||||
return |
||||
} |
||||
resp, err := http.DefaultClient.Do(req) |
||||
if err != nil { |
||||
return |
||||
} |
||||
defer resp.Body.Close() |
||||
|
||||
respBody, err := ioutil.ReadAll(resp.Body) |
||||
|
||||
list := strings.Split(string(respBody), "=") |
||||
list = strings.Split(list[1][1:len(list[1])-1], "~") |
||||
if fileObj, err := os.OpenFile(path+"/stock_codes", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666); err == nil { |
||||
defer fileObj.Close() |
||||
|
||||
realList := list[1:] |
||||
realLen := len(realList) |
||||
for index0, item := range realList { |
||||
l := strings.Split(item, "`") |
||||
for _, s := range l { |
||||
fileObj.WriteString(s + " ") |
||||
} |
||||
if index0 != realLen-1 { |
||||
fileObj.WriteString("\n") |
||||
} |
||||
|
||||
} |
||||
} |
||||
} |
||||
|
||||
func StartSwitch(s string, prefixs []string) bool { |
||||
for _, prefix := range prefixs { |
||||
if strings.HasPrefix(s, prefix) == true { |
||||
return true |
||||
} |
||||
} |
||||
return false |
||||
} |
||||
|
||||
func EndSwitch(s string, suffixs []string) bool { |
||||
for _, suffix := range suffixs { |
||||
if strings.HasSuffix(s, suffix) == true { |
||||
return true |
||||
} |
||||
} |
||||
return false |
||||
} |
||||
|
||||
// 生成随机串
|
||||
// ref:https://cloud.tencent.com/developer/article/1580647
|
||||
var src = rand.NewSource(time.Now().UnixNano()) |
||||
|
||||
const letterBytes = "abcdefghijklmnopqrstuvwxyz0123456789" |
||||
const ( |
||||
letterIdxBits = 6 // 6 bits to represent a letter index
|
||||
letterIdxMask = 1<<letterIdxBits - 1 // All 1-bits, as many as letterIdxBits
|
||||
letterIdxMax = 63 / letterIdxBits // # of letter indices fitting in 63 bits
|
||||
) |
||||
|
||||
func Random(n int) (s string) { |
||||
|
||||
b := make([]byte, n) |
||||
for i, cache, remain := n-1, src.Int63(), letterIdxMax; i >= 0; { |
||||
if remain == 0 { |
||||
cache, remain = src.Int63(), letterIdxMax |
||||
} |
||||
if idx := int(cache & letterIdxMask); idx < len(letterBytes) { |
||||
b[i] = letterBytes[idx] |
||||
i-- |
||||
} |
||||
cache >>= letterIdxBits |
||||
remain-- |
||||
} |
||||
|
||||
return *(*string)(unsafe.Pointer(&b)) |
||||
} |
||||
|
||||
func Min(a ...int) int { |
||||
m := int(^uint(0) >> 1) |
||||
for _, i := range a { |
||||
if i < m { |
||||
m = i |
||||
} |
||||
} |
||||
return m |
||||
} |
||||
|
||||
func SplitString(s string, f func(rune) bool) []string { |
||||
a := strings.FieldsFunc(s, f) |
||||
return a |
||||
} |
||||
|
||||
func DecodeStock(list []string, data reflect.Value) { |
||||
length := Min(len(list), data.NumField()) |
||||
for i := 0; i < length; i++ { |
||||
switch data.Field(i).Kind() { |
||||
case reflect.String: |
||||
data.Field(i).SetString(list[i]) |
||||
case reflect.Int: |
||||
i64, _ := strconv.ParseInt(list[i], 10,64) |
||||
data.Field(i).SetInt(i64) |
||||
case reflect.Float64: |
||||
f, _ := strconv.ParseFloat(list[i], 64) |
||||
data.Field(i).SetFloat(f) |
||||
default: |
||||
panic(errors.New("Decode error")) |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,20 @@ |
||||
package utils |
||||
|
||||
import ( |
||||
"fmt" |
||||
"testing" |
||||
) |
||||
|
||||
func TestUpdateStockCodes(t *testing.T) { |
||||
UpdateStockCodes() |
||||
} |
||||
|
||||
func TestStockMarket(t *testing.T) { |
||||
|
||||
} |
||||
|
||||
func TestRandom(t *testing.T) { |
||||
for i:=0; i< 100; i++ { |
||||
fmt.Println(Random(5)) |
||||
} |
||||
} |
Loading…
Reference in new issue