add search query to calculator+contract lookup

This commit is contained in:
MuslemRahimi 2025-04-07 01:03:56 +02:00
parent afc84c20fe
commit 652c08f556
3 changed files with 95 additions and 24 deletions

View File

@ -1,10 +1,15 @@
<script lang="ts">
import { abbreviateNumber, buildOptionSymbol } from "$lib/utils";
import {
abbreviateNumber,
buildOptionSymbol,
parseOptionSymbol,
} from "$lib/utils";
import { setCache, getCache } from "$lib/store";
import * as DropdownMenu from "$lib/components/shadcn/dropdown-menu/index.js";
import { Button } from "$lib/components/shadcn/button/index.js";
import Infobox from "$lib/components/Infobox.svelte";
import UpgradeToPro from "$lib/components/UpgradeToPro.svelte";
import { page } from "$app/stores";
import { onMount } from "svelte";
@ -15,17 +20,19 @@
export let ticker;
let isLoaded = false;
let currentStockPrice = data?.getStockQuote?.price;
let config = null;
let selectedOptionType = "Call";
let optionData = data?.getData[selectedOptionType];
let selectedOptionType;
let 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 displayList = [];
@ -34,6 +41,34 @@
let infoText = {};
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 date = new Date(dateString);
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) {
// Convert the expiration date to a Date object
const expirationDate = new Date(dateExpiration);
@ -384,7 +405,12 @@
strikeList = [...optionData[selectedDate]];
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 = [];
@ -396,6 +422,7 @@
selectedOptionType,
selectedStrike,
);
const output = await getContractHistory(optionSymbol);
rawDataHistory = output?.history;

View File

@ -43,6 +43,33 @@ export function buildOptionSymbol(ticker, dateExpiration, optionType, strikePric
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) {
// Remove punctuation, hyphens, and special characters

View File

@ -7,6 +7,7 @@
import { setCache, getCache, screenWidth } from "$lib/store";
import { Combobox } from "bits-ui";
import InfoModal from "$lib/components/InfoModal.svelte";
import Link from "lucide-svelte/icons/square-arrow-out-up-right";
import { mode } from "mode-watcher";
import highcharts from "$lib/highcharts.ts";
@ -18,6 +19,8 @@
let selectedStrategy = "Long Call";
let selectedOptionType = "Call";
let selectedTicker = "TSLA";
let assetType = "stocks";
let selectedAction = "Buy";
let selectedOptionPrice;
let selectedQuantity = 1;
@ -533,8 +536,10 @@
}, strikeList[0]);
}
async function changeTicker(symbol) {
selectedTicker = symbol;
async function changeTicker(data) {
selectedTicker = data?.symbol;
assetType = data?.type?.toLowerCase() || "stocks";
await getStockData();
await loadData("default");
}
@ -670,6 +675,8 @@
>
Price
</th>
<th scope="col" class="px-4 py-1.5 text-sm font-semibold"
></th>
</tr>
</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"
value={item?.symbol}
label={item?.symbol}
on:click={(e) => changeTicker(item?.symbol)}
on:click={(e) => changeTicker(item)}
>
<div class="flex flex-col items-start">
<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"
/>
</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">