Compare commits
30 Commits
v2025.11.2
...
v2026.01.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
aadff1c5eb | ||
|
|
6b7fed00a3 | ||
|
|
5af0e28601 | ||
|
|
c4af77954e | ||
|
|
8236adb680 | ||
|
|
0adfdab441 | ||
|
|
b1c618a9de | ||
|
|
709c372bf3 | ||
|
|
beb022c448 | ||
|
|
d1aed3419b | ||
|
|
48511c61df | ||
|
|
163e8800f9 | ||
|
|
70e0d19c58 | ||
|
|
4e04bacf22 | ||
|
|
8c75c8533a | ||
|
|
1ad02d4b0c | ||
|
|
a354ab8925 | ||
|
|
6d50be8541 | ||
|
|
6303d535bc | ||
|
|
b464d8f563 | ||
|
|
eea0856c1c | ||
|
|
1dd77d5c08 | ||
|
|
9c1c0382ca | ||
|
|
46065f448b | ||
|
|
a7cee69e68 | ||
|
|
459441f838 | ||
|
|
b4b3b61e8c | ||
|
|
b6a99940ab | ||
|
|
5b0f34a3bd | ||
|
|
e34ebf9895 |
6
.github/workflows/main.yml
vendored
6
.github/workflows/main.yml
vendored
@@ -29,6 +29,12 @@ jobs:
|
||||
- name: 'go-stock-darwin-universal'
|
||||
platform: 'darwin/universal'
|
||||
os: 'macos-latest'
|
||||
- name: 'go-stock-darwin-intel'
|
||||
platform: 'darwin'
|
||||
os: 'macos-latest'
|
||||
- name: 'go-stock-darwin-arm64'
|
||||
platform: 'darwin/arm64'
|
||||
os: 'macos-latest'
|
||||
|
||||
runs-on: ${{ matrix.build.os }}
|
||||
steps:
|
||||
|
||||
19
README.md
19
README.md
@@ -22,7 +22,7 @@
|
||||
- 开发环境主要基于Windows10+,其他平台未测试或功能受限。
|
||||
|
||||
### 📦 立即体验
|
||||
- 安装版:[go-stock-amd64-installer.exe](https://github.com/ArvinLovegood/go-stock/releases)
|
||||
[//]: # (- 安装版:[go-stock-amd64-installer.exe](https://github.com/ArvinLovegood/go-stock/releases))
|
||||
- 绿色版:[go-stock-windows-amd64.exe](https://github.com/ArvinLovegood/go-stock/releases)
|
||||
- MACOS绿色版:[go-stock-darwin-universal](https://github.com/ArvinLovegood/go-stock/releases)
|
||||
|
||||
@@ -53,7 +53,7 @@
|
||||
|:--------------------------------|----------------|:-------------------------------------------------------|
|
||||
| 每月 0 RMB | vip0 | 🌟 全部功能,软件自动更新(从GitHub下载),自行解决github平台网络问题。 |
|
||||
| 每月赞助 18.8 RMB<br>每年赞助 120 RMB | vip1 | 💕 全部功能,软件自动更新(从CDN下载),更新快速便捷。AI配置指导,提示词参考等 |
|
||||
| 每月赞助 28.8 RMB<br>每年赞助 240 RMB | vip2 | 💕 💕 vip1全部功能,赠送硅基流动AI分析服务 |
|
||||
| 每月赞助 28.8 RMB<br>每年赞助 240 RMB | vip2 | 💕 💕 vip1全部功能,赠送硅基流动AI分析服务,启动时自动同步最近24小时市场资讯(包括外媒简讯) |
|
||||
| 每月赞助 X RMB | vipX | 🧩 更多计划,视go-stock开源项目发展情况而定...(承接GitHub项目README广告推广💖) |
|
||||
|
||||
## 🧩 重大功能开发计划
|
||||
@@ -69,6 +69,11 @@
|
||||
| 不再强制依赖Chrome浏览器 | ✅ | 默认使用edge浏览器抓取新闻资讯 |
|
||||
|
||||
## 👀 更新日志
|
||||
### 2025.12.16 新增AI思考模式与热门选股策略功能
|
||||
### 2025.11.21 新增带频率权重的情感分析功能
|
||||
### 2025.10.30 添加AI智能体功能开关(默认关闭,因为使用体验不理想),移除页面水印
|
||||
### 2025.09.27 添加机构/券商的研究报告AI工具函数
|
||||
### 2025.08.09 添加AI智能体聊天功能
|
||||
### 2025.07.08 实现软件自动更新功能
|
||||
### 2025.07.07 卡片添加迷你分时图
|
||||
### 2025.07.05 MacOs支持
|
||||
@@ -116,6 +121,8 @@
|
||||
|
||||
## 🦄 重大更新
|
||||
### BIG NEWS !!! 重大更新!!!
|
||||
- 2025.11.21 新增带频率权重的情感分析功能
|
||||

|
||||
- 2025.04.25 市场资讯支持AI分析和总结:让AI帮你读市场!
|
||||

|
||||
- 2025.04.24 新增市场行情模块:即时掌握全球市场行情资讯/动态,从此再也不用偷摸去各大财经网站啦。go-stock一键帮你搞定!
|
||||
@@ -162,15 +169,15 @@
|
||||
|
||||
## 🐳 关于技术支持申明
|
||||
- 本软件基于开源技术构建,使用Wails、NaiveUI、Vue、AI大模型等开源项目。 技术上如有问题,可以先向对应的开源社区请求帮助。
|
||||
- 开源不易,本人精力和时间有限,如需一对一技术支持,请先赞助。联系微信(备注 技术支持):ArvinLovegood
|
||||
- 开源不易,本人精力和时间有限,如需一对一技术支持,请先赞助。联系QQ(备注 技术支持):506808970
|
||||
|
||||
<img src="./build/wx.jpg" width="301px" height="402px" alt="ArvinLovegood">
|
||||
[//]: # (<img src="./build/wx.jpg" width="301px" height="402px" alt="ArvinLovegood">)
|
||||
|
||||
|
||||
| 技术支持方式 | 赞助(元) |
|
||||
|:--------------------------------|:-----:|
|
||||
| 加 QQ:506808970,微信:ArvinLovegood | 100/次 |
|
||||
| 长期技术支持(不限次数,新功能优先体验等) | 5000 |
|
||||
| 加 QQ:506808970 | 100/次 |
|
||||
| 长期技术支持(不限次数,新功能优先体验等) | 5000 |
|
||||
|
||||
|
||||
|
||||
|
||||
246
app.go
246
app.go
@@ -1,6 +1,7 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/base64"
|
||||
@@ -18,6 +19,7 @@ import (
|
||||
|
||||
"github.com/duke-git/lancet/v2/cryptor"
|
||||
"github.com/inconshreveable/go-update"
|
||||
"golang.org/x/exp/slices"
|
||||
|
||||
"github.com/PuerkitoBio/goquery"
|
||||
"github.com/coocood/freecache"
|
||||
@@ -62,19 +64,21 @@ func AddTools(tools []data.Tool) []data.Tool {
|
||||
Function: data.ToolFunction{
|
||||
Name: "SearchStockByIndicators",
|
||||
Description: "根据自然语言筛选股票,返回自然语言选股条件要求的股票所有相关数据。输入股票名称可以获取当前股票最新的股价交易数据和基础财务指标信息,多个股票名称使用,分隔。",
|
||||
Parameters: data.FunctionParameters{
|
||||
Parameters: &data.FunctionParameters{
|
||||
Type: "object",
|
||||
Properties: map[string]any{
|
||||
"words": map[string]any{
|
||||
"type": "string",
|
||||
"description": "选股自然语言。" +
|
||||
"例如,查看技术指标:上海贝岭,macd,rsi,kdj,boll,5日均线,14日均线,30日均线,60日均线,成交量,OBV,EMA" +
|
||||
"例如,查看有潜力的成交量爆发股:最近7日成交量量比大于3,出现过一次,非ST" +
|
||||
"例1:创新药,半导体;PE<30;净利润增长率>50%。 " +
|
||||
"例2:上证指数,科创50。 " +
|
||||
"例3:长电科技,上海贝岭。" +
|
||||
"例4:长电科技,上海贝岭;KDJ,MACD,RSI,BOLL,主力净流入/流出" +
|
||||
"例4:长电科技,上海贝岭;KDJ,MACD,RSI,BOLL,主力资金" +
|
||||
"例5:换手率大于3%小于25%.量比1以上. 10日内有过涨停.股价处于峰值的二分之一以下.流通股本<100亿.当日和连续四日净流入;股价在20日均线以上.分时图股价在均线之上.热门板块下涨幅领先的A股. 当日量能20000手以上.沪深个股.近一年市盈率波动小于150%.MACD金叉;不要ST股及不要退市股,非北交所,每股收益>0。" +
|
||||
"例6:沪深主板.流通市值小于100亿.市值大于10亿.60分钟dif大于dea.60分钟skdj指标k值大于d值.skdj指标k值小于90.换手率大于3%.成交额大于1亿元.量比大于2.涨幅大于2%小于7%.股价大于5小于50.创业板.10日均线大于20日均线;不要ST股及不要退市股;不要北交所;不要科创板;不要创业板。" +
|
||||
"例7:股价在20日线上,一月之内涨停次数>=1,量比大于1,换手率大于3%,流通市值大于 50亿小于200亿。" +
|
||||
"例7:股价在20日线上,一月之内涨停次数>=1,量比大于1,换手率大于3%" +
|
||||
"例8:基本条件:前期有爆量,回调到 10 日线,当日是缩量阴线,均线趋势向上。;优选条件:一月之内涨停次数>=1",
|
||||
},
|
||||
},
|
||||
@@ -88,7 +92,7 @@ func AddTools(tools []data.Tool) []data.Tool {
|
||||
Function: data.ToolFunction{
|
||||
Name: "GetStockKLine",
|
||||
Description: "获取股票日K线数据。",
|
||||
Parameters: data.FunctionParameters{
|
||||
Parameters: &data.FunctionParameters{
|
||||
Type: "object",
|
||||
Properties: map[string]any{
|
||||
"days": map[string]any{
|
||||
@@ -110,7 +114,7 @@ func AddTools(tools []data.Tool) []data.Tool {
|
||||
Function: data.ToolFunction{
|
||||
Name: "InteractiveAnswer",
|
||||
Description: "获取投资者与上市公司互动问答的数据,反映当前投资者关注的热点问题",
|
||||
Parameters: data.FunctionParameters{
|
||||
Parameters: &data.FunctionParameters{
|
||||
Type: "object",
|
||||
Properties: map[string]any{
|
||||
"page": map[string]any{
|
||||
@@ -161,8 +165,8 @@ func AddTools(tools []data.Tool) []data.Tool {
|
||||
Type: "function",
|
||||
Function: data.ToolFunction{
|
||||
Name: "GetStockResearchReport",
|
||||
Description: "获取股票的分析/研究报告",
|
||||
Parameters: data.FunctionParameters{
|
||||
Description: "获取市场分析师的股票研究报告",
|
||||
Parameters: &data.FunctionParameters{
|
||||
Type: "object",
|
||||
Properties: map[string]any{
|
||||
"stockCode": map[string]any{
|
||||
@@ -175,6 +179,14 @@ func AddTools(tools []data.Tool) []data.Tool {
|
||||
},
|
||||
})
|
||||
|
||||
tools = append(tools, data.Tool{
|
||||
Type: "function",
|
||||
Function: data.ToolFunction{
|
||||
Name: "HotStrategyTable",
|
||||
Description: "获取当前热门选股策略",
|
||||
},
|
||||
})
|
||||
|
||||
return tools
|
||||
}
|
||||
|
||||
@@ -245,8 +257,15 @@ func (a *App) CheckUpdate(flag int) {
|
||||
return
|
||||
}
|
||||
logger.SugaredLogger.Infof("releaseVersion:%+v", releaseVersion.TagName)
|
||||
if releaseVersion.TagName != Version {
|
||||
|
||||
if _, vipLevel, ok := a.isVip(sponsorCode, "", releaseVersion); ok {
|
||||
level, _ := convertor.ToInt(vipLevel)
|
||||
if level >= 2 {
|
||||
go a.syncNews()
|
||||
}
|
||||
}
|
||||
|
||||
if releaseVersion.TagName != Version {
|
||||
tag := &models.Tag{}
|
||||
_, err = resty.New().R().
|
||||
SetResult(tag).
|
||||
@@ -271,59 +290,9 @@ func (a *App) CheckUpdate(flag int) {
|
||||
if IsMacOS() {
|
||||
downloadUrl = fmt.Sprintf("https://github.com/ArvinLovegood/go-stock/releases/download/%s/go-stock-darwin-universal", releaseVersion.TagName)
|
||||
}
|
||||
sponsorCode = strutil.Trim(a.GetConfig().SponsorCode)
|
||||
if sponsorCode != "" {
|
||||
encrypted, err := hex.DecodeString(sponsorCode)
|
||||
if err != nil {
|
||||
logger.SugaredLogger.Error(err.Error())
|
||||
return
|
||||
}
|
||||
key, err := hex.DecodeString(BuildKey)
|
||||
if err != nil {
|
||||
logger.SugaredLogger.Error(err.Error())
|
||||
return
|
||||
}
|
||||
decrypt := string(cryptor.AesEcbDecrypt(encrypted, key))
|
||||
err = json.Unmarshal([]byte(decrypt), &a.SponsorInfo)
|
||||
if err != nil {
|
||||
logger.SugaredLogger.Error(err.Error())
|
||||
return
|
||||
}
|
||||
vipStartTime, err := time.ParseInLocation("2006-01-02 15:04:05", a.SponsorInfo["vipStartTime"].(string), time.Local)
|
||||
vipEndTime, err := time.ParseInLocation("2006-01-02 15:04:05", a.SponsorInfo["vipEndTime"].(string), time.Local)
|
||||
vipAuthTime, err := time.ParseInLocation("2006-01-02 15:04:05", a.SponsorInfo["vipAuthTime"].(string), time.Local)
|
||||
if err != nil {
|
||||
logger.SugaredLogger.Error(err.Error())
|
||||
return
|
||||
}
|
||||
isVip := false
|
||||
|
||||
if time.Now().After(vipAuthTime) && time.Now().After(vipStartTime) && time.Now().Before(vipEndTime) {
|
||||
isVip = true
|
||||
}
|
||||
|
||||
if IsWindows() {
|
||||
if isVip {
|
||||
if a.SponsorInfo["winDownUrl"] == nil {
|
||||
downloadUrl = fmt.Sprintf("https://gitproxy.click/https://github.com/ArvinLovegood/go-stock/releases/download/%s/go-stock-windows-amd64.exe", releaseVersion.TagName)
|
||||
} else {
|
||||
downloadUrl = a.SponsorInfo["winDownUrl"].(string)
|
||||
}
|
||||
} else {
|
||||
downloadUrl = fmt.Sprintf("https://github.com/ArvinLovegood/go-stock/releases/download/%s/go-stock-windows-amd64.exe", releaseVersion.TagName)
|
||||
}
|
||||
}
|
||||
if IsMacOS() {
|
||||
if isVip {
|
||||
if a.SponsorInfo["macDownUrl"] == nil {
|
||||
downloadUrl = fmt.Sprintf("https://gitproxy.click/https://github.com/ArvinLovegood/go-stock/releases/download/%s/go-stock-darwin-universal", releaseVersion.TagName)
|
||||
} else {
|
||||
downloadUrl = a.SponsorInfo["macDownUrl"].(string)
|
||||
}
|
||||
} else {
|
||||
downloadUrl = fmt.Sprintf("https://github.com/ArvinLovegood/go-stock/releases/download/%s/go-stock-darwin-universal", releaseVersion.TagName)
|
||||
}
|
||||
}
|
||||
downloadUrl, _, done := a.isVip(sponsorCode, downloadUrl, releaseVersion)
|
||||
if !done {
|
||||
return
|
||||
}
|
||||
go runtime.EventsEmit(a.ctx, "newsPush", map[string]any{
|
||||
"time": "发现新版本:" + releaseVersion.TagName,
|
||||
@@ -379,6 +348,141 @@ func (a *App) CheckUpdate(flag int) {
|
||||
}
|
||||
}
|
||||
|
||||
func (a *App) isVip(sponsorCode string, downloadUrl string, releaseVersion *models.GitHubReleaseVersion) (string, string, bool) {
|
||||
isVip := false
|
||||
vipLevel := "0"
|
||||
sponsorCode = strutil.Trim(a.GetConfig().SponsorCode)
|
||||
if sponsorCode != "" {
|
||||
encrypted, err := hex.DecodeString(sponsorCode)
|
||||
if err != nil {
|
||||
logger.SugaredLogger.Error(err.Error())
|
||||
return "", "0", false
|
||||
}
|
||||
key, err := hex.DecodeString(BuildKey)
|
||||
if err != nil {
|
||||
logger.SugaredLogger.Error(err.Error())
|
||||
return "", "0", false
|
||||
}
|
||||
decrypt := string(cryptor.AesEcbDecrypt(encrypted, key))
|
||||
err = json.Unmarshal([]byte(decrypt), &a.SponsorInfo)
|
||||
if err != nil {
|
||||
logger.SugaredLogger.Error(err.Error())
|
||||
return "", "0", false
|
||||
}
|
||||
vipLevel = a.SponsorInfo["vipLevel"].(string)
|
||||
vipStartTime, err := time.ParseInLocation("2006-01-02 15:04:05", a.SponsorInfo["vipStartTime"].(string), time.Local)
|
||||
vipEndTime, err := time.ParseInLocation("2006-01-02 15:04:05", a.SponsorInfo["vipEndTime"].(string), time.Local)
|
||||
vipAuthTime, err := time.ParseInLocation("2006-01-02 15:04:05", a.SponsorInfo["vipAuthTime"].(string), time.Local)
|
||||
if err != nil {
|
||||
logger.SugaredLogger.Error(err.Error())
|
||||
return "", vipLevel, false
|
||||
}
|
||||
|
||||
if time.Now().After(vipAuthTime) && time.Now().After(vipStartTime) && time.Now().Before(vipEndTime) {
|
||||
isVip = true
|
||||
}
|
||||
|
||||
if IsWindows() {
|
||||
if isVip {
|
||||
if a.SponsorInfo["winDownUrl"] == nil {
|
||||
downloadUrl = fmt.Sprintf("https://gitproxy.click/https://github.com/ArvinLovegood/go-stock/releases/download/%s/go-stock-windows-amd64.exe", releaseVersion.TagName)
|
||||
} else {
|
||||
downloadUrl = a.SponsorInfo["winDownUrl"].(string)
|
||||
}
|
||||
} else {
|
||||
downloadUrl = fmt.Sprintf("https://github.com/ArvinLovegood/go-stock/releases/download/%s/go-stock-windows-amd64.exe", releaseVersion.TagName)
|
||||
}
|
||||
}
|
||||
if IsMacOS() {
|
||||
if isVip {
|
||||
if a.SponsorInfo["macDownUrl"] == nil {
|
||||
downloadUrl = fmt.Sprintf("https://gitproxy.click/https://github.com/ArvinLovegood/go-stock/releases/download/%s/go-stock-darwin-universal", releaseVersion.TagName)
|
||||
} else {
|
||||
downloadUrl = a.SponsorInfo["macDownUrl"].(string)
|
||||
}
|
||||
} else {
|
||||
downloadUrl = fmt.Sprintf("https://github.com/ArvinLovegood/go-stock/releases/download/%s/go-stock-darwin-universal", releaseVersion.TagName)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
return downloadUrl, vipLevel, isVip
|
||||
}
|
||||
|
||||
func (a *App) syncNews() {
|
||||
defer PanicHandler()
|
||||
client := resty.New()
|
||||
url := fmt.Sprintf("http://go-stock.sparkmemory.top:16666/FinancialNews/json?since=%d", time.Now().Add(-24*time.Hour).Unix())
|
||||
logger.SugaredLogger.Infof("syncNews:%s", url)
|
||||
resp, err := client.R().SetDoNotParseResponse(true).Get(url)
|
||||
body := resp.RawBody()
|
||||
defer body.Close()
|
||||
if err != nil {
|
||||
logger.SugaredLogger.Errorf("syncNews error:%s", err.Error())
|
||||
}
|
||||
scanner := bufio.NewScanner(body)
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
logger.SugaredLogger.Infof("Received data: %s", line)
|
||||
news := &models.NtfyNews{}
|
||||
err := json.Unmarshal(scanner.Bytes(), news)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
dataTime := time.UnixMilli(int64(news.Time * 1000))
|
||||
|
||||
if slice.ContainAny(news.Tags, []string{"外媒资讯", "财联社电报", "新浪财经"}) {
|
||||
isRed := false
|
||||
if slice.Contain(news.Tags, "rotating_light") {
|
||||
isRed = true
|
||||
}
|
||||
telegraph := &models.Telegraph{
|
||||
Title: news.Title,
|
||||
Content: news.Message,
|
||||
DataTime: &dataTime,
|
||||
IsRed: isRed,
|
||||
Time: dataTime.Format("15:04:05"),
|
||||
Source: GetSource(news.Tags),
|
||||
SentimentResult: data.AnalyzeSentiment(news.Message).Description,
|
||||
}
|
||||
cnt := int64(0)
|
||||
if telegraph.Title == "" {
|
||||
db.Dao.Model(telegraph).Where("content=?", telegraph.Content).Count(&cnt)
|
||||
} else {
|
||||
db.Dao.Model(telegraph).Where("title=?", telegraph.Title).Count(&cnt)
|
||||
}
|
||||
if cnt == 0 {
|
||||
db.Dao.Model(telegraph).Create(&telegraph)
|
||||
a.NewsPush(&[]models.Telegraph{*telegraph})
|
||||
for _, subject := range news.Tags {
|
||||
tag := &models.Tags{
|
||||
Name: subject,
|
||||
Type: "subject",
|
||||
}
|
||||
db.Dao.Model(tag).Where("name=? and type=?", subject, "subject").FirstOrCreate(&tag)
|
||||
db.Dao.Model(models.TelegraphTags{}).Where("telegraph_id=? and tag_id=?", telegraph.ID, tag.ID).FirstOrCreate(&models.TelegraphTags{
|
||||
TelegraphId: telegraph.ID,
|
||||
TagId: tag.ID,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func GetSource(tags []string) string {
|
||||
if slices.Contains(tags, "外媒简讯") {
|
||||
return "外媒"
|
||||
}
|
||||
if slices.Contains(tags, "财联社电报") {
|
||||
return "财联社电报"
|
||||
}
|
||||
if slices.Contains(tags, "新浪财经") {
|
||||
return "新浪财经"
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// domReady is called after front-end resources have been loaded
|
||||
func (a *App) domReady(ctx context.Context) {
|
||||
defer PanicHandler()
|
||||
@@ -418,6 +522,10 @@ func (a *App) domReady(ctx context.Context) {
|
||||
if interval <= 0 {
|
||||
interval = 1
|
||||
}
|
||||
a.cron.AddFunc(fmt.Sprintf("@every %ds", interval+60), func() {
|
||||
data.NewsAnalyze("", true)
|
||||
})
|
||||
|
||||
//ticker := time.NewTicker(time.Second * time.Duration(interval))
|
||||
//defer ticker.Stop()
|
||||
//for range ticker.C {
|
||||
@@ -686,7 +794,7 @@ func (a *App) AddCronTask(follow data.FollowedStock) func() {
|
||||
return func() {
|
||||
go runtime.EventsEmit(a.ctx, "warnMsg", "开始自动分析"+follow.Name+"_"+follow.StockCode)
|
||||
ai := data.NewDeepSeekOpenAi(a.ctx, follow.AiConfigId)
|
||||
msgs := ai.NewChatStream(follow.Name, follow.StockCode, "", nil, a.AiTools)
|
||||
msgs := ai.NewChatStream(follow.Name, follow.StockCode, "", nil, a.AiTools, true)
|
||||
var res strings.Builder
|
||||
|
||||
chatId := ""
|
||||
@@ -717,7 +825,7 @@ func refreshTelegraphList() *[]string {
|
||||
response, err := resty.New().R().
|
||||
SetHeader("Referer", "https://www.cls.cn/").
|
||||
SetHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.0.0 Safari/537.36 Edg/117.0.2045.60").
|
||||
Get(fmt.Sprintf(url))
|
||||
Get(url)
|
||||
if err != nil {
|
||||
return &[]string{}
|
||||
}
|
||||
@@ -1051,12 +1159,12 @@ func (a *App) SendDingDingMessageByType(message string, stockCode string, msgTyp
|
||||
return data.NewDingDingAPI().SendDingDingMessage(message)
|
||||
}
|
||||
|
||||
func (a *App) NewChatStream(stock, stockCode, question string, aiConfigId int, sysPromptId *int, enableTools bool) {
|
||||
func (a *App) NewChatStream(stock, stockCode, question string, aiConfigId int, sysPromptId *int, enableTools bool, think bool) {
|
||||
var msgs <-chan map[string]any
|
||||
if enableTools {
|
||||
msgs = data.NewDeepSeekOpenAi(a.ctx, aiConfigId).NewChatStream(stock, stockCode, question, sysPromptId, a.AiTools)
|
||||
msgs = data.NewDeepSeekOpenAi(a.ctx, aiConfigId).NewChatStream(stock, stockCode, question, sysPromptId, a.AiTools, think)
|
||||
} else {
|
||||
msgs = data.NewDeepSeekOpenAi(a.ctx, aiConfigId).NewChatStream(stock, stockCode, question, sysPromptId, []data.Tool{})
|
||||
msgs = data.NewDeepSeekOpenAi(a.ctx, aiConfigId).NewChatStream(stock, stockCode, question, sysPromptId, []data.Tool{}, think)
|
||||
}
|
||||
for msg := range msgs {
|
||||
runtime.EventsEmit(a.ctx, "newChatStream", msg)
|
||||
@@ -1386,12 +1494,12 @@ func (a *App) GlobalStockIndexes() map[string]any {
|
||||
return data.NewMarketNewsApi().GlobalStockIndexes(30)
|
||||
}
|
||||
|
||||
func (a *App) SummaryStockNews(question string, aiConfigId int, sysPromptId *int, enableTools bool) {
|
||||
func (a *App) SummaryStockNews(question string, aiConfigId int, sysPromptId *int, enableTools bool, think bool) {
|
||||
var msgs <-chan map[string]any
|
||||
if enableTools {
|
||||
msgs = data.NewDeepSeekOpenAi(a.ctx, aiConfigId).NewSummaryStockNewsStreamWithTools(question, sysPromptId, a.AiTools)
|
||||
msgs = data.NewDeepSeekOpenAi(a.ctx, aiConfigId).NewSummaryStockNewsStreamWithTools(question, sysPromptId, a.AiTools, think)
|
||||
} else {
|
||||
msgs = data.NewDeepSeekOpenAi(a.ctx, aiConfigId).NewSummaryStockNewsStream(question, sysPromptId)
|
||||
msgs = data.NewDeepSeekOpenAi(a.ctx, aiConfigId).NewSummaryStockNewsStream(question, sysPromptId, think)
|
||||
}
|
||||
|
||||
for msg := range msgs {
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"go-stock/backend/agent"
|
||||
"go-stock/backend/data"
|
||||
"go-stock/backend/models"
|
||||
"strings"
|
||||
|
||||
"github.com/wailsapp/wails/v2/pkg/runtime"
|
||||
)
|
||||
@@ -32,7 +31,7 @@ func (a *App) EMDictCode(code string) []any {
|
||||
return data.NewMarketNewsApi().EMDictCode(code, a.cache)
|
||||
}
|
||||
|
||||
func (a *App) AnalyzeSentiment(text string) data.SentimentResult {
|
||||
func (a *App) AnalyzeSentiment(text string) models.SentimentResult {
|
||||
return data.AnalyzeSentiment(text)
|
||||
}
|
||||
|
||||
@@ -75,17 +74,7 @@ func (a *App) ChatWithAgent(question string, aiConfigId int, sysPromptId *int) {
|
||||
}
|
||||
|
||||
func (a *App) AnalyzeSentimentWithFreqWeight(text string) map[string]any {
|
||||
if text == "" {
|
||||
telegraphs := data.NewMarketNewsApi().GetNews24HoursList("", 1000*10)
|
||||
messageText := strings.Builder{}
|
||||
for _, telegraph := range *telegraphs {
|
||||
messageText.WriteString(telegraph.Content + "\n")
|
||||
}
|
||||
text = messageText.String()
|
||||
}
|
||||
result, frequencies := data.AnalyzeSentimentWithFreqWeight(text)
|
||||
// 过滤标点符号和分隔符
|
||||
cleanFrequencies := data.FilterAndSortWords(frequencies)
|
||||
result, cleanFrequencies := data.NewsAnalyze(text, false)
|
||||
return map[string]any{
|
||||
"result": result,
|
||||
"frequencies": cleanFrequencies,
|
||||
|
||||
17
app_test.go
17
app_test.go
@@ -8,6 +8,8 @@ import (
|
||||
"go-stock/backend/models"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/go-resty/resty/v2"
|
||||
)
|
||||
|
||||
// @Author spark
|
||||
@@ -45,3 +47,18 @@ func TestJson(t *testing.T) {
|
||||
db.Dao.Model(v).Updates(v)
|
||||
|
||||
}
|
||||
|
||||
func TestUpdateCheck(t *testing.T) {
|
||||
releaseVersion := &models.GitHubReleaseVersion{}
|
||||
_, err := resty.New().R().
|
||||
SetResult(releaseVersion).
|
||||
SetHeader("Accept", "application/vnd.github+json").
|
||||
SetHeader("X-GitHub-Api-Version", "2022-11-28").
|
||||
Get("https://api.github.com/repos/ArvinLovegood/go-stock/releases/latest")
|
||||
// https://api.github.com/repos/OWNER/REPO/releases/latest
|
||||
if err != nil {
|
||||
logger.SugaredLogger.Errorf("get github release version error:%s", err.Error())
|
||||
return
|
||||
}
|
||||
logger.SugaredLogger.Infof("releaseVersion:%+v", releaseVersion)
|
||||
}
|
||||
|
||||
@@ -55,8 +55,8 @@ func (m MarketNewsApi) TelegraphList(crawlTimeOut int64) *[]models.Telegraph {
|
||||
news := v.(map[string]any)
|
||||
ctime, _ := convertor.ToInt(news["ctime"])
|
||||
dataTime := time.Unix(ctime, 0).Local()
|
||||
logger.SugaredLogger.Debugf("dataTime: %s", dataTime)
|
||||
telegraph := models.Telegraph{
|
||||
Title: news["title"].(string),
|
||||
Content: news["content"].(string),
|
||||
Time: dataTime.Format("15:04:05"),
|
||||
DataTime: &dataTime,
|
||||
@@ -66,7 +66,11 @@ func (m MarketNewsApi) TelegraphList(crawlTimeOut int64) *[]models.Telegraph {
|
||||
SentimentResult: AnalyzeSentiment(news["content"].(string)).Description,
|
||||
}
|
||||
cnt := int64(0)
|
||||
db.Dao.Model(telegraph).Where("time=? and content=?", telegraph.Time, telegraph.Content).Count(&cnt)
|
||||
if telegraph.Title == "" {
|
||||
db.Dao.Model(telegraph).Where("content=?", telegraph.Content).Count(&cnt)
|
||||
} else {
|
||||
db.Dao.Model(telegraph).Where("title=?", telegraph.Title).Count(&cnt)
|
||||
}
|
||||
if cnt > 0 {
|
||||
continue
|
||||
}
|
||||
@@ -224,6 +228,29 @@ func (m MarketNewsApi) GetTelegraphList(source string) *[]*models.Telegraph {
|
||||
}
|
||||
return news
|
||||
}
|
||||
func (m MarketNewsApi) GetTelegraphListWithPaging(source string, page, pageSize int) *[]*models.Telegraph {
|
||||
// 计算偏移量
|
||||
offset := (page - 1) * pageSize
|
||||
|
||||
news := &[]*models.Telegraph{}
|
||||
if source != "" {
|
||||
db.Dao.Model(news).Preload("TelegraphTags").Where("source=?", source).Order("data_time desc,time desc").Limit(pageSize).Offset(offset).Find(news)
|
||||
} else {
|
||||
db.Dao.Model(news).Preload("TelegraphTags").Order("data_time desc,time desc").Limit(pageSize).Offset(offset).Find(news)
|
||||
}
|
||||
for _, item := range *news {
|
||||
tags := &[]models.Tags{}
|
||||
db.Dao.Model(&models.Tags{}).Where("id in ?", lo.Map(item.TelegraphTags, func(item models.TelegraphTags, index int) uint {
|
||||
return item.TagId
|
||||
})).Find(&tags)
|
||||
tagNames := lo.Map(*tags, func(item models.Tags, index int) string {
|
||||
return item.Name
|
||||
})
|
||||
item.SubjectTags = tagNames
|
||||
logger.SugaredLogger.Infof("tagNames %v ,SubjectTags:%s", tagNames, item.SubjectTags)
|
||||
}
|
||||
return news
|
||||
}
|
||||
|
||||
func (m MarketNewsApi) GetSinaNews(crawlTimeOut uint) *[]models.Telegraph {
|
||||
news := &[]models.Telegraph{}
|
||||
@@ -265,6 +292,7 @@ func (m MarketNewsApi) GetSinaNews(crawlTimeOut uint) *[]models.Telegraph {
|
||||
data := item.(map[string]any)
|
||||
//logger.SugaredLogger.Infof("%s:%s", data["create_time"], data["rich_text"])
|
||||
telegraph.Content = data["rich_text"].(string)
|
||||
telegraph.Title = strutil.SubInBetween(data["rich_text"].(string), "【", "】")
|
||||
telegraph.Time = strings.Split(data["create_time"].(string), " ")[1]
|
||||
dataTime, _ := time.ParseInLocation("2006-01-02 15:04:05", data["create_time"].(string), time.Local)
|
||||
if &dataTime != nil {
|
||||
@@ -288,7 +316,11 @@ func (m MarketNewsApi) GetSinaNews(crawlTimeOut uint) *[]models.Telegraph {
|
||||
if telegraph.Content != "" {
|
||||
telegraph.SentimentResult = AnalyzeSentiment(telegraph.Content).Description
|
||||
cnt := int64(0)
|
||||
db.Dao.Model(telegraph).Where("content=? and source=?", telegraph.Content, telegraph.Source).Count(&cnt)
|
||||
if telegraph.Title == "" {
|
||||
db.Dao.Model(telegraph).Where("content=?", telegraph.Content).Count(&cnt)
|
||||
} else {
|
||||
db.Dao.Model(telegraph).Where("title=?", telegraph.Title).Count(&cnt)
|
||||
}
|
||||
if cnt == 0 {
|
||||
db.Dao.Create(&telegraph)
|
||||
telegraphs = append(telegraphs, telegraph)
|
||||
@@ -651,8 +683,10 @@ func (m MarketNewsApi) TradingViewNews() *[]models.Telegraph {
|
||||
TVNews := &[]models.TVNews{}
|
||||
news := &[]models.Telegraph{}
|
||||
// url := "https://news-mediator.tradingview.com/news-flow/v2/news?filter=lang:zh-Hans&filter=area:WLD&client=screener&streaming=false"
|
||||
url := "https://news-mediator.tradingview.com/news-flow/v2/news?filter=area%3AWLD&filter=lang%3Azh-Hans&client=screener&streaming=false"
|
||||
resp, err := client.SetTimeout(time.Duration(5)*time.Second).R().
|
||||
//url := "https://news-mediator.tradingview.com/news-flow/v2/news?filter=area%3AWLD&filter=lang%3Azh-Hans&client=screener&streaming=false"
|
||||
url := "https://news-mediator.tradingview.com/news-flow/v2/news?filter=lang%3Azh-Hans&client=screener&streaming=false"
|
||||
|
||||
resp, err := client.SetTimeout(time.Duration(15)*time.Second).R().
|
||||
SetHeader("Host", "news-mediator.tradingview.com").
|
||||
SetHeader("Origin", "https://cn.tradingview.com").
|
||||
SetHeader("Referer", "https://cn.tradingview.com/").
|
||||
@@ -699,7 +733,11 @@ func (m MarketNewsApi) TradingViewNews() *[]models.Telegraph {
|
||||
SentimentResult: sentimentResult,
|
||||
}
|
||||
cnt := int64(0)
|
||||
db.Dao.Model(telegraph).Where("time=? and title=? and source=?", telegraph.Time, telegraph.Title, "外媒").Count(&cnt)
|
||||
if telegraph.Title == "" {
|
||||
db.Dao.Model(telegraph).Where("content=?", telegraph.Content).Count(&cnt)
|
||||
} else {
|
||||
db.Dao.Model(telegraph).Where("title=?", telegraph.Title).Count(&cnt)
|
||||
}
|
||||
if cnt > 0 {
|
||||
continue
|
||||
}
|
||||
@@ -1027,7 +1065,8 @@ func (m MarketNewsApi) ReutersNew() *models.ReutersNews {
|
||||
client.SetProxy(config.HttpProxy)
|
||||
}
|
||||
news := &models.ReutersNews{}
|
||||
url := "https://www.reuters.com/pf/api/v3/content/fetch/articles-by-section-alias-or-id-v1?query={\"arc-site\":\"reuters\",\"fetch_type\":\"collection\",\"offset\":0,\"section_id\":\"/world/\",\"size\":9,\"uri\":\"/world/\",\"website\":\"reuters\"}&d=300&mxId=00000000&_website=reuters"
|
||||
//url := "https://www.reuters.com/pf/api/v3/content/fetch/articles-by-section-alias-or-id-v1?query={\"arc-site\":\"reuters\",\"fetch_type\":\"collection\",\"offset\":0,\"section_id\":\"/world/\",\"size\":9,\"uri\":\"/world/\",\"website\":\"reuters\"}&d=300&mxId=00000000&_website=reuters"
|
||||
url := "https://www.reuters.com/pf/api/v3/content/fetch/recent-stories-by-sections-v1?query=%7B%22section_ids%22%3A%22%2Fworld%2F%22%2C%22size%22%3A4%2C%22website%22%3A%22reuters%22%7D&d=334&mxId=00000000&_website=reuters"
|
||||
_, err := client.SetTimeout(time.Duration(5)*time.Second).R().
|
||||
SetHeader("Host", "www.reuters.com").
|
||||
SetHeader("Origin", "https://www.reuters.com").
|
||||
|
||||
@@ -6,11 +6,14 @@ import (
|
||||
"go-stock/backend/logger"
|
||||
"go-stock/backend/models"
|
||||
"go-stock/backend/util"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/coocood/freecache"
|
||||
"github.com/duke-git/lancet/v2/random"
|
||||
"github.com/duke-git/lancet/v2/strutil"
|
||||
"github.com/go-resty/resty/v2"
|
||||
"github.com/tidwall/gjson"
|
||||
)
|
||||
|
||||
@@ -89,12 +92,13 @@ func TestStockResearchReport(t *testing.T) {
|
||||
|
||||
func TestIndustryResearchReport(t *testing.T) {
|
||||
db.Init("../../data/stock.db")
|
||||
resp := NewMarketNewsApi().IndustryResearchReport("456", 7)
|
||||
resp := NewMarketNewsApi().IndustryResearchReport("", 7)
|
||||
for _, a := range resp {
|
||||
logger.SugaredLogger.Debugf("value: %+v", a)
|
||||
data := a.(map[string]any)
|
||||
logger.SugaredLogger.Debugf("value: %s infoCode:%s", data["title"], data["infoCode"])
|
||||
NewMarketNewsApi().GetIndustryReportInfo(data["infoCode"].(string))
|
||||
logger.SugaredLogger.Debugf("url: https://pdf.dfcfw.com/pdf/H3_%s_1.pdf", data["infoCode"])
|
||||
//NewMarketNewsApi().GetIndustryReportInfo(data["infoCode"].(string))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -253,3 +257,38 @@ func TestTelegraphList(t *testing.T) {
|
||||
InitAnalyzeSentiment()
|
||||
NewMarketNewsApi().TelegraphList(30)
|
||||
}
|
||||
|
||||
func TestProxy(t *testing.T) {
|
||||
response, err := resty.New().
|
||||
SetProxy("http://go-stock:778d4ff2-73f3-4d56-b3c3-d9a730a06ae3@stock.sparkmemory.top:8888").
|
||||
R().
|
||||
SetHeader("Host", "news-mediator.tradingview.com").
|
||||
SetHeader("Origin", "https://cn.tradingview.com").
|
||||
SetHeader("Referer", "https://cn.tradingview.com/").
|
||||
SetHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:140.0) Gecko/20100101 Firefox/140.0").
|
||||
//Get("https://api.ipify.org")
|
||||
Get("https://news-mediator.tradingview.com/news-flow/v2/news?filter=lang%3Azh-Hans&client=screener&streaming=false&user_prostatus=non_pro")
|
||||
if err != nil {
|
||||
logger.SugaredLogger.Error(err)
|
||||
return
|
||||
}
|
||||
logger.SugaredLogger.Debugf("value: %s", response.String())
|
||||
|
||||
}
|
||||
|
||||
func TestNtfy(t *testing.T) {
|
||||
|
||||
//attach := "http://go-stock.sparkmemory.top/%E5%88%86%E6%9E%90%E6%8A%A5%E5%91%8A/%E8%B5%84%E9%87%91%E6%B5%81%E5%90%91/2025-12/AI%EF%BC%9A%E5%B8%82%E5%9C%BA%E5%88%86%E6%9E%90%E6%8A%A5%E5%91%8A-[2025.12.11_12.02.01].html"
|
||||
//post, err := resty.New().SetBaseURL("https://go-stock.sparkmemory.top:16667").R().
|
||||
// SetHeader("Filename", "AI:市场分析报告-[2025.12.11_12.02.01].html").
|
||||
// SetHeader("Icon", "https://go-stock.sparkmemory.top/appicon.png").
|
||||
// SetHeader("Attach", attach).
|
||||
// SetBody("AI:市场分析报告-[2025.12.11_12.02.01]").Post("/go-stock")
|
||||
//if err != nil {
|
||||
// logger.SugaredLogger.Error(err)
|
||||
// return
|
||||
//}
|
||||
//logger.SugaredLogger.Debugf("value: %s", post.String())
|
||||
logger.SugaredLogger.Debugf("value: %s", filepath.Base("https://go-stock.sparkmemory.top/%E5%88%86%E6%9E%90%E6%8A%A5%E5%91%8A/2025/12/11/%E5%B8%82%E5%9C%BA%E8%B5%84%E8%AE%AF[%E5%B8%82%E5%9C%BA%E8%B5%84%E8%AE%AF]-(2025-12-11)AI%E5%88%86%E6%9E%90%E7%BB%93%E6%9E%9C_20251211131509.html"))
|
||||
logger.SugaredLogger.Debugf("value: %s", strutil.After("/data/go-stock-site/docs/分析报告/2025/12/09/市场资讯[市场资讯]-(2025-12-09)AI分析结果.md", "/data/go-stock-site/docs/"))
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ import (
|
||||
"github.com/PuerkitoBio/goquery"
|
||||
"github.com/chromedp/chromedp"
|
||||
"github.com/duke-git/lancet/v2/convertor"
|
||||
"github.com/duke-git/lancet/v2/mathutil"
|
||||
"github.com/duke-git/lancet/v2/random"
|
||||
"github.com/duke-git/lancet/v2/strutil"
|
||||
"github.com/go-resty/resty/v2"
|
||||
@@ -135,17 +136,19 @@ type Tool struct {
|
||||
Function ToolFunction `json:"function"`
|
||||
}
|
||||
type FunctionParameters struct {
|
||||
Type string `json:"type"`
|
||||
Properties map[string]any `json:"properties"`
|
||||
Required []string `json:"required"`
|
||||
Type string `json:"type"`
|
||||
Properties map[string]any `json:"properties"`
|
||||
Required []string `json:"required"`
|
||||
AdditionalProperties bool `json:"additionalProperties"`
|
||||
}
|
||||
type ToolFunction struct {
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description"`
|
||||
Parameters FunctionParameters `json:"parameters"`
|
||||
Name string `json:"name"`
|
||||
Strict bool `json:"strict"`
|
||||
Description string `json:"description"`
|
||||
Parameters *FunctionParameters `json:"parameters"`
|
||||
}
|
||||
|
||||
func (o *OpenAi) NewSummaryStockNewsStreamWithTools(userQuestion string, sysPromptId *int, tools []Tool) <-chan map[string]any {
|
||||
func (o *OpenAi) NewSummaryStockNewsStreamWithTools(userQuestion string, sysPromptId *int, tools []Tool, thinking bool) <-chan map[string]any {
|
||||
ch := make(chan map[string]any, 512)
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
@@ -185,12 +188,12 @@ func (o *OpenAi) NewSummaryStockNewsStreamWithTools(userQuestion string, sysProm
|
||||
"content": "当前时间",
|
||||
})
|
||||
msg = append(msg, map[string]interface{}{
|
||||
"role": "assistant",
|
||||
"content": "当前本地时间是:" + time.Now().Format("2006-01-02 15:04:05"),
|
||||
"role": "assistant",
|
||||
"reasoning_content": "使用工具查询",
|
||||
"content": "当前本地时间是:" + time.Now().Format("2006-01-02 15:04:05"),
|
||||
})
|
||||
wg := &sync.WaitGroup{}
|
||||
wg.Add(6)
|
||||
|
||||
wg.Add(5)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
datas := NewMarketNewsApi().InteractiveAnswer(1, 100, "")
|
||||
@@ -200,8 +203,9 @@ func (o *OpenAi) NewSummaryStockNewsStreamWithTools(userQuestion string, sysProm
|
||||
"content": "投资者互动数据",
|
||||
})
|
||||
msg = append(msg, map[string]interface{}{
|
||||
"role": "assistant",
|
||||
"content": content,
|
||||
"role": "assistant",
|
||||
"reasoning_content": "使用工具查询",
|
||||
"content": content,
|
||||
})
|
||||
}()
|
||||
|
||||
@@ -226,8 +230,9 @@ func (o *OpenAi) NewSummaryStockNewsStreamWithTools(userQuestion string, sysProm
|
||||
"content": "国内宏观经济数据",
|
||||
})
|
||||
msg = append(msg, map[string]interface{}{
|
||||
"role": "assistant",
|
||||
"content": "\n# 国内宏观经济数据:\n" + market.String(),
|
||||
"role": "assistant",
|
||||
"reasoning_content": "使用工具查询",
|
||||
"content": "\n# 国内宏观经济数据:\n" + market.String(),
|
||||
})
|
||||
}()
|
||||
|
||||
@@ -250,8 +255,9 @@ func (o *OpenAi) NewSummaryStockNewsStreamWithTools(userQuestion string, sysProm
|
||||
"content": "当前市场/大盘/行业/指数行情",
|
||||
})
|
||||
msg = append(msg, map[string]interface{}{
|
||||
"role": "assistant",
|
||||
"content": "当前市场/大盘/行业/指数行情如下:\n" + market.String(),
|
||||
"role": "assistant",
|
||||
"reasoning_content": "使用工具查询",
|
||||
"content": "当前市场/大盘/行业/指数行情如下:\n" + market.String(),
|
||||
})
|
||||
}()
|
||||
|
||||
@@ -280,65 +286,73 @@ func (o *OpenAi) NewSummaryStockNewsStreamWithTools(userQuestion string, sysProm
|
||||
"content": "近期重大事件/会议",
|
||||
})
|
||||
msg = append(msg, map[string]interface{}{
|
||||
"role": "assistant",
|
||||
"content": "近期重大事件/会议如下:\n" + md.String(),
|
||||
"role": "assistant",
|
||||
"reasoning_content": "使用工具查询",
|
||||
"content": "近期重大事件/会议如下:\n" + md.String(),
|
||||
})
|
||||
|
||||
}()
|
||||
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
resp := NewMarketNewsApi().TradingViewNews()
|
||||
var newsText strings.Builder
|
||||
//go func() {
|
||||
// defer wg.Done()
|
||||
// resp := NewMarketNewsApi().TradingViewNews()
|
||||
// var newsText strings.Builder
|
||||
//
|
||||
// for _, a := range *resp {
|
||||
// logger.SugaredLogger.Debugf("TradingViewNews: %s", a.Title)
|
||||
// newsText.WriteString(a.Title + "\n")
|
||||
// }
|
||||
// msg = append(msg, map[string]interface{}{
|
||||
// "role": "user",
|
||||
// "content": "全球新闻资讯",
|
||||
// })
|
||||
// msg = append(msg, map[string]interface{}{
|
||||
// "role": "assistant",
|
||||
// "reasoning_content": "使用工具查询",
|
||||
// "content": newsText.String(),
|
||||
// })
|
||||
//}()
|
||||
|
||||
for _, a := range *resp {
|
||||
logger.SugaredLogger.Debugf("TradingViewNews: %s", a.Title)
|
||||
newsText.WriteString(a.Title + "\n")
|
||||
}
|
||||
msg = append(msg, map[string]interface{}{
|
||||
"role": "user",
|
||||
"content": "全球新闻资讯",
|
||||
})
|
||||
msg = append(msg, map[string]interface{}{
|
||||
"role": "assistant",
|
||||
"content": newsText.String(),
|
||||
})
|
||||
}()
|
||||
//go func() {
|
||||
// defer wg.Done()
|
||||
// news := NewMarketNewsApi().ReutersNew()
|
||||
// messageText := strings.Builder{}
|
||||
// for _, article := range news.Result.Articles {
|
||||
// messageText.WriteString("## " + article.Title + "\n")
|
||||
// messageText.WriteString("### " + article.Description + "\n")
|
||||
// }
|
||||
// msg = append(msg, map[string]interface{}{
|
||||
// "role": "user",
|
||||
// "content": "外媒全球新闻资讯",
|
||||
// })
|
||||
// msg = append(msg, map[string]interface{}{
|
||||
// "role": "assistant",
|
||||
// "reasoning_content": "使用工具查询",
|
||||
// "content": messageText.String(),
|
||||
// })
|
||||
//}()
|
||||
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
news := NewMarketNewsApi().ReutersNew()
|
||||
news := NewMarketNewsApi().GetNews24HoursList("最新24小时市场资讯", random.RandInt(200, 1000))
|
||||
messageText := strings.Builder{}
|
||||
for _, article := range news.Result.Articles {
|
||||
messageText.WriteString("## " + article.Title + "\n")
|
||||
messageText.WriteString("### " + article.Description + "\n")
|
||||
for _, telegraph := range *news {
|
||||
messageText.WriteString("## " + telegraph.DataTime.Format("2006-01-02 15:04:05") + ":" + "\n")
|
||||
messageText.WriteString("### " + telegraph.Content + "\n")
|
||||
}
|
||||
msg = append(msg, map[string]interface{}{
|
||||
"role": "user",
|
||||
"content": "外媒全球新闻资讯",
|
||||
"content": "市场资讯",
|
||||
})
|
||||
msg = append(msg, map[string]interface{}{
|
||||
"role": "assistant",
|
||||
"content": messageText.String(),
|
||||
"role": "assistant",
|
||||
"reasoning_content": "使用工具查询",
|
||||
"content": messageText.String(),
|
||||
})
|
||||
}()
|
||||
wg.Wait()
|
||||
news := NewMarketNewsApi().GetNewsList2("财联社电报", random.RandInt(100, 500))
|
||||
messageText := strings.Builder{}
|
||||
for _, telegraph := range *news {
|
||||
messageText.WriteString("## " + telegraph.Time + ":" + "\n")
|
||||
messageText.WriteString("### " + telegraph.Content + "\n")
|
||||
}
|
||||
//logger.SugaredLogger.Infof("市场资讯 messageText=\n%s", messageText.String())
|
||||
|
||||
msg = append(msg, map[string]interface{}{
|
||||
"role": "user",
|
||||
"content": "市场资讯",
|
||||
})
|
||||
msg = append(msg, map[string]interface{}{
|
||||
"role": "assistant",
|
||||
"content": messageText.String(),
|
||||
})
|
||||
wg.Wait()
|
||||
|
||||
if userQuestion == "" {
|
||||
userQuestion = "请根据当前时间,总结和分析股票市场新闻中的投资机会"
|
||||
}
|
||||
@@ -346,12 +360,12 @@ func (o *OpenAi) NewSummaryStockNewsStreamWithTools(userQuestion string, sysProm
|
||||
"role": "user",
|
||||
"content": userQuestion,
|
||||
})
|
||||
AskAiWithTools(o, errors.New(""), msg, ch, userQuestion, tools)
|
||||
AskAiWithTools(o, errors.New(""), msg, ch, userQuestion, tools, thinking)
|
||||
}()
|
||||
return ch
|
||||
}
|
||||
|
||||
func (o *OpenAi) NewSummaryStockNewsStream(userQuestion string, sysPromptId *int) <-chan map[string]any {
|
||||
func (o *OpenAi) NewSummaryStockNewsStream(userQuestion string, sysPromptId *int, think bool) <-chan map[string]any {
|
||||
ch := make(chan map[string]any, 512)
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
@@ -419,42 +433,74 @@ func (o *OpenAi) NewSummaryStockNewsStream(userQuestion string, sysPromptId *int
|
||||
"content": "当前市场指数行情情况如下:\n" + market.String(),
|
||||
})
|
||||
}()
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
resp := NewMarketNewsApi().TradingViewNews()
|
||||
var newsText strings.Builder
|
||||
|
||||
for _, a := range *resp {
|
||||
logger.SugaredLogger.Debugf("TradingViewNews: %s", a.Title)
|
||||
newsText.WriteString(a.Title + "\n")
|
||||
}
|
||||
msg = append(msg, map[string]interface{}{
|
||||
"role": "user",
|
||||
"content": "外媒全球新闻资讯",
|
||||
})
|
||||
msg = append(msg, map[string]interface{}{
|
||||
"role": "assistant",
|
||||
"content": newsText.String(),
|
||||
})
|
||||
}()
|
||||
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
news := NewMarketNewsApi().ReutersNew()
|
||||
messageText := strings.Builder{}
|
||||
for _, article := range news.Result.Articles {
|
||||
messageText.WriteString("## " + article.Title + "\n")
|
||||
messageText.WriteString("### " + article.Description + "\n")
|
||||
md := strings.Builder{}
|
||||
res := NewMarketNewsApi().ClsCalendar()
|
||||
for _, a := range res {
|
||||
bytes, err := json.Marshal(a)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
//logger.SugaredLogger.Debugf("value: %+v", string(bytes))
|
||||
date := gjson.Get(string(bytes), "calendar_day")
|
||||
md.WriteString("\n### 事件/会议日期:" + date.String())
|
||||
list := gjson.Get(string(bytes), "items")
|
||||
//logger.SugaredLogger.Debugf("value: %+v,list: %+v", date.String(), list)
|
||||
list.ForEach(func(key, value gjson.Result) bool {
|
||||
logger.SugaredLogger.Debugf("key: %+v,value: %+v", key.String(), gjson.Get(value.String(), "title"))
|
||||
md.WriteString("\n- " + gjson.Get(value.String(), "title").String())
|
||||
return true
|
||||
})
|
||||
}
|
||||
msg = append(msg, map[string]interface{}{
|
||||
"role": "user",
|
||||
"content": "外媒全球新闻资讯",
|
||||
"content": "近期重大事件/会议",
|
||||
})
|
||||
msg = append(msg, map[string]interface{}{
|
||||
"role": "assistant",
|
||||
"content": messageText.String(),
|
||||
"role": "assistant",
|
||||
"reasoning_content": "使用工具查询",
|
||||
"content": "近期重大事件/会议如下:\n" + md.String(),
|
||||
})
|
||||
|
||||
}()
|
||||
//go func() {
|
||||
// defer wg.Done()
|
||||
// resp := NewMarketNewsApi().TradingViewNews()
|
||||
// var newsText strings.Builder
|
||||
//
|
||||
// for _, a := range *resp {
|
||||
// logger.SugaredLogger.Debugf("TradingViewNews: %s", a.Title)
|
||||
// newsText.WriteString(a.Title + "\n")
|
||||
// }
|
||||
// msg = append(msg, map[string]interface{}{
|
||||
// "role": "user",
|
||||
// "content": "外媒全球新闻资讯",
|
||||
// })
|
||||
// msg = append(msg, map[string]interface{}{
|
||||
// "role": "assistant",
|
||||
// "content": newsText.String(),
|
||||
// })
|
||||
//}()
|
||||
|
||||
//go func() {
|
||||
// defer wg.Done()
|
||||
// news := NewMarketNewsApi().ReutersNew()
|
||||
// messageText := strings.Builder{}
|
||||
// for _, article := range news.Result.Articles {
|
||||
// messageText.WriteString("## " + article.Title + "\n")
|
||||
// messageText.WriteString("### " + article.Description + "\n")
|
||||
// }
|
||||
// msg = append(msg, map[string]interface{}{
|
||||
// "role": "user",
|
||||
// "content": "外媒全球新闻资讯",
|
||||
// })
|
||||
// msg = append(msg, map[string]interface{}{
|
||||
// "role": "assistant",
|
||||
// "content": messageText.String(),
|
||||
// })
|
||||
//}()
|
||||
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
@@ -470,9 +516,30 @@ func (o *OpenAi) NewSummaryStockNewsStream(userQuestion string, sysPromptId *int
|
||||
})
|
||||
}()
|
||||
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
markdownTable := ""
|
||||
res := NewSearchStockApi("").HotStrategy()
|
||||
bytes, _ := json.Marshal(res)
|
||||
strategy := &models.HotStrategy{}
|
||||
json.Unmarshal(bytes, strategy)
|
||||
for _, data := range strategy.Data {
|
||||
data.Chg = mathutil.RoundToFloat(100*data.Chg, 2)
|
||||
}
|
||||
markdownTable = util.MarkdownTableWithTitle("当前热门选股策略", strategy.Data)
|
||||
msg = append(msg, map[string]interface{}{
|
||||
"role": "user",
|
||||
"content": "当前热门选股策略",
|
||||
})
|
||||
msg = append(msg, map[string]interface{}{
|
||||
"role": "assistant",
|
||||
"content": markdownTable,
|
||||
})
|
||||
}()
|
||||
|
||||
wg.Wait()
|
||||
|
||||
news := NewMarketNewsApi().GetNewsList2("财联社电报", random.RandInt(100, 500))
|
||||
news := NewMarketNewsApi().GetNews24HoursList("最近24小时市场资讯", random.RandInt(200, 1000))
|
||||
messageText := strings.Builder{}
|
||||
for _, telegraph := range *news {
|
||||
messageText.WriteString("## " + telegraph.Time + ":" + "\n")
|
||||
@@ -495,12 +562,12 @@ func (o *OpenAi) NewSummaryStockNewsStream(userQuestion string, sysPromptId *int
|
||||
"role": "user",
|
||||
"content": userQuestion,
|
||||
})
|
||||
AskAi(o, errors.New(""), msg, ch, userQuestion)
|
||||
AskAi(o, errors.New(""), msg, ch, userQuestion, think)
|
||||
}()
|
||||
return ch
|
||||
}
|
||||
|
||||
func (o *OpenAi) NewChatStream(stock, stockCode, userQuestion string, sysPromptId *int, tools []Tool) <-chan map[string]any {
|
||||
func (o *OpenAi) NewChatStream(stock, stockCode, userQuestion string, sysPromptId *int, tools []Tool, thinking bool) <-chan map[string]any {
|
||||
ch := make(chan map[string]any, 512)
|
||||
|
||||
defer func() {
|
||||
@@ -856,15 +923,15 @@ func (o *OpenAi) NewChatStream(stock, stockCode, userQuestion string, sysPromptI
|
||||
//reqJson, _ := json.Marshal(msg)
|
||||
//logger.SugaredLogger.Errorf("Stream request: \n%s\n", reqJson)
|
||||
if tools != nil && len(tools) > 0 {
|
||||
AskAiWithTools(o, err, msg, ch, question, tools)
|
||||
AskAiWithTools(o, err, msg, ch, question, tools, thinking)
|
||||
} else {
|
||||
AskAi(o, err, msg, ch, question)
|
||||
AskAi(o, err, msg, ch, question, thinking)
|
||||
}
|
||||
}()
|
||||
return ch
|
||||
}
|
||||
|
||||
func AskAi(o *OpenAi, err error, messages []map[string]interface{}, ch chan map[string]any, question string) {
|
||||
func AskAi(o *OpenAi, err error, messages []map[string]interface{}, ch chan map[string]any, question string, think bool) {
|
||||
client := resty.New()
|
||||
client.SetBaseURL(strutil.Trim(o.BaseUrl))
|
||||
client.SetHeader("Authorization", "Bearer "+o.ApiKey)
|
||||
@@ -873,11 +940,18 @@ func AskAi(o *OpenAi, err error, messages []map[string]interface{}, ch chan map[
|
||||
if o.TimeOut <= 0 {
|
||||
o.TimeOut = 300
|
||||
}
|
||||
thinking := "disabled"
|
||||
if think {
|
||||
thinking = "enabled"
|
||||
}
|
||||
client.SetTimeout(time.Duration(o.TimeOut) * time.Second)
|
||||
resp, err := client.R().
|
||||
SetDoNotParseResponse(true).
|
||||
SetBody(map[string]interface{}{
|
||||
"model": o.Model,
|
||||
"model": o.Model,
|
||||
"thinking": map[string]any{
|
||||
"type": thinking,
|
||||
},
|
||||
"max_tokens": o.MaxTokens,
|
||||
"temperature": o.Temperature,
|
||||
"stream": true,
|
||||
@@ -1005,7 +1079,10 @@ func AskAi(o *OpenAi, err error, messages []map[string]interface{}, ch chan map[
|
||||
|
||||
}
|
||||
}
|
||||
func AskAiWithTools(o *OpenAi, err error, messages []map[string]interface{}, ch chan map[string]any, question string, tools []Tool) {
|
||||
func AskAiWithTools(o *OpenAi, err error, messages []map[string]interface{}, ch chan map[string]any, question string, tools []Tool, thinkingMode bool) {
|
||||
bytes, _ := json.Marshal(messages)
|
||||
logger.SugaredLogger.Debugf("Stream request: \n%s\n", string(bytes))
|
||||
|
||||
client := resty.New()
|
||||
client.SetBaseURL(strutil.Trim(o.BaseUrl))
|
||||
client.SetHeader("Authorization", "Bearer "+o.ApiKey)
|
||||
@@ -1014,11 +1091,20 @@ func AskAiWithTools(o *OpenAi, err error, messages []map[string]interface{}, ch
|
||||
if o.TimeOut <= 0 {
|
||||
o.TimeOut = 300
|
||||
}
|
||||
thinking := "disabled"
|
||||
if thinkingMode {
|
||||
thinking = "enabled"
|
||||
}
|
||||
client.SetTimeout(time.Duration(o.TimeOut) * time.Second)
|
||||
resp, err := client.R().
|
||||
SetDoNotParseResponse(true).
|
||||
SetBody(map[string]interface{}{
|
||||
"model": o.Model,
|
||||
"model": o.Model,
|
||||
"thinking": map[string]any{
|
||||
//"type": "disabled",
|
||||
//"type": "enabled",
|
||||
"type": thinking,
|
||||
},
|
||||
"max_tokens": o.MaxTokens,
|
||||
"temperature": o.Temperature,
|
||||
"stream": true,
|
||||
@@ -1046,6 +1132,8 @@ func AskAiWithTools(o *OpenAi, err error, messages []map[string]interface{}, ch
|
||||
currentFuncName := ""
|
||||
currentCallId := ""
|
||||
var currentAIContent strings.Builder
|
||||
var reasoningContentText strings.Builder
|
||||
var contentText strings.Builder
|
||||
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
@@ -1081,6 +1169,7 @@ func AskAiWithTools(o *OpenAi, err error, messages []map[string]interface{}, ch
|
||||
if err := json.Unmarshal([]byte(data), &streamResponse); err == nil {
|
||||
for _, choice := range streamResponse.Choices {
|
||||
if content := choice.Delta.Content; content != "" {
|
||||
contentText.WriteString(content)
|
||||
//ch <- content
|
||||
//logger.SugaredLogger.Infof("Content data: %s", content)
|
||||
|
||||
@@ -1108,6 +1197,7 @@ func AskAiWithTools(o *OpenAi, err error, messages []map[string]interface{}, ch
|
||||
|
||||
}
|
||||
if reasoningContent := choice.Delta.ReasoningContent; reasoningContent != "" {
|
||||
reasoningContentText.WriteString(reasoningContent)
|
||||
//ch <- reasoningContent
|
||||
ch <- map[string]any{
|
||||
"code": 1,
|
||||
@@ -1154,7 +1244,7 @@ func AskAiWithTools(o *OpenAi, err error, messages []map[string]interface{}, ch
|
||||
}
|
||||
|
||||
content := "无符合条件的数据"
|
||||
res := NewSearchStockApi(words).SearchStock(random.RandInt(5, 20))
|
||||
res := NewSearchStockApi(words).SearchStock(random.RandInt(50, 120))
|
||||
if convertor.ToString(res["code"]) == "100" {
|
||||
resData := res["data"].(map[string]any)
|
||||
result := resData["result"].(map[string]any)
|
||||
@@ -1191,8 +1281,9 @@ func AskAiWithTools(o *OpenAi, err error, messages []map[string]interface{}, ch
|
||||
logger.SugaredLogger.Infof("SearchStockByIndicators:words:%s --> \n%s", words, content)
|
||||
|
||||
messages = append(messages, map[string]interface{}{
|
||||
"role": "assistant",
|
||||
"content": currentAIContent.String(),
|
||||
"role": "assistant",
|
||||
"content": currentAIContent.String(),
|
||||
"reasoning_content": reasoningContentText.String(),
|
||||
"tool_calls": []map[string]any{
|
||||
{
|
||||
"id": currentCallId,
|
||||
@@ -1210,6 +1301,9 @@ func AskAiWithTools(o *OpenAi, err error, messages []map[string]interface{}, ch
|
||||
"role": "tool",
|
||||
"content": content,
|
||||
"tool_call_id": currentCallId,
|
||||
//"reasoning_content": reasoningContentText.String(),
|
||||
//"tool_calls": choice.Delta.ToolCalls,
|
||||
|
||||
})
|
||||
|
||||
//ch <- map[string]any{
|
||||
@@ -1264,8 +1358,9 @@ func AskAiWithTools(o *OpenAi, err error, messages []map[string]interface{}, ch
|
||||
logger.SugaredLogger.Infof("getKLineData=\n%s", markdownTable)
|
||||
|
||||
messages = append(messages, map[string]interface{}{
|
||||
"role": "assistant",
|
||||
"content": currentAIContent.String(),
|
||||
"role": "assistant",
|
||||
"content": currentAIContent.String(),
|
||||
"reasoning_content": reasoningContentText.String(),
|
||||
"tool_calls": []map[string]any{
|
||||
{
|
||||
"id": currentCallId,
|
||||
@@ -1284,6 +1379,8 @@ func AskAiWithTools(o *OpenAi, err error, messages []map[string]interface{}, ch
|
||||
"role": "tool",
|
||||
"content": res,
|
||||
"tool_call_id": currentCallId,
|
||||
//"reasoning_content": reasoningContentText.String(),
|
||||
//"tool_calls": choice.Delta.ToolCalls,
|
||||
})
|
||||
logger.SugaredLogger.Infof("GetStockKLine:stockCode:%s days:%s --> \n%s", stockCode, days, res)
|
||||
|
||||
@@ -1297,8 +1394,9 @@ func AskAiWithTools(o *OpenAi, err error, messages []map[string]interface{}, ch
|
||||
//}
|
||||
} else {
|
||||
messages = append(messages, map[string]interface{}{
|
||||
"role": "assistant",
|
||||
"content": currentAIContent.String(),
|
||||
"role": "assistant",
|
||||
"content": currentAIContent.String(),
|
||||
"reasoning_content": reasoningContentText.String(),
|
||||
"tool_calls": []map[string]any{
|
||||
{
|
||||
"id": currentCallId,
|
||||
@@ -1316,6 +1414,8 @@ func AskAiWithTools(o *OpenAi, err error, messages []map[string]interface{}, ch
|
||||
"role": "tool",
|
||||
"content": "无数据,可能股票代码错误。(A股:sh,sz开头;港股hk开头,美股:us开头)",
|
||||
"tool_call_id": currentCallId,
|
||||
//"reasoning_content": reasoningContentText.String(),
|
||||
//"tool_calls": choice.Delta.ToolCalls,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1344,8 +1444,9 @@ func AskAiWithTools(o *OpenAi, err error, messages []map[string]interface{}, ch
|
||||
content := util.MarkdownTableWithTitle("投资互动数据", datas.Results)
|
||||
logger.SugaredLogger.Infof("InteractiveAnswer=\n%s", content)
|
||||
messages = append(messages, map[string]interface{}{
|
||||
"role": "assistant",
|
||||
"content": currentAIContent.String(),
|
||||
"role": "assistant",
|
||||
"content": currentAIContent.String(),
|
||||
"reasoning_content": reasoningContentText.String(),
|
||||
"tool_calls": []map[string]any{
|
||||
{
|
||||
"id": currentCallId,
|
||||
@@ -1363,6 +1464,8 @@ func AskAiWithTools(o *OpenAi, err error, messages []map[string]interface{}, ch
|
||||
"role": "tool",
|
||||
"content": content,
|
||||
"tool_call_id": currentCallId,
|
||||
//"reasoning_content": reasoningContentText.String(),
|
||||
//"tool_calls": choice.Delta.ToolCalls,
|
||||
})
|
||||
}
|
||||
//
|
||||
@@ -1478,8 +1581,9 @@ func AskAiWithTools(o *OpenAi, err error, messages []map[string]interface{}, ch
|
||||
}
|
||||
logger.SugaredLogger.Infof("stockCode:%s StockResearchReport:\n %s", stockCode, md.String())
|
||||
messages = append(messages, map[string]interface{}{
|
||||
"role": "assistant",
|
||||
"content": currentAIContent.String(),
|
||||
"role": "assistant",
|
||||
"content": currentAIContent.String(),
|
||||
"reasoning_content": reasoningContentText.String(),
|
||||
"tool_calls": []map[string]any{
|
||||
{
|
||||
"id": currentCallId,
|
||||
@@ -1497,11 +1601,50 @@ func AskAiWithTools(o *OpenAi, err error, messages []map[string]interface{}, ch
|
||||
"role": "tool",
|
||||
"content": md.String(),
|
||||
"tool_call_id": currentCallId,
|
||||
//"reasoning_content": reasoningContentText.String(),
|
||||
//"tool_calls": choice.Delta.ToolCalls,
|
||||
})
|
||||
}
|
||||
|
||||
if funcName == "HotStrategyTable" {
|
||||
ch <- map[string]any{
|
||||
"code": 1,
|
||||
"question": question,
|
||||
"chatId": streamResponse.Id,
|
||||
"model": streamResponse.Model,
|
||||
"content": "\r\n```\r\n开始调用工具:HotStrategyTable,\n参数:" + funcArguments + "\r\n```\r\n",
|
||||
"time": time.Now().Format(time.DateTime),
|
||||
}
|
||||
table := NewSearchStockApi("").HotStrategyTable()
|
||||
logger.SugaredLogger.Infof("%s", table)
|
||||
messages = append(messages, map[string]interface{}{
|
||||
"role": "assistant",
|
||||
"content": currentAIContent.String(),
|
||||
"reasoning_content": reasoningContentText.String(),
|
||||
"tool_calls": []map[string]any{
|
||||
{
|
||||
"id": currentCallId,
|
||||
"tool_call_id": currentCallId,
|
||||
"type": "function",
|
||||
"function": map[string]string{
|
||||
"name": funcName,
|
||||
"arguments": funcArguments,
|
||||
"parameters": funcArguments,
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
messages = append(messages, map[string]interface{}{
|
||||
"role": "tool",
|
||||
"content": table,
|
||||
"tool_call_id": currentCallId,
|
||||
//"reasoning_content": reasoningContentText.String(),
|
||||
//"tool_calls": choice.Delta.ToolCalls,
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
AskAiWithTools(o, err, messages, ch, question, tools)
|
||||
AskAiWithTools(o, err, messages, ch, question, tools, thinkingMode)
|
||||
}
|
||||
|
||||
if choice.FinishReason == "stop" {
|
||||
@@ -1549,7 +1692,7 @@ func AskAiWithTools(o *OpenAi, err error, messages []map[string]interface{}, ch
|
||||
}
|
||||
newMessages = append(newMessages, message)
|
||||
}
|
||||
AskAi(o, err, newMessages, ch, question)
|
||||
AskAi(o, err, newMessages, ch, question, thinkingMode)
|
||||
} else {
|
||||
ch <- map[string]any{
|
||||
"code": 0,
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
|
||||
func TestNewDeepSeekOpenAiConfig(t *testing.T) {
|
||||
db.Init("../../data/stock.db")
|
||||
InitAnalyzeSentiment()
|
||||
|
||||
var tools []Tool
|
||||
tools = append(tools, Tool{
|
||||
@@ -16,7 +17,7 @@ func TestNewDeepSeekOpenAiConfig(t *testing.T) {
|
||||
Function: ToolFunction{
|
||||
Name: "SearchStockByIndicators",
|
||||
Description: "根据自然语言筛选股票,返回自然语言选股条件要求的股票所有相关数据",
|
||||
Parameters: FunctionParameters{
|
||||
Parameters: &FunctionParameters{
|
||||
Type: "object",
|
||||
Properties: map[string]any{
|
||||
"words": map[string]any{
|
||||
@@ -29,9 +30,9 @@ func TestNewDeepSeekOpenAiConfig(t *testing.T) {
|
||||
},
|
||||
})
|
||||
|
||||
ai := NewDeepSeekOpenAi(context.TODO(), 1)
|
||||
ai := NewDeepSeekOpenAi(context.TODO(), 0)
|
||||
//res := ai.NewChatStream("长电科技", "sh600584", "长电科技分析和总结", nil)
|
||||
res := ai.NewSummaryStockNewsStreamWithTools("总结市场资讯,发掘潜力标的/行业/板块/概念,控制风险。调用工具函数验证", nil, tools)
|
||||
res := ai.NewSummaryStockNewsStreamWithTools("总结市场资讯,发掘潜力标的/行业/板块/概念,控制风险。调用工具函数验证", nil, tools, true)
|
||||
|
||||
for {
|
||||
select {
|
||||
|
||||
@@ -4,8 +4,11 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"go-stock/backend/logger"
|
||||
"go-stock/backend/models"
|
||||
"go-stock/backend/util"
|
||||
"time"
|
||||
|
||||
"github.com/duke-git/lancet/v2/mathutil"
|
||||
"github.com/go-resty/resty/v2"
|
||||
)
|
||||
|
||||
@@ -81,3 +84,16 @@ func (s SearchStockApi) HotStrategy() map[string]any {
|
||||
json.Unmarshal(resp.Body(), &respMap)
|
||||
return respMap
|
||||
}
|
||||
|
||||
func (s SearchStockApi) HotStrategyTable() string {
|
||||
markdownTable := ""
|
||||
res := s.HotStrategy()
|
||||
bytes, _ := json.Marshal(res)
|
||||
strategy := &models.HotStrategy{}
|
||||
json.Unmarshal(bytes, strategy)
|
||||
for _, data := range strategy.Data {
|
||||
data.Chg = mathutil.RoundToFloat(100*data.Chg, 2)
|
||||
}
|
||||
markdownTable = util.MarkdownTableWithTitle("当前热门选股策略", strategy.Data)
|
||||
return markdownTable
|
||||
}
|
||||
|
||||
@@ -4,10 +4,13 @@ import (
|
||||
"encoding/json"
|
||||
"go-stock/backend/db"
|
||||
"go-stock/backend/logger"
|
||||
"go-stock/backend/models"
|
||||
"go-stock/backend/util"
|
||||
"math"
|
||||
"testing"
|
||||
|
||||
"github.com/duke-git/lancet/v2/convertor"
|
||||
"github.com/duke-git/lancet/v2/mathutil"
|
||||
"github.com/duke-git/lancet/v2/random"
|
||||
)
|
||||
|
||||
@@ -60,10 +63,20 @@ func TestSearchStock(t *testing.T) {
|
||||
func TestSearchStockApi_HotStrategy(t *testing.T) {
|
||||
db.Init("../../data/stock.db")
|
||||
res := NewSearchStockApi("").HotStrategy()
|
||||
logger.SugaredLogger.Infof("res:%+v", res)
|
||||
dataList := res["data"].([]any)
|
||||
for _, v := range dataList {
|
||||
d := v.(map[string]any)
|
||||
logger.SugaredLogger.Infof("v:%+v", d)
|
||||
bytes, err := json.Marshal(res)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
strategy := &models.HotStrategy{}
|
||||
json.Unmarshal(bytes, strategy)
|
||||
for _, data := range strategy.Data {
|
||||
data.Chg = mathutil.RoundToFloat(100*data.Chg, 2)
|
||||
}
|
||||
markdownTable := util.MarkdownTable(strategy.Data)
|
||||
logger.SugaredLogger.Infof("res:%s", markdownTable)
|
||||
//dataList := res["data"].([]any)
|
||||
//for _, v := range dataList {
|
||||
// d := v.(map[string]any)
|
||||
// logger.SugaredLogger.Infof("v:%+v", d)
|
||||
//}
|
||||
}
|
||||
|
||||
@@ -1484,11 +1484,11 @@ func (receiver StockDataApi) getDCStockInfo(market string, page, pageSize int) {
|
||||
|
||||
url := "https://push2.eastmoney.com/api/qt/clist/get?np=1&fltt=1&invt=2&cb=data&fs=%s&fields=f12,f13,f14,f1,f2,f4,f3,f152,f5,f6,f7,f15,f18,f16,f17,f10,f8,f9,f23,f100,f265&fid=f3&pn=%d&pz=%d&po=1&dect=1&wbp2u=|0|0|0|web&_=%d"
|
||||
sprintfUrl := fmt.Sprintf(url, fs, page, pageSize, time.Now().UnixMilli())
|
||||
logger.SugaredLogger.Infof("url:%s", sprintfUrl)
|
||||
logger.SugaredLogger.Infof("page:%d url:%s", page, sprintfUrl)
|
||||
resp, err := receiver.client.SetTimeout(time.Duration(receiver.config.CrawlTimeOut)*time.Second).R().
|
||||
SetHeader("Host", "push2.eastmoney.com").
|
||||
SetHeader("Referer", "https://quote.eastmoney.com/center/gridlist.html").
|
||||
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").
|
||||
SetHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:146.0) Gecko/20100101 Firefox/146.0").
|
||||
Get(sprintfUrl)
|
||||
if err != nil {
|
||||
logger.SugaredLogger.Errorf("err:%s", err.Error())
|
||||
|
||||
@@ -121,10 +121,10 @@ func TestGetHKStockInfo(t *testing.T) {
|
||||
//NewStockDataApi().GetSinaHKStockInfo()
|
||||
//m:105,m:106,m:107 //美股
|
||||
//m:128+t:3,m:128+t:4,m:128+t:1,m:128+t:2 //港股
|
||||
//287 224 605
|
||||
for i := 1; i <= 605; i++ {
|
||||
NewStockDataApi().getDCStockInfo("us", i, 20)
|
||||
time.Sleep(time.Duration(random.RandInt(1, 3)) * time.Second)
|
||||
//274 224 605
|
||||
for i := 197; i <= 274; i++ {
|
||||
NewStockDataApi().getDCStockInfo("", i, 20)
|
||||
time.Sleep(time.Duration(random.RandInt(2, 5)) * time.Second)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -270,15 +270,16 @@ func TestName(t *testing.T) {
|
||||
db.Init("../../data/stock.db")
|
||||
|
||||
stockBasics := &[]StockBasic{}
|
||||
resty.New().R().
|
||||
resty.New().SetProxy("").R().
|
||||
SetHeader("user", "go-stock").
|
||||
SetResult(stockBasics).
|
||||
Get("http://8.134.249.145:18080/go-stock/stock_basic.json")
|
||||
|
||||
db.Dao.Unscoped().Model(&StockBasic{}).Where("1=1").Delete(&StockBasic{})
|
||||
err := db.Dao.CreateInBatches(stockBasics, 400).Error
|
||||
if err != nil {
|
||||
t.Log(err.Error())
|
||||
}
|
||||
logger.SugaredLogger.Infof("%+v", stockBasics)
|
||||
//db.Dao.Unscoped().Model(&StockBasic{}).Where("1=1").Delete(&StockBasic{})
|
||||
//err := db.Dao.CreateInBatches(stockBasics, 400).Error
|
||||
//if err != nil {
|
||||
// t.Log(err.Error())
|
||||
//}
|
||||
|
||||
}
|
||||
|
||||
@@ -73,7 +73,17 @@ var baseDict string
|
||||
var zhDict string
|
||||
|
||||
func InitAnalyzeSentiment() {
|
||||
//err := seg.LoadDictEmbed()
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
logger.SugaredLogger.Error(fmt.Sprintf("panic: %v", r))
|
||||
}
|
||||
}()
|
||||
// 加载简体中文词典
|
||||
//err := seg.LoadDict("zh_s")
|
||||
//if err != nil {
|
||||
// logger.SugaredLogger.Error(err.Error())
|
||||
//}
|
||||
|
||||
err := seg.LoadDictEmbed(baseDict)
|
||||
if err != nil {
|
||||
logger.SugaredLogger.Error(err.Error())
|
||||
@@ -96,6 +106,8 @@ func InitAnalyzeSentiment() {
|
||||
logger.SugaredLogger.Errorf("添加%s失败:%s", stock.Name, err.Error())
|
||||
}
|
||||
}
|
||||
logger.SugaredLogger.Info("加载股票名称词典成功")
|
||||
|
||||
stockhks := &[]models.StockInfoHK{}
|
||||
db.Dao.Model(&models.StockInfoHK{}).Find(stockhks)
|
||||
for _, stock := range *stockhks {
|
||||
@@ -110,6 +122,7 @@ func InitAnalyzeSentiment() {
|
||||
logger.SugaredLogger.Errorf("添加%s失败:%s", stock.Name, err.Error())
|
||||
}
|
||||
}
|
||||
logger.SugaredLogger.Info("加载港股名称词典成功")
|
||||
//stockus := &[]models.StockInfoUS{}
|
||||
//db.Dao.Model(&models.StockInfoUS{}).Where("trim(name) != ?", "").Find(stockus)
|
||||
//for _, stock := range *stockus {
|
||||
@@ -121,11 +134,17 @@ func InitAnalyzeSentiment() {
|
||||
tags := &[]models.Tags{}
|
||||
db.Dao.Model(&models.Tags{}).Where("type = ?", "subject").Find(tags)
|
||||
for _, tag := range *tags {
|
||||
err := seg.ReAddToken(tag.Name, basefreq+100, "n")
|
||||
if tag.Name == "" {
|
||||
continue
|
||||
}
|
||||
err := seg.AddToken(tag.Name, basefreq+100, "n")
|
||||
if err != nil {
|
||||
logger.SugaredLogger.Errorf("添加%s失败:%s", tag.Name, err.Error())
|
||||
} else {
|
||||
logger.SugaredLogger.Infof("添加tags词典[%s]成功", tag.Name)
|
||||
}
|
||||
}
|
||||
logger.SugaredLogger.Info("加载tags词典成功")
|
||||
seg.CalcToken()
|
||||
//加载用户自定义词典 先判断用户词典是否存在
|
||||
if fileutil.IsExist("data/dict/user.txt") {
|
||||
@@ -139,15 +158,31 @@ func InitAnalyzeSentiment() {
|
||||
continue
|
||||
}
|
||||
k := strutil.SplitAndTrim(line, " ")
|
||||
if len(k) == 0 {
|
||||
continue
|
||||
}
|
||||
_, _, ok := seg.Find(k[0])
|
||||
switch len(k) {
|
||||
case 1:
|
||||
err = seg.ReAddToken(k[0], basefreq)
|
||||
if ok {
|
||||
err = seg.ReAddToken(k[0], basefreq)
|
||||
} else {
|
||||
err = seg.AddToken(k[0], basefreq)
|
||||
}
|
||||
case 2:
|
||||
freq, _ := convertor.ToFloat(k[1])
|
||||
err = seg.ReAddToken(k[0], freq)
|
||||
if ok {
|
||||
err = seg.ReAddToken(k[0], freq)
|
||||
} else {
|
||||
err = seg.AddToken(k[0], freq)
|
||||
}
|
||||
case 3:
|
||||
freq, _ := convertor.ToFloat(k[1])
|
||||
err = seg.ReAddToken(k[0], freq, k[2])
|
||||
if ok {
|
||||
err = seg.ReAddToken(k[0], freq, k[2])
|
||||
} else {
|
||||
err = seg.AddToken(k[0], freq, k[2])
|
||||
}
|
||||
default:
|
||||
logger.SugaredLogger.Errorf("用户词典格式错误:%s", line)
|
||||
}
|
||||
@@ -158,18 +193,12 @@ func InitAnalyzeSentiment() {
|
||||
} else {
|
||||
logger.SugaredLogger.Infof("加载用户词典成功")
|
||||
}
|
||||
} else {
|
||||
logger.SugaredLogger.Info("用户词典不存在")
|
||||
}
|
||||
seg.CalcToken()
|
||||
}
|
||||
|
||||
// WordFreqWithWeight 词频统计结果,包含权重信息
|
||||
type WordFreqWithWeight struct {
|
||||
Word string
|
||||
Frequency int
|
||||
Weight float64
|
||||
Score float64
|
||||
}
|
||||
|
||||
// getWordWeight 获取词汇权重
|
||||
func getWordWeight(word string) float64 {
|
||||
// 从分词器获取词汇权重
|
||||
@@ -177,15 +206,15 @@ func getWordWeight(word string) float64 {
|
||||
freq, pos, ok := seg.Dictionary().Find([]byte(word))
|
||||
if ok {
|
||||
logger.SugaredLogger.Infof("获取%s的权重:%f,pos:%s,ok:%v", word, freq, pos, ok)
|
||||
return basefreq + freq
|
||||
return freq
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// SortByWeightAndFrequency 按权重和频次排序词频结果
|
||||
func SortByWeightAndFrequency(frequencies map[string]WordFreqWithWeight) []WordFreqWithWeight {
|
||||
func SortByWeightAndFrequency(frequencies map[string]models.WordFreqWithWeight) []models.WordFreqWithWeight {
|
||||
// 将map转换为slice以便排序
|
||||
freqSlice := make([]WordFreqWithWeight, 0, len(frequencies))
|
||||
freqSlice := make([]models.WordFreqWithWeight, 0, len(frequencies))
|
||||
for _, freq := range frequencies {
|
||||
freqSlice = append(freqSlice, freq)
|
||||
}
|
||||
@@ -200,7 +229,7 @@ func SortByWeightAndFrequency(frequencies map[string]WordFreqWithWeight) []WordF
|
||||
}
|
||||
|
||||
// FilterAndSortWords 过滤标点符号并按权重频次排序
|
||||
func FilterAndSortWords(frequencies map[string]WordFreqWithWeight) []WordFreqWithWeight {
|
||||
func FilterAndSortWords(frequencies map[string]models.WordFreqWithWeight) []models.WordFreqWithWeight {
|
||||
// 先过滤标点符号和分隔符
|
||||
cleanFrequencies := FilterPunctuationAndSeparators(frequencies)
|
||||
|
||||
@@ -209,8 +238,8 @@ func FilterAndSortWords(frequencies map[string]WordFreqWithWeight) []WordFreqWit
|
||||
|
||||
return sortedFrequencies
|
||||
}
|
||||
func FilterPunctuationAndSeparators(frequencies map[string]WordFreqWithWeight) map[string]WordFreqWithWeight {
|
||||
filteredWords := make(map[string]WordFreqWithWeight)
|
||||
func FilterPunctuationAndSeparators(frequencies map[string]models.WordFreqWithWeight) map[string]models.WordFreqWithWeight {
|
||||
filteredWords := make(map[string]models.WordFreqWithWeight)
|
||||
|
||||
for word, freqInfo := range frequencies {
|
||||
// 过滤纯标点符号和分隔符
|
||||
@@ -238,8 +267,8 @@ func isPunctuationOrSeparator(word string) bool {
|
||||
}
|
||||
|
||||
// FilterWithRegex 使用正则表达式过滤标点和特殊字符
|
||||
func FilterWithRegex(frequencies map[string]WordFreqWithWeight) map[string]WordFreqWithWeight {
|
||||
filteredWords := make(map[string]WordFreqWithWeight)
|
||||
func FilterWithRegex(frequencies map[string]models.WordFreqWithWeight) map[string]models.WordFreqWithWeight {
|
||||
filteredWords := make(map[string]models.WordFreqWithWeight)
|
||||
|
||||
// 匹配标点符号、特殊字符的正则表达式
|
||||
punctuationRegex := regexp.MustCompile(`^[[:punct:][:space:]]+$`)
|
||||
@@ -254,9 +283,9 @@ func FilterWithRegex(frequencies map[string]WordFreqWithWeight) map[string]WordF
|
||||
}
|
||||
|
||||
// countWordFrequencyWithWeight 统计词频并包含权重信息
|
||||
func countWordFrequencyWithWeight(text string) map[string]WordFreqWithWeight {
|
||||
func countWordFrequencyWithWeight(text string) map[string]models.WordFreqWithWeight {
|
||||
words := splitWords(text)
|
||||
freqMap := make(map[string]WordFreqWithWeight)
|
||||
freqMap := make(map[string]models.WordFreqWithWeight)
|
||||
|
||||
// 统计词频
|
||||
wordCount := make(map[string]int)
|
||||
@@ -267,8 +296,8 @@ func countWordFrequencyWithWeight(text string) map[string]WordFreqWithWeight {
|
||||
// 构建包含权重的结果
|
||||
for word, frequency := range wordCount {
|
||||
weight := getWordWeight(word)
|
||||
if weight > basefreq {
|
||||
freqMap[word] = WordFreqWithWeight{
|
||||
if weight >= basefreq {
|
||||
freqMap[word] = models.WordFreqWithWeight{
|
||||
Word: word,
|
||||
Frequency: frequency,
|
||||
Weight: weight,
|
||||
@@ -282,7 +311,7 @@ func countWordFrequencyWithWeight(text string) map[string]WordFreqWithWeight {
|
||||
}
|
||||
|
||||
// AnalyzeSentimentWithFreqWeight 带权重词频统计的情感分析
|
||||
func AnalyzeSentimentWithFreqWeight(text string) (SentimentResult, map[string]WordFreqWithWeight) {
|
||||
func AnalyzeSentimentWithFreqWeight(text string) (models.SentimentResult, map[string]models.WordFreqWithWeight) {
|
||||
// 原有情感分析逻辑
|
||||
result := AnalyzeSentiment(text)
|
||||
|
||||
@@ -292,26 +321,14 @@ func AnalyzeSentimentWithFreqWeight(text string) (SentimentResult, map[string]Wo
|
||||
return result, frequencies
|
||||
}
|
||||
|
||||
// SentimentResult 情感分析结果类型
|
||||
type SentimentResult struct {
|
||||
Score float64 // 情感得分
|
||||
Category SentimentType // 情感类别
|
||||
PositiveCount int // 正面词数量
|
||||
NegativeCount int // 负面词数量
|
||||
Description string // 情感描述
|
||||
}
|
||||
|
||||
// SentimentType 情感类型枚举
|
||||
type SentimentType int
|
||||
|
||||
const (
|
||||
Positive SentimentType = iota
|
||||
Positive models.SentimentType = iota
|
||||
Negative
|
||||
Neutral
|
||||
)
|
||||
|
||||
// AnalyzeSentiment 判断文本的情感
|
||||
func AnalyzeSentiment(text string) SentimentResult {
|
||||
func AnalyzeSentiment(text string) models.SentimentResult {
|
||||
// 初始化得分
|
||||
score := 0.0
|
||||
positiveCount := 0
|
||||
@@ -351,7 +368,7 @@ func AnalyzeSentiment(text string) SentimentResult {
|
||||
}
|
||||
|
||||
// 确定情感类别
|
||||
var category SentimentType
|
||||
var category models.SentimentType
|
||||
switch {
|
||||
case score > 1.0:
|
||||
category = Positive
|
||||
@@ -361,7 +378,7 @@ func AnalyzeSentiment(text string) SentimentResult {
|
||||
category = Neutral
|
||||
}
|
||||
|
||||
return SentimentResult{
|
||||
return models.SentimentResult{
|
||||
Score: score,
|
||||
Category: category,
|
||||
PositiveCount: positiveCount,
|
||||
@@ -474,7 +491,7 @@ func splitWords(text string) []string {
|
||||
}
|
||||
|
||||
// GetSentimentDescription 获取情感类别的文本描述
|
||||
func GetSentimentDescription(category SentimentType) string {
|
||||
func GetSentimentDescription(category models.SentimentType) string {
|
||||
switch category {
|
||||
case Positive:
|
||||
return "看涨"
|
||||
@@ -519,3 +536,43 @@ func main() {
|
||||
result.NegativeCount)
|
||||
}
|
||||
}
|
||||
|
||||
func SaveAnalyzeSentimentWithFreqWeight(frequencies []models.WordFreqWithWeight) {
|
||||
|
||||
sort.Slice(frequencies, func(i, j int) bool {
|
||||
return frequencies[i].Frequency > frequencies[j].Frequency
|
||||
})
|
||||
wordAnalyzes := make([]models.WordAnalyze, 0)
|
||||
for _, freq := range frequencies[:10] {
|
||||
wordAnalyze := models.WordAnalyze{
|
||||
WordFreqWithWeight: freq,
|
||||
}
|
||||
wordAnalyzes = append(wordAnalyzes, wordAnalyze)
|
||||
}
|
||||
db.Dao.CreateInBatches(wordAnalyzes, 1000)
|
||||
}
|
||||
|
||||
func SaveStockSentimentAnalysis(result models.SentimentResult) {
|
||||
db.Dao.Create(&models.SentimentResultAnalyze{
|
||||
SentimentResult: result,
|
||||
})
|
||||
}
|
||||
|
||||
func NewsAnalyze(text string, save bool) (models.SentimentResult, []models.WordFreqWithWeight) {
|
||||
if text == "" {
|
||||
telegraphs := NewMarketNewsApi().GetNews24HoursList("", 1000*10)
|
||||
messageText := strings.Builder{}
|
||||
for _, telegraph := range *telegraphs {
|
||||
messageText.WriteString(telegraph.Content + "\n")
|
||||
}
|
||||
text = messageText.String()
|
||||
}
|
||||
result, frequencies := AnalyzeSentimentWithFreqWeight(text)
|
||||
// 过滤标点符号和分隔符
|
||||
cleanFrequencies := FilterAndSortWords(frequencies)
|
||||
if save {
|
||||
go SaveAnalyzeSentimentWithFreqWeight(cleanFrequencies)
|
||||
go SaveStockSentimentAnalysis(result)
|
||||
}
|
||||
return result, cleanFrequencies
|
||||
}
|
||||
|
||||
@@ -6,6 +6,8 @@ import (
|
||||
"go-stock/backend/logger"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/duke-git/lancet/v2/random"
|
||||
)
|
||||
|
||||
// @Author spark
|
||||
@@ -19,15 +21,15 @@ func TestAnalyzeSentiment(t *testing.T) {
|
||||
InitAnalyzeSentiment()
|
||||
|
||||
messageText := strings.Builder{}
|
||||
//news := NewMarketNewsApi().GetNewsList2("", random.RandInt(500, 1000))
|
||||
//for _, telegraph := range *news {
|
||||
// messageText.WriteString(telegraph.Content + "\n")
|
||||
//}
|
||||
news := NewMarketNewsApi().GetNewsList2("", random.RandInt(500, 1000))
|
||||
for _, telegraph := range *news {
|
||||
messageText.WriteString(telegraph.Content + "\n")
|
||||
}
|
||||
|
||||
text := messageText.String()
|
||||
//text = " 【周六你需要知道的隔夜全球要闻:美联储鸽声重振 美股走势回稳】 1、纽约联储行长威廉姆斯表示,随着劳动力市场走软,美联储近期内仍有再次降息的空间。 2、美联储理事斯蒂芬·米兰表示,自上次联邦公开市场委员会(FOMC)会议以来的经济数据应“促使人们偏向鸽派立场”。 3、波士顿联邦储备银行行长柯林斯表示,由于通胀可能在一段时间内保持高位,维持利率不变“目前合适”。 4、据CME“美联储观察”,截至北京时间11月22日6时30分,美联储12月降息25个基点的概率为69.4%,维持利率不变的概率为30.6%。 5、美国劳工统计局表示,11月CPI报告将于12月18日发布,同时取消了10月CPI报告发布,表示无法追溯采集政府停摆期间未能收集的部分数据。 6、俄罗斯总统普京表示,已收到美提出解决俄乌冲突的计划,俄罗斯愿意进行和平谈判。美国总统特朗普表示,他认为27日是乌克兰接受美国支持的和平计划的最后期限。 7、美联储高官鸽派言论提振市场情绪,美股三大指数收盘集体上涨,道琼斯指数涨1.08%,标普500指数涨0.98%,纳斯达克综合指数涨0.88%。甲骨文跌超5%,英伟达跌超1%。纳指本周累计跌2.74%,标普500指数累跌1.95%,道指累跌1.91%。英伟达本周累跌5.9%。 8、热门中概股多数上涨,纳斯达克中国金龙指数收涨1.23%。蔚来涨超3%,哔哩哔哩、理想汽车涨超2%,京东、小鹏汽车涨超1%。 9、国际油价下跌,交易员评估乌克兰与俄罗斯可能达成和平协议的前景。WTI 1月期货下跌1.6%,结算价报每桶58.06美元,为过去五个交易日中第四次下跌。布伦特1月期货下跌1.3%,结算价报每桶62.56美元。 10、美联储延长压力测试改进方案征询期,为银行反馈提供更多时间。 11、由于美国人对个人财务状况的看法恶化,美国消费者信心在11月跌至接近纪录最低水平;密歇根大学数据显示,11月消费者信心指数降至51,10月为53.6。 12、日本央行政策委员会委员Kazuyuki Masu表示,日本央行接近作出加息决定。 13、穆迪将意大利信用评级从BAA3上调至BAA2,展望稳定。\n"
|
||||
text = "财联社电:英伟达周五冲高回落,股价涨幅收于1%,市场普遍认为其走势疲软"
|
||||
text = "【本轮巴以冲突已致加沙地带69733人死亡】财联社11月22日电,当地时间22日下午,以军对加沙城西部一辆汽车发动空袭,已造成5人死亡,多人受伤。自2023年10月7日巴以新一轮大规模冲突爆发以来,以色列对加沙地带的袭击已造成69733人死亡、170863人受伤。"
|
||||
//text = "财联社电:英伟达周五冲高回落,股价涨幅收于1%,市场普遍认为其走势疲软"
|
||||
//text = "【本轮巴以冲突已致加沙地带69733人死亡】财联社11月22日电,当地时间22日下午,以军对加沙城西部一辆汽车发动空袭,已造成5人死亡,多人受伤。自2023年10月7日巴以新一轮大规模冲突爆发以来,以色列对加沙地带的袭击已造成69733人死亡、170863人受伤。"
|
||||
//text = "【牛肉加工亏损 美国泰森公司关停缩减相关业务】财联社11月22日电,受牛肉加工业务亏损影响,当地时间21日,美国泰森食品公司发布公告称,将关闭位于内布拉斯加州的一家大型牛肉加工厂,还计划缩小得克萨斯州一家牛肉加工厂的生产规模。根据泰森食品公司的公告,被关闭的这家工厂位于内布拉斯加州列克星敦,日均可宰杀并处理大约5000头牛,约占全美日均牛肉屠宰数量的4.8%。与此同时,公司还计划缩小得克萨斯州一家牛肉加工厂的生产规模,这家工厂每天大约可屠宰6000头牛。据悉,泰森此次业务调整影响两个工厂大约5000个工作岗位。《华尔街日报》报道称,泰森是美国四大肉类加工公司中首家关闭主要牛肉加工厂的公司,其最新财报显示,2025财年牛肉加工是唯一亏损的业务部门,调整后的营业亏损为4.26亿美元。"
|
||||
// 分析情感
|
||||
words := splitWords(text)
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
package db
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/glebarez/sqlite"
|
||||
"gorm.io/gorm"
|
||||
"gorm.io/gorm/logger"
|
||||
"gorm.io/plugin/dbresolver"
|
||||
"log"
|
||||
"os"
|
||||
"time"
|
||||
)
|
||||
|
||||
var Dao *gorm.DB
|
||||
@@ -26,7 +27,7 @@ func Init(sqlitePath string) {
|
||||
var openDb *gorm.DB
|
||||
var err error
|
||||
if sqlitePath == "" {
|
||||
sqlitePath = "data/stock.db?cache=shared&mode=rwc&_journal_mode=WAL"
|
||||
sqlitePath = "data/stock.db?cache_size=-524288&journal_mode=WAL"
|
||||
}
|
||||
openDb, err = gorm.Open(sqlite.Open(sqlitePath), &gorm.Config{
|
||||
Logger: dbLogger,
|
||||
@@ -48,8 +49,8 @@ func Init(sqlitePath string) {
|
||||
if err != nil {
|
||||
log.Fatalf("openDb.DB error is %s", err.Error())
|
||||
}
|
||||
dbCon.SetMaxIdleConns(10)
|
||||
dbCon.SetMaxOpenConns(100)
|
||||
dbCon.SetMaxIdleConns(4)
|
||||
dbCon.SetMaxOpenConns(10)
|
||||
dbCon.SetConnMaxLifetime(time.Hour)
|
||||
Dao = openDb
|
||||
}
|
||||
|
||||
@@ -719,3 +719,63 @@ type BKDict struct {
|
||||
func (b BKDict) TableName() string {
|
||||
return "bk_dict"
|
||||
}
|
||||
|
||||
type WordAnalyze struct {
|
||||
gorm.Model
|
||||
DataTime *time.Time `json:"dataTime" gorm:"index;autoCreateTime"`
|
||||
WordFreqWithWeight
|
||||
}
|
||||
|
||||
// WordFreqWithWeight 词频统计结果,包含权重信息
|
||||
type WordFreqWithWeight struct {
|
||||
Word string
|
||||
Frequency int
|
||||
Weight float64
|
||||
Score float64
|
||||
}
|
||||
|
||||
// SentimentResult 情感分析结果类型
|
||||
type SentimentResult struct {
|
||||
Score float64 // 情感得分
|
||||
Category SentimentType // 情感类别
|
||||
PositiveCount int // 正面词数量
|
||||
NegativeCount int // 负面词数量
|
||||
Description string // 情感描述
|
||||
}
|
||||
|
||||
type SentimentResultAnalyze struct {
|
||||
gorm.Model
|
||||
DataTime *time.Time `json:"dataTime" gorm:"index;autoCreateTime"`
|
||||
SentimentResult
|
||||
}
|
||||
|
||||
// SentimentType 情感类型枚举
|
||||
type SentimentType int
|
||||
|
||||
type HotStrategy struct {
|
||||
ChgEffect bool `json:"chgEffect"`
|
||||
Code int `json:"code"`
|
||||
Data []*HotStrategyData `json:"data"`
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
type HotStrategyData struct {
|
||||
Chg float64 `json:"chg" md:"平均涨幅(%)"`
|
||||
Code string `json:"code" md:"-"`
|
||||
HeatValue int `json:"heatValue" md:"热度值"`
|
||||
Market string `json:"market" md:"-"`
|
||||
Question string `json:"question" md:"选股策略"`
|
||||
Rank int `json:"rank" md:"-"`
|
||||
}
|
||||
|
||||
type NtfyNews struct {
|
||||
Id string `json:"id"`
|
||||
Time int `json:"time"`
|
||||
Expires int `json:"expires"`
|
||||
Event string `json:"event"`
|
||||
Topic string `json:"topic"`
|
||||
Title string `json:"title"`
|
||||
Message string `json:"message"`
|
||||
Tags []string `json:"tags"`
|
||||
Icon string `json:"icon"`
|
||||
}
|
||||
|
||||
BIN
build/screenshot/img15.png
Normal file
BIN
build/screenshot/img15.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 205 KiB |
193
data/dict/user.txt
Normal file
193
data/dict/user.txt
Normal file
@@ -0,0 +1,193 @@
|
||||
# 补充:热点概念与板块(Jieba/gse兼容格式)
|
||||
# 权重说明:核心热点500-700分,事件类400分,负权重词汇按需求保留
|
||||
|
||||
# 一、负权重低优先级词汇(减少无差别匹配干扰)
|
||||
公司 -0.1 n
|
||||
国家 -0.1 n
|
||||
国际 -0.1 n
|
||||
会议 -0.1 n
|
||||
市场 -0.1 n
|
||||
经济 -0.1 n
|
||||
技术 -0.1 n
|
||||
记者 -0.1 n
|
||||
时间 -0.1 n
|
||||
项目 -0.1 n
|
||||
问题 -0.1 n
|
||||
企业 -0.1 n
|
||||
财联社 -0.1 n
|
||||
上涨 -0.1 v
|
||||
下跌 -0.1 v
|
||||
期货 -0.1 n
|
||||
跌幅 -0.1 n
|
||||
跌超 -0.1 adj
|
||||
股票 -0.1 n
|
||||
基金 -0.1 n
|
||||
电讯 -0.1 n
|
||||
建筑 -0.1 n
|
||||
平开 -0.1 n
|
||||
保险 -0.1 n
|
||||
行业 -0.1 n
|
||||
其他 -0.1 n
|
||||
a股 -0.1 n
|
||||
港股 -0.1 n
|
||||
etf -0.1 n
|
||||
涨幅 -0.1 n
|
||||
交易所 -0.1 n
|
||||
证券 -0.1 n
|
||||
ai -0.1 n
|
||||
# 二、核心热点概念(700分,最高优先级)
|
||||
端侧AI 700 n
|
||||
AI应用 700 n
|
||||
比特币 700 n
|
||||
摩尔线程 700 n
|
||||
摩尔线程概念 700 n
|
||||
AI算力 700 n
|
||||
生成式AI 700 n
|
||||
量子计算 700 n
|
||||
脑机接口 700 n
|
||||
6G通信 700 n
|
||||
人形机器人 700 n
|
||||
固态电池 700 n
|
||||
ChatGPT概念 700 n
|
||||
Web3.0 700 n
|
||||
元宇宙 700 n
|
||||
数字孪生 700 n
|
||||
量子通信 700 n
|
||||
|
||||
# 三、重点赛道板块(500分,高优先级)
|
||||
冰雪旅游 500 n
|
||||
特高压 500 n
|
||||
跨境电商 500 n
|
||||
新能源汽车 500 n
|
||||
机器人 500 n
|
||||
具身智能 500 n
|
||||
油气 500 n
|
||||
商业航天 500 n
|
||||
光伏储能 500 n
|
||||
锂电材料 500 n
|
||||
半导体设备 500 n
|
||||
集成电路 500 n
|
||||
创新药 500 n
|
||||
CXO 500 n
|
||||
医疗器械 500 n
|
||||
数字经济 500 n
|
||||
数字货币 500 n
|
||||
区块链 500 n
|
||||
低空经济 500 n
|
||||
工业互联网 500 n
|
||||
物联网 500 n
|
||||
5G应用 500 n
|
||||
充电桩 500 n
|
||||
氢能源 500 n
|
||||
核聚变 500 n
|
||||
工业母机 500 n
|
||||
新材料 500 n
|
||||
生物制造 500 n
|
||||
智能网联汽车 500 n
|
||||
乡村振兴 500 n
|
||||
国企改革 500 n
|
||||
央企重组 500 n
|
||||
跨境金融 500 n
|
||||
自贸港 500 n
|
||||
一带一路 500 n
|
||||
绿色低碳 500 n
|
||||
碳交易 500 n
|
||||
数据要素 500 n
|
||||
数字基建 500 n
|
||||
东数西算 500 n
|
||||
国产替代 500 n
|
||||
信创 500 n
|
||||
网络安全 500 n
|
||||
算力网络 500 n
|
||||
边缘计算 500 n
|
||||
虚拟现实 500 n
|
||||
增强现实 500 n
|
||||
智能穿戴 500 n
|
||||
智能家居 500 n
|
||||
车联网 500 n
|
||||
激光雷达 500 n
|
||||
氮化镓 500 n
|
||||
碳化硅 500 n
|
||||
第三代半导体 500 n
|
||||
EDA工具 500 n
|
||||
光刻胶 500 n
|
||||
芯片设计 500 n
|
||||
封装测试 500 n
|
||||
储能电池 500 n
|
||||
钠离子电池 500 n
|
||||
氢燃料电池 500 n
|
||||
光伏组件 500 n
|
||||
风电设备 500 n
|
||||
特高压设备 500 n
|
||||
电力物联网 500 n
|
||||
智能电网 500 n
|
||||
轨道交通 500 n
|
||||
航空航天 500 n
|
||||
海洋工程 500 n
|
||||
高端装备 500 n
|
||||
军工电子 500 n
|
||||
卫星互联网 500 n
|
||||
北斗导航 500 n
|
||||
国产大飞机 500 n
|
||||
生物医药 500 n
|
||||
基因测序 500 n
|
||||
疫苗 500 n
|
||||
医疗美容 500 n
|
||||
养老产业 500 n
|
||||
教育信息化 500 n
|
||||
体育产业 500 n
|
||||
文化创意 500 n
|
||||
旅游复苏 500 n
|
||||
预制菜 500 n
|
||||
白酒 500 n
|
||||
食品饮料 500 n
|
||||
家电下乡 500 n
|
||||
房地产复苏 500 n
|
||||
基建投资 500 n
|
||||
新型城镇化 500 n
|
||||
冷链物流 500 n
|
||||
快递物流 500 n
|
||||
跨境支付 500 n
|
||||
金融科技 500 n
|
||||
消费电子 500 n
|
||||
元宇宙基建 500 n
|
||||
数字藏品 500 n
|
||||
NFT 500 n
|
||||
绿色电力 500 n
|
||||
节能降碳 500 n
|
||||
抽水蓄能 500 n
|
||||
生物质能 500 n
|
||||
地热能 500 n
|
||||
潮汐能 500 n
|
||||
|
||||
# 四、事件驱动型概念(400分,中优先级)
|
||||
俄乌冲突 400 n
|
||||
中东局势 400 n
|
||||
美联储加息 400 n
|
||||
降息预期 400 n
|
||||
贸易摩擦 400 n
|
||||
供应链重构 400 n
|
||||
能源危机 400 n
|
||||
粮食安全 400 n
|
||||
疫情复苏 400 n
|
||||
政策利好 400 n
|
||||
产业扶持 400 n
|
||||
技术突破 400 n
|
||||
并购重组 400 n
|
||||
IPO提速 400 n
|
||||
解禁潮 400 n
|
||||
北向资金流入 400 n
|
||||
南向资金流入 400 n
|
||||
主力资金异动 400 n
|
||||
行业景气度 400 n
|
||||
业绩预增 400 n
|
||||
商誉减值 400 n
|
||||
退市风险 400 n
|
||||
监管新规 400 n
|
||||
税收优惠 400 n
|
||||
补贴政策 400 n
|
||||
基建刺激 400 n
|
||||
消费刺激 400 n
|
||||
新能源补贴 400 n
|
||||
碳达峰政策 400 n
|
||||
碳中和目标 400 n
|
||||
132
frontend/package-lock.json
generated
132
frontend/package-lock.json
generated
@@ -33,14 +33,14 @@
|
||||
"@vicons/ionicons5": "^0.13.0",
|
||||
"@vicons/material": "^0.13.0",
|
||||
"@vicons/tabler": "^0.13.0",
|
||||
"@vitejs/plugin-vue": "^5.2.1",
|
||||
"@vitejs/plugin-vue": "^6.0.2",
|
||||
"html-docx-js-typescript": "^0.1.5",
|
||||
"less": "^4.4.0",
|
||||
"naive-ui": "^2.41.0",
|
||||
"naive-ui": "^2.43.2",
|
||||
"unplugin-auto-import": "^20.0.0",
|
||||
"unplugin-vue-components": "^29.0.0",
|
||||
"vfonts": "^0.0.3",
|
||||
"vite": "^6.3.5"
|
||||
"vite": "7.2.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/code-frame": {
|
||||
@@ -480,6 +480,7 @@
|
||||
"resolved": "https://registry.npmmirror.com/@css-render/vue3-ssr/-/vue3-ssr-0.15.14.tgz",
|
||||
"integrity": "sha512-//8027GSbxE9n3QlD73xFY6z4ZbHbvrOVB7AO6hsmrEzGbg+h2A09HboUyDgu+xsmj7JnvJD39Irt+2D0+iV8g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"vue": "^3.0.11"
|
||||
}
|
||||
@@ -924,7 +925,8 @@
|
||||
"version": "3.4.0",
|
||||
"resolved": "https://registry.npmmirror.com/@juggle/resize-observer/-/resize-observer-3.4.0.tgz",
|
||||
"integrity": "sha512-dfLbk+PwWvFzSxwk3n5ySL0hfBog779o8h68wK/7/APo/7cgyWp5jcXockbxdk5kFRkbeXWm4Fbi9FrdN381sA==",
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"license": "Apache-2.0"
|
||||
},
|
||||
"node_modules/@lezer/common": {
|
||||
"version": "1.2.3",
|
||||
@@ -1115,6 +1117,13 @@
|
||||
"url": "https://opencollective.com/popperjs"
|
||||
}
|
||||
},
|
||||
"node_modules/@rolldown/pluginutils": {
|
||||
"version": "1.0.0-beta.50",
|
||||
"resolved": "https://registry.npmmirror.com/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.50.tgz",
|
||||
"integrity": "sha512-5e76wQiQVeL1ICOZVUg4LSOVYg9jyhGCin+icYozhsUzM+fHE7kddi1bdiE0jwVqTfkjba3jUFbEkoC9WkdvyA==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@rollup/rollup-android-arm-eabi": {
|
||||
"version": "4.43.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.43.0.tgz",
|
||||
@@ -1486,9 +1495,10 @@
|
||||
"integrity": "sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q=="
|
||||
},
|
||||
"node_modules/@types/lodash": {
|
||||
"version": "4.17.15",
|
||||
"resolved": "https://registry.npmmirror.com/@types/lodash/-/lodash-4.17.15.tgz",
|
||||
"integrity": "sha512-w/P33JFeySuhN6JLkysYUK2gEmy9kHHFN7E8ro0tkfmlDOgxBDzWEZ/J8cWA+fHqFevpswDTFZnDx+R9lbL6xw=="
|
||||
"version": "4.17.21",
|
||||
"resolved": "https://registry.npmmirror.com/@types/lodash/-/lodash-4.17.21.tgz",
|
||||
"integrity": "sha512-FOvQ0YPD5NOfPgMzJihoT+Za5pdkDJWcbpuj1DjaKZIr/gxodQjY/uWEFlTNqW2ugXHUiL8lRQgw63dzKHZdeQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/lodash-es": {
|
||||
"version": "4.17.12",
|
||||
@@ -1602,15 +1612,19 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@vitejs/plugin-vue": {
|
||||
"version": "5.2.1",
|
||||
"resolved": "https://registry.npmmirror.com/@vitejs/plugin-vue/-/plugin-vue-5.2.1.tgz",
|
||||
"integrity": "sha512-cxh314tzaWwOLqVes2gnnCtvBDcM1UMdn+iFR+UjAn411dPT3tOmqrJjbMd7koZpMAmBM/GqeV4n9ge7JSiJJQ==",
|
||||
"version": "6.0.2",
|
||||
"resolved": "https://registry.npmmirror.com/@vitejs/plugin-vue/-/plugin-vue-6.0.2.tgz",
|
||||
"integrity": "sha512-iHmwV3QcVGGvSC1BG5bZ4z6iwa1SOpAPWmnjOErd4Ske+lZua5K9TtAVdx0gMBClJ28DViCbSmZitjWZsWO3LA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@rolldown/pluginutils": "1.0.0-beta.50"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.0.0 || >=20.0.0"
|
||||
"node": "^20.19.0 || >=22.12.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"vite": "^5.0.0 || ^6.0.0",
|
||||
"vite": "^5.0.0 || ^6.0.0 || ^7.0.0",
|
||||
"vue": "^3.2.25"
|
||||
}
|
||||
},
|
||||
@@ -2100,7 +2114,8 @@
|
||||
"version": "0.2.4",
|
||||
"resolved": "https://registry.npmmirror.com/evtd/-/evtd-0.2.4.tgz",
|
||||
"integrity": "sha512-qaeGN5bx63s/AXgQo8gj6fBkxge+OoLddLniox5qtLAEY5HSnuSlISXVPxnSae1dWblvTh4/HoMIB+mbMsvZzw==",
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/exsolve": {
|
||||
"version": "1.0.7",
|
||||
@@ -2110,11 +2125,14 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/fdir": {
|
||||
"version": "6.4.6",
|
||||
"resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.6.tgz",
|
||||
"integrity": "sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==",
|
||||
"version": "6.5.0",
|
||||
"resolved": "https://registry.npmmirror.com/fdir/-/fdir-6.5.0.tgz",
|
||||
"integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=12.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"picomatch": "^3 || ^4"
|
||||
},
|
||||
@@ -2603,46 +2621,36 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/naive-ui": {
|
||||
"version": "2.41.0",
|
||||
"resolved": "https://registry.npmmirror.com/naive-ui/-/naive-ui-2.41.0.tgz",
|
||||
"integrity": "sha512-KnmLg+xPLwXV8QVR7ZZ69eCjvel7R5vru8+eFe4VoAJHEgqAJgVph6Zno9K2IVQRpSF3GBGea3tjavslOR4FAA==",
|
||||
"version": "2.43.2",
|
||||
"resolved": "https://registry.npmmirror.com/naive-ui/-/naive-ui-2.43.2.tgz",
|
||||
"integrity": "sha512-YlLMnGrwGTOc+zMj90sG3ubaH5/7czsgLgGcjTLA981IUaz8r6t4WIujNt8r9PNr+dqv6XNEr0vxkARgPPjfBQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@css-render/plugin-bem": "^0.15.14",
|
||||
"@css-render/vue3-ssr": "^0.15.14",
|
||||
"@types/katex": "^0.16.2",
|
||||
"@types/lodash": "^4.14.198",
|
||||
"@types/lodash-es": "^4.17.9",
|
||||
"@types/lodash": "^4.17.20",
|
||||
"@types/lodash-es": "^4.17.12",
|
||||
"async-validator": "^4.2.5",
|
||||
"css-render": "^0.15.14",
|
||||
"csstype": "^3.1.3",
|
||||
"date-fns": "^3.6.0",
|
||||
"date-fns-tz": "^3.1.3",
|
||||
"date-fns": "^4.1.0",
|
||||
"date-fns-tz": "^3.2.0",
|
||||
"evtd": "^0.2.4",
|
||||
"highlight.js": "^11.8.0",
|
||||
"lodash": "^4.17.21",
|
||||
"lodash-es": "^4.17.21",
|
||||
"seemly": "^0.3.8",
|
||||
"seemly": "^0.3.10",
|
||||
"treemate": "^0.3.11",
|
||||
"vdirs": "^0.1.8",
|
||||
"vooks": "^0.2.12",
|
||||
"vueuc": "^0.4.63"
|
||||
"vueuc": "^0.4.65"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"vue": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/naive-ui/node_modules/date-fns": {
|
||||
"version": "3.6.0",
|
||||
"resolved": "https://registry.npmmirror.com/date-fns/-/date-fns-3.6.0.tgz",
|
||||
"integrity": "sha512-fRHTG8g/Gif+kSh50gaGEdToemgfj74aRX3swtiouboip5JDLAyDE9F11nHMIcvOaXeOC6D7SpNhi7uFyB7Uww==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/kossnocorp"
|
||||
}
|
||||
},
|
||||
"node_modules/nanoid": {
|
||||
"version": "3.3.11",
|
||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
|
||||
@@ -2930,7 +2938,8 @@
|
||||
"version": "0.3.10",
|
||||
"resolved": "https://registry.npmmirror.com/seemly/-/seemly-0.3.10.tgz",
|
||||
"integrity": "sha512-2+SMxtG1PcsL0uyhkumlOU6Qo9TAQ/WyH7tthnPIOQB05/12jz9naq6GZ6iZ6ApVsO3rr2gsnTf3++OV63kE1Q==",
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/select": {
|
||||
"version": "1.1.2",
|
||||
@@ -3074,14 +3083,14 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/tinyglobby": {
|
||||
"version": "0.2.14",
|
||||
"resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.14.tgz",
|
||||
"integrity": "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==",
|
||||
"version": "0.2.15",
|
||||
"resolved": "https://registry.npmmirror.com/tinyglobby/-/tinyglobby-0.2.15.tgz",
|
||||
"integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"fdir": "^6.4.4",
|
||||
"picomatch": "^4.0.2"
|
||||
"fdir": "^6.5.0",
|
||||
"picomatch": "^4.0.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12.0.0"
|
||||
@@ -3309,6 +3318,7 @@
|
||||
"resolved": "https://registry.npmmirror.com/vdirs/-/vdirs-0.1.8.tgz",
|
||||
"integrity": "sha512-H9V1zGRLQZg9b+GdMk8MXDN2Lva0zx72MPahDKc30v+DtwKjfyOSXWRIX4t2mhDubM1H09gPhWeth/BJWPHGUw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"evtd": "^0.2.2"
|
||||
},
|
||||
@@ -3323,24 +3333,24 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/vite": {
|
||||
"version": "6.3.5",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-6.3.5.tgz",
|
||||
"integrity": "sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ==",
|
||||
"version": "7.2.4",
|
||||
"resolved": "https://registry.npmmirror.com/vite/-/vite-7.2.4.tgz",
|
||||
"integrity": "sha512-NL8jTlbo0Tn4dUEXEsUg8KeyG/Lkmc4Fnzb8JXN/Ykm9G4HNImjtABMJgkQoVjOBN/j2WAwDTRytdqJbZsah7w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"esbuild": "^0.25.0",
|
||||
"fdir": "^6.4.4",
|
||||
"picomatch": "^4.0.2",
|
||||
"postcss": "^8.5.3",
|
||||
"rollup": "^4.34.9",
|
||||
"tinyglobby": "^0.2.13"
|
||||
"fdir": "^6.5.0",
|
||||
"picomatch": "^4.0.3",
|
||||
"postcss": "^8.5.6",
|
||||
"rollup": "^4.43.0",
|
||||
"tinyglobby": "^0.2.15"
|
||||
},
|
||||
"bin": {
|
||||
"vite": "bin/vite.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.0.0 || ^20.0.0 || >=22.0.0"
|
||||
"node": "^20.19.0 || >=22.12.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/vitejs/vite?sponsor=1"
|
||||
@@ -3349,14 +3359,14 @@
|
||||
"fsevents": "~2.3.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0",
|
||||
"@types/node": "^20.19.0 || >=22.12.0",
|
||||
"jiti": ">=1.21.0",
|
||||
"less": "*",
|
||||
"less": "^4.0.0",
|
||||
"lightningcss": "^1.21.0",
|
||||
"sass": "*",
|
||||
"sass-embedded": "*",
|
||||
"stylus": "*",
|
||||
"sugarss": "*",
|
||||
"sass": "^1.70.0",
|
||||
"sass-embedded": "^1.70.0",
|
||||
"stylus": ">=0.54.8",
|
||||
"sugarss": "^5.0.0",
|
||||
"terser": "^5.16.0",
|
||||
"tsx": "^4.8.1",
|
||||
"yaml": "^2.4.2"
|
||||
@@ -3442,6 +3452,7 @@
|
||||
"resolved": "https://registry.npmmirror.com/vooks/-/vooks-0.2.12.tgz",
|
||||
"integrity": "sha512-iox0I3RZzxtKlcgYaStQYKEzWWGAduMmq+jS7OrNdQo1FgGfPMubGL3uGHOU9n97NIvfFDBGnpSvkWyb/NSn/Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"evtd": "^0.2.2"
|
||||
},
|
||||
@@ -3493,10 +3504,11 @@
|
||||
}
|
||||
},
|
||||
"node_modules/vueuc": {
|
||||
"version": "0.4.64",
|
||||
"resolved": "https://registry.npmmirror.com/vueuc/-/vueuc-0.4.64.tgz",
|
||||
"integrity": "sha512-wlJQj7fIwKK2pOEoOq4Aro8JdPOGpX8aWQhV8YkTW9OgWD2uj2O8ANzvSsIGjx7LTOc7QbS7sXdxHi6XvRnHPA==",
|
||||
"version": "0.4.65",
|
||||
"resolved": "https://registry.npmmirror.com/vueuc/-/vueuc-0.4.65.tgz",
|
||||
"integrity": "sha512-lXuMl+8gsBmruudfxnMF9HW4be8rFziylXFu1VHVNbLVhRTXXV4njvpRuJapD/8q+oFEMSfQMH16E/85VoWRyQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@css-render/vue3-ssr": "^0.15.10",
|
||||
"@juggle/resize-observer": "^3.3.1",
|
||||
|
||||
@@ -34,14 +34,14 @@
|
||||
"@vicons/ionicons5": "^0.13.0",
|
||||
"@vicons/material": "^0.13.0",
|
||||
"@vicons/tabler": "^0.13.0",
|
||||
"@vitejs/plugin-vue": "^5.2.1",
|
||||
"@vitejs/plugin-vue": "^6.0.2",
|
||||
"html-docx-js-typescript": "^0.1.5",
|
||||
"less": "^4.4.0",
|
||||
"naive-ui": "^2.41.0",
|
||||
"naive-ui": "^2.43.2",
|
||||
"unplugin-auto-import": "^20.0.0",
|
||||
"unplugin-vue-components": "^29.0.0",
|
||||
"vfonts": "^0.0.3",
|
||||
"vite": "^6.3.5"
|
||||
"vite": "7.2.4"
|
||||
},
|
||||
"keywords": [
|
||||
"AI赋能股票分析",
|
||||
|
||||
@@ -1 +1 @@
|
||||
b0b9f944d9af9c00b6d48234793db58c
|
||||
f4fb0059ba6044c039be717fcc2e40bc
|
||||
@@ -75,7 +75,6 @@ function getIndex() {
|
||||
function handleChart(){
|
||||
const formatUtil = echarts.format;
|
||||
AnalyzeSentimentWithFreqWeight("").then((res) => {
|
||||
//console.log(res)
|
||||
const treemapchart = echarts.init(chartRef.value);
|
||||
const gaugeChart=echarts.init(gaugeChartRef.value);
|
||||
let data = res['frequencies'].map(item => ({
|
||||
@@ -287,7 +286,14 @@ function handleChart(){
|
||||
<n-collapse-item name="1" >
|
||||
<template #header>
|
||||
<n-flex>
|
||||
<n-tag size="small" :bordered="false" v-for="(item, index) in mainIndex" :type="item.zdf>0?'error':'success'"> {{item.name}} {{item.zxj}} <n-number-animation :precision="2" :from="0" :to="item.zdf"/>%</n-tag>
|
||||
<n-tag size="small" :bordered="false" v-for="(item, index) in mainIndex" :type="item.zdf>0?'error':'success'">
|
||||
<n-flex>
|
||||
<n-image :width="20" :src="item.img" />
|
||||
<n-text style="font-size: 14px" :type="item.zdf>0?'error':'success'">{{item.name}} {{item.zxj}}</n-text>
|
||||
<n-number-animation :precision="2" :from="0" :to="item.zdf" style="font-size: 14px"/>
|
||||
<n-text style="margin-left: -12px;font-size: 14px" :type="item.zdf>0?'error':'success'">%</n-text>
|
||||
</n-flex>
|
||||
</n-tag>
|
||||
</n-flex>
|
||||
</template>
|
||||
<template #header-extra>
|
||||
|
||||
@@ -166,7 +166,7 @@ EventsOn("updateVersion",async (msg) => {
|
||||
<n-td>赞助 18.8 RMB/月<br>赞助 120 RMB/年</n-td><n-td>vip1</n-td><n-td>💕 全部功能,软件自动更新(从CDN下载),更新快速便捷。AI配置指导,提示词参考等</n-td>
|
||||
</n-tr>
|
||||
<n-tr>
|
||||
<n-td>赞助 28.8 RMB/月<br>赞助 240 RMB/年</n-td><n-td>vip2</n-td><n-td>💕 vip1全部功能,赠送硅基流动AI分析服务💕</n-td>
|
||||
<n-td>赞助 28.8 RMB/月<br>赞助 240 RMB/年</n-td><n-td>vip2</n-td><n-td>💕 vip1全部功能,赠送硅基流动AI分析服务,启动时自动同步最近24小时市场资讯(包括外媒简讯) 💕</n-td>
|
||||
</n-tr>
|
||||
<n-tr>
|
||||
<n-td>每月赞助 X RMB</n-td><n-td>vipX</n-td><n-td>🧩 更多计划,视go-stock开源项目发展情况而定...(承接GitHub项目README广告推广💖)</n-td>
|
||||
|
||||
@@ -78,6 +78,7 @@ const indexInterval = ref(null)
|
||||
const indexIndustryRank = ref(null)
|
||||
const stockCode= ref('')
|
||||
const enableTools= ref(true)
|
||||
const thinkingMode = ref(true)
|
||||
const treemapRef = ref(null);
|
||||
let treemapchart =null;
|
||||
|
||||
@@ -127,6 +128,9 @@ onBeforeMount(() => {
|
||||
|
||||
indexIndustryRank.value = setInterval(() => {
|
||||
industryRank()
|
||||
ReFlesh("财联社电报")
|
||||
ReFlesh("新浪财经")
|
||||
ReFlesh("外媒")
|
||||
}, 1000 * 10)
|
||||
|
||||
|
||||
@@ -223,7 +227,7 @@ function reAiSummary() {
|
||||
aiSummary.value = ""
|
||||
summaryModal.value = true
|
||||
loading.value = true
|
||||
SummaryStockNews(question.value,aiConfigId.value, sysPromptId.value,enableTools.value)
|
||||
SummaryStockNews(question.value,aiConfigId.value, sysPromptId.value,enableTools.value,thinkingMode.value)
|
||||
}
|
||||
|
||||
function getAiSummary() {
|
||||
@@ -353,14 +357,14 @@ function ReFlesh(source) {
|
||||
<AnalyzeMartket :dark-theme="darkTheme" :chart-height="300" :kDays="1" :name="'最近24小时热词'" />
|
||||
</n-gi>
|
||||
<n-gi>
|
||||
<n-grid :cols="httpProxyEnabled?3:2" :y-gap="0">
|
||||
<n-grid :cols="foreignNewsList.length?3:2" :y-gap="0">
|
||||
<n-gi>
|
||||
<news-list :newsList="telegraphList" :header-title="'财联社电报'" @update:message="ReFlesh"></news-list>
|
||||
</n-gi>
|
||||
<n-gi>
|
||||
<news-list :newsList="sinaNewsList" :header-title="'新浪财经'" @update:message="ReFlesh"></news-list>
|
||||
</n-gi>
|
||||
<n-gi v-if="httpProxyEnabled">
|
||||
<n-gi v-if="foreignNewsList.length>0">
|
||||
<news-list :newsList="foreignNewsList" :header-title="'外媒'" @update:message="ReFlesh"></news-list>
|
||||
</n-gi>
|
||||
|
||||
@@ -704,6 +708,16 @@ function ReFlesh(source) {
|
||||
不启用AI函数工具调用
|
||||
</template>
|
||||
</n-switch>
|
||||
<n-switch v-model:value="thinkingMode" :round="false">
|
||||
<template #checked>
|
||||
启用思考模式
|
||||
</template>
|
||||
<template #unchecked>
|
||||
不启用思考模式
|
||||
</template>
|
||||
</n-switch>
|
||||
|
||||
|
||||
<n-gradient-text type="error" style="margin-left: 10px">*AI函数工具调用可以增强AI获取数据的能力,但会消耗更多tokens。</n-gradient-text>
|
||||
</n-flex>
|
||||
<n-flex justify="space-between" style="margin-bottom: 10px">
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<script setup>
|
||||
import {ReFleshTelegraphList} from "../../wailsjs/go/main/App";
|
||||
import {RefreshCircle, RefreshCircleSharp, RefreshOutline} from "@vicons/ionicons5";
|
||||
import {computed, h, onBeforeMount, onBeforeUnmount, onMounted,onUnmounted, ref} from 'vue'
|
||||
|
||||
const { headerTitle,newsList } = defineProps({
|
||||
headerTitle: {
|
||||
@@ -18,6 +19,30 @@ const emits = defineEmits(['update:message'])
|
||||
const updateMessage = () => {
|
||||
emits('update:message', headerTitle)
|
||||
}
|
||||
// 使用 ref 创建响应式时间数据
|
||||
const time = ref(new Date())
|
||||
|
||||
// 更新时间的函数
|
||||
const updateTime = () => {
|
||||
time.value = new Date()
|
||||
}
|
||||
|
||||
let timer = null
|
||||
|
||||
// 组件挂载时启动定时器
|
||||
onMounted(() => {
|
||||
if (headerTitle === '财联社电报') {
|
||||
// 每秒更新一次时间
|
||||
timer = setInterval(updateTime, 1000)
|
||||
}
|
||||
})
|
||||
|
||||
// 组件卸载时清除定时器
|
||||
onUnmounted(() => {
|
||||
if (timer) {
|
||||
clearInterval(timer)
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -25,23 +50,32 @@ const updateMessage = () => {
|
||||
<template #header>
|
||||
<n-flex justify="space-between">
|
||||
<n-tag :bordered="false" size="large" type="success" >{{ headerTitle }}</n-tag>
|
||||
<n-tag :bordered="false" size="large" type="info" v-if="headerTitle==='财联社电报'"> <n-time :time="time"/></n-tag>
|
||||
<n-button :bordered="false" @click="updateMessage"><n-icon color="#409EFF" size="25" :component="RefreshCircleSharp"/></n-button>
|
||||
</n-flex>
|
||||
</template>
|
||||
<n-list-item v-for="item in newsList">
|
||||
<n-list-item v-for="(item,idx) in newsList" :key="item.ID">
|
||||
<n-space justify="center" v-if="idx!==0 && item.dataTime.substring(0,10) !== newsList[idx-1].dataTime.substring(0,10)">
|
||||
<n-divider>
|
||||
{{ item.dataTime.substring(0,10) }}
|
||||
</n-divider>
|
||||
</n-space>
|
||||
<n-space justify="start" >
|
||||
<n-text justify="start" :bordered="false" :type="item.isRed?'error':'info'">
|
||||
<n-collapse v-if="item.title" arrow-placement="right" >
|
||||
<n-collapse-item :name="item.title">
|
||||
<template #header>
|
||||
<n-tag size="small" :type="item.isRed?'error':'warning'" :bordered="false"> {{ item.time }}</n-tag>
|
||||
<n-text size="small" :type="item.isRed?'error':'info'" :bordered="false">{{ item.title }}</n-text>
|
||||
</template>
|
||||
<n-text justify="start" :bordered="false" :type="item.isRed?'error':'info'">
|
||||
{{ item.content }}
|
||||
</n-text>
|
||||
</n-collapse-item>
|
||||
</n-collapse>
|
||||
<n-text v-if="!item.title" justify="start" :bordered="false" :type="item.isRed?'error':'info'">
|
||||
<n-tag size="small" :type="item.isRed?'error':'warning'" :bordered="false"> {{ item.time }}</n-tag>
|
||||
<n-gradient-text :size="14" :type="'warning'" :bordered="false">{{ item.title }}</n-gradient-text> <n-text :type="item.isRed?'error':'info'">{{ item.content }}</n-text>
|
||||
{{ item.content }}
|
||||
</n-text>
|
||||
<!-- <n-collapse v-if="item.title">-->
|
||||
<!-- <n-collapse-item :title="item.title" :name="item.title">-->
|
||||
<!-- <n-text justify="start" :bordered="false" :type="item.isRed?'error':'info'">-->
|
||||
<!-- <n-tag size="small" :type="item.isRed?'error':'warning'" :bordered="false"> {{ item.time }}</n-tag>-->
|
||||
<!-- {{ item.content }}-->
|
||||
<!-- </n-text>-->
|
||||
<!-- </n-collapse-item>-->
|
||||
<!-- </n-collapse>-->
|
||||
</n-space>
|
||||
<n-space v-if="item.subjects" style="margin-top: 2px">
|
||||
<n-tag :bordered="false" type="success" size="small" v-for="sub in item.subjects">
|
||||
|
||||
@@ -110,6 +110,7 @@ const modalShow4 = ref(false)
|
||||
const modalShow5 = ref(false)
|
||||
const addBTN = ref(true)
|
||||
const enableTools = ref(false)
|
||||
const thinkingMode = ref(false)
|
||||
const formModel = ref({
|
||||
name: "",
|
||||
code: "",
|
||||
@@ -1580,7 +1581,7 @@ function aiReCheckStock(stock, stockCode) {
|
||||
//
|
||||
|
||||
//message.info("sysPromptId:"+data.sysPromptId)
|
||||
NewChatStream(stock, stockCode, data.question, data.aiConfigId, data.sysPromptId, enableTools.value)
|
||||
NewChatStream(stock, stockCode, data.question, data.aiConfigId, data.sysPromptId, enableTools.value,thinkingMode.value)
|
||||
}
|
||||
|
||||
function aiCheckStock(stock, stockCode) {
|
||||
@@ -2353,6 +2354,14 @@ function searchStockReport(stockCode) {
|
||||
不启用AI函数工具调用
|
||||
</template>
|
||||
</n-switch>
|
||||
<n-switch v-model:value="thinkingMode" :round="false">
|
||||
<template #checked>
|
||||
启用思考模式
|
||||
</template>
|
||||
<template #unchecked>
|
||||
不启用思考模式
|
||||
</template>
|
||||
</n-switch>
|
||||
<n-gradient-text type="error" style="margin-left: 10px">
|
||||
*AI函数工具调用可以增强AI获取数据的能力,但会消耗更多tokens。
|
||||
</n-gradient-text>
|
||||
|
||||
@@ -24,6 +24,10 @@ import EmbeddedUrl from "./EmbeddedUrl.vue";
|
||||
<n-tab-pane name="财联社-行情数据" tab="财联社-行情数据">
|
||||
<embedded-url url="https://www.cls.cn/quotation" :height="'calc(100vh - 252px)'"/>
|
||||
</n-tab-pane>
|
||||
<n-tab-pane name="消息墙" tab="消息墙">
|
||||
<embedded-url url="https://go-stock.sparkmemory.top:16667/go-stock" :height="'calc(100vh - 252px)'"/>
|
||||
</n-tab-pane>
|
||||
|
||||
|
||||
|
||||
<n-tab-pane name="欢迎推荐更多有趣的财经网页" tab="欢迎推荐更多有趣的财经网页">
|
||||
|
||||
6
frontend/wailsjs/go/main/App.d.ts
vendored
6
frontend/wailsjs/go/main/App.d.ts
vendored
@@ -12,7 +12,7 @@ export function AddPrompt(arg1:models.Prompt):Promise<string>;
|
||||
|
||||
export function AddStockGroup(arg1:number,arg2:string):Promise<string>;
|
||||
|
||||
export function AnalyzeSentiment(arg1:string):Promise<data.SentimentResult>;
|
||||
export function AnalyzeSentiment(arg1:string):Promise<models.SentimentResult>;
|
||||
|
||||
export function AnalyzeSentimentWithFreqWeight(arg1:string):Promise<Record<string, any>>;
|
||||
|
||||
@@ -96,7 +96,7 @@ export function InvestCalendarTimeLine(arg1:string):Promise<Array<any>>;
|
||||
|
||||
export function LongTigerRank(arg1:string):Promise<any>;
|
||||
|
||||
export function NewChatStream(arg1:string,arg2:string,arg3:string,arg4:number,arg5:any,arg6:boolean):Promise<void>;
|
||||
export function NewChatStream(arg1:string,arg2:string,arg3:string,arg4:number,arg5:any,arg6:boolean,arg7:boolean):Promise<void>;
|
||||
|
||||
export function NewsPush(arg1:any):Promise<void>;
|
||||
|
||||
@@ -136,7 +136,7 @@ export function StockNotice(arg1:string):Promise<Array<any>>;
|
||||
|
||||
export function StockResearchReport(arg1:string):Promise<Array<any>>;
|
||||
|
||||
export function SummaryStockNews(arg1:string,arg2:number,arg3:any,arg4:boolean):Promise<void>;
|
||||
export function SummaryStockNews(arg1:string,arg2:number,arg3:any,arg4:boolean,arg5:boolean):Promise<void>;
|
||||
|
||||
export function UnFollow(arg1:string):Promise<string>;
|
||||
|
||||
|
||||
@@ -186,8 +186,8 @@ export function LongTigerRank(arg1) {
|
||||
return window['go']['main']['App']['LongTigerRank'](arg1);
|
||||
}
|
||||
|
||||
export function NewChatStream(arg1, arg2, arg3, arg4, arg5, arg6) {
|
||||
return window['go']['main']['App']['NewChatStream'](arg1, arg2, arg3, arg4, arg5, arg6);
|
||||
export function NewChatStream(arg1, arg2, arg3, arg4, arg5, arg6, arg7) {
|
||||
return window['go']['main']['App']['NewChatStream'](arg1, arg2, arg3, arg4, arg5, arg6, arg7);
|
||||
}
|
||||
|
||||
export function NewsPush(arg1) {
|
||||
@@ -266,8 +266,8 @@ export function StockResearchReport(arg1) {
|
||||
return window['go']['main']['App']['StockResearchReport'](arg1);
|
||||
}
|
||||
|
||||
export function SummaryStockNews(arg1, arg2, arg3, arg4) {
|
||||
return window['go']['main']['App']['SummaryStockNews'](arg1, arg2, arg3, arg4);
|
||||
export function SummaryStockNews(arg1, arg2, arg3, arg4, arg5) {
|
||||
return window['go']['main']['App']['SummaryStockNews'](arg1, arg2, arg3, arg4, arg5);
|
||||
}
|
||||
|
||||
export function UnFollow(arg1) {
|
||||
|
||||
@@ -342,26 +342,6 @@ export namespace data {
|
||||
|
||||
|
||||
|
||||
export class SentimentResult {
|
||||
Score: number;
|
||||
Category: number;
|
||||
PositiveCount: number;
|
||||
NegativeCount: number;
|
||||
Description: string;
|
||||
|
||||
static createFrom(source: any = {}) {
|
||||
return new SentimentResult(source);
|
||||
}
|
||||
|
||||
constructor(source: any = {}) {
|
||||
if ('string' === typeof source) source = JSON.parse(source);
|
||||
this.Score = source["Score"];
|
||||
this.Category = source["Category"];
|
||||
this.PositiveCount = source["PositiveCount"];
|
||||
this.NegativeCount = source["NegativeCount"];
|
||||
this.Description = source["Description"];
|
||||
}
|
||||
}
|
||||
export class SettingConfig {
|
||||
ID: number;
|
||||
// Go type: time
|
||||
@@ -745,6 +725,26 @@ export namespace models {
|
||||
this.type = source["type"];
|
||||
}
|
||||
}
|
||||
export class SentimentResult {
|
||||
Score: number;
|
||||
Category: number;
|
||||
PositiveCount: number;
|
||||
NegativeCount: number;
|
||||
Description: string;
|
||||
|
||||
static createFrom(source: any = {}) {
|
||||
return new SentimentResult(source);
|
||||
}
|
||||
|
||||
constructor(source: any = {}) {
|
||||
if ('string' === typeof source) source = JSON.parse(source);
|
||||
this.Score = source["Score"];
|
||||
this.Category = source["Category"];
|
||||
this.PositiveCount = source["PositiveCount"];
|
||||
this.NegativeCount = source["NegativeCount"];
|
||||
this.Description = source["Description"];
|
||||
}
|
||||
}
|
||||
export class VersionInfo {
|
||||
ID: number;
|
||||
// Go type: time
|
||||
|
||||
@@ -48,6 +48,10 @@ export function EventsOff(eventName, ...additionalEventNames) {
|
||||
return window.runtime.EventsOff(eventName, ...additionalEventNames);
|
||||
}
|
||||
|
||||
export function EventsOffAll() {
|
||||
return window.runtime.EventsOffAll();
|
||||
}
|
||||
|
||||
export function EventsOnce(eventName, callback) {
|
||||
return EventsOnMultiple(eventName, callback, 1);
|
||||
}
|
||||
|
||||
109
go.mod
109
go.mod
@@ -3,95 +3,96 @@ module go-stock
|
||||
go 1.25.0
|
||||
|
||||
require (
|
||||
github.com/PuerkitoBio/goquery v1.10.1
|
||||
github.com/chromedp/chromedp v0.14.1
|
||||
github.com/cloudwego/eino v0.4.1
|
||||
github.com/cloudwego/eino-ext/components/model/ark v0.1.19
|
||||
github.com/cloudwego/eino-ext/components/model/deepseek v0.0.0-20250804092122-8845979a2228
|
||||
github.com/cloudwego/eino-ext/components/model/openai v0.0.0-20250804092122-8845979a2228
|
||||
github.com/PuerkitoBio/goquery v1.11.0
|
||||
github.com/chromedp/chromedp v0.14.2
|
||||
github.com/cloudwego/eino v0.7.9
|
||||
github.com/cloudwego/eino-ext/components/model/ark v0.1.52
|
||||
github.com/cloudwego/eino-ext/components/model/deepseek v0.1.0
|
||||
github.com/cloudwego/eino-ext/components/model/openai v0.1.5
|
||||
github.com/coocood/freecache v1.2.4
|
||||
github.com/duke-git/lancet/v2 v2.3.4
|
||||
github.com/duke-git/lancet/v2 v2.3.8
|
||||
github.com/energye/systray v1.0.2
|
||||
github.com/gen2brain/beeep v0.11.1
|
||||
github.com/glebarez/sqlite v1.11.0
|
||||
github.com/go-ego/gse v0.80.3
|
||||
github.com/go-resty/resty/v2 v2.16.2
|
||||
github.com/go-resty/resty/v2 v2.17.0
|
||||
github.com/go-toast/toast v0.0.0-20190211030409-01e6764cf0a4
|
||||
github.com/inconshreveable/go-update v0.0.0-20160112193335-8152e7eb6ccf
|
||||
github.com/robertkrimen/otto v0.5.1
|
||||
github.com/robfig/cron/v3 v3.0.1
|
||||
github.com/samber/lo v1.49.1
|
||||
github.com/stretchr/testify v1.10.0
|
||||
github.com/tidwall/gjson v1.14.4
|
||||
github.com/wailsapp/wails/v2 v2.10.1
|
||||
go.uber.org/zap v1.27.0
|
||||
golang.org/x/net v0.38.0
|
||||
golang.org/x/sys v0.36.0
|
||||
golang.org/x/text v0.26.0
|
||||
github.com/samber/lo v1.52.0
|
||||
github.com/stretchr/testify v1.11.1
|
||||
github.com/tidwall/gjson v1.18.0
|
||||
github.com/wailsapp/wails/v2 v2.11.0
|
||||
go.uber.org/zap v1.27.1
|
||||
golang.org/x/exp v0.0.0-20251125195548-87e1e737ad39
|
||||
golang.org/x/net v0.47.0
|
||||
golang.org/x/sys v0.38.0
|
||||
golang.org/x/text v0.31.0
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1
|
||||
gorm.io/gorm v1.25.12
|
||||
gorm.io/plugin/dbresolver v1.5.3
|
||||
gorm.io/gorm v1.31.1
|
||||
gorm.io/plugin/dbresolver v1.6.2
|
||||
gorm.io/plugin/soft_delete v1.2.1
|
||||
)
|
||||
|
||||
require (
|
||||
git.sr.ht/~jackmordaunt/go-toast v1.1.2 // indirect
|
||||
github.com/andybalholm/cascadia v1.3.3 // indirect
|
||||
github.com/bahlo/generic-list-go v0.2.0 // indirect
|
||||
github.com/bep/debounce v1.2.1 // indirect
|
||||
github.com/bytedance/sonic v1.14.0 // indirect
|
||||
github.com/bytedance/sonic/loader v0.3.0 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.1.2 // indirect
|
||||
github.com/buger/jsonparser v1.1.1 // indirect
|
||||
github.com/bytedance/gopkg v0.1.3 // indirect
|
||||
github.com/bytedance/sonic v1.14.2 // indirect
|
||||
github.com/bytedance/sonic/loader v0.4.0 // indirect
|
||||
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
github.com/chromedp/cdproto v0.0.0-20250803210736-d308e07a266d // indirect
|
||||
github.com/chromedp/sysutil v1.1.0 // indirect
|
||||
github.com/cloudwego/base64x v0.1.6 // indirect
|
||||
github.com/cloudwego/eino-ext/libs/acl/openai v0.0.0-20250804062529-6e67726a4b3f // indirect
|
||||
github.com/cloudwego/eino-ext/libs/acl/openai v0.1.6 // indirect
|
||||
github.com/cohesion-org/deepseek-go v1.3.2 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||
github.com/eino-contrib/jsonschema v1.0.3 // indirect
|
||||
github.com/esiqveland/notify v0.13.3 // indirect
|
||||
github.com/evanphx/json-patch v0.5.2 // indirect
|
||||
github.com/getkin/kin-openapi v0.118.0 // indirect
|
||||
github.com/glebarez/go-sqlite v1.21.2 // indirect
|
||||
github.com/go-json-experiment/json v0.0.0-20250910080747-cc2cfa0554c3 // indirect
|
||||
github.com/glebarez/go-sqlite v1.22.0 // indirect
|
||||
github.com/go-json-experiment/json v0.0.0-20251027170946-4849db3c2f7e // indirect
|
||||
github.com/go-ole/go-ole v1.3.0 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.21.0 // indirect
|
||||
github.com/go-openapi/swag v0.23.0 // indirect
|
||||
github.com/gobwas/httphead v0.1.0 // indirect
|
||||
github.com/gobwas/pool v0.2.1 // indirect
|
||||
github.com/gobwas/ws v1.4.0 // indirect
|
||||
github.com/godbus/dbus/v5 v5.1.0 // indirect
|
||||
github.com/godbus/dbus/v5 v5.2.0 // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/goph/emperror v0.17.2 // indirect
|
||||
github.com/invopop/yaml v0.1.0 // indirect
|
||||
github.com/gorilla/websocket v1.5.3 // indirect
|
||||
github.com/jackmordaunt/icns/v3 v3.0.1 // indirect
|
||||
github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e // indirect
|
||||
github.com/jchv/go-winloader v0.0.0-20250406163304-c1995be93bd1 // indirect
|
||||
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||
github.com/jinzhu/now v1.1.5 // indirect
|
||||
github.com/jmespath/go-jmespath v0.4.0 // indirect
|
||||
github.com/joho/godotenv v1.5.1 // indirect
|
||||
github.com/josharian/intern v1.0.0 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.3.0 // indirect
|
||||
github.com/labstack/echo/v4 v4.13.3 // indirect
|
||||
github.com/kr/pretty v0.3.1 // indirect
|
||||
github.com/labstack/echo/v4 v4.13.4 // indirect
|
||||
github.com/labstack/gommon v0.4.2 // indirect
|
||||
github.com/leaanthony/go-ansi-parser v1.6.1 // indirect
|
||||
github.com/leaanthony/gosod v1.0.4 // indirect
|
||||
github.com/leaanthony/slicer v1.6.0 // indirect
|
||||
github.com/leaanthony/u v1.1.1 // indirect
|
||||
github.com/mailru/easyjson v0.7.7 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mailru/easyjson v0.9.1 // indirect
|
||||
github.com/mattn/go-colorable v0.1.14 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/meguminnnnnnnnn/go-openai v0.0.0-20250723112853-3bce976e5ccc // indirect
|
||||
github.com/meguminnnnnnnnn/go-openai v0.1.1 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect
|
||||
github.com/ncruces/go-strftime v1.0.0 // indirect
|
||||
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 // indirect
|
||||
github.com/nikolalohinski/gonja v1.5.3 // indirect
|
||||
github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d // indirect
|
||||
github.com/ollama/ollama v0.6.5 // indirect
|
||||
github.com/openai/openai-go v1.10.1 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.2.2 // indirect
|
||||
github.com/perimeterx/marshmallow v1.1.5 // indirect
|
||||
github.com/ollama/ollama v0.13.0 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
|
||||
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
@@ -99,35 +100,35 @@ require (
|
||||
github.com/rivo/uniseg v0.4.7 // indirect
|
||||
github.com/rogpeppe/go-internal v1.14.1 // indirect
|
||||
github.com/sergeymakinen/go-bmp v1.0.0 // indirect
|
||||
github.com/sergeymakinen/go-ico v1.0.0-beta.0 // indirect
|
||||
github.com/sergeymakinen/go-ico v1.0.0 // indirect
|
||||
github.com/sirupsen/logrus v1.9.3 // indirect
|
||||
github.com/slongfield/pyfmt v0.0.0-20220222012616-ea85ff4c361f // indirect
|
||||
github.com/tadvi/systray v0.0.0-20190226123456-11a2b8fa57af // indirect
|
||||
github.com/tevino/abool v0.0.0-20220530134649-2bfc934cb23c // indirect
|
||||
github.com/tidwall/match v1.1.1 // indirect
|
||||
github.com/tidwall/match v1.2.0 // indirect
|
||||
github.com/tidwall/pretty v1.2.1 // indirect
|
||||
github.com/tidwall/sjson v1.2.5 // indirect
|
||||
github.com/tkrajina/go-reflector v0.5.8 // indirect
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||
github.com/valyala/fasttemplate v1.2.2 // indirect
|
||||
github.com/vcaesar/cedar v0.20.2 // indirect
|
||||
github.com/volcengine/volc-sdk-golang v1.0.23 // indirect
|
||||
github.com/volcengine/volcengine-go-sdk v1.1.21 // indirect
|
||||
github.com/wailsapp/go-webview2 v1.0.19 // indirect
|
||||
github.com/volcengine/volc-sdk-golang v1.0.229 // indirect
|
||||
github.com/volcengine/volcengine-go-sdk v1.1.50 // indirect
|
||||
github.com/wailsapp/go-webview2 v1.0.23 // indirect
|
||||
github.com/wailsapp/mimetype v1.4.1 // indirect
|
||||
github.com/wk8/go-ordered-map/v2 v2.1.8 // indirect
|
||||
github.com/yargevad/filepathx v1.0.0 // indirect
|
||||
go.uber.org/multierr v1.10.0 // indirect
|
||||
golang.org/x/arch v0.20.0 // indirect
|
||||
golang.org/x/crypto v0.39.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20250218142911-aa4b98e5adaa // indirect
|
||||
go.uber.org/multierr v1.11.0 // indirect
|
||||
golang.org/x/arch v0.23.0 // indirect
|
||||
golang.org/x/crypto v0.45.0 // indirect
|
||||
google.golang.org/protobuf v1.36.10 // indirect
|
||||
gopkg.in/sourcemap.v1 v1.0.5 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
modernc.org/libc v1.22.5 // indirect
|
||||
modernc.org/mathutil v1.5.0 // indirect
|
||||
modernc.org/memory v1.5.0 // indirect
|
||||
modernc.org/sqlite v1.23.1 // indirect
|
||||
modernc.org/libc v1.67.1 // indirect
|
||||
modernc.org/mathutil v1.7.1 // indirect
|
||||
modernc.org/memory v1.11.0 // indirect
|
||||
modernc.org/sqlite v1.40.1 // indirect
|
||||
)
|
||||
|
||||
// replace github.com/wailsapp/wails/v2 v2.9.2 => C:\Users\spark\go\pkg\mod
|
||||
|
||||
13
main.go
13
main.go
@@ -58,6 +58,13 @@ var OFFICIAL_STATEMENT string
|
||||
var BuildKey string
|
||||
|
||||
func main() {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
log.SugaredLogger.Error("panic: ", r)
|
||||
log.SugaredLogger.Error("stack: ", string(debug.Stack()))
|
||||
}
|
||||
}()
|
||||
|
||||
checkDir("data")
|
||||
db.Init("")
|
||||
data.InitAnalyzeSentiment()
|
||||
@@ -68,6 +75,10 @@ func main() {
|
||||
// Sort: 0,
|
||||
//})
|
||||
|
||||
log.SugaredLogger.Info("starting...")
|
||||
log.SugaredLogger.Infof("version: %s commit: %s", Version, VersionCommit)
|
||||
//log.SugaredLogger.Infof("build key: %s", BuildKey)
|
||||
|
||||
// Create an instance of the app structure
|
||||
app := NewApp()
|
||||
AppMenu := menu.NewMenu()
|
||||
@@ -230,6 +241,8 @@ func AutoMigrate() {
|
||||
db.Dao.AutoMigrate(&models.LongTigerRankData{})
|
||||
db.Dao.AutoMigrate(&data.AIConfig{})
|
||||
db.Dao.AutoMigrate(&models.BKDict{})
|
||||
db.Dao.AutoMigrate(&models.WordAnalyze{})
|
||||
db.Dao.AutoMigrate(&models.SentimentResultAnalyze{})
|
||||
|
||||
updateMultipleModel()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user