phase-1: optimize code
This commit is contained in:
@@ -0,0 +1,92 @@
|
||||
import { test } from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import { PortfolioAdvisor } from '../src/finance/PortfolioAdvisor.js';
|
||||
import { SIGNAL } from '../src/config/constants.js';
|
||||
|
||||
const advisor = new PortfolioAdvisor();
|
||||
|
||||
test('_position: computes gain/loss correctly', () => {
|
||||
const pos = advisor._position({ costBasis: 100, shares: 10 }, 150);
|
||||
assert.equal(pos.gainLossPct, '50.0');
|
||||
assert.equal(pos.marketValue, '1500.00');
|
||||
assert.equal(pos.totalCost, '1000.00');
|
||||
});
|
||||
|
||||
test('_position: returns null gainLoss when price unavailable', () => {
|
||||
const pos = advisor._position({ costBasis: 100, shares: 10 }, null);
|
||||
assert.equal(pos.gainLossPct, null);
|
||||
assert.equal(pos.marketValue, null);
|
||||
});
|
||||
|
||||
test('_advice: Strong Buy → Hold & Add', () => {
|
||||
const { action } = advisor._advice(SIGNAL.STRONG_BUY, { costBasis: 100, shares: 10 }, 150);
|
||||
assert.equal(action, '🟢 Hold & Add');
|
||||
});
|
||||
|
||||
test('_advice: Avoid + loss → Sell (Cut Loss)', () => {
|
||||
const { action } = advisor._advice(SIGNAL.AVOID, { costBasis: 150, shares: 10 }, 100);
|
||||
assert.equal(action, '🔴 Sell (Cut Loss)');
|
||||
});
|
||||
|
||||
test('_advice: Avoid + profit → Sell (Take Profits)', () => {
|
||||
const { action } = advisor._advice(SIGNAL.AVOID, { costBasis: 100, shares: 10 }, 150);
|
||||
assert.equal(action, '🔴 Sell (Take Profits)');
|
||||
});
|
||||
|
||||
test('_advice: Speculation + >20% gain → Reduce Position', () => {
|
||||
const { action } = advisor._advice(SIGNAL.SPECULATION, { costBasis: 100, shares: 10 }, 125);
|
||||
assert.equal(action, '🟠 Reduce Position');
|
||||
});
|
||||
|
||||
test('_cryptoAdvice: no price → No price data', () => {
|
||||
const { action } = advisor._cryptoAdvice({ costBasis: 100, shares: 1 }, null);
|
||||
assert.equal(action, '⚪ No price data');
|
||||
});
|
||||
|
||||
test('_cryptoAdvice: >100% gain → Consider taking profits', () => {
|
||||
const { action } = advisor._cryptoAdvice({ costBasis: 10000, shares: 1 }, 25000);
|
||||
assert.equal(action, '🟠 Consider taking profits');
|
||||
});
|
||||
|
||||
// ── Result map dot-notation normalisation (BRK.B / BRK-B) ───────────────────
|
||||
|
||||
test('advise: BRK-B screener result matches BRK.B holding', async () => {
|
||||
const mockResult = {
|
||||
asset: { ticker: 'BRK-B', currentPrice: 500 },
|
||||
signal: SIGNAL.STRONG_BUY,
|
||||
inflated: { label: '🟢 BUY (High Conviction)' },
|
||||
fundamental: { label: '🟢 BUY (High Conviction)' },
|
||||
};
|
||||
const screenedResults = { STOCK: [mockResult], ETF: [], BOND: [] };
|
||||
const holding = {
|
||||
ticker: 'BRK.B',
|
||||
shares: 1,
|
||||
costBasis: 400,
|
||||
type: 'stock',
|
||||
source: 'Robinhood',
|
||||
};
|
||||
|
||||
const advice = await advisor.advise([holding], screenedResults);
|
||||
// Should match and return a real signal, not "Not screened"
|
||||
assert.equal(advice[0].signal, SIGNAL.STRONG_BUY);
|
||||
});
|
||||
|
||||
test('advise: BRK.B screener result matches BRK-B holding', async () => {
|
||||
const mockResult = {
|
||||
asset: { ticker: 'BRK.B', currentPrice: 500 },
|
||||
signal: SIGNAL.STRONG_BUY,
|
||||
inflated: { label: '🟢 BUY (High Conviction)' },
|
||||
fundamental: { label: '🟢 BUY (High Conviction)' },
|
||||
};
|
||||
const screenedResults = { STOCK: [mockResult], ETF: [], BOND: [] };
|
||||
const holding = {
|
||||
ticker: 'BRK-B',
|
||||
shares: 1,
|
||||
costBasis: 400,
|
||||
type: 'stock',
|
||||
source: 'Robinhood',
|
||||
};
|
||||
|
||||
const advice = await advisor.advise([holding], screenedResults);
|
||||
assert.equal(advice[0].signal, SIGNAL.STRONG_BUY);
|
||||
});
|
||||
Reference in New Issue
Block a user