|
|
@ -13,6 +13,11 @@ import ( |
|
|
|
"reflect" |
|
|
|
"reflect" |
|
|
|
"strconv" |
|
|
|
"strconv" |
|
|
|
"strings" |
|
|
|
"strings" |
|
|
|
|
|
|
|
//"io"
|
|
|
|
|
|
|
|
"time" |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
"easyquotation/stock" |
|
|
|
|
|
|
|
mqtt "github.com/eclipse/paho.mqtt.golang" |
|
|
|
) |
|
|
|
) |
|
|
|
|
|
|
|
|
|
|
|
// const quotesURL = `http://download.finance.yahoo.com/d/quotes.csv?s=%s&f=sl1c1p2oghjkva2r2rdyj3j1`
|
|
|
|
// const quotesURL = `http://download.finance.yahoo.com/d/quotes.csv?s=%s&f=sl1c1p2oghjkva2r2rdyj3j1`
|
|
|
@ -47,6 +52,12 @@ type Stock struct { |
|
|
|
AfterHours string `json:"postMarketChangePercent,omitempty"` |
|
|
|
AfterHours string `json:"postMarketChangePercent,omitempty"` |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
type stockinfo struct { |
|
|
|
|
|
|
|
Scode string |
|
|
|
|
|
|
|
Sname string |
|
|
|
|
|
|
|
Ft string |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Quotes stores relevant pointers as well as the array of stock quotes for
|
|
|
|
// Quotes stores relevant pointers as well as the array of stock quotes for
|
|
|
|
// the tickers we are tracking.
|
|
|
|
// the tickers we are tracking.
|
|
|
|
type Quotes struct { |
|
|
|
type Quotes struct { |
|
|
@ -54,17 +65,59 @@ type Quotes struct { |
|
|
|
profile *Profile // Pointer to Profile.
|
|
|
|
profile *Profile // Pointer to Profile.
|
|
|
|
stocks []Stock // Array of stock quote data.
|
|
|
|
stocks []Stock // Array of stock quote data.
|
|
|
|
errors string // Error string if any.
|
|
|
|
errors string // Error string if any.
|
|
|
|
|
|
|
|
res map[string]*stock.Stock |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
watchlist *Watchlist |
|
|
|
|
|
|
|
client mqtt.Client |
|
|
|
|
|
|
|
upstocks map[string]string |
|
|
|
|
|
|
|
totalstocks []stockinfo |
|
|
|
|
|
|
|
needrefresh bool |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Sets the initial values and returns new Quotes struct.
|
|
|
|
// Sets the initial values and returns new Quotes struct.
|
|
|
|
func NewQuotes(market *Market, profile *Profile) *Quotes { |
|
|
|
func NewQuotes(market *Market, profile *Profile, res map[string]*stock.Stock, watchlist *Watchlist, client mqtt.Client) *Quotes { |
|
|
|
|
|
|
|
/*var watchlist Watchlist |
|
|
|
|
|
|
|
err := json.NewDecoder(respbody).Decode(&watchlist) |
|
|
|
|
|
|
|
if err != nil { |
|
|
|
|
|
|
|
// Handle error
|
|
|
|
|
|
|
|
fmt.Println(err) |
|
|
|
|
|
|
|
}*/ |
|
|
|
return &Quotes{ |
|
|
|
return &Quotes{ |
|
|
|
market: market, |
|
|
|
market: market, |
|
|
|
profile: profile, |
|
|
|
profile: profile, |
|
|
|
errors: ``, |
|
|
|
errors: ``, |
|
|
|
|
|
|
|
res: res, |
|
|
|
|
|
|
|
watchlist: watchlist, |
|
|
|
|
|
|
|
client: client, |
|
|
|
|
|
|
|
totalstocks: []stockinfo{}, |
|
|
|
|
|
|
|
upstocks: map[string]string{}, |
|
|
|
|
|
|
|
needrefresh: true, |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Define a struct that matches the structure of the JSON
|
|
|
|
|
|
|
|
type WatchlistItem struct { |
|
|
|
|
|
|
|
Scode string `json:"scode"` |
|
|
|
|
|
|
|
AnalyseFrom string `json:"analyse_from"` |
|
|
|
|
|
|
|
AnalyseDay string `json:"analyse_day"` |
|
|
|
|
|
|
|
Enterprice string `json:"enterprice"` |
|
|
|
|
|
|
|
Enterdays int `json:"enterdays"` |
|
|
|
|
|
|
|
Exchangerate string `json:"exchangerate"` |
|
|
|
|
|
|
|
Uplist []int `json:"uplist"` |
|
|
|
|
|
|
|
Last3days string `json:"last3days"` |
|
|
|
|
|
|
|
Daysback int `json:"daysback"` |
|
|
|
|
|
|
|
Inhklist bool `json:"inhklist"` |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
type Watchlist struct { |
|
|
|
|
|
|
|
Baseon string `json:"baseon"` |
|
|
|
|
|
|
|
Pdate string `json:"pdate"` |
|
|
|
|
|
|
|
Preparam int `json:"preparam"` |
|
|
|
|
|
|
|
Dates []string `json:"dates"` |
|
|
|
|
|
|
|
Total int `json:"total"` |
|
|
|
|
|
|
|
Watchlist []WatchlistItem `json:"watchlist"` |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Fetch the latest stock quotes and parse raw fetched data into array of
|
|
|
|
// Fetch the latest stock quotes and parse raw fetched data into array of
|
|
|
|
// []Stock structs.
|
|
|
|
// []Stock structs.
|
|
|
|
func (quotes *Quotes) Fetch() (self *Quotes) { |
|
|
|
func (quotes *Quotes) Fetch() (self *Quotes) { |
|
|
@ -78,6 +131,53 @@ func (quotes *Quotes) Fetch() (self *Quotes) { |
|
|
|
} |
|
|
|
} |
|
|
|
}() |
|
|
|
}() |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if quotes.profile.mode == "review" { |
|
|
|
|
|
|
|
//fmt.Println("review mode")
|
|
|
|
|
|
|
|
if quotes.res["sh600000"].Market.Open == 0 { |
|
|
|
|
|
|
|
return quotes |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
//fmt.Println("review mode")
|
|
|
|
|
|
|
|
var watchlist_selected []WatchlistItem |
|
|
|
|
|
|
|
var baseonlist []string |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//date_json := []string{"2023-04-24"}
|
|
|
|
|
|
|
|
for _, date := range quotes.profile.date_json { |
|
|
|
|
|
|
|
url := fmt.Sprintf("http://119.29.166.226/q/dayjson/%sml.json", date) |
|
|
|
|
|
|
|
response, err := http.Get(url) |
|
|
|
|
|
|
|
if err != nil { |
|
|
|
|
|
|
|
// Handle error
|
|
|
|
|
|
|
|
fmt.Println(err) |
|
|
|
|
|
|
|
continue |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
defer response.Body.Close() |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var watchlist Watchlist |
|
|
|
|
|
|
|
error := json.NewDecoder(response.Body).Decode(&watchlist) |
|
|
|
|
|
|
|
if error != nil { |
|
|
|
|
|
|
|
// Handle error
|
|
|
|
|
|
|
|
fmt.Println(error) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
for _, item := range watchlist.Watchlist { |
|
|
|
|
|
|
|
dayinfo := item.Last3days |
|
|
|
|
|
|
|
if _, ok := quotes.res[item.Scode]; ok { |
|
|
|
|
|
|
|
if dayinfo[4] == '|' { |
|
|
|
|
|
|
|
item.AnalyseFrom = date |
|
|
|
|
|
|
|
watchlist_selected = append(watchlist_selected, item) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
baseonlist = append(baseonlist, watchlist.Baseon) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
quotes.watchlist.Watchlist = watchlist_selected |
|
|
|
|
|
|
|
if len(baseonlist) > 0 { |
|
|
|
|
|
|
|
quotes.watchlist.Baseon = strings.Join(baseonlist, ",") |
|
|
|
|
|
|
|
}else{ |
|
|
|
|
|
|
|
quotes.watchlist.Baseon = "" |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
quotes.parsereview(quotes.res) |
|
|
|
|
|
|
|
}else{ |
|
|
|
url := fmt.Sprintf(quotesURLv7, strings.Join(quotes.profile.Tickers, `,`)) |
|
|
|
url := fmt.Sprintf(quotesURLv7, strings.Join(quotes.profile.Tickers, `,`)) |
|
|
|
response, err := http.Get(url + quotesURLv7QueryParts) |
|
|
|
response, err := http.Get(url + quotesURLv7QueryParts) |
|
|
|
if err != nil { |
|
|
|
if err != nil { |
|
|
@ -89,13 +189,123 @@ func (quotes *Quotes) Fetch() (self *Quotes) { |
|
|
|
if err != nil { |
|
|
|
if err != nil { |
|
|
|
panic(err) |
|
|
|
panic(err) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
res := quotes.res |
|
|
|
|
|
|
|
quotes.parse2(body, res) |
|
|
|
|
|
|
|
//fmt.Println(res["sh600111"])
|
|
|
|
|
|
|
|
//fmt.Println("mode:", quotes.profile.mode)
|
|
|
|
|
|
|
|
|
|
|
|
quotes.parse2(body) |
|
|
|
} |
|
|
|
|
|
|
|
}else{ |
|
|
|
|
|
|
|
fmt.Println("***not ready***") |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
return quotes |
|
|
|
return quotes |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//write a function to save the slice of quotes.stocks to file
|
|
|
|
|
|
|
|
func (quotes *Quotes) SaveStocks() { |
|
|
|
|
|
|
|
//fmt.Println("save")
|
|
|
|
|
|
|
|
//fmt.Println(quotes.stocks)
|
|
|
|
|
|
|
|
//fmt.Println(quotes.profile.mode)
|
|
|
|
|
|
|
|
filedata, err := json.MarshalIndent(quotes.totalstocks, "", " ") |
|
|
|
|
|
|
|
if err != nil { |
|
|
|
|
|
|
|
fmt.Println(err) |
|
|
|
|
|
|
|
return |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
err = ioutil.WriteFile("stocklist.json", filedata, 0644) |
|
|
|
|
|
|
|
if err != nil { |
|
|
|
|
|
|
|
fmt.Println(err) |
|
|
|
|
|
|
|
return |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//read stocks
|
|
|
|
|
|
|
|
func (quotes *Quotes) ReadStocks() { |
|
|
|
|
|
|
|
filedata, err := ioutil.ReadFile("stocklist.json") |
|
|
|
|
|
|
|
if err != nil { |
|
|
|
|
|
|
|
fmt.Println(err) |
|
|
|
|
|
|
|
return |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var stocks []stockinfo |
|
|
|
|
|
|
|
err = json.Unmarshal(filedata, &stocks) |
|
|
|
|
|
|
|
if err != nil { |
|
|
|
|
|
|
|
fmt.Println(err) |
|
|
|
|
|
|
|
return |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func (quotes *Quotes)Addstockcodetofile(codestoadd []string) { |
|
|
|
|
|
|
|
var stockcode []string |
|
|
|
|
|
|
|
for _, item := range quotes.watchlist.Watchlist { |
|
|
|
|
|
|
|
stockcode = append(stockcode, item.Scode) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
stockcode = append(stockcode, quotes.profile.Tickers...) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if len(codestoadd) > 0 { |
|
|
|
|
|
|
|
stockcode = append(stockcode, codestoadd...) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
// Marshal the combined array to a JSON-encoded byte slice
|
|
|
|
|
|
|
|
data, err := json.Marshal(stockcode) |
|
|
|
|
|
|
|
if err != nil { |
|
|
|
|
|
|
|
fmt.Println(err) |
|
|
|
|
|
|
|
return |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Write the byte slice to a file
|
|
|
|
|
|
|
|
err = ioutil.WriteFile("stock_in.json", data, 0644) |
|
|
|
|
|
|
|
if err != nil { |
|
|
|
|
|
|
|
fmt.Println(err) |
|
|
|
|
|
|
|
return |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func gettimediff(start string, end string) time.Duration { |
|
|
|
|
|
|
|
layout := "15:04:05" |
|
|
|
|
|
|
|
closepm,_ := time.Parse(layout, "15:01:00") |
|
|
|
|
|
|
|
openpm, _ := time.Parse(layout, "12:59:59") |
|
|
|
|
|
|
|
closespan := -90 * time.Minute |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if start =="" || end ==""{ |
|
|
|
|
|
|
|
return 0*time.Second |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
t1, err := time.Parse(layout, start) |
|
|
|
|
|
|
|
if err != nil { |
|
|
|
|
|
|
|
fmt.Println(err) |
|
|
|
|
|
|
|
return 0*time.Second |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
t2, err := time.Parse(layout, end) |
|
|
|
|
|
|
|
if err != nil { |
|
|
|
|
|
|
|
fmt.Println(err) |
|
|
|
|
|
|
|
return 0*time.Second |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if t2.After(closepm) { |
|
|
|
|
|
|
|
return 0*time.Second |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if t1.After(openpm) { |
|
|
|
|
|
|
|
t1 = t1.Add(closespan) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if t2.After(openpm) { |
|
|
|
|
|
|
|
t2 = t2.Add(closespan) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 计算时间差
|
|
|
|
|
|
|
|
diff := t2.Sub(t1) |
|
|
|
|
|
|
|
if diff > time.Hour { |
|
|
|
|
|
|
|
//fmt.Printf("Time difference between %s and %s: %v\n", end, start, diff)
|
|
|
|
|
|
|
|
return diff |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return 0*time.Second |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Ok returns two values: 1) boolean indicating whether the error has occurred,
|
|
|
|
// Ok returns two values: 1) boolean indicating whether the error has occurred,
|
|
|
|
// and 2) the error text itself.
|
|
|
|
// and 2) the error text itself.
|
|
|
|
func (quotes *Quotes) Ok() (bool, string) { |
|
|
|
func (quotes *Quotes) Ok() (bool, string) { |
|
|
@ -126,23 +336,164 @@ func (quotes *Quotes) RemoveTickers(tickers []string) (removed int, err error) { |
|
|
|
// market is still open and we might want to grab the latest quotes. In both
|
|
|
|
// market is still open and we might want to grab the latest quotes. In both
|
|
|
|
// cases we make sure the list of requested tickers is not empty.
|
|
|
|
// cases we make sure the list of requested tickers is not empty.
|
|
|
|
func (quotes *Quotes) isReady() bool { |
|
|
|
func (quotes *Quotes) isReady() bool { |
|
|
|
return (quotes.stocks == nil || !quotes.market.IsClosed) && len(quotes.profile.Tickers) > 0 |
|
|
|
return (quotes.stocks == nil || !quotes.market.IsClosed) && len(quotes.profile.Tickers) > 0 && quotes.needrefresh == true |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func inlist(slice []string, str string) bool { |
|
|
|
|
|
|
|
for _, s := range slice { |
|
|
|
|
|
|
|
if s == str { |
|
|
|
|
|
|
|
return true |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
return false |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func contains(slice []stockinfo, str string) (bool, int) { |
|
|
|
|
|
|
|
for i, s := range slice { |
|
|
|
|
|
|
|
if s.Scode == str { |
|
|
|
|
|
|
|
return true, i |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
return false, -1 |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func indexer(slice []stockinfo, str string) int { |
|
|
|
|
|
|
|
for i, s := range slice { |
|
|
|
|
|
|
|
if s.Scode == str { |
|
|
|
|
|
|
|
return i+1 |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
return 0 |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func (quotes *Quotes) getitembyscode(scode string) WatchlistItem { |
|
|
|
|
|
|
|
watchlist := quotes.watchlist.Watchlist |
|
|
|
|
|
|
|
for _, s := range watchlist { |
|
|
|
|
|
|
|
if s.Scode == scode { |
|
|
|
|
|
|
|
return s |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
return WatchlistItem{} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func padString(str string, length int) string { |
|
|
|
|
|
|
|
if len(str) < length { |
|
|
|
|
|
|
|
str = str + strings.Repeat(" ", length-len(str)) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
return str |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func (quotes *Quotes) Sendstockgraphreq(index int, istime bool) { |
|
|
|
|
|
|
|
if index > len(quotes.totalstocks){ |
|
|
|
|
|
|
|
return |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
topic := "my/topic" |
|
|
|
|
|
|
|
itemsel := quotes.getitembyscode(quotes.totalstocks[index-1].Scode) |
|
|
|
|
|
|
|
data := map[string]interface{}{ |
|
|
|
|
|
|
|
"scode": quotes.totalstocks[index-1].Scode, |
|
|
|
|
|
|
|
"tier": 0, |
|
|
|
|
|
|
|
"daysback": itemsel.Daysback, |
|
|
|
|
|
|
|
"stdprice": itemsel.Enterprice, |
|
|
|
|
|
|
|
"name": strings.TrimSpace(quotes.totalstocks[index-1].Sname), |
|
|
|
|
|
|
|
"ed": itemsel.AnalyseDay, |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
if istime { |
|
|
|
|
|
|
|
currentTime := time.Now() |
|
|
|
|
|
|
|
data["date"] = currentTime.Format("2006-01-02") |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
jsonData, err := json.Marshal(data) |
|
|
|
|
|
|
|
if err != nil { |
|
|
|
|
|
|
|
fmt.Println(err) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
message := string(jsonData) |
|
|
|
|
|
|
|
token := quotes.client.Publish(topic, 0, false, message) |
|
|
|
|
|
|
|
token.Wait() |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func (quotes *Quotes) parsereview(res map[string]*stock.Stock) (*Quotes, error) { |
|
|
|
|
|
|
|
var scodes []string |
|
|
|
|
|
|
|
fmt.Println("Start parsing review") |
|
|
|
|
|
|
|
wamap := make(map[string]WatchlistItem, len(quotes.watchlist.Watchlist)) |
|
|
|
|
|
|
|
for _, item := range quotes.watchlist.Watchlist { |
|
|
|
|
|
|
|
scodes = append(scodes, item.Scode) |
|
|
|
|
|
|
|
wamap[item.Scode] = item |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var snames []string |
|
|
|
|
|
|
|
quotes.stocks = make([]Stock, len(scodes))
|
|
|
|
|
|
|
|
fmt.Println(scodes) |
|
|
|
|
|
|
|
for i, scode := range scodes { |
|
|
|
|
|
|
|
q := res[scode].Market |
|
|
|
|
|
|
|
b := res[scode].Base |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
quotes.totalstocks = append(quotes.totalstocks, stockinfo{scode, b.Name, q.Time}) |
|
|
|
|
|
|
|
quotes.upstocks[scode] = q.Time |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
open, close, high, low, ndays := getnextdaysHL(scode, wamap[scode].AnalyseFrom) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
quotes.stocks[i].Ticker = fmt.Sprintf("%02d", indexer(quotes.totalstocks ,scode)) + padString(b.Name, 11) |
|
|
|
|
|
|
|
snames = append(snames, b.Name) |
|
|
|
|
|
|
|
quotes.stocks[i].LastTrade = wamap[scode].Enterprice |
|
|
|
|
|
|
|
stdprice, _ := strconv.ParseFloat(wamap[scode].Enterprice, 64) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
thelast := (close - stdprice) / stdprice * 100 |
|
|
|
|
|
|
|
quotes.stocks[i].ChangePct = float2Str(thelast)+"%" |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
quotes.stocks[i].Change = strconv.Itoa(ndays)+"day(s)" |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
theopen := (open - stdprice) / stdprice * 100 |
|
|
|
|
|
|
|
quotes.stocks[i].Open = float2Str(theopen)+"%" |
|
|
|
|
|
|
|
quotes.stocks[i].Low = float2Str(low) |
|
|
|
|
|
|
|
quotes.stocks[i].High = float2Str(high) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
thehigh := (high - stdprice) / stdprice * 100 |
|
|
|
|
|
|
|
thelow := (low - stdprice) / stdprice * 100 |
|
|
|
|
|
|
|
quotes.stocks[i].Low52 = float2Str(thelow) |
|
|
|
|
|
|
|
quotes.stocks[i].High52 = float2Str(thehigh) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
quotes.stocks[i].Volume = "" |
|
|
|
|
|
|
|
quotes.stocks[i].AvgVolume = wamap[scode].AnalyseFrom |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
adv, err := strconv.ParseFloat(quotes.stocks[i].High52, 64) |
|
|
|
|
|
|
|
if err == nil { |
|
|
|
|
|
|
|
if adv < 10.0 { |
|
|
|
|
|
|
|
quotes.stocks[i].Direction = -1 |
|
|
|
|
|
|
|
} else if adv > 10.0 { |
|
|
|
|
|
|
|
quotes.stocks[i].Direction = 1 |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
quotes.stocks[i].Low52 = quotes.stocks[i].Low52 + "%" |
|
|
|
|
|
|
|
quotes.stocks[i].High52 = quotes.stocks[i].High52 + "%" |
|
|
|
|
|
|
|
quotes.stocks[i].MarketCap = quotes.upstocks[scode] |
|
|
|
|
|
|
|
quotes.stocks[i].Dividend = b.Symbol |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if inlist(quotes.profile.Tickers, scode) == true { |
|
|
|
|
|
|
|
quotes.stocks[i].MarketCap = "M" |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
//fmt.Println(scode,"****")
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
quotes.needrefresh = false |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return quotes, nil
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// this will parse the json objects
|
|
|
|
// this will parse the json objects
|
|
|
|
func (quotes *Quotes) parse2(body []byte) (*Quotes, error) { |
|
|
|
func (quotes *Quotes) parse2(body []byte, res map[string]*stock.Stock) (*Quotes, error) { |
|
|
|
// response -> quoteResponse -> result|error (array) -> map[string]interface{}
|
|
|
|
// response -> quoteResponse -> result|error (array) -> map[string]interface{}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Stocks has non-int things
|
|
|
|
// Stocks has non-int things
|
|
|
|
// d := map[string]map[string][]Stock{}
|
|
|
|
// d := map[string]map[string][]Stock{}
|
|
|
|
// some of these are numbers vs strings
|
|
|
|
// some of these are numbers vs strings
|
|
|
|
// d := map[string]map[string][]map[string]string{}
|
|
|
|
// d := map[string]map[string][]map[string]string{}
|
|
|
|
d := map[string]map[string][]map[string]interface{}{} |
|
|
|
//d := map[string]map[string][]map[string]interface{}{}
|
|
|
|
err := json.Unmarshal(body, &d) |
|
|
|
//err := json.Unmarshal(body, &d)
|
|
|
|
if err != nil { |
|
|
|
//if err != nil {
|
|
|
|
return nil, err |
|
|
|
// return nil, err
|
|
|
|
} |
|
|
|
//}
|
|
|
|
results := d["quoteResponse"]["result"] |
|
|
|
//results := d["quoteResponse"]["result"]
|
|
|
|
|
|
|
|
/* |
|
|
|
quotes.stocks = make([]Stock, len(results)) |
|
|
|
quotes.stocks = make([]Stock, len(results)) |
|
|
|
for i, raw := range results { |
|
|
|
for i, raw := range results { |
|
|
|
result := map[string]string{} |
|
|
|
result := map[string]string{} |
|
|
@ -179,14 +530,14 @@ func (quotes *Quotes) parse2(body []byte) (*Quotes, error) { |
|
|
|
quotes.stocks[i].Currency = result["currency"] |
|
|
|
quotes.stocks[i].Currency = result["currency"] |
|
|
|
quotes.stocks[i].PreOpen = result["preMarketChangePercent"] |
|
|
|
quotes.stocks[i].PreOpen = result["preMarketChangePercent"] |
|
|
|
quotes.stocks[i].AfterHours = result["postMarketChangePercent"] |
|
|
|
quotes.stocks[i].AfterHours = result["postMarketChangePercent"] |
|
|
|
/* |
|
|
|
|
|
|
|
fmt.Println(i) |
|
|
|
fmt.Println(i) |
|
|
|
fmt.Println("-------------------") |
|
|
|
fmt.Println("-------------------") |
|
|
|
for k, v := range result { |
|
|
|
for k, v := range result { |
|
|
|
fmt.Println(k, v) |
|
|
|
fmt.Println(k, v) |
|
|
|
} |
|
|
|
} |
|
|
|
fmt.Println("-------------------") |
|
|
|
fmt.Println("-------------------") |
|
|
|
*/ |
|
|
|
|
|
|
|
adv, err := strconv.ParseFloat(quotes.stocks[i].Change, 64) |
|
|
|
adv, err := strconv.ParseFloat(quotes.stocks[i].Change, 64) |
|
|
|
quotes.stocks[i].Direction = 0 |
|
|
|
quotes.stocks[i].Direction = 0 |
|
|
|
if err == nil { |
|
|
|
if err == nil { |
|
|
@ -197,6 +548,164 @@ func (quotes *Quotes) parse2(body []byte) (*Quotes, error) { |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Use the "http.Get" function to fetch the URL and get the response
|
|
|
|
|
|
|
|
response, err := http.Get("http://119.29.166.226/q/dayjson/2023-04-20ml.json")//("http://119.29.166.226/q/dayjson/ml.json")
|
|
|
|
|
|
|
|
if err != nil { |
|
|
|
|
|
|
|
// Handle error
|
|
|
|
|
|
|
|
fmt.Println(err) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
defer response.Body.Close() |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//respbody, err := ioutil.ReadAll(response.Body)
|
|
|
|
|
|
|
|
//if err != nil {
|
|
|
|
|
|
|
|
// handle error
|
|
|
|
|
|
|
|
//}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Use the "json" package to decode the response body into a JSON object
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var watchlist Watchlist |
|
|
|
|
|
|
|
var scodes []string |
|
|
|
|
|
|
|
err = json.NewDecoder(response.Body).Decode(&watchlist) |
|
|
|
|
|
|
|
if err != nil { |
|
|
|
|
|
|
|
// Handle error
|
|
|
|
|
|
|
|
fmt.Println(err) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//var data Data
|
|
|
|
|
|
|
|
//err = json.Unmarshal(respbody, &data)
|
|
|
|
|
|
|
|
if err != nil { |
|
|
|
|
|
|
|
// Handle error
|
|
|
|
|
|
|
|
fmt.Println(err) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
var scodes []string |
|
|
|
|
|
|
|
wamap := make(map[string]WatchlistItem, len(quotes.watchlist.Watchlist)) |
|
|
|
|
|
|
|
for _, item := range quotes.watchlist.Watchlist { |
|
|
|
|
|
|
|
enterPrice, err := strconv.ParseFloat(item.Enterprice, 64) |
|
|
|
|
|
|
|
if err != nil { |
|
|
|
|
|
|
|
// Handle error
|
|
|
|
|
|
|
|
fmt.Println(err) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
if _, ok := res[item.Scode]; ok { |
|
|
|
|
|
|
|
//fmt.Println(item.Scode)
|
|
|
|
|
|
|
|
q := res[item.Scode].Market |
|
|
|
|
|
|
|
//fmt.Println(q.Name, q.PreClose , q.LastPrice ,q.LastPrice , enterPrice)
|
|
|
|
|
|
|
|
if q.PreClose < q.LastPrice && q.LastPrice >= enterPrice && q.PreClose < enterPrice{ |
|
|
|
|
|
|
|
//fmt.Println(enterPrice)
|
|
|
|
|
|
|
|
scodes = append(scodes, item.Scode) |
|
|
|
|
|
|
|
//fmt.Println(scodes)
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
wamap[item.Scode] = item |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
//fmt.Println(scodes)
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
//fmt.Println(scodes)
|
|
|
|
|
|
|
|
//canstocklen := len(scodes)
|
|
|
|
|
|
|
|
for _, item := range quotes.profile.Tickers { |
|
|
|
|
|
|
|
if _, ok := res[item]; ok { |
|
|
|
|
|
|
|
scodes = append(scodes, item) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
//scodes = append(scodes, quotes.profile.Tickers...)
|
|
|
|
|
|
|
|
//fmt.Println(scodes)
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
|
|
var snames []string |
|
|
|
|
|
|
|
quotes.stocks = make([]Stock, len(scodes))
|
|
|
|
|
|
|
|
//fmt.Println(res["sh600000"])
|
|
|
|
|
|
|
|
for i, scode := range scodes { |
|
|
|
|
|
|
|
q := res[scode].Market |
|
|
|
|
|
|
|
b := res[scode].Base |
|
|
|
|
|
|
|
//fmt.Println(q)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
isin, index := contains(quotes.totalstocks, scode) |
|
|
|
|
|
|
|
if isin == false { |
|
|
|
|
|
|
|
quotes.totalstocks = append(quotes.totalstocks, stockinfo{scode, q.Name, q.Time}) |
|
|
|
|
|
|
|
quotes.upstocks[scode] = q.Time |
|
|
|
|
|
|
|
}else { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//compare uptime and a string as time, if the diff is bigger than 1 hour, then change quotes.upstocks[scode] to q.Time
|
|
|
|
|
|
|
|
//quotes.upstocks[scode] = uptime
|
|
|
|
|
|
|
|
diff := gettimediff(quotes.totalstocks[index].Ft, q.Time) |
|
|
|
|
|
|
|
if diff > time.Hour { |
|
|
|
|
|
|
|
quotes.upstocks[scode] = q.Time |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
quotes.totalstocks[index].Ft = q.Time |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
quotes.stocks[i].Ticker = fmt.Sprintf("%02d", indexer(quotes.totalstocks ,scode)) + padString(q.Name, 11) |
|
|
|
|
|
|
|
snames = append(snames, q.Name) |
|
|
|
|
|
|
|
quotes.stocks[i].LastTrade = float2Str(q.LastPrice) |
|
|
|
|
|
|
|
thechange := q.LastPrice - q.PreClose |
|
|
|
|
|
|
|
thechangepercent := thechange / q.PreClose * 100 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
quotes.stocks[i].Change = float2Str(thechange)+"*" |
|
|
|
|
|
|
|
quotes.stocks[i].ChangePct = float2Str(thechangepercent) |
|
|
|
|
|
|
|
quotes.stocks[i].Open = float2Str(q.Open) |
|
|
|
|
|
|
|
quotes.stocks[i].Low = float2Str(q.Low) |
|
|
|
|
|
|
|
quotes.stocks[i].High = float2Str(q.High) |
|
|
|
|
|
|
|
quotes.stocks[i].Low52 = float2Str(q.BidPice) |
|
|
|
|
|
|
|
quotes.stocks[i].High52 = float2Str(q.OfferPice) |
|
|
|
|
|
|
|
quotes.stocks[i].Volume = float2Str(q.Volumn) |
|
|
|
|
|
|
|
quotes.stocks[i].MarketCap = q.Time |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
adv, err := strconv.ParseFloat(quotes.stocks[i].Change, 64) |
|
|
|
|
|
|
|
//quotes.stocks[i].Direction = 0
|
|
|
|
|
|
|
|
//fmt.Println(q.LastPrice, q.High, q.Low)
|
|
|
|
|
|
|
|
/**/ |
|
|
|
|
|
|
|
if err == nil { |
|
|
|
|
|
|
|
if adv < 0.0 { |
|
|
|
|
|
|
|
quotes.stocks[i].Direction = -1 |
|
|
|
|
|
|
|
} else if adv > 0.0 { |
|
|
|
|
|
|
|
quotes.stocks[i].Direction = 1 |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if q.LastPrice == q.High { |
|
|
|
|
|
|
|
quotes.stocks[i].Direction = -1 |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
quotes.stocks[i].Direction = 1 |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if q.OfferPice == 0 { |
|
|
|
|
|
|
|
quotes.stocks[i].Direction = 1 |
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if q.OfferPice >= q.High { |
|
|
|
|
|
|
|
quotes.stocks[i].Direction = -1 |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
quotes.stocks[i].AvgVolume = quotes.upstocks[scode] |
|
|
|
|
|
|
|
quotes.stocks[i].Dividend = b.Symbol |
|
|
|
|
|
|
|
if _, ok := wamap[scode]; ok { |
|
|
|
|
|
|
|
strprice, _ := strconv.ParseFloat(wamap[scode].Enterprice, 64) |
|
|
|
|
|
|
|
stdchange := q.LastPrice - strprice |
|
|
|
|
|
|
|
quotes.stocks[i].Change = float2Str(stdchange) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//quotes.stocks[i].PreOpen = wamap[scode].Enterprice
|
|
|
|
|
|
|
|
//quotes.stocks[i].AfterHours = strconv.Itoa(wamap[scode].Daysback)
|
|
|
|
|
|
|
|
//quotes.stocks[i].Yield = wamap[scode].AnalyseDay
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
if inlist(quotes.profile.Tickers, scode) == true { |
|
|
|
|
|
|
|
quotes.stocks[i].AvgVolume = "M" |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//quotes.stocks[0].PeRatio = result["trailingPE"]
|
|
|
|
|
|
|
|
/* |
|
|
|
|
|
|
|
// TODO calculate rt
|
|
|
|
|
|
|
|
quotes.stocks[0].PeRatioX = result["trailingPE"] |
|
|
|
|
|
|
|
quotes.stocks[0].Dividend = result["trailingAnnualDividendRate"] |
|
|
|
|
|
|
|
quotes.stocks[0].Yield = result["trailingAnnualDividendYield"] |
|
|
|
|
|
|
|
quotes.stocks[0].MarketCap = result["marketCap"] |
|
|
|
|
|
|
|
// TODO calculate rt?
|
|
|
|
|
|
|
|
quotes.stocks[0].MarketCapX = result["marketCap"] |
|
|
|
|
|
|
|
quotes.stocks[0].Currency = result["currency"] |
|
|
|
|
|
|
|
quotes.stocks[0].PreOpen = result["preMarketChangePercent"] |
|
|
|
|
|
|
|
quotes.stocks[0].AfterHours = result["postMarketChangePercent"] |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
//fmt.Println(quotes)
|
|
|
|
|
|
|
|
|
|
|
|
return quotes, nil |
|
|
|
return quotes, nil |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|