update chart
This commit is contained in:
parent
4859b0ff50
commit
866a437273
@ -24,7 +24,7 @@ export const load = async ({ locals }) => {
|
|||||||
|
|
||||||
const getStockQuote = async () => {
|
const getStockQuote = async () => {
|
||||||
const postData = { ticker: 'TSLA' };
|
const postData = { ticker: 'TSLA' };
|
||||||
const response = await fetch(apiURL + "/historical-price", {
|
const response = await fetch(apiURL + "/stock-quote", {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
|
|||||||
@ -2,7 +2,7 @@
|
|||||||
import * as DropdownMenu from "$lib/components/shadcn/dropdown-menu/index.js";
|
import * as DropdownMenu from "$lib/components/shadcn/dropdown-menu/index.js";
|
||||||
import { Button } from "$lib/components/shadcn/button/index.js";
|
import { Button } from "$lib/components/shadcn/button/index.js";
|
||||||
import SEO from "$lib/components/SEO.svelte";
|
import SEO from "$lib/components/SEO.svelte";
|
||||||
import { onMount } 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 } from "$lib/store";
|
||||||
|
|
||||||
@ -18,14 +18,25 @@
|
|||||||
let selectedTicker = "TSLA";
|
let selectedTicker = "TSLA";
|
||||||
let selectedAction = "Buy";
|
let selectedAction = "Buy";
|
||||||
let selectedOptionPrice;
|
let selectedOptionPrice;
|
||||||
|
let selectedQuantity = 1;
|
||||||
|
let debounceTimeout;
|
||||||
|
|
||||||
|
let currentStockPrice = data?.getStockQuote?.price;
|
||||||
|
|
||||||
let optionData = data?.getData[selectedOptionType];
|
let optionData = data?.getData[selectedOptionType];
|
||||||
let dateList = Object?.keys(optionData);
|
let dateList = Object?.keys(optionData);
|
||||||
let selectedDate = Object?.keys(optionData)[0];
|
let selectedDate = Object?.keys(optionData)[0];
|
||||||
let strikeList = optionData[selectedDate] || [];
|
let strikeList = optionData[selectedDate] || [];
|
||||||
let deltaList = optionData[selectedDate] || [];
|
let selectedStrike = strikeList.reduce((closest, strike) => {
|
||||||
let selectedStrike = strikeList?.at(0) || [];
|
return Math.abs(strike - currentStockPrice) <
|
||||||
|
Math.abs(closest - currentStockPrice)
|
||||||
|
? strike
|
||||||
|
: closest;
|
||||||
|
}, strikeList[0]);
|
||||||
|
|
||||||
let optionSymbol;
|
let optionSymbol;
|
||||||
|
let breakEvenPrice;
|
||||||
|
let premium;
|
||||||
|
|
||||||
let strategies = [
|
let strategies = [
|
||||||
{ name: "Long Call", sentiment: "Bullish" },
|
{ name: "Long Call", sentiment: "Bullish" },
|
||||||
@ -51,14 +62,26 @@
|
|||||||
*/
|
*/
|
||||||
];
|
];
|
||||||
|
|
||||||
const tradeInfo = {
|
const payoffFunctions = {
|
||||||
type: "Long Call",
|
"Buy Call": (s, strike, premium) =>
|
||||||
details: "BUY +1 TSLA Apr 11, 2025 280.0 call @$30.0",
|
s < strike ? -premium : (s - strike) * 100 - premium,
|
||||||
currentPrice: "$239.34",
|
|
||||||
breakEvenPrice: "$310.00",
|
"Sell Call": (s, strike, premium) =>
|
||||||
costOfTrade: "$3,000.00",
|
s < strike ? premium : premium - (s - strike) * 100,
|
||||||
maxProfit: "Unlimited",
|
|
||||||
maxLoss: "-$3,000.00",
|
"Buy Put": (s, strike, premium) =>
|
||||||
|
s > strike ? -premium : (strike - s) * 100 - premium,
|
||||||
|
|
||||||
|
"Sell Put": (s, strike, premium) =>
|
||||||
|
s > strike ? premium : premium - (strike - s) * 100,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Define break-even calculators for each scenario (using per-share price)
|
||||||
|
const breakEvenCalculators = {
|
||||||
|
"Buy Call": (strike, optionPrice) => strike + optionPrice,
|
||||||
|
"Sell Call": (strike, optionPrice) => strike + optionPrice,
|
||||||
|
"Buy Put": (strike, optionPrice) => strike - optionPrice,
|
||||||
|
"Sell Put": (strike, optionPrice) => strike - optionPrice,
|
||||||
};
|
};
|
||||||
|
|
||||||
const formatDate = (dateString) => {
|
const formatDate = (dateString) => {
|
||||||
@ -71,15 +94,24 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
function plotData() {
|
function plotData() {
|
||||||
const action = "Buy";
|
// total premium paid for 1 contract (premium is calculated per share times 100 shares)
|
||||||
const optionType = "Call";
|
premium = selectedOptionPrice * 100 * selectedQuantity;
|
||||||
|
|
||||||
const strikePrice = 230;
|
// Create a key from the selected action and option type
|
||||||
const numOfQuantities = 1;
|
const scenarioKey = `${selectedAction} ${selectedOptionType}`;
|
||||||
const currentStockPrice = 239;
|
|
||||||
const cost = 30.13;
|
// Calculate break-even price per share using the mapping above.
|
||||||
const premium = cost * 100 * numOfQuantities; // total premium paid for 1 contract
|
// Note: For display, we assume optionPrice is per share.
|
||||||
const breakEven = strikePrice + cost;
|
breakEvenPrice;
|
||||||
|
if (breakEvenCalculators[scenarioKey]) {
|
||||||
|
breakEvenPrice = breakEvenCalculators[scenarioKey](
|
||||||
|
selectedStrike,
|
||||||
|
selectedOptionPrice,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
console.error("Break-even scenario not implemented:", scenarioKey);
|
||||||
|
breakEvenPrice = selectedStrike; // default fallback
|
||||||
|
}
|
||||||
|
|
||||||
// 1) Build payoff data from 0 to 600 (in steps of 10)
|
// 1) Build payoff data from 0 to 600 (in steps of 10)
|
||||||
const dataPoints = [];
|
const dataPoints = [];
|
||||||
@ -87,13 +119,17 @@
|
|||||||
const xMax = 600;
|
const xMax = 600;
|
||||||
const step = 10;
|
const step = 10;
|
||||||
|
|
||||||
for (let s = xMin; s <= xMax; s += step) {
|
if (payoffFunctions[scenarioKey]) {
|
||||||
// Payoff for a long call:
|
for (let s = xMin; s <= xMax; s += step) {
|
||||||
// If underlying price < strike, payoff = -premium
|
// For each price point, calculate payoff based on the scenario.
|
||||||
// Else payoff = (underlying - strike)*100 - premium
|
const payoff = payoffFunctions[scenarioKey](s, selectedStrike, premium);
|
||||||
const payoff =
|
dataPoints.push([s, payoff]);
|
||||||
s < strikePrice ? -premium : (s - strikePrice) * 100 - premium;
|
}
|
||||||
dataPoints.push([s, payoff]);
|
} else {
|
||||||
|
console.error(
|
||||||
|
"Payoff function not implemented for scenario:",
|
||||||
|
scenarioKey,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const options = {
|
const options = {
|
||||||
@ -112,12 +148,11 @@
|
|||||||
useHTML: true,
|
useHTML: true,
|
||||||
},
|
},
|
||||||
xAxis: {
|
xAxis: {
|
||||||
// numeric axis from 0 to 600
|
|
||||||
min: xMin,
|
min: xMin,
|
||||||
max: xMax,
|
max: xMax,
|
||||||
tickInterval: 50,
|
tickInterval: 50,
|
||||||
title: {
|
title: {
|
||||||
text: "TSLA Price at Expiration ($)",
|
text: `${selectedTicker} Price at Expiration ($)`,
|
||||||
style: { color: $mode === "light" ? "#545454" : "white" },
|
style: { color: $mode === "light" ? "#545454" : "white" },
|
||||||
},
|
},
|
||||||
labels: {
|
labels: {
|
||||||
@ -137,12 +172,12 @@
|
|||||||
},
|
},
|
||||||
// Break-Even line
|
// Break-Even line
|
||||||
{
|
{
|
||||||
value: breakEven,
|
value: breakEvenPrice,
|
||||||
color: "#10B981",
|
color: "#10B981",
|
||||||
dashStyle: "Dash",
|
dashStyle: "Dash",
|
||||||
width: 1.2,
|
width: 1.2,
|
||||||
label: {
|
label: {
|
||||||
text: `<span class="text-black dark:text-white">Breakeven $${breakEven.toFixed(2)}</span>`,
|
text: `<span class="text-black dark:text-white">Breakeven $${breakEvenPrice.toFixed(2)}</span>`,
|
||||||
},
|
},
|
||||||
zIndex: 5,
|
zIndex: 5,
|
||||||
},
|
},
|
||||||
@ -268,7 +303,12 @@
|
|||||||
strikeList = [...optionData[selectedDate]];
|
strikeList = [...optionData[selectedDate]];
|
||||||
|
|
||||||
if (!strikeList?.includes(selectedStrike)) {
|
if (!strikeList?.includes(selectedStrike)) {
|
||||||
selectedStrike = strikeList?.at(0); // Set to first element if not found
|
selectedStrike = strikeList.reduce((closest, strike) => {
|
||||||
|
return Math.abs(strike - currentStockPrice) <
|
||||||
|
Math.abs(closest - currentStockPrice)
|
||||||
|
? strike
|
||||||
|
: closest;
|
||||||
|
}, strikeList[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
optionSymbol = buildOptionSymbol(
|
optionSymbol = buildOptionSymbol(
|
||||||
@ -280,29 +320,48 @@
|
|||||||
const output = await getContractHistory(optionSymbol);
|
const output = await getContractHistory(optionSymbol);
|
||||||
|
|
||||||
selectedOptionPrice = output?.history?.at(-1)?.mark;
|
selectedOptionPrice = output?.history?.at(-1)?.mark;
|
||||||
|
|
||||||
|
config = plotData();
|
||||||
|
|
||||||
isLoaded = true;
|
isLoaded = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleOptionType() {
|
async function handleOptionType() {
|
||||||
if (selectedOptionType === "Call") {
|
if (selectedOptionType === "Call") {
|
||||||
selectedOptionType = "Put";
|
selectedOptionType = "Put";
|
||||||
} else {
|
} else {
|
||||||
selectedOptionType = "Call";
|
selectedOptionType = "Call";
|
||||||
}
|
}
|
||||||
|
await loadData("optionType");
|
||||||
}
|
}
|
||||||
function handleAction() {
|
async function handleAction() {
|
||||||
if (selectedAction === "Buy") {
|
if (selectedAction === "Buy") {
|
||||||
selectedAction = "Sell";
|
selectedAction = "Sell";
|
||||||
} else {
|
} else {
|
||||||
selectedAction = "But";
|
selectedAction = "Buy";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
config = plotData();
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleOptionPriceInput(event) {
|
||||||
|
selectedOptionPrice = +event.target.value;
|
||||||
|
// Clear any existing debounce timeout
|
||||||
|
if (debounceTimeout) clearTimeout(debounceTimeout);
|
||||||
|
|
||||||
|
// Set a new debounce timeout (1 second)
|
||||||
|
debounceTimeout = setTimeout(() => {
|
||||||
|
config = plotData();
|
||||||
|
}, 500);
|
||||||
}
|
}
|
||||||
|
|
||||||
onMount(async () => {
|
onMount(async () => {
|
||||||
await loadData("default");
|
await loadData("default");
|
||||||
});
|
});
|
||||||
|
|
||||||
config = plotData();
|
onDestroy(() => {
|
||||||
|
if (debounceTimeout) clearTimeout(debounceTimeout);
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<SEO
|
<SEO
|
||||||
@ -425,8 +484,8 @@
|
|||||||
<tbody class="bg-[#F8F9FA] divide-y divide-gray-200 text-sm">
|
<tbody class="bg-[#F8F9FA] divide-y divide-gray-200 text-sm">
|
||||||
<!-- Example Option Leg Row -->
|
<!-- Example Option Leg Row -->
|
||||||
<tr>
|
<tr>
|
||||||
<td class="px-4 py-3 whitespace-nowrap font-bold">
|
<td class="px-4 py-3 whitespace-nowrap font-semibold">
|
||||||
TSLA
|
{selectedTicker}
|
||||||
</td>
|
</td>
|
||||||
<td class="px-4 py-3 whitespace-nowrap">
|
<td class="px-4 py-3 whitespace-nowrap">
|
||||||
<label
|
<label
|
||||||
@ -550,7 +609,8 @@
|
|||||||
<input
|
<input
|
||||||
type="number"
|
type="number"
|
||||||
step="0.1"
|
step="0.1"
|
||||||
value={selectedOptionPrice}
|
bind:value={selectedOptionPrice}
|
||||||
|
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 rounded px-2 py-1 w-24 focus:outline-none focus:ring-1 focus:ring-blue-500"
|
||||||
/>
|
/>
|
||||||
</td>
|
</td>
|
||||||
@ -610,9 +670,13 @@
|
|||||||
<div
|
<div
|
||||||
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>Long Call</div>
|
<div>{selectedStrategy}</div>
|
||||||
<div class="text-green-800 font-semibold">
|
<div class="text-green-800 font-semibold">
|
||||||
BUY +1 TSLA Apr 11, 2025 280.0 call @$30.0
|
{selectedAction?.toUpperCase()} +{selectedQuantity}
|
||||||
|
{selectedTicker}
|
||||||
|
{formatDate(selectedDate)}
|
||||||
|
{selectedStrike}
|
||||||
|
{selectedOptionType} @${selectedOptionPrice}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -620,17 +684,19 @@
|
|||||||
<h2 class="text-xl font-bold text-gray-800 mb-4">Stock</h2>
|
<h2 class="text-xl font-bold text-gray-800 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">TSLA Current Price</div>
|
<div class="text-gray-600">
|
||||||
|
{selectedTicker} Current Price
|
||||||
|
</div>
|
||||||
<div class="flex items-baseline">
|
<div class="flex items-baseline">
|
||||||
<span class="text-lg font-semibold"
|
<span class="text-lg font-semibold"
|
||||||
>{tradeInfo.currentPrice}</span
|
>${currentStockPrice}</span
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<div class="flex items-center text-gray-600">
|
<div class="flex items-center text-gray-600">
|
||||||
TSLA Breakeven Price
|
{selectedTicker} Breakeven Price
|
||||||
<svg
|
<svg
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
class="h-4 w-4 ml-1"
|
class="h-4 w-4 ml-1"
|
||||||
@ -648,7 +714,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="flex items-baseline">
|
<div class="flex items-baseline">
|
||||||
<span class="text-lg font-semibold"
|
<span class="text-lg font-semibold"
|
||||||
>{tradeInfo.breakEvenPrice}</span
|
>${breakEvenPrice}</span
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -681,7 +747,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="flex items-baseline">
|
<div class="flex items-baseline">
|
||||||
<span class="text-lg font-semibold"
|
<span class="text-lg font-semibold"
|
||||||
>{tradeInfo.costOfTrade}</span
|
>${premium?.toLocaleString("en-US")}</span
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -705,7 +771,7 @@
|
|||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
<div class="text-lg font-semibold text-green-800">
|
<div class="text-lg font-semibold text-green-800">
|
||||||
{tradeInfo.maxProfit}
|
Unlimited
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -728,7 +794,7 @@
|
|||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
<div class="text-lg font-semibold text-red-600">
|
<div class="text-lg font-semibold text-red-600">
|
||||||
{tradeInfo.maxLoss}
|
-${premium?.toLocaleString("en-US")}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user