diff --git a/backend/data/openai_api.go b/backend/data/openai_api.go index 36b8036..913a387 100644 --- a/backend/data/openai_api.go +++ b/backend/data/openai_api.go @@ -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) + } + 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" + markdownTable, + }) + logger.SugaredLogger.Infof("getKLineData=\n%s", markdownTable) } - 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, - }) + }() go func() { diff --git a/backend/data/openai_api_test.go b/backend/data/openai_api_test.go index ab2806a..057dcad 100644 --- a/backend/data/openai_api_test.go +++ b/backend/data/openai_api_test.go @@ -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: diff --git a/backend/data/stock_data_api.go b/backend/data/stock_data_api.go index c983061..062d65a 100644 --- a/backend/data/stock_data_api.go +++ b/backend/data/stock_data_api.go @@ -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"` +} diff --git a/backend/data/stock_data_api_test.go b/backend/data/stock_data_api_test.go index 61bb01e..7acc573 100644 --- a/backend/data/stock_data_api_test.go +++ b/backend/data/stock_data_api_test.go @@ -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()