1、MACD
(Moving Average Convergence Divergence)是一种常用的技术分析工具,它通过计算两个不同周期的移动平均线(MA)的差值来分析股票或其他金融产品的价格趋势。MACD的主要组成部分包括:
DIF(Difference of Fast and Slow Moving Average):这是两个不同周期的移动平均线(MA)之间的差值,其中较快的移动平均线减去较慢的移动平均线。
DEA(Difference of DIF and its Moving Average):这是DIF与其自身的移动平均线之间的差值,也称为MACD的信号线。
柱状图:表示DIF与DEA之间的差异,通常在0轴上方或下方。
MACD的主要用途包括:
趋势判断:当MACD从负数转向正数时,是买入信号;当MACD从正数转向负数时,是卖出信号。
背离分析:MACD还可以用于分析价格与指标之间的背离情况,这有助于预测价格趋势的转折点。
柱状图分析:柱状图的收缩和放大可以帮助判断趋势的强度和可能的转折点。
MACD的优点在于它能够平滑地处理数据,减少随机波动的影响,从而提供更稳定的趋势分析。然而,它也有其局限性,例如在市场波动剧烈时可能无法提供清晰的信号。
2、KDJ
KDJ指标中文名叫随机指标,是一种相当新颖、实用的技术分析指标,它起先用于期货市场的分析,后被广泛用于股市的中短期趋势分析,是期货和股票市场上最常用的技术分析工具。
随机指标KDJ一般是用于股票分析的统计体系,根据统计学原理,通过一个特定的周期(常为9日、9周等)内出现过的最高价、最低价及最后一个计算周期的收盘价及这三者之间的比例关系,来计算最后一个计算周期的未成熟随机值RSV,然后根据平滑移动平均线的方法来计算K值、D值与J值,并绘成曲线图来研判股票走势。
3、数据来源
东方财富网
//0:深圳,1:上海,2:其他
secid="0.399997";
String url="https://push2his.eastmoney.com/api/qt/stock/kline/get?secid="+secid+"&fields1=f1,f2,f3,f4,f5&fields2=f51,f52,f53,f54,f55,f56,f57,f58,f59,f60,f61&lmt=1000&klt=101&fqt=1&end=30000101&ut=fa5fd1943c7b386f172d6893dbfba10b&cb=cb_1701680426583_1676563&cb_1701680426583_1676563=cb_1701680426583_1676563";
secid为股票代码指数
4、计算工具类
package cn.demo;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.read.listener.ReadListener;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.tictactec.ta.lib.Core;
import com.tictactec.ta.lib.MAType;
import com.tictactec.ta.lib.MInteger;
import com.tictactec.ta.lib.RetCode;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
/**
* @date 2022年3月29日13:50:24
* @Description
* 股票指标工具类
* 用来计算各种指标(如:ma,rsi,kdj)
* @author flashylife
*/
public class StockIndexUtils {
/**
* 禁止初始化
*/
private StockIndexUtils() {}
/**
* 获取ma
*
* @param value ma的参数(5,10,15,20等)
* @return
*/
public static double[] ma(int value, List<StockBasicInfoVO> list) {
if (value <= 0) {
throw new RuntimeException("天数不能小于0");
}
// 收盘价
double[] inClose = new double[list.size()];
// 返回的数据最后value + 1没有值
double[] output = new double[list.size() - value + 1];
for (int i = 0; i < list.size(); i++) {
inClose[i] = list.get(i).getSbiClose().doubleValue();
}
Core core = new Core();
// ma 需要四舍五入
RetCode code = core.sma(0, inClose.length - 1, inClose, value, new MInteger(), new MInteger(), output);
if (code == RetCode.Success) {
output = Arrays.stream(output)
.map(e -> new BigDecimal(e)
.setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue())
.toArray();
}
return output;
}
/**
* 获取rsi
* @param value rsi的参数(6,12,24等)
* @return
*/
public static double[] rsi(int value, List<StockBasicInfoVO> list) {
// 收盘价
double[] inClose = new double[list.size()];
// 返回的数据最后value没有值
double[] output = new double[list.size() - value];
for (int i = 0; i < list.size(); i++) {
inClose[i] = list.get(i).getSbiClose().doubleValue();
}
Core core = new Core();
// rsi 直接截取
RetCode code = core.rsi(0, inClose.length - 1, inClose, value, new MInteger(), new MInteger(), output);
if (code == RetCode.Success) {
output = Arrays.stream(output)
.map(e -> new BigDecimal(e)
.setScale(2, BigDecimal.ROUND_DOWN).doubleValue())
.toArray();
}
return output;
}
/**
* 获取kdj(使用默认的9,3,3)
*
* @return
*/
public static List<double[]> kdj(List<StockBasicInfoVO> list) {
// 最高
double[] inHigh = new double[list.size()];
// 收盘
double[] inClose = new double[list.size()];
// 最低
double[] inLow = new double[list.size()];
// 输出的k(最后16位没有值)
double[] k = new double[list.size() - 16];
// 输出的d(最后16位没有值)
double[] d = new double[list.size() - 16];
// 手动计算j值(3*k - 2*d)
double[] j = new double[list.size() - 16];
for (int i = 0; i < list.size(); i++) {
inHigh[i] = list.get(i).getSbiHigh().doubleValue();
inClose[i] = list.get(i).getSbiClose().doubleValue();
inLow[i] = list.get(i).getSbiLow().doubleValue();
}
List<double[]> kdjList = null;
Core core = new Core();
// kd 直接截取
RetCode code = core.stoch(0, inHigh.length - 1, inHigh, inLow, inClose, 9, 5, MAType.Ema, 5, MAType.Ema, new MInteger(), new MInteger(), k, d);
if (code == RetCode.Success) {
// 计算j值(保留2位)
for (int i = 0; i < d.length; i++) {
BigDecimal b1 = new BigDecimal(k[i]).multiply(new BigDecimal(3));
BigDecimal b2 = new BigDecimal(d[i]).multiply(new BigDecimal(2));
j[i] = b1.subtract(b2).setScale(2, BigDecimal.ROUND_DOWN).doubleValue();
}
// 计算k值(保留2位)
k = Arrays.stream(k)
.map(e -> new BigDecimal(e)
.setScale(2, BigDecimal.ROUND_DOWN).doubleValue())
.toArray();
// 计算d值(保留2位)
d = Arrays.stream(d)
.map(e -> new BigDecimal(e)
.setScale(2, BigDecimal.ROUND_DOWN).doubleValue())
.toArray();
kdjList = new ArrayList();
kdjList.add(k);
kdjList.add(d);
kdjList.add(j);
}
return kdjList == null ? Collections.emptyList() : kdjList;
}
public static List<KdjVO> getKdjVO( String code,Integer max) throws IOException, InterruptedException {
String url="https://push2his.eastmoney.com/api/qt/stock/kline/get?secid=0."+code+"&fields1=f1,f2,f3,f4,f5&fields2=f51,f52,f53,f54,f55,f56,f57,f58,f59,f60,f61&lmt="+max+"&klt=101&fqt=1&end=30000101&ut=fa5fd1943c7b386f172d6893dbfba10b&cb=cb_1701680426583_1676563&cb_1701680426583_1676563=cb_1701680426583_1676563";
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(url))
.method("POST", HttpRequest.BodyPublishers.noBody())
.build();
HttpResponse<String> response = HttpClient.newHttpClient().send(request, HttpResponse.BodyHandlers.ofString());
String body = response.body();
int i = body.indexOf("{");
String json = body.substring(i, body.length() - 2);
JSONObject jsonObject = JSONObject.parseObject(json);
String data = jsonObject.getString("data");
JSONObject dataJson = JSONObject.parseObject(data);
JSONArray klines = dataJson.getJSONArray("klines");
List<String> list = klines.toList(String.class);
List<StockBasicInfoVO> stockBasicInfoVOList=new ArrayList<>();
list.stream().forEach(item->{
String[] split = item.split(",");
StockBasicInfoVO build = StockBasicInfoVO.builder()
.sbiTradeDate(split[0])
.sbiHigh(new BigDecimal(split[3]))
.sbiClose(new BigDecimal(split[2]))
.sbiLow(new BigDecimal(split[4]))
.build();
stockBasicInfoVOList.add(build);
});
stockBasicInfoVOList.sort(Comparator.comparing(StockBasicInfoVO::getSbiTradeDate));
List<double[]> kdj = StockIndexUtils.kdj(stockBasicInfoVOList);
List<KdjVO> kdjVOList= new ArrayList<>();
List<String> dateList=new ArrayList<>();
List<Double> k =new ArrayList<>();
List<Double> d =new ArrayList<>();
List<Double> js =new ArrayList<>();
List<Double> sp =new ArrayList<>();
kdj.stream().forEach(item->{
for (int j=0;j<item.length;j++){
KdjVO kdjVO = KdjVO.builder()
.date(stockBasicInfoVOList.get(j + 16).getSbiTradeDate())
.k(kdj.get(0)[j])
.d(kdj.get(1)[j])
.j(kdj.get(2)[j])
.build();
kdjVOList.add(kdjVO);
dateList.add("'"+kdjVO.getDate()+"'");
k.add(kdjVO.getK());
d.add(kdjVO.getD());
js.add(kdjVO.getJ());
sp.add(stockBasicInfoVOList.get(j + 16).getSbiClose().doubleValue()/100);
}
});
System.out.println(dateList.size());
System.out.println(k.size());
System.out.println(d);
System.out.println(js);
System.out.println(sp);
return kdjVOList;
}
public static void main(String[] args) throws IOException, InterruptedException {
DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyy-MM-dd");
LocalDate begin = LocalDate.parse("2022-12-05", fmt);
LocalDate end = LocalDate.parse("2023-12-20", fmt);
List<KdjVO> kdjVOSource = StockIndexUtils.getKdjVO("399997", 1000);
List<KdjVO> kdjVOList=new ArrayList<>();
kdjVOSource.stream().forEach(item->{
if(LocalDate.parse(item.getDate(), fmt).isAfter(LocalDate.parse("2022-12-04", fmt))){
kdjVOList.add(item);
}
});
System.out.println(kdjVOList.get(0));
List<BigDecimal> input = new ArrayList<>();
String path="D:\\biaoge\\历史基金数据.xlsx";
ReadListener readListener =new ReadListener(){
@Override
public void invoke(Object o, AnalysisContext analysisContext) {
Excel excel= (Excel) o;
LocalDate now = LocalDate.now();
LocalDate date = LocalDate.parse(excel.getNetValueDate(), fmt);
if(date.isBefore(end)&&date.isAfter(begin)){
input.add(new BigDecimal(excel.getDailyReturnRate().replaceAll("%","")));
}
}
@Override
public void doAfterAllAnalysed(AnalysisContext analysisContext) {
}
};
EasyExcel.read(path, Excel.class, readListener).sheet("招商中证白酒指数(LO-012414").doRead();
// 当前涨幅
BigDecimal gainsDateYieldR=null;
BigDecimal gainsDateYield=new BigDecimal("1.00");
// 一份金额
BigDecimal one = new BigDecimal("300");
BigDecimal sum=BigDecimal.ZERO;
int a=0;
// input.add(new BigDecimal("-2.00"));
for (int i=0; i<input.size(); i++){
BigDecimal add = new BigDecimal("1").add(input.get(i).divide(new BigDecimal("100")));
gainsDateYield=gainsDateYield.multiply(add);
sum=sum.multiply(add);
gainsDateYieldR = (gainsDateYield.subtract(BigDecimal.ONE)).multiply(new BigDecimal("100")).setScale(4, RoundingMode.HALF_UP);
BigDecimal multiple = gainsDateYieldR.setScale(0, RoundingMode.HALF_UP);
int m =0;
if(kdjVOList.get(i).getJ()<0&&multiple.compareTo(BigDecimal.ZERO)<0){
System.out.println("买入时kdj:"+kdjVOList.get(i)+input.get(i));
sum=sum.add(one.multiply(multiple.multiply(new BigDecimal("-1"))));
a += multiple.multiply(new BigDecimal("-1")).intValue();
}
}
BigDecimal addStock = one.multiply(new BigDecimal(a));
BigDecimal principal = new BigDecimal("0").add(addStock);
System.out.println("加仓金额:"+addStock);
System.out.println("本金:"+principal);
BigDecimal currentYield = sum.divide(addStock, 4, RoundingMode.HALF_UP).subtract(BigDecimal.ONE).multiply(new BigDecimal("100"));
System.out.println(sum);
System.out.println("收益率:"+currentYield+"%");
System.out.println("至今涨幅:"+(gainsDateYield.subtract(BigDecimal.ONE)).multiply(new BigDecimal("100")).setScale(4, RoundingMode.HALF_UP)+"%");
}
}
5、计算kdj代码案例
static Map<String,KdjVO> getKdjMap(String secid) {
Map<String,KdjVO> map=new HashMap<>();
try{
if(StringUtils.isEmpty(secid)){
//0:深圳,1:上海,2:其他
secid="0.399997";
}
String url="https://push2his.eastmoney.com/api/qt/stock/kline/get?secid="+secid+"&fields1=f1,f2,f3,f4,f5&fields2=f51,f52,f53,f54,f55,f56,f57,f58,f59,f60,f61&lmt=1000&klt=101&fqt=1&end=30000101&ut=fa5fd1943c7b386f172d6893dbfba10b&cb=cb_1701680426583_1676563&cb_1701680426583_1676563=cb_1701680426583_1676563";
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(url))
.method("POST", HttpRequest.BodyPublishers.noBody())
.build();
HttpResponse<String> response = HttpClient.newHttpClient().send(request, HttpResponse.BodyHandlers.ofString());
String body = response.body();
int i = body.indexOf("{");
String json = body.substring(i, body.length() - 2);
JSONObject jsonObject = JSONObject.parseObject(json);
String data = jsonObject.getString("data");
JSONObject dataJson = JSONObject.parseObject(data);
JSONArray klines = dataJson.getJSONArray("klines");
List<String> list = klines.toList(String.class);
List<StockBasicInfoVO> stockBasicInfoVOList=new ArrayList<>();
list.stream().forEach(item->{
String[] split = item.split(",");
StockBasicInfoVO build = StockBasicInfoVO.builder()
.sbiTradeDate(split[0])
.sbiHigh(new BigDecimal(split[3]))
.sbiClose(new BigDecimal(split[2]))
.sbiLow(new BigDecimal(split[4]))
.build();
stockBasicInfoVOList.add(build);
});
stockBasicInfoVOList.sort(Comparator.comparing(StockBasicInfoVO::getSbiTradeDate));
List<double[]> kdj = StockIndexUtils.kdj(stockBasicInfoVOList);
List<KdjVO> kdjVOList= new ArrayList<>();
kdj.stream().forEach(item->{
for (int j=0;j<item.length;j++){
KdjVO kdjVO = KdjVO.builder()
.date(stockBasicInfoVOList.get(j + 16).getSbiTradeDate())
.k(kdj.get(0)[j])
.d(kdj.get(1)[j])
.j(kdj.get(2)[j])
.build();
kdjVOList.add(kdjVO);
map.put(kdjVO.getDate(),kdjVO);
}
});
}catch (Exception e){
}
return map;
}