4 changed files with 171 additions and 4 deletions
-
7src/main/java/com/deepchart/controller/IndexController.java
-
2src/main/java/com/deepchart/service/IndexService.java
-
10src/main/java/com/deepchart/service/impl/IndexServiceImpl.java
-
156src/main/java/com/deepchart/utils/EXPMAUtil.java
@ -0,0 +1,156 @@ |
|||||
|
package com.deepchart.utils; |
||||
|
|
||||
|
import com.deepchart.entity.StockDailyData; |
||||
|
|
||||
|
import java.time.LocalDate; |
||||
|
import java.util.*; |
||||
|
|
||||
|
public class EXPMAUtil { |
||||
|
|
||||
|
public static String analyzeEXPMA(List<StockDailyData> stockData, int shortPeriod, int longPeriod) { |
||||
|
if (stockData == null || stockData.size() < Math.max(shortPeriod, longPeriod)) { |
||||
|
return "数据不足,无法进行 EXPMA 分析。请至少提供 " + Math.max(shortPeriod, longPeriod) + " 天数据。"; |
||||
|
} |
||||
|
|
||||
|
// 1. 计算 EXPMA |
||||
|
List<Double> expmaShort = calculateSingleExpma(stockData, shortPeriod); |
||||
|
List<Double> expmaLong = calculateSingleExpma(stockData, longPeriod); |
||||
|
|
||||
|
int n = stockData.size(); |
||||
|
LocalDate latestDate = stockData.get(n - 1).getDate(); |
||||
|
double latestPrice = stockData.get(n - 1).getClosePrice(); |
||||
|
double currentShort = expmaShort.get(n - 1); |
||||
|
double currentLong = expmaLong.get(n - 1); |
||||
|
|
||||
|
StringBuilder analysis = new StringBuilder(); |
||||
|
analysis.append("📊 EXPMA 趋势分析(截至 ").append(latestDate).append(")\n"); |
||||
|
analysis.append(" • 短期EXPMA(").append(shortPeriod).append("): ").append(String.format("%.2f", currentShort)).append("\n"); |
||||
|
analysis.append(" • 长期EXPMA(").append(longPeriod).append("): ").append(String.format("%.2f", currentLong)).append("\n"); |
||||
|
analysis.append(" • 当前收盘价: ").append(String.format("%.2f", latestPrice)).append("\n\n"); |
||||
|
|
||||
|
// 2. 判断多头/空头排列 |
||||
|
boolean isBullish = currentShort > currentLong; |
||||
|
boolean shortUp = n >= 2 && expmaShort.get(n - 1) > expmaShort.get(n - 2); |
||||
|
boolean longUp = n >= 2 && expmaLong.get(n - 1) > expmaLong.get(n - 2); |
||||
|
|
||||
|
if (isBullish && shortUp && longUp) { |
||||
|
analysis.append("📈 趋势状态:【多头排列】\n"); |
||||
|
analysis.append(" → 市场处于上升趋势,短期均线在长期均线上方且双双上行。\n"); |
||||
|
if (latestPrice >= currentShort) { |
||||
|
analysis.append(" → 股价站稳短期EXPMA之上,强势格局。\n"); |
||||
|
} else { |
||||
|
analysis.append(" → 股价回踩短期EXPMA,若不破可视为加仓机会。\n"); |
||||
|
} |
||||
|
} else if (!isBullish && !shortUp && !longUp) { |
||||
|
analysis.append("📉 趋势状态:【空头排列】\n"); |
||||
|
analysis.append(" → 市场处于下降趋势,短期均线在长期均线下方且双双下行。\n"); |
||||
|
if (latestPrice <= currentShort) { |
||||
|
analysis.append(" → 股价受压于短期EXPMA,弱势明显,建议观望。\n"); |
||||
|
} else { |
||||
|
analysis.append(" → 股价反弹至短期EXPMA附近,可能是做空或减仓时机。\n"); |
||||
|
} |
||||
|
} else { |
||||
|
analysis.append("🔄 趋势状态:【震荡整理】\n"); |
||||
|
analysis.append(" → 均线缠绕或方向不一致,市场缺乏明确趋势。\n"); |
||||
|
} |
||||
|
|
||||
|
// 3. 检测最近是否发生金叉/死叉(过去5日内) |
||||
|
boolean goldenCross = false; |
||||
|
boolean deathCross = false; |
||||
|
int crossDayIndex = -1; |
||||
|
String crossType = ""; |
||||
|
|
||||
|
// 从后往前找最近一次交叉(最多看最近10天) |
||||
|
int lookback = Math.min(10, n - 1); |
||||
|
for (int i = n - 1; i >= lookback; i--) { |
||||
|
double shortToday = expmaShort.get(i); |
||||
|
double longToday = expmaLong.get(i); |
||||
|
double shortYesterday = expmaShort.get(i - 1); |
||||
|
double longYesterday = expmaLong.get(i - 1); |
||||
|
|
||||
|
// 金叉:昨日 short <= long,今日 short > long |
||||
|
if (shortYesterday <= longYesterday && shortToday > longToday) { |
||||
|
goldenCross = true; |
||||
|
crossDayIndex = i; |
||||
|
crossType = "黄金交叉"; |
||||
|
break; |
||||
|
} |
||||
|
// 死叉:昨日 short >= long,今日 short < long |
||||
|
if (shortYesterday >= longYesterday && shortToday < longToday) { |
||||
|
deathCross = true; |
||||
|
crossDayIndex = i; |
||||
|
crossType = "死亡交叉"; |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (goldenCross || deathCross) { |
||||
|
LocalDate crossDate = stockData.get(crossDayIndex).getDate(); |
||||
|
analysis.append("\n🔔 近期信号:") |
||||
|
.append(crossType) |
||||
|
.append("(").append(crossDate).append(")\n"); |
||||
|
if (goldenCross) { |
||||
|
analysis.append(" → 短期动能转强,可视为潜在买入信号。\n"); |
||||
|
if (currentLong > expmaLong.get(Math.max(0, crossDayIndex - 5))) { |
||||
|
analysis.append(" → 且长期EXPMA已走平或向上,信号可靠性较高。\n"); |
||||
|
} |
||||
|
} else { |
||||
|
analysis.append(" → 短期动能转弱,可视为潜在卖出信号。\n"); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// 4. 支撑/阻力提示 |
||||
|
if (isBullish) { |
||||
|
double support = currentShort; |
||||
|
if (Math.abs(latestPrice - support) / support < 0.01) { // 价格接近短期EXPMA(1%内) |
||||
|
analysis.append("\n🛡️ 支撑观察:当前股价接近短期EXPMA(") |
||||
|
.append(String.format("%.2f", support)) |
||||
|
.append("),若在此企稳,可能延续升势。\n"); |
||||
|
} |
||||
|
} else if (!isBullish) { |
||||
|
double resistance = currentShort; |
||||
|
if (Math.abs(latestPrice - resistance) / resistance < 0.01) { |
||||
|
analysis.append("\n⚠️ 阻力观察:当前股价接近短期EXPMA(") |
||||
|
.append(String.format("%.2f", resistance)) |
||||
|
.append("),若遇阻回落,可能延续跌势。\n"); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// 5. 总结建议 |
||||
|
analysis.append("\n📌 操作建议:\n"); |
||||
|
if (isBullish && shortUp) { |
||||
|
analysis.append(" • 多头趋势中,持股待涨;回调至短期EXPMA附近可考虑低吸。\n"); |
||||
|
} else if (!isBullish && !shortUp) { |
||||
|
analysis.append(" • 空头趋势中,避免抄底;反弹至短期EXPMA附近可考虑离场。\n"); |
||||
|
} else { |
||||
|
analysis.append(" • 市场方向不明,建议结合成交量或震荡指标(如KDJ)辅助决策。\n"); |
||||
|
} |
||||
|
analysis.append("\n❗ 注意:EXPMA为趋势跟踪指标,在横盘震荡市中易产生假信号,请勿单独依赖。"); |
||||
|
|
||||
|
return analysis.toString(); |
||||
|
} |
||||
|
|
||||
|
// 辅助方法:计算单周期 EXPMA |
||||
|
private static List<Double> calculateSingleExpma(List<StockDailyData> data, int period) { |
||||
|
List<Double> expma = new ArrayList<>(data.size()); |
||||
|
double alpha = 2.0 / (period + 1); |
||||
|
double value = 0.0; |
||||
|
|
||||
|
for (int i = 0; i < data.size(); i++) { |
||||
|
double price = data.get(i).getClosePrice(); |
||||
|
if (i == 0) { |
||||
|
// 初始值用 SMA(period) |
||||
|
double sum = 0; |
||||
|
int count = Math.min(period, data.size()); |
||||
|
for (int j = 0; j < count; j++) { |
||||
|
sum += data.get(j).getClosePrice(); |
||||
|
} |
||||
|
value = sum / count; |
||||
|
} else { |
||||
|
value = alpha * price + (1 - alpha) * value; |
||||
|
} |
||||
|
expma.add(value); |
||||
|
} |
||||
|
return expma; |
||||
|
} |
||||
|
} |
||||
Write
Preview
Loading…
Cancel
Save
Reference in new issue