+
Bitcoin prices
+
+ Upload a CSV of BTC/USD prices (a date column and a close/price column). Investors
+ then see their capital in bitcoin terms; paid-in is valued at the price on each
+ fund's close date — set that on the fund's overview page.
+
+ {status && status.count > 0 && (
+
+ {status.count} prices on file
+ {status.first_date && status.last_date && (
+ <> · {formatDate(status.first_date)} → {formatDate(status.last_date)}>
+ )}
+ {status.latest_price_cents != null && (
+ <> · latest ${(status.latest_price_cents / 100).toLocaleString()}>
+ )}
+
+ )}
+
+ setFile(e.target.files?.[0] ?? null)}
+ className="text-sm"
+ />
+
+
+ {error &&
{error}
}
+ {msg &&
{msg}
}
+
+ );
+}
diff --git a/frontend/src/portal/InvestorPortalView.tsx b/frontend/src/portal/InvestorPortalView.tsx
index 208091a..c9a4a8b 100644
--- a/frontend/src/portal/InvestorPortalView.tsx
+++ b/frontend/src/portal/InvestorPortalView.tsx
@@ -5,6 +5,13 @@ import CapitalChart, { type CapitalPoint } from "../components/CapitalChart";
const pct = (p: number) => `${p >= 0 ? "+" : "−"}${Math.abs(p).toFixed(1)}%`;
+// Dollars-at-cents ÷ BTC-price-at-cents, formatted to a sensible precision for the size.
+const formatBtc = (usdCents: number, priceCents: number) => {
+ const btc = usdCents / priceCents;
+ const dp = btc >= 10 ? 2 : btc >= 1 ? 3 : 4;
+ return `₿${btc.toLocaleString(undefined, { minimumFractionDigits: dp, maximumFractionDigits: dp })}`;
+};
+
// The investor-facing portal, rendered purely from data. Used by the investor's own home
// (InvestorHome) and by the admin read-only Investor View, so both show exactly the same thing.
export default function InvestorPortalView({
@@ -52,6 +59,7 @@ export default function InvestorPortalView({
exitedCount={exitedPositions}
/>
)}
+