ignore option mark to market in schwab
This commit is contained in:
parent
74fa8d91fa
commit
fb1178b5ca
1 changed files with 52 additions and 12 deletions
|
|
@ -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", .{});
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue