Files
collector/internal/logic/book.go
2026-04-17 19:56:22 +08:00

136 lines
4.2 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package logic
import (
"errors"
"fmt"
"log"
"git.apinb.com/bsm-sdk/core/utils"
"git.apinb.com/quant/collector/internal/impl"
"git.apinb.com/quant/collector/internal/models"
"git.apinb.com/quant/collector/internal/types"
"gorm.io/gorm"
)
// CheckAndRepairOrderBooks 检查并补全订单簿数据
func CheckAndRepairOrderBooks(accountID string) error {
log.Println("开始检查订单簿数据完整性...")
err := checkAccountOrderBooks(accountID)
if err != nil {
return fmt.Errorf("检查账户 %s 的订单簿失败: %w", accountID, err)
}
return nil
}
// checkAccountOrderBooks 检查单个账户的订单簿数据
func checkAccountOrderBooks(accountID string) error {
// 获取该账户最新的持仓日期
var latestYmd int
err := impl.DBService.Model(&models.CollectorPosition{}).
Where("account_id = ?", accountID).
Select("MAX(ymd)").
Scan(&latestYmd).Error
if err != nil {
return fmt.Errorf("查询最新持仓日期失败: %w", err)
}
if latestYmd == 0 {
log.Printf("账户 %s 没有持仓记录", accountID)
return nil
}
log.Printf("账户 %s 最新持仓日期: %d", accountID, latestYmd)
// 获取该账户最新日期的持仓列表
var latestPositions []models.CollectorPosition
err = impl.DBService.Where("account_id = ? AND ymd = ?", accountID, latestYmd).
Find(&latestPositions).Error
if err != nil {
return fmt.Errorf("查询持仓失败: %w", err)
}
log.Printf("账户 %s 有 %d 个持仓股票需要检查", accountID, len(latestPositions))
// 1. 检查股票持仓列表是否有未闭合的订单簿记录
for _, position := range latestPositions {
// 跳过无持仓的股票
if position.Volume <= 0 {
continue
}
var existingOrderBooks models.OrderBook
err := impl.DBService.Where("account_id = ? AND stock_code = ? AND is_closed = ?", accountID, position.Code, false).First(&existingOrderBooks).Error
if errors.Is(err, gorm.ErrRecordNotFound) {
// 没有找到未闭合的订单簿记录,写入新的记录
newOrderBook := models.OrderBook{
AccountID: accountID,
StockCode: position.Code,
Ymd: latestYmd,
BuyPrice: position.AvgPrice,
BuyVolume: position.Volume,
IsClosed: false,
}
if err := impl.DBService.Create(&newOrderBook).Error; err != nil {
log.Printf("创建订单簿记录失败: %v", err)
continue
}
log.Printf("新建买入订单簿: account=%s, stock=%s, volume=%d", accountID, position.Code, position.Volume)
}
}
// 2. 检查所有未闭合的订单簿记录开仓成本价格与CollectorOrders对比是否正确不正确采用CollectorOrders中的价格更新
var orderBooks []models.OrderBook
err = impl.DBService.Where("account_id = ? AND is_closed = ?", accountID, false).Find(&orderBooks).Error
if err != nil {
return fmt.Errorf("查询未闭合的订单簿记录失败: %w", err)
}
for _, orderBook := range orderBooks {
newAvgPrice, err := GetOrderAvgPrice(orderBook.StockCode, accountID, orderBook.BuyVolume)
if err != nil {
log.Printf("获取订单簿 %s 的开仓成本价格失败: %v", orderBook.StockCode, err)
continue
}
if newAvgPrice > orderBook.BuyPrice {
log.Printf("订单簿 %s 的开仓成本价格不一致, 更新前: %v, 更新后: %v", orderBook.StockCode, orderBook.BuyPrice, newAvgPrice)
orderBook.BuyPrice = newAvgPrice
if err := impl.DBService.Save(&orderBook).Error; err != nil {
log.Printf("更新订单簿 %s 的开仓成本价格失败: %v", orderBook.StockCode, err)
}
}
}
return nil
}
func GetOrderAvgPrice(code, account_id string, amount int) (float64, error) {
if amount <= 0 {
return 0, fmt.Errorf("数量必须大于0")
}
var orders []models.CollectorOrder
impl.DBService.Model(&models.CollectorOrder{}).
Where("stock_code = ? AND account_id = ? AND offset_flag = ?", code, account_id, types.FLAG_BUY).
Order("id desc").
Find(&orders)
var stepIdx = 0
var currentAmount int
for idx, order := range orders {
currentAmount += order.TradedVolume
if currentAmount == amount {
stepIdx = idx
break
}
}
var avgPrice float64
newOrders := orders[0 : stepIdx+1]
for _, order := range newOrders {
avgPrice += order.TradedPrice
}
avgPrice = utils.FloatRound(avgPrice/float64(len(newOrders)), 2)
return avgPrice, nil
}