Commit Graph

9 Commits

Author SHA1 Message Date
b140a1163d Fix Yahoo Finance auth (crumb/cookie), add error UI, disable autocorrect on tickers
Yahoo Finance auth fix (root cause of empty Setups tab):
- Options endpoint has required cookie+crumb auth since 2024; our old
  client sent no auth and got 401, which try? silently turned into nil.
- New flow in ensureAuth(): (1) GET finance.yahoo.com/quote/AAPL/ with
  a desktop Chrome User-Agent to seed the A1S session cookie into
  URLSession.shared cookie storage, (2) GET /v1/test/getcrumb with that
  cookie, cache the crumb for 1 hour. All options API calls now append
  ?crumb= and automatically retry once on 401.
- Switched User-Agent from mobile Safari to desktop Chrome — Yahoo Finance
  returns different (broken) auth behaviour for the mobile UA on options.
- Price history endpoint still works without crumb (200 confirmed) so it
  uses a plain request; only the options endpoints go through fetch().

Error surfacing:
- RecommendationsViewModel.refresh() now uses do/catch instead of try?
  so failures are printed to console and surfaced via vm.error
- RecommendationsView shows a wifi-exclamationmark error card with a
  Retry button when vm.error is set

Autocorrect disabled:
- Added .autocorrectionDisabled() to the ticker TextField in
  AddPositionSheet (PortfolioView) and LogTradeSheet

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-10 08:55:31 -04:00
7b3c5691e1 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>
2026-04-10 08:46:33 -04:00
86ad024252 Portfolio edit/sort + fix Setups tab showing no recommendations
Portfolio:
- Alphabetize list on every add/edit (sort by ticker after upsert)
- Add DataStore.updatePortfolioPosition() and PortfolioViewModel.update()
- Add EditPositionSheet: tap any row to edit shares, cost basis, or delete
  with a confirmation dialog; ticker is read-only in edit mode
- Swipe-to-delete still works as before

Setups tab (three bugs fixed):
1. Auto-refresh on first open — .task now triggers vm.refresh() when
   DataStore has tickers but no cached recommendations, so the tab
   populates immediately without requiring a manual ↻ tap
2. Loading state — replaced the never-set vm.isLoading check with
   vm.isRefreshing so the spinner actually shows during fetch
3. Strike selection used a hardcoded T=30 days for all time horizons,
   causing weekly/1DTE options to pick strikes too far OTM (near-zero
   delta at actual expiry → zero bid → filtered out → no results).
   selectBestContract() now receives real daysToExpiry, relaxes the
   delta floor for very short expirations (≤3 days: 0.05 min vs 0.10),
   and adds a closest-to-ATM fallback when the delta filter finds nothing.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-10 08:43:43 -04:00
d153296ac1 Fix YahooFinanceClient Swift 6 actor isolation and type-path errors
Two bugs:

1. Swift 6 strict concurrency: JSONDecoder().decode() inside actor-isolated
   methods triggers "Main actor-isolated conformance cannot be used in
   actor-isolated context". Fix: move all Decodable usage into nonisolated
   helper methods (parseChart, parseOptions, makeContract) so the conformance
   is invoked outside the actor's isolation boundary.

2. Wrong nested type path: OptionsResponse.OptionData.YFOption does not exist;
   YFOption is nested directly under OptionsResponse. Fix: reference
   OptionsResponse.YFOption everywhere and remove the old private extension
   in favour of the nonisolated makeContract(from:) factory method.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-09 16:29:36 -04:00
be6cd86f11 Fix: @StateObject → @ObservedObject for DataStore singleton in ContentView
@StateObject is for objects SwiftUI creates and owns. DataStore.shared is
an existing singleton, so @ObservedObject is the correct wrapper.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-09 16:24:41 -04:00
2c595876f1 Pivot to fully client-side architecture — remove backend dependency
- Add Services/YahooFinanceClient.swift: Swift actor wrapping Yahoo Finance
  unofficial APIs (price history, option chain, expirations, earnings date)
  with 15-min in-memory cache
- Add Services/SignalEngine.swift: on-device IV Rank (rolling HV), SMA-50/200,
  swing-level pivot support/resistance, Black-Scholes delta/theta, signal
  strength scoring, and SHA-256 signal hash for change detection
- Add Services/RecommendationEngine.swift: strike selection (delta 0.15–0.40
  target 0.25), expiration mapping per horizon (0DTE/1DTE/weekly/monthly),
  rationale builder
- Add Services/DataStore.swift: @MainActor ObservableObject persisting all app
  state as JSON files in the documents directory (UUID-keyed, no server IDs)
- Add Services/BackgroundRefreshManager.swift: BGAppRefreshTask registration +
  foreground 15-min Timer; runs signal check per open position and fires local
  notifications when hash changes or delta/profit thresholds are crossed
- Add Services/NotificationService.swift: UNUserNotificationCenter local
  notification helper + badge count management
- Rewrite all 5 ViewModels to use DataStore + local services (no APIClient)
- Update Models to use UUID ids; remove snake_case CodingKeys (no network layer)
- Simplify AppDelegate: remove APNs, register BGTaskScheduler, start/stop timer
- Simplify NotificationPermissions/Handler: local notifications only, UUID
  position deep-link
- Update Info.plist: replace remote-notification with fetch+processing background
  modes, add BGTaskSchedulerPermittedIdentifiers
- Gut APIClient.swift and Endpoints.swift (no longer used)
- Update all Views to match new sync/async patterns and UUID-based models

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-09 16:13:20 -04:00
080f10f1c5 Fix all build errors in Xcode-managed nested source files
The Xcode project was created inside the existing folder structure,
producing a second copy of all source files at the deeper nested path
that Xcode actually compiles. All fixes are now applied to both paths.

Changes:
- Add `import Combine` to nested ViewModels (Portfolio, Recommendations,
  Positions, Alerts) and NotificationHandler — required for @Published
- Add `import UIKit` to NotificationPermissions — UIApplication is UIKit
- Rewrite APIClient to use `(any Encodable)?` instead of invalid
  `(some Encodable)?` syntax; add encodeAny() helper to open existential
  for JSONEncoder; remove private EmptyBody type
- Replace all `body: Optional<String>.none` / `Optional<EmptyBody>.none`
  call sites with plain `nil` across all ViewModels and Views
- Sync all fixes between nested Xcode path and outer source path

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-09 15:51:09 -04:00
14d715ed14 Fix 25 Xcode build errors and 2 warnings
- Add `import Combine` to PortfolioViewModel, RecommendationsViewModel,
  PositionsViewModel, AlertsViewModel, and NotificationHandler — required
  for @Published and ObservableObject to resolve correctly in Swift
- Fix @MainActor isolation error in PositionDetailView: replace broken
  default-parameter init (PositionsViewModel() in sync context) with
  @StateObject private var localVM and an optional parentVM parameter
- Update OpenPositionsView call site to use new parentVM: label
- Fix var→let warning in PortfolioViewModel.add()
- Remove unused `old` variable in AppDelegate.registerDevice()

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-09 15:46:16 -04:00
b7d4e900cc Initial implementation of Options Sidekick
Full-stack iOS options trading assistant:
- Python FastAPI backend with SQLite, APScheduler (15-min position monitor),
  APNs push notifications, and yfinance market data integration
- Signal engine: IV Rank (rolling HV proxy), SMA-50/200, swing-based
  support/resistance, earnings detection, signal strength scoring and
  noise-resistant SHA hash for change detection
- Recommendation engine: covered call and cash-secured put strike/expiry
  selection across 0DTE, 1DTE, weekly, and monthly horizons
- REST API: /devices, /portfolio, /recommendations, /positions, /signals, /alerts
- iOS SwiftUI app (iOS 17+): dashboard, recommendations, trades, portfolio,
  and alerts tabs with push notification deep-linking
- Unit + integration tests for signal engine and API layer

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-09 14:38:25 -04:00