SPB Git

spb/vquant Public MIT

VibeQuant — AI-powered institutional-grade financial intelligence platform.

TypeScript 84.3% Python 11.7% JavaScript 1.6% CSS 1.5% HTML 0.7%
10.6 KB · 227 lines tsx
Raw Blame History
1/*2 * =============================================================================3 *  VibeQuant (vquant) — AI-Powered Financial Intelligence Platform4 * -----------------------------------------------------------------------------5 *  File:      client/src/components/chat/monte-carlo-results.tsx6 *7 *  Author:    Simon-Pierre Boucher8 *  Contact:   contact@spboucher.ai9 *  Website:   https://www.spboucher.ai10 *  Demo:      https://www.vquant.ai11 *  License:   MIT (see LICENSE)12 *13 *  Copyright © 2026 Simon-Pierre Boucher. All rights reserved.14 * =============================================================================15 */1617import { motion } from "framer-motion";18import { Card, CardHeader, CardTitle, CardContent } from "@/components/ui/card";19import { TrendingUp, Code2, BarChart3, DollarSign } from "lucide-react";20import { Badge } from "@/components/ui/badge";21import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";2223interface MonteCarloResultsProps {24  pythonCode?: string;25  results?: {26    success: boolean;27    parameters?: {28      num_simulations: number;29      time_horizon: number;30      initial_investment: number;31      mean_return: number;32      std_return: number;33    };34    statistics?: {35      mean_final_value: number;36      median_final_value: number;37      std_final_value: number;38      min_final_value: number;39      max_final_value: number;40      percentile_5: number;41      percentile_25: number;42      percentile_75: number;43      percentile_95: number;44      probability_of_profit: number;45    };46  };47}4849export function MonteCarloResults({ pythonCode, results }: MonteCarloResultsProps) {50  if (!pythonCode && !results) return null;5152  const formatCurrency = (value: number) => {53    return new Intl.NumberFormat('fr-FR', {54      style: 'currency',55      currency: 'USD',56      minimumFractionDigits: 2,57      maximumFractionDigits: 258    }).format(value);59  };6061  const formatPercent = (value: number) => {62    return `${value.toFixed(2)}%`;63  };6465  return (66    <motion.div67      initial={{ opacity: 0, y: 8 }}68      animate={{ opacity: 1, y: 0 }}69      transition={{ duration: 0.3 }}70      className="w-full max-w-5xl mx-auto"71      data-testid="monte-carlo-results"72    >73      <Card className="border-primary/20 shadow-lg">74        <CardHeader className="gap-2">75          <div className="flex items-center gap-2">76            <BarChart3 className="w-5 h-5 text-primary" />77            <CardTitle className="text-xl">Simulation Monte Carlo</CardTitle>78          </div>79          <p className="text-sm text-muted-foreground">80            Analyse de risque et projection des rendements futurs81          </p>82        </CardHeader>83        <CardContent>84          <Tabs defaultValue="results" className="w-full">85            <TabsList className="grid w-full grid-cols-2 mb-4" data-testid="tabs-monte-carlo">86              <TabsTrigger value="results" data-testid="tab-results">87                <TrendingUp className="w-4 h-4 mr-2" />88                Résultats89              </TabsTrigger>90              <TabsTrigger value="code" data-testid="tab-code">91                <Code2 className="w-4 h-4 mr-2" />92                Code Python93              </TabsTrigger>94            </TabsList>9596            <TabsContent value="results" data-testid="content-results">97              {results?.success && results.statistics && results.parameters ? (98                <div className="space-y-6">99                  {/* Parameters */}100                  <div className="bg-muted/30 rounded-lg p-4 space-y-2">101                    <h3 className="font-semibold text-sm text-muted-foreground mb-3">Paramètres de simulation</h3>102                    <div className="grid grid-cols-2 md:grid-cols-4 gap-3">103                      <div className="space-y-1" data-testid="param-simulations">104                        <p className="text-xs text-muted-foreground">Simulations</p>105                        <p className="text-sm font-medium">{results.parameters.num_simulations.toLocaleString()}</p>106                      </div>107                      <div className="space-y-1" data-testid="param-horizon">108                        <p className="text-xs text-muted-foreground">Horizon (jours)</p>109                        <p className="text-sm font-medium">{results.parameters.time_horizon}</p>110                      </div>111                      <div className="space-y-1" data-testid="param-investment">112                        <p className="text-xs text-muted-foreground">Investissement initial</p>113                        <p className="text-sm font-medium">{formatCurrency(results.parameters.initial_investment)}</p>114                      </div>115                      <div className="space-y-1" data-testid="param-volatility">116                        <p className="text-xs text-muted-foreground">Volatilité</p>117                        <p className="text-sm font-medium">{formatPercent(results.parameters.std_return * 100)}</p>118                      </div>119                    </div>120                  </div>121122                  {/* Key Statistics */}123                  <div className="grid grid-cols-1 md:grid-cols-3 gap-4">124                    <Card data-testid="stat-mean">125                      <CardContent className="pt-6">126                        <div className="flex items-center gap-2 mb-2">127                          <DollarSign className="w-4 h-4 text-primary" />128                          <p className="text-sm text-muted-foreground">Valeur moyenne</p>129                        </div>130                        <p className="text-2xl font-bold">{formatCurrency(results.statistics.mean_final_value)}</p>131                      </CardContent>132                    </Card>133                    134                    <Card data-testid="stat-median">135                      <CardContent className="pt-6">136                        <div className="flex items-center gap-2 mb-2">137                          <DollarSign className="w-4 h-4 text-primary" />138                          <p className="text-sm text-muted-foreground">Valeur médiane</p>139                        </div>140                        <p className="text-2xl font-bold">{formatCurrency(results.statistics.median_final_value)}</p>141                      </CardContent>142                    </Card>143144                    <Card data-testid="stat-profit-prob">145                      <CardContent className="pt-6">146                        <div className="flex items-center gap-2 mb-2">147                          <TrendingUp className="w-4 h-4 text-primary" />148                          <p className="text-sm text-muted-foreground">Probabilité de profit</p>149                        </div>150                        <p className="text-2xl font-bold">{formatPercent(results.statistics.probability_of_profit)}</p>151                      </CardContent>152                    </Card>153                  </div>154155                  {/* Percentiles */}156                  <div className="bg-muted/30 rounded-lg p-4 space-y-3">157                    <h3 className="font-semibold text-sm text-muted-foreground mb-3">Distribution des résultats</h3>158                    <div className="space-y-2">159                      <div className="flex justify-between items-center" data-testid="percentile-5">160                        <span className="text-sm text-muted-foreground">5e percentile (pire scénario probable)</span>161                        <Badge variant="outline">{formatCurrency(results.statistics.percentile_5)}</Badge>162                      </div>163                      <div className="flex justify-between items-center" data-testid="percentile-25">164                        <span className="text-sm text-muted-foreground">25e percentile</span>165                        <Badge variant="outline">{formatCurrency(results.statistics.percentile_25)}</Badge>166                      </div>167                      <div className="flex justify-between items-center" data-testid="percentile-75">168                        <span className="text-sm text-muted-foreground">75e percentile</span>169                        <Badge variant="outline">{formatCurrency(results.statistics.percentile_75)}</Badge>170                      </div>171                      <div className="flex justify-between items-center" data-testid="percentile-95">172                        <span className="text-sm text-muted-foreground">95e percentile (meilleur scénario probable)</span>173                        <Badge variant="outline">{formatCurrency(results.statistics.percentile_95)}</Badge>174                      </div>175                    </div>176                  </div>177178                  {/* Range */}179                  <div className="bg-muted/30 rounded-lg p-4">180                    <h3 className="font-semibold text-sm text-muted-foreground mb-3">Plage de résultats</h3>181                    <div className="flex justify-between items-center">182                      <div className="text-center" data-testid="min-value">183                        <p className="text-xs text-muted-foreground mb-1">Minimum</p>184                        <p className="text-sm font-medium">{formatCurrency(results.statistics.min_final_value)}</p>185                      </div>186                      <div className="flex-1 mx-4">187                        <div className="h-2 bg-gradient-to-r from-red-500 via-yellow-500 to-green-500 rounded-full" />188                      </div>189                      <div className="text-center" data-testid="max-value">190                        <p className="text-xs text-muted-foreground mb-1">Maximum</p>191                        <p className="text-sm font-medium">{formatCurrency(results.statistics.max_final_value)}</p>192                      </div>193                    </div>194                  </div>195                </div>196              ) : (197                <div className="text-center py-8 text-muted-foreground">198                  Aucun résultat disponible199                </div>200              )}201            </TabsContent>202203            <TabsContent value="code" data-testid="content-code">204              {pythonCode ? (205                <div className="relative">206                  <div className="absolute top-2 right-2 z-10">207                    <Badge variant="outline" className="text-xs">208                      Python209                    </Badge>210                  </div>211                  <pre className="bg-muted p-4 rounded-lg overflow-x-auto text-sm font-mono" data-testid="python-code-block">212                    <code className="text-foreground">{pythonCode}</code>213                  </pre>214                </div>215              ) : (216                <div className="text-center py-8 text-muted-foreground">217                  Aucun code disponible218                </div>219              )}220            </TabsContent>221          </Tabs>222        </CardContent>223      </Card>224    </motion.div>225  );226}227