Files
big-qmt/go-client/apps/zt/logic/order.go
2026-08-26 16:37:06 +08:00

155 lines
3.4 KiB
Go

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