基于mpandroidchart的几种指标实现

k线介绍

image

股市及期货市场中的K线图的画法包含四个数据,即开盘价、最高价、最低价、收盘价,所有的k线都是围绕这四个数据展开

k线指标介绍

k线指标其实就是技术指标, 认为的根据某些算法来预测价格未来的走势,或者帮助分析价格背后的市场因素。

k线的几种指标实现

[图片上传失败...(image-d8e3a-1617104321100)]

SAR的实现

SAR指标又叫抛物线指标或停损转向操作点指标,其全称叫“Stop and Reverse,缩写SAR”,是由美国技术分析大师威尔斯-威尔德(Wells Wilder)所创造的,是一种简单易学、比较准确的中短期技术分析工具。

更多信息查看百度百科

    public List<Entry> getSar(List<CandleEntry> entries) {
        if (sarList == null) {
            sarList = new ArrayList<>();
        }
        if (!sarList.isEmpty()) {
            sarList.clear();
        }
        int len = entries.size();
        for (int i = 0; i < len; i++) {
            Entry entry = new Entry(calculateSar(i, entries), i);
            entry.setTrendUp(currentTrend);
            sarList.add(entry);
        }
        return sarList;
    }

    /**
     *
     * @param entries source data
     * @param index 下标
     * @param windowSize 窗口size
     * @return
     */
    private float getHighestValue(List<CandleEntry> entries, int index, int windowSize){
        if (entries.get(index).getHigh() == Float.NaN && windowSize != 1) {
            return  getHighestValue(entries, index - 1, windowSize - 1);
        }
        int end = Math.max(0, index - windowSize + 1);
        float highest = entries.get(index).getHigh();
        for (int i = index - 1; i >= end; i--) {
            if (highest < entries.get(i).getHigh()) {
                highest = entries.get(i).getHigh();
            }
        }
        return highest;
    }

    /**
     *
     * @param entries
     * @param index
     * @param windowSize
     * @return
     */
    private float getLowestValue(List<CandleEntry> entries, int index, int windowSize){
        if (entries.get(index).getHigh() == Float.NaN && windowSize != 1) {
            return getLowestValue(entries, index - 1, windowSize - 1);
        }
        int end = Math.max(0, index - windowSize + 1);
        float lowest =  entries.get(index).getLow();
        for (int i = index - 1; i >= end; i--) {
            if (lowest > entries.get(i).getLow()) {
                lowest = entries.get(i).getLow();
            }
        }
        return lowest;
    }

    protected float calculateSar(int index, List<CandleEntry> entries) {
        float sar = 0f;
        Entry entry = null;
        if (index == 0) {
            return entries.get(0).getClose(); // no trend detection possible for the first value
        } else if (index == 1) {// start trend detection
            currentTrend = entries.get(0).getClose() < entries.get(1).getClose();
            if (!currentTrend) { // down trend
                sar = entries.get(index).getHigh(); // put sar on max price of candlestick
            } else { // up trend
                sar = entries.get(index).getLow(); // put sar on min price of candlestick
            }
            currentExtremePoint = sar;
            minMaxExtremePoint = currentExtremePoint;
            return sar;
        }

        float priorSar = sarList.get(index - 1).getVal();
        if (currentTrend) { // if up trend
            sar = priorSar + (accelerationFactor * (currentExtremePoint - priorSar));
            currentTrend = entries.get(index).getLow() > sar;
            if (!currentTrend) { // check if sar touches the min price
                sar = minMaxExtremePoint; // sar starts at the highest extreme point of previous up trend
                currentTrend = false; // switch to down trend and reset values
                startTrendIndex = index;
                accelerationFactor = accelerationStart;
                currentExtremePoint = entries.get(index).getLow(); // put point on max
                minMaxExtremePoint = currentExtremePoint;
            } else { // up trend is going on
                // currentExtremePoint = new HighestValueIndicator(highPriceIndicator, index - startTrendIndex)
                //                        .getValue(index);
                currentExtremePoint = getHighestValue(entries, index, index - startTrendIndex);
                if (currentExtremePoint > minMaxExtremePoint) {
                    incrementAcceleration();
                    minMaxExtremePoint = currentExtremePoint;
                }

            }
        } else { // downtrend
            sar = priorSar - (accelerationFactor * (priorSar - currentExtremePoint));
            currentTrend = entries.get(index).getHigh() > sar;
            if (currentTrend) { // check if switch to up trend
                sar = minMaxExtremePoint; // sar starts at the lowest extreme point of previous down trend
                accelerationFactor = accelerationStart;
                startTrendIndex = index;
                currentExtremePoint = entries.get(index).getHigh();;
                minMaxExtremePoint = currentExtremePoint;
            } else { // down trend io going on
                //currentExtremePoint = new LowestValueIndicator(lowPriceIndicator, index - startTrendIndex)
                //                        .getValue(index);
                currentExtremePoint = getLowestValue(entries, index, index - startTrendIndex);
                if (currentExtremePoint < minMaxExtremePoint) {
                    incrementAcceleration();
                    minMaxExtremePoint = currentExtremePoint;
                }
            }
        }
        return sar;
    }

    /**
     * Increments the acceleration factor.
     */
    private void incrementAcceleration() {
        if (accelerationFactor > maxAcceleration) {
            accelerationFactor = maxAcceleration;
        } else {
            accelerationFactor = accelerationFactor + accelerationIncrement;
        }
    }

OBV的实现

OBV 的英文全称是:On Balance Volume,是由美国的投资分析家Joe Granville所创。该指标通过统计成交量变动的趋势来推测股价趋势。OBV以“N”字型为波动单位,并且由许许多多“N”型波构成了OBV的曲线图,对一浪高于一浪的“N”型波,称其为“上升潮”(UP TIDE),至于上升潮中的下跌回落则称为“跌潮”(DOWN FIELD)。

更多信息查看百度百科

  • 代码实现
    private float calculateObv(int index, List<KlinePointBean> entries) {
        if (index == 0) {
            return 0f;
        }
        final float prevClose = entries.get(index - 1).getClose();
        final float currentClose = entries.get(index).getClose();

        final float obvPrev = obvList.get(index - 1).getVal();
        if (prevClose > currentClose) {
            return obvPrev - (entries.get(index).getAmount());
        } else if (prevClose < currentClose) {
            return obvPrev + (entries.get(index).getAmount());
        } else {
            return 0;
        }
    }

RSI的实现

RSI最早被用于期货交易中,后来人们发现用该指标来指导股票市场投资效果也十分不错,并对该指标的特点不断进行归纳和总结。现在,RSI已经成为被投资者应用最广泛的技术指标之一。投资的一般原理认为,投资者的买卖行为是各种因素综合结果的反映,行情的变化最终取决于供求关系,而RSI指标正是根据供求平衡的原理,通过测量某一个期间内股价上涨总幅度占股价变化总幅度平均值的百分比,来评估多空力量的强弱程度,进而提示具体操作的。RSI的应用法则表面上比较复杂,包括了交叉、数值、形态和背离等多方面的判断原则。

更多信息查看百度百科

  • 代码实现
    public Triple<List<Entry>, List<Entry>, List<Entry>> getRsiIndicator(List<CandleEntry> candleEntries) {
        if (candleEntries == null || candleEntries.size() == 0) {
            return new Triple<>(new ArrayList<>(), new ArrayList<>(), new ArrayList<>());
        }
        List<Entry> rsiList = new ArrayList<>();
        List<Entry> rsiList12 = new ArrayList<>();
        List<Entry> rsiList24 = new ArrayList<>();
        float lastSmValue6 = 0, lastSmValue12 = 0, lastSmValue24 = 0;
        float lastSaValue6 = 0, lastSaValue12 = 0, lastSaValue24 = 0;
        CandleEntry lastEntry = candleEntries.get(0);
        int len = candleEntries.size();
        for (int i = 1; i < len; i++) {
            CandleEntry entry = candleEntries.get(i);
            float m = Math.max(entry.getClose() - lastEntry.getClose(), 0);
            float a = Math.abs(entry.getClose() - lastEntry.getClose());

            if (i >= 5) {
                lastSmValue6 = (m + 5 * lastSmValue6) / 6;
                lastSaValue6 = (a + 5 * lastSaValue6) / 6;
                rsiList.add(new Entry(lastSmValue6 / lastSaValue6 * 100, entry.getXIndex()));
            }

            if (i >= 11) {
                lastSmValue12 = (m + 11 * lastSmValue12) / 12;
                lastSaValue12 = (a + 11 * lastSaValue12) / 12;
                rsiList12.add(new Entry(lastSmValue12 / lastSaValue12 * 100, entry.getXIndex()));
            }

            if (i >= 23) {
                lastSmValue24 = (m + 23 * lastSmValue24) / 24;
                lastSaValue24 = (a + 23 * lastSaValue24) / 24;
                rsiList24.add(new Entry(lastSmValue24 / lastSaValue24 * 100, entry.getXIndex()));
            }
            lastEntry = candleEntries.get(i);
        }

        return new Triple<>(rsiList, rsiList12, rsiList24);
    }

TRIX的实现

三重指数平滑平均线(TRIX)属于中长线指标。它过滤掉许多不必要的波动来反映股价的长期波动趋势。在使用均线系统的交叉时,有时会出现骗线的情况,有时还会出现频繁交叉的情况,通常还有一个时间上的确认。为了解决这些问题,因而发明了TRIX这个指标把均线的数值再一次地算出平均数,并在此基础上算出第三重的平均数。这样就可以比较有效地避免频繁出现交叉信号。 TRIX指标又叫三重指数平滑移动平均指标,其英文全名为“Triple Exponentially Smoothed Average”,是一种研究股价趋势的长期技术分析工具。

更多信息查看百度百科

    /**
     * EMA算法
     * EMA(N) = 2/(N+1)*C + (N-1)/(N+1)*EMA', EMA'为前一天的ema; 通常N取12和26
     *
     * @param entries
     * @param n
     * @return
     */
    public static List<Entry> getEMA(List<CandleEntry> entries, int n) {
        List<Entry> result = new ArrayList<>();
        float lastEma = entries.get(0).getClose();// 第一个EMA为第一个数据的价格

        float[] emaFactor = getEMAFactor(n);
        for (int i = n - 1; i < entries.size(); i++) {
            float ema = emaFactor[0] * entries.get(i).getClose() + emaFactor[1] * lastEma;
            result.add(new Entry(ema, entries.get(i).getXIndex()));
            lastEma = ema;
        }
        return result;
    }

    /**
     * EMA算法
     * EMA(N) = 2/(N+1)*C + (N-1)/(N+1)*EMA', EMA'为前一天的ema; 通常N取12和26
     *
     * @param entries
     * @param n
     * @return
     */
    public static List<Entry> getEMAFromEntry(List<Entry> entries, int n) {
        List<Entry> result = new ArrayList<>();
        float lastEma = entries.get(0).getVal();// 第一个EMA为第一个数据的价格

        float[] emaFactor = getEMAFactor(n);
        for (int i = n - 1; i < entries.size(); i++) {
            float ema = emaFactor[0] * entries.get(i).getVal() + emaFactor[1] * lastEma;
            result.add(new Entry(ema, entries.get(i).getXIndex()));
            lastEma = ema;
        }
        return result;
    }
    
    private List<Entry> trixList;
    private List<Entry> trixMaList;
    public Pair<List<Entry>, List<Entry>> getTrix(List<CandleEntry> entries) {
        int n = 12, m = 9;
        if (entries == null) {
            return new Pair<>(new ArrayList<>(), new ArrayList<>());
        }
        if (trixList == null) {
            trixList = new ArrayList<>();
            trixMaList = new ArrayList<>();
        }

        //clear;
        trixList.clear();
        trixMaList.clear();

        List<Entry> ema1 = getEMA(entries, n);
        List<Entry> ema2 = getEMAFromEntry(ema1, n);
        List<Entry> ema3 = getEMAFromEntry(ema2, n);
        int len = ema3.size();

        for (int i = 1; i < len; i++) {
            float trixValue = (ema3.get(i).getVal() - ema3.get(i - 1).getVal()) * 100 / ema3.get(i - 1).getVal();
            trixList.add(new Entry(trixValue, ema3.get(i).getXIndex()));
        }

        float ma = 0.0f;
        int index = m - 1;
        len = trixList.size();
        for (int i = 0; i < len; i++) {
            if (i >= index) {
                float sum = getSumFromEntry(i - index, i, trixList);
                ma = sum / m;
                trixMaList.add(new Entry(ma, trixList.get(i).getXIndex()));
            }
        }

        return new Pair<>(trixList, trixMaList);

    }

CCI的实现

顺势指标又叫CCI指标,CCI指标是美国股市技术分析 家唐纳德·蓝伯特(Donald Lambert)于20世纪80年代提出的,专门测量股价、外汇或者贵金属交易是否已超出常态分布范围。属于超买超卖类指标中较特殊的一种。波动于正无穷大和负无穷大之间。但是,又不需要以0为中轴线,这一点也和波动于正无穷大和负无穷大的指标不同。

更多信息查看

    private List<Entry> cciList;
    private List<Float> tpList;
    private List<Float> cciMaList;
    private List<Float> mdList;
    public List<Entry> getCciData(List<CandleEntry> entries){
        int n = 20;
        if (entries == null || entries.size() < n) {
            return new ArrayList<>();
        }
        if (cciList == null) {
            cciList = new ArrayList<>();
            tpList = new ArrayList<>();
            mdList = new ArrayList<>();
            cciMaList = new ArrayList<>();
        }

        //clear
        cciList.clear();
        tpList.clear();
        mdList.clear();
        cciMaList.clear();

        int len = entries.size();
        for (int i = 0; i < len; i++) {
            float tpValue = getTp(i, entries);
            tpList.add(tpValue);
            float maValue = getSum(Math.max(0, i - n + 1), i, tpList) / Math.min(n, i + 1);
            cciMaList.add(maValue);

            final int startIndex = Math.max(0, i - n + 1);
            final int nbValues = i - startIndex + 1;
            float absoluteDeviations = 0;
            for (int j = startIndex; j <= i; j++) {
                // For each period...
                absoluteDeviations = absoluteDeviations + (Math.abs(tpList.get(j) - maValue));
            }

            float mdValue = absoluteDeviations / nbValues;
            mdList.add(mdValue);

            if (mdValue == 0) {
                if (i >= n - 1) {
                    cciList.add(new Entry(0, i));
                }
            } else {
                if (i >= n - 1) {
                    float cci = (tpList.get(i) - cciMaList.get(i)) / mdList.get(i) / 0.015f;
                    cciList.add(new Entry(cci, i));
                }
            }

        }

        return cciList;
    }

    private float getSum(Integer start, Integer end, List<Float> datas) {
        float sum = 0;
        for (int i = start; i <= end; i++) {
            sum += datas.get(i);
        }
        return sum;
    }

    private float getTp(int index, List<CandleEntry> entries){
        float maxPrice = entries.get(index).getHigh();
        float minPrice = entries.get(index).getLow();
        float closePrice = entries.get(index).getClose();
        return (maxPrice + minPrice + closePrice) / 3;
    }

ROC的实现

变动率指标(ROC),是以当日的收盘价和N天前的收盘价比较,通过计算股价某一段时间内收盘价变动的比例,应用价格的移动比较来测量价位动量,达到事先探测股价买卖供需力量的强弱,进而分析股价的趋势及其是否有转势的意愿,属于反趋势指标之一。

更多信息查看

    private List<Entry> rocList;
    private List<Entry> rocMaList;
    public Pair<List<Entry>, List<Entry>> getRoc(List<CandleEntry> entries) {
        int n = 12, m = 6;
        if (entries == null) {
            return new Pair<>(new ArrayList<>(), new ArrayList<>());
        }
        if (rocList == null) {
            rocList = new ArrayList<>();
            rocMaList = new ArrayList<>();
        }

        //clear;
        rocList.clear();
        rocMaList.clear();
        int len = entries.size();
        for (int i = n; i < len; i++) {
            int nIndex = Math.max(i - n, 0);
            float nPeriodsAgoValue = entries.get(nIndex).getClose();
            float currentValue = entries.get(i).getClose();
            float rocValue = (currentValue - nPeriodsAgoValue) / nPeriodsAgoValue  * 100;
            rocList.add(new Entry(rocValue, i));
        }

        len = rocList.size();
        for (int i = 0; i < len; i++) {
            if (i >= m - 1) {
                float sum = getSumFromEntry(i - m + 1, i, rocList);
                float maValue = sum / m;
                rocMaList.add(new Entry(maValue, rocList.get(i).getXIndex()));
            }
        }

        return new Pair<>(rocList, rocMaList);
    }

后续

MACD的实现 /KDJ的实现/ BOLL的实现 / WR的实现

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,406评论 6 503
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,732评论 3 393
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,711评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,380评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,432评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,301评论 1 301
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,145评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,008评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,443评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,649评论 3 334
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,795评论 1 347
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,501评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,119评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,731评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,865评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,899评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,724评论 2 354

推荐阅读更多精彩内容