Compare commits
85 Commits
v2025.9.27
...
dev
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
469c4826a3 | ||
|
|
6c31f5aa76 | ||
|
|
e797b64d41 | ||
|
|
12299e8b47 | ||
|
|
7412d56409 | ||
|
|
f75b457082 | ||
|
|
a43095cdd4 | ||
|
|
6ca0d0df32 | ||
|
|
fea9b06a27 | ||
|
|
1d1e437b47 | ||
|
|
eca2f8adee | ||
|
|
49d2109d60 | ||
|
|
f9294fbffd | ||
|
|
cc12a886c1 | ||
|
|
34ea989d47 | ||
|
|
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 | ||
|
|
c3521c6d7f | ||
|
|
93b37ca621 | ||
|
|
7069af869b | ||
|
|
dbb6789c05 | ||
|
|
8aed4d2753 | ||
|
|
6bd1bdae02 | ||
|
|
9a40d343aa | ||
|
|
e4cdad6ffe | ||
|
|
a0005dab96 | ||
|
|
c945ca9322 | ||
|
|
7bbc6831f4 | ||
|
|
ab0ccc4fe0 | ||
|
|
fa658357c9 | ||
|
|
746589a972 | ||
|
|
401dd17fa8 | ||
|
|
c365bd9534 | ||
|
|
104ee51e13 | ||
|
|
00f3e5f0e0 | ||
|
|
483ffa2244 | ||
|
|
63d278b9aa | ||
|
|
5621d40c71 | ||
|
|
26e9753b94 | ||
|
|
b7f6dbd2da | ||
|
|
18dd01b613 | ||
|
|
81bb33a135 | ||
|
|
9926b61fac | ||
|
|
5e975b060c | ||
|
|
e8f063fd9b | ||
|
|
8b0b53fae7 | ||
|
|
b29c380055 | ||
|
|
cf58a707c7 | ||
|
|
1ae1bb0116 | ||
|
|
d8971935ee | ||
|
|
9c68458b81 | ||
|
|
b367d1eb40 | ||
|
|
8fe79adbb1 | ||
|
|
1d81fdba87 | ||
|
|
6aca0e15cc | ||
|
|
173ce6f243 | ||
|
|
e7875e73d3 |
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:
|
||||
|
||||
23
README.md
23
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)
|
||||
|
||||
@@ -37,11 +37,9 @@
|
||||
| [LMStudio](https://lmstudio.ai/) | ✅ | 本地大模型运行平台 |
|
||||
| [AnythingLLM](https://anythingllm.com/) | ✅ | 本地知识库 |
|
||||
| [DeepSeek](https://www.deepseek.com/) | ✅ | deepseek-reasoner,deepseek-chat |
|
||||
| [大模型聚合平台](https://cloud.siliconflow.cn/i/foufCerk) | ✅ | 如:[302.AI](https://share.302.ai/1KUpfG),[硅基流动](https://cloud.siliconflow.cn/i/foufCerk),[火山方舟](https://www.volcengine.com/experience/ark?utm_term=202502dsinvite&ac=DSASUQY5&rc=IJSE43PZ) |
|
||||
| [大模型聚合平台](https://cloud.siliconflow.cn/i/foufCerk) | ✅ | 如:[硅基流动](https://cloud.siliconflow.cn/i/foufCerk),[火山方舟](https://www.volcengine.com/experience/ark?utm_term=202502dsinvite&ac=DSASUQY5&rc=IJSE43PZ) |
|
||||
|
||||
### <span style="color: #568DF4;">各位亲爱的朋友们,如果您对这个项目感兴趣,请先给我一个<i style="color: #EA2626;">star</i>吧,谢谢!</span>💕
|
||||
- 302.AI:新用户使用邀请码注册,即可领取 $1 测试额度
|
||||
|
||||
[//]: # (- 优云智算(by UCloud):万卡规模4090免费用10小时,新人注册另增50万tokens,海量热门源项目镜像一键部署,[注册链接](https://www.compshare.cn/image-community?ytag=GPU_YY-gh_gostock))
|
||||
- 火山方舟:新用户每个模型注册即送50万tokens,[注册链接](https://www.volcengine.com/experience/ark?utm_term=202502dsinvite&ac=DSASUQY5&rc=IJSE43PZ)
|
||||
- 硅基流动(siliconflow),注册即送2000万Tokens,[注册链接](https://cloud.siliconflow.cn/i/foufCerk)
|
||||
@@ -55,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全部功能,启动时自动同步最近24小时市场资讯(包括外媒简讯) |
|
||||
| 每月赞助 X RMB | vipX | 🧩 更多计划,视go-stock开源项目发展情况而定...(承接GitHub项目README广告推广💖) |
|
||||
|
||||
## 🧩 重大功能开发计划
|
||||
@@ -71,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支持
|
||||
@@ -118,6 +121,8 @@
|
||||
|
||||
## 🦄 重大更新
|
||||
### BIG NEWS !!! 重大更新!!!
|
||||
- 2025.11.21 新增带频率权重的情感分析功能
|
||||

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

|
||||
- 2025.04.24 新增市场行情模块:即时掌握全球市场行情资讯/动态,从此再也不用偷摸去各大财经网站啦。go-stock一键帮你搞定!
|
||||
@@ -164,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 |
|
||||
|
||||
|
||||
|
||||
|
||||
668
app.go
668
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"
|
||||
@@ -38,6 +40,7 @@ type App struct {
|
||||
cronEntrys map[string]cron.EntryID
|
||||
AiTools []data.Tool
|
||||
SponsorInfo map[string]any
|
||||
VipLevel int64
|
||||
}
|
||||
|
||||
// NewApp creates a new App application struct
|
||||
@@ -60,22 +63,87 @@ func AddTools(tools []data.Tool) []data.Tool {
|
||||
tools = append(tools, data.Tool{
|
||||
Type: "function",
|
||||
Function: data.ToolFunction{
|
||||
Name: "SearchStockByIndicators",
|
||||
Description: "根据自然语言筛选股票,返回自然语言选股条件要求的股票所有相关数据。输入股票名称可以获取当前股票最新的股价交易数据和基础财务指标信息,多个股票名称使用,分隔。",
|
||||
Parameters: data.FunctionParameters{
|
||||
Name: "SearchStockByIndicators",
|
||||
Description: "根据自然语言筛选股票,返回自然语言选股条件要求的股票所有相关数据。输入股票名称可以获取当前股票最新的股价交易数据和基础财务指标信息,多个股票名称使用,分隔。" +
|
||||
"例如:查看涨停板:涨停板,按涨幅从高到低排序。" +
|
||||
"例如:查看跌停板:跌停板,按跌幅从高到低排序。" +
|
||||
"例如:查看龙虎榜:龙虎榜,按涨幅从高到低排序。" +
|
||||
"例如:查看昨日龙虎榜:昨日龙虎榜。" +
|
||||
"例如:查看板块龙头行情:板块/概念龙头,按涨幅从高到低排序。" +
|
||||
"例如:查看板块龙头行情:龙头股,按成交量从高到低排序。" +
|
||||
"例如:查看技术指标:上海贝岭,macd,rsi,kdj,boll,5日均线,14日均线,30日均线,60日均线,成交量,OBV,EMA。" +
|
||||
"例如:查看近期趋势:量比连续2天>1,主力连续2日净流入且递增,主力净额>3000万元,行业,股价在20日线上。按成交量从高到低排序。" +
|
||||
"例如:当日成交量 ≥ 近 5 日平均成交量 ×1.5,收盘价 ≥ 20 日均线,20 日均线 ≥ 60 日均线,当日涨幅 3%-7%, 3日主力资金净流入累计≥5000 万元,当日换手率 5%-15%,筹码集中度(90% 筹码峰)≤15%,非创业板非科创板非ST非北交所,行业。按成交量从高到低排序。" +
|
||||
"例如:查看有潜力的成交量爆发股:最近7日成交量量比大于3,出现过一次,非ST。按成交量从高到低排序。" +
|
||||
"例如:超短线策略:当日成交量大于前一日成交量的1.8倍;当日最高价创60日新高当日收盘价大于5日均线;当日为阳线;股价小于200;" +
|
||||
"例1:创新药,半导体;PE<30;净利润增长率>50%。 按成交量从高到低排序。" +
|
||||
"例2:上证指数,科创50。 " +
|
||||
"例3:长电科技,上海贝岭。" +
|
||||
"例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%。按成交量从高到低排序。" +
|
||||
"例8:基本条件:前期有爆量,回调到 10 日线,当日是缩量阴线,均线趋势向上。;优选条件:一月之内涨停次数>=1。按成交量从高到低排序。",
|
||||
Parameters: &data.FunctionParameters{
|
||||
Type: "object",
|
||||
Properties: map[string]any{
|
||||
"words": map[string]any{
|
||||
"type": "string",
|
||||
"description": "选股自然语言。" +
|
||||
"例1:创新药,半导体;PE<30;净利润增长率>50%。 " +
|
||||
"例如:查看涨停板:涨停板,按涨幅从高到低排序。" +
|
||||
"例如:查看跌停板:跌停板,按跌幅从高到低排序。" +
|
||||
"例如:查看龙虎榜:龙虎榜,按涨幅从高到低排序。" +
|
||||
"例如:查看昨日龙虎榜:昨日龙虎榜。" +
|
||||
"例如:查看板块龙头行情:板块/概念龙头,按涨幅从高到低排序。" +
|
||||
"例如:查看板块龙头行情:龙头股,按成交量从高到低排序。" +
|
||||
"例如:查看技术指标:上海贝岭,macd,rsi,kdj,boll,5日均线,14日均线,30日均线,60日均线,成交量,OBV,EMA。" +
|
||||
"例如:查看近期趋势:量比连续2天>1,主力连续2日净流入且递增,主力净额>3000万元,行业,股价在20日线上。按成交量从高到低排序。" +
|
||||
"例如:当日成交量 ≥ 近 5 日平均成交量 ×1.5,收盘价 ≥ 20 日均线,20 日均线 ≥ 60 日均线,当日涨幅 3%-7%, 3日主力资金净流入累计≥5000 万元,当日换手率 5%-15%,筹码集中度(90% 筹码峰)≤15%,非创业板非科创板非ST非北交所,行业。按成交量从高到低排序。" +
|
||||
"例如:查看有潜力的成交量爆发股:最近7日成交量量比大于3,出现过一次,非ST。按成交量从高到低排序。" +
|
||||
"例如:超短线策略:当日成交量大于前一日成交量的1.8倍;当日最高价创60日新高当日收盘价大于5日均线;当日为阳线;股价小于200;" +
|
||||
"例1:创新药,半导体;PE<30;净利润增长率>50%。 按成交量从高到低排序。" +
|
||||
"例2:上证指数,科创50。 " +
|
||||
"例3:长电科技,上海贝岭。" +
|
||||
"例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亿。" +
|
||||
"例8:基本条件:前期有爆量,回调到 10 日线,当日是缩量阴线,均线趋势向上。;优选条件:一月之内涨停次数>=1",
|
||||
"例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%。按成交量从高到低排序。" +
|
||||
"例8:基本条件:前期有爆量,回调到 10 日线,当日是缩量阴线,均线趋势向上。;优选条件:一月之内涨停次数>=1。按成交量从高到低排序。",
|
||||
},
|
||||
},
|
||||
Required: []string{"words"},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
tools = append(tools, data.Tool{
|
||||
Type: "function",
|
||||
Function: data.ToolFunction{
|
||||
Name: "SearchBk",
|
||||
Description: "根据自然语言查询板块/概念/指数整体数据。" +
|
||||
"例如:存储芯片,成分股" +
|
||||
"例如:查看指数:上证指数。" +
|
||||
"例如:查看存储芯片板块:存储芯片。" +
|
||||
"例如:查看概念板块排名:今日涨幅前5的概念板块。" +
|
||||
"例如:查看概念板块排名:今日净流入前5的概念板块。" +
|
||||
"例如:查看板块/概念排名数据:今日主力净流出前15的概念板块。" +
|
||||
"例如:查看板块板块/概念:今日成交量前15的概念板块。" +
|
||||
"例如:通过市盈率查询板块:当前市盈率介于30-50的板块/概念。",
|
||||
|
||||
Parameters: &data.FunctionParameters{
|
||||
Type: "object",
|
||||
Properties: map[string]any{
|
||||
"words": map[string]any{
|
||||
"type": "string",
|
||||
"description": "板块/概念数据查询自然语言。" +
|
||||
"例如:存储芯片,成分股" +
|
||||
"例如:查看指数:上证指数。" +
|
||||
"例如:查看存储芯片板块:存储芯片。" +
|
||||
"例如:查看概念板块排名:今日涨幅前5的概念板块。" +
|
||||
"例如:查看概念板块排名:今日净流入前5的概念板块。" +
|
||||
"例如:查看板块/概念排名数据:今日主力净流出前15的概念板块。" +
|
||||
"例如:查看板块板块/概念:今日成交量前15的概念板块。" +
|
||||
"例如:通过市盈率查询板块:当前市盈率介于30-50的板块/概念。",
|
||||
},
|
||||
},
|
||||
Required: []string{"words"},
|
||||
@@ -88,7 +156,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 +178,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 +229,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 +243,218 @@ func AddTools(tools []data.Tool) []data.Tool {
|
||||
},
|
||||
})
|
||||
|
||||
tools = append(tools, data.Tool{
|
||||
Type: "function",
|
||||
Function: data.ToolFunction{
|
||||
Name: "HotStrategyTable",
|
||||
Description: "获取当前热门选股策略",
|
||||
},
|
||||
})
|
||||
|
||||
tools = append(tools, data.Tool{
|
||||
Type: "function",
|
||||
Function: data.ToolFunction{
|
||||
Name: "HotStockTable",
|
||||
Description: "当前热门股票排名",
|
||||
Parameters: &data.FunctionParameters{
|
||||
Type: "object",
|
||||
Properties: map[string]any{
|
||||
"pageSize": map[string]any{
|
||||
"type": "string",
|
||||
"description": "分页大小",
|
||||
},
|
||||
},
|
||||
Required: []string{"pageSize"},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
tools = append(tools, data.Tool{
|
||||
Type: "function",
|
||||
Function: data.ToolFunction{
|
||||
Name: "GetStockMoneyData",
|
||||
Description: "今日股票资金流入排名",
|
||||
Parameters: &data.FunctionParameters{
|
||||
Type: "object",
|
||||
Properties: map[string]any{
|
||||
"pageSize": map[string]any{
|
||||
"type": "string",
|
||||
"description": "分页大小",
|
||||
},
|
||||
},
|
||||
Required: []string{"pageSize"},
|
||||
},
|
||||
},
|
||||
})
|
||||
tools = append(tools, data.Tool{
|
||||
Type: "function",
|
||||
Function: data.ToolFunction{
|
||||
Name: "GetStockConceptInfo",
|
||||
Description: "获取股票所属概念详细信息",
|
||||
Parameters: &data.FunctionParameters{
|
||||
Type: "object",
|
||||
Properties: map[string]any{
|
||||
"code": map[string]any{
|
||||
"type": "string",
|
||||
"description": "股票代码,如:601138.SH。注意 上海证券交易所股票以.SH结尾,深圳证券交易所股票以.SZ结尾,港股股票以.HK结尾,北交所股票以.BJ结尾,",
|
||||
},
|
||||
},
|
||||
Required: []string{"code"},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
//CreateAiRecommendStocks
|
||||
tools = append(tools, data.Tool{
|
||||
Type: "function",
|
||||
Function: data.ToolFunction{
|
||||
Name: "CreateAiRecommendStocks",
|
||||
Description: "创建/保存AI推荐股票记录",
|
||||
Parameters: &data.FunctionParameters{
|
||||
Type: "object",
|
||||
Properties: map[string]any{
|
||||
"modelName": map[string]any{
|
||||
"type": "string",
|
||||
"description": "模型名称",
|
||||
},
|
||||
"stockCode": map[string]any{
|
||||
"type": "string",
|
||||
"description": "股票代码,如:601138.SH。注意 上海证券交易所股票以.SH结尾,深圳证券交易所股票以.SZ结尾,港股股票以.HK结尾,北交所股票以.BJ结尾,",
|
||||
},
|
||||
"stockName": map[string]any{
|
||||
"type": "string",
|
||||
"description": "股票名称",
|
||||
},
|
||||
"bkCode": map[string]any{
|
||||
"type": "string",
|
||||
"description": "板块/行业代码",
|
||||
},
|
||||
"bkName": map[string]any{
|
||||
"type": "string",
|
||||
"description": "板块/概念/行业名称",
|
||||
},
|
||||
"stockPrice": map[string]any{
|
||||
"type": "string",
|
||||
"description": "推荐时股票价格",
|
||||
},
|
||||
"stockPrePrice": map[string]any{
|
||||
"type": "string",
|
||||
"description": "前一交易日股票价格",
|
||||
},
|
||||
"stockClosePrice": map[string]any{
|
||||
"type": "string",
|
||||
"description": "推荐时股票收盘价格",
|
||||
},
|
||||
"recommendReason": map[string]any{
|
||||
"type": "string",
|
||||
"description": "推荐理由/驱动因素/逻辑",
|
||||
},
|
||||
"recommendBuyPrice": map[string]any{
|
||||
"type": "string",
|
||||
"description": "ai建议买入价区间最低价和最高价之间用`-`分隔",
|
||||
},
|
||||
"recommendStopProfitPrice": map[string]any{
|
||||
"type": "string",
|
||||
"description": "ai建议止盈价区间最低价和最高价之间用`-`分隔",
|
||||
},
|
||||
"recommendStopLossPrice": map[string]any{
|
||||
"type": "string",
|
||||
"description": "ai建议止损价",
|
||||
},
|
||||
"riskRemarks": map[string]any{
|
||||
"type": "string",
|
||||
"description": "风险提示",
|
||||
},
|
||||
"remarks": map[string]any{
|
||||
"type": "string",
|
||||
"description": "操作总结/备注",
|
||||
},
|
||||
},
|
||||
Required: []string{"stockCode", "stockName", "bkName"},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
//BatchCreateAiRecommendStocks
|
||||
tools = append(tools, data.Tool{
|
||||
Type: "function",
|
||||
Function: data.ToolFunction{
|
||||
Name: "BatchCreateAiRecommendStocks",
|
||||
Description: "批量创建/保存AI推荐股票记录,建议每次批量保存5条记录",
|
||||
Parameters: &data.FunctionParameters{
|
||||
Type: "object",
|
||||
Properties: map[string]any{
|
||||
"stocks": map[string]any{
|
||||
"type": "array",
|
||||
"items": map[string]any{
|
||||
"type": "object",
|
||||
"properties": map[string]any{
|
||||
"modelName": map[string]any{
|
||||
"type": "string",
|
||||
"description": "模型名称",
|
||||
},
|
||||
"stockCode": map[string]any{
|
||||
"type": "string",
|
||||
"description": "股票代码,如:601138.SH。注意 上海证券交易所股票以.SH结尾,深圳证券交易所股票以.SZ结尾,港股股票以.HK结尾,北交所股票以.BJ结尾,",
|
||||
},
|
||||
"stockName": map[string]any{
|
||||
"type": "string",
|
||||
"description": "股票名称",
|
||||
},
|
||||
"bkCode": map[string]any{
|
||||
"type": "string",
|
||||
"description": "板块/行业代码",
|
||||
},
|
||||
"bkName": map[string]any{
|
||||
"type": "string",
|
||||
"description": "板块/概念/行业名称",
|
||||
},
|
||||
"stockPrice": map[string]any{
|
||||
"type": "string",
|
||||
"description": "推荐时股票价格",
|
||||
},
|
||||
"stockPrePrice": map[string]any{
|
||||
"type": "string",
|
||||
"description": "前一交易日股票价格",
|
||||
},
|
||||
"stockClosePrice": map[string]any{
|
||||
"type": "string",
|
||||
"description": "推荐时股票收盘价格",
|
||||
},
|
||||
"recommendReason": map[string]any{
|
||||
"type": "string",
|
||||
"description": "推荐理由/驱动因素/逻辑",
|
||||
},
|
||||
"recommendBuyPrice": map[string]any{
|
||||
"type": "string",
|
||||
"description": "ai建议买入价区间最低价和最高价之间用`-`分隔",
|
||||
},
|
||||
"recommendStopProfitPrice": map[string]any{
|
||||
"type": "string",
|
||||
"description": "ai建议止盈价区间最低价和最高价之间用`-`分隔",
|
||||
},
|
||||
"recommendStopLossPrice": map[string]any{
|
||||
"type": "string",
|
||||
"description": "ai建议止损价",
|
||||
},
|
||||
"riskRemarks": map[string]any{
|
||||
"type": "string",
|
||||
"description": "风险提示",
|
||||
},
|
||||
"remarks": map[string]any{
|
||||
"type": "string",
|
||||
"description": "操作总结/备注",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
Required: []string{"stockCode", "stockName", "bkName"},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
return tools
|
||||
}
|
||||
|
||||
@@ -245,8 +525,16 @@ 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)
|
||||
a.VipLevel = level
|
||||
if level >= 2 {
|
||||
go a.syncNews()
|
||||
}
|
||||
}
|
||||
|
||||
if releaseVersion.TagName != Version {
|
||||
tag := &models.Tag{}
|
||||
_, err = resty.New().R().
|
||||
SetResult(tag).
|
||||
@@ -271,59 +559,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 +617,147 @@ 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)
|
||||
//计算时间差如果<5分钟则推送
|
||||
if time.Now().Sub(dataTime) < 5*time.Minute {
|
||||
a.NewsPush(&[]models.Telegraph{*telegraph})
|
||||
}
|
||||
tags := slice.Filter(news.Tags, func(index int, item string) bool {
|
||||
return !(item == "rotating_light" || item == "loudspeaker")
|
||||
})
|
||||
for _, subject := range 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 slice.ContainAny(tags, []string{"外媒简讯", "外媒资讯", "外媒"}) {
|
||||
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()
|
||||
@@ -410,10 +789,18 @@ func (a *App) domReady(ctx context.Context) {
|
||||
//定时更新数据
|
||||
config := data.GetSettingConfig()
|
||||
go func() {
|
||||
go data.NewMarketNewsApi().TelegraphList(30)
|
||||
go data.NewMarketNewsApi().GetSinaNews(30)
|
||||
go data.NewMarketNewsApi().TradingViewNews()
|
||||
|
||||
interval := config.RefreshInterval
|
||||
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 {
|
||||
@@ -428,7 +815,8 @@ func (a *App) domReady(ctx context.Context) {
|
||||
a.cronEntrys["MonitorStockPrices"] = id
|
||||
}
|
||||
entryID, err := a.cron.AddFunc(fmt.Sprintf("@every %ds", interval+10), func() {
|
||||
news := data.NewMarketNewsApi().GetNewTelegraph(30)
|
||||
//news := data.NewMarketNewsApi().GetNewTelegraph(30)
|
||||
news := data.NewMarketNewsApi().TelegraphList(30)
|
||||
if config.EnablePushNews {
|
||||
go a.NewsPush(news)
|
||||
}
|
||||
@@ -452,6 +840,19 @@ func (a *App) domReady(ctx context.Context) {
|
||||
} else {
|
||||
a.cronEntrys["newSinaNews"] = entryIDSina
|
||||
}
|
||||
|
||||
entryIDTradingViewNews, err := a.cron.AddFunc(fmt.Sprintf("@every %ds", interval+10), func() {
|
||||
news := data.NewMarketNewsApi().TradingViewNews()
|
||||
if config.EnablePushNews {
|
||||
go a.NewsPush(news)
|
||||
}
|
||||
go runtime.EventsEmit(a.ctx, "tradingViewNews", news)
|
||||
})
|
||||
if err != nil {
|
||||
logger.SugaredLogger.Errorf("AddFunc error:%s", err.Error())
|
||||
} else {
|
||||
a.cronEntrys["tradingViewNews"] = entryIDTradingViewNews
|
||||
}
|
||||
}()
|
||||
|
||||
//刷新基金净值信息
|
||||
@@ -564,65 +965,84 @@ func (a *App) CheckStockBaseInfo(ctx context.Context) {
|
||||
SetResult(stockBasics).
|
||||
Get("http://8.134.249.145:18080/go-stock/stock_basic.json")
|
||||
|
||||
count := int64(0)
|
||||
db.Dao.Model(&data.StockBasic{}).Count(&count)
|
||||
if count == int64(len(*stockBasics)) {
|
||||
return
|
||||
}
|
||||
for _, stock := range *stockBasics {
|
||||
stockInfo := &data.StockBasic{
|
||||
TsCode: stock.TsCode,
|
||||
Name: stock.Name,
|
||||
Symbol: stock.Symbol,
|
||||
BKCode: stock.BKCode,
|
||||
BKName: stock.BKName,
|
||||
}
|
||||
db.Dao.Model(&data.StockBasic{}).Where("ts_code = ?", stock.TsCode).First(stockInfo)
|
||||
if stockInfo.ID == 0 {
|
||||
db.Dao.Model(&data.StockBasic{}).Create(stockInfo)
|
||||
} else {
|
||||
db.Dao.Model(&data.StockBasic{}).Where("ts_code = ?", stock.TsCode).Updates(stockInfo)
|
||||
}
|
||||
db.Dao.Unscoped().Model(&data.StockBasic{}).Where("1=1").Delete(&data.StockBasic{})
|
||||
err := db.Dao.CreateInBatches(stockBasics, 400).Error
|
||||
if err != nil {
|
||||
logger.SugaredLogger.Errorf("保存StockBasic股票基础信息失败:%s", err.Error())
|
||||
}
|
||||
|
||||
//count := int64(0)
|
||||
//db.Dao.Model(&data.StockBasic{}).Count(&count)
|
||||
//if count == int64(len(*stockBasics)) {
|
||||
// return
|
||||
//}
|
||||
//for _, stock := range *stockBasics {
|
||||
// stockInfo := &data.StockBasic{
|
||||
// TsCode: stock.TsCode,
|
||||
// Name: stock.Name,
|
||||
// Symbol: stock.Symbol,
|
||||
// BKCode: stock.BKCode,
|
||||
// BKName: stock.BKName,
|
||||
// }
|
||||
// db.Dao.Model(&data.StockBasic{}).Where("ts_code = ?", stock.TsCode).First(stockInfo)
|
||||
// if stockInfo.ID == 0 {
|
||||
// db.Dao.Model(&data.StockBasic{}).Create(stockInfo)
|
||||
// } else {
|
||||
// db.Dao.Model(&data.StockBasic{}).Where("ts_code = ?", stock.TsCode).Updates(stockInfo)
|
||||
// }
|
||||
//}
|
||||
|
||||
stockHKBasics := &[]models.StockInfoHK{}
|
||||
resty.New().R().
|
||||
SetHeader("user", "go-stock").
|
||||
SetResult(stockHKBasics).
|
||||
Get("http://8.134.249.145:18080/go-stock/stock_base_info_hk.json")
|
||||
for _, stock := range *stockHKBasics {
|
||||
stockInfo := &models.StockInfoHK{
|
||||
Code: stock.Code,
|
||||
Name: stock.Name,
|
||||
BKName: stock.BKName,
|
||||
BKCode: stock.BKCode,
|
||||
}
|
||||
db.Dao.Model(&models.StockInfoHK{}).Where("code = ?", stock.Code).First(stockInfo)
|
||||
if stockInfo.ID == 0 {
|
||||
db.Dao.Model(&models.StockInfoHK{}).Create(stockInfo)
|
||||
} else {
|
||||
db.Dao.Model(&models.StockInfoHK{}).Where("code = ?", stock.Code).Updates(stockInfo)
|
||||
}
|
||||
|
||||
db.Dao.Unscoped().Model(&models.StockInfoHK{}).Where("1=1").Delete(&models.StockInfoHK{})
|
||||
err = db.Dao.CreateInBatches(stockHKBasics, 400).Error
|
||||
if err != nil {
|
||||
logger.SugaredLogger.Errorf("保存StockInfoHK股票基础信息失败:%s", err.Error())
|
||||
}
|
||||
|
||||
//for _, stock := range *stockHKBasics {
|
||||
// stockInfo := &models.StockInfoHK{
|
||||
// Code: stock.Code,
|
||||
// Name: stock.Name,
|
||||
// BKName: stock.BKName,
|
||||
// BKCode: stock.BKCode,
|
||||
// }
|
||||
// db.Dao.Model(&models.StockInfoHK{}).Where("code = ?", stock.Code).First(stockInfo)
|
||||
// if stockInfo.ID == 0 {
|
||||
// db.Dao.Model(&models.StockInfoHK{}).Create(stockInfo)
|
||||
// } else {
|
||||
// db.Dao.Model(&models.StockInfoHK{}).Where("code = ?", stock.Code).Updates(stockInfo)
|
||||
// }
|
||||
//}
|
||||
stockUSBasics := &[]models.StockInfoUS{}
|
||||
resty.New().R().
|
||||
SetHeader("user", "go-stock").
|
||||
SetResult(stockUSBasics).
|
||||
Get("http://8.134.249.145:18080/go-stock/stock_base_info_us.json")
|
||||
for _, stock := range *stockUSBasics {
|
||||
stockInfo := &models.StockInfoUS{
|
||||
Code: stock.Code,
|
||||
Name: stock.Name,
|
||||
BKName: stock.BKName,
|
||||
BKCode: stock.BKCode,
|
||||
}
|
||||
db.Dao.Model(&models.StockInfoUS{}).Where("code = ?", stock.Code).First(stockInfo)
|
||||
if stockInfo.ID == 0 {
|
||||
db.Dao.Model(&models.StockInfoUS{}).Create(stockInfo)
|
||||
} else {
|
||||
db.Dao.Model(&models.StockInfoUS{}).Where("code = ?", stock.Code).Updates(stockInfo)
|
||||
}
|
||||
|
||||
db.Dao.Unscoped().Model(&models.StockInfoUS{}).Where("1=1").Delete(&models.StockInfoUS{})
|
||||
err = db.Dao.CreateInBatches(stockUSBasics, 400).Error
|
||||
if err != nil {
|
||||
logger.SugaredLogger.Errorf("保存StockInfoUS股票基础信息失败:%s", err.Error())
|
||||
}
|
||||
//for _, stock := range *stockUSBasics {
|
||||
// stockInfo := &models.StockInfoUS{
|
||||
// Code: stock.Code,
|
||||
// Name: stock.Name,
|
||||
// BKName: stock.BKName,
|
||||
// BKCode: stock.BKCode,
|
||||
// }
|
||||
// db.Dao.Model(&models.StockInfoUS{}).Where("code = ?", stock.Code).First(stockInfo)
|
||||
// if stockInfo.ID == 0 {
|
||||
// db.Dao.Model(&models.StockInfoUS{}).Create(stockInfo)
|
||||
// } else {
|
||||
// db.Dao.Model(&models.StockInfoUS{}).Where("code = ?", stock.Code).Updates(stockInfo)
|
||||
// }
|
||||
//}
|
||||
|
||||
}
|
||||
func (a *App) NewsPush(news *[]models.Telegraph) {
|
||||
@@ -649,7 +1069,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 := ""
|
||||
@@ -680,7 +1100,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{}
|
||||
}
|
||||
@@ -1014,12 +1434,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)
|
||||
@@ -1337,8 +1757,10 @@ func (a *App) GetTelegraphList(source string) *[]*models.Telegraph {
|
||||
}
|
||||
|
||||
func (a *App) ReFleshTelegraphList(source string) *[]*models.Telegraph {
|
||||
data.NewMarketNewsApi().GetNewTelegraph(30)
|
||||
data.NewMarketNewsApi().GetSinaNews(30)
|
||||
//data.NewMarketNewsApi().GetNewTelegraph(30)
|
||||
go data.NewMarketNewsApi().TelegraphList(30)
|
||||
go data.NewMarketNewsApi().GetSinaNews(30)
|
||||
go data.NewMarketNewsApi().TradingViewNews()
|
||||
telegraphs := data.NewMarketNewsApi().GetTelegraphList(source)
|
||||
return telegraphs
|
||||
}
|
||||
@@ -1347,12 +1769,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 {
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/wailsapp/wails/v2/pkg/runtime"
|
||||
"go-stock/backend/agent"
|
||||
"go-stock/backend/data"
|
||||
"go-stock/backend/models"
|
||||
|
||||
"github.com/wailsapp/wails/v2/pkg/runtime"
|
||||
)
|
||||
|
||||
// @Author spark
|
||||
@@ -26,48 +27,86 @@ func (a *App) StockNotice(stockCode string) []any {
|
||||
func (a *App) IndustryResearchReport(industryCode string) []any {
|
||||
return data.NewMarketNewsApi().IndustryResearchReport(industryCode, 7)
|
||||
}
|
||||
func (a App) EMDictCode(code string) []any {
|
||||
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)
|
||||
}
|
||||
|
||||
func (a App) HotStock(marketType string) *[]models.HotItem {
|
||||
func (a *App) HotStock(marketType string) *[]models.HotItem {
|
||||
return data.NewMarketNewsApi().XUEQIUHotStock(100, marketType)
|
||||
}
|
||||
|
||||
func (a App) HotEvent(size int) *[]models.HotEvent {
|
||||
func (a *App) HotEvent(size int) *[]models.HotEvent {
|
||||
if size <= 0 {
|
||||
size = 10
|
||||
}
|
||||
return data.NewMarketNewsApi().HotEvent(size)
|
||||
}
|
||||
func (a App) HotTopic(size int) []any {
|
||||
func (a *App) HotTopic(size int) []any {
|
||||
if size <= 0 {
|
||||
size = 10
|
||||
}
|
||||
return data.NewMarketNewsApi().HotTopic(size)
|
||||
}
|
||||
|
||||
func (a App) InvestCalendarTimeLine(yearMonth string) []any {
|
||||
func (a *App) InvestCalendarTimeLine(yearMonth string) []any {
|
||||
return data.NewMarketNewsApi().InvestCalendar(yearMonth)
|
||||
}
|
||||
func (a App) ClsCalendar() []any {
|
||||
func (a *App) ClsCalendar() []any {
|
||||
return data.NewMarketNewsApi().ClsCalendar()
|
||||
}
|
||||
|
||||
func (a App) SearchStock(words string) map[string]any {
|
||||
func (a *App) SearchStock(words string) map[string]any {
|
||||
return data.NewSearchStockApi(words).SearchStock(5000)
|
||||
}
|
||||
func (a App) GetHotStrategy() map[string]any {
|
||||
func (a *App) GetHotStrategy() map[string]any {
|
||||
return data.NewSearchStockApi("").HotStrategy()
|
||||
}
|
||||
|
||||
func (a App) ChatWithAgent(question string, aiConfigId int, sysPromptId *int) {
|
||||
func (a *App) ChatWithAgent(question string, aiConfigId int, sysPromptId *int) {
|
||||
ch := agent.NewStockAiAgentApi().Chat(question, aiConfigId, sysPromptId)
|
||||
for msg := range ch {
|
||||
runtime.EventsEmit(a.ctx, "agent-message", msg)
|
||||
}
|
||||
}
|
||||
|
||||
func (a *App) AnalyzeSentimentWithFreqWeight(text string) map[string]any {
|
||||
result, cleanFrequencies := data.NewsAnalyze(text, false)
|
||||
return map[string]any{
|
||||
"result": result,
|
||||
"frequencies": cleanFrequencies,
|
||||
}
|
||||
}
|
||||
|
||||
func (a *App) GetAIResponseResultList(query models.AIResponseResultQuery) *models.AIResponseResultPageData {
|
||||
page, err := data.NewAIResponseResultService().GetAIResponseResultList(query)
|
||||
if err != nil {
|
||||
return &models.AIResponseResultPageData{}
|
||||
}
|
||||
return page
|
||||
}
|
||||
func (a *App) DeleteAIResponseResult(id string) string {
|
||||
err := data.NewAIResponseResultService().DeleteAIResponseResult(id)
|
||||
if err != nil {
|
||||
return "删除失败"
|
||||
}
|
||||
return "删除成功"
|
||||
}
|
||||
func (a *App) BatchDeleteAIResponseResult(ids []uint) string {
|
||||
err := data.NewAIResponseResultService().BatchDeleteAIResponseResult(ids)
|
||||
if err != nil {
|
||||
return "删除失败"
|
||||
}
|
||||
return "删除成功"
|
||||
}
|
||||
|
||||
func (a *App) GetAiRecommendStocksList(query models.AiRecommendStocksQuery) *models.AiRecommendStocksPageData {
|
||||
page, err := data.NewAiRecommendStocksService().GetAiRecommendStocksList(&query)
|
||||
if err != nil {
|
||||
return &models.AiRecommendStocksPageData{}
|
||||
}
|
||||
return page
|
||||
}
|
||||
|
||||
27
app_test.go
27
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,28 @@ 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)
|
||||
}
|
||||
|
||||
func TestGetScreenResolution(t *testing.T) {
|
||||
x, y, w, h, err := getScreenResolution()
|
||||
if err != nil {
|
||||
logger.SugaredLogger.Errorf("get screen resolution error:%s", err.Error())
|
||||
return
|
||||
}
|
||||
logger.SugaredLogger.Infof("x:%d,y:%d,w:%d,h:%d", x, y, w, h)
|
||||
|
||||
}
|
||||
|
||||
@@ -6,16 +6,17 @@ package main
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"go-stock/backend/data"
|
||||
"go-stock/backend/db"
|
||||
"go-stock/backend/logger"
|
||||
"time"
|
||||
|
||||
"github.com/duke-git/lancet/v2/convertor"
|
||||
"github.com/duke-git/lancet/v2/strutil"
|
||||
"github.com/energye/systray"
|
||||
"github.com/go-toast/toast"
|
||||
"github.com/wailsapp/wails/v2/pkg/options"
|
||||
"github.com/wailsapp/wails/v2/pkg/runtime"
|
||||
"go-stock/backend/data"
|
||||
"go-stock/backend/db"
|
||||
"go-stock/backend/logger"
|
||||
"time"
|
||||
)
|
||||
|
||||
// startup is called at application startup
|
||||
@@ -209,6 +210,7 @@ func getScreenResolution() (int, int, int, int, error) {
|
||||
//
|
||||
//width, _, _ := getSystemMetrics.Call(0)
|
||||
//height, _, _ := getSystemMetrics.Call(1)
|
||||
//return int(width), int(height), 1456, 768, nil
|
||||
|
||||
return int(1366), int(768), 1456, 768, nil
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ import (
|
||||
"github.com/cloudwego/eino/compose"
|
||||
"github.com/cloudwego/eino/flow/agent"
|
||||
"github.com/cloudwego/eino/schema"
|
||||
"github.com/duke-git/lancet/v2/fileutil"
|
||||
)
|
||||
|
||||
// @Author spark
|
||||
@@ -61,7 +62,7 @@ func TestGetStockAiAgent(t *testing.T) {
|
||||
logger.SugaredLogger.Errorf("failed to recv: %v", err)
|
||||
return
|
||||
}
|
||||
//logger.SugaredLogger.Infof("stream recv: %v", msg)
|
||||
logger.SugaredLogger.Infof("stream recv: %v", msg)
|
||||
if msg.ReasoningContent != "" {
|
||||
md.WriteString(msg.ReasoningContent)
|
||||
}
|
||||
@@ -76,9 +77,12 @@ func TestGetStockAiAgent(t *testing.T) {
|
||||
func TestAgent(t *testing.T) {
|
||||
db.Init("../../data/stock.db")
|
||||
|
||||
ch := NewStockAiAgentApi().Chat("分析一下海立股份,使用工具", 1, nil)
|
||||
md := strings.Builder{}
|
||||
ch := NewStockAiAgentApi().Chat("分析一下立讯精密", 0, nil)
|
||||
for message := range ch {
|
||||
logger.SugaredLogger.Infof("res:%s", message.String())
|
||||
md.WriteString(message.String())
|
||||
}
|
||||
|
||||
logger.SugaredLogger.Info(md.String())
|
||||
fileutil.WriteStringToFile("../../data/result.md", md.String(), false)
|
||||
}
|
||||
|
||||
@@ -4,11 +4,12 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"go-stock/backend/data"
|
||||
|
||||
"github.com/cloudwego/eino/components/tool"
|
||||
"github.com/cloudwego/eino/schema"
|
||||
"github.com/duke-git/lancet/v2/convertor"
|
||||
"github.com/duke-git/lancet/v2/random"
|
||||
"go-stock/backend/data"
|
||||
)
|
||||
|
||||
// @Author spark
|
||||
@@ -55,7 +56,7 @@ func (c ChoiceStockByIndicators) InvokableRun(ctx context.Context, argumentsInJS
|
||||
}
|
||||
content := "无符合条件的数据"
|
||||
words := parms["words"].(string)
|
||||
res := data.NewSearchStockApi(words).SearchStock(random.RandInt(5, 10))
|
||||
res := data.NewSearchStockApi(words).SearchStock(random.RandInt(5, 20))
|
||||
if convertor.ToString(res["code"]) == "100" {
|
||||
resData := res["data"].(map[string]any)
|
||||
result := resData["result"].(map[string]any)
|
||||
|
||||
@@ -3,13 +3,14 @@ package tools
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"go-stock/backend/data"
|
||||
"go-stock/backend/logger"
|
||||
"strings"
|
||||
|
||||
"github.com/cloudwego/eino/components/tool"
|
||||
"github.com/cloudwego/eino/schema"
|
||||
"github.com/duke-git/lancet/v2/random"
|
||||
"github.com/tidwall/gjson"
|
||||
"go-stock/backend/data"
|
||||
"go-stock/backend/logger"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// @Author spark
|
||||
@@ -51,7 +52,7 @@ func (q QueryMarketNews) InvokableRun(ctx context.Context, argumentsInJSON strin
|
||||
})
|
||||
}
|
||||
|
||||
news := data.NewMarketNewsApi().GetNewsList("财联社电报", random.RandInt(100, 500))
|
||||
news := data.NewMarketNewsApi().GetNewsList("", random.RandInt(100, 500))
|
||||
messageText := strings.Builder{}
|
||||
for _, telegraph := range *news {
|
||||
messageText.WriteString("## " + telegraph.Time + ":" + "\n")
|
||||
|
||||
138
backend/data/ai_recommend_stocks_api.go
Normal file
138
backend/data/ai_recommend_stocks_api.go
Normal file
@@ -0,0 +1,138 @@
|
||||
// Package data ai_recommend_stocks_api.go
|
||||
package data
|
||||
|
||||
import (
|
||||
"go-stock/backend/db"
|
||||
"go-stock/backend/models"
|
||||
|
||||
"github.com/duke-git/lancet/v2/slice"
|
||||
"github.com/duke-git/lancet/v2/strutil"
|
||||
)
|
||||
|
||||
type AiRecommendStocksService struct{}
|
||||
|
||||
func NewAiRecommendStocksService() *AiRecommendStocksService {
|
||||
return &AiRecommendStocksService{}
|
||||
}
|
||||
|
||||
// CreateAiRecommendStocks 创建AI推荐股票记录
|
||||
func (s *AiRecommendStocksService) CreateAiRecommendStocks(recommend *models.AiRecommendStocks) error {
|
||||
result := db.Dao.Create(recommend)
|
||||
return result.Error
|
||||
}
|
||||
|
||||
func (s *AiRecommendStocksService) BatchCreateAiRecommendStocks(recommends []*models.AiRecommendStocks) error {
|
||||
result := db.Dao.Create(recommends)
|
||||
return result.Error
|
||||
}
|
||||
|
||||
// GetAiRecommendStocksList 分页查询AI推荐股票记录
|
||||
func (s *AiRecommendStocksService) GetAiRecommendStocksList(query *models.AiRecommendStocksQuery) (*models.AiRecommendStocksPageData, error) {
|
||||
var list []models.AiRecommendStocks
|
||||
var total int64
|
||||
|
||||
q := db.Dao.Model(&models.AiRecommendStocks{})
|
||||
|
||||
// 构建查询条件
|
||||
if query.StockCode != "" {
|
||||
q.Or("stock_code LIKE ?", "%"+query.StockCode+"%")
|
||||
}
|
||||
if query.StockName != "" {
|
||||
q.Or("stock_name LIKE ?", "%"+query.StockName+"%")
|
||||
}
|
||||
if query.BkCode != "" {
|
||||
q.Or("bk_code LIKE ?", "%"+query.BkCode+"%")
|
||||
}
|
||||
if query.BkName != "" {
|
||||
q.Or("bk_name LIKE ?", "%"+query.BkName+"%")
|
||||
}
|
||||
|
||||
if query.StartDate != "" && query.EndDate != "" {
|
||||
query.StartDate = strutil.ReplaceWithMap(query.StartDate, map[string]string{
|
||||
"T": " ",
|
||||
"Z": "",
|
||||
})
|
||||
query.StartDate = strutil.ReplaceWithMap(query.StartDate, map[string]string{
|
||||
"T": " ",
|
||||
"Z": "",
|
||||
})
|
||||
q = q.Where("data_time BETWEEN ? AND ?", query.StartDate, query.EndDate)
|
||||
}
|
||||
|
||||
// 计算总数
|
||||
err := q.Count(&total).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 设置默认分页参数
|
||||
page := query.Page
|
||||
pageSize := query.PageSize
|
||||
if page <= 0 {
|
||||
page = 1
|
||||
}
|
||||
if pageSize <= 0 || pageSize > 100 {
|
||||
pageSize = 10
|
||||
}
|
||||
|
||||
// 执行分页查询
|
||||
offset := (page - 1) * pageSize
|
||||
err = q.Offset(offset).Limit(pageSize).Order("created_at DESC").Find(&list).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
totalPages := int((total + int64(pageSize) - 1) / int64(pageSize))
|
||||
|
||||
stockCodes := slice.Map(list, func(index int, item models.AiRecommendStocks) string {
|
||||
return ConvertTushareCodeToStockCode(item.StockCode)
|
||||
})
|
||||
stockData, _ := NewStockDataApi().GetStockCodeRealTimeData(stockCodes...)
|
||||
for _, info := range *stockData {
|
||||
for idx, item := range list {
|
||||
if ConvertTushareCodeToStockCode(item.StockCode) == ConvertTushareCodeToStockCode(info.Code) {
|
||||
list[idx].StockCurrentPrice = info.Price
|
||||
list[idx].StockPrePrice = info.PreClose
|
||||
list[idx].StockCurrentPriceTime = info.Date + " " + info.Time
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return &models.AiRecommendStocksPageData{
|
||||
List: list,
|
||||
Total: total,
|
||||
Page: page,
|
||||
PageSize: pageSize,
|
||||
TotalPages: totalPages,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// GetAiRecommendStocksByID 根据ID获取AI推荐股票记录
|
||||
func (s *AiRecommendStocksService) GetAiRecommendStocksByID(id uint) (*models.AiRecommendStocks, error) {
|
||||
var recommend models.AiRecommendStocks
|
||||
err := db.Dao.First(&recommend, id).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &recommend, nil
|
||||
}
|
||||
|
||||
// UpdateAiRecommendStocks 更新AI推荐股票记录
|
||||
func (s *AiRecommendStocksService) UpdateAiRecommendStocks(id uint, recommend *models.AiRecommendStocks) error {
|
||||
result := db.Dao.Model(&models.AiRecommendStocks{}).Where("id = ?", id).Updates(recommend)
|
||||
return result.Error
|
||||
}
|
||||
|
||||
// DeleteAiRecommendStocks 根据ID删除AI推荐股票记录
|
||||
func (s *AiRecommendStocksService) DeleteAiRecommendStocks(id uint) error {
|
||||
// 使用软删除
|
||||
result := db.Dao.Where("id = ?", id).Delete(&models.AiRecommendStocks{})
|
||||
return result.Error
|
||||
}
|
||||
|
||||
// BatchDeleteAiRecommendStocks 批量删除AI推荐股票记录
|
||||
func (s *AiRecommendStocksService) BatchDeleteAiRecommendStocks(ids []uint) error {
|
||||
// 使用软删除
|
||||
result := db.Dao.Where("id IN ?", ids).Delete(&models.AiRecommendStocks{})
|
||||
return result.Error
|
||||
}
|
||||
97
backend/data/ai_response_result_api.go
Normal file
97
backend/data/ai_response_result_api.go
Normal file
@@ -0,0 +1,97 @@
|
||||
package data
|
||||
|
||||
import (
|
||||
"go-stock/backend/db"
|
||||
"go-stock/backend/models"
|
||||
|
||||
"github.com/duke-git/lancet/v2/strutil"
|
||||
)
|
||||
|
||||
type AIResponseResultService struct{}
|
||||
|
||||
func NewAIResponseResultService() *AIResponseResultService {
|
||||
return &AIResponseResultService{}
|
||||
}
|
||||
|
||||
// GetAIResponseResultList 分页查询AI响应结果
|
||||
func (s *AIResponseResultService) GetAIResponseResultList(query models.AIResponseResultQuery) (*models.AIResponseResultPageData, error) {
|
||||
var list []models.AIResponseResult
|
||||
var total int64
|
||||
|
||||
q := db.Dao.Model(&models.AIResponseResult{})
|
||||
|
||||
// 构建查询条件
|
||||
if query.ChatId != "" {
|
||||
q.Where("chat_id LIKE ?", "%"+query.ChatId+"%")
|
||||
}
|
||||
if query.ModelName != "" {
|
||||
q.Or("model_name LIKE ?", "%"+query.ModelName+"%")
|
||||
}
|
||||
if query.StockCode != "" {
|
||||
q.Or("stock_code LIKE ?", "%"+query.StockCode+"%")
|
||||
}
|
||||
if query.Question != "" {
|
||||
q.Or("question LIKE ?", "%"+query.Question+"%")
|
||||
}
|
||||
if query.StartDate != "" && query.EndDate != "" {
|
||||
query.StartDate = strutil.ReplaceWithMap(query.StartDate, map[string]string{
|
||||
"T": " ",
|
||||
"Z": "",
|
||||
})
|
||||
query.StartDate = strutil.ReplaceWithMap(query.StartDate, map[string]string{
|
||||
"T": " ",
|
||||
"Z": "",
|
||||
})
|
||||
q = q.Where("created_at BETWEEN ? AND ?", query.StartDate, query.EndDate)
|
||||
}
|
||||
|
||||
// 计算总数
|
||||
err := q.Count(&total).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 设置默认分页参数
|
||||
page := query.Page
|
||||
pageSize := query.PageSize
|
||||
if page <= 0 {
|
||||
page = 1
|
||||
}
|
||||
if pageSize <= 0 || pageSize > 100 {
|
||||
pageSize = 10
|
||||
}
|
||||
|
||||
// 执行分页查询
|
||||
offset := (page - 1) * pageSize
|
||||
err = q.Offset(offset).Limit(pageSize).Order("created_at DESC").Find(&list).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
totalPages := int((total + int64(pageSize) - 1) / int64(pageSize))
|
||||
|
||||
return &models.AIResponseResultPageData{
|
||||
List: list,
|
||||
Total: total,
|
||||
Page: page,
|
||||
PageSize: pageSize,
|
||||
TotalPages: totalPages,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// DeleteAIResponseResult 根据ID删除AI响应结果
|
||||
func (s *AIResponseResultService) DeleteAIResponseResult(id string) error {
|
||||
|
||||
// 使用软删除
|
||||
result := db.Dao.Where("id = ?", id).Delete(&models.AIResponseResult{})
|
||||
|
||||
return result.Error
|
||||
}
|
||||
|
||||
// BatchDeleteAIResponseResult 批量删除AI响应结果
|
||||
func (s *AIResponseResultService) BatchDeleteAIResponseResult(ids []uint) error {
|
||||
// 使用软删除
|
||||
result := db.Dao.Where("id IN ?", ids).Delete(&models.AIResponseResult{})
|
||||
|
||||
return result.Error
|
||||
}
|
||||
26
backend/data/ai_response_result_api_test.go
Normal file
26
backend/data/ai_response_result_api_test.go
Normal file
@@ -0,0 +1,26 @@
|
||||
package data
|
||||
|
||||
import (
|
||||
"go-stock/backend/db"
|
||||
"go-stock/backend/models"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// @Author spark
|
||||
// @Date 2026/1/23 17:39
|
||||
// @Desc
|
||||
//-----------------------------------------------------------------------------------
|
||||
|
||||
func TestAIResponseResultService_GetAIResponseResultList(t *testing.T) {
|
||||
db.Init("../../data/stock.db")
|
||||
service := NewAIResponseResultService()
|
||||
list, err := service.GetAIResponseResultList(models.AIResponseResultQuery{
|
||||
Page: 1,
|
||||
PageSize: 10,
|
||||
})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
t.Log(list)
|
||||
|
||||
}
|
||||
1
backend/data/data/dict/README.md
Normal file
1
backend/data/data/dict/README.md
Normal file
@@ -0,0 +1 @@
|
||||
Some dict/zh data is from [github.com/fxsjy/jieba](https://github.com/fxsjy/jieba)
|
||||
428
backend/data/data/dict/base.txt
Normal file
428
backend/data/data/dict/base.txt
Normal file
@@ -0,0 +1,428 @@
|
||||
# 金融股票全场景分词字典(最终去重优化版)
|
||||
# 格式:单词 权重 词性 | 权重280-350分,核心术语优先匹配,无重复词汇
|
||||
|
||||
# 一、净买卖与资金流向(核心交易表述)
|
||||
净卖出 340 v
|
||||
净买入 340 v
|
||||
净卖出额 330 n
|
||||
净买入额 330 n
|
||||
净卖出量 330 n
|
||||
净买入量 330 n
|
||||
资金净流出 340 n
|
||||
资金净流入 340 n
|
||||
净额 330 n
|
||||
买卖净额 330 n
|
||||
资金净额 330 n
|
||||
北向资金净买入 330 n
|
||||
北向资金净卖出 330 n
|
||||
南向资金净买入 320 n
|
||||
南向资金净卖出 320 n
|
||||
主力资金净买入 330 n
|
||||
主力资金净卖出 330 n
|
||||
散户资金净买入 320 n
|
||||
散户资金净卖出 320 n
|
||||
机构资金净买入 330 n
|
||||
机构资金净卖出 330 n
|
||||
游资净买入 320 n
|
||||
游资净卖出 320 n
|
||||
大单净买入 320 n
|
||||
大单净卖出 320 n
|
||||
中单净买入 320 n
|
||||
中单净卖出 320 n
|
||||
小单净买入 320 n
|
||||
小单净卖出 320 n
|
||||
净买入占比 320 n
|
||||
净卖出占比 320 n
|
||||
净买入率 320 n
|
||||
净卖出率 320 n
|
||||
连续净买入 320 v
|
||||
连续净卖出 320 v
|
||||
单日净买入 320 n
|
||||
单日净卖出 320 n
|
||||
累计净买入 320 n
|
||||
累计净卖出 320 n
|
||||
净买入创纪录 310 adj
|
||||
净卖出创纪录 310 adj
|
||||
净买入放量 310 v
|
||||
净卖出放量 310 v
|
||||
净买入缩量 310 v
|
||||
净卖出缩量 310 v
|
||||
净多 310 n
|
||||
净空 310 n
|
||||
净多头 310 n
|
||||
净空头 310 n
|
||||
净多头头寸 310 n
|
||||
净空头头寸 310 n
|
||||
跌超 310 n
|
||||
跌逾 310 n
|
||||
|
||||
# 二、金融资讯与市场分析
|
||||
金融资讯 350 n
|
||||
市场快讯 340 n
|
||||
财经新闻 340 n
|
||||
政策解读 330 n
|
||||
市场分析 330 n
|
||||
行业研报 320 n
|
||||
宏观经济 330 n
|
||||
微观层面 310 n
|
||||
基本面 320 n
|
||||
技术面 320 n
|
||||
资金面 320 n
|
||||
政策面 320 n
|
||||
市场情绪 320 n
|
||||
风险偏好 310 n
|
||||
流动性 320 n
|
||||
估值修复 310 n
|
||||
价值投资 310 n
|
||||
趋势投资 310 n
|
||||
波段操作 310 n
|
||||
左侧交易 290 n
|
||||
右侧交易 290 n
|
||||
止损止盈 300 n
|
||||
仓位管理 300 n
|
||||
资产配置 310 n
|
||||
分散投资 290 n
|
||||
集中投资 290 n
|
||||
风险控制 310 n
|
||||
系统性风险 300 n
|
||||
非系统性风险 290 n
|
||||
黑天鹅事件 310 n
|
||||
灰犀牛事件 300 n
|
||||
熔断机制 300 n
|
||||
市场监管 310 n
|
||||
信息披露 310 n
|
||||
内幕交易 300 n
|
||||
操纵市场 300 n
|
||||
亏损 100 n
|
||||
加工 100 n
|
||||
|
||||
# 三、全球主要股指(含中英文缩写)
|
||||
# 中国市场
|
||||
A股 350 n
|
||||
港股 350 n
|
||||
上证指数 350 n
|
||||
深证成指 350 n
|
||||
创业板指 340 n
|
||||
科创板指 330 n
|
||||
北证50 330 n
|
||||
沪深300 350 n
|
||||
沪深300指数 350 n
|
||||
中证500 340 n
|
||||
中证500指数 340 n
|
||||
中证1000 330 n
|
||||
中证1000指数 330 n
|
||||
上证50 340 n
|
||||
上证50指数 340 n
|
||||
科创50 330 n
|
||||
科创50指数 330 n
|
||||
上证综指 350 n
|
||||
富时中国A50指数 340 n
|
||||
恒生指数 340 n
|
||||
恒生科技指数 340 n
|
||||
恒生国企指数 330 n
|
||||
H股指数 330 n
|
||||
# 美洲市场
|
||||
道琼斯工业平均指数 350 n
|
||||
标普500指数 350 n
|
||||
纳斯达克综合指数 340 n
|
||||
纳斯达克100指数 340 n
|
||||
罗素2000指数 320 n
|
||||
标普400中型股指数 310 n
|
||||
标普600小型股指数 310 n
|
||||
纽约证交所综合指数 310 n
|
||||
纳斯达克中国金龙指数 310 n
|
||||
# 欧洲市场
|
||||
德国DAX指数 330 n
|
||||
法国CAC40指数 330 n
|
||||
富时100指数 330 n
|
||||
欧元斯托克50指数 320 n
|
||||
英国富时250指数 310 n
|
||||
意大利富时MIB指数 310 n
|
||||
西班牙IBEX 35指数 310 n
|
||||
# 亚太其他市场
|
||||
日经225指数 330 n
|
||||
日经500指数 310 n
|
||||
韩国综合股价指数 320 n
|
||||
韩国kospi指数 320 n
|
||||
KOSPI 310 n
|
||||
澳洲标普200指数 310 n
|
||||
印度孟买敏感指数 310 n
|
||||
Sensex 300 n
|
||||
印度Nifty 50指数 310 n
|
||||
# 全球综合指数
|
||||
MSCI指数 320 n
|
||||
MSCI全球指数 330 n
|
||||
MSCI新兴市场指数 330 n
|
||||
富时罗素全球指数 320 n
|
||||
摩根大通全球债券指数 310 n
|
||||
全球股指 300 n
|
||||
发达市场指数 300 n
|
||||
新兴市场指数 300 n
|
||||
金砖国家指数 300 n
|
||||
G20国家指数 300 n
|
||||
# 股指衍生工具
|
||||
指数期货 320 n
|
||||
股指期货 320 n
|
||||
富时中国A50指数期货 320 n
|
||||
沪深300股指期货 320 n
|
||||
标普500股指期货 320 n
|
||||
纳斯达克100股指期货 310 n
|
||||
指数成分股 320 n
|
||||
指数权重股 320 n
|
||||
指数涨幅 320 n
|
||||
指数跌幅 320 n
|
||||
指数反弹 310 n
|
||||
指数回调 310 n
|
||||
指数创新高 310 v
|
||||
指数创新低 310 v
|
||||
指数估值 310 n
|
||||
指数市盈率 310 n
|
||||
|
||||
# 四、财务与估值核心指标
|
||||
市盈率 350 n
|
||||
PE 350 n
|
||||
动态市盈率 340 n
|
||||
静态市盈率 340 n
|
||||
滚动市盈率 340 n
|
||||
市净率 350 n
|
||||
PB 350 n
|
||||
市销率 330 n
|
||||
PS 330 n
|
||||
市现率 320 n
|
||||
PCF 320 n
|
||||
净资产收益率 350 n
|
||||
ROE 350 n
|
||||
总资产收益率 330 n
|
||||
ROA 330 n
|
||||
毛利率 340 n
|
||||
净利率 340 n
|
||||
销售净利率 330 n
|
||||
资产负债率 340 n
|
||||
营收 340 n
|
||||
营业收入 340 n
|
||||
净利润 350 n
|
||||
归母净利润 340 n
|
||||
扣非净利润 340 n
|
||||
EPS 330 n
|
||||
每股收益 330 n
|
||||
现金流 340 n
|
||||
经营活动现金流 330 n
|
||||
自由现金流 330 n
|
||||
营收增长率 330 n
|
||||
净利润增长率 330 n
|
||||
股息率 320 n
|
||||
分红率 320 n
|
||||
换手率 330 n
|
||||
成交量 340 n
|
||||
成交额 340 n
|
||||
量比 320 n
|
||||
振幅 320 n
|
||||
|
||||
# 五、政策与宏观经济
|
||||
货币政策 330 n
|
||||
财政政策 330 n
|
||||
稳健货币政策 320 n
|
||||
积极财政政策 320 n
|
||||
宽松政策 320 n
|
||||
紧缩政策 320 n
|
||||
利率 330 n
|
||||
基准利率 320 n
|
||||
LPR 330 n
|
||||
贷款市场报价利率 320 n
|
||||
存款准备金率 320 n
|
||||
MLF 320 n
|
||||
中期借贷便利 310 n
|
||||
逆回购 320 n
|
||||
正回购 310 n
|
||||
汇率 330 n
|
||||
人民币汇率 330 n
|
||||
美元汇率 320 n
|
||||
通胀 320 n
|
||||
CPI 330 n
|
||||
PPI 330 n
|
||||
GDP 330 n
|
||||
国内生产总值 320 n
|
||||
PMI 330 n
|
||||
采购经理人指数 320 n
|
||||
行业政策 320 n
|
||||
产业政策 320 n
|
||||
税收政策 310 n
|
||||
补贴政策 310 n
|
||||
关税 310 n
|
||||
贸易政策 310 n
|
||||
地缘政治 310 n
|
||||
大宗商品 320 n
|
||||
原油价格 310 n
|
||||
黄金价格 310 n
|
||||
有色金属价格 300 n
|
||||
|
||||
# 六、金融产品与机构
|
||||
股票 320 n
|
||||
基金 320 n
|
||||
公募基金 310 n
|
||||
私募基金 310 n
|
||||
ETF 320 n
|
||||
指数基金 310 n
|
||||
混合型基金 300 n
|
||||
股票型基金 310 n
|
||||
债券型基金 300 n
|
||||
货币基金 290 n
|
||||
REITs 310 n
|
||||
可转债 310 n
|
||||
可交换债 300 n
|
||||
期货 310 n
|
||||
股指期货 310 n
|
||||
国债期货 300 n
|
||||
商品期货 300 n
|
||||
期权 300 n
|
||||
融资融券 310 n
|
||||
两融余额 300 n
|
||||
北向资金 320 n
|
||||
南向资金 310 n
|
||||
沪股通 310 n
|
||||
深股通 310 n
|
||||
陆股通 310 n
|
||||
证券公司 310 n
|
||||
券商 320 n
|
||||
基金公司 300 n
|
||||
保险公司 300 n
|
||||
银行 310 n
|
||||
监管机构 310 n
|
||||
证监会 320 n
|
||||
交易所 320 n
|
||||
上交所 320 n
|
||||
深交所 320 n
|
||||
北交所 310 n
|
||||
港交所 310 n
|
||||
社保基金 310 n
|
||||
养老金 300 n
|
||||
QFII 300 n
|
||||
RQFII 290 n
|
||||
北向资金机构 300 n
|
||||
|
||||
# 七、热点概念与行业
|
||||
AI 330 n
|
||||
人工智能 350 n
|
||||
算力 330 n
|
||||
大数据 320 n
|
||||
云计算 320 n
|
||||
半导体 350 n
|
||||
芯片 350 n
|
||||
集成电路 340 n
|
||||
新能源 350 n
|
||||
光伏 340 n
|
||||
锂电 320 n
|
||||
储能 340 n
|
||||
充电桩 310 n
|
||||
新能源车 320 n
|
||||
智能汽车 310 n
|
||||
自动驾驶 330 n
|
||||
军工 310 n
|
||||
国防军工 300 n
|
||||
医药 310 n
|
||||
创新药 310 n
|
||||
医疗器械 300 n
|
||||
CXO 300 n
|
||||
白酒 310 n
|
||||
消费 320 n
|
||||
可选消费 300 n
|
||||
必选消费 300 n
|
||||
食品饮料 310 n
|
||||
家电 300 n
|
||||
地产 300 n
|
||||
房地产 300 n
|
||||
基建 300 n
|
||||
新基建 310 n
|
||||
数字经济 350 n
|
||||
数字货币 310 n
|
||||
区块链 300 n
|
||||
元宇宙 300 n
|
||||
低空经济 340 n
|
||||
人形机器人 330 n
|
||||
工业互联网 330 n
|
||||
物联网 300 n
|
||||
5G 300 n
|
||||
6G 340 n
|
||||
|
||||
# 八、交易操作与行情
|
||||
上涨 310 v
|
||||
下跌 310 v
|
||||
涨停 310 v
|
||||
跌停 310 v
|
||||
反弹 300 v
|
||||
反转 300 v
|
||||
回调 300 v
|
||||
横盘 290 v
|
||||
震荡 290 v
|
||||
跳水 300 v
|
||||
拉升 300 v
|
||||
砸盘 300 v
|
||||
护盘 290 v
|
||||
建仓 300 v
|
||||
加仓 300 v
|
||||
减仓 300 v
|
||||
清仓 300 v
|
||||
平仓 300 v
|
||||
抄底 300 v
|
||||
逃顶 300 v
|
||||
追涨 290 v
|
||||
杀跌 290 v
|
||||
套牢 280 v
|
||||
解套 280 v
|
||||
净流入 300 n
|
||||
净流出 300 n
|
||||
主力资金 300 n
|
||||
资金流入 290 v
|
||||
资金流出 290 v
|
||||
放量 290 v
|
||||
缩量 290 v
|
||||
高换手 290 n
|
||||
低换手 280 n
|
||||
高估值 290 n
|
||||
低估值 290 n
|
||||
超预期 300 v
|
||||
不及预期 300 v
|
||||
符合预期 290 v
|
||||
利好 310 n
|
||||
利空 310 n
|
||||
政策利好 310 n
|
||||
业绩利好 310 n
|
||||
风险警示 300 n
|
||||
涨停板 300 n
|
||||
跌停板 300 n
|
||||
一字涨停 290 n
|
||||
一字跌停 290 n
|
||||
打开涨停 320 v
|
||||
打开跌停 320 v
|
||||
集合竞价 290 n
|
||||
连续竞价 280 n
|
||||
开盘价 340 n
|
||||
收盘价 340 n
|
||||
最高价 330 n
|
||||
最低价 330 n
|
||||
均价 330 n
|
||||
昨日收盘价 320 n
|
||||
涨跌额 330 n
|
||||
涨跌幅 340 n
|
||||
涨幅 340 n
|
||||
跌幅 340 n
|
||||
涨停价 330 n
|
||||
跌停价 330 n
|
||||
熔断 330 n
|
||||
临时停牌 320 n
|
||||
复牌 320 v
|
||||
停牌 320 n
|
||||
量价齐升 320 n
|
||||
量价背离 320 n
|
||||
高开 320 n
|
||||
低开 320 n
|
||||
平开 320 n
|
||||
高走 320 v
|
||||
低走 320 v
|
||||
震荡上行 320 v
|
||||
震荡下行 320 v
|
||||
|
||||
# 九、委托交易与规则
|
||||
限价委托 340 n
|
||||
市价委托 340 n
|
||||
止损委托 330 n
|
||||
0
backend/data/data/dict/en/dict.txt
Normal file
0
backend/data/data/dict/en/dict.txt
Normal file
1
backend/data/data/dict/jp/README.md
Normal file
1
backend/data/data/dict/jp/README.md
Normal file
@@ -0,0 +1 @@
|
||||
dict.txt 通过内部工具生成, Copyright 2017 ego authors. 商用和拷贝请注明来源和版权
|
||||
885298
backend/data/data/dict/jp/dict.txt
Normal file
885298
backend/data/data/dict/jp/dict.txt
Normal file
File diff suppressed because it is too large
Load Diff
185
backend/data/data/dict/user.txt
Normal file
185
backend/data/data/dict/user.txt
Normal file
@@ -0,0 +1,185 @@
|
||||
# 补充:热点概念与板块(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
|
||||
|
||||
# 二、核心热点概念(700分,最高优先级)
|
||||
比特币 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
|
||||
270132
backend/data/data/dict/zh/idf.txt
Normal file
270132
backend/data/data/dict/zh/idf.txt
Normal file
File diff suppressed because it is too large
Load Diff
352279
backend/data/data/dict/zh/s_1.txt
Normal file
352279
backend/data/data/dict/zh/s_1.txt
Normal file
File diff suppressed because it is too large
Load Diff
1161
backend/data/data/dict/zh/stop_tokens.txt
Normal file
1161
backend/data/data/dict/zh/stop_tokens.txt
Normal file
File diff suppressed because it is too large
Load Diff
88
backend/data/data/dict/zh/stop_word.txt
Normal file
88
backend/data/data/dict/zh/stop_word.txt
Normal file
@@ -0,0 +1,88 @@
|
||||
,
|
||||
.
|
||||
?
|
||||
!
|
||||
"
|
||||
@
|
||||
,
|
||||
。
|
||||
、
|
||||
?
|
||||
!
|
||||
:
|
||||
“
|
||||
”
|
||||
;
|
||||
|
||||
(
|
||||
)
|
||||
《
|
||||
》
|
||||
~
|
||||
*
|
||||
<
|
||||
>
|
||||
/
|
||||
\
|
||||
|
|
||||
-
|
||||
_
|
||||
+
|
||||
=
|
||||
&
|
||||
^
|
||||
%
|
||||
#
|
||||
`
|
||||
;
|
||||
$
|
||||
¥
|
||||
‘
|
||||
’
|
||||
〉
|
||||
〈
|
||||
…
|
||||
>
|
||||
<
|
||||
@
|
||||
#
|
||||
$
|
||||
%
|
||||
︿
|
||||
&
|
||||
*
|
||||
+
|
||||
~
|
||||
|
|
||||
[
|
||||
]
|
||||
{
|
||||
}
|
||||
啊
|
||||
阿
|
||||
哎
|
||||
哎呀
|
||||
哎哟
|
||||
唉
|
||||
俺
|
||||
俺们
|
||||
按
|
||||
按照
|
||||
吧
|
||||
吧哒
|
||||
把
|
||||
罢了
|
||||
被
|
||||
本
|
||||
本着
|
||||
比
|
||||
比方
|
||||
比如
|
||||
鄙人
|
||||
彼
|
||||
彼此
|
||||
边
|
||||
别
|
||||
别的
|
||||
别说
|
||||
并
|
||||
236754
backend/data/data/dict/zh/t_1.txt
Normal file
236754
backend/data/data/dict/zh/t_1.txt
Normal file
File diff suppressed because it is too large
Load Diff
@@ -8,6 +8,7 @@ import (
|
||||
"go-stock/backend/logger"
|
||||
"go-stock/backend/models"
|
||||
"go-stock/backend/util"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -33,6 +34,74 @@ func NewMarketNewsApi() *MarketNewsApi {
|
||||
return &MarketNewsApi{}
|
||||
}
|
||||
|
||||
func (m MarketNewsApi) TelegraphList(crawlTimeOut int64) *[]models.Telegraph {
|
||||
//https://www.cls.cn/nodeapi/telegraphList
|
||||
url := "https://www.cls.cn/nodeapi/telegraphList"
|
||||
res := map[string]any{}
|
||||
_, _ = resty.New().SetTimeout(time.Duration(crawlTimeOut)*time.Second).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").
|
||||
SetResult(&res).
|
||||
Get(url)
|
||||
var telegraphs []models.Telegraph
|
||||
|
||||
if v, _ := convertor.ToInt(res["error"]); v == 0 {
|
||||
if res["data"] == nil {
|
||||
return m.GetNewTelegraph(30)
|
||||
}
|
||||
data := res["data"].(map[string]any)
|
||||
rollData := data["roll_data"].([]any)
|
||||
for _, v := range rollData {
|
||||
news := v.(map[string]any)
|
||||
ctime, _ := convertor.ToInt(news["ctime"])
|
||||
dataTime := time.Unix(ctime, 0).Local()
|
||||
telegraph := models.Telegraph{
|
||||
Title: news["title"].(string),
|
||||
Content: news["content"].(string),
|
||||
Time: dataTime.Format("15:04:05"),
|
||||
DataTime: &dataTime,
|
||||
Url: news["shareurl"].(string),
|
||||
Source: "财联社电报",
|
||||
IsRed: (news["level"].(string)) != "C",
|
||||
SentimentResult: AnalyzeSentiment(news["content"].(string)).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 {
|
||||
continue
|
||||
}
|
||||
telegraphs = append(telegraphs, telegraph)
|
||||
db.Dao.Model(&models.Telegraph{}).Create(&telegraph)
|
||||
logger.SugaredLogger.Debugf("telegraph: %+v", &telegraph)
|
||||
if news["subjects"] == nil {
|
||||
continue
|
||||
}
|
||||
subjects := news["subjects"].([]any)
|
||||
for _, subject := range subjects {
|
||||
name := subject.(map[string]any)["subject_name"].(string)
|
||||
tag := &models.Tags{
|
||||
Name: name,
|
||||
Type: "subject",
|
||||
}
|
||||
db.Dao.Model(tag).Where("name=? and type=?", name, "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,
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
//db.Dao.Model(&models.Telegraph{}).Create(&telegraphs)
|
||||
//logger.SugaredLogger.Debugf("telegraphs: %+v", &telegraphs)
|
||||
}
|
||||
|
||||
return &telegraphs
|
||||
}
|
||||
|
||||
func (m MarketNewsApi) GetNewTelegraph(crawlTimeOut int64) *[]models.Telegraph {
|
||||
url := "https://www.cls.cn/telegraph"
|
||||
response, _ := resty.New().SetTimeout(time.Duration(crawlTimeOut)*time.Second).R().
|
||||
@@ -77,7 +146,7 @@ func (m MarketNewsApi) GetNewTelegraph(crawlTimeOut int64) *[]models.Telegraph {
|
||||
if telegraph.Content != "" {
|
||||
telegraph.SentimentResult = AnalyzeSentiment(telegraph.Content).Description
|
||||
cnt := int64(0)
|
||||
db.Dao.Model(telegraph).Where("time=? and source=?", telegraph.Time, telegraph.Source).Count(&cnt)
|
||||
db.Dao.Model(telegraph).Where("time=? and content=?", telegraph.Time, telegraph.Content).Count(&cnt)
|
||||
if cnt == 0 {
|
||||
db.Dao.Create(&telegraph)
|
||||
telegraphs = append(telegraphs, telegraph)
|
||||
@@ -100,9 +169,9 @@ func (m MarketNewsApi) GetNewTelegraph(crawlTimeOut int64) *[]models.Telegraph {
|
||||
func (m MarketNewsApi) GetNewsList(source string, limit int) *[]*models.Telegraph {
|
||||
news := &[]*models.Telegraph{}
|
||||
if source != "" {
|
||||
db.Dao.Model(news).Preload("TelegraphTags").Where("source=?", source).Order("id desc").Limit(limit).Find(news)
|
||||
db.Dao.Model(news).Preload("TelegraphTags").Where("source=?", source).Order("data_time desc,time desc").Limit(limit).Find(news)
|
||||
} else {
|
||||
db.Dao.Model(news).Preload("TelegraphTags").Order("id desc").Limit(limit).Find(news)
|
||||
db.Dao.Model(news).Preload("TelegraphTags").Order("data_time desc,time desc").Limit(limit).Find(news)
|
||||
}
|
||||
for _, item := range *news {
|
||||
tags := &[]models.Tags{}
|
||||
@@ -117,12 +186,57 @@ func (m MarketNewsApi) GetNewsList(source string, limit int) *[]*models.Telegrap
|
||||
}
|
||||
return news
|
||||
}
|
||||
func (m MarketNewsApi) GetNewsList2(source string, limit int) *[]*models.Telegraph {
|
||||
NewMarketNewsApi().TelegraphList(30)
|
||||
news := &[]*models.Telegraph{}
|
||||
if source != "" {
|
||||
db.Dao.Model(news).Preload("TelegraphTags").Where("source=?", source).Order("data_time desc,is_red desc").Limit(limit).Find(news)
|
||||
} else {
|
||||
db.Dao.Model(news).Preload("TelegraphTags").Order("data_time desc,is_red desc").Limit(limit).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) GetTelegraphList(source string) *[]*models.Telegraph {
|
||||
news := &[]*models.Telegraph{}
|
||||
if source != "" {
|
||||
db.Dao.Model(news).Preload("TelegraphTags").Where("source=?", source).Order("id desc").Limit(20).Find(news)
|
||||
db.Dao.Model(news).Preload("TelegraphTags").Where("source=?", source).Order("data_time desc,time desc").Limit(50).Find(news)
|
||||
} else {
|
||||
db.Dao.Model(news).Preload("TelegraphTags").Order("id desc").Limit(20).Find(news)
|
||||
db.Dao.Model(news).Preload("TelegraphTags").Order("data_time desc,time desc").Limit(50).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) 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{}
|
||||
@@ -178,7 +292,12 @@ 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 {
|
||||
telegraph.DataTime = &dataTime
|
||||
}
|
||||
tags := data["tag"].([]any)
|
||||
telegraph.SubjectTags = lo.Map(tags, func(tagItem any, index int) string {
|
||||
name := tagItem.(map[string]any)["name"].(string)
|
||||
@@ -197,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("time=? and source=?", telegraph.Time, 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)
|
||||
@@ -551,15 +674,19 @@ func (m MarketNewsApi) EMDictCode(code string, cache *freecache.Cache) []any {
|
||||
return respMap["data"].([]any)
|
||||
}
|
||||
|
||||
func (m MarketNewsApi) TradingViewNews() *[]models.TVNews {
|
||||
func (m MarketNewsApi) TradingViewNews() *[]models.Telegraph {
|
||||
client := resty.New()
|
||||
config := GetSettingConfig()
|
||||
if config.HttpProxyEnabled && config.HttpProxy != "" {
|
||||
client.SetProxy(config.HttpProxy)
|
||||
}
|
||||
TVNews := &[]models.TVNews{}
|
||||
url := "https://news-mediator.tradingview.com/news-flow/v2/news?filter=lang:zh-Hans&filter=provider:panews,reuters&client=screener&streaming=false"
|
||||
resp, err := client.SetTimeout(time.Duration(5)*time.Second).R().
|
||||
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"
|
||||
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/").
|
||||
@@ -567,19 +694,85 @@ func (m MarketNewsApi) TradingViewNews() *[]models.TVNews {
|
||||
Get(url)
|
||||
if err != nil {
|
||||
logger.SugaredLogger.Errorf("TradingViewNews err:%s", err.Error())
|
||||
return TVNews
|
||||
return news
|
||||
}
|
||||
respMap := map[string]any{}
|
||||
err = json.Unmarshal(resp.Body(), &respMap)
|
||||
if err != nil {
|
||||
return TVNews
|
||||
return news
|
||||
}
|
||||
items, err := json.Marshal(respMap["items"])
|
||||
if err != nil {
|
||||
return TVNews
|
||||
return news
|
||||
}
|
||||
json.Unmarshal(items, TVNews)
|
||||
return TVNews
|
||||
|
||||
for i, a := range *TVNews {
|
||||
if i > 10 {
|
||||
break
|
||||
}
|
||||
detail := NewMarketNewsApi().TradingViewNewsDetail(a.Id)
|
||||
dataTime := time.Unix(int64(a.Published), 0).Local()
|
||||
description := ""
|
||||
sentimentResult := ""
|
||||
if detail != nil {
|
||||
description = detail.ShortDescription
|
||||
sentimentResult = AnalyzeSentiment(description).Description
|
||||
}
|
||||
if a.Title == "" {
|
||||
continue
|
||||
}
|
||||
telegraph := &models.Telegraph{
|
||||
Title: a.Title,
|
||||
Content: description,
|
||||
DataTime: &dataTime,
|
||||
IsRed: false,
|
||||
Time: dataTime.Format("15:04:05"),
|
||||
Source: "外媒",
|
||||
Url: fmt.Sprintf("https://cn.tradingview.com/news/%s", a.Id),
|
||||
SentimentResult: sentimentResult,
|
||||
}
|
||||
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 {
|
||||
continue
|
||||
}
|
||||
db.Dao.Model(&models.Telegraph{}).Where("time=? and title=? and source=?", telegraph.Time, telegraph.Title, "外媒").FirstOrCreate(&telegraph)
|
||||
*news = append(*news, *telegraph)
|
||||
}
|
||||
return news
|
||||
}
|
||||
func (m MarketNewsApi) TradingViewNewsDetail(id string) *models.TVNewsDetail {
|
||||
//https://news-headlines.tradingview.com/v3/story?id=panews%3A9be7cf057e3f9%3A0&lang=zh-Hans
|
||||
newsDetail := &models.TVNewsDetail{}
|
||||
newsUrl := fmt.Sprintf("https://news-headlines.tradingview.com/v3/story?id=%s&lang=zh-Hans", url.QueryEscape(id))
|
||||
|
||||
client := resty.New()
|
||||
config := GetSettingConfig()
|
||||
if config.HttpProxyEnabled && config.HttpProxy != "" {
|
||||
client.SetProxy(config.HttpProxy)
|
||||
}
|
||||
request := client.SetTimeout(time.Duration(3) * time.Second).R()
|
||||
_, err := request.
|
||||
SetHeader("Host", "news-headlines.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:146.0) Gecko/20100101 Firefox/146.0").
|
||||
//SetHeader("TE", "trailers").
|
||||
//SetHeader("Priority", "u=4").
|
||||
//SetHeader("Connection", "keep-alive").
|
||||
SetResult(newsDetail).
|
||||
Get(newsUrl)
|
||||
if err != nil {
|
||||
logger.SugaredLogger.Errorf("TradingViewNewsDetail err:%s", err.Error())
|
||||
return newsDetail
|
||||
}
|
||||
logger.SugaredLogger.Infof("resp:%+v", newsDetail)
|
||||
return newsDetail
|
||||
}
|
||||
|
||||
func (m MarketNewsApi) XUEQIUHotStock(size int, marketType string) *[]models.HotItem {
|
||||
@@ -872,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").
|
||||
@@ -938,3 +1132,33 @@ func (m MarketNewsApi) CailianpressWeb(searchWords string) *models.CailianpressW
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
func (m MarketNewsApi) GetNews24HoursList(source string, limit int) *[]*models.Telegraph {
|
||||
news := &[]*models.Telegraph{}
|
||||
if source != "" {
|
||||
db.Dao.Model(news).Preload("TelegraphTags").Where("source=? and created_at>?", source, time.Now().Add(-24*time.Hour)).Order("data_time desc,is_red desc").Limit(limit).Find(news)
|
||||
} else {
|
||||
db.Dao.Model(news).Preload("TelegraphTags").Where("created_at>?", time.Now().Add(-24*time.Hour)).Order("data_time desc,is_red desc").Limit(limit).Find(news)
|
||||
}
|
||||
// 内容去重
|
||||
uniqueNews := make([]*models.Telegraph, 0)
|
||||
seenContent := make(map[string]bool)
|
||||
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)
|
||||
// 使用内容作为去重键值,可以考虑只使用内容的前几个字符或哈希值
|
||||
contentKey := strings.TrimSpace(item.Content)
|
||||
if contentKey != "" && !seenContent[contentKey] {
|
||||
seenContent[contentKey] = true
|
||||
uniqueNews = append(uniqueNews, item)
|
||||
}
|
||||
}
|
||||
return &uniqueNews
|
||||
}
|
||||
|
||||
@@ -6,10 +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"
|
||||
)
|
||||
|
||||
@@ -20,7 +24,12 @@ import (
|
||||
|
||||
func TestGetSinaNews(t *testing.T) {
|
||||
db.Init("../../data/stock.db")
|
||||
NewMarketNewsApi().GetSinaNews(30)
|
||||
InitAnalyzeSentiment()
|
||||
news := NewMarketNewsApi().GetSinaNews(30)
|
||||
for i, telegraph := range *news {
|
||||
logger.SugaredLogger.Debugf("key: %+v, value: %+v", i, telegraph)
|
||||
|
||||
}
|
||||
//NewMarketNewsApi().GetNewTelegraph(30)
|
||||
|
||||
}
|
||||
@@ -38,7 +47,6 @@ func TestGetIndustryRank(t *testing.T) {
|
||||
res := NewMarketNewsApi().GetIndustryRank("0", 10)
|
||||
for s, a := range res["data"].([]any) {
|
||||
logger.SugaredLogger.Debugf("key: %+v, value: %+v", s, a)
|
||||
|
||||
}
|
||||
}
|
||||
func TestGetIndustryMoneyRankSina(t *testing.T) {
|
||||
@@ -84,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))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -122,10 +131,8 @@ func TestEMDictCode(t *testing.T) {
|
||||
|
||||
func TestTradingViewNews(t *testing.T) {
|
||||
db.Init("../../data/stock.db")
|
||||
resp := NewMarketNewsApi().TradingViewNews()
|
||||
for _, a := range *resp {
|
||||
logger.SugaredLogger.Debugf("value: %+v", a)
|
||||
}
|
||||
InitAnalyzeSentiment()
|
||||
NewMarketNewsApi().TradingViewNews()
|
||||
}
|
||||
|
||||
func TestXUEQIUHotStock(t *testing.T) {
|
||||
@@ -135,6 +142,8 @@ func TestXUEQIUHotStock(t *testing.T) {
|
||||
logger.SugaredLogger.Debugf("value: %+v", a)
|
||||
}
|
||||
|
||||
md := util.MarkdownTableWithTitle("当前热门股票排名", res)
|
||||
logger.SugaredLogger.Debugf(md)
|
||||
}
|
||||
|
||||
func TestHotEvent(t *testing.T) {
|
||||
@@ -228,9 +237,60 @@ func TestReutersNew(t *testing.T) {
|
||||
|
||||
func TestInteractiveAnswer(t *testing.T) {
|
||||
db.Init("../../data/stock.db")
|
||||
datas := NewMarketNewsApi().InteractiveAnswer(1, 100, "")
|
||||
datas := NewMarketNewsApi().InteractiveAnswer(1, 100, "立讯精密")
|
||||
logger.SugaredLogger.Debugf("PageSize:%d", datas.PageSize)
|
||||
md := util.MarkdownTableWithTitle("投资互动", datas.Results)
|
||||
logger.SugaredLogger.Debugf(md)
|
||||
|
||||
}
|
||||
func TestGetNewsList2(t *testing.T) {
|
||||
db.Init("../../data/stock.db")
|
||||
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.Debugf("value: %s", messageText.String())
|
||||
}
|
||||
|
||||
func TestTelegraphList(t *testing.T) {
|
||||
db.Init("../../data/stock.db")
|
||||
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/"))
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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(), 11)
|
||||
//res := ai.NewChatStream("长电科技", "sh600584", "长电科技分析和总结", nil)
|
||||
res := ai.NewSummaryStockNewsStreamWithTools("总结市场资讯,发掘潜力标的/行业/板块/概念,控制风险。调用工具函数验证", nil, tools)
|
||||
res := ai.NewSummaryStockNewsStreamWithTools("总结市场资讯,发掘潜力标的/行业/板块/概念,控制风险。调用工具函数验证", nil, tools, false)
|
||||
|
||||
for {
|
||||
select {
|
||||
@@ -64,6 +65,6 @@ func TestSearchGuShiTongStockInfo(t *testing.T) {
|
||||
|
||||
func TestGetZSInfo(t *testing.T) {
|
||||
db.Init("../../data/stock.db")
|
||||
GetZSInfo("中证银行", "sz399986", 30)
|
||||
GetZSInfo("上海贝岭", "sh600171", 30)
|
||||
GetZSInfo("中证银行", "sz399986", 5)
|
||||
GetZSInfo("上海贝岭", "sh600171", 5)
|
||||
}
|
||||
|
||||
@@ -3,9 +3,13 @@ package data
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/go-resty/resty/v2"
|
||||
"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"
|
||||
)
|
||||
|
||||
// @Author spark
|
||||
@@ -20,33 +24,88 @@ func NewSearchStockApi(words string) *SearchStockApi {
|
||||
return &SearchStockApi{words: words}
|
||||
}
|
||||
func (s SearchStockApi) SearchStock(pageSize int) map[string]any {
|
||||
qgqpBId := NewSettingsApi().Config.QgqpBId
|
||||
if qgqpBId == "" {
|
||||
return map[string]any{
|
||||
"code": -1,
|
||||
"message": "请先获取东财用户标识(qgqp_b_id):打开浏览器,访问东财网站,按F12打开开发人员工具-》网络面板,随便点开一个请求,复制请求cookie中qgqp_b_id对应的值。保存到设置中的东财唯一标识输入框",
|
||||
}
|
||||
}
|
||||
url := "https://np-tjxg-g.eastmoney.com/api/smart-tag/stock/v3/pw/search-code"
|
||||
resp, err := resty.New().SetTimeout(time.Duration(30)*time.Second).R().
|
||||
SetHeader("Host", "np-tjxg-g.eastmoney.com").
|
||||
SetHeader("Origin", "https://xuangu.eastmoney.com").
|
||||
SetHeader("Referer", "https://xuangu.eastmoney.com/").
|
||||
SetHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:140.0) Gecko/20100101 Firefox/140.0").
|
||||
SetHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:145.0) Gecko/20100101 Firefox/145.0").
|
||||
SetHeader("Content-Type", "application/json").
|
||||
SetBody(fmt.Sprintf(`{
|
||||
"keyWord": "%s",
|
||||
"pageSize": %d,
|
||||
"pageNo": 1,
|
||||
"fingerprint": "e38b5faabf9378c8238e57219f0ebc9b",
|
||||
"fingerprint": "%s",
|
||||
"gids": [],
|
||||
"matchWord": "",
|
||||
"timestamp": "1751113883290349",
|
||||
"timestamp": "%d",
|
||||
"shareToGuba": false,
|
||||
"requestId": "8xTWgCDAjvQ5lmvz5mDA3Ydk2AE4yoiJ1751113883290",
|
||||
"requestId": "",
|
||||
"needCorrect": true,
|
||||
"removedConditionIdList": [],
|
||||
"xcId": "xc0af28549ab330013ed",
|
||||
"xcId": "",
|
||||
"ownSelectAll": false,
|
||||
"dxInfo": [],
|
||||
"extraCondition": ""
|
||||
}`, s.words, pageSize)).Post(url)
|
||||
}`, s.words, pageSize, qgqpBId, time.Now().Unix())).Post(url)
|
||||
if err != nil {
|
||||
logger.SugaredLogger.Errorf("SearchStock-err:%+v", err)
|
||||
return map[string]any{}
|
||||
return map[string]any{
|
||||
"code": -1,
|
||||
"message": err.Error(),
|
||||
}
|
||||
}
|
||||
respMap := map[string]any{}
|
||||
json.Unmarshal(resp.Body(), &respMap)
|
||||
//logger.SugaredLogger.Infof("resp:%+v", respMap["data"])
|
||||
return respMap
|
||||
}
|
||||
|
||||
func (s SearchStockApi) SearchBk(pageSize int) map[string]any {
|
||||
url := "https://np-tjxg-b.eastmoney.com/api/smart-tag/bkc/v3/pw/search-code"
|
||||
qgqpBId := NewSettingsApi().Config.QgqpBId
|
||||
if qgqpBId == "" {
|
||||
return map[string]any{
|
||||
"code": -1,
|
||||
"message": "请先获取东财用户标识(qgqp_b_id):打开浏览器,访问东财网站,按F12打开开发人员工具-》网络面板,随便点开一个请求,复制请求cookie中qgqp_b_id对应的值。保存到设置中的东财唯一标识输入框",
|
||||
}
|
||||
}
|
||||
resp, err := resty.New().SetTimeout(time.Duration(30)*time.Second).R().
|
||||
SetHeader("Host", "np-tjxg-g.eastmoney.com").
|
||||
SetHeader("Origin", "https://xuangu.eastmoney.com").
|
||||
SetHeader("Referer", "https://xuangu.eastmoney.com/").
|
||||
SetHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:145.0) Gecko/20100101 Firefox/145.0").
|
||||
SetHeader("Content-Type", "application/json").
|
||||
SetBody(fmt.Sprintf(`{
|
||||
"keyWord": "%s",
|
||||
"pageSize": %d,
|
||||
"pageNo": 1,
|
||||
"fingerprint": "%s",
|
||||
"gids": [],
|
||||
"matchWord": "",
|
||||
"timestamp": "%d",
|
||||
"shareToGuba": false,
|
||||
"requestId": "",
|
||||
"needCorrect": true,
|
||||
"removedConditionIdList": [],
|
||||
"xcId": "",
|
||||
"ownSelectAll": false,
|
||||
"dxInfo": [],
|
||||
"extraCondition": ""
|
||||
}`, s.words, pageSize, qgqpBId, time.Now().Unix())).Post(url)
|
||||
if err != nil {
|
||||
logger.SugaredLogger.Errorf("SearchStock-err:%+v", err)
|
||||
return map[string]any{
|
||||
"code": -1,
|
||||
"message": err.Error(),
|
||||
}
|
||||
}
|
||||
respMap := map[string]any{}
|
||||
json.Unmarshal(resp.Body(), &respMap)
|
||||
@@ -70,3 +129,35 @@ 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
|
||||
}
|
||||
|
||||
func (s SearchStockApi) StrategySquare() map[string]any {
|
||||
//https://backtest.10jqka.com.cn/strategysquare/list?order=desc&page=1&pageNum=10&sortType=hot&keyword=
|
||||
url := "https://backtest.10jqka.com.cn/strategysquare/list?order=desc&page=1&pageNum=10&sortType=hot&keyword="
|
||||
resp, err := resty.New().SetTimeout(time.Duration(30)*time.Second).R().
|
||||
SetHeader("Host", "backtest.10jqka.com.cn").
|
||||
SetHeader("Origin", "https://backtest.10jqka.com.cn").
|
||||
SetHeader("Referer", "https://backtest.10jqka.com.cn/strategysquare/list").
|
||||
SetHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:140.0) Gecko/20100101 Firefox/140.0").
|
||||
Get(url)
|
||||
if err != nil {
|
||||
logger.SugaredLogger.Errorf("StrategySquare-err:%+v", err)
|
||||
return map[string]any{}
|
||||
}
|
||||
respMap := map[string]any{}
|
||||
json.Unmarshal(resp.Body(), &respMap)
|
||||
logger.SugaredLogger.Infof("resp:%+v", respMap["data"])
|
||||
return respMap
|
||||
}
|
||||
|
||||
@@ -2,16 +2,31 @@ package data
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/duke-git/lancet/v2/convertor"
|
||||
"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"
|
||||
)
|
||||
|
||||
func TestSearchStock(t *testing.T) {
|
||||
db.Init("../../data/stock.db")
|
||||
|
||||
res := NewSearchStockApi("算力股;净利润连续3年增长").SearchStock(10)
|
||||
e := convertor.ToString(math.Floor(float64(9*random.RandFloat(0, 1, 12) + 1)))
|
||||
for i := 0; i < 19; i++ {
|
||||
e += convertor.ToString(math.Floor(float64(9 * random.RandFloat(0, 1, 12))))
|
||||
}
|
||||
logger.SugaredLogger.Infof("e:%s", e)
|
||||
|
||||
//res := NewSearchStockApi("量比大于2,基本面优秀,2025年三季报已披露,主力连续3日净流入,非创业板非科创板非ST").SearchStock(20)
|
||||
res := NewSearchStockApi("今日涨幅前5的概念板块").SearchBk(50)
|
||||
|
||||
logger.SugaredLogger.Infof("res:%+v", res)
|
||||
data := res["data"].(map[string]any)
|
||||
result := data["result"].(map[string]any)
|
||||
dataList := result["dataList"].([]any)
|
||||
@@ -50,10 +65,25 @@ 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)
|
||||
//}
|
||||
}
|
||||
func TestSearchStockApi_HotStrategyTable(t *testing.T) {
|
||||
db.Init("../../data/stock.db")
|
||||
res := NewSearchStockApi("").StrategySquare()
|
||||
logger.SugaredLogger.Infof("res:%+v", res)
|
||||
}
|
||||
|
||||
@@ -3,11 +3,12 @@ package data
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"github.com/samber/lo"
|
||||
"go-stock/backend/db"
|
||||
"go-stock/backend/logger"
|
||||
"gorm.io/gorm"
|
||||
"time"
|
||||
|
||||
"github.com/samber/lo"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type Settings struct {
|
||||
@@ -35,6 +36,8 @@ type Settings struct {
|
||||
SponsorCode string `json:"sponsorCode"`
|
||||
HttpProxy string `json:"httpProxy"`
|
||||
HttpProxyEnabled bool `json:"httpProxyEnabled"`
|
||||
EnableAgent bool `json:"enableAgent"`
|
||||
QgqpBId string `json:"qgqpBId" gorm:"column:qgqp_b_id"`
|
||||
}
|
||||
|
||||
func (receiver Settings) TableName() string {
|
||||
@@ -42,16 +45,18 @@ func (receiver Settings) TableName() string {
|
||||
}
|
||||
|
||||
type AIConfig struct {
|
||||
ID uint `gorm:"primarykey"`
|
||||
CreatedAt time.Time
|
||||
UpdatedAt time.Time
|
||||
Name string `json:"name"`
|
||||
BaseUrl string `json:"baseUrl"`
|
||||
ApiKey string `json:"apiKey" `
|
||||
ModelName string `json:"modelName"`
|
||||
MaxTokens int `json:"maxTokens"`
|
||||
Temperature float64 `json:"temperature"`
|
||||
TimeOut int `json:"timeOut"`
|
||||
ID uint `gorm:"primarykey"`
|
||||
CreatedAt time.Time
|
||||
UpdatedAt time.Time
|
||||
Name string `json:"name"`
|
||||
BaseUrl string `json:"baseUrl"`
|
||||
ApiKey string `json:"apiKey" `
|
||||
ModelName string `json:"modelName"`
|
||||
MaxTokens int `json:"maxTokens"`
|
||||
Temperature float64 `json:"temperature"`
|
||||
TimeOut int `json:"timeOut"`
|
||||
HttpProxy string `json:"httpProxy"`
|
||||
HttpProxyEnabled bool `json:"httpProxyEnabled"`
|
||||
}
|
||||
|
||||
func (AIConfig) TableName() string {
|
||||
@@ -105,6 +110,8 @@ func UpdateConfig(s *SettingConfig) string {
|
||||
"sponsor_code": s.SponsorCode,
|
||||
"http_proxy": s.HttpProxy,
|
||||
"http_proxy_enabled": s.HttpProxyEnabled,
|
||||
"enable_agent": s.EnableAgent,
|
||||
"qgqp_b_id": s.QgqpBId,
|
||||
})
|
||||
|
||||
//更新AiConfig
|
||||
@@ -158,13 +165,15 @@ func updateAiConfigs(aiConfigs []*AIConfig) error {
|
||||
} else {
|
||||
notDeleteIds = append(notDeleteIds, item.ID)
|
||||
e = db.Dao.Model(&AIConfig{}).Where("id=?", item.ID).Updates(map[string]interface{}{
|
||||
"name": item.Name,
|
||||
"base_url": item.BaseUrl,
|
||||
"api_key": item.ApiKey,
|
||||
"model_name": item.ModelName,
|
||||
"max_tokens": item.MaxTokens,
|
||||
"temperature": item.Temperature,
|
||||
"time_out": item.TimeOut,
|
||||
"name": item.Name,
|
||||
"base_url": item.BaseUrl,
|
||||
"api_key": item.ApiKey,
|
||||
"model_name": item.ModelName,
|
||||
"max_tokens": item.MaxTokens,
|
||||
"temperature": item.Temperature,
|
||||
"time_out": item.TimeOut,
|
||||
"http_proxy": item.HttpProxy,
|
||||
"http_proxy_enabled": item.HttpProxyEnabled,
|
||||
}).Error
|
||||
if e != nil {
|
||||
return
|
||||
@@ -193,12 +202,6 @@ func GetSettingConfig() *SettingConfig {
|
||||
aiConfigs := make([]*AIConfig, 0)
|
||||
// 处理数据库查询可能返回的空结果
|
||||
result := db.Dao.Model(&Settings{}).First(settings)
|
||||
if errors.Is(result.Error, gorm.ErrRecordNotFound) {
|
||||
// 初始化默认设置并保存到数据库
|
||||
settings = &Settings{OpenAiEnable: false, CrawlTimeOut: 60}
|
||||
db.Dao.Create(settings)
|
||||
}
|
||||
|
||||
if settings.OpenAiEnable {
|
||||
// 处理AI配置查询可能出现的错误
|
||||
result = db.Dao.Model(&AIConfig{}).Find(&aiConfigs)
|
||||
@@ -215,7 +218,7 @@ func GetSettingConfig() *SettingConfig {
|
||||
settings.CrawlTimeOut = 60
|
||||
}
|
||||
if settings.KDays < 30 {
|
||||
settings.KDays = 120
|
||||
settings.KDays = 60
|
||||
}
|
||||
}
|
||||
if settings.BrowserPath == "" {
|
||||
|
||||
@@ -9,6 +9,15 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"go-stock/backend/db"
|
||||
"go-stock/backend/logger"
|
||||
"go-stock/backend/models"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
url2 "net/url"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/PuerkitoBio/goquery"
|
||||
"github.com/chromedp/chromedp"
|
||||
"github.com/duke-git/lancet/v2/convertor"
|
||||
@@ -17,17 +26,10 @@ import (
|
||||
"github.com/go-resty/resty/v2"
|
||||
"github.com/robertkrimen/otto"
|
||||
"github.com/samber/lo"
|
||||
"go-stock/backend/db"
|
||||
"go-stock/backend/logger"
|
||||
"go-stock/backend/models"
|
||||
"golang.org/x/text/encoding/simplifiedchinese"
|
||||
"golang.org/x/text/transform"
|
||||
"gorm.io/gorm"
|
||||
"gorm.io/plugin/soft_delete"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
const sinaStockUrl = "http://hq.sinajs.cn/rn=%d&list=%s"
|
||||
@@ -1202,7 +1204,7 @@ func GetZSInfo(name, stockCode string, crawlTimeOut int64) string {
|
||||
price := strutil.RemoveWhiteSpace(document.Find("div#price").First().Text(), false)
|
||||
hqTime := strutil.RemoveWhiteSpace(document.Find("div#hqTime").First().Text(), false)
|
||||
|
||||
if strutil.ContainsAny(price, []string{"-", "--", ""}) {
|
||||
if strutil.ContainsAny(price, []string{"-", "--"}) {
|
||||
return "暂无数据"
|
||||
}
|
||||
|
||||
@@ -1483,11 +1485,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())
|
||||
@@ -1744,6 +1746,70 @@ func (receiver StockDataApi) GetCommonKLineData(stockCode string, kLineType stri
|
||||
return K
|
||||
}
|
||||
|
||||
// GetStockHistoryMoneyData 获取股票历史资金流向数据
|
||||
func (receiver StockDataApi) GetStockHistoryMoneyData() {
|
||||
|
||||
}
|
||||
|
||||
// GetStockMoneyData 获取个股资金流数据
|
||||
func (receiver StockDataApi) GetStockMoneyData() models.StockMoneyDataResp {
|
||||
var resData models.StockMoneyDataResp
|
||||
url := "https://push2.eastmoney.com/api/qt/clist/get?cb=data&fid=f62&po=1&pz=50&pn=1&np=1&fltt=2&invt=2&ut=8dec03ba335b81bf4ebdf7b29ec27d15&fs=m:0+t:6+f:!2,m:0+t:13+f:!2,m:0+t:80+f:!2,m:1+t:2+f:!2,m:1+t:23+f:!2,m:0+t:7+f:!2,m:1+t:3+f:!2&fields=f12,f14,f2,f3,f62,f184,f66,f69,f72,f75,f78,f81,f84,f87,f204,f205,f124,f1,f13,f100,f265"
|
||||
resp, err := receiver.client.SetTimeout(time.Duration(receiver.config.CrawlTimeOut)*time.Second).R().
|
||||
SetHeader("Host", "push2.eastmoney.com").
|
||||
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").
|
||||
Get(url)
|
||||
if err != nil {
|
||||
logger.SugaredLogger.Errorf("err:%s", err.Error())
|
||||
}
|
||||
body := string(resp.Body())
|
||||
logger.SugaredLogger.Infof("resp:%s", body)
|
||||
vm := otto.New()
|
||||
vm.Run("function data(res){return res};")
|
||||
val, err := vm.Run(body)
|
||||
if err != nil {
|
||||
logger.SugaredLogger.Errorf("err:%s", err.Error())
|
||||
}
|
||||
value, err := val.Export()
|
||||
if err != nil {
|
||||
logger.SugaredLogger.Errorf("err:%s", err.Error())
|
||||
}
|
||||
marshal, err := json.Marshal(value)
|
||||
if err != nil {
|
||||
logger.SugaredLogger.Errorf("err:%s", err.Error())
|
||||
return models.StockMoneyDataResp{}
|
||||
}
|
||||
err = json.Unmarshal(marshal, &resData)
|
||||
if err != nil {
|
||||
logger.SugaredLogger.Errorf("err:%s", err.Error())
|
||||
return models.StockMoneyDataResp{}
|
||||
}
|
||||
return resData
|
||||
}
|
||||
|
||||
// 获取股票概念题材信息
|
||||
func (receiver StockDataApi) GetStockConceptInfo(stockCode string) models.StockConceptInfoResp {
|
||||
//601138.SH
|
||||
url := "https://datacenter.eastmoney.com/securities/api/data/v1/get?reportName=RPT_F10_CORETHEME_BOARDTYPE&columns=SECUCODE%2CSECURITY_CODE%2CSECURITY_NAME_ABBR%2CNEW_BOARD_CODE%2CBOARD_NAME%2CSELECTED_BOARD_REASON%2CIS_PRECISE%2CBOARD_RANK%2CBOARD_YIELD%2CDERIVE_BOARD_CODE"eColumns=f3~05~NEW_BOARD_CODE~BOARD_YIELD&filter=(SECUCODE%3D%22" + stockCode + "%22)(IS_PRECISE%3D%221%22)&pageNumber=1&pageSize=&sortTypes=1&sortColumns=BOARD_RANK&source=HSF10&client=PC&v=005634233622011753"
|
||||
logger.SugaredLogger.Infof("url:%s", url2.QueryEscape(url))
|
||||
var data models.StockConceptInfoResp
|
||||
resp, err := receiver.client.SetTimeout(time.Duration(receiver.config.CrawlTimeOut)*time.Second).R().
|
||||
SetHeader("Host", "datacenter.eastmoney.com").
|
||||
SetHeader("Referer", "https://emweb.securities.eastmoney.com/").
|
||||
SetHeader("Origin", "https://emweb.securities.eastmoney.com").
|
||||
SetHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:148.0) Gecko/20100101 Firefox/148.0").
|
||||
Get(url)
|
||||
if err != nil {
|
||||
logger.SugaredLogger.Errorf("err:%s", err.Error())
|
||||
}
|
||||
err = json.Unmarshal(resp.Body(), &data)
|
||||
if err != nil {
|
||||
logger.SugaredLogger.Errorf("err:%s", err.Error())
|
||||
return models.StockConceptInfoResp{}
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
// JSONToMarkdownTable 将JSON数据转换为Markdown表格
|
||||
func JSONToMarkdownTable(jsonData []byte) (string, error) {
|
||||
var data []map[string]interface{}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -195,7 +195,7 @@ func TestParseFullSingleStockData(t *testing.T) {
|
||||
func TestNewStockDataApi(t *testing.T) {
|
||||
db.Init("../../data/stock.db")
|
||||
stockDataApi := NewStockDataApi()
|
||||
datas, _ := stockDataApi.GetStockCodeRealTimeData("sh600859", "sh600745", "gb_tsla", "hk09660", "hk00700")
|
||||
datas, _ := stockDataApi.GetStockCodeRealTimeData("sz002352", "sh600859", "sh600745", "gb_tsla", "hk09660", "hk00700")
|
||||
for _, data := range *datas {
|
||||
t.Log(data)
|
||||
}
|
||||
@@ -265,3 +265,35 @@ func TestStockDataApi_GetIndexBasic(t *testing.T) {
|
||||
stockDataApi := NewStockDataApi()
|
||||
stockDataApi.GetIndexBasic()
|
||||
}
|
||||
|
||||
func TestName(t *testing.T) {
|
||||
db.Init("../../data/stock.db")
|
||||
|
||||
stockBasics := &[]StockBasic{}
|
||||
resty.New().SetProxy("").R().
|
||||
SetHeader("user", "go-stock").
|
||||
SetResult(stockBasics).
|
||||
Get("http://8.134.249.145:18080/go-stock/stock_basic.json")
|
||||
|
||||
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())
|
||||
//}
|
||||
|
||||
}
|
||||
func TestGetStockMoneyData(t *testing.T) {
|
||||
db.Init("../../data/stock.db")
|
||||
stockDataApi := NewStockDataApi()
|
||||
res := stockDataApi.GetStockMoneyData()
|
||||
logger.SugaredLogger.Infof("%s", util.MarkdownTableWithTitle("今日个股资金流向Top50", res.Data.Diff))
|
||||
}
|
||||
|
||||
func TestGetStockConceptInfo(t *testing.T) {
|
||||
db.Init("../../data/stock.db")
|
||||
stockDataApi := NewStockDataApi()
|
||||
res := stockDataApi.GetStockConceptInfo("601138.SH")
|
||||
logger.SugaredLogger.Infof("%s", util.MarkdownTableWithTitle("601138.SH所属概念/板块信息", res.Result.Data))
|
||||
|
||||
}
|
||||
|
||||
@@ -2,38 +2,50 @@ package data
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
_ "embed"
|
||||
"fmt"
|
||||
"go-stock/backend/db"
|
||||
"go-stock/backend/logger"
|
||||
"go-stock/backend/models"
|
||||
"os"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strings"
|
||||
"unicode"
|
||||
|
||||
"github.com/duke-git/lancet/v2/convertor"
|
||||
"github.com/duke-git/lancet/v2/fileutil"
|
||||
"github.com/duke-git/lancet/v2/strutil"
|
||||
"github.com/go-ego/gse"
|
||||
)
|
||||
|
||||
const basefreq float64 = 100
|
||||
|
||||
// 金融情感词典,包含股票市场相关的专业词汇
|
||||
var (
|
||||
seg gse.Segmenter
|
||||
|
||||
// 正面金融词汇及其权重
|
||||
positiveFinanceWords = map[string]float64{
|
||||
"上涨": 2.0, "涨停": 3.0, "牛市": 3.0, "反弹": 2.0, "新高": 2.5,
|
||||
"涨": 1.0, "上涨": 2.0, "涨停": 3.0, "牛市": 3.0, "反弹": 2.0, "新高": 2.5,
|
||||
"利好": 2.5, "增持": 2.0, "买入": 2.0, "推荐": 1.5, "看多": 2.0,
|
||||
"盈利": 2.0, "增长": 2.0, "超预期": 2.5, "强劲": 1.5, "回升": 1.5,
|
||||
"复苏": 2.0, "突破": 2.0, "创新高": 3.0, "回暖": 1.5, "上扬": 1.5,
|
||||
"利好消息": 3.0, "收益增长": 2.5, "利润增长": 2.5, "业绩优异": 2.5,
|
||||
"潜力股": 2.0, "绩优股": 2.0, "强势": 1.5, "走高": 1.5, "攀升": 1.5,
|
||||
"大涨": 2.5, "飙升": 3.0, "井喷": 3.0, "爆发": 2.5, "暴涨": 3.0,
|
||||
"大涨": 2.5, "飙升": 3.0, "井喷": 3.0, "暴涨": 3.0,
|
||||
}
|
||||
|
||||
// 负面金融词汇及其权重
|
||||
negativeFinanceWords = map[string]float64{
|
||||
"下跌": 2.0, "跌停": 3.0, "熊市": 3.0, "回调": 1.5, "新低": 2.5,
|
||||
"跌": 2.0, "下跌": 2.0, "跌停": 3.0, "熊市": 3.0, "回调": 2.5, "新低": 2.5,
|
||||
"利空": 2.5, "减持": 2.0, "卖出": 2.0, "看空": 2.0, "亏损": 2.5,
|
||||
"下滑": 2.0, "萎缩": 2.0, "不及预期": 2.5, "疲软": 1.5, "恶化": 2.0,
|
||||
"衰退": 2.0, "跌破": 2.0, "创新低": 3.0, "走弱": 1.5, "下挫": 1.5,
|
||||
"衰退": 2.0, "跌破": 2.0, "创新低": 3.0, "走弱": 2.5, "下挫": 2.5,
|
||||
"利空消息": 3.0, "收益下降": 2.5, "利润下滑": 2.5, "业绩不佳": 2.5,
|
||||
"垃圾股": 2.0, "风险股": 2.0, "弱势": 1.5, "走低": 1.5, "缩量": 2.5,
|
||||
"大跌": 2.5, "暴跌": 3.0, "崩盘": 3.0, "跳水": 3.0, "重挫": 3.0,
|
||||
"垃圾股": 2.0, "风险股": 2.0, "弱势": 2.5, "走低": 2.5, "缩量": 2.5,
|
||||
"大跌": 2.5, "暴跌": 3.0, "崩盘": 3.0, "跳水": 3.0, "重挫": 3.0, "跌超": 2.5, "跌逾": 2.5, "跌近": 3.0,
|
||||
"被抓": 3.0, "被抓捕": 3.0, "回吐": 3.0, "转跌": 3.0,
|
||||
}
|
||||
|
||||
// 否定词,用于反转情感极性
|
||||
@@ -45,7 +57,7 @@ var (
|
||||
degreeWords = map[string]float64{
|
||||
"非常": 1.8, "极其": 2.2, "太": 1.8, "很": 1.5,
|
||||
"比较": 0.8, "稍微": 0.6, "有点": 0.7, "显著": 1.5,
|
||||
"大幅": 1.8, "急剧": 2.0, "轻微": 0.6, "小幅": 0.7,
|
||||
"大幅": 1.8, "急剧": 2.0, "轻微": 0.6, "小幅": 0.7, "逾": 1.8, "超": 1.8,
|
||||
}
|
||||
|
||||
// 转折词,用于识别情感转折
|
||||
@@ -54,34 +66,269 @@ var (
|
||||
}
|
||||
)
|
||||
|
||||
func init() {
|
||||
// 加载默认词典
|
||||
err := seg.LoadDict()
|
||||
//go:embed data/dict/base.txt
|
||||
var baseDict string
|
||||
|
||||
//go:embed data/dict/zh/s_1.txt
|
||||
var zhDict string
|
||||
|
||||
func InitAnalyzeSentiment() {
|
||||
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())
|
||||
} else {
|
||||
logger.SugaredLogger.Info("加载默认词典成功")
|
||||
}
|
||||
seg.CalcToken()
|
||||
|
||||
stocks := &[]StockBasic{}
|
||||
db.Dao.Model(&StockBasic{}).Find(stocks)
|
||||
for _, stock := range *stocks {
|
||||
if strutil.Trim(stock.Name) == "" {
|
||||
continue
|
||||
}
|
||||
err := seg.AddToken(stock.Name, basefreq+100, "n")
|
||||
if strutil.Trim(stock.BKName) != "" {
|
||||
err = seg.AddToken(stock.BKName, basefreq+100, "n")
|
||||
}
|
||||
if err != nil {
|
||||
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 {
|
||||
if strutil.Trim(stock.Name) == "" {
|
||||
continue
|
||||
}
|
||||
err := seg.AddToken(stock.Name, basefreq+100, "n")
|
||||
if strutil.Trim(stock.BKName) != "" {
|
||||
err = seg.AddToken(stock.BKName, basefreq+100, "n")
|
||||
}
|
||||
if err != nil {
|
||||
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 {
|
||||
// err := seg.AddToken(stock.Name, 500)
|
||||
// if err != nil {
|
||||
// logger.SugaredLogger.Errorf("添加%s失败:%s", stock.Name, err.Error())
|
||||
// }
|
||||
//}
|
||||
tags := &[]models.Tags{}
|
||||
db.Dao.Model(&models.Tags{}).Where("type = ?", "subject").Find(tags)
|
||||
for _, tag := range *tags {
|
||||
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") {
|
||||
lines, err := fileutil.ReadFileByLine("data/dict/user.txt")
|
||||
if err != nil {
|
||||
logger.SugaredLogger.Error(err.Error())
|
||||
return
|
||||
}
|
||||
for _, line := range lines {
|
||||
if len(line) == 0 || line[0] == '#' {
|
||||
continue
|
||||
}
|
||||
k := strutil.SplitAndTrim(line, " ")
|
||||
if len(k) == 0 {
|
||||
continue
|
||||
}
|
||||
_, _, ok := seg.Find(k[0])
|
||||
switch len(k) {
|
||||
case 1:
|
||||
if ok {
|
||||
err = seg.ReAddToken(k[0], basefreq)
|
||||
} else {
|
||||
err = seg.AddToken(k[0], basefreq)
|
||||
}
|
||||
case 2:
|
||||
freq, _ := convertor.ToFloat(k[1])
|
||||
if ok {
|
||||
err = seg.ReAddToken(k[0], freq)
|
||||
} else {
|
||||
err = seg.AddToken(k[0], freq)
|
||||
}
|
||||
case 3:
|
||||
freq, _ := convertor.ToFloat(k[1])
|
||||
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)
|
||||
}
|
||||
logger.SugaredLogger.Infof("添加用户词典[%s]成功", line)
|
||||
}
|
||||
if err != nil {
|
||||
logger.SugaredLogger.Error(err.Error())
|
||||
} else {
|
||||
logger.SugaredLogger.Infof("加载用户词典成功")
|
||||
}
|
||||
} else {
|
||||
logger.SugaredLogger.Info("用户词典不存在")
|
||||
}
|
||||
seg.CalcToken()
|
||||
}
|
||||
|
||||
// SentimentResult 情感分析结果类型
|
||||
type SentimentResult struct {
|
||||
Score float64 // 情感得分
|
||||
Category SentimentType // 情感类别
|
||||
PositiveCount int // 正面词数量
|
||||
NegativeCount int // 负面词数量
|
||||
Description string // 情感描述
|
||||
// getWordWeight 获取词汇权重
|
||||
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 freq
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// SentimentType 情感类型枚举
|
||||
type SentimentType int
|
||||
// SortByWeightAndFrequency 按权重和频次排序词频结果
|
||||
func SortByWeightAndFrequency(frequencies map[string]models.WordFreqWithWeight) []models.WordFreqWithWeight {
|
||||
// 将map转换为slice以便排序
|
||||
freqSlice := make([]models.WordFreqWithWeight, 0, len(frequencies))
|
||||
for _, freq := range frequencies {
|
||||
freqSlice = append(freqSlice, freq)
|
||||
}
|
||||
|
||||
// 按权重*频次降序排列
|
||||
sort.Slice(freqSlice, func(i, j int) bool {
|
||||
return freqSlice[i].Weight*float64(freqSlice[i].Frequency) > freqSlice[j].Weight*float64(freqSlice[j].Frequency)
|
||||
})
|
||||
logger.SugaredLogger.Infof("排序后的结果:%v", freqSlice)
|
||||
|
||||
return freqSlice
|
||||
}
|
||||
|
||||
// FilterAndSortWords 过滤标点符号并按权重频次排序
|
||||
func FilterAndSortWords(frequencies map[string]models.WordFreqWithWeight) []models.WordFreqWithWeight {
|
||||
// 先过滤标点符号和分隔符
|
||||
cleanFrequencies := FilterPunctuationAndSeparators(frequencies)
|
||||
|
||||
// 再按权重和频次排序
|
||||
sortedFrequencies := SortByWeightAndFrequency(cleanFrequencies)
|
||||
|
||||
return sortedFrequencies
|
||||
}
|
||||
func FilterPunctuationAndSeparators(frequencies map[string]models.WordFreqWithWeight) map[string]models.WordFreqWithWeight {
|
||||
filteredWords := make(map[string]models.WordFreqWithWeight)
|
||||
|
||||
for word, freqInfo := range frequencies {
|
||||
// 过滤纯标点符号和分隔符
|
||||
if !isPunctuationOrSeparator(word) {
|
||||
filteredWords[word] = freqInfo
|
||||
}
|
||||
}
|
||||
return filteredWords
|
||||
}
|
||||
|
||||
// isPunctuationOrSeparator 判断是否为标点符号或分隔符
|
||||
func isPunctuationOrSeparator(word string) bool {
|
||||
// 空字符串
|
||||
if strings.TrimSpace(word) == "" {
|
||||
return true
|
||||
}
|
||||
|
||||
// 检查是否全部由标点符号组成
|
||||
for _, r := range word {
|
||||
if !unicode.IsPunct(r) && !unicode.IsSymbol(r) && !unicode.IsSpace(r) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// FilterWithRegex 使用正则表达式过滤标点和特殊字符
|
||||
func FilterWithRegex(frequencies map[string]models.WordFreqWithWeight) map[string]models.WordFreqWithWeight {
|
||||
filteredWords := make(map[string]models.WordFreqWithWeight)
|
||||
|
||||
// 匹配标点符号、特殊字符的正则表达式
|
||||
punctuationRegex := regexp.MustCompile(`^[[:punct:][:space:]]+$`)
|
||||
|
||||
for word, freqInfo := range frequencies {
|
||||
// 过滤纯标点符号
|
||||
if !punctuationRegex.MatchString(word) && strings.TrimSpace(word) != "" {
|
||||
filteredWords[word] = freqInfo
|
||||
}
|
||||
}
|
||||
return filteredWords
|
||||
}
|
||||
|
||||
// countWordFrequencyWithWeight 统计词频并包含权重信息
|
||||
func countWordFrequencyWithWeight(text string) map[string]models.WordFreqWithWeight {
|
||||
words := splitWords(text)
|
||||
freqMap := make(map[string]models.WordFreqWithWeight)
|
||||
|
||||
// 统计词频
|
||||
wordCount := make(map[string]int)
|
||||
for _, word := range words {
|
||||
wordCount[word]++
|
||||
}
|
||||
|
||||
// 构建包含权重的结果
|
||||
for word, frequency := range wordCount {
|
||||
weight := getWordWeight(word)
|
||||
if weight >= basefreq {
|
||||
freqMap[word] = models.WordFreqWithWeight{
|
||||
Word: word,
|
||||
Frequency: frequency,
|
||||
Weight: weight,
|
||||
Score: float64(frequency) * weight,
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return freqMap
|
||||
}
|
||||
|
||||
// AnalyzeSentimentWithFreqWeight 带权重词频统计的情感分析
|
||||
func AnalyzeSentimentWithFreqWeight(text string) (models.SentimentResult, map[string]models.WordFreqWithWeight) {
|
||||
// 原有情感分析逻辑
|
||||
result := AnalyzeSentiment(text)
|
||||
|
||||
// 带权重的词频统计
|
||||
frequencies := countWordFrequencyWithWeight(text)
|
||||
|
||||
return result, frequencies
|
||||
}
|
||||
|
||||
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
|
||||
@@ -121,7 +368,7 @@ func AnalyzeSentiment(text string) SentimentResult {
|
||||
}
|
||||
|
||||
// 确定情感类别
|
||||
var category SentimentType
|
||||
var category models.SentimentType
|
||||
switch {
|
||||
case score > 1.0:
|
||||
category = Positive
|
||||
@@ -131,7 +378,7 @@ func AnalyzeSentiment(text string) SentimentResult {
|
||||
category = Neutral
|
||||
}
|
||||
|
||||
return SentimentResult{
|
||||
return models.SentimentResult{
|
||||
Score: score,
|
||||
Category: category,
|
||||
PositiveCount: positiveCount,
|
||||
@@ -244,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 "看涨"
|
||||
@@ -289,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
|
||||
}
|
||||
|
||||
@@ -2,8 +2,12 @@ package data
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go-stock/backend/db"
|
||||
"go-stock/backend/logger"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/duke-git/lancet/v2/random"
|
||||
)
|
||||
|
||||
// @Author spark
|
||||
@@ -12,25 +16,34 @@ import (
|
||||
//-----------------------------------------------------------------------------------
|
||||
|
||||
func TestAnalyzeSentiment(t *testing.T) {
|
||||
// 分析情感
|
||||
text := " 【调查:韩国近两成中小学生过度使用智能手机或互联网】财联社6月19日电,韩国女性家族部18日公布的一项年度调查结果显示,接受调查的韩国中小学生中,共计约17.3%、即超过21万人使用智能手机或互联网的程度达到了“危险水平”,这意味着他们因过度依赖智能手机或互联网而需要关注或干预,这一比例引人担忧。 (新华社)\n"
|
||||
text = "消息人士称,联合利华(Unilever)正在为Graze零食品牌寻找买家。\n"
|
||||
text = "【韩国未来5年将投入51万亿韩元发展文化产业】 据韩联社,韩国文化体育观光部(文体部)今后5年将投入51万亿韩元(约合人民币2667亿元)预算,落实总统李在明在竞选时期提出的“将韩国打造成全球五大文化强国之一”的承诺。\n"
|
||||
//text = "【油气股持续拉升 国际实业午后涨停】财联社6月19日电,油气股午后持续拉升,国际实业、宝莫股份午后涨停,准油股份、山东墨龙。茂化实华此前涨停,通源石油、海默科技、贝肯能源、中曼石油、科力股份等多股涨超5%。\n"
|
||||
//text = " 【三大指数均跌逾1% 下跌个股近4800只】财联社6月19日电,指数持续走弱,沪指下挫跌逾1.00%,深成指跌1.25%,创业板指跌1.39%。核聚变、风电、军工、食品消费等板块指数跌幅居前,沪深京三市下跌个股近4800只。\n"
|
||||
text = "【银行理财首单网下打新落地】财联社6月20日电,记者从多渠道获悉,光大理财以申报价格17元参与信通电子网下打新,并成功入围有效报价,成为行业内首家参与网下打新的银行理财公司。光大理财工作人员向证券时报记者表示,本次光大理财是以其管理的混合类产品“阳光橙增盈绝对收益策略”参与了此次网下打新,该产品为光大理财“固收+”银行理财产品。资料显示,信通电子成立于1996年,核心产品包括输电线路智能巡检系统、变电站智能辅控系统、移动智能终端及其他产品。根据其招股说明书,信通电子2023、2024年营业收入分别较上年增长19.08%和7.97%,净利润分别较上年增长5.6%和15.11%。 (证券时报)"
|
||||
text = " 【以军称拦截数枚伊朗导弹】财联社6月20日电,据央视新闻报道,以军在贝尔谢巴及周边区域拦截了数枚伊朗导弹,但仍有导弹或拦截残骸落地。以色列国防军发文表示,搜救队伍正在一处“空中物体落地”的所在区域开展工作,公众目前可以离开避难场所。伊朗方面对上述说法暂无回应。"
|
||||
|
||||
db.Init("../../data/stock.db")
|
||||
InitAnalyzeSentiment()
|
||||
|
||||
messageText := strings.Builder{}
|
||||
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 = "【牛肉加工亏损 美国泰森公司关停缩减相关业务】财联社11月22日电,受牛肉加工业务亏损影响,当地时间21日,美国泰森食品公司发布公告称,将关闭位于内布拉斯加州的一家大型牛肉加工厂,还计划缩小得克萨斯州一家牛肉加工厂的生产规模。根据泰森食品公司的公告,被关闭的这家工厂位于内布拉斯加州列克星敦,日均可宰杀并处理大约5000头牛,约占全美日均牛肉屠宰数量的4.8%。与此同时,公司还计划缩小得克萨斯州一家牛肉加工厂的生产规模,这家工厂每天大约可屠宰6000头牛。据悉,泰森此次业务调整影响两个工厂大约5000个工作岗位。《华尔街日报》报道称,泰森是美国四大肉类加工公司中首家关闭主要牛肉加工厂的公司,其最新财报显示,2025财年牛肉加工是唯一亏损的业务部门,调整后的营业亏损为4.26亿美元。"
|
||||
// 分析情感
|
||||
words := splitWords(text)
|
||||
fmt.Println(strings.Join(words, " "))
|
||||
|
||||
result := AnalyzeSentiment(text)
|
||||
|
||||
result, frequencies := AnalyzeSentimentWithFreqWeight(text)
|
||||
// 过滤标点符号和分隔符
|
||||
cleanFrequencies := FilterPunctuationAndSeparators(frequencies)
|
||||
// 输出结果
|
||||
fmt.Printf("情感分析结果: %s (得分: %.2f, 正面词:%d, 负面词:%d)\n",
|
||||
logger.SugaredLogger.Infof("情感分析结果: %s (得分: %.2f, 正面词:%d, 负面词:%d)\n 词频统计结果: %v",
|
||||
result.Description,
|
||||
result.Score,
|
||||
result.PositiveCount,
|
||||
result.NegativeCount)
|
||||
result.NegativeCount,
|
||||
cleanFrequencies,
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -149,6 +149,34 @@ func (receiver AIResponseResult) TableName() string {
|
||||
return "ai_response_result"
|
||||
}
|
||||
|
||||
// AIResponseResultQuery 分页查询参数
|
||||
type AIResponseResultQuery struct {
|
||||
Page int `form:"page" json:"page"` // 页码
|
||||
PageSize int `form:"pageSize" json:"pageSize"` // 每页大小
|
||||
ChatId string `form:"chatId" json:"chatId"` // 聊天ID筛选
|
||||
ModelName string `form:"modelName" json:"modelName"` // 模型名称筛选
|
||||
StockCode string `form:"stockCode" json:"stockCode"` // 股票代码筛选
|
||||
StockName string `form:"stockName" json:"stockName"` // 股票名称筛选
|
||||
Question string `form:"question" json:"question"` // 问题内容模糊搜索
|
||||
StartDate string `form:"startDate" json:"startDate"` // 开始日期
|
||||
EndDate string `form:"endDate" json:"endDate"` // 结束日期
|
||||
}
|
||||
|
||||
// AIResponseResultPageResp 分页查询响应
|
||||
type AIResponseResultPageResp struct {
|
||||
Code int `json:"code"`
|
||||
Message string `json:"message"`
|
||||
Data AIResponseResultPageData `json:"data"`
|
||||
}
|
||||
|
||||
type AIResponseResultPageData struct {
|
||||
List []AIResponseResult `json:"list"`
|
||||
Total int64 `json:"total"`
|
||||
Page int `json:"page"`
|
||||
PageSize int `json:"pageSize"`
|
||||
TotalPages int `json:"totalPages"`
|
||||
}
|
||||
|
||||
type VersionInfo struct {
|
||||
gorm.Model
|
||||
Version string `json:"version"`
|
||||
@@ -232,14 +260,16 @@ type Prompt struct {
|
||||
type Telegraph struct {
|
||||
gorm.Model
|
||||
Time string `json:"time"`
|
||||
Content string `json:"content"`
|
||||
DataTime *time.Time `json:"dataTime" gorm:"index"`
|
||||
Title string `json:"title" gorm:"index"`
|
||||
Content string `json:"content" gorm:"index"`
|
||||
SubjectTags []string `json:"subjects" gorm:"-:all"`
|
||||
StocksTags []string `json:"stocks" gorm:"-:all"`
|
||||
IsRed bool `json:"isRed"`
|
||||
IsRed bool `json:"isRed" gorm:"index"`
|
||||
Url string `json:"url"`
|
||||
Source string `json:"source"`
|
||||
Source string `json:"source" gorm:"index"`
|
||||
TelegraphTags []TelegraphTags `json:"tags" gorm:"-:migration;foreignKey:TelegraphId"`
|
||||
SentimentResult string `json:"sentimentResult" gorm:"-:all"`
|
||||
SentimentResult string `json:"sentimentResult" gorm:"index"`
|
||||
}
|
||||
type TelegraphTags struct {
|
||||
gorm.Model
|
||||
@@ -330,6 +360,22 @@ type TVNews struct {
|
||||
LogoId string `json:"logo_id"`
|
||||
} `json:"provider"`
|
||||
}
|
||||
type TVNewsDetail struct {
|
||||
ShortDescription string `json:"shortDescription"`
|
||||
Tags []struct {
|
||||
Title string `json:"title"`
|
||||
Args []struct {
|
||||
Id string `json:"id"`
|
||||
Value string `json:"value"`
|
||||
} `json:"args"`
|
||||
} `json:"tags"`
|
||||
Copyright string `json:"copyright"`
|
||||
Id string `json:"id"`
|
||||
Title string `json:"title"`
|
||||
Published int `json:"published"`
|
||||
Urgency int `json:"urgency"`
|
||||
StoryPath string `json:"storyPath"`
|
||||
}
|
||||
|
||||
type XUEQIUHot struct {
|
||||
Data struct {
|
||||
@@ -341,29 +387,29 @@ type XUEQIUHot struct {
|
||||
}
|
||||
|
||||
type HotItem struct {
|
||||
Type int `json:"type"`
|
||||
Code string `json:"code"`
|
||||
Name string `json:"name"`
|
||||
Value float64 `json:"value"`
|
||||
Increment int `json:"increment"`
|
||||
RankChange int `json:"rank_change"`
|
||||
HasExist interface{} `json:"has_exist"`
|
||||
Symbol string `json:"symbol"`
|
||||
Percent float64 `json:"percent"`
|
||||
Current float64 `json:"current"`
|
||||
Chg float64 `json:"chg"`
|
||||
Exchange string `json:"exchange"`
|
||||
StockType int `json:"stock_type"`
|
||||
SubType string `json:"sub_type"`
|
||||
Ad int `json:"ad"`
|
||||
AdId interface{} `json:"ad_id"`
|
||||
ContentId interface{} `json:"content_id"`
|
||||
Page interface{} `json:"page"`
|
||||
Model interface{} `json:"model"`
|
||||
Location interface{} `json:"location"`
|
||||
TradeSession interface{} `json:"trade_session"`
|
||||
CurrentExt interface{} `json:"current_ext"`
|
||||
PercentExt interface{} `json:"percent_ext"`
|
||||
Type int `json:"type" md:"-"`
|
||||
Code string `json:"code" md:"股票代码"`
|
||||
Name string `json:"name" md:"股票名称"`
|
||||
Value float64 `json:"value" md:"热度"`
|
||||
Increment int `json:"increment" md:"热度变化"`
|
||||
RankChange int `json:"rank_change" md:"排名变化"`
|
||||
HasExist interface{} `json:"has_exist" md:"-"`
|
||||
Symbol string `json:"symbol" md:"-"`
|
||||
Percent float64 `json:"percent" md:"涨跌幅(%)"`
|
||||
Current float64 `json:"current" md:"股价"`
|
||||
Chg float64 `json:"chg" md:"股价变化"`
|
||||
Exchange string `json:"exchange" md:"交易所代码"`
|
||||
StockType int `json:"stock_type" md:"-"`
|
||||
SubType string `json:"sub_type" md:"-"`
|
||||
Ad int `json:"ad" md:"-"`
|
||||
AdId interface{} `json:"ad_id" md:"-"`
|
||||
ContentId interface{} `json:"content_id" md:"-"`
|
||||
Page interface{} `json:"page" md:"-"`
|
||||
Model interface{} `json:"model" md:"-"`
|
||||
Location interface{} `json:"location" md:"-"`
|
||||
TradeSession interface{} `json:"trade_session" md:"-"`
|
||||
CurrentExt interface{} `json:"current_ext" md:"-"`
|
||||
PercentExt interface{} `json:"percent_ext" md:"-"`
|
||||
}
|
||||
|
||||
type HotEvent struct {
|
||||
@@ -701,3 +747,208 @@ 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"`
|
||||
}
|
||||
|
||||
type THSHotStrategy struct {
|
||||
Result struct {
|
||||
Num int `json:"num"`
|
||||
List []struct {
|
||||
Author struct {
|
||||
Avatar string `json:"avatar"`
|
||||
UserName string `json:"userName"`
|
||||
UserId int `json:"userId"`
|
||||
} `json:"author"`
|
||||
Property struct {
|
||||
Id int `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Query string `json:"query"`
|
||||
Logic string `json:"logic"`
|
||||
BuyPosition interface{} `json:"buyPosition"`
|
||||
Ctime string `json:"ctime"`
|
||||
Tags []string `json:"tags"`
|
||||
WinRate string `json:"winRate"`
|
||||
AnnualYield string `json:"annualYield"`
|
||||
Type int `json:"type"`
|
||||
} `json:"property"`
|
||||
Interaction struct {
|
||||
CommentNum int `json:"commentNum"`
|
||||
CollectNum int `json:"collectNum"`
|
||||
IsCollected bool `json:"isCollected"`
|
||||
IsSubscribe int `json:"isSubscribe"`
|
||||
IsPublish int `json:"isPublish"`
|
||||
Pid int `json:"pid"`
|
||||
} `json:"interaction"`
|
||||
} `json:"list"`
|
||||
} `json:"result"`
|
||||
}
|
||||
|
||||
type StockMoneyDataResp struct {
|
||||
Rc int `json:"rc"`
|
||||
Rt int `json:"rt"`
|
||||
Svr int `json:"svr"`
|
||||
Lt int `json:"lt"`
|
||||
Full int `json:"full"`
|
||||
Dlmkts string `json:"dlmkts"`
|
||||
Data StockMoneyData `json:"data"`
|
||||
}
|
||||
|
||||
type StockMoneyData struct {
|
||||
Total int `json:"total"`
|
||||
Diff []StockMoneyDataDiff `json:"diff"`
|
||||
}
|
||||
|
||||
type StockMoneyDataDiff struct {
|
||||
F1 int `json:"f1" md:"-"`
|
||||
F12 string `json:"f12" md:"股票代码"`
|
||||
F13 int `json:"f13" md:"-"`
|
||||
F14 string `json:"f14" md:"股票名称"`
|
||||
F2 float64 `json:"f2" md:"最新价"`
|
||||
F3 float64 `json:"f3" md:"今日涨跌幅(%)"`
|
||||
F62 float64 `json:"f62" md:"今日主力净额(元)"`
|
||||
F184 float64 `json:"f184" md:"今日主力净占比(%)"`
|
||||
F66 float64 `json:"f66" md:"今日超大单净额(元)"`
|
||||
F69 float64 `json:"f69" md:"今日超大单净占比(%)"`
|
||||
F72 float64 `json:"f72" md:"今日大单净额(元)"`
|
||||
F75 float64 `json:"f75" md:"今日大单净占比(%)"`
|
||||
F78 float64 `json:"f78" md:"今日中单净额(元)"`
|
||||
F81 float64 `json:"f81" md:"今日中单净占比(%)"`
|
||||
F84 float64 `json:"f84" md:"今日小单净额(元)"`
|
||||
F87 float64 `json:"f87" md:"今日小单净占比(%)"`
|
||||
F124 int `json:"f124" md:"f124"`
|
||||
F100 string `json:"f100" md:"所属板块"`
|
||||
F265 string `json:"f265" md:"板块代码"`
|
||||
}
|
||||
|
||||
type StockConceptInfoResp struct {
|
||||
Version string `json:"version"`
|
||||
Result StockConceptInfoResult `json:"result"`
|
||||
Success bool `json:"success"`
|
||||
Message string `json:"message"`
|
||||
Code int `json:"code"`
|
||||
}
|
||||
|
||||
type StockConceptInfoResult struct {
|
||||
Pages int `json:"pages"`
|
||||
Data []StockConceptInfo `json:"data"`
|
||||
Count int `json:"count"`
|
||||
}
|
||||
|
||||
type StockConceptInfo struct {
|
||||
SECUCODE string `json:"SECUCODE" md:"完整股票代码"`
|
||||
SECURITYCODE string `json:"SECURITY_CODE" md:"股票代码"`
|
||||
SECURITYNAMEABBR string `json:"SECURITY_NAME_ABBR" md:"股票名称"`
|
||||
NEWBOARDCODE string `json:"NEW_BOARD_CODE" md:"板块/概念代码"`
|
||||
BOARDNAME string `json:"BOARD_NAME" md:"板块/概念名称"`
|
||||
SELECTEDBOARDREASON string `json:"SELECTED_BOARD_REASON" md:"板块/概念描述"`
|
||||
ISPRECISE string `json:"IS_PRECISE" md:"-"`
|
||||
BOARDRANK int `json:"BOARD_RANK" md:"-"`
|
||||
BOARDYIELD float64 `json:"BOARD_YIELD" md:"板块/概念涨跌幅(%)"`
|
||||
DERIVEBOARDCODE string `json:"DERIVE_BOARD_CODE" md:"-"`
|
||||
}
|
||||
|
||||
type AiRecommendStocks struct {
|
||||
gorm.Model
|
||||
DataTime *time.Time `json:"dataTime" gorm:"index;autoCreateTime"`
|
||||
ModelName string `json:"modelName" md:"模型名称"`
|
||||
StockCode string `json:"stockCode" md:"股票代码"`
|
||||
StockName string `json:"stockName" md:"股票名称"`
|
||||
BkCode string `json:"bkCode" md:"行业/板块代码"`
|
||||
BkName string `json:"bkName" md:"行业/板块名称"`
|
||||
StockPrice string `json:"stockPrice" md:"推荐时股票价格"`
|
||||
StockCurrentPrice string `json:"stockCurrentPrice" md:"当前价格"`
|
||||
StockCurrentPriceTime string `json:"stockCurrentPriceTime" md:"当前价格时间"`
|
||||
StockClosePrice string `json:"stockClosePrice" md:"推荐时股票收盘价格"`
|
||||
StockPrePrice string `json:"stockPrePrice" md:"前一交易日股票价格"`
|
||||
RecommendReason string `json:"recommendReason" md:"推荐理由/驱动因素/逻辑"`
|
||||
RecommendBuyPrice string `json:"recommendBuyPrice" md:"ai建议买入价"`
|
||||
RecommendStopProfitPrice string `json:"recommendStopProfitPrice" md:"ai建议止盈价"`
|
||||
RecommendStopLossPrice string `json:"recommendStopLossPrice" md:"ai建议止损价"`
|
||||
RiskRemarks string `json:"riskRemarks" md:"风险提示"`
|
||||
Remarks string `json:"remarks" md:"备注"`
|
||||
}
|
||||
|
||||
func (receiver AiRecommendStocks) TableName() string { return "ai_recommend_stocks" }
|
||||
|
||||
type AiRecommendStocksQuery struct {
|
||||
Page int `form:"page" json:"page"` // 页码
|
||||
PageSize int `form:"pageSize" json:"pageSize"` // 每页大小
|
||||
StockCode string `form:"stockCode" json:"stockCode"` // 股票代码筛选
|
||||
StockName string `form:"stockName" json:"stockName"` // 股票名称筛选
|
||||
BkCode string `form:"bkCode" json:"bkCode"` // 板块代码筛选
|
||||
BkName string `form:"bkName" json:"bkName"` // 板块名称筛选
|
||||
StartDate string `form:"startDate" json:"startDate"` // 开始日期
|
||||
EndDate string `form:"endDate" json:"endDate"` // 结束日期
|
||||
}
|
||||
|
||||
type AiRecommendStocksPageResp struct {
|
||||
Code int `json:"code"`
|
||||
Message string `json:"message"`
|
||||
Data AiRecommendStocksPageData `json:"data"`
|
||||
}
|
||||
|
||||
type AiRecommendStocksPageData struct {
|
||||
List []AiRecommendStocks `json:"list"`
|
||||
Total int64 `json:"total"`
|
||||
Page int `json:"page"`
|
||||
PageSize int `json:"pageSize"`
|
||||
TotalPages int `json:"totalPages"`
|
||||
}
|
||||
|
||||
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
|
||||
4
frontend/components.d.ts
vendored
4
frontend/components.d.ts
vendored
@@ -11,6 +11,8 @@ declare module 'vue' {
|
||||
About: typeof import('./src/components/about.vue')['default']
|
||||
AgentChat: typeof import('./src/components/agent-chat.vue')['default']
|
||||
AgentChat_bk: typeof import('./src/components/agent-chat_bk.vue')['default']
|
||||
AiRecommendStocksList: typeof import('./src/components/aiRecommendStocksList.vue')['default']
|
||||
AnalyzeMartket: typeof import('./src/components/AnalyzeMartket.vue')['default']
|
||||
ClsCalendarTimeLine: typeof import('./src/components/ClsCalendarTimeLine.vue')['default']
|
||||
EmbeddedUrl: typeof import('./src/components/EmbeddedUrl.vue')['default']
|
||||
Fund: typeof import('./src/components/fund.vue')['default']
|
||||
@@ -26,6 +28,8 @@ declare module 'vue' {
|
||||
MoneyTrend: typeof import('./src/components/moneyTrend.vue')['default']
|
||||
NewsList: typeof import('./src/components/newsList.vue')['default']
|
||||
RankTable: typeof import('./src/components/rankTable.vue')['default']
|
||||
ResearchIndex: typeof import('./src/components/researchIndex.vue')['default']
|
||||
ResearchReport: typeof import('./src/components/researchReport.vue')['default']
|
||||
RouterLink: typeof import('vue-router')['RouterLink']
|
||||
RouterView: typeof import('vue-router')['RouterView']
|
||||
SelectStock: typeof import('./src/components/SelectStock.vue')['default']
|
||||
|
||||
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
|
||||
@@ -6,7 +6,8 @@ import {
|
||||
Quit,
|
||||
WindowFullscreen,
|
||||
WindowHide,
|
||||
WindowUnfullscreen
|
||||
WindowUnfullscreen,
|
||||
WindowSetTitle
|
||||
} from '../wailsjs/runtime'
|
||||
import {h, onBeforeMount, onBeforeUnmount, onMounted, ref} from "vue";
|
||||
import {RouterLink, useRouter} from 'vue-router'
|
||||
@@ -14,9 +15,9 @@ import {createDiscreteApi,darkTheme,lightTheme , NIcon, NText,NButton,dateZhCN,z
|
||||
import {
|
||||
AlarmOutline,
|
||||
AnalyticsOutline,
|
||||
BarChartSharp, Bonfire, BonfireOutline, EaselSharp,
|
||||
BarChartSharp, Bonfire, BonfireOutline, DiamondOutline, EaselSharp,
|
||||
ExpandOutline, Flag,
|
||||
Flame, FlameSharp, InformationOutline,
|
||||
Flame, FlameSharp, FlaskOutline, InformationOutline,
|
||||
LogoGithub,
|
||||
NewspaperOutline,
|
||||
NewspaperSharp, Notifications,
|
||||
@@ -29,7 +30,7 @@ import {
|
||||
} from '@vicons/ionicons5'
|
||||
import {AnalyzeSentiment, GetConfig, GetGroupList,GetVersionInfo} from "../wailsjs/go/main/App";
|
||||
import {Dragon, Fire, FirefoxBrowser, Gripfire, Robot} from "@vicons/fa";
|
||||
import {ReportSearch} from "@vicons/tabler";
|
||||
import {ReportAnalytics, ReportMoney, ReportSearch} from "@vicons/tabler";
|
||||
import {LocalFireDepartmentRound} from "@vicons/material";
|
||||
import {BoxSearch20Regular, CommentNote20Filled} from "@vicons/fluent";
|
||||
import {FireFilled, FireOutlined, NotificationFilled, StockOutlined} from "@vicons/antd";
|
||||
@@ -43,6 +44,7 @@ const loadingMsg = ref("加载数据中...")
|
||||
const enableNews = ref(false)
|
||||
const contentStyle = ref("")
|
||||
const enableFund = ref(false)
|
||||
const enableAgent = ref(false)
|
||||
const enableDarkTheme = ref(null)
|
||||
const content = ref('未经授权,禁止商业目的!\n\n数据来源于网络,仅供参考;投资有风险,入市需谨慎')
|
||||
const isFullscreen = ref(false)
|
||||
@@ -51,6 +53,7 @@ const containerRef = ref({})
|
||||
const realtimeProfit = ref(0)
|
||||
const telegraph = ref([])
|
||||
const groupList = ref([])
|
||||
const officialStatement= ref("")
|
||||
const menuOptions = ref([
|
||||
{
|
||||
label: () =>
|
||||
@@ -440,8 +443,80 @@ const menuOptions = ref([
|
||||
{default: () => 'Ai智能体'}
|
||||
),
|
||||
key: 'agent',
|
||||
show:enableAgent.value,
|
||||
icon: renderIcon(Robot),
|
||||
},
|
||||
{
|
||||
label: () =>
|
||||
h(
|
||||
RouterLink,
|
||||
{
|
||||
to: {
|
||||
name: 'research',
|
||||
query: {
|
||||
name:"研究中心",
|
||||
},
|
||||
},
|
||||
onClick: () => {
|
||||
activeKey.value = 'research'
|
||||
setTimeout(() => {
|
||||
EventsEmit("changeResearchTab", {ID: 0, name: 'AI分析报告'})
|
||||
}, 100)
|
||||
},
|
||||
},
|
||||
{default: () => '研究中心'}
|
||||
),
|
||||
key: 'research',
|
||||
icon: renderIcon(FlaskOutline),
|
||||
children:[
|
||||
{
|
||||
label: () =>
|
||||
h(
|
||||
RouterLink,
|
||||
{
|
||||
to: {
|
||||
name: 'research',
|
||||
query: {
|
||||
name:"AI分析报告",
|
||||
},
|
||||
},
|
||||
onClick: () => {
|
||||
activeKey.value = 'research'
|
||||
setTimeout(() => {
|
||||
EventsEmit("changeResearchTab", {ID: 0, name: 'AI分析报告'})
|
||||
}, 100)
|
||||
},
|
||||
},
|
||||
{default: () => 'AI分析报告'}
|
||||
),
|
||||
key: 'research1',
|
||||
icon: renderIcon(ReportAnalytics),
|
||||
},
|
||||
{
|
||||
label: () =>
|
||||
h(
|
||||
RouterLink,
|
||||
{
|
||||
to: {
|
||||
name: 'research',
|
||||
query: {
|
||||
name:"股票推荐记录",
|
||||
},
|
||||
},
|
||||
onClick: () => {
|
||||
activeKey.value = 'research'
|
||||
setTimeout(() => {
|
||||
EventsEmit("changeResearchTab", {ID: 1, name: '股票推荐记录'})
|
||||
}, 100)
|
||||
},
|
||||
},
|
||||
{default: () => '股票推荐记录'}
|
||||
),
|
||||
key: 'research2',
|
||||
icon: renderIcon(DiamondOutline),
|
||||
}
|
||||
],
|
||||
},
|
||||
{
|
||||
label: () =>
|
||||
h(
|
||||
@@ -596,6 +671,7 @@ onBeforeMount(() => {
|
||||
GetVersionInfo().then(result => {
|
||||
if(result.officialStatement){
|
||||
content.value = result.officialStatement+"\n\n"+content.value
|
||||
officialStatement.value = result.officialStatement
|
||||
}
|
||||
})
|
||||
|
||||
@@ -646,11 +722,15 @@ onBeforeMount(() => {
|
||||
GetConfig().then((res) => {
|
||||
//console.log(res)
|
||||
enableFund.value = res.enableFund
|
||||
enableAgent.value = res.enableAgent
|
||||
|
||||
menuOptions.value.filter((item) => {
|
||||
if (item.key === 'fund') {
|
||||
item.show = res.enableFund
|
||||
}
|
||||
if (item.key === 'agent') {
|
||||
item.show = res.enableAgent
|
||||
}
|
||||
})
|
||||
|
||||
if (res.darkTheme) {
|
||||
@@ -662,12 +742,14 @@ onBeforeMount(() => {
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
WindowSetTitle("go-stock:AI赋能股票分析✨ "+officialStatement.value+" 未经授权,禁止商业目的! [数据来源于网络,仅供参考;投资有风险,入市需谨慎]")
|
||||
contentStyle.value = "max-height: calc(92vh);overflow: hidden"
|
||||
GetConfig().then((res) => {
|
||||
if (res.enableNews) {
|
||||
enableNews.value = true
|
||||
}
|
||||
enableFund.value = res.enableFund
|
||||
enableAgent.value = res.enableAgent
|
||||
const {notification } =createDiscreteApi(["notification"], {
|
||||
configProviderProps: {
|
||||
theme: enableDarkTheme.value ? darkTheme : lightTheme ,
|
||||
@@ -714,7 +796,7 @@ onMounted(() => {
|
||||
<n-modal-provider>
|
||||
<n-dialog-provider>
|
||||
<n-watermark
|
||||
:content="content"
|
||||
:content="''"
|
||||
cross
|
||||
selectable
|
||||
:font-size="16"
|
||||
|
||||
316
frontend/src/components/AnalyzeMartket.vue
Normal file
316
frontend/src/components/AnalyzeMartket.vue
Normal file
@@ -0,0 +1,316 @@
|
||||
<script setup>
|
||||
|
||||
import {AnalyzeSentimentWithFreqWeight,GlobalStockIndexes} from "../../wailsjs/go/main/App";
|
||||
import * as echarts from "echarts";
|
||||
import {onMounted,onUnmounted, ref} from "vue";
|
||||
import _ from "lodash";
|
||||
const { name,darkTheme,kDays ,chartHeight} = defineProps({
|
||||
name: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
kDays: {
|
||||
type: Number,
|
||||
default: 14
|
||||
},
|
||||
chartHeight: {
|
||||
type: Number,
|
||||
default: 500
|
||||
},
|
||||
darkTheme: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
})
|
||||
const common = ref([])
|
||||
const america = ref([])
|
||||
const europe = ref([])
|
||||
const asia = ref([])
|
||||
const mainIndex = ref([])
|
||||
const chinaIndex = ref([])
|
||||
const other = ref([])
|
||||
const globalStockIndexes = ref(null)
|
||||
const chartRef = ref(null);
|
||||
const gaugeChartRef = ref(null);
|
||||
const triggerAreas=ref(["main","extra","arrow"])
|
||||
let handleChartInterval=null
|
||||
let handleIndexInterval=null
|
||||
onMounted(() => {
|
||||
handleChart()
|
||||
getIndex()
|
||||
handleChartInterval=setInterval(function () {
|
||||
handleChart()
|
||||
}, 1000 * 60)
|
||||
|
||||
handleIndexInterval=setInterval(function () {
|
||||
getIndex()
|
||||
}, 1000 * 2)
|
||||
})
|
||||
|
||||
onUnmounted(()=>{
|
||||
clearInterval(handleChartInterval)
|
||||
clearInterval(handleIndexInterval)
|
||||
})
|
||||
|
||||
function getIndex() {
|
||||
GlobalStockIndexes().then((res) => {
|
||||
globalStockIndexes.value = res
|
||||
common.value = res["common"]
|
||||
america.value = res["america"]
|
||||
europe.value = res["europe"]
|
||||
asia.value = res["asia"]
|
||||
other.value = res["other"]
|
||||
mainIndex.value=asia.value.filter(function (item) {
|
||||
return ['上海',"深圳","香港","台湾","北京","东京","首尔","纽约","纳斯达克"].includes(item.location)
|
||||
}).concat(america.value.filter(function (item) {
|
||||
return ['上海',"深圳","香港","台湾","北京","东京","首尔","纽约","纳斯达克"].includes(item.location)
|
||||
}))
|
||||
|
||||
chinaIndex.value=asia.value.filter(function (item) {
|
||||
return ['上海',"深圳","香港","台湾","北京"].includes(item.location)
|
||||
})
|
||||
|
||||
})
|
||||
}
|
||||
function handleChart(){
|
||||
const formatUtil = echarts.format;
|
||||
AnalyzeSentimentWithFreqWeight("").then((res) => {
|
||||
const treemapchart = echarts.init(chartRef.value);
|
||||
const gaugeChart=echarts.init(gaugeChartRef.value);
|
||||
let data = res['frequencies'].map(item => ({
|
||||
name: item.Word,
|
||||
// value: item.Frequency,
|
||||
// value: item.Weight,
|
||||
frequency: item.Frequency,
|
||||
weight: item.Weight,
|
||||
value: item.Score,
|
||||
}));
|
||||
|
||||
let data2 = res['frequencies'].map(item => ({
|
||||
name: item.Word,
|
||||
value: item.Frequency,
|
||||
// value: item.Weight,
|
||||
frequency: item.Frequency,
|
||||
weight: item.Weight,
|
||||
//value: item.Score,
|
||||
}));
|
||||
|
||||
let data3 = res['frequencies'].map(item => ({
|
||||
name: item.Word,
|
||||
//value: item.Frequency,
|
||||
value: item.Weight,
|
||||
frequency: item.Frequency,
|
||||
weight: item.Weight,
|
||||
//value: item.Score,
|
||||
}));
|
||||
|
||||
let option = {
|
||||
darkMode: darkTheme,
|
||||
title: {
|
||||
text:name,
|
||||
left: 'center',
|
||||
textStyle: {
|
||||
color: darkTheme?'#ccc':'#456'
|
||||
}
|
||||
},
|
||||
legend: {
|
||||
show: false
|
||||
},
|
||||
toolbox: {
|
||||
left: '20px',
|
||||
tooltip:{
|
||||
textStyle: {
|
||||
color: darkTheme?'#ccc':'#456'
|
||||
}
|
||||
},
|
||||
feature: {
|
||||
saveAsImage: {title: '保存图片'},
|
||||
restore: {
|
||||
title: '默认',
|
||||
},
|
||||
myTool2: {
|
||||
show: true,
|
||||
title: '按权重',
|
||||
icon:"path://M393.8816 148.1216a29.3376 29.3376 0 0 1-15.2576 38.0928c-43.776 17.152-81.92 43.8272-114.2784 76.2368A345.7536 345.7536 0 0 0 159.5392 512 352.8704 352.8704 0 0 0 512 864.4608a351.744 351.744 0 0 0 249.5488-102.912 353.536 353.536 0 0 0 76.2368-114.2784c5.6832-15.2576 22.8352-20.992 38.0928-15.2576 15.2576 5.7344 20.992 22.8864 15.2576 38.0928a421.2224 421.2224 0 0 1-89.6 133.376A412.6208 412.6208 0 0 1 512 921.6c-226.7136 0-409.6-182.8864-409.6-409.6 0-108.544 41.9328-211.456 120.0128-289.5872A421.2224 421.2224 0 0 1 355.84 132.864a29.3376 29.3376 0 0 1 38.0928 15.2576zM512 102.4c226.7136 0 409.6 182.8864 409.6 409.6 0 15.2576-13.312 28.5696-28.5696 28.5696H512A29.2864 29.2864 0 0 1 483.4304 512V130.9696c0-15.2576 13.312-28.5696 28.5696-28.5696z m28.5696 59.0336v321.9968h321.9968a350.976 350.976 0 0 0-321.9968-321.9968z",
|
||||
onclick: function (){
|
||||
treemapchart.setOption( {series:{
|
||||
data: data3
|
||||
}})
|
||||
}
|
||||
},
|
||||
myTool1: {
|
||||
show: true,
|
||||
title: '按频次',
|
||||
icon:"path://M895.466667 476.8l-87.424-87.424v-123.626667a49.770667 49.770667 0 0 0-49.770667-49.770666h-123.626667L547.2 128.533333a49.792 49.792 0 0 0-70.4 0l-87.424 87.424h-123.626667a49.770667 49.770667 0 0 0-49.770666 49.770667v123.626667L128.533333 476.8a49.792 49.792 0 0 0 0 70.4l87.424 87.424v123.626667a49.770667 49.770667 0 0 0 49.770667 49.770666h123.626667l87.424 87.424a49.792 49.792 0 0 0 70.4 0l87.424-87.424h123.626666a49.770667 49.770667 0 0 0 49.770667-49.770666v-123.626667l87.424-87.424a49.749333 49.749333 0 0 0 0.042667-70.4z m-137.216 137.194667v144.256h-144.256L512 860.266667l-101.994667-101.994667h-144.256v-144.256L163.733333 512l101.994667-101.994667v-144.256h144.256L512 163.733333l101.994667 101.994667h144.256v144.256L860.266667 512l-102.016 101.994667z M414.378667 514.730667l28.672 10.922666c-18.090667 47.445333-38.229333 92.16-60.757334 133.802667l-30.037333-13.653333a1042.133333 1042.133333 0 0 0 62.122667-131.072zM381.952 367.616L355.669333 384c25.258667 26.282667 45.056 50.176 60.074667 72.021333l25.6-17.749333c-13.994667-20.48-33.792-44.032-59.392-70.656zM537.258667 455.338667c-0.682667 43.690667-6.144 79.189333-16.725334 106.837333-14.336 32.768-44.373333 60.416-89.429333 82.944l21.162667 25.941333c52.224-26.624 85.333333-60.074667 99.328-100.693333 1.706667-5.12 3.413333-10.24 4.778666-15.36 21.504 45.738667 52.906667 83.968 93.866667 115.370667l21.504-24.917334c-51.2-34.474667-86.357333-81.237333-105.813333-140.288 1.706667-15.701333 2.730667-32.085333 2.730666-49.834666h-31.402666z M508.586667 434.858667h115.712c-6.826667 25.258667-15.018667 47.786667-24.917334 66.901333l31.744 8.874667a627.008 627.008 0 0 0 27.989334-85.674667v-21.162667H517.12c3.413333-14.336 6.144-29.354667 8.874667-45.738666l-32.426667-5.12c-7.850667 59.392-25.6 105.813333-52.906667 139.264l26.965334 19.114666c16.725333-19.114667 30.378667-44.373333 40.96-76.458666z",
|
||||
onclick: function (){
|
||||
treemapchart.setOption( {series:{
|
||||
data: data2
|
||||
}})
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
tooltip: {
|
||||
formatter: function (info) {
|
||||
var value = info.value.toFixed(2);
|
||||
var frequency = info.data.frequency;
|
||||
var weight = info.data.weight;
|
||||
return [
|
||||
'<div class="tooltip-title">' + info.name+ '</div>',
|
||||
'热度: ' + formatUtil.addCommas(value) + '',
|
||||
'<div class="tooltip-title">频次: ' + formatUtil.addCommas(frequency)+ '</div>',
|
||||
'<div class="tooltip-title">权重: ' + formatUtil.addCommas(weight)+ '</div>',
|
||||
].join('');
|
||||
}
|
||||
},
|
||||
series: [
|
||||
{
|
||||
type: 'treemap',
|
||||
breadcrumb:{show: false},
|
||||
left: '0',
|
||||
top: '40',
|
||||
right: '0',
|
||||
bottom: '0',
|
||||
tooltip: {
|
||||
show: true
|
||||
},
|
||||
data: data
|
||||
}
|
||||
]
|
||||
};
|
||||
treemapchart.setOption(option);
|
||||
|
||||
|
||||
|
||||
let option2 = {
|
||||
darkMode: darkTheme,
|
||||
series: [
|
||||
{
|
||||
type: 'gauge',
|
||||
startAngle: 180,
|
||||
endAngle: 0,
|
||||
center: ['50%', '75%'],
|
||||
radius: '90%',
|
||||
min: -100,
|
||||
max: 100,
|
||||
splitNumber: 8,
|
||||
axisLine: {
|
||||
lineStyle: {
|
||||
width: 6,
|
||||
color: [
|
||||
// [0.25, '#FF6E76'],
|
||||
// [0.5, '#FDDD60'],
|
||||
// [0.75, '#58D9F9'],
|
||||
// [1, '#7CFFB2'],
|
||||
|
||||
[0.25, '#03fb6a'],
|
||||
[0.5, '#58e1f9'],
|
||||
[0.75, '#ef5922'],
|
||||
[1, '#f11d29'],
|
||||
|
||||
]
|
||||
}
|
||||
},
|
||||
pointer: {
|
||||
icon: 'path://M12.8,0.7l12,40.1H0.7L12.8,0.7z',
|
||||
length: '12%',
|
||||
width: 20,
|
||||
offsetCenter: [0, '-60%'],
|
||||
itemStyle: {
|
||||
color: 'auto'
|
||||
}
|
||||
},
|
||||
axisTick: {
|
||||
length: 12,
|
||||
lineStyle: {
|
||||
color: 'auto',
|
||||
width: 2
|
||||
}
|
||||
},
|
||||
splitLine: {
|
||||
length: 20,
|
||||
lineStyle: {
|
||||
color: 'auto',
|
||||
width: 5
|
||||
}
|
||||
},
|
||||
axisLabel: {
|
||||
color: darkTheme?'#ccc':'#456',
|
||||
fontSize: 20,
|
||||
distance: -45,
|
||||
rotate: 'tangential',
|
||||
formatter: function (value) {
|
||||
if (value ===100) {
|
||||
return '极热';
|
||||
} else if (value === 50) {
|
||||
return '乐观';
|
||||
} else if (value === 0) {
|
||||
return '中性';
|
||||
}else if (value === -50) {
|
||||
return '谨慎';
|
||||
} else if (value === -100) {
|
||||
return '冰点';
|
||||
}
|
||||
return '';
|
||||
}
|
||||
},
|
||||
title: {
|
||||
offsetCenter: [0, '-10%'],
|
||||
fontSize: 20
|
||||
},
|
||||
detail: {
|
||||
fontSize: 30,
|
||||
offsetCenter: [0, '-35%'],
|
||||
valueAnimation: true,
|
||||
formatter: function (value) {
|
||||
return value.toFixed(2) + '';
|
||||
},
|
||||
color: 'inherit'
|
||||
},
|
||||
data: [
|
||||
{
|
||||
value: res.result.Score*0.2,
|
||||
name: '市场情绪强弱'
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
};
|
||||
gaugeChart.setOption(option2);
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<n-collapse :trigger-areas="triggerAreas" :default-expanded-names="['1']" display-directive="show">
|
||||
<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'">
|
||||
<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>
|
||||
主要股指
|
||||
</template>
|
||||
<n-grid :cols="24" :y-gap="0">
|
||||
<n-gi span="6">
|
||||
<div ref="gaugeChartRef" style="width: 100%;height: auto;--wails-draggable:no-drag" :style="{height:chartHeight+'px'}" ></div>
|
||||
</n-gi>
|
||||
<n-gi span="18">
|
||||
<div ref="chartRef" style="width: 100%;height: auto;--wails-draggable:no-drag" :style="{height:chartHeight+'px'}" ></div>
|
||||
</n-gi>
|
||||
</n-grid>
|
||||
</n-collapse-item>
|
||||
</n-collapse>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -59,7 +59,7 @@ function getMarketCode(item) {
|
||||
<template #trigger>
|
||||
<n-tag type="info" :bordered="false"> {{item.name}} {{item.code}}</n-tag>
|
||||
</template>
|
||||
<k-line-chart style="width: 800px" :code="getMarketCode(item)" :chart-height="500" :name="item.name" :k-days="20" :dark-theme="true"></k-line-chart>
|
||||
<k-line-chart style="width: 800px" :code="getMarketCode(item)" :chart-height="500" :stockName="item.name" :k-days="20" :dark-theme="true"></k-line-chart>
|
||||
</n-popover>
|
||||
</n-text></n-td>
|
||||
<n-td><n-text :type="item.percent>0?'error':'success'">{{item.percent}}%</n-text></n-td>
|
||||
|
||||
@@ -4,12 +4,12 @@ import {GetStockKLine} from "../../wailsjs/go/main/App";
|
||||
import * as echarts from "echarts";
|
||||
import {onMounted, ref} from "vue";
|
||||
import _ from "lodash";
|
||||
const { code,name,darkTheme,kDays ,chartHeight} = defineProps({
|
||||
const { code,stockName,darkTheme,kDays ,chartHeight} = defineProps({
|
||||
code: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
name: {
|
||||
stockName: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
@@ -33,13 +33,15 @@ const downBorderColor = '';
|
||||
const kLineChartRef = ref(null);
|
||||
|
||||
onMounted(() => {
|
||||
handleKLine(code,name)
|
||||
handleKLine(code,stockName)
|
||||
})
|
||||
|
||||
function handleKLine(code,name){
|
||||
GetStockKLine(code,name,365).then(result => {
|
||||
function handleKLine(code,stockName){
|
||||
console.log("handleKLine",code,stockName)
|
||||
const chart = echarts.init(kLineChartRef.value);
|
||||
chart.showLoading()
|
||||
GetStockKLine(code,stockName,365).then(result => {
|
||||
//console.log("GetStockKLine",result)
|
||||
const chart = echarts.init(kLineChartRef.value);
|
||||
const categoryData = [];
|
||||
const values = [];
|
||||
const volumns=[];
|
||||
@@ -47,24 +49,28 @@ function handleKLine(code,name){
|
||||
let resultElement=result[i]
|
||||
//console.log("resultElement:{}",resultElement)
|
||||
categoryData.push(resultElement.day)
|
||||
let flag=resultElement.close>resultElement.open?1:-1
|
||||
let flag=Number(resultElement.close)>Number(resultElement.open)?1:-1
|
||||
if(i>0){
|
||||
flag=Number(resultElement.close)>Number(result[i-1].close)?1:-1
|
||||
}
|
||||
values.push([
|
||||
resultElement.open,
|
||||
resultElement.close,
|
||||
resultElement.low,
|
||||
resultElement.high
|
||||
Number(resultElement.open),
|
||||
Number(resultElement.close),
|
||||
Number(resultElement.low),
|
||||
Number(resultElement.high)
|
||||
])
|
||||
volumns.push([i,resultElement.volume/10000,flag])
|
||||
volumns.push([i,Number(resultElement.volume)/10000,flag])
|
||||
}
|
||||
////console.log("categoryData",categoryData)
|
||||
////console.log("values",values)
|
||||
let option = {
|
||||
title: {
|
||||
text: name+" "+code,
|
||||
left: '20px',
|
||||
text: stockName+" "+categoryData[values.length-1]+" "+values[values.length-1][1]+" "+((values[values.length-1][1]-values[values.length-2][1])/values[values.length-2][1]*100).toFixed(2)+"%",
|
||||
left: '0px',
|
||||
textStyle: {
|
||||
color: darkTheme?'#ccc':'#456'
|
||||
}
|
||||
color: Number(values[values.length-1][1])>Number(values[values.length-2][1])?'red':'green',
|
||||
fontSize: 14
|
||||
},
|
||||
},
|
||||
darkMode: darkTheme,
|
||||
//backgroundColor: '#1c1c1c',
|
||||
@@ -150,7 +156,7 @@ function handleKLine(code,name){
|
||||
left: '8%',
|
||||
right: '8%',
|
||||
top: '66%',
|
||||
height: '15%'
|
||||
height: '18%'
|
||||
}
|
||||
],
|
||||
xAxis: [
|
||||
@@ -184,7 +190,11 @@ function handleKLine(code,name){
|
||||
scale: true,
|
||||
splitArea: {
|
||||
show: true
|
||||
}
|
||||
},
|
||||
axisLabel: { show: true },
|
||||
axisLine: { show: true },
|
||||
axisTick: { show: true },
|
||||
splitLine: { show: false }
|
||||
},
|
||||
{
|
||||
scale: true,
|
||||
@@ -354,10 +364,7 @@ function handleKLine(code,name){
|
||||
]
|
||||
};
|
||||
chart.setOption(option);
|
||||
|
||||
chart.on('click',{seriesName:'日K'}, function(params) {
|
||||
//console.log("click:",params);
|
||||
});
|
||||
chart.hideLoading()
|
||||
})
|
||||
}
|
||||
function calculateMA(dayCount,values) {
|
||||
|
||||
@@ -168,7 +168,7 @@ function handleEXPLANATION(value, option){
|
||||
<template #trigger>
|
||||
<n-button tag="a" text :type="item.CHANGE_RATE>0?'error':'success'" :bordered=false >{{ item.SECURITY_NAME_ABBR }}</n-button>
|
||||
</template>
|
||||
<k-line-chart style="width: 800px" :code="item.SECUCODE.split('.')[1].toLowerCase()+item.SECUCODE.split('.')[0]" :chart-height="500" :name="item.SECURITY_NAME_ABBR" :k-days="20" :dark-theme="true"></k-line-chart>
|
||||
<k-line-chart style="width: 800px" :code="item.SECUCODE.split('.')[1].toLowerCase()+item.SECUCODE.split('.')[0]" :chart-height="500" :stockName="item.SECURITY_NAME_ABBR" :k-days="20" :dark-theme="true"></k-line-chart>
|
||||
</n-popover>
|
||||
</n-td>
|
||||
<n-td>
|
||||
|
||||
@@ -125,7 +125,12 @@ function Search() {
|
||||
// 计算并设置表格宽度
|
||||
tableScrollX.value = calculateTableWidth(columns.value);
|
||||
} else {
|
||||
message.error(res.msg)
|
||||
if(res.msg){
|
||||
message.error(res.msg)
|
||||
}
|
||||
if(res.message){
|
||||
message.error(res.message)
|
||||
}
|
||||
}
|
||||
}).catch(err => {
|
||||
message.error(err)
|
||||
|
||||
@@ -123,7 +123,7 @@ function getmMarketCode(market,code) {
|
||||
<template #trigger>
|
||||
<n-tag type="info" :bordered="false">{{item.codes[0].short_name }}</n-tag>
|
||||
</template>
|
||||
<k-line-chart style="width: 800px" :code="getmMarketCode(item.codes[0].market_code,item.codes[0].stock_code)" :chart-height="500" :name="item.codes[0].short_name" :k-days="20" :dark-theme="true"></k-line-chart>
|
||||
<k-line-chart style="width: 800px" :code="getmMarketCode(item.codes[0].market_code,item.codes[0].stock_code)" :chart-height="500" :stockName="item.codes[0].short_name" :k-days="20" :dark-theme="true"></k-line-chart>
|
||||
</n-popover>
|
||||
</n-td>
|
||||
<n-td>
|
||||
|
||||
@@ -111,7 +111,7 @@ function handleSearch(value) {
|
||||
<template #trigger>
|
||||
<n-tag type="info" :bordered="false">{{item.stockName}}</n-tag>
|
||||
</template>
|
||||
<k-line-chart style="width: 800px" :code="getmMarketCode(item.market,item.stockCode)" :chart-height="500" :name="item.stockName" :k-days="20" :dark-theme="true"></k-line-chart>
|
||||
<k-line-chart style="width: 800px" :code="getmMarketCode(item.market,item.stockCode)" :chart-height="500" :stockName="item.stockName" :k-days="20" :dark-theme="true"></k-line-chart>
|
||||
</n-popover>
|
||||
</n-td>
|
||||
<n-td><n-tag type="info" :bordered="false">{{item.indvInduName}}</n-tag></n-td>
|
||||
|
||||
@@ -5,7 +5,9 @@ import 'md-editor-v3/lib/preview.css';
|
||||
import {h, onBeforeUnmount, onMounted, ref} from 'vue';
|
||||
import {CheckUpdate, GetVersionInfo,GetSponsorInfo,OpenURL} from "../../wailsjs/go/main/App";
|
||||
import {EventsOff, EventsOn,Environment} from "../../wailsjs/runtime";
|
||||
import {NAvatar, NButton, useNotification} from "naive-ui";
|
||||
import {NAvatar, NButton, useNotification,NText} from "naive-ui";
|
||||
import { addMonths, format ,parse} from 'date-fns';
|
||||
import { zhCN } from 'date-fns/locale';
|
||||
const updateLog = ref('');
|
||||
const versionInfo = ref('');
|
||||
const icon = ref('https://raw.githubusercontent.com/ArvinLovegood/go-stock/master/build/appicon.png');
|
||||
@@ -16,6 +18,7 @@ const notify = useNotification()
|
||||
const vipLevel=ref("");
|
||||
const vipStartTime=ref("");
|
||||
const vipEndTime=ref("");
|
||||
const expired=ref(false)
|
||||
|
||||
onMounted(() => {
|
||||
document.title = '关于软件';
|
||||
@@ -31,6 +34,13 @@ onMounted(() => {
|
||||
vipLevel.value = res.vipLevel;
|
||||
vipStartTime.value = res.vipStartTime;
|
||||
vipEndTime.value = res.vipEndTime;
|
||||
//判断时间是否到期
|
||||
if (res.vipLevel) {
|
||||
if (res.vipEndTime < format(new Date(), 'yyyy-MM-dd HH:mm:ss')) {
|
||||
notify.warning({content: 'VIP已到期'})
|
||||
expired.value = true;
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
});
|
||||
@@ -111,14 +121,14 @@ EventsOn("updateVersion",async (msg) => {
|
||||
<n-space vertical >
|
||||
<n-image width="100" :src="icon" />
|
||||
<h1>
|
||||
<n-badge v-if="!vipLevel" :value="versionInfo" :offset="[50,10]" type="success">
|
||||
<n-badge v-if="!vipLevel" :value="versionInfo" :offset="[80,10]" type="success">
|
||||
<n-gradient-text type="info" :size="50" >go-stock</n-gradient-text>
|
||||
</n-badge>
|
||||
<n-badge v-if="vipLevel" :value="versionInfo" :offset="[50,10]" type="success">
|
||||
<n-gradient-text type="warning" :size="50" >go-stock</n-gradient-text><n-tag :bordered="false" size="small" type="warning">VIP{{vipLevel}}</n-tag>
|
||||
<n-badge v-if="vipLevel" :value="versionInfo" :offset="[70,10]" type="success">
|
||||
<n-gradient-text :type="expired?'error':'warning'" :size="50" >go-stock</n-gradient-text><n-tag :bordered="false" size="small" type="warning">VIP{{vipLevel}}</n-tag>
|
||||
</n-badge>
|
||||
</h1>
|
||||
<n-gradient-text type="warning" v-if="vipLevel" >vip到期时间:{{vipEndTime}}</n-gradient-text>
|
||||
<n-gradient-text :type="expired?'error':'warning'" v-if="vipLevel" >vip到期时间:{{vipEndTime}}</n-gradient-text>
|
||||
<n-button size="tiny" @click="CheckUpdate(1)" type="info" tertiary >检查更新</n-button>
|
||||
<div style="justify-self: center;text-align: left" >
|
||||
<p>自选股行情实时监控,基于Wails和NaiveUI构建的AI赋能股票分析工具</p>
|
||||
@@ -156,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全部功能,启动时自动同步最近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>
|
||||
|
||||
405
frontend/src/components/aiRecommendStocksList.vue
Normal file
405
frontend/src/components/aiRecommendStocksList.vue
Normal file
@@ -0,0 +1,405 @@
|
||||
<script setup>
|
||||
import {computed, h, onBeforeMount, onBeforeUnmount, onMounted,onUnmounted, ref,reactive} from 'vue'
|
||||
import {
|
||||
GetAiRecommendStocksList,
|
||||
GetConfig,
|
||||
GetSponsorInfo,
|
||||
SaveAsMarkdown,
|
||||
ShareAnalysis
|
||||
} from "../../wailsjs/go/main/App";
|
||||
import {NAvatar, NButton, NEllipsis, NTag, NText, useMessage, useNotification} from "naive-ui";
|
||||
import KLineChart from "./KLineChart.vue";
|
||||
import {format} from "date-fns";
|
||||
|
||||
const notify = useNotification()
|
||||
const vipLevel=ref("");
|
||||
const vipStartTime=ref("");
|
||||
const vipEndTime=ref("");
|
||||
const expired=ref(false)
|
||||
const isValidVip=ref(false) // 是否是会员
|
||||
|
||||
onBeforeMount(()=> {
|
||||
GetConfig().then(result => {
|
||||
if (result.darkTheme) {
|
||||
editorDataRef.darkTheme = true
|
||||
}
|
||||
})
|
||||
|
||||
GetSponsorInfo().then((res) => {
|
||||
// console.log(res)
|
||||
vipLevel.value = res.vipLevel;
|
||||
vipStartTime.value = res.vipStartTime;
|
||||
vipEndTime.value = res.vipEndTime;
|
||||
//判断时间是否到期
|
||||
if (res.vipLevel) {
|
||||
if (res.vipEndTime < format(new Date(), 'yyyy-MM-dd HH:mm:ss')) {
|
||||
//notify.warning({content: 'VIP已到期'})
|
||||
expired.value = true;
|
||||
}
|
||||
}else{
|
||||
//notify.success({content: '未开通VIP'})
|
||||
}
|
||||
isValidVip.value = !(vipLevel.value === "" || Number(vipLevel.value) <= 0);
|
||||
|
||||
})
|
||||
|
||||
})
|
||||
onMounted(() => {
|
||||
query({
|
||||
page: 1,
|
||||
pageSize: paginationReactive.pageSize,
|
||||
order: "desc",
|
||||
keyword: paginationReactive.keyword,
|
||||
startDate: paginationReactive.range[0],
|
||||
endDate: paginationReactive.range[1]
|
||||
}).then((data) => {
|
||||
console.log( data)
|
||||
dataRef.value = data.data
|
||||
paginationReactive.page = 1
|
||||
paginationReactive.pageCount = data.pageCount
|
||||
paginationReactive.itemCount = data.total
|
||||
loadingRef.value = false
|
||||
})
|
||||
})
|
||||
const message = useMessage()
|
||||
const mdPreviewRef = ref(null)
|
||||
const mdEditorRef = ref(null)
|
||||
const editorDataRef = reactive({
|
||||
show: false,
|
||||
loading: false,
|
||||
darkTheme: false,
|
||||
chatId: "",
|
||||
modelName: "",
|
||||
CreatedAt: "",
|
||||
stockName: "",
|
||||
stockCode: "",
|
||||
question: "",
|
||||
content: "",
|
||||
})
|
||||
const dataRef = ref([])
|
||||
const loadingRef = ref(true)
|
||||
|
||||
// StockClosePrice string `json:"StockClosePrice" md:"推荐时股票收盘价格"`
|
||||
// StockPrePrice string `json:"stockPrePricePrice" md:"前一交易日股票价格"`
|
||||
// RecommendReason string `json:"recommendReason" md:"推荐理由/驱动因素/逻辑"`
|
||||
// RecommendBuyPrice string `json:"recommendBuyPrice" md:"ai建议买入价"`
|
||||
// RecommendStopProfitPrice string `json:"recommendStopProfitPrice" md:"ai建议止盈价"`
|
||||
// RecommendStopLossPrice string `json:"recommendStopLossPrice" md:"ai建议止损价"`
|
||||
// RiskRemarks string `json:"riskRemarks" md:"风险提示"`
|
||||
// Remarks string `json:"remarks" md:"备注"`
|
||||
const columnsRef = ref([
|
||||
{
|
||||
title: '推荐时间',
|
||||
key: 'dataTime',
|
||||
render(row, index) {
|
||||
//2026-01-14T22:13:27.2693252+08:00 格式化为常用时间格式
|
||||
return row.CreatedAt.substring(0, 19).replace('T', ' ')
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '板块概念',
|
||||
key: 'bkName'
|
||||
},
|
||||
{
|
||||
title: '股票名称',
|
||||
key: 'stockName',
|
||||
render(row, index) {
|
||||
return h(NText, { type: "info" }, { default: () => row.stockName })
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '股票代码',
|
||||
key: 'stockCode'
|
||||
},
|
||||
{
|
||||
title: '最新',
|
||||
key: 'stockCurrentPrice',
|
||||
minWidth: 120,
|
||||
render(row, index) {
|
||||
|
||||
let diff = ((Number(row.stockCurrentPrice) - Number(row.stockPrePrice))/ Number(row.stockPrePrice)*100).toFixed(2)
|
||||
|
||||
if(Number(row.stockCurrentPrice)< Number(row.stockPrePrice)) {
|
||||
return [h(NText, { type: "success", bordered: false }, { default: () => row.stockCurrentPrice+` | ${diff}%` })]
|
||||
} else {
|
||||
return [h(NText, { type: "error" , bordered: false}, { default: () => row.stockCurrentPrice+` | ${diff}%` })]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '推荐时',
|
||||
key: 'stockPrice',
|
||||
render(row, index) {
|
||||
|
||||
if(vipLevel.value===""|| Number(vipLevel.value) <=0){
|
||||
return h(NText, { type: "info" }, { default: () => row.stockPrice })
|
||||
}
|
||||
|
||||
let diff = ((Number(row.stockCurrentPrice) - Number(row.stockPrice))/ Number(row.stockPrice)*100).toFixed(2)
|
||||
let flagStr="暂平"
|
||||
let flag="info"
|
||||
if(Number(row.stockCurrentPrice)>Number(row.stockPrice)) {
|
||||
flagStr="暂赢 "+diff+"%"
|
||||
flag="error"
|
||||
}else if(Number(row.stockCurrentPrice)===Number(row.stockPrice)){
|
||||
flagStr="暂平"
|
||||
flag="info"
|
||||
}else{
|
||||
flagStr="暂亏 "+ diff+"%"
|
||||
flag="success"
|
||||
}
|
||||
|
||||
return [h(NText, { type: "info" }, { default: () => row.stockPrice }),h(NTag, { type: flag,size: "tiny", bordered: false }, { default: () => flagStr })]
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '昨收',
|
||||
key: 'stockPrePrice',
|
||||
render(row, index) {
|
||||
return h(NText, { type: "info" }, { default: () => row.stockPrePrice })
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'ai建议买入价',
|
||||
key: 'recommendBuyPrice',
|
||||
render(row, index) {
|
||||
if(vipLevel.value===""|| Number(vipLevel.value) <=0){
|
||||
return h(NText, { type: "info" }, { default: () => row.recommendBuyPrice })
|
||||
}
|
||||
|
||||
|
||||
if(row.recommendBuyPrice.includes("-")){
|
||||
let prices= row.recommendBuyPrice.split("-")
|
||||
if(Number(row.stockCurrentPrice)>=Number(prices[0])&&Number(row.stockCurrentPrice)<=Number(prices[1])){
|
||||
return [h(NText, { type: "success" }, { default: () => row.recommendBuyPrice }),h(NTag, { type: "error", size: "tiny", bordered: false }, { default: () => "Buy" })]
|
||||
}
|
||||
}
|
||||
return h(NText, { type: "info" }, { default: () => row.recommendBuyPrice })
|
||||
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'ai建议止盈价',
|
||||
key: 'recommendStopProfitPrice'
|
||||
},
|
||||
{
|
||||
title: 'ai建议止损价',
|
||||
key: 'recommendStopLossPrice'
|
||||
},
|
||||
{
|
||||
title: '推荐理由',
|
||||
key: 'recommendReason',
|
||||
ellipsis: {
|
||||
tooltip: isValidVip
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '风险提示',
|
||||
key: 'riskRemarks',
|
||||
ellipsis: {
|
||||
tooltip: isValidVip
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '备注',
|
||||
key: 'remarks',
|
||||
ellipsis: {
|
||||
tooltip: isValidVip
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
render(row, index) {
|
||||
return h(
|
||||
NButton,
|
||||
{
|
||||
strong: true,
|
||||
tertiary: true,
|
||||
size: 'small',
|
||||
type: 'warning', // 橙色按钮
|
||||
style: 'font-size: 14px; padding: 0 10px;', // 稍微大一点的按钮
|
||||
onClick: () => rowProps(row)
|
||||
},
|
||||
{ default: () => '查看详细' }
|
||||
)
|
||||
}
|
||||
},
|
||||
])
|
||||
const paginationReactive = reactive({
|
||||
page: 1,
|
||||
pageCount: 1,
|
||||
pageSize: 12,
|
||||
itemCount: 0,
|
||||
keyword: "",
|
||||
range: [new Date(new Date().getTime() - 7 * 24 * 60 * 60 * 1000), new Date(new Date().getTime() + 24 * 60 * 60 * 1000)],
|
||||
prefix({ itemCount }) {
|
||||
return `${itemCount} 条记录`
|
||||
}
|
||||
})
|
||||
|
||||
const modalDataRef = reactive({
|
||||
visible: false,
|
||||
title: "",
|
||||
content: "",
|
||||
riskRemarks: "",
|
||||
stockCode: "",
|
||||
stockName: "",
|
||||
remarks: "",
|
||||
})
|
||||
|
||||
const theme = computed(() => {
|
||||
return editorDataRef.darkTheme ? 'dark' : 'light'
|
||||
})
|
||||
|
||||
|
||||
function query({
|
||||
page,
|
||||
pageSize = 10,
|
||||
order = 'desc',
|
||||
keyword = "",
|
||||
startDate = "",
|
||||
endDate = ""
|
||||
}) {
|
||||
return new Promise((resolve) => {
|
||||
|
||||
GetAiRecommendStocksList({
|
||||
"page": page,
|
||||
"pageSize": pageSize,
|
||||
"modelName":keyword,
|
||||
"stockName":keyword,
|
||||
"stockCode":keyword,
|
||||
"bkName":keyword,
|
||||
"startDate": startDate,
|
||||
"endDate": endDate
|
||||
}).then((res) => {
|
||||
const pagedData =res.list
|
||||
const total = res.total
|
||||
const pageCount =res.totalPages
|
||||
resolve({
|
||||
pageCount,
|
||||
data: pagedData,
|
||||
total
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
function handlePageChange(currentPage) {
|
||||
if (!loadingRef.value) {
|
||||
loadingRef.value = true
|
||||
query({
|
||||
page: currentPage,
|
||||
pageSize: paginationReactive.pageSize,
|
||||
order: "desc",
|
||||
keyword: paginationReactive.keyword,
|
||||
startDate: formatDate(paginationReactive.range[0]), // Format date to string
|
||||
endDate: formatDate(paginationReactive.range[1]) // Format date to string
|
||||
}).then((data) => {
|
||||
dataRef.value = data.data
|
||||
paginationReactive.page = currentPage
|
||||
paginationReactive.pageCount = data.pageCount
|
||||
paginationReactive.itemCount = data.total
|
||||
loadingRef.value = false
|
||||
})
|
||||
}
|
||||
}
|
||||
function handleSearch() {
|
||||
if (!loadingRef.value) {
|
||||
loadingRef.value = true
|
||||
query({
|
||||
page: 1,
|
||||
pageSize: paginationReactive.pageSize,
|
||||
order: "desc",
|
||||
keyword: paginationReactive.keyword,
|
||||
startDate: formatDate(paginationReactive.range[0]),
|
||||
endDate: formatDate(paginationReactive.range[1])
|
||||
}).then((data) => {
|
||||
dataRef.value = data.data
|
||||
paginationReactive.page = 1
|
||||
paginationReactive.pageCount = data.pageCount
|
||||
paginationReactive.itemCount = data.total
|
||||
loadingRef.value = false
|
||||
})
|
||||
}
|
||||
}
|
||||
function formatDate(dateString) {
|
||||
const date = new Date(dateString)
|
||||
const year = date.getFullYear()
|
||||
const month = String(date.getMonth() + 1).padStart(2, '0')
|
||||
const day = String(date.getDate()).padStart(2, '0')
|
||||
const hours = String(date.getHours()).padStart(2, '0')
|
||||
const minutes = String(date.getMinutes()).padStart(2, '0')
|
||||
const seconds = String(date.getSeconds()).padStart(2, '0')
|
||||
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`
|
||||
}
|
||||
function getStockCode(stockCode) {
|
||||
if(stockCode.indexOf( ".")>0){
|
||||
stockCode=stockCode.split(".")[1]+stockCode.split(".")[0]
|
||||
}
|
||||
//转化为小写
|
||||
stockCode=stockCode.toLowerCase()
|
||||
return stockCode
|
||||
|
||||
}
|
||||
|
||||
function rowProps(row) {
|
||||
return {
|
||||
style: 'cursor: pointer;',
|
||||
onClick: () => {
|
||||
if(vipLevel.value===""|| Number(vipLevel.value) <=0){
|
||||
notify.warning({content: '未开通VIP或者已经过期'})
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
//message.info(row.stockName)
|
||||
modalDataRef.title = row.stockName
|
||||
modalDataRef.content = row.recommendReason
|
||||
modalDataRef.riskRemarks = row.riskRemarks
|
||||
modalDataRef.stockCode = getStockCode(row.stockCode)
|
||||
modalDataRef.stockName = row.stockName
|
||||
modalDataRef.visible = true
|
||||
modalDataRef.remarks = row.remarks
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<n-input-group>
|
||||
<n-date-picker v-model:value="paginationReactive.range" type="datetimerange" style="width: 50%"/>
|
||||
<n-input clearable placeholder="输入关键词搜索" v-model:value="paginationReactive.keyword"/>
|
||||
<n-button type="primary" ghost @click="handleSearch" @input="handleSearch">
|
||||
搜索
|
||||
</n-button>
|
||||
</n-input-group>
|
||||
<n-data-table
|
||||
remote
|
||||
:row-props="rowProps"
|
||||
size="small"
|
||||
:columns="columnsRef"
|
||||
:data="dataRef"
|
||||
:loading="loadingRef"
|
||||
:pagination="paginationReactive"
|
||||
:row-key="(rowData)=>rowData.ID"
|
||||
@update:page="handlePageChange"
|
||||
flex-height
|
||||
style="height: calc(100vh - 210px);margin-top: 10px"
|
||||
/>
|
||||
|
||||
<n-modal v-model:show="modalDataRef.visible" :title="modalDataRef.title" preset="card" style="width: 850px;">
|
||||
<n-gradient-text :size="16" type="warning">{{modalDataRef.remarks}}</n-gradient-text>
|
||||
<n-card size="small">
|
||||
<KLineChart style="width: 800px" :code="getStockCode(modalDataRef.stockCode)" :chart-height="500" :stock-name="modalDataRef.stockName" :k-days="30" :dark-theme="editorDataRef.darkTheme"></KLineChart>
|
||||
</n-card>
|
||||
<n-card size="small">
|
||||
<n-text type="info">{{modalDataRef.content}}</n-text>
|
||||
<n-divider><n-gradient-text type="error">风险提示</n-gradient-text></n-divider>
|
||||
<n-text type="error">{{modalDataRef.riskRemarks}}</n-text>
|
||||
</n-card>
|
||||
</n-modal>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -1,5 +1,6 @@
|
||||
<script setup>
|
||||
import {computed, h, onBeforeMount, onBeforeUnmount, onMounted, ref} from 'vue'
|
||||
import * as echarts from "echarts";
|
||||
import {computed, h, onBeforeMount, onBeforeUnmount, onMounted,onUnmounted, ref} from 'vue'
|
||||
import {
|
||||
GetAIResponseResult,
|
||||
GetConfig,
|
||||
@@ -12,7 +13,7 @@ import {
|
||||
SaveAsMarkdown,
|
||||
ShareAnalysis,
|
||||
SummaryStockNews,
|
||||
GetAiConfigs
|
||||
GetAiConfigs,
|
||||
} from "../../wailsjs/go/main/App";
|
||||
import {EventsOff, EventsOn} from "../../wailsjs/runtime";
|
||||
import NewsList from "./newsList.vue";
|
||||
@@ -44,6 +45,7 @@ const panelHeight = ref(window.innerHeight - 240)
|
||||
|
||||
const telegraphList = ref([])
|
||||
const sinaNewsList = ref([])
|
||||
const foreignNewsList = ref([])
|
||||
const common = ref([])
|
||||
const america = ref([])
|
||||
const europe = ref([])
|
||||
@@ -53,6 +55,7 @@ const globalStockIndexes = ref(null)
|
||||
const summaryModal = ref(false)
|
||||
const summaryBTN = ref(true)
|
||||
const darkTheme = ref(false)
|
||||
const httpProxyEnabled = ref(false)
|
||||
const theme = computed(() => {
|
||||
return darkTheme ? 'dark' : 'light'
|
||||
})
|
||||
@@ -75,6 +78,9 @@ 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;
|
||||
|
||||
function getIndex() {
|
||||
GlobalStockIndexes().then((res) => {
|
||||
@@ -93,6 +99,7 @@ onBeforeMount(() => {
|
||||
GetConfig().then(result => {
|
||||
summaryBTN.value = result.openAiEnable
|
||||
darkTheme.value = result.darkTheme
|
||||
httpProxyEnabled.value = result.httpProxyEnabled
|
||||
})
|
||||
GetPromptTemplates("", "").then(res => {
|
||||
promptTemplates.value = res
|
||||
@@ -104,13 +111,15 @@ onBeforeMount(() => {
|
||||
aiConfigs.value = res
|
||||
aiConfigId.value = res[0].ID
|
||||
})
|
||||
|
||||
GetTelegraphList("财联社电报").then((res) => {
|
||||
telegraphList.value = res
|
||||
})
|
||||
GetTelegraphList("新浪财经").then((res) => {
|
||||
sinaNewsList.value = res
|
||||
})
|
||||
GetTelegraphList("外媒").then((res) => {
|
||||
foreignNewsList.value = res
|
||||
})
|
||||
getIndex();
|
||||
industryRank();
|
||||
indexInterval.value = setInterval(() => {
|
||||
@@ -119,8 +128,16 @@ onBeforeMount(() => {
|
||||
|
||||
indexIndustryRank.value = setInterval(() => {
|
||||
industryRank()
|
||||
ReFlesh("财联社电报")
|
||||
ReFlesh("新浪财经")
|
||||
ReFlesh("外媒")
|
||||
}, 1000 * 10)
|
||||
|
||||
|
||||
})
|
||||
onMounted(() => {
|
||||
})
|
||||
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
EventsOff("changeMarketTab")
|
||||
@@ -131,8 +148,12 @@ onBeforeUnmount(() => {
|
||||
clearInterval(indexIndustryRank.value)
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
|
||||
});
|
||||
EventsOn("changeMarketTab", async (msg) => {
|
||||
//message.info(msg.name)
|
||||
console.log(msg.name)
|
||||
updateTab(msg.name)
|
||||
})
|
||||
|
||||
@@ -152,6 +173,14 @@ EventsOn("newSinaNews", (data) => {
|
||||
sinaNewsList.value.unshift(...data)
|
||||
}
|
||||
})
|
||||
EventsOn("tradingViewNews", (data) => {
|
||||
if (data!=null) {
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
foreignNewsList.value.pop()
|
||||
}
|
||||
foreignNewsList.value.unshift(...data)
|
||||
}
|
||||
})
|
||||
|
||||
//获取页面高度
|
||||
window.onresize = () => {
|
||||
@@ -198,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() {
|
||||
@@ -312,6 +341,9 @@ function ReFlesh(source) {
|
||||
if (source === "新浪财经") {
|
||||
sinaNewsList.value = res
|
||||
}
|
||||
if (source === "外媒") {
|
||||
foreignNewsList.value = res
|
||||
}
|
||||
})
|
||||
}
|
||||
</script>
|
||||
@@ -320,14 +352,26 @@ function ReFlesh(source) {
|
||||
<n-card>
|
||||
<n-tabs type="line" animated @update-value="updateTab" :value="nowTab" style="--wails-draggable:no-drag">
|
||||
<n-tab-pane name="市场快讯" tab="市场快讯">
|
||||
<n-grid :cols="2" :y-gap="0">
|
||||
<n-grid :cols="1" :y-gap="0">
|
||||
<n-gi>
|
||||
<news-list :newsList="telegraphList" :header-title="'财联社电报'" @update:message="ReFlesh"></news-list>
|
||||
<AnalyzeMartket :dark-theme="darkTheme" :chart-height="300" :kDays="1" :name="'最近24小时热词'" />
|
||||
</n-gi>
|
||||
<n-gi>
|
||||
<news-list :newsList="sinaNewsList" :header-title="'新浪财经'" @update:message="ReFlesh"></news-list>
|
||||
<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="foreignNewsList.length>0">
|
||||
<news-list :newsList="foreignNewsList" :header-title="'外媒'" @update:message="ReFlesh"></news-list>
|
||||
</n-gi>
|
||||
|
||||
</n-grid>
|
||||
</n-gi>
|
||||
</n-grid>
|
||||
|
||||
</n-tab-pane>
|
||||
<n-tab-pane name="全球股指" tab="全球股指">
|
||||
<n-tabs type="segment" animated>
|
||||
@@ -367,31 +411,31 @@ function ReFlesh(source) {
|
||||
</n-grid>
|
||||
</n-tab-pane>
|
||||
<n-tab-pane name="上证指数" tab="上证指数">
|
||||
<k-line-chart code="sh000001" :chart-height="panelHeight" name="上证指数" :k-days="20"
|
||||
<k-line-chart code="sh000001" :chart-height="panelHeight" stockName="上证指数" :k-days="20"
|
||||
:dark-theme="true"></k-line-chart>
|
||||
</n-tab-pane>
|
||||
<n-tab-pane name="深证成指" tab="深证成指">
|
||||
<k-line-chart code="sz399001" :chart-height="panelHeight" name="深证成指" :k-days="20"
|
||||
<k-line-chart code="sz399001" :chart-height="panelHeight" stockName="深证成指" :k-days="20"
|
||||
:dark-theme="true"></k-line-chart>
|
||||
</n-tab-pane>
|
||||
<n-tab-pane name="创业板指" tab="创业板指">
|
||||
<k-line-chart code="sz399006" :chart-height="panelHeight" name="创业板指" :k-days="20"
|
||||
<k-line-chart code="sz399006" :chart-height="panelHeight" stockName="创业板指" :k-days="20"
|
||||
:dark-theme="true"></k-line-chart>
|
||||
</n-tab-pane>
|
||||
<n-tab-pane name="恒生指数" tab="恒生指数">
|
||||
<k-line-chart code="hkHSI" :chart-height="panelHeight" name="恒生指数" :k-days="20"
|
||||
<k-line-chart code="hkHSI" :chart-height="panelHeight" stockName="恒生指数" :k-days="20"
|
||||
:dark-theme="true"></k-line-chart>
|
||||
</n-tab-pane>
|
||||
<n-tab-pane name="纳斯达克" tab="纳斯达克">
|
||||
<k-line-chart code="us.IXIC" :chart-height="panelHeight" name="纳斯达克" :k-days="20"
|
||||
<k-line-chart code="us.IXIC" :chart-height="panelHeight" stockName="纳斯达克" :k-days="20"
|
||||
:dark-theme="true"></k-line-chart>
|
||||
</n-tab-pane>
|
||||
<n-tab-pane name="道琼斯" tab="道琼斯">
|
||||
<k-line-chart code="us.DJI" :chart-height="panelHeight" name="道琼斯" :k-days="20"
|
||||
<k-line-chart code="us.DJI" :chart-height="panelHeight" stockName="道琼斯" :k-days="20"
|
||||
:dark-theme="true"></k-line-chart>
|
||||
</n-tab-pane>
|
||||
<n-tab-pane name="标普500" tab="标普500">
|
||||
<k-line-chart code="us.INX" :chart-height="panelHeight" name="标普500" :k-days="20"
|
||||
<k-line-chart code="us.INX" :chart-height="panelHeight" stockName="标普500" :k-days="20"
|
||||
:dark-theme="true"></k-line-chart>
|
||||
</n-tab-pane>
|
||||
</n-tabs>
|
||||
@@ -399,59 +443,59 @@ function ReFlesh(source) {
|
||||
<n-tab-pane name="重大指数" tab="重大指数">
|
||||
<n-tabs type="segment" animated>
|
||||
<n-tab-pane name="恒生科技指数" tab="恒生科技指数">
|
||||
<k-line-chart code="hkHSTECH" :chart-height="panelHeight" name="恒生科技指数" :k-days="20"
|
||||
<k-line-chart code="hkHSTECH" :chart-height="panelHeight" stockName="恒生科技指数" :k-days="20"
|
||||
:dark-theme="true"></k-line-chart>
|
||||
</n-tab-pane>
|
||||
<n-tab-pane name="科创50" tab="科创50" >
|
||||
<k-line-chart code="sh000688" :chart-height="panelHeight" name="科创50" :k-days="20"
|
||||
<k-line-chart code="sh000688" :chart-height="panelHeight" stockName="科创50" :k-days="20"
|
||||
:dark-theme="true"></k-line-chart>
|
||||
</n-tab-pane>
|
||||
<n-tab-pane name="科创芯片" tab="科创芯片" >
|
||||
<k-line-chart code="sh000685" :chart-height="panelHeight" name="科创芯片" :k-days="20"
|
||||
<k-line-chart code="sh000685" :chart-height="panelHeight" stockName="科创芯片" :k-days="20"
|
||||
:dark-theme="true"></k-line-chart>
|
||||
</n-tab-pane>
|
||||
<n-tab-pane name="证券龙头" tab="证券龙头" >
|
||||
<k-line-chart code="sz399437" :chart-height="panelHeight" name="证券龙头" :k-days="20"
|
||||
<k-line-chart code="sz399437" :chart-height="panelHeight" stockName="证券龙头" :k-days="20"
|
||||
:dark-theme="true"></k-line-chart>
|
||||
</n-tab-pane>
|
||||
<n-tab-pane name="高端装备" tab="高端装备" >
|
||||
<k-line-chart code="sz399437" :chart-height="panelHeight" name="高端装备" :k-days="20"
|
||||
<k-line-chart code="sz399437" :chart-height="panelHeight" stockName="高端装备" :k-days="20"
|
||||
:dark-theme="true"></k-line-chart>
|
||||
</n-tab-pane>
|
||||
<n-tab-pane name="中证银行" tab="中证银行">
|
||||
<k-line-chart code="sz399986" :chart-height="panelHeight" name="中证银行" :k-days="20"
|
||||
<k-line-chart code="sz399986" :chart-height="panelHeight" stockName="中证银行" :k-days="20"
|
||||
:dark-theme="true"></k-line-chart>
|
||||
</n-tab-pane>
|
||||
<n-tab-pane name="上证医药" tab="上证医药">
|
||||
<k-line-chart code="sh000037" :chart-height="panelHeight" name="上证医药" :k-days="20"
|
||||
<k-line-chart code="sh000037" :chart-height="panelHeight" stockName="上证医药" :k-days="20"
|
||||
:dark-theme="true"></k-line-chart>
|
||||
</n-tab-pane>
|
||||
<n-tab-pane name="沪深300" tab="沪深300">
|
||||
<k-line-chart code="sh000300" :chart-height="panelHeight" name="沪深300" :k-days="20"
|
||||
<k-line-chart code="sh000300" :chart-height="panelHeight" stockName="沪深300" :k-days="20"
|
||||
:dark-theme="true"></k-line-chart>
|
||||
</n-tab-pane>
|
||||
<n-tab-pane name="上证50" tab="上证50">
|
||||
<k-line-chart code="sh000016" :chart-height="panelHeight" name="上证50" :k-days="20"
|
||||
<k-line-chart code="sh000016" :chart-height="panelHeight" stockName="上证50" :k-days="20"
|
||||
:dark-theme="true"></k-line-chart>
|
||||
</n-tab-pane>
|
||||
<n-tab-pane name="中证A500" tab="中证A500">
|
||||
<k-line-chart code="sh000510" :chart-height="panelHeight" name="中证A500" :k-days="20"
|
||||
<k-line-chart code="sh000510" :chart-height="panelHeight" stockName="中证A500" :k-days="20"
|
||||
:dark-theme="true"></k-line-chart>
|
||||
</n-tab-pane>
|
||||
<n-tab-pane name="中证1000" tab="中证1000">
|
||||
<k-line-chart code="sh000852" :chart-height="panelHeight" name="中证1000" :k-days="20"
|
||||
<k-line-chart code="sh000852" :chart-height="panelHeight" stockName="中证1000" :k-days="20"
|
||||
:dark-theme="true"></k-line-chart>
|
||||
</n-tab-pane>
|
||||
<n-tab-pane name="中证白酒" tab="中证白酒">
|
||||
<k-line-chart code="sz399997" :chart-height="panelHeight" name="中证白酒" :k-days="20"
|
||||
<k-line-chart code="sz399997" :chart-height="panelHeight" stockName="中证白酒" :k-days="20"
|
||||
:dark-theme="true"></k-line-chart>
|
||||
</n-tab-pane>
|
||||
<n-tab-pane name="富时中国三倍做多" tab="富时中国三倍做多">
|
||||
<k-line-chart code="usYINN.AM" :chart-height="panelHeight" name="富时中国三倍做多" :k-days="20"
|
||||
<k-line-chart code="usYINN.AM" :chart-height="panelHeight" stockName="富时中国三倍做多" :k-days="20"
|
||||
:dark-theme="true"></k-line-chart>
|
||||
</n-tab-pane>
|
||||
<n-tab-pane name="VIX恐慌指数" tab="VIX恐慌指数">
|
||||
<k-line-chart code="usUVXY.AM" :chart-height="panelHeight" name="VIX恐慌指数" :k-days="20"
|
||||
<k-line-chart code="usUVXY.AM" :chart-height="panelHeight" stockName="VIX恐慌指数" :k-days="20"
|
||||
:dark-theme="true"></k-line-chart>
|
||||
</n-tab-pane>
|
||||
</n-tabs>
|
||||
@@ -664,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,12 +50,29 @@ 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-space justify="start">
|
||||
<n-text justify="start" :bordered="false" :type="item.isRed?'error':'info'">
|
||||
<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-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>
|
||||
{{ item.content }}
|
||||
</n-text>
|
||||
@@ -49,6 +91,9 @@ const updateMessage = () => {
|
||||
<n-text type="warning">查看原文</n-text>
|
||||
</a>
|
||||
</n-tag>
|
||||
<n-tag v-if="item.sentimentResult" :bordered="false" :type="item.sentimentResult==='看涨'?'error':item.sentimentResult==='看跌'?'success':'info'" size="small">
|
||||
{{ item.sentimentResult }}
|
||||
</n-tag>
|
||||
</n-space>
|
||||
</n-list-item>
|
||||
</n-list>
|
||||
|
||||
@@ -72,7 +72,7 @@ function GetMoneyRankSinaData(){
|
||||
<template #trigger>
|
||||
<n-button tag="a" text :type="item.changeratio>0?'error':'success'" :bordered=false >{{ item.name }}</n-button>
|
||||
</template>
|
||||
<k-line-chart style="width: 800px" :code="item.symbol" :chart-height="500" :name="item.name" :k-days="20" :dark-theme="true"></k-line-chart>
|
||||
<k-line-chart style="width: 800px" :code="item.symbol" :chart-height="500" :stockName="item.name" :k-days="20" :dark-theme="true"></k-line-chart>
|
||||
</n-popover>
|
||||
</n-td>
|
||||
<n-td><n-text :type="item.changeratio>0?'error':'success'">{{item.trade}}</n-text></n-td>
|
||||
|
||||
49
frontend/src/components/researchIndex.vue
Normal file
49
frontend/src/components/researchIndex.vue
Normal file
@@ -0,0 +1,49 @@
|
||||
<script setup>
|
||||
import {computed, h, onBeforeMount, onBeforeUnmount, onMounted,onUnmounted, ref,reactive} from 'vue'
|
||||
import {GetAIResponseResultList} from "../../wailsjs/go/main/App";
|
||||
import {NButton, NEllipsis, NText} from "naive-ui";
|
||||
import ResearchReport from "./researchReport.vue";
|
||||
import AiRecommendStocksList from "./aiRecommendStocksList.vue";
|
||||
import {EventsOff, EventsOn} from "../../wailsjs/runtime";
|
||||
import {useRoute} from 'vue-router'
|
||||
|
||||
|
||||
const nowTab = ref("AI分析报告")
|
||||
const route = useRoute()
|
||||
onBeforeMount(() => {
|
||||
nowTab.value = route.query.name
|
||||
})
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
EventsOff("changeResearchTab")
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
|
||||
});
|
||||
|
||||
EventsOn("changeResearchTab", async (msg) => {
|
||||
console.log("changeResearchTab", msg)
|
||||
updateTab(msg.name)
|
||||
})
|
||||
function updateTab(name) {
|
||||
nowTab.value = name
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<n-card>
|
||||
<n-tabs type="line" animated @update-value="updateTab" :value="nowTab" style="--wails-draggable:no-drag">
|
||||
<n-tab-pane name="AI分析报告">
|
||||
<ResearchReport/>
|
||||
</n-tab-pane>
|
||||
<n-tab-pane name="股票推荐记录">
|
||||
<AiRecommendStocksList/>
|
||||
</n-tab-pane>
|
||||
</n-tabs>
|
||||
</n-card>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
292
frontend/src/components/researchReport.vue
Normal file
292
frontend/src/components/researchReport.vue
Normal file
@@ -0,0 +1,292 @@
|
||||
<script setup>
|
||||
import {computed, h, onBeforeMount, onBeforeUnmount, onMounted,onUnmounted, ref,reactive} from 'vue'
|
||||
import {GetAIResponseResultList, GetConfig, SaveAsMarkdown, ShareAnalysis} from "../../wailsjs/go/main/App";
|
||||
import {NAvatar, NButton, NEllipsis, NText, useMessage} from "naive-ui";
|
||||
import {MdEditor, MdPreview} from 'md-editor-v3';
|
||||
|
||||
|
||||
|
||||
onBeforeMount(()=> {
|
||||
GetConfig().then(result => {
|
||||
if (result.darkTheme) {
|
||||
editorDataRef.darkTheme = true
|
||||
}
|
||||
})
|
||||
})
|
||||
onMounted(() => {
|
||||
query({
|
||||
page: 1,
|
||||
pageSize: paginationReactive.pageSize,
|
||||
order: "desc",
|
||||
keyword: paginationReactive.keyword,
|
||||
startDate: paginationReactive.range[0],
|
||||
endDate: paginationReactive.range[1]
|
||||
}).then((data) => {
|
||||
console.log( data)
|
||||
dataRef.value = data.data
|
||||
paginationReactive.page = 1
|
||||
paginationReactive.pageCount = data.pageCount
|
||||
paginationReactive.itemCount = data.total
|
||||
loadingRef.value = false
|
||||
})
|
||||
})
|
||||
const message = useMessage()
|
||||
const mdPreviewRef = ref(null)
|
||||
const mdEditorRef = ref(null)
|
||||
const editorDataRef = reactive({
|
||||
show: false,
|
||||
loading: false,
|
||||
darkTheme: false,
|
||||
chatId: "",
|
||||
modelName: "",
|
||||
CreatedAt: "",
|
||||
stockName: "",
|
||||
stockCode: "",
|
||||
question: "",
|
||||
content: "",
|
||||
})
|
||||
const dataRef = ref([])
|
||||
const loadingRef = ref(true)
|
||||
const columnsRef = ref([
|
||||
{
|
||||
title: '分析时间',
|
||||
key: 'CreatedAt',
|
||||
render(row, index) {
|
||||
//2026-01-14T22:13:27.2693252+08:00 格式化为常用时间格式
|
||||
return row.CreatedAt.substring(0, 19).replace('T', ' ')
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '模型名称',
|
||||
key: 'modelName'
|
||||
},
|
||||
{
|
||||
title: '分析对象',
|
||||
key: 'stockName'
|
||||
},
|
||||
{
|
||||
title: '提示词',
|
||||
key: 'question',
|
||||
render(row, index) {
|
||||
return h(NEllipsis, { tooltip: true ,style: "max-width: 240px;"}, {default: () => h(NText,{type: "info"},{default: () => row.question}),})
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
render(row, index) {
|
||||
return h(
|
||||
NButton,
|
||||
{
|
||||
strong: true,
|
||||
tertiary: true,
|
||||
size: 'small',
|
||||
type: 'warning', // 橙色按钮
|
||||
style: 'font-size: 14px; padding: 0 10px;', // 稍微大一点的按钮
|
||||
onClick: () => showReport(row)
|
||||
},
|
||||
{ default: () => '查看分析报告' }
|
||||
)
|
||||
}
|
||||
},
|
||||
])
|
||||
const paginationReactive = reactive({
|
||||
page: 1,
|
||||
pageCount: 1,
|
||||
pageSize: 12,
|
||||
itemCount: 0,
|
||||
keyword: "",
|
||||
range: [new Date(new Date().getTime() - 7 * 24 * 60 * 60 * 1000), new Date(new Date().getTime() + 24 * 60 * 60 * 1000)],
|
||||
prefix({ itemCount }) {
|
||||
return `${itemCount} 条记录`
|
||||
}
|
||||
})
|
||||
const theme = computed(() => {
|
||||
return editorDataRef.darkTheme ? 'dark' : 'light'
|
||||
})
|
||||
function showReport(row) {
|
||||
|
||||
editorDataRef.show = true
|
||||
editorDataRef.chatId = row.chatId
|
||||
editorDataRef.modelName = row.modelName
|
||||
editorDataRef.CreatedAt = row.CreatedAt.substring(0, 19).replace('T', ' ')
|
||||
editorDataRef.stockName = row.stockName
|
||||
editorDataRef.stockCode = row.stockCode
|
||||
editorDataRef.question = row.question
|
||||
editorDataRef.content = row.content
|
||||
editorDataRef.loading = false
|
||||
}
|
||||
|
||||
function query({
|
||||
page,
|
||||
pageSize = 10,
|
||||
order = 'desc',
|
||||
keyword = "",
|
||||
startDate = "",
|
||||
endDate = ""
|
||||
}) {
|
||||
return new Promise((resolve) => {
|
||||
|
||||
GetAIResponseResultList({
|
||||
"page": page,
|
||||
"pageSize": pageSize,
|
||||
"modelName":keyword,
|
||||
"question":keyword,
|
||||
"stockName":keyword,
|
||||
"stockCode":keyword,
|
||||
"startDate":startDate,
|
||||
"endDate":endDate
|
||||
}).then((res) => {
|
||||
const pagedData =res.list
|
||||
const total = res.total
|
||||
const pageCount =res.totalPages
|
||||
resolve({
|
||||
pageCount,
|
||||
data: pagedData,
|
||||
total
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
function handlePageChange(currentPage) {
|
||||
if (!loadingRef.value) {
|
||||
loadingRef.value = true
|
||||
query({
|
||||
page: currentPage,
|
||||
pageSize: paginationReactive.pageSize,
|
||||
order: "desc",
|
||||
keyword: paginationReactive.keyword,
|
||||
startDate: formatDate(paginationReactive.range[0]),
|
||||
endDate: formatDate(paginationReactive.range[1])
|
||||
}).then((data) => {
|
||||
dataRef.value = data.data
|
||||
paginationReactive.page = currentPage
|
||||
paginationReactive.pageCount = data.pageCount
|
||||
paginationReactive.itemCount = data.total
|
||||
loadingRef.value = false
|
||||
})
|
||||
}
|
||||
}
|
||||
function handleSearch() {
|
||||
if (!loadingRef.value) {
|
||||
loadingRef.value = true
|
||||
query({
|
||||
page: 1,
|
||||
pageSize: paginationReactive.pageSize,
|
||||
order: "desc",
|
||||
keyword: paginationReactive.keyword,
|
||||
startDate: formatDate(paginationReactive.range[0]),
|
||||
endDate: formatDate(paginationReactive.range[1])
|
||||
}).then((data) => {
|
||||
dataRef.value = data.data
|
||||
paginationReactive.page = 1
|
||||
paginationReactive.pageCount = data.pageCount
|
||||
paginationReactive.itemCount = data.total
|
||||
loadingRef.value = false
|
||||
})
|
||||
}
|
||||
}
|
||||
function share(code, name) {
|
||||
ShareAnalysis(code, name).then(msg => {
|
||||
//message.info(msg)
|
||||
notify.info({
|
||||
avatar: () =>
|
||||
h(NAvatar, {
|
||||
size: 'small',
|
||||
round: false,
|
||||
src: icon.value
|
||||
}),
|
||||
title: '分享到社区',
|
||||
duration: 1000 * 30,
|
||||
content: () => {
|
||||
return h('div', {
|
||||
style: {
|
||||
'text-align': 'left',
|
||||
'font-size': '14px',
|
||||
}
|
||||
}, {default: () => msg})
|
||||
},
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
function saveAsMarkdown(code,name) {
|
||||
SaveAsMarkdown(code, name).then(result => {
|
||||
if(result !== ""){
|
||||
message.success(result)
|
||||
}
|
||||
})
|
||||
}
|
||||
async function copyToClipboard() {
|
||||
try {
|
||||
await navigator.clipboard.writeText(editorDataRef.content);
|
||||
message.success('分析结果已复制到剪切板');
|
||||
} catch (err) {
|
||||
message.error('复制失败: ' + err);
|
||||
}
|
||||
}
|
||||
function formatDate(dateString) {
|
||||
const date = new Date(dateString)
|
||||
const year = date.getFullYear()
|
||||
const month = String(date.getMonth() + 1).padStart(2, '0')
|
||||
const day = String(date.getDate()).padStart(2, '0')
|
||||
const hours = String(date.getHours()).padStart(2, '0')
|
||||
const minutes = String(date.getMinutes()).padStart(2, '0')
|
||||
const seconds = String(date.getSeconds()).padStart(2, '0')
|
||||
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<n-input-group>
|
||||
<n-date-picker v-model:value="paginationReactive.range" type="datetimerange" style="width: 50%"/>
|
||||
<n-input clearable placeholder="输入关键词搜索" v-model:value="paginationReactive.keyword"/>
|
||||
<n-button type="primary" ghost @click="handleSearch" @input="handleSearch">
|
||||
搜索
|
||||
</n-button>
|
||||
</n-input-group>
|
||||
<n-data-table
|
||||
remote
|
||||
size="small"
|
||||
:columns="columnsRef"
|
||||
:data="dataRef"
|
||||
:loading="loadingRef"
|
||||
:pagination="paginationReactive"
|
||||
:row-key="(rowData)=>rowData.ID"
|
||||
@update:page="handlePageChange"
|
||||
flex-height
|
||||
style="height: calc(100vh - 210px);margin-top: 10px"
|
||||
/>
|
||||
|
||||
|
||||
|
||||
<n-modal transform-origin="center" v-model:show="editorDataRef.show" preset="card" style="width: 800px;"
|
||||
:title="'['+editorDataRef.stockName+']AI分析'">
|
||||
<n-spin size="small" :show="editorDataRef.loading">
|
||||
<MdPreview ref="mdPreviewRef" style="height: 540px;text-align: left"
|
||||
:modelValue="editorDataRef.content" :theme="theme"/>
|
||||
</n-spin>
|
||||
<template #footer>
|
||||
<n-flex justify="space-between" ref="tipsRef">
|
||||
<n-text type="info" v-if="editorDataRef.chatId">
|
||||
<n-tag v-if="editorDataRef.modelName" type="warning" round :title="editorDataRef.chatId" :bordered="false">
|
||||
{{ editorDataRef.modelName }}
|
||||
</n-tag>
|
||||
{{ editorDataRef.CreatedAt }}
|
||||
</n-text>
|
||||
<n-text type="error">*AI分析结果仅供参考,请以实际行情为准。投资需谨慎,风险自担。</n-text>
|
||||
</n-flex>
|
||||
</template>
|
||||
<template #action>
|
||||
<n-flex justify="right">
|
||||
<n-button size="tiny" type="success" @click="copyToClipboard">复制到剪切板</n-button>
|
||||
<n-button size="tiny" type="primary" @click="saveAsMarkdown(editorDataRef.stockCode,editorDataRef.stockName)">保存为Markdown文件</n-button>
|
||||
<n-button size="tiny" type="error" @click="share(editorDataRef.stockCode,editorDataRef.stockName)">分享到项目社区</n-button>
|
||||
</n-flex>
|
||||
</template>
|
||||
</n-modal>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -34,6 +34,8 @@ const formValue = ref({
|
||||
questionTemplate: "{{stockName}}分析和总结",
|
||||
crawlTimeOut: 30,
|
||||
kDays: 30,
|
||||
httpProxy:"",
|
||||
httpProxyEnabled:false,
|
||||
},
|
||||
enableDanmu: false,
|
||||
browserPath: '',
|
||||
@@ -45,6 +47,8 @@ const formValue = ref({
|
||||
sponsorCode: "",
|
||||
httpProxy:"",
|
||||
httpProxyEnabled:false,
|
||||
enableAgent: false,
|
||||
qgqpBId: '',
|
||||
})
|
||||
|
||||
// 添加一个新的AI配置到列表
|
||||
@@ -55,8 +59,10 @@ function addAiConfig() {
|
||||
apiKey: '',
|
||||
modelName: 'deepseek-chat',
|
||||
temperature: 0.1,
|
||||
maxTokens: 1024,
|
||||
maxTokens: 4096,
|
||||
timeOut: 60,
|
||||
httpProxy:"",
|
||||
httpProxyEnabled:false,
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -90,6 +96,8 @@ onMounted(() => {
|
||||
questionTemplate: res.questionTemplate ? res.questionTemplate : '{{stockName}}分析和总结',
|
||||
crawlTimeOut: res.crawlTimeOut,
|
||||
kDays: res.kDays,
|
||||
httpProxy:"",
|
||||
httpProxyEnabled:false,
|
||||
}
|
||||
|
||||
|
||||
@@ -103,6 +111,8 @@ onMounted(() => {
|
||||
formValue.value.sponsorCode = res.sponsorCode
|
||||
formValue.value.httpProxy=res.httpProxy;
|
||||
formValue.value.httpProxyEnabled=res.httpProxyEnabled;
|
||||
formValue.value.enableAgent = res.enableAgent;
|
||||
formValue.value.qgqpBId = res.qgqpBId;
|
||||
|
||||
})
|
||||
|
||||
@@ -142,6 +152,8 @@ function saveConfig() {
|
||||
sponsorCode: formValue.value.sponsorCode,
|
||||
httpProxy:formValue.value.httpProxy,
|
||||
httpProxyEnabled:formValue.value.httpProxyEnabled,
|
||||
enableAgent: formValue.value.enableAgent,
|
||||
qgqpBId: formValue.value.qgqpBId
|
||||
})
|
||||
|
||||
if (config.sponsorCode) {
|
||||
@@ -231,6 +243,8 @@ function importConfig() {
|
||||
formValue.value.sponsorCode = config.sponsorCode
|
||||
formValue.value.httpProxy=config.httpProxy
|
||||
formValue.value.httpProxyEnabled=config.httpProxyEnabled
|
||||
formValue.value.enableAgent = config.enableAgent
|
||||
formValue.value.qgqpBId = config.qgqpBId
|
||||
};
|
||||
reader.readAsText(file);
|
||||
};
|
||||
@@ -321,6 +335,13 @@ function deletePrompt(ID) {
|
||||
<n-form-item-gi :span="3" label="指数基金:" path="enableFund">
|
||||
<n-switch v-model:value="formValue.enableFund"/>
|
||||
</n-form-item-gi>
|
||||
<n-form-item-gi :span="3" label="AI智能体:" path="enableAgent">
|
||||
<n-switch v-model:value="formValue.enableAgent"/>
|
||||
</n-form-item-gi>
|
||||
<n-form-item-gi :span="11" label="东财唯一标识:" path="qgqpBId">
|
||||
<n-input type="text" placeholder="东财唯一标识" v-model:value="formValue.qgqpBId" clearable/>
|
||||
</n-form-item-gi>
|
||||
|
||||
<n-form-item-gi :span="11" label="赞助码:" path="sponsorCode">
|
||||
<n-input-group>
|
||||
<n-input :show-count="true" placeholder="赞助码" v-model:value="formValue.sponsorCode"/>
|
||||
@@ -373,14 +394,14 @@ function deletePrompt(ID) {
|
||||
</n-form-item-gi>
|
||||
<n-form-item-gi :span="4" v-if="formValue.openAI.enable" title="天数越多消耗tokens越多"
|
||||
label="日K线数据(天)" path="openAI.kDays">
|
||||
<n-input-number min="30" step="1" max="365" v-model:value="formValue.openAI.kDays"/>
|
||||
<n-input-number min="30" step="1" max="60" v-model:value="formValue.openAI.kDays"/>
|
||||
</n-form-item-gi>
|
||||
<n-form-item-gi :span="2" label="http代理" path="httpProxyEnabled">
|
||||
<n-form-item-gi :span="2" label="爬虫http代理" path="httpProxyEnabled">
|
||||
<n-switch v-model:value="formValue.httpProxyEnabled"/>
|
||||
</n-form-item-gi>
|
||||
<n-form-item-gi :span="10" v-if="formValue.httpProxyEnabled" title="http代理地址"
|
||||
label="http代理地址" path="httpProxy">
|
||||
<n-input type="text" placeholder="http代理地址" v-model:value="formValue.httpProxy" clearable/>
|
||||
<n-input type="text" placeholder="爬虫http代理地址" v-model:value="formValue.httpProxy" clearable/>
|
||||
</n-form-item-gi>
|
||||
|
||||
|
||||
@@ -437,6 +458,12 @@ function deletePrompt(ID) {
|
||||
<n-form-item-gi :span="5" label="Timeout(秒)" :path="`openAI.aiConfigs[${index}].timeOut`">
|
||||
<n-input-number min="60" step="1" placeholder="超时(秒)" v-model:value="aiConfig.timeOut"/>
|
||||
</n-form-item-gi>
|
||||
<n-form-item-gi :span="12" label="http代理" :path="`openAI.aiConfigs[${index}].httpProxyEnabled`">
|
||||
<n-switch v-model:value="aiConfig.httpProxyEnabled"/>
|
||||
</n-form-item-gi>
|
||||
<n-form-item-gi :span="12" v-if="aiConfig.httpProxyEnabled" title="http代理地址" :path="`openAI.aiConfigs[${index}].httpProxy`">
|
||||
<n-input type="text" placeholder="http代理地址" v-model:value="aiConfig.httpProxy" clearable/>
|
||||
</n-form-item-gi>
|
||||
</n-grid>
|
||||
</n-card>
|
||||
<n-button type="primary" dashed @click="addAiConfig" style="width: 100%;">+ 添加AI配置</n-button>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -9,18 +9,25 @@ import EmbeddedUrl from "./EmbeddedUrl.vue";
|
||||
<n-tab-pane name="选股通" tab="选股通">
|
||||
<embedded-url url="https://xuangutong.com.cn" :height="'calc(100vh - 252px)'"/>
|
||||
</n-tab-pane>
|
||||
<!-- <n-tab-pane name="百度股市通" tab="百度股市通">-->
|
||||
<!-- <embedded-url url="https://gushitong.baidu.com" :height="'calc(100vh - 252px)'"/>-->
|
||||
<!-- </n-tab-pane>-->
|
||||
<n-tab-pane name="百度股市通" tab="百度股市通">
|
||||
<embedded-url url="https://gushitong.baidu.com" :height="'calc(100vh - 252px)'"/>
|
||||
</n-tab-pane>
|
||||
<n-tab-pane name="东财大盘星图" tab="东财大盘星图">
|
||||
<embedded-url url="https://quote.eastmoney.com/stockhotmap/" :height="'calc(100vh - 252px)'"/>
|
||||
</n-tab-pane>
|
||||
<n-tab-pane name="TopHub" tab="TopHub(今日热榜)">
|
||||
<embedded-url url="https://tophub.today/c/finance" :height="'calc(100vh - 252px)'"/>
|
||||
</n-tab-pane>
|
||||
<!-- <n-tab-pane name="摸鱼" tab="摸鱼">-->
|
||||
<!-- <embedded-url url="https://996.ninja/" :height="'calc(100vh - 252px)'"/>-->
|
||||
<!-- </n-tab-pane>-->
|
||||
<n-tab-pane name="摸鱼" tab="摸鱼">
|
||||
<embedded-url url="https://996.ninja/" :height="'calc(100vh - 252px)'"/>
|
||||
</n-tab-pane>
|
||||
<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,6 +6,7 @@ import aboutView from "../components/about.vue";
|
||||
import fundView from "../components/fund.vue";
|
||||
import marketView from "../components/market.vue";
|
||||
import agentChat from "../components/agent-chat.vue"
|
||||
import research from "../components/researchIndex.vue";
|
||||
|
||||
const routes = [
|
||||
{ path: '/', component: stockView,name: 'stock'},
|
||||
@@ -14,6 +15,8 @@ const routes = [
|
||||
{ path: '/about', component: aboutView,name: 'about' },
|
||||
{ path: '/market', component: marketView,name: 'market' },
|
||||
{ path: '/agent', component: agentChat,name: 'agent' },
|
||||
{ path: '/research', component: research,name: 'research' },
|
||||
|
||||
]
|
||||
|
||||
const router = createRouter({
|
||||
|
||||
16
frontend/wailsjs/go/main/App.d.ts
vendored
16
frontend/wailsjs/go/main/App.d.ts
vendored
@@ -12,7 +12,11 @@ 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>>;
|
||||
|
||||
export function BatchDeleteAIResponseResult(arg1:Array<number>):Promise<string>;
|
||||
|
||||
export function ChatWithAgent(arg1:string,arg2:number,arg3:any):Promise<void>;
|
||||
|
||||
@@ -26,6 +30,8 @@ export function ClsCalendar():Promise<Array<any>>;
|
||||
|
||||
export function DelPrompt(arg1:number):Promise<string>;
|
||||
|
||||
export function DeleteAIResponseResult(arg1:string):Promise<string>;
|
||||
|
||||
export function EMDictCode(arg1:string):Promise<Array<any>>;
|
||||
|
||||
export function ExportConfig():Promise<string>;
|
||||
@@ -36,8 +42,12 @@ export function FollowFund(arg1:string):Promise<string>;
|
||||
|
||||
export function GetAIResponseResult(arg1:string):Promise<models.AIResponseResult>;
|
||||
|
||||
export function GetAIResponseResultList(arg1:models.AIResponseResultQuery):Promise<models.AIResponseResultPageData>;
|
||||
|
||||
export function GetAiConfigs():Promise<Array<data.AIConfig>>;
|
||||
|
||||
export function GetAiRecommendStocksList(arg1:models.AiRecommendStocksQuery):Promise<models.AiRecommendStocksPageData>;
|
||||
|
||||
export function GetConfig():Promise<data.SettingConfig>;
|
||||
|
||||
export function GetFollowList(arg1:number):Promise<any>;
|
||||
@@ -94,7 +104,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>;
|
||||
|
||||
@@ -134,7 +144,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>;
|
||||
|
||||
|
||||
@@ -22,6 +22,14 @@ export function AnalyzeSentiment(arg1) {
|
||||
return window['go']['main']['App']['AnalyzeSentiment'](arg1);
|
||||
}
|
||||
|
||||
export function AnalyzeSentimentWithFreqWeight(arg1) {
|
||||
return window['go']['main']['App']['AnalyzeSentimentWithFreqWeight'](arg1);
|
||||
}
|
||||
|
||||
export function BatchDeleteAIResponseResult(arg1) {
|
||||
return window['go']['main']['App']['BatchDeleteAIResponseResult'](arg1);
|
||||
}
|
||||
|
||||
export function ChatWithAgent(arg1, arg2, arg3) {
|
||||
return window['go']['main']['App']['ChatWithAgent'](arg1, arg2, arg3);
|
||||
}
|
||||
@@ -46,6 +54,10 @@ export function DelPrompt(arg1) {
|
||||
return window['go']['main']['App']['DelPrompt'](arg1);
|
||||
}
|
||||
|
||||
export function DeleteAIResponseResult(arg1) {
|
||||
return window['go']['main']['App']['DeleteAIResponseResult'](arg1);
|
||||
}
|
||||
|
||||
export function EMDictCode(arg1) {
|
||||
return window['go']['main']['App']['EMDictCode'](arg1);
|
||||
}
|
||||
@@ -66,10 +78,18 @@ export function GetAIResponseResult(arg1) {
|
||||
return window['go']['main']['App']['GetAIResponseResult'](arg1);
|
||||
}
|
||||
|
||||
export function GetAIResponseResultList(arg1) {
|
||||
return window['go']['main']['App']['GetAIResponseResultList'](arg1);
|
||||
}
|
||||
|
||||
export function GetAiConfigs() {
|
||||
return window['go']['main']['App']['GetAiConfigs']();
|
||||
}
|
||||
|
||||
export function GetAiRecommendStocksList(arg1) {
|
||||
return window['go']['main']['App']['GetAiRecommendStocksList'](arg1);
|
||||
}
|
||||
|
||||
export function GetConfig() {
|
||||
return window['go']['main']['App']['GetConfig']();
|
||||
}
|
||||
@@ -182,8 +202,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) {
|
||||
@@ -262,8 +282,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) {
|
||||
|
||||
@@ -13,6 +13,8 @@ export namespace data {
|
||||
maxTokens: number;
|
||||
temperature: number;
|
||||
timeOut: number;
|
||||
httpProxy: string;
|
||||
httpProxyEnabled: boolean;
|
||||
|
||||
static createFrom(source: any = {}) {
|
||||
return new AIConfig(source);
|
||||
@@ -30,6 +32,8 @@ export namespace data {
|
||||
this.maxTokens = source["maxTokens"];
|
||||
this.temperature = source["temperature"];
|
||||
this.timeOut = source["timeOut"];
|
||||
this.httpProxy = source["httpProxy"];
|
||||
this.httpProxyEnabled = source["httpProxyEnabled"];
|
||||
}
|
||||
|
||||
convertValues(a: any, classs: any, asMap: boolean = false): any {
|
||||
@@ -342,26 +346,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
|
||||
@@ -393,6 +377,8 @@ export namespace data {
|
||||
sponsorCode: string;
|
||||
httpProxy: string;
|
||||
httpProxyEnabled: boolean;
|
||||
enableAgent: boolean;
|
||||
qgqpBId: string;
|
||||
aiConfigs: AIConfig[];
|
||||
|
||||
static createFrom(source: any = {}) {
|
||||
@@ -428,6 +414,8 @@ export namespace data {
|
||||
this.sponsorCode = source["sponsorCode"];
|
||||
this.httpProxy = source["httpProxy"];
|
||||
this.httpProxyEnabled = source["httpProxyEnabled"];
|
||||
this.enableAgent = source["enableAgent"];
|
||||
this.qgqpBId = source["qgqpBId"];
|
||||
this.aiConfigs = this.convertValues(source["aiConfigs"], AIConfig);
|
||||
}
|
||||
|
||||
@@ -723,6 +711,210 @@ export namespace models {
|
||||
return a;
|
||||
}
|
||||
}
|
||||
export class AIResponseResultPageData {
|
||||
list: AIResponseResult[];
|
||||
total: number;
|
||||
page: number;
|
||||
pageSize: number;
|
||||
totalPages: number;
|
||||
|
||||
static createFrom(source: any = {}) {
|
||||
return new AIResponseResultPageData(source);
|
||||
}
|
||||
|
||||
constructor(source: any = {}) {
|
||||
if ('string' === typeof source) source = JSON.parse(source);
|
||||
this.list = this.convertValues(source["list"], AIResponseResult);
|
||||
this.total = source["total"];
|
||||
this.page = source["page"];
|
||||
this.pageSize = source["pageSize"];
|
||||
this.totalPages = source["totalPages"];
|
||||
}
|
||||
|
||||
convertValues(a: any, classs: any, asMap: boolean = false): any {
|
||||
if (!a) {
|
||||
return a;
|
||||
}
|
||||
if (a.slice && a.map) {
|
||||
return (a as any[]).map(elem => this.convertValues(elem, classs));
|
||||
} else if ("object" === typeof a) {
|
||||
if (asMap) {
|
||||
for (const key of Object.keys(a)) {
|
||||
a[key] = new classs(a[key]);
|
||||
}
|
||||
return a;
|
||||
}
|
||||
return new classs(a);
|
||||
}
|
||||
return a;
|
||||
}
|
||||
}
|
||||
export class AIResponseResultQuery {
|
||||
page: number;
|
||||
pageSize: number;
|
||||
chatId: string;
|
||||
modelName: string;
|
||||
stockCode: string;
|
||||
stockName: string;
|
||||
question: string;
|
||||
startDate: string;
|
||||
endDate: string;
|
||||
|
||||
static createFrom(source: any = {}) {
|
||||
return new AIResponseResultQuery(source);
|
||||
}
|
||||
|
||||
constructor(source: any = {}) {
|
||||
if ('string' === typeof source) source = JSON.parse(source);
|
||||
this.page = source["page"];
|
||||
this.pageSize = source["pageSize"];
|
||||
this.chatId = source["chatId"];
|
||||
this.modelName = source["modelName"];
|
||||
this.stockCode = source["stockCode"];
|
||||
this.stockName = source["stockName"];
|
||||
this.question = source["question"];
|
||||
this.startDate = source["startDate"];
|
||||
this.endDate = source["endDate"];
|
||||
}
|
||||
}
|
||||
export class AiRecommendStocks {
|
||||
ID: number;
|
||||
// Go type: time
|
||||
CreatedAt: any;
|
||||
// Go type: time
|
||||
UpdatedAt: any;
|
||||
// Go type: gorm
|
||||
DeletedAt: any;
|
||||
// Go type: time
|
||||
dataTime?: any;
|
||||
modelName: string;
|
||||
stockCode: string;
|
||||
stockName: string;
|
||||
bkCode: string;
|
||||
bkName: string;
|
||||
stockPrice: string;
|
||||
stockCurrentPrice: string;
|
||||
stockCurrentPriceTime: string;
|
||||
stockClosePrice: string;
|
||||
stockPrePrice: string;
|
||||
recommendReason: string;
|
||||
recommendBuyPrice: string;
|
||||
recommendStopProfitPrice: string;
|
||||
recommendStopLossPrice: string;
|
||||
riskRemarks: string;
|
||||
remarks: string;
|
||||
|
||||
static createFrom(source: any = {}) {
|
||||
return new AiRecommendStocks(source);
|
||||
}
|
||||
|
||||
constructor(source: any = {}) {
|
||||
if ('string' === typeof source) source = JSON.parse(source);
|
||||
this.ID = source["ID"];
|
||||
this.CreatedAt = this.convertValues(source["CreatedAt"], null);
|
||||
this.UpdatedAt = this.convertValues(source["UpdatedAt"], null);
|
||||
this.DeletedAt = this.convertValues(source["DeletedAt"], null);
|
||||
this.dataTime = this.convertValues(source["dataTime"], null);
|
||||
this.modelName = source["modelName"];
|
||||
this.stockCode = source["stockCode"];
|
||||
this.stockName = source["stockName"];
|
||||
this.bkCode = source["bkCode"];
|
||||
this.bkName = source["bkName"];
|
||||
this.stockPrice = source["stockPrice"];
|
||||
this.stockCurrentPrice = source["stockCurrentPrice"];
|
||||
this.stockCurrentPriceTime = source["stockCurrentPriceTime"];
|
||||
this.stockClosePrice = source["stockClosePrice"];
|
||||
this.stockPrePrice = source["stockPrePrice"];
|
||||
this.recommendReason = source["recommendReason"];
|
||||
this.recommendBuyPrice = source["recommendBuyPrice"];
|
||||
this.recommendStopProfitPrice = source["recommendStopProfitPrice"];
|
||||
this.recommendStopLossPrice = source["recommendStopLossPrice"];
|
||||
this.riskRemarks = source["riskRemarks"];
|
||||
this.remarks = source["remarks"];
|
||||
}
|
||||
|
||||
convertValues(a: any, classs: any, asMap: boolean = false): any {
|
||||
if (!a) {
|
||||
return a;
|
||||
}
|
||||
if (a.slice && a.map) {
|
||||
return (a as any[]).map(elem => this.convertValues(elem, classs));
|
||||
} else if ("object" === typeof a) {
|
||||
if (asMap) {
|
||||
for (const key of Object.keys(a)) {
|
||||
a[key] = new classs(a[key]);
|
||||
}
|
||||
return a;
|
||||
}
|
||||
return new classs(a);
|
||||
}
|
||||
return a;
|
||||
}
|
||||
}
|
||||
export class AiRecommendStocksPageData {
|
||||
list: AiRecommendStocks[];
|
||||
total: number;
|
||||
page: number;
|
||||
pageSize: number;
|
||||
totalPages: number;
|
||||
|
||||
static createFrom(source: any = {}) {
|
||||
return new AiRecommendStocksPageData(source);
|
||||
}
|
||||
|
||||
constructor(source: any = {}) {
|
||||
if ('string' === typeof source) source = JSON.parse(source);
|
||||
this.list = this.convertValues(source["list"], AiRecommendStocks);
|
||||
this.total = source["total"];
|
||||
this.page = source["page"];
|
||||
this.pageSize = source["pageSize"];
|
||||
this.totalPages = source["totalPages"];
|
||||
}
|
||||
|
||||
convertValues(a: any, classs: any, asMap: boolean = false): any {
|
||||
if (!a) {
|
||||
return a;
|
||||
}
|
||||
if (a.slice && a.map) {
|
||||
return (a as any[]).map(elem => this.convertValues(elem, classs));
|
||||
} else if ("object" === typeof a) {
|
||||
if (asMap) {
|
||||
for (const key of Object.keys(a)) {
|
||||
a[key] = new classs(a[key]);
|
||||
}
|
||||
return a;
|
||||
}
|
||||
return new classs(a);
|
||||
}
|
||||
return a;
|
||||
}
|
||||
}
|
||||
export class AiRecommendStocksQuery {
|
||||
page: number;
|
||||
pageSize: number;
|
||||
stockCode: string;
|
||||
stockName: string;
|
||||
bkCode: string;
|
||||
bkName: string;
|
||||
startDate: string;
|
||||
endDate: string;
|
||||
|
||||
static createFrom(source: any = {}) {
|
||||
return new AiRecommendStocksQuery(source);
|
||||
}
|
||||
|
||||
constructor(source: any = {}) {
|
||||
if ('string' === typeof source) source = JSON.parse(source);
|
||||
this.page = source["page"];
|
||||
this.pageSize = source["pageSize"];
|
||||
this.stockCode = source["stockCode"];
|
||||
this.stockName = source["stockName"];
|
||||
this.bkCode = source["bkCode"];
|
||||
this.bkName = source["bkName"];
|
||||
this.startDate = source["startDate"];
|
||||
this.endDate = source["endDate"];
|
||||
}
|
||||
}
|
||||
export class Prompt {
|
||||
ID: number;
|
||||
name: string;
|
||||
@@ -741,6 +933,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
|
||||
|
||||
27
main.go
27
main.go
@@ -58,8 +58,16 @@ 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()
|
||||
go AutoMigrate()
|
||||
|
||||
//db.Dao.Model(&data.Group{}).Where("id = ?", 0).FirstOrCreate(&data.Group{
|
||||
@@ -67,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()
|
||||
@@ -124,7 +136,7 @@ func main() {
|
||||
|
||||
// Create application with options
|
||||
err = wails.Run(&options.App{
|
||||
Title: "go-stock:AI赋能股票分析✨",
|
||||
Title: "go-stock:AI赋能股票分析✨ " + OFFICIAL_STATEMENT,
|
||||
Width: width * 4 / 5,
|
||||
Height: 920,
|
||||
MinWidth: minWidth,
|
||||
@@ -140,16 +152,16 @@ func main() {
|
||||
BackgroundColour: backgroundColour,
|
||||
Assets: assets,
|
||||
Menu: AppMenu,
|
||||
Logger: nil,
|
||||
Logger: logger.NewFileLogger("./logs/wails.log"),
|
||||
LogLevel: logger.DEBUG,
|
||||
LogLevelProduction: logger.ERROR,
|
||||
LogLevelProduction: logger.INFO,
|
||||
OnStartup: app.startup,
|
||||
OnDomReady: app.domReady,
|
||||
OnBeforeClose: app.beforeClose,
|
||||
OnShutdown: app.shutdown,
|
||||
WindowStartState: options.Normal,
|
||||
SingleInstanceLock: &options.SingleInstanceLock{
|
||||
UniqueId: "go-stock-dev",
|
||||
UniqueId: "go-stock",
|
||||
OnSecondInstanceLaunch: OnSecondInstanceLaunch,
|
||||
},
|
||||
Bind: []interface{}{
|
||||
@@ -177,7 +189,7 @@ func main() {
|
||||
WindowIsTranslucent: true,
|
||||
About: &mac.AboutInfo{
|
||||
Title: "go-stock",
|
||||
Message: "",
|
||||
Message: "go-stock:AI赋能股票分析✨ ",
|
||||
Icon: icon,
|
||||
},
|
||||
},
|
||||
@@ -229,8 +241,11 @@ 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{})
|
||||
db.Dao.AutoMigrate(&models.AiRecommendStocks{})
|
||||
|
||||
updateMultipleModel()
|
||||
//updateMultipleModel()
|
||||
}
|
||||
|
||||
func initStockDataUS(ctx context.Context) {
|
||||
|
||||
Reference in New Issue
Block a user