はじめに
こんにちはsuzukiです。引き続きChartsを使いボリンジャーバンドの描画に挑戦します。
前回記事で移動平均線を描画する際に少し詳しくChartsライブラリの説明をしています。よければご覧ください。
ボリンジャーバンドとは
ボリンジャーバンドはローソク足を分析する際の手法です。詳しい内容自体はリンクを参照ください。
今回プログラムでは下記の三点と一部のデータだけ画面に表示するということを行っていきたいと思います。
- 標準偏差σを求める
- 移動平均線に±σを行いグラフのデータを作成する
- CombineChartで描画を行う
ライブラリの導入
前回記事同様にChartsライブラリをPodで導入します。
描画の準備
次にStoryBoardでチャートの描画範囲を決めましょう。
- CombinedChartViewを追加
- 上下左右に制約を追加
- CombinedChartViewを関連付け
ボリンジャーバンドを描画する
それでは実際にボリンジャーバンドを描画しましょう。
完成予定
CombinedChartViewの設定
CombineChartViewにはこの前とほとんど同じなのですが、新たにデータ数を増やす影響もありX軸の表示数に制限を行います。
chart.setVisibleXRangeMaximum(30)
chart.setVisibleXRangeMinimum(5)
またスワイプの動作を行うためdragEnabledをtrueに変更します
chart.dragEnabled = true
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | // 複合チャートの描画 func setupCombineChart(_ chart: CombinedChartView, data: CombinedChartData){ // dragを有効化 chart.dragEnabled = true chart.setScaleEnabled(true) chart.pinchZoomEnabled = true // legendの設定 chart.legend.horizontalAlignment = .left chart.legend.verticalAlignment = .bottom chart.legend.orientation = .horizontal chart.legend.drawInside = false chart.legend.font = UIFont(name: "HelveticaNeue-Light", size: 10)! // 左の目盛りの設定 chart.leftAxis.labelFont = UIFont(name: "HelveticaNeue-Light", size: 10)! chart.leftAxis.spaceTop = 0.3 chart.leftAxis.spaceBottom = 0.3 chart.leftAxis.axisMinimum = 90 // chart.rightAxis.enabled = false chart.xAxis.labelPosition = .bottom chart.xAxis.labelFont = UIFont(name: "HelveticaNeue-Light", size: 10)! chart.xAxis.axisMaximum = data.xMax + 0.25 chart.data = data //チャートにデータ設定を行った後に設定 chart.setVisibleXRangeMaximum(30) chart.setVisibleXRangeMinimum(5) } |
CombinedChartView用のデータ作成
今回はボリンジャーバンドを作成するためのデータを作成します。
ローソク足用のデータは移動平均線と同様のデータ
var endPoints: [Double] = []
上記に加えて、移動平均線は5日としてデータ作成したのですが、ボリンジャーバンドは期間が20or25と書いてあったため下記を宣言します。
//移動平均線の計算、および標準偏差を求める際の期間とりあえず20を設定
let periodCount = 20
宣言したendPointsにキャンドルデータの作成のロジック部分で終値を保存します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | func generateCandleData(_ count: Int, range: UInt32) -> CandleChartData { //endPointsを初期化ローソク足のチャートでも読んでいるため endPoints = [] let yVals1 = (0..<count).map { (i) -> CandleChartDataEntry in let mult = range + 1 let val = Double(arc4random_uniform(40) + mult) let high = Double(arc4random_uniform(9) + 8) let low = Double(arc4random_uniform(9) + 8) let open = Double(arc4random_uniform(6) + 1) let close = Double(arc4random_uniform(6) + 1) let even = i % 2 == 0 //終値のデータを保存 endPoints.append( even ? val - close : val + close) //ローソク足用のデータ作成 return CandleChartDataEntry(x: Double(i), shadowH: val + high, shadowL: val - low, open: even ? val + open : val - open, close: even ? val - close : val + close, icon: nil) } |
続いてボリンジャーバンド用のデータです。
移動平均線は一つの折れ線でしたがボリンジャーバンドは下記の5つの折れ線です。
- アッパーバンド2:単純移動平均線+2標準偏差(σ)
- アッパーバンド1:単純移動平均線+1標準偏差(σ)
- ミッドバンド:単純移動平均線
- ロワーバンド1:単純移動平均線-1標準偏差(σ)
- ロワーバンド2:単純移動平均線-2標準偏差(σ)
前回と同じようなコードだととても縦長になってしまうため。下記の関数を作成します。
1 2 3 4 5 6 7 8 9 | //ChartDataEntry,ラベルの文字列、線の色を受け取り LineChartDataSetを返却する関数 func createDataSet(entry: [ChartDataEntry] , label: String ,color: UIColor) -> LineChartDataSet{ let dataSet = LineChartDataSet(values: entry, label: label) dataSet.lineWidth = 1.75 dataSet.setColor(color) dataSet.drawValuesEnabled = false dataSet.drawCirclesEnabled = false return dataSet } |
続いてendPointからボリンジャーバンド用にデータを作成します。
引数でendPointsを明示的に渡すようにしました。
sliceでarrayを作ったのでそのまま書いていますが、標準偏差sigmaを求める関数作った方がシンプルに見えるかもしれません。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 | func bolingerBandData(endPoints: [Double]) -> LineChartData //アッパーバンド2=単純移動平均線+2標準偏差(σ) var upperBand2Entry: [ChartDataEntry] = [] //アッパーバンド1=単純移動平均線+1標準偏差(σ) var upperBand1Entry: [ChartDataEntry] = [] //ミッドバンド=単純移動平均線 var midBandEntry: [ChartDataEntry] = [] //ロワーバンド1=単純移動平均線-1標準偏差(σ) var lowerBand1Entry: [ChartDataEntry] = [] //ロワーバンド2=単純移動平均線-2標準偏差(σ) var lowerBand2Entry: [ChartDataEntry] = [] endPoints.enumerated().forEach { (i, data) in //最初の2件と終わりの2件はデータを表示しない guard i >= periodCount - 1 else{ return } //期間中のデータ let array = endPoints[i - (periodCount - 1) ... i] //単純移動平均線用のデータ let val = array.reduce(0, +) / Double(periodCount) ///標準偏差σを求める計算式 //期間毎価格の合計の自乗の合計 let left = array.map{pow($0,2)}.reduce(0, +) * Double(periodCount) //価格の合計の自乗 let right = pow(array.reduce(0, +),2) //期間*(期間-1) let under = Double(periodCount * (periodCount - 1)) //標準偏差 let sigma = sqrt((left - right) / under) //移動平均線に±σを行いグラフのデータを作成する upperBand2Entry.append(ChartDataEntry(x: Double(i), y: val + (sigma*2))) upperBand1Entry.append(ChartDataEntry(x: Double(i), y: val + sigma)) midBandEntry.append(ChartDataEntry(x: Double(i), y: val)) lowerBand1Entry.append(ChartDataEntry(x: Double(i), y: val - sigma)) lowerBand2Entry.append(ChartDataEntry(x: Double(i), y: val - (sigma*2))) } return LineChartData(dataSets: [ createDataSet(entry: upperBand2Entry, label: "upper2", color: .red), createDataSet(entry: upperBand1Entry, label: "upper1", color: .blue), createDataSet(entry: midBandEntry, label: "mid", color: .black), createDataSet(entry: lowerBand1Entry, label: "lower1", color: .blue), createDataSet(entry: lowerBand2Entry, label: "lower2", color: .red)]) } |
CombinedChartViewに描画
最後に下記で描画します。ローソク足の個数はとりあえず200で描画しています。
1 2 3 4 5 6 7 8 9 | override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view, typically from a nib. //CombineChartで描画を行う let cobinedData = CombinedChartData() cobinedData.candleData = generateCandleData(200, range: 100) cobinedData.lineData = bolingerBandData(endPoints: endPoints) setupCombineChart(combinedChartView,data: cobinedData) } |
さいごに
テクニカルチャートの要件があり勉強中ですが、実際にテクニカルチャートをもとに取引をした事がありません。
テクニカルチャートをもとに取引を行って重要性を学んでみたいですね。取引額は控えめで!
それでは最後までありがとうございました。今度はKotlinか一目均衡表を学んでみようと思います。