trading-order-admin/src/main/java/com/yami/trading/admin/task/StockTask.java
@@ -79,6 +79,8 @@ } } /** * 加载所有股票数据 */ @@ -111,55 +113,54 @@ .eq("type", Item.indices)); log.info("同步股票 已有数据 {} 获取数据 {}", stockList.size(), list.size()); System.out.println(stockList); List<Item> updateStockList = new ArrayList<>(); for (StockRealTimeBean o : list) { //System.out.println(o); Item indices = indicesList.stream() .filter(x -> x.getSymbol().equals(o.getSymbol())) .findFirst() .orElse(null); if (indices != null) { //指数不添加 continue; } Item item = stockList.stream() .filter(x -> x.getSymbol().equals(o.getSymbol()) && x.getStockCode().equals(o.getId())) .findFirst() .orElse(null); if (item != null) { //已有不添加 continue; } item = indicesList.stream() .filter(x -> x.getSymbol().equals(o.getSymbol())) .findFirst() .orElse(null); if (item != null) { //已有不添加 continue; } if (item == null) { item = new Item(); String name = StringUtils.trim(o.getName()); item.setEnName(name); item.setName(name); item.setSymbolFullName(name); item.setSymbol(o.getSymbol()); item.setSymbolData(o.getSymbol()); item.setPips(BigDecimal.valueOf(0.01).doubleValue()); item.setPipsAmount(BigDecimal.valueOf(0.02).doubleValue()); item.setAdjustmentValue(BigDecimal.ZERO.doubleValue()); item.setUnitAmount(BigDecimal.valueOf(1000).doubleValue()); item.setUnitFee(BigDecimal.valueOf(30).doubleValue()); item.setMarket("FOREVER"); item.setDecimals(2); item.setMultiple(BigDecimal.ZERO.doubleValue()); item.setBorrowingRate(BigDecimal.ZERO.doubleValue()); item.setDelFlag(0); item.setType(Item.US_STOCKS); item.setCategory(Item.US_STOCKS); item.setSorted("100"); item.setOpenCloseType(Item.US_STOCKS); item.setFake("0"); item.setShowStatus("1"); item.setTradeStatus("1"); item.setQuoteCurrency("USDT"); item.setCrawlStatus("default_active"); item.setStockCode(o.getId()); item = new Item(); String name = StringUtils.trim(o.getName()); item.setEnName(name); item.setName(name); item.setSymbolFullName(name); item.setSymbol(o.getSymbol()); item.setSymbolData(o.getSymbol()); item.setPips(BigDecimal.valueOf(0.01).doubleValue()); item.setPipsAmount(BigDecimal.valueOf(0.02).doubleValue()); item.setAdjustmentValue(BigDecimal.ZERO.doubleValue()); item.setUnitAmount(BigDecimal.valueOf(1000).doubleValue()); item.setUnitFee(BigDecimal.valueOf(30).doubleValue()); item.setMarket("FOREVER"); item.setDecimals(2); item.setMultiple(BigDecimal.ZERO.doubleValue()); item.setBorrowingRate(BigDecimal.ZERO.doubleValue()); item.setDelFlag(0); item.setType(Item.US_STOCKS); item.setCategory(Item.US_STOCKS); item.setSorted("100"); item.setOpenCloseType(Item.US_STOCKS); item.setFake("0"); item.setShowStatus("1"); item.setTradeStatus("1"); item.setQuoteCurrency("USDT"); item.setCrawlStatus("default_active"); item.setStockCode(o.getId()); updateStockList.add(item); updateStockList.add(item); } Realtime realtime = new Realtime(); @@ -191,6 +192,10 @@ } } public static void main(String[] args) { StockTask task = new StockTask(); task.loadAllStock(EStockType.US); trading-order-admin/src/main/java/com/yami/trading/api/controller/KlineController.java
@@ -8,6 +8,7 @@ import com.yami.trading.common.web.ResultObject; import com.yami.trading.huobi.constants.KlinePeriodEnum; import com.yami.trading.huobi.data.TimeZoneConverterService; import com.yami.trading.huobi.data.internal.KlineService; import com.yami.trading.huobi.tradingview.service.TradingViewService; import com.yami.trading.service.data.DataService; import com.yami.trading.service.item.ItemService; @@ -50,6 +51,9 @@ @Autowired RedisTemplate redisTemplate; @Autowired KlineService klineService; @ApiOperation(value = "行情") @GetMapping("/api/hobi!getKline.action") @@ -62,13 +66,21 @@ // Fetch item details based on symbol Item bySymbol = itemService.findBySymbol(symbol); List<Kline> data; // Handle non-cryptos separately if (!Item.cryptos.equals(bySymbol.getType())) { return getKlineForNonCryptos(bySymbol, line, symbol); if (bySymbol.getType().equalsIgnoreCase(Item.US_STOCKS)) { data = klineService.getKData(bySymbol, line); formatKlineTimestamps(data, line); return Result.succeed(this.build(data, line, symbol)); } else { return getKlineForNonCryptos(bySymbol, line, symbol); } } data = this.dataService.kline(symbol, line); // Fetch Kline data from service (for cryptos) List<Kline> data = this.dataService.kline(symbol, line); // Return an empty response if no data is found if (Objects.isNull(data)) { trading-order-huobi/src/main/java/com/yami/trading/huobi/data/internal/KlineService.java
@@ -2,6 +2,7 @@ import com.yami.trading.bean.data.domain.Kline; import com.yami.trading.bean.data.domain.Realtime; import com.yami.trading.bean.item.domain.Item; import java.util.List; import java.util.Map; @@ -115,4 +116,8 @@ public List<Kline> calculateKline(String symbol, int seq, String period, List<Kline> klineList) ; } List<Kline> getKData(Item item, String interval); } trading-order-huobi/src/main/java/com/yami/trading/huobi/data/internal/KlineServiceImpl.java
@@ -1,13 +1,19 @@ package com.yami.trading.huobi.data.internal; import cn.hutool.http.HttpUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.google.common.collect.Lists; import com.google.gson.Gson; import com.google.gson.reflect.TypeToken; import com.yami.trading.bean.data.domain.Kline; import com.yami.trading.bean.data.domain.Realtime; import com.yami.trading.bean.item.domain.Item; import com.yami.trading.common.config.RequestDataHelper; import com.yami.trading.common.constants.Constants; import com.yami.trading.common.domain.Result; import com.yami.trading.common.exception.YamiShopBindException; import com.yami.trading.common.util.DateUtils; import com.yami.trading.huobi.websocket.constant.enums.EStockType; import com.yami.trading.service.MarketOpenChecker; import com.yami.trading.common.util.ThreadUtils; import com.yami.trading.common.util.UTCDateUtils; @@ -16,6 +22,7 @@ import com.yami.trading.service.data.RealtimeService; import com.yami.trading.service.item.ItemService; import com.yami.trading.service.syspara.SysparaService; import lombok.Data; import org.jetbrains.annotations.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -952,4 +959,49 @@ } return result; } @Data class kData { long t; String c; String o; String h; String l; String v; String vo; } @Override public List<Kline> getKData(Item item, String interval) { try { EStockType eStockType = EStockType.US; Object object = HttpUtil.get(eStockType.stockUrl + "kline?pid=" + item.getStockCode() + "&interval=" + interval + "&key=" + eStockType.stockKey); Gson gson = new Gson(); List<kData> dataList = gson.fromJson(object.toString(), new TypeToken<List<kData>>(){}.getType()); logger.info("{}获取{}条K线" , item.getSymbol() ,dataList.size()); Realtime realtime = DataCache.getRealtime(item.getSymbol()); // 修改 List 中的最后一条数据 kData lastData = dataList.get(dataList.size() - 1); lastData.setC(String.valueOf(realtime.getClose())); lastData.setO(String.valueOf(realtime.getOpen())); lastData.setH(String.valueOf(realtime.getHigh())); lastData.setL(String.valueOf(realtime.getLow())); List<Kline> list = new ArrayList<>(); for (int i = 0; i < dataList.size(); i++) { kData kData = dataList.get(i); Kline kline = new Kline(); kline.setTs(Long.valueOf(kData.getT() + "000")); kline.setClose(Double.valueOf(kData.getC())); kline.setOpen(Double.valueOf(kData.getO())); kline.setHigh(Double.valueOf(kData.getH())); kline.setLow(Double.valueOf(kData.getL())); list.add(kline); } return list; } catch (Exception e) { logger.error("getKData error", e); } return null; } } trading-order-huobi/src/main/java/com/yami/trading/huobi/jsws/WebSocketClientBeanConfig.java
@@ -4,6 +4,8 @@ import com.yami.trading.huobi.websocket.constant.enums.EStockType; import lombok.extern.slf4j.Slf4j; import org.java_websocket.client.WebSocketClient; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.stereotype.Component; @@ -15,13 +17,22 @@ @Component public class WebSocketClientBeanConfig { // 注入Spring上下文,用于获取原型Bean @Autowired private ApplicationContext applicationContext; @Bean public Map<String, WebSocketClient> websocketRunClientMap() { Map<String, WebSocketClient> retMap = new HashMap<>(1); try { WebsocketRunClient websocketRunClient = new WebsocketRunClient(new URI(EStockType.US.getWsUrl()), EStockType.US); // 关键:通过Spring上下文获取WebsocketRunClient实例(而非直接new) // 传入参数创建实例(需配合原型作用域) WebsocketRunClient websocketRunClient = applicationContext.getBean( WebsocketRunClient.class, new URI(EStockType.US.getWsUrl()), // 第一个参数:serverUri EStockType.US // 第二个参数:eStockType ); websocketRunClient.connect(); websocketRunClient.setConnectionLostTimeout(0); startHeartbeatThread(websocketRunClient); trading-order-huobi/src/main/java/com/yami/trading/huobi/jsws/WebsocketRunClient.java
@@ -12,6 +12,8 @@ import org.java_websocket.client.WebSocketClient; import org.java_websocket.handshake.ServerHandshake; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Component; import java.lang.reflect.Type; import java.math.BigDecimal; @@ -20,6 +22,8 @@ import java.util.*; @Slf4j @Component @Scope("prototype") public class WebsocketRunClient extends WebSocketClient { private EStockType eStockType; @@ -27,16 +31,20 @@ @Autowired ItemService itemService; public WebsocketRunClient() { super(URI.create("wss://ws.jinmanxuan.com/websocket-server")); } public WebsocketRunClient(URI serverUri, EStockType eStockType) { // 修改为新的WebSocket服务器地址 super(URI.create("wss://usws.yanshiz.com/websocket-server")); super(URI.create("wss://ws.jinmanxuan.com/websocket-server")); this.eStockType = eStockType; } @Override public void onOpen(ServerHandshake serverHandshake) { log.info("WebSocket连接已建立,连接到: wss://usws.yanshiz.com/websocket-server"); log.info("WebSocket连接已建立,连接到: wss://ws.jinmanxuan.com/websocket-server"); // 发送身份验证消息 send(("key:"+ eStockType.getStockKey()+":"+eStockType.getContryId()).getBytes()); Timer heartbeatTimer; @@ -58,10 +66,9 @@ if(!s.equals("pong") && !s.equals("身份验证成功")){ try { Map<String, String> stringObjectMap = jsonToMap(s); String symbol = stringObjectMap.get("symbol"); Item item = itemService.findBySymbol(symbol); if (!item.getType().equalsIgnoreCase(Item.US_STOCKS)) { String symbol = stringObjectMap.get("symbol").trim(); Item item = itemService.findCaCheBySymbol(symbol); if (item == null || !item.getType().equalsIgnoreCase(Item.US_STOCKS)) { return; } Realtime realtime = new Realtime(); trading-order-service/src/main/java/com/yami/trading/service/item/ItemService.java
@@ -165,6 +165,24 @@ } /** * 通过 symbol 找去缓存对象 * * @param symbol * @return */ @Transactional(propagation = Propagation.NOT_SUPPORTED) public Item findCaCheBySymbol(String symbol) { Item item; if (CollectionUtil.isNotEmpty(symbolItem)) { item = symbolItem.get(symbol); if (item != null) { return item; } } return null; } /** * 基于 remarks 字段去缓存提取 item 对象,特性是基于 remarks 映射 symbol,从缓存中提取 item; * 如果缓存中没有数据,则直接去数据库查询返回。 *