2

My question is two fold but the logic may be similar.

I have a simple candlestick chart with two different indicators. One is at the bottom and the other is overlayed on top of the candlestick data. (See screenshot)

First: In the lower chart I do not want to draw values before they are calculated. In this case 14 periods for a Simple Moving Average. (See screenshot with yellow box. Do not want to draw these values)

Second: On the overlay I want the "Open", lowest value, highest value, and "Close" of the last bar. I want to draw a line between these four points and ignore all data in-between. (It should look like the yellow line in the screenshot).

How can i reformat the set chart function to ignore values before 14 periods on the lower line chart?

How can I ignore other points and interpolate between the four values on the line chart for the combined view?

Currently I have to set the values I want "ignored" to zero (1950 in screenshot to make it readable and not squashed) to get around out of bounds errors.

Tried to simplify the code as much as possible to make it more readable here:

//
//  ViewController.swift
//

import Foundation
import Cocoa
import Charts

var yValuesIndicatorLower: [Double] = []
var yValuesIndicatorOverlay: [Double] = []

class ViewController: NSViewController {

    //Chart view outlets

    @IBOutlet weak var combinedChartView: CombinedChartView!

    @IBOutlet weak var lineChartView: LineChartView!

    // Zoom Buttons

    @IBAction func zoomAll(sender: AnyObject) {
        combinedChartView.fitScreen()
        lineChartView.fitScreen()
    }

    @IBAction func zoomIn(sender: AnyObject) {
        combinedChartView.zoomIn()
        lineChartView.zoomIn()
    }

    @IBAction func zoomOut(sender: AnyObject) {
        combinedChartView.zoomOut()
        lineChartView.zoomOut()
    }


    override func viewDidLoad() {
        super.viewDidLoad()

        // Do any additional setup after loading the view.

        view.wantsLayer = true
        view.layer?.backgroundColor = NSColor(red: 0.2, green: 0.2, blue: 0.2, alpha: 0.95).CGColor

        combinedChartView.noDataText = "Loading data..."
        lineChartView.noDataText = "Loading data..."

        // Do any additional setup after loading the view.
        let xValues = ["05-16", "05-17", "05-18", "05-19", "05-20", "05-23", "05-24", "05-25", "05-26", "05-27", "05-31", "06-01", "06-02", "06-03", "06-06", "06-07", "06-08", "06-09", "06-10", "06-13", "06-14", "06-15", "06-16", "06-17", "06-20", "06-21", "06-22", "06-23", "06-24", "06-27", "06-28", "06-29", "06-30", "07-01", "07-05", "07-06", "07-07", "07-08", "07-11", "07-12", "07-13", "07-14", "07-15", "07-18", "07-19", "07-20", "07-21", "07-22", "07-25", "07-26", "07-27", "07-28", "07-29", "08-01", "08-02", "08-03"]

        ////Prices are ["Open"=[0], "High"=[1], "Low"=[2], "Last"[3]]
        let yValues: [[Double]] = [[2032.75, 2060.0, 2027.0, 2053.5], [2052.5, 2061.0, 2028.75, 2036.5], [2035.75, 2049.5, 2022.75, 2034.75], [2035.75, 2036.25, 2014.0, 2030.5], [2030.5, 2047.5, 2029.5, 2042.0], [2043.75, 2047.5, 2035.25, 2036.75], [2037.5, 2069.0, 2032.75, 2067.0], [2066.5, 2084.25, 2065.75, 2080.75], [2080.5, 2083.75, 2074.5, 2083.0], [2082.0, 2090.25, 2080.0, 2090.25], [2091.25, 2095.25, 2078.0, 2086.25], [2085.0, 2090.75, 2075.0, 2088.75], [2088.75, 2096.0, 2078.25, 2095.25], [2094.5, 2097.25, 2074.0, 2088.25], [2088.5, 2103.25, 2085.25, 2099.75], [2099.25, 2108.75, 2098.0, 2101.75], [2101.25, 2110.75, 2098.0, 2108.5], [2108.5, 2109.5, 2097.25, 2104.75], [2104.5, 2104.75, 2079.5, 2087.0], [2081.0, 2089.25, 2066.5, 2067.25], [2067.25, 2072.75, 2054.75, 2064.75], [2064.5, 2079.5, 2059.75, 2062.5], [2062.5, 2071.5, 2040.75, 2069.5], [2068.75, 2074.75, 2053.25, 2059.75], [2071.0, 2092.5, 2070.25, 2082.75], [2079.0, 2086.0, 2074.0, 2079.25], [2079.25, 2091.25, 2075.25, 2077.0], [2083.0, 2113.25, 2083.0, 2113.25], [2115.75, 2119.5, 1999.0, 2018.25], [2013.75, 2022.5, 1981.5, 1982.0], [1982.5, 2029.75, 1982.25, 2028.25], [2028.25, 2074.75, 2022.75, 2074.25], [2072.25, 2091.25, 2056.5, 2086.5], [2086.5, 2100.75, 2081.5, 2096.25], [2099.75, 2104.75, 2072.5, 2084.5], [2084.0, 2094.5, 2065.75, 2093.0], [2092.0, 2102.0, 2081.75, 2092.25], [2092.0, 2125.5, 2087.5, 2120.75], [2120.25, 2136.75, 2120.0, 2130.5], [2130.75, 2149.25, 2128.5, 2146.0], [2145.75, 2152.25, 2139.5, 2145.75], [2145.25, 2168.0, 2142.75, 2157.75], [2156.5, 2164.75, 2143.25, 2146.0], [2154.5, 2163.25, 2152.75, 2160.25], [2160.0, 2160.75, 2151.25, 2159.0], [2159.0, 2169.75, 2155.25, 2168.75], [2168.25, 2170.25, 2153.5, 2159.25], [2159.0, 2169.25, 2156.0, 2167.25], [2167.75, 2172.5, 2155.75, 2163.25], [2162.75, 2168.0, 2153.75, 2166.0], [2166.0, 2169.25, 2152.0, 2163.25], [2162.75, 2168.5, 2153.5, 2166.0], [2165.5, 2171.75, 2157.5, 2171.0], [2174.0, 2177.75, 2159.75, 2165.5], [2166.0, 2171.0, 2141.5, 2153.0], [2152.25, 2158.25, 2145.25, 2156.75]]

        //Pass values to indicators and get returned indicator values
        yValuesIndicatorOverlay = indicatorOverlay(yValues)
        yValuesIndicatorLower = indicatorLower(yValues)

        //Pass values to chart setting function
        setChart(xValues, valuesCandleChart: yValues, indicatorLowerValues: yValuesIndicatorLower, indicatorOverlayValues: yValuesIndicatorOverlay)
    }


    func indicatorOverlay(yValues: [[Double]]) -> [Double]{

        var highest = Double()
        var ZigZag: [Double] = []

        for i in 0..<yValues.count {
            highest = max(highest, yValues[i][1])
        }

        var lowest = highest
        for i in 0..<yValues.count {
            lowest = min(lowest, yValues[i][2])
        }

        for i in 0..<yValues.count {
            if i == 0 {
                ZigZag.append(yValues[i][0])
            }
            else if yValues[i][1] == highest {
                ZigZag.append(highest)
            }
            else if yValues[i][2] == lowest {
                ZigZag.append(lowest)
            }
            else if i == yValues.count-1 {
                ZigZag.append(yValues[i][3])
            }
            else {
                ZigZag.append(0)//These values should be ignored and then interpolated between other values.
            }
        }
        print(highest)
        print(lowest)

        return ZigZag

    }

    func indicatorLower(yValues: [[Double]]) -> [Double]{

        let peroid = 14.0

        var sum = 0.0
        var SMA: [Double] = []

        //Simple Moving Average
        for i in 0..<Int(peroid) {
            sum  += yValues[i][3]
            if i < Int(peroid)-1 {
                SMA.append(0)//These values are to be ignored
            }
        }
        SMA.append(sum/peroid)
        for i in Int(peroid)..<yValues.count {
            sum = (sum - yValues[i - Int(peroid)][3]) + yValues[i][3]
            SMA.append(sum/peroid)
        }

        return SMA

    }

    //Function to set chart values

    func setChart(xValues: [String], valuesCandleChart: [[Double]], indicatorLowerValues: [Double], indicatorOverlayValues: [Double]) {


        var yValsCandleChart : [CandleChartDataEntry] = []

        for i in 0..<valuesCandleChart.count {
            let high = valuesCandleChart[i][1]
            let low = valuesCandleChart[i][2]
            let open = valuesCandleChart[i][0]
            let close = valuesCandleChart[i][3]

            yValsCandleChart.append(CandleChartDataEntry(xIndex: i, shadowH: high, shadowL: low, open: open, close: close))
        }

        //How to ignore values that are "0" or below a set index?
        var indicatorLower_yValues: [ChartDataEntry] = []

        for i in 0..<xValues.count {
            let indicatorLower_yValue = ChartDataEntry(value: indicatorLowerValues[i], xIndex: i)
            indicatorLower_yValues.append(indicatorLower_yValue)
        }

        //How to use only vlaues that are calculated and not "0" to draw a ZigZag across the candlestick chart?
        var indicatorOverlay_yValues: [ChartDataEntry] = []

        for i in 0..<xValues.count {
            let indicatorOverlay_yValue = ChartDataEntry(value: indicatorOverlayValues[i], xIndex: i)
            indicatorOverlay_yValues.append(indicatorOverlay_yValue)
        }

        let candleChartDataSet = CandleChartDataSet(yVals: yValsCandleChart, label: "Price")
        let lineOverlayChartDataSet = LineChartDataSet(yVals: indicatorOverlay_yValues, label: "indicatorOverlay")

        let data: CombinedChartData = CombinedChartData(xVals: xValues)

        data.candleData = CandleChartData(xVals: xValues, dataSets: [candleChartDataSet])
        data.lineData = LineChartData(xVals: xValues, dataSets: [lineOverlayChartDataSet])
        combinedChartView.drawOrder = [3, 2]

        combinedChartView.data = data

        candleChartDataSet.decreasingColor = NSColor.redColor()
        candleChartDataSet.increasingColor = NSColor.greenColor()
        candleChartDataSet.neutralColor = NSColor.blueColor()
        candleChartDataSet.shadowColorSameAsCandle = true
        candleChartDataSet.shadowWidth = 1
        candleChartDataSet.decreasingFilled = true
        candleChartDataSet.increasingFilled = false
        candleChartDataSet.drawValuesEnabled = false
        combinedChartView.doubleTapToZoomEnabled = false
        lineOverlayChartDataSet.drawCirclesEnabled = false
        lineOverlayChartDataSet.drawValuesEnabled = false

        let lineChartDataSet = LineChartDataSet(yVals: indicatorLower_yValues, label: "indicatorLower")
        let lineChartData = LineChartData(xVals: xValues, dataSet: lineChartDataSet)
        lineChartView.data = lineChartData


        lineChartView.doubleTapToZoomEnabled = false
        lineChartDataSet.drawCirclesEnabled = false
        lineChartDataSet.drawValuesEnabled = false


    }

    override var representedObject: AnyObject? {
        didSet {
            // Update the view, if already loaded.
        }
    }


}

Chart indicators markup

Julien Quere
  • 2,407
  • 16
  • 21
Moon47
  • 103
  • 2
  • 11

2 Answers2

1

Wow that was really easy... =]

Shows how much I like this library.

Per suggestion on the issue here: LineChartView avoid painting 0 values #830

All I had to do was change these:

    //How to ignore values that are "0" or below a set index?
    var indicatorLower_yValues: [ChartDataEntry] = []

    for i in 0..<xValues.count {
        let indicatorLower_yValue = ChartDataEntry(value: indicatorLowerValues[i], xIndex: i)
        indicatorLower_yValues.append(indicatorLower_yValue)
    }

    //How to use only vlaues that are calculated and not "0" to draw a ZigZag across the candlestick chart?
    var indicatorOverlay_yValues: [ChartDataEntry] = []

    for i in 0..<xValues.count {
        let indicatorOverlay_yValue = ChartDataEntry(value: indicatorOverlayValues[i], xIndex: i)
        indicatorOverlay_yValues.append(indicatorOverlay_yValue)
    }

To these:

        //Ignore values that are "0"
        var indicatorLower_yValues: [ChartDataEntry] = []

        for i in 0..<xValues.count {
            if indicatorLowerValues[i] != 0 {//ADD: If clause to skip "0"
                let indicatorLower_yValue = ChartDataEntry(value: indicatorLowerValues[i], xIndex: i)
                indicatorLower_yValues.append(indicatorLower_yValue)
            }
        }

        //Use only vlaues that are not "0" to draw a ZigZag
        var indicatorOverlay_yValues: [ChartDataEntry] = []

        for i in 0..<xValues.count {
            if indicatorOverlayValues[i] != 0 {//ADD: If clause to skip "0"
                let indicatorOverlay_yValue = ChartDataEntry(value: indicatorOverlayValues[i], xIndex: i)
                indicatorOverlay_yValues.append(indicatorOverlay_yValue)
            }
        }

enter image description here

Moon47
  • 103
  • 2
  • 11
1

To be 100% precise and replicate the bottom chart (i.e the solution) the below line must be added. If not the MA will start on the top left and the x-axis between the 2 charts will not be aligned anymore.

lineChartView.xAxis.axisMinimum = 0.0 
Dids
  • 125
  • 1
  • 9