first commit
This commit is contained in:
@@ -0,0 +1,74 @@
|
||||
/**
|
||||
* Format a number as a price string with 2 decimal places and thousands separators.
|
||||
* e.g. 1234.5 → "1,234.50"
|
||||
*/
|
||||
export function formatPrice(n: number): string {
|
||||
return n.toLocaleString('en-US', {
|
||||
minimumFractionDigits: 2,
|
||||
maximumFractionDigits: 2,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Format a number as a percentage string with 2 decimal places.
|
||||
* e.g. 12.345 → "12.35%"
|
||||
*/
|
||||
export function formatPercent(n: number): string {
|
||||
return `${n.toLocaleString('en-US', {
|
||||
minimumFractionDigits: 2,
|
||||
maximumFractionDigits: 2,
|
||||
})}%`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Format a large number with K/M/B suffix.
|
||||
* Values >= 1_000_000_000 → "1.23B"
|
||||
* Values >= 1_000_000 → "456.7M"
|
||||
* Values >= 1_000 → "12.3K"
|
||||
* Values < 1_000 → plain number, no suffix
|
||||
*/
|
||||
export function formatLargeNumber(n: number): string {
|
||||
const abs = Math.abs(n);
|
||||
const sign = n < 0 ? '-' : '';
|
||||
|
||||
if (abs >= 1_000_000_000) {
|
||||
return `${sign}${(abs / 1_000_000_000).toFixed(2).replace(/\.?0+$/, '')}B`;
|
||||
}
|
||||
if (abs >= 1_000_000) {
|
||||
return `${sign}${(abs / 1_000_000).toFixed(1).replace(/\.?0+$/, '')}M`;
|
||||
}
|
||||
if (abs >= 1_000) {
|
||||
return `${sign}${(abs / 1_000).toFixed(1).replace(/\.?0+$/, '')}K`;
|
||||
}
|
||||
return n.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Format an ISO date string as a short date.
|
||||
* e.g. "2025-01-15T14:30:00Z" → "Jan 15, 2025"
|
||||
*/
|
||||
export function formatDate(d: string): string {
|
||||
const date = new Date(d);
|
||||
return date.toLocaleDateString('en-US', {
|
||||
month: 'short',
|
||||
day: 'numeric',
|
||||
year: 'numeric',
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Format an ISO date string as a date with time.
|
||||
* e.g. "2025-01-15T14:30:00Z" → "Jan 15, 2025 2:30 PM"
|
||||
*/
|
||||
export function formatDateTime(d: string): string {
|
||||
const date = new Date(d);
|
||||
return `${date.toLocaleDateString('en-US', {
|
||||
month: 'short',
|
||||
day: 'numeric',
|
||||
year: 'numeric',
|
||||
})} ${date.toLocaleTimeString('en-US', {
|
||||
hour: 'numeric',
|
||||
minute: '2-digit',
|
||||
hour12: true,
|
||||
})}`;
|
||||
}
|
||||
@@ -0,0 +1,172 @@
|
||||
// API envelope (before unwrapping)
|
||||
export interface APIEnvelope<T = unknown> {
|
||||
status: 'success' | 'error';
|
||||
data: T | null;
|
||||
error: string | null;
|
||||
}
|
||||
|
||||
// Auth
|
||||
export interface TokenResponse {
|
||||
access_token: string;
|
||||
token_type: string;
|
||||
}
|
||||
|
||||
// Watchlist
|
||||
export interface WatchlistEntry {
|
||||
symbol: string;
|
||||
entry_type: 'auto' | 'manual';
|
||||
composite_score: number | null;
|
||||
dimensions: DimensionScore[];
|
||||
rr_ratio: number | null;
|
||||
rr_direction: string | null;
|
||||
sr_levels: SRLevelSummary[];
|
||||
added_at: string;
|
||||
}
|
||||
|
||||
export interface DimensionScore {
|
||||
dimension: string;
|
||||
score: number;
|
||||
}
|
||||
|
||||
export interface SRLevelSummary {
|
||||
price_level: number;
|
||||
type: 'support' | 'resistance';
|
||||
strength: number;
|
||||
}
|
||||
|
||||
// OHLCV
|
||||
export interface OHLCVBar {
|
||||
id: number;
|
||||
ticker_id: number;
|
||||
date: string;
|
||||
open: number;
|
||||
high: number;
|
||||
low: number;
|
||||
close: number;
|
||||
volume: number;
|
||||
created_at: string;
|
||||
}
|
||||
|
||||
// Scores
|
||||
export interface ScoreResponse {
|
||||
symbol: string;
|
||||
composite_score: number | null;
|
||||
composite_stale: boolean;
|
||||
weights: Record<string, number>;
|
||||
dimensions: DimensionScoreDetail[];
|
||||
missing_dimensions: string[];
|
||||
computed_at: string | null;
|
||||
}
|
||||
|
||||
export interface DimensionScoreDetail {
|
||||
dimension: string;
|
||||
score: number;
|
||||
is_stale: boolean;
|
||||
computed_at: string | null;
|
||||
}
|
||||
|
||||
export interface RankingEntry {
|
||||
symbol: string;
|
||||
composite_score: number;
|
||||
dimensions: DimensionScoreDetail[];
|
||||
}
|
||||
|
||||
export interface RankingsResponse {
|
||||
rankings: RankingEntry[];
|
||||
weights: Record<string, number>;
|
||||
}
|
||||
|
||||
// Trade Setups
|
||||
export interface TradeSetup {
|
||||
id: number;
|
||||
symbol: string;
|
||||
direction: string;
|
||||
entry_price: number;
|
||||
stop_loss: number;
|
||||
target: number;
|
||||
rr_ratio: number;
|
||||
composite_score: number;
|
||||
detected_at: string;
|
||||
}
|
||||
|
||||
// S/R Levels
|
||||
export interface SRLevel {
|
||||
id: number;
|
||||
price_level: number;
|
||||
type: 'support' | 'resistance';
|
||||
strength: number;
|
||||
detection_method: string;
|
||||
created_at: string;
|
||||
}
|
||||
|
||||
export interface SRLevelResponse {
|
||||
symbol: string;
|
||||
levels: SRLevel[];
|
||||
count: number;
|
||||
}
|
||||
|
||||
// Sentiment
|
||||
export interface SentimentScore {
|
||||
id: number;
|
||||
classification: 'bullish' | 'bearish' | 'neutral';
|
||||
confidence: number;
|
||||
source: string;
|
||||
timestamp: string;
|
||||
}
|
||||
|
||||
export interface SentimentResponse {
|
||||
symbol: string;
|
||||
scores: SentimentScore[];
|
||||
count: number;
|
||||
dimension_score: number | null;
|
||||
lookback_hours: number;
|
||||
}
|
||||
|
||||
// Fundamentals
|
||||
export interface FundamentalResponse {
|
||||
symbol: string;
|
||||
pe_ratio: number | null;
|
||||
revenue_growth: number | null;
|
||||
earnings_surprise: number | null;
|
||||
market_cap: number | null;
|
||||
fetched_at: string | null;
|
||||
}
|
||||
|
||||
// Indicators
|
||||
export interface IndicatorResult {
|
||||
indicator_type: string;
|
||||
values: Record<string, unknown>;
|
||||
score: number;
|
||||
bars_used: number;
|
||||
}
|
||||
|
||||
export interface EMACrossResult {
|
||||
short_ema: number;
|
||||
long_ema: number;
|
||||
short_period: number;
|
||||
long_period: number;
|
||||
signal: 'bullish' | 'bearish' | 'neutral';
|
||||
}
|
||||
|
||||
// Tickers
|
||||
export interface Ticker {
|
||||
id: number;
|
||||
symbol: string;
|
||||
created_at: string;
|
||||
}
|
||||
|
||||
// Admin
|
||||
export interface AdminUser {
|
||||
id: number;
|
||||
username: string;
|
||||
role: string;
|
||||
has_access: boolean;
|
||||
created_at: string | null;
|
||||
updated_at: string | null;
|
||||
}
|
||||
|
||||
export interface SystemSetting {
|
||||
key: string;
|
||||
value: string;
|
||||
updated_at: string | null;
|
||||
}
|
||||
Reference in New Issue
Block a user