feat: add AI grid trading and market regime classification

- Add GridTrader interface with PlaceLimitOrder, CancelOrder, GetOrderBook
- Implement GridTrader for all exchanges (Binance, Bybit, OKX, Bitget, Hyperliquid, Aster, Lighter)
- Add grid engine with ATR-based boundary calculation and fund distribution
- Add market regime classification documents (Chinese/English)
- Add GridConfigEditor component for frontend configuration
This commit is contained in:
tinkle-community
2026-01-13 10:33:02 +08:00
parent 705aa641b0
commit 5fb26c17dc
22 changed files with 3901 additions and 32 deletions

View File

@@ -716,6 +716,125 @@ func (t *FuturesTrader) CancelAllOrders(symbol string) error {
return nil
}
// PlaceLimitOrder places a limit order for grid trading
// This implements the GridTrader interface for FuturesTrader
func (t *FuturesTrader) PlaceLimitOrder(req *LimitOrderRequest) (*LimitOrderResult, error) {
// Format quantity to correct precision
quantityStr, err := t.FormatQuantity(req.Symbol, req.Quantity)
if err != nil {
return nil, fmt.Errorf("failed to format quantity: %w", err)
}
// Format price to correct precision
priceStr, err := t.FormatPrice(req.Symbol, req.Price)
if err != nil {
return nil, fmt.Errorf("failed to format price: %w", err)
}
// Set leverage if specified
if req.Leverage > 0 {
if err := t.SetLeverage(req.Symbol, req.Leverage); err != nil {
logger.Warnf("Failed to set leverage: %v", err)
}
}
// Determine side and position side
var side futures.SideType
var positionSide futures.PositionSideType
if req.Side == "BUY" {
side = futures.SideTypeBuy
positionSide = futures.PositionSideTypeLong
} else {
side = futures.SideTypeSell
positionSide = futures.PositionSideTypeShort
}
// Build order service with broker ID
orderService := t.client.NewCreateOrderService().
Symbol(req.Symbol).
Side(side).
PositionSide(positionSide).
Type(futures.OrderTypeLimit).
TimeInForce(futures.TimeInForceTypeGTC).
Quantity(quantityStr).
Price(priceStr).
NewClientOrderID(getBrOrderID())
// Execute order
order, err := orderService.Do(context.Background())
if err != nil {
return nil, fmt.Errorf("failed to place limit order: %w", err)
}
logger.Infof("✓ [Grid] Placed limit order: %s %s %s @ %s, qty=%s, orderID=%d",
req.Symbol, req.Side, positionSide, priceStr, quantityStr, order.OrderID)
return &LimitOrderResult{
OrderID: fmt.Sprintf("%d", order.OrderID),
ClientID: order.ClientOrderID,
Symbol: order.Symbol,
Side: string(order.Side),
PositionSide: string(order.PositionSide),
Price: req.Price,
Quantity: req.Quantity,
Status: string(order.Status),
}, nil
}
// CancelOrder cancels a specific order by ID
// This implements the GridTrader interface for FuturesTrader
func (t *FuturesTrader) CancelOrder(symbol, orderID string) error {
// Parse order ID to int64
orderIDInt, err := strconv.ParseInt(orderID, 10, 64)
if err != nil {
return fmt.Errorf("invalid order ID: %w", err)
}
_, err = t.client.NewCancelOrderService().
Symbol(symbol).
OrderID(orderIDInt).
Do(context.Background())
if err != nil {
return fmt.Errorf("failed to cancel order: %w", err)
}
logger.Infof("✓ [Grid] Cancelled order: %s/%s", symbol, orderID)
return nil
}
// GetOrderBook gets the order book for a symbol
// This implements the GridTrader interface for FuturesTrader
func (t *FuturesTrader) GetOrderBook(symbol string, depth int) (bids, asks [][]float64, err error) {
book, err := t.client.NewDepthService().
Symbol(symbol).
Limit(depth).
Do(context.Background())
if err != nil {
return nil, nil, fmt.Errorf("failed to get order book: %w", err)
}
// Convert bids
bids = make([][]float64, len(book.Bids))
for i, bid := range book.Bids {
price, _ := strconv.ParseFloat(bid.Price, 64)
qty, _ := strconv.ParseFloat(bid.Quantity, 64)
bids[i] = []float64{price, qty}
}
// Convert asks
asks = make([][]float64, len(book.Asks))
for i, ask := range book.Asks {
price, _ := strconv.ParseFloat(ask.Price, 64)
qty, _ := strconv.ParseFloat(ask.Quantity, 64)
asks[i] = []float64{price, qty}
}
return bids, asks, nil
}
// CancelStopOrders cancels take-profit/stop-loss orders for this symbol (used to adjust TP/SL positions)
// Now uses both legacy API and new Algo Order API (Binance migrated stop orders to Algo system)
func (t *FuturesTrader) CancelStopOrders(symbol string) error {
@@ -1035,6 +1154,42 @@ func (t *FuturesTrader) FormatQuantity(symbol string, quantity float64) (string,
return fmt.Sprintf(format, quantity), nil
}
// GetSymbolPricePrecision gets the price precision for a trading pair
func (t *FuturesTrader) GetSymbolPricePrecision(symbol string) (int, error) {
exchangeInfo, err := t.client.NewExchangeInfoService().Do(context.Background())
if err != nil {
return 0, fmt.Errorf("failed to get trading rules: %w", err)
}
for _, s := range exchangeInfo.Symbols {
if s.Symbol == symbol {
// Get precision from PRICE_FILTER filter
for _, filter := range s.Filters {
if filter["filterType"] == "PRICE_FILTER" {
tickSize := filter["tickSize"].(string)
precision := calculatePrecision(tickSize)
return precision, nil
}
}
}
}
// Default to 2 decimal places for price
return 2, nil
}
// FormatPrice formats price to correct precision
func (t *FuturesTrader) FormatPrice(symbol string, price float64) (string, error) {
precision, err := t.GetSymbolPricePrecision(symbol)
if err != nil {
// If retrieval fails, use default format
return fmt.Sprintf("%.2f", price), nil
}
format := fmt.Sprintf("%%.%df", precision)
return fmt.Sprintf(format, price), nil
}
// Helper functions
func contains(s, substr string) bool {
return len(s) >= len(substr) && stringContains(s, substr)