This commit is contained in:
Boki 2025-06-11 10:38:05 -04:00
parent 597c6efc9b
commit 8b5e06954a
26 changed files with 532 additions and 186 deletions

View file

@ -82,11 +82,17 @@ export class NotificationsComponent {
const diff = now.getTime() - timestamp.getTime();
const minutes = Math.floor(diff / 60000);
if (minutes < 1) {return 'Just now';}
if (minutes < 60) {return `${minutes}m ago`;}
if (minutes < 1) {
return 'Just now';
}
if (minutes < 60) {
return `${minutes}m ago`;
}
const hours = Math.floor(minutes / 60);
if (hours < 24) {return `${hours}h ago`;}
if (hours < 24) {
return `${hours}h ago`;
}
const days = Math.floor(hours / 24);
return `${days}d ago`;

View file

@ -161,8 +161,12 @@ export class PortfolioComponent implements OnInit, OnDestroy {
}
getPnLColor(value: number): string {
if (value > 0) {return 'text-green-600';}
if (value < 0) {return 'text-red-600';}
if (value > 0) {
return 'text-green-600';
}
if (value < 0) {
return 'text-red-600';
}
return 'text-gray-600';
}
}

View file

@ -40,7 +40,9 @@ export class DrawdownChartComponent implements OnChanges {
}
private renderChart(): void {
if (!this.chartElement || !this.backtestResult) {return;}
if (!this.chartElement || !this.backtestResult) {
return;
}
// Clean up previous chart if it exists
if (this.chart) {

View file

@ -40,7 +40,9 @@ export class EquityChartComponent implements OnChanges {
}
private renderChart(): void {
if (!this.chartElement || !this.backtestResult) {return;}
if (!this.chartElement || !this.backtestResult) {
return;
}
// Clean up previous chart if it exists
if (this.chart) {

View file

@ -278,27 +278,45 @@ export class PerformanceMetricsComponent {
// Conditional classes
getReturnClass(value: number): string {
if (value > 0) {return 'positive';}
if (value < 0) {return 'negative';}
if (value > 0) {
return 'positive';
}
if (value < 0) {
return 'negative';
}
return '';
}
getRatioClass(value: number): string {
if (value >= 1.5) {return 'positive';}
if (value >= 1) {return 'neutral';}
if (value < 0) {return 'negative';}
if (value >= 1.5) {
return 'positive';
}
if (value >= 1) {
return 'neutral';
}
if (value < 0) {
return 'negative';
}
return '';
}
getWinRateClass(value: number): string {
if (value >= 0.55) {return 'positive';}
if (value >= 0.45) {return 'neutral';}
if (value >= 0.55) {
return 'positive';
}
if (value >= 0.45) {
return 'neutral';
}
return 'negative';
}
getProfitFactorClass(value: number): string {
if (value >= 1.5) {return 'positive';}
if (value >= 1) {return 'neutral';}
if (value >= 1.5) {
return 'positive';
}
if (value >= 1) {
return 'neutral';
}
return 'negative';
}
}

View file

@ -139,7 +139,9 @@ export class BacktestDialogComponent implements OnInit {
}
addSymbol(symbol: string): void {
if (!symbol || this.selectedSymbols.includes(symbol)) {return;}
if (!symbol || this.selectedSymbols.includes(symbol)) {
return;
}
this.selectedSymbols.push(symbol);
}

View file

@ -126,7 +126,9 @@ export class StrategyDialogComponent implements OnInit {
}
addSymbol(symbol: string): void {
if (!symbol || this.selectedSymbols.includes(symbol)) {return;}
if (!symbol || this.selectedSymbols.includes(symbol)) {
return;
}
this.selectedSymbols.push(symbol);
}

View file

@ -172,8 +172,12 @@ let proxyStats: ProxySource[] = PROXY_CONFIG.PROXY_SOURCES.map(source => ({
async function updateProxyStats(sourceId: string, success: boolean) {
const source = proxyStats.find(s => s.id === sourceId);
if (source !== undefined) {
if (typeof source.working !== 'number') {source.working = 0;}
if (typeof source.total !== 'number') {source.total = 0;}
if (typeof source.working !== 'number') {
source.working = 0;
}
if (typeof source.total !== 'number') {
source.total = 0;
}
source.total += 1;
if (success) {
source.working += 1;
@ -400,7 +404,9 @@ export async function fetchProxiesFromSource(source: ProxySource): Promise<Proxy
for (const line of lines) {
let trimmed = line.trim();
trimmed = cleanProxyUrl(trimmed);
if (!trimmed || trimmed.startsWith('#')) {continue;}
if (!trimmed || trimmed.startsWith('#')) {
continue;
}
// Parse formats like "host:port" or "host:port:user:pass"
const parts = trimmed.split(':');

View file

@ -13,7 +13,9 @@
"@stock-bot/shutdown#build"
],
"outputs": ["dist/**"],
"inputs": ["src/**", "package.json", "tsconfig.json", "!**/*.test.ts", "!**/*.spec.ts", "!**/test/**", "!**/tests/**", "!**/__tests__/**"]
"inputs": ["src/**",
"package.json",
"tsconfig.json", "!**/*.test.ts", "!**/*.spec.ts", "!**/test/**", "!**/tests/**", "!**/__tests__/**"]
}
}
}

View file

@ -68,7 +68,9 @@ export class PerformanceAnalyzer {
}
private calculateReturns(_period: 'daily' | 'weekly' | 'monthly'): number[] {
if (this.snapshots.length < 2) {return [];}
if (this.snapshots.length < 2) {
return [];
}
const returns: number[] = [];
@ -83,7 +85,9 @@ export class PerformanceAnalyzer {
}
private calculateTotalReturn(): number {
if (this.snapshots.length < 2) {return 0;}
if (this.snapshots.length < 2) {
return 0;
}
const firstValue = this.snapshots[0].totalValue;
const lastValue = this.snapshots[this.snapshots.length - 1].totalValue;
@ -92,14 +96,18 @@ export class PerformanceAnalyzer {
}
private calculateAnnualizedReturn(returns: number[]): number {
if (returns.length === 0) {return 0;}
if (returns.length === 0) {
return 0;
}
const avgReturn = returns.reduce((sum, ret) => sum + ret, 0) / returns.length;
return Math.pow(1 + avgReturn, 252) - 1; // 252 trading days per year
}
private calculateVolatility(returns: number[]): number {
if (returns.length === 0) {return 0;}
if (returns.length === 0) {
return 0;
}
const avgReturn = returns.reduce((sum, ret) => sum + ret, 0) / returns.length;
const variance =
@ -109,19 +117,25 @@ export class PerformanceAnalyzer {
}
private calculateSharpeRatio(returns: number[], riskFreeRate: number): number {
if (returns.length === 0) {return 0;}
if (returns.length === 0) {
return 0;
}
const avgReturn = returns.reduce((sum, ret) => sum + ret, 0) / returns.length;
const annualizedReturn = Math.pow(1 + avgReturn, 252) - 1;
const volatility = this.calculateVolatility(returns);
if (volatility === 0) {return 0;}
if (volatility === 0) {
return 0;
}
return (annualizedReturn - riskFreeRate) / volatility;
}
private calculateMaxDrawdown(): number {
if (this.snapshots.length === 0) {return 0;}
if (this.snapshots.length === 0) {
return 0;
}
let maxDrawdown = 0;
let peak = this.snapshots[0].totalValue;
@ -139,7 +153,9 @@ export class PerformanceAnalyzer {
}
private calculateBeta(returns: number[]): number {
if (returns.length === 0 || this.benchmarkReturns.length === 0) {return 1.0;}
if (returns.length === 0 || this.benchmarkReturns.length === 0) {
return 1.0;
}
// Simple beta calculation - would need actual benchmark data
return 1.0; // Placeholder
@ -157,7 +173,9 @@ export class PerformanceAnalyzer {
const annualizedReturn = this.calculateAnnualizedReturn(returns);
const maxDrawdown = this.calculateMaxDrawdown();
if (maxDrawdown === 0) {return 0;}
if (maxDrawdown === 0) {
return 0;
}
return annualizedReturn / maxDrawdown;
}
@ -166,16 +184,22 @@ export class PerformanceAnalyzer {
const annualizedReturn = this.calculateAnnualizedReturn(returns);
const downsideDeviation = this.calculateDownsideDeviation(returns);
if (downsideDeviation === 0) {return 0;}
if (downsideDeviation === 0) {
return 0;
}
return (annualizedReturn - riskFreeRate) / downsideDeviation;
}
private calculateDownsideDeviation(returns: number[]): number {
if (returns.length === 0) {return 0;}
if (returns.length === 0) {
return 0;
}
const negativeReturns = returns.filter(ret => ret < 0);
if (negativeReturns.length === 0) {return 0;}
if (negativeReturns.length === 0) {
return 0;
}
const avgNegativeReturn =
negativeReturns.reduce((sum, ret) => sum + ret, 0) / negativeReturns.length;
@ -187,7 +211,9 @@ export class PerformanceAnalyzer {
}
private calculateVaR(returns: number[], confidence: number): number {
if (returns.length === 0) {return 0;}
if (returns.length === 0) {
return 0;
}
const sortedReturns = returns.slice().sort((a, b) => a - b);
const index = Math.floor((1 - confidence) * sortedReturns.length);
@ -196,13 +222,17 @@ export class PerformanceAnalyzer {
}
private calculateCVaR(returns: number[], confidence: number): number {
if (returns.length === 0) {return 0;}
if (returns.length === 0) {
return 0;
}
const sortedReturns = returns.slice().sort((a, b) => a - b);
const cutoffIndex = Math.floor((1 - confidence) * sortedReturns.length);
const tailReturns = sortedReturns.slice(0, cutoffIndex + 1);
if (tailReturns.length === 0) {return 0;}
if (tailReturns.length === 0) {
return 0;
}
const avgTailReturn = tailReturns.reduce((sum, ret) => sum + ret, 0) / tailReturns.length;
return -avgTailReturn; // Return as positive value

View file

@ -172,7 +172,9 @@ async function saveResults(result: any, outputPath: string): Promise<void> {
}
function convertTradesToCSV(trades: any[]): string {
if (trades.length === 0) {return 'No trades executed\n';}
if (trades.length === 0) {
return 'No trades executed\n';
}
const headers = Object.keys(trades[0]).join(',');
const rows = trades.map(trade =>