Fix three compiler errors
1. RecommendationEngine: daysToExp used before declaration Move daysToExp/T calculation above selectBestContract() call so the variable exists when it is passed as daysToExpiry parameter. 2. YahooFinanceClient: Swift 6 actor-isolation on Decodable conformances nonisolated methods on an actor still inherit actor isolation in Swift 6. Fix: lift all three decode helpers (yfDecodeChart, yfDecodeOptions, yfMakeContract) to file-scope free functions completely outside the actor. File-scope functions are naturally nonisolated. Also renamed the private JSON model structs to YFChartResponse / YFOptionsResponse to avoid any name collisions now that they live at file scope. 3. LogTradeSheet: unused 'let s = strike' binding Replace 'let s = strike' with 'strike != nil' since the bound value was never referenced inside the if-body. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -49,6 +49,13 @@ enum RecommendationEngine {
|
|||||||
let currentPrice = history.currentPrice
|
let currentPrice = history.currentPrice
|
||||||
guard currentPrice > 0 else { return nil }
|
guard currentPrice > 0 else { return nil }
|
||||||
|
|
||||||
|
// T in years — must be computed before selectBestContract so it can use real DTE
|
||||||
|
let daysToExp = max(
|
||||||
|
Calendar.current.dateComponents([.day], from: Date(), to: expiration).day ?? 1,
|
||||||
|
1
|
||||||
|
)
|
||||||
|
let T = Double(daysToExp) / 365.0
|
||||||
|
|
||||||
let contracts = strategy == "covered_call" ? chain.calls : chain.puts
|
let contracts = strategy == "covered_call" ? chain.calls : chain.puts
|
||||||
guard let best = selectBestContract(
|
guard let best = selectBestContract(
|
||||||
contracts: contracts,
|
contracts: contracts,
|
||||||
@@ -57,13 +64,6 @@ enum RecommendationEngine {
|
|||||||
signal: signal,
|
signal: signal,
|
||||||
daysToExpiry: daysToExp
|
daysToExpiry: daysToExp
|
||||||
) else { return nil }
|
) else { return nil }
|
||||||
|
|
||||||
// T in years
|
|
||||||
let daysToExp = max(
|
|
||||||
Calendar.current.dateComponents([.day], from: Date(), to: expiration).day ?? 1,
|
|
||||||
1
|
|
||||||
)
|
|
||||||
let T = Double(daysToExp) / 365.0
|
|
||||||
let isCall = strategy == "covered_call"
|
let isCall = strategy == "covered_call"
|
||||||
|
|
||||||
let delta = SignalEngine.bsDelta(
|
let delta = SignalEngine.bsDelta(
|
||||||
|
|||||||
@@ -75,18 +75,6 @@ actor YahooFinanceClient {
|
|||||||
private func set(_ value: Any, key: String) { cache[key] = (value, Date()) }
|
private func set(_ value: Any, key: String) { cache[key] = (value, Date()) }
|
||||||
func clearCache() { cache.removeAll() }
|
func clearCache() { cache.removeAll() }
|
||||||
|
|
||||||
// MARK: - nonisolated decode helpers
|
|
||||||
// Placed outside actor isolation so Swift 6 doesn't flag the Decodable
|
|
||||||
// conformance as being used in an actor-isolated context.
|
|
||||||
|
|
||||||
nonisolated private func parseChart(_ data: Data) throws -> ChartResponse {
|
|
||||||
try JSONDecoder().decode(ChartResponse.self, from: data)
|
|
||||||
}
|
|
||||||
|
|
||||||
nonisolated private func parseOptions(_ data: Data) throws -> OptionsResponse {
|
|
||||||
try JSONDecoder().decode(OptionsResponse.self, from: data)
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - Price history
|
// MARK: - Price history
|
||||||
|
|
||||||
func priceHistory(ticker: String) async throws -> PriceHistory {
|
func priceHistory(ticker: String) async throws -> PriceHistory {
|
||||||
@@ -98,7 +86,8 @@ actor YahooFinanceClient {
|
|||||||
else { throw YFError.badURL }
|
else { throw YFError.badURL }
|
||||||
|
|
||||||
let (data, _) = try await session.data(from: url)
|
let (data, _) = try await session.data(from: url)
|
||||||
let resp = try parseChart(data)
|
// Decode outside actor isolation via file-scope free function
|
||||||
|
let resp = try yfDecodeChart(data)
|
||||||
|
|
||||||
guard let r = resp.chart.result?.first,
|
guard let r = resp.chart.result?.first,
|
||||||
let q = r.indicators.quote.first else { throw YFError.noData }
|
let q = r.indicators.quote.first else { throw YFError.noData }
|
||||||
@@ -137,7 +126,7 @@ actor YahooFinanceClient {
|
|||||||
else { throw YFError.badURL }
|
else { throw YFError.badURL }
|
||||||
|
|
||||||
let (data, _) = try await session.data(from: url)
|
let (data, _) = try await session.data(from: url)
|
||||||
let resp = try parseOptions(data)
|
let resp = try yfDecodeOptions(data)
|
||||||
|
|
||||||
guard let r = resp.optionChain.result?.first else { throw YFError.noData }
|
guard let r = resp.optionChain.result?.first else { throw YFError.noData }
|
||||||
let dates = r.expirationDates.map { Date(timeIntervalSince1970: Double($0)) }
|
let dates = r.expirationDates.map { Date(timeIntervalSince1970: Double($0)) }
|
||||||
@@ -170,33 +159,19 @@ actor YahooFinanceClient {
|
|||||||
else { throw YFError.badURL }
|
else { throw YFError.badURL }
|
||||||
|
|
||||||
let (data, _) = try await session.data(from: url)
|
let (data, _) = try await session.data(from: url)
|
||||||
let resp = try parseOptions(data)
|
let resp = try yfDecodeOptions(data)
|
||||||
|
|
||||||
guard let r = resp.optionChain.result?.first,
|
guard let r = resp.optionChain.result?.first,
|
||||||
let opts = r.options.first else { throw YFError.noData }
|
let opts = r.options.first else { throw YFError.noData }
|
||||||
|
|
||||||
let calls = opts.calls.compactMap { makeContract(from: $0) }
|
let calls = opts.calls.compactMap { yfMakeContract($0) }
|
||||||
let puts = opts.puts .compactMap { makeContract(from: $0) }
|
let puts = opts.puts .compactMap { yfMakeContract($0) }
|
||||||
|
|
||||||
let chain = OptionChain(expiration: expiration, calls: calls, puts: puts)
|
let chain = OptionChain(expiration: expiration, calls: calls, puts: puts)
|
||||||
set(chain, key: key)
|
set(chain, key: key)
|
||||||
return chain
|
return chain
|
||||||
}
|
}
|
||||||
|
|
||||||
/// nonisolated factory so the OptionsResponse.YFOption type is not
|
|
||||||
/// referenced inside actor-isolated code (avoids Swift 6 conformance error).
|
|
||||||
nonisolated private func makeContract(from yf: OptionsResponse.YFOption) -> OptionContract? {
|
|
||||||
guard let bid = yf.bid, let ask = yf.ask, bid >= 0, ask >= 0 else { return nil }
|
|
||||||
return OptionContract(
|
|
||||||
strike: yf.strike,
|
|
||||||
bid: bid,
|
|
||||||
ask: ask,
|
|
||||||
impliedVolatility: yf.impliedVolatility ?? 0.3,
|
|
||||||
volume: yf.volume ?? 0,
|
|
||||||
openInterest: yf.openInterest ?? 0
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - Earnings date
|
// MARK: - Earnings date
|
||||||
|
|
||||||
func nextEarningsDate(ticker: String) async throws -> Date? {
|
func nextEarningsDate(ticker: String) async throws -> Date? {
|
||||||
@@ -228,10 +203,33 @@ actor YahooFinanceClient {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Private JSON models
|
// MARK: - File-scope free functions (naturally nonisolated — no actor context)
|
||||||
// Defined at file scope (outside the actor) as plain Decodable value types.
|
// Swift 6 requires Decodable conformances used in nonisolated code to themselves
|
||||||
|
// be nonisolated. Placing the decode calls here, outside any actor, satisfies that.
|
||||||
|
|
||||||
private struct ChartResponse: Decodable {
|
private func yfDecodeChart(_ data: Data) throws -> YFChartResponse {
|
||||||
|
try JSONDecoder().decode(YFChartResponse.self, from: data)
|
||||||
|
}
|
||||||
|
|
||||||
|
private func yfDecodeOptions(_ data: Data) throws -> YFOptionsResponse {
|
||||||
|
try JSONDecoder().decode(YFOptionsResponse.self, from: data)
|
||||||
|
}
|
||||||
|
|
||||||
|
private func yfMakeContract(_ yf: YFOptionsResponse.YFOption) -> OptionContract? {
|
||||||
|
guard let bid = yf.bid, let ask = yf.ask, bid >= 0, ask >= 0 else { return nil }
|
||||||
|
return OptionContract(
|
||||||
|
strike: yf.strike,
|
||||||
|
bid: bid,
|
||||||
|
ask: ask,
|
||||||
|
impliedVolatility: yf.impliedVolatility ?? 0.3,
|
||||||
|
volume: yf.volume ?? 0,
|
||||||
|
openInterest: yf.openInterest ?? 0
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Private JSON models
|
||||||
|
|
||||||
|
private struct YFChartResponse: Decodable {
|
||||||
let chart: Wrapper
|
let chart: Wrapper
|
||||||
struct Wrapper: Decodable { let result: [Result]? }
|
struct Wrapper: Decodable { let result: [Result]? }
|
||||||
struct Result: Decodable {
|
struct Result: Decodable {
|
||||||
@@ -247,7 +245,7 @@ private struct ChartResponse: Decodable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private struct OptionsResponse: Decodable {
|
private struct YFOptionsResponse: Decodable {
|
||||||
let optionChain: Wrapper
|
let optionChain: Wrapper
|
||||||
struct Wrapper: Decodable { let result: [Result]? }
|
struct Wrapper: Decodable { let result: [Result]? }
|
||||||
struct Result: Decodable {
|
struct Result: Decodable {
|
||||||
@@ -258,7 +256,6 @@ private struct OptionsResponse: Decodable {
|
|||||||
let calls: [YFOption]
|
let calls: [YFOption]
|
||||||
let puts: [YFOption]
|
let puts: [YFOption]
|
||||||
}
|
}
|
||||||
// YFOption is a direct nested type of OptionsResponse, NOT of OptionData.
|
|
||||||
struct YFOption: Decodable {
|
struct YFOption: Decodable {
|
||||||
let strike: Double
|
let strike: Double
|
||||||
let bid: Double?
|
let bid: Double?
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ struct LogTradeSheet: View {
|
|||||||
.focused($focusedField, equals: .contracts)
|
.focused($focusedField, equals: .contracts)
|
||||||
}
|
}
|
||||||
|
|
||||||
if let p = premium, let s = strike, contracts > 0 {
|
if let p = premium, strike != nil, contracts > 0 {
|
||||||
Section("") {
|
Section("") {
|
||||||
HStack {
|
HStack {
|
||||||
Text("Total credit received")
|
Text("Total credit received")
|
||||||
|
|||||||
Reference in New Issue
Block a user