refactor(okxNewPrice): 账户配置
| | |
| | | private final PnLPriceMode unrealizedPnlPriceMode; |
| | | private final BigDecimal maxPosSize; |
| | | private BigDecimal step; |
| | | private String instIdCode; |
| | | |
| | | private OkxConfig(Builder builder) { |
| | | this.apiKey = builder.apiKey; |
| | |
| | | public BigDecimal getStep() { return step; } |
| | | public void setStep(BigDecimal step) { this.step = step; } |
| | | |
| | | public String getInstIdCode() { return instIdCode; } |
| | | public void setInstIdCode(String instIdCode) { this.instIdCode = instIdCode; } |
| | | |
| | | // ==================== 环境 ==================== |
| | | |
| | | public boolean isProduction() { return isProduction; } |
| | |
| | | public OkxGridTradeService(OkxConfig config, String accountName) { |
| | | this.config = config; |
| | | this.accountName = accountName; |
| | | this.executor = new OkxTradeExecutor(config.getContract(), config.getMarginMode(), accountName); |
| | | this.executor = new OkxTradeExecutor(config.getContract(), config.getMarginMode(), accountName, config.getInstIdCode()); |
| | | } |
| | | |
| | | public void setWebSocketClient(WebSocketClient wsClient) { |
| | |
| | | return isSuccess(result, "设置杠杆"); |
| | | } |
| | | |
| | | public String fetchInstIdCode(String instType, String instId) { |
| | | String path = "/api/v5/account/instruments?instType=" + instType + "&instId=" + instId; |
| | | JSONObject result = get(path); |
| | | if (result == null || !"0".equals(result.getString("code"))) { |
| | | log.error("[REST] 获取instIdCode失败, code:{}, msg:{}", |
| | | result != null ? result.getString("code") : "null", |
| | | result != null ? result.getString("msg") : "no response"); |
| | | return null; |
| | | } |
| | | com.alibaba.fastjson.JSONArray data = result.getJSONArray("data"); |
| | | if (data == null || data.isEmpty()) { |
| | | log.error("[REST] 获取instIdCode失败: data为空"); |
| | | return null; |
| | | } |
| | | JSONObject first = data.getJSONObject(0); |
| | | String instIdCode = first.getString("instIdCode"); |
| | | log.info("[REST] 获取instIdCode成功, instId:{}, instIdCode:{}", instId, instIdCode); |
| | | return instIdCode; |
| | | } |
| | | |
| | | private JSONObject get(String path) { |
| | | HttpURLConnection conn = null; |
| | | try { |
| | | String timestamp = OkxWsUtil.getIso8601Timestamp(); |
| | | String sign = OkxWsUtil.signRest(timestamp, "GET", path, "", secretKey); |
| | | |
| | | URL url = new URL(baseUrl + path); |
| | | conn = (HttpURLConnection) url.openConnection(); |
| | | conn.setRequestMethod("GET"); |
| | | conn.setConnectTimeout(15000); |
| | | conn.setReadTimeout(15000); |
| | | conn.setRequestProperty("Content-Type", "application/json"); |
| | | conn.setRequestProperty("OK-ACCESS-KEY", apiKey); |
| | | conn.setRequestProperty("OK-ACCESS-SIGN", sign); |
| | | conn.setRequestProperty("OK-ACCESS-TIMESTAMP", timestamp); |
| | | conn.setRequestProperty("OK-ACCESS-PASSPHRASE", passphrase); |
| | | if (isSimulate) { |
| | | conn.setRequestProperty("x-simulated-trading", "1"); |
| | | } |
| | | |
| | | int code = conn.getResponseCode(); |
| | | String response = readResponse(conn); |
| | | log.info("[REST] GET {} → HTTP {} body:{}", path, code, response); |
| | | |
| | | return JSON.parseObject(response); |
| | | } catch (Exception e) { |
| | | log.error("[REST] GET {} 失败: {}", path, e.getMessage()); |
| | | return null; |
| | | } finally { |
| | | if (conn != null) { |
| | | conn.disconnect(); |
| | | } |
| | | } |
| | | } |
| | | |
| | | private JSONObject post(String path, JSONObject body) { |
| | | HttpURLConnection conn = null; |
| | | try { |
| | |
| | | private final String contract; |
| | | private final String marginMode; |
| | | private final String accountName; |
| | | private final String instIdCode; |
| | | |
| | | private volatile WebSocketClient wsClient; |
| | | |
| | | private final ExecutorService executor; |
| | | |
| | | public OkxTradeExecutor(String contract, String marginMode, String accountName) { |
| | | public OkxTradeExecutor(String contract, String marginMode, String accountName, String instIdCode) { |
| | | this.contract = contract; |
| | | this.marginMode = marginMode; |
| | | this.accountName = accountName; |
| | | this.instIdCode = instIdCode; |
| | | this.executor = new ThreadPoolExecutor( |
| | | 1, 1, |
| | | 60L, TimeUnit.SECONDS, |
| | |
| | | |
| | | JSONArray argsArray = new JSONArray(); |
| | | JSONObject args = new JSONObject(); |
| | | args.put("instId", contract); |
| | | args.put("instIdCode", instIdCode); |
| | | args.put("tdMode", marginMode); |
| | | args.put("side", side); |
| | | args.put("posSide", posSide); |
| | |
| | | } |
| | | JSONArray argsArray = new JSONArray(); |
| | | JSONObject args = new JSONObject(); |
| | | args.put("instId", contract); |
| | | args.put("instIdCode", instIdCode); |
| | | args.put("algoOrdType", "conditional"); |
| | | argsArray.add(args); |
| | | |
| | |
| | | |
| | | private JSONObject buildOrderArgs(TradeRequestParam param) { |
| | | JSONObject args = new JSONObject(); |
| | | args.put("instId", param.getInstId()); |
| | | args.put("instIdCode", instIdCode); |
| | | args.put("tdMode", param.getTdMode()); |
| | | args.put("clOrdId", param.getClOrdId()); |
| | | args.put("side", param.getSide()); |
| | |
| | | |
| | | String connId = OkxWsUtil.getOrderNum("order"); |
| | | JSONObject msg = OkxWsUtil.buildJsonObject(connId, "order", argsArray); |
| | | wsClient.send(msg.toJSONString()); |
| | | log.info("[TradeExec] 发送下单: side={}, sz={}", param.getSide(), param.getSz()); |
| | | String msgStr = msg.toJSONString(); |
| | | log.info("[TradeExec] 发送下单: {}", msgStr); |
| | | wsClient.send(msgStr); |
| | | log.info("[TradeExec] 下单已发送: side={}, sz={}, instId={}", param.getSide(), param.getSz(), param.getInstId()); |
| | | } |
| | | |
| | | private void sendBatchOrders(List<TradeRequestParam> params) { |
| | |
| | | log.error("[管理器] 设置杠杆倍数失败,策略可能无法正常运作"); |
| | | } |
| | | |
| | | log.info("[管理器] 账户配置完成, posMode:{}, leverage:{}, marginMode:{}", |
| | | config.getPosMode(), config.getLeverage(), config.getMarginMode()); |
| | | String instIdCode = restClient.fetchInstIdCode("SWAP", config.getContract()); |
| | | if (instIdCode != null) { |
| | | config.setInstIdCode(instIdCode); |
| | | } else { |
| | | log.error("[管理器] 获取instIdCode失败,WS下单将无法正常工作"); |
| | | } |
| | | |
| | | log.info("[管理器] 账户配置完成, posMode:{}, leverage:{}, marginMode:{}, instIdCode:{}", |
| | | config.getPosMode(), config.getLeverage(), config.getMarginMode(), config.getInstIdCode()); |
| | | } |
| | | |
| | | public OkxKlineWebSocketClient getKlineWebSocketClient() { return wsKlineClient; } |
| | |
| | | if (details != null) { |
| | | for (int j = 0; j < details.size(); j++) { |
| | | JSONObject detail = details.getJSONObject(j); |
| | | log.info("[{}] 币种:{}, 可用余额:{}, 现金余额:{}, 权益:{}", |
| | | if ("USDT".equals(detail.getString("ccy"))) { |
| | | log.info("[{}] USDT可用余额:{}, 权益:{}", |
| | | CHANNEL_NAME, |
| | | detail.get("ccy"), detail.get("availBal"), |
| | | detail.get("cashBal"), detail.get("eq")); |
| | | detail.get("availBal"), detail.get("eq")); |
| | | } |
| | | } |
| | | } |
| | | } |