diff --git a/cmd/mop/main.go b/cmd/mop/main.go
index fbf58cd..f304265 100644
--- a/cmd/mop/main.go
+++ b/cmd/mop/main.go
@@ -55,54 +55,96 @@ Enter comma-delimited list of stock tickers when prompted.
Press any key to continue
`
-func getuserinput() string{
+
+
+func getuserinput(preset *Preset, sc mop.Stock) string{
scanner := bufio.NewScanner(os.Stdin)
+ var precondition string
+ var instrumentname string
+ var buyorsell string
- // Prompt the user to enter the first date
- fmt.Print("Enter Command: ")
- scanner.Scan()
- cmdstr := scanner.Text()
- preset := Preset{}
- inputcmd := strings.Split(cmdstr, " ")
- if len(inputcmd) == 2 {
- if inputcmd[0] == "buy" {
- preset.Direction = 23
- }else if inputcmd[0] == "sell" {
- preset.Direction = 24
+ if sc.Ticker == "" {
+ fmt.Print("Enter Command: ")
+ scanner.Scan()
+ cmdstr := scanner.Text()
+
+ inputcmd := strings.Split(cmdstr, " ")
+ if len(inputcmd) == 2 {
+ if inputcmd[0] == "buy" {
+ preset.Direction = 23
+ }else if inputcmd[0] == "sell" {
+ preset.Direction = 24
+ }else{
+ return ""
+ }
}else{
return ""
}
+
preset.Scode = inputcmd[1]
+ }else{
+ preset.Direction = 23
+ preset.Scode = sc.Dividend[2:]
+ f1, _ := strconv.ParseFloat(sc.LastTrade, 64)
+ f2, _ := strconv.ParseFloat(sc.Change, 64)
+ precondition = fmt.Sprintf("%s>%.2f", sc.LastTrade, f1-f2)
+ instrumentname = sc.Ticker[2:]
+ }
- fmt.Print("Enter Condition: ")
- scanner.Scan()
- condition := scanner.Text()
- if strings.Contains(condition, ">") {
- preset.Ifbelow = 0
- preset.Ifabove, _ = strconv.ParseFloat(strings.Replace(condition, ">", "", -1), 64)
- }else if strings.Contains(condition, "<") {
- preset.Ifabove = 0
- preset.Ifbelow, _ = strconv.ParseFloat(strings.Replace(condition, "<", "", -1), 64)
- }
-
- fmt.Print("Enter Vol: ")
- scanner.Scan()
- vol := scanner.Text()
- preset.Vol, _ = strconv.ParseFloat(vol, 64)
+ if preset.Direction == 23 {
+ buyorsell = "buy"
+ } else {
+ buyorsell = "sell"
+ }
- //fmt.Print(preset)
- jsonData, err := json.Marshal(preset)
- if err != nil {
- fmt.Println(err)
- }
- extraField := `,"cmd": "preset"}`
- jsonData = append(jsonData[:len(jsonData)-1], extraField...)
- message := string(jsonData)
- fmt.Println(message)
+ if preset.Ifbelow == 0 && preset.Ifabove == 0 {
+
+ }else if preset.Ifbelow == 0 {
+ precondition = fmt.Sprintf(">%f", preset.Ifabove)
+ }else{
+ precondition = fmt.Sprintf("<%f", preset.Ifbelow)
+ }
- return message
+ fmt.Printf("Enter Condition[%s %s][%s]: ", buyorsell, preset.Scode+instrumentname, precondition)
+
+ scanner.Scan()
+ condition := scanner.Text()
+ if strings.Contains(condition, ">") {
+ preset.Ifbelow = 0
+ preset.Ifabove, _ = strconv.ParseFloat(strings.Replace(condition, ">", "", -1), 64)
+ }else if strings.Contains(condition, "<") {
+ preset.Ifabove = 0
+ preset.Ifbelow, _ = strconv.ParseFloat(strings.Replace(condition, "<", "", -1), 64)
+ }else{
+ return ""
+ }
+
+ if preset.Ifbelow == 0 && preset.Ifabove == 0 {
+ precondition = ""
+ }else if preset.Ifbelow == 0 {
+ precondition = fmt.Sprintf(">%.2f", preset.Ifabove)
+ }else{
+ precondition = fmt.Sprintf("<%.2f", preset.Ifbelow)
}
- return ""
+ fmt.Printf("Enter Vol[%s %s][%s]: ", buyorsell, preset.Scode+instrumentname, precondition)
+ scanner.Scan()
+ vol := scanner.Text()
+ preset.Vol, _ = strconv.ParseFloat(vol, 64)
+ if preset.Vol == 0 {
+ return ""
+ }
+
+ //fmt.Print(preset)
+ jsonData, err := json.Marshal(preset)
+ if err != nil {
+ fmt.Println(err)
+ }
+ extraField := `,"cmd": "preset"}`
+ jsonData = append(jsonData[:len(jsonData)-1], extraField...)
+ message := string(jsonData)
+ fmt.Println(message)
+
+ return message
}
//-----------------------------------------------------------------------------
@@ -123,6 +165,8 @@ func mainLoop(screen *mop.Screen, profile *mop.Profile, mode string) {
upDownJump := profile.UpDownJump
redrawQuotesFlag := false
redrawMarketFlag := false
+ account := ""
+ var canstock mop.Stock
// 创建一个用于存储用户输入的缓冲区
input := ""
@@ -145,6 +189,12 @@ func mainLoop(screen *mop.Screen, profile *mop.Profile, mode string) {
// create a new MQTT client
opts := mqtt.NewClientOptions()
opts.AddBroker("tcp://119.29.166.226:1883")
+ currentTime := time.Now().UnixNano() / int64(time.Millisecond)
+ millisecondsString := strconv.FormatInt(currentTime, 10)
+ opts.SetClientID("client-mop-"+millisecondsString)
+ opts.SetUsername("sandy")
+ opts.SetPassword("Abb123456.")
+
client := mqtt.NewClient(opts)
// connect to the MQTT broker
@@ -230,11 +280,23 @@ loop:
redrawQuotesFlag = true
} else if event.Key == termbox.KeyArrowUp || event.Ch == 'k' {
screen.DecreaseOffset(1)
- screen.Selectmoveup()
+ screen.Selectmoveup(quotes)
+ selstock := quotes.Getselectedinfo(screen.Selectindex())
+ if selstock != nil {
+ canstock = *selstock
+ }else{
+ canstock = mop.Stock{}
+ }
redrawQuotesFlag = true
} else if event.Key == termbox.KeyArrowDown || event.Ch == 'j' {
screen.IncreaseOffset(1)
screen.Selectmovedown(quotes)
+ selstock := quotes.Getselectedinfo(screen.Selectindex())
+ if selstock != nil {
+ canstock = *selstock
+ }else{
+ canstock = mop.Stock{}
+ }
redrawQuotesFlag = true
} else if event.Key == termbox.KeyHome {
screen.ScrollTop()
@@ -242,6 +304,18 @@ loop:
} else if event.Key == termbox.KeyEnd {
screen.ScrollBottom()
redrawQuotesFlag = true
+ } else if event.Ch == 'b' {
+ data := map[string]string{
+ "cmd": "getaccount",
+ }
+ jsonData, err := json.Marshal(data)
+ if err != nil {
+ fmt.Println(err)
+ }
+ message := string(jsonData)
+ token := client.Publish("stock/request/470100037961", 0, false, message)
+ token.Wait()
+ account = "470100037961"
} else if event.Key == termbox.KeySpace {
data := map[string]string{
"cmd": "getaccount",
@@ -251,21 +325,24 @@ loop:
fmt.Println(err)
}
message := string(jsonData)
- token := client.Publish("stock/request", 0, false, message)
+ token := client.Publish("stock/request/620000301588", 0, false, message)
token.Wait()
+ account = "620000301588"
} else if event.Ch >= '0' && event.Ch <= '9' {
input += string(event.Ch)
} else if event.Key == termbox.KeyEnter {
indexnum, err := strconv.Atoi(input)
selectindex := screen.Selectindex()
if selectindex > 0 {
- indexnum = selectindex
- err = nil
- }
+ selcode := canstock.Dividend
+ quotes.Sendstockgraphreq(selcode, false)
+ quotes.Sendstockgraphreq(selcode, true)
+ }else{
//fmt.Println("indexnum:", indexnum)
- if err == nil && indexnum > 0 {
- quotes.Sendstockgraphreq(indexnum, false)
- quotes.Sendstockgraphreq(indexnum, true)
+ if err == nil && indexnum > 0 {
+ quotes.Sendstockgraphreq(indexnum, false)
+ quotes.Sendstockgraphreq(indexnum, true)
+ }
}
// 清空输入缓冲区
input = ""
@@ -356,19 +433,21 @@ loop:
}else if strings.HasPrefix(msg.Topic(), "stock/image/time"){
os.Stdout.Write(msg.Payload())
- jsonstr := getuserinput()
+ preset := &Preset{}
+ jsonstr := getuserinput(preset, canstock)
if jsonstr != "" {
- token := client.Publish("stock/request", 0, false, jsonstr)
+ token := client.Publish("stock/request/"+account, 0, false, jsonstr)
token.Wait()
}
}
}else if strings.HasPrefix(msg.Topic(), "stock/response"){
fmt.Print("\033[2J")
showposition(string(msg.Payload()) ,si)
-
- jsonstr := getuserinput()
+
+ preset := &Preset{}
+ jsonstr := getuserinput(preset, canstock)
if jsonstr != "" {
- token := client.Publish("stock/request", 0, false, jsonstr)
+ token := client.Publish("stock/request/"+account, 0, false, jsonstr)
token.Wait()
}
}
@@ -412,6 +491,10 @@ type Positions struct {
Latestinfo string `json:"lastinfo"`
}
+func float2Str(f float64) string {
+ return fmt.Sprintf("%.2f", f)
+}
+
func showposition(payload string,si map[string]*stock.Stock) string{
var positions Positions
@@ -422,10 +505,14 @@ func showposition(payload string,si map[string]*stock.Stock) string{
}
var data [][]string
+ var totalFloatProfit float64 = 0.0
+ var totalMarketValue float64 = 0.0
//fmt.Println(positions.Position)
for _, pos := range positions.Position {
row := []string{pos.Scode + " " +pos.Sname, fmt.Sprintf("%.2f", pos.Openprice), fmt.Sprintf("%.2f", pos.Floatprofit), fmt.Sprintf("%.2f", pos.Marketvalue)}
data = append(data, row)
+ totalFloatProfit += pos.Floatprofit
+ totalMarketValue += pos.Marketvalue
}
for _, pos := range positions.Preset {
@@ -455,8 +542,8 @@ func showposition(payload string,si map[string]*stock.Stock) string{
tableString := &strings.Builder{}
table := tablewriter.NewWriter(tableString)
- table.SetHeader([]string{"Date", "Description", "CV2", "Amount"})
- table.SetFooter([]string{"", "", "Total", "$146.93"}) // Add Footer
+ table.SetHeader([]string{"Instrument", "Avg. Price", "Float Profit", "Market Value"})
+ table.SetFooter([]string{"", "Total", float2Str(totalFloatProfit), float2Str(totalMarketValue)}) // Add Footer
//table.EnableBorder(false) // Set Border to false
table.SetHeaderColor(tablewriter.Colors{tablewriter.Bold, tablewriter.BgGreenColor},
diff --git a/day.go b/day.go
new file mode 100644
index 0000000..d15776d
--- /dev/null
+++ b/day.go
@@ -0,0 +1,297 @@
+package mop
+
+import (
+ "encoding/json"
+ "fmt"
+ "io/ioutil"
+ "net/http"
+ "strconv"
+ "strings"
+ "time"
+
+ //"github.com/markcheno/go-talib"
+)
+
+type KLineData struct {
+ Day string `json:"day"`
+ Open float64 `json:"open,string"`
+ High float64 `json:"high,string"`
+ Low float64 `json:"low,string"`
+ Close float64 `json:"close,string"`
+ Volume float64 `json:"volume,string"`
+}
+
+func getURL(code string, ts int, count int) string {
+ return fmt.Sprintf("http://money.finance.sina.com.cn/quotes_service/api/json_v2.php/CN_MarketData.getKLineData?symbol=%s&scale=%d&ma=5&datalen=%d", code, ts, count)
+}
+
+func getKLineData(url string) ([]KLineData, error) {
+ resp, err := http.Get(url)
+ if err != nil {
+ return nil, err
+ }
+ defer resp.Body.Close()
+
+ body, err := ioutil.ReadAll(resp.Body)
+ if err != nil {
+ return nil, err
+ }
+
+ var data []KLineData
+ err = json.Unmarshal(body, &data)
+ if err != nil {
+ return nil, err
+ }
+
+ return data, nil
+}
+func get_price_sina(code string, end_date string, count int, frequency string, startdate string) ([]KLineData, error) {
+ frequency = strings.Replace(frequency, "1d", "240m", 1)
+ frequency = strings.Replace(frequency, "1w", "1200m", 1)
+ frequency = strings.Replace(frequency, "1M", "7200m", 1)
+
+ ts, err := strconv.Atoi(strings.TrimSuffix(frequency, "m"))
+ if err != nil {
+ return nil, err
+ }
+
+ if end_date != "" && (frequency == "240m" || frequency == "1200m" || frequency == "7200m") {
+ endDate, err := time.Parse("2006-01-02", end_date)
+ if err != nil {
+ return nil, err
+ }
+
+ unit := 1
+ if frequency == "1200m" {
+ unit = 4
+ } else if frequency == "7200m" {
+ unit = 29
+ }
+
+ days := int(time.Since(endDate).Hours() / 24)
+ count = count + days/unit
+ }
+
+ url := getURL(code, ts, count)
+ //fmt.Printf("url: %s\n", url)
+ data, err := getKLineData(url)
+ if err != nil {
+ return nil, err
+ }
+ //fmt.Printf("data: %v\n", data)
+ if startdate != "" && (frequency == "240m" || frequency == "1200m" || frequency == "7200m") {
+ endDate := time.Now()
+ //endDate, err := time.Parse("2006-01-02", end_date)
+ //if err != nil {
+ // return nil, err
+ //}
+
+ start_date, err := time.Parse("2006-01-02", startdate)
+ if err != nil {
+ return nil, err
+ }
+
+ filteredData := []KLineData{}
+ for _, d := range data {
+ day, err := time.Parse("2006-01-02", d.Day)
+ if err != nil {
+ return nil, err
+ }
+
+ if day.After(start_date) && (day.Before(endDate) || day.Equal(endDate)) {
+ filteredData = append(filteredData, d)
+ }
+ }
+
+ return filteredData, nil
+ }
+
+ return data, nil
+}
+
+func findMaxHighAndMinLow(data []KLineData) (float64, float64) {
+ maxHigh := data[0].High
+ minLow := data[0].Low
+
+ for _, d := range data {
+ if d.High > maxHigh {
+ maxHigh = d.High
+ }
+ if d.Low < minLow {
+ minLow = d.Low
+ }
+ }
+
+ return maxHigh, minLow
+}
+
+// 将interface{}类型转换为float64类型
+func convertToFloat64(value interface{}) float64 {
+ if floatValue, ok := value.(float64); ok {
+ return floatValue
+ }
+ if stringValue, ok := value.(string); ok {
+ floatValue, err := strconv.ParseFloat(stringValue, 64)
+ if err == nil {
+ return floatValue
+ }
+ }
+ return 0
+}
+
+func getURLTencent(code string, start string, count int) string {
+ // 给定的日期
+ datestr := ""
+ date, err := time.Parse("2006-01-02", start)
+ if err != nil {
+ fmt.Println("解析日期失败:", err)
+ }else{
+ futureDate := date.AddDate(0, 0, 10)
+ if futureDate.Before(time.Now()){
+ datestr = futureDate.Format("2006-01-02")
+ fmt.Println(datestr)
+ }
+ }
+
+ return fmt.Sprintf("https://web.ifzq.gtimg.cn/appstock/app/fqkline/get?param=%s,day,,%s,%d,qfq", code, datestr, count)
+}
+
+func getKLineTencent(url string, code string)([]KLineData, error){
+ resp, err := http.Get(url)
+ if err != nil {
+ return nil, err
+ }
+ defer resp.Body.Close()
+
+ body, err := ioutil.ReadAll(resp.Body)
+ if err != nil {
+ return nil, err
+ }
+
+ var dataMap map[string]interface{}
+ err = json.Unmarshal(body, &dataMap)
+ if err != nil {
+ fmt.Println("解析JSON字符串失败", err)
+ return nil, err
+ }
+ klineData := make([]KLineData, 0)
+ // 获取K线数据
+ if shData, ok := dataMap["data"].(map[string]interface{})[code].(map[string]interface{}); ok {
+ if qfqday, ok := shData["qfqday"].([]interface{}); ok {
+
+ for _, item := range qfqday {
+ if data, ok := item.([]interface{}); ok {
+ klineData = append(klineData, KLineData{
+ Day: data[0].(string),
+ Open: convertToFloat64(data[1]),
+ High: convertToFloat64(data[3]),
+ Low: convertToFloat64(data[4]),
+ Close: convertToFloat64(data[2]),
+ Volume: convertToFloat64(data[5]),
+ })
+ }
+ }
+ //fmt.Println(klineData)
+ return klineData,nil
+ }else if day, ok := shData["day"].([]interface{}); ok {
+ for _, item := range day {
+ if data, ok := item.([]interface{}); ok {
+ klineData = append(klineData, KLineData{
+ Day: data[0].(string),
+ Open: convertToFloat64(data[1]),
+ High: convertToFloat64(data[3]),
+ Low: convertToFloat64(data[4]),
+ Close: convertToFloat64(data[2]),
+ Volume: convertToFloat64(data[5]),
+ })
+ }
+ }
+ return klineData,nil
+ }
+ }
+ return klineData,nil
+}
+
+func get_price_tencent(code string, end_date string, count int, frequency string, startdate string) ([]KLineData, error) {
+ frequency = strings.Replace(frequency, "1d", "240m", 1)
+ frequency = strings.Replace(frequency, "1w", "1200m", 1)
+ frequency = strings.Replace(frequency, "1M", "7200m", 1)
+
+ if end_date != "" && (frequency == "240m" || frequency == "1200m" || frequency == "7200m") {
+ endDate, err := time.Parse("2006-01-02", end_date)
+ if err != nil {
+ return nil, err
+ }
+
+ unit := 1
+ if frequency == "1200m" {
+ unit = 4
+ } else if frequency == "7200m" {
+ unit = 29
+ }
+
+ days := int(time.Since(endDate).Hours() / 24)
+ count = count + days/unit
+ }
+ /*
+ url := getURL(code, ts, count)
+ fmt.Printf("url: %s\n", url)
+ data, err := getKLineData(url)
+ */
+ url := getURLTencent(code, startdate, 10)
+ //fmt.Printf("url: %s\n", url)
+ data, err := getKLineTencent(url, code)
+ if err != nil {
+ return nil, err
+ }
+ //fmt.Printf("data: %v\n", data)
+ if startdate != "" && (frequency == "240m" || frequency == "1200m" || frequency == "7200m") {
+ endDate := time.Now()
+ //endDate, err := time.Parse("2006-01-02", end_date)
+ //if err != nil {
+ // return nil, err
+ //}
+ start_date, err := time.Parse("2006-01-02", startdate)
+ if err != nil {
+ return nil, err
+ }
+
+ filteredData := []KLineData{}
+ for _, d := range data {
+ day, err := time.Parse("2006-01-02", d.Day)
+ if err != nil {
+ return nil, err
+ }
+ //fmt.Println(start_date, endDate, day)
+ if day.After(start_date) && (day.Before(endDate) || day.Equal(endDate)) {
+ filteredData = append(filteredData, d)
+ }
+ }
+
+ return filteredData, nil
+ }
+
+ return data, nil
+}
+
+func getnextdaysHL(code string, start_date string) (float64, float64, float64, float64 ,int) {
+ //data, err := get_price_sina(code, "", 30, "240m", start_date)
+ data, err := get_price_tencent(code, "", 30, "240m", start_date)
+ if err != nil {
+ return 0, 0, 0, 0, 0
+ }
+
+ pre := 3
+ if len(data) < 3 {
+ pre = len(data)
+ }
+
+ //fmt.Printf("data: %v\n", data[:3])
+ caldata := data[:pre]
+ maxHigh, minLow := findMaxHighAndMinLow(caldata)
+ //fmt.Println(maxHigh, minLow)
+
+ return caldata[0].Open, caldata[pre-1].Close, maxHigh, minLow, pre
+}
+
+
diff --git a/line_editor.go b/line_editor.go
index 1d6d3f6..de9c269 100644
--- a/line_editor.go
+++ b/line_editor.go
@@ -64,6 +64,30 @@ func (editor *LineEditor) Prompt(command rune) *LineEditor {
return editor
}
+func (editor *LineEditor) PromptInstrumet(command rune) *LineEditor {
+ filterPrompt := `Set filter: `
+
+ if filter := editor.quotes.profile.Filter; len(filter) > 0 {
+ filterPrompt = `Set filter (` + filter + `): `
+ }
+
+ prompts := map[rune]string{
+ '+': `Add tickers: `, '-': `Remove tickers: `,
+ 'f': filterPrompt,
+ }
+ if prompt, ok := prompts[command]; ok {
+ editor.prompt = prompt
+ editor.command = command
+
+ editor.screen.DrawLine(0, 3, ``+editor.prompt+`>`)
+ termbox.SetCursor(len(editor.prompt), 3)
+ termbox.Flush()
+
+ }
+
+ return editor
+}
+
func (editor *LineEditor) insertString(str string) {
for _, ch := range str {
editor.insertCharacter(ch)
diff --git a/screen.go b/screen.go
index 9e08d8d..0561432 100644
--- a/screen.go
+++ b/screen.go
@@ -117,22 +117,32 @@ func (screen *Screen) DecreaseOffset(n int) {
}
}
-func (screen *Screen) Selectmoveup() {
+func (screen *Screen) Selectmoveup(quotes *Quotes) {
if screen.selectindex >= 5 {
screen.selectindex -= 1
}
+ if screen.selectindex == 4 {
+ screen.DrawLine(0, 3, ``+" "+`>`)
+ }else{
+ screen.DrawLine(0, 3, ``+quotes.stocks[screen.selectindex-5].Ticker+`>`)
+ }
}
func (screen *Screen) Selectmovedown(quotes *Quotes) {
if screen.selectindex < len(quotes.stocks) + 4 {
screen.selectindex += 1
}
+ screen.DrawLine(0, 3, ``+quotes.stocks[screen.selectindex-5].Ticker+`>`)
}
func (screen *Screen) Selectindex() int {
return screen.selectindex - 4
}
+func (screen *Screen) Selectpreset() int {
+ return screen.selectindex - 4
+}
+
func (screen *Screen) ScrollTop() {
screen.offset = 0
}
diff --git a/yahoo_crumb.go b/yahoo_crumb.go
new file mode 100644
index 0000000..fc8a9c4
--- /dev/null
+++ b/yahoo_crumb.go
@@ -0,0 +1,102 @@
+// Copyright (c) 2013-2023 by Michael Dvorkin and contributors. All Rights Reserved.
+// Use of this source code is governed by a MIT-style license that can
+// be found in the LICENSE file.
+
+package mop
+
+import (
+ "io/ioutil"
+ "net/http"
+ "strings"
+ "net/url"
+)
+
+const crumbURL = "https://query1.finance.yahoo.com/v1/test/getcrumb"
+const cookieURL = "https://login.yahoo.com"
+const userAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/113.0"
+
+func fetchCrumb(cookies string) string {
+ proxyUrl, err := url.Parse("http://172.29.100.107:28888")
+ if err != nil {
+ panic(err)
+ }
+
+ transport := &http.Transport{
+ Proxy: http.ProxyURL(proxyUrl),
+ }
+
+ client := &http.Client{
+ Transport: transport,
+ }
+ //client := http.Client{}
+ request, err := http.NewRequest("GET", crumbURL, nil)
+ if err != nil {
+ panic(err)
+ }
+
+ request.Header = http.Header{
+ "Accept": {"*/*"},
+ "Accept-Encoding": {"gzip, deflate, br"},
+ "Accept-Language": {"en-US,en;q=0.5"},
+ "Connection": {"keep-alive"},
+ "Content-Type": {"text/plain"},
+ "Cookie": {cookies},
+ "Host": {"query1.finance.yahoo.com"},
+ "Sec-Fetch-Dest": {"empty"},
+ "Sec-Fetch-Mode": {"cors"},
+ "Sec-Fetch-Site": {"same-site"},
+ "TE": {"trailers"},
+ "User-Agent": {userAgent},
+ }
+
+ response, err := client.Do(request)
+ if err != nil {
+ panic(err)
+ }
+ defer response.Body.Close()
+
+ body, err := ioutil.ReadAll(response.Body)
+ if err != nil {
+ panic(err)
+ }
+
+ return string(body[:])
+}
+
+func fetchCookies() string {
+ client := http.Client{}
+ request, err := http.NewRequest("GET", cookieURL, nil)
+ if err != nil {
+ panic(err)
+ }
+
+ request.Header = http.Header{
+ "Accept": {"*/*"},
+ "Accept-Encoding": {"gzip, deflate, br"},
+ "Accept-Language": {"en-US,en;q=0.5"},
+ "Connection": {"keep-alive"},
+ "Host": {"login.yahoo.com"},
+ "Sec-Fetch-Dest": {"document"},
+ "Sec-Fetch-Mode": {"navigate"},
+ "Sec-Fetch-Site": {"none"},
+ "Sec-Fetch-User": {"?1"},
+ "TE": {"trailers"},
+ "Update-Insecure-Requests": {"1"},
+ "User-Agent": {userAgent},
+ }
+
+ response, err := client.Do(request)
+ if err != nil {
+ panic(err)
+ }
+ defer response.Body.Close()
+
+ var result string
+ for _, cookie := range response.Cookies() {
+ if cookie.Name != "AS" {
+ result += cookie.Name + "=" + cookie.Value + "; "
+ }
+ }
+ result = strings.TrimSuffix(result, "; ")
+ return result
+}
diff --git a/yahoo_market.go b/yahoo_market.go
index 1584124..753405f 100644
--- a/yahoo_market.go
+++ b/yahoo_market.go
@@ -5,16 +5,16 @@
package mop
import (
+ "encoding/json"
"fmt"
"io/ioutil"
"net/http"
- "encoding/json"
- //"net/url"
+ "net/url"
"easyquotation/stock"
)
-const marketURL = `https://query1.finance.yahoo.com/v6/finance/quote?symbols=%s`
+const marketURL = `https://query1.finance.yahoo.com/v7/finance/quote?crumb=%s&symbols=%s`
const marketURLQueryParts = `&range=1d&interval=5m&indicators=close&includeTimestamps=false&includePrePost=false&corsDomain=finance.yahoo.com&.tsrc=finance`
// Market stores current market information displayed in the top three lines of
@@ -38,6 +38,8 @@ type Market struct {
res map[string]*stock.Stock
watchlist *Watchlist
+ cookies string // cookies for auth
+ crumb string // crumb for the cookies, to be applied as a query param
}
// Returns new initialized Market struct.
@@ -59,7 +61,9 @@ func NewMarket(res map[string]*stock.Stock, watchlist *Watchlist) *Market {
market.Euro = make(map[string]string)
market.Gold = make(map[string]string)
- market.url = fmt.Sprintf(marketURL, `^DJI,^IXIC,^GSPC,^N225,^HSI,^FTSE,^GDAXI,^TNX,CL=F,CNH=X,EUR=X,GC=F`) + marketURLQueryParts
+ market.cookies = fetchCookies()
+ market.crumb = fetchCrumb(market.cookies)
+ market.url = fmt.Sprintf(marketURL, market.crumb, `^DJI,^IXIC,^GSPC,^N225,^HSI,^FTSE,^GDAXI,^TNX,CL=F,CNH=X,EUR=X,GC=F`) + marketURLQueryParts
market.errors = ``
market.res = res
@@ -78,6 +82,7 @@ func (market *Market) Fetch() (self *Market) {
market.errors = ""
}
}()
+ /*
// Define the proxy URL
//proxyUrl, err := url.Parse("http://172.29.100.107:28888")
//if err != nil {
@@ -98,12 +103,55 @@ func (market *Market) Fetch() (self *Market) {
panic(err)
}
+ defer response.Body.Close()
+ body, err := ioutil.ReadAll(response.Body)
+ if err != nil {
+ panic(err)
+ }*/
+ proxyUrl, err := url.Parse("http://172.29.100.107:28888")
+ if err != nil {
+ panic(err)
+ }
+
+ transport := &http.Transport{
+ Proxy: http.ProxyURL(proxyUrl),
+ }
+
+ client := &http.Client{
+ Transport: transport,
+ }
+ //client := http.Client{}
+ request, err := http.NewRequest("GET", market.url, nil)
+ if err != nil {
+ panic(err)
+ }
+
+ request.Header = http.Header{
+ "Accept": {"*/*"},
+ "Accept-Language": {"en-US,en;q=0.5"},
+ "Connection": {"keep-alive"},
+ "Content-Type": {"application/json"},
+ "Cookie": {market.cookies},
+ "Host": {"query1.finance.yahoo.com"},
+ "Origin": {"https://finance.yahoo.com"},
+ "Referer": {"https://finance.yahoo.com"},
+ "Sec-Fetch-Dest": {"empty"},
+ "Sec-Fetch-Mode": {"cors"},
+ "Sec-Fetch-Site": {"same-site"},
+ "TE": {"trailers"},
+ "User-Agent": {userAgent},
+ }
+
+ response, err := client.Do(request)
+ if err != nil {
+ panic(err)
+ }
+
defer response.Body.Close()
body, err := ioutil.ReadAll(response.Body)
if err != nil {
panic(err)
}
- //fmt.Println(string(body))
body = market.isMarketOpen(body)
return market.extract(body)
}
diff --git a/yahoo_quotes.go b/yahoo_quotes.go
index 1913fee..07fba90 100644
--- a/yahoo_quotes.go
+++ b/yahoo_quotes.go
@@ -204,9 +204,10 @@ func (quotes *Quotes) Fetch() (self *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)
+ if quotes.profile.mode == "review"{
+ return
+ }
+
filedata, err := json.MarshalIndent(quotes.totalstocks, "", " ")
if err != nil {
fmt.Println(err)
@@ -383,18 +384,27 @@ func padString(str string, length int) string {
return str
}
-func (quotes *Quotes) Sendstockgraphreq(index int, istime bool) {
- if index > len(quotes.totalstocks){
- return
+func (quotes *Quotes) Sendstockgraphreq(index interface{}, istime bool) {
+ var selcode string
+ if intvalue, ok := index.(int); ok {
+ if intvalue > len(quotes.totalstocks){
+ return
+ }
+ selcode = quotes.totalstocks[intvalue-1].Scode
+ }
+
+ if stringValue, ok := index.(string); ok {
+ selcode = stringValue
}
+
topic := "my/topic"
- itemsel := quotes.getitembyscode(quotes.totalstocks[index-1].Scode)
+ itemsel := quotes.getitembyscode(selcode)
data := map[string]interface{}{
- "scode": quotes.totalstocks[index-1].Scode,
+ "scode": selcode,//quotes.totalstocks[index-1].Scode,
"tier": 0,
"daysback": itemsel.Daysback,
"stdprice": itemsel.Enterprice,
- "name": strings.TrimSpace(quotes.totalstocks[index-1].Sname),
+ "name": quotes.res[selcode].Base.Name, //strings.TrimSpace(quotes.totalstocks[index-1].Sname),
"ed": itemsel.AnalyseDay,
}
if istime {
@@ -410,6 +420,13 @@ func (quotes *Quotes) Sendstockgraphreq(index int, istime bool) {
token.Wait()
}
+func (quotes *Quotes) Getselectedinfo(index int) *Stock {
+ if index > len(quotes.totalstocks) || index < 1 {
+ return nil
+ }
+ return "es.stocks[index-1]
+}
+
func (quotes *Quotes) parsereview(res map[string]*stock.Stock) (*Quotes, error) {
var scodes []string
fmt.Println("Start parsing review")