From fb1178b5ca611894ea2bb156fa9d27e730a71f46 Mon Sep 17 00:00:00 2001 From: Emil Lerch Date: Sat, 25 Apr 2026 09:07:13 -0700 Subject: [PATCH] ignore option mark to market in schwab --- src/commands/audit.zig | 64 ++++++++++++++++++++++++++++++++++-------- 1 file changed, 52 insertions(+), 12 deletions(-) diff --git a/src/commands/audit.zig b/src/commands/audit.zig index e7b926e..8d526e8 100644 --- a/src/commands/audit.zig +++ b/src/commands/audit.zig @@ -710,6 +710,7 @@ pub const SymbolComparison = struct { shares_delta: ?f64, value_delta: ?f64, is_cash: bool, + is_option: bool, only_in_brokerage: bool, only_in_portfolio: bool, }; @@ -723,6 +724,7 @@ pub const AccountComparison = struct { portfolio_total: f64, brokerage_total: f64, total_delta: f64, + option_value_delta: f64, has_discrepancies: bool, }; @@ -769,6 +771,7 @@ pub fn compareAccounts( var portfolio_total: f64 = 0; var brokerage_total: f64 = 0; + var option_value_delta: f64 = 0; var has_discrepancies = false; // Track which portfolio symbols we've matched @@ -793,6 +796,7 @@ pub fn compareAccounts( .shares_delta = if (bp.quantity) |q| q else null, .value_delta = bp.current_value, .is_cash = bp.is_cash, + .is_option = false, .only_in_brokerage = true, .only_in_portfolio = false, }); @@ -804,6 +808,7 @@ pub fn compareAccounts( var pf_shares: f64 = 0; var pf_value: f64 = 0; var pf_price: ?f64 = null; + var is_option = false; if (bp.is_cash) { pf_shares = portfolio.cashForAccount(portfolio_acct_name.?); @@ -842,6 +847,7 @@ pub fn compareAccounts( pf_shares += lot.shares; pf_value += @abs(lot.shares) * lot.open_price * lot.multiplier; pf_price = lot.open_price * lot.multiplier; + is_option = true; }, else => {}, } @@ -859,7 +865,14 @@ pub fn compareAccounts( const shares_match = if (shares_delta) |d| @abs(d) < 0.01 else true; const value_match = if (value_delta) |d| @abs(d) < 1.0 else true; - if (!shares_match or !value_match) has_discrepancies = true; + // Option value deltas are expected (cost basis vs mark-to-market) + // — track them separately rather than flagging as discrepancies + if (is_option) { + if (value_delta) |d| option_value_delta += d; + if (!shares_match) has_discrepancies = true; + } else { + if (!shares_match or !value_match) has_discrepancies = true; + } const br_price: ?f64 = if (bp.quantity) |q| if (bp.current_value) |v| if (q != 0) v / q else null else null else null; @@ -874,6 +887,7 @@ pub fn compareAccounts( .shares_delta = shares_delta, .value_delta = value_delta, .is_cash = bp.is_cash, + .is_option = is_option, .only_in_brokerage = pf_shares == 0 and pf_value == 0, .only_in_portfolio = false, }); @@ -906,6 +920,7 @@ pub fn compareAccounts( .shares_delta = null, .value_delta = null, .is_cash = false, + .is_option = false, .only_in_brokerage = false, .only_in_portfolio = true, }); @@ -962,6 +977,7 @@ pub fn compareAccounts( .shares_delta = null, .value_delta = null, .is_cash = is_cd, + .is_option = !is_cd, .only_in_brokerage = false, .only_in_portfolio = true, }); @@ -977,6 +993,7 @@ pub fn compareAccounts( .portfolio_total = portfolio_total, .brokerage_total = brokerage_total, .total_delta = brokerage_total - portfolio_total, + .option_value_delta = option_value_delta, .has_discrepancies = has_discrepancies, }); } @@ -997,6 +1014,7 @@ fn displayResults(results: []const AccountComparison, color: bool, out: *std.Io. var total_portfolio: f64 = 0; var total_brokerage: f64 = 0; + var total_option_delta: f64 = 0; var discrepancy_count: usize = 0; for (results) |acct| { @@ -1096,6 +1114,10 @@ fn displayResults(results: []const AccountComparison, color: bool, out: *std.Io. } } + // Options: shares match is sufficient — value delta is expected + // (cost basis vs mark-to-market) and not actionable + if (cmp.is_option) break :blk "Option"; + // Shares match — show value delta (stale price) if any, muted if (cmp.value_delta) |d| { if (@abs(d) >= 1.0) { @@ -1151,23 +1173,32 @@ fn displayResults(results: []const AccountComparison, color: bool, out: *std.Io. }); try cli.reset(out, color); - const delta = acct.total_delta; - if (@abs(delta) < 1.0) { - try out.print("\n", .{}); + const adj_delta = acct.total_delta - acct.option_value_delta; + if (@abs(adj_delta) < 1.0) { + // no delta text needed } else { - const sign: []const u8 = if (delta >= 0) "+" else "-"; - if (delta >= 0) { + const sign: []const u8 = if (adj_delta >= 0) "+" else "-"; + if (adj_delta >= 0) { try cli.setFg(out, color, cli.CLR_NEGATIVE); } else { try cli.setFg(out, color, cli.CLR_POSITIVE); } - try out.print("Delta {s}{s}\n", .{ sign, fmt.fmtMoneyAbs(&delta_buf, @abs(delta)) }); + try out.print("Delta {s}{s}", .{ sign, fmt.fmtMoneyAbs(&delta_buf, @abs(adj_delta)) }); try cli.reset(out, color); } - try out.print("\n", .{}); + + if (@abs(acct.option_value_delta) >= 1.0) { + var opt_delta_buf: [24]u8 = undefined; + const opt_sign: []const u8 = if (acct.option_value_delta >= 0) "+" else "-"; + try cli.setFg(out, color, cli.CLR_MUTED); + try out.print(" (options {s}{s})", .{ opt_sign, fmt.fmtMoneyAbs(&opt_delta_buf, @abs(acct.option_value_delta)) }); + try cli.reset(out, color); + } + try out.print("\n\n", .{}); total_portfolio += acct.portfolio_total; total_brokerage += acct.brokerage_total; + total_option_delta += acct.option_value_delta; } // Grand totals @@ -1175,6 +1206,7 @@ fn displayResults(results: []const AccountComparison, color: bool, out: *std.Io. var br_grand_buf: [24]u8 = undefined; var grand_delta_buf: [24]u8 = undefined; const grand_delta = total_brokerage - total_portfolio; + const grand_adj_delta = grand_delta - total_option_delta; try cli.setBold(out, color); try out.print(" Total: portfolio {s} brokerage {s}", .{ @@ -1183,16 +1215,24 @@ fn displayResults(results: []const AccountComparison, color: bool, out: *std.Io. }); try cli.reset(out, color); - if (@abs(grand_delta) < 1.0) { + if (@abs(grand_adj_delta) < 1.0) { // no delta text needed } else { - const sign: []const u8 = if (grand_delta >= 0) "+" else "-"; - if (grand_delta >= 0) { + const sign: []const u8 = if (grand_adj_delta >= 0) "+" else "-"; + if (grand_adj_delta >= 0) { try cli.setFg(out, color, cli.CLR_NEGATIVE); } else { try cli.setFg(out, color, cli.CLR_POSITIVE); } - try out.print(" delta {s}{s}", .{ sign, fmt.fmtMoneyAbs(&grand_delta_buf, @abs(grand_delta)) }); + try out.print(" delta {s}{s}", .{ sign, fmt.fmtMoneyAbs(&grand_delta_buf, @abs(grand_adj_delta)) }); + try cli.reset(out, color); + } + + if (@abs(total_option_delta) >= 1.0) { + var opt_grand_buf: [24]u8 = undefined; + const opt_sign: []const u8 = if (total_option_delta >= 0) "+" else "-"; + try cli.setFg(out, color, cli.CLR_MUTED); + try out.print(" (options {s}{s})", .{ opt_sign, fmt.fmtMoneyAbs(&opt_grand_buf, @abs(total_option_delta)) }); try cli.reset(out, color); } try out.print("\n", .{});