add search query to calculator+contract lookup
This commit is contained in:
parent
afc84c20fe
commit
652c08f556
@ -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;
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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">
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user