mirror of
https://github.com/VGEAREN/Stockbar.git
synced 2026-10-07 16:37:57 +08:00
Adding Market.krStock cascades into several exhaustive switches that must all update together: ExchangeRates.convert, DisplayCurrency, StockChartView ranges and labels. Bundles tasks 2/3/4/5/14 into one build-passing landing. - Market enum: add krStock case + kr_ prefix routing in Market.from - ExchangeRates: add usdToKrw (fallback 1380), krwToCny derived, KRW path in convert - DisplayCurrency: add .krw (₩), display name 韩元 - CurrencyService.fetchRates: include fx_susdkrw in Sina FX batch URL - StockChartView: KR fullDayRange 0...389 (KST 09:00-15:30, 390 min); KR axis labels 09:00/10:30/12:00/13:30/15:30 Tests: +5 Stock cases for KR, +8 CurrencyKRWTests covering all KR↔CNY/USD/HKD/KRW conversion paths and fallback constant.
64 lines
2.3 KiB
Swift
64 lines
2.3 KiB
Swift
import Foundation
|
|
|
|
enum Market: String, Codable, CaseIterable {
|
|
case aStock = "A股"
|
|
case hkStock = "港股"
|
|
case usStock = "美股"
|
|
case krStock = "韩股"
|
|
|
|
static func from(code: String) -> Market {
|
|
if code.hasPrefix("hk") { return .hkStock }
|
|
if code.hasPrefix("usr_") { return .usStock }
|
|
if code.hasPrefix("kr_") { return .krStock }
|
|
return .aStock
|
|
}
|
|
}
|
|
|
|
struct Stock: Identifiable, Equatable {
|
|
var id: String // 股票代码,如 "sh600000"、"usr_aapl"、"hk00700"
|
|
var name: String
|
|
var market: Market
|
|
var costPrice: Double? // 持仓成本价(nil = 未设置)
|
|
var holdingShares: Double? // 持仓股数(nil = 未设置)
|
|
|
|
/// 持仓浮盈亏 = (当前价 - 成本价) × 股数;未设置持仓则返回 nil
|
|
func pnl(quote: Quote) -> Double? {
|
|
guard let cost = costPrice, let shares = holdingShares else { return nil }
|
|
return (quote.price - cost) * shares
|
|
}
|
|
|
|
/// 当日盈亏 = 涨跌额 × 股数;未设置股数则返回 nil
|
|
func dailyPnl(quote: Quote) -> Double? {
|
|
guard let shares = holdingShares else { return nil }
|
|
return quote.change * shares
|
|
}
|
|
|
|
/// 浮盈亏百分比 = (现价 - 成本价) / 成本价 × 100
|
|
func pnlPercent(quote: Quote) -> Double? {
|
|
guard let cost = costPrice, cost > 0 else { return nil }
|
|
return (quote.price - cost) / cost * 100
|
|
}
|
|
|
|
/// 当日盈亏百分比 = 涨跌额 / 成本价 × 100
|
|
func dailyPnlPercent(quote: Quote) -> Double? {
|
|
guard let cost = costPrice, cost > 0 else { return nil }
|
|
return quote.change / cost * 100
|
|
}
|
|
}
|
|
|
|
// MARK: - Codable(容错:未知字段用默认值,避免新版本解码老数据时整条失败)
|
|
extension Stock: Codable {
|
|
enum CodingKeys: String, CodingKey {
|
|
case id, name, market, costPrice, holdingShares
|
|
}
|
|
|
|
init(from decoder: Decoder) throws {
|
|
let c = try decoder.container(keyedBy: CodingKeys.self)
|
|
id = try c.decode(String.self, forKey: .id)
|
|
name = try c.decode(String.self, forKey: .name)
|
|
market = try c.decode(Market.self, forKey: .market)
|
|
costPrice = try c.decodeIfPresent(Double.self, forKey: .costPrice)
|
|
holdingShares = try c.decodeIfPresent(Double.self, forKey: .holdingShares)
|
|
}
|
|
}
|