← back to jdy8739__glassbox

Function bodies 106 total

All specs Real LLM only Function bodies
parse_date function · python · L25-L39 (15 LOC)
apps/backend/python/efficient_frontier.py
def parse_date(date_input):
    """
    Convert string date to datetime object

    Args:
        date_input: String (YYYY-MM-DD), datetime, or None

    Returns:
        datetime object or None
    """
    if date_input is None:
        return None
    if isinstance(date_input, str):
        return datetime.strptime(date_input, '%Y-%m-%d')
    return date_input
validate_data_sufficiency function · python · L42-L57 (16 LOC)
apps/backend/python/efficient_frontier.py
def validate_data_sufficiency(data, min_points=MIN_DATA_POINTS, data_type="price"):
    """
    Validate that we have sufficient data points

    Args:
        data: DataFrame or Series to validate
        min_points: Minimum required data points
        data_type: Description of data for error message

    Raises:
        ValueError if insufficient data
    """
    if data.empty or len(data) < min_points:
        raise ValueError(
            f"Insufficient {data_type} data. Need at least {min_points} points, got {len(data)}"
        )
calculate_annualized_risk_free_rate function · python · L62-L76 (15 LOC)
apps/backend/python/efficient_frontier.py
def calculate_annualized_risk_free_rate(prices, ticker='SGOV'):
    """
    Calculate annualized risk-free rate from treasury ETF

    Args:
        prices: DataFrame with price data
        ticker: Treasury ETF ticker (default: SGOV)

    Returns:
        Annualized risk-free rate (float)
    """
    if ticker in prices.columns:
        returns = prices[ticker].pct_change().dropna()
        return returns.mean() * TRADING_DAYS_PER_YEAR
    return DEFAULT_RISK_FREE_RATE
calculate_sharpe_ratio function · python · L79-L93 (15 LOC)
apps/backend/python/efficient_frontier.py
def calculate_sharpe_ratio(portfolio_return, portfolio_std, risk_free_rate):
    """
    Calculate Sharpe ratio with zero-volatility handling

    Args:
        portfolio_return: Expected portfolio return
        portfolio_std: Portfolio standard deviation
        risk_free_rate: Risk-free rate

    Returns:
        Sharpe ratio (float)
    """
    if portfolio_std > 0:
        return (portfolio_return - risk_free_rate) / portfolio_std
    return 0.0
calculate_portfolio_metrics function · python · L96-L117 (22 LOC)
apps/backend/python/efficient_frontier.py
def calculate_portfolio_metrics(weights, mu, cov_matrix, risk_free_rate):
    """
    Calculate portfolio return, volatility, and Sharpe ratio

    Args:
        weights: Portfolio weights (numpy array)
        mu: Expected returns vector
        cov_matrix: Covariance matrix
        risk_free_rate: Risk-free rate

    Returns:
        dict with return, volatility, and sharpeRatio
    """
    portfolio_return = np.dot(weights, mu)
    portfolio_std = np.sqrt(np.dot(weights.T, np.dot(cov_matrix, weights)))
    sharpe = calculate_sharpe_ratio(portfolio_return, portfolio_std, risk_free_rate)

    return {
        'return': float(portfolio_return),
        'volatility': float(portfolio_std),
        'sharpeRatio': float(sharpe)
    }
fetch_price_data function · python · L122-L191 (70 LOC)
apps/backend/python/efficient_frontier.py
def fetch_price_data(tickers, start_date=None, end_date=None):
    """
    Fetch historical adjusted close prices for given tickers

    Args:
        tickers: List of ticker symbols
        start_date: Start date (string YYYY-MM-DD or datetime, default: 3 years ago)
        end_date: End date (string YYYY-MM-DD or datetime, default: today)

    Returns:
        DataFrame with adjusted close prices
    """
    # Parse dates (always provided from frontend)
    end_date = parse_date(end_date)
    start_date = parse_date(start_date)

    # Validate date range
    if start_date >= end_date:
        raise ValueError(
            f"Start date ({start_date.date()}) must be before end date ({end_date.date()})"
        )

    # Check for future dates
    now = datetime.now()
    today = now.date()

    if end_date > now:
        raise ValueError(f"End date ({end_date.date()}) cannot be in the future")

    if start_date > now:
        raise ValueError(f"Start date ({start_date.date()}) cannot b
calculate_efficient_frontier function · python · L194-L283 (90 LOC)
apps/backend/python/efficient_frontier.py
def calculate_efficient_frontier(prices, num_portfolios=500):
    """
    Calculate efficient frontier using PyPortfolioOpt

    Args:
        prices: DataFrame with historical prices
        num_portfolios: Number of random portfolios to generate

    Returns:
        dict with GMV, Max Sharpe, and efficient frontier points
    """
    # Calculate expected returns and covariance matrix
    mu = expected_returns.mean_historical_return(prices)
    S = risk_models.sample_cov(prices)

    # Calculate risk-free rate
    risk_free_rate = calculate_annualized_risk_free_rate(prices)

    # === Global Minimum Variance (GMV) Portfolio ===
    ef_gmv = EfficientFrontier(mu, S)
    ef_gmv.min_volatility()
    gmv_weights = ef_gmv.clean_weights()
    gmv_performance = ef_gmv.portfolio_performance(verbose=False, risk_free_rate=risk_free_rate)

    # === Maximum Sharpe Ratio Portfolio ===
    ef_sharpe = EfficientFrontier(mu, S)
    ef_sharpe.max_sharpe(risk_free_rate=risk_free_rate)
    sharpe_weig
Want this analysis on your repo? https://repobility.com/scan/
fetch_benchmark_prices function · python · L286-L309 (24 LOC)
apps/backend/python/efficient_frontier.py
def fetch_benchmark_prices(prices, benchmark_ticker='SPY'):
    """
    Fetch or extract benchmark prices

    Args:
        prices: DataFrame with portfolio price data
        benchmark_ticker: Benchmark ticker symbol

    Returns:
        pandas Series of benchmark prices
    """
    if benchmark_ticker in prices.columns:
        return prices[benchmark_ticker]

    # Fetch benchmark data
    start_date = prices.index[0]
    end_date = prices.index[-1]
    benchmark = yf.Ticker(benchmark_ticker)
    benchmark_data = benchmark.history(start=start_date, end=end_date, auto_adjust=True)

    if benchmark_data.empty:
        raise ValueError(f"Failed to fetch benchmark data for {benchmark_ticker}")

    return benchmark_data['Close']
calculate_weighted_returns function · python · L312-L334 (23 LOC)
apps/backend/python/efficient_frontier.py
def calculate_weighted_returns(prices, portfolio_weights):
    """
    Calculate weighted portfolio return series

    Args:
        prices: DataFrame with historical prices
        portfolio_weights: Dict of ticker -> weight

    Returns:
        pandas Series of portfolio returns
    """
    # Get tickers with positive weights
    portfolio_tickers = [t for t in portfolio_weights.keys() if portfolio_weights[t] > 0]

    if not portfolio_tickers:
        return pd.Series(dtype=float)

    # Calculate returns for each asset
    portfolio_returns = prices[portfolio_tickers].pct_change().dropna()

    # Apply weights
    weights_array = np.array([portfolio_weights[t] for t in portfolio_tickers])
    return portfolio_returns.dot(weights_array)
calculate_portfolio_beta function · python · L337-L383 (47 LOC)
apps/backend/python/efficient_frontier.py
def calculate_portfolio_beta(prices, portfolio_weights, benchmark_ticker='SPY'):
    """
    Calculate portfolio beta against a benchmark using OLS regression

    Args:
        prices: DataFrame with historical prices
        portfolio_weights: Dict of ticker -> weight
        benchmark_ticker: Benchmark ticker (default: SPY)

    Returns:
        float: Portfolio beta
    """
    # Get benchmark prices
    benchmark_prices = fetch_benchmark_prices(prices, benchmark_ticker)

    # Calculate weighted portfolio returns
    portfolio_return_series = calculate_weighted_returns(prices, portfolio_weights)

    # Check if portfolio is empty
    if portfolio_return_series.empty:
        return 0.0

    # Calculate benchmark returns
    benchmark_returns = benchmark_prices.pct_change().dropna()

    # Align dates
    common_dates = portfolio_return_series.index.intersection(benchmark_returns.index)

    # Validate sufficient overlapping data
    validate_data_sufficiency(
        pd.Series(com
calculate_hedge_sizing function · python · L386-L426 (41 LOC)
apps/backend/python/efficient_frontier.py
def calculate_hedge_sizing(portfolio_beta, target_beta, portfolio_value, spy_price):
    """
    Calculate hedge sizing for SPY and ES futures

    Args:
        portfolio_beta: Current portfolio beta
        target_beta: Target beta (e.g., 0 for market-neutral)
        portfolio_value: Total portfolio value in dollars
        spy_price: Current SPY price

    Returns:
        dict with SPY and ES hedge sizing
    """
    # Validate SPY price
    if spy_price <= 0:
        raise ValueError(
            f"Invalid SPY price: {spy_price}. Cannot calculate hedge sizing with zero or negative price."
        )

    # Calculate required hedge notional
    hedge_notional = (portfolio_beta - target_beta) * portfolio_value

    # SPY shares to short
    spy_shares = int(hedge_notional / spy_price)
    spy_notional = spy_shares * spy_price

    # ES futures (assuming $50 multiplier and current ES price ~= SPY * 10)
    es_multiplier = 50
    es_price = spy_price * 10  # Approximate ES price
    e
main function · python · L429-L518 (90 LOC)
apps/backend/python/efficient_frontier.py
def main():
    """
    Main function - expects JSON input from stdin
    Expected input format:
    {
        "tickers": ["AAPL", "MSFT", "NVDA"],
        "quantities": [10, 20, 15],
        "portfolioValue": 100000,
        "targetBeta": 0,
        "startDate": "2023-01-01",
        "endDate": "2024-12-31"
    }
    """
    try:
        # Read input from stdin
        input_data = json.loads(sys.stdin.read())

        tickers = input_data.get('tickers', [])
        quantities = input_data.get('quantities', [])
        portfolio_value = input_data.get('portfolioValue', 100000)
        target_beta = input_data.get('targetBeta', 0)
        start_date = input_data['startDate']  # Required
        end_date = input_data['endDate']  # Required

        # Validate input
        if not tickers:
            raise ValueError("No tickers provided")

        if len(tickers) != len(quantities):
            raise ValueError(
                f"Tickers and quantities must have same length. "
        
AppService.getInfo method · typescript · L5-L12 (8 LOC)
apps/backend/src/app.service.ts
  getInfo() {
    return {
      name: 'Glassbox API',
      version: '0.1.0',
      description: 'Portfolio optimization and beta hedging API',
      docs: '/api',
    };
  }
AuthService.signup method · typescript · L18-L92 (75 LOC)
apps/backend/src/auth/auth.service.ts
  async signup(dto: SignupDto) {
    const timestamp = new Date().toISOString();

    // Audit log: signup attempt
    this.logger.log({
      event: 'SIGNUP_ATTEMPT',
      email: dto.email,
      timestamp,
    });

    try {
      // Check if user already exists
      const existingUser = await this.prisma.user.findUnique({
        where: { email: dto.email },
      });

      if (existingUser) {
        // Audit log: signup failed (duplicate)
        this.logger.warn({
          event: 'SIGNUP_FAILED',
          email: dto.email,
          reason: 'EMAIL_ALREADY_EXISTS',
          timestamp,
        });
        throw new ConflictException('Email already registered');
      }

      // Hash password
      const hashedPassword = await bcrypt.hash(dto.password, this.SALT_ROUNDS);

      // Create user
      const user = await this.prisma.user.create({
        data: {
          email: dto.email,
          password: hashedPassword,
          name: dto.name,
        },
        select: {
AuthService.login method · typescript · L94-L183 (90 LOC)
apps/backend/src/auth/auth.service.ts
  async login(dto: LoginDto) {
    const timestamp = new Date().toISOString();

    // Audit log: login attempt
    this.logger.log({
      event: 'LOGIN_ATTEMPT',
      email: dto.email,
      timestamp,
    });

    try {
      // Find user
      const user = await this.prisma.user.findUnique({
        where: { email: dto.email },
      });

      if (!user) {
        // Audit log: login failed (user not found)
        this.logger.warn({
          event: 'LOGIN_FAILED',
          email: dto.email,
          reason: 'USER_NOT_FOUND',
          timestamp,
        });
        throw new UnauthorizedException('Invalid credentials');
      }

      // Check if user has a password (not OAuth-only user)
      if (!user.password) {
        // Audit log: login failed (OAuth user trying manual login)
        this.logger.warn({
          event: 'LOGIN_FAILED',
          email: dto.email,
          userId: user.id,
          reason: 'OAUTH_ONLY_ACCOUNT',
          timestamp,
        });
        t
Powered by Repobility — scan your code at https://repobility.com
AuthService.validateUser method · typescript · L185-L195 (11 LOC)
apps/backend/src/auth/auth.service.ts
  async validateUser(userId: string) {
    return this.prisma.user.findUnique({
      where: { id: userId },
      select: {
        id: true,
        email: true,
        name: true,
        createdAt: true,
      },
    });
  }
AuthService.generateTokens method · typescript · L197-L209 (13 LOC)
apps/backend/src/auth/auth.service.ts
  private async generateTokens(userId: string, email: string) {
    const payload = { sub: userId, email };

    const accessToken = await this.jwtService.signAsync(payload, {
      expiresIn: '7d',
    });

    return {
      accessToken,
      tokenType: 'Bearer',
      expiresIn: 604800, // 7 days in seconds
    };
  }
JwtAuthGuard.canActivate method · typescript · L12-L23 (12 LOC)
apps/backend/src/auth/jwt-auth.guard.ts
  canActivate(context: ExecutionContext) {
    const isPublic = this.reflector.getAllAndOverride<boolean>(IS_PUBLIC_KEY, [
      context.getHandler(),
      context.getClass(),
    ]);

    if (isPublic) {
      return true;
    }

    return super.canActivate(context);
  }
JwtStrategy.constructor method · typescript · L10-L36 (27 LOC)
apps/backend/src/auth/jwt.strategy.ts
  constructor() {
    // Validate JWT secret before initializing strategy
    const secret = process.env.JWT_SECRET || process.env.NEXTAUTH_SECRET;
    if (!secret) {
      throw new Error(
        'JWT_SECRET or NEXTAUTH_SECRET environment variable must be set. ' +
        'Generate a secure secret with: node -e "console.log(require(\'crypto\').randomBytes(32).toString(\'hex\'))"'
      );
    }

    super({
      jwtFromRequest: ExtractJwt.fromExtractors([
        // Extract from cookie first
        (request: Request) => {
          const token = request?.cookies?.accessToken;
          if (!token) {
            this.logger.warn('No accessToken cookie found in request');
          }
          return token;
        },
        // Fallback to Authorization header (for backward compatibility)
        ExtractJwt.fromAuthHeaderAsBearerToken(),
      ]),
      ignoreExpiration: false,
      secretOrKey: secret,
    });
  }
JwtStrategy.validate method · typescript · L38-L43 (6 LOC)
apps/backend/src/auth/jwt.strategy.ts
  async validate(payload: any) {
    return {
      userId: payload.sub || payload.userId,
      email: payload.email,
    };
  }
LogAction function · typescript · L14-L43 (30 LOC)
apps/backend/src/common/decorators/log-action.decorator.ts
export function LogAction(actionName: string) {
  return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
    const originalMethod = descriptor.value;
    const logger = new Logger(`${target.constructor.name}.${propertyKey}`);

    descriptor.value = async function (...args: any[]) {
      const request = args[0];
      const context = extractContext(request);
      const timestamp = new Date().toISOString();

      try {
        logger.log({ event: `${actionName}_START`, ...context, timestamp });
        const result = await originalMethod.apply(this, args);
        logger.log({ event: `${actionName}_SUCCESS`, ...context, timestamp });
        return result;
      } catch (error) {
        logger.error({
          event: `${actionName}_FAILED`,
          ...context,
          error: error instanceof Error ? error.message : 'Unknown error',
          stack: error instanceof Error ? error.stack : undefined,
          timestamp,
        });
        throw erro
extractContext function · typescript · L45-L53 (9 LOC)
apps/backend/src/common/decorators/log-action.decorator.ts
function extractContext(obj: any): Record<string, any> {
  if (!obj || typeof obj !== 'object') return {};

  if (obj.user) return { userId: obj.user.userId, email: obj.user.email };
  if (obj.email) return { email: obj.email };
  if (obj.id) return { id: obj.id };

  return {};
}
ArrayLengthMatchConstraint.validate method · typescript · L14-L23 (10 LOC)
apps/backend/src/common/validators/array-length-match.validator.ts
  validate(value: any, args: ValidationArguments) {
    const [relatedPropertyName] = args.constraints;
    const relatedValue = (args.object as any)[relatedPropertyName];

    if (!Array.isArray(value) || !Array.isArray(relatedValue)) {
      return false;
    }

    return value.length === relatedValue.length;
  }
Repobility — the code-quality scanner for AI-generated software · https://repobility.com
ArrayLengthMatch function · typescript · L36-L49 (14 LOC)
apps/backend/src/common/validators/array-length-match.validator.ts
export function ArrayLengthMatch(
  property: string,
  validationOptions?: ValidationOptions,
) {
  return function (object: Object, propertyName: string) {
    registerDecorator({
      target: object.constructor,
      propertyName: propertyName,
      options: validationOptions,
      constraints: [property],
      validator: ArrayLengthMatchConstraint,
    });
  };
}
PinoLoggerService.constructor method · typescript · L8-L25 (18 LOC)
apps/backend/src/logger/pino-logger.service.ts
  constructor() {
    this.logger = pino({
      level: process.env.NODE_ENV === 'production' ? 'info' : 'debug',
      transport:
        process.env.NODE_ENV !== 'production'
          ? {
              target: 'pino-pretty',
              options: {
                colorize: true,
                translateTime: 'SYS:standard',
                singleLine: false,
                messageFormat: '{levelLabel} [{time}] {msg}',
                ignore: 'pid,hostname',
              },
            }
          : undefined,
    });
  }
bootstrap function · typescript · L9-L70 (62 LOC)
apps/backend/src/main.ts
async function bootstrap() {
  const app = await NestFactory.create(AppModule, {
    bufferLogs: true,
  });

  const logger = app.get(PinoLoggerService);
  app.useLogger(logger);

  // Security headers with Helmet
  app.use(helmet({
    contentSecurityPolicy: process.env.NODE_ENV === 'production' ? undefined : false,
    hsts: {
      maxAge: 31536000, // 1 year in seconds
      includeSubDomains: true,
      preload: true,
    },
    crossOriginEmbedderPolicy: false, // Allow embedding (if needed for Swagger)
  }));

  logger.log('Security headers enabled with Helmet');

  // Enable cookie parsing
  app.use(cookieParser());

  // Enable CORS for Next.js frontend
  const frontendUrl = process.env.FRONTEND_URL;
  if (!frontendUrl) {
    logger.warn('FRONTEND_URL not set, defaulting to http://localhost:3000');
  }

  app.enableCors({
    origin: frontendUrl || 'http://localhost:3000',
    credentials: true,
  });

  logger.log(`CORS enabled for origin: ${frontendUrl || 'http://localhost
PortfolioController.update method · typescript · L49-L55 (7 LOC)
apps/backend/src/portfolio/portfolio.controller.ts
  async update(
    @Request() req: any,
    @Param('id') id: string,
    @Body() dto: UpdatePortfolioDto,
  ) {
    return this.portfolioService.update(req.user.userId, id, dto);
  }
PortfolioService.create method · typescript · L19-L72 (54 LOC)
apps/backend/src/portfolio/portfolio.service.ts
  async create(userId: string, dto: CreatePortfolioDto): Promise<void> {
    try {
      // Check for duplicate portfolio name
      const existingPortfolio = await this.prisma.portfolio.findFirst({
        where: {
          userId,
          name: dto.name,
        },
      });

      if (existingPortfolio) {
        throw new ConflictException('A portfolio with this name already exists');
      }

      // Extract summary stats if analysis is present
      let stats: {
        sharpeRatio?: number | null;
        volatility?: number | null;
        expectedReturn?: number | null;
        analysisDate?: string | null;
        analysisEndDate?: string | null;
      } = {};
      if (dto.analysisSnapshot && dto.analysisSnapshot.maxSharpe?.stats) {
        stats = {
          sharpeRatio: dto.analysisSnapshot.maxSharpe.stats.sharpe,
          volatility: dto.analysisSnapshot.maxSharpe.stats.volatility,
          expectedReturn: dto.analysisSnapshot.maxSharpe.stats.return,
          anal
PortfolioService.findAll method · typescript · L74-L121 (48 LOC)
apps/backend/src/portfolio/portfolio.service.ts
  async findAll(userId: string) {
    // Fetch portfolios without the large analysisSnapshot JSON
    const portfolios = await this.prisma.portfolio.findMany({
      where: { userId },
      orderBy: { updatedAt: 'desc' },
      select: {
        id: true,
        userId: true,
        name: true,
        tickers: true,
        quantities: true,
        sharpeRatio: true,
        volatility: true,
        expectedReturn: true,
        analysisDate: true,
        analysisEndDate: true,
        createdAt: true,
        updatedAt: true,
        // Explicitly exclude analysisSnapshot to reduce payload size
      },
    });

    this.logger.log(`Retrieved ${portfolios.length} portfolios for user`);

    // Reconstruct the expected structure for frontend
    return portfolios.map((p) => {
      // If we have stats, reconstruct a "lite" snapshot
      let liteSnapshot: Prisma.JsonValue = null;
      if (p.sharpeRatio !== null && p.volatility !== null && p.expectedReturn !== null) {
        li
PortfolioService.findOne method · typescript · L123-L139 (17 LOC)
apps/backend/src/portfolio/portfolio.service.ts
  async findOne(userId: string, id: string) {
    const portfolio = await this.prisma.portfolio.findUnique({
      where: { id },
    });

    if (!portfolio) {
      this.logger.warn(`Portfolio not found: ${id}`);
      throw new NotFoundException(`Portfolio with ID ${id} not found`);
    }

    if (portfolio.userId !== userId) {
      this.logger.warn(`Unauthorized access attempt to portfolio: ${id}`);
      throw new ForbiddenException('You do not have access to this portfolio');
    }

    return portfolio;
  }
PortfolioService.update method · typescript · L141-L189 (49 LOC)
apps/backend/src/portfolio/portfolio.service.ts
  async update(userId: string, id: string, dto: UpdatePortfolioDto) {
    try {
      // Check existence and ownership first
      await this.findOne(userId, id);

      // Extract stats if updating snapshot
      let stats: {
        sharpeRatio?: number | null;
        volatility?: number | null;
        expectedReturn?: number | null;
        analysisDate?: string | null;
        analysisEndDate?: string | null;
      } = {};
      if (dto.analysisSnapshot && dto.analysisSnapshot.maxSharpe?.stats) {
        stats = {
          sharpeRatio: dto.analysisSnapshot.maxSharpe.stats.sharpe,
          volatility: dto.analysisSnapshot.maxSharpe.stats.volatility,
          expectedReturn: dto.analysisSnapshot.maxSharpe.stats.return,
          analysisDate: dto.analysisSnapshot.analysisDate,
          analysisEndDate: dto.analysisSnapshot.analysisEndDate,
        };
      }

      const result = await this.prisma.portfolio.update({
        where: { id },
        data: {
          ...(dto.name 
Methodology: Repobility · https://repobility.com/research/state-of-ai-code-2026/
PortfolioService.remove method · typescript · L191-L205 (15 LOC)
apps/backend/src/portfolio/portfolio.service.ts
  async remove(userId: string, id: string): Promise<void> {
    try {
      // Check existence and ownership first
      await this.findOne(userId, id);

      await this.prisma.portfolio.delete({
        where: { id },
      });

      this.logger.log(`Portfolio deleted: ${id}`);
    } catch (error) {
      this.logger.error(`Failed to delete portfolio ${id}: ${error instanceof Error ? error.message : 'Unknown error'}`);
      throw error;
    }
  }
PortfolioService.analyzePortfolio method · typescript · L212-L245 (34 LOC)
apps/backend/src/portfolio/portfolio.service.ts
  async analyzePortfolio(dto: AnalyzePortfolioDto): Promise<PortfolioAnalysisResultDto> {
    this.logger.log(`Analyzing portfolio with ${dto.tickers.length} assets`);

    // Set defaults
    const portfolioValue = dto.portfolioValue || 100000;
    const targetBeta = dto.targetBeta !== undefined ? dto.targetBeta : 0;
    const startDate = dto.startDate;
    const endDate = dto.endDate;

    // Execute Python script
    const result = await this.pythonExecutor.executeEfficientFrontier({
      tickers: dto.tickers,
      quantities: dto.quantities,
      portfolioValue,
      targetBeta,
      startDate,
      endDate,
    });

    this.logger.log('Portfolio analysis completed successfully');

    // Return formatted result
    return {
      gmv: result.gmv,
      maxSharpe: result.maxSharpe,
      efficientFrontier: result.efficientFrontier,
      randomPortfolios: result.randomPortfolios,
      portfolioBeta: result.portfolioBeta,
      hedging: result.hedging,
      riskFreeRate: re
PythonExecutorService.executeEfficientFrontier method · typescript · L68-L141 (74 LOC)
apps/backend/src/portfolio/python-executor.service.ts
  async executeEfficientFrontier(input: PythonExecutorInput): Promise<PythonExecutorResult> {
    this.logger.log(`Executing Python script for tickers: ${input.tickers.join(', ')}`);

    try {
      // Prepare options for PythonShell
      const options = {
        mode: 'text' as const,  // Use text mode to avoid JSON parsing issues
        pythonPath: 'python3', // or 'python' depending on system
        scriptPath: join(__dirname, '..', '..', 'python'),
        args: [],
      };

      const result = await new Promise<PythonExecutorResult>((resolve, reject) => {
        const pyshell = new PythonShell('efficient_frontier.py', options);

        // Send input data to Python script via stdin
        pyshell.send(JSON.stringify(input));
        pyshell.end((err) => {
          if (err) {
            this.logger.error('Python script execution error:', err);
            this.logger.error('Error details:', JSON.stringify(err, null, 2));
            reject(new Error(`Python script failed
PythonExecutorService.checkPythonEnvironment method · typescript · L147-L162 (16 LOC)
apps/backend/src/portfolio/python-executor.service.ts
  async checkPythonEnvironment(): Promise<boolean> {
    try {
      const options = {
        mode: 'text' as const,
        pythonPath: 'python3',
        args: ['--version'],
      };

      await PythonShell.run('--version', options);
      this.logger.log('Python environment is available');
      return true;
    } catch (error: unknown) {
      this.logger.error('Python environment not available:', error instanceof Error ? error.message : String(error));
      return false;
    }
  }
TickerService.searchTickers method · typescript · L35-L76 (42 LOC)
apps/backend/src/ticker/ticker.service.ts
  async searchTickers(query: string): Promise<TickerSearchResult[]> {
    if (!query || query.trim().length === 0) {
      throw new BadRequestException('Query parameter is required');
    }

    const sanitizedQuery = query.trim();
    const cacheKey = `ticker:search:${sanitizedQuery.toLowerCase()}`;

    // Try to get from cache
    const cached = await this.cacheManager.get<TickerSearchResult[]>(cacheKey);
    if (cached) {
      this.logger.debug(`Cache HIT: ${cacheKey}`);
      return cached;
    }

    this.logger.debug(`Cache MISS: ${cacheKey}`);

    try {
      // Fetch from Yahoo Finance
      const results = (await this.yahoo.search(sanitizedQuery)) as YahooFinanceSearchResponse;
      this.logger.debug('Yahoo Finance search results:', results);

      // Filter and transform results
      const filtered: TickerSearchResult[] = results.quotes
        .filter((q: YahooFinanceQuote) => q.isYahooFinance !== false && q.quoteType === 'EQUITY') // Only stocks from Yahoo Finance
  
TickerService.getQuote method · typescript · L78-L100 (23 LOC)
apps/backend/src/ticker/ticker.service.ts
  async getQuote(ticker: string) {
    const cacheKey = `ticker:quote:${ticker.toUpperCase()}`;

    const cached = await this.cacheManager.get(cacheKey);
    if (cached) {
      this.logger.debug(`Cache HIT: ${cacheKey}`);
      return cached;
    }

    this.logger.debug(`Cache MISS: ${cacheKey}`);

    try {
      const quote = await this.yahoo.quote(ticker);

      // Cache quotes for 5 minutes (300 seconds)
      await this.cacheManager.set(cacheKey, quote, 300);

      return quote;
    } catch (error) {
      this.logger.error('Error fetching quote from Yahoo Finance:', error);
      throw new InternalServerErrorException('Failed to get quote');
    }
  }
TickerService.getHistoricalPrices method · typescript · L102-L131 (30 LOC)
apps/backend/src/ticker/ticker.service.ts
  async getHistoricalPrices(
    ticker: string,
    startDate: string,
    endDate: string,
  ) {
    const cacheKey = `ticker:history:${ticker}:${startDate}:${endDate}`;

    const cached = await this.cacheManager.get(cacheKey);
    if (cached) {
      this.logger.debug(`Cache HIT: ${cacheKey}`);
      return cached;
    }

    this.logger.debug(`Cache MISS: ${cacheKey}`);

    try {
      const history = await this.yahoo.historical(ticker, {
        period1: startDate,
        period2: endDate,
      });

      // Cache historical data for 24 hours (86400 seconds)
      await this.cacheManager.set(cacheKey, history, 86400);

      return history;
    } catch (error) {
      this.logger.error('Error fetching historical prices from Yahoo Finance:', error);
      throw new InternalServerErrorException('Failed to get historical prices');
    }
  }
UsersController.changePassword method · typescript · L75-L81 (7 LOC)
apps/backend/src/users/users.controller.ts
  async changePassword(@Request() req: any, @Body() dto: ChangePasswordDto) {
    await this.usersService.changePassword(
      req.user.userId,
      dto.currentPassword,
      dto.newPassword,
    );
  }
Want this analysis on your repo? https://repobility.com/scan/
UsersService.syncUser method · typescript · L29-L79 (51 LOC)
apps/backend/src/users/users.service.ts
  async syncUser(dto: SyncUserDto) {
    const timestamp = new Date().toISOString();

    // Audit log: OAuth sync attempt
    this.logger.log({
      event: 'OAUTH_SYNC_ATTEMPT',
      email: dto.email,
      timestamp,
    });

    // Check if user exists
    const existingUser = await this.findByEmail(dto.email);

    if (existingUser) {
      // Audit log: OAuth user updated
      this.logger.log({
        event: 'OAUTH_USER_UPDATED',
        userId: existingUser.id,
        email: dto.email,
        timestamp,
      });

      // Update user info if changed
      return this.prisma.user.update({
        where: { email: dto.email },
        data: {
          name: dto.name,
          googleId: dto.googleId,
        },
      });
    }

    // Create new user
    const newUser = await this.prisma.user.create({
      data: {
        email: dto.email,
        name: dto.name,
        googleId: dto.googleId,
      },
    });

    // Audit log: OAuth user created
    this.logger.log({
   
UsersService.updateUserName method · typescript · L81-L87 (7 LOC)
apps/backend/src/users/users.service.ts
  async updateUserName(userId: string, name: string) {
    this.logger.log(`Updating user ${userId} name to: ${name}`);
    return this.prisma.user.update({
      where: { id: userId },
      data: { name },
    });
  }
UsersService.changePassword method · typescript · L89-L161 (73 LOC)
apps/backend/src/users/users.service.ts
  async changePassword(
    userId: string,
    currentPassword: string,
    newPassword: string,
  ) {
    const timestamp = new Date().toISOString();

    // Fetch user with password
    const user = await this.prisma.user.findUnique({
      where: { id: userId },
    });

    if (!user) {
      throw new UnauthorizedException('User not found');
    }

    // Check if user is OAuth user (no password)
    if (user.googleId) {
      this.logger.warn({
        event: 'PASSWORD_CHANGE_OAUTH_ATTEMPT',
        userId,
        timestamp,
      });
      throw new BadRequestException(
        'Cannot change password for OAuth accounts',
      );
    }

    // Verify user has a password
    if (!user.password) {
      throw new BadRequestException('User has no password set');
    }

    // Verify current password
    const isCurrentPasswordValid = await bcrypt.compare(
      currentPassword,
      user.password,
    );

    if (!isCurrentPasswordValid) {
      this.logger.warn({
        event
UsersService.deleteUser method · typescript · L163-L188 (26 LOC)
apps/backend/src/users/users.service.ts
  async deleteUser(userId: string) {
    const timestamp = new Date().toISOString();

    // Audit log: account deletion
    this.logger.warn({
      event: 'ACCOUNT_DELETED',
      userId,
      timestamp,
    });

    try {
      // Prisma will cascade delete portfolios due to onDelete: Cascade in schema
      return await this.prisma.user.delete({
        where: { id: userId },
      });
    } catch (error) {
      // Audit log: deletion error
      this.logger.error({
        event: 'ACCOUNT_DELETION_ERROR',
        userId,
        error: error instanceof Error ? error.message : 'Unknown error',
        timestamp,
      });
      throw error;
    }
  }
HedgingComparison function · typescript · L18-L95 (78 LOC)
apps/web/src/app/[lang]/analysis/result/components/HedgingComparison.tsx
export function HedgingComparison({ data }: { data: HedgingProps }) {
  const { t } = useTranslation();

  // Determine recommendation based on portfolio size/efficiency (simple logic: > $100k implies ES is efficient)
  // Since we assume $100k, we'll mark SPY as "Retail Friendly" and ES as "Capital Efficient"

  if (!data?.spy || !data?.es) return null;

  return (
    <div className="grid md:grid-cols-2 gap-6">
      {/* SPY Strategy */}
      <div className="glass-panel p-6 relative overflow-hidden group hover:border-cyan-400/30 transition-all">
        <div className="absolute top-0 right-0 bg-cyan-500/10 px-3 py-1 rounded-bl-xl border-b border-l border-cyan-500/20">
          <span className="text-xs font-bold text-cyan-600 dark:text-cyan-400">{t('analysis.hedging.spy.badge')}</span>
        </div>

        <div className="flex items-center gap-3 mb-6">
          <div className="p-3 rounded-xl bg-cyan-500/10 text-cyan-600 dark:text-cyan-400">
            <TrendingUp className="w-6
MetricCard function · typescript · L15-L37 (23 LOC)
apps/web/src/app/[lang]/analysis/result/components/KeyMetrics.tsx
function MetricCard({ label, value, subValue, icon: Icon, color }: MetricProps) {
  const colorStyles = {
    cyan: 'bg-cyan-500/10 text-cyan-600 dark:text-cyan-400 border-cyan-500/20',
    purple: 'bg-purple-500/10 text-purple-600 dark:text-purple-400 border-purple-500/20',
    pink: 'bg-pink-500/10 text-pink-600 dark:text-pink-400 border-pink-500/20',
    emerald: 'bg-emerald-500/10 text-emerald-600 dark:text-emerald-400 border-emerald-500/20',
  };

  return (
    <div className="glass-panel p-5 flex items-start justify-between group hover:scale-[1.02] transition-transform">
      <div>
        <p className="text-xs font-semibold text-black/50 dark:text-white/50 uppercase tracking-wider mb-1">{label}</p>
        <p className="text-2xl font-bold text-black dark:text-white mb-1">{value}</p>
        <p className="text-xs font-medium opacity-70 flex items-center gap-1">
          {subValue}
        </p>
      </div>
      <div className={`p-3 rounded-xl border ${colorStyles[color]} tran
KeyMetrics function · typescript · L51-L86 (36 LOC)
apps/web/src/app/[lang]/analysis/result/components/KeyMetrics.tsx
export function KeyMetrics({ stats }: { stats: PortfolioStats }) {
  const { t } = useTranslation();

  return (
    <div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4">
      <MetricCard
        label={t('analysis.metrics.return')}
        value={`${(stats.maxSharpe.return * 100).toFixed(1)}%`}
        subValue={t('analysis.metrics.return-sub')}
        icon={TrendingUp}
        color="emerald"
      />
      <MetricCard
        label={t('analysis.metrics.sharpe')}
        value={stats.maxSharpe.sharpe.toFixed(2)}
        subValue={t('analysis.metrics.sharpe-sub')}
        icon={Zap}
        color="purple"
      />
      <MetricCard
        label={t('analysis.metrics.volatility')}
        value={`${(stats.maxSharpe.volatility * 100).toFixed(1)}%`}
        subValue={`${t('analysis.metrics.volatility-sub')}: ${(stats.gmv.volatility * 100).toFixed(1)}%`}
        icon={Activity}
        color="pink"
      />
      <MetricCard
        label={t('analysis.metrics.beta')}
  
MarketScenarioSimulatorBase function · typescript · L12-L129 (118 LOC)
apps/web/src/app/[lang]/analysis/result/components/MarketScenarioSimulator.tsx
function MarketScenarioSimulatorBase({ beta, portfolioValue = 100000 }: MarketScenarioProps) {
  const { t } = useTranslation();

  // Scenarios: Market moves -5%, 0%, +5%
  const marketMoves = [-0.05, 0, 0.05];

  const data = marketMoves.map((marketMove) => {
    const marketChange = marketMove * 100;
    const unhedgedChange = marketMove * beta * 100;
    const hedgedChange = 0; // Ideally 0

    return {
      scenario: marketMove === 0 ? t('analysis.stress-test.flat-market') : marketMove > 0 ? t('analysis.stress-test.market-up') : t('analysis.stress-test.market-down'),
      Market: marketChange,
      Unhedged: unhedgedChange,
      Hedged: hedgedChange,

      // For Tooltip Value display
      unhedgedValue: portfolioValue * (1 + (marketMove * beta)),
      hedgedValue: portfolioValue,
    };
  });

  return (
    <div className="glass-panel p-6 space-y-6 hover:bg-cyan-500/5 hover:border-cyan-400/20 dark:hover:bg-cyan-500/10 dark:hover:border-cyan-400/30">
      <div className=
Powered by Repobility — scan your code at https://repobility.com
SavePortfolioModal function · typescript · L15-L149 (135 LOC)
apps/web/src/app/[lang]/analysis/result/components/SavePortfolioModal.tsx
export function SavePortfolioModal({ isOpen, onClose, onSave, error }: SavePortfolioModalProps) {
  const { t } = useTranslation();
  const [portfolioName, setPortfolioName] = useState('');
  const [isLoading, setIsLoading] = useState(false);

  // Reset name when modal opens
  useEffect(() => {
    if (isOpen) {
      setPortfolioName('');
    }
  }, [isOpen]);

  // Lock scroll when modal is open
  useScrollLock(isOpen);

  // Validation (duplicate check is now server-side)
  const trimmedName = portfolioName.trim();
  const isTooShort = trimmedName.length > 0 && trimmedName.length < 3;
  const isTooLong = trimmedName.length > 50;
  const isValid = trimmedName.length >= 3 && !isTooLong;

  // Keyboard shortcuts
  const handleKeyDown = (e: React.KeyboardEvent) => {
    if (e.key === 'Enter' && isValid && !isLoading) {
      e.preventDefault();
      handleSubmit();
    }
    if (e.key === 'Escape') {
      e.preventDefault();
      onClose();
    }
  };

  const handleSubmit = async (
EfficientFrontierChartBase function · typescript · L24-L176 (153 LOC)
apps/web/src/app/[lang]/analysis/result/efficient-frontier-chart.tsx
function EfficientFrontierChartBase({ data }: EfficientFrontierChartProps) {
  const { t } = useTranslation();
  const { efficientFrontier, gmv, maxSharpe, randomPortfolios } = data;

  // Format data for chart
  const frontierData = efficientFrontier.map((point) => ({
    x: point.volatility,
    y: point.return,
    sharpe: point.sharpeRatio,
    name: 'Efficient Frontier',
  }));

  // Add random portfolios if available (Monte Carlo simulation points)
  const randomData = randomPortfolios?.map((point) => ({
    x: point.volatility,
    y: point.return,
    sharpe: point.sharpeRatio,
    name: 'Random Portfolio',
  })) || [];

  const gmvData = {
    x: gmv.stats.volatility,
    y: gmv.stats.return,
    name: 'Global Min Variance',
  };

  const maxSharpeData = {
    x: maxSharpe.stats.volatility,
    y: maxSharpe.stats.return,
    name: 'Max Sharpe Ratio',
  };

  // Custom Tooltip
  const CustomTooltip = ({ active, payload, t }: any) => {
    const debouncedPayload = useDebounce(pa
CalculationErrorFallback function · typescript · L547-L583 (37 LOC)
apps/web/src/app/[lang]/analysis/result/page.tsx
function CalculationErrorFallback() {
  const { t } = useTranslation();
  return (
    <main className="min-h-screen px-6 py-8 flex items-center justify-center">
      <div className="glass-panel p-8 max-w-md w-full text-center space-y-6 border-orange-500/20 bg-orange-500/5">
        <div className="mx-auto w-16 h-16 rounded-full bg-orange-100 dark:bg-orange-900/30 flex items-center justify-center text-orange-500">
          <AlertCircle className="w-8 h-8" />
        </div>

        <div>
          <h2 className="text-xl font-bold text-black dark:text-white mb-2">
            {t('analysis.error.calculation-title')}
          </h2>
          <p className="text-sm text-black/60 dark:text-white/60">
            {t('analysis.error.calculation-message')}
          </p>
        </div>

        <div className="flex gap-3 justify-center">
          <Link
            href="/portfolio/new"
            className="px-4 py-2 rounded-lg bg-black/5 dark:bg-white/5 hover:bg-black/10 dark:hover:bg-whi
page 1 / 3next ›