Browse Source

EXPMA指标

liruiqiang
liruiqiang 1 week ago
parent
commit
48aa7dafbc
  1. 7
      src/main/java/com/deepchart/controller/IndexController.java
  2. 2
      src/main/java/com/deepchart/service/IndexService.java
  3. 10
      src/main/java/com/deepchart/service/impl/IndexServiceImpl.java
  4. 156
      src/main/java/com/deepchart/utils/EXPMAUtil.java

7
src/main/java/com/deepchart/controller/IndexController.java

@ -39,4 +39,11 @@ public class IndexController {
String result = indexService.cci(list);
return Result.success("success", result);
}
@PostMapping("/expma")
public Result expma(@RequestBody StockInfo stock) {
List<StockDailyData> list = indexService.getStockData(stock);
String result = indexService.expma(list);
return Result.success("success", result);
}
}

2
src/main/java/com/deepchart/service/IndexService.java

@ -13,4 +13,6 @@ public interface IndexService {
String ma(List<StockDailyData> list);
String cci(List<StockDailyData> list);
String expma(List<StockDailyData> list);
}

10
src/main/java/com/deepchart/service/impl/IndexServiceImpl.java

@ -3,10 +3,7 @@ package com.deepchart.service.impl;
import com.deepchart.entity.StockDailyData;
import com.deepchart.entity.StockInfo;
import com.deepchart.service.IndexService;
import com.deepchart.utils.CCIUtil;
import com.deepchart.utils.MACDUtil;
import com.deepchart.utils.MAUtil;
import com.deepchart.utils.StockDataUtil;
import com.deepchart.utils.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@ -37,4 +34,9 @@ public class IndexServiceImpl implements IndexService {
public String cci(List<StockDailyData> list) {
return CCIUtil.analyzeCCI(list);
}
@Override
public String expma(List<StockDailyData> list) {
return EXPMAUtil.analyzeEXPMA(list,12,40);
}
}

156
src/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) { // 价格接近短期EXPMA1%
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;
}
}
Loading…
Cancel
Save