parent
a9f4de28d0
commit
56e6e0a711
@ -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 |
||||||
|
} |
||||||
|
|
||||||
|
|
@ -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 |
||||||
|
} |
Loading…
Reference in new issue