diff --git a/pom.xml b/pom.xml index cf60adb83cfe6d19733521a8ea9d0c662e83e61a..23fc37b72c817a0d1dd616cb34aa84940aa14ca2 100644 --- a/pom.xml +++ b/pom.xml @@ -1,64 +1,76 @@ - - 4.0.0 - org.dromara - northstar-gateway-tiger - 0.2.0 - - - 17 - UTF-8 - ${java.version} - ${java.version} - - - - - org.dromara - northstar-api - - - com.alibaba - fastjson - - - commons-io - commons-io - - - org.apache.commons - commons-lang3 - - - org.projectlombok - lombok - - - org.slf4j - slf4j-api - - - io.github.tigerbrokers - openapi-java-sdk - 1.4.9 - - - org.springframework.boot - spring-boot-starter-test - 2.6.14 - test - - - - - - - org.dromara - northstar - 6.1.1.Final - pom - import - - - - - \ No newline at end of file + + 4.0.0 + org.dromara + northstar-gateway-tiger + 0.2.0 + + + 21 + UTF-8 + ${java.version} + ${java.version} + + + + + org.dromara + northstar-api + + + com.alibaba + fastjson + + + commons-io + commons-io + + + org.apache.commons + commons-lang3 + + + org.projectlombok + lombok + + + org.slf4j + slf4j-api + + + org.springframework.boot + spring-boot-starter-test + test + + + io.github.tigerbrokers + openapi-java-sdk + 2.1.5 + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + 21 + 21 + + + + + + + + + org.dromara + northstar + 7.3.4 + pom + import + + + + + diff --git a/src/main/java/org/dromara/northstar/gateway/tiger/OrderTradeQueryProxy.java b/src/main/java/org/dromara/northstar/gateway/tiger/OrderTradeQueryProxy.java index a19789e34cb7ca611d9799004c2b915402d3936b..ba1001678ad9b490e960fefcdc9734c20d8cf5c6 100644 --- a/src/main/java/org/dromara/northstar/gateway/tiger/OrderTradeQueryProxy.java +++ b/src/main/java/org/dromara/northstar/gateway/tiger/OrderTradeQueryProxy.java @@ -1,22 +1,5 @@ package org.dromara.northstar.gateway.tiger; -import java.time.Instant; -import java.time.LocalDateTime; -import java.time.ZoneId; -import java.time.ZoneOffset; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Set; - -import org.apache.commons.lang3.StringUtils; -import org.dromara.northstar.common.constant.ChannelType; -import org.dromara.northstar.common.constant.DateTimeConstant; -import org.dromara.northstar.gateway.IContractManager; - import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; @@ -26,224 +9,227 @@ import com.tigerbrokers.stock.openapi.client.https.response.TigerHttpResponse; import com.tigerbrokers.stock.openapi.client.struct.enums.MethodName; import com.tigerbrokers.stock.openapi.client.struct.enums.SecType; import com.tigerbrokers.stock.openapi.client.util.builder.AccountParamBuilder; - import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.dromara.northstar.common.constant.ChannelType; +import org.dromara.northstar.common.constant.DateTimeConstant; +import org.dromara.northstar.common.model.core.Contract; +import org.dromara.northstar.common.model.core.Order; +import org.dromara.northstar.common.model.core.Trade; +import org.dromara.northstar.gateway.IContractManager; import xyz.redtorch.pb.CoreEnum.DirectionEnum; import xyz.redtorch.pb.CoreEnum.OffsetFlagEnum; import xyz.redtorch.pb.CoreEnum.OrderStatusEnum; import xyz.redtorch.pb.CoreEnum.TimeConditionEnum; -import xyz.redtorch.pb.CoreField.ContractField; -import xyz.redtorch.pb.CoreField.OrderField; -import xyz.redtorch.pb.CoreField.TradeField; + +import java.time.*; +import java.util.*; @Slf4j public class OrderTradeQueryProxy { - private static final long EXPIRY = 24 * 3600 * 1000; // 一天以内 - private final TigerHttpClient client; - private final IContractManager contractMgr; - private final String accountId; - private final String gatewayId; - - private Map orderIds = new HashMap<>(); - private Set tradeIds = new HashSet<>(); - - public OrderTradeQueryProxy(TigerHttpClient client, IContractManager contractMgr,String gatewayId, String accountId) { - this.client = client; - this.contractMgr = contractMgr; - this.accountId = accountId; - this.gatewayId = gatewayId; - } - - public List getDeltaOrder() { - TigerHttpRequest request = new TigerHttpRequest(MethodName.ORDERS); - String bizContent = AccountParamBuilder.instance() - .account(accountId) - .limit(100) - .buildJson(); - - request.setBizContent(bizContent); - TigerHttpResponse response = client.execute(request); - if(!response.isSuccess()) { - log.warn("查询订单返回异常:{}", response.getMessage()); - throw new IllegalStateException(response.getMessage()); - } - JSONObject data = JSON.parseObject(response.getData()); - JSONArray items = data.getJSONArray("items"); - List resultList = new ArrayList<>(100); - for(int i=0; i EXPIRY; - } - - private long getOpenTime(JSONObject json) { - return json.getLongValue("openTime"); - } - - private OrderField convertOrder(JSONObject json) { - Long id = json.getLong("id"); - String orderId = id + ""; - DirectionEnum direction = switch(json.getString("action")) { - case "BUY" -> DirectionEnum.D_Buy; - case "SELL" -> DirectionEnum.D_Sell; - default -> DirectionEnum.D_Unknown; - }; - - OffsetFlagEnum offset = switch(direction) { - case D_Buy -> OffsetFlagEnum.OF_Open; - case D_Sell -> OffsetFlagEnum.OF_Close; - default -> throw new IllegalArgumentException("Unexpected value: " + direction); - }; - - OrderStatusEnum orderStatus = switch(json.getString("status")) { - case "Filled" -> OrderStatusEnum.OS_AllTraded; - case "Cancelled" -> OrderStatusEnum.OS_Canceled; - case "Initial", "PendingSubmit", "Submitted" -> OrderStatusEnum.OS_Touched; - case "Invalid", "Inactive" -> OrderStatusEnum.OS_Rejected; - default -> throw new IllegalArgumentException("Unexpected value: " + json.getString("status")); - }; - - TimeConditionEnum timeCondition = switch(json.getString("timeInForce")) { - case "DAY" -> TimeConditionEnum.TC_GFD; - case "GTC" -> TimeConditionEnum.TC_GTC; - case "GTD" -> TimeConditionEnum.TC_GTD; - default -> throw new IllegalArgumentException("Unexpected value: " + json.getString("")); - }; - - String symbol = json.getString("symbol"); - ContractField contract = contractMgr.getContract(ChannelType.TIGER, symbol).contractField(); - String orderMsg = json.getString("remark"); - if(orderStatus == OrderStatusEnum.OS_Rejected && !orderIds.containsKey(id)) { - if(StringUtils.isEmpty(orderMsg)) { - log.warn("废单反馈:{}", json.toString(SerializerFeature.PrettyFormat)); - } else { - long openTime = getOpenTime(json); - LocalDateTime ldt = LocalDateTime.ofInstant(Instant.ofEpochMilli(openTime), ZoneId.systemDefault()); - log.warn("废单信息:{} {} {}", ldt, contract.getName(), orderMsg); - } - } - Instant ins = Instant.ofEpochMilli(getOpenTime(json)); - String tradingDay = LocalDateTime.ofInstant(ins, ZoneOffset.ofHours(0)).toLocalDate().format(DateTimeConstant.D_FORMAT_INT_FORMATTER); - String orderDate = LocalDateTime.ofInstant(ins, ZoneId.systemDefault()).toLocalDate().format(DateTimeConstant.D_FORMAT_INT_FORMATTER); - int tradedVol = json.getIntValue("filledQuantity"); - int totalVol = json.getIntValue("totalQuantity"); - double price = (int) (json.getDoubleValue("limitPrice") / contract.getPriceTick()) * contract.getPriceTick(); - return OrderField.newBuilder() - .setAccountId(accountId) - .setGatewayId(gatewayId) - .setContract(contract) - .setOrderId(orderId) - .setDirection(direction) - .setOffsetFlag(offset) - .setOrderDate(orderDate) - .setTotalVolume(totalVol) - .setTradedVolume(tradedVol) - .setPrice(price) - .setTradingDay(tradingDay) - .setOrderDate(orderDate) - .setOrderTime(LocalDateTime.ofInstant(ins, ZoneId.systemDefault()).toLocalTime().format(DateTimeConstant.T_FORMAT_FORMATTER)) - .setTimeCondition(timeCondition) - .setOrderStatus(orderStatus) - .build(); - } - - public List getTrades(String symbol) { - log.trace("查询TIGER成交信息"); - - TigerHttpRequest request = new TigerHttpRequest(MethodName.ORDER_TRANSACTIONS); - String bizContent = AccountParamBuilder.instance() - .account(accountId) - .secType(SecType.STK) - .symbol(symbol) - .limit(100) - .buildJson(); - request.setBizContent(bizContent); - TigerHttpResponse response = client.execute(request); - if(!response.isSuccess()) { - log.warn("查询成交返回异常:{}", response.getMessage()); - throw new IllegalStateException(response.getMessage()); - } - - return resolveData(response); - } - - public List getDeltaTrade(Long id) { - log.trace("查询TIGER成交信息"); - - TigerHttpRequest request = new TigerHttpRequest(MethodName.ORDER_TRANSACTIONS); - String bizContent = AccountParamBuilder.instance() - .account(accountId) - .secType(SecType.STK) - .orderId(id) - .limit(100) - .buildJson(); - request.setBizContent(bizContent); - TigerHttpResponse response = client.execute(request); - if(!response.isSuccess()) { - log.warn("查询成交返回异常:{}", response.getMessage()); - throw new IllegalStateException(response.getMessage()); - } - - return resolveData(response); - } - - private List resolveData(TigerHttpResponse response){ - JSONArray data = JSON.parseObject(response.getData()).getJSONArray("items"); - List resultList = new ArrayList<>(); - for(int i=0; i DirectionEnum.D_Buy; - case "SELL" -> DirectionEnum.D_Sell; - default -> DirectionEnum.D_Unknown; - }; - OffsetFlagEnum offset = switch(direction) { - case D_Buy -> OffsetFlagEnum.OF_Open; - case D_Sell -> OffsetFlagEnum.OF_Close; - default -> throw new IllegalArgumentException("Unexpected value: " + direction); - }; - TradeField trade = TradeField.newBuilder() - .setAccountId(accountId) - .setGatewayId(gatewayId) - .setTradeId(tradeId + "") - .setOrderId(orderId) - .setDirection(direction) - .setOffsetFlag(offset) - .setContract(contract) - .setPrice(json.getDoubleValue("filledPrice")) - .setVolume(json.getIntValue("filledQuantity")) - .setTradeDate(json.getString("transactedAt").split(" ")[0]) - .setTradeTime(json.getString("transactedAt").split(" ")[1]) - .setTradeTimestamp(tradeTime) - .setContract(contractMgr.getContract(ChannelType.TIGER, symbol).contractField()) - .build(); - resultList.add(trade); - } - return resultList; - } + private static final long EXPIRY = 24 * 3600 * 1000; // 一天以内 + private final TigerHttpClient client; + private final IContractManager contractMgr; + private final String accountId; + private final String gatewayId; + + private Map orderIds = new HashMap<>(); + private Set tradeIds = new HashSet<>(); + + public OrderTradeQueryProxy(TigerHttpClient client, IContractManager contractMgr, String gatewayId, String accountId) { + this.client = client; + this.contractMgr = contractMgr; + this.accountId = accountId; + this.gatewayId = gatewayId; + } + + public List getDeltaOrder() { + TigerHttpRequest request = new TigerHttpRequest(MethodName.ORDERS); + String bizContent = AccountParamBuilder.instance() + .account(accountId) + .limit(100) + .buildJson(); + + request.setBizContent(bizContent); + TigerHttpResponse response = client.execute(request); + if (!response.isSuccess()) { + log.warn("查询订单返回异常:{}", response.getMessage()); + throw new IllegalStateException(response.getMessage()); + } + JSONObject data = JSON.parseObject(response.getData()); + JSONArray items = data.getJSONArray("items"); + List resultList = new ArrayList<>(100); + for (int i = 0; i < items.size(); i++) { + JSONObject json = items.getJSONObject(i); + long openTime = getOpenTime(json); + if (expired(openTime)) { + continue; + } + Long id = json.getLong("id"); + Order order = convertOrder(json); + Order oldOrder = orderIds.get(id); + if (Objects.isNull(oldOrder) || order.tradedVolume() != oldOrder.tradedVolume() || order.orderStatus() != oldOrder.orderStatus()) { + orderIds.put(id, order); + resultList.add(order); + } + } + return resultList; + } + + private boolean expired(long time) { + return System.currentTimeMillis() - time > EXPIRY; + } + + private long getOpenTime(JSONObject json) { + return json.getLongValue("openTime"); + } + + private Order convertOrder(JSONObject json) { + Long id = json.getLong("id"); + String orderId = id + ""; + DirectionEnum direction = switch (json.getString("action")) { + case "BUY" -> DirectionEnum.D_Buy; + case "SELL" -> DirectionEnum.D_Sell; + default -> DirectionEnum.D_Unknown; + }; + + OffsetFlagEnum offset = switch (direction) { + case D_Buy -> OffsetFlagEnum.OF_Open; + case D_Sell -> OffsetFlagEnum.OF_Close; + default -> throw new IllegalArgumentException("Unexpected value: " + direction); + }; + + OrderStatusEnum orderStatus = switch (json.getString("status")) { + case "Filled" -> OrderStatusEnum.OS_AllTraded; + case "Cancelled" -> OrderStatusEnum.OS_Canceled; + case "Initial", "PendingSubmit", "Submitted" -> OrderStatusEnum.OS_Touched; + case "Invalid", "Inactive" -> OrderStatusEnum.OS_Rejected; + default -> throw new IllegalArgumentException("Unexpected value: " + json.getString("status")); + }; + + TimeConditionEnum timeCondition = switch (json.getString("timeInForce")) { + case "DAY" -> TimeConditionEnum.TC_GFD; + case "GTC" -> TimeConditionEnum.TC_GTC; + case "GTD" -> TimeConditionEnum.TC_GTD; + default -> throw new IllegalArgumentException("Unexpected value: " + json.getString("")); + }; + + String symbol = json.getString("symbol"); + Contract contract = contractMgr.getContract(ChannelType.TIGER, symbol).contract(); + String orderMsg = json.getString("remark"); + if (orderStatus == OrderStatusEnum.OS_Rejected && !orderIds.containsKey(id)) { + if (StringUtils.isEmpty(orderMsg)) { + log.warn("废单反馈:{}", json.toString(SerializerFeature.PrettyFormat)); + } else { + long openTime = getOpenTime(json); + LocalDateTime ldt = LocalDateTime.ofInstant(Instant.ofEpochMilli(openTime), ZoneId.systemDefault()); + log.warn("废单信息:{} {} {}", ldt, contract.name(), orderMsg); + } + } + Instant ins = Instant.ofEpochMilli(getOpenTime(json)); + String tradingDay = LocalDateTime.ofInstant(ins, ZoneOffset.ofHours(0)).toLocalDate().format(DateTimeConstant.D_FORMAT_INT_FORMATTER); + String orderDate = LocalDateTime.ofInstant(ins, ZoneId.systemDefault()).toLocalDate().format(DateTimeConstant.D_FORMAT_INT_FORMATTER); + int tradedVol = json.getIntValue("filledQuantity"); + int totalVol = json.getIntValue("totalQuantity"); + double price = (int) (json.getDoubleValue("limitPrice") / contract.priceTick()) * contract.priceTick(); + + return Order.builder() + .gatewayId(gatewayId) + .contract(contract) + .orderId(orderId) + .direction(direction) + .offsetFlag(offset) + .orderDate(LocalDate.parse(orderDate)) + .totalVolume(totalVol) + .tradedVolume(tradedVol) + .price(price) + .tradingDay(LocalDate.parse(tradingDay)) + .orderTime(LocalTime.parse(LocalDateTime.ofInstant(ins, ZoneId.systemDefault()).toLocalTime().format(DateTimeConstant.T_FORMAT_FORMATTER))) + .timeCondition(timeCondition) + .orderStatus(orderStatus).build(); + + } + + public List getTrades(String symbol) { + log.trace("查询TIGER成交信息"); + + TigerHttpRequest request = new TigerHttpRequest(MethodName.ORDER_TRANSACTIONS); + String bizContent = AccountParamBuilder.instance() + .account(accountId) + .secType(SecType.STK) + .symbol(symbol) + .limit(100) + .buildJson(); + request.setBizContent(bizContent); + TigerHttpResponse response = client.execute(request); + if (!response.isSuccess()) { + log.warn("查询成交返回异常:{}", response.getMessage()); + throw new IllegalStateException(response.getMessage()); + } + + return resolveData(response); + } + + public List getDeltaTrade(Long id) { + log.trace("查询TIGER成交信息"); + + TigerHttpRequest request = new TigerHttpRequest(MethodName.ORDER_TRANSACTIONS); + String bizContent = AccountParamBuilder.instance() + .account(accountId) + .secType(SecType.STK) + .orderId(id) + .limit(100) + .buildJson(); + request.setBizContent(bizContent); + TigerHttpResponse response = client.execute(request); + if (!response.isSuccess()) { + log.warn("查询成交返回异常:{}", response.getMessage()); + throw new IllegalStateException(response.getMessage()); + } + + return resolveData(response); + } + + private List resolveData(TigerHttpResponse response) { + JSONArray data = JSON.parseObject(response.getData()).getJSONArray("items"); + List resultList = new ArrayList<>(); + for (int i = 0; i < data.size(); i++) { + JSONObject json = data.getJSONObject(i); + String symbol = json.getString("symbol"); + Contract contract = contractMgr.getContract(ChannelType.TIGER, symbol).contract(); + Long tradeId = json.getLong("id"); + Long tradeTime = json.getLongValue("transactionTime"); + if (tradeIds.contains(tradeId) || expired(tradeTime)) { + continue; + } + tradeIds.add(tradeId); + String orderId = json.getString("orderId"); + DirectionEnum direction = switch (json.getString("action")) { + case "BUY" -> DirectionEnum.D_Buy; + case "SELL" -> DirectionEnum.D_Sell; + default -> DirectionEnum.D_Unknown; + }; + OffsetFlagEnum offset = switch (direction) { + case D_Buy -> OffsetFlagEnum.OF_Open; + case D_Sell -> OffsetFlagEnum.OF_Close; + default -> throw new IllegalArgumentException("Unexpected value: " + direction); + }; + Trade trade = Trade.builder() + .gatewayId(gatewayId) + .orderId(orderId) + .direction(direction) + .offsetFlag(offset) + .contract(contract) + .price(json.getDoubleValue("filledPrice")) + .volume(json.getIntValue("filledQuantity")) + .tradeDate(LocalDate.parse(json.getString("transactedAt").split(" ")[0])) + .tradeTime(LocalTime.parse(json.getString("transactedAt").split(" ")[1])) + .tradeTimestamp(tradeTime) + .contract(contractMgr.getContract(ChannelType.TIGER, symbol).contract()) + .build(); + resultList.add(trade); + } + return resultList; + } } diff --git a/src/main/java/org/dromara/northstar/gateway/tiger/TigerConfig.java b/src/main/java/org/dromara/northstar/gateway/tiger/TigerConfig.java index bae88d20d5cd1914954bc8a12a53d27404f64660..bb649a729aaa647fca5a3b630dc575eb64a5ab14 100644 --- a/src/main/java/org/dromara/northstar/gateway/tiger/TigerConfig.java +++ b/src/main/java/org/dromara/northstar/gateway/tiger/TigerConfig.java @@ -1,29 +1,35 @@ package org.dromara.northstar.gateway.tiger; +import lombok.extern.slf4j.Slf4j; import org.dromara.northstar.common.event.FastEventEngine; import org.dromara.northstar.gateway.IMarketCenter; +import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import lombok.extern.slf4j.Slf4j; - @Slf4j @Configuration public class TigerConfig { - - static { - log.info("====================================================="); - log.info(" 加载gateway-tiger "); - log.info("====================================================="); - } - - @Bean - TigerGatewayFactory tigerGatewayFactory(FastEventEngine feEngine, IMarketCenter marketCenter) { - return new TigerGatewayFactory(feEngine, marketCenter); - } - - @Bean - TigerDataServiceManager tigerDataServiceManager() { - return new TigerDataServiceManager(); - } + + static { + log.info("====================================================="); + log.info(" 加载gateway-tiger "); + log.info("====================================================="); + } + + @Bean + TigerDataServiceManager tigerDataServiceManager() { + return new TigerDataServiceManager(); + } + + @Bean + TigerGatewayFactory tigerGatewayFactory(FastEventEngine feEngine, IMarketCenter marketCenter, + @Qualifier("tigerDataServiceManager") TigerDataServiceManager dataMgr) { + return new TigerGatewayFactory(feEngine, marketCenter, dataMgr); + } + + @Bean + TigerGatewaySettings tigerGatewaySettings() { + return new TigerGatewaySettings(); + } } diff --git a/src/main/java/org/dromara/northstar/gateway/tiger/TigerContract.java b/src/main/java/org/dromara/northstar/gateway/tiger/TigerContract.java index c9bc649d6325b0143d2d75d1c0d3f863d41d0805..aea39677eb0ace939c5eb9e4e9104da1126282ac 100644 --- a/src/main/java/org/dromara/northstar/gateway/tiger/TigerContract.java +++ b/src/main/java/org/dromara/northstar/gateway/tiger/TigerContract.java @@ -1,112 +1,116 @@ package org.dromara.northstar.gateway.tiger; -import java.util.Objects; -import java.util.Optional; - +import com.tigerbrokers.stock.openapi.client.https.domain.contract.item.ContractItem; +import com.tigerbrokers.stock.openapi.client.https.domain.contract.item.TickSizeItem; +import org.dromara.northstar.common.IDataSource; import org.dromara.northstar.common.constant.ChannelType; import org.dromara.northstar.common.model.Identifier; +import org.dromara.northstar.common.model.core.Contract; +import org.dromara.northstar.common.model.core.ContractDefinition; import org.dromara.northstar.gateway.Instrument; -import org.dromara.northstar.gateway.TradeTimeDefinition; -import org.dromara.northstar.gateway.model.ContractDefinition; -import org.dromara.northstar.gateway.tiger.time.CnStockTradeTime; -import org.dromara.northstar.gateway.tiger.time.HkStockTradeTime; -import org.dromara.northstar.gateway.tiger.time.UsStockTradeTime; - -import com.tigerbrokers.stock.openapi.client.https.domain.contract.item.ContractItem; - import xyz.redtorch.pb.CoreEnum.CurrencyEnum; import xyz.redtorch.pb.CoreEnum.ExchangeEnum; import xyz.redtorch.pb.CoreEnum.ProductClassEnum; -import xyz.redtorch.pb.CoreField.ContractField; - -public class TigerContract implements Instrument{ - - private ContractItem item; - - private ContractDefinition contractDef; - - public TigerContract(ContractItem item) { - this.item = item; - } - - @Override - public String name() { - return item.getName() + "-" + item.getSymbol(); - } - - @Override - public Identifier identifier() { - return Identifier.of(String.format("%s@%s@%s@%s", item.getSymbol(), exchange(), productClass(), channelType())); - } - - @Override - public ProductClassEnum productClass() { - return switch(item.getSecType()) { - case "STK" -> ProductClassEnum.EQUITY; - case "OPT" -> ProductClassEnum.OPTION; - case "FUT" -> ProductClassEnum.FUTURES; - case "WAR" -> ProductClassEnum.WARRANTS; - case "IOPT" -> ProductClassEnum.SPOTOPTION; - default -> throw new IllegalArgumentException("Unexpected value: " + item.getSecType()); - }; - } - - @Override - public ExchangeEnum exchange() { - return switch(item.getExchange()) { - case "SMART","VALUE" -> ExchangeEnum.SMART; - case "SEHKSZSE" -> ExchangeEnum.SZSE; - case "SEHKNTL" -> ExchangeEnum.SSE; - case "SEHK" -> ExchangeEnum.SEHK; - default -> throw new IllegalArgumentException("Unexpected value: " + item.getExchange()); - }; - } - - @Override - public TradeTimeDefinition tradeTimeDefinition() { - if(Objects.isNull(contractDef)) { - throw new IllegalStateException(identifier().value() + " 没有合约定义信息"); - } - return switch(contractDef.getTradeTimeType()) { - case "CN_STK_TT" -> new CnStockTradeTime(); - case "HK_STK_TT" -> new HkStockTradeTime(); - case "US_STK_TT" -> new UsStockTradeTime(); - default -> throw new IllegalArgumentException("Unexpected value: " + contractDef.getTradeTimeType()); - }; - } - - @Override - public ChannelType channelType() { - return ChannelType.TIGER; - } - - @Override - public ContractField contractField() { - return ContractField.newBuilder() - .setGatewayId("TIGER") - .setSymbol(item.getSymbol()) - .setUnifiedSymbol(String.format("%s@%s@%s", item.getSymbol(), exchange(), productClass())) - .setName(item.getName()) - .setFullName(item.getName()) - .setCurrency(CurrencyEnum.valueOf(item.getCurrency())) - .setExchange(exchange()) - .setProductClass(productClass()) - .setContractId(identifier().value()) - .setMultiplier(Optional.ofNullable(item.getMultiplier()).orElse(1D)) - .setPriceTick(item.getMinTick()) - .setLongMarginRatio(Optional.ofNullable(item.getLongInitialMargin()).orElse(0D)) - .setShortMarginRatio(Optional.ofNullable(item.getShortInitialMargin()).orElse(0D)) - .setLastTradeDateOrContractMonth(Optional.ofNullable(item.getContractMonth()).orElse("")) - .setStrikePrice(Optional.ofNullable(item.getStrike()).orElse(0D)) - .setThirdPartyId(String.format("%s@TIGER", item.getSymbol())) - .setCommissionFee(contractDef.getCommissionFee()) - .setCommissionRate(contractDef.getCommissionRate()) - .build(); - } - - @Override - public void setContractDefinition(ContractDefinition contractDef) { - this.contractDef = contractDef; - } - + +import java.util.List; +import java.util.Optional; + +public class TigerContract implements Instrument { + + private ContractItem item; + + private ContractDefinition contractDef; + + private IDataSource dataSrc; + + public TigerContract(ContractItem item, IDataSource dataSrc) { + this.item = item; + this.dataSrc = dataSrc; + } + + @Override + public String name() { + return item.getSymbol() + "-" + item.getName(); + } + + @Override + public Identifier identifier() { + return Identifier.of(String.format("%s-%s@%s@%s@%s", item.getSymbol(), item.getName(), exchange(), productClass(), channelType())); + } + + @Override + public ProductClassEnum productClass() { + return switch (item.getSecType()) { + case "STK" -> ProductClassEnum.EQUITY; + case "OPT" -> ProductClassEnum.OPTION; + case "FUT" -> ProductClassEnum.FUTURES; + case "WAR" -> ProductClassEnum.WARRANTS; + case "IOPT" -> ProductClassEnum.SPOTOPTION; + default -> throw new IllegalArgumentException("Unexpected value: " + item.getSecType()); + }; + } + + @Override + public ExchangeEnum exchange() { + //交易所(exchange),STK类型的合约一般不会用到交易所字段,订单会自动路由,期货合约都用到交易所字段。 + if (item.getExchange() == null) { + return ExchangeEnum.SMART; + } else { + return switch (item.getExchange()) { + case "SMART", "VALUE" -> ExchangeEnum.SMART; + case "SEHKSZSE" -> ExchangeEnum.SZSE; + case "SEHKNTL" -> ExchangeEnum.SSE; + case "SEHK" -> ExchangeEnum.SEHK; + default -> throw new IllegalArgumentException("Unexpected value: " + item.getExchange()); + }; + } + } + + @Override + public ChannelType channelType() { + return ChannelType.TIGER; + } + + @Override + public Contract contract() { + // 检查是否有有效的 tickSizes 数据 + Double minTick = item.getMinTick(); // 默认最小报价单位 + List tickSizes = item.getTickSizes(); + + if ("STK".equals(item.getSecType())) { + // 假设我们使用第一个 tickSize 的 tickSize 作为最小报价单位 + minTick = tickSizes.getFirst().getTickSize(); + } else if (minTick == null && tickSizes != null) { + minTick = tickSizes.getFirst().getTickSize(); + } + return Contract.builder() + .gatewayId(ChannelType.TIGER.toString()) + .symbol(item.getSymbol()) + .unifiedSymbol(String.format("%s@%s@%s", item.getSymbol(), exchange(), productClass())) + .name(item.getName()) + .fullName(item.getName()) + .currency(CurrencyEnum.valueOf(item.getCurrency())) + .exchange(exchange()) + .productClass(productClass()) + .contractId(identifier().value()) + .multiplier(Optional.ofNullable(item.getMultiplier()).orElse(1D)) + .priceTick(minTick) + .longMarginRatio(Optional.ofNullable(item.getLongInitialMargin()).orElse(0D)) + .shortMarginRatio(Optional.ofNullable(item.getShortInitialMargin()).orElse(0D)) + //.lastTradeDate(LocalDate.parse(Optional.ofNullable(item.getContractMonth()).orElse(""))) + //.strikePrice(Optional.ofNullable(item.getStrike()).orElse(0D)) + .thirdPartyId(String.format("%s@TIGER", item.getSymbol())) + .build(); + } + + @Override + public IDataSource dataSource() { + return dataSrc; + } + + @Override + public void setContractDefinition(ContractDefinition contractDef) { + this.contractDef = contractDef; + } + } diff --git a/src/main/java/org/dromara/northstar/gateway/tiger/TigerContractProvider.java b/src/main/java/org/dromara/northstar/gateway/tiger/TigerContractProvider.java index 934ded4f62a4ed2ff9f33915bfb2127b81a2074a..36e1006a0069a4d39f8970e4cabd00ec976ad13e 100644 --- a/src/main/java/org/dromara/northstar/gateway/tiger/TigerContractProvider.java +++ b/src/main/java/org/dromara/northstar/gateway/tiger/TigerContractProvider.java @@ -1,11 +1,5 @@ package org.dromara.northstar.gateway.tiger; -import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; - -import org.dromara.northstar.gateway.IMarketCenter; - import com.tigerbrokers.stock.openapi.client.config.ClientConfig; import com.tigerbrokers.stock.openapi.client.https.domain.contract.model.ContractsModel; import com.tigerbrokers.stock.openapi.client.https.domain.quote.item.SymbolNameItem; @@ -15,53 +9,90 @@ import com.tigerbrokers.stock.openapi.client.https.response.contract.ContractsRe import com.tigerbrokers.stock.openapi.client.https.response.quote.QuoteSymbolNameResponse; import com.tigerbrokers.stock.openapi.client.struct.enums.Language; import com.tigerbrokers.stock.openapi.client.struct.enums.Market; - import lombok.extern.slf4j.Slf4j; +import org.dromara.northstar.common.model.core.ContractDefinition; +import org.dromara.northstar.common.model.core.TimeSlot; +import org.dromara.northstar.common.model.core.TradeTimeDefinition; +import org.dromara.northstar.common.utils.DateTimeUtils; +import org.dromara.northstar.gateway.IMarketCenter; +import org.springframework.stereotype.Component; + +import java.util.List; +import java.util.Map; +import java.util.regex.Pattern; +import java.util.stream.Collectors; @Slf4j +@Component public class TigerContractProvider { - private TigerGatewaySettings settings; - - private IMarketCenter mktCenter; - - public TigerContractProvider(TigerGatewaySettings settings, IMarketCenter mktCenter) { - this.mktCenter = mktCenter; - this.settings = settings; - } - - /** - * 加载可用合约 - */ - public void loadContractOptions() { - ClientConfig clientConfig = ClientConfig.DEFAULT_CONFIG; - clientConfig.tigerId = settings.getTigerId(); + private TigerGatewaySettings settings; + + private TigerDataServiceManager dataMgr; + + private IMarketCenter mktCenter; + + private TimeSlot allDay = TimeSlot.builder().start(DateTimeUtils.fromCacheTime(0, 0)).end(DateTimeUtils.fromCacheTime(0, 0)).build(); + + public TigerContractProvider(TigerGatewaySettings settings, IMarketCenter mktCenter, TigerDataServiceManager dataMgr) { + this.settings = settings; + this.mktCenter = mktCenter; + this.dataMgr = dataMgr; + } + + /** + * 加载可用合约 + */ + public void loadContractOptions() { + ClientConfig clientConfig = ClientConfig.DEFAULT_CONFIG; + clientConfig.tigerId = settings.getTigerId(); clientConfig.defaultAccount = settings.getAccountId(); clientConfig.privateKey = settings.getPrivateKey(); clientConfig.license = settings.getLicense(); clientConfig.secretKey = settings.getSecretKey(); clientConfig.language = Language.zh_CN; - TigerHttpClient client = TigerHttpClient.getInstance().clientConfig(clientConfig); - doLoadContracts(Market.CN, client); - doLoadContracts(Market.HK, client); - doLoadContracts(Market.US, client); - } - - private void doLoadContracts(Market market, TigerHttpClient client) { - QuoteSymbolNameResponse response = client.execute(QuoteSymbolNameRequest.newRequest(market)); - if(!response.isSuccess()) { - log.warn("TIGER 加载 [{}] 市场合约失败", market); - return; - } - Map symbolNameMap = response.getSymbolNameItems().stream().collect(Collectors.toMap(item -> item.getSymbol(), item -> item.getName())); - List symbols = response.getSymbolNameItems().stream().map(SymbolNameItem::getSymbol).toList(); - ContractsRequest contractsRequest = ContractsRequest.newRequest(new ContractsModel(symbols)); - ContractsResponse contractsResponse = client.execute(contractsRequest); - contractsResponse.getItems().stream().map(item -> { - item.setName(symbolNameMap.get(item.getSymbol())); - return new TigerContract(item); - }).forEach(c -> mktCenter.addInstrument(c)); - log.info("加载TIGER网关 [{}] 的合约{}个", market, contractsResponse.getItems().size()); - } - + TigerHttpClient client = TigerHttpClient.getInstance().clientConfig(clientConfig); + doLoadContracts(Market.CN, client); + doLoadContracts(Market.HK, client); + doLoadContracts(Market.US, client); + } + + private void doLoadContracts(Market market, TigerHttpClient client) { + QuoteSymbolNameResponse response = client.execute(QuoteSymbolNameRequest.newRequest(market)); + if (!response.isSuccess()) { + log.warn("TIGER 加载 [{}] 市场合约失败", market); + return; + } + Map symbolNameMap = response.getSymbolNameItems().stream().collect(Collectors.toMap(SymbolNameItem::getSymbol, SymbolNameItem::getName)); + List symbols = response.getSymbolNameItems().stream().map(SymbolNameItem::getSymbol).collect(Collectors.toList()); + ContractsRequest contractsRequest = ContractsRequest.newRequest(new ContractsModel(symbols)); + ContractsResponse contractsResponse = client.execute(contractsRequest); + + // 创建合约定义列表 + List contractDefs = contractsResponse.getItems().stream().map(item -> { + item.setName(symbolNameMap.get(item.getSymbol())); + TigerContract contract = new TigerContract(item, dataMgr); + return ContractDefinition.builder() + .name(contract.name()) + .exchange(contract.exchange()) + .productClass(contract.productClass()) + .symbolPattern(Pattern.compile(contract.name() + "@[A-Z]+@[A-Z]+@[A-Z]+$")) + .commissionRate(3 / 10000D) + .dataSource(contract.dataSource()) + .tradeTimeDef(TradeTimeDefinition.builder().timeSlots(List.of(allDay)).build()) + .build(); + }).collect(Collectors.toList()); + + // 增加合约定义 + mktCenter.addDefinitions(contractDefs); + + // 注册合约 + contractsResponse.getItems().forEach(item -> { + TigerContract contract = new TigerContract(item, dataMgr); + mktCenter.addInstrument(contract); + }); + + log.info("加载TIGER网关 [{}] 的合约{}个", market, contractsResponse.getItems().size()); + } + } diff --git a/src/main/java/org/dromara/northstar/gateway/tiger/TigerDataServiceManager.java b/src/main/java/org/dromara/northstar/gateway/tiger/TigerDataServiceManager.java index db50bef2e493495a4f874bd4a14ccbfb2bd14351..73bee3262b825223b31dae56b6cfc381c8fe3645 100644 --- a/src/main/java/org/dromara/northstar/gateway/tiger/TigerDataServiceManager.java +++ b/src/main/java/org/dromara/northstar/gateway/tiger/TigerDataServiceManager.java @@ -1,50 +1,43 @@ package org.dromara.northstar.gateway.tiger; +import org.dromara.northstar.common.IDataSource; +import org.dromara.northstar.common.constant.ChannelType; +import org.dromara.northstar.common.model.core.Bar; +import org.dromara.northstar.common.model.core.Contract; + import java.time.LocalDate; import java.util.List; -import org.dromara.northstar.common.IDataServiceManager; - -import xyz.redtorch.pb.CoreEnum.ExchangeEnum; -import xyz.redtorch.pb.CoreField.BarField; -import xyz.redtorch.pb.CoreField.ContractField; - -public class TigerDataServiceManager implements IDataServiceManager{ - - @Override - public List getMinutelyData(ContractField contract, LocalDate startDate, LocalDate endDate) { - // TODO Auto-generated method stub - return null; - } - - @Override - public List getQuarterlyData(ContractField contract, LocalDate startDate, LocalDate endDate) { - // TODO Auto-generated method stub - return null; - } - - @Override - public List getHourlyData(ContractField contract, LocalDate startDate, LocalDate endDate) { - // TODO Auto-generated method stub - return null; - } - - @Override - public List getDailyData(ContractField contract, LocalDate startDate, LocalDate endDate) { - // TODO Auto-generated method stub - return null; - } - - @Override - public List getHolidays(ExchangeEnum exchange, LocalDate startDate, LocalDate endDate) { - // TODO Auto-generated method stub - return null; - } - - @Override - public List getAllContracts(ExchangeEnum exchange) { - // TODO Auto-generated method stub - return null; - } +public class TigerDataServiceManager implements IDataSource { + + + @Override + public List getMinutelyData(Contract contract, LocalDate startDate, LocalDate endDate) { + return List.of(); + } + + @Override + public List getQuarterlyData(Contract contract, LocalDate startDate, LocalDate endDate) { + return List.of(); + } + + @Override + public List getHourlyData(Contract contract, LocalDate startDate, LocalDate endDate) { + return List.of(); + } + + @Override + public List getDailyData(Contract contract, LocalDate startDate, LocalDate endDate) { + return List.of(); + } + + @Override + public List getHolidays(ChannelType channelType, LocalDate startDate, LocalDate endDate) { + return List.of(); + } + @Override + public List getAllContracts() { + return List.of(); + } } diff --git a/src/main/java/org/dromara/northstar/gateway/tiger/TigerGatewayFactory.java b/src/main/java/org/dromara/northstar/gateway/tiger/TigerGatewayFactory.java index b28d5ec4d4540d726617de1e145ebe7c0e374412..a11aa00450424482ca5708fc25eec7d37393fb9e 100644 --- a/src/main/java/org/dromara/northstar/gateway/tiger/TigerGatewayFactory.java +++ b/src/main/java/org/dromara/northstar/gateway/tiger/TigerGatewayFactory.java @@ -1,40 +1,43 @@ package org.dromara.northstar.gateway.tiger; +import com.alibaba.fastjson.JSON; import org.dromara.northstar.common.event.FastEventEngine; import org.dromara.northstar.common.model.GatewayDescription; import org.dromara.northstar.gateway.Gateway; import org.dromara.northstar.gateway.GatewayFactory; +import org.dromara.northstar.gateway.IContractManager; import org.dromara.northstar.gateway.IMarketCenter; -import com.alibaba.fastjson.JSON; +public class TigerGatewayFactory implements GatewayFactory { + + private FastEventEngine feEngine; + + private IMarketCenter mktCenter; + + private TigerDataServiceManager dataMgr; + + private boolean contractLoaded; + + public TigerGatewayFactory(FastEventEngine feEngine, IMarketCenter mktCenter, TigerDataServiceManager dataMgr) { + this.feEngine = feEngine; + this.mktCenter = mktCenter; + this.dataMgr = dataMgr; + } + + @Override + public Gateway newInstance(GatewayDescription gatewayDescription) { + TigerGatewaySettings settings = JSON.parseObject(JSON.toJSONString(gatewayDescription.getSettings()), TigerGatewaySettings.class); + gatewayDescription.setSettings(settings); + if (!contractLoaded) { + new TigerContractProvider(settings, mktCenter,dataMgr).loadContractOptions(); + contractLoaded = true; + } -public class TigerGatewayFactory implements GatewayFactory{ - - private FastEventEngine feEngine; - - private IMarketCenter mktCenter; - - private boolean contractLoaded; - - public TigerGatewayFactory(FastEventEngine feEngine, IMarketCenter mktCenter) { - this.feEngine = feEngine; - this.mktCenter = mktCenter; - } - - @Override - public Gateway newInstance(GatewayDescription gatewayDescription) { - TigerGatewaySettings settings = JSON.parseObject(JSON.toJSONString(gatewayDescription.getSettings()), TigerGatewaySettings.class); - gatewayDescription.setSettings(settings); - if(!contractLoaded) { - new TigerContractProvider(settings, mktCenter).loadContractOptions(); - contractLoaded = true; - } - - return switch(gatewayDescription.getGatewayUsage()) { - case MARKET_DATA -> new TigerMarketGatewayAdapter(gatewayDescription, feEngine, mktCenter); - case TRADE -> new TigerTradeGatewayAdapter(feEngine, gatewayDescription, mktCenter); - default -> throw new IllegalArgumentException("未知网关用途:" + gatewayDescription.getGatewayUsage()); - }; - } + return switch (gatewayDescription.getGatewayUsage()) { + case MARKET_DATA -> new TigerMarketGatewayAdapter(gatewayDescription, feEngine, mktCenter); + case TRADE -> new TigerTradeGatewayAdapter(feEngine, gatewayDescription, mktCenter); + default -> throw new IllegalArgumentException("未知网关用途:" + gatewayDescription.getGatewayUsage()); + }; + } } diff --git a/src/main/java/org/dromara/northstar/gateway/tiger/TigerGatewaySettings.java b/src/main/java/org/dromara/northstar/gateway/tiger/TigerGatewaySettings.java index 80e574cd059285db5873c871ed55693c2eb800d7..4689c4ee9380ce06b36be9fe764124208cdcebf4 100644 --- a/src/main/java/org/dromara/northstar/gateway/tiger/TigerGatewaySettings.java +++ b/src/main/java/org/dromara/northstar/gateway/tiger/TigerGatewaySettings.java @@ -1,32 +1,30 @@ package org.dromara.northstar.gateway.tiger; -import org.dromara.northstar.common.constant.FieldType; -import org.dromara.northstar.common.model.DynamicParams; -import org.dromara.northstar.common.model.GatewaySettings; -import org.dromara.northstar.common.model.Setting; - import com.tigerbrokers.stock.openapi.client.struct.enums.License; - import lombok.Getter; import lombok.Setter; +import org.dromara.northstar.common.GatewaySettings; +import org.dromara.northstar.common.constant.FieldType; +import org.dromara.northstar.common.model.DynamicParams; +import org.dromara.northstar.common.model.Setting; @Getter @Setter public class TigerGatewaySettings extends DynamicParams implements GatewaySettings { - @Setting(label="用户ID", order=10, type=FieldType.TEXT) - private String tigerId; - - @Setting(label="账户ID", order=20, type=FieldType.TEXT) - private String accountId; - - @Setting(label="RSA私钥", order=30, type=FieldType.TEXT) - private String privateKey; - - @Setting(label="证书类型", order=40, type=FieldType.SELECT, optionsVal = {"TBNZ", "TBSG"}) - private License license; - - @Setting(label="secretKey", order=50, type=FieldType.TEXT, required = false) - private String secretKey; - + @Setting(label = "用户ID", order = 10, type = FieldType.TEXT) + private String tigerId; + + @Setting(label = "账户ID", order = 20, type = FieldType.TEXT) + private String accountId; + + @Setting(label = "RSA私钥", order = 30, type = FieldType.TEXT) + private String privateKey; + + @Setting(label = "证书类型", order = 40, type = FieldType.SELECT, optionsVal = {"TBNZ", "TBSG"}) + private License license; + + @Setting(label = "secretKey", order = 50, type = FieldType.TEXT, required = false) + private String secretKey; + } diff --git a/src/main/java/org/dromara/northstar/gateway/tiger/TigerHttpClient.java b/src/main/java/org/dromara/northstar/gateway/tiger/TigerHttpClient.java index 16d5da95ada257c3d9b4f5027178fa2739905789..870ec1537f9a988ac0009babfa9900124194e18b 100644 --- a/src/main/java/org/dromara/northstar/gateway/tiger/TigerHttpClient.java +++ b/src/main/java/org/dromara/northstar/gateway/tiger/TigerHttpClient.java @@ -3,403 +3,347 @@ package org.dromara.northstar.gateway.tiger; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import com.alibaba.fastjson.serializer.SerializerFeature; -import com.alibaba.fastjson2.JSONWriter.Feature; import com.tigerbrokers.stock.openapi.client.TigerApiException; import com.tigerbrokers.stock.openapi.client.config.ClientConfig; import com.tigerbrokers.stock.openapi.client.constant.TigerApiConstants; import com.tigerbrokers.stock.openapi.client.https.client.TigerClient; +import com.tigerbrokers.stock.openapi.client.https.client.TokenManager; import com.tigerbrokers.stock.openapi.client.https.domain.ApiModel; import com.tigerbrokers.stock.openapi.client.https.domain.BatchApiModel; -import com.tigerbrokers.stock.openapi.client.https.domain.contract.item.ContractItem; import com.tigerbrokers.stock.openapi.client.https.domain.trade.model.TradeOrderModel; -import com.tigerbrokers.stock.openapi.client.https.domain.user.item.LicenseItem; +import com.tigerbrokers.stock.openapi.client.https.request.TigerCommonRequest; import com.tigerbrokers.stock.openapi.client.https.request.TigerHttpRequest; import com.tigerbrokers.stock.openapi.client.https.request.TigerRequest; +import com.tigerbrokers.stock.openapi.client.https.request.user.UserLicenseRequest; import com.tigerbrokers.stock.openapi.client.https.response.TigerHttpResponse; import com.tigerbrokers.stock.openapi.client.https.response.TigerResponse; -import com.tigerbrokers.stock.openapi.client.https.response.contract.ContractResponse; +import com.tigerbrokers.stock.openapi.client.https.response.user.UserLicenseResponse; import com.tigerbrokers.stock.openapi.client.https.validator.ValidatorManager; -import com.tigerbrokers.stock.openapi.client.struct.enums.AccountType; -import com.tigerbrokers.stock.openapi.client.struct.enums.BizType; -import com.tigerbrokers.stock.openapi.client.struct.enums.Env; -import com.tigerbrokers.stock.openapi.client.struct.enums.License; -import com.tigerbrokers.stock.openapi.client.struct.enums.MethodName; -import com.tigerbrokers.stock.openapi.client.struct.enums.MethodType; -import com.tigerbrokers.stock.openapi.client.struct.enums.TigerApiCode; -import com.tigerbrokers.stock.openapi.client.util.AccountUtil; -import com.tigerbrokers.stock.openapi.client.util.ApiLogger; -import com.tigerbrokers.stock.openapi.client.util.FastJsonPropertyFilter; -import com.tigerbrokers.stock.openapi.client.util.HttpUtils; -import com.tigerbrokers.stock.openapi.client.util.NetworkUtil; -import com.tigerbrokers.stock.openapi.client.util.SdkVersionUtils; -import com.tigerbrokers.stock.openapi.client.util.StringUtils; -import com.tigerbrokers.stock.openapi.client.util.TigerSignature; +import com.tigerbrokers.stock.openapi.client.struct.enums.*; +import com.tigerbrokers.stock.openapi.client.util.*; import com.tigerbrokers.stock.openapi.client.util.builder.AccountParamBuilder; + import java.security.Security; import java.util.HashMap; -import java.util.List; import java.util.Map; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.ThreadFactory; import java.util.concurrent.TimeUnit; -import static com.tigerbrokers.stock.openapi.client.constant.TigerApiConstants.ACCESS_TOKEN; -import static com.tigerbrokers.stock.openapi.client.constant.TigerApiConstants.ACCOUNT_TYPE; -import static com.tigerbrokers.stock.openapi.client.constant.TigerApiConstants.BIZ_CONTENT; -import static com.tigerbrokers.stock.openapi.client.constant.TigerApiConstants.CHARSET; -import static com.tigerbrokers.stock.openapi.client.constant.TigerApiConstants.DEVICE_ID; -import static com.tigerbrokers.stock.openapi.client.constant.TigerApiConstants.METHOD; -import static com.tigerbrokers.stock.openapi.client.constant.TigerApiConstants.SDK_VERSION; -import static com.tigerbrokers.stock.openapi.client.constant.TigerApiConstants.SIGN; -import static com.tigerbrokers.stock.openapi.client.constant.TigerApiConstants.SIGN_TYPE; -import static com.tigerbrokers.stock.openapi.client.constant.TigerApiConstants.TIGER_ID; -import static com.tigerbrokers.stock.openapi.client.constant.TigerApiConstants.TIMESTAMP; -import static com.tigerbrokers.stock.openapi.client.constant.TigerApiConstants.TRADE_TOKEN; -import static com.tigerbrokers.stock.openapi.client.constant.TigerApiConstants.VERSION; -import static com.tigerbrokers.stock.openapi.client.https.request.TigerCommonRequest.V2_0; +import static com.tigerbrokers.stock.openapi.client.constant.TigerApiConstants.*; public class TigerHttpClient implements TigerClient { - private String serverUrl; - private String quoteServerUrl; - private String paperServerUrl; - private String tigerId; - private String privateKey; - private String tigerPublicKey; - private String accessToken; - private String tradeToken; - private String accountType; - private String deviceId; - - private static final String ONLINE_PUBLIC_KEY = - "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDNF3G8SoEcCZh2rshUbayDgLLrj6rKgzNMxDL2HSnKcB0+GPOsndqSv+a4IBu9+I3fyBp5hkyMMG2+AXugd9pMpy6VxJxlNjhX1MYbNTZJUT4nudki4uh+LMOkIBHOceGNXjgB+cXqmlUnjlqha/HgboeHSnSgpM3dKSJQlIOsDwIDAQAB"; - - private static final String SANDBOX_PUBLIC_KEY = - "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCbm21i11hgAENGd3/f280PSe4g9YGkS3TEXBYMidihTvHHf+tJ0PYD0o3PruI0hl3qhEjHTAxb75T5YD3SGK4IBhHn/Rk6mhqlGgI+bBrBVYaXixmHfRo75RpUUuWACyeqQkZckgR0McxuW9xRMIa2cXZOoL1E4SL4lXKGhKoWbwIDAQAB"; - - private String signType = TigerApiConstants.SIGN_TYPE_RSA; - private String charset = TigerApiConstants.CHARSET_UTF8; - - private static final long REFRESH_URL_INTERVAL_SECONDS = 300; - private ScheduledThreadPoolExecutor domainExecutorService; - - static { - Security.setProperty("jdk.certpath.disabledAlgorithms", ""); - } - - private TigerHttpClient() { - } - - private static class SingletonInner { - private static TigerHttpClient singleton = new TigerHttpClient(); - } - - /** - * get TigerHttpClient instance - * @return TigerHttpClient - */ - public static TigerHttpClient getInstance() { - return TigerHttpClient.SingletonInner.singleton; - } - - public TigerHttpClient clientConfig(ClientConfig clientConfig) { - init(clientConfig.serverUrl, clientConfig.tigerId, clientConfig.privateKey); - initDomainRefreshTask(); - if (clientConfig.isAutoGrabPermission) { - TigerHttpRequest request = new TigerHttpRequest(MethodName.GRAB_QUOTE_PERMISSION); - request.setBizContent(AccountParamBuilder.instance().buildJsonWithoutDefaultAccount()); - TigerHttpResponse response = execute(request); - ApiLogger.info("tigerId:{}, grab_quote_permission:{}, data:{}", - tigerId, response.getMessage(), response.getData()); - } - return this; - } - - /** please use TigerHttpClient.getInstance().clientConfig(ClientConfig.DEFAULT_CONFIG) */ - @Deprecated - public TigerHttpClient(String serverUrl, String tigerId, String privateKey) { - init(serverUrl, tigerId, privateKey); - } - - private void init(String serverUrl, String tigerId, String privateKey) { - if (tigerId == null) { - throw new RuntimeException("tigerId is empty."); + private String serverUrl; + private String quoteServerUrl; + private String paperServerUrl; + private String tigerId; + private String privateKey; + private String tigerPublicKey; + private String accessToken; + private String tradeToken; + private String accountType; + private String deviceId; + private int failRetryCounts = TigerApiConstants.DEFAULT_FAIL_RETRY_COUNT; + + private static final String ONLINE_PUBLIC_KEY = + "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDNF3G8SoEcCZh2rshUbayDgLLrj6rKgzNMxDL2HSnKcB0+GPOsndqSv+a4IBu9+I3fyBp5hkyMMG2+AXugd9pMpy6VxJxlNjhX1MYbNTZJUT4nudki4uh+LMOkIBHOceGNXjgB+cXqmlUnjlqha/HgboeHSnSgpM3dKSJQlIOsDwIDAQAB"; + + private static final String SANDBOX_PUBLIC_KEY = + "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCbm21i11hgAENGd3/f280PSe4g9YGkS3TEXBYMidihTvHHf+tJ0PYD0o3PruI0hl3qhEjHTAxb75T5YD3SGK4IBhHn/Rk6mhqlGgI+bBrBVYaXixmHfRo75RpUUuWACyeqQkZckgR0McxuW9xRMIa2cXZOoL1E4SL4lXKGhKoWbwIDAQAB"; + + private String signType = TigerApiConstants.SIGN_TYPE_RSA; + private String charset = TigerApiConstants.UTF_8; + + private static final long REFRESH_URL_INTERVAL_SECONDS = 300; + private ScheduledThreadPoolExecutor domainExecutorService; + + static { + Security.setProperty("jdk.certpath.disabledAlgorithms", ""); } - if (privateKey == null) { - throw new RuntimeException("privateKey is empty."); + + private TigerHttpClient() { } - this.tigerId = tigerId; - this.privateKey = privateKey; - if (ClientConfig.DEFAULT_CONFIG.getEnv() == Env.PROD) { - this.tigerPublicKey = ONLINE_PUBLIC_KEY; - } else { - this.tigerPublicKey = SANDBOX_PUBLIC_KEY; + + private static class SingletonInner { + private static TigerHttpClient singleton = new TigerHttpClient(); } - this.deviceId = NetworkUtil.getDeviceId(); - initLicense(); - if (Env.PROD == ClientConfig.DEFAULT_CONFIG.getEnv() || StringUtils.isEmpty(serverUrl)) { - refreshUrl(); - } else { - this.serverUrl = serverUrl; + /** + * get TigerHttpClient instance + * @return TigerHttpClient + */ + public static TigerHttpClient getInstance() { + return TigerHttpClient.SingletonInner.singleton; } - if (this.serverUrl == null) { - throw new RuntimeException("serverUrl is empty."); + + public TigerHttpClient clientConfig(ClientConfig clientConfig) { + ConfigFileUtil.loadConfigFile(clientConfig); + init(clientConfig.tigerId, clientConfig.privateKey); + if (clientConfig.failRetryCounts <= TigerApiConstants.MAX_FAIL_RETRY_COUNT) { + this.failRetryCounts = Math.max(clientConfig.failRetryCounts, 0); + } + TokenManager.getInstance().init(clientConfig); + initDomainRefreshTask(); + if (clientConfig.isAutoGrabPermission) { + TigerHttpRequest request = new TigerHttpRequest(MethodName.GRAB_QUOTE_PERMISSION); + request.setBizContent(AccountParamBuilder.instance().buildJsonWithoutDefaultAccount()); + TigerHttpResponse response = execute(request); + ApiLogger.info("tigerId:{}, grab_quote_permission:{}, data:{}", + tigerId, response.getMessage(), response.getData()); + } + return this; } - } - - @Deprecated - public TigerHttpClient(String serverUrl) { - this.serverUrl = serverUrl; - } - - @Deprecated - public TigerHttpClient(String serverUrl, String accessToken) { - this.serverUrl = serverUrl; - this.accessToken = accessToken; - } - - private void initLicense() { - if (null == ClientConfig.DEFAULT_CONFIG.license) { - try { - Map urlMap = NetworkUtil.getHttpServerAddress(null, this.serverUrl); - this.serverUrl = urlMap.get(BizType.COMMON); - TigerHttpRequest request = new TigerHttpRequest(MethodName.USER_LICENSE); - request.setBizContent(AccountParamBuilder.instance().buildJsonWithoutDefaultAccount()); - TigerHttpResponse response = execute(request); - if (response.isSuccess()) { - LicenseItem data = JSON.parseObject(response.getData(), LicenseItem.class); - ApiLogger.debug("license:{}", data); - ClientConfig.DEFAULT_CONFIG.license = License.valueOf(data.getLicense()); + + private void init(String tigerId, String privateKey) { + if (tigerId == null) { + throw new RuntimeException("tigerId is empty."); + } + if (privateKey == null) { + throw new RuntimeException("privateKey is empty."); + } + this.tigerId = tigerId; + this.privateKey = privateKey; + if (ClientConfig.DEFAULT_CONFIG.getEnv() == Env.PROD) { + this.tigerPublicKey = ONLINE_PUBLIC_KEY; + } else { + this.tigerPublicKey = SANDBOX_PUBLIC_KEY; + } + this.deviceId = NetworkUtil.getDeviceId(); + + initLicense(); + refreshUrl(); + if (this.serverUrl == null) { + throw new RuntimeException("serverUrl is empty."); } - } catch (Exception e) { - ApiLogger.debug("get license fail. tigerId:{}", tigerId); - } } - } - - private void initDomainRefreshTask() { - synchronized (TigerHttpClient.SingletonInner.singleton) { - if (domainExecutorService == null || domainExecutorService.isTerminated()) { - domainExecutorService = new ScheduledThreadPoolExecutor(1, new ThreadFactory() { - @Override - public Thread newThread(Runnable r) { - Thread t = Executors.defaultThreadFactory().newThread(r); - t.setDaemon(true); - return t; - } - }); - domainExecutorService.scheduleWithFixedDelay( - new Runnable() { - @Override - public void run() { - refreshUrl(); - } - }, REFRESH_URL_INTERVAL_SECONDS, - REFRESH_URL_INTERVAL_SECONDS, TimeUnit.SECONDS); - } + + public TigerHttpClient accessToken(String accessToken) { + this.accessToken = accessToken; + return this; } - } - - private void refreshUrl() { - try { - Map urlMap = NetworkUtil.getHttpServerAddress(ClientConfig.DEFAULT_CONFIG.license, this.serverUrl); - String newServerUrl = urlMap.get(BizType.TRADE); - if (newServerUrl == null) { - newServerUrl = urlMap.get(BizType.COMMON); - } - String newQuoteServerUrl = urlMap.get(BizType.QUOTE) == null ? newServerUrl : urlMap.get(BizType.QUOTE); - String newPaperServerUrl = urlMap.get(BizType.PAPER) == null ? newServerUrl : urlMap.get(BizType.PAPER); - - this.serverUrl = newServerUrl; - this.quoteServerUrl = newQuoteServerUrl; - this.paperServerUrl = newPaperServerUrl; - } catch (Throwable t) { - ApiLogger.error("refresh serverUrl error", t); + + private void initLicense() { + if (null == ClientConfig.DEFAULT_CONFIG.license) { + try { + Map urlMap = NetworkUtil.getHttpServerAddress(null, this.serverUrl); + this.serverUrl = urlMap.get(BizType.COMMON); + UserLicenseRequest request = UserLicenseRequest.newRequest(); + UserLicenseResponse response = execute(request); + if (response.isSuccess() && response.getLicenseItem() != null) { + ApiLogger.debug("license:{}", JSON.toJSONString(response.getLicenseItem(), SerializerFeature.WriteEnumUsingToString)); + ClientConfig.DEFAULT_CONFIG.license = License.valueOf(response.getLicenseItem().getLicense()); + } + } catch (Exception e) { + ApiLogger.debug("get license fail. tigerId:{}", tigerId); + } + } } - } - public String getAccessToken() { - return accessToken; - } + private void initDomainRefreshTask() { + synchronized (TigerHttpClient.SingletonInner.singleton) { + if (domainExecutorService == null || domainExecutorService.isTerminated()) { + domainExecutorService = new ScheduledThreadPoolExecutor(1, new ThreadFactory() { + @Override + public Thread newThread(Runnable r) { + Thread t = Executors.defaultThreadFactory().newThread(r); + t.setDaemon(true); + return t; + } + }); + domainExecutorService.scheduleWithFixedDelay( + new Runnable() { + @Override + public void run() { + refreshUrl(); + } + }, REFRESH_URL_INTERVAL_SECONDS, + REFRESH_URL_INTERVAL_SECONDS, TimeUnit.SECONDS); + } + } + } - public void setAccessToken(String accessToken) { - this.accessToken = accessToken; - } + private void refreshUrl() { + try { + Map urlMap = NetworkUtil.getHttpServerAddress(ClientConfig.DEFAULT_CONFIG.license, this.serverUrl); + String newServerUrl = urlMap.get(BizType.TRADE); + if (newServerUrl == null) { + newServerUrl = urlMap.get(BizType.COMMON); + } + String newQuoteServerUrl = urlMap.get(BizType.QUOTE) == null ? newServerUrl : urlMap.get(BizType.QUOTE); + String newPaperServerUrl = urlMap.get(BizType.PAPER) == null ? newServerUrl : urlMap.get(BizType.PAPER); + + this.serverUrl = newServerUrl; + this.quoteServerUrl = newQuoteServerUrl; + this.paperServerUrl = newPaperServerUrl; + } catch (Throwable t) { + ApiLogger.error("refresh serverUrl error", t); + } + } - public void setTradeToken(String tradeToken) { - this.tradeToken = tradeToken; - } + public String getAccessToken() { + return accessToken; + } - public String getTradeToken() { - return tradeToken; - } + public void setAccessToken(String accessToken) { + this.accessToken = accessToken; + } - public void setAccountType(AccountType accountType) { - if (accountType != null) { - this.accountType = accountType.name(); + public void setTradeToken(String tradeToken) { + this.tradeToken = tradeToken; } - } - - public String getAccountType() { - return accountType; - } - - @Override - public T execute(TigerRequest request) { - T response; - String param = null; - String data = null; - try { - validate(request); - // after successful verification(string enumeration values may be reset), generate JSON data - param = JSON.toJSONString(buildParams(request), SerializerFeature.WriteEnumUsingToString); - ApiLogger.debug("request param:{}", param); - - data = HttpUtils.post(getServerUrl(request), param); - - if (StringUtils.isEmpty(data)) { - throw new TigerApiException(TigerApiCode.EMPTY_DATA_ERROR); - } - response = JSON.parseObject(data, request.getResponseClass()); - if (MethodName.CONTRACT == request.getApiMethodName()) { - convertContractItem(response, request.getApiVersion()); - } - if (StringUtils.isEmpty(this.tigerPublicKey) || response.getSign() == null) { - return response; - } - boolean signSuccess = - TigerSignature.rsaCheckContent(request.getTimestamp(), response.getSign(), this.tigerPublicKey, this.charset); - - if (!signSuccess) { - throw new TigerApiException(TigerApiCode.SIGN_CHECK_FAILED); - } - return response; - } catch (RuntimeException e) { - ApiLogger.error(tigerId, request.getApiMethodName(), request.getApiVersion(), param, data, e); - return errorResponse(tigerId, request, e); - } catch (TigerApiException e) { - ApiLogger.error(tigerId, request.getApiMethodName(), request.getApiVersion(), param, data, e); - return errorResponse(tigerId, request, e); - } catch (Exception e) { - ApiLogger.error(tigerId, request.getApiMethodName(), request.getApiVersion(), param, data, e); - return errorResponse(tigerId, request, e); + + public String getTradeToken() { + return tradeToken; } - } - - private void convertContractItem(TigerResponse response, String apiVersion) { - if (response instanceof ContractResponse) { - ContractResponse contractResponse = (ContractResponse) response; - if (StringUtils.isEmpty(contractResponse.getData())) { - return; - } - if (V2_0.equals(apiVersion)) { - List items = ContractItem.convertFromJsonV2(contractResponse.getData()); - contractResponse.setItems(items); - if (items != null && items.size() > 0) { - contractResponse.setItem(items.get(0)); + + public void setAccountType(AccountType accountType) { + if (accountType != null) { + this.accountType = accountType.name(); } - } else { - contractResponse.setItem(ContractItem.convertFromJson(contractResponse.getData())); - } - } - } - - private T errorResponse(String tigerId, TigerRequest request, TigerApiException e) { - try { - ApiLogger.error(tigerId, request.getApiMethodName(), request.getApiVersion(), e); - - T response = request.getResponseClass().newInstance(); - response.setCode(e.getErrCode()); - response.setMessage(e.getErrMsg()); - return response; - } catch (Exception e1) { - ApiLogger.error(tigerId, request.getApiMethodName(), request.getApiVersion(), e1); - return null; - } - } - - private T errorResponse(String tigerId, TigerRequest request, Exception e) { - try { - ApiLogger.error(tigerId, request.getApiMethodName(), request.getApiVersion(), e); - - T response = request.getResponseClass().newInstance(); - response.setCode(TigerApiCode.CLIENT_API_ERROR.getCode()); - response.setMessage(TigerApiCode.CLIENT_API_ERROR.getMessage() + "(" + e.getMessage() + ")"); - return response; - } catch (Exception e1) { - ApiLogger.error(tigerId, request.getApiMethodName(), request.getApiVersion(), e1); - return null; - } - } - - private Map buildParams(TigerRequest request) { - Map params = new HashMap<>(); - params.put(METHOD, request.getApiMethodName().getValue()); - params.put(VERSION, request.getApiVersion()); - params.put(SDK_VERSION, SdkVersionUtils.getSdkVersion()); - if (request instanceof TigerHttpRequest) { - params.put(BIZ_CONTENT, ((TigerHttpRequest) request).getBizContent()); - } else { - ApiModel apiModel = request.getApiModel(); - if (apiModel instanceof BatchApiModel) { - params.put(BIZ_CONTENT, JSON.toJSONString(((BatchApiModel) apiModel).getItems(), SerializerFeature.WriteEnumUsingToString)); - } else if (apiModel instanceof TradeOrderModel) { - params.put(BIZ_CONTENT, JSON.toJSONString(apiModel, SerializerFeature.WriteEnumUsingToString)); - } else { - params.put(BIZ_CONTENT, JSON.toJSONString(apiModel, SerializerFeature.WriteEnumUsingToString)); - } } - params.put(TIMESTAMP, request.getTimestamp()); - params.put(CHARSET, this.charset); - params.put(TIGER_ID, this.tigerId); - params.put(SIGN_TYPE, this.signType); - if (this.accessToken != null) { - params.put(ACCESS_TOKEN, this.accessToken); + + public String getAccountType() { + return accountType; } - if (this.tradeToken != null) { - params.put(TRADE_TOKEN, this.tradeToken); + + @Override + public T execute(TigerRequest request) { + T response; + String param = null; + String data = null; + try { + validate(request); + // after successful verification(string enumeration values may be reset), generate JSON data + param = JSONObject.toJSONString(buildParams(request), SerializerFeature.WriteEnumUsingToString); + ApiLogger.debug("request param:{}", param); + + data = HttpUtils.post(getServerUrl(request), param, + MethodName.PLACE_ORDER == request.getApiMethodName() ? 0 : failRetryCounts); + + ApiLogger.debug("response result:{}", data); + if (StringUtils.isEmpty(data)) { + throw new TigerApiException(TigerApiCode.EMPTY_DATA_ERROR); + } + response = JSON.parseObject(data, request.getResponseClass()); + if (StringUtils.isEmpty(this.tigerPublicKey) || response.getSign() == null) { + return response; + } + boolean signSuccess = + TigerSignature.rsaCheckContent(request.getTimestamp(), response.getSign(), this.tigerPublicKey, this.charset); + + if (!signSuccess) { + throw new TigerApiException(TigerApiCode.SIGN_CHECK_FAILED); + } + return response; + } catch (RuntimeException e) { + ApiLogger.error("request fail. tigerId:{}, method:{}, param:{}, response:{}", + tigerId, request == null ? null : request.getApiMethodName(), param, data, e); + return errorResponse(tigerId, request, e); + } catch (TigerApiException e) { + ApiLogger.error("request fail. tigerId:{}, method:{}, param:{}, response:{}", + tigerId, request == null ? null : request.getApiMethodName(), param, data, e); + return errorResponse(tigerId, request, e); + } catch (Exception e) { + ApiLogger.error("request fail. tigerId:{}, method:{}, param:{}, response:{}", + tigerId, request == null ? null : request.getApiMethodName(), param, data, e); + return errorResponse(tigerId, request, e); + } } - if (this.accountType != null) { - params.put(ACCOUNT_TYPE, this.accountType); + + private T errorResponse(String tigerId, TigerRequest request, TigerApiException e) { + try { + T response = request.getResponseClass().newInstance(); + response.setCode(e.getErrCode()); + response.setMessage(e.getErrMsg()); + return response; + } catch (Exception e1) { + ApiLogger.error(tigerId, request.getApiMethodName(), request.getApiVersion(), e1); + return null; + } } - if (this.deviceId != null) { - params.put(DEVICE_ID, this.deviceId); + + private T errorResponse(String tigerId, TigerRequest request, Exception e) { + try { + T response = request.getResponseClass().newInstance(); + response.setCode(TigerApiCode.CLIENT_API_ERROR.getCode()); + response.setMessage(TigerApiCode.CLIENT_API_ERROR.getMessage() + "(" + e.getMessage() + ")"); + return response; + } catch (Exception e1) { + ApiLogger.error(tigerId, request.getApiMethodName(), request.getApiVersion(), e1); + return null; + } } - if (this.tigerId != null) { - String content = TigerSignature.getSignContent(params); - params.put(SIGN, TigerSignature.rsaSign(content, privateKey, charset)); + + private Map buildParams(TigerRequest request) { + Map params = new HashMap<>(); + params.put(METHOD, request.getApiMethodName().getValue()); + params.put(VERSION, request.getApiVersion()); + params.put(SDK_VERSION, SdkVersionUtils.getSdkVersion()); + if (request instanceof TigerHttpRequest) { + params.put(BIZ_CONTENT, ((TigerHttpRequest) request).getBizContent()); + } else if (request.getApiModel() == null && request instanceof TigerCommonRequest) { + params.put(BIZ_CONTENT, ((TigerCommonRequest) request).getBizContent()); + } else { + ApiModel apiModel = request.getApiModel(); + if (apiModel instanceof BatchApiModel) { + params.put(BIZ_CONTENT, JSONObject.toJSONString(((BatchApiModel) apiModel).getItems(), SerializerFeature.WriteEnumUsingToString)); + } else if (apiModel instanceof TradeOrderModel) { + params.put(BIZ_CONTENT, JSONObject.toJSONString(apiModel)); + } else { + params.put(BIZ_CONTENT, JSONObject.toJSONString(apiModel, SerializerFeature.WriteEnumUsingToString)); + } + } + params.put(TIMESTAMP, request.getTimestamp()); + params.put(CHARSET, this.charset); + params.put(TIGER_ID, this.tigerId); + params.put(SIGN_TYPE, this.signType); + if (this.accessToken != null) { + params.put(ACCESS_TOKEN, this.accessToken); + } + if (this.tradeToken != null) { + params.put(TRADE_TOKEN, this.tradeToken); + } + if (this.accountType != null) { + params.put(ACCOUNT_TYPE, this.accountType); + } + if (this.deviceId != null) { + params.put(DEVICE_ID, this.deviceId); + } + if (this.tigerId != null) { + String content = TigerSignature.getSignContent(params); + params.put(SIGN, TigerSignature.rsaSign(content, privateKey, charset)); + } + + return params; } - return params; - } - - /** - * validate parameters - * @param request - * @throws TigerApiException - */ - private void validate(TigerRequest request) throws TigerApiException { - if (request instanceof TigerHttpRequest) { - return; + /** + * validate parameters + * @param request + * @throws TigerApiException + */ + private void validate(TigerRequest request) throws TigerApiException { + if (request instanceof TigerHttpRequest) { + return; + } + // TigerCommonRequest + ValidatorManager.getInstance().validate(request.getApiModel()); } - // TigerCommonRequest - ValidatorManager.getInstance().validate(request.getApiModel()); - } - - private String getServerUrl(TigerRequest request) { - String url = null; - MethodType methodType = request.getApiMethodName().getType(); - if (MethodType.QUOTE == methodType) { - url = this.quoteServerUrl; - } else if (MethodType.TRADE == methodType && paperServerUrl != null) { - String account = AccountUtil.parseAccount(request); - if (AccountUtil.isVirtualAccount(account)) { - url = this.paperServerUrl; - } + + private String getServerUrl(TigerRequest request) { + String url = null; + MethodType methodType = request.getApiMethodName().getType(); + if (MethodType.QUOTE == methodType) { + url = this.quoteServerUrl; + } else if (MethodType.TRADE == methodType && paperServerUrl != null) { + String account = AccountUtil.parseAccount(request); + if (AccountUtil.isVirtualAccount(account)) { + url = this.paperServerUrl; + } + } + return url == null ? this.serverUrl : url; } - return url == null ? this.serverUrl : url; - } } diff --git a/src/main/java/org/dromara/northstar/gateway/tiger/TigerLoader.java b/src/main/java/org/dromara/northstar/gateway/tiger/TigerLoader.java index 09cd82b835777b67efd9ac0d3f46c1c1fa27ee0d..4aec613b8bb907c7017fef28ea088c5a20e532dd 100644 --- a/src/main/java/org/dromara/northstar/gateway/tiger/TigerLoader.java +++ b/src/main/java/org/dromara/northstar/gateway/tiger/TigerLoader.java @@ -1,5 +1,6 @@ package org.dromara.northstar.gateway.tiger; +import lombok.extern.slf4j.Slf4j; import org.dromara.northstar.common.constant.ChannelType; import org.dromara.northstar.gateway.GatewayMetaProvider; import org.dromara.northstar.gateway.IMarketCenter; @@ -8,28 +9,26 @@ import org.springframework.boot.CommandLineRunner; import org.springframework.core.annotation.Order; import org.springframework.stereotype.Component; -import lombok.extern.slf4j.Slf4j; - @Slf4j -@Order(0) // 加载顺序需要显式声明,否则会最后才被加载,从而导致加载网关与模组时报异常 +@Order(0) // 加载顺序需要显式声明,否则会最后才被加载,从而导致加载网关与模组时报异常 @Component -public class TigerLoader implements CommandLineRunner{ - - @Autowired - private IMarketCenter mktCenter; - - @Autowired - private TigerDataServiceManager dsMgr; - - @Autowired - private GatewayMetaProvider gatewayMetaProvider; - - @Autowired - private TigerGatewayFactory tigerFactory; - - public void run(String... args) throws Exception { - gatewayMetaProvider.add(ChannelType.TIGER, new TigerGatewaySettings(), tigerFactory, dsMgr); - - } +public class TigerLoader implements CommandLineRunner { + + @Autowired + private IMarketCenter mktCenter; + + @Autowired + private TigerDataServiceManager dsMgr; + + @Autowired + private GatewayMetaProvider gatewayMetaProvider; + + @Autowired + private TigerGatewayFactory tigerFactory; + + public void run(String... args) throws Exception { + gatewayMetaProvider.add(ChannelType.TIGER, new TigerGatewaySettings(), tigerFactory); + + } } diff --git a/src/main/java/org/dromara/northstar/gateway/tiger/TigerMarketGatewayAdapter.java b/src/main/java/org/dromara/northstar/gateway/tiger/TigerMarketGatewayAdapter.java index f8437c05131952213ef77cecaa76d27cc2c39e48..e598ff54bbc899bd11434c12dc3e2e1cfb509d38 100644 --- a/src/main/java/org/dromara/northstar/gateway/tiger/TigerMarketGatewayAdapter.java +++ b/src/main/java/org/dromara/northstar/gateway/tiger/TigerMarketGatewayAdapter.java @@ -1,39 +1,37 @@ package org.dromara.northstar.gateway.tiger; -import java.time.Instant; -import java.time.LocalDateTime; -import java.time.ZoneId; -import java.time.ZoneOffset; -import java.util.List; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; - +import com.tigerbrokers.stock.openapi.client.config.ClientConfig; +import com.tigerbrokers.stock.openapi.client.socket.ApiComposeCallback; +import com.tigerbrokers.stock.openapi.client.socket.WebSocketClient; +import com.tigerbrokers.stock.openapi.client.socket.data.TradeTick; +import com.tigerbrokers.stock.openapi.client.socket.data.pb.*; +import com.tigerbrokers.stock.openapi.client.struct.SubscribedSymbol; +import com.tigerbrokers.stock.openapi.client.struct.enums.Language; +import com.tigerbrokers.stock.openapi.client.util.ApiLogger; +import lombok.extern.slf4j.Slf4j; import org.dromara.northstar.common.constant.ChannelType; import org.dromara.northstar.common.constant.ConnectionState; import org.dromara.northstar.common.constant.DateTimeConstant; import org.dromara.northstar.common.event.FastEventEngine; import org.dromara.northstar.common.event.NorthstarEventType; import org.dromara.northstar.common.model.GatewayDescription; +import org.dromara.northstar.common.model.core.Contract; import org.dromara.northstar.gateway.IContractManager; import org.dromara.northstar.gateway.MarketGateway; - -import com.alibaba.fastjson.JSONObject; -import com.tigerbrokers.stock.openapi.client.config.ClientConfig; -import com.tigerbrokers.stock.openapi.client.socket.ApiComposeCallback; -import com.tigerbrokers.stock.openapi.client.socket.WebSocketClient; -import com.tigerbrokers.stock.openapi.client.struct.SubscribedSymbol; -import com.tigerbrokers.stock.openapi.client.struct.enums.Language; -import com.tigerbrokers.stock.openapi.client.struct.enums.QuoteKeyType; -import com.tigerbrokers.stock.openapi.client.util.ApiLogger; - -import lombok.extern.slf4j.Slf4j; import xyz.redtorch.pb.CoreEnum.CommonStatusEnum; import xyz.redtorch.pb.CoreEnum.ProductClassEnum; -import xyz.redtorch.pb.CoreField.ContractField; import xyz.redtorch.pb.CoreField.NoticeField; import xyz.redtorch.pb.CoreField.TickField; +import java.time.Instant; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.time.ZoneOffset; +import java.util.List; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + /** * 老虎证券行情网关适配器 * @author KevinHuangwl @@ -42,276 +40,319 @@ import xyz.redtorch.pb.CoreField.TickField; @Slf4j public class TigerMarketGatewayAdapter implements MarketGateway { - private GatewayDescription gd; - - private FastEventEngine feEngine; - - private WebSocketClient client; - - private TigerSpi spi; - - private ConnectionState connState = ConnectionState.DISCONNECTED; - - public TigerMarketGatewayAdapter(GatewayDescription gd, FastEventEngine feEngine, IContractManager contractMgr) { - this.gd = gd; - this.feEngine = feEngine; - this.spi = new TigerSpi(feEngine, contractMgr); - - TigerGatewaySettings settings = (TigerGatewaySettings) gd.getSettings(); - ClientConfig clientConfig = ClientConfig.DEFAULT_CONFIG; - clientConfig.tigerId = settings.getTigerId(); + private GatewayDescription gd; + + private FastEventEngine feEngine; + + private WebSocketClient client; + + private TigerSpi spi; + + private ConnectionState connState = ConnectionState.DISCONNECTED; + + public TigerMarketGatewayAdapter(GatewayDescription gd, FastEventEngine feEngine, IContractManager contractMgr) { + this.gd = gd; + this.feEngine = feEngine; + this.spi = new TigerSpi(feEngine, contractMgr); + + TigerGatewaySettings settings = (TigerGatewaySettings) gd.getSettings(); + ClientConfig clientConfig = ClientConfig.DEFAULT_CONFIG; + clientConfig.tigerId = settings.getTigerId(); clientConfig.defaultAccount = settings.getAccountId(); clientConfig.privateKey = settings.getPrivateKey(); clientConfig.license = settings.getLicense(); clientConfig.secretKey = settings.getSecretKey(); clientConfig.language = Language.zh_CN; - this.client = WebSocketClient.getInstance().clientConfig(clientConfig).apiComposeCallback(spi); - ApiLogger.setEnabled(true, "logs/"); - } - - @Override - public void connect() { - client.connect(); - connState = ConnectionState.CONNECTED; - feEngine.emitEvent(NorthstarEventType.GATEWAY_READY, gd.getGatewayId()); - } - - @Override - public void disconnect() { - client.disconnect(); - connState = ConnectionState.DISCONNECTED; - } - - @Override - public ConnectionState getConnectionState() { - return connState; - } - - @Override - public boolean getAuthErrorFlag() { - return false; - } - - @Override - public boolean subscribe(ContractField contract) { - if(contract.getProductClass() == ProductClassEnum.EQUITY) { - client.subscribeQuote(Set.of(contract.getSymbol()), QuoteKeyType.ALL); - log.info("TIGER网关订阅合约 {} {}", contract.getName(), contract.getUnifiedSymbol()); - } - // TODO 期货期权暂没实现 - return true; - } - - @Override - public boolean unsubscribe(ContractField contract) { - if(contract.getProductClass() == ProductClassEnum.EQUITY) { - client.cancelSubscribeQuote(Set.of(contract.getSymbol())); - } - // TODO 期货期权暂没实现 - return true; - } - - @Override - public boolean isActive() { - return spi.isActive(); - } - - @Override - public ChannelType channelType() { - return ChannelType.TIGER; - } - - @Override - public GatewayDescription gatewayDescription() { - gd.setConnectionState(getConnectionState()); - return gd; - } - - @Override - public String gatewayId() { - return gd.getGatewayId(); - } - - class TigerSpi implements ApiComposeCallback { - - static final String MKT_STAT = "marketStatus"; - static final String ASK_P = "askPrice"; - static final String ASK_V = "askSize"; - static final String BID_P = "bidPrice"; - static final String BID_V = "bidSize"; - - private ConcurrentMap tickBuilderMap = new ConcurrentHashMap<>(); - - private FastEventEngine feEngine; - private IContractManager contractMgr; - - private long lastActive; - - public TigerSpi(FastEventEngine feEngine, IContractManager contractMgr) { - this.feEngine = feEngine; - this.contractMgr = contractMgr; - } - - @Override - public void orderStatusChange(JSONObject jsonObject) { - // TODO Auto-generated method stub - - } - - @Override - public void positionChange(JSONObject jsonObject) { - // TODO Auto-generated method stub - - } - - @Override - public void assetChange(JSONObject jsonObject) { - // TODO Auto-generated method stub - - } - - @Override - public void quoteChange(JSONObject jsonObject) { - lastActive = System.currentTimeMillis(); - if(log.isTraceEnabled()) { - log.trace("数据回报:{}", jsonObject); - } - if(jsonObject.containsKey("hourTradingTag")) { - return; //忽略盘前盘后数据 - } - try { - - String symbol = jsonObject.getString("symbol"); - long timestamp = jsonObject.getLongValue("timestamp"); - LocalDateTime ldt = LocalDateTime.ofInstant(Instant.ofEpochMilli(timestamp), ZoneId.systemDefault()); - ZoneId londonTimeZone = ZoneId.of(ZoneOffset.ofHours(0).getId()); // 伦敦时区正好可以让所有时区的交易计算在同一天 - tickBuilderMap.computeIfAbsent(symbol, key -> TickField.newBuilder() - .setGatewayId(gd.getGatewayId()) - .addAllAskPrice(List.of(0D)) - .addAllAskVolume(List.of(0)) - .addAllBidPrice(List.of(0D)) - .addAllBidVolume(List.of(0)) - .setUnifiedSymbol(contractMgr.getContract(ChannelType.TIGER, symbol).contractField().getUnifiedSymbol())); - if(jsonObject.containsKey(ASK_P)) tickBuilderMap.get(symbol).setAskPrice(0, jsonObject.getDoubleValue(ASK_P)); - if(jsonObject.containsKey(BID_P)) tickBuilderMap.get(symbol).setBidPrice(0, jsonObject.getDoubleValue(BID_P)); - if(jsonObject.containsKey(ASK_V)) tickBuilderMap.get(symbol).setAskVolume(0, jsonObject.getIntValue(ASK_V)); - if(jsonObject.containsKey(BID_V)) tickBuilderMap.get(symbol).setBidVolume(0, jsonObject.getIntValue(BID_V)); - - if(jsonObject.containsKey(MKT_STAT) && jsonObject.getString(MKT_STAT).equals("交易中")) { - // 交易数据更新 - feEngine.emitEvent(NorthstarEventType.TICK, tickBuilderMap.get(symbol) - .setActionDay(ldt.toLocalDate().format(DateTimeConstant.D_FORMAT_INT_FORMATTER)) - .setActionTime(ldt.toLocalTime().format(DateTimeConstant.T_FORMAT_WITH_MS_INT_FORMATTER)) - .setTradingDay(LocalDateTime.ofInstant(Instant.ofEpochMilli(timestamp), londonTimeZone).toLocalDate().format(DateTimeConstant.D_FORMAT_INT_FORMATTER)) - .setActionTimestamp(timestamp) - .setPreClosePrice(jsonObject.getDoubleValue("preClose")) - .setLastPrice(jsonObject.getDoubleValue("latestPrice")) - .setHighPrice(jsonObject.getDoubleValue("high")) - .setLowPrice(jsonObject.getDoubleValue("low")) - .setOpenPrice(jsonObject.getDoubleValue("open")) - .setVolume(jsonObject.getLongValue("volume")) - .build()); - } else if(!jsonObject.containsKey(MKT_STAT)){ - // 盘口数据更新 - feEngine.emitEvent(NorthstarEventType.TICK, tickBuilderMap.get(symbol) - .setActionDay(ldt.toLocalDate().format(DateTimeConstant.D_FORMAT_INT_FORMATTER)) - .setActionTime(ldt.toLocalTime().format(DateTimeConstant.T_FORMAT_WITH_MS_INT_FORMATTER)) - .setTradingDay(LocalDateTime.ofInstant(Instant.ofEpochMilli(timestamp), londonTimeZone).toLocalDate().format(DateTimeConstant.D_FORMAT_INT_FORMATTER)) - .setActionTimestamp(timestamp) - .build()); - } - } catch(Exception e) { - log.warn("异常数据:{}", jsonObject); - log.error("", e); - } - } - - @Override - public void tradeTickChange(JSONObject jsonObject) { - // TODO Auto-generated method stub - - } - - @Override - public void optionChange(JSONObject jsonObject) { - lastActive = System.currentTimeMillis(); - } - - @Override - public void futureChange(JSONObject jsonObject) { - lastActive = System.currentTimeMillis(); - } - - @Override - public void depthQuoteChange(JSONObject jsonObject) { - } - - @Override - public void subscribeEnd(String id, String subject, JSONObject jsonObject) { - log.info("成功订阅 [{} {} {}]", id, subject, jsonObject); - } - - @Override - public void cancelSubscribeEnd(String id, String subject, JSONObject jsonObject) { - log.info("取消订阅 [{} {} {}]", id, subject, jsonObject); - } - - @Override - public void getSubscribedSymbolEnd(SubscribedSymbol subscribedSymbol) { - } - - @Override - public void error(String errorMsg) { - NoticeField notice = NoticeField.newBuilder() - .setStatus(CommonStatusEnum.COMS_WARN) - .setContent(errorMsg) - .setTimestamp(System.currentTimeMillis()) - .build(); - feEngine.emitEvent(NorthstarEventType.NOTICE, notice); - } - - @Override - public void error(int id, int errorCode, String errorMsg) { - log.error("TIGER网关出错 [{} {} {}]", id, errorCode, errorMsg); - } - - @Override - public void connectionClosed() { - log.info("TIGER网关断开"); - } - - @Override - public void connectionKickoff(int errorCode, String errorMsg) { - NoticeField notice = NoticeField.newBuilder() - .setStatus(CommonStatusEnum.COMS_WARN) - .setContent(errorMsg) - .setTimestamp(System.currentTimeMillis()) - .build(); - feEngine.emitEvent(NorthstarEventType.NOTICE, notice); - } - - @Override - public void connectionAck() { - log.info("TIGER网关应答"); - } - - @Override - public void connectionAck(int serverSendInterval, int serverReceiveInterval) { - } - - @Override - public void hearBeat(String heartBeatContent) { - } - - @Override - public void serverHeartBeatTimeOut(String channelIdAsLongText) { - log.info("TIGER网关服务响应超时:{}", channelIdAsLongText); - } - - public boolean isActive() { - return System.currentTimeMillis() - lastActive < 3000; - } - - } + this.client = WebSocketClient.getInstance().clientConfig(clientConfig).apiComposeCallback(spi); + ApiLogger.setEnabled(true, "logs/"); + } + + @Override + public void connect() { + client.connect(); + connState = ConnectionState.CONNECTED; + feEngine.emitEvent(NorthstarEventType.GATEWAY_READY, gd.getGatewayId()); + } + + @Override + public void disconnect() { + client.disconnect(); + connState = ConnectionState.DISCONNECTED; + } + + @Override + public ConnectionState getConnectionState() { + return connState; + } + + @Override + public boolean getAuthErrorFlag() { + return false; + } + + @Override + public boolean subscribe(Contract contract) { + if (contract.productClass() == ProductClassEnum.EQUITY) { + client.cancelSubscribeKline(Set.of(contract.symbol())); + log.info("TIGER网关订阅合约 {} {}", contract.name(), contract.unifiedSymbol()); + } + // TODO 期货期权暂没实现 + return true; + } + + @Override + public boolean unsubscribe(Contract contract) { + if (contract.productClass() == ProductClassEnum.EQUITY) { + client.cancelSubscribeQuote(Set.of(contract.symbol())); + } + // TODO 期货期权暂没实现 + return true; + } + + @Override + public boolean isActive() { + return spi.isActive(); + } + + @Override + public ChannelType channelType() { + return ChannelType.TIGER; + } + + @Override + public GatewayDescription gatewayDescription() { + gd.setConnectionState(getConnectionState()); + return gd; + } + + @Override + public String gatewayId() { + return gd.getGatewayId(); + } + + class TigerSpi implements ApiComposeCallback { + + static final String MKT_STAT = "marketStatus"; + static final String ASK_P = "askPrice"; + static final String ASK_V = "askSize"; + static final String BID_P = "bidPrice"; + static final String BID_V = "bidSize"; + + private ConcurrentMap tickBuilderMap = new ConcurrentHashMap<>(); + + private FastEventEngine feEngine; + private IContractManager contractMgr; + + private long lastActive; + + public TigerSpi(FastEventEngine feEngine, IContractManager contractMgr) { + this.feEngine = feEngine; + this.contractMgr = contractMgr; + } + + @Override + public void orderStatusChange(OrderStatusData orderStatusData) { + // TODO Auto-generated method stub + + } + + @Override + public void orderTransactionChange(OrderTransactionData orderTransactionData) { + + } + + @Override + public void positionChange(PositionData positionData) { + // TODO Auto-generated method stub + + } + + @Override + public void assetChange(AssetData assetData) { + // TODO Auto-generated method stub + + } + + @Override + public void quoteChange(QuoteBasicData quoteBasicData) { + lastActive = System.currentTimeMillis(); + if (log.isTraceEnabled()) { + log.trace("数据回报:{}", quoteBasicData); + } + if (quoteBasicData.getHourTradingTag() != null && !quoteBasicData.getHourTradingTag().isEmpty()) { + return; // 忽略盘前盘后数据 + } + try { + String symbol = quoteBasicData.getSymbol(); + long timestamp = quoteBasicData.getTimestamp(); + LocalDateTime ldt = LocalDateTime.ofInstant(Instant.ofEpochMilli(timestamp), ZoneId.systemDefault()); + ZoneId londonTimeZone = ZoneId.of(ZoneOffset.UTC.getId()); + tickBuilderMap.computeIfAbsent(symbol, key -> TickField.newBuilder() + .setGatewayId(gd.getGatewayId()) + .addAllAskPrice(List.of(0D)) + .addAllAskVolume(List.of(0)) + .addAllBidPrice(List.of(0D)) + .addAllBidVolume(List.of(0)) + .setUnifiedSymbol(contractMgr.getContract(ChannelType.TIGER, symbol).contract().unifiedSymbol())); + + // Example: replace with actual fields + // Assuming `quoteBasicData` has methods like `getAskPrice`, `getBidPrice`, etc. + tickBuilderMap.get(symbol).setAskPrice(0, quoteBasicData.getAvgPrice()); // Example field + tickBuilderMap.get(symbol).setBidPrice(0, quoteBasicData.getAvgPrice()); // Example field + tickBuilderMap.get(symbol).setAskVolume(0, (int) quoteBasicData.getVolume()); // Example field + tickBuilderMap.get(symbol).setBidVolume(0, (int) quoteBasicData.getVolume()); // Example field + + if (quoteBasicData.getMarketStatus() != null && quoteBasicData.getMarketStatus().equals("交易中")) { + // 交易数据更新 + feEngine.emitEvent(NorthstarEventType.TICK, tickBuilderMap.get(symbol) + .setActionDay(ldt.toLocalDate().format(DateTimeConstant.D_FORMAT_INT_FORMATTER)) + .setActionTime(ldt.toLocalTime().format(DateTimeConstant.T_FORMAT_WITH_MS_INT_FORMATTER)) + .setTradingDay(LocalDateTime.ofInstant(Instant.ofEpochMilli(timestamp), londonTimeZone).toLocalDate().format(DateTimeConstant.D_FORMAT_INT_FORMATTER)) + .setActionTimestamp(timestamp) + .setPreClosePrice(quoteBasicData.getPreClose()) + .setLastPrice(quoteBasicData.getLatestPrice()) + .setHighPrice(quoteBasicData.getHigh()) + .setLowPrice(quoteBasicData.getLow()) + .setOpenPrice(quoteBasicData.getOpen()) + .setVolume(quoteBasicData.getVolume()) + .build()); + } else if (quoteBasicData.getMarketStatus() == null || quoteBasicData.getMarketStatus().isEmpty()) { + // 盘口数据更新 + feEngine.emitEvent(NorthstarEventType.TICK, tickBuilderMap.get(symbol) + .setActionDay(ldt.toLocalDate().format(DateTimeConstant.D_FORMAT_INT_FORMATTER)) + .setActionTime(ldt.toLocalTime().format(DateTimeConstant.T_FORMAT_WITH_MS_INT_FORMATTER)) + .setTradingDay(LocalDateTime.ofInstant(Instant.ofEpochMilli(timestamp), londonTimeZone).toLocalDate().format(DateTimeConstant.D_FORMAT_INT_FORMATTER)) + .setActionTimestamp(timestamp) + .build()); + } + } catch (Exception e) { + log.warn("异常数据:{}", quoteBasicData); + log.error("", e); + } + } + + @Override + public void quoteAskBidChange(QuoteBBOData quoteBBOData) { + + } + + + @Override + public void tradeTickChange(TradeTick tradeTick) { + // TODO Auto-generated method stub + + } + + @Override + public void fullTickChange(TickData tickData) { + + } + + @Override + public void optionChange(QuoteBasicData quoteBasicData) { + lastActive = System.currentTimeMillis(); + } + + @Override + public void optionAskBidChange(QuoteBBOData quoteBBOData) { + + } + + @Override + public void futureChange(QuoteBasicData quoteBasicData) { + lastActive = System.currentTimeMillis(); + } + + @Override + public void futureAskBidChange(QuoteBBOData quoteBBOData) { + + } + + @Override + public void depthQuoteChange(QuoteDepthData quoteDepthData) { + } + + @Override + public void klineChange(KlineData klineData) { + + } + + @Override + public void stockTopPush(StockTopData stockTopData) { + + } + + @Override + public void optionTopPush(OptionTopData optionTopData) { + + } + + @Override + public void subscribeEnd(int id, String subject, String jsonObject) { + log.info("成功订阅 [{} {} {}]", id, subject, jsonObject); + } + + @Override + public void cancelSubscribeEnd(int id, String subject, String jsonObject) { + log.info("取消订阅 [{} {} {}]", id, subject, jsonObject); + } + + @Override + public void getSubscribedSymbolEnd(SubscribedSymbol subscribedSymbol) { + } + + @Override + public void error(String errorMsg) { + NoticeField notice = NoticeField.newBuilder() + .setStatus(CommonStatusEnum.COMS_WARN) + .setContent(errorMsg) + .setTimestamp(System.currentTimeMillis()) + .build(); + feEngine.emitEvent(NorthstarEventType.NOTICE, notice); + } + + @Override + public void error(int id, int errorCode, String errorMsg) { + log.error("TIGER网关出错 [{} {} {}]", id, errorCode, errorMsg); + } + + @Override + public void connectionClosed() { + log.info("TIGER网关断开"); + } + + @Override + public void connectionKickout(int errorCode, String errorMsg) { + NoticeField notice = NoticeField.newBuilder() + .setStatus(CommonStatusEnum.COMS_WARN) + .setContent(errorMsg) + .setTimestamp(System.currentTimeMillis()) + .build(); + feEngine.emitEvent(NorthstarEventType.NOTICE, notice); + } + + @Override + public void connectionAck() { + log.info("TIGER网关应答"); + } + + @Override + public void connectionAck(int serverSendInterval, int serverReceiveInterval) { + } + + @Override + public void hearBeat(String heartBeatContent) { + } + + @Override + public void serverHeartBeatTimeOut(String channelIdAsLongText) { + log.info("TIGER网关服务响应超时:{}", channelIdAsLongText); + } + + public boolean isActive() { + return System.currentTimeMillis() - lastActive < 3000; + } + + } } diff --git a/src/main/java/org/dromara/northstar/gateway/tiger/TigerTradeGatewayAdapter.java b/src/main/java/org/dromara/northstar/gateway/tiger/TigerTradeGatewayAdapter.java index 08cf53adc8d45b93a5ba68b2f5e8d15d84f9cab4..b76589f4342b1ffc606af341272f5f166b9afeef 100644 --- a/src/main/java/org/dromara/northstar/gateway/tiger/TigerTradeGatewayAdapter.java +++ b/src/main/java/org/dromara/northstar/gateway/tiger/TigerTradeGatewayAdapter.java @@ -1,26 +1,5 @@ package org.dromara.northstar.gateway.tiger; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Optional; -import java.util.Timer; -import java.util.TimerTask; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; -import java.util.concurrent.Executor; -import java.util.concurrent.Executors; - -import org.apache.commons.lang3.StringUtils; -import org.dromara.northstar.common.constant.ChannelType; -import org.dromara.northstar.common.constant.ConnectionState; -import org.dromara.northstar.common.event.FastEventEngine; -import org.dromara.northstar.common.event.NorthstarEventType; -import org.dromara.northstar.common.model.GatewayDescription; -import org.dromara.northstar.gateway.IContractManager; -import org.dromara.northstar.gateway.TradeGateway; - import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; @@ -33,27 +12,31 @@ import com.tigerbrokers.stock.openapi.client.https.request.trade.TradeOrderReque import com.tigerbrokers.stock.openapi.client.https.response.TigerHttpResponse; import com.tigerbrokers.stock.openapi.client.https.response.trade.PrimeAssetResponse; import com.tigerbrokers.stock.openapi.client.https.response.trade.TradeOrderResponse; -import com.tigerbrokers.stock.openapi.client.struct.enums.ActionType; -import com.tigerbrokers.stock.openapi.client.struct.enums.Category; import com.tigerbrokers.stock.openapi.client.struct.enums.Currency; -import com.tigerbrokers.stock.openapi.client.struct.enums.Language; -import com.tigerbrokers.stock.openapi.client.struct.enums.MethodName; -import com.tigerbrokers.stock.openapi.client.struct.enums.OrderType; -import com.tigerbrokers.stock.openapi.client.struct.enums.SecType; +import com.tigerbrokers.stock.openapi.client.struct.enums.*; import com.tigerbrokers.stock.openapi.client.struct.param.OrderParameter; import com.tigerbrokers.stock.openapi.client.util.builder.AccountParamBuilder; import com.tigerbrokers.stock.openapi.client.util.builder.TradeParamBuilder; - import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.dromara.northstar.common.constant.ChannelType; +import org.dromara.northstar.common.constant.ConnectionState; +import org.dromara.northstar.common.event.FastEventEngine; +import org.dromara.northstar.common.event.NorthstarEventType; +import org.dromara.northstar.common.model.GatewayDescription; +import org.dromara.northstar.common.model.core.*; +import org.dromara.northstar.gateway.IContractManager; +import org.dromara.northstar.gateway.TradeGateway; import xyz.redtorch.pb.CoreEnum.CurrencyEnum; import xyz.redtorch.pb.CoreEnum.PositionDirectionEnum; import xyz.redtorch.pb.CoreField.AccountField; -import xyz.redtorch.pb.CoreField.CancelOrderReqField; -import xyz.redtorch.pb.CoreField.ContractField; -import xyz.redtorch.pb.CoreField.OrderField; -import xyz.redtorch.pb.CoreField.PositionField; -import xyz.redtorch.pb.CoreField.SubmitOrderReqField; -import xyz.redtorch.pb.CoreField.TradeField; + +import java.util.*; +import java.util.Map.Entry; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; /** * 老虎证券交易网关适配器 @@ -61,247 +44,250 @@ import xyz.redtorch.pb.CoreField.TradeField; * */ @Slf4j -public class TigerTradeGatewayAdapter implements TradeGateway{ - - private final FastEventEngine feEngine; - private final GatewayDescription gd; - private final TigerGatewaySettings settings; - private final IContractManager contractMgr; - - private final ConcurrentMap orderIdMap = new ConcurrentHashMap<>(); - - private TigerHttpClient client; - private OrderTradeQueryProxy proxy; - private Timer timer; - - private ConnectionState connState = ConnectionState.DISCONNECTED; - - private Executor exec = Executors.newSingleThreadExecutor(); - - private Map lastPositions = new HashMap<>(); - - public TigerTradeGatewayAdapter(FastEventEngine feEngine, GatewayDescription gd, IContractManager contractMgr) { - this.feEngine = feEngine; - this.gd = gd; - this.settings = (TigerGatewaySettings) gd.getSettings(); - this.contractMgr = contractMgr; - } - - @Override - public synchronized void connect() { - ClientConfig clientConfig = ClientConfig.DEFAULT_CONFIG; - clientConfig.tigerId = settings.getTigerId(); +public class TigerTradeGatewayAdapter implements TradeGateway { + + private final FastEventEngine feEngine; + private final GatewayDescription gd; + private final TigerGatewaySettings settings; + private final IContractManager contractMgr; + + private final ConcurrentMap orderIdMap = new ConcurrentHashMap<>(); + + private TigerHttpClient client; + private OrderTradeQueryProxy proxy; + private Timer timer; + + private ConnectionState connState = ConnectionState.DISCONNECTED; + + private Executor exec = Executors.newSingleThreadExecutor(); + + private Map lastPositions = new HashMap<>(); + + public TigerTradeGatewayAdapter(FastEventEngine feEngine, GatewayDescription gd, IContractManager contractMgr) { + this.feEngine = feEngine; + this.gd = gd; + this.settings = (TigerGatewaySettings) gd.getSettings(); + this.contractMgr = contractMgr; + } + + @Override + public synchronized void connect() { + ClientConfig clientConfig = ClientConfig.DEFAULT_CONFIG; + clientConfig.tigerId = settings.getTigerId(); clientConfig.defaultAccount = settings.getAccountId(); clientConfig.privateKey = settings.getPrivateKey(); clientConfig.license = settings.getLicense(); clientConfig.secretKey = settings.getSecretKey(); clientConfig.language = Language.zh_CN; - client = TigerHttpClient.getInstance().clientConfig(clientConfig); - proxy = new OrderTradeQueryProxy(client, contractMgr, gatewayId(), settings.getAccountId()); - List orders = proxy.getDeltaOrder(); - List symbols = orders.stream().map(OrderField::getContract).map(ContractField::getSymbol).distinct().toList(); - - connState = ConnectionState.CONNECTED; - feEngine.emitEvent(NorthstarEventType.LOGGED_IN, gatewayId()); - - orders.forEach(order -> feEngine.emitEvent(NorthstarEventType.ORDER, order)); - symbols.forEach(symbol -> { - List trades = proxy.getTrades(symbol); - trades.forEach(trade -> feEngine.emitEvent(NorthstarEventType.TRADE, trade)); - }); - - timer = new Timer("TIGER_" + gatewayId(), true); - timer.scheduleAtFixedRate(new TimerTask() { - - @Override - public void run() { - try { - queryAccount(); - queryPosition(); - TigerTradeGatewayAdapter.this.doQueryOrderAndTrade(); - } catch(Exception e) { - log.error("", e); - } - } - }, 3000, 1500); - } - - private void doQueryOrderAndTrade() { - proxy.getDeltaOrder().forEach(order -> { - Long id = Long.valueOf(order.getOrderId()); - String originOrderId = orderIdMap.get(id); - feEngine.emitEvent(NorthstarEventType.ORDER, order.toBuilder().setOriginOrderId(Optional.ofNullable(originOrderId).orElse("")).build()); - if(order.getTradedVolume() > 0) { - proxy.getDeltaTrade(Long.valueOf(order.getOrderId())) - .forEach(trade -> feEngine.emitEvent(NorthstarEventType.TRADE, trade.toBuilder().setOriginOrderId(Optional.ofNullable(originOrderId).orElse("")).build())); - } - }); - } - - @Override - public synchronized void disconnect() { - timer.cancel(); - timer = null; - client = null; - proxy = null; - feEngine.emitEvent(NorthstarEventType.LOGGED_OUT, gatewayId()); - connState = ConnectionState.DISCONNECTED; - } - - private void queryAccount() { - log.trace("查询TIGER账户信息"); - PrimeAssetRequest assetRequest = PrimeAssetRequest.buildPrimeAssetRequest(settings.getAccountId()); - PrimeAssetResponse primeAssetResponse = client.execute(assetRequest); - if(!primeAssetResponse.isSuccess()) { - log.warn("查询账户返回异常:{}", primeAssetResponse.getMessage()); - return; - } - //查询证券相关资产信息 - PrimeAssetItem.Segment segment = primeAssetResponse.getSegment(Category.S); // 暂不实现期货资产查询 - //查询账号中美元相关资产信息 - if (segment != null) { - PrimeAssetItem.CurrencyAssets assetByCurrency = segment.getAssetByCurrency(Currency.USD); - feEngine.emitEvent(NorthstarEventType.ACCOUNT, AccountField.newBuilder() - .setAccountId(settings.getAccountId()) - .setGatewayId(gatewayId()) - .setAvailable(assetByCurrency.getCashBalance()) - .setMargin(segment.getGrossPositionValue()) - .setBalance(segment.getNetLiquidation()) - .setPositionProfit(segment.getUnrealizedPL()) - .setCloseProfit(segment.getRealizedPL()) - .setCurrency(CurrencyEnum.USD) - .build()); - } - } - - private void queryPosition() { - log.trace("查询TIGER持仓信息"); - TigerHttpRequest request = new TigerHttpRequest(MethodName.POSITIONS); - String bizContent = AccountParamBuilder.instance() - .account(settings.getAccountId()) - .secType(SecType.STK) - .buildJson(); - - request.setBizContent(bizContent); - TigerHttpResponse response = client.execute(request); - if(!response.isSuccess()) { - log.warn("查询持仓返回异常:{}", response.getMessage()); - return; - } - // 解析具体字段 - JSONArray positions = JSON.parseObject(response.getData()).getJSONArray("items"); - Map positionMap = new HashMap<>(); - for(int i=0; i { - feEngine.emitEvent(NorthstarEventType.POSITION, pf.toBuilder().setPosition(0).build()); - }); - lastPositions = positionMap; - } - - @Override - public ConnectionState getConnectionState() { - return connState; - } - - @Override - public boolean getAuthErrorFlag() { - return false; - } - - @Override - public String submitOrder(SubmitOrderReqField submitOrderReq) { - if(connState != ConnectionState.CONNECTED) { - throw new IllegalStateException("网关未连线"); - } - - SecType secType = switch(submitOrderReq.getContract().getProductClass()) { - case EQUITY -> SecType.STK; - case FUTURES -> SecType.FUT; - default -> throw new IllegalArgumentException("Unexpected value: " + submitOrderReq.getContract().getProductClass()); - }; - - OrderType orderType = switch(submitOrderReq.getOrderPriceType()) { - case OPT_LimitPrice -> OrderType.LMT; - case OPT_AnyPrice -> OrderType.MKT; - default -> throw new IllegalArgumentException("老虎证券仅支持限价与市价两种订单类型"); - }; - - ActionType actionType = switch(submitOrderReq.getDirection()) { - case D_Buy -> ActionType.BUY; - case D_Sell -> ActionType.SELL; - default -> throw new IllegalArgumentException("Unexpected value: " + submitOrderReq.getDirection()); - }; - - TradeOrderModel model = new TradeOrderModel(); - model.setAccount(settings.getAccountId()); - model.setSymbol(submitOrderReq.getContract().getSymbol()); - model.setSecType(secType); - model.setOrderType(orderType); - model.setAction(actionType); - model.setLimitPrice(orderType == OrderType.MKT ? null : submitOrderReq.getPrice()); - model.setTotalQuantity(submitOrderReq.getVolume()); - model.setSecretKey(settings.getSecretKey()); - log.info("网关[{}] 下单:{}", gatewayId(), model); - TradeOrderResponse response = client.execute(TradeOrderRequest.newRequest(model)); - log.info("网关[{}] 下单反馈:{}", gatewayId(), JSON.toJSONString(response)); - Long id = response.getItem().getId(); - orderIdMap.put(id, submitOrderReq.getOriginOrderId()); - exec.execute(this::doQueryOrderAndTrade); - return id + ""; - } - - @Override - public boolean cancelOrder(CancelOrderReqField cancelOrderReq) { - if(connState != ConnectionState.CONNECTED) { - throw new IllegalStateException("网关未连线"); - } - for(Entry e : orderIdMap.entrySet()) { - if(StringUtils.isNotEmpty(cancelOrderReq.getOriginOrderId()) && StringUtils.equals(cancelOrderReq.getOriginOrderId(), e.getValue())) { - Long id = e.getKey(); - OrderParameter params = TradeParamBuilder.instance().account(settings.getAccountId()).id(id).secretKey(settings.getSecretKey()).build(); - String bizContent = JSON.toJSONString(params); - TigerHttpRequest request = new TigerHttpRequest(MethodName.CANCEL_ORDER); - request.setBizContent(bizContent); - log.info("网关[{}] 撤单:{}", gatewayId(), bizContent); - TigerHttpResponse response = client.execute(request); - log.info("网关[{}] 撤单反馈:{}", gatewayId(), JSON.toJSONString(response)); - return response.isSuccess(); - } - } - - return false; - } - - @Override - public GatewayDescription gatewayDescription() { - gd.setConnectionState(getConnectionState()); - return gd; - } - - @Override - public String gatewayId() { - return gd.getGatewayId(); - } + client = TigerHttpClient.getInstance().clientConfig(clientConfig); + proxy = new OrderTradeQueryProxy(client, contractMgr, gatewayId(), settings.getAccountId()); + List orders = proxy.getDeltaOrder(); + List symbols = orders.stream().map(Order::contract).map(Contract::symbol).distinct().toList(); + + connState = ConnectionState.CONNECTED; + feEngine.emitEvent(NorthstarEventType.LOGGED_IN, gatewayId()); + + orders.forEach(order -> feEngine.emitEvent(NorthstarEventType.ORDER, order)); + symbols.forEach(symbol -> { + List trades = proxy.getTrades(symbol); + trades.forEach(trade -> feEngine.emitEvent(NorthstarEventType.TRADE, trade)); + }); + + timer = new Timer("TIGER_" + gatewayId(), true); + timer.scheduleAtFixedRate(new TimerTask() { + + @Override + public void run() { + try { + queryAccount(); + queryPosition(); + TigerTradeGatewayAdapter.this.doQueryOrderAndTrade(); + } catch (Exception e) { + log.error("", e); + } + } + }, 3000, 1500); + } + + private void doQueryOrderAndTrade() { + proxy.getDeltaOrder().forEach(order -> { + Long id = Long.valueOf(order.orderId()); + String originOrderId = orderIdMap.get(id); + feEngine.emitEvent(NorthstarEventType.ORDER, order.toBuilder().originOrderId(Optional.ofNullable(originOrderId).orElse("")).build()); + if (order.tradedVolume() > 0) { + proxy.getDeltaTrade(Long.valueOf(order.orderId())) + .forEach(trade -> feEngine.emitEvent(NorthstarEventType.TRADE, trade.toBuilder().originOrderId(Optional.ofNullable(originOrderId).orElse("")).build())); + } + }); + } + + @Override + public synchronized void disconnect() { + timer.cancel(); + timer = null; + client = null; + proxy = null; + feEngine.emitEvent(NorthstarEventType.LOGGED_OUT, gatewayId()); + connState = ConnectionState.DISCONNECTED; + } + + private void queryAccount() { + log.trace("查询TIGER账户信息"); + PrimeAssetRequest assetRequest = PrimeAssetRequest.buildPrimeAssetRequest(settings.getAccountId()); + PrimeAssetResponse primeAssetResponse = client.execute(assetRequest); + if (!primeAssetResponse.isSuccess()) { + log.warn("查询账户返回异常:{}", primeAssetResponse.getMessage()); + return; + } + //查询证券相关资产信息 + PrimeAssetItem.Segment segment = primeAssetResponse.getSegment(Category.S); // 暂不实现期货资产查询 + //查询账号中美元相关资产信息 + if (segment != null) { + PrimeAssetItem.CurrencyAssets assetByCurrency = segment.getAssetByCurrency(Currency.USD); + feEngine.emitEvent(NorthstarEventType.ACCOUNT, AccountField.newBuilder() + .setAccountId(settings.getAccountId()) + .setGatewayId(gatewayId()) + .setAvailable(assetByCurrency.getCashBalance()) + .setMargin(segment.getGrossPositionValue()) + .setBalance(segment.getNetLiquidation()) + .setPositionProfit(segment.getUnrealizedPL()) + .setCloseProfit(segment.getRealizedPL()) + .setCurrency(CurrencyEnum.USD) + .build()); + } + } + + private void queryPosition() { + log.trace("查询TIGER持仓信息"); + TigerHttpRequest request = new TigerHttpRequest(MethodName.POSITIONS); + String bizContent = AccountParamBuilder.instance() + .account(settings.getAccountId()) + .secType(SecType.STK) + .buildJson(); + + request.setBizContent(bizContent); + TigerHttpResponse response = client.execute(request); + if (!response.isSuccess()) { + log.warn("查询持仓返回异常:{}", response.getMessage()); + return; + } + // 解析具体字段 + JSONArray positions = JSON.parseObject(response.getData()).getJSONArray("items"); + Map positionMap = new HashMap<>(); + for (int i = 0; i < positions.size(); i++) { + JSONObject json = positions.getJSONObject(i); + Contract contract = contractMgr.getContract(ChannelType.TIGER, json.getString("symbol")).contract(); + String positionId = String.format("%s@%s@%s", contract.unifiedSymbol(), PositionDirectionEnum.PD_Long, gatewayId()); + double openPrice = (int) (json.getDoubleValue("averageCost") / contract.priceTick()) * contract.priceTick(); + Position pos = Position.builder() + .gatewayId(gd.getGatewayId()) + .positionId(settings.getAccountId()) + .contract(contract) + .positionProfit(json.getDoubleValue("unrealizedPnl")) + .positionDirection(PositionDirectionEnum.PD_Long) + .position(json.getIntValue("position")) + //.frozen(frozen) + //.tdFrozen(tdFrozen) + //.ydFrozen(ydFrozen) + .openPrice(openPrice) + .openPriceDiff(json.getDoubleValue("latestPrice") - openPrice) + .build(); + + positionMap.put(positionId, pos); + feEngine.emitEvent(NorthstarEventType.POSITION, pos); + lastPositions.remove(positionId); + } + + lastPositions.values().forEach(pf -> { + feEngine.emitEvent(NorthstarEventType.POSITION, pf.toBuilder().position(0).build()); + }); + lastPositions = positionMap; + } + + @Override + public ConnectionState getConnectionState() { + return connState; + } + + @Override + public boolean getAuthErrorFlag() { + return false; + } + + @Override + public String submitOrder(SubmitOrderReq submitOrderReq) { + if (connState != ConnectionState.CONNECTED) { + throw new IllegalStateException("网关未连线"); + } + + SecType secType = switch (submitOrderReq.contract().productClass()) { + case EQUITY -> SecType.STK; + case FUTURES -> SecType.FUT; + default -> + throw new IllegalArgumentException("Unexpected value: " + submitOrderReq.contract().productClass()); + }; + + OrderType orderType = switch (submitOrderReq.orderPriceType()) { + case OPT_LimitPrice -> OrderType.LMT; + case OPT_AnyPrice -> OrderType.MKT; + default -> throw new IllegalArgumentException("老虎证券仅支持限价与市价两种订单类型"); + }; + + ActionType actionType = switch (submitOrderReq.direction()) { + case D_Buy -> ActionType.BUY; + case D_Sell -> ActionType.SELL; + default -> throw new IllegalArgumentException("Unexpected value: " + submitOrderReq.direction()); + }; + + TradeOrderModel model = new TradeOrderModel(); + model.setAccount(settings.getAccountId()); + model.setSymbol(submitOrderReq.contract().symbol()); + model.setSecType(secType); + model.setOrderType(orderType); + model.setAction(actionType); + model.setLimitPrice(orderType == OrderType.MKT ? null : submitOrderReq.price()); + model.setTotalQuantity((long) submitOrderReq.volume()); + model.setSecretKey(settings.getSecretKey()); + log.info("网关[{}] 下单:{}", gatewayId(), model); + TradeOrderResponse response = client.execute(TradeOrderRequest.newRequest(model)); + log.info("网关[{}] 下单反馈:{}", gatewayId(), JSON.toJSONString(response)); + Long id = response.getItem().getId(); + orderIdMap.put(id, submitOrderReq.originOrderId()); + exec.execute(this::doQueryOrderAndTrade); + return id + ""; + } + + @Override + public boolean cancelOrder(String originOrderId) { + if (connState != ConnectionState.CONNECTED) { + throw new IllegalStateException("网关未连线"); + } + for (Entry e : orderIdMap.entrySet()) { + if (StringUtils.isNotEmpty(originOrderId) && StringUtils.equals(originOrderId, e.getValue())) { + Long id = e.getKey(); + OrderParameter params = TradeParamBuilder.instance().account(settings.getAccountId()).id(id).secretKey(settings.getSecretKey()).build(); + String bizContent = JSON.toJSONString(params); + TigerHttpRequest request = new TigerHttpRequest(MethodName.CANCEL_ORDER); + request.setBizContent(bizContent); + log.info("网关[{}] 撤单:{}", gatewayId(), bizContent); + TigerHttpResponse response = client.execute(request); + log.info("网关[{}] 撤单反馈:{}", gatewayId(), JSON.toJSONString(response)); + return response.isSuccess(); + } + } + + return false; + } + + @Override + public GatewayDescription gatewayDescription() { + gd.setConnectionState(getConnectionState()); + return gd; + } + + @Override + public String gatewayId() { + return gd.getGatewayId(); + } } diff --git a/src/main/java/org/dromara/northstar/gateway/tiger/time/CnStockTradeTime.java b/src/main/java/org/dromara/northstar/gateway/tiger/time/CnStockTradeTime.java index f0d53bb7b8350df323659b69adda11f6b6500dc0..2ab73ec140886cae8da1febb5ac2f2e100c6d0f5 100644 --- a/src/main/java/org/dromara/northstar/gateway/tiger/time/CnStockTradeTime.java +++ b/src/main/java/org/dromara/northstar/gateway/tiger/time/CnStockTradeTime.java @@ -1,17 +1,18 @@ package org.dromara.northstar.gateway.tiger.time; +import org.dromara.northstar.common.model.core.TradeTimeDefinition; + import java.time.LocalTime; import java.util.List; -import org.dromara.northstar.gateway.TradeTimeDefinition; -import org.dromara.northstar.gateway.model.PeriodSegment; /** * A股连续交易时段 * @author KevinHuangwl * */ -public class CnStockTradeTime implements TradeTimeDefinition{ +/* +public class CnStockTradeTime implements TradeTimeDefinition { @Override public List tradeTimeSegments() { @@ -21,3 +22,4 @@ public class CnStockTradeTime implements TradeTimeDefinition{ } } +*/ diff --git a/src/main/java/org/dromara/northstar/gateway/tiger/time/HkStockTradeTime.java b/src/main/java/org/dromara/northstar/gateway/tiger/time/HkStockTradeTime.java index d22d43d847a24b6cff551245b0f4157f0cf36060..bbe166f17669697b3202e4e64e2ff70e8460080d 100644 --- a/src/main/java/org/dromara/northstar/gateway/tiger/time/HkStockTradeTime.java +++ b/src/main/java/org/dromara/northstar/gateway/tiger/time/HkStockTradeTime.java @@ -1,17 +1,18 @@ package org.dromara.northstar.gateway.tiger.time; +import org.dromara.northstar.common.model.core.TradeTimeDefinition; + import java.time.LocalTime; import java.util.List; -import org.dromara.northstar.gateway.TradeTimeDefinition; -import org.dromara.northstar.gateway.model.PeriodSegment; /** * 港股连续交易时段 * @author KevinHuangwl * */ -public class HkStockTradeTime implements TradeTimeDefinition{ +/* +public class HkStockTradeTime implements TradeTimeDefinition { @Override public List tradeTimeSegments() { @@ -21,3 +22,4 @@ public class HkStockTradeTime implements TradeTimeDefinition{ } } +*/ diff --git a/src/main/java/org/dromara/northstar/gateway/tiger/time/UsStockTradeTime.java b/src/main/java/org/dromara/northstar/gateway/tiger/time/UsStockTradeTime.java index 8207036fe2948e6a60e5ecebe67a35e4a8ffb92d..6e8ffde80329150ff3dfdd446687c3193d02f2e1 100644 --- a/src/main/java/org/dromara/northstar/gateway/tiger/time/UsStockTradeTime.java +++ b/src/main/java/org/dromara/northstar/gateway/tiger/time/UsStockTradeTime.java @@ -6,15 +6,14 @@ import java.time.LocalTime; import java.time.Month; import java.util.List; -import org.dromara.northstar.gateway.TradeTimeDefinition; -import org.dromara.northstar.gateway.model.PeriodSegment; -import org.dromara.northstar.gateway.time.DateUtils; +import org.dromara.northstar.common.model.core.TradeTimeDefinition; /** * 美股连续交易时段 * @author KevinHuangwl * */ +/* public class UsStockTradeTime implements TradeTimeDefinition { @@ -30,3 +29,4 @@ public class UsStockTradeTime implements TradeTimeDefinition { } } +*/ diff --git a/src/test/java/org/dromara/northstar/gateway/tiger/OrderTradeQueryProxyTest.java b/src/test/java/org/dromara/northstar/gateway/tiger/OrderTradeQueryProxyTest.java index 992ff1b87a89c021c24dd8a555dd73513861837f..77e464a68527eddc6ca3e61d69f329c5e8950476 100644 --- a/src/test/java/org/dromara/northstar/gateway/tiger/OrderTradeQueryProxyTest.java +++ b/src/test/java/org/dromara/northstar/gateway/tiger/OrderTradeQueryProxyTest.java @@ -1,3 +1,4 @@ +/* package org.dromara.northstar.gateway.tiger; import static org.assertj.core.api.Assertions.assertThat; @@ -6,7 +7,7 @@ import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; -import org.dromara.northstar.gateway.Contract; +import org.dromara.northstar.common.model.core.Contract; import org.dromara.northstar.gateway.IContractManager; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -26,12 +27,12 @@ class OrderTradeQueryProxyTest { private TestFieldFactory factory = new TestFieldFactory("testGateway"); private TigerHttpClient client = mock(TigerHttpClient.class); - + @BeforeEach void prepare() { IContractManager contractMgr = mock(IContractManager.class); Contract contract = mock(Contract.class); - when(contract.contractField()).thenReturn(factory.makeContract("rb2205")); + when(contract.toContractField()).thenReturn(factory.makeContract("rb2205")); when(contractMgr.getContract(any(), anyString())).thenReturn(contract); proxy = new OrderTradeQueryProxy(client, contractMgr, "testGateway", "testAccount"); } @@ -74,3 +75,4 @@ class OrderTradeQueryProxyTest { } } +*/