Compare commits
11 Commits
v2025.12.1
...
v2026.01.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6b7fed00a3 | ||
|
|
5af0e28601 | ||
|
|
c4af77954e | ||
|
|
8236adb680 | ||
|
|
0adfdab441 | ||
|
|
b1c618a9de | ||
|
|
709c372bf3 | ||
|
|
beb022c448 | ||
|
|
d1aed3419b | ||
|
|
48511c61df | ||
|
|
163e8800f9 |
17
README.md
17
README.md
@@ -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 |
|
||||
|
||||
|
||||
|
||||
|
||||
208
app.go
208
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"
|
||||
@@ -68,13 +70,15 @@ func AddTools(tools []data.Tool) []data.Tool {
|
||||
"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",
|
||||
},
|
||||
},
|
||||
@@ -253,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).
|
||||
@@ -279,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,
|
||||
@@ -387,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()
|
||||
|
||||
@@ -228,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{}
|
||||
|
||||
@@ -193,7 +193,7 @@ func (o *OpenAi) NewSummaryStockNewsStreamWithTools(userQuestion string, sysProm
|
||||
"content": "当前本地时间是:" + time.Now().Format("2006-01-02 15:04:05"),
|
||||
})
|
||||
wg := &sync.WaitGroup{}
|
||||
wg.Add(7)
|
||||
wg.Add(5)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
datas := NewMarketNewsApi().InteractiveAnswer(1, 100, "")
|
||||
@@ -293,51 +293,51 @@ func (o *OpenAi) NewSummaryStockNewsStreamWithTools(userQuestion string, sysProm
|
||||
|
||||
}()
|
||||
|
||||
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",
|
||||
"reasoning_content": "使用工具查询",
|
||||
"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()
|
||||
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().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")
|
||||
messageText.WriteString("## " + telegraph.DataTime.Format("2006-01-02 15:04:05") + ":" + "\n")
|
||||
messageText.WriteString("### " + telegraph.Content + "\n")
|
||||
}
|
||||
msg = append(msg, map[string]interface{}{
|
||||
@@ -409,7 +409,7 @@ func (o *OpenAi) NewSummaryStockNewsStream(userQuestion string, sysPromptId *int
|
||||
"content": "当前本地时间是:" + time.Now().Format("2006-01-02 15:04:05"),
|
||||
})
|
||||
wg := &sync.WaitGroup{}
|
||||
wg.Add(5)
|
||||
wg.Add(4)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
var market strings.Builder
|
||||
@@ -433,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()
|
||||
@@ -507,7 +539,7 @@ func (o *OpenAi) NewSummaryStockNewsStream(userQuestion string, sysPromptId *int
|
||||
|
||||
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")
|
||||
|
||||
@@ -17,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{
|
||||
@@ -32,7 +32,7 @@ func TestNewDeepSeekOpenAiConfig(t *testing.T) {
|
||||
|
||||
ai := NewDeepSeekOpenAi(context.TODO(), 0)
|
||||
//res := ai.NewChatStream("长电科技", "sh600584", "长电科技分析和总结", nil)
|
||||
res := ai.NewSummaryStockNewsStreamWithTools("总结市场资讯,发掘潜力标的/行业/板块/概念,控制风险。调用工具函数验证", nil, tools)
|
||||
res := ai.NewSummaryStockNewsStreamWithTools("总结市场资讯,发掘潜力标的/行业/板块/概念,控制风险。调用工具函数验证", nil, tools, true)
|
||||
|
||||
for {
|
||||
select {
|
||||
|
||||
@@ -767,3 +767,15 @@ type HotStrategyData struct {
|
||||
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
|
||||
@@ -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>
|
||||
|
||||
@@ -128,6 +128,9 @@ onBeforeMount(() => {
|
||||
|
||||
indexIndustryRank.value = setInterval(() => {
|
||||
industryRank()
|
||||
ReFlesh("财联社电报")
|
||||
ReFlesh("新浪财经")
|
||||
ReFlesh("外媒")
|
||||
}, 1000 * 10)
|
||||
|
||||
|
||||
@@ -354,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>
|
||||
|
||||
|
||||
@@ -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,17 +50,18 @@ 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'" style="overflow-wrap: break-word;">-->
|
||||
<!-- <n-tag size="small" :type="item.isRed?'error':'warning'" :bordered="false"> {{ item.time }}</n-tag>-->
|
||||
<!-- <n-text size="small" v-if="item.title" type="warning" :bordered="false">{{ item.title }} </n-text>-->
|
||||
<!-- <n-text style="overflow-wrap: break-word;word-break: break-all; word-wrap: break-word;" :type="item.isRed?'error':'info'">{{ item.content }}</n-text>-->
|
||||
<!-- </n-text>-->
|
||||
<n-collapse v-if="item.title" arrow-placement="right">
|
||||
<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>
|
||||
|
||||
2
go.mod
2
go.mod
@@ -25,6 +25,7 @@ require (
|
||||
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
|
||||
@@ -120,7 +121,6 @@ require (
|
||||
go.uber.org/multierr v1.11.0 // indirect
|
||||
golang.org/x/arch v0.23.0 // indirect
|
||||
golang.org/x/crypto v0.45.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20251125195548-87e1e737ad39 // 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
|
||||
|
||||
Reference in New Issue
Block a user