Compare commits
17 Commits
v2025.11.2
...
v2025.11.2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7bbc6831f4 | ||
|
|
ab0ccc4fe0 | ||
|
|
fa658357c9 | ||
|
|
746589a972 | ||
|
|
401dd17fa8 | ||
|
|
c365bd9534 | ||
|
|
104ee51e13 | ||
|
|
00f3e5f0e0 | ||
|
|
483ffa2244 | ||
|
|
63d278b9aa | ||
|
|
5621d40c71 | ||
|
|
26e9753b94 | ||
|
|
b7f6dbd2da | ||
|
|
18dd01b613 | ||
|
|
81bb33a135 | ||
|
|
9926b61fac | ||
|
|
5e975b060c |
@@ -1,10 +1,12 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/wailsapp/wails/v2/pkg/runtime"
|
||||
"go-stock/backend/agent"
|
||||
"go-stock/backend/data"
|
||||
"go-stock/backend/models"
|
||||
"strings"
|
||||
|
||||
"github.com/wailsapp/wails/v2/pkg/runtime"
|
||||
)
|
||||
|
||||
// @Author spark
|
||||
@@ -26,48 +28,66 @@ 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) data.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 {
|
||||
if text == "" {
|
||||
telegraphs := data.NewMarketNewsApi().GetNews24HoursList("", 1000)
|
||||
messageText := strings.Builder{}
|
||||
for _, telegraph := range *telegraphs {
|
||||
messageText.WriteString(telegraph.Content + "\n")
|
||||
}
|
||||
text = messageText.String()
|
||||
}
|
||||
result, frequencies := data.AnalyzeSentimentWithFreqWeight(text)
|
||||
// 过滤标点符号和分隔符
|
||||
cleanFrequencies := data.FilterAndSortWords(frequencies)
|
||||
return map[string]any{
|
||||
"result": result,
|
||||
"frequencies": cleanFrequencies,
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
479
backend/data/data/dict/base.txt
Normal file
479
backend/data/data/dict/base.txt
Normal file
@@ -0,0 +1,479 @@
|
||||
# 金融股票全场景分词字典(最终去重优化版)
|
||||
# 格式:单词 权重 词性 | 权重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
|
||||
FTSE China A50 330 n
|
||||
恒生指数 340 n
|
||||
HSI 330 n
|
||||
恒生科技指数 340 n
|
||||
恒生国企指数 330 n
|
||||
H股指数 330 n
|
||||
# 美洲市场
|
||||
道琼斯工业平均指数 350 n
|
||||
DJIA 340 n
|
||||
标普500指数 350 n
|
||||
S&P 500 340 n
|
||||
纳斯达克综合指数 340 n
|
||||
纳斯达克100指数 340 n
|
||||
Nasdaq 100 330 n
|
||||
罗素2000指数 320 n
|
||||
Russell 2000 310 n
|
||||
标普400中型股指数 310 n
|
||||
标普600小型股指数 310 n
|
||||
纽约证交所综合指数 310 n
|
||||
NYSE Composite 300 n
|
||||
纳斯达克中国金龙指数 310 n
|
||||
# 欧洲市场
|
||||
德国DAX指数 330 n
|
||||
DAX 30 320 n
|
||||
法国CAC40指数 330 n
|
||||
CAC 40 320 n
|
||||
富时100指数 330 n
|
||||
FTSE 100 320 n
|
||||
欧元斯托克50指数 320 n
|
||||
Euro Stoxx 50 310 n
|
||||
英国富时250指数 310 n
|
||||
FTSE 250 300 n
|
||||
意大利富时MIB指数 310 n
|
||||
FTSE MIB 300 n
|
||||
西班牙IBEX 35指数 310 n
|
||||
IBEX 35 300 n
|
||||
# 亚太其他市场
|
||||
日经225指数 330 n
|
||||
Nikkei 225 320 n
|
||||
日经500指数 310 n
|
||||
韩国综合股价指数 320 n
|
||||
韩国kospi指数 320 n
|
||||
KOSPI 310 n
|
||||
澳洲标普200指数 310 n
|
||||
S&P/ASX 200 300 n
|
||||
印度孟买敏感指数 310 n
|
||||
Sensex 300 n
|
||||
印度Nifty 50指数 310 n
|
||||
Nifty 50 300 n
|
||||
# 全球综合指数
|
||||
MSCI指数 320 n
|
||||
MSCI全球指数 330 n
|
||||
MSCI World Index 320 n
|
||||
MSCI新兴市场指数 330 n
|
||||
MSCI Emerging Markets 320 n
|
||||
富时罗素全球指数 320 n
|
||||
FTSE Russell Global Index 310 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
|
||||
|
||||
# 四、A股龙头公司(资讯高频)
|
||||
贵州茅台 310 n
|
||||
宁德时代 350 n
|
||||
比亚迪 340 n
|
||||
隆基绿能 300 n
|
||||
长江电力 290 n
|
||||
中国平安 300 n
|
||||
招商银行 300 n
|
||||
五粮液 290 n
|
||||
美的集团 290 n
|
||||
格力电器 290 n
|
||||
海康威视 290 n
|
||||
迈瑞医疗 290 n
|
||||
恒瑞医药 290 n
|
||||
中芯国际 300 n
|
||||
中兴通讯 290 n
|
||||
东方财富 290 n
|
||||
爱尔眼科 290 n
|
||||
通威股份 290 n
|
||||
药明康德 320 n
|
||||
阳光电源 290 n
|
||||
天齐锂业 290 n
|
||||
赣锋锂业 290 n
|
||||
中国中免 290 n
|
||||
海螺水泥 280 n
|
||||
万科A 280 n
|
||||
保利发展 280 n
|
||||
招商蛇口 280 n
|
||||
上汽集团 280 n
|
||||
宝钢股份 280 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
26
backend/data/data/dict/user.txt
Normal file
26
backend/data/data/dict/user.txt
Normal file
@@ -0,0 +1,26 @@
|
||||
公司 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
|
||||
冰雪旅游 500 n
|
||||
特高压 500 n
|
||||
跨境电商 500 n
|
||||
俄乌冲突 400 n
|
||||
新能源汽车 500 n
|
||||
机器人 500 n
|
||||
具身智能 500 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
@@ -45,6 +45,9 @@ func (m MarketNewsApi) TelegraphList(crawlTimeOut int64) *[]models.Telegraph {
|
||||
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 {
|
||||
@@ -94,7 +97,7 @@ func (m MarketNewsApi) TelegraphList(crawlTimeOut int64) *[]models.Telegraph {
|
||||
return &telegraphs
|
||||
}
|
||||
func GetLevel(s string) bool {
|
||||
return s >= "C"
|
||||
return s > "C"
|
||||
}
|
||||
|
||||
func (m MarketNewsApi) GetNewTelegraph(crawlTimeOut int64) *[]models.Telegraph {
|
||||
@@ -1024,3 +1027,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("id 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("id 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
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
"跌": 1.0, "下跌": 2.0, "跌停": 3.0, "熊市": 3.0, "回调": 1.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,
|
||||
"利空消息": 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.5, "暴跌": 3.0, "崩盘": 3.0, "跳水": 3.0, "重挫": 3.0, "跌超": 2.5, "跌逾": 2.5,
|
||||
"被抓": 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,12 +66,230 @@ 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() {
|
||||
//err := seg.LoadDictEmbed()
|
||||
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+500, "n")
|
||||
if strutil.Trim(stock.BKName) != "" {
|
||||
err = seg.AddToken(stock.BKName, basefreq+500, "n")
|
||||
}
|
||||
if err != nil {
|
||||
logger.SugaredLogger.Errorf("添加%s失败:%s", stock.Name, err.Error())
|
||||
}
|
||||
}
|
||||
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+500, "n")
|
||||
if strutil.Trim(stock.BKName) != "" {
|
||||
err = seg.AddToken(stock.BKName, basefreq+500, "n")
|
||||
}
|
||||
if err != nil {
|
||||
logger.SugaredLogger.Errorf("添加%s失败:%s", stock.Name, err.Error())
|
||||
}
|
||||
}
|
||||
//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{}).Find(tags)
|
||||
for _, tag := range *tags {
|
||||
err := seg.AddToken(tag.Name, basefreq, "n")
|
||||
if err != nil {
|
||||
logger.SugaredLogger.Errorf("添加%s失败:%s", tag.Name, err.Error())
|
||||
}
|
||||
}
|
||||
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, " ")
|
||||
switch len(k) {
|
||||
case 1:
|
||||
err = seg.ReAddToken(k[0], 100)
|
||||
case 2:
|
||||
freq, _ := convertor.ToFloat(k[1])
|
||||
err = seg.ReAddToken(k[0], freq)
|
||||
case 3:
|
||||
freq, _ := convertor.ToFloat(k[1])
|
||||
err = seg.ReAddToken(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("加载用户词典成功")
|
||||
}
|
||||
}
|
||||
seg.CalcToken()
|
||||
}
|
||||
|
||||
// WordFreqWithWeight 词频统计结果,包含权重信息
|
||||
type WordFreqWithWeight struct {
|
||||
Word string
|
||||
Frequency int
|
||||
Weight float64
|
||||
Score float64
|
||||
}
|
||||
|
||||
// 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 basefreq + freq
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// SortByWeightAndFrequency 按权重和频次排序词频结果
|
||||
func SortByWeightAndFrequency(frequencies map[string]WordFreqWithWeight) []WordFreqWithWeight {
|
||||
// 将map转换为slice以便排序
|
||||
freqSlice := make([]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]WordFreqWithWeight) []WordFreqWithWeight {
|
||||
// 先过滤标点符号和分隔符
|
||||
cleanFrequencies := FilterPunctuationAndSeparators(frequencies)
|
||||
|
||||
// 再按权重和频次排序
|
||||
sortedFrequencies := SortByWeightAndFrequency(cleanFrequencies)
|
||||
|
||||
return sortedFrequencies
|
||||
}
|
||||
func FilterPunctuationAndSeparators(frequencies map[string]WordFreqWithWeight) map[string]WordFreqWithWeight {
|
||||
filteredWords := make(map[string]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]WordFreqWithWeight) map[string]WordFreqWithWeight {
|
||||
filteredWords := make(map[string]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]WordFreqWithWeight {
|
||||
words := splitWords(text)
|
||||
freqMap := make(map[string]WordFreqWithWeight)
|
||||
|
||||
// 统计词频
|
||||
wordCount := make(map[string]int)
|
||||
for _, word := range words {
|
||||
wordCount[word]++
|
||||
}
|
||||
|
||||
// 构建包含权重的结果
|
||||
for word, frequency := range wordCount {
|
||||
weight := getWordWeight(word)
|
||||
if weight > 200 {
|
||||
freqMap[word] = WordFreqWithWeight{
|
||||
Word: word,
|
||||
Frequency: frequency,
|
||||
Weight: weight,
|
||||
Score: float64(frequency) * weight,
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return freqMap
|
||||
}
|
||||
|
||||
// AnalyzeSentimentWithFreqWeight 带权重词频统计的情感分析
|
||||
func AnalyzeSentimentWithFreqWeight(text string) (SentimentResult, map[string]WordFreqWithWeight) {
|
||||
// 原有情感分析逻辑
|
||||
result := AnalyzeSentiment(text)
|
||||
|
||||
// 带权重的词频统计
|
||||
frequencies := countWordFrequencyWithWeight(text)
|
||||
|
||||
return result, frequencies
|
||||
}
|
||||
|
||||
// SentimentResult 情感分析结果类型
|
||||
|
||||
@@ -2,6 +2,8 @@ package data
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go-stock/backend/db"
|
||||
"go-stock/backend/logger"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
@@ -12,25 +14,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
frontend/components.d.ts
vendored
1
frontend/components.d.ts
vendored
@@ -11,6 +11,7 @@ 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']
|
||||
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']
|
||||
|
||||
@@ -53,6 +53,7 @@ const containerRef = ref({})
|
||||
const realtimeProfit = ref(0)
|
||||
const telegraph = ref([])
|
||||
const groupList = ref([])
|
||||
const officialStatement= ref("")
|
||||
const menuOptions = ref([
|
||||
{
|
||||
label: () =>
|
||||
@@ -599,6 +600,7 @@ onBeforeMount(() => {
|
||||
GetVersionInfo().then(result => {
|
||||
if(result.officialStatement){
|
||||
content.value = result.officialStatement+"\n\n"+content.value
|
||||
officialStatement.value = result.officialStatement
|
||||
}
|
||||
})
|
||||
|
||||
@@ -669,7 +671,7 @@ onBeforeMount(() => {
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
WindowSetTitle("go-stock:AI赋能股票分析✨ 未经授权,禁止商业目的! [数据来源于网络,仅供参考;投资有风险,入市需谨慎]")
|
||||
WindowSetTitle("go-stock:AI赋能股票分析✨ "+officialStatement.value+" 未经授权,禁止商业目的! [数据来源于网络,仅供参考;投资有风险,入市需谨慎]")
|
||||
contentStyle.value = "max-height: calc(92vh);overflow: hidden"
|
||||
GetConfig().then((res) => {
|
||||
if (res.enableNews) {
|
||||
|
||||
262
frontend/src/components/AnalyzeMartket.vue
Normal file
262
frontend/src/components/AnalyzeMartket.vue
Normal file
@@ -0,0 +1,262 @@
|
||||
<script setup>
|
||||
|
||||
import {AnalyzeSentimentWithFreqWeight} 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 chartRef = ref(null);
|
||||
const gaugeChartRef = ref(null);
|
||||
let handleChartInterval=null
|
||||
onMounted(() => {
|
||||
handleChart()
|
||||
handleChartInterval=setInterval(function () {
|
||||
handleChart()
|
||||
}, 1000 * 60)
|
||||
})
|
||||
|
||||
onUnmounted(()=>{
|
||||
clearInterval(handleChartInterval)
|
||||
})
|
||||
|
||||
function handleChart(){
|
||||
const formatUtil = echarts.format;
|
||||
AnalyzeSentimentWithFreqWeight("").then((res) => {
|
||||
console.log(res)
|
||||
const treemapchart = echarts.init(chartRef.value);
|
||||
const gaugeChart=echarts.init(gaugeChartRef.value);
|
||||
let data = res['frequencies'].map(item => ({
|
||||
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;
|
||||
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-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>
|
||||
</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";
|
||||
@@ -75,6 +76,8 @@ const indexInterval = ref(null)
|
||||
const indexIndustryRank = ref(null)
|
||||
const stockCode= ref('')
|
||||
const enableTools= ref(true)
|
||||
const treemapRef = ref(null);
|
||||
let treemapchart =null;
|
||||
|
||||
function getIndex() {
|
||||
GlobalStockIndexes().then((res) => {
|
||||
@@ -120,7 +123,12 @@ onBeforeMount(() => {
|
||||
indexIndustryRank.value = setInterval(() => {
|
||||
industryRank()
|
||||
}, 1000 * 10)
|
||||
|
||||
|
||||
})
|
||||
onMounted(() => {
|
||||
})
|
||||
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
EventsOff("changeMarketTab")
|
||||
@@ -131,8 +139,12 @@ onBeforeUnmount(() => {
|
||||
clearInterval(indexIndustryRank.value)
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
|
||||
});
|
||||
EventsOn("changeMarketTab", async (msg) => {
|
||||
//message.info(msg.name)
|
||||
console.log(msg.name)
|
||||
updateTab(msg.name)
|
||||
})
|
||||
|
||||
@@ -320,14 +332,22 @@ 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="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-grid>
|
||||
</n-gi>
|
||||
</n-grid>
|
||||
|
||||
</n-tab-pane>
|
||||
<n-tab-pane name="全球股指" tab="全球股指">
|
||||
<n-tabs type="segment" animated>
|
||||
|
||||
2
frontend/wailsjs/go/main/App.d.ts
vendored
2
frontend/wailsjs/go/main/App.d.ts
vendored
@@ -14,6 +14,8 @@ export function AddStockGroup(arg1:number,arg2:string):Promise<string>;
|
||||
|
||||
export function AnalyzeSentiment(arg1:string):Promise<data.SentimentResult>;
|
||||
|
||||
export function AnalyzeSentimentWithFreqWeight(arg1:string):Promise<Record<string, any>>;
|
||||
|
||||
export function ChatWithAgent(arg1:string,arg2:number,arg3:any):Promise<void>;
|
||||
|
||||
export function CheckSponsorCode(arg1:string):Promise<Record<string, any>>;
|
||||
|
||||
@@ -22,6 +22,10 @@ export function AnalyzeSentiment(arg1) {
|
||||
return window['go']['main']['App']['AnalyzeSentiment'](arg1);
|
||||
}
|
||||
|
||||
export function AnalyzeSentimentWithFreqWeight(arg1) {
|
||||
return window['go']['main']['App']['AnalyzeSentimentWithFreqWeight'](arg1);
|
||||
}
|
||||
|
||||
export function ChatWithAgent(arg1, arg2, arg3) {
|
||||
return window['go']['main']['App']['ChatWithAgent'](arg1, arg2, arg3);
|
||||
}
|
||||
|
||||
5
main.go
5
main.go
@@ -60,6 +60,7 @@ var BuildKey string
|
||||
func main() {
|
||||
checkDir("data")
|
||||
db.Init("")
|
||||
data.InitAnalyzeSentiment()
|
||||
go AutoMigrate()
|
||||
|
||||
//db.Dao.Model(&data.Group{}).Where("id = ?", 0).FirstOrCreate(&data.Group{
|
||||
@@ -124,7 +125,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,
|
||||
@@ -149,7 +150,7 @@ func main() {
|
||||
OnShutdown: app.shutdown,
|
||||
WindowStartState: options.Normal,
|
||||
SingleInstanceLock: &options.SingleInstanceLock{
|
||||
UniqueId: "go-stock-dev",
|
||||
UniqueId: "go-stock",
|
||||
OnSecondInstanceLaunch: OnSecondInstanceLaunch,
|
||||
},
|
||||
Bind: []interface{}{
|
||||
|
||||
Reference in New Issue
Block a user