Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
141 changes: 141 additions & 0 deletions src/advanced-analytics/advanced-analytics.controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
import {
Controller,
Get,
Post,
Body,
Query,
Param,
UseGuards,
Logger,
HttpStatus,
HttpException,
} from '@nestjs/common';
import { ApiTags, ApiOperation, ApiResponse, ApiQuery } from '@nestjs/swagger';
import { JwtAuthGuard } from '../auth/guards/jwt-auth.guard';
import { AdvancedAnalyticsService } from './advanced-analytics.service';
import { CurrentUser } from '../auth/decorators/current-user.decorator';
import { PortfolioPerformanceRequestDto } from './dto/portfolio-performance.dto';
import { BenchmarkComparisonRequestDto } from './dto/benchmark-comparison.dto';
import { TaxReportRequestDto } from './dto/tax-report.dto';
import { RebalancingRequestDto } from './dto/rebalancing.dto';

@ApiTags('advanced-analytics')
@Controller('analytics')
@UseGuards(JwtAuthGuard)
export class AdvancedAnalyticsController {
private readonly logger = new Logger(AdvancedAnalyticsController.name);

constructor(private readonly advancedAnalyticsService: AdvancedAnalyticsService) {}

@Get('risk-metrics/:userId')
@ApiOperation({ summary: 'Get advanced risk metrics for a user portfolio' })
@ApiResponse({ status: HttpStatus.OK, description: 'Risk metrics retrieved successfully' })
async getRiskMetrics(@Param('userId') userId: string, @CurrentUser() user: any) {
if (user.id !== userId && !user.roles?.includes('admin')) {
throw new HttpException('Unauthorized', HttpStatus.FORBIDDEN);
}
return this.advancedAnalyticsService.getAdvancedRiskMetrics(userId);
}

@Post('portfolio-performance')
@ApiOperation({ summary: 'Calculate comprehensive portfolio performance metrics' })
@ApiResponse({ status: HttpStatus.OK, description: 'Performance metrics calculated successfully' })
async calculatePortfolioPerformance(@Body() dto: PortfolioPerformanceRequestDto) {
const trades = dto.trades.map(t => ({
...t,
date: new Date(t.date),
}));

const historicalPrices = new Map<string, any[]>();
for (const [asset, prices] of Object.entries(dto.historicalPrices)) {
historicalPrices.set(
asset,
prices.map(p => ({ ...p, date: new Date(p.date) }))
);
}

return this.advancedAnalyticsService.getPortfolioPerformance(trades, historicalPrices);
}

@Post('benchmark-comparison')
@ApiOperation({ summary: 'Compare portfolio performance against market benchmarks' })
@ApiResponse({ status: HttpStatus.OK, description: 'Benchmark comparison completed successfully' })
async compareToBenchmarks(@Body() dto: BenchmarkComparisonRequestDto) {
const benchmarkPrices = new Map<string, number[]>();
for (const [benchmark, prices] of Object.entries(dto.benchmarkPrices)) {
benchmarkPrices.set(benchmark, prices);
}
return this.advancedAnalyticsService.getBenchmarkComparison(dto.portfolioReturns, benchmarkPrices);
}

@Post('tax-report')
@ApiOperation({ summary: 'Generate tax report for specified time period' })
@ApiResponse({ status: HttpStatus.OK, description: 'Tax report generated successfully' })
async generateTaxReport(@Body() dto: TaxReportRequestDto) {
const trades = dto.trades.map(t => ({
...t,
date: new Date(t.date),
}));
const startDate = new Date(dto.startDate);
const endDate = new Date(dto.endDate);
return this.advancedAnalyticsService.generateTaxReport(trades, startDate, endDate);
}

@Post('rebalancing-recommendations')
@ApiOperation({ summary: 'Get AI-driven portfolio rebalancing recommendations' })
@ApiResponse({ status: HttpStatus.OK, description: 'Rebalancing recommendations generated successfully' })
async getRebalancingRecommendations(@Body() dto: RebalancingRequestDto) {
const assetVolatilities = new Map<string, number>();
for (const [asset, volatility] of Object.entries(dto.assetVolatilities)) {
assetVolatilities.set(asset, volatility);
}
return this.advancedAnalyticsService.getRebalancingRecommendations(
dto.currentAllocation,
dto.totalPortfolioValue,
dto.riskTolerance,
assetVolatilities
);
}

@Get('portfolio-optimization/:userId')
@ApiOperation({ summary: 'Get portfolio optimization suggestions' })
@ApiResponse({ status: HttpStatus.OK, description: 'Optimization suggestions retrieved successfully' })
async getPortfolioOptimization(@Param('userId') userId: string, @CurrentUser() user: any) {
if (user.id !== userId && !user.roles?.includes('admin')) {
throw new HttpException('Unauthorized', HttpStatus.FORBIDDEN);
}
return this.advancedAnalyticsService.getPortfolioOptimization(userId);
}

@Get('export/:userId')
@ApiQuery({ name: 'format', enum: ['csv', 'xlsx'], required: true })
@ApiOperation({ summary: 'Export user analytics data' })
@ApiResponse({ status: HttpStatus.OK, description: 'Analytics exported successfully' })
async exportAnalytics(
@Param('userId') userId: string,
@Query('format') format: 'csv' | 'xlsx',
@CurrentUser() user: any
) {
if (user.id !== userId && !user.roles?.includes('admin')) {
throw new HttpException('Unauthorized', HttpStatus.FORBIDDEN);
}
return this.advancedAnalyticsService.exportUserAnalytics(userId, format);
}

@Post('export-tax-report')
@ApiQuery({ name: 'format', enum: ['csv', 'xlsx'], required: true })
@ApiOperation({ summary: 'Export tax report as CSV or Excel' })
@ApiResponse({ status: HttpStatus.OK, description: 'Tax report exported successfully' })
async exportTaxReport(
@Body() dto: TaxReportRequestDto,
@Query('format') format: 'csv' | 'xlsx'
) {
const trades = dto.trades.map(t => ({
...t,
date: new Date(t.date),
}));
const startDate = new Date(dto.startDate);
const endDate = new Date(dto.endDate);
return this.advancedAnalyticsService.exportTaxReport(trades, startDate, endDate, format);
}
}
4 changes: 3 additions & 1 deletion src/advanced-analytics/advanced-analytics.module.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import { Module } from '@nestjs/common';
import { AdvancedAnalyticsService } from './advanced-analytics.service';
import { AdvancedAnalyticsController } from './advanced-analytics.controller';
import { PricePredictionService } from './services/price-prediction/price-prediction.service';
import { PortfolioRiskScoringService } from './services/portfolio-risk-scoring.service';
import { UserBehaviorAnalysisService } from './services/user-behavior-analysis/user-behavior-analysis.service';
import { AnalyticsExportService } from './services/analytics-export/analytics-export.service';

@Module({
controllers: [AdvancedAnalyticsController],
providers: [
AdvancedAnalyticsService,
PricePredictionService,
Expand All @@ -15,4 +17,4 @@ import { AnalyticsExportService } from './services/analytics-export/analytics-ex
],
exports: [AdvancedAnalyticsService],
})
export class AdvancedAnalyticsModule {}
export class AdvancedAnalyticsModule {}
87 changes: 86 additions & 1 deletion src/advanced-analytics/advanced-analytics.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import { PortfolioRiskScoringService } from './services/portfolio-risk-scoring.s
import { PricePredictionService } from './services/price-prediction/price-prediction.service';
import { UserBehaviorAnalysisService } from './services/user-behavior-analysis/user-behavior-analysis.service';
import { AnalyticsExportService } from './services/analytics-export/analytics-export.service';
import { Trade } from '../common/interfaces/portfolio.interface';
import { IAssetAllocation } from '../common/interfaces/risk-portfolio.interface';

@Injectable()
export class AdvancedAnalyticsService {
Expand Down Expand Up @@ -30,6 +32,71 @@ export class AdvancedAnalyticsService {
};
}

/**
* Get comprehensive portfolio performance metrics
*/
async getPortfolioPerformance(trades: Trade[], historicalPrices: Map<string, any[]>): Promise<any> {
this.logger.debug(`Calculating portfolio performance for ${trades.length} trades`);
const performance = await this.portfolioRisk.calculatePortfolioPerformance(trades, historicalPrices);

return {
performance,
generatedAt: new Date().toISOString(),
};
}

/**
* Compare portfolio performance against benchmarks (BTC, ETH)
*/
async getBenchmarkComparison(
portfolioReturns: number[],
benchmarkPrices: Map<string, number[]>
): Promise<any> {
this.logger.debug(`Comparing portfolio against ${benchmarkPrices.size} benchmarks`);
const comparisons = await this.portfolioRisk.compareToBenchmarks(portfolioReturns, benchmarkPrices);

return {
benchmarks: comparisons,
generatedAt: new Date().toISOString(),
};
}

/**
* Generate tax report for a specific time period
*/
generateTaxReport(trades: Trade[], startDate: Date, endDate: Date): any {
this.logger.debug(`Generating tax report for period ${startDate.toISOString()} to ${endDate.toISOString()}`);
const taxReport = this.portfolioRisk.generateTaxReport(trades, startDate, endDate);

return {
...taxReport,
generatedAt: new Date().toISOString(),
};
}

/**
* Get portfolio rebalancing recommendations
*/
getRebalancingRecommendations(
currentAllocation: IAssetAllocation[],
totalPortfolioValue: number,
riskTolerance: 'conservative' | 'moderate' | 'aggressive',
assetVolatilities: Map<string, number>
): any {
this.logger.debug(`Generating rebalancing recommendations for ${currentAllocation.length} assets`);
const recommendations = this.portfolioRisk.generateRebalancingRecommendations(
currentAllocation,
totalPortfolioValue,
riskTolerance,
assetVolatilities
);

return {
...recommendations,
generatedAt: new Date().toISOString(),
};
}

async getPortfolioOptimization(userId: string): Promise<any> {
// Baseline: combine 7-day price predictions + risk scoring.
const [risk, predictions] = await Promise.all([
Expand Down Expand Up @@ -59,4 +126,22 @@ export class AdvancedAnalyticsService {
const analytics = await this.getAdvancedRiskMetrics(userId);
return this.exportService.exportAnalytics(analytics, format);
}
}

/**
* Export tax report with all transaction details
*/
async exportTaxReport(
trades: Trade[],
startDate: Date,
endDate: Date,
format: 'csv' | 'xlsx',
): Promise<{
mimeType: string;
fileName: string;
buffer: Buffer;
}> {
this.logger.debug(`Exporting tax report as ${format}`);
const taxReport = this.generateTaxReport(trades, startDate, endDate);
return this.exportService.exportAnalytics(taxReport, format);
}
}
23 changes: 23 additions & 0 deletions src/advanced-analytics/dto/benchmark-comparison.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { IsArray, IsNumber, IsString } from 'class-validator';
import { ApiProperty } from '@nestjs/swagger';

export class BenchmarkComparisonRequestDto {
@ApiProperty({
example: [0.01, -0.005, 0.02, 0.015, -0.01],
description: 'Array of daily portfolio returns',
})
@IsArray()
@IsNumber({}, { each: true })
portfolioReturns: number[];

@ApiProperty({
type: 'object',
additionalProperties: { type: 'array', items: { type: 'number' } },
description: 'Historical prices for benchmarks (BTC, ETH, etc.)',
example: {
BTC: [42000, 42500, 42300, 43000, 42800],
ETH: [2200, 2250, 2230, 2300, 2280],
},
})
benchmarkPrices: Record<string, number[]>;
}
64 changes: 64 additions & 0 deletions src/advanced-analytics/dto/portfolio-performance.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import {
IsString,
IsNumber,
IsArray,
ValidateNested,
IsDateString,
IsEnum,
} from 'class-validator';
import { Type } from 'class-transformer';
import { ApiProperty } from '@nestjs/swagger';

class TradeDto {
@ApiProperty({ example: 'trade_123', description: 'Unique trade identifier' })
@IsString()
id: string;

@ApiProperty({ example: 'BTC', description: 'Asset symbol' })
@IsString()
asset: string;

@ApiProperty({ example: 0.5, description: 'Quantity traded' })
@IsNumber()
quantity: number;

@ApiProperty({ example: 45000, description: 'Price at execution' })
@IsNumber()
price: number;

@ApiProperty({ enum: ['BUY', 'SELL'], description: 'Trade side' })
@IsEnum(['BUY', 'SELL'])
side: 'BUY' | 'SELL';

@ApiProperty({ example: '2024-01-15T10:30:00Z', description: 'Trade execution date' })
@IsDateString()
date: string;
}

class HistoricalPriceDto {
@ApiProperty({ example: '2024-01-01T00:00:00Z', description: 'Date of price record' })
@IsDateString()
date: string;

@ApiProperty({ example: 42000, description: 'Asset price at this date' })
@IsNumber()
price: number;
}

export class PortfolioPerformanceRequestDto {
@ApiProperty({ type: [TradeDto], description: 'List of all trades in the portfolio' })
@IsArray()
@ValidateNested({ each: true })
@Type(() => TradeDto)
trades: TradeDto[];

@ApiProperty({
type: 'object',
additionalProperties: { type: 'array', items: { $ref: '#/components/schemas/HistoricalPriceDto' } },
description: 'Historical prices keyed by asset symbol',
example: {
BTC: [{ date: '2024-01-01T00:00:00Z', price: 42000 }],
},
})
historicalPrices: Record<string, HistoricalPriceDto[]>;
}
Loading
Loading