194 lines
5.8 KiB
Go
194 lines
5.8 KiB
Go
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
|
||
}
|