Files
big-qmt/go-client/apps/trend/logic/positions.go

133 lines
3.0 KiB
Go
Raw Normal View History

2026-08-26 23:27:42 +08:00
package logic
import (
"math"
"sync"
"big-qmt/go-client/config"
"big-qmt/go-client/libs"
"big-qmt/go-client/sdk"
)
const (
legBase = "base"
legAdded = "add"
)
var (
peakMu sync.Mutex
peakGrids = make(map[string]int)
)
func managePositions(client *sdk.Client, ticks map[string]sdk.Tick, positions []sdk.Position, marketOK bool) {
}
func syncAdded(item *StateItem, position sdk.Position) {
addedQty := position.Volume - item.BaseQty
if addedQty <= 0 {
item.BaseQty = position.Volume
item.BaseCost = position.OpenPrice
item.AddedQty = 0
item.AddedCost = 0
item.AddedStatus = StatusNone
peakMu.Lock()
delete(peakGrids, item.Code+"|"+legAdded)
peakMu.Unlock()
return
}
item.AddedQty = addedQty
totalCost := position.OpenPrice * float64(position.Volume)
baseCost := item.BaseCost * float64(item.BaseQty)
item.AddedCost = math.Max(0, (totalCost-baseCost)/float64(addedQty))
item.AddedStatus = StatusOk
}
func buyAdded(client *sdk.Client, item *StateItem, price float64, marketOK bool, budget *float64) {
if !marketOK || !PosbuyWatch.Triggered("补仓", item.Code, price) {
return
}
volume := libs.CalcBuyVolume(price, config.Account.BuyValue)
amount := price * float64(volume)
if amount > *budget || orderBusy(item.Code, "BUY") {
return
}
orderID := NewOrderID(legAdded)
if !OrderBook.Place(client, sdk.OpBuy, item.Code, volume, orderID) {
return
}
item.AddedOrderId = orderID
item.AddedNum++
item.AddedQty = volume
item.AddedCost = price
item.AddedStatus = StatusIng
QuantState.Set(item)
*budget -= amount
}
func sell(client *sdk.Client, item *StateItem, usable, volume int, leg string, pnl float64) {
volume -= volume % 100
if volume <= 0 || usable < volume || orderBusy(item.Code, "SELL") {
return
}
orderID := NewOrderID(leg)
if !OrderBook.Place(client, sdk.OpSell, item.Code, volume, orderID) {
return
}
if leg == legAdded {
item.AddedOrderId = orderID
item.AddedStatus = StatusIng
} else {
item.BaseOrderId = orderID
item.BaseStatus = StatusIng
}
QuantState.Set(item)
logf("INFO", "[止盈] %s 卖出%d股盈利=%.2f%%", item.Code, volume, pnl)
}
func orderBusy(code, side string) bool {
OrderBook.mu.Lock()
defer OrderBook.mu.Unlock()
order := OrderBook.Data[side+"-"+code]
if order == nil {
return false
}
switch order.Status {
case "48", "49", "50", "51", "52", "55":
return true
default:
return false
}
}
func shouldSell(code, leg string, pnl float64) bool {
if pnl < config.Account.MinProfitPct {
return false
}
grid := int(math.Floor(pnl / config.Account.GridStepPct))
key := code + "|" + leg
peakMu.Lock()
defer peakMu.Unlock()
peak, tracked := peakGrids[key]
if !tracked || grid > peak {
peakGrids[key] = grid
return false
}
return grid < peak
}
func forget(code string) {
OpenWatch.mu.Lock()
delete(OpenWatch.Data, code)
OpenWatch.mu.Unlock()
PosbuyWatch.mu.Lock()
delete(PosbuyWatch.Data, code)
PosbuyWatch.mu.Unlock()
peakMu.Lock()
delete(peakGrids, code+"|"+legBase)
delete(peakGrids, code+"|"+legAdded)
peakMu.Unlock()
}