iT邦幫忙

2021 iThome 鐵人賽

DAY 4
0
Mobile Development

使用 Swift 和公開資訊,打造投資理財的 Apps系列 第 4

D4-用 Swift 和公開資訊,打造投資理財的 Apps { 下載公司股票代號和股票名稱等基本資料 }

第一個要下載的資料,就是台灣所有上市公司的代號 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。

https://ithelp.ithome.com.tw/upload/images/20210913/2014062280rZcyBSoa.png

下載完 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", "電子郵件信箱": "finance@taiwancement.com", "股票過戶機構": "中國信託商業銀行代理部", "總經理": "李鐘培", "上市日期": "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"]


上一篇
D3-用 Swift 和公開資訊,打造投資理財的 Apps { 使用 Alamofire 套件進行 URLRequest }
下一篇
D5-用 Swift 和公開資訊,打造投資理財的 Apps { 實作 上市/上櫃/興櫃 所有資料的列表 }
系列文
使用 Swift 和公開資訊,打造投資理財的 Apps37

尚未有邦友留言

立即登入留言