refactor(stock): 重构股票数据处理逻辑

- 移除定时更新标题的代码
- 优化股票数据获取和处理流程
- 增加更多股票相关信息的计算和展示
- 调整前端组件以适应新的数据结构
- 修复了一些潜在的数值计算问题
This commit is contained in:
spark 2025-01-09 13:31:18 +08:00
parent d3c6c1d570
commit ad428f83f8
4 changed files with 140 additions and 121 deletions

114
app.go
View File

@ -4,6 +4,7 @@ package main
import (
"context"
"fmt"
"github.com/coocood/freecache"
"github.com/duke-git/lancet/v2/convertor"
"github.com/duke-git/lancet/v2/mathutil"
@ -47,16 +48,6 @@ func (a *App) startup(ctx context.Context) {
// domReady is called after front-end resources have been loaded
func (a *App) domReady(ctx context.Context) {
// Add your action here
//定时更新数据
go func() {
ticker := time.NewTicker(time.Second)
defer ticker.Stop()
for range ticker.C {
runtime.WindowSetTitle(ctx, "go-stock "+time.Now().Format("2006-01-02 15:04"))
}
}()
//定时更新数据
go func() {
ticker := time.NewTicker(time.Second * 2)
@ -105,28 +96,79 @@ func isTradingTime(date time.Time) bool {
func MonitorStockPrices(a *App) {
dest := &[]data.FollowedStock{}
db.Dao.Model(&data.FollowedStock{}).Find(dest)
for _, item := range *dest {
follow := item
stockCode := follow.StockCode
go func() {
stockData, err := data.NewStockDataApi().GetStockCodeRealTimeData(stockCode)
if err != nil {
logger.SugaredLogger.Errorf("get stock code real time data error:%s", err.Error())
return
}
price, err := convertor.ToFloat(stockData.Price)
if err != nil {
return
}
stockData.PrePrice = follow.Price
if follow.Price != price {
runtime.EventsEmit(a.ctx, "stock_price", stockData)
go db.Dao.Model(follow).Where("stock_code = ?", stockCode).Updates(map[string]interface{}{
"price": stockData.Price,
})
}
}()
total := float64(0)
for _, follow := range *dest {
stockData := getStockInfo(follow)
total += stockData.ProfitAmountToday
price, _ := convertor.ToFloat(stockData.Price)
if stockData.PrePrice != price {
go runtime.EventsEmit(a.ctx, "stock_price", stockData)
}
}
title := "go-stock " + time.Now().Format(time.DateTime) + fmt.Sprintf(" %.2f¥", total)
runtime.WindowSetTitle(a.ctx, title)
systray.SetTooltip(title)
}
func getStockInfo(follow data.FollowedStock) *data.StockInfo {
stockCode := follow.StockCode
stockData, err := data.NewStockDataApi().GetStockCodeRealTimeData(stockCode)
if err != nil {
logger.SugaredLogger.Errorf("get stock code real time data error:%s", err.Error())
return nil
}
stockData.PrePrice = follow.Price //上次当前价格
stockData.Sort = follow.Sort
stockData.CostPrice = follow.CostPrice //成本价
stockData.CostVolume = follow.Volume //成本量
stockData.AlarmChangePercent = follow.AlarmChangePercent
stockData.AlarmPrice = follow.AlarmPrice
//当前价格
price, _ := convertor.ToFloat(stockData.Price)
//当前价格为0 时 使用卖一价格作为当前价格
if price == 0 {
price, _ = convertor.ToFloat(stockData.A1P)
}
//当前价格依然为0 时 使用买一报价作为当前价格
if price == 0 {
price, _ = convertor.ToFloat(stockData.B1P)
}
//昨日收盘价
preClosePrice, _ := convertor.ToFloat(stockData.PreClose)
//今日最高价
highPrice, _ := convertor.ToFloat(stockData.High)
if highPrice == 0 {
highPrice, _ = convertor.ToFloat(stockData.Open)
}
//今日最低价
lowPrice, _ := convertor.ToFloat(stockData.Low)
if lowPrice == 0 {
lowPrice, _ = convertor.ToFloat(stockData.Open)
}
//开盘价
//openPrice, _ := convertor.ToFloat(stockData.Open)
stockData.ChangePrice = mathutil.RoundToFloat(price-preClosePrice, 2)
stockData.ChangePercent = mathutil.RoundToFloat(mathutil.Div(price-preClosePrice, preClosePrice)*100, 3)
stockData.HighRate = mathutil.RoundToFloat(mathutil.Div(highPrice-preClosePrice, preClosePrice)*100, 3)
stockData.LowRate = mathutil.RoundToFloat(mathutil.Div(lowPrice-preClosePrice, preClosePrice)*100, 3)
if follow.CostPrice > 0 && follow.Volume > 0 {
stockData.Profit = mathutil.RoundToFloat(mathutil.Div(price-follow.CostPrice, follow.CostPrice)*100, 3)
stockData.ProfitAmount = mathutil.RoundToFloat((price-follow.CostPrice)*float64(follow.Volume), 2)
stockData.ProfitAmountToday = mathutil.RoundToFloat((price-preClosePrice)*float64(follow.Volume), 2)
}
//logger.SugaredLogger.Debugf("stockData:%+v", stockData)
if follow.Price != price {
go db.Dao.Model(follow).Where("stock_code = ?", stockCode).Updates(map[string]interface{}{
"price": stockData.Price,
})
}
return stockData
}
// beforeClose is called when the application is about to quit,
@ -161,8 +203,14 @@ func (a *App) shutdown(ctx context.Context) {
}
// Greet returns a greeting for the given name
func (a *App) Greet(name string) *data.StockInfo {
stockInfo, _ := data.NewStockDataApi().GetStockCodeRealTimeData(name)
func (a *App) Greet(stockCode string) *data.StockInfo {
//stockInfo, _ := data.NewStockDataApi().GetStockCodeRealTimeData(stockCode)
follow := &data.FollowedStock{
StockCode: stockCode,
}
db.Dao.Model(follow).Where("stock_code = ?", stockCode).First(follow)
stockInfo := getStockInfo(*follow)
return stockInfo
}

View File

@ -43,8 +43,8 @@ type StockInfo struct {
Amount string `json:"成交金额"`
Open string `json:"今日开盘价"`
PreClose string `json:"昨日收盘价"`
High string `json:"今日最价"`
Low string `json:"今日最价"`
High string `json:"今日最价"`
Low string `json:"今日最价"`
Bid string `json:"竞买价"`
Ask string `json:"竞卖价"`
B1P string `json:"买一报价"`
@ -67,6 +67,21 @@ type StockInfo struct {
A4V string `json:"卖四申报"`
A5P string `json:"卖五报价"`
A5V string `json:"卖五申报"`
//以下是字段值需二次计算
ChangePercent float64 `json:"changePercent"` //涨跌幅
ChangePrice float64 `json:"changePrice"` //涨跌额
HighRate float64 `json:"highRate"` //最高涨跌
LowRate float64 `json:"lowRate"` //最低涨跌
CostPrice float64 `json:"costPrice"` //成本价
CostVolume int64 `json:"costVolume"` //持仓数量
Profit float64 `json:"profit"` //总盈亏率
ProfitAmount float64 `json:"profitAmount"` //总盈亏金额
ProfitAmountToday float64 `json:"profitAmountToday"` //今日盈亏金额
Sort int64 `json:"sort"` //排序
AlarmChangePercent float64 `json:"alarmChangePercent"`
AlarmPrice float64 `json:"alarmPrice"`
}
func (receiver StockInfo) TableName() string {

View File

@ -192,56 +192,36 @@ async function updateData(result) {
result["当前价格"]=result["卖一报价"]
}
let s=(result["当前价格"]-result["昨日收盘价"])*100/result["昨日收盘价"]
let roundedNum = s.toFixed(2); //
// let s2=(result[""]-result[""])*100/result[""]
// let roundedNum2 = s2.toFixed(2); //
result.rf=roundedNum
// result.rf2=roundedNum2
result.s=roundedNum+"%"
result.highRate=((result["今日最高价"]-result["今日开盘价"])*100/result["今日开盘价"]).toFixed(2)+"%"
result.lowRate=((result["今日最低价"]-result["今日开盘价"])*100/result["今日开盘价"]).toFixed(2)+"%"
if (roundedNum>0) {
if (result.changePercent>0) {
result.type="error"
result.color="#E88080"
}else if (roundedNum<0) {
}else if (result.changePercent<0) {
result.type="success"
result.color="#63E2B7"
}else {
result.type="default"
result.color="#FFFFFF"
}
let res= followList.value.filter(item => item.StockCode===result['股票代码'])
if (res.length>0) {
result.Sort=res[0].Sort
result.costPrice=res[0].CostPrice
result.volume=res[0].Volume
result.profit=((result["当前价格"]-result.costPrice)*100/result.costPrice).toFixed(3)
result.profitAmountToday=(result.volume*(result["当前价格"]-result["昨日收盘价"])).toFixed(2)
result.profitAmount=(result.volume*(result["当前价格"]-result.costPrice)).toFixed(2)
if(result.profitAmount>0){
result.profitType="error"
}else if(result.profitAmount<0){
result.profitType="success"
}
if(result["当前价格"]){
if(res[0].AlarmChangePercent>0&&Math.abs(roundedNum)>=res[0].AlarmChangePercent){
if(result.alarmChangePercent>0&&Math.abs(result.changePercent)>=result.alarmChangePercent){
SendMessage(result,1)
}
if(res[0].AlarmPrice>0&&result["当前价格"]>=res[0].AlarmPrice){
if(result.alarmPrice>0&&result["当前价格"]>=result.alarmPrice){
SendMessage(result,2)
}
if(res[0].CostPrice>0&&result["当前价格"]>=res[0].CostPrice){
if(result.costPrice>0&&result["当前价格"]>=result.costPrice){
SendMessage(result,3)
}
}
}
result.key=GetSortKey(result.Sort,result["股票代码"])
results.value[GetSortKey(result.Sort,result["股票代码"])]=result
}
@ -251,57 +231,7 @@ async function monitor() {
for (let code of stocks.value) {
// console.log(code)
Greet(code).then(result => {
if(result["当前价格"]<=0){
result["当前价格"]=result["卖一报价"]
}
let s=(result["当前价格"]-result["昨日收盘价"])*100/result["昨日收盘价"]
let roundedNum = s.toFixed(2); //
result.s=roundedNum+"%"
result.rf=roundedNum
result.highRate=((result["今日最高价"]-result["今日开盘价"])*100/result["今日开盘价"]).toFixed(2)+"%"
result.lowRate=((result["今日最低价"]-result["今日开盘价"])*100/result["今日开盘价"]).toFixed(2)+"%"
if (roundedNum>0) {
result.type="error"
result.color="#E88080"
}else if (roundedNum<0) {
result.type="success"
result.color="#63E2B7"
}else {
result.type="default"
result.color="#FFFFFF"
}
let res= followList.value.filter(item => item.StockCode===code)
if (res.length>0) {
result.Sort=res[0].Sort
result.costPrice=res[0].CostPrice
result.volume=res[0].Volume
result.profit=((result["当前价格"]-result.costPrice)*100/result.costPrice).toFixed(3)
result.profitAmountToday=(result.volume*(result["当前价格"]-result["昨日收盘价"])).toFixed(2)
result.profitAmount=(result.volume*(result["当前价格"]-result.costPrice)).toFixed(2)
if(result.profitAmount>0){
result.profitType="error"
}else if(result.profitAmount<0){
result.profitType="success"
}
if(result["当前价格"]){
if(res[0].AlarmChangePercent>0&&Math.abs(roundedNum)>=res[0].AlarmChangePercent){
SendMessage(result,1)
}
if(res[0].AlarmPrice>0&&result["当前价格"]>=res[0].AlarmPrice){
SendMessage(result,2)
}
if(res[0].CostPrice>0&&result["当前价格"]>=res[0].CostPrice){
SendMessage(result,3)
}
}
}
result.key=GetSortKey(result.Sort,result["股票代码"])
results.value[GetSortKey(result.Sort,result["股票代码"])]=result
updateData(result)
})
}
}
@ -456,20 +386,22 @@ function getHeight() {
<n-grid :cols="1" :y-gap="6">
<n-gi>
<n-text :type="result.type" >
<n-number-animation :precision="2" :from="result['上次当前价格']" :to="Number(result['当前价格'])" />
<n-number-animation :duration="1000" :precision="2" :from="result['上次当前价格']" :to="Number(result['当前价格'])" />
</n-text>
<n-text style="padding-left: 10px;" :type="result.type">
<n-number-animation :precision="2" :from="0" :to="result.rf" />%
<n-number-animation :duration="1000" :precision="3" :from="0" :to="result.changePercent" />%
</n-text>&nbsp;
<n-text size="small" v-if="result.profitAmountToday>0" :type="result.type">{{result.profitAmountToday}}</n-text>
<n-text size="small" v-if="result.costVolume>0" :type="result.type">
<n-number-animation :duration="1000" :precision="2" :from="0" :to="result.profitAmountToday" />
</n-text>
</n-gi>
</n-grid>
<n-grid :cols="2" :y-gap="4" :x-gap="4" >
<n-gi>
<n-text :type="'info'">{{"最高 "+result["今日最高价"]+" "+result.highRate }}</n-text>
<n-text :type="'info'">{{"最高 "+result["今日最高价"]+" "+result.highRate }}%</n-text>
</n-gi>
<n-gi>
<n-text :type="'info'">{{"最低 "+result["今日最低价"]+" "+result.lowRate }}</n-text>
<n-text :type="'info'">{{"最低 "+result["今日最低价"]+" "+result.lowRate }}%</n-text>
</n-gi>
<n-gi>
<n-text :type="'info'">{{"昨收 "+result["昨日收盘价"]}}</n-text>
@ -484,7 +416,7 @@ function getHeight() {
<template #footer>
<n-flex justify="center">
<n-tag size="small" v-if="result.volume>0" :type="result.profitType">{{result.volume+""}}</n-tag>
<n-tag size="small" v-if="result.costPrice>0" :type="result.profitType">{{"成本:"+result.costPrice+" "+result.profit+"%"+" ( "+result.profitAmount+" ¥ )"}}</n-tag>
<n-tag size="small" v-if="result.costPrice>0" :type="result.profitType">{{"成本:"+result.costPrice+"*"+result.costVolume+" "+result.profit+"%"+" ( "+result.profitAmount+" ¥ )"}}</n-tag>
</n-flex>
</template>
<template #action>

View File

@ -144,8 +144,8 @@ export namespace data {
"成交金额": string;
"今日开盘价": string;
"昨日收盘价": string;
"今日最低价": string;
"今日最高价": string;
"今日最低价": string;
"竞买价": string;
"竞卖价": string;
"买一报价": string;
@ -168,6 +168,18 @@ export namespace data {
"卖四申报": string;
"卖五报价": string;
"卖五申报": string;
changePercent: number;
changePrice: number;
highRate: number;
lowRate: number;
costPrice: number;
costVolume: number;
profit: number;
profitAmount: number;
profitAmountToday: number;
sort: number;
alarmChangePercent: number;
alarmPrice: number;
static createFrom(source: any = {}) {
return new StockInfo(source);
@ -189,8 +201,8 @@ export namespace data {
this["成交金额"] = source["成交金额"];
this["今日开盘价"] = source["今日开盘价"];
this["昨日收盘价"] = source["昨日收盘价"];
this["今日最低价"] = source["今日最低价"];
this["今日最高价"] = source["今日最高价"];
this["今日最低价"] = source["今日最低价"];
this["竞买价"] = source["竞买价"];
this["竞卖价"] = source["竞卖价"];
this["买一报价"] = source["买一报价"];
@ -213,6 +225,18 @@ export namespace data {
this["卖四申报"] = source["卖四申报"];
this["卖五报价"] = source["卖五报价"];
this["卖五申报"] = source["卖五申报"];
this.changePercent = source["changePercent"];
this.changePrice = source["changePrice"];
this.highRate = source["highRate"];
this.lowRate = source["lowRate"];
this.costPrice = source["costPrice"];
this.costVolume = source["costVolume"];
this.profit = source["profit"];
this.profitAmount = source["profitAmount"];
this.profitAmountToday = source["profitAmountToday"];
this.sort = source["sort"];
this.alarmChangePercent = source["alarmChangePercent"];
this.alarmPrice = source["alarmPrice"];
}
convertValues(a: any, classs: any, asMap: boolean = false): any {