optimize
This commit is contained in:
@@ -6,6 +6,7 @@ import (
|
|||||||
"git.apinb.com/bsm-sdk/core/utils"
|
"git.apinb.com/bsm-sdk/core/utils"
|
||||||
"git.apinb.com/quant/gostock/internal/config"
|
"git.apinb.com/quant/gostock/internal/config"
|
||||||
"git.apinb.com/quant/gostock/internal/impl"
|
"git.apinb.com/quant/gostock/internal/impl"
|
||||||
|
"git.apinb.com/quant/gostock/internal/logic/libs"
|
||||||
"git.apinb.com/quant/gostock/internal/models"
|
"git.apinb.com/quant/gostock/internal/models"
|
||||||
"github.com/jedib0t/go-pretty/v6/table"
|
"github.com/jedib0t/go-pretty/v6/table"
|
||||||
)
|
)
|
||||||
@@ -20,7 +21,8 @@ func main() {
|
|||||||
|
|
||||||
fmt.Println("")
|
fmt.Println("")
|
||||||
ClosedTable()
|
ClosedTable()
|
||||||
UnclosedTable()
|
UnclosedTableByRealtime()
|
||||||
|
UnclosedTableByDay()
|
||||||
fmt.Println("")
|
fmt.Println("")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -28,7 +30,7 @@ func ClosedTable() {
|
|||||||
tw := table.NewWriter()
|
tw := table.NewWriter()
|
||||||
tw.SetStyle(table.StyleLight)
|
tw.SetStyle(table.StyleLight)
|
||||||
tw.SetTitle("已清仓列表")
|
tw.SetTitle("已清仓列表")
|
||||||
tw.AppendHeader(table.Row{"ID", "Code", "Name", "OpenDate", "OpenPrice", "CloseDate", "ClosePrice", "PNL/Per", "PNLRate(%)"})
|
tw.AppendHeader(table.Row{"ID", "Code", "Name", "Open_Date", "Open_Price", "Close_Date", "Close_Price", "PNL/Per", "PNLRate(%)"})
|
||||||
var data []models.MockPosition
|
var data []models.MockPosition
|
||||||
impl.DBService.Where("status=?", 1).Find(&data)
|
impl.DBService.Where("status=?", 1).Find(&data)
|
||||||
var tPNL, tPNLR, cost, sell float64
|
var tPNL, tPNLR, cost, sell float64
|
||||||
@@ -54,11 +56,49 @@ func ClosedTable() {
|
|||||||
fmt.Println(tw.Render())
|
fmt.Println(tw.Render())
|
||||||
}
|
}
|
||||||
|
|
||||||
func UnclosedTable() {
|
func UnclosedTableByRealtime() {
|
||||||
tw := table.NewWriter()
|
tw := table.NewWriter()
|
||||||
tw.SetStyle(table.StyleLight)
|
tw.SetStyle(table.StyleLight)
|
||||||
tw.SetTitle("未平仓列表")
|
tw.SetTitle("未平仓列表(实时)")
|
||||||
tw.AppendHeader(table.Row{"ID", "Code", "Name", "OpenDate", "OpenPrice", "TodayPrice", "PNL/Per", "PNLRate(%)"})
|
tw.AppendHeader(table.Row{"ID", "Code", "Name", "Open_Date", "Open_Price", "Realtime_Price", "PNL/Per", "PNLRate(%)"})
|
||||||
|
var data []models.MockPosition
|
||||||
|
impl.DBService.Where("status=?", 0).Find(&data)
|
||||||
|
var tPNL, tPNLR, cost, sell float64
|
||||||
|
|
||||||
|
for idx, item := range data {
|
||||||
|
var stock models.StockBasic
|
||||||
|
impl.DBService.Where("ts_code=?", item.Code).First(&stock)
|
||||||
|
|
||||||
|
currentPrice, err := libs.GetSinaStockPrice(item.Code)
|
||||||
|
if err != nil {
|
||||||
|
var daily models.StockDaily
|
||||||
|
impl.DBService.Model(&models.StockDaily{}).Where("ts_code=?", item.Code).Order("id desc").Limit(1).First(&daily)
|
||||||
|
currentPrice = daily.High
|
||||||
|
}
|
||||||
|
|
||||||
|
high := utils.FloatRound(currentPrice*0.995, 2)
|
||||||
|
pnl := utils.FloatRound(high-item.OpenPrice, 2)
|
||||||
|
pnlRate := utils.FloatRound(pnl/item.OpenPrice*100, 2)
|
||||||
|
|
||||||
|
tPNL = tPNL + pnl
|
||||||
|
cost = cost + item.OpenPrice
|
||||||
|
sell = sell + high
|
||||||
|
|
||||||
|
tw.AppendRow(table.Row{idx + 1, item.Code, stock.Name, item.CreatedAt.Format("2006-01-02"), item.OpenPrice, high, pnl, pnlRate})
|
||||||
|
}
|
||||||
|
|
||||||
|
tPNLR = utils.FloatRound(((sell-cost)/cost)*100, 2)
|
||||||
|
|
||||||
|
tw.AppendFooter(table.Row{"", "", "", "TOTAL", utils.FloatRound(cost, 2), utils.FloatRound(sell, 2), utils.FloatRound(tPNL, 2), tPNLR})
|
||||||
|
|
||||||
|
fmt.Println(tw.Render())
|
||||||
|
}
|
||||||
|
|
||||||
|
func UnclosedTableByDay() {
|
||||||
|
tw := table.NewWriter()
|
||||||
|
tw.SetStyle(table.StyleLight)
|
||||||
|
tw.SetTitle("未平仓列表(日线)")
|
||||||
|
tw.AppendHeader(table.Row{"ID", "Code", "Name", "Open_Date", "Open_Price", "Today_Price", "PNL/Per", "PNLRate(%)"})
|
||||||
var data []models.MockPosition
|
var data []models.MockPosition
|
||||||
impl.DBService.Where("status=?", 0).Find(&data)
|
impl.DBService.Where("status=?", 0).Find(&data)
|
||||||
var tPNL, tPNLR, cost, sell float64
|
var tPNL, tPNLR, cost, sell float64
|
||||||
|
|||||||
17
cmd/rk/main.go
Normal file
17
cmd/rk/main.go
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"git.apinb.com/quant/gostock/internal/logic/libs"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
// 示例:获取贵州茅台(sh600519)的最新价
|
||||||
|
price, err := libs.GetSinaStockPrice("600519.SH")
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("获取失败: %v\n", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fmt.Printf("贵州茅台最新价: %.2f\n", price)
|
||||||
|
}
|
||||||
1
go.mod
1
go.mod
@@ -43,6 +43,7 @@ require (
|
|||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
github.com/ShawnRong/tushare-go v0.0.0-20200418035301-a5d4e0f72854
|
||||||
github.com/bitly/go-simplejson v0.5.1
|
github.com/bitly/go-simplejson v0.5.1
|
||||||
github.com/bytedance/sonic v1.15.0 // indirect
|
github.com/bytedance/sonic v1.15.0 // indirect
|
||||||
github.com/bytedance/sonic/loader v0.5.0 // indirect
|
github.com/bytedance/sonic/loader v0.5.0 // indirect
|
||||||
|
|||||||
2
go.sum
2
go.sum
@@ -4,6 +4,8 @@ git.apinb.com/bsm-sdk/core v0.0.95 h1:UD4Z1lZ+Hn+k+u4lG3HhO+4Ucdko2h9NgjdXuX4+tE
|
|||||||
git.apinb.com/bsm-sdk/core v0.0.95/go.mod h1:rCmMma8R2pvByImgoZDm2OPLdr+IUNr7LBPyayb8aN0=
|
git.apinb.com/bsm-sdk/core v0.0.95/go.mod h1:rCmMma8R2pvByImgoZDm2OPLdr+IUNr7LBPyayb8aN0=
|
||||||
git.apinb.com/bsm-sdk/core v0.1.8 h1:INSp+Yw+X+SzRV1Refxx1kqnFCflWc0RyMdPy+r7YHk=
|
git.apinb.com/bsm-sdk/core v0.1.8 h1:INSp+Yw+X+SzRV1Refxx1kqnFCflWc0RyMdPy+r7YHk=
|
||||||
git.apinb.com/bsm-sdk/core v0.1.8/go.mod h1:rCmMma8R2pvByImgoZDm2OPLdr+IUNr7LBPyayb8aN0=
|
git.apinb.com/bsm-sdk/core v0.1.8/go.mod h1:rCmMma8R2pvByImgoZDm2OPLdr+IUNr7LBPyayb8aN0=
|
||||||
|
github.com/ShawnRong/tushare-go v0.0.0-20200418035301-a5d4e0f72854 h1:JuJt9mJKjvALjxD9qOoHivQNr5Cw58nDQLr3dmbXOYU=
|
||||||
|
github.com/ShawnRong/tushare-go v0.0.0-20200418035301-a5d4e0f72854/go.mod h1:itk99hhMTCA0hAiX614YcECQZaqd56qysQzZluyCbs0=
|
||||||
github.com/allegro/bigcache/v3 v3.1.0 h1:H2Vp8VOvxcrB91o86fUSVJFqeuz8kpyyB02eH3bSzwk=
|
github.com/allegro/bigcache/v3 v3.1.0 h1:H2Vp8VOvxcrB91o86fUSVJFqeuz8kpyyB02eH3bSzwk=
|
||||||
github.com/allegro/bigcache/v3 v3.1.0/go.mod h1:aPyh7jEvrog9zAwx5N7+JUQX5dZTSGpxF1LAR4dr35I=
|
github.com/allegro/bigcache/v3 v3.1.0/go.mod h1:aPyh7jEvrog9zAwx5N7+JUQX5dZTSGpxF1LAR4dr35I=
|
||||||
github.com/bitly/go-simplejson v0.5.1 h1:xgwPbetQScXt1gh9BmoJ6j9JMr3TElvuIyjR8pgdoow=
|
github.com/bitly/go-simplejson v0.5.1 h1:xgwPbetQScXt1gh9BmoJ6j9JMr3TElvuIyjR8pgdoow=
|
||||||
|
|||||||
79
internal/logic/libs/real.go
Normal file
79
internal/logic/libs/real.go
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
package libs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GetSinaStockPrice 获取新浪财经某只股票的最新价
|
||||||
|
// 股票代码格式: 沪市为 sh + 代码,深市为 sz + 代码,例如 "sh600519"
|
||||||
|
func GetSinaStockPrice(stockCode string) (float64, error) {
|
||||||
|
tmp := strings.Split(strings.ToLower(stockCode), ".")
|
||||||
|
code := tmp[1] + tmp[0]
|
||||||
|
// 1. 构建请求URL
|
||||||
|
url := fmt.Sprintf("http://hq.sinajs.cn/list=%s", code)
|
||||||
|
|
||||||
|
// 2. 创建HTTP客户端并设置超时(避免长时间阻塞)
|
||||||
|
client := &http.Client{
|
||||||
|
Timeout: 5 * time.Second,
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. 创建请求,并设置Referer,增加稳定性 [citation:5]
|
||||||
|
req, err := http.NewRequest("GET", url, nil)
|
||||||
|
if err != nil {
|
||||||
|
return 0, fmt.Errorf("创建请求失败: %w", err)
|
||||||
|
}
|
||||||
|
req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36")
|
||||||
|
req.Header.Set("Referer", "https://finance.sina.com.cn")
|
||||||
|
|
||||||
|
// 4. 发送请求
|
||||||
|
resp, err := client.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return 0, fmt.Errorf("HTTP请求失败: %w", err)
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
// 5. 读取响应体
|
||||||
|
body, err := io.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return 0, fmt.Errorf("读取响应体失败: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 6. 解析数据
|
||||||
|
// 响应示例: var hq_str_sh600519="贵州茅台,1710.00,1705.00,1720.00,...";
|
||||||
|
dataStr := string(body)
|
||||||
|
|
||||||
|
// 检查是否获取到数据
|
||||||
|
if !strings.Contains(dataStr, "hq_str_") {
|
||||||
|
return 0, fmt.Errorf("返回数据格式异常或股票代码不存在")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 提取引号内的部分
|
||||||
|
start := strings.Index(dataStr, "\"")
|
||||||
|
end := strings.LastIndex(dataStr, "\"")
|
||||||
|
if start == -1 || end == -1 || end <= start {
|
||||||
|
return 0, fmt.Errorf("无法解析数据格式")
|
||||||
|
}
|
||||||
|
|
||||||
|
content := dataStr[start+1 : end]
|
||||||
|
// 按逗号分割
|
||||||
|
fields := strings.Split(content, ",")
|
||||||
|
|
||||||
|
// 检查字段数量是否足够 (至少需要4个字段来获取价格)
|
||||||
|
if len(fields) < 4 {
|
||||||
|
return 0, fmt.Errorf("返回数据字段不足: %v", fields)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 当前价格是第4个字段 (索引为3) [citation:8][citation:9]
|
||||||
|
priceStr := fields[3]
|
||||||
|
var price float64
|
||||||
|
_, err = fmt.Sscanf(priceStr, "%f", &price)
|
||||||
|
if err != nil {
|
||||||
|
return 0, fmt.Errorf("解析价格失败: %s, 错误: %w", priceStr, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return price, nil
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user