180 lines
5.4 KiB
Go
180 lines
5.4 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"`
|
|
}
|
|
|
|
type Assets struct {
|
|
Total float64 `json:"total"`
|
|
Available float64 `json:"available"`
|
|
}
|
|
|
|
func (c *Client) Positions(ctx context.Context) ([]string, []Position, error) {
|
|
return c.decodePositions(ctx, "/api/v2/positions")
|
|
}
|
|
|
|
func (c *Client) Holding(ctx context.Context) ([]string, []Position, error) {
|
|
return c.decodePositions(ctx, "/api/holding")
|
|
}
|
|
|
|
func (c *Client) decodePositions(ctx context.Context, path string) ([]string, []Position, error) {
|
|
raw := map[string]json.RawMessage{}
|
|
if err := c.post(ctx, path, map[string]any{"account": c.accountType}, &raw); err != nil {
|
|
return nil, nil, err
|
|
}
|
|
codes := make([]string, 0, len(raw))
|
|
out := make([]Position, 0, len(raw))
|
|
for code, blob := range raw {
|
|
var p Position
|
|
if err := json.Unmarshal(blob, &p); err != nil {
|
|
return nil, nil, fmt.Errorf("position %s: %w", code, err)
|
|
}
|
|
if p.StockCode == "" {
|
|
p.StockCode = code
|
|
}
|
|
codes = append(codes, code)
|
|
out = append(out, p)
|
|
}
|
|
return codes, out, nil
|
|
}
|
|
|
|
func (c *Client) Assets(ctx context.Context) (*Assets, error) {
|
|
var out Assets
|
|
if err := c.post(ctx, "/api/v2/assets", map[string]any{"account": c.accountType}, &out); err != nil {
|
|
return nil, err
|
|
}
|
|
return &out, nil
|
|
}
|
|
|
|
func (c *Client) TotalMoney(ctx context.Context) (float64, error) {
|
|
var out struct {
|
|
TotalMoney float64 `json:"total_money"`
|
|
}
|
|
if err := c.post(ctx, "/api/money/total", map[string]any{"account": c.accountType}, &out); err != nil {
|
|
return 0, err
|
|
}
|
|
return out.TotalMoney, nil
|
|
}
|
|
|
|
func (c *Client) AvailableMoney(ctx context.Context) (float64, error) {
|
|
var out struct {
|
|
AvailableMoney float64 `json:"available_money"`
|
|
}
|
|
if err := c.post(ctx, "/api/money/available", map[string]any{"account": c.accountType}, &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) ([]OrderStatus, error) {
|
|
var out struct {
|
|
Orders []OrderStatus `json:"orders"`
|
|
}
|
|
if err := c.post(ctx, "/api/order/status", map[string]any{"account": c.accountType}, &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) (*CancelAllResult, error) {
|
|
var out CancelAllResult
|
|
if err := c.post(ctx, "/api/order/cancel_all", map[string]any{"account": c.accountType}, &out); err != nil {
|
|
return nil, err
|
|
}
|
|
return &out, nil
|
|
}
|
|
|
|
// CancelByRule 按「代码.市场 + (剩余+已成)」匹配撤单。HTTP 没有按委托号撤单。
|
|
func (c *Client) CancelByRule(ctx context.Context, stock string, volume int) (*CancelAllResult, error) {
|
|
var out CancelAllResult
|
|
body := map[string]any{"stock": stock, "volume": volume, "account": c.accountType}
|
|
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) ([]map[string]string, error) {
|
|
var out struct {
|
|
Deals []map[string]string `json:"deals"`
|
|
}
|
|
if err := c.post(ctx, "/api/order/deal", map[string]any{"account": c.accountType}, &out); err != nil {
|
|
return nil, err
|
|
}
|
|
return out.Deals, nil
|
|
}
|