phase-8:server code enhancements.

This commit is contained in:
Kazuma
2026-06-05 22:44:04 -04:00
committed by Kazuma
parent 7cb1b03fd5
commit 7f2108129a
15 changed files with 781 additions and 94 deletions
+15 -17
View File
@@ -25,7 +25,7 @@ export class PortfolioAdvisor {
resultMap[t.replace(/\./g, '-')] = r;
}
const cryptoPrices = await this._cryptoPrices(holdings.filter((h) => h.type === 'crypto'));
const cryptoPrices = await this.cryptoPrices(holdings.filter((h) => h.type === 'crypto'));
return holdings.map((holding) => {
const type = (holding.type ?? 'stock').toLowerCase();
@@ -36,35 +36,35 @@ export class PortfolioAdvisor {
: (resultMap[holding.ticker.toUpperCase()]?.asset?.currentPrice ?? null);
return type === 'crypto'
? this._row(holding, price, source, '—', '—', '—', this._cryptoAdvice(holding, price))
: this._stockRow(holding, price, source, resultMap[holding.ticker.toUpperCase()]);
? this.row(holding, price, source, '—', '—', '—', this.cryptoAdvice(holding, price))
: this.stockRow(holding, price, source, resultMap[holding.ticker.toUpperCase()]);
});
}
private _stockRow(
private stockRow(
holding: PortfolioHolding,
price: number | null,
source: string,
result: AssetResult | undefined,
): AdviceRow {
if (!result) {
return this._row(holding, price, source, '—', '—', '—', {
return this.row(holding, price, source, '—', '—', '—', {
action: '⚪ Not screened',
reason: 'No screener data available — Yahoo Finance may not support this ticker.',
});
}
return this._row(
return this.row(
holding,
price,
source,
result.signal,
result.inflated.label,
result.fundamental.label,
this._advice(result.signal, holding, price),
this.advice(result.signal, holding, price),
);
}
private _row(
private row(
holding: PortfolioHolding,
currentPrice: number | null,
source: string,
@@ -73,7 +73,7 @@ export class PortfolioAdvisor {
fundamental: string,
{ action, reason }: AdviceOutput,
): AdviceRow {
const { marketValue, totalCost, gainLossPct } = this._position(holding, currentPrice);
const { marketValue, totalCost, gainLossPct } = this.position(holding, currentPrice);
return {
ticker: holding.ticker,
type: holding.type ?? 'stock',
@@ -92,7 +92,7 @@ export class PortfolioAdvisor {
};
}
private _position(holding: PortfolioHolding, currentPrice: number | null): PositionCalc {
private position(holding: PortfolioHolding, currentPrice: number | null): PositionCalc {
return {
totalCost: (holding.costBasis * holding.shares).toFixed(2),
marketValue: currentPrice != null ? (currentPrice * holding.shares).toFixed(2) : null,
@@ -103,8 +103,8 @@ export class PortfolioAdvisor {
};
}
private _cryptoAdvice(holding: PortfolioHolding, price: number | null): AdviceOutput {
const { gainLossPct } = this._position(holding, price);
private cryptoAdvice(holding: PortfolioHolding, price: number | null): AdviceOutput {
const { gainLossPct } = this.position(holding, price);
const g = parseFloat(gainLossPct ?? 'NaN');
if (gainLossPct == null)
return {
@@ -127,8 +127,8 @@ export class PortfolioAdvisor {
};
}
private _advice(signal: Signal, holding: PortfolioHolding, price: number | null): AdviceOutput {
const { gainLossPct } = this._position(holding, price);
private advice(signal: Signal, holding: PortfolioHolding, price: number | null): AdviceOutput {
const { gainLossPct } = this.position(holding, price);
const gain = parseFloat(gainLossPct ?? '0');
switch (signal) {
case SIGNAL.STRONG_BUY:
@@ -164,9 +164,7 @@ export class PortfolioAdvisor {
}
}
private async _cryptoPrices(
holdings: PortfolioHolding[],
): Promise<Record<string, number | null>> {
private async cryptoPrices(holdings: PortfolioHolding[]): Promise<Record<string, number | null>> {
const prices: Record<string, number | null> = {};
for (const h of holdings) {
try {