Pre loader

iOS & macOS Trade Marker Example

iOS & macOS charts - Examples

Please note! These examples are new to SciChart iOS v4 release! SciChart’s OpenGL ES and Metal iOS and Metal macOS Chart library ships with hundred of Objective-C and Swift iOS & macOS Chart Examples which you can browse, play with and view the source-code. All of this is possible with the new and improved SciChart iOS Examples Suite and demo application for Mac, which ships as part of the SciChart SDK.

Download Examples

Shows how to visualize Buy and Sell trades directly on a stock chart using image annotations. Price labels make it easy to identify trade execution points at a glance. Trade markers are positioned using data coordinates and include configurable labels, styles, and alignment to represent execution price on financial charts.

The Swift and Objective-C source code for the iOS & macOS Trade Marker example is included below (Scroll down!).

Did you know that we have the source code for all our example available for free on Github?

Clone the SciChart.iOS.Examples from Github.

Also the SciChart iOS and Scichart macOS Trials contain the full source for the examples (link below).

DOWNLOAD THE IOS CHART EXAMPLES

BuySellMarkerAnnotationChartView.swift
View source code
//******************************************************************************
// SCICHART® Copyright SciChart Ltd. 2011-2023. All rights reserved.
//
// Web: http://www.scichart.com
// Support: support@scichart.com
// Sales:   sales@scichart.com
//
// BuySellMarkerAnnotationChartView.swift is part of the SCICHART® Examples. Permission is hereby granted
// to modify, create derivative works, distribute and publish any part of this source
// code whether for commercial, private or personal use.
//
// The SCICHART® examples are distributed in the hope that they will be useful, but
// without any warranty. It is provided "AS IS" without warranty of any kind, either
// expressed or implied.
//******************************************************************************

class StackedYAxisLayoutStrategy: SCIVerticalAxisLayoutStrategy {
    
    override func measureAxes(withAvailableWidth width: CGFloat, height: CGFloat, andChartLayoutState chartLayoutState: SCIChartLayoutState) {
        for i in 0 ..< axes.count {
            let axis = axes[i] as! ISCIAxis
            axis.updateMeasurements()
            
            let requiredAxisSize = SCIVerticalAxisLayoutStrategy.getRequiredAxisSize(from: axis.axisLayoutState)
            chartLayoutState.rightOuterAreaSize = max(requiredAxisSize, chartLayoutState.rightOuterAreaSize)
        }
    }
    
    override func layout(withLeft left: CGFloat, top: CGFloat, right: CGFloat, bottom: CGFloat) {
        let count = axes.count
        let height = bottom - top
        let ratios: [CGFloat] = [0.8, 0.2]
        
        var topPlacement = top
        for i in 0 ..< count {
            let axis = axes[i] as! ISCIAxis
            let axisLayoutState = axis.axisLayoutState
            
            let axisHeight = height * ratios[i]
            let bottomPlacement = topPlacement + axisHeight
            
            let requiredAxisSize = SCIVerticalAxisLayoutStrategy.getRequiredAxisSize(from: axisLayoutState)
            axis.layoutArea(withLeft: left, top: topPlacement, right: left + requiredAxisSize, bottom: bottomPlacement)
            
            topPlacement = bottomPlacement
        }
    }
}

class BuySellMarkerAnnotationChartView: SCDSingleChartViewController<SCIChartSurface> {
    
    override var associatedType: AnyClass { return SCIChartSurface.self }
    
    override var showDefaultModifiersInToolbar: Bool { return false }
    
    override func initExample() {
        let historicalData = SCIOhlcDataSeries(xType: .date, yType: .double)
        let priceSeries = SCDDataManager.getPriceDataIndex()
        let dateData = priceSeries.dateData
        let indexDataProvider = SCIDataSeriesIndexDataProvider(dataSeriesValues: historicalData)
        
        let xAxis = SCIIndexDateAxis()
        xAxis.setIndexDataProvider(indexDataProvider)
        xAxis.visibleRange = SCIDateRange(min: dateData.getValueAt(0), max: dateData.getValueAt(30))
        xAxis.growBy = SCIDoubleRange(min: 0.05, max: 0.05)
        
        let xAxis1 = SCINumericAxis()
        xAxis1.axisId = "balance"
        xAxis1.isVisible = false
        
        let yAxis = SCINumericAxis()
        yAxis.autoRange = .never
        yAxis.growBy = SCIDoubleRange(min: 0.1, max: 0.1)
        yAxis.axisAlignment = .right
        
        let balanceYAxis = SCINumericAxis()
        balanceYAxis.axisId = "Balance"
        balanceYAxis.growBy = SCIDoubleRange(min: 0.1, max: 0.1)
        balanceYAxis.axisAlignment = .right
        balanceYAxis.autoRange = .never
        
        historicalData.append(x: dateData, open: priceSeries.openData, high: priceSeries.highData, low: priceSeries.lowData, close: priceSeries.closeData)
        
        let historicalPrices = SCIFastCandlestickRenderableSeries()
        historicalPrices.strokeUpStyle = SCISolidPenStyle(color: 0xFF84E2FF, thickness: 1)
        historicalPrices.strokeDownStyle = SCISolidPenStyle(color: 0xFF84E2FF, thickness: 1)
        historicalPrices.fillUpBrushStyle = SCISolidBrushStyle(color: 0xFF84E2FF)
        historicalPrices.fillDownBrushStyle = SCISolidBrushStyle(color: 0x00000000)
        historicalPrices.dataSeries = historicalData
        
        // MARK: - Info Annotation
        let infoText = SCITextAnnotation()
        infoText.set(x1: dateData.getValueAt(1))
        infoText.set(y1: 160)
        infoText.text = "Tap markers to see simulated trading info"
        surface.annotations.add(infoText)
        
        var position = 0.0
        var equity = 0.0
        var balance = 100.0
        var avgPrice = 0.0
        
        let positionSeries = SCIXyDataSeries(xType: .int, yType: .double)
        let balanceSeries = SCIXyDataSeries(xType: .date, yType: .double)
        
        for i in 0..<priceSeries.count { //priceSeries.count
            let low = priceSeries.lowData.itemsArray[i]
            let high = priceSeries.highData.itemsArray[i]
            let price = low + Double.random(in: 0...1) * (high - low)
            
            if Double.random(in: 0...1) < 0.3 {
                let t = equity / (equity + balance)
                if Double.random(in: 0...1) > t {
                    // Buy
                    let quantity = Double(Int.random(in: 1...50))
                    let size = quantity * price
                    avgPrice = (avgPrice * position + size) / (position + quantity)
                    position += quantity
                    balance -= size
                    
                    let marker = TradeMarkerAnnotation(index: dateData.getValueAt(i), isBuy: true, yPoint: low, price: price)
                    marker.delegate = self
                    surface.annotations.add(marker)
                } else {
                    // Sell
                    let quantity = Double(Int.random(in: 1...50))
                    let size = quantity * price
                    position -= quantity
                    balance += size
                    //let pnl = (price - avgPrice) * quantity
                    
                    let marker = TradeMarkerAnnotation(index: dateData.getValueAt(i), isBuy: false, yPoint: high, price: price)
                    marker.delegate = self
                    surface.annotations.add(marker)
                }
            }
            
            equity = position * priceSeries.closeData.itemsArray[i]
            positionSeries.append(x: i, y: position)
            balanceSeries.append(x: dateData.getValueAt(i), y: balance + equity)
            
            //Every 20th bar, add a news event marker
            if i % 20 == 0 {
                surface.annotations.add(newsBulletAnnotation(index: dateData.getValueAt(i)))
            }
        }
        
        let balanceRenderable = SCIFastMountainRenderableSeries()
        balanceRenderable.dataSeries = balanceSeries
        balanceRenderable.yAxisId = "Balance"
        balanceRenderable.areaStyle = SCISolidBrushStyle(color: 0xFFBC88D2)
        balanceRenderable.strokeStyle = SCISolidPenStyle(color: 0xFFAA4BBD, thickness: 2)
        
        let layoutManager = SCIDefaultLayoutManager()
        layoutManager.rightOuterAxisLayoutStrategy = StackedYAxisLayoutStrategy()
        self.surface.layoutManager = layoutManager
        
        let priceRange = SCIDoubleRange(min: priceSeries.lowData.itemsArray.min() ?? 0,
                                        max: priceSeries.highData.itemsArray.max() ?? 100)
        yAxis.visibleRange = priceRange
        
        let balanceRange = SCIDoubleRange(min: balanceSeries.yMin.toDouble(),
                                          max: balanceSeries.yMax.toDouble())
        balanceYAxis.visibleRange = balanceRange
        
        surface.invalidateElement()
        
        SCIUpdateSuspender.usingWith(surface) {
            self.surface.xAxes.add(xAxis)
            self.surface.yAxes.add(yAxis)
            self.surface.yAxes.add(balanceYAxis)
            self.surface.renderableSeries.add(items: historicalPrices, balanceRenderable)
            self.surface.chartModifiers.add(SCDExampleBaseViewController.createDefaultModifiers())
        }
    }
    
    fileprivate func newLineSeriesi(dataSeries: ISCIXyDataSeries, color: UInt32, axisId: String) -> SCIFastLineRenderableSeries {
        let rSeries = SCIFastLineRenderableSeries()
        rSeries.dataSeries = dataSeries
        rSeries.strokeStyle = SCISolidPenStyle(color: color, thickness: 1.0)
        rSeries.yAxisId = axisId
        
        SCIAnimations.sweep(rSeries, duration: 3.0, easingFunction: SCICubicEase())
        
        return rSeries
    }
    
    fileprivate func newAxis(axisId: String) -> ISCIAxis {
        let axis = SCINumericAxis()
        axis.axisAlignment = .left
        axis.axisId = axisId
        axis.axisTitle = axisId
        axis.visibleRange = SCIDoubleRange(min: -2, max: 2)
        axis.autoRange = .never
        axis.drawMajorBands = false
        axis.drawMajorGridLines = false
        axis.drawMinorGridLines = false
        
        return axis
    }
    
    
    //MARK: - News Annotation
    func newsBulletAnnotation(index: Date) -> SCITextAnnotation {
        let annotation = SCITextAnnotation()
        annotation.set(x1: index)
        annotation.set(y1: 120)
        annotation.verticalAnchorPoint = .bottom
        annotation.horizontalAnchorPoint = .center
        annotation.text = "Div"
        annotation.padding = SCIEdgeInsets(top: 5, left: 5, bottom: 5, right: 5)
        annotation.backgroundColor = SCIColor.white
        annotation.fontStyle = SCIFontStyle(fontSize: 12, andTextColorCode: 0xFF000000)
        return annotation
    }
}

extension BuySellMarkerAnnotationChartView : TradeAnnotationDelegate {
    func didTradeAnnotationTapped(_ annotation: TradeMarkerAnnotation, atPoint: CGPoint) {
        
        if annotation.hasLabel {
            hidePriceLabel(for: annotation)
        }
        else if let info = annotation.userInfo?.first as? [String : Any] {
            let isBuy = info["isBuy"] as? Bool ?? false
            let price = info["price"] as? Double ?? 0.0
            
            showPriceLabel(for: annotation, price: price, isBuy: isBuy)
            surface.invalidateElement()
        }
    }
    
    func showPriceLabel(for annotation: TradeMarkerAnnotation, price: Double, isBuy: Bool) {
        let label = SCITextAnnotation()
        let x1: Date = annotation.getX1()
        let y1: Double = annotation.getY1()
        label.set(x1: x1)
        label.set(y1: y1)
        label.text = String(format: "%.2f", price)
        label.backgroundColor = SCIColor.red.withAlphaComponent(0.6)
        label.fontStyle = SCIFontStyle(fontSize: 12, andTextColorCode: 0xFFFFFFFF)
        label.backgroundColor = (isBuy ? SCIColor.systemGreen : SCIColor.systemRed).withAlphaComponent(0.8)
        label.horizontalAnchorPoint = .center
        label.verticalAnchorPoint = isBuy ? .bottom : .top 
        self.surface.annotations.add(label)
        annotation.hasLabel = true
        annotation.userInfo?.append(["text": label])
    }

    func hidePriceLabel(for annotation: TradeMarkerAnnotation) {
        if ((annotation.userInfo?.count) ?? 0) > 1, let info = annotation.userInfo?[1] as? [String : Any], let label = info["text"] as? SCITextAnnotation {
            
            DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
                self.surface.annotations.remove(label)
                annotation.hasLabel = false
                annotation.userInfo?.remove(at: 1)
                self.surface.invalidateElement()
             }
        }
    }
}
Back to iOS & macOS charts Examples