update calculator
This commit is contained in:
parent
1af68b024b
commit
ef9fdca0b6
38
src/routes/api/options-calculator/+server.ts
Normal file
38
src/routes/api/options-calculator/+server.ts
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
import type { RequestHandler } from "./$types";
|
||||||
|
|
||||||
|
export const POST: RequestHandler = async ({ request, locals }) => {
|
||||||
|
const data = await request.json();
|
||||||
|
const { apiURL, apiKey } = locals;
|
||||||
|
|
||||||
|
const postData = { ticker: data?.ticker };
|
||||||
|
|
||||||
|
// First API call: contract lookup summary
|
||||||
|
const contractResponse = await fetch(apiURL + "/contract-lookup-summary", {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
"X-API-KEY": apiKey,
|
||||||
|
},
|
||||||
|
body: JSON.stringify(postData),
|
||||||
|
});
|
||||||
|
|
||||||
|
const contractOutput = await contractResponse.json();
|
||||||
|
|
||||||
|
// Second API call: stock quote
|
||||||
|
const stockResponse = await fetch(apiURL + "/stock-quote", {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
"X-API-KEY": apiKey,
|
||||||
|
},
|
||||||
|
body: JSON.stringify(postData),
|
||||||
|
});
|
||||||
|
|
||||||
|
const stockOutput = await stockResponse.json();
|
||||||
|
|
||||||
|
// Combine both outputs into a single object
|
||||||
|
const output = { getData: contractOutput, getStockQuote: stockOutput };
|
||||||
|
|
||||||
|
|
||||||
|
return new Response(JSON.stringify(output));
|
||||||
|
};
|
||||||
@ -1,49 +0,0 @@
|
|||||||
export const load = async ({ locals }) => {
|
|
||||||
const { apiKey, apiURL } = locals;
|
|
||||||
|
|
||||||
const getData = async () => {
|
|
||||||
const postData = {
|
|
||||||
ticker: 'TSLA',
|
|
||||||
};
|
|
||||||
|
|
||||||
const response = await fetch(apiURL + "/contract-lookup-summary", {
|
|
||||||
method: "POST",
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
"X-API-KEY": apiKey,
|
|
||||||
},
|
|
||||||
body: JSON.stringify(postData),
|
|
||||||
});
|
|
||||||
|
|
||||||
const output = await response.json();
|
|
||||||
|
|
||||||
|
|
||||||
return output;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
const getStockQuote = async () => {
|
|
||||||
const postData = { ticker: 'TSLA' };
|
|
||||||
const response = await fetch(apiURL + "/stock-quote", {
|
|
||||||
method: "POST",
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
"X-API-KEY": apiKey,
|
|
||||||
},
|
|
||||||
body: JSON.stringify(postData),
|
|
||||||
});
|
|
||||||
|
|
||||||
const output = await response.json();
|
|
||||||
return output;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Make sure to return a promise
|
|
||||||
return {
|
|
||||||
getData: await getData(),
|
|
||||||
getStockQuote: await getStockQuote(),
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
@ -4,7 +4,8 @@
|
|||||||
import SEO from "$lib/components/SEO.svelte";
|
import SEO from "$lib/components/SEO.svelte";
|
||||||
import { onMount, onDestroy } from "svelte";
|
import { onMount, onDestroy } from "svelte";
|
||||||
import { abbreviateNumber, buildOptionSymbol } from "$lib/utils";
|
import { abbreviateNumber, buildOptionSymbol } from "$lib/utils";
|
||||||
import { setCache, getCache } from "$lib/store";
|
import { setCache, getCache, screenWidth } from "$lib/store";
|
||||||
|
import { Combobox } from "bits-ui";
|
||||||
|
|
||||||
import { mode } from "mode-watcher";
|
import { mode } from "mode-watcher";
|
||||||
import highcharts from "$lib/highcharts.ts";
|
import highcharts from "$lib/highcharts.ts";
|
||||||
@ -21,23 +22,25 @@
|
|||||||
let selectedQuantity = 1;
|
let selectedQuantity = 1;
|
||||||
let debounceTimeout;
|
let debounceTimeout;
|
||||||
|
|
||||||
let currentStockPrice = data?.getStockQuote?.price;
|
let currentStockPrice;
|
||||||
|
|
||||||
let optionData = data?.getData[selectedOptionType];
|
let optionData = {};
|
||||||
let dateList = Object?.keys(optionData);
|
let dateList = [];
|
||||||
let selectedDate = Object?.keys(optionData)[0];
|
let selectedDate;
|
||||||
let strikeList = optionData[selectedDate] || [];
|
let strikeList = [];
|
||||||
let selectedStrike = strikeList.reduce((closest, strike) => {
|
let selectedStrike;
|
||||||
return Math.abs(strike - currentStockPrice) <
|
|
||||||
Math.abs(closest - currentStockPrice)
|
|
||||||
? strike
|
|
||||||
: closest;
|
|
||||||
}, strikeList[0]);
|
|
||||||
|
|
||||||
let optionSymbol;
|
let optionSymbol;
|
||||||
let breakEvenPrice;
|
let breakEvenPrice;
|
||||||
let premium;
|
let premium;
|
||||||
let limits = {};
|
let limits = {};
|
||||||
|
let rawData = {};
|
||||||
|
|
||||||
|
let searchBarData = [];
|
||||||
|
let timeoutId;
|
||||||
|
|
||||||
|
let inputValue = selectedTicker;
|
||||||
|
let touchedInput = false;
|
||||||
|
|
||||||
let strategies = [
|
let strategies = [
|
||||||
{
|
{
|
||||||
@ -173,24 +176,48 @@
|
|||||||
if (scenarioKey === "Buy Call") {
|
if (scenarioKey === "Buy Call") {
|
||||||
limits = {
|
limits = {
|
||||||
maxProfit: "Unlimited",
|
maxProfit: "Unlimited",
|
||||||
maxLoss: `-$${premium?.toLocaleString("en-US")}`,
|
maxLoss: `-$${premium?.toLocaleString("en-US", {
|
||||||
|
minimumFractionDigits: 2,
|
||||||
|
maximumFractionDigits: 2,
|
||||||
|
})}`,
|
||||||
};
|
};
|
||||||
} else if (scenarioKey === "Sell Call") {
|
} else if (scenarioKey === "Sell Call") {
|
||||||
limits = {
|
limits = {
|
||||||
maxProfit: `+$${premium?.toLocaleString("en-US")}`,
|
maxProfit: `$${premium?.toLocaleString("en-US", {
|
||||||
|
minimumFractionDigits: 2,
|
||||||
|
maximumFractionDigits: 2,
|
||||||
|
})}`,
|
||||||
maxLoss: "Unlimited",
|
maxLoss: "Unlimited",
|
||||||
};
|
};
|
||||||
} else if (scenarioKey === "Buy Put") {
|
} else if (scenarioKey === "Buy Put") {
|
||||||
limits = {
|
limits = {
|
||||||
// Maximum profit when underlying goes to 0
|
// Maximum profit when underlying goes to 0
|
||||||
maxProfit: `+$${(selectedStrike * 100 - premium)?.toLocaleString("en-US")}`,
|
maxProfit: `$${(selectedStrike * 100 - premium)?.toLocaleString(
|
||||||
maxLoss: `-$${premium?.toLocaleString("en-US")}`,
|
"en-US",
|
||||||
|
{
|
||||||
|
minimumFractionDigits: 2,
|
||||||
|
maximumFractionDigits: 2,
|
||||||
|
},
|
||||||
|
)}`,
|
||||||
|
maxLoss: `-$${premium?.toLocaleString("en-US", {
|
||||||
|
minimumFractionDigits: 2,
|
||||||
|
maximumFractionDigits: 2,
|
||||||
|
})}`,
|
||||||
};
|
};
|
||||||
} else if (scenarioKey === "Sell Put") {
|
} else if (scenarioKey === "Sell Put") {
|
||||||
limits = {
|
limits = {
|
||||||
maxProfit: `+$${premium?.toLocaleString("en-US")}`,
|
maxProfit: `$${premium?.toLocaleString("en-US", {
|
||||||
|
minimumFractionDigits: 2,
|
||||||
|
maximumFractionDigits: 2,
|
||||||
|
})}`,
|
||||||
// Maximum loss when underlying goes to 0
|
// Maximum loss when underlying goes to 0
|
||||||
maxLoss: `-$${(selectedStrike * 100 - premium)?.toLocaleString("en-US")}`,
|
maxLoss: `-$${(selectedStrike * 100 - premium)?.toLocaleString(
|
||||||
|
"en-US",
|
||||||
|
{
|
||||||
|
minimumFractionDigits: 2,
|
||||||
|
maximumFractionDigits: 2,
|
||||||
|
},
|
||||||
|
)}`,
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
console.error("Limits not defined for scenario:", scenarioKey);
|
console.error("Limits not defined for scenario:", scenarioKey);
|
||||||
@ -245,22 +272,23 @@
|
|||||||
// Underlying Price line
|
// Underlying Price line
|
||||||
{
|
{
|
||||||
value: currentStockPrice,
|
value: currentStockPrice,
|
||||||
color: "black",
|
color: $mode === "light" ? "black" : "white",
|
||||||
dashStyle: "Dash",
|
dashStyle: "Dash",
|
||||||
width: 1.2,
|
width: 1.2,
|
||||||
label: {
|
label: {
|
||||||
text: `<span class="text-black dark:text-white">Underlying Price $${currentStockPrice}</span>`,
|
text: `<span class="text-black dark:text-white">Underlying Price $${currentStockPrice}</span>`,
|
||||||
|
style: { color: $mode === "light" ? "black" : "white" },
|
||||||
},
|
},
|
||||||
zIndex: 5,
|
zIndex: 5,
|
||||||
},
|
},
|
||||||
// Break-Even line
|
|
||||||
{
|
{
|
||||||
value: breakEvenPrice,
|
value: breakEvenPrice,
|
||||||
color: "#10B981",
|
color: "#10B981",
|
||||||
dashStyle: "Dash",
|
dashStyle: "Dash",
|
||||||
width: 1.2,
|
width: $screenWidth < 640 ? 0 : 1.2,
|
||||||
label: {
|
label: {
|
||||||
text: `<span class="text-black dark:text-white">Breakeven $${breakEvenPrice.toFixed(2)}</span>`,
|
text: `<span class="hidden sm:block text-black dark:text-white">Breakeven $${breakEvenPrice.toFixed(2)}</span>`,
|
||||||
|
style: { color: $mode === "light" ? "black" : "white" },
|
||||||
},
|
},
|
||||||
zIndex: 5,
|
zIndex: 5,
|
||||||
},
|
},
|
||||||
@ -282,11 +310,16 @@
|
|||||||
},
|
},
|
||||||
tooltip: {
|
tooltip: {
|
||||||
shared: true,
|
shared: true,
|
||||||
backgroundColor: $mode === "light" ? "#f9fafb" : "#1f2937",
|
useHTML: true,
|
||||||
borderColor: "#6b7280",
|
backgroundColor: "rgba(0, 0, 0, 0.8)", // Semi-transparent black
|
||||||
|
borderColor: "rgba(255, 255, 255, 0.2)", // Slightly visible white border
|
||||||
|
borderWidth: 1,
|
||||||
style: {
|
style: {
|
||||||
color: $mode === "light" ? "black" : "white",
|
color: "#fff",
|
||||||
|
fontSize: "16px",
|
||||||
|
padding: "10px",
|
||||||
},
|
},
|
||||||
|
borderRadius: 2,
|
||||||
formatter: function () {
|
formatter: function () {
|
||||||
const underlyingPrice = this.x;
|
const underlyingPrice = this.x;
|
||||||
const profitLoss = this.y;
|
const profitLoss = this.y;
|
||||||
@ -297,15 +330,16 @@
|
|||||||
const profitLossPctChange = (profitLoss / premium) * 100;
|
const profitLossPctChange = (profitLoss / premium) * 100;
|
||||||
|
|
||||||
return `
|
return `
|
||||||
<div class="flex flex-col items-start">
|
<div class="flex flex-col items-start text-sm">
|
||||||
<div>
|
<div>
|
||||||
<span class="text-start text-muted font-semibold">Underlying Price:</span>
|
<span class="text-start font-semibold">Underlying Price:</span>
|
||||||
$${underlyingPrice}
|
$${underlyingPrice}
|
||||||
(<span>${underlyingPctChange.toFixed(2)}%</span>)
|
(<span>${underlyingPctChange.toFixed(2)}%</span>)
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<br>
|
||||||
<span class="text-start text-muted font-semibold">Profit or Loss:</span>
|
<div class="">
|
||||||
$${profitLoss.toLocaleString("en-US")}
|
<span class="text-start font-semibold">Profit or Loss:</span>
|
||||||
|
${profitLoss < 0 ? "-$" : "$"}${Math.abs(profitLoss).toLocaleString("en-US")}
|
||||||
(<span>${profitLossPctChange.toFixed(2)}%</span>)
|
(<span>${profitLossPctChange.toFixed(2)}%</span>)
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -377,38 +411,6 @@
|
|||||||
return output;
|
return output;
|
||||||
};
|
};
|
||||||
|
|
||||||
async function loadData(state: string) {
|
|
||||||
isLoaded = false;
|
|
||||||
optionData = data?.getData[selectedOptionType];
|
|
||||||
|
|
||||||
dateList = [...Object?.keys(optionData)];
|
|
||||||
|
|
||||||
strikeList = [...optionData[selectedDate]];
|
|
||||||
|
|
||||||
if (!strikeList?.includes(selectedStrike)) {
|
|
||||||
selectedStrike = strikeList.reduce((closest, strike) => {
|
|
||||||
return Math.abs(strike - currentStockPrice) <
|
|
||||||
Math.abs(closest - currentStockPrice)
|
|
||||||
? strike
|
|
||||||
: closest;
|
|
||||||
}, strikeList[0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
optionSymbol = buildOptionSymbol(
|
|
||||||
selectedTicker,
|
|
||||||
selectedDate,
|
|
||||||
selectedOptionType,
|
|
||||||
selectedStrike,
|
|
||||||
);
|
|
||||||
const output = await getContractHistory(optionSymbol);
|
|
||||||
|
|
||||||
selectedOptionPrice = output?.history?.at(-1)?.mark;
|
|
||||||
|
|
||||||
config = plotData();
|
|
||||||
|
|
||||||
isLoaded = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function handleOptionType() {
|
async function handleOptionType() {
|
||||||
if (selectedOptionType === "Call") {
|
if (selectedOptionType === "Call") {
|
||||||
selectedOptionType = "Put";
|
selectedOptionType = "Put";
|
||||||
@ -449,13 +451,106 @@
|
|||||||
}, 500);
|
}, 500);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function search() {
|
||||||
|
clearTimeout(timeoutId); // Clear any existing timeout
|
||||||
|
|
||||||
|
if (!inputValue.trim()) {
|
||||||
|
// Skip if query is empty or just whitespace
|
||||||
|
searchBarData = []; // Clear previous results
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
timeoutId = setTimeout(async () => {
|
||||||
|
const response = await fetch(
|
||||||
|
`/api/searchbar?query=${encodeURIComponent(inputValue)}&limit=10`,
|
||||||
|
);
|
||||||
|
searchBarData = await response?.json();
|
||||||
|
}, 50); // delay
|
||||||
|
}
|
||||||
|
|
||||||
|
async function loadData(state: string) {
|
||||||
|
if (!rawData || !rawData.getData) {
|
||||||
|
console.error("rawData is undefined or invalid in loadData");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
isLoaded = false;
|
||||||
|
|
||||||
|
optionData = rawData?.getData[selectedOptionType];
|
||||||
|
|
||||||
|
dateList = [...Object?.keys(optionData)];
|
||||||
|
|
||||||
|
strikeList = [...optionData[selectedDate]];
|
||||||
|
|
||||||
|
if (!strikeList?.includes(selectedStrike)) {
|
||||||
|
selectedStrike = strikeList.reduce((closest, strike) => {
|
||||||
|
return Math.abs(strike - currentStockPrice) <
|
||||||
|
Math.abs(closest - currentStockPrice)
|
||||||
|
? strike
|
||||||
|
: closest;
|
||||||
|
}, strikeList[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
optionSymbol = buildOptionSymbol(
|
||||||
|
selectedTicker,
|
||||||
|
selectedDate,
|
||||||
|
selectedOptionType,
|
||||||
|
selectedStrike,
|
||||||
|
);
|
||||||
|
const output = await getContractHistory(optionSymbol);
|
||||||
|
|
||||||
|
selectedOptionPrice = output?.history?.at(-1)?.mark;
|
||||||
|
|
||||||
|
config = plotData();
|
||||||
|
|
||||||
|
isLoaded = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getStockData() {
|
||||||
|
const postData = { ticker: selectedTicker };
|
||||||
|
const response = await fetch("/api/options-calculator", {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
body: JSON.stringify(postData),
|
||||||
|
});
|
||||||
|
|
||||||
|
rawData = (await response.json()) || {};
|
||||||
|
|
||||||
|
currentStockPrice = rawData?.getStockQuote?.price;
|
||||||
|
|
||||||
|
optionData = rawData?.getData[selectedOptionType];
|
||||||
|
dateList = Object?.keys(optionData);
|
||||||
|
selectedDate = Object?.keys(optionData)[0];
|
||||||
|
strikeList = optionData[selectedDate] || [];
|
||||||
|
selectedStrike = strikeList.reduce((closest, strike) => {
|
||||||
|
return Math.abs(strike - currentStockPrice) <
|
||||||
|
Math.abs(closest - currentStockPrice)
|
||||||
|
? strike
|
||||||
|
: closest;
|
||||||
|
}, strikeList[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function changeTicker(symbol) {
|
||||||
|
selectedTicker = symbol;
|
||||||
|
await getStockData();
|
||||||
|
await loadData("default");
|
||||||
|
}
|
||||||
onMount(async () => {
|
onMount(async () => {
|
||||||
|
await getStockData();
|
||||||
await loadData("default");
|
await loadData("default");
|
||||||
});
|
});
|
||||||
|
|
||||||
onDestroy(() => {
|
onDestroy(() => {
|
||||||
if (debounceTimeout) clearTimeout(debounceTimeout);
|
if (debounceTimeout) clearTimeout(debounceTimeout);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$: {
|
||||||
|
if ($mode) {
|
||||||
|
config = plotData();
|
||||||
|
}
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<SEO
|
<SEO
|
||||||
@ -495,16 +590,17 @@
|
|||||||
<div
|
<div
|
||||||
on:click={() => changeStrategy(strategy)}
|
on:click={() => changeStrategy(strategy)}
|
||||||
class="{selectedStrategy === strategy?.name
|
class="{selectedStrategy === strategy?.name
|
||||||
? 'bg-blue-100'
|
? 'bg-blue-100 dark:bg-primary text-muted'
|
||||||
: ''} select-none flex items-center space-x-2 border rounded-full px-3 py-1 text-sm font-medium border border-gray-300 cursor-pointer sm:hover:bg-blue-100"
|
: ''} text-sm elect-none flex items-center space-x-2 border border-gray-300 dark:border-gray-600 rounded-full px-3 py-1 text-blue-700 dark:text-white dark:sm:hover:text-white sm:hover:text-muted cursor-pointer"
|
||||||
>
|
>
|
||||||
<span>{strategy.name}</span>
|
<span>{strategy.name}</span>
|
||||||
{#if strategy?.sentiment}
|
{#if strategy?.sentiment}
|
||||||
<span
|
<span
|
||||||
class="badge px-2 text-xs rounded-full {strategy.sentiment ===
|
class="badge px-2 text-xs rounded-full {strategy.sentiment ===
|
||||||
'Bullish'
|
'Bullish'
|
||||||
? 'bg-green-100 text-green-800'
|
? 'bg-green-100 text-green-800 dark:bg-green-300 dark:text-black'
|
||||||
: 'bg-red-100 text-red-800'}">{strategy.sentiment}</span
|
: 'bg-red-100 text-red-800 dark:bg-red-300 dark:text-black'}"
|
||||||
|
>{strategy.sentiment}</span
|
||||||
>
|
>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
@ -522,10 +618,14 @@
|
|||||||
<!-- Table header -->
|
<!-- Table header -->
|
||||||
|
|
||||||
<!-- Table container -->
|
<!-- Table container -->
|
||||||
<div class="overflow-x-auto border rounded-md">
|
<div
|
||||||
<table class="min-w-full divide-y divide-gray-200">
|
class="overflow-x-auto border border-gray-300 dark:border-gray-600 rounded"
|
||||||
|
>
|
||||||
|
<table
|
||||||
|
class="min-w-full divide-y divide-gray-200 dark:divide-gray-600"
|
||||||
|
>
|
||||||
<!-- Table head -->
|
<!-- Table head -->
|
||||||
<thead class="bg-gray-50">
|
<thead class="bg-gray-50 dark:bg-secondary">
|
||||||
<tr>
|
<tr>
|
||||||
<th
|
<th
|
||||||
scope="col"
|
scope="col"
|
||||||
@ -573,19 +673,64 @@
|
|||||||
</thead>
|
</thead>
|
||||||
|
|
||||||
<!-- Table body -->
|
<!-- Table body -->
|
||||||
<tbody class="bg-[#F8F9FA] divide-y divide-gray-200 text-sm">
|
<tbody
|
||||||
|
class="bg-[#F8F9FA] dark:bg-secondary divide-y divide-gray-200 dark:divide-gray-800 text-sm"
|
||||||
|
>
|
||||||
<!-- Example Option Leg Row -->
|
<!-- Example Option Leg Row -->
|
||||||
<tr>
|
<tr>
|
||||||
<td class="px-4 py-3 whitespace-nowrap font-semibold">
|
<td class="px-4 py-3 whitespace-nowrap font-semibold">
|
||||||
{selectedTicker}
|
<Combobox.Root
|
||||||
|
items={searchBarData}
|
||||||
|
bind:inputValue
|
||||||
|
bind:touchedInput
|
||||||
|
>
|
||||||
|
<div class="relative w-full">
|
||||||
|
<Combobox.Input
|
||||||
|
on:input={search}
|
||||||
|
class="text-sm controls-input bg-white dark:bg-[#2A2E39] focus:outline-hidden border border-gray-300 dark:border-gray-500 rounded placeholder:text-gray-600 dark:placeholder:text-gray-200 px-2 py-1.5 grow w-full"
|
||||||
|
placeholder="Search Ticker"
|
||||||
|
aria-label="Search new stock"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
{#if inputValue?.length !== 0 && inputValue !== selectedTicker}
|
||||||
|
<Combobox.Content
|
||||||
|
class=" z-10 rounded border border-gray-300 dark:border-gray-700 bg-white dark:bg-default px-1 py-3 shadow-sm outline-hidden"
|
||||||
|
sideOffset={8}
|
||||||
|
>
|
||||||
|
{#each searchBarData as item}
|
||||||
|
<Combobox.Item
|
||||||
|
class="cursor-pointer border-b border-gray-300 dark:border-gray-500 last:border-none flex h-fit w-auto select-none items-center rounded-button py-3 pl-5 pr-1.5 text-sm capitalize outline-hidden transition-all duration-75 data-highlighted:bg-gray-200 dark:data-highlighted:bg-primary"
|
||||||
|
value={item?.symbol}
|
||||||
|
label={item?.symbol}
|
||||||
|
on:click={(e) => changeTicker(item?.symbol)}
|
||||||
|
>
|
||||||
|
<div class="flex flex-col items-start">
|
||||||
|
<span
|
||||||
|
class="text-sm text-blue-700 dark:text-blue-400"
|
||||||
|
>{item?.symbol}</span
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="text-xs sm:text-sm text-muted dark:text-white"
|
||||||
|
>{item?.name}</span
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</Combobox.Item>
|
||||||
|
{:else}
|
||||||
|
<span class="block px-5 py-2 text-sm">
|
||||||
|
No results found
|
||||||
|
</span>
|
||||||
|
{/each}
|
||||||
|
</Combobox.Content>
|
||||||
|
{/if}
|
||||||
|
</Combobox.Root>
|
||||||
</td>
|
</td>
|
||||||
<td class="px-4 py-3 whitespace-nowrap">
|
<td class="px-4 py-3 whitespace-nowrap">
|
||||||
<label
|
<label
|
||||||
on:click={handleAction}
|
on:click={handleAction}
|
||||||
class="badge px-2 select-none rounded-md {selectedAction ===
|
class="badge px-2 select-none rounded-md {selectedAction ===
|
||||||
'Buy'
|
'Buy'
|
||||||
? 'bg-green-100 text-green-800'
|
? 'bg-green-100 text-green-800 dark:bg-green-300 dark:text-muted'
|
||||||
: 'bg-red-100 text-red-800'} font-semibold cursor-pointer"
|
: 'bg-red-100 text-red-800 dark:bg-red-300 dark:text-muted'} font-semibold cursor-pointer"
|
||||||
>{selectedAction}</label
|
>{selectedAction}</label
|
||||||
>
|
>
|
||||||
</td>
|
</td>
|
||||||
@ -595,7 +740,7 @@
|
|||||||
min="1"
|
min="1"
|
||||||
bind:value={selectedQuantity}
|
bind:value={selectedQuantity}
|
||||||
on:input={handleQuantityInput}
|
on:input={handleQuantityInput}
|
||||||
class="border border-gray-300 rounded px-2 py-1 w-20 focus:outline-none focus:ring-1 focus:ring-blue-500"
|
class="border border-gray-300 dark:border-gray-500 rounded px-2 py-1 w-20 focus:outline-none focus:ring-1 focus:ring-blue-500"
|
||||||
/>
|
/>
|
||||||
</td>
|
</td>
|
||||||
<td class="px-4 py-3 whitespace-nowrap">
|
<td class="px-4 py-3 whitespace-nowrap">
|
||||||
@ -694,7 +839,7 @@
|
|||||||
<td class="px-4 py-3 whitespace-nowrap">
|
<td class="px-4 py-3 whitespace-nowrap">
|
||||||
<label
|
<label
|
||||||
on:click={handleOptionType}
|
on:click={handleOptionType}
|
||||||
class="select-none badge px-2 rounded-md bg-blue-100 text-blue-800 font-semibold cursor-pointer"
|
class="select-none badge px-2 rounded-md bg-blue-100 text-blue-800 dark:bg-blue-300 dark:text-muted font-semibold cursor-pointer"
|
||||||
>{selectedOptionType}</label
|
>{selectedOptionType}</label
|
||||||
>
|
>
|
||||||
</td>
|
</td>
|
||||||
@ -704,7 +849,7 @@
|
|||||||
step="0.1"
|
step="0.1"
|
||||||
bind:value={selectedOptionPrice}
|
bind:value={selectedOptionPrice}
|
||||||
on:input={handleOptionPriceInput}
|
on:input={handleOptionPriceInput}
|
||||||
class="border border-gray-300 rounded px-2 py-1 w-24 focus:outline-none focus:ring-1 focus:ring-blue-500"
|
class="border border-gray-300 dark:border-gray-500 rounded px-2 py-1 w-24 focus:outline-none focus:ring-1 focus:ring-blue-500"
|
||||||
/>
|
/>
|
||||||
</td>
|
</td>
|
||||||
|
|
||||||
@ -755,7 +900,9 @@
|
|||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<div class="mt-5">
|
<div class="mt-5">
|
||||||
<h1 class="text-2xl font-bold text-gray-800 mb-6">
|
<h1
|
||||||
|
class="text-2xl font-bold text-gray-800 dark:text-white mb-6"
|
||||||
|
>
|
||||||
Trade Information
|
Trade Information
|
||||||
</h1>
|
</h1>
|
||||||
|
|
||||||
@ -764,7 +911,7 @@
|
|||||||
class="border border-gray-300 dark:border-gray-800 rounded-lg p-4 mb-6 shadow-sm max-w-sm"
|
class="border border-gray-300 dark:border-gray-800 rounded-lg p-4 mb-6 shadow-sm max-w-sm"
|
||||||
>
|
>
|
||||||
<div>{selectedStrategy}</div>
|
<div>{selectedStrategy}</div>
|
||||||
<div class="text-green-800 font-semibold">
|
<div class="text-green-800 dark:text-white font-semibold">
|
||||||
{selectedAction?.toUpperCase()} +{selectedQuantity}
|
{selectedAction?.toUpperCase()} +{selectedQuantity}
|
||||||
{selectedTicker}
|
{selectedTicker}
|
||||||
{formatDate(selectedDate)}
|
{formatDate(selectedDate)}
|
||||||
@ -774,10 +921,14 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Stock Section -->
|
<!-- Stock Section -->
|
||||||
<h2 class="text-xl font-bold text-gray-800 mb-4">Stock</h2>
|
<h2
|
||||||
|
class="text-xl font-bold text-gray-800 dark:text-white mb-4"
|
||||||
|
>
|
||||||
|
Stock
|
||||||
|
</h2>
|
||||||
<div class="grid grid-cols-2 sm:grid-cols-4 mb-6">
|
<div class="grid grid-cols-2 sm:grid-cols-4 mb-6">
|
||||||
<div>
|
<div>
|
||||||
<div class="text-gray-600">
|
<div class="text-gray-600 dark:text-white">
|
||||||
{selectedTicker} Current Price
|
{selectedTicker} Current Price
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-baseline">
|
<div class="flex items-baseline">
|
||||||
@ -788,7 +939,9 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<div class="flex items-center text-gray-600">
|
<div
|
||||||
|
class="flex items-center text-gray-600 dark:text-white"
|
||||||
|
>
|
||||||
{selectedTicker} Breakeven Price
|
{selectedTicker} Breakeven Price
|
||||||
<svg
|
<svg
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
@ -814,14 +967,18 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Trade Details Section -->
|
<!-- Trade Details Section -->
|
||||||
<h2 class="text-xl font-bold text-gray-800 mb-4">
|
<h2
|
||||||
|
class="text-xl font-bold text-gray-800 dark:text-white mb-4"
|
||||||
|
>
|
||||||
Trade Details
|
Trade Details
|
||||||
</h2>
|
</h2>
|
||||||
<div
|
<div
|
||||||
class="grid grid-cols-2 md:grid-cols-4 gap-y-6 sm:gap-y-0 mb-6"
|
class="grid grid-cols-2 md:grid-cols-4 gap-y-6 sm:gap-y-0 mb-6"
|
||||||
>
|
>
|
||||||
<div>
|
<div>
|
||||||
<div class="flex items-center text-gray-600">
|
<div
|
||||||
|
class="flex items-center text-gray-600 dark:text-white"
|
||||||
|
>
|
||||||
Cost of Trade
|
Cost of Trade
|
||||||
<svg
|
<svg
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
@ -840,13 +997,18 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="flex items-baseline">
|
<div class="flex items-baseline">
|
||||||
<span class="text-lg font-semibold"
|
<span class="text-lg font-semibold"
|
||||||
>${premium?.toLocaleString("en-US")}</span
|
>${premium?.toLocaleString("en-US", {
|
||||||
>
|
minimumFractionDigits: 2,
|
||||||
|
maximumFractionDigits: 2,
|
||||||
|
})}
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<div class="flex items-center text-gray-600">
|
<div
|
||||||
|
class="flex items-center text-gray-600 dark:text-white"
|
||||||
|
>
|
||||||
Maximum Profit
|
Maximum Profit
|
||||||
<svg
|
<svg
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
@ -863,13 +1025,17 @@
|
|||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
<div class="text-lg font-semibold text-green-800">
|
<div
|
||||||
|
class="text-lg font-semibold text-green-800 dark:text-green-400"
|
||||||
|
>
|
||||||
{limits?.maxProfit}
|
{limits?.maxProfit}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<div class="flex items-center text-gray-600">
|
<div
|
||||||
|
class="flex items-center text-gray-600 dark:text-white"
|
||||||
|
>
|
||||||
Maximum Loss
|
Maximum Loss
|
||||||
<svg
|
<svg
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
@ -886,18 +1052,20 @@
|
|||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
<div class="text-lg font-semibold text-red-600">
|
<div
|
||||||
|
class="text-lg font-semibold text-red-600 dark:text-red-400"
|
||||||
|
>
|
||||||
{limits?.maxLoss}
|
{limits?.maxLoss}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!--
|
<!--
|
||||||
<h2 class="text-xl font-bold text-gray-800 mb-4">
|
<h2 class="text-xl font-bold text-gray-800 dark:text-white mb-4">
|
||||||
Probability Analysis
|
Probability Analysis
|
||||||
</h2>
|
</h2>
|
||||||
<div class="grid grid-cols-2 md:grid-cols-4 mb-6">
|
<div class="grid grid-cols-2 md:grid-cols-4 mb-6">
|
||||||
<div>
|
<div>
|
||||||
<div class="flex items-center text-gray-600">
|
<div class="flex items-center text-gray-600 dark:text-white">
|
||||||
Probability of Profit (PoP)
|
Probability of Profit (PoP)
|
||||||
<svg
|
<svg
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
@ -933,7 +1101,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<div class="flex items-center text-gray-600">
|
<div class="flex items-center text-gray-600 dark:text-white">
|
||||||
Probability of Max Profit
|
Probability of Max Profit
|
||||||
<svg
|
<svg
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
@ -969,7 +1137,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<div class="flex items-center text-gray-600">
|
<div class="flex items-center text-gray-600 dark:text-white">
|
||||||
Probability of Max Loss
|
Probability of Max Loss
|
||||||
<svg
|
<svg
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
@ -1005,12 +1173,12 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h2 class="text-xl font-bold text-gray-800 mb-4">
|
<h2 class="text-xl font-bold text-gray-800 dark:text-white mb-4">
|
||||||
Risk Reward Analysis
|
Risk Reward Analysis
|
||||||
</h2>
|
</h2>
|
||||||
<div class="grid grid-cols-2 md:grid-cols-3 gap-6 mb-6">
|
<div class="grid grid-cols-2 md:grid-cols-3 gap-6 mb-6">
|
||||||
<div>
|
<div>
|
||||||
<div class="flex items-center text-gray-600">
|
<div class="flex items-center text-gray-600 dark:text-white">
|
||||||
Expected Value (EV)
|
Expected Value (EV)
|
||||||
<svg
|
<svg
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
@ -1046,7 +1214,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<div class="flex items-center text-gray-600">
|
<div class="flex items-center text-gray-600 dark:text-white">
|
||||||
Expected Return (EV/risk)
|
Expected Return (EV/risk)
|
||||||
<svg
|
<svg
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
@ -1082,7 +1250,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<div class="flex items-center text-gray-600">
|
<div class="flex items-center text-gray-600 dark:text-white">
|
||||||
Reward/Risk
|
Reward/Risk
|
||||||
<svg
|
<svg
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
@ -1119,7 +1287,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h2
|
<h2
|
||||||
class="text-xl font-bold text-gray-800 mb-4 flex items-center"
|
class="text-xl font-bold text-gray-800 dark:text-white mb-4 flex items-center"
|
||||||
>
|
>
|
||||||
Position Greeks
|
Position Greeks
|
||||||
<svg
|
<svg
|
||||||
@ -1139,7 +1307,7 @@
|
|||||||
</h2>
|
</h2>
|
||||||
<div class="grid grid-cols-2 md:grid-cols-4 gap-6 mb-6">
|
<div class="grid grid-cols-2 md:grid-cols-4 gap-6 mb-6">
|
||||||
<div>
|
<div>
|
||||||
<div class="flex items-center text-gray-600">
|
<div class="flex items-center text-gray-600 dark:text-white">
|
||||||
Delta (Δ)
|
Delta (Δ)
|
||||||
<svg
|
<svg
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
@ -1175,7 +1343,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<div class="flex items-center text-gray-600">
|
<div class="flex items-center text-gray-600 dark:text-white">
|
||||||
Gamma (Γ)
|
Gamma (Γ)
|
||||||
<svg
|
<svg
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
@ -1211,7 +1379,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<div class="flex items-center text-gray-600">
|
<div class="flex items-center text-gray-600 dark:text-white">
|
||||||
Theta (Θ)
|
Theta (Θ)
|
||||||
<svg
|
<svg
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
@ -1247,7 +1415,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<div class="flex items-center text-gray-600">
|
<div class="flex items-center text-gray-600 dark:text-white">
|
||||||
Vega (v)
|
Vega (v)
|
||||||
<svg
|
<svg
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user