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 }