@@ -431,7 +431,7 @@ internal class CtpTdApi(val config: CtpConfig, val kEvent: KEvent, val sourceId:
431431 }
432432
433433 /* *
434- * 查询某一特定合约的信息
434+ * 查询某一特定合约的信息。[extras.queryFee: Boolean = false]【是否查询保证金率及手续费率,如果之前没查过,可能会耗时。当 useCache 为 false 时无效】
435435 */
436436 suspend fun queryInstrument (code : String , useCache : Boolean = true, extras : Map <String , Any >? = null): Security ? {
437437 if (useCache) {
@@ -896,17 +896,21 @@ internal class CtpTdApi(val config: CtpConfig, val kEvent: KEvent, val sourceId:
896896 when (instrument.type) {
897897 SecurityType .FUTURES -> {
898898 if (instrument.commissionRate == null ) {
899+ postBrokerLogEvent(LogLevel .INFO , " 自动查询期货手续费率:$code " )
899900 runWithRetry({ queryFuturesCommissionRate(code) }) { e -> handleException(e, " 【CtpTdApi.prepareFeeCalculation】查询期货手续费率出错:$code , $e " ) }
900901 }
901902 if (instrument.marginRate == null ) {
903+ postBrokerLogEvent(LogLevel .INFO , " 自动查询期货保证金率:$code " )
902904 runWithRetry({ queryFuturesMarginRate(code) }) { e -> handleException(e, " 【CtpTdApi.prepareFeeCalculation】查询期货保证金率出错:$code , $e " ) }
903905 }
904906 }
905907 SecurityType .OPTIONS -> {
906908 if (instrument.commissionRate == null ) {
909+ postBrokerLogEvent(LogLevel .INFO , " 自动查询期权手续费率:$code " )
907910 runWithRetry({ queryOptionsCommissionRate(code) }) { e -> handleException(e, " 查询期权手续费率出错:$code , $e " ) }
908911 }
909912 if (instrument.marginRate == null ) {
913+ postBrokerLogEvent(LogLevel .INFO , " 自动查询期权保证金:$code " )
910914 runWithRetry({ queryOptionsMargin(code) }) { e -> handleException(e, " 查询期保证金出错:$code , $e " ) }
911915 }
912916 }
@@ -920,7 +924,6 @@ internal class CtpTdApi(val config: CtpConfig, val kEvent: KEvent, val sourceId:
920924 */
921925 suspend fun prepareFeeCalculation (codes : Collection <String >? = null, extras : Map <String , Any >? = null) {
922926 if (codes == null ) {
923- // 因为 queryFuturesCommissionRate 可能会进行申报手续费的二次异步查询,所以先查手续费率
924927 runWithRetry({ queryFuturesCommissionRate() })
925928 runWithRetry({ queryFuturesMarginRate() })
926929 runWithRetry({ queryOptionsCommissionRate() })
@@ -945,6 +948,7 @@ internal class CtpTdApi(val config: CtpConfig, val kEvent: KEvent, val sourceId:
945948 // 如果缓存的 tick 为空,说明未订阅该合约,那么订阅该合约以方便后续计算
946949 if (tick == null ) {
947950 try {
951+ postBrokerLogEvent(LogLevel .INFO , " 自动订阅行情:$code " )
948952 runBlocking { mdApi.subscribeMarketData(listOf (code)) }
949953 } catch (e: Exception ) {
950954 postBrokerLogEvent(LogLevel .ERROR , " 【CtpTdApi.getOrQueryTick】计算保证金时自动订阅合约行情失败:$code , $e " )
@@ -975,6 +979,7 @@ internal class CtpTdApi(val config: CtpConfig, val kEvent: KEvent, val sourceId:
975979 * 计算期货保证金
976980 */
977981 private fun calculateFuturesMargin (instrument : Security , direction : Direction , yesterdayVolume : Int , todayVolume : Int , avgOpenPrice : Double , fallback : Double ): Double {
982+ if (yesterdayVolume + todayVolume == 0 ) return 0.0
978983 val marginRate = getOrQueryMarginRate(instrument) ? : return fallback
979984 val (tick, isLatestTick) = getOrQueryTick(instrument.code)
980985 if (tick == null ) {
@@ -1015,6 +1020,7 @@ internal class CtpTdApi(val config: CtpConfig, val kEvent: KEvent, val sourceId:
10151020 * 计算期权保证金
10161021 */
10171022 private fun calculateOptionsMargin (instrument : Security , direction : Direction , volume : Int , avgOpenPrice : Double , fallback : Double , isOpen : Boolean ): Double {
1023+ if (volume == 0 ) return 0.0
10181024 when (direction) {
10191025 Direction .LONG -> { // 买方
10201026 return if (isOpen) avgOpenPrice * instrument.volumeMultiple else 0.0
@@ -1267,27 +1273,31 @@ internal class CtpTdApi(val config: CtpConfig, val kEvent: KEvent, val sourceId:
12671273 val result = withTimeoutOrNull(60000 ) {
12681274 // 请求客户端认证
12691275 try {
1276+ postBrokerLogEvent(LogLevel .INFO , " 【交易接口登录】客户端认证..." )
12701277 reqAuthenticate()
12711278 postBrokerLogEvent(LogLevel .INFO , " 【交易接口登录】客户端认证成功" )
12721279 } catch (e: Exception ) {
12731280 resumeRequestsWithException(" connect" , " 请求客户端认证失败:$e " )
12741281 }
12751282 // 请求用户登录
12761283 try {
1284+ postBrokerLogEvent(LogLevel .INFO , " 【交易接口登录】资金账户登录..." )
12771285 reqUserLogin()
12781286 postBrokerLogEvent(LogLevel .INFO , " 【交易接口登录】资金账户登录成功" )
12791287 } catch (e: Exception ) {
12801288 resumeRequestsWithException(" connect" , " 请求用户登录失败:$e " )
12811289 }
12821290 // 请求结算单确认
12831291 try {
1292+ postBrokerLogEvent(LogLevel .INFO , " 【交易接口登录】结算单确认..." )
12841293 reqSettlementInfoConfirm()
12851294 postBrokerLogEvent(LogLevel .INFO , " 【交易接口登录】结算单确认成功" )
12861295 } catch (e: Exception ) {
12871296 resumeRequestsWithException(" connect" , " 请求结算单确认失败:$e " )
12881297 }
12891298 // 查询全市场合约
12901299 runWithRetry({
1300+ postBrokerLogEvent(LogLevel .INFO , " 【交易接口登录】查询全市场合约..." )
12911301 val allInstruments = queryAllInstruments(false , null )
12921302 allInstruments.forEach {
12931303 instruments[it.code] = it
@@ -1296,19 +1306,21 @@ internal class CtpTdApi(val config: CtpConfig, val kEvent: KEvent, val sourceId:
12961306 }
12971307 postBrokerLogEvent(LogLevel .INFO , " 【交易接口登录】查询全市场合约成功" )
12981308 }) { e ->
1299- resumeRequestsWithException(" connect" , " 查询全市场合约信息失败 :$e " )
1309+ resumeRequestsWithException(" connect" , " 查询全市场合约失败 :$e " )
13001310 }
13011311 // 查询保证金价格类型、持仓合约的保证金率及手续费率(如果未禁止费用计算)
13021312 if (! config.disableFeeCalculation) {
13031313 // 查询保证金价格类型
13041314 runWithRetry({
1315+ postBrokerLogEvent(LogLevel .INFO , " 【交易接口登录】查询保证金价格类型..." )
13051316 queryMarginPriceType()
13061317 postBrokerLogEvent(LogLevel .INFO , " 【交易接口登录】查询保证金价格类型成功" )
13071318 }) { e ->
13081319 resumeRequestsWithException(" connect" , " 查询保证金价格类型失败:$e " )
13091320 }
13101321 // 查询持仓合约的手续费率及保证金率
13111322 try {
1323+ postBrokerLogEvent(LogLevel .INFO , " 【交易接口登录】查询持仓合约手续费率及保证金率..." )
13121324 prepareFeeCalculation()
13131325 postBrokerLogEvent(LogLevel .INFO , " 【交易接口登录】查询持仓合约手续费率及保证金率成功" )
13141326 } catch (e: Exception ) {
@@ -1317,13 +1329,21 @@ internal class CtpTdApi(val config: CtpConfig, val kEvent: KEvent, val sourceId:
13171329 }
13181330 // 查询账户持仓
13191331 runWithRetry({
1332+ postBrokerLogEvent(LogLevel .INFO , " 【交易接口登录】查询账户持仓..." )
13201333 queryPositions(useCache = false )
13211334 postBrokerLogEvent(LogLevel .INFO , " 【交易接口登录】查询账户持仓成功" )
13221335 }) { e ->
13231336 resumeRequestsWithException(" connect" , " 查询账户持仓失败:$e " )
13241337 }
1338+ // 订阅持仓合约行情(如果行情可用且未禁止自动订阅)
1339+ if (mdApi.connected && ! config.disableAutoSubscribe) {
1340+ postBrokerLogEvent(LogLevel .INFO , " 【交易接口登录】订阅持仓合约行情..." )
1341+ mdApi.subscribeMarketData(positions.keys)
1342+ postBrokerLogEvent(LogLevel .INFO , " 【交易接口登录】订阅持仓合约行情成功" )
1343+ }
13251344 // 查询当日订单
13261345 runWithRetry({
1346+ postBrokerLogEvent(LogLevel .INFO , " 【交易接口登录】查询当日订单..." )
13271347 val orders = queryOrders(onlyUnfinished = false , useCache = false )
13281348 val finishedStatus = setOf (OrderStatus .CANCELED , OrderStatus .FILLED , OrderStatus .ERROR )
13291349 orders.forEach {
@@ -1345,25 +1365,21 @@ internal class CtpTdApi(val config: CtpConfig, val kEvent: KEvent, val sourceId:
13451365 }
13461366 // 查询当日成交记录
13471367 runWithRetry({
1368+ postBrokerLogEvent(LogLevel .INFO , " 【交易接口登录】查询当日成交记录..." )
13481369 val trades = queryTrades(useCache = false )
13491370 todayTrades.addAll(trades)
13501371 postBrokerLogEvent(LogLevel .INFO , " 【交易接口登录】查询当日成交记录成功" )
13511372 }) { e ->
13521373 resumeRequestsWithException(" connect" , " 查询当日成交记录失败:$e " )
13531374 }
1354- // 订阅持仓合约行情(如果行情可用且未禁止自动订阅)
1355- if (mdApi.connected && ! config.disableAutoSubscribe) {
1356- mdApi.subscribeMarketData(positions.keys)
1357- postBrokerLogEvent(LogLevel .INFO , " 【交易接口登录】订阅持仓合约行情成功" )
1358- }
13591375 }
13601376 if (result == null ) {
13611377 resumeRequestsWithException(" connect" , " 登录操作超时" )
13621378 } else {
1379+ postBrokerConnectionEvent(ConnectionEventType .TD_NET_CONNECTED )
13631380 // 如果以上的所有操作都成功,那么登录成功
13641381 if (requestMap.values.any { it.tag == " connect" }) {
13651382 connected = true
1366- postBrokerConnectionEvent(ConnectionEventType .TD_NET_CONNECTED )
13671383 resumeRequests(" connect" , Unit )
13681384 }
13691385 }
@@ -2052,8 +2068,6 @@ internal class CtpTdApi(val config: CtpConfig, val kEvent: KEvent, val sourceId:
20522068 posList.forEach { calculatePosition(it, false ) }
20532069 // 如果是查询总持仓,更新持仓缓存
20542070 if (request.tag == " " ) {
2055- (request.continuation as Continuation <List <Position >>).resume(posList)
2056- requestMap.remove(nRequestID)
20572071 positions.clear()
20582072 posList.forEach {
20592073 val biPosition = positions.getOrPut(it.code) { BiPosition () }
@@ -2063,6 +2077,8 @@ internal class CtpTdApi(val config: CtpConfig, val kEvent: KEvent, val sourceId:
20632077 else -> postBrokerLogEvent(LogLevel .WARNING , " 【CtpTdSpi.OnRspQryInvestorPosition】查询到未知的持仓方向(${it.code} , ${it.direction} )" )
20642078 }
20652079 }
2080+ (request.continuation as Continuation <List <Position >>).resume(posList)
2081+ requestMap.remove(nRequestID)
20662082 } else { // 查询单合约持仓
20672083 when (request.tag) {
20682084 Direction .LONG .name -> {
0 commit comments