第一个要下载的资料,就是台湾所有上市公司的代号 vs. 名称,因为在後续的资料中,可能有些资料只会有代号,也可能有些资料只会有名称,所以这个铁人赛第一步,决定先下载股票代号对照表。而下面所提到的资料表,连公司基本资讯都有。
这些资料放在政府的资料开放平台上,不过我们只需要下载位置就可以了。
上市公司资料
https://mopsfin.twse.com.tw/opendata/t187ap03_L.csv
上柜公司资料
https://mopsfin.twse.com.tw/opendata/t187ap03_O.csv
兴柜公司资料
https://mopsfin.twse.com.tw/opendata/t187ap03_R.csv
决定好目标之後,先分析需要哪些 column。
下载完 csv 档之後,我会需要[公司代号]、[公司名称]、[公司简称]、[资本额],这些栏位。
那装载资料的 Data Model 就会是这样
import Foundation
struct StockBasicInfo {
let stockCode: String
let stockName: String
let companyName: String
let capital: String
}
这个 csv 档在 iOS 这边也有套件可以处理,我选择用 SwiftCSV
安装方法说明文件在下方 repo 的 readme
https://github.com/swiftcsv/SwiftCSV
和 Alamofire 一样,我们要有个 adapter 来包第三方套件。
import Foundation
import SwiftCSV
struct CSVAdapter {
var header = [String]()
var namedRows = [[String: String]]()
var namedColumns = [String: [String]]()
init?(rawString: String) {
if let csv = try? CSV(string: rawString) {
self.header = csv.header
self.namedRows = csv.namedRows
self.namedColumns = csv.namedColumns
}
}
}
接下来就是写一个专门处理股票资讯的类别,在基本资料上,因为上市、上柜、兴柜的资料被放在三个不同的地方,所以在 request function 上,会先分成三个 func,後续可以看情况再决定是否要再开一个 func 对这三个 func 进行连续呼叫。
import Foundation
/// 这一个类别主要处理上市上柜兴兴公司相关资料
class StockInfoManager {
private lazy var alamofireAdapter: AlamofireAdapter = {
return AlamofireAdapter()
}()
/// 取得上市公司基本资料
func requestTwStockCodeAndName(completion: @escaping (([StockBasicInfo], Error?) -> Void)) {
let urlString = "https://mopsfin.twse.com.tw/opendata/t187ap03_L.csv"
requestStockInfoBasic(urlString) { list, error in
completion(list, error)
}
}
/// 取得上柜公司基本资料
func requestOTCCodeAndName(completion: @escaping (([StockBasicInfo], Error?) -> Void)) {
let urlString = "https://mopsfin.twse.com.tw/opendata/t187ap03_O.csv"
requestStockInfoBasic(urlString) { list, error in
completion(list, error)
}
}
/// 取得兴柜公司基本资料
func requestEmerginCodeAndName(completion: @escaping (([StockBasicInfo], Error?) -> Void)) {
let urlString = "https://mopsfin.twse.com.tw/opendata/t187ap03_R.csv"
requestStockInfoBasic(urlString) { list, error in
completion(list, error)
}
}
private func requestStockInfoBasic(_ urlString: String, completion: @escaping ([StockBasicInfo], Error?) -> Void) {
var companyList = [StockBasicInfo]()
alamofireAdapter.request(urlString, method: .get) { data, response, error in
if let error = error {
print("tw stock fetch error: \(error.localizedDescription)")
completion(companyList, error)
return
}
if let data = data,
let string = String(data: data, encoding: .utf8),
let csv = CSVAdapter(rawString: string) {
for company in csv.namedRows {
let stockCode = company["公司代号"] ?? ""
let stockName = company["公司简称"] ?? ""
let companyName = company["公司名称"] ?? ""
let capital = company["实收资本额"] ?? ""
let info = StockBasicInfo(stockCode: stockCode, stockName: stockName, companyName: companyName, capital: capital)
companyList.append(info)
}
}
completion(companyList, nil)
}
}
}
当完成後,可以在 VC 里面试着打 request,看有没有回应。可以试着印出 csv.headers,如果有印出下列文字,就表示有拿到资料
["出表日期", "公司代号", "公司名称", "公司简称", "外国企业注册地国", "产业别", "住址", "营利事业统一编号", "董事长", "总经理", "发言人", "发言人职称", "代理发言人", "总机电话", "成立日期", "上市日期", "普通股每股面额", "实收资本额", "私募股数", "特别股", "编制财务报表类型", "股票过户机构", "过户电话", "过户地址", "签证会计师事务所", "签证会计师1", "签证会计师2", "英文简称", "英文通讯地址", "传真机号码", "电子邮件信箱", "网址"]
而使用 csv.namedRows 就可以操作 csv 档里面的 row。如果要操作 column,就用 csv.namedColumns。
试着印出第 0 个 namedRows,资讯应该如下。
["过户电话": "66365566", "签证会计师事务所": "勤业众信联合会计师事务所", "发言人": "黄健强", "过户地址": "台北市重庆南路一段83号5楼", "住址": "台北市中山北路2段113号", "外国企业注册地国": "- ", "总机电话": "(02)2531-7099", "实收资本额": "61574403270", "成立日期": "19501229", "签证会计师1": "邵志明", "公司代号": "1101", "编制财务报表类型": "1", "营利事业统一编号": "11913502", "传真机号码": "(02)2531-6529", "特别股": "200000000", "电子邮件信箱": "[email protected]", "股票过户机构": "中国信托商业银行代理部", "总经理": "李钟培", "上市日期": "19620209", "产业别": "01", "公司名称": "台湾水泥股份有限公司", "网址": "http://www.taiwancement.com", "出表日期": "1100903", "英文简称": "TCC", "公司简称": "台泥", "普通股每股面额": "新台币 10.0000元", "签证会计师2": "黄惠敏", "发言人职称": "资深副总经理", "代理发言人": "赖家柔", "董事长": "张安平", "英文通讯地址": "No.113, Sec.2, Zhongshan N. Rd.,Taipei City 104,Taiwan (R.O.C.)", "私募股数": "0"]
准备程序竞赛的技巧大多都和平常准备考试的方法相同,但因为程序竞赛有许多规定,在第一次入门时会被很多规...
撰写中 在求发展的道路上,又过了一日...... 这时,成员 20 人。 ...
LSTM 前言 系列文章简介 大家好,我们是 AI . FREE Team - 人工智慧自由团队,这...
同步发表到驴形笔记 前情题要 在上这一篇中我们成功让webpack可以吃下".ts&qu...
昨天角色的 CRUD 功能都完成了,接着就是要把角色指派给使用者了,先建立一个 ViewModel ...