Files
big-qmt/go-client/sdk/account.go
2026-08-25 16:40:18 +08:00

194 lines
5.8 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package sdk
import (
"context"
"encoding/json"
"fmt"
)
// Position 对应 HoldingHandler 封装后的持仓。
type Position struct {
StockCode string `json:"StockCode"`
StockName string `json:"StockName"`
Direction any `json:"Direction"`
Volume int `json:"Volume"`
OpenPrice float64 `json:"OpenPrice"`
FloatProfit float64 `json:"FloatProfit"`
MarketValue float64 `json:"MarketValue"`
StockHolder string `json:"StockHolder"`
FrozenVolume int `json:"FrozenVolume"`
CanUseVolume int `json:"CanUseVolume"`
OnRoadVolume int `json:"OnRoadVolume"`
YesterdayVolume int `json:"YesterdayVolume"`
LastPrice float64 `json:"LastPrice"`
ProfitRate float64 `json:"ProfitRate"`
FutureTradeType any `json:"FutureTradeType"`
ExpireDate string `json:"ExpireDate"`
}
// Assets 对应 /api/v2/assets。
type Assets struct {
Total float64 `json:"total"`
Available float64 `json:"available"`
}
type accountBody struct {
Account string `json:"account"`
}
func (c *Client) Positions(ctx context.Context, account string) ([]Position, error) {
raw := map[string]json.RawMessage{}
if err := c.post(ctx, "/api/v2/positions", accountBody{Account: c.Account(account)}, &raw); err != nil {
return nil, err
}
out := make([]Position, 0, len(raw))
for code, blob := range raw {
var p Position
if err := json.Unmarshal(blob, &p); err != nil {
return nil, fmt.Errorf("position %s: %w", code, err)
}
if p.StockCode == "" {
p.StockCode = code
}
out = append(out, p)
}
return out, nil
}
func (c *Client) Holding(ctx context.Context, account string) ([]Position, error) {
raw := map[string]json.RawMessage{}
if err := c.post(ctx, "/api/holding", accountBody{Account: c.Account(account)}, &raw); err != nil {
return nil, err
}
out := make([]Position, 0, len(raw))
for code, blob := range raw {
var p Position
if err := json.Unmarshal(blob, &p); err != nil {
return nil, fmt.Errorf("holding %s: %w", code, err)
}
if p.StockCode == "" {
p.StockCode = code
}
out = append(out, p)
}
return out, nil
}
func (c *Client) Assets(ctx context.Context, account string) (*Assets, error) {
var out Assets
if err := c.post(ctx, "/api/v2/assets", accountBody{Account: c.Account(account)}, &out); err != nil {
return nil, err
}
return &out, nil
}
func (c *Client) TotalMoney(ctx context.Context, account string) (float64, error) {
var out struct {
TotalMoney float64 `json:"total_money"`
}
if err := c.post(ctx, "/api/money/total", accountBody{Account: c.Account(account)}, &out); err != nil {
return 0, err
}
return out.TotalMoney, nil
}
func (c *Client) AvailableMoney(ctx context.Context, account string) (float64, error) {
var out struct {
AvailableMoney float64 `json:"available_money"`
}
if err := c.post(ctx, "/api/money/available", accountBody{Account: c.Account(account)}, &out); err != nil {
return 0, err
}
return out.AvailableMoney, nil
}
type OrderRefResult struct {
Status string `json:"status"`
Action string `json:"action"`
Stock string `json:"stock"`
OpType int `json:"opType"`
OrderRef string `json:"order_ref"`
}
func (c *Client) Buy(ctx context.Context, stock string, price float64, volume int, prType int) (*OrderRefResult, error) {
body := map[string]any{"stock": stock, "price": price, "volume": volume}
if prType != 0 {
body["prType"] = prType
}
var out OrderRefResult
if err := c.post(ctx, "/api/order/buy", body, &out); err != nil {
return nil, err
}
return &out, nil
}
func (c *Client) Sell(ctx context.Context, stock string, price float64, volume int, prType int) (*OrderRefResult, error) {
body := map[string]any{"stock": stock, "price": price, "volume": volume}
if prType != 0 {
body["prType"] = prType
}
var out OrderRefResult
if err := c.post(ctx, "/api/order/sell", body, &out); err != nil {
return nil, err
}
return &out, nil
}
type OrderStatus struct {
OrderSysID string `json:"order_sys_id"`
Status int `json:"status"`
VolumeLeft int `json:"volume_left"`
VolumeTraded int `json:"volume_traded"`
}
func (c *Client) OrderStatusList(ctx context.Context, account string) ([]OrderStatus, error) {
var out struct {
Orders []OrderStatus `json:"orders"`
}
if err := c.post(ctx, "/api/order/status", accountBody{Account: c.Account(account)}, &out); err != nil {
return nil, err
}
return out.Orders, nil
}
type CanceledOrder struct {
OrderSysID string `json:"order_sys_id"`
Stock string `json:"stock"`
VolumeLeft int `json:"volume_left"`
}
type CancelAllResult struct {
Status string `json:"status"`
Message string `json:"message"`
CanceledOrders []CanceledOrder `json:"canceled_orders"`
CanceledSysIDs []string `json:"canceled_sys_ids"`
}
func (c *Client) CancelAll(ctx context.Context, account string) (*CancelAllResult, error) {
var out CancelAllResult
if err := c.post(ctx, "/api/order/cancel_all", accountBody{Account: c.Account(account)}, &out); err != nil {
return nil, err
}
return &out, nil
}
// CancelByRule 按「代码.市场 + (剩余+已成)」匹配撤单。HTTP 没有按委托号撤单ZT 用它代替 cancel(orderId)。
func (c *Client) CancelByRule(ctx context.Context, stock string, volume int, account string) (*CancelAllResult, error) {
var out CancelAllResult
body := map[string]any{"stock": stock, "volume": volume, "account": c.Account(account)}
if err := c.post(ctx, "/api/order/cancel_order", body, &out); err != nil {
return nil, err
}
return &out, nil
}
func (c *Client) Deals(ctx context.Context, account string) ([]map[string]string, error) {
var out struct {
Deals []map[string]string `json:"deals"`
}
if err := c.post(ctx, "/api/order/deal", accountBody{Account: c.Account(account)}, &out); err != nil {
return nil, err
}
return out.Deals, nil
}