package logic import ( "context" "crypto/rand" "encoding/hex" "fmt" "slices" "strconv" "strings" "sync" "time" "big-qmt/go-client/sdk" ) var ( STOCK_DIRECTION = 48 STOCK_SIDE_BUY = 48 STOCK_SIDE_SELL = 49 OffsetFlag = map[string]string{"23": "BUY", "24": "SELL", "48": "BUY", "49": "SELL"} OrderTimeout = 5 * time.Minute OrderBook *Books ) type OrderItem struct { ID string Code string Side string Remark string Status string CreatedAt time.Time Volume int } type Books struct { mu sync.Mutex Data map[string]*OrderItem Index []string } func NewOrderBook() { OrderBook = &Books{Data: make(map[string]*OrderItem), Index: make([]string, 0)} } func NewOrderID(leg string) string { var random [6]byte _, _ = rand.Read(random[:]) tag := fmt.Sprintf("zt-%s-%s", leg, hex.EncodeToString(random[:])) if len(tag) > 24 { return tag[:24] } return tag } func (o *Books) IsLock(side, code string) bool { o.mu.Lock() defer o.mu.Unlock() keyStr := fmt.Sprintf("%s-%s", side, code) return slices.Contains(o.Index, keyStr) } func (o *Books) Refresh(client *sdk.Client) error { o.mu.Lock() defer o.mu.Unlock() raw, err := client.TradeDetailData(context.Background(), "order") if err != nil { return err } var idx []string orders := make(map[string]*OrderItem) for _, row := range raw { keyStr, item := parseOrder(row) orders[keyStr] = item idx = append(idx, keyStr) } o.Data = orders o.Index = idx return nil } func (o *Books) CancelExpired(client *sdk.Client) error { ctx := context.Background() err := o.Refresh(client) if err != nil { return fmt.Errorf("[委托] 查询失败: %v", err) } for _, order := range o.Data { if order.CreatedAt.IsZero() || time.Since(order.CreatedAt) <= OrderTimeout { continue } if order.ID != "" { rs, err := client.CanCancelOrder(ctx, order.ID) if err != nil { logf("ERROR", "[委托] 撤销失败:%v", err) continue } else { logf("INFO", "[委托] 撤销成功:%v", rs) } } } return nil } func (o *Books) Place(client *sdk.Client, op int, code string, volume int, sn string) bool { if _, err := client.PassorderLatestTagged(context.Background(), op, code, volume, sn); err != nil { logf("ERROR", "[委托] %s 下单失败: %v", code, err) return false } o.mu.Lock() defer o.mu.Unlock() keyStr := fmt.Sprintf("%s-%s", OffsetFlag[strconv.Itoa(op)], code) o.Index = append(o.Index, keyStr) logf("INFO", "[委托] 下单已提交 %d %s %d股", op, code, volume) return true } func parseOrder(row map[string]string) (string, *OrderItem) { left, _ := strconv.Atoi(row["m_nVolumeTotal"]) traded, _ := strconv.Atoi(row["m_nVolumeTraded"]) volume := left + traded item := &OrderItem{ ID: row["m_strOrderSysID"], Code: row["m_strInstrumentID"], Side: OffsetFlag[row["m_nOffsetFlag"]], Remark: row["m_strRemark"], Status: row["m_nOrderStatus"], Volume: volume, CreatedAt: time.Unix(parseTimestamp(row), 0), } keyStr := fmt.Sprintf("%s-%s", item.Side, item.Code) return keyStr, item } func parseTimestamp(row map[string]string) int64 { ts, _ := strconv.ParseInt(row["m_nOrderTime"], 10, 64) if ts > 1e11 { return ts / 1000 } if ts > 0 { return ts } date := row["m_strInsertDate"] clock := strings.ReplaceAll(row["m_strInsertTime"], ":", "") clock = strings.Repeat("0", max(0, 6-len(clock))) + clock t, _ := time.ParseInLocation("20060102150405", date+clock, time.Local) return t.Unix() }