1 line
205 KiB
JSON
1 line
205 KiB
JSON
{"repo": "bliplink/com.app.dc.indsvr", "files": {"src/test/java/com/app/dc/indsvr/TestDeepSeekMultiTurn.java": {"c7be7ec53ddd7a857655e745e12841f637520d01": {"t": 1785747158.095059, "n": 6718, "h": "e949a64e9cd1c9e3", "c": "package com.app.dc.indsvr;\n\nimport com.app.common.utils.JsonUtils;\nimport com.app.dc.fix.message.MarketDataSnapshotFullRefresh;\nimport com.app.dc.po.deepseek.DeepSeekJsonParser;\nimport com.app.dc.po.deepseek.TDeepSeekChoice;\nimport com.app.dc.po.deepseek.TDeepSeekResponse;\nimport com.app.dc.po.kline.Candle;\nimport com.app.dc.service.utils.RestTemplateConfig;\nimport org.springframework.http.HttpHeaders;\nimport org.ta4j.core.Bar;\nimport org.ta4j.core.BaseBar;\n//import org.springframework.http.HttpEntity;\n//import org.springframework.http.HttpHeaders;\n//import org.springframework.http.MediaType;\n\nimport java.io.IOException;\nimport java.math.BigDecimal;\nimport java.nio.charset.StandardCharsets;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.time.*;\nimport java.time.format.DateTimeFormatter;\nimport java.util.*;\n\npublic class TestDeepSeekMultiTurn {\n\n String apiKey = \"sk-a4251656f2f748cd8b536ee8372ca7d2\";\n String baseUrl = \"https://api.deepseek.com/chat/completions\";\n private static final DateTimeFormatter YYYYMMDD_FORMATTER =\n DateTimeFormatter.ofPattern(\"yyyyMMdd\");\n\n // 保存对话历史的成员变量\n private List<Map<String, String>> chatHistory = new ArrayList<>();\n private boolean isFirstMessage = true;\n\n public void TestAPI() throws IOException {\n RestTemplateConfig restTemplateConfig = new RestTemplateConfig();\n restTemplateConfig.restTemplate();\n\n Map<String, Object> requestMap = new HashMap<>();\n requestMap.put(\"model\", \"deepseek-chat\");\n requestMap.put(\"stream\", false);\n requestMap.put(\"max_tokens\", 2000);\n requestMap.put(\"temperature\", 0.1);\n requestMap.put(\"response_format\", Map.of(\"type\", \"json_object\"));\n\n // ============ 关键变化:消息构建 ============\n if (isFirstMessage) {\n // 第一轮:初始化对话\n chatHistory.clear();\n\n // 系统消息(只在第一轮发送)\n chatHistory.add(Map.of(\n \"role\", \"system\",\n \"content\", \"角色:B圈ETH艾略特波浪理论和震荡首席分析师,请用JSON格式回答。\"\n ));\n\n // 用户第一轮问题\n String klineJsonArray = getKLineData();\n String firstQuestion = \"\";//buildFirstQuestion(klineJsonArray);\n chatHistory.add(Map.of(\"role\", \"user\", \"content\", firstQuestion));\n\n isFirstMessage = false;\n }\n // 后续轮次:chatHistory已经包含了所有历史对话\n // 只需要在外部调用时,使用addUserMessage()方法添加新问题\n\n // ============ 发送请求 ============\n requestMap.put(\"messages\", chatHistory);\n String requestJson = JsonUtils.Serializer(requestMap);\n\n HttpHeaders headers = new HttpHeaders();\n headers.setContentType(org.springframework.http.MediaType.APPLICATION_JSON);\n headers.set(\"Authorization\", \"Bearer \" + apiKey);\n\n String response = restTemplateConfig.post(baseUrl, requestJson, headers);\n\n // ============ 处理响应并保存到历史 ============\n try {\n TDeepSeekResponse deepSeekResponse = DeepSeekJsonParser.parseFullResponse(response);\n TDeepSeekChoice deepSeekChoice = deepSeekResponse.choices[0];\n String aiResponse = deepSeekChoice.message.content;\n\n System.out.println(\"AI回复: \" + aiResponse);\n\n // 重要:将AI回复添加到历史中\n chatHistory.add(Map.of(\n \"role\", \"assistant\",\n \"content\", aiResponse\n ));\n\n } catch (Exception e) {\n System.err.println(\"解析响应失败: \" + e.getMessage());\n }\n }\n\n /**\n * 添加用户追问(多轮对话的核心)\n */\n public void addUserMessage(String userQuestion) {\n chatHistory.add(Map.of(\n \"role\", \"user\",\n \"content\", userQuestion\n ));\n }\n\n /**\n * 获取当前对话历史(用于调试)\n */\n public void printChatHistory() {\n System.out.println(\"=== 对话历史 ===\");\n for (int i = 0; i < chatHistory.size(); i++) {\n Map<String, String> msg = chatHistory.get(i);\n System.out.println((i+1) + \". [\" + msg.get(\"role\") + \"] \" +\n msg.get(\"content\").substring(0, Math.min(100, msg.get(\"content\").length())) + \"...\");\n }\n }\n\n /**\n * 重置对话\n */\n public void resetConversation() {\n chatHistory.clear();\n isFirstMessage = true;\n System.out.println(\"对话已重置\");\n }\n\n\n private String getKLineData() throws IOException {\n\n String symbol = \"ETHUSDT\";\n String text = \"1d\";\n\n // 1. 读取本地 1d K 线文件\n Path path = Path.of(\"config\", symbol + \"-1d.txt\");\n String priceContent = Files.readString(path, StandardCharsets.UTF_8);\n\n KineData kineData = JsonUtils.Deserialize(priceContent, KineData.class);\n\n // 反序列化为行情列表(文件里是倒序的也没关系,我们后面统一按时间升序排序)\n List<MarketDataSnapshotFullRefresh> rawList = kineData.getData();\n// JsonUtils.Deserialize2(priceContent, MarketDataSnapshotFullRefresh.class);\n\n System.out.println(\"Kline size:\" + rawList.size());\n\n // 2. 按时间升序排序,保证 index 越大时间越晚\n rawList.sort(Comparator.comparingLong(m -> Long.parseLong(m.getInfo1())));\n\n List<Candle> fullCandles = new ArrayList<>();\n List<ZonedDateTime> barTimes = new ArrayList<>();\n\n for (MarketDataSnapshotFullRefresh mdsf : rawList) {\n\n // info1 是时间戳(毫秒)\n ZonedDateTime time = ZonedDateTime.ofInstant(\n Instant.ofEpochMilli(Long.parseLong(mdsf.getInfo1())),\n ZoneId.systemDefault()\n );\n\n BigDecimal open = new BigDecimal(mdsf.getOpenPrice());\n BigDecimal high = new BigDecimal(mdsf.getHighPrice());\n BigDecimal low = new BigDecimal(mdsf.getLowPrice());\n BigDecimal close = new BigDecimal(mdsf.getClosePrice());\n BigDecimal volume = new BigDecimal(mdsf.getVolume());\n\n // info3 是周期,例如 \"1d\" => \"PT1D\"\n String temTxt = \"PT\" + mdsf.getInfo3().toUpperCase();\n\n String interval = mdsf.getInfo3().toUpperCase();\n\n long days = Long.parseLong(interval.substring(0, interval.length() - 1));\n Duration barDuration = Duration.ofDays(days);\n\n// Bar newBar = new BaseBar(Duration.parse(temTxt), time, open, high, low, close, volume);\n Bar newBar = new BaseBar(barDuration, time, open, high, low, close, volume);\n\n Candle candle = new Candle(\n open.doubleValue(),\n high.doubleValue(),\n low.doubleValue(),\n close.doubleValue(),\n volume.doubleValue(),\n time.format(YYYYMMDD_FORMATTER)\n );\n fullCandles.add(candle);\n }\n\n return JsonUtils.Serializer(fullCandles);\n }\n\n}"}, "a20b36a75988e5d11ec3d0e532170b5441e488db": {"t": 1785770758.2201953, "n": 6718, "h": "e949a64e9cd1c9e3", "c": "package com.app.dc.indsvr;\n\nimport com.app.common.utils.JsonUtils;\nimport com.app.dc.fix.message.MarketDataSnapshotFullRefresh;\nimport com.app.dc.po.deepseek.DeepSeekJsonParser;\nimport com.app.dc.po.deepseek.TDeepSeekChoice;\nimport com.app.dc.po.deepseek.TDeepSeekResponse;\nimport com.app.dc.po.kline.Candle;\nimport com.app.dc.service.utils.RestTemplateConfig;\nimport org.springframework.http.HttpHeaders;\nimport org.ta4j.core.Bar;\nimport org.ta4j.core.BaseBar;\n//import org.springframework.http.HttpEntity;\n//import org.springframework.http.HttpHeaders;\n//import org.springframework.http.MediaType;\n\nimport java.io.IOException;\nimport java.math.BigDecimal;\nimport java.nio.charset.StandardCharsets;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.time.*;\nimport java.time.format.DateTimeFormatter;\nimport java.util.*;\n\npublic class TestDeepSeekMultiTurn {\n\n String apiKey = \"sk-a4251656f2f748cd8b536ee8372ca7d2\";\n String baseUrl = \"https://api.deepseek.com/chat/completions\";\n private static final DateTimeFormatter YYYYMMDD_FORMATTER =\n DateTimeFormatter.ofPattern(\"yyyyMMdd\");\n\n // 保存对话历史的成员变量\n private List<Map<String, String>> chatHistory = new ArrayList<>();\n private boolean isFirstMessage = true;\n\n public void TestAPI() throws IOException {\n RestTemplateConfig restTemplateConfig = new RestTemplateConfig();\n restTemplateConfig.restTemplate();\n\n Map<String, Object> requestMap = new HashMap<>();\n requestMap.put(\"model\", \"deepseek-chat\");\n requestMap.put(\"stream\", false);\n requestMap.put(\"max_tokens\", 2000);\n requestMap.put(\"temperature\", 0.1);\n requestMap.put(\"response_format\", Map.of(\"type\", \"json_object\"));\n\n // ============ 关键变化:消息构建 ============\n if (isFirstMessage) {\n // 第一轮:初始化对话\n chatHistory.clear();\n\n // 系统消息(只在第一轮发送)\n chatHistory.add(Map.of(\n \"role\", \"system\",\n \"content\", \"角色:B圈ETH艾略特波浪理论和震荡首席分析师,请用JSON格式回答。\"\n ));\n\n // 用户第一轮问题\n String klineJsonArray = getKLineData();\n String firstQuestion = \"\";//buildFirstQuestion(klineJsonArray);\n chatHistory.add(Map.of(\"role\", \"user\", \"content\", firstQuestion));\n\n isFirstMessage = false;\n }\n // 后续轮次:chatHistory已经包含了所有历史对话\n // 只需要在外部调用时,使用addUserMessage()方法添加新问题\n\n // ============ 发送请求 ============\n requestMap.put(\"messages\", chatHistory);\n String requestJson = JsonUtils.Serializer(requestMap);\n\n HttpHeaders headers = new HttpHeaders();\n headers.setContentType(org.springframework.http.MediaType.APPLICATION_JSON);\n headers.set(\"Authorization\", \"Bearer \" + apiKey);\n\n String response = restTemplateConfig.post(baseUrl, requestJson, headers);\n\n // ============ 处理响应并保存到历史 ============\n try {\n TDeepSeekResponse deepSeekResponse = DeepSeekJsonParser.parseFullResponse(response);\n TDeepSeekChoice deepSeekChoice = deepSeekResponse.choices[0];\n String aiResponse = deepSeekChoice.message.content;\n\n System.out.println(\"AI回复: \" + aiResponse);\n\n // 重要:将AI回复添加到历史中\n chatHistory.add(Map.of(\n \"role\", \"assistant\",\n \"content\", aiResponse\n ));\n\n } catch (Exception e) {\n System.err.println(\"解析响应失败: \" + e.getMessage());\n }\n }\n\n /**\n * 添加用户追问(多轮对话的核心)\n */\n public void addUserMessage(String userQuestion) {\n chatHistory.add(Map.of(\n \"role\", \"user\",\n \"content\", userQuestion\n ));\n }\n\n /**\n * 获取当前对话历史(用于调试)\n */\n public void printChatHistory() {\n System.out.println(\"=== 对话历史 ===\");\n for (int i = 0; i < chatHistory.size(); i++) {\n Map<String, String> msg = chatHistory.get(i);\n System.out.println((i+1) + \". [\" + msg.get(\"role\") + \"] \" +\n msg.get(\"content\").substring(0, Math.min(100, msg.get(\"content\").length())) + \"...\");\n }\n }\n\n /**\n * 重置对话\n */\n public void resetConversation() {\n chatHistory.clear();\n isFirstMessage = true;\n System.out.println(\"对话已重置\");\n }\n\n\n private String getKLineData() throws IOException {\n\n String symbol = \"ETHUSDT\";\n String text = \"1d\";\n\n // 1. 读取本地 1d K 线文件\n Path path = Path.of(\"config\", symbol + \"-1d.txt\");\n String priceContent = Files.readString(path, StandardCharsets.UTF_8);\n\n KineData kineData = JsonUtils.Deserialize(priceContent, KineData.class);\n\n // 反序列化为行情列表(文件里是倒序的也没关系,我们后面统一按时间升序排序)\n List<MarketDataSnapshotFullRefresh> rawList = kineData.getData();\n// JsonUtils.Deserialize2(priceContent, MarketDataSnapshotFullRefresh.class);\n\n System.out.println(\"Kline size:\" + rawList.size());\n\n // 2. 按时间升序排序,保证 index 越大时间越晚\n rawList.sort(Comparator.comparingLong(m -> Long.parseLong(m.getInfo1())));\n\n List<Candle> fullCandles = new ArrayList<>();\n List<ZonedDateTime> barTimes = new ArrayList<>();\n\n for (MarketDataSnapshotFullRefresh mdsf : rawList) {\n\n // info1 是时间戳(毫秒)\n ZonedDateTime time = ZonedDateTime.ofInstant(\n Instant.ofEpochMilli(Long.parseLong(mdsf.getInfo1())),\n ZoneId.systemDefault()\n );\n\n BigDecimal open = new BigDecimal(mdsf.getOpenPrice());\n BigDecimal high = new BigDecimal(mdsf.getHighPrice());\n BigDecimal low = new BigDecimal(mdsf.getLowPrice());\n BigDecimal close = new BigDecimal(mdsf.getClosePrice());\n BigDecimal volume = new BigDecimal(mdsf.getVolume());\n\n // info3 是周期,例如 \"1d\" => \"PT1D\"\n String temTxt = \"PT\" + mdsf.getInfo3().toUpperCase();\n\n String interval = mdsf.getInfo3().toUpperCase();\n\n long days = Long.parseLong(interval.substring(0, interval.length() - 1));\n Duration barDuration = Duration.ofDays(days);\n\n// Bar newBar = new BaseBar(Duration.parse(temTxt), time, open, high, low, close, volume);\n Bar newBar = new BaseBar(barDuration, time, open, high, low, close, volume);\n\n Candle candle = new Candle(\n open.doubleValue(),\n high.doubleValue(),\n low.doubleValue(),\n close.doubleValue(),\n volume.doubleValue(),\n time.format(YYYYMMDD_FORMATTER)\n );\n fullCandles.add(candle);\n }\n\n return JsonUtils.Serializer(fullCandles);\n }\n\n}"}, "676607fe31eb901f505e26e27021885bcba816db": {"t": 1785770759.8674257, "n": 6939, "h": "da947cad8b9c89c0", "c": "package com.app.dc.indsvr;\n\nimport com.app.common.utils.JsonUtils;\nimport com.app.dc.fix.message.MarketDataSnapshotFullRefresh;\nimport com.app.dc.po.deepseek.DeepSeekJsonParser;\nimport com.app.dc.po.deepseek.TDeepSeekChoice;\nimport com.app.dc.po.deepseek.TDeepSeekResponse;\nimport com.app.dc.po.deepseek.TWaveAnalysisResult;\nimport com.app.dc.service.signal.day.Candle;\nimport com.app.dc.service.utils.RestTemplateConfig;\nimport okhttp3.*;\nimport org.junit.Test;\nimport org.springframework.http.HttpEntity;\nimport org.springframework.http.HttpHeaders;\nimport org.ta4j.core.Bar;\nimport org.ta4j.core.BarSeries;\nimport org.ta4j.core.BaseBar;\nimport org.ta4j.core.BaseBarSeriesBuilder;\n//import org.springframework.http.HttpEntity;\n//import org.springframework.http.HttpHeaders;\n//import org.springframework.http.MediaType;\n\nimport java.io.IOException;\nimport java.math.BigDecimal;\nimport java.nio.charset.StandardCharsets;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.time.*;\nimport java.time.format.DateTimeFormatter;\nimport java.util.*;\n\npublic class TestDeepSeekMultiTurn {\n\n String apiKey = \"sk-a4251656f2f748cd8b536ee8372ca7d2\";\n String baseUrl = \"https://api.deepseek.com/chat/completions\";\n private static final DateTimeFormatter YYYYMMDD_FORMATTER =\n DateTimeFormatter.ofPattern(\"yyyyMMdd\");\n\n // 保存对话历史的成员变量\n private List<Map<String, String>> chatHistory = new ArrayList<>();\n private boolean isFirstMessage = true;\n\n public void TestAPI() throws IOException {\n RestTemplateConfig restTemplateConfig = new RestTemplateConfig();\n restTemplateConfig.restTemplate();\n\n Map<String, Object> requestMap = new HashMap<>();\n requestMap.put(\"model\", \"deepseek-chat\");\n requestMap.put(\"stream\", false);\n requestMap.put(\"max_tokens\", 2000);\n requestMap.put(\"temperature\", 0.1);\n requestMap.put(\"response_format\", Map.of(\"type\", \"json_object\"));\n\n // ============ 关键变化:消息构建 ============\n if (isFirstMessage) {\n // 第一轮:初始化对话\n chatHistory.clear();\n\n // 系统消息(只在第一轮发送)\n chatHistory.add(Map.of(\n \"role\", \"system\",\n \"content\", \"角色:B圈ETH艾略特波浪理论和震荡首席分析师,请用JSON格式回答。\"\n ));\n\n // 用户第一轮问题\n String klineJsonArray = getKLineData();\n String firstQuestion = \"\";//buildFirstQuestion(klineJsonArray);\n chatHistory.add(Map.of(\"role\", \"user\", \"content\", firstQuestion));\n\n isFirstMessage = false;\n }\n // 后续轮次:chatHistory已经包含了所有历史对话\n // 只需要在外部调用时,使用addUserMessage()方法添加新问题\n\n // ============ 发送请求 ============\n requestMap.put(\"messages\", chatHistory);\n String requestJson = JsonUtils.Serializer(requestMap);\n\n HttpHeaders headers = new HttpHeaders();\n headers.setContentType(org.springframework.http.MediaType.APPLICATION_JSON);\n headers.set(\"Authorization\", \"Bearer \" + apiKey);\n\n String response = restTemplateConfig.post(baseUrl, requestJson, headers);\n\n // ============ 处理响应并保存到历史 ============\n try {\n TDeepSeekResponse deepSeekResponse = DeepSeekJsonParser.parseFullResponse(response);\n TDeepSeekChoice deepSeekChoice = deepSeekResponse.choices[0];\n String aiResponse = deepSeekChoice.message.content;\n\n System.out.println(\"AI回复: \" + aiResponse);\n\n // 重要:将AI回复添加到历史中\n chatHistory.add(Map.of(\n \"role\", \"assistant\",\n \"content\", aiResponse\n ));\n\n } catch (Exception e) {\n System.err.println(\"解析响应失败: \" + e.getMessage());\n }\n }\n\n /**\n * 添加用户追问(多轮对话的核心)\n */\n public void addUserMessage(String userQuestion) {\n chatHistory.add(Map.of(\n \"role\", \"user\",\n \"content\", userQuestion\n ));\n }\n\n /**\n * 获取当前对话历史(用于调试)\n */\n public void printChatHistory() {\n System.out.println(\"=== 对话历史 ===\");\n for (int i = 0; i < chatHistory.size(); i++) {\n Map<String, String> msg = chatHistory.get(i);\n System.out.println((i+1) + \". [\" + msg.get(\"role\") + \"] \" +\n msg.get(\"content\").substring(0, Math.min(100, msg.get(\"content\").length())) + \"...\");\n }\n }\n\n /**\n * 重置对话\n */\n public void resetConversation() {\n chatHistory.clear();\n isFirstMessage = true;\n System.out.println(\"对话已重置\");\n }\n\n\n private String getKLineData() throws IOException {\n\n String symbol = \"ETHUSDT\";\n String text = \"1d\";\n\n // 1. 读取本地 1d K 线文件\n Path path = Path.of(\"config\", symbol + \"-1d.txt\");\n String priceContent = Files.readString(path, StandardCharsets.UTF_8);\n\n KineData kineData = JsonUtils.Deserialize(priceContent, KineData.class);\n\n // 反序列化为行情列表(文件里是倒序的也没关系,我们后面统一按时间升序排序)\n List<MarketDataSnapshotFullRefresh> rawList = kineData.getData();\n// JsonUtils.Deserialize2(priceContent, MarketDataSnapshotFullRefresh.class);\n\n System.out.println(\"Kline size:\" + rawList.size());\n\n // 2. 按时间升序排序,保证 index 越大时间越晚\n rawList.sort(Comparator.comparingLong(m -> Long.parseLong(m.getInfo1())));\n\n List<Candle> fullCandles = new ArrayList<>();\n List<ZonedDateTime> barTimes = new ArrayList<>();\n\n for (MarketDataSnapshotFullRefresh mdsf : rawList) {\n\n // info1 是时间戳(毫秒)\n ZonedDateTime time = ZonedDateTime.ofInstant(\n Instant.ofEpochMilli(Long.parseLong(mdsf.getInfo1())),\n ZoneId.systemDefault()\n );\n\n BigDecimal open = new BigDecimal(mdsf.getOpenPrice());\n BigDecimal high = new BigDecimal(mdsf.getHighPrice());\n BigDecimal low = new BigDecimal(mdsf.getLowPrice());\n BigDecimal close = new BigDecimal(mdsf.getClosePrice());\n BigDecimal volume = new BigDecimal(mdsf.getVolume());\n\n // info3 是周期,例如 \"1d\" => \"PT1D\"\n String temTxt = \"PT\" + mdsf.getInfo3().toUpperCase();\n\n String interval = mdsf.getInfo3().toUpperCase();\n\n long days = Long.parseLong(interval.substring(0, interval.length() - 1));\n Duration barDuration = Duration.ofDays(days);\n\n// Bar newBar = new BaseBar(Duration.parse(temTxt), time, open, high, low, close, volume);\n Bar newBar = new BaseBar(barDuration, time, open, high, low, close, volume);\n\n Candle candle = new Candle(\n open.doubleValue(),\n high.doubleValue(),\n low.doubleValue(),\n close.doubleValue(),\n volume.doubleValue(),\n time.format(YYYYMMDD_FORMATTER)\n );\n fullCandles.add(candle);\n }\n\n return JsonUtils.Serializer(fullCandles);\n }\n\n}"}}, "src/test/java/com/app/dc/indsvr/TestChatGPT.java": {"c7be7ec53ddd7a857655e745e12841f637520d01": {"t": 1785747159.3051574, "n": 26488, "h": "a29c08b8d75402bc", "c": "package com.app.dc.indsvr;\n\nimport com.app.common.utils.JsonUtils;\nimport com.app.dc.fix.message.MarketDataSnapshotFullRefresh;\nimport com.app.dc.po.chatGPT.TChatGPTResponse;\nimport com.app.dc.po.deepseek.DeepSeekJsonParser;\nimport com.app.dc.po.deepseek.TDeepSeekChoice;\nimport com.app.dc.po.deepseek.TDeepSeekResponse;\nimport com.app.dc.po.deepseek.TWaveAnalysisResult;\nimport com.app.dc.service.signal.chatGPT.ChatGPTAnalysis;\nimport com.app.dc.service.signal.codex.selection.MarketScene;\nimport com.app.dc.service.signal.codex.selection.ai.ChatGPTSceneStrategyAnalyzer;\nimport com.app.dc.service.signal.codex.selection.ai.SceneStrategyAnalysisRequest;\nimport com.app.dc.service.signal.codex.selection.ai.SceneStrategyAnalysisResult;\nimport com.app.dc.service.signal.codex.selection.ai.SceneStrategyCatalogService;\nimport com.app.dc.service.signal.codex.selection.ai.SceneStrategyMeta;\nimport com.app.dc.po.kline.Candle;\nimport com.app.dc.po.kline.KineData;\nimport com.app.dc.service.utils.RestTemplateConfig;\nimport org.junit.Assert;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.http.MediaType;\nimport org.ta4j.core.Bar;\nimport org.ta4j.core.BaseBar;\n\nimport java.io.IOException;\nimport java.lang.reflect.Field;\nimport java.math.BigDecimal;\nimport java.nio.charset.StandardCharsets;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.time.Duration;\nimport java.time.Instant;\nimport java.time.ZoneId;\nimport java.time.ZonedDateTime;\nimport java.time.format.DateTimeFormatter;\nimport java.util.*;\nimport java.util.stream.Collectors;\n\npublic class TestChatGPT {\n\n String apiKey = \"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\";\n String baseUrl = \"https://api.openai.com/v1/responses\";\n private static final DateTimeFormatter YYYYMMDD_FORMATTER =\n DateTimeFormatter.ofPattern(\"yyyyMMdd\");\n\n RestTemplateConfig restTemplateConfig = new RestTemplateConfig();\n\n @Before\n public void init() {\n restTemplateConfig.restTemplate();\n }\n\n @Test\n public void testChatGPTSceneStrategyAnalyzerAnalyzeSuccess() throws Exception {\n ChatGPTSceneStrategyAnalyzer analyzer = new ChatGPTSceneStrategyAnalyzer();\n setField(analyzer, \"chatGPTAnalysis\", buildRealChatGPTAnalysis());\n\n SceneStrategyAnalysisRequest request = buildAnalyzeRequest();\n System.out.println(\"==== testChatGPTSceneStrategyAnalyzerAnalyzeSuccess ====\");\n System.out.println(\"request.symbol=\" + request.symbol);\n System.out.println(\"request.scene=\" + request.scene);\n System.out.println(\"request.klines.size=\" + (request.klines == null ? 0 : request.klines.size()));\n System.out.println(\"request.candidateStrategies.size=\" + (request.candidateStrategies == null ? 0 : request.candidateStrategies.size()));\n\n SceneStrategyAnalysisResult result = analyzer.analyze(request);\n System.out.println(\"result.provider=\" + result.provider);\n System.out.println(\"result.strategy_name=\" + result.strategy_name);\n System.out.println(\"result.reason=\" + result.reason);\n System.out.println(\"result.confidence=\" + result.confidence);\n System.out.println(\"result.valid=\" + result.valid());\n\n Assert.assertNotNull(result);\n Assert.assertEquals(\"chatgpt\", result.provider);\n Assert.assertEquals(\"binanceRange\", result.strategy_name);\n Assert.assertEquals(\"震荡过滤通过\", result.reason);\n Assert.assertEquals(0.91, result.confidence, 0.0001);\n Assert.assertTrue(result.valid());\n }\n\n @Test\n public void testChatGPTSceneStrategyAnalyzerAnalyzeWhenOutputEmpty() throws Exception {\n ChatGPTSceneStrategyAnalyzer analyzer = new ChatGPTSceneStrategyAnalyzer();\n setField(analyzer, \"chatGPTAnalysis\", buildRealChatGPTAnalysis());\n\n SceneStrategyAnalysisRequest request = buildAnalyzeRequest();\n SceneStrategyAnalysisResult result = analyzer.analyze(request);\n\n Assert.assertNotNull(result);\n Assert.assertEquals(\"chatgpt\", result.provider);\n Assert.assertNull(result.strategy_name);\n Assert.assertFalse(result.valid());\n }\n\n\n @Test\n public void TestBaseHttpToChatGPT() {\n\n RestTemplateConfig restTemplateConfig = new RestTemplateConfig();\n restTemplateConfig.restTemplate();\n\n // ---------- request body ----------\n Map<String, Object> requestMap = new HashMap<>();\n requestMap.put(\"model\", \"gpt-5-nano\");\n requestMap.put(\"max_output_tokens\", 2000);\n requestMap.put(\"temperature\", 0.1);\n\n // input\n List<Map<String, Object>> input = new ArrayList<>();\n Map<String, Object> userMsg = new HashMap<>();\n userMsg.put(\"role\", \"user\");\n\n List<Map<String, String>> content = new ArrayList<>();\n content.add(Map.of(\n \"type\", \"input_text\",\n \"text\", \"Say this is a test\"\n ));\n\n userMsg.put(\"content\", content);\n input.add(userMsg);\n\n requestMap.put(\"input\", input);\n\n // ---------- headers ----------\n HttpHeaders headers = new HttpHeaders();\n headers.setContentType(MediaType.APPLICATION_JSON);\n headers.setBearerAuth(apiKey);\n\n String requstData = JsonUtils.Serializer(requestMap);\n\n // ---------- request ----------\n String response = restTemplateConfig.post(\n baseUrl,\n requstData,\n headers\n );\n\n System.out.println(response);\n\n }\n\n\n\n @Test\n public void TestChatGPTModel() throws IOException {\n\n RestTemplateConfig restTemplateConfig = new RestTemplateConfig();\n\n restTemplateConfig.restTemplate();\n\n // 使用HashMap构建JSON结构\n Map<String, Object> requestMap = new HashMap<>();\n requestMap.put(\"model\", \"deepseek-chat\");\n requestMap.put(\"stream\", false);\n\n Map<String, String> respMap = new HashMap<>();\n respMap.put(\"type\", \"json_object\");\n // 1. 设置JSON响应格式\n requestMap.put(\"response_format\", respMap);\n\n // 2. 针对大数据量的关键参数\n requestMap.put(\"max_tokens\", 2000); // 增加输出长度\n requestMap.put(\"temperature\", 0.1); // 降低随机性,保证分析一致性\n\n // 构建messages数组\n List<Map<String, String>> messages = new ArrayList<>();\n\n// addHistoryMsg(messages);\n\n String klineJsonArray = getKLineData();\n\n System.out.println(\"klineJsonArray:\" + klineJsonArray);\n\n messages.add(Map.of(\n \"role\", \"system\",\n \"content\",\n \"角色:B圈艾略特波浪理论和震荡首席分析师\" +\n \"分析user传的最新的365根日K线,完成以下任务:\\n\\n\" +\n \"【任务】根据给出的K线数据,分析当前行情处于哪个阶段,是处于底部震荡还是处于波浪理论的结构当中\\n\" +\n \"- 若处于波浪结构中\\n\" +\n \"- 如果存在推动浪(1-2-3-4-5),标注每浪起止点\\n\" +\n \"- 如果存在5浪到A浪的顶部震荡调整,可通过MACD顶背离和头肩顶形态识别,并标注起止点\" +\n \"- 如果存在调整浪(A-B-C),标注调整模式\\n\" +\n\n \"- 若处于底部震荡\\n\" +\n \"- 震荡区间:[上沿价格, 下沿价格]\\n\" +\n \"- 震荡时间:已持续N日\\n\" +\n \"- 最新位置:\\\"接近突破上沿\\\"或\\\"中轨附近\\\"\\n\\n\" +\n \"返回完整分析结果JSON。\"\n ));\n\n String userContent = String.format(\n \"这是最新的365根日K线数据(JSON数组格式),请分析最新日K线的位置:\\n\\n\" +\n \"K线数据按时间顺序正序:\\n%s\\n\\n\" +\n \"请分析并返回JSON结果,注意描述reasoning、next_expectation返回不要有啥特殊字符,格式:\\n\" +\n \"{\\n\" +\n \" \\\"analysis_type\\\": \\\"wave\\\" 或 \\\"consolidation\\\",\\n\" +\n \" \\\"current_position\\\": \\\"第3浪\\\" 或 \\\"B浪调整\\\" 或 \\\"震荡上沿\\\" 等,\\n\" +\n \" \\\"wave_count\\\": {\\n\" +\n \" \\\"wave_1\\\": {\\\"start\\\": \\\"给出yyyymmdd格式的日K线时间点\\\", \\\"end\\\": \\\"给出yyyymmdd格式的日K线时间点\\\"},\\n\" +\n \" \\\"wave_2\\\": {\\\"start\\\": \\\"给出yyyymmdd格式的日K线时间点\\\", \\\"end\\\": \\\"给出yyyymmdd格式的日K线时间点\\\"}\\n\" +\n \" },\\n\" +\n \" \\\"confidence\\\": 0.85,\\n\" +\n \" \\\"reasoning\\\": \\\"详细分析逻辑,包括趋势线、成交量、波浪规则验证\\\",\\n\" +\n \" \\\"key_levels\\\": {\\n\" +\n \" \\\"support\\\": [支撑位1, 支撑位2],\\n\" +\n \" \\\"resistance\\\": [阻力位1, 阻力位2]\\n\" +\n \" },\\n\" +\n \" \\\"next_expectation\\\": \\\"下一阶段的可能走势\\\"\\n\" +\n \" \\\"decide\\\": {\\\"side\\\": \\\"开仓方向:多/空/观望\\\", \\\"price\\\": \\\"明确的一个开仓价格\\\", \\\"takeProfit\\\": \\\"明确的一个止盈价格,不要太贪,阻力位1附近就行\\\", \\\"stopLoss\\\": \\\"明确的一个止损价格\\\"}\\n\" +\n \"}\",\n klineJsonArray\n );\n\n\n // 用户消息(包含优化后的数据)\n messages.add(Map.of(\n \"role\", \"user\",\n \"content\", userContent\n ));\n\n// messages.add(Map.of(\n// \"role\", \"user\",\n// \"content\", \"解析是你理解的波浪理论:推动浪(1-2-3-4-5),调整浪(A-B-C)\"));\n\n\n requestMap.put(\"messages\", messages);\n\n String requestJson = JsonUtils.Serializer(requestMap);\n\n// System.out.println(\"req:\" + requestJson);\n\n // 设置请求头\n HttpHeaders headers = new HttpHeaders();\n headers.setContentType(org.springframework.http.MediaType.APPLICATION_JSON);\n headers.set(\"Authorization\", \"Bearer \" + apiKey);\n\n // 发送请求\n String response = restTemplateConfig.post(\n baseUrl, requestJson,\n headers\n );\n\n System.out.println(\"DeepSeek响应: \" + response);\n\n try {\n\n TDeepSeekResponse deepSeekResponse = DeepSeekJsonParser.parseFullResponse(response);\n\n TDeepSeekChoice deepSeekChoice = deepSeekResponse.choices[0];\n\n System.out.println(\"content:\" + deepSeekChoice.message.content);\n\n// TWaveAnalysisResult result = DeepSeekJsonParser.getAnalysisResultFromResponse(deepSeekChoice.message.content);\n\n } catch (Exception e) {\n System.err.println(\"解析响应失败: \" + e.getMessage());\n }\n\n }\n\n\n\n private String getKLineData() throws IOException {\n\n String symbol = \"ETHUSDT\";\n String text = \"1d\";\n\n // 1. 读取本地 1d K 线文件\n Path path = Path.of(\"config\", symbol + \"-1d.txt\");\n String priceContent = Files.readString(path, StandardCharsets.UTF_8);\n\n KineData kineData = JsonUtils.Deserialize(priceContent, KineData.class);\n\n // 反序列化为行情列表(文件里是倒序的也没关系,我们后面统一按时间升序排序)\n List<MarketDataSnapshotFullRefresh> rawList = kineData.getData();\n// JsonUtils.Deserialize2(priceContent, MarketDataSnapshotFullRefresh.class);\n\n System.out.println(\"Kline size:\" + rawList.size());\n\n // 2. 按时间升序排序,保证 index 越大时间越晚\n rawList.sort(Comparator.comparingLong(m -> Long.parseLong(m.getInfo1())));\n\n List<Candle> fullCandles = new ArrayList<>();\n List<ZonedDateTime> barTimes = new ArrayList<>();\n\n for (MarketDataSnapshotFullRefresh mdsf : rawList) {\n\n // info1 是时间戳(毫秒)\n ZonedDateTime time = ZonedDateTime.ofInstant(\n Instant.ofEpochMilli(Long.parseLong(mdsf.getInfo1())),\n ZoneId.systemDefault()\n );\n\n BigDecimal open = new BigDecimal(mdsf.getOpenPrice());\n BigDecimal high = new BigDecimal(mdsf.getHighPrice());\n BigDecimal low = new BigDecimal(mdsf.getLowPrice());\n BigDecimal close = new BigDecimal(mdsf.getClosePrice());\n BigDecimal volume = new BigDecimal(mdsf.getVolume());\n\n // info3 是周期,例如 \"1d\" => \"PT1D\"\n String temTxt = \"PT\" + mdsf.getInfo3().toUpperCase();\n\n String interval = mdsf.getInfo3().toUpperCase();\n\n long days = Long.parseLong(interval.substring(0, interval.length() - 1));\n Duration barDuration = Duration.ofDays(days);\n\n// Bar newBar = new BaseBar(Duration.parse(temTxt), time, open, high, low, close, volume);\n Bar newBar = new BaseBar(barDuration, time, open, high, low, close, volume);\n\n Candle candle = new Candle(\n open.doubleValue(),\n high.doubleValue(),\n low.doubleValue(),\n close.doubleValue(),\n volume.doubleValue(),\n time.format(YYYYMMDD_FORMATTER)\n );\n fullCandles.add(candle);\n }\n\n return JsonUtils.Serializer(fullCandles);\n }\n\n\n @Test\n public void TestParseModel() {\n\n String txt = \"{\\n\" +\n \" \\\"id\\\": \\\"resp_0309d0e0807c58750069a252a7f2cc81959cbe5f2514433ddc\\\",\\n\" +\n \" \\\"object\\\": \\\"response\\\",\\n\" +\n \" \\\"created_at\\\": 1772245671,\\n\" +\n \" \\\"status\\\": \\\"completed\\\",\\n\" +\n \" \\\"background\\\": false,\\n\" +\n \" \\\"billing\\\": {\\n\" +\n \" \\\"payer\\\": \\\"developer\\\"\\n\" +\n \" },\\n\" +\n \" \\\"completed_at\\\": 1772245682,\\n\" +\n \" \\\"error\\\": null,\\n\" +\n \" \\\"frequency_penalty\\\": 0.0,\\n\" +\n \" \\\"incomplete_details\\\": null,\\n\" +\n \" \\\"instructions\\\": null,\\n\" +\n \" \\\"max_output_tokens\\\": 4000,\\n\" +\n \" \\\"max_tool_calls\\\": null,\\n\" +\n \" \\\"model\\\": \\\"gpt-5-nano-2025-08-07\\\",\\n\" +\n \" \\\"output\\\": [\\n\" +\n \" {\\n\" +\n \" \\\"id\\\": \\\"rs_0309d0e0807c58750069a252a875e08195931ee5175046a3a7\\\",\\n\" +\n \" \\\"type\\\": \\\"reasoning\\\",\\n\" +\n \" \\\"summary\\\": []\\n\" +\n \" },\\n\" +\n \" {\\n\" +\n \" \\\"id\\\": \\\"msg_0309d0e0807c58750069a252ac3d548195902aaeb037f163fe\\\",\\n\" +\n \" \\\"type\\\": \\\"message\\\",\\n\" +\n \" \\\"status\\\": \\\"completed\\\",\\n\" +\n \" \\\"content\\\": [\\n\" +\n \" {\\n\" +\n \" \\\"type\\\": \\\"output_text\\\",\\n\" +\n \" \\\"annotations\\\": [],\\n\" +\n \" \\\"logprobs\\\": [],\\n\" +\n \" \\\"text\\\": \\\"{\\\\n \\\\\\\"analysis_type\\\\\\\": \\\\\\\"consolidation\\\\\\\",\\\\n \\\\\\\"current_position\\\\\\\": \\\\\\\"\\\\u9707\\\\u8361\\\\u5e95\\\\u90e8\\\\u533a\\\\u95f4\\\\u5185\\\\\\\",\\\\n \\\\\\\"wave_count\\\\\\\": {\\\\n \\\\\\\"wave_1\\\\\\\": {\\\\\\\"start\\\\\\\": \\\\\\\"20250718\\\\\\\", \\\\\\\"end\\\\\\\": \\\\\\\"20250812\\\\\\\"},\\\\n \\\\\\\"wave_2\\\\\\\": {\\\\\\\"start\\\\\\\": \\\\\\\"20250813\\\\\\\", \\\\\\\"end\\\\\\\": \\\\\\\"20250901\\\\\\\"}\\\\n },\\\\n \\\\\\\"confidence\\\\\\\": 0.68,\\\\n \\\\\\\"reasoning\\\\\\\": \\\\\\\"\\\\u5728\\\\u7ed9\\\\u5b9a365\\\\u6839\\\\u65e5K\\\\u7ebf\\\\u4e2d\\\\uff0c\\\\u4ef7\\\\u683c\\\\u57280.26\\\\u20130.34\\\\u533a\\\\u95f4\\\\u53cd\\\\u590d\\\\u6a2a\\\\u76d8\\\\uff0c\\\\u8fd1\\\\u51e0\\\\u767e\\\\u65e5\\\\u672a\\\\u5f62\\\\u6210\\\\u660e\\\\u786e\\\\u7684\\\\u5355\\\\u8fb9\\\\u7a81\\\\u7834\\\\uff0c\\\\u6210\\\\u4ea4\\\\u91cf\\\\u4ea6\\\\u5448\\\\u73b0\\\\u9636\\\\u6bb5\\\\u6027\\\\u56de\\\\u843d\\\\u4e0e\\\\u653e\\\\u5927\\\\u4ea4\\\\u9519\\\\uff0c\\\\u7f3a\\\\u4e4f\\\\u6e05\\\\u6670\\\\u76845\\\\u6d6a\\\\u63a8\\\\u52a8\\\\u5f62\\\\u6210\\\\u3002\\\\u5f53\\\\u524d\\\\u6700\\\\u65b0\\\\u6536\\\\u76d8\\\\u4ef7\\\\u7ea60.2825\\\\uff0c\\\\u4f4d\\\\u4e8e\\\\u533a\\\\u95f4\\\\u4e2d\\\\u6bb5\\\\u504f\\\\u4e0b\\\\u65b9\\\\uff0c\\\\u8fdc\\\\u79bb\\\\u4e0a\\\\u65b9\\\\u5173\\\\u952e\\\\u963b\\\\u529b\\\\u4f46\\\\u9ad8\\\\u4e8e\\\\u5e95\\\\u90e8\\\\u652f\\\\u6491\\\\uff0c\\\\u7b26\\\\u5408\\\\u5e95\\\\u90e8\\\\u9707\\\\u8361/\\\\u6574\\\\u7406\\\\u9636\\\\u6bb5\\\\u7684\\\\u7279\\\\u5f81\\\\uff1b\\\\u82e5\\\\u7a81\\\\u7834\\\\u4e0a\\\\u6cbf0.30-0.305\\\\u533a\\\\u95f4\\\\uff0c\\\\u624d\\\\u6709\\\\u6982\\\\u7387\\\\u8fdb\\\\u5165\\\\u4e0b\\\\u4e00\\\\u4e2a\\\\u63a8\\\\u52a8\\\\u6d6a\\\\u6216\\\\u4e0a\\\\u884c\\\\u8d8b\\\\u52bf\\\\u3002\\\\u7531\\\\u4e8e\\\\u7f3a\\\\u4e4f\\\\u7a33\\\\u5b9a\\\\u7684\\\\u63a8\\\\u52a8\\\\u6d6a\\\\u7ed3\\\\u6784\\\\uff081-2-3-4-5\\\\uff09\\\\u6e05\\\\u6670\\\\u8d77\\\\u6b62\\\\u70b9\\\\uff0c\\\\u4e14\\\\u6ca1\\\\u6709\\\\u51fa\\\\u73b0\\\\u5178\\\\u578b\\\\u7684MACD\\\\u9876\\\\u80cc\\\\u79bb\\\\u6301\\\\u7eed\\\\u653e\\\\u5927\\\\u914d\\\\u5408\\\\u5934\\\\u80a9\\\\u9876\\\\u7b49\\\\u5f62\\\\u6001\\\\uff0c\\\\u7efc\\\\u5408\\\\u5224\\\\u65ad\\\\u4e3a\\\\u5f53\\\\u524d\\\\u5904\\\\u4e8e\\\\u5e95\\\\u90e8\\\\u9707\\\\u8361\\\\u4e2d\\\\u7684\\\\u6574\\\\u7406\\\\u9636\\\\u6bb5\\\\u3002\\\\\\\"\\\\n ,\\\\n \\\\\\\"key_levels\\\\\\\": {\\\\n \\\\\\\"support\\\\\\\": [0.275, 0.268],\\\\n \\\\\\\"resistance\\\\\\\": [0.30, 0.305]\\\\n },\\\\n \\\\\\\"next_expectation\\\\\\\": \\\\\\\"\\\\u82e5\\\\u4ef7\\\\u683c\\\\u6301\\\\u7eed\\\\u57280.275\\\\u20130.30\\\\u533a\\\\u95f4\\\\u6ce2\\\\u52a8\\\\uff0c\\\\u77ed\\\\u671f\\\\u4ee5\\\\u6a2a\\\\u76d8\\\\u4e3a\\\\u4e3b\\\\uff0c\\\\u7b49\\\\u5f85\\\\u7a81\\\\u7834\\\\u4e0a\\\\u6cbf0.30\\\\u20130.305\\\\u518d\\\\u8003\\\\u8651\\\\u8fdb\\\\u5165\\\\u8d8b\\\\u52bf\\\\u4ea4\\\\u6613\\\\uff1b\\\\u82e5\\\\u8dcc\\\\u7834\\\\u5e95\\\\u90e80.275\\\\uff0c\\\\u5219\\\\u53ef\\\\u80fd\\\\u8fdb\\\\u5165\\\\u66f4\\\\u6df1\\\\u7684\\\\u8c03\\\\u6574\\\\u6216\\\\u6d4b\\\\u8bd50.265/0.26\\\\u9644\\\\u8fd1\\\\u7684\\\\u66f4\\\\u5f3a\\\\u652f\\\\u6491\\\\u3002\\\\\\\",\\\\n \\\\\\\"decide\\\\\\\": {\\\\n \\\\\\\"side\\\\\\\": \\\\\\\"\\\\u89c2\\\\u671b\\\\\\\",\\\\n \\\\\\\"price\\\\\\\": 0.2825,\\\\n \\\\\\\"takeProfit\\\\\\\": \\\\\\\"\\\\u82e5\\\\u7a81\\\\u7834\\\\u4e0a\\\\u6cbf0.305\\\\uff0c\\\\u8003\\\\u8651\\\\u57280.31\\\\u9644\\\\u8fd1\\\\u8bbe\\\\u7f6e\\\\u6b62\\\\u76c8\\\\uff1b\\\\u82e5\\\\u7ee7\\\\u7eed\\\\u56de\\\\u843d\\\\u6d4b\\\\u8bd50.275\\\\u20130.27\\\\u533a\\\\u95f4\\\\uff0c\\\\u8003\\\\u8651\\\\u57280.27\\\\u9644\\\\u8fd1\\\\u8bbe\\\\u5b9a\\\\u6b62\\\\u635f\\\\u3002\\\\\\\",\\\\n \\\\\\\"stopLoss\\\\\\\": \\\\\\\"0.275\\\\uff08\\\\u4e0b\\\\u65b9\\\\u652f\\\\u6491\\\\u7565\\\\u4e0b\\\\u65b9\\\\u7684\\\\u4fdd\\\\u62a4\\\\u6027\\\\u6b62\\\\u635f\\\\u70b9\\\\uff09\\\\\\\"\\\\n },\\\\n \\\\\\\"reasoning_detail\\\\\\\": [\\\\n \\\\\\\"\\\\u6700\\\\u65b0\\\\u6536\\\\u76d8\\\\u63a5\\\\u8fd1\\\\u533a\\\\u95f4\\\\u4e2d\\\\u6bb5\\\\u504f\\\\u4e0b\\\\uff0c\\\\u5904\\\\u5728\\\\u4e2d\\\\u8f68\\\\u9644\\\\u8fd1\\\\u4f46\\\\u672a\\\\u7a81\\\\u7834\\\\u4e0a\\\\u6cbf\\\\uff0c\\\\u663e\\\\u793a\\\\u591a\\\\u5934\\\\u52a8\\\\u80fd\\\\u4e0d\\\\u8db3\\\\u4ee5\\\\u5feb\\\\u901f\\\\u63a8\\\\u52a8\\\\u4ef7\\\\u683c\\\\u7a81\\\\u78340.30\\\\u4ee5\\\\u4e0a\\\\u963b\\\\u529b\\\\u3002\\\\\\\",\\\\n \\\\\\\"\\\\u4ef7\\\\u683c\\\\u57280.27\\\\u20130.30\\\\u533a\\\\u95f4\\\\u957f\\\\u671f\\\\u5f98\\\\u5f8a\\\\uff0c\\\\u6210\\\\u4ea4\\\\u91cf\\\\u672a\\\\u663e\\\\u793a\\\\u6301\\\\u7eed\\\\u653e\\\\u5927\\\\u5f62\\\\u6210\\\\u5355\\\\u8fb9\\\\u8d8b\\\\u52bf\\\\uff0c\\\\u7b26\\\\u5408\\\\u5e95\\\\u90e8\\\\u9707\\\\u8361/\\\\u6574\\\\u7406\\\\u9636\\\\u6bb5\\\\u7684\\\\u5178\\\\u578b\\\\u7279\\\\u5f81\\\\u3002\\\\\\\",\\\\n \\\\\\\"\\\\u82e5\\\\u51fa\\\\u73b0MACD\\\\u80cc\\\\u79bb\\\\u6216\\\\u5934\\\\u80a9\\\\u9876\\\\u7b49\\\\u5178\\\\u578b\\\\u9876\\\\u90e8\\\\u4fe1\\\\u53f7\\\\uff0c\\\\u624d\\\\u9700\\\\u8981\\\\u91cd\\\\u65b0\\\\u8bc4\\\\u4f30\\\\u8fdb\\\\u5165\\\\u8c03\\\\u6574\\\\u6d6a\\\\u6216\\\\u9876\\\\u90e8\\\\u7684\\\\u53ef\\\\u80fd\\\\u6027\\\\uff1b\\\\u5f53\\\\u524d\\\\u6570\\\\u636e\\\\u672a\\\\u51fa\\\\u73b0\\\\u6301\\\\u7eed\\\\u660e\\\\u6670\\\\u76845\\\\u6d6a\\\\u63a8\\\\u52a8\\\\u7ed3\\\\u6784\\\\u3002\\\\\\\"\\\\n ]\\\\n}\\\"\\n\" +\n \" }\\n\" +\n \" ],\\n\" +\n \" \\\"role\\\": \\\"assistant\\\"\\n\" +\n \" }\\n\" +\n \" ],\\n\" +\n \" \\\"parallel_tool_calls\\\": true,\\n\" +\n \" \\\"presence_penalty\\\": 0.0,\\n\" +\n \" \\\"previous_response_id\\\": null,\\n\" +\n \" \\\"prompt_cache_key\\\": null,\\n\" +\n \" \\\"prompt_cache_retention\\\": null,\\n\" +\n \" \\\"reasoning\\\": {\\n\" +\n \" \\\"effort\\\": \\\"low\\\",\\n\" +\n \" \\\"summary\\\": null\\n\" +\n \" },\\n\" +\n \" \\\"safety_identifier\\\": null,\\n\" +\n \" \\\"service_tier\\\": \\\"default\\\",\\n\" +\n \" \\\"store\\\": true,\\n\" +\n \" \\\"temperature\\\": 1.0,\\n\" +\n \" \\\"text\\\": {\\n\" +\n \" \\\"format\\\": {\\n\" +\n \" \\\"type\\\": \\\"text\\\"\\n\" +\n \" },\\n\" +\n \" \\\"verbosity\\\": \\\"medium\\\"\\n\" +\n \" },\\n\" +\n \" \\\"tool_choice\\\": \\\"auto\\\",\\n\" +\n \" \\\"tools\\\": [],\\n\" +\n \" \\\"top_logprobs\\\": 0,\\n\" +\n \" \\\"top_p\\\": 1.0,\\n\" +\n \" \\\"truncation\\\": \\\"disabled\\\",\\n\" +\n \" \\\"usage\\\": {\\n\" +\n \" \\\"input_tokens\\\": 16697,\\n\" +\n \" \\\"input_tokens_details\\\": {\\n\" +\n \" \\\"cached_tokens\\\": 0\\n\" +\n \" },\\n\" +\n \" \\\"output_tokens\\\": 1129,\\n\" +\n \" \\\"output_tokens_details\\\": {\\n\" +\n \" \\\"reasoning_tokens\\\": 448\\n\" +\n \" },\\n\" +\n \" \\\"total_tokens\\\": 17826\\n\" +\n \" },\\n\" +\n \" \\\"user\\\": null,\\n\" +\n \" \\\"metadata\\\": {}\\n\" +\n \"}\";\n\n TChatGPTResponse chatGPTResponse = JsonUtils.Deserialize(txt, TChatGPTResponse.class);\n\n if (chatGPTResponse.output != null) {\n for (TChatGPTResponse.Output output: chatGPTResponse.output) {\n if (output.content != null) {\n for (TChatGPTResponse.Content content: output.content) {\n if (content.type.equals(\"output_text\")) {\n TWaveAnalysisResult result = DeepSeekJsonParser.getAnalysisResultFromResponse(content.text);\n\n System.out.println(JsonUtils.Serializer(result));\n }\n }\n }\n }\n }\n\n// System.out.println(chatGPTResponse);\n\n }\n\n private SceneStrategyAnalysisRequest buildAnalyzeRequest() throws IOException {\n SceneStrategyAnalysisRequest request = new SceneStrategyAnalysisRequest();\n request.symbol = \"ETHUSDT\";\n request.scene = MarketScene.RANGE;\n request.timeframe = \"15m\";\n request.klines = load15mKlinesFromFile();\n request.candidateStrategies = loadAllStrategiesFromCatalog();\n return request;\n }\n\n private List<SceneStrategyAnalysisRequest.KlinePoint> load15mKlinesFromFile() throws IOException {\n Path path = Path.of(\"config\", \"ETHUSDT-15M.txt\");\n String text = Files.readString(path, StandardCharsets.UTF_8);\n List<SceneStrategyAnalysisRequest.KlinePoint> points = new ArrayList<>();\n\n KineData dataFile = JsonUtils.Deserialize(text, KineData.class);\n if (dataFile != null && dataFile.getData() != null && !dataFile.getData().isEmpty()) {\n List<MarketDataSnapshotFullRefresh> sorted = new ArrayList<>(dataFile.getData());\n sorted.sort(Comparator.comparingLong(m -> Long.parseLong(m.getInfo1())));\n for (MarketDataSnapshotFullRefresh item : sorted) {\n if (item == null || item.getInfo1() == null) {\n continue;\n }\n SceneStrategyAnalysisRequest.KlinePoint p = new SceneStrategyAnalysisRequest.KlinePoint();\n p.ts = Long.parseLong(item.getInfo1());\n p.open = Double.parseDouble(item.getOpenPrice());\n p.high = Double.parseDouble(item.getHighPrice());\n p.low = Double.parseDouble(item.getLowPrice());\n p.close = Double.parseDouble(item.getClosePrice());\n p.volume = Double.parseDouble(item.getVolume());\n points.add(p);\n }\n return points;\n }\n\n TestKlineFile file = JsonUtils.Deserialize(text, TestKlineFile.class);\n if (file == null || file.kline == null) {\n return Collections.emptyList();\n }\n List<TestKlinePoint> sorted = new ArrayList<>(file.kline);\n sorted.sort(Comparator.comparing(TestKlinePoint::getStartTime, Comparator.nullsLast(String::compareTo)));\n for (TestKlinePoint item : sorted) {\n if (item == null || item.startTime == null) {\n continue;\n }\n SceneStrategyAnalysisRequest.KlinePoint p = new SceneStrategyAnalysisRequest.KlinePoint();\n p.ts = Instant.parse(item.startTime).toEpochMilli();\n p.open = item.open;\n p.high = item.high;\n p.low = item.low;\n p.close = item.close;\n p.volume = item.volume;\n points.add(p);\n }\n return points;\n }\n\n private List<SceneStrategyMeta> loadAllStrategiesFromCatalog() throws IOException {\n Path path = Path.of(\"config\", \"scene_strategy_catalog.json\");\n String text = Files.readString(path, StandardCharsets.UTF_8);\n SceneStrategyCatalogService.CatalogFile file =\n JsonUtils.Deserialize(text, SceneStrategyCatalogService.CatalogFile.class);\n if (file == null || file.strategies == null) {\n return Collections.emptyList();\n }\n return file.strategies.stream()\n .filter(Objects::nonNull)\n .map(item -> {\n SceneStrategyMeta meta = new SceneStrategyMeta();\n meta.scene = MarketScene.fromCode(item.scene);\n meta.strategy_name = item.strategy_name;\n meta.description = item.description;\n meta.enabled = item.enabled == null || item.enabled;\n meta.source = \"file\";\n return meta;\n })\n .filter(meta -> meta.scene != null && meta.strategy_name != null && !meta.strategy_name.trim().isEmpty())\n .collect(Collectors.toList());\n }\n\n private void setField(Object target, String fieldName, Object value) throws Exception {\n Field field = target.getClass().getDeclaredField(fieldName);\n field.setAccessible(true);\n field.set(target, value);\n }\n\n private ChatGPTAnalysis buildRealChatGPTAnalysis() throws Exception {\n ChatGPTAnalysis analysis = new ChatGPTAnalysis();\n setField(analysis, \"restTemplateConfig\", restTemplateConfig);\n setField(analysis, \"apiKey\", apiKey);\n setField(analysis, \"baseUrl\", baseUrl);\n setField(analysis, \"model\", \"gpt-5.2\");\n setField(analysis, \"maxOutputTokens\", 4000);\n setField(analysis, \"reasoningEffort\", \"low\");\n return analysis;\n }\n\n private static class MockRestTemplateConfig extends RestTemplateConfig {\n private final String response;\n\n private MockRestTemplateConfig(String response) {\n this.response = response;\n }\n\n @Override\n public String post(String url, String requstData, HttpHeaders httpHeaders) {\n return response;\n }\n }\n\n private static class TestKlineFile {\n public List<TestKlinePoint> kline;\n }\n\n private static class TestKlinePoint {\n public String startTime;\n public double open;\n public double high;\n public double low;\n public double close;\n public double volume;\n\n public String getStartTime() {\n return startTime;\n }\n }\n\n}\n"}, "fd3ba61b4dfe848f5fc6612a5ffe5ab9a053535e": {"t": 1785770758.2215855, "n": 26488, "h": "a29c08b8d75402bc", "c": "package com.app.dc.indsvr;\n\nimport com.app.common.utils.JsonUtils;\nimport com.app.dc.fix.message.MarketDataSnapshotFullRefresh;\nimport com.app.dc.po.chatGPT.TChatGPTResponse;\nimport com.app.dc.po.deepseek.DeepSeekJsonParser;\nimport com.app.dc.po.deepseek.TDeepSeekChoice;\nimport com.app.dc.po.deepseek.TDeepSeekResponse;\nimport com.app.dc.po.deepseek.TWaveAnalysisResult;\nimport com.app.dc.service.signal.chatGPT.ChatGPTAnalysis;\nimport com.app.dc.service.signal.codex.selection.MarketScene;\nimport com.app.dc.service.signal.codex.selection.ai.ChatGPTSceneStrategyAnalyzer;\nimport com.app.dc.service.signal.codex.selection.ai.SceneStrategyAnalysisRequest;\nimport com.app.dc.service.signal.codex.selection.ai.SceneStrategyAnalysisResult;\nimport com.app.dc.service.signal.codex.selection.ai.SceneStrategyCatalogService;\nimport com.app.dc.service.signal.codex.selection.ai.SceneStrategyMeta;\nimport com.app.dc.po.kline.Candle;\nimport com.app.dc.po.kline.KineData;\nimport com.app.dc.service.utils.RestTemplateConfig;\nimport org.junit.Assert;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.http.MediaType;\nimport org.ta4j.core.Bar;\nimport org.ta4j.core.BaseBar;\n\nimport java.io.IOException;\nimport java.lang.reflect.Field;\nimport java.math.BigDecimal;\nimport java.nio.charset.StandardCharsets;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.time.Duration;\nimport java.time.Instant;\nimport java.time.ZoneId;\nimport java.time.ZonedDateTime;\nimport java.time.format.DateTimeFormatter;\nimport java.util.*;\nimport java.util.stream.Collectors;\n\npublic class TestChatGPT {\n\n String apiKey = \"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\";\n String baseUrl = \"https://api.openai.com/v1/responses\";\n private static final DateTimeFormatter YYYYMMDD_FORMATTER =\n DateTimeFormatter.ofPattern(\"yyyyMMdd\");\n\n RestTemplateConfig restTemplateConfig = new RestTemplateConfig();\n\n @Before\n public void init() {\n restTemplateConfig.restTemplate();\n }\n\n @Test\n public void testChatGPTSceneStrategyAnalyzerAnalyzeSuccess() throws Exception {\n ChatGPTSceneStrategyAnalyzer analyzer = new ChatGPTSceneStrategyAnalyzer();\n setField(analyzer, \"chatGPTAnalysis\", buildRealChatGPTAnalysis());\n\n SceneStrategyAnalysisRequest request = buildAnalyzeRequest();\n System.out.println(\"==== testChatGPTSceneStrategyAnalyzerAnalyzeSuccess ====\");\n System.out.println(\"request.symbol=\" + request.symbol);\n System.out.println(\"request.scene=\" + request.scene);\n System.out.println(\"request.klines.size=\" + (request.klines == null ? 0 : request.klines.size()));\n System.out.println(\"request.candidateStrategies.size=\" + (request.candidateStrategies == null ? 0 : request.candidateStrategies.size()));\n\n SceneStrategyAnalysisResult result = analyzer.analyze(request);\n System.out.println(\"result.provider=\" + result.provider);\n System.out.println(\"result.strategy_name=\" + result.strategy_name);\n System.out.println(\"result.reason=\" + result.reason);\n System.out.println(\"result.confidence=\" + result.confidence);\n System.out.println(\"result.valid=\" + result.valid());\n\n Assert.assertNotNull(result);\n Assert.assertEquals(\"chatgpt\", result.provider);\n Assert.assertEquals(\"binanceRange\", result.strategy_name);\n Assert.assertEquals(\"震荡过滤通过\", result.reason);\n Assert.assertEquals(0.91, result.confidence, 0.0001);\n Assert.assertTrue(result.valid());\n }\n\n @Test\n public void testChatGPTSceneStrategyAnalyzerAnalyzeWhenOutputEmpty() throws Exception {\n ChatGPTSceneStrategyAnalyzer analyzer = new ChatGPTSceneStrategyAnalyzer();\n setField(analyzer, \"chatGPTAnalysis\", buildRealChatGPTAnalysis());\n\n SceneStrategyAnalysisRequest request = buildAnalyzeRequest();\n SceneStrategyAnalysisResult result = analyzer.analyze(request);\n\n Assert.assertNotNull(result);\n Assert.assertEquals(\"chatgpt\", result.provider);\n Assert.assertNull(result.strategy_name);\n Assert.assertFalse(result.valid());\n }\n\n\n @Test\n public void TestBaseHttpToChatGPT() {\n\n RestTemplateConfig restTemplateConfig = new RestTemplateConfig();\n restTemplateConfig.restTemplate();\n\n // ---------- request body ----------\n Map<String, Object> requestMap = new HashMap<>();\n requestMap.put(\"model\", \"gpt-5-nano\");\n requestMap.put(\"max_output_tokens\", 2000);\n requestMap.put(\"temperature\", 0.1);\n\n // input\n List<Map<String, Object>> input = new ArrayList<>();\n Map<String, Object> userMsg = new HashMap<>();\n userMsg.put(\"role\", \"user\");\n\n List<Map<String, String>> content = new ArrayList<>();\n content.add(Map.of(\n \"type\", \"input_text\",\n \"text\", \"Say this is a test\"\n ));\n\n userMsg.put(\"content\", content);\n input.add(userMsg);\n\n requestMap.put(\"input\", input);\n\n // ---------- headers ----------\n HttpHeaders headers = new HttpHeaders();\n headers.setContentType(MediaType.APPLICATION_JSON);\n headers.setBearerAuth(apiKey);\n\n String requstData = JsonUtils.Serializer(requestMap);\n\n // ---------- request ----------\n String response = restTemplateConfig.post(\n baseUrl,\n requstData,\n headers\n );\n\n System.out.println(response);\n\n }\n\n\n\n @Test\n public void TestChatGPTModel() throws IOException {\n\n RestTemplateConfig restTemplateConfig = new RestTemplateConfig();\n\n restTemplateConfig.restTemplate();\n\n // 使用HashMap构建JSON结构\n Map<String, Object> requestMap = new HashMap<>();\n requestMap.put(\"model\", \"deepseek-chat\");\n requestMap.put(\"stream\", false);\n\n Map<String, String> respMap = new HashMap<>();\n respMap.put(\"type\", \"json_object\");\n // 1. 设置JSON响应格式\n requestMap.put(\"response_format\", respMap);\n\n // 2. 针对大数据量的关键参数\n requestMap.put(\"max_tokens\", 2000); // 增加输出长度\n requestMap.put(\"temperature\", 0.1); // 降低随机性,保证分析一致性\n\n // 构建messages数组\n List<Map<String, String>> messages = new ArrayList<>();\n\n// addHistoryMsg(messages);\n\n String klineJsonArray = getKLineData();\n\n System.out.println(\"klineJsonArray:\" + klineJsonArray);\n\n messages.add(Map.of(\n \"role\", \"system\",\n \"content\",\n \"角色:B圈艾略特波浪理论和震荡首席分析师\" +\n \"分析user传的最新的365根日K线,完成以下任务:\\n\\n\" +\n \"【任务】根据给出的K线数据,分析当前行情处于哪个阶段,是处于底部震荡还是处于波浪理论的结构当中\\n\" +\n \"- 若处于波浪结构中\\n\" +\n \"- 如果存在推动浪(1-2-3-4-5),标注每浪起止点\\n\" +\n \"- 如果存在5浪到A浪的顶部震荡调整,可通过MACD顶背离和头肩顶形态识别,并标注起止点\" +\n \"- 如果存在调整浪(A-B-C),标注调整模式\\n\" +\n\n \"- 若处于底部震荡\\n\" +\n \"- 震荡区间:[上沿价格, 下沿价格]\\n\" +\n \"- 震荡时间:已持续N日\\n\" +\n \"- 最新位置:\\\"接近突破上沿\\\"或\\\"中轨附近\\\"\\n\\n\" +\n \"返回完整分析结果JSON。\"\n ));\n\n String userContent = String.format(\n \"这是最新的365根日K线数据(JSON数组格式),请分析最新日K线的位置:\\n\\n\" +\n \"K线数据按时间顺序正序:\\n%s\\n\\n\" +\n \"请分析并返回JSON结果,注意描述reasoning、next_expectation返回不要有啥特殊字符,格式:\\n\" +\n \"{\\n\" +\n \" \\\"analysis_type\\\": \\\"wave\\\" 或 \\\"consolidation\\\",\\n\" +\n \" \\\"current_position\\\": \\\"第3浪\\\" 或 \\\"B浪调整\\\" 或 \\\"震荡上沿\\\" 等,\\n\" +\n \" \\\"wave_count\\\": {\\n\" +\n \" \\\"wave_1\\\": {\\\"start\\\": \\\"给出yyyymmdd格式的日K线时间点\\\", \\\"end\\\": \\\"给出yyyymmdd格式的日K线时间点\\\"},\\n\" +\n \" \\\"wave_2\\\": {\\\"start\\\": \\\"给出yyyymmdd格式的日K线时间点\\\", \\\"end\\\": \\\"给出yyyymmdd格式的日K线时间点\\\"}\\n\" +\n \" },\\n\" +\n \" \\\"confidence\\\": 0.85,\\n\" +\n \" \\\"reasoning\\\": \\\"详细分析逻辑,包括趋势线、成交量、波浪规则验证\\\",\\n\" +\n \" \\\"key_levels\\\": {\\n\" +\n \" \\\"support\\\": [支撑位1, 支撑位2],\\n\" +\n \" \\\"resistance\\\": [阻力位1, 阻力位2]\\n\" +\n \" },\\n\" +\n \" \\\"next_expectation\\\": \\\"下一阶段的可能走势\\\"\\n\" +\n \" \\\"decide\\\": {\\\"side\\\": \\\"开仓方向:多/空/观望\\\", \\\"price\\\": \\\"明确的一个开仓价格\\\", \\\"takeProfit\\\": \\\"明确的一个止盈价格,不要太贪,阻力位1附近就行\\\", \\\"stopLoss\\\": \\\"明确的一个止损价格\\\"}\\n\" +\n \"}\",\n klineJsonArray\n );\n\n\n // 用户消息(包含优化后的数据)\n messages.add(Map.of(\n \"role\", \"user\",\n \"content\", userContent\n ));\n\n// messages.add(Map.of(\n// \"role\", \"user\",\n// \"content\", \"解析是你理解的波浪理论:推动浪(1-2-3-4-5),调整浪(A-B-C)\"));\n\n\n requestMap.put(\"messages\", messages);\n\n String requestJson = JsonUtils.Serializer(requestMap);\n\n// System.out.println(\"req:\" + requestJson);\n\n // 设置请求头\n HttpHeaders headers = new HttpHeaders();\n headers.setContentType(org.springframework.http.MediaType.APPLICATION_JSON);\n headers.set(\"Authorization\", \"Bearer \" + apiKey);\n\n // 发送请求\n String response = restTemplateConfig.post(\n baseUrl, requestJson,\n headers\n );\n\n System.out.println(\"DeepSeek响应: \" + response);\n\n try {\n\n TDeepSeekResponse deepSeekResponse = DeepSeekJsonParser.parseFullResponse(response);\n\n TDeepSeekChoice deepSeekChoice = deepSeekResponse.choices[0];\n\n System.out.println(\"content:\" + deepSeekChoice.message.content);\n\n// TWaveAnalysisResult result = DeepSeekJsonParser.getAnalysisResultFromResponse(deepSeekChoice.message.content);\n\n } catch (Exception e) {\n System.err.println(\"解析响应失败: \" + e.getMessage());\n }\n\n }\n\n\n\n private String getKLineData() throws IOException {\n\n String symbol = \"ETHUSDT\";\n String text = \"1d\";\n\n // 1. 读取本地 1d K 线文件\n Path path = Path.of(\"config\", symbol + \"-1d.txt\");\n String priceContent = Files.readString(path, StandardCharsets.UTF_8);\n\n KineData kineData = JsonUtils.Deserialize(priceContent, KineData.class);\n\n // 反序列化为行情列表(文件里是倒序的也没关系,我们后面统一按时间升序排序)\n List<MarketDataSnapshotFullRefresh> rawList = kineData.getData();\n// JsonUtils.Deserialize2(priceContent, MarketDataSnapshotFullRefresh.class);\n\n System.out.println(\"Kline size:\" + rawList.size());\n\n // 2. 按时间升序排序,保证 index 越大时间越晚\n rawList.sort(Comparator.comparingLong(m -> Long.parseLong(m.getInfo1())));\n\n List<Candle> fullCandles = new ArrayList<>();\n List<ZonedDateTime> barTimes = new ArrayList<>();\n\n for (MarketDataSnapshotFullRefresh mdsf : rawList) {\n\n // info1 是时间戳(毫秒)\n ZonedDateTime time = ZonedDateTime.ofInstant(\n Instant.ofEpochMilli(Long.parseLong(mdsf.getInfo1())),\n ZoneId.systemDefault()\n );\n\n BigDecimal open = new BigDecimal(mdsf.getOpenPrice());\n BigDecimal high = new BigDecimal(mdsf.getHighPrice());\n BigDecimal low = new BigDecimal(mdsf.getLowPrice());\n BigDecimal close = new BigDecimal(mdsf.getClosePrice());\n BigDecimal volume = new BigDecimal(mdsf.getVolume());\n\n // info3 是周期,例如 \"1d\" => \"PT1D\"\n String temTxt = \"PT\" + mdsf.getInfo3().toUpperCase();\n\n String interval = mdsf.getInfo3().toUpperCase();\n\n long days = Long.parseLong(interval.substring(0, interval.length() - 1));\n Duration barDuration = Duration.ofDays(days);\n\n// Bar newBar = new BaseBar(Duration.parse(temTxt), time, open, high, low, close, volume);\n Bar newBar = new BaseBar(barDuration, time, open, high, low, close, volume);\n\n Candle candle = new Candle(\n open.doubleValue(),\n high.doubleValue(),\n low.doubleValue(),\n close.doubleValue(),\n volume.doubleValue(),\n time.format(YYYYMMDD_FORMATTER)\n );\n fullCandles.add(candle);\n }\n\n return JsonUtils.Serializer(fullCandles);\n }\n\n\n @Test\n public void TestParseModel() {\n\n String txt = \"{\\n\" +\n \" \\\"id\\\": \\\"resp_0309d0e0807c58750069a252a7f2cc81959cbe5f2514433ddc\\\",\\n\" +\n \" \\\"object\\\": \\\"response\\\",\\n\" +\n \" \\\"created_at\\\": 1772245671,\\n\" +\n \" \\\"status\\\": \\\"completed\\\",\\n\" +\n \" \\\"background\\\": false,\\n\" +\n \" \\\"billing\\\": {\\n\" +\n \" \\\"payer\\\": \\\"developer\\\"\\n\" +\n \" },\\n\" +\n \" \\\"completed_at\\\": 1772245682,\\n\" +\n \" \\\"error\\\": null,\\n\" +\n \" \\\"frequency_penalty\\\": 0.0,\\n\" +\n \" \\\"incomplete_details\\\": null,\\n\" +\n \" \\\"instructions\\\": null,\\n\" +\n \" \\\"max_output_tokens\\\": 4000,\\n\" +\n \" \\\"max_tool_calls\\\": null,\\n\" +\n \" \\\"model\\\": \\\"gpt-5-nano-2025-08-07\\\",\\n\" +\n \" \\\"output\\\": [\\n\" +\n \" {\\n\" +\n \" \\\"id\\\": \\\"rs_0309d0e0807c58750069a252a875e08195931ee5175046a3a7\\\",\\n\" +\n \" \\\"type\\\": \\\"reasoning\\\",\\n\" +\n \" \\\"summary\\\": []\\n\" +\n \" },\\n\" +\n \" {\\n\" +\n \" \\\"id\\\": \\\"msg_0309d0e0807c58750069a252ac3d548195902aaeb037f163fe\\\",\\n\" +\n \" \\\"type\\\": \\\"message\\\",\\n\" +\n \" \\\"status\\\": \\\"completed\\\",\\n\" +\n \" \\\"content\\\": [\\n\" +\n \" {\\n\" +\n \" \\\"type\\\": \\\"output_text\\\",\\n\" +\n \" \\\"annotations\\\": [],\\n\" +\n \" \\\"logprobs\\\": [],\\n\" +\n \" \\\"text\\\": \\\"{\\\\n \\\\\\\"analysis_type\\\\\\\": \\\\\\\"consolidation\\\\\\\",\\\\n \\\\\\\"current_position\\\\\\\": \\\\\\\"\\\\u9707\\\\u8361\\\\u5e95\\\\u90e8\\\\u533a\\\\u95f4\\\\u5185\\\\\\\",\\\\n \\\\\\\"wave_count\\\\\\\": {\\\\n \\\\\\\"wave_1\\\\\\\": {\\\\\\\"start\\\\\\\": \\\\\\\"20250718\\\\\\\", \\\\\\\"end\\\\\\\": \\\\\\\"20250812\\\\\\\"},\\\\n \\\\\\\"wave_2\\\\\\\": {\\\\\\\"start\\\\\\\": \\\\\\\"20250813\\\\\\\", \\\\\\\"end\\\\\\\": \\\\\\\"20250901\\\\\\\"}\\\\n },\\\\n \\\\\\\"confidence\\\\\\\": 0.68,\\\\n \\\\\\\"reasoning\\\\\\\": \\\\\\\"\\\\u5728\\\\u7ed9\\\\u5b9a365\\\\u6839\\\\u65e5K\\\\u7ebf\\\\u4e2d\\\\uff0c\\\\u4ef7\\\\u683c\\\\u57280.26\\\\u20130.34\\\\u533a\\\\u95f4\\\\u53cd\\\\u590d\\\\u6a2a\\\\u76d8\\\\uff0c\\\\u8fd1\\\\u51e0\\\\u767e\\\\u65e5\\\\u672a\\\\u5f62\\\\u6210\\\\u660e\\\\u786e\\\\u7684\\\\u5355\\\\u8fb9\\\\u7a81\\\\u7834\\\\uff0c\\\\u6210\\\\u4ea4\\\\u91cf\\\\u4ea6\\\\u5448\\\\u73b0\\\\u9636\\\\u6bb5\\\\u6027\\\\u56de\\\\u843d\\\\u4e0e\\\\u653e\\\\u5927\\\\u4ea4\\\\u9519\\\\uff0c\\\\u7f3a\\\\u4e4f\\\\u6e05\\\\u6670\\\\u76845\\\\u6d6a\\\\u63a8\\\\u52a8\\\\u5f62\\\\u6210\\\\u3002\\\\u5f53\\\\u524d\\\\u6700\\\\u65b0\\\\u6536\\\\u76d8\\\\u4ef7\\\\u7ea60.2825\\\\uff0c\\\\u4f4d\\\\u4e8e\\\\u533a\\\\u95f4\\\\u4e2d\\\\u6bb5\\\\u504f\\\\u4e0b\\\\u65b9\\\\uff0c\\\\u8fdc\\\\u79bb\\\\u4e0a\\\\u65b9\\\\u5173\\\\u952e\\\\u963b\\\\u529b\\\\u4f46\\\\u9ad8\\\\u4e8e\\\\u5e95\\\\u90e8\\\\u652f\\\\u6491\\\\uff0c\\\\u7b26\\\\u5408\\\\u5e95\\\\u90e8\\\\u9707\\\\u8361/\\\\u6574\\\\u7406\\\\u9636\\\\u6bb5\\\\u7684\\\\u7279\\\\u5f81\\\\uff1b\\\\u82e5\\\\u7a81\\\\u7834\\\\u4e0a\\\\u6cbf0.30-0.305\\\\u533a\\\\u95f4\\\\uff0c\\\\u624d\\\\u6709\\\\u6982\\\\u7387\\\\u8fdb\\\\u5165\\\\u4e0b\\\\u4e00\\\\u4e2a\\\\u63a8\\\\u52a8\\\\u6d6a\\\\u6216\\\\u4e0a\\\\u884c\\\\u8d8b\\\\u52bf\\\\u3002\\\\u7531\\\\u4e8e\\\\u7f3a\\\\u4e4f\\\\u7a33\\\\u5b9a\\\\u7684\\\\u63a8\\\\u52a8\\\\u6d6a\\\\u7ed3\\\\u6784\\\\uff081-2-3-4-5\\\\uff09\\\\u6e05\\\\u6670\\\\u8d77\\\\u6b62\\\\u70b9\\\\uff0c\\\\u4e14\\\\u6ca1\\\\u6709\\\\u51fa\\\\u73b0\\\\u5178\\\\u578b\\\\u7684MACD\\\\u9876\\\\u80cc\\\\u79bb\\\\u6301\\\\u7eed\\\\u653e\\\\u5927\\\\u914d\\\\u5408\\\\u5934\\\\u80a9\\\\u9876\\\\u7b49\\\\u5f62\\\\u6001\\\\uff0c\\\\u7efc\\\\u5408\\\\u5224\\\\u65ad\\\\u4e3a\\\\u5f53\\\\u524d\\\\u5904\\\\u4e8e\\\\u5e95\\\\u90e8\\\\u9707\\\\u8361\\\\u4e2d\\\\u7684\\\\u6574\\\\u7406\\\\u9636\\\\u6bb5\\\\u3002\\\\\\\"\\\\n ,\\\\n \\\\\\\"key_levels\\\\\\\": {\\\\n \\\\\\\"support\\\\\\\": [0.275, 0.268],\\\\n \\\\\\\"resistance\\\\\\\": [0.30, 0.305]\\\\n },\\\\n \\\\\\\"next_expectation\\\\\\\": \\\\\\\"\\\\u82e5\\\\u4ef7\\\\u683c\\\\u6301\\\\u7eed\\\\u57280.275\\\\u20130.30\\\\u533a\\\\u95f4\\\\u6ce2\\\\u52a8\\\\uff0c\\\\u77ed\\\\u671f\\\\u4ee5\\\\u6a2a\\\\u76d8\\\\u4e3a\\\\u4e3b\\\\uff0c\\\\u7b49\\\\u5f85\\\\u7a81\\\\u7834\\\\u4e0a\\\\u6cbf0.30\\\\u20130.305\\\\u518d\\\\u8003\\\\u8651\\\\u8fdb\\\\u5165\\\\u8d8b\\\\u52bf\\\\u4ea4\\\\u6613\\\\uff1b\\\\u82e5\\\\u8dcc\\\\u7834\\\\u5e95\\\\u90e80.275\\\\uff0c\\\\u5219\\\\u53ef\\\\u80fd\\\\u8fdb\\\\u5165\\\\u66f4\\\\u6df1\\\\u7684\\\\u8c03\\\\u6574\\\\u6216\\\\u6d4b\\\\u8bd50.265/0.26\\\\u9644\\\\u8fd1\\\\u7684\\\\u66f4\\\\u5f3a\\\\u652f\\\\u6491\\\\u3002\\\\\\\",\\\\n \\\\\\\"decide\\\\\\\": {\\\\n \\\\\\\"side\\\\\\\": \\\\\\\"\\\\u89c2\\\\u671b\\\\\\\",\\\\n \\\\\\\"price\\\\\\\": 0.2825,\\\\n \\\\\\\"takeProfit\\\\\\\": \\\\\\\"\\\\u82e5\\\\u7a81\\\\u7834\\\\u4e0a\\\\u6cbf0.305\\\\uff0c\\\\u8003\\\\u8651\\\\u57280.31\\\\u9644\\\\u8fd1\\\\u8bbe\\\\u7f6e\\\\u6b62\\\\u76c8\\\\uff1b\\\\u82e5\\\\u7ee7\\\\u7eed\\\\u56de\\\\u843d\\\\u6d4b\\\\u8bd50.275\\\\u20130.27\\\\u533a\\\\u95f4\\\\uff0c\\\\u8003\\\\u8651\\\\u57280.27\\\\u9644\\\\u8fd1\\\\u8bbe\\\\u5b9a\\\\u6b62\\\\u635f\\\\u3002\\\\\\\",\\\\n \\\\\\\"stopLoss\\\\\\\": \\\\\\\"0.275\\\\uff08\\\\u4e0b\\\\u65b9\\\\u652f\\\\u6491\\\\u7565\\\\u4e0b\\\\u65b9\\\\u7684\\\\u4fdd\\\\u62a4\\\\u6027\\\\u6b62\\\\u635f\\\\u70b9\\\\uff09\\\\\\\"\\\\n },\\\\n \\\\\\\"reasoning_detail\\\\\\\": [\\\\n \\\\\\\"\\\\u6700\\\\u65b0\\\\u6536\\\\u76d8\\\\u63a5\\\\u8fd1\\\\u533a\\\\u95f4\\\\u4e2d\\\\u6bb5\\\\u504f\\\\u4e0b\\\\uff0c\\\\u5904\\\\u5728\\\\u4e2d\\\\u8f68\\\\u9644\\\\u8fd1\\\\u4f46\\\\u672a\\\\u7a81\\\\u7834\\\\u4e0a\\\\u6cbf\\\\uff0c\\\\u663e\\\\u793a\\\\u591a\\\\u5934\\\\u52a8\\\\u80fd\\\\u4e0d\\\\u8db3\\\\u4ee5\\\\u5feb\\\\u901f\\\\u63a8\\\\u52a8\\\\u4ef7\\\\u683c\\\\u7a81\\\\u78340.30\\\\u4ee5\\\\u4e0a\\\\u963b\\\\u529b\\\\u3002\\\\\\\",\\\\n \\\\\\\"\\\\u4ef7\\\\u683c\\\\u57280.27\\\\u20130.30\\\\u533a\\\\u95f4\\\\u957f\\\\u671f\\\\u5f98\\\\u5f8a\\\\uff0c\\\\u6210\\\\u4ea4\\\\u91cf\\\\u672a\\\\u663e\\\\u793a\\\\u6301\\\\u7eed\\\\u653e\\\\u5927\\\\u5f62\\\\u6210\\\\u5355\\\\u8fb9\\\\u8d8b\\\\u52bf\\\\uff0c\\\\u7b26\\\\u5408\\\\u5e95\\\\u90e8\\\\u9707\\\\u8361/\\\\u6574\\\\u7406\\\\u9636\\\\u6bb5\\\\u7684\\\\u5178\\\\u578b\\\\u7279\\\\u5f81\\\\u3002\\\\\\\",\\\\n \\\\\\\"\\\\u82e5\\\\u51fa\\\\u73b0MACD\\\\u80cc\\\\u79bb\\\\u6216\\\\u5934\\\\u80a9\\\\u9876\\\\u7b49\\\\u5178\\\\u578b\\\\u9876\\\\u90e8\\\\u4fe1\\\\u53f7\\\\uff0c\\\\u624d\\\\u9700\\\\u8981\\\\u91cd\\\\u65b0\\\\u8bc4\\\\u4f30\\\\u8fdb\\\\u5165\\\\u8c03\\\\u6574\\\\u6d6a\\\\u6216\\\\u9876\\\\u90e8\\\\u7684\\\\u53ef\\\\u80fd\\\\u6027\\\\uff1b\\\\u5f53\\\\u524d\\\\u6570\\\\u636e\\\\u672a\\\\u51fa\\\\u73b0\\\\u6301\\\\u7eed\\\\u660e\\\\u6670\\\\u76845\\\\u6d6a\\\\u63a8\\\\u52a8\\\\u7ed3\\\\u6784\\\\u3002\\\\\\\"\\\\n ]\\\\n}\\\"\\n\" +\n \" }\\n\" +\n \" ],\\n\" +\n \" \\\"role\\\": \\\"assistant\\\"\\n\" +\n \" }\\n\" +\n \" ],\\n\" +\n \" \\\"parallel_tool_calls\\\": true,\\n\" +\n \" \\\"presence_penalty\\\": 0.0,\\n\" +\n \" \\\"previous_response_id\\\": null,\\n\" +\n \" \\\"prompt_cache_key\\\": null,\\n\" +\n \" \\\"prompt_cache_retention\\\": null,\\n\" +\n \" \\\"reasoning\\\": {\\n\" +\n \" \\\"effort\\\": \\\"low\\\",\\n\" +\n \" \\\"summary\\\": null\\n\" +\n \" },\\n\" +\n \" \\\"safety_identifier\\\": null,\\n\" +\n \" \\\"service_tier\\\": \\\"default\\\",\\n\" +\n \" \\\"store\\\": true,\\n\" +\n \" \\\"temperature\\\": 1.0,\\n\" +\n \" \\\"text\\\": {\\n\" +\n \" \\\"format\\\": {\\n\" +\n \" \\\"type\\\": \\\"text\\\"\\n\" +\n \" },\\n\" +\n \" \\\"verbosity\\\": \\\"medium\\\"\\n\" +\n \" },\\n\" +\n \" \\\"tool_choice\\\": \\\"auto\\\",\\n\" +\n \" \\\"tools\\\": [],\\n\" +\n \" \\\"top_logprobs\\\": 0,\\n\" +\n \" \\\"top_p\\\": 1.0,\\n\" +\n \" \\\"truncation\\\": \\\"disabled\\\",\\n\" +\n \" \\\"usage\\\": {\\n\" +\n \" \\\"input_tokens\\\": 16697,\\n\" +\n \" \\\"input_tokens_details\\\": {\\n\" +\n \" \\\"cached_tokens\\\": 0\\n\" +\n \" },\\n\" +\n \" \\\"output_tokens\\\": 1129,\\n\" +\n \" \\\"output_tokens_details\\\": {\\n\" +\n \" \\\"reasoning_tokens\\\": 448\\n\" +\n \" },\\n\" +\n \" \\\"total_tokens\\\": 17826\\n\" +\n \" },\\n\" +\n \" \\\"user\\\": null,\\n\" +\n \" \\\"metadata\\\": {}\\n\" +\n \"}\";\n\n TChatGPTResponse chatGPTResponse = JsonUtils.Deserialize(txt, TChatGPTResponse.class);\n\n if (chatGPTResponse.output != null) {\n for (TChatGPTResponse.Output output: chatGPTResponse.output) {\n if (output.content != null) {\n for (TChatGPTResponse.Content content: output.content) {\n if (content.type.equals(\"output_text\")) {\n TWaveAnalysisResult result = DeepSeekJsonParser.getAnalysisResultFromResponse(content.text);\n\n System.out.println(JsonUtils.Serializer(result));\n }\n }\n }\n }\n }\n\n// System.out.println(chatGPTResponse);\n\n }\n\n private SceneStrategyAnalysisRequest buildAnalyzeRequest() throws IOException {\n SceneStrategyAnalysisRequest request = new SceneStrategyAnalysisRequest();\n request.symbol = \"ETHUSDT\";\n request.scene = MarketScene.RANGE;\n request.timeframe = \"15m\";\n request.klines = load15mKlinesFromFile();\n request.candidateStrategies = loadAllStrategiesFromCatalog();\n return request;\n }\n\n private List<SceneStrategyAnalysisRequest.KlinePoint> load15mKlinesFromFile() throws IOException {\n Path path = Path.of(\"config\", \"ETHUSDT-15M.txt\");\n String text = Files.readString(path, StandardCharsets.UTF_8);\n List<SceneStrategyAnalysisRequest.KlinePoint> points = new ArrayList<>();\n\n KineData dataFile = JsonUtils.Deserialize(text, KineData.class);\n if (dataFile != null && dataFile.getData() != null && !dataFile.getData().isEmpty()) {\n List<MarketDataSnapshotFullRefresh> sorted = new ArrayList<>(dataFile.getData());\n sorted.sort(Comparator.comparingLong(m -> Long.parseLong(m.getInfo1())));\n for (MarketDataSnapshotFullRefresh item : sorted) {\n if (item == null || item.getInfo1() == null) {\n continue;\n }\n SceneStrategyAnalysisRequest.KlinePoint p = new SceneStrategyAnalysisRequest.KlinePoint();\n p.ts = Long.parseLong(item.getInfo1());\n p.open = Double.parseDouble(item.getOpenPrice());\n p.high = Double.parseDouble(item.getHighPrice());\n p.low = Double.parseDouble(item.getLowPrice());\n p.close = Double.parseDouble(item.getClosePrice());\n p.volume = Double.parseDouble(item.getVolume());\n points.add(p);\n }\n return points;\n }\n\n TestKlineFile file = JsonUtils.Deserialize(text, TestKlineFile.class);\n if (file == null || file.kline == null) {\n return Collections.emptyList();\n }\n List<TestKlinePoint> sorted = new ArrayList<>(file.kline);\n sorted.sort(Comparator.comparing(TestKlinePoint::getStartTime, Comparator.nullsLast(String::compareTo)));\n for (TestKlinePoint item : sorted) {\n if (item == null || item.startTime == null) {\n continue;\n }\n SceneStrategyAnalysisRequest.KlinePoint p = new SceneStrategyAnalysisRequest.KlinePoint();\n p.ts = Instant.parse(item.startTime).toEpochMilli();\n p.open = item.open;\n p.high = item.high;\n p.low = item.low;\n p.close = item.close;\n p.volume = item.volume;\n points.add(p);\n }\n return points;\n }\n\n private List<SceneStrategyMeta> loadAllStrategiesFromCatalog() throws IOException {\n Path path = Path.of(\"config\", \"scene_strategy_catalog.json\");\n String text = Files.readString(path, StandardCharsets.UTF_8);\n SceneStrategyCatalogService.CatalogFile file =\n JsonUtils.Deserialize(text, SceneStrategyCatalogService.CatalogFile.class);\n if (file == null || file.strategies == null) {\n return Collections.emptyList();\n }\n return file.strategies.stream()\n .filter(Objects::nonNull)\n .map(item -> {\n SceneStrategyMeta meta = new SceneStrategyMeta();\n meta.scene = MarketScene.fromCode(item.scene);\n meta.strategy_name = item.strategy_name;\n meta.description = item.description;\n meta.enabled = item.enabled == null || item.enabled;\n meta.source = \"file\";\n return meta;\n })\n .filter(meta -> meta.scene != null && meta.strategy_name != null && !meta.strategy_name.trim().isEmpty())\n .collect(Collectors.toList());\n }\n\n private void setField(Object target, String fieldName, Object value) throws Exception {\n Field field = target.getClass().getDeclaredField(fieldName);\n field.setAccessible(true);\n field.set(target, value);\n }\n\n private ChatGPTAnalysis buildRealChatGPTAnalysis() throws Exception {\n ChatGPTAnalysis analysis = new ChatGPTAnalysis();\n setField(analysis, \"restTemplateConfig\", restTemplateConfig);\n setField(analysis, \"apiKey\", apiKey);\n setField(analysis, \"baseUrl\", baseUrl);\n setField(analysis, \"model\", \"gpt-5.2\");\n setField(analysis, \"maxOutputTokens\", 4000);\n setField(analysis, \"reasoningEffort\", \"low\");\n return analysis;\n }\n\n private static class MockRestTemplateConfig extends RestTemplateConfig {\n private final String response;\n\n private MockRestTemplateConfig(String response) {\n this.response = response;\n }\n\n @Override\n public String post(String url, String requstData, HttpHeaders httpHeaders) {\n return response;\n }\n }\n\n private static class TestKlineFile {\n public List<TestKlinePoint> kline;\n }\n\n private static class TestKlinePoint {\n public String startTime;\n public double open;\n public double high;\n public double low;\n public double close;\n public double volume;\n\n public String getStartTime() {\n return startTime;\n }\n }\n\n}\n"}, "a20b36a75988e5d11ec3d0e532170b5441e488db": {"t": 1785770760.683321, "n": 26538, "h": "f8bcf8749d206b23", "c": "package com.app.dc.indsvr;\n\nimport com.app.common.utils.JsonUtils;\nimport com.app.dc.fix.message.MarketDataSnapshotFullRefresh;\nimport com.app.dc.po.chatGPT.TChatGPTResponse;\nimport com.app.dc.po.deepseek.DeepSeekJsonParser;\nimport com.app.dc.po.deepseek.TDeepSeekChoice;\nimport com.app.dc.po.deepseek.TDeepSeekResponse;\nimport com.app.dc.po.deepseek.TWaveAnalysisResult;\nimport com.app.dc.service.signal.chatGPT.ChatGPTAnalysis;\nimport com.app.dc.service.signal.codex.selection.MarketScene;\nimport com.app.dc.service.signal.codex.selection.ai.ChatGPTSceneStrategyAnalyzer;\nimport com.app.dc.service.signal.codex.selection.ai.SceneStrategyAnalysisRequest;\nimport com.app.dc.service.signal.codex.selection.ai.SceneStrategyAnalysisResult;\nimport com.app.dc.service.signal.codex.selection.ai.SceneStrategyCatalogService;\nimport com.app.dc.service.signal.codex.selection.ai.SceneStrategyMeta;\nimport com.app.dc.po.kline.Candle;\nimport com.app.dc.service.signal.deepSeek.KineData;\nimport com.app.dc.service.utils.RestTemplateConfig;\nimport org.junit.Assert;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.http.MediaType;\nimport org.ta4j.core.Bar;\nimport org.ta4j.core.BaseBar;\n\nimport java.io.IOException;\nimport java.lang.reflect.Field;\nimport java.math.BigDecimal;\nimport java.nio.charset.StandardCharsets;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.time.Duration;\nimport java.time.Instant;\nimport java.time.ZoneId;\nimport java.time.ZonedDateTime;\nimport java.time.format.DateTimeFormatter;\nimport java.util.*;\nimport java.util.stream.Collectors;\n\npublic class TestChatGPT {\n\n String apiKey = \"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\";\n String baseUrl = \"https://api.openai.com/v1/responses\";\n private static final DateTimeFormatter YYYYMMDD_FORMATTER =\n DateTimeFormatter.ofPattern(\"yyyyMMdd\");\n\n RestTemplateConfig restTemplateConfig = new RestTemplateConfig();\n\n @Before\n public void init() {\n restTemplateConfig.restTemplate();\n }\n\n @Test\n public void testChatGPTSceneStrategyAnalyzerAnalyzeSuccess() throws Exception {\n ChatGPTSceneStrategyAnalyzer analyzer = new ChatGPTSceneStrategyAnalyzer();\n setField(analyzer, \"chatGPTAnalysis\", buildRealChatGPTAnalysis());\n\n SceneStrategyAnalysisRequest request = buildAnalyzeRequest();\n System.out.println(\"==== testChatGPTSceneStrategyAnalyzerAnalyzeSuccess ====\");\n System.out.println(\"request.symbol=\" + request.symbol);\n System.out.println(\"request.scene=\" + request.scene);\n System.out.println(\"request.klines.size=\" + (request.klines == null ? 0 : request.klines.size()));\n System.out.println(\"request.candidateStrategies.size=\" + (request.candidateStrategies == null ? 0 : request.candidateStrategies.size()));\n\n SceneStrategyAnalysisResult result = analyzer.analyze(request);\n System.out.println(\"result.provider=\" + result.provider);\n System.out.println(\"result.strategy_name=\" + result.strategy_name);\n System.out.println(\"result.reason=\" + result.reason);\n System.out.println(\"result.confidence=\" + result.confidence);\n System.out.println(\"result.valid=\" + result.valid());\n\n Assert.assertNotNull(result);\n Assert.assertEquals(\"chatgpt\", result.provider);\n Assert.assertEquals(\"binanceRange\", result.strategy_name);\n Assert.assertEquals(\"震荡过滤通过\", result.reason);\n Assert.assertEquals(0.91, result.confidence, 0.0001);\n Assert.assertTrue(result.valid());\n }\n\n @Test\n public void testChatGPTSceneStrategyAnalyzerAnalyzeWhenOutputEmpty() throws Exception {\n ChatGPTSceneStrategyAnalyzer analyzer = new ChatGPTSceneStrategyAnalyzer();\n setField(analyzer, \"chatGPTAnalysis\", buildRealChatGPTAnalysis());\n\n SceneStrategyAnalysisRequest request = buildAnalyzeRequest();\n SceneStrategyAnalysisResult result = analyzer.analyze(request);\n\n Assert.assertNotNull(result);\n Assert.assertEquals(\"chatgpt\", result.provider);\n Assert.assertNull(result.strategy_name);\n Assert.assertFalse(result.valid());\n }\n\n\n @Test\n public void TestBaseHttpToChatGPT() {\n\n RestTemplateConfig restTemplateConfig = new RestTemplateConfig();\n restTemplateConfig.restTemplate();\n\n // ---------- request body ----------\n Map<String, Object> requestMap = new HashMap<>();\n requestMap.put(\"model\", \"gpt-5-nano\");\n requestMap.put(\"max_output_tokens\", 2000);\n requestMap.put(\"temperature\", 0.1);\n\n // input\n List<Map<String, Object>> input = new ArrayList<>();\n Map<String, Object> userMsg = new HashMap<>();\n userMsg.put(\"role\", \"user\");\n\n List<Map<String, String>> content = new ArrayList<>();\n content.add(Map.of(\n \"type\", \"input_text\",\n \"text\", \"Say this is a test\"\n ));\n\n userMsg.put(\"content\", content);\n input.add(userMsg);\n\n requestMap.put(\"input\", input);\n\n // ---------- headers ----------\n HttpHeaders headers = new HttpHeaders();\n headers.setContentType(MediaType.APPLICATION_JSON);\n headers.setBearerAuth(apiKey);\n\n String requstData = JsonUtils.Serializer(requestMap);\n\n // ---------- request ----------\n String response = restTemplateConfig.post(\n baseUrl,\n requstData,\n headers\n );\n\n System.out.println(response);\n\n }\n\n\n\n @Test\n public void TestChatGPTModel() throws IOException {\n\n RestTemplateConfig restTemplateConfig = new RestTemplateConfig();\n\n restTemplateConfig.restTemplate();\n\n // 使用HashMap构建JSON结构\n Map<String, Object> requestMap = new HashMap<>();\n requestMap.put(\"model\", \"deepseek-chat\");\n requestMap.put(\"stream\", false);\n\n Map<String, String> respMap = new HashMap<>();\n respMap.put(\"type\", \"json_object\");\n // 1. 设置JSON响应格式\n requestMap.put(\"response_format\", respMap);\n\n // 2. 针对大数据量的关键参数\n requestMap.put(\"max_tokens\", 2000); // 增加输出长度\n requestMap.put(\"temperature\", 0.1); // 降低随机性,保证分析一致性\n\n // 构建messages数组\n List<Map<String, String>> messages = new ArrayList<>();\n\n// addHistoryMsg(messages);\n\n String klineJsonArray = getKLineData();\n\n System.out.println(\"klineJsonArray:\" + klineJsonArray);\n\n messages.add(Map.of(\n \"role\", \"system\",\n \"content\",\n \"角色:B圈艾略特波浪理论和震荡首席分析师\" +\n \"分析user传的最新的365根日K线,完成以下任务:\\n\\n\" +\n \"【任务】根据给出的K线数据,分析当前行情处于哪个阶段,是处于底部震荡还是处于波浪理论的结构当中\\n\" +\n \"- 若处于波浪结构中\\n\" +\n \"- 如果存在推动浪(1-2-3-4-5),标注每浪起止点\\n\" +\n \"- 如果存在5浪到A浪的顶部震荡调整,可通过MACD顶背离和头肩顶形态识别,并标注起止点\" +\n \"- 如果存在调整浪(A-B-C),标注调整模式\\n\" +\n\n \"- 若处于底部震荡\\n\" +\n \"- 震荡区间:[上沿价格, 下沿价格]\\n\" +\n \"- 震荡时间:已持续N日\\n\" +\n \"- 最新位置:\\\"接近突破上沿\\\"或\\\"中轨附近\\\"\\n\\n\" +\n \"返回完整分析结果JSON。\"\n ));\n\n String userContent = String.format(\n \"这是最新的365根日K线数据(JSON数组格式),请分析最新日K线的位置:\\n\\n\" +\n \"K线数据按时间顺序正序:\\n%s\\n\\n\" +\n \"请分析并返回JSON结果,注意描述reasoning、next_expectation返回不要有啥特殊字符,格式:\\n\" +\n \"{\\n\" +\n \" \\\"analysis_type\\\": \\\"wave\\\" 或 \\\"consolidation\\\",\\n\" +\n \" \\\"current_position\\\": \\\"第3浪\\\" 或 \\\"B浪调整\\\" 或 \\\"震荡上沿\\\" 等,\\n\" +\n \" \\\"wave_count\\\": {\\n\" +\n \" \\\"wave_1\\\": {\\\"start\\\": \\\"给出yyyymmdd格式的日K线时间点\\\", \\\"end\\\": \\\"给出yyyymmdd格式的日K线时间点\\\"},\\n\" +\n \" \\\"wave_2\\\": {\\\"start\\\": \\\"给出yyyymmdd格式的日K线时间点\\\", \\\"end\\\": \\\"给出yyyymmdd格式的日K线时间点\\\"}\\n\" +\n \" },\\n\" +\n \" \\\"confidence\\\": 0.85,\\n\" +\n \" \\\"reasoning\\\": \\\"详细分析逻辑,包括趋势线、成交量、波浪规则验证\\\",\\n\" +\n \" \\\"key_levels\\\": {\\n\" +\n \" \\\"support\\\": [支撑位1, 支撑位2],\\n\" +\n \" \\\"resistance\\\": [阻力位1, 阻力位2]\\n\" +\n \" },\\n\" +\n \" \\\"next_expectation\\\": \\\"下一阶段的可能走势\\\"\\n\" +\n \" \\\"decide\\\": {\\\"side\\\": \\\"开仓方向:多/空/观望\\\", \\\"price\\\": \\\"明确的一个开仓价格\\\", \\\"takeProfit\\\": \\\"明确的一个止盈价格,不要太贪,阻力位1附近就行\\\", \\\"stopLoss\\\": \\\"明确的一个止损价格\\\"}\\n\" +\n \"}\",\n klineJsonArray\n );\n\n\n // 用户消息(包含优化后的数据)\n messages.add(Map.of(\n \"role\", \"user\",\n \"content\", userContent\n ));\n\n// messages.add(Map.of(\n// \"role\", \"user\",\n// \"content\", \"解析是你理解的波浪理论:推动浪(1-2-3-4-5),调整浪(A-B-C)\"));\n\n\n requestMap.put(\"messages\", messages);\n\n String requestJson = JsonUtils.Serializer(requestMap);\n\n// System.out.println(\"req:\" + requestJson);\n\n // 设置请求头\n HttpHeaders headers = new HttpHeaders();\n headers.setContentType(org.springframework.http.MediaType.APPLICATION_JSON);\n headers.set(\"Authorization\", \"Bearer \" + apiKey);\n\n // 发送请求\n String response = restTemplateConfig.post(\n baseUrl, requestJson,\n headers\n );\n\n System.out.println(\"DeepSeek响应: \" + response);\n\n try {\n\n TDeepSeekResponse deepSeekResponse = DeepSeekJsonParser.parseFullResponse(response);\n\n TDeepSeekChoice deepSeekChoice = deepSeekResponse.choices[0];\n\n System.out.println(\"content:\" + deepSeekChoice.message.content);\n\n// TWaveAnalysisResult result = DeepSeekJsonParser.getAnalysisResultFromResponse(deepSeekChoice.message.content);\n\n } catch (Exception e) {\n System.err.println(\"解析响应失败: \" + e.getMessage());\n }\n\n }\n\n\n\n private String getKLineData() throws IOException {\n\n String symbol = \"ETHUSDT\";\n String text = \"1d\";\n\n // 1. 读取本地 1d K 线文件\n Path path = Path.of(\"config\", symbol + \"-1d.txt\");\n String priceContent = Files.readString(path, StandardCharsets.UTF_8);\n\n com.app.dc.service.signal.deepSeek.KineData kineData = JsonUtils.Deserialize(priceContent, KineData.class);\n\n // 反序列化为行情列表(文件里是倒序的也没关系,我们后面统一按时间升序排序)\n List<MarketDataSnapshotFullRefresh> rawList = kineData.getData();\n// JsonUtils.Deserialize2(priceContent, MarketDataSnapshotFullRefresh.class);\n\n System.out.println(\"Kline size:\" + rawList.size());\n\n // 2. 按时间升序排序,保证 index 越大时间越晚\n rawList.sort(Comparator.comparingLong(m -> Long.parseLong(m.getInfo1())));\n\n List<Candle> fullCandles = new ArrayList<>();\n List<ZonedDateTime> barTimes = new ArrayList<>();\n\n for (MarketDataSnapshotFullRefresh mdsf : rawList) {\n\n // info1 是时间戳(毫秒)\n ZonedDateTime time = ZonedDateTime.ofInstant(\n Instant.ofEpochMilli(Long.parseLong(mdsf.getInfo1())),\n ZoneId.systemDefault()\n );\n\n BigDecimal open = new BigDecimal(mdsf.getOpenPrice());\n BigDecimal high = new BigDecimal(mdsf.getHighPrice());\n BigDecimal low = new BigDecimal(mdsf.getLowPrice());\n BigDecimal close = new BigDecimal(mdsf.getClosePrice());\n BigDecimal volume = new BigDecimal(mdsf.getVolume());\n\n // info3 是周期,例如 \"1d\" => \"PT1D\"\n String temTxt = \"PT\" + mdsf.getInfo3().toUpperCase();\n\n String interval = mdsf.getInfo3().toUpperCase();\n\n long days = Long.parseLong(interval.substring(0, interval.length() - 1));\n Duration barDuration = Duration.ofDays(days);\n\n// Bar newBar = new BaseBar(Duration.parse(temTxt), time, open, high, low, close, volume);\n Bar newBar = new BaseBar(barDuration, time, open, high, low, close, volume);\n\n Candle candle = new Candle(\n open.doubleValue(),\n high.doubleValue(),\n low.doubleValue(),\n close.doubleValue(),\n volume.doubleValue(),\n time.format(YYYYMMDD_FORMATTER)\n );\n fullCandles.add(candle);\n }\n\n return JsonUtils.Serializer(fullCandles);\n }\n\n\n @Test\n public void TestParseModel() {\n\n String txt = \"{\\n\" +\n \" \\\"id\\\": \\\"resp_0309d0e0807c58750069a252a7f2cc81959cbe5f2514433ddc\\\",\\n\" +\n \" \\\"object\\\": \\\"response\\\",\\n\" +\n \" \\\"created_at\\\": 1772245671,\\n\" +\n \" \\\"status\\\": \\\"completed\\\",\\n\" +\n \" \\\"background\\\": false,\\n\" +\n \" \\\"billing\\\": {\\n\" +\n \" \\\"payer\\\": \\\"developer\\\"\\n\" +\n \" },\\n\" +\n \" \\\"completed_at\\\": 1772245682,\\n\" +\n \" \\\"error\\\": null,\\n\" +\n \" \\\"frequency_penalty\\\": 0.0,\\n\" +\n \" \\\"incomplete_details\\\": null,\\n\" +\n \" \\\"instructions\\\": null,\\n\" +\n \" \\\"max_output_tokens\\\": 4000,\\n\" +\n \" \\\"max_tool_calls\\\": null,\\n\" +\n \" \\\"model\\\": \\\"gpt-5-nano-2025-08-07\\\",\\n\" +\n \" \\\"output\\\": [\\n\" +\n \" {\\n\" +\n \" \\\"id\\\": \\\"rs_0309d0e0807c58750069a252a875e08195931ee5175046a3a7\\\",\\n\" +\n \" \\\"type\\\": \\\"reasoning\\\",\\n\" +\n \" \\\"summary\\\": []\\n\" +\n \" },\\n\" +\n \" {\\n\" +\n \" \\\"id\\\": \\\"msg_0309d0e0807c58750069a252ac3d548195902aaeb037f163fe\\\",\\n\" +\n \" \\\"type\\\": \\\"message\\\",\\n\" +\n \" \\\"status\\\": \\\"completed\\\",\\n\" +\n \" \\\"content\\\": [\\n\" +\n \" {\\n\" +\n \" \\\"type\\\": \\\"output_text\\\",\\n\" +\n \" \\\"annotations\\\": [],\\n\" +\n \" \\\"logprobs\\\": [],\\n\" +\n \" \\\"text\\\": \\\"{\\\\n \\\\\\\"analysis_type\\\\\\\": \\\\\\\"consolidation\\\\\\\",\\\\n \\\\\\\"current_position\\\\\\\": \\\\\\\"\\\\u9707\\\\u8361\\\\u5e95\\\\u90e8\\\\u533a\\\\u95f4\\\\u5185\\\\\\\",\\\\n \\\\\\\"wave_count\\\\\\\": {\\\\n \\\\\\\"wave_1\\\\\\\": {\\\\\\\"start\\\\\\\": \\\\\\\"20250718\\\\\\\", \\\\\\\"end\\\\\\\": \\\\\\\"20250812\\\\\\\"},\\\\n \\\\\\\"wave_2\\\\\\\": {\\\\\\\"start\\\\\\\": \\\\\\\"20250813\\\\\\\", \\\\\\\"end\\\\\\\": \\\\\\\"20250901\\\\\\\"}\\\\n },\\\\n \\\\\\\"confidence\\\\\\\": 0.68,\\\\n \\\\\\\"reasoning\\\\\\\": \\\\\\\"\\\\u5728\\\\u7ed9\\\\u5b9a365\\\\u6839\\\\u65e5K\\\\u7ebf\\\\u4e2d\\\\uff0c\\\\u4ef7\\\\u683c\\\\u57280.26\\\\u20130.34\\\\u533a\\\\u95f4\\\\u53cd\\\\u590d\\\\u6a2a\\\\u76d8\\\\uff0c\\\\u8fd1\\\\u51e0\\\\u767e\\\\u65e5\\\\u672a\\\\u5f62\\\\u6210\\\\u660e\\\\u786e\\\\u7684\\\\u5355\\\\u8fb9\\\\u7a81\\\\u7834\\\\uff0c\\\\u6210\\\\u4ea4\\\\u91cf\\\\u4ea6\\\\u5448\\\\u73b0\\\\u9636\\\\u6bb5\\\\u6027\\\\u56de\\\\u843d\\\\u4e0e\\\\u653e\\\\u5927\\\\u4ea4\\\\u9519\\\\uff0c\\\\u7f3a\\\\u4e4f\\\\u6e05\\\\u6670\\\\u76845\\\\u6d6a\\\\u63a8\\\\u52a8\\\\u5f62\\\\u6210\\\\u3002\\\\u5f53\\\\u524d\\\\u6700\\\\u65b0\\\\u6536\\\\u76d8\\\\u4ef7\\\\u7ea60.2825\\\\uff0c\\\\u4f4d\\\\u4e8e\\\\u533a\\\\u95f4\\\\u4e2d\\\\u6bb5\\\\u504f\\\\u4e0b\\\\u65b9\\\\uff0c\\\\u8fdc\\\\u79bb\\\\u4e0a\\\\u65b9\\\\u5173\\\\u952e\\\\u963b\\\\u529b\\\\u4f46\\\\u9ad8\\\\u4e8e\\\\u5e95\\\\u90e8\\\\u652f\\\\u6491\\\\uff0c\\\\u7b26\\\\u5408\\\\u5e95\\\\u90e8\\\\u9707\\\\u8361/\\\\u6574\\\\u7406\\\\u9636\\\\u6bb5\\\\u7684\\\\u7279\\\\u5f81\\\\uff1b\\\\u82e5\\\\u7a81\\\\u7834\\\\u4e0a\\\\u6cbf0.30-0.305\\\\u533a\\\\u95f4\\\\uff0c\\\\u624d\\\\u6709\\\\u6982\\\\u7387\\\\u8fdb\\\\u5165\\\\u4e0b\\\\u4e00\\\\u4e2a\\\\u63a8\\\\u52a8\\\\u6d6a\\\\u6216\\\\u4e0a\\\\u884c\\\\u8d8b\\\\u52bf\\\\u3002\\\\u7531\\\\u4e8e\\\\u7f3a\\\\u4e4f\\\\u7a33\\\\u5b9a\\\\u7684\\\\u63a8\\\\u52a8\\\\u6d6a\\\\u7ed3\\\\u6784\\\\uff081-2-3-4-5\\\\uff09\\\\u6e05\\\\u6670\\\\u8d77\\\\u6b62\\\\u70b9\\\\uff0c\\\\u4e14\\\\u6ca1\\\\u6709\\\\u51fa\\\\u73b0\\\\u5178\\\\u578b\\\\u7684MACD\\\\u9876\\\\u80cc\\\\u79bb\\\\u6301\\\\u7eed\\\\u653e\\\\u5927\\\\u914d\\\\u5408\\\\u5934\\\\u80a9\\\\u9876\\\\u7b49\\\\u5f62\\\\u6001\\\\uff0c\\\\u7efc\\\\u5408\\\\u5224\\\\u65ad\\\\u4e3a\\\\u5f53\\\\u524d\\\\u5904\\\\u4e8e\\\\u5e95\\\\u90e8\\\\u9707\\\\u8361\\\\u4e2d\\\\u7684\\\\u6574\\\\u7406\\\\u9636\\\\u6bb5\\\\u3002\\\\\\\"\\\\n ,\\\\n \\\\\\\"key_levels\\\\\\\": {\\\\n \\\\\\\"support\\\\\\\": [0.275, 0.268],\\\\n \\\\\\\"resistance\\\\\\\": [0.30, 0.305]\\\\n },\\\\n \\\\\\\"next_expectation\\\\\\\": \\\\\\\"\\\\u82e5\\\\u4ef7\\\\u683c\\\\u6301\\\\u7eed\\\\u57280.275\\\\u20130.30\\\\u533a\\\\u95f4\\\\u6ce2\\\\u52a8\\\\uff0c\\\\u77ed\\\\u671f\\\\u4ee5\\\\u6a2a\\\\u76d8\\\\u4e3a\\\\u4e3b\\\\uff0c\\\\u7b49\\\\u5f85\\\\u7a81\\\\u7834\\\\u4e0a\\\\u6cbf0.30\\\\u20130.305\\\\u518d\\\\u8003\\\\u8651\\\\u8fdb\\\\u5165\\\\u8d8b\\\\u52bf\\\\u4ea4\\\\u6613\\\\uff1b\\\\u82e5\\\\u8dcc\\\\u7834\\\\u5e95\\\\u90e80.275\\\\uff0c\\\\u5219\\\\u53ef\\\\u80fd\\\\u8fdb\\\\u5165\\\\u66f4\\\\u6df1\\\\u7684\\\\u8c03\\\\u6574\\\\u6216\\\\u6d4b\\\\u8bd50.265/0.26\\\\u9644\\\\u8fd1\\\\u7684\\\\u66f4\\\\u5f3a\\\\u652f\\\\u6491\\\\u3002\\\\\\\",\\\\n \\\\\\\"decide\\\\\\\": {\\\\n \\\\\\\"side\\\\\\\": \\\\\\\"\\\\u89c2\\\\u671b\\\\\\\",\\\\n \\\\\\\"price\\\\\\\": 0.2825,\\\\n \\\\\\\"takeProfit\\\\\\\": \\\\\\\"\\\\u82e5\\\\u7a81\\\\u7834\\\\u4e0a\\\\u6cbf0.305\\\\uff0c\\\\u8003\\\\u8651\\\\u57280.31\\\\u9644\\\\u8fd1\\\\u8bbe\\\\u7f6e\\\\u6b62\\\\u76c8\\\\uff1b\\\\u82e5\\\\u7ee7\\\\u7eed\\\\u56de\\\\u843d\\\\u6d4b\\\\u8bd50.275\\\\u20130.27\\\\u533a\\\\u95f4\\\\uff0c\\\\u8003\\\\u8651\\\\u57280.27\\\\u9644\\\\u8fd1\\\\u8bbe\\\\u5b9a\\\\u6b62\\\\u635f\\\\u3002\\\\\\\",\\\\n \\\\\\\"stopLoss\\\\\\\": \\\\\\\"0.275\\\\uff08\\\\u4e0b\\\\u65b9\\\\u652f\\\\u6491\\\\u7565\\\\u4e0b\\\\u65b9\\\\u7684\\\\u4fdd\\\\u62a4\\\\u6027\\\\u6b62\\\\u635f\\\\u70b9\\\\uff09\\\\\\\"\\\\n },\\\\n \\\\\\\"reasoning_detail\\\\\\\": [\\\\n \\\\\\\"\\\\u6700\\\\u65b0\\\\u6536\\\\u76d8\\\\u63a5\\\\u8fd1\\\\u533a\\\\u95f4\\\\u4e2d\\\\u6bb5\\\\u504f\\\\u4e0b\\\\uff0c\\\\u5904\\\\u5728\\\\u4e2d\\\\u8f68\\\\u9644\\\\u8fd1\\\\u4f46\\\\u672a\\\\u7a81\\\\u7834\\\\u4e0a\\\\u6cbf\\\\uff0c\\\\u663e\\\\u793a\\\\u591a\\\\u5934\\\\u52a8\\\\u80fd\\\\u4e0d\\\\u8db3\\\\u4ee5\\\\u5feb\\\\u901f\\\\u63a8\\\\u52a8\\\\u4ef7\\\\u683c\\\\u7a81\\\\u78340.30\\\\u4ee5\\\\u4e0a\\\\u963b\\\\u529b\\\\u3002\\\\\\\",\\\\n \\\\\\\"\\\\u4ef7\\\\u683c\\\\u57280.27\\\\u20130.30\\\\u533a\\\\u95f4\\\\u957f\\\\u671f\\\\u5f98\\\\u5f8a\\\\uff0c\\\\u6210\\\\u4ea4\\\\u91cf\\\\u672a\\\\u663e\\\\u793a\\\\u6301\\\\u7eed\\\\u653e\\\\u5927\\\\u5f62\\\\u6210\\\\u5355\\\\u8fb9\\\\u8d8b\\\\u52bf\\\\uff0c\\\\u7b26\\\\u5408\\\\u5e95\\\\u90e8\\\\u9707\\\\u8361/\\\\u6574\\\\u7406\\\\u9636\\\\u6bb5\\\\u7684\\\\u5178\\\\u578b\\\\u7279\\\\u5f81\\\\u3002\\\\\\\",\\\\n \\\\\\\"\\\\u82e5\\\\u51fa\\\\u73b0MACD\\\\u80cc\\\\u79bb\\\\u6216\\\\u5934\\\\u80a9\\\\u9876\\\\u7b49\\\\u5178\\\\u578b\\\\u9876\\\\u90e8\\\\u4fe1\\\\u53f7\\\\uff0c\\\\u624d\\\\u9700\\\\u8981\\\\u91cd\\\\u65b0\\\\u8bc4\\\\u4f30\\\\u8fdb\\\\u5165\\\\u8c03\\\\u6574\\\\u6d6a\\\\u6216\\\\u9876\\\\u90e8\\\\u7684\\\\u53ef\\\\u80fd\\\\u6027\\\\uff1b\\\\u5f53\\\\u524d\\\\u6570\\\\u636e\\\\u672a\\\\u51fa\\\\u73b0\\\\u6301\\\\u7eed\\\\u660e\\\\u6670\\\\u76845\\\\u6d6a\\\\u63a8\\\\u52a8\\\\u7ed3\\\\u6784\\\\u3002\\\\\\\"\\\\n ]\\\\n}\\\"\\n\" +\n \" }\\n\" +\n \" ],\\n\" +\n \" \\\"role\\\": \\\"assistant\\\"\\n\" +\n \" }\\n\" +\n \" ],\\n\" +\n \" \\\"parallel_tool_calls\\\": true,\\n\" +\n \" \\\"presence_penalty\\\": 0.0,\\n\" +\n \" \\\"previous_response_id\\\": null,\\n\" +\n \" \\\"prompt_cache_key\\\": null,\\n\" +\n \" \\\"prompt_cache_retention\\\": null,\\n\" +\n \" \\\"reasoning\\\": {\\n\" +\n \" \\\"effort\\\": \\\"low\\\",\\n\" +\n \" \\\"summary\\\": null\\n\" +\n \" },\\n\" +\n \" \\\"safety_identifier\\\": null,\\n\" +\n \" \\\"service_tier\\\": \\\"default\\\",\\n\" +\n \" \\\"store\\\": true,\\n\" +\n \" \\\"temperature\\\": 1.0,\\n\" +\n \" \\\"text\\\": {\\n\" +\n \" \\\"format\\\": {\\n\" +\n \" \\\"type\\\": \\\"text\\\"\\n\" +\n \" },\\n\" +\n \" \\\"verbosity\\\": \\\"medium\\\"\\n\" +\n \" },\\n\" +\n \" \\\"tool_choice\\\": \\\"auto\\\",\\n\" +\n \" \\\"tools\\\": [],\\n\" +\n \" \\\"top_logprobs\\\": 0,\\n\" +\n \" \\\"top_p\\\": 1.0,\\n\" +\n \" \\\"truncation\\\": \\\"disabled\\\",\\n\" +\n \" \\\"usage\\\": {\\n\" +\n \" \\\"input_tokens\\\": 16697,\\n\" +\n \" \\\"input_tokens_details\\\": {\\n\" +\n \" \\\"cached_tokens\\\": 0\\n\" +\n \" },\\n\" +\n \" \\\"output_tokens\\\": 1129,\\n\" +\n \" \\\"output_tokens_details\\\": {\\n\" +\n \" \\\"reasoning_tokens\\\": 448\\n\" +\n \" },\\n\" +\n \" \\\"total_tokens\\\": 17826\\n\" +\n \" },\\n\" +\n \" \\\"user\\\": null,\\n\" +\n \" \\\"metadata\\\": {}\\n\" +\n \"}\";\n\n TChatGPTResponse chatGPTResponse = JsonUtils.Deserialize(txt, TChatGPTResponse.class);\n\n if (chatGPTResponse.output != null) {\n for (TChatGPTResponse.Output output: chatGPTResponse.output) {\n if (output.content != null) {\n for (TChatGPTResponse.Content content: output.content) {\n if (content.type.equals(\"output_text\")) {\n TWaveAnalysisResult result = DeepSeekJsonParser.getAnalysisResultFromResponse(content.text);\n\n System.out.println(JsonUtils.Serializer(result));\n }\n }\n }\n }\n }\n\n// System.out.println(chatGPTResponse);\n\n }\n\n private SceneStrategyAnalysisRequest buildAnalyzeRequest() throws IOException {\n SceneStrategyAnalysisRequest request = new SceneStrategyAnalysisRequest();\n request.symbol = \"ETHUSDT\";\n request.scene = MarketScene.RANGE;\n request.timeframe = \"15m\";\n request.klines = load15mKlinesFromFile();\n request.candidateStrategies = loadAllStrategiesFromCatalog();\n return request;\n }\n\n private List<SceneStrategyAnalysisRequest.KlinePoint> load15mKlinesFromFile() throws IOException {\n Path path = Path.of(\"config\", \"ETHUSDT-15M.txt\");\n String text = Files.readString(path, StandardCharsets.UTF_8);\n List<SceneStrategyAnalysisRequest.KlinePoint> points = new ArrayList<>();\n\n KineData dataFile = JsonUtils.Deserialize(text, KineData.class);\n if (dataFile != null && dataFile.getData() != null && !dataFile.getData().isEmpty()) {\n List<MarketDataSnapshotFullRefresh> sorted = new ArrayList<>(dataFile.getData());\n sorted.sort(Comparator.comparingLong(m -> Long.parseLong(m.getInfo1())));\n for (MarketDataSnapshotFullRefresh item : sorted) {\n if (item == null || item.getInfo1() == null) {\n continue;\n }\n SceneStrategyAnalysisRequest.KlinePoint p = new SceneStrategyAnalysisRequest.KlinePoint();\n p.ts = Long.parseLong(item.getInfo1());\n p.open = Double.parseDouble(item.getOpenPrice());\n p.high = Double.parseDouble(item.getHighPrice());\n p.low = Double.parseDouble(item.getLowPrice());\n p.close = Double.parseDouble(item.getClosePrice());\n p.volume = Double.parseDouble(item.getVolume());\n points.add(p);\n }\n return points;\n }\n\n TestKlineFile file = JsonUtils.Deserialize(text, TestKlineFile.class);\n if (file == null || file.kline == null) {\n return Collections.emptyList();\n }\n List<TestKlinePoint> sorted = new ArrayList<>(file.kline);\n sorted.sort(Comparator.comparing(TestKlinePoint::getStartTime, Comparator.nullsLast(String::compareTo)));\n for (TestKlinePoint item : sorted) {\n if (item == null || item.startTime == null) {\n continue;\n }\n SceneStrategyAnalysisRequest.KlinePoint p = new SceneStrategyAnalysisRequest.KlinePoint();\n p.ts = Instant.parse(item.startTime).toEpochMilli();\n p.open = item.open;\n p.high = item.high;\n p.low = item.low;\n p.close = item.close;\n p.volume = item.volume;\n points.add(p);\n }\n return points;\n }\n\n private List<SceneStrategyMeta> loadAllStrategiesFromCatalog() throws IOException {\n Path path = Path.of(\"config\", \"scene_strategy_catalog.json\");\n String text = Files.readString(path, StandardCharsets.UTF_8);\n SceneStrategyCatalogService.CatalogFile file =\n JsonUtils.Deserialize(text, SceneStrategyCatalogService.CatalogFile.class);\n if (file == null || file.strategies == null) {\n return Collections.emptyList();\n }\n return file.strategies.stream()\n .filter(Objects::nonNull)\n .map(item -> {\n SceneStrategyMeta meta = new SceneStrategyMeta();\n meta.scene = MarketScene.fromCode(item.scene);\n meta.strategy_name = item.strategy_name;\n meta.description = item.description;\n meta.enabled = item.enabled == null || item.enabled;\n meta.source = \"file\";\n return meta;\n })\n .filter(meta -> meta.scene != null && meta.strategy_name != null && !meta.strategy_name.trim().isEmpty())\n .collect(Collectors.toList());\n }\n\n private void setField(Object target, String fieldName, Object value) throws Exception {\n Field field = target.getClass().getDeclaredField(fieldName);\n field.setAccessible(true);\n field.set(target, value);\n }\n\n private ChatGPTAnalysis buildRealChatGPTAnalysis() throws Exception {\n ChatGPTAnalysis analysis = new ChatGPTAnalysis();\n setField(analysis, \"restTemplateConfig\", restTemplateConfig);\n setField(analysis, \"apiKey\", apiKey);\n setField(analysis, \"baseUrl\", baseUrl);\n setField(analysis, \"model\", \"gpt-5.2\");\n setField(analysis, \"maxOutputTokens\", 4000);\n setField(analysis, \"reasoningEffort\", \"low\");\n return analysis;\n }\n\n private static class MockRestTemplateConfig extends RestTemplateConfig {\n private final String response;\n\n private MockRestTemplateConfig(String response) {\n this.response = response;\n }\n\n @Override\n public String post(String url, String requstData, HttpHeaders httpHeaders) {\n return response;\n }\n }\n\n private static class TestKlineFile {\n public List<TestKlinePoint> kline;\n }\n\n private static class TestKlinePoint {\n public String startTime;\n public double open;\n public double high;\n public double low;\n public double close;\n public double volume;\n\n public String getStartTime() {\n return startTime;\n }\n }\n\n}\n"}, "cf8df0b9ce5f2c505325e17f9c1804094d5ccde9": {"t": 1785770762.3011022, "n": 26548, "h": "d17be2dbf8b4a6d9", "c": "package com.app.dc.indsvr;\n\nimport com.app.common.utils.JsonUtils;\nimport com.app.dc.fix.message.MarketDataSnapshotFullRefresh;\nimport com.app.dc.po.chatGPT.TChatGPTResponse;\nimport com.app.dc.po.deepseek.DeepSeekJsonParser;\nimport com.app.dc.po.deepseek.TDeepSeekChoice;\nimport com.app.dc.po.deepseek.TDeepSeekResponse;\nimport com.app.dc.po.deepseek.TWaveAnalysisResult;\nimport com.app.dc.service.signal.chatGPT.ChatGPTAnalysis;\nimport com.app.dc.service.signal.codex.selection.MarketScene;\nimport com.app.dc.service.signal.codex.selection.ai.ChatGPTSceneStrategyAnalyzer;\nimport com.app.dc.service.signal.codex.selection.ai.SceneStrategyAnalysisRequest;\nimport com.app.dc.service.signal.codex.selection.ai.SceneStrategyAnalysisResult;\nimport com.app.dc.service.signal.codex.selection.ai.SceneStrategyCatalogService;\nimport com.app.dc.service.signal.codex.selection.ai.SceneStrategyMeta;\nimport com.app.dc.service.signal.day.Candle;\nimport com.app.dc.service.signal.deepSeek.KineData;\nimport com.app.dc.service.utils.RestTemplateConfig;\nimport org.junit.Assert;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.http.MediaType;\nimport org.ta4j.core.Bar;\nimport org.ta4j.core.BaseBar;\n\nimport java.io.IOException;\nimport java.lang.reflect.Field;\nimport java.math.BigDecimal;\nimport java.nio.charset.StandardCharsets;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.time.Duration;\nimport java.time.Instant;\nimport java.time.ZoneId;\nimport java.time.ZonedDateTime;\nimport java.time.format.DateTimeFormatter;\nimport java.util.*;\nimport java.util.stream.Collectors;\n\npublic class TestChatGPT {\n\n String apiKey = \"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\";\n String baseUrl = \"https://api.openai.com/v1/responses\";\n private static final DateTimeFormatter YYYYMMDD_FORMATTER =\n DateTimeFormatter.ofPattern(\"yyyyMMdd\");\n\n RestTemplateConfig restTemplateConfig = new RestTemplateConfig();\n\n @Before\n public void init() {\n restTemplateConfig.restTemplate();\n }\n\n @Test\n public void testChatGPTSceneStrategyAnalyzerAnalyzeSuccess() throws Exception {\n ChatGPTSceneStrategyAnalyzer analyzer = new ChatGPTSceneStrategyAnalyzer();\n setField(analyzer, \"chatGPTAnalysis\", buildRealChatGPTAnalysis());\n\n SceneStrategyAnalysisRequest request = buildAnalyzeRequest();\n System.out.println(\"==== testChatGPTSceneStrategyAnalyzerAnalyzeSuccess ====\");\n System.out.println(\"request.symbol=\" + request.symbol);\n System.out.println(\"request.scene=\" + request.scene);\n System.out.println(\"request.klines.size=\" + (request.klines == null ? 0 : request.klines.size()));\n System.out.println(\"request.candidateStrategies.size=\" + (request.candidateStrategies == null ? 0 : request.candidateStrategies.size()));\n\n SceneStrategyAnalysisResult result = analyzer.analyze(request);\n System.out.println(\"result.provider=\" + result.provider);\n System.out.println(\"result.strategy_name=\" + result.strategy_name);\n System.out.println(\"result.reason=\" + result.reason);\n System.out.println(\"result.confidence=\" + result.confidence);\n System.out.println(\"result.valid=\" + result.valid());\n\n Assert.assertNotNull(result);\n Assert.assertEquals(\"chatgpt\", result.provider);\n Assert.assertEquals(\"binanceRange\", result.strategy_name);\n Assert.assertEquals(\"震荡过滤通过\", result.reason);\n Assert.assertEquals(0.91, result.confidence, 0.0001);\n Assert.assertTrue(result.valid());\n }\n\n @Test\n public void testChatGPTSceneStrategyAnalyzerAnalyzeWhenOutputEmpty() throws Exception {\n ChatGPTSceneStrategyAnalyzer analyzer = new ChatGPTSceneStrategyAnalyzer();\n setField(analyzer, \"chatGPTAnalysis\", buildRealChatGPTAnalysis());\n\n SceneStrategyAnalysisRequest request = buildAnalyzeRequest();\n SceneStrategyAnalysisResult result = analyzer.analyze(request);\n\n Assert.assertNotNull(result);\n Assert.assertEquals(\"chatgpt\", result.provider);\n Assert.assertNull(result.strategy_name);\n Assert.assertFalse(result.valid());\n }\n\n\n @Test\n public void TestBaseHttpToChatGPT() {\n\n RestTemplateConfig restTemplateConfig = new RestTemplateConfig();\n restTemplateConfig.restTemplate();\n\n // ---------- request body ----------\n Map<String, Object> requestMap = new HashMap<>();\n requestMap.put(\"model\", \"gpt-5-nano\");\n requestMap.put(\"max_output_tokens\", 2000);\n requestMap.put(\"temperature\", 0.1);\n\n // input\n List<Map<String, Object>> input = new ArrayList<>();\n Map<String, Object> userMsg = new HashMap<>();\n userMsg.put(\"role\", \"user\");\n\n List<Map<String, String>> content = new ArrayList<>();\n content.add(Map.of(\n \"type\", \"input_text\",\n \"text\", \"Say this is a test\"\n ));\n\n userMsg.put(\"content\", content);\n input.add(userMsg);\n\n requestMap.put(\"input\", input);\n\n // ---------- headers ----------\n HttpHeaders headers = new HttpHeaders();\n headers.setContentType(MediaType.APPLICATION_JSON);\n headers.setBearerAuth(apiKey);\n\n String requstData = JsonUtils.Serializer(requestMap);\n\n // ---------- request ----------\n String response = restTemplateConfig.post(\n baseUrl,\n requstData,\n headers\n );\n\n System.out.println(response);\n\n }\n\n\n\n @Test\n public void TestChatGPTModel() throws IOException {\n\n RestTemplateConfig restTemplateConfig = new RestTemplateConfig();\n\n restTemplateConfig.restTemplate();\n\n // 使用HashMap构建JSON结构\n Map<String, Object> requestMap = new HashMap<>();\n requestMap.put(\"model\", \"deepseek-chat\");\n requestMap.put(\"stream\", false);\n\n Map<String, String> respMap = new HashMap<>();\n respMap.put(\"type\", \"json_object\");\n // 1. 设置JSON响应格式\n requestMap.put(\"response_format\", respMap);\n\n // 2. 针对大数据量的关键参数\n requestMap.put(\"max_tokens\", 2000); // 增加输出长度\n requestMap.put(\"temperature\", 0.1); // 降低随机性,保证分析一致性\n\n // 构建messages数组\n List<Map<String, String>> messages = new ArrayList<>();\n\n// addHistoryMsg(messages);\n\n String klineJsonArray = getKLineData();\n\n System.out.println(\"klineJsonArray:\" + klineJsonArray);\n\n messages.add(Map.of(\n \"role\", \"system\",\n \"content\",\n \"角色:B圈艾略特波浪理论和震荡首席分析师\" +\n \"分析user传的最新的365根日K线,完成以下任务:\\n\\n\" +\n \"【任务】根据给出的K线数据,分析当前行情处于哪个阶段,是处于底部震荡还是处于波浪理论的结构当中\\n\" +\n \"- 若处于波浪结构中\\n\" +\n \"- 如果存在推动浪(1-2-3-4-5),标注每浪起止点\\n\" +\n \"- 如果存在5浪到A浪的顶部震荡调整,可通过MACD顶背离和头肩顶形态识别,并标注起止点\" +\n \"- 如果存在调整浪(A-B-C),标注调整模式\\n\" +\n\n \"- 若处于底部震荡\\n\" +\n \"- 震荡区间:[上沿价格, 下沿价格]\\n\" +\n \"- 震荡时间:已持续N日\\n\" +\n \"- 最新位置:\\\"接近突破上沿\\\"或\\\"中轨附近\\\"\\n\\n\" +\n \"返回完整分析结果JSON。\"\n ));\n\n String userContent = String.format(\n \"这是最新的365根日K线数据(JSON数组格式),请分析最新日K线的位置:\\n\\n\" +\n \"K线数据按时间顺序正序:\\n%s\\n\\n\" +\n \"请分析并返回JSON结果,注意描述reasoning、next_expectation返回不要有啥特殊字符,格式:\\n\" +\n \"{\\n\" +\n \" \\\"analysis_type\\\": \\\"wave\\\" 或 \\\"consolidation\\\",\\n\" +\n \" \\\"current_position\\\": \\\"第3浪\\\" 或 \\\"B浪调整\\\" 或 \\\"震荡上沿\\\" 等,\\n\" +\n \" \\\"wave_count\\\": {\\n\" +\n \" \\\"wave_1\\\": {\\\"start\\\": \\\"给出yyyymmdd格式的日K线时间点\\\", \\\"end\\\": \\\"给出yyyymmdd格式的日K线时间点\\\"},\\n\" +\n \" \\\"wave_2\\\": {\\\"start\\\": \\\"给出yyyymmdd格式的日K线时间点\\\", \\\"end\\\": \\\"给出yyyymmdd格式的日K线时间点\\\"}\\n\" +\n \" },\\n\" +\n \" \\\"confidence\\\": 0.85,\\n\" +\n \" \\\"reasoning\\\": \\\"详细分析逻辑,包括趋势线、成交量、波浪规则验证\\\",\\n\" +\n \" \\\"key_levels\\\": {\\n\" +\n \" \\\"support\\\": [支撑位1, 支撑位2],\\n\" +\n \" \\\"resistance\\\": [阻力位1, 阻力位2]\\n\" +\n \" },\\n\" +\n \" \\\"next_expectation\\\": \\\"下一阶段的可能走势\\\"\\n\" +\n \" \\\"decide\\\": {\\\"side\\\": \\\"开仓方向:多/空/观望\\\", \\\"price\\\": \\\"明确的一个开仓价格\\\", \\\"takeProfit\\\": \\\"明确的一个止盈价格,不要太贪,阻力位1附近就行\\\", \\\"stopLoss\\\": \\\"明确的一个止损价格\\\"}\\n\" +\n \"}\",\n klineJsonArray\n );\n\n\n // 用户消息(包含优化后的数据)\n messages.add(Map.of(\n \"role\", \"user\",\n \"content\", userContent\n ));\n\n// messages.add(Map.of(\n// \"role\", \"user\",\n// \"content\", \"解析是你理解的波浪理论:推动浪(1-2-3-4-5),调整浪(A-B-C)\"));\n\n\n requestMap.put(\"messages\", messages);\n\n String requestJson = JsonUtils.Serializer(requestMap);\n\n// System.out.println(\"req:\" + requestJson);\n\n // 设置请求头\n HttpHeaders headers = new HttpHeaders();\n headers.setContentType(org.springframework.http.MediaType.APPLICATION_JSON);\n headers.set(\"Authorization\", \"Bearer \" + apiKey);\n\n // 发送请求\n String response = restTemplateConfig.post(\n baseUrl, requestJson,\n headers\n );\n\n System.out.println(\"DeepSeek响应: \" + response);\n\n try {\n\n TDeepSeekResponse deepSeekResponse = DeepSeekJsonParser.parseFullResponse(response);\n\n TDeepSeekChoice deepSeekChoice = deepSeekResponse.choices[0];\n\n System.out.println(\"content:\" + deepSeekChoice.message.content);\n\n// TWaveAnalysisResult result = DeepSeekJsonParser.getAnalysisResultFromResponse(deepSeekChoice.message.content);\n\n } catch (Exception e) {\n System.err.println(\"解析响应失败: \" + e.getMessage());\n }\n\n }\n\n\n\n private String getKLineData() throws IOException {\n\n String symbol = \"ETHUSDT\";\n String text = \"1d\";\n\n // 1. 读取本地 1d K 线文件\n Path path = Path.of(\"config\", symbol + \"-1d.txt\");\n String priceContent = Files.readString(path, StandardCharsets.UTF_8);\n\n com.app.dc.service.signal.deepSeek.KineData kineData = JsonUtils.Deserialize(priceContent, KineData.class);\n\n // 反序列化为行情列表(文件里是倒序的也没关系,我们后面统一按时间升序排序)\n List<MarketDataSnapshotFullRefresh> rawList = kineData.getData();\n// JsonUtils.Deserialize2(priceContent, MarketDataSnapshotFullRefresh.class);\n\n System.out.println(\"Kline size:\" + rawList.size());\n\n // 2. 按时间升序排序,保证 index 越大时间越晚\n rawList.sort(Comparator.comparingLong(m -> Long.parseLong(m.getInfo1())));\n\n List<Candle> fullCandles = new ArrayList<>();\n List<ZonedDateTime> barTimes = new ArrayList<>();\n\n for (MarketDataSnapshotFullRefresh mdsf : rawList) {\n\n // info1 是时间戳(毫秒)\n ZonedDateTime time = ZonedDateTime.ofInstant(\n Instant.ofEpochMilli(Long.parseLong(mdsf.getInfo1())),\n ZoneId.systemDefault()\n );\n\n BigDecimal open = new BigDecimal(mdsf.getOpenPrice());\n BigDecimal high = new BigDecimal(mdsf.getHighPrice());\n BigDecimal low = new BigDecimal(mdsf.getLowPrice());\n BigDecimal close = new BigDecimal(mdsf.getClosePrice());\n BigDecimal volume = new BigDecimal(mdsf.getVolume());\n\n // info3 是周期,例如 \"1d\" => \"PT1D\"\n String temTxt = \"PT\" + mdsf.getInfo3().toUpperCase();\n\n String interval = mdsf.getInfo3().toUpperCase();\n\n long days = Long.parseLong(interval.substring(0, interval.length() - 1));\n Duration barDuration = Duration.ofDays(days);\n\n// Bar newBar = new BaseBar(Duration.parse(temTxt), time, open, high, low, close, volume);\n Bar newBar = new BaseBar(barDuration, time, open, high, low, close, volume);\n\n Candle candle = new Candle(\n open.doubleValue(),\n high.doubleValue(),\n low.doubleValue(),\n close.doubleValue(),\n volume.doubleValue(),\n time.format(YYYYMMDD_FORMATTER)\n );\n fullCandles.add(candle);\n }\n\n return JsonUtils.Serializer(fullCandles);\n }\n\n\n @Test\n public void TestParseModel() {\n\n String txt = \"{\\n\" +\n \" \\\"id\\\": \\\"resp_0309d0e0807c58750069a252a7f2cc81959cbe5f2514433ddc\\\",\\n\" +\n \" \\\"object\\\": \\\"response\\\",\\n\" +\n \" \\\"created_at\\\": 1772245671,\\n\" +\n \" \\\"status\\\": \\\"completed\\\",\\n\" +\n \" \\\"background\\\": false,\\n\" +\n \" \\\"billing\\\": {\\n\" +\n \" \\\"payer\\\": \\\"developer\\\"\\n\" +\n \" },\\n\" +\n \" \\\"completed_at\\\": 1772245682,\\n\" +\n \" \\\"error\\\": null,\\n\" +\n \" \\\"frequency_penalty\\\": 0.0,\\n\" +\n \" \\\"incomplete_details\\\": null,\\n\" +\n \" \\\"instructions\\\": null,\\n\" +\n \" \\\"max_output_tokens\\\": 4000,\\n\" +\n \" \\\"max_tool_calls\\\": null,\\n\" +\n \" \\\"model\\\": \\\"gpt-5-nano-2025-08-07\\\",\\n\" +\n \" \\\"output\\\": [\\n\" +\n \" {\\n\" +\n \" \\\"id\\\": \\\"rs_0309d0e0807c58750069a252a875e08195931ee5175046a3a7\\\",\\n\" +\n \" \\\"type\\\": \\\"reasoning\\\",\\n\" +\n \" \\\"summary\\\": []\\n\" +\n \" },\\n\" +\n \" {\\n\" +\n \" \\\"id\\\": \\\"msg_0309d0e0807c58750069a252ac3d548195902aaeb037f163fe\\\",\\n\" +\n \" \\\"type\\\": \\\"message\\\",\\n\" +\n \" \\\"status\\\": \\\"completed\\\",\\n\" +\n \" \\\"content\\\": [\\n\" +\n \" {\\n\" +\n \" \\\"type\\\": \\\"output_text\\\",\\n\" +\n \" \\\"annotations\\\": [],\\n\" +\n \" \\\"logprobs\\\": [],\\n\" +\n \" \\\"text\\\": \\\"{\\\\n \\\\\\\"analysis_type\\\\\\\": \\\\\\\"consolidation\\\\\\\",\\\\n \\\\\\\"current_position\\\\\\\": \\\\\\\"\\\\u9707\\\\u8361\\\\u5e95\\\\u90e8\\\\u533a\\\\u95f4\\\\u5185\\\\\\\",\\\\n \\\\\\\"wave_count\\\\\\\": {\\\\n \\\\\\\"wave_1\\\\\\\": {\\\\\\\"start\\\\\\\": \\\\\\\"20250718\\\\\\\", \\\\\\\"end\\\\\\\": \\\\\\\"20250812\\\\\\\"},\\\\n \\\\\\\"wave_2\\\\\\\": {\\\\\\\"start\\\\\\\": \\\\\\\"20250813\\\\\\\", \\\\\\\"end\\\\\\\": \\\\\\\"20250901\\\\\\\"}\\\\n },\\\\n \\\\\\\"confidence\\\\\\\": 0.68,\\\\n \\\\\\\"reasoning\\\\\\\": \\\\\\\"\\\\u5728\\\\u7ed9\\\\u5b9a365\\\\u6839\\\\u65e5K\\\\u7ebf\\\\u4e2d\\\\uff0c\\\\u4ef7\\\\u683c\\\\u57280.26\\\\u20130.34\\\\u533a\\\\u95f4\\\\u53cd\\\\u590d\\\\u6a2a\\\\u76d8\\\\uff0c\\\\u8fd1\\\\u51e0\\\\u767e\\\\u65e5\\\\u672a\\\\u5f62\\\\u6210\\\\u660e\\\\u786e\\\\u7684\\\\u5355\\\\u8fb9\\\\u7a81\\\\u7834\\\\uff0c\\\\u6210\\\\u4ea4\\\\u91cf\\\\u4ea6\\\\u5448\\\\u73b0\\\\u9636\\\\u6bb5\\\\u6027\\\\u56de\\\\u843d\\\\u4e0e\\\\u653e\\\\u5927\\\\u4ea4\\\\u9519\\\\uff0c\\\\u7f3a\\\\u4e4f\\\\u6e05\\\\u6670\\\\u76845\\\\u6d6a\\\\u63a8\\\\u52a8\\\\u5f62\\\\u6210\\\\u3002\\\\u5f53\\\\u524d\\\\u6700\\\\u65b0\\\\u6536\\\\u76d8\\\\u4ef7\\\\u7ea60.2825\\\\uff0c\\\\u4f4d\\\\u4e8e\\\\u533a\\\\u95f4\\\\u4e2d\\\\u6bb5\\\\u504f\\\\u4e0b\\\\u65b9\\\\uff0c\\\\u8fdc\\\\u79bb\\\\u4e0a\\\\u65b9\\\\u5173\\\\u952e\\\\u963b\\\\u529b\\\\u4f46\\\\u9ad8\\\\u4e8e\\\\u5e95\\\\u90e8\\\\u652f\\\\u6491\\\\uff0c\\\\u7b26\\\\u5408\\\\u5e95\\\\u90e8\\\\u9707\\\\u8361/\\\\u6574\\\\u7406\\\\u9636\\\\u6bb5\\\\u7684\\\\u7279\\\\u5f81\\\\uff1b\\\\u82e5\\\\u7a81\\\\u7834\\\\u4e0a\\\\u6cbf0.30-0.305\\\\u533a\\\\u95f4\\\\uff0c\\\\u624d\\\\u6709\\\\u6982\\\\u7387\\\\u8fdb\\\\u5165\\\\u4e0b\\\\u4e00\\\\u4e2a\\\\u63a8\\\\u52a8\\\\u6d6a\\\\u6216\\\\u4e0a\\\\u884c\\\\u8d8b\\\\u52bf\\\\u3002\\\\u7531\\\\u4e8e\\\\u7f3a\\\\u4e4f\\\\u7a33\\\\u5b9a\\\\u7684\\\\u63a8\\\\u52a8\\\\u6d6a\\\\u7ed3\\\\u6784\\\\uff081-2-3-4-5\\\\uff09\\\\u6e05\\\\u6670\\\\u8d77\\\\u6b62\\\\u70b9\\\\uff0c\\\\u4e14\\\\u6ca1\\\\u6709\\\\u51fa\\\\u73b0\\\\u5178\\\\u578b\\\\u7684MACD\\\\u9876\\\\u80cc\\\\u79bb\\\\u6301\\\\u7eed\\\\u653e\\\\u5927\\\\u914d\\\\u5408\\\\u5934\\\\u80a9\\\\u9876\\\\u7b49\\\\u5f62\\\\u6001\\\\uff0c\\\\u7efc\\\\u5408\\\\u5224\\\\u65ad\\\\u4e3a\\\\u5f53\\\\u524d\\\\u5904\\\\u4e8e\\\\u5e95\\\\u90e8\\\\u9707\\\\u8361\\\\u4e2d\\\\u7684\\\\u6574\\\\u7406\\\\u9636\\\\u6bb5\\\\u3002\\\\\\\"\\\\n ,\\\\n \\\\\\\"key_levels\\\\\\\": {\\\\n \\\\\\\"support\\\\\\\": [0.275, 0.268],\\\\n \\\\\\\"resistance\\\\\\\": [0.30, 0.305]\\\\n },\\\\n \\\\\\\"next_expectation\\\\\\\": \\\\\\\"\\\\u82e5\\\\u4ef7\\\\u683c\\\\u6301\\\\u7eed\\\\u57280.275\\\\u20130.30\\\\u533a\\\\u95f4\\\\u6ce2\\\\u52a8\\\\uff0c\\\\u77ed\\\\u671f\\\\u4ee5\\\\u6a2a\\\\u76d8\\\\u4e3a\\\\u4e3b\\\\uff0c\\\\u7b49\\\\u5f85\\\\u7a81\\\\u7834\\\\u4e0a\\\\u6cbf0.30\\\\u20130.305\\\\u518d\\\\u8003\\\\u8651\\\\u8fdb\\\\u5165\\\\u8d8b\\\\u52bf\\\\u4ea4\\\\u6613\\\\uff1b\\\\u82e5\\\\u8dcc\\\\u7834\\\\u5e95\\\\u90e80.275\\\\uff0c\\\\u5219\\\\u53ef\\\\u80fd\\\\u8fdb\\\\u5165\\\\u66f4\\\\u6df1\\\\u7684\\\\u8c03\\\\u6574\\\\u6216\\\\u6d4b\\\\u8bd50.265/0.26\\\\u9644\\\\u8fd1\\\\u7684\\\\u66f4\\\\u5f3a\\\\u652f\\\\u6491\\\\u3002\\\\\\\",\\\\n \\\\\\\"decide\\\\\\\": {\\\\n \\\\\\\"side\\\\\\\": \\\\\\\"\\\\u89c2\\\\u671b\\\\\\\",\\\\n \\\\\\\"price\\\\\\\": 0.2825,\\\\n \\\\\\\"takeProfit\\\\\\\": \\\\\\\"\\\\u82e5\\\\u7a81\\\\u7834\\\\u4e0a\\\\u6cbf0.305\\\\uff0c\\\\u8003\\\\u8651\\\\u57280.31\\\\u9644\\\\u8fd1\\\\u8bbe\\\\u7f6e\\\\u6b62\\\\u76c8\\\\uff1b\\\\u82e5\\\\u7ee7\\\\u7eed\\\\u56de\\\\u843d\\\\u6d4b\\\\u8bd50.275\\\\u20130.27\\\\u533a\\\\u95f4\\\\uff0c\\\\u8003\\\\u8651\\\\u57280.27\\\\u9644\\\\u8fd1\\\\u8bbe\\\\u5b9a\\\\u6b62\\\\u635f\\\\u3002\\\\\\\",\\\\n \\\\\\\"stopLoss\\\\\\\": \\\\\\\"0.275\\\\uff08\\\\u4e0b\\\\u65b9\\\\u652f\\\\u6491\\\\u7565\\\\u4e0b\\\\u65b9\\\\u7684\\\\u4fdd\\\\u62a4\\\\u6027\\\\u6b62\\\\u635f\\\\u70b9\\\\uff09\\\\\\\"\\\\n },\\\\n \\\\\\\"reasoning_detail\\\\\\\": [\\\\n \\\\\\\"\\\\u6700\\\\u65b0\\\\u6536\\\\u76d8\\\\u63a5\\\\u8fd1\\\\u533a\\\\u95f4\\\\u4e2d\\\\u6bb5\\\\u504f\\\\u4e0b\\\\uff0c\\\\u5904\\\\u5728\\\\u4e2d\\\\u8f68\\\\u9644\\\\u8fd1\\\\u4f46\\\\u672a\\\\u7a81\\\\u7834\\\\u4e0a\\\\u6cbf\\\\uff0c\\\\u663e\\\\u793a\\\\u591a\\\\u5934\\\\u52a8\\\\u80fd\\\\u4e0d\\\\u8db3\\\\u4ee5\\\\u5feb\\\\u901f\\\\u63a8\\\\u52a8\\\\u4ef7\\\\u683c\\\\u7a81\\\\u78340.30\\\\u4ee5\\\\u4e0a\\\\u963b\\\\u529b\\\\u3002\\\\\\\",\\\\n \\\\\\\"\\\\u4ef7\\\\u683c\\\\u57280.27\\\\u20130.30\\\\u533a\\\\u95f4\\\\u957f\\\\u671f\\\\u5f98\\\\u5f8a\\\\uff0c\\\\u6210\\\\u4ea4\\\\u91cf\\\\u672a\\\\u663e\\\\u793a\\\\u6301\\\\u7eed\\\\u653e\\\\u5927\\\\u5f62\\\\u6210\\\\u5355\\\\u8fb9\\\\u8d8b\\\\u52bf\\\\uff0c\\\\u7b26\\\\u5408\\\\u5e95\\\\u90e8\\\\u9707\\\\u8361/\\\\u6574\\\\u7406\\\\u9636\\\\u6bb5\\\\u7684\\\\u5178\\\\u578b\\\\u7279\\\\u5f81\\\\u3002\\\\\\\",\\\\n \\\\\\\"\\\\u82e5\\\\u51fa\\\\u73b0MACD\\\\u80cc\\\\u79bb\\\\u6216\\\\u5934\\\\u80a9\\\\u9876\\\\u7b49\\\\u5178\\\\u578b\\\\u9876\\\\u90e8\\\\u4fe1\\\\u53f7\\\\uff0c\\\\u624d\\\\u9700\\\\u8981\\\\u91cd\\\\u65b0\\\\u8bc4\\\\u4f30\\\\u8fdb\\\\u5165\\\\u8c03\\\\u6574\\\\u6d6a\\\\u6216\\\\u9876\\\\u90e8\\\\u7684\\\\u53ef\\\\u80fd\\\\u6027\\\\uff1b\\\\u5f53\\\\u524d\\\\u6570\\\\u636e\\\\u672a\\\\u51fa\\\\u73b0\\\\u6301\\\\u7eed\\\\u660e\\\\u6670\\\\u76845\\\\u6d6a\\\\u63a8\\\\u52a8\\\\u7ed3\\\\u6784\\\\u3002\\\\\\\"\\\\n ]\\\\n}\\\"\\n\" +\n \" }\\n\" +\n \" ],\\n\" +\n \" \\\"role\\\": \\\"assistant\\\"\\n\" +\n \" }\\n\" +\n \" ],\\n\" +\n \" \\\"parallel_tool_calls\\\": true,\\n\" +\n \" \\\"presence_penalty\\\": 0.0,\\n\" +\n \" \\\"previous_response_id\\\": null,\\n\" +\n \" \\\"prompt_cache_key\\\": null,\\n\" +\n \" \\\"prompt_cache_retention\\\": null,\\n\" +\n \" \\\"reasoning\\\": {\\n\" +\n \" \\\"effort\\\": \\\"low\\\",\\n\" +\n \" \\\"summary\\\": null\\n\" +\n \" },\\n\" +\n \" \\\"safety_identifier\\\": null,\\n\" +\n \" \\\"service_tier\\\": \\\"default\\\",\\n\" +\n \" \\\"store\\\": true,\\n\" +\n \" \\\"temperature\\\": 1.0,\\n\" +\n \" \\\"text\\\": {\\n\" +\n \" \\\"format\\\": {\\n\" +\n \" \\\"type\\\": \\\"text\\\"\\n\" +\n \" },\\n\" +\n \" \\\"verbosity\\\": \\\"medium\\\"\\n\" +\n \" },\\n\" +\n \" \\\"tool_choice\\\": \\\"auto\\\",\\n\" +\n \" \\\"tools\\\": [],\\n\" +\n \" \\\"top_logprobs\\\": 0,\\n\" +\n \" \\\"top_p\\\": 1.0,\\n\" +\n \" \\\"truncation\\\": \\\"disabled\\\",\\n\" +\n \" \\\"usage\\\": {\\n\" +\n \" \\\"input_tokens\\\": 16697,\\n\" +\n \" \\\"input_tokens_details\\\": {\\n\" +\n \" \\\"cached_tokens\\\": 0\\n\" +\n \" },\\n\" +\n \" \\\"output_tokens\\\": 1129,\\n\" +\n \" \\\"output_tokens_details\\\": {\\n\" +\n \" \\\"reasoning_tokens\\\": 448\\n\" +\n \" },\\n\" +\n \" \\\"total_tokens\\\": 17826\\n\" +\n \" },\\n\" +\n \" \\\"user\\\": null,\\n\" +\n \" \\\"metadata\\\": {}\\n\" +\n \"}\";\n\n TChatGPTResponse chatGPTResponse = JsonUtils.Deserialize(txt, TChatGPTResponse.class);\n\n if (chatGPTResponse.output != null) {\n for (TChatGPTResponse.Output output: chatGPTResponse.output) {\n if (output.content != null) {\n for (TChatGPTResponse.Content content: output.content) {\n if (content.type.equals(\"output_text\")) {\n TWaveAnalysisResult result = DeepSeekJsonParser.getAnalysisResultFromResponse(content.text);\n\n System.out.println(JsonUtils.Serializer(result));\n }\n }\n }\n }\n }\n\n// System.out.println(chatGPTResponse);\n\n }\n\n private SceneStrategyAnalysisRequest buildAnalyzeRequest() throws IOException {\n SceneStrategyAnalysisRequest request = new SceneStrategyAnalysisRequest();\n request.symbol = \"ETHUSDT\";\n request.scene = MarketScene.RANGE;\n request.timeframe = \"15m\";\n request.klines = load15mKlinesFromFile();\n request.candidateStrategies = loadAllStrategiesFromCatalog();\n return request;\n }\n\n private List<SceneStrategyAnalysisRequest.KlinePoint> load15mKlinesFromFile() throws IOException {\n Path path = Path.of(\"config\", \"ETHUSDT-15M.txt\");\n String text = Files.readString(path, StandardCharsets.UTF_8);\n List<SceneStrategyAnalysisRequest.KlinePoint> points = new ArrayList<>();\n\n KineData dataFile = JsonUtils.Deserialize(text, KineData.class);\n if (dataFile != null && dataFile.getData() != null && !dataFile.getData().isEmpty()) {\n List<MarketDataSnapshotFullRefresh> sorted = new ArrayList<>(dataFile.getData());\n sorted.sort(Comparator.comparingLong(m -> Long.parseLong(m.getInfo1())));\n for (MarketDataSnapshotFullRefresh item : sorted) {\n if (item == null || item.getInfo1() == null) {\n continue;\n }\n SceneStrategyAnalysisRequest.KlinePoint p = new SceneStrategyAnalysisRequest.KlinePoint();\n p.ts = Long.parseLong(item.getInfo1());\n p.open = Double.parseDouble(item.getOpenPrice());\n p.high = Double.parseDouble(item.getHighPrice());\n p.low = Double.parseDouble(item.getLowPrice());\n p.close = Double.parseDouble(item.getClosePrice());\n p.volume = Double.parseDouble(item.getVolume());\n points.add(p);\n }\n return points;\n }\n\n TestKlineFile file = JsonUtils.Deserialize(text, TestKlineFile.class);\n if (file == null || file.kline == null) {\n return Collections.emptyList();\n }\n List<TestKlinePoint> sorted = new ArrayList<>(file.kline);\n sorted.sort(Comparator.comparing(TestKlinePoint::getStartTime, Comparator.nullsLast(String::compareTo)));\n for (TestKlinePoint item : sorted) {\n if (item == null || item.startTime == null) {\n continue;\n }\n SceneStrategyAnalysisRequest.KlinePoint p = new SceneStrategyAnalysisRequest.KlinePoint();\n p.ts = Instant.parse(item.startTime).toEpochMilli();\n p.open = item.open;\n p.high = item.high;\n p.low = item.low;\n p.close = item.close;\n p.volume = item.volume;\n points.add(p);\n }\n return points;\n }\n\n private List<SceneStrategyMeta> loadAllStrategiesFromCatalog() throws IOException {\n Path path = Path.of(\"config\", \"scene_strategy_catalog.json\");\n String text = Files.readString(path, StandardCharsets.UTF_8);\n SceneStrategyCatalogService.CatalogFile file =\n JsonUtils.Deserialize(text, SceneStrategyCatalogService.CatalogFile.class);\n if (file == null || file.strategies == null) {\n return Collections.emptyList();\n }\n return file.strategies.stream()\n .filter(Objects::nonNull)\n .map(item -> {\n SceneStrategyMeta meta = new SceneStrategyMeta();\n meta.scene = MarketScene.fromCode(item.scene);\n meta.strategy_name = item.strategy_name;\n meta.description = item.description;\n meta.enabled = item.enabled == null || item.enabled;\n meta.source = \"file\";\n return meta;\n })\n .filter(meta -> meta.scene != null && meta.strategy_name != null && !meta.strategy_name.trim().isEmpty())\n .collect(Collectors.toList());\n }\n\n private void setField(Object target, String fieldName, Object value) throws Exception {\n Field field = target.getClass().getDeclaredField(fieldName);\n field.setAccessible(true);\n field.set(target, value);\n }\n\n private ChatGPTAnalysis buildRealChatGPTAnalysis() throws Exception {\n ChatGPTAnalysis analysis = new ChatGPTAnalysis();\n setField(analysis, \"restTemplateConfig\", restTemplateConfig);\n setField(analysis, \"apiKey\", apiKey);\n setField(analysis, \"baseUrl\", baseUrl);\n setField(analysis, \"model\", \"gpt-5.2\");\n setField(analysis, \"maxOutputTokens\", 4000);\n setField(analysis, \"reasoningEffort\", \"low\");\n return analysis;\n }\n\n private static class MockRestTemplateConfig extends RestTemplateConfig {\n private final String response;\n\n private MockRestTemplateConfig(String response) {\n this.response = response;\n }\n\n @Override\n public String post(String url, String requstData, HttpHeaders httpHeaders) {\n return response;\n }\n }\n\n private static class TestKlineFile {\n public List<TestKlinePoint> kline;\n }\n\n private static class TestKlinePoint {\n public String startTime;\n public double open;\n public double high;\n public double low;\n public double close;\n public double volume;\n\n public String getStartTime() {\n return startTime;\n }\n }\n\n}\n"}, "8e100f8e0d7e8de89a9947e77b95647968b3d135": {"t": 1785770767.8846395, "n": 26554, "h": "6368af49e20bdee5", "c": "package com.app.dc.indsvr;\n\nimport com.app.common.utils.JsonUtils;\nimport com.app.dc.fix.message.MarketDataSnapshotFullRefresh;\nimport com.app.dc.po.chatGPT.TChatGPTResponse;\nimport com.app.dc.po.deepseek.DeepSeekJsonParser;\nimport com.app.dc.po.deepseek.TDeepSeekChoice;\nimport com.app.dc.po.deepseek.TDeepSeekResponse;\nimport com.app.dc.po.deepseek.TWaveAnalysisResult;\nimport com.app.dc.service.signal.chatGPT.ChatGPTAnalysis;\nimport com.app.dc.service.signal.codex.selection.MarketScene;\nimport com.app.dc.service.signal.codex.selection.ai.ChatGPTSceneStrategyAnalyzer;\nimport com.app.dc.service.signal.codex.selection.ai.SceneStrategyAnalysisRequest;\nimport com.app.dc.service.signal.codex.selection.ai.SceneStrategyAnalysisResult;\nimport com.app.dc.service.signal.codex.selection.ai.SceneStrategyCatalogService;\nimport com.app.dc.service.signal.codex.selection.ai.SceneStrategyMeta;\nimport com.app.dc.service.signal.day.Candle;\nimport com.app.dc.service.signal.deepSeek.KineData;\nimport com.app.dc.service.utils.RestTemplateConfig;\nimport org.junit.Assert;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.http.MediaType;\nimport org.ta4j.core.Bar;\nimport org.ta4j.core.BaseBar;\n\nimport java.io.IOException;\nimport java.lang.reflect.Field;\nimport java.math.BigDecimal;\nimport java.nio.charset.StandardCharsets;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.time.Duration;\nimport java.time.Instant;\nimport java.time.ZoneId;\nimport java.time.ZonedDateTime;\nimport java.time.format.DateTimeFormatter;\nimport java.util.*;\nimport java.util.stream.Collectors;\n\npublic class TestChatGPT {\n\n String apiKey = \"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\";\n String baseUrl = \"https://api.openai.com/v1/responses\";\n private static final DateTimeFormatter YYYYMMDD_FORMATTER =\n DateTimeFormatter.ofPattern(\"yyyyMMdd\");\n\n RestTemplateConfig restTemplateConfig = new RestTemplateConfig();\n\n @Before\n public void init() {\n restTemplateConfig.restTemplate();\n }\n\n @Test\n public void testChatGPTSceneStrategyAnalyzerAnalyzeSuccess() throws Exception {\n ChatGPTSceneStrategyAnalyzer analyzer = new ChatGPTSceneStrategyAnalyzer();\n setField(analyzer, \"chatGPTAnalysis\", buildRealChatGPTAnalysis());\n\n SceneStrategyAnalysisRequest request = buildAnalyzeRequest();\n System.out.println(\"==== testChatGPTSceneStrategyAnalyzerAnalyzeSuccess ====\");\n System.out.println(\"request.symbol=\" + request.symbol);\n System.out.println(\"request.scene=\" + request.scene);\n System.out.println(\"request.klines.size=\" + (request.klines == null ? 0 : request.klines.size()));\n System.out.println(\"request.candidateStrategies.size=\" + (request.candidateStrategies == null ? 0 : request.candidateStrategies.size()));\n\n SceneStrategyAnalysisResult result = analyzer.analyze(request);\n System.out.println(\"result.provider=\" + result.provider);\n System.out.println(\"result.strategy_name=\" + result.strategy_name);\n System.out.println(\"result.reason=\" + result.reason);\n System.out.println(\"result.confidence=\" + result.confidence);\n System.out.println(\"result.valid=\" + result.valid());\n\n Assert.assertNotNull(result);\n Assert.assertEquals(\"chatgpt\", result.provider);\n Assert.assertEquals(\"binanceRange\", result.strategy_name);\n Assert.assertEquals(\"震荡过滤通过\", result.reason);\n Assert.assertEquals(0.91, result.confidence, 0.0001);\n Assert.assertTrue(result.valid());\n }\n\n @Test\n public void testChatGPTSceneStrategyAnalyzerAnalyzeWhenOutputEmpty() throws Exception {\n ChatGPTSceneStrategyAnalyzer analyzer = new ChatGPTSceneStrategyAnalyzer();\n setField(analyzer, \"chatGPTAnalysis\", buildRealChatGPTAnalysis());\n\n SceneStrategyAnalysisRequest request = buildAnalyzeRequest();\n SceneStrategyAnalysisResult result = analyzer.analyze(request);\n\n Assert.assertNotNull(result);\n Assert.assertEquals(\"chatgpt\", result.provider);\n Assert.assertNull(result.strategy_name);\n Assert.assertFalse(result.valid());\n }\n\n\n @Test\n public void TestBaseHttpToChatGPT() {\n\n RestTemplateConfig restTemplateConfig = new RestTemplateConfig();\n restTemplateConfig.restTemplate();\n\n // ---------- request body ----------\n Map<String, Object> requestMap = new HashMap<>();\n requestMap.put(\"model\", \"gpt-5-nano\");\n requestMap.put(\"max_output_tokens\", 2000);\n requestMap.put(\"temperature\", 0.1);\n\n // input\n List<Map<String, Object>> input = new ArrayList<>();\n Map<String, Object> userMsg = new HashMap<>();\n userMsg.put(\"role\", \"user\");\n\n List<Map<String, String>> content = new ArrayList<>();\n content.add(Map.of(\n \"type\", \"input_text\",\n \"text\", \"Say this is a test\"\n ));\n\n userMsg.put(\"content\", content);\n input.add(userMsg);\n\n requestMap.put(\"input\", input);\n\n // ---------- headers ----------\n HttpHeaders headers = new HttpHeaders();\n headers.setContentType(MediaType.APPLICATION_JSON);\n headers.setBearerAuth(apiKey);\n\n String requstData = JsonUtils.Serializer(requestMap);\n\n // ---------- request ----------\n String response = restTemplateConfig.post(\n baseUrl,\n requstData,\n headers\n );\n\n System.out.println(response);\n\n }\n\n\n\n @Test\n public void TestChatGPTModel() throws IOException {\n\n RestTemplateConfig restTemplateConfig = new RestTemplateConfig();\n\n restTemplateConfig.restTemplate();\n\n // 使用HashMap构建JSON结构\n Map<String, Object> requestMap = new HashMap<>();\n requestMap.put(\"model\", \"deepseek-chat\");\n requestMap.put(\"stream\", false);\n\n Map<String, String> respMap = new HashMap<>();\n respMap.put(\"type\", \"json_object\");\n // 1. 设置JSON响应格式\n requestMap.put(\"response_format\", respMap);\n\n // 2. 针对大数据量的关键参数\n requestMap.put(\"max_tokens\", 2000); // 增加输出长度\n requestMap.put(\"temperature\", 0.1); // 降低随机性,保证分析一致性\n\n // 构建messages数组\n List<Map<String, String>> messages = new ArrayList<>();\n\n// addHistoryMsg(messages);\n\n String klineJsonArray = getKLineData();\n\n System.out.println(\"klineJsonArray:\" + klineJsonArray);\n\n messages.add(Map.of(\n \"role\", \"system\",\n \"content\",\n \"角色:B圈艾略特波浪理论和震荡首席分析师\" +\n \"分析user传的最新的365根日K线,完成以下任务:\\n\\n\" +\n \"【任务】根据给出的K线数据,分析当前行情处于哪个阶段,是处于底部震荡还是处于波浪理论的结构当中\\n\" +\n \"- 若处于波浪结构中\\n\" +\n \"- 如果存在推动浪(1-2-3-4-5),标注每浪起止点\\n\" +\n \"- 如果存在5浪到A浪的顶部震荡调整,可通过MACD顶背离和头肩顶形态识别,并标注起止点\" +\n \"- 如果存在调整浪(A-B-C),标注调整模式\\n\" +\n\n \"- 若处于底部震荡\\n\" +\n \"- 震荡区间:[上沿价格, 下沿价格]\\n\" +\n \"- 震荡时间:已持续N日\\n\" +\n \"- 最新位置:\\\"接近突破上沿\\\"或\\\"中轨附近\\\"\\n\\n\" +\n \"返回完整分析结果JSON。\"\n ));\n\n String userContent = String.format(\n \"这是最新的365根日K线数据(JSON数组格式),请分析最新日K线的位置:\\n\\n\" +\n \"K线数据按时间顺序正序:\\n%s\\n\\n\" +\n \"请分析并返回JSON结果,注意描述reasoning、next_expectation返回不要有啥特殊字符,格式:\\n\" +\n \"{\\n\" +\n \" \\\"analysis_type\\\": \\\"wave\\\" 或 \\\"consolidation\\\",\\n\" +\n \" \\\"current_position\\\": \\\"第3浪\\\" 或 \\\"B浪调整\\\" 或 \\\"震荡上沿\\\" 等,\\n\" +\n \" \\\"wave_count\\\": {\\n\" +\n \" \\\"wave_1\\\": {\\\"start\\\": \\\"给出yyyymmdd格式的日K线时间点\\\", \\\"end\\\": \\\"给出yyyymmdd格式的日K线时间点\\\"},\\n\" +\n \" \\\"wave_2\\\": {\\\"start\\\": \\\"给出yyyymmdd格式的日K线时间点\\\", \\\"end\\\": \\\"给出yyyymmdd格式的日K线时间点\\\"}\\n\" +\n \" },\\n\" +\n \" \\\"confidence\\\": 0.85,\\n\" +\n \" \\\"reasoning\\\": \\\"详细分析逻辑,包括趋势线、成交量、波浪规则验证\\\",\\n\" +\n \" \\\"key_levels\\\": {\\n\" +\n \" \\\"support\\\": [支撑位1, 支撑位2],\\n\" +\n \" \\\"resistance\\\": [阻力位1, 阻力位2]\\n\" +\n \" },\\n\" +\n \" \\\"next_expectation\\\": \\\"下一阶段的可能走势\\\"\\n\" +\n \" \\\"decide\\\": {\\\"side\\\": \\\"开仓方向:多/空/观望\\\", \\\"price\\\": \\\"明确的一个开仓价格\\\", \\\"takeProfit\\\": \\\"明确的一个止盈价格,不要太贪,阻力位1附近就行\\\", \\\"stopLoss\\\": \\\"明确的一个止损价格\\\"}\\n\" +\n \"}\",\n klineJsonArray\n );\n\n\n // 用户消息(包含优化后的数据)\n messages.add(Map.of(\n \"role\", \"user\",\n \"content\", userContent\n ));\n\n// messages.add(Map.of(\n// \"role\", \"user\",\n// \"content\", \"解析是你理解的波浪理论:推动浪(1-2-3-4-5),调整浪(A-B-C)\"));\n\n\n requestMap.put(\"messages\", messages);\n\n String requestJson = JsonUtils.Serializer(requestMap);\n\n// System.out.println(\"req:\" + requestJson);\n\n // 设置请求头\n HttpHeaders headers = new HttpHeaders();\n headers.setContentType(org.springframework.http.MediaType.APPLICATION_JSON);\n headers.set(\"Authorization\", \"Bearer \" + apiKey);\n\n // 发送请求\n String response = restTemplateConfig.post(\n baseUrl, requestJson,\n headers\n );\n\n System.out.println(\"DeepSeek响应: \" + response);\n\n try {\n\n TDeepSeekResponse deepSeekResponse = DeepSeekJsonParser.parseFullResponse(response);\n\n TDeepSeekChoice deepSeekChoice = deepSeekResponse.choices[0];\n\n System.out.println(\"content:\" + deepSeekChoice.message.content);\n\n// TWaveAnalysisResult result = DeepSeekJsonParser.getAnalysisResultFromResponse(deepSeekChoice.message.content);\n\n } catch (Exception e) {\n System.err.println(\"解析响应失败: \" + e.getMessage());\n }\n\n }\n\n\n\n private String getKLineData() throws IOException {\n\n String symbol = \"ETHUSDT\";\n String text = \"1d\";\n\n // 1. 读取本地 1d K 线文件\n Path path = Path.of(\"config\", symbol + \"-1d.txt\");\n String priceContent = Files.readString(path, StandardCharsets.UTF_8);\n\n com.app.dc.service.signal.deepSeek.KineData kineData = JsonUtils.Deserialize(priceContent, KineData.class);\n\n // 反序列化为行情列表(文件里是倒序的也没关系,我们后面统一按时间升序排序)\n List<MarketDataSnapshotFullRefresh> rawList = kineData.getData();\n// JsonUtils.Deserialize2(priceContent, MarketDataSnapshotFullRefresh.class);\n\n System.out.println(\"Kline size:\" + rawList.size());\n\n // 2. 按时间升序排序,保证 index 越大时间越晚\n rawList.sort(Comparator.comparingLong(m -> Long.parseLong(m.getInfo1())));\n\n List<Candle> fullCandles = new ArrayList<>();\n List<ZonedDateTime> barTimes = new ArrayList<>();\n\n for (MarketDataSnapshotFullRefresh mdsf : rawList) {\n\n // info1 是时间戳(毫秒)\n ZonedDateTime time = ZonedDateTime.ofInstant(\n Instant.ofEpochMilli(Long.parseLong(mdsf.getInfo1())),\n ZoneId.systemDefault()\n );\n\n BigDecimal open = new BigDecimal(mdsf.getOpenPrice());\n BigDecimal high = new BigDecimal(mdsf.getHighPrice());\n BigDecimal low = new BigDecimal(mdsf.getLowPrice());\n BigDecimal close = new BigDecimal(mdsf.getClosePrice());\n BigDecimal volume = new BigDecimal(mdsf.getVolume());\n\n // info3 是周期,例如 \"1d\" => \"PT1D\"\n String temTxt = \"PT\" + mdsf.getInfo3().toUpperCase();\n\n String interval = mdsf.getInfo3().toUpperCase();\n\n long days = Long.parseLong(interval.substring(0, interval.length() - 1));\n Duration barDuration = Duration.ofDays(days);\n\n// Bar newBar = new BaseBar(Duration.parse(temTxt), time, open, high, low, close, volume);\n Bar newBar = new BaseBar(barDuration, time, open, high, low, close, volume);\n\n Candle candle = new Candle(\n open.doubleValue(),\n high.doubleValue(),\n low.doubleValue(),\n close.doubleValue(),\n volume.doubleValue(),\n time.format(YYYYMMDD_FORMATTER)\n );\n fullCandles.add(candle);\n }\n\n return JsonUtils.Serializer(fullCandles);\n }\n\n\n @Test\n public void TestParseModel() {\n\n String txt = \"{\\n\" +\n \" \\\"id\\\": \\\"resp_0309d0e0807c58750069a252a7f2cc81959cbe5f2514433ddc\\\",\\n\" +\n \" \\\"object\\\": \\\"response\\\",\\n\" +\n \" \\\"created_at\\\": 1772245671,\\n\" +\n \" \\\"status\\\": \\\"completed\\\",\\n\" +\n \" \\\"background\\\": false,\\n\" +\n \" \\\"billing\\\": {\\n\" +\n \" \\\"payer\\\": \\\"developer\\\"\\n\" +\n \" },\\n\" +\n \" \\\"completed_at\\\": 1772245682,\\n\" +\n \" \\\"error\\\": null,\\n\" +\n \" \\\"frequency_penalty\\\": 0.0,\\n\" +\n \" \\\"incomplete_details\\\": null,\\n\" +\n \" \\\"instructions\\\": null,\\n\" +\n \" \\\"max_output_tokens\\\": 4000,\\n\" +\n \" \\\"max_tool_calls\\\": null,\\n\" +\n \" \\\"model\\\": \\\"gpt-5-nano-2025-08-07\\\",\\n\" +\n \" \\\"output\\\": [\\n\" +\n \" {\\n\" +\n \" \\\"id\\\": \\\"rs_0309d0e0807c58750069a252a875e08195931ee5175046a3a7\\\",\\n\" +\n \" \\\"type\\\": \\\"reasoning\\\",\\n\" +\n \" \\\"summary\\\": []\\n\" +\n \" },\\n\" +\n \" {\\n\" +\n \" \\\"id\\\": \\\"msg_0309d0e0807c58750069a252ac3d548195902aaeb037f163fe\\\",\\n\" +\n \" \\\"type\\\": \\\"message\\\",\\n\" +\n \" \\\"status\\\": \\\"completed\\\",\\n\" +\n \" \\\"content\\\": [\\n\" +\n \" {\\n\" +\n \" \\\"type\\\": \\\"output_text\\\",\\n\" +\n \" \\\"annotations\\\": [],\\n\" +\n \" \\\"logprobs\\\": [],\\n\" +\n \" \\\"text\\\": \\\"{\\\\n \\\\\\\"analysis_type\\\\\\\": \\\\\\\"consolidation\\\\\\\",\\\\n \\\\\\\"current_position\\\\\\\": \\\\\\\"\\\\u9707\\\\u8361\\\\u5e95\\\\u90e8\\\\u533a\\\\u95f4\\\\u5185\\\\\\\",\\\\n \\\\\\\"wave_count\\\\\\\": {\\\\n \\\\\\\"wave_1\\\\\\\": {\\\\\\\"start\\\\\\\": \\\\\\\"20250718\\\\\\\", \\\\\\\"end\\\\\\\": \\\\\\\"20250812\\\\\\\"},\\\\n \\\\\\\"wave_2\\\\\\\": {\\\\\\\"start\\\\\\\": \\\\\\\"20250813\\\\\\\", \\\\\\\"end\\\\\\\": \\\\\\\"20250901\\\\\\\"}\\\\n },\\\\n \\\\\\\"confidence\\\\\\\": 0.68,\\\\n \\\\\\\"reasoning\\\\\\\": \\\\\\\"\\\\u5728\\\\u7ed9\\\\u5b9a365\\\\u6839\\\\u65e5K\\\\u7ebf\\\\u4e2d\\\\uff0c\\\\u4ef7\\\\u683c\\\\u57280.26\\\\u20130.34\\\\u533a\\\\u95f4\\\\u53cd\\\\u590d\\\\u6a2a\\\\u76d8\\\\uff0c\\\\u8fd1\\\\u51e0\\\\u767e\\\\u65e5\\\\u672a\\\\u5f62\\\\u6210\\\\u660e\\\\u786e\\\\u7684\\\\u5355\\\\u8fb9\\\\u7a81\\\\u7834\\\\uff0c\\\\u6210\\\\u4ea4\\\\u91cf\\\\u4ea6\\\\u5448\\\\u73b0\\\\u9636\\\\u6bb5\\\\u6027\\\\u56de\\\\u843d\\\\u4e0e\\\\u653e\\\\u5927\\\\u4ea4\\\\u9519\\\\uff0c\\\\u7f3a\\\\u4e4f\\\\u6e05\\\\u6670\\\\u76845\\\\u6d6a\\\\u63a8\\\\u52a8\\\\u5f62\\\\u6210\\\\u3002\\\\u5f53\\\\u524d\\\\u6700\\\\u65b0\\\\u6536\\\\u76d8\\\\u4ef7\\\\u7ea60.2825\\\\uff0c\\\\u4f4d\\\\u4e8e\\\\u533a\\\\u95f4\\\\u4e2d\\\\u6bb5\\\\u504f\\\\u4e0b\\\\u65b9\\\\uff0c\\\\u8fdc\\\\u79bb\\\\u4e0a\\\\u65b9\\\\u5173\\\\u952e\\\\u963b\\\\u529b\\\\u4f46\\\\u9ad8\\\\u4e8e\\\\u5e95\\\\u90e8\\\\u652f\\\\u6491\\\\uff0c\\\\u7b26\\\\u5408\\\\u5e95\\\\u90e8\\\\u9707\\\\u8361/\\\\u6574\\\\u7406\\\\u9636\\\\u6bb5\\\\u7684\\\\u7279\\\\u5f81\\\\uff1b\\\\u82e5\\\\u7a81\\\\u7834\\\\u4e0a\\\\u6cbf0.30-0.305\\\\u533a\\\\u95f4\\\\uff0c\\\\u624d\\\\u6709\\\\u6982\\\\u7387\\\\u8fdb\\\\u5165\\\\u4e0b\\\\u4e00\\\\u4e2a\\\\u63a8\\\\u52a8\\\\u6d6a\\\\u6216\\\\u4e0a\\\\u884c\\\\u8d8b\\\\u52bf\\\\u3002\\\\u7531\\\\u4e8e\\\\u7f3a\\\\u4e4f\\\\u7a33\\\\u5b9a\\\\u7684\\\\u63a8\\\\u52a8\\\\u6d6a\\\\u7ed3\\\\u6784\\\\uff081-2-3-4-5\\\\uff09\\\\u6e05\\\\u6670\\\\u8d77\\\\u6b62\\\\u70b9\\\\uff0c\\\\u4e14\\\\u6ca1\\\\u6709\\\\u51fa\\\\u73b0\\\\u5178\\\\u578b\\\\u7684MACD\\\\u9876\\\\u80cc\\\\u79bb\\\\u6301\\\\u7eed\\\\u653e\\\\u5927\\\\u914d\\\\u5408\\\\u5934\\\\u80a9\\\\u9876\\\\u7b49\\\\u5f62\\\\u6001\\\\uff0c\\\\u7efc\\\\u5408\\\\u5224\\\\u65ad\\\\u4e3a\\\\u5f53\\\\u524d\\\\u5904\\\\u4e8e\\\\u5e95\\\\u90e8\\\\u9707\\\\u8361\\\\u4e2d\\\\u7684\\\\u6574\\\\u7406\\\\u9636\\\\u6bb5\\\\u3002\\\\\\\"\\\\n ,\\\\n \\\\\\\"key_levels\\\\\\\": {\\\\n \\\\\\\"support\\\\\\\": [0.275, 0.268],\\\\n \\\\\\\"resistance\\\\\\\": [0.30, 0.305]\\\\n },\\\\n \\\\\\\"next_expectation\\\\\\\": \\\\\\\"\\\\u82e5\\\\u4ef7\\\\u683c\\\\u6301\\\\u7eed\\\\u57280.275\\\\u20130.30\\\\u533a\\\\u95f4\\\\u6ce2\\\\u52a8\\\\uff0c\\\\u77ed\\\\u671f\\\\u4ee5\\\\u6a2a\\\\u76d8\\\\u4e3a\\\\u4e3b\\\\uff0c\\\\u7b49\\\\u5f85\\\\u7a81\\\\u7834\\\\u4e0a\\\\u6cbf0.30\\\\u20130.305\\\\u518d\\\\u8003\\\\u8651\\\\u8fdb\\\\u5165\\\\u8d8b\\\\u52bf\\\\u4ea4\\\\u6613\\\\uff1b\\\\u82e5\\\\u8dcc\\\\u7834\\\\u5e95\\\\u90e80.275\\\\uff0c\\\\u5219\\\\u53ef\\\\u80fd\\\\u8fdb\\\\u5165\\\\u66f4\\\\u6df1\\\\u7684\\\\u8c03\\\\u6574\\\\u6216\\\\u6d4b\\\\u8bd50.265/0.26\\\\u9644\\\\u8fd1\\\\u7684\\\\u66f4\\\\u5f3a\\\\u652f\\\\u6491\\\\u3002\\\\\\\",\\\\n \\\\\\\"decide\\\\\\\": {\\\\n \\\\\\\"side\\\\\\\": \\\\\\\"\\\\u89c2\\\\u671b\\\\\\\",\\\\n \\\\\\\"price\\\\\\\": 0.2825,\\\\n \\\\\\\"takeProfit\\\\\\\": \\\\\\\"\\\\u82e5\\\\u7a81\\\\u7834\\\\u4e0a\\\\u6cbf0.305\\\\uff0c\\\\u8003\\\\u8651\\\\u57280.31\\\\u9644\\\\u8fd1\\\\u8bbe\\\\u7f6e\\\\u6b62\\\\u76c8\\\\uff1b\\\\u82e5\\\\u7ee7\\\\u7eed\\\\u56de\\\\u843d\\\\u6d4b\\\\u8bd50.275\\\\u20130.27\\\\u533a\\\\u95f4\\\\uff0c\\\\u8003\\\\u8651\\\\u57280.27\\\\u9644\\\\u8fd1\\\\u8bbe\\\\u5b9a\\\\u6b62\\\\u635f\\\\u3002\\\\\\\",\\\\n \\\\\\\"stopLoss\\\\\\\": \\\\\\\"0.275\\\\uff08\\\\u4e0b\\\\u65b9\\\\u652f\\\\u6491\\\\u7565\\\\u4e0b\\\\u65b9\\\\u7684\\\\u4fdd\\\\u62a4\\\\u6027\\\\u6b62\\\\u635f\\\\u70b9\\\\uff09\\\\\\\"\\\\n },\\\\n \\\\\\\"reasoning_detail\\\\\\\": [\\\\n \\\\\\\"\\\\u6700\\\\u65b0\\\\u6536\\\\u76d8\\\\u63a5\\\\u8fd1\\\\u533a\\\\u95f4\\\\u4e2d\\\\u6bb5\\\\u504f\\\\u4e0b\\\\uff0c\\\\u5904\\\\u5728\\\\u4e2d\\\\u8f68\\\\u9644\\\\u8fd1\\\\u4f46\\\\u672a\\\\u7a81\\\\u7834\\\\u4e0a\\\\u6cbf\\\\uff0c\\\\u663e\\\\u793a\\\\u591a\\\\u5934\\\\u52a8\\\\u80fd\\\\u4e0d\\\\u8db3\\\\u4ee5\\\\u5feb\\\\u901f\\\\u63a8\\\\u52a8\\\\u4ef7\\\\u683c\\\\u7a81\\\\u78340.30\\\\u4ee5\\\\u4e0a\\\\u963b\\\\u529b\\\\u3002\\\\\\\",\\\\n \\\\\\\"\\\\u4ef7\\\\u683c\\\\u57280.27\\\\u20130.30\\\\u533a\\\\u95f4\\\\u957f\\\\u671f\\\\u5f98\\\\u5f8a\\\\uff0c\\\\u6210\\\\u4ea4\\\\u91cf\\\\u672a\\\\u663e\\\\u793a\\\\u6301\\\\u7eed\\\\u653e\\\\u5927\\\\u5f62\\\\u6210\\\\u5355\\\\u8fb9\\\\u8d8b\\\\u52bf\\\\uff0c\\\\u7b26\\\\u5408\\\\u5e95\\\\u90e8\\\\u9707\\\\u8361/\\\\u6574\\\\u7406\\\\u9636\\\\u6bb5\\\\u7684\\\\u5178\\\\u578b\\\\u7279\\\\u5f81\\\\u3002\\\\\\\",\\\\n \\\\\\\"\\\\u82e5\\\\u51fa\\\\u73b0MACD\\\\u80cc\\\\u79bb\\\\u6216\\\\u5934\\\\u80a9\\\\u9876\\\\u7b49\\\\u5178\\\\u578b\\\\u9876\\\\u90e8\\\\u4fe1\\\\u53f7\\\\uff0c\\\\u624d\\\\u9700\\\\u8981\\\\u91cd\\\\u65b0\\\\u8bc4\\\\u4f30\\\\u8fdb\\\\u5165\\\\u8c03\\\\u6574\\\\u6d6a\\\\u6216\\\\u9876\\\\u90e8\\\\u7684\\\\u53ef\\\\u80fd\\\\u6027\\\\uff1b\\\\u5f53\\\\u524d\\\\u6570\\\\u636e\\\\u672a\\\\u51fa\\\\u73b0\\\\u6301\\\\u7eed\\\\u660e\\\\u6670\\\\u76845\\\\u6d6a\\\\u63a8\\\\u52a8\\\\u7ed3\\\\u6784\\\\u3002\\\\\\\"\\\\n ]\\\\n}\\\"\\n\" +\n \" }\\n\" +\n \" ],\\n\" +\n \" \\\"role\\\": \\\"assistant\\\"\\n\" +\n \" }\\n\" +\n \" ],\\n\" +\n \" \\\"parallel_tool_calls\\\": true,\\n\" +\n \" \\\"presence_penalty\\\": 0.0,\\n\" +\n \" \\\"previous_response_id\\\": null,\\n\" +\n \" \\\"prompt_cache_key\\\": null,\\n\" +\n \" \\\"prompt_cache_retention\\\": null,\\n\" +\n \" \\\"reasoning\\\": {\\n\" +\n \" \\\"effort\\\": \\\"low\\\",\\n\" +\n \" \\\"summary\\\": null\\n\" +\n \" },\\n\" +\n \" \\\"safety_identifier\\\": null,\\n\" +\n \" \\\"service_tier\\\": \\\"default\\\",\\n\" +\n \" \\\"store\\\": true,\\n\" +\n \" \\\"temperature\\\": 1.0,\\n\" +\n \" \\\"text\\\": {\\n\" +\n \" \\\"format\\\": {\\n\" +\n \" \\\"type\\\": \\\"text\\\"\\n\" +\n \" },\\n\" +\n \" \\\"verbosity\\\": \\\"medium\\\"\\n\" +\n \" },\\n\" +\n \" \\\"tool_choice\\\": \\\"auto\\\",\\n\" +\n \" \\\"tools\\\": [],\\n\" +\n \" \\\"top_logprobs\\\": 0,\\n\" +\n \" \\\"top_p\\\": 1.0,\\n\" +\n \" \\\"truncation\\\": \\\"disabled\\\",\\n\" +\n \" \\\"usage\\\": {\\n\" +\n \" \\\"input_tokens\\\": 16697,\\n\" +\n \" \\\"input_tokens_details\\\": {\\n\" +\n \" \\\"cached_tokens\\\": 0\\n\" +\n \" },\\n\" +\n \" \\\"output_tokens\\\": 1129,\\n\" +\n \" \\\"output_tokens_details\\\": {\\n\" +\n \" \\\"reasoning_tokens\\\": 448\\n\" +\n \" },\\n\" +\n \" \\\"total_tokens\\\": 17826\\n\" +\n \" },\\n\" +\n \" \\\"user\\\": null,\\n\" +\n \" \\\"metadata\\\": {}\\n\" +\n \"}\";\n\n TChatGPTResponse chatGPTResponse = JsonUtils.Deserialize(txt, TChatGPTResponse.class);\n\n if (chatGPTResponse.output != null) {\n for (TChatGPTResponse.Output output: chatGPTResponse.output) {\n if (output.content != null) {\n for (TChatGPTResponse.Content content: output.content) {\n if (content.type.equals(\"output_text\")) {\n TWaveAnalysisResult result = DeepSeekJsonParser.getAnalysisResultFromResponse(content.text);\n\n System.out.println(JsonUtils.Serializer(result));\n }\n }\n }\n }\n }\n\n// System.out.println(chatGPTResponse);\n\n }\n\n private SceneStrategyAnalysisRequest buildAnalyzeRequest() throws IOException {\n SceneStrategyAnalysisRequest request = new SceneStrategyAnalysisRequest();\n request.symbol = \"ETHUSDT\";\n request.scene = MarketScene.OSCILLATION;\n request.timeframe = \"15m\";\n request.klines = load15mKlinesFromFile();\n request.candidateStrategies = loadAllStrategiesFromCatalog();\n return request;\n }\n\n private List<SceneStrategyAnalysisRequest.KlinePoint> load15mKlinesFromFile() throws IOException {\n Path path = Path.of(\"config\", \"ETHUSDT-15M.txt\");\n String text = Files.readString(path, StandardCharsets.UTF_8);\n List<SceneStrategyAnalysisRequest.KlinePoint> points = new ArrayList<>();\n\n KineData dataFile = JsonUtils.Deserialize(text, KineData.class);\n if (dataFile != null && dataFile.getData() != null && !dataFile.getData().isEmpty()) {\n List<MarketDataSnapshotFullRefresh> sorted = new ArrayList<>(dataFile.getData());\n sorted.sort(Comparator.comparingLong(m -> Long.parseLong(m.getInfo1())));\n for (MarketDataSnapshotFullRefresh item : sorted) {\n if (item == null || item.getInfo1() == null) {\n continue;\n }\n SceneStrategyAnalysisRequest.KlinePoint p = new SceneStrategyAnalysisRequest.KlinePoint();\n p.ts = Long.parseLong(item.getInfo1());\n p.open = Double.parseDouble(item.getOpenPrice());\n p.high = Double.parseDouble(item.getHighPrice());\n p.low = Double.parseDouble(item.getLowPrice());\n p.close = Double.parseDouble(item.getClosePrice());\n p.volume = Double.parseDouble(item.getVolume());\n points.add(p);\n }\n return points;\n }\n\n TestKlineFile file = JsonUtils.Deserialize(text, TestKlineFile.class);\n if (file == null || file.kline == null) {\n return Collections.emptyList();\n }\n List<TestKlinePoint> sorted = new ArrayList<>(file.kline);\n sorted.sort(Comparator.comparing(TestKlinePoint::getStartTime, Comparator.nullsLast(String::compareTo)));\n for (TestKlinePoint item : sorted) {\n if (item == null || item.startTime == null) {\n continue;\n }\n SceneStrategyAnalysisRequest.KlinePoint p = new SceneStrategyAnalysisRequest.KlinePoint();\n p.ts = Instant.parse(item.startTime).toEpochMilli();\n p.open = item.open;\n p.high = item.high;\n p.low = item.low;\n p.close = item.close;\n p.volume = item.volume;\n points.add(p);\n }\n return points;\n }\n\n private List<SceneStrategyMeta> loadAllStrategiesFromCatalog() throws IOException {\n Path path = Path.of(\"config\", \"scene_strategy_catalog.json\");\n String text = Files.readString(path, StandardCharsets.UTF_8);\n SceneStrategyCatalogService.CatalogFile file =\n JsonUtils.Deserialize(text, SceneStrategyCatalogService.CatalogFile.class);\n if (file == null || file.strategies == null) {\n return Collections.emptyList();\n }\n return file.strategies.stream()\n .filter(Objects::nonNull)\n .map(item -> {\n SceneStrategyMeta meta = new SceneStrategyMeta();\n meta.scene = MarketScene.fromCode(item.scene);\n meta.strategy_name = item.strategy_name;\n meta.description = item.description;\n meta.enabled = item.enabled == null || item.enabled;\n meta.source = \"file\";\n return meta;\n })\n .filter(meta -> meta.scene != null && meta.strategy_name != null && !meta.strategy_name.trim().isEmpty())\n .collect(Collectors.toList());\n }\n\n private void setField(Object target, String fieldName, Object value) throws Exception {\n Field field = target.getClass().getDeclaredField(fieldName);\n field.setAccessible(true);\n field.set(target, value);\n }\n\n private ChatGPTAnalysis buildRealChatGPTAnalysis() throws Exception {\n ChatGPTAnalysis analysis = new ChatGPTAnalysis();\n setField(analysis, \"restTemplateConfig\", restTemplateConfig);\n setField(analysis, \"apiKey\", apiKey);\n setField(analysis, \"baseUrl\", baseUrl);\n setField(analysis, \"model\", \"gpt-5.2\");\n setField(analysis, \"maxOutputTokens\", 4000);\n setField(analysis, \"reasoningEffort\", \"low\");\n return analysis;\n }\n\n private static class MockRestTemplateConfig extends RestTemplateConfig {\n private final String response;\n\n private MockRestTemplateConfig(String response) {\n this.response = response;\n }\n\n @Override\n public String post(String url, String requstData, HttpHeaders httpHeaders) {\n return response;\n }\n }\n\n private static class TestKlineFile {\n public List<TestKlinePoint> kline;\n }\n\n private static class TestKlinePoint {\n public String startTime;\n public double open;\n public double high;\n public double low;\n public double close;\n public double volume;\n\n public String getStartTime() {\n return startTime;\n }\n }\n\n}\n"}, "1480ce59c7669de4b393c96e9a8b82ed169ee4eb": {"t": 1785770769.1775582, "n": 18193, "h": "184779db1777cbd3", "c": "package com.app.dc.indsvr;\n\nimport com.app.common.utils.JsonUtils;\nimport com.app.dc.fix.message.MarketDataSnapshotFullRefresh;\nimport com.app.dc.po.chatGPT.TChatGPTResponse;\nimport com.app.dc.po.deepseek.DeepSeekJsonParser;\nimport com.app.dc.po.deepseek.TDeepSeekChoice;\nimport com.app.dc.po.deepseek.TDeepSeekResponse;\nimport com.app.dc.po.deepseek.TWaveAnalysisResult;\nimport com.app.dc.service.signal.day.Candle;\nimport com.app.dc.service.signal.deepSeek.KineData;\nimport com.app.dc.service.utils.RestTemplateConfig;\nimport org.junit.Test;\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.http.MediaType;\nimport org.ta4j.core.Bar;\nimport org.ta4j.core.BaseBar;\n\nimport java.io.IOException;\nimport java.math.BigDecimal;\nimport java.nio.charset.StandardCharsets;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.time.Duration;\nimport java.time.Instant;\nimport java.time.ZoneId;\nimport java.time.ZonedDateTime;\nimport java.time.format.DateTimeFormatter;\nimport java.util.*;\n\npublic class TestChatGPT {\n\n String apiKey = \"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\";\n String baseUrl = \"https://api.openai.com/v1/responses\";\n private static final DateTimeFormatter YYYYMMDD_FORMATTER =\n DateTimeFormatter.ofPattern(\"yyyyMMdd\");\n\n\n @Test\n public void TestBaseHttpToChatGPT() {\n\n RestTemplateConfig restTemplateConfig = new RestTemplateConfig();\n restTemplateConfig.restTemplate();\n\n // ---------- request body ----------\n Map<String, Object> requestMap = new HashMap<>();\n requestMap.put(\"model\", \"gpt-5-nano\");\n requestMap.put(\"max_output_tokens\", 2000);\n requestMap.put(\"temperature\", 0.1);\n\n // input\n List<Map<String, Object>> input = new ArrayList<>();\n Map<String, Object> userMsg = new HashMap<>();\n userMsg.put(\"role\", \"user\");\n\n List<Map<String, String>> content = new ArrayList<>();\n content.add(Map.of(\n \"type\", \"input_text\",\n \"text\", \"Say this is a test\"\n ));\n\n userMsg.put(\"content\", content);\n input.add(userMsg);\n\n requestMap.put(\"input\", input);\n\n // ---------- headers ----------\n HttpHeaders headers = new HttpHeaders();\n headers.setContentType(MediaType.APPLICATION_JSON);\n headers.setBearerAuth(apiKey);\n\n String requstData = JsonUtils.Serializer(requestMap);\n\n // ---------- request ----------\n String response = restTemplateConfig.post(\n baseUrl,\n requstData,\n headers\n );\n\n System.out.println(response);\n\n }\n\n\n\n @Test\n public void TestChatGPTModel() throws IOException {\n\n RestTemplateConfig restTemplateConfig = new RestTemplateConfig();\n\n restTemplateConfig.restTemplate();\n\n // 使用HashMap构建JSON结构\n Map<String, Object> requestMap = new HashMap<>();\n requestMap.put(\"model\", \"deepseek-chat\");\n requestMap.put(\"stream\", false);\n\n Map<String, String> respMap = new HashMap<>();\n respMap.put(\"type\", \"json_object\");\n // 1. 设置JSON响应格式\n requestMap.put(\"response_format\", respMap);\n\n // 2. 针对大数据量的关键参数\n requestMap.put(\"max_tokens\", 2000); // 增加输出长度\n requestMap.put(\"temperature\", 0.1); // 降低随机性,保证分析一致性\n\n // 构建messages数组\n List<Map<String, String>> messages = new ArrayList<>();\n\n// addHistoryMsg(messages);\n\n String klineJsonArray = getKLineData();\n\n System.out.println(\"klineJsonArray:\" + klineJsonArray);\n\n messages.add(Map.of(\n \"role\", \"system\",\n \"content\",\n \"角色:B圈艾略特波浪理论和震荡首席分析师\" +\n \"分析user传的最新的365根日K线,完成以下任务:\\n\\n\" +\n \"【任务】根据给出的K线数据,分析当前行情处于哪个阶段,是处于底部震荡还是处于波浪理论的结构当中\\n\" +\n \"- 若处于波浪结构中\\n\" +\n \"- 如果存在推动浪(1-2-3-4-5),标注每浪起止点\\n\" +\n \"- 如果存在5浪到A浪的顶部震荡调整,可通过MACD顶背离和头肩顶形态识别,并标注起止点\" +\n \"- 如果存在调整浪(A-B-C),标注调整模式\\n\" +\n\n \"- 若处于底部震荡\\n\" +\n \"- 震荡区间:[上沿价格, 下沿价格]\\n\" +\n \"- 震荡时间:已持续N日\\n\" +\n \"- 最新位置:\\\"接近突破上沿\\\"或\\\"中轨附近\\\"\\n\\n\" +\n \"返回完整分析结果JSON。\"\n ));\n\n String userContent = String.format(\n \"这是最新的365根日K线数据(JSON数组格式),请分析最新日K线的位置:\\n\\n\" +\n \"K线数据按时间顺序正序:\\n%s\\n\\n\" +\n \"请分析并返回JSON结果,注意描述reasoning、next_expectation返回不要有啥特殊字符,格式:\\n\" +\n \"{\\n\" +\n \" \\\"analysis_type\\\": \\\"wave\\\" 或 \\\"consolidation\\\",\\n\" +\n \" \\\"current_position\\\": \\\"第3浪\\\" 或 \\\"B浪调整\\\" 或 \\\"震荡上沿\\\" 等,\\n\" +\n \" \\\"wave_count\\\": {\\n\" +\n \" \\\"wave_1\\\": {\\\"start\\\": \\\"给出yyyymmdd格式的日K线时间点\\\", \\\"end\\\": \\\"给出yyyymmdd格式的日K线时间点\\\"},\\n\" +\n \" \\\"wave_2\\\": {\\\"start\\\": \\\"给出yyyymmdd格式的日K线时间点\\\", \\\"end\\\": \\\"给出yyyymmdd格式的日K线时间点\\\"}\\n\" +\n \" },\\n\" +\n \" \\\"confidence\\\": 0.85,\\n\" +\n \" \\\"reasoning\\\": \\\"详细分析逻辑,包括趋势线、成交量、波浪规则验证\\\",\\n\" +\n \" \\\"key_levels\\\": {\\n\" +\n \" \\\"support\\\": [支撑位1, 支撑位2],\\n\" +\n \" \\\"resistance\\\": [阻力位1, 阻力位2]\\n\" +\n \" },\\n\" +\n \" \\\"next_expectation\\\": \\\"下一阶段的可能走势\\\"\\n\" +\n \" \\\"decide\\\": {\\\"side\\\": \\\"开仓方向:多/空/观望\\\", \\\"price\\\": \\\"明确的一个开仓价格\\\", \\\"takeProfit\\\": \\\"明确的一个止盈价格,不要太贪,阻力位1附近就行\\\", \\\"stopLoss\\\": \\\"明确的一个止损价格\\\"}\\n\" +\n \"}\",\n klineJsonArray\n );\n\n\n // 用户消息(包含优化后的数据)\n messages.add(Map.of(\n \"role\", \"user\",\n \"content\", userContent\n ));\n\n// messages.add(Map.of(\n// \"role\", \"user\",\n// \"content\", \"解析是你理解的波浪理论:推动浪(1-2-3-4-5),调整浪(A-B-C)\"));\n\n\n requestMap.put(\"messages\", messages);\n\n String requestJson = JsonUtils.Serializer(requestMap);\n\n// System.out.println(\"req:\" + requestJson);\n\n // 设置请求头\n HttpHeaders headers = new HttpHeaders();\n headers.setContentType(org.springframework.http.MediaType.APPLICATION_JSON);\n headers.set(\"Authorization\", \"Bearer \" + apiKey);\n\n // 发送请求\n String response = restTemplateConfig.post(\n baseUrl, requestJson,\n headers\n );\n\n System.out.println(\"DeepSeek响应: \" + response);\n\n try {\n\n TDeepSeekResponse deepSeekResponse = DeepSeekJsonParser.parseFullResponse(response);\n\n TDeepSeekChoice deepSeekChoice = deepSeekResponse.choices[0];\n\n System.out.println(\"content:\" + deepSeekChoice.message.content);\n\n// TWaveAnalysisResult result = DeepSeekJsonParser.getAnalysisResultFromResponse(deepSeekChoice.message.content);\n\n } catch (Exception e) {\n System.err.println(\"解析响应失败: \" + e.getMessage());\n }\n\n }\n\n\n\n private String getKLineData() throws IOException {\n\n String symbol = \"ETHUSDT\";\n String text = \"1d\";\n\n // 1. 读取本地 1d K 线文件\n Path path = Path.of(\"config\", symbol + \"-1d.txt\");\n String priceContent = Files.readString(path, StandardCharsets.UTF_8);\n\n com.app.dc.service.signal.deepSeek.KineData kineData = JsonUtils.Deserialize(priceContent, KineData.class);\n\n // 反序列化为行情列表(文件里是倒序的也没关系,我们后面统一按时间升序排序)\n List<MarketDataSnapshotFullRefresh> rawList = kineData.getData();\n// JsonUtils.Deserialize2(priceContent, MarketDataSnapshotFullRefresh.class);\n\n System.out.println(\"Kline size:\" + rawList.size());\n\n // 2. 按时间升序排序,保证 index 越大时间越晚\n rawList.sort(Comparator.comparingLong(m -> Long.parseLong(m.getInfo1())));\n\n List<Candle> fullCandles = new ArrayList<>();\n List<ZonedDateTime> barTimes = new ArrayList<>();\n\n for (MarketDataSnapshotFullRefresh mdsf : rawList) {\n\n // info1 是时间戳(毫秒)\n ZonedDateTime time = ZonedDateTime.ofInstant(\n Instant.ofEpochMilli(Long.parseLong(mdsf.getInfo1())),\n ZoneId.systemDefault()\n );\n\n BigDecimal open = new BigDecimal(mdsf.getOpenPrice());\n BigDecimal high = new BigDecimal(mdsf.getHighPrice());\n BigDecimal low = new BigDecimal(mdsf.getLowPrice());\n BigDecimal close = new BigDecimal(mdsf.getClosePrice());\n BigDecimal volume = new BigDecimal(mdsf.getVolume());\n\n // info3 是周期,例如 \"1d\" => \"PT1D\"\n String temTxt = \"PT\" + mdsf.getInfo3().toUpperCase();\n\n String interval = mdsf.getInfo3().toUpperCase();\n\n long days = Long.parseLong(interval.substring(0, interval.length() - 1));\n Duration barDuration = Duration.ofDays(days);\n\n// Bar newBar = new BaseBar(Duration.parse(temTxt), time, open, high, low, close, volume);\n Bar newBar = new BaseBar(barDuration, time, open, high, low, close, volume);\n\n Candle candle = new Candle(\n open.doubleValue(),\n high.doubleValue(),\n low.doubleValue(),\n close.doubleValue(),\n volume.doubleValue(),\n time.format(YYYYMMDD_FORMATTER)\n );\n fullCandles.add(candle);\n }\n\n return JsonUtils.Serializer(fullCandles);\n }\n\n\n @Test\n public void TestParseModel() {\n\n String txt = \"{\\n\" +\n \" \\\"id\\\": \\\"resp_0309d0e0807c58750069a252a7f2cc81959cbe5f2514433ddc\\\",\\n\" +\n \" \\\"object\\\": \\\"response\\\",\\n\" +\n \" \\\"created_at\\\": 1772245671,\\n\" +\n \" \\\"status\\\": \\\"completed\\\",\\n\" +\n \" \\\"background\\\": false,\\n\" +\n \" \\\"billing\\\": {\\n\" +\n \" \\\"payer\\\": \\\"developer\\\"\\n\" +\n \" },\\n\" +\n \" \\\"completed_at\\\": 1772245682,\\n\" +\n \" \\\"error\\\": null,\\n\" +\n \" \\\"frequency_penalty\\\": 0.0,\\n\" +\n \" \\\"incomplete_details\\\": null,\\n\" +\n \" \\\"instructions\\\": null,\\n\" +\n \" \\\"max_output_tokens\\\": 4000,\\n\" +\n \" \\\"max_tool_calls\\\": null,\\n\" +\n \" \\\"model\\\": \\\"gpt-5-nano-2025-08-07\\\",\\n\" +\n \" \\\"output\\\": [\\n\" +\n \" {\\n\" +\n \" \\\"id\\\": \\\"rs_0309d0e0807c58750069a252a875e08195931ee5175046a3a7\\\",\\n\" +\n \" \\\"type\\\": \\\"reasoning\\\",\\n\" +\n \" \\\"summary\\\": []\\n\" +\n \" },\\n\" +\n \" {\\n\" +\n \" \\\"id\\\": \\\"msg_0309d0e0807c58750069a252ac3d548195902aaeb037f163fe\\\",\\n\" +\n \" \\\"type\\\": \\\"message\\\",\\n\" +\n \" \\\"status\\\": \\\"completed\\\",\\n\" +\n \" \\\"content\\\": [\\n\" +\n \" {\\n\" +\n \" \\\"type\\\": \\\"output_text\\\",\\n\" +\n \" \\\"annotations\\\": [],\\n\" +\n \" \\\"logprobs\\\": [],\\n\" +\n \" \\\"text\\\": \\\"{\\\\n \\\\\\\"analysis_type\\\\\\\": \\\\\\\"consolidation\\\\\\\",\\\\n \\\\\\\"current_position\\\\\\\": \\\\\\\"\\\\u9707\\\\u8361\\\\u5e95\\\\u90e8\\\\u533a\\\\u95f4\\\\u5185\\\\\\\",\\\\n \\\\\\\"wave_count\\\\\\\": {\\\\n \\\\\\\"wave_1\\\\\\\": {\\\\\\\"start\\\\\\\": \\\\\\\"20250718\\\\\\\", \\\\\\\"end\\\\\\\": \\\\\\\"20250812\\\\\\\"},\\\\n \\\\\\\"wave_2\\\\\\\": {\\\\\\\"start\\\\\\\": \\\\\\\"20250813\\\\\\\", \\\\\\\"end\\\\\\\": \\\\\\\"20250901\\\\\\\"}\\\\n },\\\\n \\\\\\\"confidence\\\\\\\": 0.68,\\\\n \\\\\\\"reasoning\\\\\\\": \\\\\\\"\\\\u5728\\\\u7ed9\\\\u5b9a365\\\\u6839\\\\u65e5K\\\\u7ebf\\\\u4e2d\\\\uff0c\\\\u4ef7\\\\u683c\\\\u57280.26\\\\u20130.34\\\\u533a\\\\u95f4\\\\u53cd\\\\u590d\\\\u6a2a\\\\u76d8\\\\uff0c\\\\u8fd1\\\\u51e0\\\\u767e\\\\u65e5\\\\u672a\\\\u5f62\\\\u6210\\\\u660e\\\\u786e\\\\u7684\\\\u5355\\\\u8fb9\\\\u7a81\\\\u7834\\\\uff0c\\\\u6210\\\\u4ea4\\\\u91cf\\\\u4ea6\\\\u5448\\\\u73b0\\\\u9636\\\\u6bb5\\\\u6027\\\\u56de\\\\u843d\\\\u4e0e\\\\u653e\\\\u5927\\\\u4ea4\\\\u9519\\\\uff0c\\\\u7f3a\\\\u4e4f\\\\u6e05\\\\u6670\\\\u76845\\\\u6d6a\\\\u63a8\\\\u52a8\\\\u5f62\\\\u6210\\\\u3002\\\\u5f53\\\\u524d\\\\u6700\\\\u65b0\\\\u6536\\\\u76d8\\\\u4ef7\\\\u7ea60.2825\\\\uff0c\\\\u4f4d\\\\u4e8e\\\\u533a\\\\u95f4\\\\u4e2d\\\\u6bb5\\\\u504f\\\\u4e0b\\\\u65b9\\\\uff0c\\\\u8fdc\\\\u79bb\\\\u4e0a\\\\u65b9\\\\u5173\\\\u952e\\\\u963b\\\\u529b\\\\u4f46\\\\u9ad8\\\\u4e8e\\\\u5e95\\\\u90e8\\\\u652f\\\\u6491\\\\uff0c\\\\u7b26\\\\u5408\\\\u5e95\\\\u90e8\\\\u9707\\\\u8361/\\\\u6574\\\\u7406\\\\u9636\\\\u6bb5\\\\u7684\\\\u7279\\\\u5f81\\\\uff1b\\\\u82e5\\\\u7a81\\\\u7834\\\\u4e0a\\\\u6cbf0.30-0.305\\\\u533a\\\\u95f4\\\\uff0c\\\\u624d\\\\u6709\\\\u6982\\\\u7387\\\\u8fdb\\\\u5165\\\\u4e0b\\\\u4e00\\\\u4e2a\\\\u63a8\\\\u52a8\\\\u6d6a\\\\u6216\\\\u4e0a\\\\u884c\\\\u8d8b\\\\u52bf\\\\u3002\\\\u7531\\\\u4e8e\\\\u7f3a\\\\u4e4f\\\\u7a33\\\\u5b9a\\\\u7684\\\\u63a8\\\\u52a8\\\\u6d6a\\\\u7ed3\\\\u6784\\\\uff081-2-3-4-5\\\\uff09\\\\u6e05\\\\u6670\\\\u8d77\\\\u6b62\\\\u70b9\\\\uff0c\\\\u4e14\\\\u6ca1\\\\u6709\\\\u51fa\\\\u73b0\\\\u5178\\\\u578b\\\\u7684MACD\\\\u9876\\\\u80cc\\\\u79bb\\\\u6301\\\\u7eed\\\\u653e\\\\u5927\\\\u914d\\\\u5408\\\\u5934\\\\u80a9\\\\u9876\\\\u7b49\\\\u5f62\\\\u6001\\\\uff0c\\\\u7efc\\\\u5408\\\\u5224\\\\u65ad\\\\u4e3a\\\\u5f53\\\\u524d\\\\u5904\\\\u4e8e\\\\u5e95\\\\u90e8\\\\u9707\\\\u8361\\\\u4e2d\\\\u7684\\\\u6574\\\\u7406\\\\u9636\\\\u6bb5\\\\u3002\\\\\\\"\\\\n ,\\\\n \\\\\\\"key_levels\\\\\\\": {\\\\n \\\\\\\"support\\\\\\\": [0.275, 0.268],\\\\n \\\\\\\"resistance\\\\\\\": [0.30, 0.305]\\\\n },\\\\n \\\\\\\"next_expectation\\\\\\\": \\\\\\\"\\\\u82e5\\\\u4ef7\\\\u683c\\\\u6301\\\\u7eed\\\\u57280.275\\\\u20130.30\\\\u533a\\\\u95f4\\\\u6ce2\\\\u52a8\\\\uff0c\\\\u77ed\\\\u671f\\\\u4ee5\\\\u6a2a\\\\u76d8\\\\u4e3a\\\\u4e3b\\\\uff0c\\\\u7b49\\\\u5f85\\\\u7a81\\\\u7834\\\\u4e0a\\\\u6cbf0.30\\\\u20130.305\\\\u518d\\\\u8003\\\\u8651\\\\u8fdb\\\\u5165\\\\u8d8b\\\\u52bf\\\\u4ea4\\\\u6613\\\\uff1b\\\\u82e5\\\\u8dcc\\\\u7834\\\\u5e95\\\\u90e80.275\\\\uff0c\\\\u5219\\\\u53ef\\\\u80fd\\\\u8fdb\\\\u5165\\\\u66f4\\\\u6df1\\\\u7684\\\\u8c03\\\\u6574\\\\u6216\\\\u6d4b\\\\u8bd50.265/0.26\\\\u9644\\\\u8fd1\\\\u7684\\\\u66f4\\\\u5f3a\\\\u652f\\\\u6491\\\\u3002\\\\\\\",\\\\n \\\\\\\"decide\\\\\\\": {\\\\n \\\\\\\"side\\\\\\\": \\\\\\\"\\\\u89c2\\\\u671b\\\\\\\",\\\\n \\\\\\\"price\\\\\\\": 0.2825,\\\\n \\\\\\\"takeProfit\\\\\\\": \\\\\\\"\\\\u82e5\\\\u7a81\\\\u7834\\\\u4e0a\\\\u6cbf0.305\\\\uff0c\\\\u8003\\\\u8651\\\\u57280.31\\\\u9644\\\\u8fd1\\\\u8bbe\\\\u7f6e\\\\u6b62\\\\u76c8\\\\uff1b\\\\u82e5\\\\u7ee7\\\\u7eed\\\\u56de\\\\u843d\\\\u6d4b\\\\u8bd50.275\\\\u20130.27\\\\u533a\\\\u95f4\\\\uff0c\\\\u8003\\\\u8651\\\\u57280.27\\\\u9644\\\\u8fd1\\\\u8bbe\\\\u5b9a\\\\u6b62\\\\u635f\\\\u3002\\\\\\\",\\\\n \\\\\\\"stopLoss\\\\\\\": \\\\\\\"0.275\\\\uff08\\\\u4e0b\\\\u65b9\\\\u652f\\\\u6491\\\\u7565\\\\u4e0b\\\\u65b9\\\\u7684\\\\u4fdd\\\\u62a4\\\\u6027\\\\u6b62\\\\u635f\\\\u70b9\\\\uff09\\\\\\\"\\\\n },\\\\n \\\\\\\"reasoning_detail\\\\\\\": [\\\\n \\\\\\\"\\\\u6700\\\\u65b0\\\\u6536\\\\u76d8\\\\u63a5\\\\u8fd1\\\\u533a\\\\u95f4\\\\u4e2d\\\\u6bb5\\\\u504f\\\\u4e0b\\\\uff0c\\\\u5904\\\\u5728\\\\u4e2d\\\\u8f68\\\\u9644\\\\u8fd1\\\\u4f46\\\\u672a\\\\u7a81\\\\u7834\\\\u4e0a\\\\u6cbf\\\\uff0c\\\\u663e\\\\u793a\\\\u591a\\\\u5934\\\\u52a8\\\\u80fd\\\\u4e0d\\\\u8db3\\\\u4ee5\\\\u5feb\\\\u901f\\\\u63a8\\\\u52a8\\\\u4ef7\\\\u683c\\\\u7a81\\\\u78340.30\\\\u4ee5\\\\u4e0a\\\\u963b\\\\u529b\\\\u3002\\\\\\\",\\\\n \\\\\\\"\\\\u4ef7\\\\u683c\\\\u57280.27\\\\u20130.30\\\\u533a\\\\u95f4\\\\u957f\\\\u671f\\\\u5f98\\\\u5f8a\\\\uff0c\\\\u6210\\\\u4ea4\\\\u91cf\\\\u672a\\\\u663e\\\\u793a\\\\u6301\\\\u7eed\\\\u653e\\\\u5927\\\\u5f62\\\\u6210\\\\u5355\\\\u8fb9\\\\u8d8b\\\\u52bf\\\\uff0c\\\\u7b26\\\\u5408\\\\u5e95\\\\u90e8\\\\u9707\\\\u8361/\\\\u6574\\\\u7406\\\\u9636\\\\u6bb5\\\\u7684\\\\u5178\\\\u578b\\\\u7279\\\\u5f81\\\\u3002\\\\\\\",\\\\n \\\\\\\"\\\\u82e5\\\\u51fa\\\\u73b0MACD\\\\u80cc\\\\u79bb\\\\u6216\\\\u5934\\\\u80a9\\\\u9876\\\\u7b49\\\\u5178\\\\u578b\\\\u9876\\\\u90e8\\\\u4fe1\\\\u53f7\\\\uff0c\\\\u624d\\\\u9700\\\\u8981\\\\u91cd\\\\u65b0\\\\u8bc4\\\\u4f30\\\\u8fdb\\\\u5165\\\\u8c03\\\\u6574\\\\u6d6a\\\\u6216\\\\u9876\\\\u90e8\\\\u7684\\\\u53ef\\\\u80fd\\\\u6027\\\\uff1b\\\\u5f53\\\\u524d\\\\u6570\\\\u636e\\\\u672a\\\\u51fa\\\\u73b0\\\\u6301\\\\u7eed\\\\u660e\\\\u6670\\\\u76845\\\\u6d6a\\\\u63a8\\\\u52a8\\\\u7ed3\\\\u6784\\\\u3002\\\\\\\"\\\\n ]\\\\n}\\\"\\n\" +\n \" }\\n\" +\n \" ],\\n\" +\n \" \\\"role\\\": \\\"assistant\\\"\\n\" +\n \" }\\n\" +\n \" ],\\n\" +\n \" \\\"parallel_tool_calls\\\": true,\\n\" +\n \" \\\"presence_penalty\\\": 0.0,\\n\" +\n \" \\\"previous_response_id\\\": null,\\n\" +\n \" \\\"prompt_cache_key\\\": null,\\n\" +\n \" \\\"prompt_cache_retention\\\": null,\\n\" +\n \" \\\"reasoning\\\": {\\n\" +\n \" \\\"effort\\\": \\\"low\\\",\\n\" +\n \" \\\"summary\\\": null\\n\" +\n \" },\\n\" +\n \" \\\"safety_identifier\\\": null,\\n\" +\n \" \\\"service_tier\\\": \\\"default\\\",\\n\" +\n \" \\\"store\\\": true,\\n\" +\n \" \\\"temperature\\\": 1.0,\\n\" +\n \" \\\"text\\\": {\\n\" +\n \" \\\"format\\\": {\\n\" +\n \" \\\"type\\\": \\\"text\\\"\\n\" +\n \" },\\n\" +\n \" \\\"verbosity\\\": \\\"medium\\\"\\n\" +\n \" },\\n\" +\n \" \\\"tool_choice\\\": \\\"auto\\\",\\n\" +\n \" \\\"tools\\\": [],\\n\" +\n \" \\\"top_logprobs\\\": 0,\\n\" +\n \" \\\"top_p\\\": 1.0,\\n\" +\n \" \\\"truncation\\\": \\\"disabled\\\",\\n\" +\n \" \\\"usage\\\": {\\n\" +\n \" \\\"input_tokens\\\": 16697,\\n\" +\n \" \\\"input_tokens_details\\\": {\\n\" +\n \" \\\"cached_tokens\\\": 0\\n\" +\n \" },\\n\" +\n \" \\\"output_tokens\\\": 1129,\\n\" +\n \" \\\"output_tokens_details\\\": {\\n\" +\n \" \\\"reasoning_tokens\\\": 448\\n\" +\n \" },\\n\" +\n \" \\\"total_tokens\\\": 17826\\n\" +\n \" },\\n\" +\n \" \\\"user\\\": null,\\n\" +\n \" \\\"metadata\\\": {}\\n\" +\n \"}\";\n\n TChatGPTResponse chatGPTResponse = JsonUtils.Deserialize(txt, TChatGPTResponse.class);\n\n if (chatGPTResponse.output != null) {\n for (TChatGPTResponse.Output output: chatGPTResponse.output) {\n if (output.content != null) {\n for (TChatGPTResponse.Content content: output.content) {\n if (content.type.equals(\"output_text\")) {\n TWaveAnalysisResult result = DeepSeekJsonParser.getAnalysisResultFromResponse(content.text);\n\n System.out.println(JsonUtils.Serializer(result));\n }\n }\n }\n }\n }\n\n// System.out.println(chatGPTResponse);\n\n }\n\n}\n"}, "7d01240c61e6b87219e8634850b3a318279396f1": {"t": 1785770770.863758, "n": 9594, "h": "9568b472774e3f5b", "c": "package com.app.dc.indsvr;\n\nimport com.app.common.utils.JsonUtils;\nimport com.app.dc.fix.message.MarketDataSnapshotFullRefresh;\nimport com.app.dc.po.deepseek.DeepSeekJsonParser;\nimport com.app.dc.po.deepseek.TDeepSeekChoice;\nimport com.app.dc.po.deepseek.TDeepSeekResponse;\nimport com.app.dc.service.signal.day.Candle;\nimport com.app.dc.service.signal.deepSeek.KineData;\nimport com.app.dc.service.utils.RestTemplateConfig;\nimport org.junit.Test;\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.http.MediaType;\nimport org.ta4j.core.Bar;\nimport org.ta4j.core.BaseBar;\n\nimport java.io.IOException;\nimport java.math.BigDecimal;\nimport java.nio.charset.StandardCharsets;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.time.Duration;\nimport java.time.Instant;\nimport java.time.ZoneId;\nimport java.time.ZonedDateTime;\nimport java.time.format.DateTimeFormatter;\nimport java.util.*;\n\npublic class TestChatGPT {\n\n String apiKey = \"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\";\n String baseUrl = \"https://api.openai.com/v1/responses\";\n private static final DateTimeFormatter YYYYMMDD_FORMATTER =\n DateTimeFormatter.ofPattern(\"yyyyMMdd\");\n\n\n @Test\n public void TestBaseHttpToChatGPT() {\n\n RestTemplateConfig restTemplateConfig = new RestTemplateConfig();\n restTemplateConfig.restTemplate();\n\n // ---------- request body ----------\n Map<String, Object> requestMap = new HashMap<>();\n requestMap.put(\"model\", \"gpt-5-nano\");\n requestMap.put(\"max_output_tokens\", 2000);\n requestMap.put(\"temperature\", 0.1);\n\n // input\n List<Map<String, Object>> input = new ArrayList<>();\n Map<String, Object> userMsg = new HashMap<>();\n userMsg.put(\"role\", \"user\");\n\n List<Map<String, String>> content = new ArrayList<>();\n content.add(Map.of(\n \"type\", \"input_text\",\n \"text\", \"Say this is a test\"\n ));\n\n userMsg.put(\"content\", content);\n input.add(userMsg);\n\n requestMap.put(\"input\", input);\n\n // ---------- headers ----------\n HttpHeaders headers = new HttpHeaders();\n headers.setContentType(MediaType.APPLICATION_JSON);\n headers.setBearerAuth(apiKey);\n\n String requstData = JsonUtils.Serializer(requestMap);\n\n // ---------- request ----------\n String response = restTemplateConfig.post(\n baseUrl,\n requstData,\n headers\n );\n\n System.out.println(response);\n\n }\n\n\n\n @Test\n public void TestChatGPTModel() throws IOException {\n\n RestTemplateConfig restTemplateConfig = new RestTemplateConfig();\n\n restTemplateConfig.restTemplate();\n\n // 使用HashMap构建JSON结构\n Map<String, Object> requestMap = new HashMap<>();\n requestMap.put(\"model\", \"deepseek-chat\");\n requestMap.put(\"stream\", false);\n\n Map<String, String> respMap = new HashMap<>();\n respMap.put(\"type\", \"json_object\");\n // 1. 设置JSON响应格式\n requestMap.put(\"response_format\", respMap);\n\n // 2. 针对大数据量的关键参数\n requestMap.put(\"max_tokens\", 2000); // 增加输出长度\n requestMap.put(\"temperature\", 0.1); // 降低随机性,保证分析一致性\n\n // 构建messages数组\n List<Map<String, String>> messages = new ArrayList<>();\n\n// addHistoryMsg(messages);\n\n String klineJsonArray = getKLineData();\n\n System.out.println(\"klineJsonArray:\" + klineJsonArray);\n\n messages.add(Map.of(\n \"role\", \"system\",\n \"content\",\n \"角色:B圈艾略特波浪理论和震荡首席分析师\" +\n \"分析user传的最新的365根日K线,完成以下任务:\\n\\n\" +\n \"【任务】根据给出的K线数据,分析当前行情处于哪个阶段,是处于底部震荡还是处于波浪理论的结构当中\\n\" +\n \"- 若处于波浪结构中\\n\" +\n \"- 如果存在推动浪(1-2-3-4-5),标注每浪起止点\\n\" +\n \"- 如果存在5浪到A浪的顶部震荡调整,可通过MACD顶背离和头肩顶形态识别,并标注起止点\" +\n \"- 如果存在调整浪(A-B-C),标注调整模式\\n\" +\n\n \"- 若处于底部震荡\\n\" +\n \"- 震荡区间:[上沿价格, 下沿价格]\\n\" +\n \"- 震荡时间:已持续N日\\n\" +\n \"- 最新位置:\\\"接近突破上沿\\\"或\\\"中轨附近\\\"\\n\\n\" +\n \"返回完整分析结果JSON。\"\n ));\n\n String userContent = String.format(\n \"这是最新的365根日K线数据(JSON数组格式),请分析最新日K线的位置:\\n\\n\" +\n \"K线数据按时间顺序正序:\\n%s\\n\\n\" +\n \"请分析并返回JSON结果,注意描述reasoning、next_expectation返回不要有啥特殊字符,格式:\\n\" +\n \"{\\n\" +\n \" \\\"analysis_type\\\": \\\"wave\\\" 或 \\\"consolidation\\\",\\n\" +\n \" \\\"current_position\\\": \\\"第3浪\\\" 或 \\\"B浪调整\\\" 或 \\\"震荡上沿\\\" 等,\\n\" +\n \" \\\"wave_count\\\": {\\n\" +\n \" \\\"wave_1\\\": {\\\"start\\\": \\\"给出yyyymmdd格式的日K线时间点\\\", \\\"end\\\": \\\"给出yyyymmdd格式的日K线时间点\\\"},\\n\" +\n \" \\\"wave_2\\\": {\\\"start\\\": \\\"给出yyyymmdd格式的日K线时间点\\\", \\\"end\\\": \\\"给出yyyymmdd格式的日K线时间点\\\"}\\n\" +\n \" },\\n\" +\n \" \\\"confidence\\\": 0.85,\\n\" +\n \" \\\"reasoning\\\": \\\"详细分析逻辑,包括趋势线、成交量、波浪规则验证\\\",\\n\" +\n \" \\\"key_levels\\\": {\\n\" +\n \" \\\"support\\\": [支撑位1, 支撑位2],\\n\" +\n \" \\\"resistance\\\": [阻力位1, 阻力位2]\\n\" +\n \" },\\n\" +\n \" \\\"next_expectation\\\": \\\"下一阶段的可能走势\\\"\\n\" +\n \" \\\"decide\\\": {\\\"side\\\": \\\"开仓方向:多/空/观望\\\", \\\"price\\\": \\\"明确的一个开仓价格\\\", \\\"takeProfit\\\": \\\"明确的一个止盈价格,不要太贪,阻力位1附近就行\\\", \\\"stopLoss\\\": \\\"明确的一个止损价格\\\"}\\n\" +\n \"}\",\n klineJsonArray\n );\n\n\n // 用户消息(包含优化后的数据)\n messages.add(Map.of(\n \"role\", \"user\",\n \"content\", userContent\n ));\n\n// messages.add(Map.of(\n// \"role\", \"user\",\n// \"content\", \"解析是你理解的波浪理论:推动浪(1-2-3-4-5),调整浪(A-B-C)\"));\n\n\n requestMap.put(\"messages\", messages);\n\n String requestJson = JsonUtils.Serializer(requestMap);\n\n// System.out.println(\"req:\" + requestJson);\n\n // 设置请求头\n HttpHeaders headers = new HttpHeaders();\n headers.setContentType(org.springframework.http.MediaType.APPLICATION_JSON);\n headers.set(\"Authorization\", \"Bearer \" + apiKey);\n\n // 发送请求\n String response = restTemplateConfig.post(\n baseUrl, requestJson,\n headers\n );\n\n System.out.println(\"DeepSeek响应: \" + response);\n\n try {\n\n TDeepSeekResponse deepSeekResponse = DeepSeekJsonParser.parseFullResponse(response);\n\n TDeepSeekChoice deepSeekChoice = deepSeekResponse.choices[0];\n\n System.out.println(\"content:\" + deepSeekChoice.message.content);\n\n// TWaveAnalysisResult result = DeepSeekJsonParser.getAnalysisResultFromResponse(deepSeekChoice.message.content);\n\n } catch (Exception e) {\n System.err.println(\"解析响应失败: \" + e.getMessage());\n }\n\n }\n\n\n\n private String getKLineData() throws IOException {\n\n String symbol = \"ETHUSDT\";\n String text = \"1d\";\n\n // 1. 读取本地 1d K 线文件\n Path path = Path.of(\"config\", symbol + \"-1d.txt\");\n String priceContent = Files.readString(path, StandardCharsets.UTF_8);\n\n com.app.dc.service.signal.deepSeek.KineData kineData = JsonUtils.Deserialize(priceContent, KineData.class);\n\n // 反序列化为行情列表(文件里是倒序的也没关系,我们后面统一按时间升序排序)\n List<MarketDataSnapshotFullRefresh> rawList = kineData.getData();\n// JsonUtils.Deserialize2(priceContent, MarketDataSnapshotFullRefresh.class);\n\n System.out.println(\"Kline size:\" + rawList.size());\n\n // 2. 按时间升序排序,保证 index 越大时间越晚\n rawList.sort(Comparator.comparingLong(m -> Long.parseLong(m.getInfo1())));\n\n List<Candle> fullCandles = new ArrayList<>();\n List<ZonedDateTime> barTimes = new ArrayList<>();\n\n for (MarketDataSnapshotFullRefresh mdsf : rawList) {\n\n // info1 是时间戳(毫秒)\n ZonedDateTime time = ZonedDateTime.ofInstant(\n Instant.ofEpochMilli(Long.parseLong(mdsf.getInfo1())),\n ZoneId.systemDefault()\n );\n\n BigDecimal open = new BigDecimal(mdsf.getOpenPrice());\n BigDecimal high = new BigDecimal(mdsf.getHighPrice());\n BigDecimal low = new BigDecimal(mdsf.getLowPrice());\n BigDecimal close = new BigDecimal(mdsf.getClosePrice());\n BigDecimal volume = new BigDecimal(mdsf.getVolume());\n\n // info3 是周期,例如 \"1d\" => \"PT1D\"\n String temTxt = \"PT\" + mdsf.getInfo3().toUpperCase();\n\n String interval = mdsf.getInfo3().toUpperCase();\n\n long days = Long.parseLong(interval.substring(0, interval.length() - 1));\n Duration barDuration = Duration.ofDays(days);\n\n// Bar newBar = new BaseBar(Duration.parse(temTxt), time, open, high, low, close, volume);\n Bar newBar = new BaseBar(barDuration, time, open, high, low, close, volume);\n\n Candle candle = new Candle(\n open.doubleValue(),\n high.doubleValue(),\n low.doubleValue(),\n close.doubleValue(),\n volume.doubleValue(),\n time.format(YYYYMMDD_FORMATTER)\n );\n fullCandles.add(candle);\n }\n\n return JsonUtils.Serializer(fullCandles);\n }\n\n\n}\n"}}}} |