fix bug
This commit is contained in:
@@ -22,6 +22,18 @@ func main() {
|
||||
config.New(ServiceKey)
|
||||
impl.NewImpl()
|
||||
|
||||
code := "601899.SH"
|
||||
model := models.NewStratModel("selector", code)
|
||||
stratRule := rule.NewRule(model)
|
||||
stratRule.RunAi(code)
|
||||
|
||||
}
|
||||
|
||||
func main2() {
|
||||
log.Println("Hello Cli!")
|
||||
config.New(ServiceKey)
|
||||
impl.NewImpl()
|
||||
|
||||
for _, code := range strategy.GetStocks() {
|
||||
strategy.InitCacheByCode(code)
|
||||
model := models.NewStratModel("selector", code)
|
||||
@@ -40,7 +52,7 @@ func main() {
|
||||
}
|
||||
}
|
||||
|
||||
func main2() {
|
||||
func main3() {
|
||||
log.Println("Hello Cli!")
|
||||
config.New(ServiceKey)
|
||||
impl.NewImpl()
|
||||
|
||||
@@ -9,6 +9,8 @@ Databases:
|
||||
# cache DB的选择请在后面直接带参数,不带会自动HASH计算选择DB库。
|
||||
Cache: redis://null:Weidong2023~!@8.137.107.29:19379/
|
||||
|
||||
# 密钥
|
||||
DeepSeekApiKey: sk-7e69ad7a07d74c409ad52bfab18be786
|
||||
|
||||
# 日志配置
|
||||
Log:
|
||||
|
||||
@@ -9,6 +9,8 @@ Databases:
|
||||
# cache DB的选择请在后面直接带参数,不带会自动HASH计算选择DB库。
|
||||
Cache: redis://null:Weidong2023~!@8.137.107.29:19379/
|
||||
|
||||
# 密钥
|
||||
DeepSeekApiKey: sk-7e69ad7a07d74c409ad52bfab18be786
|
||||
|
||||
# 日志配置
|
||||
Log:
|
||||
|
||||
@@ -9,6 +9,8 @@ Databases:
|
||||
# cache DB的选择请在后面直接带参数,不带会自动HASH计算选择DB库。
|
||||
Cache: redis://null:Weidong2023~!@8.137.107.29:19379/
|
||||
|
||||
# 密钥
|
||||
DeepSeekApiKey: sk-7e69ad7a07d74c409ad52bfab18be786
|
||||
|
||||
# 日志配置
|
||||
Log:
|
||||
|
||||
1
go.mod
1
go.mod
@@ -45,6 +45,7 @@ require (
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.10 // indirect
|
||||
github.com/gin-contrib/sse v1.1.0 // indirect
|
||||
github.com/go-deepseek/deepseek v0.8.0
|
||||
github.com/go-playground/locales v0.14.1 // indirect
|
||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||
github.com/go-playground/validator/v10 v10.28.0 // indirect
|
||||
|
||||
2
go.sum
2
go.sum
@@ -39,6 +39,8 @@ github.com/gin-contrib/sse v1.1.0 h1:n0w2GMuUpWDVp7qSpvze6fAu9iRxJY4Hmj6AmBOU05w
|
||||
github.com/gin-contrib/sse v1.1.0/go.mod h1:hxRZ5gVpWMT7Z0B0gSNYqqsSCNIJMjzvm6fqCz9vjwM=
|
||||
github.com/gin-gonic/gin v1.11.0 h1:OW/6PLjyusp2PPXtyxKHU0RbX6I/l28FTdDlae5ueWk=
|
||||
github.com/gin-gonic/gin v1.11.0/go.mod h1:+iq/FyxlGzII0KHiBGjuNn4UNENUlKbGlNmc+W50Dls=
|
||||
github.com/go-deepseek/deepseek v0.8.0 h1:uB+iC63LtWKt892Bm3H6/4YSXqlkwDVo4FRodAHMs+Y=
|
||||
github.com/go-deepseek/deepseek v0.8.0/go.mod h1:dhwH6SkBBaizgFTgzPkcKBT0kivqS17SiWYOhrtd+j8=
|
||||
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
|
||||
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||
|
||||
@@ -11,8 +11,9 @@ var (
|
||||
)
|
||||
|
||||
type SrvConfig struct {
|
||||
conf.Base `yaml:",inline"`
|
||||
Databases *conf.DBConf `yaml:"Databases"`
|
||||
conf.Base `yaml:",inline"`
|
||||
Databases *conf.DBConf `yaml:"Databases"`
|
||||
DeepSeekApiKey string `yaml:"DeepSeekApiKey"`
|
||||
}
|
||||
|
||||
func New(srvKey string) {
|
||||
|
||||
266
internal/logic/deepseek/dpsk.go
Normal file
266
internal/logic/deepseek/dpsk.go
Normal file
@@ -0,0 +1,266 @@
|
||||
package deepseek
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"mime/multipart"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
)
|
||||
|
||||
// 配置结构体
|
||||
type Config struct {
|
||||
APIKey string `json:"api_key"`
|
||||
BaseURL string `json:"base_url"`
|
||||
Model string `json:"model"`
|
||||
MaxTokens int `json:"max_tokens"`
|
||||
Temperature float64 `json:"temperature"`
|
||||
}
|
||||
|
||||
// 上传文件响应结构体
|
||||
type UploadResponse struct {
|
||||
ID string `json:"id"`
|
||||
Object string `json:"object"`
|
||||
Bytes int `json:"bytes"`
|
||||
CreatedAt int64 `json:"created_at"`
|
||||
Filename string `json:"filename"`
|
||||
Purpose string `json:"purpose"`
|
||||
}
|
||||
|
||||
// 消息结构体
|
||||
type Message struct {
|
||||
Role string `json:"role"`
|
||||
Content string `json:"content"`
|
||||
}
|
||||
|
||||
// 聊天请求结构体
|
||||
type ChatRequest struct {
|
||||
Model string `json:"model"`
|
||||
Messages []Message `json:"messages"`
|
||||
MaxTokens int `json:"max_tokens,omitempty"`
|
||||
Temperature float64 `json:"temperature,omitempty"`
|
||||
Stream bool `json:"stream,omitempty"`
|
||||
}
|
||||
|
||||
// 聊天响应结构体
|
||||
type ChatResponse struct {
|
||||
ID string `json:"id"`
|
||||
Object string `json:"object"`
|
||||
Created int64 `json:"created"`
|
||||
Model string `json:"model"`
|
||||
Choices []struct {
|
||||
Index int `json:"index"`
|
||||
Message struct {
|
||||
Role string `json:"role"`
|
||||
Content string `json:"content"`
|
||||
} `json:"message"`
|
||||
FinishReason string `json:"finish_reason"`
|
||||
} `json:"choices"`
|
||||
Usage struct {
|
||||
PromptTokens int `json:"prompt_tokens"`
|
||||
CompletionTokens int `json:"completion_tokens"`
|
||||
TotalTokens int `json:"total_tokens"`
|
||||
} `json:"usage"`
|
||||
}
|
||||
|
||||
// JSON响应包装器
|
||||
type JSONResponse struct {
|
||||
Success bool `json:"success"`
|
||||
Message string `json:"message,omitempty"`
|
||||
Data interface{} `json:"data,omitempty"`
|
||||
Error string `json:"error,omitempty"`
|
||||
}
|
||||
|
||||
// 上传文件到DeepSeek
|
||||
func UploadFile(apiKey, filePath string) (*UploadResponse, error) {
|
||||
file, err := os.Open(filePath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("打开文件失败: %v", err)
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
// 创建multipart表单
|
||||
body := &bytes.Buffer{}
|
||||
writer := multipart.NewWriter(body)
|
||||
|
||||
// 添加文件字段
|
||||
part, err := writer.CreateFormFile("file", filepath.Base(filePath))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("创建表单文件失败: %v", err)
|
||||
}
|
||||
|
||||
_, err = io.Copy(part, file)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("复制文件内容失败: %v", err)
|
||||
}
|
||||
|
||||
// 添加purpose字段
|
||||
_ = writer.WriteField("purpose", "assistants")
|
||||
|
||||
err = writer.Close()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("关闭writer失败: %v", err)
|
||||
}
|
||||
|
||||
// 创建请求
|
||||
req, err := http.NewRequest("POST", "https://api.deepseek.com/files", body)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("创建请求失败: %v", err)
|
||||
}
|
||||
|
||||
req.Header.Set("Authorization", "Bearer "+apiKey)
|
||||
req.Header.Set("Content-Type", writer.FormDataContentType())
|
||||
|
||||
// 发送请求
|
||||
client := &http.Client{Timeout: 30 * time.Second}
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("发送请求失败: %v", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
// 读取响应
|
||||
respBody, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("读取响应失败: %v", err)
|
||||
}
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return nil, fmt.Errorf("API返回错误: %d %s", resp.StatusCode, string(respBody))
|
||||
}
|
||||
|
||||
// 解析响应
|
||||
var uploadResp UploadResponse
|
||||
err = json.Unmarshal(respBody, &uploadResp)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("解析响应失败: %v", err)
|
||||
}
|
||||
|
||||
return &uploadResp, nil
|
||||
}
|
||||
|
||||
// 发送聊天请求(带文件)
|
||||
func ChatWithFile(apiKey, fileID, question string, config Config) (*ChatResponse, error) {
|
||||
// 构建消息内容
|
||||
content := fmt.Sprintf(`请分析我上传的Markdown文件并回答以下问题:
|
||||
问题:%s
|
||||
|
||||
请基于文件内容提供详细的回答。`, question)
|
||||
|
||||
// 创建聊天请求
|
||||
chatReq := ChatRequest{
|
||||
Model: config.Model,
|
||||
Messages: []Message{
|
||||
{
|
||||
Role: "user",
|
||||
Content: content,
|
||||
},
|
||||
},
|
||||
MaxTokens: config.MaxTokens,
|
||||
Temperature: config.Temperature,
|
||||
Stream: false,
|
||||
}
|
||||
|
||||
// 如果有文件ID,添加到消息中
|
||||
if fileID != "" {
|
||||
chatReq.Messages[0].Content = fmt.Sprintf(`我上传了一个Markdown文件(ID: %s),请分析这个文件并回答以下问题:
|
||||
问题:%s
|
||||
|
||||
请基于文件内容提供详细的回答。`, fileID, question)
|
||||
}
|
||||
|
||||
// 序列化请求体
|
||||
reqBody, err := json.Marshal(chatReq)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("序列化请求失败: %v", err)
|
||||
}
|
||||
|
||||
// 创建请求
|
||||
req, err := http.NewRequest("POST", config.BaseURL+"/chat/completions", bytes.NewBuffer(reqBody))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("创建请求失败: %v", err)
|
||||
}
|
||||
|
||||
req.Header.Set("Authorization", "Bearer "+apiKey)
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
|
||||
// 发送请求
|
||||
client := &http.Client{Timeout: 60 * time.Second}
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("发送请求失败: %v", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
// 读取响应
|
||||
respBody, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("读取响应失败: %v", err)
|
||||
}
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return nil, fmt.Errorf("API返回错误: %d - %s", resp.StatusCode, string(respBody))
|
||||
}
|
||||
|
||||
// 解析响应
|
||||
var chatResp ChatResponse
|
||||
err = json.Unmarshal(respBody, &chatResp)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("解析响应失败: %v", err)
|
||||
}
|
||||
|
||||
return &chatResp, nil
|
||||
}
|
||||
|
||||
// 处理Markdown文件上传和问答
|
||||
func ProcessMarkdownQA(config Config, filePath, question string) ([]byte, error) {
|
||||
var jsonResponse JSONResponse
|
||||
|
||||
// 1. 上传文件
|
||||
fmt.Println("正在上传Markdown文件...")
|
||||
uploadResp, err := UploadFile(config.APIKey, filePath)
|
||||
if err != nil {
|
||||
jsonResponse.Success = false
|
||||
jsonResponse.Error = fmt.Sprintf("上传文件失败: %v", err)
|
||||
return json.Marshal(jsonResponse)
|
||||
}
|
||||
|
||||
fmt.Printf("文件上传成功!文件ID: %s\n", uploadResp.ID)
|
||||
|
||||
// 2. 使用文件进行问答
|
||||
fmt.Println("正在分析文件并回答问题...")
|
||||
chatResp, err := ChatWithFile(config.APIKey, uploadResp.ID, question, config)
|
||||
if err != nil {
|
||||
jsonResponse.Success = false
|
||||
jsonResponse.Error = fmt.Sprintf("问答失败: %v", err)
|
||||
return json.Marshal(jsonResponse)
|
||||
}
|
||||
|
||||
// 3. 构建成功响应
|
||||
if len(chatResp.Choices) > 0 {
|
||||
responseData := map[string]interface{}{
|
||||
"file_info": map[string]interface{}{
|
||||
"file_id": uploadResp.ID,
|
||||
"filename": uploadResp.Filename,
|
||||
"size_bytes": uploadResp.Bytes,
|
||||
},
|
||||
"question": question,
|
||||
"answer": chatResp.Choices[0].Message.Content,
|
||||
"usage": chatResp.Usage,
|
||||
"model": chatResp.Model,
|
||||
}
|
||||
|
||||
jsonResponse.Success = true
|
||||
jsonResponse.Message = "处理成功"
|
||||
jsonResponse.Data = responseData
|
||||
} else {
|
||||
jsonResponse.Success = false
|
||||
jsonResponse.Error = "未收到有效回答"
|
||||
}
|
||||
|
||||
return json.MarshalIndent(jsonResponse, "", " ")
|
||||
}
|
||||
@@ -1,9 +1,6 @@
|
||||
package strategy
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"git.apinb.com/bsm-sdk/core/utils"
|
||||
"git.apinb.com/quant/gostock/internal/impl"
|
||||
"git.apinb.com/quant/gostock/internal/models"
|
||||
)
|
||||
@@ -36,462 +33,6 @@ func GetFullData(code string) *StockData {
|
||||
return &data
|
||||
}
|
||||
|
||||
func GenMarkData(code string) {
|
||||
md := NewMarkdownBuilder()
|
||||
|
||||
var basic models.StockBasic
|
||||
impl.DBService.Where("ts_code = ?", code).First(&basic)
|
||||
|
||||
md.Title(basic.TsCode + " " + basic.Name + " 股票详情")
|
||||
md.Catalog("公司基础情况")
|
||||
md.KvText("股票代码", basic.TsCode)
|
||||
md.KvText("股票名称", basic.Name)
|
||||
md.KvText("股票行业", basic.Industry)
|
||||
md.KvText("股票地域", basic.Area)
|
||||
md.KvText("上市日期", basic.ListDate)
|
||||
md.KvText("股票市场", basic.Market)
|
||||
md.KvText("股票类型", basic.Market)
|
||||
md.KvText("股票是否沪深港通", basic.IsHS)
|
||||
md.KvText("股票中文全称", basic.FullName)
|
||||
md.KvText("股票实控人", basic.ActName)
|
||||
md.KvText("股票实控人企业性质", basic.ActEntType)
|
||||
md.BR()
|
||||
|
||||
var daily []models.StockDaily
|
||||
impl.DBService.Where("ts_code = ?", code).Order("trade_date desc").Limit(200).Find(&daily)
|
||||
var data [][]string
|
||||
for _, row := range daily {
|
||||
data = append(data, []string{
|
||||
utils.Int2String(row.TradeDate),
|
||||
fmt.Sprintf("%.2f", row.Open),
|
||||
fmt.Sprintf("%.2f", row.Close),
|
||||
fmt.Sprintf("%.2f", row.High),
|
||||
fmt.Sprintf("%.2f", row.Low),
|
||||
fmt.Sprintf("%.2f", row.Vol),
|
||||
fmt.Sprintf("%.2f", row.Amount),
|
||||
})
|
||||
}
|
||||
md.Table("最近200天股票行情", []string{"日期", "开盘价", "收盘价", "最高价", "最低价", "成交量", "成交额"}, data)
|
||||
md.BR()
|
||||
|
||||
var indicator []models.StockIndicator
|
||||
impl.DBService.Where("ts_code = ?", code).Order("trade_date desc").Limit(200).Find(&indicator)
|
||||
var indiData [][]string
|
||||
for _, row := range indicator {
|
||||
indiData = append(indiData, []string{
|
||||
utils.Int2String(row.TradeDate),
|
||||
fmt.Sprintf("%.2f", row.Close),
|
||||
fmt.Sprintf("%.2f%%", row.TurnoverRate),
|
||||
fmt.Sprintf("%.2f", row.VolumeRatio),
|
||||
fmt.Sprintf("%.2f", row.Pe),
|
||||
fmt.Sprintf("%.2f", row.PeTtm),
|
||||
fmt.Sprintf("%.2f", row.Pb),
|
||||
fmt.Sprintf("%.2f", row.Ps),
|
||||
fmt.Sprintf("%.2f", row.PsTtm),
|
||||
fmt.Sprintf("%.2f", row.DvRatio),
|
||||
fmt.Sprintf("%.2f", row.DvTtm),
|
||||
fmt.Sprintf("%.2f", row.TotalShare),
|
||||
fmt.Sprintf("%.2f", row.FloatShare),
|
||||
fmt.Sprintf("%.2f", row.FreeShare),
|
||||
fmt.Sprintf("%.2f", row.TotalMv),
|
||||
fmt.Sprintf("%.2f", row.CircMv),
|
||||
})
|
||||
}
|
||||
md.Table("最近200天股票指标", []string{"日期", "当日收盘价", "换手率", "量比", "市盈率", "市盈率 TTM", "市净率", "市销率", "市销率 TTM", "股息率", "股息率 TTM", "总股本", "流通股本", "自由流通股本", "总市值", "流通市值"}, indiData)
|
||||
md.BR()
|
||||
|
||||
var finaIndicator []models.StockFinaIndicator
|
||||
impl.DBService.Where("ts_code = ?", code).Order("period desc").Find(&finaIndicator)
|
||||
var fiHeaders []string = []string{
|
||||
"报告期数",
|
||||
"公告日期",
|
||||
"报告期",
|
||||
|
||||
// 每股指标
|
||||
"基本每股收益",
|
||||
"稀释每股收益",
|
||||
"每股营业总收入",
|
||||
"每股营业收入",
|
||||
"每股资本公积",
|
||||
"每股盈余公积",
|
||||
"每股未分配利润",
|
||||
"期末摊薄每股收益",
|
||||
"每股净资产",
|
||||
"每股经营活动产生的现金流量净额",
|
||||
"每股留存收益",
|
||||
"每股现金流量净额",
|
||||
"每股息税前利润",
|
||||
"每股企业自由现金流量",
|
||||
"每股股东自由现金流量",
|
||||
|
||||
// 利润表相关
|
||||
"非经常性损益",
|
||||
"扣除非经常性损益后的净利润",
|
||||
"毛利",
|
||||
"经营活动净收益",
|
||||
"价值变动净收益",
|
||||
"利息费用",
|
||||
"折旧与摊销",
|
||||
"息税前利润",
|
||||
"息税折旧摊销前利润",
|
||||
"企业自由现金流量",
|
||||
"股权自由现金流量",
|
||||
"研发费用",
|
||||
"固定资产合计",
|
||||
"扣除财务费用前营业利润",
|
||||
"非营业利润",
|
||||
|
||||
// 资产负债表相关
|
||||
"无息流动负债",
|
||||
"无息非流动负债",
|
||||
"带息债务",
|
||||
"净债务",
|
||||
"有形资产",
|
||||
"营运资金",
|
||||
"营运流动资本",
|
||||
"全部投入资本",
|
||||
"留存收益",
|
||||
|
||||
// 偿债能力指标
|
||||
"流动比率",
|
||||
"速动比率",
|
||||
"保守速动比率",
|
||||
"资产负债率",
|
||||
"权益乘数",
|
||||
"权益乘数(杜邦分析)",
|
||||
"产权比率",
|
||||
"归属于母公司的股东权益/负债合计",
|
||||
"经营活动产生的现金流量净额/流动负债",
|
||||
"已获利息倍数",
|
||||
|
||||
// 运营能力指标
|
||||
"存货周转天数",
|
||||
"应收账款周转天数",
|
||||
"存货周转率",
|
||||
"应收账款周转率",
|
||||
"流动资产周转率",
|
||||
"固定资产周转率",
|
||||
"总资产周转率",
|
||||
"营业周期",
|
||||
|
||||
// 盈利能力指标
|
||||
"销售净利率",
|
||||
"销售毛利率",
|
||||
"销售成本率",
|
||||
"销售期间费用率",
|
||||
"净资产收益率",
|
||||
"加权平均净资产收益率",
|
||||
"净资产收益率(扣除非经常损益)",
|
||||
"总资产报酬率",
|
||||
"总资产净利润",
|
||||
"投入资本回报率",
|
||||
"总资产净利率(杜邦分析)",
|
||||
|
||||
// 结构指标
|
||||
"流动资产/总资产",
|
||||
"非流动资产/总资产",
|
||||
"有形资产/总资产",
|
||||
"带息债务/全部投入资本",
|
||||
"归属于母公司的股东权益/全部投入资本",
|
||||
"流动负债/负债合计",
|
||||
"非流动负债/负债合计",
|
||||
"有形资产/负债合计",
|
||||
|
||||
// 单季度指标
|
||||
"经营活动单季度净收益",
|
||||
"价值变动单季度净收益",
|
||||
"扣除非经常损益后的单季度净利润",
|
||||
"每股收益(单季度)",
|
||||
"销售净利率(单季度)",
|
||||
"销售毛利率(单季度)",
|
||||
"销售期间费用率(单季度)",
|
||||
"净资产收益率(单季度)",
|
||||
"净资产单季度收益率(扣除非经常损益)",
|
||||
"总资产净利润(单季度)",
|
||||
|
||||
// 同比增长率
|
||||
"基本每股收益同比增长率(%)",
|
||||
"稀释每股收益同比增长率(%)",
|
||||
"每股经营活动产生的现金流量净额同比增长率(%)",
|
||||
"营业利润同比增长率(%)",
|
||||
"利润总额同比增长率(%)",
|
||||
"归属母公司股东的净利润同比增长率(%)",
|
||||
"归属母公司股东的净利润-扣除非经常损益同比增长率(%)",
|
||||
"经营活动产生的现金流量净额同比增长率(%)",
|
||||
"净资产收益率(摊薄)同比增长率(%)",
|
||||
"每股净资产相对年初增长率(%)",
|
||||
"资产总计相对年初增长率(%)",
|
||||
"归属母公司的股东权益相对年初增长率(%)",
|
||||
"营业总收入同比增长率(%)",
|
||||
"营业收入同比增长率(%)",
|
||||
"净资产同比增长率",
|
||||
|
||||
// 其他比率指标
|
||||
"净利润/营业总收入",
|
||||
"销售费用/营业总收入",
|
||||
"管理费用/营业总收入",
|
||||
"财务费用/营业总收入",
|
||||
"资产减值损失/营业总收入",
|
||||
"营业总成本/营业总收入",
|
||||
"营业利润/营业总收入",
|
||||
"息税前利润/营业总收入",
|
||||
"经营活动净收益/利润总额",
|
||||
"价值变动净收益/利润总额",
|
||||
"营业外收支净额/利润总额",
|
||||
"所得税/利润总额",
|
||||
"扣除非经常损益后的净利润/净利润",
|
||||
"销售商品提供劳务收到的现金/营业收入",
|
||||
"经营活动产生的现金流量净额/营业收入",
|
||||
"经营活动产生的现金流量净额/经营活动净收益",
|
||||
"资本支出/折旧和摊销",
|
||||
"经营活动产生的现金流量净额/负债合计",
|
||||
"经营活动产生的现金流量净额/带息债务",
|
||||
"经营活动产生的现金流量净额/净债务",
|
||||
"长期债务与营运资金比率",
|
||||
"息税折旧摊销前利润/负债合计",
|
||||
"营业利润/利润总额",
|
||||
"非营业利润/利润总额",
|
||||
"经营活动产生的现金流量净额/营业利润",
|
||||
"货币资金/流动负债",
|
||||
"货币资金/带息流动负债",
|
||||
"营业利润/流动负债",
|
||||
"营业利润/负债合计",
|
||||
"利润总额/营业收入",
|
||||
|
||||
// 年度化指标
|
||||
"年化净资产收益率",
|
||||
"年化总资产报酬率",
|
||||
"年化总资产净利率",
|
||||
"年化投入资本回报率",
|
||||
"平均净资产收益率(增发条件)",
|
||||
|
||||
// 单季度增长比率
|
||||
"营业总收入同比增长率(%)(单季度)",
|
||||
"营业总收入环比增长率(%)(单季度)",
|
||||
"营业收入同比增长率(%)(单季度)",
|
||||
"营业收入环比增长率(%)(单季度)",
|
||||
"营业利润同比增长率(%)(单季度)",
|
||||
"营业利润环比增长率(%)(单季度)",
|
||||
"净利润同比增长率(%)(单季度)",
|
||||
"净利润环比增长率(%)(单季度)",
|
||||
"归属母公司股东的净利润同比增长率(%)(单季度)",
|
||||
"归属母公司股东的净利润环比增长率(%)(单季度)",
|
||||
|
||||
// 单季度比率指标
|
||||
"净利润/营业总收入(单季度)",
|
||||
"销售费用/营业总收入 (单季度)",
|
||||
"管理费用/营业总收入 (单季度)",
|
||||
"财务费用/营业总收入 (单季度)",
|
||||
"资产减值损失/营业总收入(单季度)",
|
||||
"营业总成本/营业总收入 (单季度)",
|
||||
"营业利润/营业总收入(单季度)",
|
||||
"经营活动净收益/利润总额(单季度)",
|
||||
"价值变动净收益/利润总额(单季度)",
|
||||
"扣除非经常损益后的净利润/净利润(单季度)",
|
||||
"销售商品提供劳务收到的现金/营业收入(单季度)",
|
||||
"经营活动产生的现金流量净额/营业收入(单季度)",
|
||||
"经营活动产生的现金流量净额/经营活动净收益(单季度)",
|
||||
}
|
||||
var fiData [][]string
|
||||
for _, row := range finaIndicator {
|
||||
fiData = append(fiData, []string{
|
||||
fmt.Sprintf("%d", row.Period),
|
||||
row.AnnDate,
|
||||
row.EndDate,
|
||||
|
||||
// 每股指标
|
||||
fmt.Sprintf("%.2f", row.Eps),
|
||||
fmt.Sprintf("%.2f", row.DtEps),
|
||||
fmt.Sprintf("%.2f", row.TotalRevenuePs),
|
||||
fmt.Sprintf("%.2f", row.RevenuePs),
|
||||
fmt.Sprintf("%.2f", row.CapitalResePs),
|
||||
fmt.Sprintf("%.2f", row.SurplusResePs),
|
||||
fmt.Sprintf("%.2f", row.UndistProfitPs),
|
||||
fmt.Sprintf("%.2f", row.Diluted2Eps),
|
||||
fmt.Sprintf("%.2f", row.Bps),
|
||||
fmt.Sprintf("%.2f", row.Ocfps),
|
||||
fmt.Sprintf("%.2f", row.Retainedps),
|
||||
fmt.Sprintf("%.2f", row.Cfps),
|
||||
fmt.Sprintf("%.2f", row.EbitPs),
|
||||
fmt.Sprintf("%.2f", row.FcffPs),
|
||||
fmt.Sprintf("%.2f", row.FcfePs),
|
||||
|
||||
// 利润表相关
|
||||
fmt.Sprintf("%.2f", row.ExtraItem),
|
||||
fmt.Sprintf("%.2f", row.ProfitDedt),
|
||||
fmt.Sprintf("%.2f", row.GrossMargin),
|
||||
fmt.Sprintf("%.2f", row.OpIncome),
|
||||
fmt.Sprintf("%.2f", row.ValuechangeIncome),
|
||||
fmt.Sprintf("%.2f", row.InterstIncome),
|
||||
fmt.Sprintf("%.2f", row.Daa),
|
||||
fmt.Sprintf("%.2f", row.Ebit),
|
||||
fmt.Sprintf("%.2f", row.Ebitda),
|
||||
fmt.Sprintf("%.2f", row.Fcff),
|
||||
fmt.Sprintf("%.2f", row.Fcfe),
|
||||
fmt.Sprintf("%.2f", row.RdExp),
|
||||
fmt.Sprintf("%.2f", row.FixedAssets),
|
||||
fmt.Sprintf("%.2f", row.ProfitPrefinExp),
|
||||
fmt.Sprintf("%.2f", row.NonOpProfit),
|
||||
|
||||
// 资产负债表相关
|
||||
fmt.Sprintf("%.2f", row.CurrentExint),
|
||||
fmt.Sprintf("%.2f", row.NoncurrentExint),
|
||||
fmt.Sprintf("%.2f", row.Interestdebt),
|
||||
fmt.Sprintf("%.2f", row.Netdebt),
|
||||
fmt.Sprintf("%.2f", row.TangibleAsset),
|
||||
fmt.Sprintf("%.2f", row.WorkingCapital),
|
||||
fmt.Sprintf("%.2f", row.NetworkingCapital),
|
||||
fmt.Sprintf("%.2f", row.InvestCapital),
|
||||
fmt.Sprintf("%.2f", row.RetainedEarnings),
|
||||
|
||||
// 偿债能力指标
|
||||
fmt.Sprintf("%.2f", row.CurrentRatio),
|
||||
fmt.Sprintf("%.2f", row.QuickRatio),
|
||||
fmt.Sprintf("%.2f", row.CashRatio),
|
||||
fmt.Sprintf("%.2f", row.DebtToAssets),
|
||||
fmt.Sprintf("%.2f", row.AssetsToEqt),
|
||||
fmt.Sprintf("%.2f", row.DpAssetsToEqt),
|
||||
fmt.Sprintf("%.2f", row.DebtToEqt),
|
||||
fmt.Sprintf("%.2f", row.EqtToDebt),
|
||||
fmt.Sprintf("%.2f", row.OcfToShortdebt),
|
||||
fmt.Sprintf("%.2f", row.EbitToInterest),
|
||||
|
||||
// 运营能力指标
|
||||
fmt.Sprintf("%.2f", row.InvturnDays),
|
||||
fmt.Sprintf("%.2f", row.ArturnDays),
|
||||
fmt.Sprintf("%.2f", row.InvTurn),
|
||||
fmt.Sprintf("%.2f", row.ArTurn),
|
||||
fmt.Sprintf("%.2f", row.CaTurn),
|
||||
fmt.Sprintf("%.2f", row.FaTurn),
|
||||
fmt.Sprintf("%.2f", row.AssetsTurn),
|
||||
fmt.Sprintf("%.2f", row.TurnDays),
|
||||
fmt.Sprintf("%.2f", row.TotalFaTrun),
|
||||
|
||||
// 盈利能力指标
|
||||
fmt.Sprintf("%.2f", row.NetprofitMargin),
|
||||
fmt.Sprintf("%.2f", row.GrossprofitMargin),
|
||||
fmt.Sprintf("%.2f", row.CogsOfSales),
|
||||
fmt.Sprintf("%.2f", row.ExpenseOfSales),
|
||||
fmt.Sprintf("%.2f", row.Roe),
|
||||
fmt.Sprintf("%.2f", row.RoeWaa),
|
||||
fmt.Sprintf("%.2f", row.RoeDt),
|
||||
fmt.Sprintf("%.2f", row.Roa),
|
||||
fmt.Sprintf("%.2f", row.Npta),
|
||||
fmt.Sprintf("%.2f", row.Roic),
|
||||
fmt.Sprintf("%.2f", row.RoaDp),
|
||||
|
||||
// 结构指标
|
||||
fmt.Sprintf("%.2f", row.CaToAssets),
|
||||
fmt.Sprintf("%.2f", row.NcaToAssets),
|
||||
fmt.Sprintf("%.2f", row.TbassetsToTotalassets),
|
||||
fmt.Sprintf("%.2f", row.IntToTalcap),
|
||||
fmt.Sprintf("%.2f", row.EqtToTalcapital),
|
||||
fmt.Sprintf("%.2f", row.CurrentdebtToDebt),
|
||||
fmt.Sprintf("%.2f", row.LongdebToDebt),
|
||||
fmt.Sprintf("%.2f", row.TangibleassetToDebt),
|
||||
|
||||
// 单季度指标
|
||||
fmt.Sprintf("%.2f", row.QOpincome),
|
||||
fmt.Sprintf("%.2f", row.QInvestincome),
|
||||
fmt.Sprintf("%.2f", row.QDtprofit),
|
||||
fmt.Sprintf("%.2f", row.QEps),
|
||||
fmt.Sprintf("%.2f", row.QNetprofitMargin),
|
||||
fmt.Sprintf("%.2f", row.QGscaleprofitMargin),
|
||||
fmt.Sprintf("%.2f", row.QExpToSales),
|
||||
fmt.Sprintf("%.2f", row.QRoe),
|
||||
fmt.Sprintf("%.2f", row.QDtRoe),
|
||||
fmt.Sprintf("%.2f", row.QNpta),
|
||||
|
||||
// 同比增长率
|
||||
fmt.Sprintf("%.2f%%", row.BasicEpsYoy),
|
||||
fmt.Sprintf("%.2f%%", row.DtEpsYoy),
|
||||
fmt.Sprintf("%.2f%%", row.CfpsYoy),
|
||||
fmt.Sprintf("%.2f%%", row.OpYoy),
|
||||
fmt.Sprintf("%.2f%%", row.EbtYoy),
|
||||
fmt.Sprintf("%.2f%%", row.NetprofitYoy),
|
||||
fmt.Sprintf("%.2f%%", row.DtNetprofitYoy),
|
||||
fmt.Sprintf("%.2f%%", row.OcfYoy),
|
||||
fmt.Sprintf("%.2f%%", row.RoeYoy),
|
||||
fmt.Sprintf("%.2f%%", row.BpsYoy),
|
||||
fmt.Sprintf("%.2f%%", row.AssetsYoy),
|
||||
fmt.Sprintf("%.2f%%", row.EqtYoy),
|
||||
fmt.Sprintf("%.2f%%", row.TrYoy),
|
||||
fmt.Sprintf("%.2f%%", row.OrYoy),
|
||||
fmt.Sprintf("%.2f%%", row.EquityYoy),
|
||||
|
||||
// 其他比率指标
|
||||
fmt.Sprintf("%.2f", row.ProfitToGr),
|
||||
fmt.Sprintf("%.2f", row.SaleexpToGr),
|
||||
fmt.Sprintf("%.2f", row.AdminexpOfGr),
|
||||
fmt.Sprintf("%.2f", row.FinaexpOfGr),
|
||||
fmt.Sprintf("%.2f", row.ImpaiTtm),
|
||||
fmt.Sprintf("%.2f", row.GcOfGr),
|
||||
fmt.Sprintf("%.2f", row.OpOfGr),
|
||||
fmt.Sprintf("%.2f", row.EbitOfGr),
|
||||
fmt.Sprintf("%.2f", row.OpincomeOfEbt),
|
||||
fmt.Sprintf("%.2f", row.InvestincomeOfEbt),
|
||||
fmt.Sprintf("%.2f", row.NOpProfitOfEbt),
|
||||
fmt.Sprintf("%.2f", row.TaxToEbt),
|
||||
fmt.Sprintf("%.2f", row.DtprofitToProfit),
|
||||
fmt.Sprintf("%.2f", row.SalescashToOr),
|
||||
fmt.Sprintf("%.2f", row.OcfToOr),
|
||||
fmt.Sprintf("%.2f", row.OcfToOpincome),
|
||||
fmt.Sprintf("%.2f", row.CapitalizedToDa),
|
||||
fmt.Sprintf("%.2f", row.OcfToDebt),
|
||||
fmt.Sprintf("%.2f", row.OcfToInterestdebt),
|
||||
fmt.Sprintf("%.2f", row.OcfToNetdebt),
|
||||
fmt.Sprintf("%.2f", row.LongdebtToWorkingcapital),
|
||||
fmt.Sprintf("%.2f", row.EbitdaToDebt),
|
||||
fmt.Sprintf("%.2f", row.OpToEbt),
|
||||
fmt.Sprintf("%.2f", row.NopToEbt),
|
||||
fmt.Sprintf("%.2f", row.OcfToProfit),
|
||||
fmt.Sprintf("%.2f", row.CashToLiqdebt),
|
||||
fmt.Sprintf("%.2f", row.CashToLiqdebtWithinterest),
|
||||
fmt.Sprintf("%.2f", row.OpToLiqdebt),
|
||||
fmt.Sprintf("%.2f", row.OpToDebt),
|
||||
fmt.Sprintf("%.2f", row.ProfitToOp),
|
||||
|
||||
// 年度化指标
|
||||
fmt.Sprintf("%.2f", row.RoeYearly),
|
||||
fmt.Sprintf("%.2f", row.Roa2Yearly),
|
||||
fmt.Sprintf("%.2f", row.RoaYearly),
|
||||
fmt.Sprintf("%.2f", row.RoicYearly),
|
||||
fmt.Sprintf("%.2f", row.RoeAvg),
|
||||
|
||||
// 单季度增长比率
|
||||
fmt.Sprintf("%.2f%%", row.QGrYoy),
|
||||
fmt.Sprintf("%.2f%%", row.QGrQoq),
|
||||
fmt.Sprintf("%.2f%%", row.QSalesYoy),
|
||||
fmt.Sprintf("%.2f%%", row.QSalesQoq),
|
||||
fmt.Sprintf("%.2f%%", row.QOpYoy),
|
||||
fmt.Sprintf("%.2f%%", row.QOpQoq),
|
||||
fmt.Sprintf("%.2f%%", row.QProfitYoy),
|
||||
fmt.Sprintf("%.2f%%", row.QProfitQoq),
|
||||
fmt.Sprintf("%.2f%%", row.QNetprofitYoy),
|
||||
fmt.Sprintf("%.2f%%", row.QNetprofitQoq),
|
||||
|
||||
// 单季度比率指标
|
||||
fmt.Sprintf("%.2f", row.QProfitToGr),
|
||||
fmt.Sprintf("%.2f", row.QSaleexpToGr),
|
||||
fmt.Sprintf("%.2f", row.QAdminexpToGr),
|
||||
fmt.Sprintf("%.2f", row.QFinaexpToGr),
|
||||
fmt.Sprintf("%.2f", row.QImpairToGrTtm),
|
||||
fmt.Sprintf("%.2f", row.QGcToGr),
|
||||
fmt.Sprintf("%.2f", row.QOpToGr),
|
||||
fmt.Sprintf("%.2f", row.QOpincomeToEbt),
|
||||
fmt.Sprintf("%.2f", row.QInvestincomeToEbt),
|
||||
fmt.Sprintf("%.2f", row.QDtprofitToProfit),
|
||||
fmt.Sprintf("%.2f", row.QSalescashToOr),
|
||||
fmt.Sprintf("%.2f", row.QOcfToSales),
|
||||
fmt.Sprintf("%.2f", row.QOcfToOr),
|
||||
})
|
||||
}
|
||||
md.Table("最近3年的股票财务指标", fiHeaders, fiData)
|
||||
md.BR()
|
||||
|
||||
md.SaveToFile("./result/" + code + ".md")
|
||||
}
|
||||
|
||||
func GetStocks() (stocks []string) {
|
||||
impl.DBService.Model(&models.StockBasic{}).Group("ts_code").Pluck("ts_code", &stocks)
|
||||
return
|
||||
|
||||
@@ -1,79 +0,0 @@
|
||||
package strategy
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type MarkdownBuilder struct {
|
||||
buf bytes.Buffer
|
||||
}
|
||||
|
||||
func NewMarkdownBuilder() *MarkdownBuilder {
|
||||
return &MarkdownBuilder{}
|
||||
}
|
||||
func (m *MarkdownBuilder) Title(title string) {
|
||||
m.buf.WriteString("# " + title + "\n\n")
|
||||
}
|
||||
|
||||
func (m *MarkdownBuilder) Catalog(text string) {
|
||||
m.buf.WriteString("## " + text + "\n\n")
|
||||
}
|
||||
func (m *MarkdownBuilder) Text(text string) {
|
||||
m.buf.WriteString(text + "\n\n")
|
||||
}
|
||||
|
||||
func (m *MarkdownBuilder) KvText(key, val string) {
|
||||
m.buf.WriteString(key + " :" + val + " \n")
|
||||
}
|
||||
|
||||
func (m *MarkdownBuilder) BR() {
|
||||
m.buf.WriteString("\n")
|
||||
}
|
||||
|
||||
func (m *MarkdownBuilder) Code(lang, code string) {
|
||||
m.buf.WriteString("```" + lang + "\n")
|
||||
m.buf.WriteString(code + "\n")
|
||||
m.buf.WriteString("```\n\n")
|
||||
}
|
||||
|
||||
// 生成Markdown表格
|
||||
// | Header1 | Header2 |
|
||||
// |---------|---------|
|
||||
// | Cell1 | Cell2 |
|
||||
func (m *MarkdownBuilder) Table(catalog string, headers []string, rows [][]string) {
|
||||
if catalog != "" {
|
||||
m.buf.WriteString("## " + catalog + "\n")
|
||||
}
|
||||
m.buf.WriteString("| " + strings.Join(headers, " | ") + " |\n")
|
||||
|
||||
m.buf.WriteString("| ")
|
||||
for _, header := range headers {
|
||||
m.buf.WriteString(strings.Repeat("-", len(header)) + "|")
|
||||
}
|
||||
m.buf.WriteString("\n")
|
||||
for _, row := range rows {
|
||||
m.buf.WriteString("| " + strings.Join(row, " | ") + " |\n")
|
||||
}
|
||||
}
|
||||
|
||||
func (m *MarkdownBuilder) Build() string {
|
||||
return m.buf.String()
|
||||
}
|
||||
|
||||
func (m *MarkdownBuilder) SaveToFile(filePath string) error {
|
||||
rf, err := os.OpenFile(filePath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, os.ModePerm)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer rf.Close()
|
||||
|
||||
// 将buffer内容复制到文件
|
||||
_, err = io.Copy(rf, &m.buf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
42
internal/logic/strategy/rule/ai.go
Normal file
42
internal/logic/strategy/rule/ai.go
Normal file
@@ -0,0 +1,42 @@
|
||||
package rule
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"git.apinb.com/bsm-sdk/core/utils"
|
||||
"git.apinb.com/quant/gostock/internal/config"
|
||||
"git.apinb.com/quant/gostock/internal/logic/deepseek"
|
||||
)
|
||||
|
||||
var (
|
||||
MarkdataPath = "./markdata/"
|
||||
)
|
||||
|
||||
func (r *RuleFactory) RunAi(code string) {
|
||||
mdPath := MarkdataPath + code + ".md"
|
||||
if !utils.PathExists(mdPath) {
|
||||
r.Model.AiScrore = -1
|
||||
r.Model.AddDesc(fmt.Sprintf("%s markdown 文件未找友", mdPath))
|
||||
return
|
||||
}
|
||||
|
||||
c := deepseek.Config{
|
||||
APIKey: config.Spec.DeepSeekApiKey, // 替换为你的API Key
|
||||
BaseURL: "https://api.deepseek.com",
|
||||
Model: "deepseek-chat", // 或 "deepseek-coder"
|
||||
MaxTokens: 2000,
|
||||
Temperature: 0.7,
|
||||
}
|
||||
|
||||
prompt := "你是一名资深量化投研分析师。请根据markdown格式的附件内容,给出详细总结与买入评分(0-10),输出JSON,字段:summary(500字内中文总结)、score(0-10数字)、action(买入/谨慎/观望)、risk(一句风险提示)。"
|
||||
result, err := deepseek.ProcessMarkdownQA(c, mdPath, prompt)
|
||||
if err != nil {
|
||||
r.Model.AiScrore = -1
|
||||
r.Model.AddDesc(fmt.Sprintf("处理失败: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
// 输出JSON格式的结果
|
||||
fmt.Println(string(result))
|
||||
|
||||
}
|
||||
@@ -30,6 +30,10 @@ type StratModel struct {
|
||||
ValRsiPrve float64
|
||||
ValRsiLast float64
|
||||
Desc []StratDesc `gorm:"foreignKey:StratModelID"`
|
||||
|
||||
//AI
|
||||
AiScrore int
|
||||
AiReply string
|
||||
}
|
||||
|
||||
func init() {
|
||||
|
||||
Reference in New Issue
Block a user