Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -939,80 +939,88 @@ HTML_TEMPLATE = """
|
|
| 939 |
|
| 940 |
// --- VALIDATION LOGIC FOR HISTORICAL DATA ---
|
| 941 |
function validateSnapshot(data) {
|
| 942 |
-
// 1.
|
| 943 |
let refPrice = 0;
|
| 944 |
|
| 945 |
-
//
|
| 946 |
-
|
| 947 |
-
|
| 948 |
-
|
| 949 |
-
|
| 950 |
-
|
| 951 |
-
|
| 952 |
-
|
| 953 |
-
if(data[
|
|
|
|
|
|
|
|
|
|
| 954 |
}
|
| 955 |
}
|
| 956 |
|
| 957 |
-
// CRITERIA 1: Missing Reference Price
|
| 958 |
if(refPrice === 0) return false;
|
| 959 |
|
| 960 |
-
// 2.
|
| 961 |
-
|
| 962 |
-
let allStrikes = [];
|
| 963 |
|
| 964 |
-
for
|
| 965 |
-
|
| 966 |
-
|
| 967 |
-
|
| 968 |
-
if(match && match[1]) {
|
| 969 |
-
let k = parseFloat(match[1]);
|
| 970 |
-
let type = match[2]; // 'CE' or 'PE'
|
| 971 |
-
|
| 972 |
-
if(!strikesMap[k]) strikesMap[k] = { CE: null, PE: null };
|
| 973 |
-
strikesMap[k][type] = data[sym];
|
| 974 |
-
|
| 975 |
-
if(!allStrikes.includes(k)) allStrikes.push(k);
|
| 976 |
-
}
|
| 977 |
-
}
|
| 978 |
|
| 979 |
-
|
|
|
|
|
|
|
| 980 |
|
| 981 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 982 |
allStrikes.sort((a,b) => a - b);
|
|
|
|
|
|
|
| 983 |
let closest = allStrikes.reduce((prev, curr) =>
|
| 984 |
Math.abs(curr - refPrice) < Math.abs(prev - refPrice) ? curr : prev
|
| 985 |
);
|
| 986 |
let atmIdx = allStrikes.indexOf(closest);
|
| 987 |
|
| 988 |
-
// 4.
|
| 989 |
-
let
|
| 990 |
-
let
|
| 991 |
-
|
| 992 |
-
//
|
| 993 |
-
for(let i =
|
| 994 |
let k = allStrikes[i];
|
| 995 |
-
let
|
| 996 |
|
| 997 |
-
//
|
| 998 |
-
|
| 999 |
-
|
| 1000 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1001 |
}
|
| 1002 |
|
| 1003 |
-
//
|
| 1004 |
-
|
| 1005 |
-
|
| 1006 |
-
|
| 1007 |
-
|
| 1008 |
-
|
| 1009 |
-
|
| 1010 |
-
|
| 1011 |
-
|
| 1012 |
-
|
| 1013 |
}
|
| 1014 |
|
| 1015 |
-
return true; //
|
| 1016 |
}
|
| 1017 |
|
| 1018 |
function renderSecondBar() {
|
|
@@ -1026,7 +1034,7 @@ HTML_TEMPLATE = """
|
|
| 1026 |
<div class="sec-pill ${idx===0?'active':''} ${statusClass}"
|
| 1027 |
onclick="selectSnapshot(${idx})"
|
| 1028 |
id="pill-${idx}"
|
| 1029 |
-
title="${isValid ? 'Data OK' : '
|
| 1030 |
:${item.s}
|
| 1031 |
</div>
|
| 1032 |
`}).join('');
|
|
|
|
| 939 |
|
| 940 |
// --- VALIDATION LOGIC FOR HISTORICAL DATA ---
|
| 941 |
function validateSnapshot(data) {
|
| 942 |
+
// 1. Get Reference Price (Spot/Future) from the HISTORICAL packet
|
| 943 |
let refPrice = 0;
|
| 944 |
|
| 945 |
+
// Try Spot first (using global SPOT_SYM)
|
| 946 |
+
if (SPOT_SYM && data[SPOT_SYM] && data[SPOT_SYM].ltp > 0) {
|
| 947 |
+
refPrice = data[SPOT_SYM].ltp;
|
| 948 |
+
}
|
| 949 |
+
|
| 950 |
+
// If no spot, try Futures (using global FUT_SYMS)
|
| 951 |
+
if (refPrice === 0 && FUT_SYMS.length > 0) {
|
| 952 |
+
for(let f of FUT_SYMS) {
|
| 953 |
+
if(data[f.s] && data[f.s].ltp > 0) {
|
| 954 |
+
refPrice = data[f.s].ltp;
|
| 955 |
+
break;
|
| 956 |
+
}
|
| 957 |
}
|
| 958 |
}
|
| 959 |
|
| 960 |
+
// CRITERIA 1: Missing Reference Price is a Critical Error
|
| 961 |
if(refPrice === 0) return false;
|
| 962 |
|
| 963 |
+
// 2. Identify Target Strikes based on ACTIVE EXPIRY (Selected in UI)
|
| 964 |
+
if(!ACTIVE_EXP || !CHAIN_DATA) return true; // Cannot validate without Master context
|
|
|
|
| 965 |
|
| 966 |
+
// Filter Master DB for items belonging to the Selected Expiry only
|
| 967 |
+
const relevantItems = CHAIN_DATA.filter(x => x.e === ACTIVE_EXP && (x.t === 'CE' || x.t === 'PE'));
|
| 968 |
+
|
| 969 |
+
if(relevantItems.length === 0) return true;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 970 |
|
| 971 |
+
// Group Master Items by Strike { 21000: { CE: "Sym...", PE: "Sym..." } }
|
| 972 |
+
let masterMap = {};
|
| 973 |
+
let allStrikes = [];
|
| 974 |
|
| 975 |
+
relevantItems.forEach(item => {
|
| 976 |
+
if(!masterMap[item.k]) {
|
| 977 |
+
masterMap[item.k] = { CE: null, PE: null };
|
| 978 |
+
allStrikes.push(item.k);
|
| 979 |
+
}
|
| 980 |
+
masterMap[item.k][item.t] = item.s;
|
| 981 |
+
});
|
| 982 |
+
|
| 983 |
allStrikes.sort((a,b) => a - b);
|
| 984 |
+
|
| 985 |
+
// 3. Find ATM Strike based on the Historical Reference Price
|
| 986 |
let closest = allStrikes.reduce((prev, curr) =>
|
| 987 |
Math.abs(curr - refPrice) < Math.abs(prev - refPrice) ? curr : prev
|
| 988 |
);
|
| 989 |
let atmIdx = allStrikes.indexOf(closest);
|
| 990 |
|
| 991 |
+
// 4. Define Range: ATM ± 7 Strikes
|
| 992 |
+
let startIdx = Math.max(0, atmIdx - 7);
|
| 993 |
+
let endIdx = Math.min(allStrikes.length - 1, atmIdx + 7);
|
| 994 |
+
|
| 995 |
+
// 5. Strict Validation Loop
|
| 996 |
+
for(let i = startIdx; i <= endIdx; i++) {
|
| 997 |
let k = allStrikes[i];
|
| 998 |
+
let requiredSyms = masterMap[k];
|
| 999 |
|
| 1000 |
+
// --- CHECK A: CALL OPTION ---
|
| 1001 |
+
if(requiredSyms.CE) {
|
| 1002 |
+
let d = data[requiredSyms.CE];
|
| 1003 |
+
|
| 1004 |
+
// 1. Is the instrument completely MISSING from history packet?
|
| 1005 |
+
if(!d) return false;
|
| 1006 |
+
|
| 1007 |
+
// 2. Is data insufficient (Zero Buy Qty AND Zero Sell Qty)?
|
| 1008 |
+
if((d.totalbuyqty || 0) === 0 && (d.totalsellqty || 0) === 0) return false;
|
| 1009 |
}
|
| 1010 |
|
| 1011 |
+
// --- CHECK B: PUT OPTION ---
|
| 1012 |
+
if(requiredSyms.PE) {
|
| 1013 |
+
let d = data[requiredSyms.PE];
|
| 1014 |
+
|
| 1015 |
+
// 1. Is the instrument completely MISSING from history packet?
|
| 1016 |
+
if(!d) return false;
|
| 1017 |
+
|
| 1018 |
+
// 2. Is data insufficient (Zero Buy Qty AND Zero Sell Qty)?
|
| 1019 |
+
if((d.totalbuyqty || 0) === 0 && (d.totalsellqty || 0) === 0) return false;
|
| 1020 |
+
}
|
| 1021 |
}
|
| 1022 |
|
| 1023 |
+
return true; // Passed all checks
|
| 1024 |
}
|
| 1025 |
|
| 1026 |
function renderSecondBar() {
|
|
|
|
| 1034 |
<div class="sec-pill ${idx===0?'active':''} ${statusClass}"
|
| 1035 |
onclick="selectSnapshot(${idx})"
|
| 1036 |
id="pill-${idx}"
|
| 1037 |
+
title="${isValid ? 'Data OK' : 'MISSING DATA (Ref/Inst) OR ZERO QTY'}">
|
| 1038 |
:${item.s}
|
| 1039 |
</div>
|
| 1040 |
`}).join('');
|