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