版权声明:本文为博主原创文章,未经博主允许不得转载。
Swift版:算法代码
//
// ViewController.swift
// CalculatePortfolioComposition
//
// Created by JimmyLaw on 2017/1/11.
// Copyright © 2017年 JimmyStudio. All rights reserved.
//
import UIKit
import Surge
class ViewController: UIViewController {
// stock
struct Stock {
var name: String
var code: String
var dailyYield: [Double]
var expect: Double
var variance: Double = 0.0
var percent: Double = 0.0
init(name: String, code: String, dailyYield: [Double]) {
self.name = name
self.code = code
self.dailyYield = dailyYield
self.expect = mean(dailyYield)
}
}
//result
struct Result {
var stocks: [Stock]
var expect: Double
var variance: Double
var ev: Double
var percents: [Double]
init(stocks: [Stock], expect: Double, variance: Double, ev:Double,percents: [Double] ) {
self.stocks = stocks
self.expect = expect
self.variance = variance
self.ev = ev
self.percents = percents
}
init(result: Result) {
self.stocks = result.stocks
self.expect = result.expect
self.variance = result.variance
self.ev = result.ev
self.percents = result.percents
}
}
struct MinAndMax {
var min: Double = Double(MAXFLOAT)
var max: Double = -(Double)(MAXFLOAT)
init(exps: [Double]) {
for exp in exps {
if exp >= max {
max = exp
}
if exp <= min {
min = exp
}
}
}
}
//creat stockData from CSV
func ReadStockFile(fileName: String) -> [Stock] {
var stocks = [Stock]()
let filePath = Bundle.main.path(forResource: fileName, ofType: "csv")
let fileUrl = URL.init(fileURLWithPath: filePath!)
do{
let content: String! = try String(contentsOf: fileUrl, encoding: String.Encoding.utf8)
let r = content.replacingOccurrences(of: "\r", with: "")
let n = r.replacingOccurrences(of: "\n", with: "")
let arr = n.components(separatedBy: ",")
for i in 0...9 {
var name = ""
var code = ""
var yields = [Double]()
for j in 0...29 {
if j == 0 {
name = arr[i + 10*j]
}else if j == 1{
code = arr[i + 10*j]
}else{
yields.insert((arr[i + 10*j] as NSString).doubleValue , at: yields.endIndex)
}
}
var s = Stock.init(name: name, code: code, dailyYield: yields)
s.variance = Variance(yields: yields)
stocks.insert(s, at: stocks.endIndex)
}
return stocks
}catch{
print("error")
return stocks
}
}
//simulate stocks
var simulateStocks: [Stock]{
return ReadStockFile(fileName: "stockData")
}
//计算期[Stock]的望值
func Expects(stocks: [Stock]) -> [Double] {
var output = [Double]()
for stock in stocks {
var exp: Double = 0.0
for yield in stock.dailyYield {
exp += yield
}
let count: Double = Double(stock.dailyYield.count)
output.insert(exp/count, at: output.endIndex)
}
return output
}
//计算方差
func Variance(yields: [Double]) -> Double {
let exp = mean(yields)
var output = 0.0
for yield in yields {
output += pow((yield - exp), 2)
}
output /= Double(yields.count - 1)
return output
}
//计算协方差
func Eigen (stocks: [Stock]) -> [[Double]] {
let exps = Expects(stocks: stocks)
var output = [[Double]]()
for i in 0...stocks.count - 1 {
var cov_is = [Double]()
let yield_i = stocks[i].dailyYield
for j in 0...stocks.count - 1 {
let yield_j = stocks[j].dailyYield
var cov_i: Double = 0.0
for k in 0...yield_j.count - 1 {
cov_i += ((yield_i[k] - exps[i]) * (yield_j[k] - exps[j]))
}
cov_i /= (Double(yield_j.count - 1))
cov_is .insert(cov_i, at: cov_is.endIndex)
}
output .insert(cov_is, at: output.endIndex)
}
return output
}
var combines = [[Int]]()
func Combine(arr: [Int], nLen: Int, m: Int, out: [Int], outLen: Int){
var temp: [Int] = out
if m == 0 {
combines.insert(out, at: combines.endIndex)
return
}
for i in (m...nLen).reversed() {
temp.remove(at: m - 1)
temp.insert(arr[i - 1], at: (m - 1))
Combine(arr: arr, nLen: i - 1, m: m - 1, out: temp, outLen: outLen)
}
}
func CalculateCombines(stocks: [Stock], num: Int) -> [[Stock]] {
print("计算所有可能组合...\n")
var arr = [Int]()
var output = [[Stock]]()
for i in 0...stocks.count - 1 {
arr.insert(i, at: i)
}
let out = [Int](repeating: 0 , count: num)
Combine(arr: arr, nLen: arr.count, m: num, out: out, outLen: num)
print("合计\(combines.count)种成分配比...\n")
print("组成所有可能成分配比...\n")
for com in combines {
var temp = [Stock]()
for i in com {
temp.insert(stocks[i], at: temp.endIndex)
}
output.insert(temp, at: output.endIndex)
}
return output
}
var calculateTimes = 0
func CalculateBestPercent(stocks: [Stock]) -> [Result]{
var output = [Result]()
let exps = Expects(stocks: stocks)
let R_T = Matrix([exps])
let R = transpose(R_T)
let E_T = Matrix.init(rows: 1, columns: stocks.count, repeatedValue: 1.0) //(1,1...1)
let E = Matrix.init(rows: stocks.count, columns: 1, repeatedValue: 1.0) //(1,1...1)T
let V = Matrix(Eigen(stocks: stocks))
let V_1 = inv(V)
let A = (E_T * V_1 * R)[0,0]
let B = (R_T * V_1 * R)[0,0]
let C = (E_T * V_1 * E)[0,0]
let D = B * C - A * A
if D <= 0 {
print("D < 0 无解\n")
return output
}else{
print("D: ",D," 尝试计算最优解...")
let D_1 = 1.0/D
let B_V_1_E = B * V_1 * E
let A_V_1_R = A * V_1 * R
let B_V_1_E_A_V_1_R = sub(B_V_1_E[column: 0], y: A_V_1_R[column: 0])
let g = mul(D_1, x: transpose(Matrix([B_V_1_E_A_V_1_R])))
let C_V_1_R = C * V_1 * R
let A_V_1_E = A * V_1 * E
let C_V_1_R_A_V_1_E = sub(C_V_1_R[column: 0], y: A_V_1_E[column: 0])
let h = mul(D_1, x: transpose(Matrix([C_V_1_R_A_V_1_E])))
let mm = MinAndMax.init(exps: exps)
let times = Int((mm.max - mm.min)*10000)
if times <= 0 {
print("该组收益期望值过小!无解!\n")
return output
}
calculateTimes += times
var i = mm.min
for _ in 0...times {
let W = g + mul(i, x: h)
let mi = min(W[column: 0])
let ma = max(W[column: 0])
if i <= 0 {
print("期望收益 < 0 不再求解\n")
}else if mi < 0.0 {
print("组合中存在 < 0.00% 的配比,收益期望\(i)无解\n")
}else if ma >= 0.9999 {
print("组合中存在 >= 99.99% 的配比,收益期望\(i)无解\n")
print(ma)
}else
{
print("=====组合收益期望\(i)有解=====\n")
let ex = R_T * W;//反算期望收益
let W_T = transpose(W);
let vari = W_T * V * W
let ev = ex[0,0]/vari[0,0] //均值方差比
let result = Result.init(stocks: stocks, expect: ex[0,0], variance: vari[0,0], ev: ev, percents: W[column: 0])
output.insert(result, at: output.endIndex)
}
i += 0.0001
}
return output
}
}
override func viewDidLoad() {
super.viewDidLoad()
let combinesOfStock = CalculateCombines(stocks: simulateStocks, num: 5)
var results = [Result]()
for i in 0...combinesOfStock.count - 1 {
print("计算第\(i + 1)种组合最优配比")
let result = CalculateBestPercent(stocks: combinesOfStock[i])
if (result.count > 0) {
print("第\(i + 1)组共\(result.count)组解")
for res in result {
results.insert(res, at: results.endIndex)
}
}
}
print("**************结论**************\n")
print("***********计算了\(calculateTimes)次***********\n")
print("***********合计\(combines.count)种成分配比***********\n")
print("***********共产生\(results.count)种组合***********\n")
var evMax: Double = Double(-MAXFLOAT)
var expMax: Double = Double(-MAXFLOAT)
var varMin: Double = Double(MAXFLOAT)
for i in 0...results.count - 1{
if (results[i].ev >= evMax) {
evMax = results[i].ev
}
if results[i].expect >= expMax {
expMax = results[i].expect
}
if results[i].variance <= varMin {
varMin = results[i].variance
}
}
for res in results{
if res.ev == evMax {
print("******均值方差比最大的组合******")
print("期望收益率:\(res.expect)\n方差:\(res.variance)\n均值方差比:\(res.ev)")
print("配比:")
for i in 0...res.stocks.count - 1 {
print("名称: ",res.stocks[i].name,"代码: ",res.stocks[i].code,"配比: ",res.percents[i])
}
}
if res.expect == expMax {
print("\n******期望收益率最大的组合******")
print("期望收益率:\(res.expect)\n方差:\(res.variance)\n均值方差比:\(res.ev)")
print("配比:")
for i in 0...res.stocks.count - 1 {
print("名称: ",res.stocks[i].name,"代码: ",res.stocks[i].code,"配比: ",res.percents[i])
}
}
if res.variance == varMin {
print("\n******方差最小的组合******")
print("期望收益率:\(res.expect)\n方差:\(res.variance)\n均值方差比:\(res.ev)")
print("配比:")
for i in 0...res.stocks.count - 1 {
print("名称: ",res.stocks[i].name,"代码: ",res.stocks[i].code,"配比: ",res.percents[i])
}
}
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
任选10个股票的历史交易记录,任取其中4种股票,进行组合配比求解
运算结果如下
**************结论**************
***********计算了13248次***********
***********合计210种成分配比***********
***********共产生3097种组合***********
******方差最小的组合******
期望收益率:2.00357142857126e-05
方差:3.00676879937876e-06
均值方差比:6.66353671418042
配比:
名称: 中来股份 代码: 300393 配比: 0.00414454618364605
名称: 浪莎股份 代码: 600137 配比: 0.000805787492222925
名称: 湖南天雁 代码: 600698 配比: 0.874775672115665
名称: 信隆健康 代码: 002105 配比: 0.120273994208466
******均值方差之比最大的组合******
期望收益率:0.000132885357142857
方差:8.24153470892431e-06
均值方差比:16.1238606444213
配比:
名称: 超讯通信 代码: 603322 配比: 0.00158115108426157
名称: 新黄浦 代码: 600638 配比: 0.0133675998585093
名称: 湖南天雁 代码: 600698 配比: 0.574677855077226
名称: 信隆健康 代码: 002105 配比: 0.410373393980003
******期望收益率最大的组合******
期望收益率:0.00672003571428572
方差:0.00072940202654893
均值方差比:9.21307518993426
配比:
名称: 光电股份 代码: 600184 配比: 0.00182208214544699
名称: 中来股份 代码: 300393 配比: 0.0011345357693352
名称: 新黄浦 代码: 600638 配比: 0.787917304207837
名称: 能科股份 代码: 603859 配比: 0.20912607787738
网友评论