gitee.com/haifengat/gotap_dipper@v0.0.4-0.20231212021028-041a6fa876e5/trade/trade_ext.go (about)

     1  package trade
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"strings"
     7  	"sync"
     8  
     9  	"github.com/sirupsen/logrus"
    10  )
    11  
    12  type TradeExt struct {
    13  	*Trade
    14  	UserID    string
    15  	AccountID []string // 子号
    16  	chReady   chan TAPIINT32
    17  	// 品种@类型@交易所
    18  	//
    19  	// ("%s@%c@%s", info.CommodityNo.String(), info.CommodityType, info.ExchangeNo.String())
    20  	Product map[string]TapAPICommodityInfo
    21  	// 品种@合约1@合约2@类型@交易所@CP@行权价
    22  	//
    23  	// ("%s@%s@%s@%c@%s@%c@%s", info.CommodityNo.String(), info.ContractNo1.String(), info.ContractNo2.String(), info.CommodityType, info.ExchangeNo.String(), info.CallOrPutFlag1, info.StrikePrice1.String())
    24  	Instrument map[string]TapAPITradeContractInfo
    25  
    26  	// fmt.Sprintf("%s|%s", info.AccountNo.String(), info.Key())
    27  	// key: ("%s@%s@%c@%s", info.CommodityNo.String(), info.ContractNo.String(), info.CommodityType, info.ExchangeNo.String())
    28  	//
    29  	// TapAPIPositionSummary
    30  	MapPositionSummary sync.Map
    31  
    32  	// MatchNo:TapAPIFillInfo
    33  	MapTrade sync.Map
    34  
    35  	// 委托响应
    36  	// OnOrder func(info *TapAPIOrderInfo)
    37  	// 成交响应
    38  	OnTrade func(info *TapAPIFillInfo)
    39  	// 持仓响应
    40  	OnPosition func(info *TapAPIPositionSummary)
    41  }
    42  
    43  func NewTradeExt(appID, authCode string) (*TradeExt, error) {
    44  	var err error
    45  	ext := new(TradeExt)
    46  	ext.chReady = make(chan TAPIINT32)
    47  	ext.Trade, err = NewTrade(appID, authCode)
    48  	if err != nil {
    49  		return nil, err
    50  	}
    51  	ext.Instrument = make(map[string]TapAPITradeContractInfo)
    52  	ext.Product = make(map[string]TapAPICommodityInfo)
    53  
    54  	return ext, nil
    55  }
    56  
    57  func (t *TradeExt) ReqConnect(tradeIP string, port uint) error {
    58  	t.OnRspQryCommodity = func(sessionID TAPIUINT32, errorCode TAPIINT32, isLast TAPIYNFLAG, info *TapAPICommodityInfo) {
    59  		if info != nil {
    60  			key := fmt.Sprintf("%s@%c@%s", info.CommodityNo.String(), info.CommodityType, info.ExchangeNo.String())
    61  			if _, exists := t.Product[key]; exists {
    62  				fmt.Println(key, " exists!")
    63  			} else {
    64  				t.Product[key] = *info
    65  			}
    66  		}
    67  		if isLast == APIYNFLAG_YES {
    68  			fmt.Println("查询品种: ", len(t.Product))
    69  			var s TAPIUINT32
    70  			res := t.QryContract(&s, &TapAPICommodity{}) // 查合约
    71  			if res != 0 {
    72  				fmt.Println("查合约: ", t.GetITapErrorDescribe(FromCint32(res)))
    73  				// 登录成功通知
    74  				t.chReady <- FromCint32(res)
    75  			}
    76  		}
    77  	}
    78  	t.OnRspQryContract = func(sessionID TAPIUINT32, errorCode TAPIINT32, isLast TAPIYNFLAG, info *TapAPITradeContractInfo) {
    79  		if info != nil {
    80  			// key := fmt.Sprintf("%s@%s@%c@%s", info.CommodityNo.String(), info.ContractNo1.String(), info.CommodityType)
    81  			// if info.CommodityType == "" {
    82  			key := fmt.Sprintf("%s@%s@%s@%c@%s@%c@%s", info.CommodityNo.String(), info.ContractNo1.String(), info.ContractNo2.String(), info.CommodityType, info.ExchangeNo.String(), info.CallOrPutFlag1, info.StrikePrice1.String())
    83  			// }
    84  			if _, exists := t.Instrument[key]; exists {
    85  				// fmt.Println(key, " is Exists!")
    86  			} else {
    87  				t.Instrument[key] = *info
    88  			}
    89  		}
    90  		if isLast == APIYNFLAG_YES {
    91  			fmt.Printf("查合约: %d\n", len(t.Instrument))
    92  			go func() {
    93  				// 查所有持仓
    94  				var s TAPIUINT32
    95  				res := t.QryPositionSummary(&s, &TapAPIPositionQryReq{})
    96  				if res != 0 {
    97  					fmt.Println("查持仓: ", t.GetITapErrorDescribe(FromCint32(res)))
    98  					// 登录成功通知
    99  					t.chReady <- FromCint32(res)
   100  				}
   101  			}()
   102  		}
   103  	}
   104  	// 用于查询持仓的响应
   105  	t.OnRspQryPositionSummary = func(sessionID TAPIUINT32, errorCode TAPIINT32, isLast TAPIYNFLAG, info *TapAPIPositionSummary) {
   106  		if info != nil {
   107  			key := fmt.Sprintf("%s|%s", info.AccountNo.String(), info.Key())
   108  			t.MapPositionSummary.Store(key, *info)
   109  		}
   110  		if isLast == APIYNFLAG_YES {
   111  			// 登录成功通知
   112  			t.chReady <- FromInt32(0)
   113  		}
   114  	}
   115  	t.OnRspQryAccount = func(sessionID, errorCode TAPIUINT32, isLast TAPIYNFLAG, info *TapAPIAccountInfo) {
   116  		if info != nil {
   117  			t.AccountID = append(t.AccountID, info.AccountNo.String())
   118  		}
   119  		if isLast == APIYNFLAG_YES {
   120  			var s TAPIUINT32
   121  			res := t.QryCommodity(&s) // 查品种
   122  			if res != 0 {
   123  				fmt.Println("查品种: ", t.GetITapErrorDescribe(FromCint32(res)))
   124  				// 登录成功通知
   125  				t.chReady <- FromCint32(res)
   126  			}
   127  		}
   128  	}
   129  	t.OnAPIReady = func(errorCode TAPIINT32) {
   130  		if errorCode.Int32() == 0 {
   131  			var s TAPIUINT32
   132  			res := t.QryAccount(&s, &TapAPIAccQryReq{})
   133  			if res != 0 {
   134  				fmt.Println("查子交易帐号: ", t.GetITapErrorDescribe(FromCint32(res)))
   135  				// 登录成功通知
   136  				t.chReady <- FromCint32(res)
   137  			}
   138  		} else {
   139  			logrus.Error(t.GetITapErrorDescribe(errorCode))
   140  		}
   141  	}
   142  
   143  	t.OnConnect = func(HostAddress string) {
   144  		fmt.Println("连接前置: ", HostAddress)
   145  	}
   146  	t.OnRspLogin = func(errorCode TAPIINT32, loginRspInfo *TapAPITradeLoginRspInfo) {
   147  		if errorCode.Int32() == 0 {
   148  			fmt.Printf("%+v\n", *loginRspInfo)
   149  		} else {
   150  			fmt.Printf("登录错误: {%d:%s}\n", errorCode.Int32(), t.GetITapErrorDescribe(errorCode))
   151  			t.chReady <- errorCode
   152  		}
   153  	}
   154  	t.OnDisconnect = func(reasonCode TAPIINT32) {
   155  		fmt.Printf("断开: {%d:%s}\n", reasonCode.Int32(), t.GetITapErrorDescribe(reasonCode))
   156  	}
   157  	t.OnRtnPosition = func(info *TapAPIPositionInfo) {
   158  		// fmt.Printf("OnRtnPosition: %+v", info)
   159  	}
   160  	t.OnRtnPositionProfit = func(info *TapAPIPositionProfitNotice) {
   161  		// fmt.Printf("OnRtnPositionProfit: %+v", *info)
   162  	}
   163  	t.OnRtnPositionSummary = func(info *TapAPIPositionSummary) {
   164  		key := fmt.Sprintf("%s|%s", info.AccountNo.String(), info.Key())
   165  		t.MapPositionSummary.Store(key, *info)
   166  		if t.OnPosition != nil {
   167  			t.OnPosition(info)
   168  		}
   169  	}
   170  	t.OnRspOrderAction = func(sessionID TAPIUINT32, errorCode TAPIINT32, info *TapAPIOrderActionRsp) {
   171  		// fmt.Println(info.ActionType)
   172  	}
   173  	t.OnRtnOrder = func(info *TapAPIOrderInfoNotice) {
   174  		fmt.Printf("OnRtnOrder, sessionID:%d {%d:%s}\n", info.SessionID.Uint32(), info.ErrorCode.Uint32(), t.GetITapErrorDescribe(TAPIINT32(info.ErrorCode)))
   175  	}
   176  	t.OnRtnSpecialOrder = func(info *TapAPISpecialOrderInfo) {
   177  		fmt.Printf("OnRtnSpecialOrder, sessionID:%d %d:%s\n", info.SessionID.Uint32(), info.ErrorCode, t.GetITapErrorDescribe(TAPIINT32(info.ErrorCode)))
   178  	}
   179  	t.OnRtnFill = func(info *TapAPIFillInfo) {
   180  		if t.OnTrade == nil {
   181  			// fmt.Printf("OnRtnFill %+v\n", *info)
   182  			t.MapTrade.Store(info.MatchNo, *info)
   183  		} else {
   184  			t.OnTrade(info)
   185  		}
   186  	}
   187  
   188  	if res := t.SetHostAddress(tradeIP, TAPIUINT16(port)); res != 0 {
   189  		msg := t.GetITapErrorDescribe(FromCint32(res))
   190  		return errors.New(msg)
   191  	}
   192  	return nil
   193  }
   194  
   195  func (t *TradeExt) ReqLogin(userID, passWord string) error {
   196  	logAuth := TapAPITradeLoginAuth{
   197  		ISModifyPassword: APIYNFLAG_NO,
   198  	}
   199  	copy(logAuth.UserNo[:], userID)
   200  	copy(logAuth.Password[:], passWord)
   201  	if res := t.Login(&logAuth); res != 0 {
   202  		msg := t.GetITapErrorDescribe(FromCint32(res))
   203  		return errors.New(msg)
   204  	}
   205  	res := <-t.chReady
   206  	if res.Int32() != 0 { // 登录错误
   207  		return fmt.Errorf("%d:%s", res.Int32(), t.GetITapErrorDescribe(res))
   208  	}
   209  	t.UserID = userID
   210  	return nil
   211  }
   212  
   213  func (t *TradeExt) ReqLogout() error {
   214  	res := t.Disconnect()
   215  	if res != 0 {
   216  		return errors.New(t.GetITapErrorDescribe(FromCint32(res)))
   217  	}
   218  	// t.FreeITapTradeAPI(t.api) // 崩溃
   219  	t.Trade = nil
   220  	return nil
   221  }
   222  
   223  func (t *TradeExt) ReqOrderInsert(exchangeID, commodityNo, contractNo string, orderType TAPIOrderTypeType, price float64, qty int, side TAPISideType, commodityType TAPICommodityType, accountID ...string) (session uint, clientNo string, err error) {
   224  	account := t.UserID
   225  	if len(accountID) > 0 {
   226  		account = accountID[0]
   227  	}
   228  	var order = TapAPINewOrder{}
   229  	copy(order.AccountNo[:], []byte(account))
   230  	copy(order.ExchangeNo[:], []byte(exchangeID))
   231  	copy(order.CommodityNo[:], []byte(commodityNo))
   232  	copy(order.ContractNo[:], []byte(contractNo))
   233  	// copy(order.ContractNo2[:], contractNo) // 提示: 下单无效的合约
   234  
   235  	order.OrderType = orderType // 限价 市价...
   236  	order.OrderPrice = FromFloat64(price)
   237  	order.OrderQty = FromUint32(uint32(qty))
   238  	order.OrderMinQty = FromUint32(uint32(1))
   239  
   240  	order.HedgeFlag = TAPI_HEDGEFLAG_NONE
   241  	// order.ClientID
   242  	// order.ExpireTime // GTD 时使用
   243  	order.TimeInForce = TAPI_ORDER_TIMEINFORCE_GFD
   244  
   245  	order.OrderSide = side              // 买卖
   246  	order.CommodityType = commodityType // 品种类型
   247  
   248  	order.CallOrPutFlag = TAPI_CALLPUT_FLAG_NONE
   249  	order.CallOrPutFlag2 = TAPI_CALLPUT_FLAG_NONE
   250  
   251  	order.TimeInForce = TAPI_ORDER_TIMEINFORCE_GFD // 有效期
   252  	order.IsRiskOrder = APIYNFLAG_NO
   253  	order.PositionEffect = TAPI_PositionEffect_NONE
   254  	order.PositionEffect2 = TAPI_PositionEffect_NONE
   255  	order.HedgeFlag = TAPI_HEDGEFLAG_NONE
   256  	// {-12035:输入错误的:TAPITacticsTypeType}
   257  	// order.TacticsType = TAPI_TACTICS_TYPE_ATUO
   258  	order.TacticsType = TAPI_TACTICS_TYPE_NONE    // 策略单类型
   259  	order.OrderSource = TAPI_ORDER_SOURCE_PROGRAM // 来源
   260  	order.AddOneIsValid = APIYNFLAG_NO
   261  
   262  	order.TriggerCondition = TAPI_TRIGGER_CONDITION_NONE
   263  	order.TriggerPriceType = TAPI_TRIGGER_PRICE_NONE
   264  
   265  	var s TAPIUINT32
   266  	var c TAPISTR_50
   267  	res := t.InsertOrder(&s, &c, &order)
   268  	if res != 0 {
   269  		err = fmt.Errorf("委托: {%d:%s}", res, t.GetITapErrorDescribe(FromCint32(res)))
   270  		return
   271  	}
   272  	session = uint(s.Uint32())
   273  	clientNo = c.String()
   274  	return
   275  }
   276  
   277  func (t *TradeExt) ReqOrderInsertLMT(exchangeID, commodityNo, contractNo string, price float64, qty int, side TAPISideType, commodityType TAPICommodityType) (session uint, clientNo string, err error) {
   278  	return t.ReqOrderInsert(exchangeID, commodityNo, contractNo, TAPI_ORDER_TYPE_LIMIT, price, qty, side, commodityType)
   279  }
   280  
   281  func (t *TradeExt) ReqOrderInsertMKT(exchangeID, commodityNo, contractNo string, qty int, side TAPISideType, commodityType TAPICommodityType) (session uint, clientNo string, err error) {
   282  	return t.ReqOrderInsert(exchangeID, commodityNo, contractNo, TAPI_ORDER_TYPE_MARKET, 0, qty, side, commodityType)
   283  }
   284  
   285  func (t *TradeExt) ReqQryPosition(accountID string) []TapAPIPositionSummary {
   286  	res := make([]TapAPIPositionSummary, 0)
   287  	t.MapPositionSummary.Range(func(key, value any) bool {
   288  		// 删除指定帐号的持仓
   289  		if strings.HasPrefix(key.(string), accountID+"|") {
   290  			res = append(res, value.(TapAPIPositionSummary))
   291  		}
   292  		return true
   293  	})
   294  	return res
   295  }