美股記帳 App / Part 4. Websocket 抓stock real time price
Published in
6 min readMar 21, 2022
Websocket
- 只要 shakeHand 成功過後,兩者就可以,兩者就可以持久性的連接,並且能雙向傳遞資料,不用一直 get post
- Finnhub 提供了用 websocket 連線的 Last Price Updates,比在 watchList 裡取得的股價資訊更即時,但提供的資訊更即時,但提供的資訊比較少,因此決定將即時的價格放在各個股票的頁面
建立 WebSocket
- 遵從 URLSessionWebSocketDelegate
- 建立取得資料的 struct,等一下用 JSONEncoder 得到資料
struct websocketStockInfo : Codable{
var type : String
var data : [priceData]
}struct priceData : Codable{
var s : String // symbol
var p : Double // price
}
- func setSession() :開啟連線
- func ping() :確保連線正常,並回傳一個pong
- func send() :送字串訊息過去,讓他知道你要什麼
- func receive():收到 result type 的訊息,在 string的結過裡解 json 並把得到的價格 update 到 label 裡
- func close : 中斷連線
- func urlSession(_ session: URLSession, webSocketTask: URLSessionWebSocketTask, didOpenWithProtocol protocol: String?) :當 websocket 連線成功後 依序啟動 ping send receive function
判斷股票市場開市了沒
使用 websocket 所得到的價格資訊,只有在美股開市的時候才有,所以在不是開市的時間,我會用 https api (watch List 裡的方法)取的價格資訊
- 美股開市時間 :紐約時間週一到週五的早上九點半到下午四點
- 記得 now variable 裡 取的時間也要是紐約時間
- 用 NSCalandar ().isDateInWeekend 判斷是不是週末
let newYorkTimeZone = TimeZone(identifier: "America/New_York")!let now = Date().addingTimeInterval(TimeInterval.init(newYorkTimeZone.secondsFromGMT()))let nineAM = now.dateAt(hours: 9, minutes: 30).addingTimeInterval(TimeInterval.init(newYorkTimeZone.secondsFromGMT()))let fourPM = now.dateAt(hours: 16, minutes: 0).addingTimeInterval(TimeInterval.init(newYorkTimeZone.secondsFromGMT()))
if !NSCalendar(identifier: .gregorian)!.isDateInWeekend(now) &&
now >= nineAM &&
now <= fourPM{
print("stockMarketOpen")
setSession()
}else{
print("stockMarketClose")
if let stockSymbol = stockSymbol {
fetchStockPrice(stockSymbol: stockSymbol)
}
}
- function to get 9:00 and 16:00
extension Date{func dateAt(hours: Int, minutes: Int) -> Date{
let calendar = NSCalendar(calendarIdentifier: NSCalendar.Identifier.gregorian)!
calendar.timeZone = TimeZone(identifier: "America/New_York")!
var dateComponents = calendar.components([NSCalendar.Unit.year,
NSCalendar.Unit.month,
NSCalendar.Unit.day], from: self)
dateComponents.hour = hours
dateComponents.minute = minutes
let newDate = calendar.date(from:dateComponents)!
return newDate
}
}