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() }