add search query to calculator+contract lookup
This commit is contained in:
parent
afc84c20fe
commit
652c08f556
@ -1,10 +1,15 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { abbreviateNumber, buildOptionSymbol } from "$lib/utils";
|
import {
|
||||||
|
abbreviateNumber,
|
||||||
|
buildOptionSymbol,
|
||||||
|
parseOptionSymbol,
|
||||||
|
} from "$lib/utils";
|
||||||
import { setCache, getCache } from "$lib/store";
|
import { setCache, getCache } from "$lib/store";
|
||||||
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 Infobox from "$lib/components/Infobox.svelte";
|
import Infobox from "$lib/components/Infobox.svelte";
|
||||||
import UpgradeToPro from "$lib/components/UpgradeToPro.svelte";
|
import UpgradeToPro from "$lib/components/UpgradeToPro.svelte";
|
||||||
|
import { page } from "$app/stores";
|
||||||
|
|
||||||
import { onMount } from "svelte";
|
import { onMount } from "svelte";
|
||||||
|
|
||||||
@ -15,17 +20,19 @@
|
|||||||
export let ticker;
|
export let ticker;
|
||||||
|
|
||||||
let isLoaded = false;
|
let isLoaded = false;
|
||||||
|
let currentStockPrice = data?.getStockQuote?.price;
|
||||||
let config = null;
|
let config = null;
|
||||||
let selectedOptionType = "Call";
|
let selectedOptionType;
|
||||||
let optionData = data?.getData[selectedOptionType];
|
|
||||||
|
let optionData = {};
|
||||||
|
|
||||||
let dateList = Object?.keys(optionData);
|
let dateList = Object?.keys(optionData);
|
||||||
|
|
||||||
let selectedDate = Object?.keys(optionData)[0];
|
let selectedDate;
|
||||||
|
|
||||||
let strikeList = optionData[selectedDate] || [];
|
let strikeList = [];
|
||||||
|
|
||||||
let selectedStrike = strikeList?.at(0) || [];
|
let selectedStrike;
|
||||||
let optionSymbol = "n/a";
|
let optionSymbol = "n/a";
|
||||||
|
|
||||||
let displayList = [];
|
let displayList = [];
|
||||||
@ -34,6 +41,34 @@
|
|||||||
let infoText = {};
|
let infoText = {};
|
||||||
let tooltipTitle;
|
let tooltipTitle;
|
||||||
|
|
||||||
|
let optionQuery = $page.url.searchParams.get("query") || "";
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (optionQuery?.length > 0) {
|
||||||
|
const parsedData = parseOptionSymbol(optionQuery);
|
||||||
|
selectedOptionType = parsedData?.optionType;
|
||||||
|
optionData = data?.getData[selectedOptionType];
|
||||||
|
selectedDate = parsedData?.dateExpiration;
|
||||||
|
strikeList = optionData[selectedDate] || [];
|
||||||
|
selectedStrike = parsedData?.strikePrice;
|
||||||
|
} else {
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
selectedOptionType = "Call";
|
||||||
|
optionData = data?.getData[selectedOptionType];
|
||||||
|
|
||||||
|
selectedDate = Object?.keys(optionData)[0];
|
||||||
|
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]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const formatDate = (dateString) => {
|
const formatDate = (dateString) => {
|
||||||
const date = new Date(dateString);
|
const date = new Date(dateString);
|
||||||
return date.toLocaleDateString("en-US", {
|
return date.toLocaleDateString("en-US", {
|
||||||
@ -43,20 +78,6 @@
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const currentTime = new Date(
|
|
||||||
new Date().toLocaleString("en-US", { timeZone: "America/New_York" }),
|
|
||||||
)?.getTime();
|
|
||||||
|
|
||||||
function daysLeft(targetDate) {
|
|
||||||
const targetTime = new Date(targetDate).getTime();
|
|
||||||
const difference = targetTime - currentTime;
|
|
||||||
|
|
||||||
const millisecondsPerDay = 1000 * 60 * 60 * 24;
|
|
||||||
const daysLeft = Math?.ceil(difference / millisecondsPerDay);
|
|
||||||
|
|
||||||
return daysLeft + "D";
|
|
||||||
}
|
|
||||||
|
|
||||||
function calculateDTE(data, dateExpiration) {
|
function calculateDTE(data, dateExpiration) {
|
||||||
// Convert the expiration date to a Date object
|
// Convert the expiration date to a Date object
|
||||||
const expirationDate = new Date(dateExpiration);
|
const expirationDate = new Date(dateExpiration);
|
||||||
@ -384,7 +405,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]);
|
||||||
}
|
}
|
||||||
|
|
||||||
displayList = [];
|
displayList = [];
|
||||||
@ -396,6 +422,7 @@
|
|||||||
selectedOptionType,
|
selectedOptionType,
|
||||||
selectedStrike,
|
selectedStrike,
|
||||||
);
|
);
|
||||||
|
|
||||||
const output = await getContractHistory(optionSymbol);
|
const output = await getContractHistory(optionSymbol);
|
||||||
rawDataHistory = output?.history;
|
rawDataHistory = output?.history;
|
||||||
|
|
||||||
|
|||||||
@ -43,6 +43,33 @@ export function buildOptionSymbol(ticker, dateExpiration, optionType, strikePric
|
|||||||
return `${ticker}${expirationStr}${optionTypeChar}${strikeStr}`;
|
return `${ticker}${expirationStr}${optionTypeChar}${strikeStr}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function parseOptionSymbol(optionSymbol) {
|
||||||
|
// Extract the ticker (everything before the 6-digit date + 1 character + 8-digit strike)
|
||||||
|
const match = optionSymbol.match(/^([A-Z]+)(\d{6})([CP])(\d{8})$/);
|
||||||
|
if (!match) throw new Error("Invalid option symbol format");
|
||||||
|
|
||||||
|
const [, ticker, datePart, optionTypeChar, strikeStr] = match;
|
||||||
|
|
||||||
|
// Parse and format the expiration date manually
|
||||||
|
const year = (parseInt(datePart.slice(0, 2), 10) + 2000).toString();
|
||||||
|
const month = datePart.slice(2, 4); // Already 2-digit string
|
||||||
|
const day = datePart.slice(4, 6); // Already 2-digit string
|
||||||
|
const dateExpiration = `${year}-${month}-${day}`;
|
||||||
|
|
||||||
|
// Convert the option type character back to "call" or "put"
|
||||||
|
const optionType = optionTypeChar === "C" ? "Call" : "Put";
|
||||||
|
|
||||||
|
// Convert strike price string back to a float
|
||||||
|
const strikePrice = parseInt(strikeStr, 10) / 1000;
|
||||||
|
|
||||||
|
return {
|
||||||
|
ticker,
|
||||||
|
dateExpiration,
|
||||||
|
optionType,
|
||||||
|
strikePrice,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
export function convertToSlug(title) {
|
export function convertToSlug(title) {
|
||||||
// Remove punctuation, hyphens, and special characters
|
// Remove punctuation, hyphens, and special characters
|
||||||
|
|||||||
@ -7,6 +7,7 @@
|
|||||||
import { setCache, getCache, screenWidth } from "$lib/store";
|
import { setCache, getCache, screenWidth } from "$lib/store";
|
||||||
import { Combobox } from "bits-ui";
|
import { Combobox } from "bits-ui";
|
||||||
import InfoModal from "$lib/components/InfoModal.svelte";
|
import InfoModal from "$lib/components/InfoModal.svelte";
|
||||||
|
import Link from "lucide-svelte/icons/square-arrow-out-up-right";
|
||||||
|
|
||||||
import { mode } from "mode-watcher";
|
import { mode } from "mode-watcher";
|
||||||
import highcharts from "$lib/highcharts.ts";
|
import highcharts from "$lib/highcharts.ts";
|
||||||
@ -18,6 +19,8 @@
|
|||||||
let selectedStrategy = "Long Call";
|
let selectedStrategy = "Long Call";
|
||||||
let selectedOptionType = "Call";
|
let selectedOptionType = "Call";
|
||||||
let selectedTicker = "TSLA";
|
let selectedTicker = "TSLA";
|
||||||
|
let assetType = "stocks";
|
||||||
|
|
||||||
let selectedAction = "Buy";
|
let selectedAction = "Buy";
|
||||||
let selectedOptionPrice;
|
let selectedOptionPrice;
|
||||||
let selectedQuantity = 1;
|
let selectedQuantity = 1;
|
||||||
@ -533,8 +536,10 @@
|
|||||||
}, strikeList[0]);
|
}, strikeList[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function changeTicker(symbol) {
|
async function changeTicker(data) {
|
||||||
selectedTicker = symbol;
|
selectedTicker = data?.symbol;
|
||||||
|
assetType = data?.type?.toLowerCase() || "stocks";
|
||||||
|
|
||||||
await getStockData();
|
await getStockData();
|
||||||
await loadData("default");
|
await loadData("default");
|
||||||
}
|
}
|
||||||
@ -670,6 +675,8 @@
|
|||||||
>
|
>
|
||||||
Price
|
Price
|
||||||
</th>
|
</th>
|
||||||
|
<th scope="col" class="px-4 py-1.5 text-sm font-semibold"
|
||||||
|
></th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
|
|
||||||
@ -703,7 +710,7 @@
|
|||||||
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"
|
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}
|
value={item?.symbol}
|
||||||
label={item?.symbol}
|
label={item?.symbol}
|
||||||
on:click={(e) => changeTicker(item?.symbol)}
|
on:click={(e) => changeTicker(item)}
|
||||||
>
|
>
|
||||||
<div class="flex flex-col items-start">
|
<div class="flex flex-col items-start">
|
||||||
<span
|
<span
|
||||||
@ -853,6 +860,16 @@
|
|||||||
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"
|
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>
|
||||||
|
<td class="px-4 py-3 whitespace-nowrap">
|
||||||
|
<a
|
||||||
|
href={`/${["stocks", "stock"]?.includes(assetType) ? "stocks" : assetType === "etf" ? "etf" : "index"}/${selectedTicker}/options/contract-lookup?query=${optionSymbol}`}
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
target="_blank"
|
||||||
|
class="option-leg-link-to-contract"
|
||||||
|
>
|
||||||
|
<Link class="w-4 h-4 text-gray-100" />
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
<td class="px-4 py-3 whitespace-nowrap">
|
<td class="px-4 py-3 whitespace-nowrap">
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user