agentbnb 5.1.9 → 5.1.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-BTTL24TZ.js → chunk-EGUOAHCW.js} +1 -1
- package/dist/{chunk-VE3E4AMH.js → chunk-NLAWT4DT.js} +2 -2
- package/dist/{chunk-MNO4COST.js → chunk-WTXRY7R2.js} +3 -3
- package/dist/cli/index.js +9 -7
- package/dist/{conduct-WGTMQND5.js → conduct-6LKIJJKQ.js} +3 -3
- package/dist/{conduct-KM6ZNJGE.js → conduct-WU3VEXB6.js} +3 -3
- package/dist/{conductor-mode-OL2FNOYY.js → conductor-mode-Q4IIDY5E.js} +2 -2
- package/dist/{conductor-mode-VRO7TYW2.js → conductor-mode-ZMTFZGJP.js} +2 -2
- package/dist/index.js +2 -2
- package/dist/{request-YOWPXVLQ.js → request-VOXBFUOG.js} +4 -4
- package/dist/{server-WY7KWD3X.js → server-JVQW2TID.js} +2 -2
- package/dist/{service-coordinator-BXVY77J5.js → service-coordinator-EYRDTHL5.js} +2 -3
- package/dist/skills/agentbnb/bootstrap.js +4 -4
- package/openclaw.plugin.json +1 -1
- package/package.json +12 -18
- package/skills/agentbnb/SKILL.md +26 -1
- package/skills/agentbnb/install.sh +0 -0
- package/skills/deep-stock-analyst/package.json +0 -24
- package/skills/deep-stock-analyst/src/analysis/financial-health.ts +0 -167
- package/skills/deep-stock-analyst/src/analysis/sentiment.ts +0 -68
- package/skills/deep-stock-analyst/src/analysis/signal.ts +0 -188
- package/skills/deep-stock-analyst/src/analysis/technicals.ts +0 -318
- package/skills/deep-stock-analyst/src/analysis/utils.ts +0 -137
- package/skills/deep-stock-analyst/src/analysis/valuation.ts +0 -95
- package/skills/deep-stock-analyst/src/api/alpha-vantage.ts +0 -133
- package/skills/deep-stock-analyst/src/api/types.ts +0 -238
- package/skills/deep-stock-analyst/src/index.ts +0 -84
- package/skills/deep-stock-analyst/src/llm/thesis.ts +0 -101
- package/skills/deep-stock-analyst/src/orchestrator.ts +0 -228
- package/skills/deep-stock-analyst/tsconfig.json +0 -21
|
@@ -1,137 +0,0 @@
|
|
|
1
|
-
/** Shared math utilities — all pure functions, no LLM. */
|
|
2
|
-
|
|
3
|
-
export interface ScoreThresholds {
|
|
4
|
-
excellent: number;
|
|
5
|
-
good: number;
|
|
6
|
-
fair: number;
|
|
7
|
-
poor: number;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Score a metric where lower is better (e.g. P/E ratio, debt/equity).
|
|
12
|
-
* Returns 0–100, where 100 = excellent (very cheap / low risk).
|
|
13
|
-
*/
|
|
14
|
-
export function scoreMetric(value: number, t: ScoreThresholds): number {
|
|
15
|
-
if (isNaN(value) || !isFinite(value)) return 50;
|
|
16
|
-
if (value <= t.excellent) return 100;
|
|
17
|
-
if (value <= t.good) return mapRange(value, t.excellent, t.good, 100, 75);
|
|
18
|
-
if (value <= t.fair) return mapRange(value, t.good, t.fair, 75, 50);
|
|
19
|
-
if (value <= t.poor) return mapRange(value, t.fair, t.poor, 50, 25);
|
|
20
|
-
return Math.max(0, 25 - ((value - t.poor) / t.poor) * 25);
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* Score a metric where higher is better (e.g. gross margin, ROE, FCF yield).
|
|
25
|
-
* Returns 0–100, where 100 = excellent (high margin / high return).
|
|
26
|
-
*/
|
|
27
|
-
export function scoreMetricInverse(value: number, t: ScoreThresholds): number {
|
|
28
|
-
if (isNaN(value) || !isFinite(value)) return 50;
|
|
29
|
-
if (value >= t.excellent) return 100;
|
|
30
|
-
if (value >= t.good) return mapRange(value, t.good, t.excellent, 75, 100);
|
|
31
|
-
if (value >= t.fair) return mapRange(value, t.fair, t.good, 50, 75);
|
|
32
|
-
if (value >= t.poor) return mapRange(value, t.poor, t.fair, 25, 50);
|
|
33
|
-
return Math.max(0, ((value - t.poor) / t.poor) * 25);
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
/** Weighted average: [[value, weight], ...] → number */
|
|
37
|
-
export function weightedAvg(pairs: [number, number][]): number {
|
|
38
|
-
let totalWeight = 0;
|
|
39
|
-
let totalValue = 0;
|
|
40
|
-
for (const [val, w] of pairs) {
|
|
41
|
-
if (!isNaN(val) && isFinite(val)) {
|
|
42
|
-
totalValue += val * w;
|
|
43
|
-
totalWeight += w;
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
return totalWeight > 0 ? totalValue / totalWeight : 50;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
/** Linear interpolation: map x from [inMin,inMax] to [outMin,outMax] */
|
|
50
|
-
export function mapRange(
|
|
51
|
-
x: number,
|
|
52
|
-
inMin: number,
|
|
53
|
-
inMax: number,
|
|
54
|
-
outMin: number,
|
|
55
|
-
outMax: number,
|
|
56
|
-
): number {
|
|
57
|
-
if (inMax === inMin) return (outMin + outMax) / 2;
|
|
58
|
-
const clamped = Math.max(inMin, Math.min(inMax, x));
|
|
59
|
-
return outMin + ((clamped - inMin) / (inMax - inMin)) * (outMax - outMin);
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
/** Simple Moving Average over the last `period` closing prices */
|
|
63
|
-
export function calcSMA(prices: { close: string }[], period: number): number {
|
|
64
|
-
const slice = prices.slice(0, period);
|
|
65
|
-
if (slice.length === 0) return 0;
|
|
66
|
-
const sum = slice.reduce((acc, p) => acc + parseFloat(p.close), 0);
|
|
67
|
-
return sum / slice.length;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
/** Average volume over `period` days */
|
|
71
|
-
export function calcAvgVolume(prices: { volume: string }[], period: number): number {
|
|
72
|
-
const slice = prices.slice(0, period);
|
|
73
|
-
if (slice.length === 0) return 0;
|
|
74
|
-
const sum = slice.reduce((acc, p) => acc + parseFloat(p.volume), 0);
|
|
75
|
-
return sum / slice.length;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
/** Format large numbers: 1234567 → "1.23M" */
|
|
79
|
-
export function formatNumber(n: number): string {
|
|
80
|
-
if (n >= 1e9) return `${(n / 1e9).toFixed(2)}B`;
|
|
81
|
-
if (n >= 1e6) return `${(n / 1e6).toFixed(2)}M`;
|
|
82
|
-
if (n >= 1e3) return `${(n / 1e3).toFixed(1)}K`;
|
|
83
|
-
return n.toFixed(0);
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
/** Safe parse: returns NaN-safe float */
|
|
87
|
-
export function sp(v: unknown): number {
|
|
88
|
-
const n = parseFloat(String(v ?? 'NaN'));
|
|
89
|
-
return isFinite(n) ? n : 0;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
/** YoY growth rate from quarterly reports (Q0 vs Q4) */
|
|
93
|
-
export function calcYoYGrowth(
|
|
94
|
-
reports: Array<Record<string, string>>,
|
|
95
|
-
field: string,
|
|
96
|
-
): number {
|
|
97
|
-
const current = sp(reports[0]?.[field]);
|
|
98
|
-
const prior = sp(reports[4]?.[field]);
|
|
99
|
-
if (prior === 0) return 0;
|
|
100
|
-
return ((current - prior) / Math.abs(prior)) * 100;
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
/** Score growth rate: 0–100 */
|
|
104
|
-
export function scoreGrowth(pct: number): number {
|
|
105
|
-
if (pct >= 30) return 100;
|
|
106
|
-
if (pct >= 15) return mapRange(pct, 15, 30, 70, 100);
|
|
107
|
-
if (pct >= 5) return mapRange(pct, 5, 15, 50, 70);
|
|
108
|
-
if (pct >= 0) return mapRange(pct, 0, 5, 40, 50);
|
|
109
|
-
if (pct >= -10) return mapRange(pct, -10, 0, 20, 40);
|
|
110
|
-
return Math.max(0, 20 + pct * 2);
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
/** Score earnings surprise percentage: 0–100 */
|
|
114
|
-
export function scoreSurprise(pct: number): number {
|
|
115
|
-
if (pct >= 10) return 100;
|
|
116
|
-
if (pct >= 5) return 80;
|
|
117
|
-
if (pct >= 0) return 60;
|
|
118
|
-
if (pct >= -5) return 40;
|
|
119
|
-
return 20;
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
/** Count consecutive earnings beats from quarterly earnings array */
|
|
123
|
-
export function countConsecutiveBeats(
|
|
124
|
-
earnings: Array<{ surprisePercentage: string }>,
|
|
125
|
-
): number {
|
|
126
|
-
let count = 0;
|
|
127
|
-
for (const e of earnings) {
|
|
128
|
-
if (sp(e.surprisePercentage) > 0) count++;
|
|
129
|
-
else break;
|
|
130
|
-
}
|
|
131
|
-
return count;
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
/** Clamp to [0, 100] */
|
|
135
|
-
export function clamp100(n: number): number {
|
|
136
|
-
return Math.max(0, Math.min(100, n));
|
|
137
|
-
}
|
|
@@ -1,95 +0,0 @@
|
|
|
1
|
-
import type { AVOverview } from '../api/types.js';
|
|
2
|
-
import { scoreMetric, scoreMetricInverse, weightedAvg, sp } from './utils.js';
|
|
3
|
-
|
|
4
|
-
export interface ValuationScore {
|
|
5
|
-
pe_score: number;
|
|
6
|
-
ps_score: number;
|
|
7
|
-
peg_score: number;
|
|
8
|
-
fcf_yield_score: number;
|
|
9
|
-
ev_ebitda_score: number;
|
|
10
|
-
composite: number;
|
|
11
|
-
verdict: 'undervalued' | 'fair' | 'overvalued' | 'expensive';
|
|
12
|
-
raw: {
|
|
13
|
-
pe: number;
|
|
14
|
-
forwardPE: number;
|
|
15
|
-
peg: number;
|
|
16
|
-
ps: number;
|
|
17
|
-
evEbitda: number;
|
|
18
|
-
fcfYieldPct: number;
|
|
19
|
-
};
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
export function calculateValuation(overview: AVOverview): ValuationScore {
|
|
23
|
-
const pe = sp(overview.PERatio);
|
|
24
|
-
const forwardPE = sp(overview.ForwardPE);
|
|
25
|
-
const peg = sp(overview.PEGRatio);
|
|
26
|
-
const ps = sp(overview.PriceToSalesRatioTTM);
|
|
27
|
-
const evEbitda = sp(overview.EVToEBITDA);
|
|
28
|
-
const marketCap = sp(overview.MarketCapitalization);
|
|
29
|
-
const ocf = sp(overview.OperatingCashflowTTM);
|
|
30
|
-
|
|
31
|
-
// FCF Yield = Operating Cash Flow / Market Cap (as percentage)
|
|
32
|
-
const fcfYieldPct = marketCap > 0 ? (ocf / marketCap) * 100 : 0;
|
|
33
|
-
|
|
34
|
-
// Score each metric: 100 = very cheap, 0 = very expensive
|
|
35
|
-
// Thresholds calibrated to S&P 500 historical medians
|
|
36
|
-
const pe_score = scoreMetric(pe > 0 ? pe : forwardPE, {
|
|
37
|
-
excellent: 12,
|
|
38
|
-
good: 18,
|
|
39
|
-
fair: 25,
|
|
40
|
-
poor: 40,
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
const peg_score = scoreMetric(peg, {
|
|
44
|
-
excellent: 0.5,
|
|
45
|
-
good: 1.0,
|
|
46
|
-
fair: 1.5,
|
|
47
|
-
poor: 2.5,
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
const fcf_yield_score = scoreMetricInverse(fcfYieldPct, {
|
|
51
|
-
excellent: 8,
|
|
52
|
-
good: 5,
|
|
53
|
-
fair: 3,
|
|
54
|
-
poor: 1,
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
const ev_ebitda_score = scoreMetric(evEbitda, {
|
|
58
|
-
excellent: 8,
|
|
59
|
-
good: 12,
|
|
60
|
-
fair: 18,
|
|
61
|
-
poor: 30,
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
const ps_score = scoreMetric(ps, {
|
|
65
|
-
excellent: 1,
|
|
66
|
-
good: 3,
|
|
67
|
-
fair: 6,
|
|
68
|
-
poor: 12,
|
|
69
|
-
});
|
|
70
|
-
|
|
71
|
-
const composite = weightedAvg([
|
|
72
|
-
[pe_score, 0.25],
|
|
73
|
-
[peg_score, 0.20],
|
|
74
|
-
[fcf_yield_score, 0.25],
|
|
75
|
-
[ev_ebitda_score, 0.15],
|
|
76
|
-
[ps_score, 0.15],
|
|
77
|
-
]);
|
|
78
|
-
|
|
79
|
-
const verdict: ValuationScore['verdict'] =
|
|
80
|
-
composite > 70 ? 'undervalued'
|
|
81
|
-
: composite > 50 ? 'fair'
|
|
82
|
-
: composite > 30 ? 'overvalued'
|
|
83
|
-
: 'expensive';
|
|
84
|
-
|
|
85
|
-
return {
|
|
86
|
-
pe_score,
|
|
87
|
-
ps_score,
|
|
88
|
-
peg_score,
|
|
89
|
-
fcf_yield_score,
|
|
90
|
-
ev_ebitda_score,
|
|
91
|
-
composite,
|
|
92
|
-
verdict,
|
|
93
|
-
raw: { pe, forwardPE, peg, ps, evEbitda, fcfYieldPct },
|
|
94
|
-
};
|
|
95
|
-
}
|
|
@@ -1,133 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Alpha Vantage API client.
|
|
3
|
-
* Free key: 25 req/day, 5 req/min → serialize calls with 200ms gap.
|
|
4
|
-
* Standard analysis = 12 calls.
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import type {
|
|
8
|
-
AllAVData,
|
|
9
|
-
AVDailyPrice,
|
|
10
|
-
AVRSIEntry,
|
|
11
|
-
AVMACDEntry,
|
|
12
|
-
AVBBandsEntry,
|
|
13
|
-
AVStochEntry,
|
|
14
|
-
AVADXEntry,
|
|
15
|
-
AVOverview,
|
|
16
|
-
AVIncomeStatement,
|
|
17
|
-
AVBalanceSheet,
|
|
18
|
-
AVCashFlow,
|
|
19
|
-
AVEarnings,
|
|
20
|
-
AVNewsSentiment,
|
|
21
|
-
} from './types.js';
|
|
22
|
-
|
|
23
|
-
const BASE_URL = 'https://www.alphavantage.co/query';
|
|
24
|
-
const RATE_DELAY_MS = 200; // 5 req/min on free key → be conservative
|
|
25
|
-
|
|
26
|
-
async function sleep(ms: number): Promise<void> {
|
|
27
|
-
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
async function avFetch(params: Record<string, string>, apiKey: string): Promise<unknown> {
|
|
31
|
-
const url = new URL(BASE_URL);
|
|
32
|
-
url.searchParams.set('apikey', apiKey);
|
|
33
|
-
for (const [k, v] of Object.entries(params)) {
|
|
34
|
-
url.searchParams.set(k, v);
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
const res = await fetch(url.toString());
|
|
38
|
-
if (!res.ok) {
|
|
39
|
-
throw new Error(`Alpha Vantage HTTP ${res.status}: ${url.searchParams.get('function')}`);
|
|
40
|
-
}
|
|
41
|
-
const data = await res.json() as Record<string, unknown>;
|
|
42
|
-
|
|
43
|
-
// AV returns error messages as JSON object with "Information" or "Note" keys
|
|
44
|
-
if (typeof data['Information'] === 'string' && data['Information'].includes('rate limit')) {
|
|
45
|
-
throw new Error(`Alpha Vantage rate limit hit: ${data['Information']}`);
|
|
46
|
-
}
|
|
47
|
-
if (typeof data['Note'] === 'string') {
|
|
48
|
-
throw new Error(`Alpha Vantage note (likely rate limited): ${data['Note']}`);
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
return data;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
/** Parse TIME_SERIES_DAILY_ADJUSTED into flat array sorted newest-first */
|
|
55
|
-
function parseDailySeries(raw: unknown): AVDailyPrice[] {
|
|
56
|
-
const data = raw as Record<string, unknown>;
|
|
57
|
-
const series = data['Time Series (Daily)'] as Record<string, Record<string, string>> | undefined;
|
|
58
|
-
if (!series) return [];
|
|
59
|
-
|
|
60
|
-
return Object.entries(series)
|
|
61
|
-
.map(([date, v]) => ({
|
|
62
|
-
date,
|
|
63
|
-
open: v['1. open'] ?? '0',
|
|
64
|
-
high: v['2. high'] ?? '0',
|
|
65
|
-
low: v['3. low'] ?? '0',
|
|
66
|
-
close: v['4. close'] ?? '0',
|
|
67
|
-
adjustedClose: v['5. adjusted close'] ?? '0',
|
|
68
|
-
volume: v['6. volume'] ?? '0',
|
|
69
|
-
dividendAmount: v['7. dividend amount'] ?? '0',
|
|
70
|
-
splitCoefficient: v['8. split coefficient'] ?? '1',
|
|
71
|
-
}))
|
|
72
|
-
.sort((a, b) => b.date.localeCompare(a.date)); // newest first
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
/** Parse a technical indicator response into flat array sorted newest-first */
|
|
76
|
-
function parseIndicator<T>(raw: unknown, key: string): T[] {
|
|
77
|
-
const data = raw as Record<string, unknown>;
|
|
78
|
-
const series = data[key] as Record<string, T> | undefined;
|
|
79
|
-
if (!series) return [];
|
|
80
|
-
return Object.entries(series)
|
|
81
|
-
.sort(([a], [b]) => b.localeCompare(a)) // newest first
|
|
82
|
-
.map(([, v]) => v);
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
/**
|
|
86
|
-
* Fetch all 12 Alpha Vantage endpoints for a standard analysis.
|
|
87
|
-
* Calls are serialized with a short delay to avoid rate limits.
|
|
88
|
-
*/
|
|
89
|
-
export async function fetchAllData(ticker: string, apiKey: string): Promise<AllAVData> {
|
|
90
|
-
const t = ticker.toUpperCase();
|
|
91
|
-
const results: unknown[] = [];
|
|
92
|
-
|
|
93
|
-
const calls: Array<Record<string, string>> = [
|
|
94
|
-
{ function: 'OVERVIEW', symbol: t },
|
|
95
|
-
{ function: 'INCOME_STATEMENT', symbol: t },
|
|
96
|
-
{ function: 'BALANCE_SHEET', symbol: t },
|
|
97
|
-
{ function: 'CASH_FLOW', symbol: t },
|
|
98
|
-
{ function: 'EARNINGS', symbol: t },
|
|
99
|
-
{ function: 'TIME_SERIES_DAILY_ADJUSTED', symbol: t, outputsize: 'full' },
|
|
100
|
-
{ function: 'RSI', symbol: t, interval: 'daily', time_period: '14', series_type: 'close' },
|
|
101
|
-
{ function: 'MACD', symbol: t, interval: 'daily', series_type: 'close' },
|
|
102
|
-
{ function: 'BBANDS', symbol: t, interval: 'daily', time_period: '20', series_type: 'close' },
|
|
103
|
-
{ function: 'STOCH', symbol: t, interval: 'daily' },
|
|
104
|
-
{ function: 'ADX', symbol: t, interval: 'daily', time_period: '14' },
|
|
105
|
-
{ function: 'NEWS_SENTIMENT', tickers: t, limit: '50', sort: 'LATEST' },
|
|
106
|
-
];
|
|
107
|
-
|
|
108
|
-
for (let i = 0; i < calls.length; i++) {
|
|
109
|
-
const call = calls[i];
|
|
110
|
-
if (i > 0) await sleep(RATE_DELAY_MS);
|
|
111
|
-
results.push(await avFetch(call!, apiKey));
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
const [
|
|
115
|
-
overview, income, balance, cashflow, earnings,
|
|
116
|
-
dailyRaw, rsiRaw, macdRaw, bbandsRaw, stochRaw, adxRaw, newsRaw,
|
|
117
|
-
] = results;
|
|
118
|
-
|
|
119
|
-
return {
|
|
120
|
-
overview: overview as AVOverview,
|
|
121
|
-
income: income as AVIncomeStatement,
|
|
122
|
-
balance: balance as AVBalanceSheet,
|
|
123
|
-
cashflow: cashflow as AVCashFlow,
|
|
124
|
-
earnings: earnings as AVEarnings,
|
|
125
|
-
daily: parseDailySeries(dailyRaw),
|
|
126
|
-
rsi: parseIndicator<AVRSIEntry>(rsiRaw, 'Technical Analysis: RSI'),
|
|
127
|
-
macd: parseIndicator<AVMACDEntry>(macdRaw, 'Technical Analysis: MACD'),
|
|
128
|
-
bbands: parseIndicator<AVBBandsEntry>(bbandsRaw, 'Technical Analysis: BBANDS'),
|
|
129
|
-
stoch: parseIndicator<AVStochEntry>(stochRaw, 'Technical Analysis: STOCH'),
|
|
130
|
-
adx: parseIndicator<AVADXEntry>(adxRaw, 'Technical Analysis: ADX'),
|
|
131
|
-
news: newsRaw as AVNewsSentiment,
|
|
132
|
-
};
|
|
133
|
-
}
|
|
@@ -1,238 +0,0 @@
|
|
|
1
|
-
/** Alpha Vantage API response types */
|
|
2
|
-
|
|
3
|
-
export interface AVOverview {
|
|
4
|
-
Symbol: string;
|
|
5
|
-
Name: string;
|
|
6
|
-
Description: string;
|
|
7
|
-
Sector: string;
|
|
8
|
-
Industry: string;
|
|
9
|
-
MarketCapitalization: string;
|
|
10
|
-
PERatio: string;
|
|
11
|
-
ForwardPE: string;
|
|
12
|
-
PEGRatio: string;
|
|
13
|
-
PriceToSalesRatioTTM: string;
|
|
14
|
-
PriceToBookRatio: string;
|
|
15
|
-
EVToEBITDA: string;
|
|
16
|
-
EVToRevenue: string;
|
|
17
|
-
GrossProfitTTM: string;
|
|
18
|
-
RevenueTTM: string;
|
|
19
|
-
OperatingMarginTTM: string;
|
|
20
|
-
ProfitMargin: string;
|
|
21
|
-
ReturnOnEquityTTM: string;
|
|
22
|
-
ReturnOnAssetsTTM: string;
|
|
23
|
-
DebtToEquity: string;
|
|
24
|
-
CurrentRatio: string;
|
|
25
|
-
QuickRatio: string;
|
|
26
|
-
OperatingCashflowTTM: string;
|
|
27
|
-
RevenuePerShareTTM: string;
|
|
28
|
-
EPS: string;
|
|
29
|
-
DilutedEPSTTM: string;
|
|
30
|
-
Beta: string;
|
|
31
|
-
'52WeekHigh': string;
|
|
32
|
-
'52WeekLow': string;
|
|
33
|
-
'50DayMovingAverage': string;
|
|
34
|
-
'200DayMovingAverage': string;
|
|
35
|
-
SharesOutstanding: string;
|
|
36
|
-
DividendYield: string;
|
|
37
|
-
ExDividendDate: string;
|
|
38
|
-
AnalystTargetPrice: string;
|
|
39
|
-
AnalystRatingStrongBuy: string;
|
|
40
|
-
AnalystRatingBuy: string;
|
|
41
|
-
AnalystRatingHold: string;
|
|
42
|
-
AnalystRatingSell: string;
|
|
43
|
-
AnalystRatingStrongSell: string;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
export interface AVQuarterlyReport {
|
|
47
|
-
fiscalDateEnding: string;
|
|
48
|
-
reportedCurrency: string;
|
|
49
|
-
totalRevenue: string;
|
|
50
|
-
netIncome: string;
|
|
51
|
-
grossProfit: string;
|
|
52
|
-
ebit: string;
|
|
53
|
-
ebitda: string;
|
|
54
|
-
operatingIncome: string;
|
|
55
|
-
interestExpense: string;
|
|
56
|
-
researchAndDevelopment: string;
|
|
57
|
-
sellingGeneralAndAdministrative: string;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
export interface AVAnnualReport {
|
|
61
|
-
fiscalDateEnding: string;
|
|
62
|
-
reportedCurrency: string;
|
|
63
|
-
totalRevenue: string;
|
|
64
|
-
netIncome: string;
|
|
65
|
-
grossProfit: string;
|
|
66
|
-
ebit: string;
|
|
67
|
-
ebitda: string;
|
|
68
|
-
operatingIncome: string;
|
|
69
|
-
interestExpense: string;
|
|
70
|
-
researchAndDevelopment: string;
|
|
71
|
-
sellingGeneralAndAdministrative: string;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
export interface AVIncomeStatement {
|
|
75
|
-
symbol: string;
|
|
76
|
-
annualReports: AVAnnualReport[];
|
|
77
|
-
quarterlyReports: AVQuarterlyReport[];
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
export interface AVBalanceSheetReport {
|
|
81
|
-
fiscalDateEnding: string;
|
|
82
|
-
reportedCurrency: string;
|
|
83
|
-
totalAssets: string;
|
|
84
|
-
totalCurrentAssets: string;
|
|
85
|
-
totalNonCurrentAssets: string;
|
|
86
|
-
totalLiabilities: string;
|
|
87
|
-
totalCurrentLiabilities: string;
|
|
88
|
-
totalNonCurrentLiabilities: string;
|
|
89
|
-
totalShareholderEquity: string;
|
|
90
|
-
longTermDebt: string;
|
|
91
|
-
shortTermDebt: string;
|
|
92
|
-
cashAndCashEquivalentsAtCarryingValue: string;
|
|
93
|
-
currentNetReceivables: string;
|
|
94
|
-
inventory: string;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
export interface AVBalanceSheet {
|
|
98
|
-
symbol: string;
|
|
99
|
-
annualReports: AVBalanceSheetReport[];
|
|
100
|
-
quarterlyReports: AVBalanceSheetReport[];
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
export interface AVCashFlowReport {
|
|
104
|
-
fiscalDateEnding: string;
|
|
105
|
-
reportedCurrency: string;
|
|
106
|
-
operatingCashflow: string;
|
|
107
|
-
capitalExpenditures: string;
|
|
108
|
-
cashflowFromInvestment: string;
|
|
109
|
-
cashflowFromFinancing: string;
|
|
110
|
-
netIncome: string;
|
|
111
|
-
dividendPayout: string;
|
|
112
|
-
changeInOperatingAssets: string;
|
|
113
|
-
changeInOperatingLiabilities: string;
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
export interface AVCashFlow {
|
|
117
|
-
symbol: string;
|
|
118
|
-
annualReports: AVCashFlowReport[];
|
|
119
|
-
quarterlyReports: AVCashFlowReport[];
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
export interface AVQuarterlyEarning {
|
|
123
|
-
fiscalDateEnding: string;
|
|
124
|
-
reportedDate: string;
|
|
125
|
-
reportedEPS: string;
|
|
126
|
-
estimatedEPS: string;
|
|
127
|
-
surprise: string;
|
|
128
|
-
surprisePercentage: string;
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
export interface AVEarnings {
|
|
132
|
-
symbol: string;
|
|
133
|
-
quarterlyEarnings: AVQuarterlyEarning[];
|
|
134
|
-
annualEarnings: Array<{ fiscalDateEnding: string; reportedEPS: string }>;
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
export interface AVDailyPrice {
|
|
138
|
-
date: string;
|
|
139
|
-
open: string;
|
|
140
|
-
high: string;
|
|
141
|
-
low: string;
|
|
142
|
-
close: string;
|
|
143
|
-
adjustedClose: string;
|
|
144
|
-
volume: string;
|
|
145
|
-
dividendAmount: string;
|
|
146
|
-
splitCoefficient: string;
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
export interface AVDailyTimeSeries {
|
|
150
|
-
'Meta Data': {
|
|
151
|
-
'1. Information': string;
|
|
152
|
-
'2. Symbol': string;
|
|
153
|
-
'3. Last Refreshed': string;
|
|
154
|
-
'4. Output Size': string;
|
|
155
|
-
'5. Time Zone': string;
|
|
156
|
-
};
|
|
157
|
-
'Time Series (Daily)': Record<string, {
|
|
158
|
-
'1. open': string;
|
|
159
|
-
'2. high': string;
|
|
160
|
-
'3. low': string;
|
|
161
|
-
'4. close': string;
|
|
162
|
-
'5. adjusted close': string;
|
|
163
|
-
'6. volume': string;
|
|
164
|
-
'7. dividend amount': string;
|
|
165
|
-
'8. split coefficient': string;
|
|
166
|
-
}>;
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
export interface AVRSIEntry {
|
|
170
|
-
RSI: string;
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
export interface AVMACDEntry {
|
|
174
|
-
MACD: string;
|
|
175
|
-
MACD_Hist: string;
|
|
176
|
-
MACD_Signal: string;
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
export interface AVBBandsEntry {
|
|
180
|
-
'Real Upper Band': string;
|
|
181
|
-
'Real Middle Band': string;
|
|
182
|
-
'Real Lower Band': string;
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
export interface AVStochEntry {
|
|
186
|
-
SlowK: string;
|
|
187
|
-
SlowD: string;
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
export interface AVADXEntry {
|
|
191
|
-
ADX: string;
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
export interface AVIndicatorResponse<T> {
|
|
195
|
-
'Meta Data': Record<string, string>;
|
|
196
|
-
'Technical Analysis: RSI'?: Record<string, T>;
|
|
197
|
-
'Technical Analysis: MACD'?: Record<string, T>;
|
|
198
|
-
'Technical Analysis: BBANDS'?: Record<string, T>;
|
|
199
|
-
'Technical Analysis: STOCH'?: Record<string, T>;
|
|
200
|
-
'Technical Analysis: ADX'?: Record<string, T>;
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
export interface AVNewsTickerSentiment {
|
|
204
|
-
ticker: string;
|
|
205
|
-
relevance_score: string;
|
|
206
|
-
ticker_sentiment_score: string;
|
|
207
|
-
ticker_sentiment_label: string;
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
export interface AVNewsArticle {
|
|
211
|
-
title: string;
|
|
212
|
-
url: string;
|
|
213
|
-
time_published: string;
|
|
214
|
-
summary: string;
|
|
215
|
-
overall_sentiment_score: string;
|
|
216
|
-
overall_sentiment_label: string;
|
|
217
|
-
ticker_sentiment: AVNewsTickerSentiment[];
|
|
218
|
-
topics: Array<{ topic: string; relevance_score: string }>;
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
export interface AVNewsSentiment {
|
|
222
|
-
feed: AVNewsArticle[];
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
export interface AllAVData {
|
|
226
|
-
overview: AVOverview;
|
|
227
|
-
income: AVIncomeStatement;
|
|
228
|
-
balance: AVBalanceSheet;
|
|
229
|
-
cashflow: AVCashFlow;
|
|
230
|
-
earnings: AVEarnings;
|
|
231
|
-
daily: AVDailyPrice[];
|
|
232
|
-
rsi: AVRSIEntry[];
|
|
233
|
-
macd: AVMACDEntry[];
|
|
234
|
-
bbands: AVBBandsEntry[];
|
|
235
|
-
stoch: AVStochEntry[];
|
|
236
|
-
adx: AVADXEntry[];
|
|
237
|
-
news: AVNewsSentiment;
|
|
238
|
-
}
|
|
@@ -1,84 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* CLI entry point for deep-stock-analyst.
|
|
4
|
-
* Usage: node dist/index.js --ticker IBM --depth standard --style hybrid
|
|
5
|
-
* Output: JSON to stdout (genesis-bot reads this)
|
|
6
|
-
*
|
|
7
|
-
* Exit codes:
|
|
8
|
-
* 0 = success, JSON on stdout
|
|
9
|
-
* 1 = invalid args or API error
|
|
10
|
-
* 2 = rate limit / daily limit reached
|
|
11
|
-
*/
|
|
12
|
-
|
|
13
|
-
import { runAnalysis } from './orchestrator.js';
|
|
14
|
-
import type { InvestmentStyle } from './analysis/signal.js';
|
|
15
|
-
|
|
16
|
-
function parseArgs(): { ticker: string; depth: string; style: InvestmentStyle } | null {
|
|
17
|
-
const args = process.argv.slice(2);
|
|
18
|
-
const get = (flag: string): string | undefined => {
|
|
19
|
-
const i = args.indexOf(flag);
|
|
20
|
-
return i >= 0 ? args[i + 1] : undefined;
|
|
21
|
-
};
|
|
22
|
-
|
|
23
|
-
const ticker = get('--ticker');
|
|
24
|
-
const depth = get('--depth') ?? 'standard';
|
|
25
|
-
const style = (get('--style') ?? 'hybrid') as InvestmentStyle;
|
|
26
|
-
|
|
27
|
-
if (!ticker) {
|
|
28
|
-
console.error('Usage: node dist/index.js --ticker <TICKER> [--depth quick|standard|deep] [--style growth|value|momentum|hybrid]');
|
|
29
|
-
return null;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
const validDepths = ['quick', 'standard', 'deep'];
|
|
33
|
-
const validStyles = ['growth', 'value', 'momentum', 'hybrid'];
|
|
34
|
-
|
|
35
|
-
if (!validDepths.includes(depth)) {
|
|
36
|
-
console.error(`Invalid depth: ${depth}. Must be one of: ${validDepths.join(', ')}`);
|
|
37
|
-
return null;
|
|
38
|
-
}
|
|
39
|
-
if (!validStyles.includes(style)) {
|
|
40
|
-
console.error(`Invalid style: ${style}. Must be one of: ${validStyles.join(', ')}`);
|
|
41
|
-
return null;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
return { ticker, depth, style };
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
async function main(): Promise<void> {
|
|
48
|
-
const args = parseArgs();
|
|
49
|
-
if (!args) {
|
|
50
|
-
process.exit(1);
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
const apiKey = process.env['ALPHA_VANTAGE_API_KEY'];
|
|
54
|
-
if (!apiKey) {
|
|
55
|
-
console.error('Missing ALPHA_VANTAGE_API_KEY environment variable');
|
|
56
|
-
process.exit(1);
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
try {
|
|
60
|
-
const result = await runAnalysis({
|
|
61
|
-
ticker: args.ticker,
|
|
62
|
-
depth: args.depth as 'quick' | 'standard' | 'deep',
|
|
63
|
-
style: args.style,
|
|
64
|
-
apiKey,
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
// Output clean JSON to stdout (genesis-bot reads this)
|
|
68
|
-
process.stdout.write(JSON.stringify(result, null, 2));
|
|
69
|
-
process.stdout.write('\n');
|
|
70
|
-
process.exit(0);
|
|
71
|
-
} catch (err: unknown) {
|
|
72
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
73
|
-
|
|
74
|
-
if (msg.includes('rate limit')) {
|
|
75
|
-
console.error(`[rate-limit] ${msg}`);
|
|
76
|
-
process.exit(2);
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
console.error(`[error] ${msg}`);
|
|
80
|
-
process.exit(1);
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
main();
|