@@ -15,6 +15,7 @@ package ibctlrealtime
1515import (
1616 "context"
1717 "fmt"
18+ "log/slog"
1819
1920 datav1 "github.com/bufdev/ibctl/internal/gen/proto/go/ibctl/data/v1"
2021 "github.com/bufdev/ibctl/internal/ibctl/ibctlconfig"
@@ -27,6 +28,10 @@ import (
2728// assetCategoryCash is the IBKR asset category for cash/FX positions.
2829const assetCategoryCash = "CASH"
2930
31+ // assetCategoryBond is the IBKR asset category for bond positions.
32+ // Yahoo Finance does not provide quotes for individual corporate bonds.
33+ const assetCategoryBond = "BOND"
34+
3035// RealTimeData holds real-time prices and FX rates fetched from Yahoo Finance.
3136// All values are in micros (6 decimal places). This data is transient and not persisted.
3237type RealTimeData struct {
@@ -42,14 +47,15 @@ type RealTimeData struct {
4247// Returns an error if the API is unreachable or any symbol/FX pair cannot be resolved.
4348func ApplyOverrides (
4449 ctx context.Context ,
50+ logger * slog.Logger ,
4551 positions []* datav1.Position ,
4652 fxStore * ibctlfxrates.Store ,
4753 config * ibctlconfig.Config ,
4854 baseCurrency string ,
4955 todayDate string ,
5056) error {
5157 yahooClient := yahoofinance .NewClient ()
52- realTimeData , err := FetchData (ctx , yahooClient , positions , config , baseCurrency )
58+ realTimeData , err := FetchData (ctx , logger , yahooClient , positions , config , baseCurrency )
5359 if err != nil {
5460 return err
5561 }
@@ -73,6 +79,7 @@ func ApplyOverrides(
7379// resolved.
7480func FetchData (
7581 ctx context.Context ,
82+ logger * slog.Logger ,
7683 yahooClient yahoofinance.Client ,
7784 positions []* datav1.Position ,
7885 config * ibctlconfig.Config ,
@@ -83,8 +90,10 @@ func FetchData(
8390 ibkrToYahoo := make (map [string ]string )
8491 currencies := make (map [string ]struct {})
8592 for _ , position := range positions {
86- // Skip cash positions — they have no equity quote.
87- if position .GetAssetCategory () == assetCategoryCash {
93+ // Skip cash and bond positions — cash has no equity quote, and Yahoo Finance
94+ // does not provide quotes for individual corporate bonds.
95+ assetCategory := position .GetAssetCategory ()
96+ if assetCategory == assetCategoryCash || assetCategory == assetCategoryBond {
8897 continue
8998 }
9099 ibkrSymbol := position .GetSymbol ()
@@ -136,21 +145,31 @@ func FetchData(
136145 for _ , quote := range quotes {
137146 yahooQuoteMap [quote .Symbol ] = quote
138147 }
139- // Verify all requested equity symbols were resolved and convert to micros.
148+ // Convert resolved equity symbols to micros. Symbols not found on Yahoo Finance
149+ // are skipped — positions without an override keep their cached IBKR price.
140150 quoteMicros := make (map [string ]int64 , len (ibkrToYahoo ))
141151 for ibkrSymbol , yahooSymbol := range ibkrToYahoo {
142152 quote , ok := yahooQuoteMap [yahooSymbol ]
143153 if ! ok {
144- return nil , fmt .Errorf ("symbol %q not found on Yahoo Finance (queried as %q), add a mapping under realtime_symbols in ibctl.yaml" , ibkrSymbol , yahooSymbol )
154+ logger .Warn ("real-time quote not found, using cached price" ,
155+ slog .String ("symbol" , ibkrSymbol ),
156+ slog .String ("yahoo_symbol" , yahooSymbol ),
157+ )
158+ continue
145159 }
146160 quoteMicros [ibkrSymbol ] = mathpb .ToMicros (quote .RegularMarketPrice )
147161 }
148- // Verify all requested FX pairs were resolved and convert to micros.
162+ // Convert resolved FX pairs to micros. Pairs not found on Yahoo Finance
163+ // are skipped — the FX store falls back to its cached historical rate.
149164 fxRateMicros := make (map [string ]int64 , len (fxYahooToPair ))
150165 for yahooFXSymbol , pairKey := range fxYahooToPair {
151166 quote , ok := yahooQuoteMap [yahooFXSymbol ]
152167 if ! ok {
153- return nil , fmt .Errorf ("FX pair %q not found on Yahoo Finance (queried as %q)" , pairKey , yahooFXSymbol )
168+ logger .Warn ("real-time FX rate not found, using cached rate" ,
169+ slog .String ("pair" , pairKey ),
170+ slog .String ("yahoo_symbol" , yahooFXSymbol ),
171+ )
172+ continue
154173 }
155174 fxRateMicros [pairKey ] = mathpb .ToMicros (quote .RegularMarketPrice )
156175 }
0 commit comments