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%
9.3 KB · 247 lines tsx
Raw Blame History
1/*2 * =============================================================================3 *  VibeQuant (vquant) — AI-Powered Financial Intelligence Platform4 * -----------------------------------------------------------------------------5 *  File:      client/src/components/financial/options-pricing-card.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 { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";18import { Badge } from "@/components/ui/badge";19import { TrendingUp, TrendingDown, DollarSign, Clock, Percent, Activity } from "lucide-react";2021interface OptionGreeks {22  delta: number;23  gamma: number;24  theta: number;25  vega: number;26  rho: number;27}2829interface OptionResult {30  price: number;31  intrinsic_value: number;32  time_value: number;33  greeks: OptionGreeks;34  moneyness: string;35}3637interface OptionsPricingData {38  parameters: {39    stock_price: number;40    strike_price: number;41    time_to_maturity: number;42    risk_free_rate: number;43    volatility: number;44    option_type: string;45  };46  call_option: OptionResult;47  put_option: OptionResult;48  parity_check: {49    call_minus_put: number;50    stock_minus_pv_strike: number;51    parity_holds: boolean;52  };53}5455interface OptionsPricingCardProps {56  data: OptionsPricingData;57}5859export function OptionsPricingCard({ data }: OptionsPricingCardProps) {60  const formatPrice = (value: number) => `$${value.toFixed(4)}`;61  const formatPercent = (value: number) => `${(value * 100).toFixed(2)}%`;62  const formatGreek = (value: number, decimals: number = 4) => value.toFixed(decimals);6364  const getMoneynessColor = (moneyness: string) => {65    switch (moneyness) {66      case 'ITM':67        return 'bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-200';68      case 'ATM':69        return 'bg-yellow-100 text-yellow-800 dark:bg-yellow-900 dark:text-yellow-200';70      case 'OTM':71        return 'bg-red-100 text-red-800 dark:bg-red-900 dark:text-red-200';72      default:73        return 'bg-gray-100 text-gray-800 dark:bg-gray-900 dark:text-gray-200';74    }75  };7677  const GreekIndicator = ({ label, value, tooltip }: { label: string; value: number; tooltip: string }) => (78    <div className="flex items-center justify-between py-2 border-b border-border/40 last:border-0">79      <div className="flex items-center gap-2">80        <span className="text-sm font-medium text-muted-foreground">{label}</span>81        <span className="text-xs text-muted-foreground/60" title={tooltip}>ⓘ</span>82      </div>83      <span className={`text-sm font-semibold ${value >= 0 ? 'text-green-600 dark:text-green-400' : 'text-red-600 dark:text-red-400'}`}>84        {formatGreek(value)}85      </span>86    </div>87  );8889  const OptionCard = ({ title, option, icon }: { title: string; option: OptionResult; icon: React.ReactNode }) => (90    <Card className="h-full">91      <CardHeader>92        <div className="flex items-center justify-between">93          <div className="flex items-center gap-2">94            {icon}95            <CardTitle className="text-lg">{title}</CardTitle>96          </div>97          <Badge className={getMoneynessColor(option.moneyness)}>98            {option.moneyness}99          </Badge>100        </div>101      </CardHeader>102      <CardContent className="space-y-4">103        {/* Option Price */}104        <div className="p-4 bg-primary/5 rounded-lg">105          <div className="text-sm text-muted-foreground mb-1">Option Price</div>106          <div className="text-3xl font-bold text-primary">{formatPrice(option.price)}</div>107        </div>108109        {/* Value Breakdown */}110        <div className="grid grid-cols-2 gap-3">111          <div className="p-3 bg-muted/50 rounded-lg">112            <div className="text-xs text-muted-foreground mb-1">Intrinsic</div>113            <div className="text-lg font-semibold">{formatPrice(option.intrinsic_value)}</div>114          </div>115          <div className="p-3 bg-muted/50 rounded-lg">116            <div className="text-xs text-muted-foreground mb-1">Time Value</div>117            <div className="text-lg font-semibold">{formatPrice(option.time_value)}</div>118          </div>119        </div>120121        {/* Greeks */}122        <div className="pt-2">123          <div className="text-sm font-semibold mb-3 flex items-center gap-2">124            <Activity className="h-4 w-4" />125            Greeks (Sensitivities)126          </div>127          <div className="space-y-1">128            <GreekIndicator129              label="Delta (Δ)"130              value={option.greeks.delta}131              tooltip="Change in option price for $1 change in stock price"132            />133            <GreekIndicator134              label="Gamma (Γ)"135              value={option.greeks.gamma}136              tooltip="Rate of change of Delta"137            />138            <GreekIndicator139              label="Theta (Θ)"140              value={option.greeks.theta}141              tooltip="Time decay - change in price per day"142            />143            <GreekIndicator144              label="Vega (ν)"145              value={option.greeks.vega}146              tooltip="Change in price for 1% change in volatility"147            />148            <GreekIndicator149              label="Rho (ρ)"150              value={option.greeks.rho}151              tooltip="Change in price for 1% change in interest rate"152            />153          </div>154        </div>155      </CardContent>156    </Card>157  );158159  return (160    <div className="space-y-6">161      {/* Parameters Header */}162      <Card>163        <CardHeader>164          <CardTitle className="flex items-center gap-2">165            <DollarSign className="h-5 w-5" />166            Black-Scholes Option Pricing167          </CardTitle>168          <CardDescription>Theoretical option values and Greeks for European options</CardDescription>169        </CardHeader>170        <CardContent>171          <div className="grid grid-cols-2 md:grid-cols-5 gap-4">172            <div className="space-y-1">173              <div className="text-xs text-muted-foreground">Stock Price</div>174              <div className="text-lg font-semibold">{formatPrice(data.parameters.stock_price)}</div>175            </div>176            <div className="space-y-1">177              <div className="text-xs text-muted-foreground">Strike Price</div>178              <div className="text-lg font-semibold">{formatPrice(data.parameters.strike_price)}</div>179            </div>180            <div className="space-y-1">181              <div className="text-xs text-muted-foreground flex items-center gap-1">182                <Clock className="h-3 w-3" />183                Time to Expiry184              </div>185              <div className="text-lg font-semibold">{data.parameters.time_to_maturity.toFixed(4)} yr</div>186              <div className="text-xs text-muted-foreground">{(data.parameters.time_to_maturity * 365).toFixed(0)} days</div>187            </div>188            <div className="space-y-1">189              <div className="text-xs text-muted-foreground flex items-center gap-1">190                <Percent className="h-3 w-3" />191                Volatility192              </div>193              <div className="text-lg font-semibold">{formatPercent(data.parameters.volatility)}</div>194            </div>195            <div className="space-y-1">196              <div className="text-xs text-muted-foreground">Risk-Free Rate</div>197              <div className="text-lg font-semibold">{formatPercent(data.parameters.risk_free_rate)}</div>198            </div>199          </div>200        </CardContent>201      </Card>202203      {/* Call and Put Options Side by Side */}204      <div className="grid md:grid-cols-2 gap-4">205        <OptionCard206          title="Call Option"207          option={data.call_option}208          icon={<TrendingUp className="h-5 w-5 text-green-600" />}209        />210        <OptionCard211          title="Put Option"212          option={data.put_option}213          icon={<TrendingDown className="h-5 w-5 text-red-600" />}214        />215      </div>216217      {/* Put-Call Parity Check */}218      <Card>219        <CardHeader>220          <CardTitle className="text-base">Put-Call Parity Verification</CardTitle>221          <CardDescription className="text-xs">222            Verifies that C - P = S - K×e^(-rT)223          </CardDescription>224        </CardHeader>225        <CardContent>226          <div className="grid grid-cols-3 gap-4">227            <div className="space-y-1">228              <div className="text-xs text-muted-foreground">Call - Put</div>229              <div className="text-sm font-semibold">{formatPrice(data.parity_check.call_minus_put)}</div>230            </div>231            <div className="space-y-1">232              <div className="text-xs text-muted-foreground">S - PV(K)</div>233              <div className="text-sm font-semibold">{formatPrice(data.parity_check.stock_minus_pv_strike)}</div>234            </div>235            <div className="space-y-1">236              <div className="text-xs text-muted-foreground">Parity Check</div>237              <Badge variant={data.parity_check.parity_holds ? "default" : "destructive"}>238                {data.parity_check.parity_holds ? '✓ Holds' : '✗ Failed'}239              </Badge>240            </div>241          </div>242        </CardContent>243      </Card>244    </div>245  );246}247