Files

1 line
205 KiB
JSON
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{"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"}}}}