代码尚未整理
package main
import (
"bufio"
"fmt"
"io"
"io/ioutil"
"log"
"net/http"
"os"
"path/filepath"
"strconv"
"strings"
"time"
"github.com/PuerkitoBio/goquery"
"github.com/go-echarts/go-echarts/v2/charts"
"github.com/go-echarts/go-echarts/v2/components"
"github.com/go-echarts/go-echarts/v2/opts"
)
var (
MyStockCode = "300144"
fileNameS = "data_s.txt"
fileNameH = "data_h.txt"
fileNameM = fmt.Sprintf("data_%s.txt", MyStockCode)
)
type klineData struct {
date string
data [4]float32
}
// 当前为第几季度
func Season() (y, m, s int) {
y = time.Now().Year()
m = int(time.Now().Month())
s = m/4 + 1
return
}
// 获取历史数据
func WebSearch(url string) (ret []string) {
var lineData string
res, err := http.Get(url)
if err != nil {
fmt.Println(err)
}
defer res.Body.Close()
if res.StatusCode != 200 {
fmt.Printf("status code error: %d %s", res.StatusCode, res.Status)
}
doc, err := goquery.NewDocumentFromReader(res.Body)
if err != nil {
fmt.Println(err)
}
doc.Find(".table_bg001 tbody tr").Each(func(i int, s *goquery.Selection) {
lineData = ""
s.Find("td").Each(func(n int, m *goquery.Selection) {
lineData = fmt.Sprintf("%s %s", lineData, m.Text())
})
ret = append(ret, lineData)
})
return
}
// 保存文件
func writeFile(buf []byte, fileName string) error {
tmpPath := "./"
err := os.MkdirAll(tmpPath, 0700)
if err != nil {
return err
}
file := filepath.Join(tmpPath, fileName)
err = ioutil.WriteFile(file, buf, 0600)
if err != nil {
return err
}
return nil
}
// 数组倒序函数
func Reverse(arr *[]string) {
var temp string
length := len(*arr)
for i := 0; i < length/2; i++ {
temp = (*arr)[i]
(*arr)[i] = (*arr)[length-1-i]
(*arr)[length-1-i] = temp
}
}
// 文件存在判断
func isExist(path string) bool {
_, err := os.Stat(path)
if err != nil {
if os.IsExist(err) {
return true
}
if os.IsNotExist(err) {
return false
}
return false
}
return true
}
// 读文件
func ReadFile(filename string) (ret []string) {
fi, err := os.Open(filename)
if err != nil {
panic(err)
}
r := bufio.NewReader(fi) // 创建 Reader
for {
lineBytes, err := r.ReadBytes('\n')
line := strings.TrimSpace(string(lineBytes))
if err != nil && err != io.EOF {
panic(err)
}
if err == io.EOF {
break
}
ret = append(ret, line)
}
return
}
func GetData_KLine(fileName string) (kd []klineData) {
var k klineData
Stock := ReadFile(fileName)
for _, n := range Stock {
x := strings.Split(n, " ")
n1, _ := strconv.ParseFloat(strings.ReplaceAll(x[4], ",", ""), 32) // 收盘价
n2, _ := strconv.ParseFloat(strings.ReplaceAll(x[1], ",", ""), 32) // 开盘价
n3, _ := strconv.ParseFloat(strings.ReplaceAll(x[3], ",", ""), 32) // 最低价
n4, _ := strconv.ParseFloat(strings.ReplaceAll(x[2], ",", ""), 32) // 最高价
k = klineData{date: x[0], data: [4]float32{float32(n1), float32(n2), float32(n3), float32(n4)}}
kd = append(kd, k)
}
return
}
func klineDataZoomBoth(fileName, title string) *charts.Kline {
kline := charts.NewKLine()
kd := GetData_KLine(fileName)
x := make([]string, 0)
y := make([]opts.KlineData, 0)
for i := 0; i < len(kd); i++ {
x = append(x, kd[i].date)
y = append(y, opts.KlineData{Value: kd[i].data})
}
kline.SetGlobalOptions(
charts.WithTitleOpts(opts.Title{Title: title, Right: "40%", Top: "5%"}),
charts.WithXAxisOpts(opts.XAxis{SplitNumber: 20}),
charts.WithYAxisOpts(opts.YAxis{Scale: true}),
charts.WithDataZoomOpts(opts.DataZoom{
Type: "inside",
Start: 50,
End: 100,
XAxisIndex: []int{0},
}),
charts.WithDataZoomOpts(opts.DataZoom{
Type: "slider",
Start: 50,
End: 100,
XAxisIndex: []int{0},
}),
)
kline.SetXAxis(x).AddSeries("kline", y).
SetSeriesOptions(
charts.WithMarkPointNameTypeItemOpts(opts.MarkPointNameTypeItem{
Name: "highest value",
Type: "max",
ValueDim: "highest",
}),
charts.WithMarkPointNameTypeItemOpts(opts.MarkPointNameTypeItem{
Name: "lowest value",
Type: "min",
ValueDim: "lowest",
}),
charts.WithMarkPointStyleOpts(opts.MarkPointStyle{
Label: &opts.Label{Show: true},
}),
charts.WithItemStyleOpts(opts.ItemStyle{
Color: "#00da3c", Color0: "#ec0000",
BorderColor: "#000099", BorderColor0: "#222222",
}),
)
return kline
}
func GetData_Bar(fileName string) (title []string, bd1 []opts.BarData, bd2 []opts.BarData) {
Stock := ReadFile(fileName)
for _, n := range Stock {
x := strings.Split(n, " ")
n1, _ := strconv.ParseFloat(strings.ReplaceAll(x[8], ",", ""), 32) // 成交金额(万元)
n2, _ := strconv.ParseFloat(strings.ReplaceAll(x[4], ",", ""), 32) // 收盘价
title = append(title, strings.ReplaceAll(strings.ReplaceAll(x[0], "2022", ""), "-", ""))
bd1 = append(bd1, opts.BarData{Value: float32(n1)})
bd2 = append(bd2, opts.BarData{Value: float32(n2)})
}
return
}
func barDataZoomSlider() *charts.Bar {
t, d1, _ := GetData_Bar(fileNameM)
bar := charts.NewBar()
bar.SetGlobalOptions(
charts.WithTitleOpts(opts.Title{
Title: MyStockCode + "成交量",
Right: "40%",
Top: "5%",
}),
charts.WithDataZoomOpts(opts.DataZoom{
Type: "slider",
Start: 10,
End: 50,
}),
charts.WithTooltipOpts(opts.Tooltip{Show: true}),
//charts.WithColorsOpts(opts.Colors{"blue", "pink"}),
)
bar.SetXAxis(t).
AddSeries("成交金额", d1)
//AddSeries("现价", d2)
//SetSeriesOptions(charts.WithLabelOpts(opts.Label{Show: true, Position: "top"}))
return bar
}
func init() {
y, _, s := Season() // 获取年份及季度
if isExist(fileNameS) == false || isExist(fileNameH) == false { // 数据文件存在
log.Print("获取数据中...")
fs, _ := os.OpenFile(fileNameS, os.O_APPEND|os.O_CREATE, 0666)
fh, _ := os.OpenFile(fileNameH, os.O_APPEND|os.O_CREATE, 0666)
defer fs.Close()
defer fh.Close()
for i := 1; i <= s; i++ {
log.Printf("%d年%d季度上证", y, i)
ShangZhen := WebSearch(fmt.Sprintf("http://quotes.money.163.com/trade/lsjysj_zhishu_000001.html?year=%d&season=%d", y, i))
log.Printf("%d年%d季度沪证", y, i)
HuZhen := WebSearch(fmt.Sprintf("http://quotes.money.163.com/trade/lsjysj_zhishu_399001.html?year=%d&season=%d", y, i))
Reverse(&ShangZhen)
Reverse(&HuZhen)
for _, n := range ShangZhen {
io.WriteString(fs, strings.TrimSpace(n)+"\n")
}
for _, n := range HuZhen {
io.WriteString(fh, strings.TrimSpace(n)+"\n")
}
}
}
if isExist(fileNameM) == false {
f, _ := os.OpenFile(fileNameM, os.O_APPEND|os.O_CREATE, 0666)
defer f.Close()
for i := 1; i <= s; i++ {
log.Printf("%d年%d季度 %s", y, i, MyStockCode)
Stock := WebSearch(fmt.Sprintf("http://quotes.money.163.com/trade/lsjysj_%s.html?year=%d&season=%d", MyStockCode, y, i))
Reverse(&Stock)
for _, n := range Stock {
io.WriteString(f, strings.TrimSpace(n)+"\n")
}
}
}
}
func main() {
page := components.NewPage()
page.AddCharts(
klineDataZoomBoth(fileNameH, "沪证趋势图"),
klineDataZoomBoth(fileNameS, "上证趋势图"),
barDataZoomSlider(),
klineDataZoomBoth(fileNameM, MyStockCode+"价格"),
)
f, _ := os.Create("line.html")
page.Render(io.MultiWriter(f))
}