java计算股票技术指标之kdj与macd

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;
    }

  • 13
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
以下是使用.NET语言计算股票技术指标KDJ的代码: ``` public static void CalculateKDJ(List<double> closePrices, List<double> highPrices, List<double> lowPrices, int n, int m1, int m2, out List<double> kValues, out List<double> dValues, out List<double> jValues) { kValues = new List<double>(); dValues = new List<double>(); jValues = new List<double>(); double[] rsvValues = new double[closePrices.Count]; for (int i = 0; i < closePrices.Count; i++) { double c = closePrices[i]; double h = highPrices[i]; double l = lowPrices[i]; double rsv = (c - l) / (h - l) * 100; if (double.IsNaN(rsv)) { rsv = 0; } rsvValues[i] = rsv; } double[] kSmaValues = new double[closePrices.Count]; double[] dSmaValues = new double[closePrices.Count]; for (int i = 0; i < closePrices.Count; i++) { double kSma = 0; double dSma = 0; if (i == 0) { kSma = 50; dSma = 50; } else { int startIndex = Math.Max(0, i - n + 1); int endIndex = i; double[] rsvValuesSubset = new double[endIndex - startIndex + 1]; Array.Copy(rsvValues, startIndex, rsvValuesSubset, 0, endIndex - startIndex + 1); double maxRsv = rsvValuesSubset.Max(); double minRsv = rsvValuesSubset.Min(); double kPrev = kSmaValues[i - 1]; double dPrev = dSmaValues[i - 1]; kSma = (kPrev * (m1 - 1) / m1) + (maxRsv * 1 / m1); dSma = (dPrev * (m2 - 1) / m2) + (kSma * 1 / m2); } kSmaValues[i] = kSma; dSmaValues[i] = dSma; } for (int i = 0; i < closePrices.Count; i++) { double k = kSmaValues[i]; double d = dSmaValues[i]; double j = 3 * k - 2 * d; kValues.Add(k); dValues.Add(d); jValues.Add(j); } } ``` 其中,closePrices、highPrices和lowPrices分别代表收盘价、最高价和最低价的列表,n、m1和m2分别代表KDJ指标中的参数,kValues、dValues和jValues分别代表计算出的K、D、J值的列表。
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值