update dark pool feed flow
This commit is contained in:
parent
aa697ccf45
commit
fd286269a1
@ -49,8 +49,8 @@
|
|||||||
premium: "none",
|
premium: "none",
|
||||||
assetType: "none",
|
assetType: "none",
|
||||||
volume: "none",
|
volume: "none",
|
||||||
avgVolume: "none",
|
sizeAvgVolRatio: "none",
|
||||||
dailyVolume: "none",
|
sizeVolRatio: "none",
|
||||||
size: "none",
|
size: "none",
|
||||||
sector: "none",
|
sector: "none",
|
||||||
};
|
};
|
||||||
@ -128,14 +128,14 @@
|
|||||||
const volB = parseFloat(b.volume);
|
const volB = parseFloat(b.volume);
|
||||||
return sortOrder === "asc" ? volA - volB : volB - volA;
|
return sortOrder === "asc" ? volA - volB : volB - volA;
|
||||||
},
|
},
|
||||||
dailyVolume: (a, b) => {
|
sizeVolRatio: (a, b) => {
|
||||||
const volA = parseFloat(a.dailyVolumePercentage);
|
const volA = parseFloat(a.sizeVolRatio);
|
||||||
const volB = parseFloat(b.dailyVolumePercentage);
|
const volB = parseFloat(b.sizeVolRatio);
|
||||||
return sortOrder === "asc" ? volA - volB : volB - volA;
|
return sortOrder === "asc" ? volA - volB : volB - volA;
|
||||||
},
|
},
|
||||||
avgVolume: (a, b) => {
|
sizeAvgVolRatio: (a, b) => {
|
||||||
const volA = parseFloat(a.avgVolumePercentage);
|
const volA = parseFloat(a.sizeAvgVolRatio);
|
||||||
const volB = parseFloat(b.avgVolumePercentage);
|
const volB = parseFloat(b.sizeAvgVolRatio);
|
||||||
return sortOrder === "asc" ? volA - volB : volB - volA;
|
return sortOrder === "asc" ? volA - volB : volB - volA;
|
||||||
},
|
},
|
||||||
assetType: (a, b) => {
|
assetType: (a, b) => {
|
||||||
@ -304,16 +304,16 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
on:click={() => sortData("dailyVolume")}
|
on:click={() => sortData("sizeVolRatio")}
|
||||||
class="td cursor-pointer select-none bg-[#121217] text-slate-300 font-bold text-xs text-start uppercase"
|
class="td cursor-pointer select-none bg-[#121217] text-slate-300 font-bold text-xs text-start uppercase"
|
||||||
>
|
>
|
||||||
% Size / Vol
|
% Size / Vol
|
||||||
<svg
|
<svg
|
||||||
class="flex-shrink-0 w-4 h-4 inline-block {sortOrders[
|
class="flex-shrink-0 w-4 h-4 inline-block {sortOrders[
|
||||||
'dailyVolume'
|
'sizeVolRatio'
|
||||||
] === 'asc'
|
] === 'asc'
|
||||||
? 'rotate-180'
|
? 'rotate-180'
|
||||||
: sortOrders['dailyVolume'] === 'desc'
|
: sortOrders['sizeVolRatio'] === 'desc'
|
||||||
? ''
|
? ''
|
||||||
: 'hidden'} "
|
: 'hidden'} "
|
||||||
viewBox="0 0 20 20"
|
viewBox="0 0 20 20"
|
||||||
@ -328,16 +328,16 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
on:click={() => sortData("avgVolume")}
|
on:click={() => sortData("sizeAvgVolRatio")}
|
||||||
class="td cursor-pointer select-none bg-[#121217] text-slate-300 font-bold text-xs text-start uppercase"
|
class="td cursor-pointer select-none bg-[#121217] text-slate-300 font-bold text-xs text-start uppercase"
|
||||||
>
|
>
|
||||||
% Size / Avg Vol
|
% Size / Avg Vol
|
||||||
<svg
|
<svg
|
||||||
class="flex-shrink-0 w-4 h-4 inline-block {sortOrders[
|
class="flex-shrink-0 w-4 h-4 inline-block {sortOrders[
|
||||||
'avgVolume'
|
'sizeAvgVolRatio'
|
||||||
] === 'asc'
|
] === 'asc'
|
||||||
? 'rotate-180'
|
? 'rotate-180'
|
||||||
: sortOrders['avgVolume'] === 'desc'
|
: sortOrders['sizeAvgVolRatio'] === 'desc'
|
||||||
? ''
|
? ''
|
||||||
: 'hidden'} "
|
: 'hidden'} "
|
||||||
viewBox="0 0 20 20"
|
viewBox="0 0 20 20"
|
||||||
@ -464,8 +464,8 @@
|
|||||||
style="justify-content: center;"
|
style="justify-content: center;"
|
||||||
class="td text-sm sm:text-[1rem] text-white text-end"
|
class="td text-sm sm:text-[1rem] text-white text-end"
|
||||||
>
|
>
|
||||||
{displayedData[index]?.dailyVolumePercentage > 0.01
|
{displayedData[index]?.sizeVolRatio > 0.01
|
||||||
? displayedData[index]?.dailyVolumePercentage?.toFixed(2) + "%"
|
? displayedData[index]?.sizeVolRatio?.toFixed(2) + "%"
|
||||||
: "< 0.01%"}
|
: "< 0.01%"}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -473,8 +473,8 @@
|
|||||||
style="justify-content: center;"
|
style="justify-content: center;"
|
||||||
class="td text-sm sm:text-[1rem] text-white text-end"
|
class="td text-sm sm:text-[1rem] text-white text-end"
|
||||||
>
|
>
|
||||||
{displayedData[index]?.avgVolume > 0.01
|
{displayedData[index]?.sizeAvgVolRatio > 0.01
|
||||||
? displayedData[index]?.avgVolume?.toFixed(2) + "%"
|
? displayedData[index]?.sizeAvgVolRatio?.toFixed(2) + "%"
|
||||||
: "< 0.01%"}
|
: "< 0.01%"}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@ -7,7 +7,7 @@
|
|||||||
isOpen,
|
isOpen,
|
||||||
} from "$lib/store";
|
} from "$lib/store";
|
||||||
|
|
||||||
import { cn } from "$lib/utils";
|
import { cn, sectorList } from "$lib/utils";
|
||||||
import { onMount, onDestroy } from "svelte";
|
import { onMount, onDestroy } from "svelte";
|
||||||
import toast from "svelte-french-toast";
|
import toast from "svelte-french-toast";
|
||||||
import { DateFormatter, type DateValue } from "@internationalized/date";
|
import { DateFormatter, type DateValue } from "@internationalized/date";
|
||||||
@ -25,10 +25,11 @@
|
|||||||
export let data;
|
export let data;
|
||||||
let shouldLoadWorker = writable(false);
|
let shouldLoadWorker = writable(false);
|
||||||
|
|
||||||
let ruleOfList = data?.getPredefinedCookieRuleOfList || [];
|
let ruleOfList = [];
|
||||||
let displayRules = [];
|
let displayRules = [];
|
||||||
let filteredData = [];
|
let filteredData = [];
|
||||||
let filterQuery = $page.url.searchParams.get("query") || "";
|
let filterQuery = $page.url.searchParams.get("query") || "";
|
||||||
|
let pagePathName = $page?.url?.pathname;
|
||||||
|
|
||||||
let socket: WebSocket | null = null; // Initialize socket as null
|
let socket: WebSocket | null = null; // Initialize socket as null
|
||||||
|
|
||||||
@ -55,7 +56,19 @@
|
|||||||
},
|
},
|
||||||
volume: {
|
volume: {
|
||||||
label: "Volume",
|
label: "Volume",
|
||||||
step: ["100K", "50K", "20K", "10K", "5K", "2K", "1K", "100", "0"],
|
step: ["100M", "50M", "20M", "10M", "1M", "500K", "200K", "100K", "50K"],
|
||||||
|
defaultCondition: "over",
|
||||||
|
defaultValue: "any",
|
||||||
|
},
|
||||||
|
sizeVolRatio: {
|
||||||
|
label: "Size / Volume",
|
||||||
|
step: ["20%", "15%", "10%", "5%", "3%", "1%"],
|
||||||
|
defaultCondition: "over",
|
||||||
|
defaultValue: "any",
|
||||||
|
},
|
||||||
|
sizeAvgVolRatio: {
|
||||||
|
label: "Size / Avg Volume",
|
||||||
|
step: ["20%", "15%", "10%", "5%", "3%", "1%"],
|
||||||
defaultCondition: "over",
|
defaultCondition: "over",
|
||||||
defaultValue: "any",
|
defaultValue: "any",
|
||||||
},
|
},
|
||||||
@ -77,27 +90,19 @@
|
|||||||
defaultCondition: "over",
|
defaultCondition: "over",
|
||||||
defaultValue: "any",
|
defaultValue: "any",
|
||||||
},
|
},
|
||||||
option_activity_type: {
|
|
||||||
label: "Option Type",
|
|
||||||
step: ["Sweep", "Trade"],
|
|
||||||
defaultValue: "any",
|
|
||||||
},
|
|
||||||
assetType: {
|
assetType: {
|
||||||
label: "Asset Type",
|
label: "Asset Type",
|
||||||
step: ["Stock", "ETF"],
|
step: ["Stock", "ETF"],
|
||||||
defaultValue: "any",
|
defaultValue: "any",
|
||||||
},
|
},
|
||||||
|
sector: {
|
||||||
|
label: "Sector",
|
||||||
|
step: sectorList,
|
||||||
|
defaultValue: "any",
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const categoricalRules = [
|
const categoricalRules = ["assetType", "sector"];
|
||||||
"moneyness",
|
|
||||||
"flowType",
|
|
||||||
"put_call",
|
|
||||||
"sentiment",
|
|
||||||
"execution_estimate",
|
|
||||||
"option_activity_type",
|
|
||||||
"assetType",
|
|
||||||
];
|
|
||||||
|
|
||||||
// Generate allRows from allRules
|
// Generate allRows from allRules
|
||||||
$: allRows = Object?.entries(allRules)
|
$: allRows = Object?.entries(allRules)
|
||||||
@ -121,13 +126,6 @@
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Update ruleCondition and valueMappings based on existing rules
|
|
||||||
ruleOfList.forEach((rule) => {
|
|
||||||
ruleCondition[rule.name] =
|
|
||||||
rule.condition || allRules[rule.name].defaultCondition;
|
|
||||||
valueMappings[rule.name] = rule.value || allRules[rule.name].defaultValue;
|
|
||||||
});
|
|
||||||
|
|
||||||
async function handleDeleteRule(state) {
|
async function handleDeleteRule(state) {
|
||||||
for (let i = 0; i < ruleOfList.length; i++) {
|
for (let i = 0; i < ruleOfList.length; i++) {
|
||||||
if (ruleOfList[i].name === state) {
|
if (ruleOfList[i].name === state) {
|
||||||
@ -147,7 +145,7 @@
|
|||||||
ruleOfList?.some((rule) => rule.name === row.rule),
|
ruleOfList?.some((rule) => rule.name === row.rule),
|
||||||
);
|
);
|
||||||
shouldLoadWorker.set(true);
|
shouldLoadWorker.set(true);
|
||||||
//await saveCookieRuleOfList();
|
saveRules();
|
||||||
}
|
}
|
||||||
|
|
||||||
async function handleResetAll() {
|
async function handleResetAll() {
|
||||||
@ -165,7 +163,7 @@
|
|||||||
ruleOfList.some((rule) => rule.name === row.rule),
|
ruleOfList.some((rule) => rule.name === row.rule),
|
||||||
);
|
);
|
||||||
displayedData = rawData;
|
displayedData = rawData;
|
||||||
//await saveCookieRuleOfList();
|
saveRules();
|
||||||
}
|
}
|
||||||
|
|
||||||
function changeRule(state: string) {
|
function changeRule(state: string) {
|
||||||
@ -380,7 +378,7 @@
|
|||||||
|
|
||||||
// Trigger worker load and save cookie
|
// Trigger worker load and save cookie
|
||||||
shouldLoadWorker.set(true);
|
shouldLoadWorker.set(true);
|
||||||
//await saveCookieRuleOfList();
|
saveRules();
|
||||||
}
|
}
|
||||||
|
|
||||||
async function stepSizeValue(value, condition) {
|
async function stepSizeValue(value, condition) {
|
||||||
@ -534,18 +532,13 @@
|
|||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
async function saveCookieRuleOfList() {
|
function saveRules() {
|
||||||
const postData = {
|
try {
|
||||||
ruleOfList: ruleOfList,
|
// Save the version along with the rules
|
||||||
};
|
localStorage?.setItem(pagePathName, JSON?.stringify(ruleOfList));
|
||||||
|
} catch (e) {
|
||||||
const response = await fetch("/api/options-flow-filter-cookie", {
|
console.log("Failed saving indicator rules: ", e);
|
||||||
method: "POST",
|
}
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
},
|
|
||||||
body: JSON.stringify(postData),
|
|
||||||
}); // make a POST request to the server with the FormData object
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -555,10 +548,36 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
onMount(async () => {
|
onMount(async () => {
|
||||||
|
try {
|
||||||
|
const savedRules = localStorage?.getItem(pagePathName);
|
||||||
|
|
||||||
|
if (savedRules) {
|
||||||
|
const parsedRules = JSON.parse(savedRules);
|
||||||
|
// Compare and update ruleOfList based on allRows
|
||||||
|
ruleOfList = parsedRules.map((rule) => {
|
||||||
|
const matchingRow = allRows.find((row) => row.name === rule.name);
|
||||||
|
if (matchingRow && matchingRow.type !== rule.type) {
|
||||||
|
return { ...rule, type: matchingRow.type };
|
||||||
|
}
|
||||||
|
return rule;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
ruleOfList = [];
|
||||||
|
console.warn(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update ruleCondition and valueMappings based on existing rules
|
||||||
|
ruleOfList?.forEach((rule) => {
|
||||||
|
ruleCondition[rule.name] =
|
||||||
|
rule.condition || allRules[rule.name].defaultCondition;
|
||||||
|
valueMappings[rule.name] = rule.value || allRules[rule.name].defaultValue;
|
||||||
|
});
|
||||||
|
|
||||||
if (filterQuery?.length > 0) {
|
if (filterQuery?.length > 0) {
|
||||||
shouldLoadWorker.set(true);
|
shouldLoadWorker.set(true);
|
||||||
}
|
}
|
||||||
if (ruleOfList?.length !== 0) {
|
if (ruleOfList?.length > 0) {
|
||||||
shouldLoadWorker.set(true);
|
shouldLoadWorker.set(true);
|
||||||
console.log("initial filter");
|
console.log("initial filter");
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,14 +1,13 @@
|
|||||||
|
import { sectorList } from "$lib/utils";
|
||||||
|
|
||||||
|
|
||||||
interface FilterContext {
|
interface FilterContext {
|
||||||
flowTypeCache?: Map<string, number>;
|
flowTypeCache?: Map<string, number>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const categoricalFields = [
|
const categoricalFields = [
|
||||||
'put_call',
|
'assetType',
|
||||||
'sentiment',
|
'sector',
|
||||||
'execution_estimate',
|
|
||||||
'option_activity_type',
|
|
||||||
'underlying_type'
|
|
||||||
];
|
];
|
||||||
|
|
||||||
function convertUnitToValue(
|
function convertUnitToValue(
|
||||||
@ -22,26 +21,14 @@ function convertUnitToValue(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
const lowerInput = input?.toLowerCase();
|
const lowerInput = input?.toLowerCase();
|
||||||
const nonNumericValues = new Set([
|
const nonNumericValues = new Set([
|
||||||
"any",
|
"any",
|
||||||
"puts",
|
"stock",
|
||||||
"calls",
|
"etf",
|
||||||
"bullish",
|
...sectorList.map(sector => sector.toLowerCase()),
|
||||||
"neutral",
|
]);
|
||||||
"bearish",
|
|
||||||
"at bid",
|
|
||||||
"at ask",
|
|
||||||
"at midpoint",
|
|
||||||
"above ask",
|
|
||||||
"below bid",
|
|
||||||
"sweep",
|
|
||||||
"trade",
|
|
||||||
"stock",
|
|
||||||
"etf",
|
|
||||||
"itm",
|
|
||||||
"otm",
|
|
||||||
"repeated flow"
|
|
||||||
]);
|
|
||||||
if (nonNumericValues.has(lowerInput)) return input;
|
if (nonNumericValues.has(lowerInput)) return input;
|
||||||
if (input.endsWith("%")) {
|
if (input.endsWith("%")) {
|
||||||
const numericValue = parseFloat(input.slice(0, -1));
|
const numericValue = parseFloat(input.slice(0, -1));
|
||||||
@ -64,80 +51,18 @@ function convertUnitToValue(
|
|||||||
return numericValue;
|
return numericValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
function isAny(value: string | string[]): boolean {
|
|
||||||
if (typeof value === "string") return value.toLowerCase() === "any";
|
|
||||||
if (Array.isArray(value))
|
|
||||||
return value.length === 1 && value[0].toLowerCase() === "any";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
function createRuleCheck(rule, ruleName, ruleValue) {
|
function createRuleCheck(rule, ruleName, ruleValue) {
|
||||||
const now = new Date(new Date().toLocaleString("en-US", { timeZone: "America/New_York" }));
|
if (rule.value === 'any') return () => true;
|
||||||
|
|
||||||
|
if (['sizeVolRatio','sizeAvgVolRatio']?.includes(ruleName)) {
|
||||||
if (ruleName === 'flowtype') {
|
|
||||||
return (item: any, context: FilterContext = {}) => {
|
|
||||||
// Check for 'any' rule or other non-repeated flow conditions
|
|
||||||
if (ruleValue === 'any' || ruleValue?.includes("any")) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle Repeated Flow logic
|
|
||||||
if (ruleValue?.includes('Repeated Flow')) {
|
|
||||||
// Initialize flowTypeCache if it doesn't exist
|
|
||||||
context.flowTypeCache = context.flowTypeCache || new Map();
|
|
||||||
|
|
||||||
// Create a unique key for repeated flow based on item characteristics
|
|
||||||
const key = `${item.ticker}-${item.put_call}-${item.strike_price}-${item.date_expiration}`;
|
|
||||||
|
|
||||||
// Increment the count for the key in the flowTypeCache
|
|
||||||
const currentCount = (context.flowTypeCache.get(key) || 0) + 1;
|
|
||||||
context.flowTypeCache.set(key, currentCount);
|
|
||||||
|
|
||||||
// Return true if this flow appears more than N times (3 in this case)
|
|
||||||
return currentCount > 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fallback for other flow type conditions (i.e., non-repeated flow)
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if (ruleName === 'moneyness') {
|
|
||||||
return (item) => {
|
|
||||||
if (ruleValue === 'any' || ruleValue?.includes("any")) return true;
|
|
||||||
|
|
||||||
const currentPrice = parseFloat(item?.underlying_price);
|
|
||||||
const strikePrice = parseFloat(item?.strike_price);
|
|
||||||
const optionType = item?.put_call;
|
|
||||||
if (isNaN(currentPrice) || isNaN(strikePrice)) return false;
|
|
||||||
|
|
||||||
// Determine moneyness
|
|
||||||
let moneyness = '';
|
|
||||||
if (optionType === 'Calls') {
|
|
||||||
moneyness = currentPrice > strikePrice ? 'ITM' : 'OTM';
|
|
||||||
} else if (optionType === 'Puts') {
|
|
||||||
moneyness = currentPrice < strikePrice ? 'ITM' : 'OTM';
|
|
||||||
}
|
|
||||||
// Check if the item matches the ruleValue ('itm' or 'otm')
|
|
||||||
if (!ruleValue?.includes(moneyness)) return false;
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ruleName === 'volumeoiratio') {
|
|
||||||
return (item) => {
|
return (item) => {
|
||||||
const volume = parseFloat(item.volume);
|
|
||||||
const openInterest = parseFloat(item.open_interest);
|
|
||||||
|
|
||||||
if (isNaN(volume) || isNaN(openInterest) || openInterest === 0) {
|
if (isNaN(ruleValue) || ruleValue === 0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ratio = Math.ceil((volume / openInterest) * 100);
|
const ratio = rule?.value;
|
||||||
|
|
||||||
// Handle 'between' condition for volume to open interest ratio
|
// Handle 'between' condition for volume to open interest ratio
|
||||||
if (rule.condition === 'between' && Array.isArray(ruleValue)) {
|
if (rule.condition === 'between' && Array.isArray(ruleValue)) {
|
||||||
@ -161,125 +86,15 @@ if (ruleName === 'volumeoiratio') {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (ruleName === 'sizeoiratio') {
|
|
||||||
return (item) => {
|
|
||||||
const size = parseFloat(item?.size);
|
|
||||||
const openInterest = parseFloat(item?.open_interest);
|
|
||||||
if (isNaN(size) || isNaN(openInterest) || openInterest === 0) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const ratio = Math?.ceil((size / openInterest) * 100);
|
|
||||||
|
|
||||||
// Handle 'between' condition for size to open interest ratio
|
|
||||||
if (rule.condition === 'between' && Array.isArray(ruleValue)) {
|
|
||||||
const [minRatio, maxRatio] = ruleValue?.map(convertUnitToValue); // Convert ruleValue to numeric values
|
|
||||||
|
|
||||||
if (minRatio === null && maxRatio === null) return true;
|
|
||||||
if (minRatio === null) return ratio <= maxRatio;
|
|
||||||
if (maxRatio === null) return ratio >= minRatio;
|
|
||||||
|
|
||||||
return ratio >= minRatio && ratio <= maxRatio;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Existing conditions for 'over' and 'under'
|
|
||||||
if (rule.condition === 'over' && ratio <= ruleValue) return false;
|
|
||||||
if (rule.condition === 'under' && ratio >= ruleValue) return false;
|
|
||||||
if (rule.condition === 'exactly' && ratio !== ruleValue) return false;
|
|
||||||
|
|
||||||
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if (ruleName === 'date_expiration') {
|
|
||||||
// If ruleValue is empty, undefined, "any", or an array containing only "any", return a function that always returns true
|
|
||||||
if (ruleValue === "" || ruleValue === undefined || isAny(ruleValue)) {
|
|
||||||
return () => true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
return (item) => {
|
|
||||||
const expirationDate = new Date(item[rule.name]);
|
|
||||||
if (isNaN(expirationDate)) return false; // Handle invalid dates
|
|
||||||
|
|
||||||
const daysDiff = Math?.ceil((expirationDate.getTime() - now.getTime()) / (1000 * 60 * 60 * 24));
|
|
||||||
|
|
||||||
|
|
||||||
if (rule.condition === 'between' && Array.isArray(ruleValue)) {
|
|
||||||
const [minDays, maxDays] = ruleValue.map(val =>
|
|
||||||
val === '' || val === null || val === undefined ? null : parseFloat(val.toString())
|
|
||||||
);
|
|
||||||
|
|
||||||
if (minDays === null && maxDays === null) return true;
|
|
||||||
if (minDays === null) return daysDiff <= maxDays;
|
|
||||||
if (maxDays === null) return daysDiff >= minDays;
|
|
||||||
|
|
||||||
return daysDiff >= minDays && daysDiff <= maxDays;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rule.condition === 'over' && typeof ruleValue === 'number') {
|
|
||||||
return daysDiff >= ruleValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rule.condition === 'under' && typeof ruleValue === 'number') {
|
|
||||||
return daysDiff <= ruleValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rule.condition === 'exactly' && typeof ruleValue === 'number') {
|
|
||||||
return daysDiff === ruleValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
// Handle string-based conditions (sentiment, option_type, etc.)
|
// Handle string-based conditions (sentiment, option_type, etc.)
|
||||||
if (categoricalFields?.includes(ruleName)) {
|
if (categoricalFields.includes(rule.name)) {
|
||||||
return (item) => {
|
return (item) => {
|
||||||
// If ruleValue is empty, undefined, or "any", return true for all items
|
|
||||||
if (ruleValue === "" || ruleValue === undefined || isAny(ruleValue)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
const itemValue = item[rule.name];
|
const itemValue = item[rule.name];
|
||||||
|
if (Array.isArray(ruleValue)) {
|
||||||
// Handle array of values for categorical fields
|
return ruleValue.includes(itemValue);
|
||||||
if (Array?.isArray(ruleValue)) {
|
|
||||||
// Remove any empty or undefined values from ruleValue
|
|
||||||
const validRuleValues = ruleValue?.filter(val => val !== "" && val !== undefined);
|
|
||||||
|
|
||||||
// If no valid values remain, return true for all items
|
|
||||||
if (validRuleValues.length === 0) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If itemValue is an array, check if any of the values match
|
|
||||||
if (Array?.isArray(itemValue)) {
|
|
||||||
return validRuleValues?.some(val =>
|
|
||||||
itemValue.some(iv => iv.toLowerCase() === val.toLowerCase())
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// If itemValue is a string, check if it's in the validRuleValues array
|
|
||||||
return validRuleValues?.some(val =>
|
|
||||||
itemValue?.toLowerCase() === val.toLowerCase()
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
return itemValue === ruleValue;
|
||||||
// Handle single string value
|
|
||||||
if (typeof ruleValue === 'string') {
|
|
||||||
// If itemValue is an array, check if any value matches
|
|
||||||
if (Array?.isArray(itemValue)) {
|
|
||||||
return itemValue?.some(iv => iv.toLowerCase() === ruleValue.toLowerCase());
|
|
||||||
}
|
|
||||||
|
|
||||||
// If both are strings, do a direct comparison
|
|
||||||
return itemValue?.toLowerCase() === ruleValue.toLowerCase();
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user