feat(data):替换A股K线数据源(不再强制依赖Tushare)

- 新增 GetKLineData 方法,用于获取指定股票的 K线数据
- 实现了将 JSON 数据转换为 Markdown 表格的函数 JSONToMarkdownTable- 在 NewChatStream 中添加了对 A 股 K线数据的获取和展示逻辑- 增加了相关测试用例
This commit is contained in:
ArvinLovegood 2025-04-02 11:42:06 +08:00
parent fdaa80777d
commit 2ae3893325
4 changed files with 138 additions and 15 deletions

View File

@ -7,6 +7,7 @@ import (
"fmt"
"github.com/PuerkitoBio/goquery"
"github.com/chromedp/chromedp"
"github.com/duke-git/lancet/v2/convertor"
"github.com/duke-git/lancet/v2/strutil"
"github.com/go-resty/resty/v2"
"github.com/wailsapp/wails/v2/pkg/runtime"
@ -133,6 +134,15 @@ func (o OpenAi) NewChatStream(stock, stockCode, userQuestion string, sysPromptId
},
}
msg = append(msg, map[string]interface{}{
"role": "user",
"content": "当前时间",
})
msg = append(msg, map[string]interface{}{
"role": "assistant",
"content": "当前本地时间是:" + time.Now().Format("2006-01-02 15:04:05"),
})
question := ""
if userQuestion == "" {
replaceTemplates := map[string]string{
@ -180,21 +190,49 @@ func (o OpenAi) NewChatStream(stock, stockCode, userQuestion string, sysPromptId
go func() {
defer wg.Done()
endDate := time.Now().Format("20060102")
startDate := time.Now().Add(-time.Hour * time.Duration(24*o.KDays)).Format("20060102")
code := stockCode
if strutil.HasPrefixAny(stockCode, []string{"hk", "sz", "sh"}) {
code = ConvertStockCodeToTushareCode(stockCode)
//endDate := time.Now().Format("20060102")
//startDate := time.Now().Add(-time.Hour * time.Duration(24*o.KDays)).Format("20060102")
//code := stockCode
//if strutil.HasPrefixAny(stockCode, []string{"hk"}) {
// code = ConvertStockCodeToTushareCode(stockCode)
// K := NewTushareApi(GetConfig()).GetDaily(code, startDate, endDate, o.CrawlTimeOut)
// msg = append(msg, map[string]interface{}{
// "role": "user",
// "content": stock + "日K数据",
// })
// msg = append(msg, map[string]interface{}{
// "role": "assistant",
// "content": stock + "日K数据如下\n" + K,
// })
//}
if strutil.HasPrefixAny(stockCode, []string{"sz", "sh"}) {
K := NewStockDataApi().GetKLineData(stockCode, "240", o.KDays)
Kmap := &[]map[string]any{}
for _, kline := range *K {
mapk := make(map[string]any, 6)
mapk["日期"] = kline.Day
mapk["开盘价"] = kline.Open
mapk["最高价"] = kline.High
mapk["最低价"] = kline.Low
mapk["收盘价"] = kline.Close
Volume, _ := convertor.ToFloat(kline.Volume)
mapk["成交量(万手)"] = Volume / 10000.00 / 100.00
*Kmap = append(*Kmap, mapk)
}
K := NewTushareApi(GetConfig()).GetDaily(code, startDate, endDate, o.CrawlTimeOut)
jsonData, _ := json.Marshal(Kmap)
markdownTable, _ := JSONToMarkdownTable(jsonData)
msg = append(msg, map[string]interface{}{
"role": "user",
"content": stock + "日K数据",
})
msg = append(msg, map[string]interface{}{
"role": "assistant",
"content": stock + "日K数据如下\n" + K,
"content": "## " + stock + "日K数据如下\n" + markdownTable,
})
logger.SugaredLogger.Infof("getKLineData=\n%s", markdownTable)
}
}()
go func() {

View File

@ -9,7 +9,7 @@ import (
func TestNewDeepSeekOpenAiConfig(t *testing.T) {
db.Init("../../data/stock.db")
ai := NewDeepSeekOpenAi(context.TODO())
res := ai.NewChatStream("中信证券", "hk06030", "中信证券分析和总结", nil)
res := ai.NewChatStream("长电科技", "sh600584", "长电科技分析和总结", nil)
for {
select {
case msg := <-res:

View File

@ -1075,3 +1075,73 @@ func CheckBrowserOnWindows() (string, bool) {
}
return path + "\\msedge.exe", true
}
func (receiver StockDataApi) GetKLineData(stockCode string, kLineType string, days int64) *[]KLineData {
url := fmt.Sprintf("http://quotes.sina.cn/cn/api/json_v2.php/CN_MarketDataService.getKLineData?symbol=%s&scale=%s&ma=yes&days=%d", stockCode, kLineType, days)
K := &[]KLineData{}
_, err := receiver.client.SetTimeout(time.Duration(receiver.config.CrawlTimeOut)*time.Second).R().
SetHeader("Host", "quotes.sina.cn").
SetHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36 Edg/119.0.0.0").
SetResult(K).
Get(url)
if err != nil {
logger.SugaredLogger.Errorf("err:%s", err.Error())
return K
}
return K
}
// JSONToMarkdownTable 将JSON数据转换为Markdown表格
func JSONToMarkdownTable(jsonData []byte) (string, error) {
var data []map[string]interface{}
err := json.Unmarshal(jsonData, &data)
if err != nil {
return "", err
}
if len(data) == 0 {
return "", nil
}
// 获取表头
headers := []string{}
for key := range data[0] {
headers = append(headers, key)
}
// 构建表头行
headerRow := "|"
for _, header := range headers {
headerRow += fmt.Sprintf(" %s |", header)
}
headerRow += "\n"
// 构建分隔行
separatorRow := "|"
for range headers {
separatorRow += " --- |"
}
separatorRow += "\n"
// 构建数据行
bodyRows := ""
for _, rowData := range data {
bodyRow := "|"
for _, header := range headers {
value := rowData[header]
bodyRow += fmt.Sprintf(" %v |", value)
}
bodyRows += bodyRow + "\n"
}
return headerRow + separatorRow + bodyRows, nil
}
type KLineData struct {
Day string `json:"day"`
Open string `json:"open"`
High string `json:"high"`
Low string `json:"low"`
Close string `json:"close"`
Volume string `json:"volume"`
}

View File

@ -57,6 +57,21 @@ func TestSearchStockPriceInfo(t *testing.T) {
}
func TestGetKLineData(t *testing.T) {
db.Init("../../data/stock.db")
k := NewStockDataApi().GetKLineData("sh600171", "240", 30)
//for _, kline := range *k {
// logger.SugaredLogger.Infof("%+#v", kline)
//}
jsonData, _ := json.Marshal(*k)
markdownTable, err := JSONToMarkdownTable(jsonData)
if err != nil {
logger.SugaredLogger.Errorf("json.Marshal error:%s", err.Error())
}
logger.SugaredLogger.Infof("markdownTable:\n%s", markdownTable)
}
func TestGetRealTimeStockPriceInfo(t *testing.T) {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()