前几天看到一个CoreML的教程视频,感觉挺有意思的样子,所以去了解了一下,决议尝试将他用于猜测涨跌,看下机器学习的能不能猜测的准,就算不可,也无所谓,就相当于学习好了
模型类型
CoreML需求选定一个模型来进行练习 翻开developer tool,然后点击New document就能够看到有多少类型 前面几个都是图片分类,图片辨认,手势辨认,文字辨认啥的,我也没细看,今天咱们就用表格的Tabular Regression来给他一些根底数据,让他来猜测
模型选型
曾经也想过给复杂的数据给他去练习,有点复杂,还是先给一些简单的了,我的想法就是以最后一根k线的涨跌幅作为方针,往前数五根k线作为根底数据 生成模型需求准备一个csv文件,我准备像下面这样的格式,前面的k是根底数据,target是方针数据,这样的数据交给模型练习器进行练习
k0 | k1 | k2 | k3 | k4 | target |
---|---|---|---|---|---|
0.09 | -0.16 | 0.23 | -0.06 | 0.07 | -0.05 |
-0.16 | 0.23 | -0.06 | 0.07 | -0.05 | -0.10 |
0.23 | -0.06 | 0.07 | -0.05 | -0.10 | 0.05 |
-0.06 | 0.07 | -0.05 | -0.10 | 0.05 | -0.06 |
单位是百分点
获取数据
要得到这样的数据,需求从买卖平台恳求过往的k线数据 我从币安的api接口查到接口和参数,然后查询从某个时刻开端的数据,一次恳求1500条,1500条乘以5分钟
open func requestCandles(startTime: Int? = nil, limit: Int? = nil) async throws -> (response: HTTPURLResponse, candles: [Candle]) {
let path = "GET /api/v1/klines"
var params = ["symbol": instId, "interval": intervalStr] as [String: Any]
if let startTime = startTime {
params["startTime"] = startTime
}
params["limit"] = limit ?? self.limit
let response = await RestAPI.send(path: path, params: params)
if response.succeed {
if let data = response.data as? [[Any]] {
var candles = [Candle]()
for arr in data {
let candle = Candle(array: arr)
candles.append(candle)
}
return (response.res.urlResponse!, candles)
} else {
throw CommonError(message: "data类型不对")
}
} else {
logInfo("恳求k线失利:(response.errMsg ?? "")")
throw CommonError(message: response.errMsg ?? "")
}
}
通过接口测验,我发现最早只能查到2020/01/01 00:00:00的数据,时刻戳就是1577808000000, 所以我就起了一个定时器不断调用接口把数据拉下来
/// 执行一次恳求
func request() async throws {
let lastTime = lastTime ?? initStartTime
let next = lastTime + 1
do {
logInfo("开端恳求(next.dateDesc)的数据")
// 恳求当前时刻的k线
let (response, candles) = try await candleManager.requestCandles(startTime: next)
let requestCompletion = await self.saveCandles(candles)
if !requestCompletion {
// 恳求没完结,继续恳求下一页
try await self.continueWith(response: response)
return
}
logInfo("恳求完结,开端生成Candle模型")
try await createCandleModels()
} catch {
logInfo("恳求失利:(error)")
}
}
Candle是一个根底的k线数据结构,CandleModel就是上面的模型了,一切Candle会拼装到一个数组中,然后再来生成一切的CandleModel,生成完之后,就开端拼装csv需求的格式了
生成csv文件
/// 生成csv文件
func createCSVFile() async throws {
// 每行的内容
var contents = [String]()
// 文件表头
let header = columns.joined(separator: ",")
contents.append(header)
logInfo("拼装Header:(header)")
// 每天的
for candleModel in candleModels {
var lines = [String]()
// 前面的涨跌幅
lines += candleModel.previousCandles
.map({ $0.rate })
// 当天的涨跌幅
if let current = candleModel.current {
lines.append(current.rate)
}
// 当前的字符串
let line = lines.joined(separator: ",")
logInfo("拼装字符串:(line)")
contents.append(line)
}
// 拼装成最后字符
let content = contents.joined(separator: "n")
if let data = content.data(using: .utf8) {
try data.write(to: csvFileURL)
}
logInfo("生csv成功,准备生成CoreML模型")
try await createML()
}
生成了一个14M的文件,模型还是比较多的
生成CoreML模型
接下来就是生成CoreML模型了
/// 生成模型
func createML() async throws {
guard FileManager.default.fileExists(atPath: csvFileURL.path()) else {
print("csv路径不存在")
throw CreateError.csvNotFound
}
// 生成MLTable
let dataFrame = try DataFrame(contentsOfCSVFile: csvFileURL, columns: columns)
// 划分数据,0.8用于练习,0.2用于验证
let (trainingData, testingData) = dataFrame.randomSplit(by: 0.2, seed: 5)
// 开端练习
logInfo("开端练习")
let regressor = try MLLinearRegressor(trainingData: DataFrame(trainingData), targetColumn: "rate")
/// 获取练习成果
let trainintError = regressor.trainingMetrics.error
let trainintValid = regressor.trainingMetrics.isValid
let worstTrainingError = regressor.trainingMetrics.maximumError
logInfo("练习成果->: error: (String(describing: trainintError)),是否有用:(trainintValid),辨认率:(worstTrainingError)")
let validationError = regressor.validationMetrics.error
let validationValid = regressor.validationMetrics.isValid
let worstValidationError = regressor.validationMetrics.maximumError
logInfo("验证成果->: error: (String(describing: validationError)),是否有用:(validationValid),辨认率:(worstValidationError)")
/// 评价
logInfo("开端评价")
let regressorEvalutation = regressor.evaluation(on: DataFrame(testingData))
/// 评价e的成果
let evalutationError = regressorEvalutation.error
let evalutationValid = regressorEvalutation.isValid
let worstEvaluationError = regressorEvalutation.maximumError
logInfo("评价成果->: error: (String(describing: evalutationError)),是否有用:(evalutationValid),辨认率:(worstEvaluationError)")
// 保存
let regressorMetaData = MLModelMetadata(author: "zhtg@me.com", shortDescription: "BTC5mk线涨跌猜测模型", version: "1.0")
try regressor.write(to: modelFileURL, metadata: regressorMetaData)
// 测验
// let testcsvFile = documentsPath + "/test.csv"
// let testDataFrame = try DataFrame(contentsOfCSVFile: URL(filePath: testcsvFile), columns: ["first", "second"])
// let pResult = try regressor.predictions(from: testDataFrame)
// print("result: (pResult)")
logInfo("完结生成")
// xcrun coremlcompiler compile CandleModelRegressor.mlmodel .
// xcrun coremlcompiler generate --language Swift CandleModelRegressor.mlmodel .
}
有了csv也能够用developer tool来生成,我这里为了方便,就爽性用代码一同生成好了,其间有用和成果字段没用上,这个应该要判断一下,假如不满足,则直接中止的
生成的模型是一个CandleModelRegressor.mlmodel称号的,mlmodel结束,这个文件还不能够用于spm,用于xcode project却是能够,
不能够用于spm可能是bug,他编译的时分说缺少一个言语,可是spm没有设置言语的地方,走不下去了,只能别的想办法,google到能够手动编译,编译完再放进去就能够
xcrun coremlcompiler compile CandleModelRegressor.mlmodel .
xcrun coremlcompiler generate --language Swift CandleModelRegressor.mlmodel .
手动指令如上,他会生成以下两个文件,
集成
拖入spm项目根目录即可,
然后target还要添加一个resource的配置,需求运用.copy
,其他都不可
resources: [
.copy("CandleModelRegressor.mlmodelc"),
]
运用
用的时分需求先生成一个regressor
// 生成辨认器
var regressor: CandleModelRegressor = {
let bundle = Bundle.module
let url = bundle.url(forResource: "CandleModelRegressor", withExtension:"mlmodelc")!
return try! CandleModelRegressor(contentsOf: url)
}()
然后辨认的时分,传入当前k线往前的5个节点的涨跌幅,即可用模型猜测出来一个成果了
guard let c0 = candleModel.previousCandles[0].rate.double,
let c1 = candleModel.previousCandles[1].rate.double,
let c2 = candleModel.previousCandles[2].rate.double,
let c3 = candleModel.previousCandles[3].rate.double,
let c4 = candleModel.previousCandles[4].rate.double else {
throw CommonError(message: "涨跌幅转成double失利")
}
logInfo("开端辨认:(c0),(c1),(c2),(c3),(c4)")
let output = try regressor.prediction(_0: c0, _1: c1, _2: c2, _3: c3, _4: c4)
logInfo("辨认成果:(output.rate)")
接下来就是买卖的代码了,买卖的代码就不帖了,有爱好大家能够去github自己看,回头我会布置到linux服务上,跑一段时刻,再来看成果,成果也会更新到这里
布置
本来写好了DockerFile准备布置的,之前写一个后台服务都能够成功布置到linux,成果发现这个不可,因为CoreML模块在swift的linux服务端没有,只能跑在macOS了 正好我有个Mac Mini的Nas服务器,因为没有Mac的docker镜像,所以我就在终端直接运转好了,不关掉这个终端就能一向运转,懒得搞进程看护了
swift run -c release MLTrader