在原来的 func 上加上 range: Int 的输入,然後把原来计算的区间全部用代入变数的替换。
import Foundation
/// 专门处理移动平均线的物件
struct MovingAverageUtility {
/// 将已得到的 K 棒,转出 n MA 的资料
/// - Parameter stockTicks: 传入的 K 棒需先保证 date 从远排到近
/// - Returns: 回传的 MA 点也保证会是 x 从小排到大
func getMAPoints(from stockTicks: [StockKLine], range: Int) -> [MovingAveragePoint] {
let maPeriod = range
let tickIndices = Array(stockTicks.indices).sorted { $0 > $1 } // 先拿出 index 并把 index 从大到小排
var maPoints = [MovingAveragePoint]()
for tickIndex in tickIndices {
let startIndex = tickIndex - maPeriod + 1
if !stockTicks.indices.contains(startIndex) || !stockTicks.indices.contains(tickIndex) {
break
}
let needCalculateTicks = Array(stockTicks[startIndex...tickIndex])
// 从这里开始计算 n 日内收盘价的平均,有更有效率的做法,像是动态规画的方式实作。这一段就留给读者自行优化
let closePriceList = needCalculateTicks.map { tick in
return tick.close ?? 0 // 这边先不考虑如果没有收盘价(暂停交易)的情况,如果有,应该把这个点去除掉,使用 filter 即可
}
let sum = closePriceList.reduce(0, +) //总合
let maValue = sum / Double(maPeriod)
let point = MovingAveragePoint(x: Double(tickIndex), y: maValue)
maPoints.append(point)
}
return maPoints.sorted { $0.x < $1.x }
}
}
先决定我们要的 MA 线分别是 5MA, 10MA, 20 MA 的线。再长下去,目前 csv 的数量是不够的。如果要更大范围的 MA,那所需要的资料量也是相对大。
在 ChartsAdapter 补上输入 Range 和 Color,回传 LineChartDataSet 的 func 即可
private func getMALineData(stockSticks: [StockKLine], range: Int, color: UIColor) -> LineChartDataSet
整个 ChartsAdapter 的程序码如下
// MARK: - Combine Charts 相关 func
extension ChartsAdapter {
private var maUtiltiy: MovingAverageUtility {
return MovingAverageUtility()
}
func getCombineChartView() -> UIView {
let view = CombinedChartView()
setupCombinedChartView(view)
return view
}
func updateWithMALine(stockSticks: [StockKLine], combinedView: UIView) {
if let combinedView = combinedView as? CombinedChartView {
let ma5DataSet = getMALineData(stockSticks: stockSticks, range: 5, color: .blue)
let ma10DataSet = getMALineData(stockSticks: stockSticks, range: 10, color: .red)
let ma20DataSet = getMALineData(stockSticks: stockSticks, range: 20, color: .systemOrange)
let lineData = LineChartData(dataSets: [ma5DataSet, ma10DataSet, ma20DataSet])
let candleData = getCandleData(stockSticks: stockSticks)
let combinedData = CombinedChartData()
combinedData.lineData = lineData
combinedData.candleData = candleData
combinedView.data = combinedData
// 这边有优化空间,请读者自行优化
let candleDataEntry = convert(stockStick: stockSticks)
let dataSet = convert(dataEntry: candleDataEntry)
updateMaxMin(combinedView, dataSet: dataSet)
let indexDateLabels = getIndexDateLabels(from: stockSticks)
updateXAxis(combinedView, indexDateLabels: indexDateLabels)
}
}
private func getMALineData(stockSticks: [StockKLine], range: Int, color: UIColor) -> LineChartDataSet {
var lineDataEntry = [ChartDataEntry]()
let maPoints = maUtiltiy.getMAPoints(from: stockSticks, range: range)
for point in maPoints {
let dataEntry = ChartDataEntry(x: point.x, y: point.y)
lineDataEntry.append(dataEntry)
}
// maPoints 得到了
let maDataSet = LineChartDataSet(entries: lineDataEntry, label: "\(range) MA")
maDataSet.setColor(color)
maDataSet.lineWidth = 1
maDataSet.drawCirclesEnabled = false
maDataSet.drawValuesEnabled = false
maDataSet.axisDependency = .left
maDataSet.highlightEnabled = true
return maDataSet
}
private func getCandleData(stockSticks: [StockKLine]) -> CandleChartData {
let candleDataEntry = convert(stockStick: stockSticks)
let candleDataSet = convert(dataEntry: candleDataEntry)
let candleData = convert(dataSet: candleDataSet)
return candleData
}
private func setupCombinedChartView(_ chartView: CombinedChartView) {
chartView.dragEnabled = false
chartView.setScaleEnabled(true)
chartView.maxVisibleCount = 1000
chartView.pinchZoomEnabled = true
chartView.legend.horizontalAlignment = .right
chartView.legend.verticalAlignment = .top
chartView.legend.orientation = .vertical
chartView.legend.drawInside = false
chartView.legend.font = UIFont.systemFont(ofSize: 10)
chartView.leftAxis.labelFont = UIFont.systemFont(ofSize: 10)
chartView.leftAxis.spaceTop = 0.3
chartView.leftAxis.spaceBottom = 0.3
chartView.leftAxis.axisMinimum = 0
chartView.rightAxis.enabled = false
chartView.xAxis.labelPosition = .bottom
chartView.xAxis.labelFont = UIFont.systemFont(ofSize: 10)
chartView.xAxis.labelCount = 10
}
}
Build and Run App 後,均线就在 K 线上了。
台股申购日历
IT铁人赛Demo App
下方是这次 D1 ~ D12 的完成品,可以下载来试
App Store - 台股申购日历
您的订阅是我制作影片的动力 订阅点这里~ 影片程序码(延续昨天) #步骤二: 资料分群,哪个演算法?...
前情提要 我们整理专案後,现在专案有更明确的模组来封装元件,不仅让 App 效能提升,也让专案更「语...
如果不使用 ES6 的 Class,则可以考虑用 create-react-class 。 var ...
ASTRA 这个热门的WordPress主题,付费版一共有3种方案+2种付费模式;最引以为傲的是☞一...
常见的搜寻引擎 为什麽要利用搜寻引擎找到有关於目标的资讯。 因为搜寻引擎最方便,透过浏览器最容易可以...