"function updateOpenPortfolio() {
  var ss = SpreadsheetApp.getActiveSpreadsheet();
  var sheet = ss.getSheetByName("Trade Log");
  if (!sheet) {
    Logger.log("Trade Log sheet not found. Please check the sheet name.");
    return;
  }

  var portfolioSheet = ss.getSheetByName("Portfolio");
  if (!portfolioSheet) {
    portfolioSheet = ss.insertSheet("Portfolio");
  }

  // Set headers if they are not already set
  var headerRange = portfolioSheet.getRange('A3:H3');
  var currentHeaders = headerRange.getValues()[0];
  var newHeaders = ["SN.", "Ticker", "Type", "# Shares", "Avg. Buy @", "Asset Class", "Broker", "User"];

  // Only set headers if they are different
  if (JSON.stringify(currentHeaders) !== JSON.stringify(newHeaders)) {
    headerRange.setValues([newHeaders]);
  }

  // Clear existing data in columns A to H starting from row 4
  var lastRow = portfolioSheet.getLastRow();
  if (lastRow > 3) {
    portfolioSheet.getRange(4, 1, lastRow - 3, 8).clearContent(); // Clear only columns A to H
  }

  // Batch read data from the Trade Log
  var dataRange = sheet.getRange(2, 1, sheet.getLastRow() - 1, sheet.getLastColumn());
  var data = dataRange.getValues();

  if (data.length === 0) {
    Logger.log("No data found in Trade Log sheet.");
    return;
  }

  // Process header row
  var headers = data[0].map(header => header.toString().trim().toLowerCase());
  var expectedHeaders = {
    "type": ["type"],
    "date": ["date"],
    "security": ["security"],
    "action": ["action"],
    "quantity": ["quantity"],
    "@price": ["@price", "price"],
    "fees": ["fees"], // Added Fees
    "asset class": ["asset class"],
    "broker": ["broker"],
    "user": ["user"]
  };

  var headerIndices = {};
  var missingHeaders = [];

  for (var key in expectedHeaders) {
    var found = false;
    for (var i = 0; i < expectedHeaders[key].length; i++) {
      var possibleHeader = expectedHeaders[key][i].toLowerCase();
      var idx = headers.indexOf(possibleHeader);
      if (idx !== -1) {
        headerIndices[key] = idx;
        found = true;
        break;
      }
    }
    if (!found) {
      missingHeaders.push(key);
    }
  }

  if (missingHeaders.length > 0) {
    Logger.log("Missing Headers:", missingHeaders.join(", "));
    throw new Error("One or more required columns are missing in Trade Log sheet.");
  }

  var positionsMap = new Map();
  var pendingTransfers = {};

  function addPosition(ticker, assetClass, broker, user, position) {
    var key = `${ticker}|${assetClass}|${broker}|${user}`;
    if (!positionsMap.has(key)) {
      positionsMap.set(key, []);
    }
    positionsMap.get(key).push(position);
  }

  function removePositions(ticker, assetClass, broker, user, quantity) {
    var key = `${ticker}|${assetClass}|${broker}|${user}`;
    var remainingQuantity = quantity;
    var removedPositions = [];

    if (positionsMap.has(key)) {
      var positions = positionsMap.get(key);
      positions.sort((a, b) => a.date - b.date); // FIFO
      positionsMap.set(key, positions.filter(pos => {
        if (remainingQuantity <= 0) return true;
        if (pos.quantity <= remainingQuantity) {
          removedPositions.push({ ...pos });
          remainingQuantity -= pos.quantity;
          return false;
        } else {
          var splitPos = { ...pos, quantity: remainingQuantity };
          removedPositions.push(splitPos);
          pos.quantity -= remainingQuantity;
          remainingQuantity = 0;
          return true;
        }
      }));
    } else {
      // No matching positions found under this key
      Logger.log(`Warning: No positions found for key ${key} when removing positions.`);
    }
    return removedPositions;
  }

  for (var i = 1; i < data.length; i++) {
    var row = data[i];

    // Check if the row is empty
    if (row.every(cell => cell === "")) {
      continue; // Skip empty rows
    }

    var type = (row[headerIndices["type"]] || "").toString().trim().toLowerCase();
    var dateCell = row[headerIndices["date"]];
    var date;
    if (dateCell instanceof Date) {
      date = dateCell;
    } else {
      date = new Date(dateCell);
    }
    if (isNaN(date.getTime())) {
      Logger.log(`Invalid date in row ${i+2}: ${dateCell}`);
      continue;
    }
    var action = (row[headerIndices["action"]] || "").toString().trim().toLowerCase();
    var ticker = (row[headerIndices["security"]] || "").toString().trim();
    var quantity = parseFloat(row[headerIndices["quantity"]]) || 0;
    var price = parseFloat(row[headerIndices["@price"]]) || 0.0;
    var fees = parseFloat(row[headerIndices["fees"]]) || 0.0; // Extract Fees
    var assetClass = (row[headerIndices["asset class"]] || "").toString().trim().toLowerCase();
    var broker = (row[headerIndices["broker"]] || "").toString().trim().toLowerCase();
    var user = (row[headerIndices["user"]] || "").toString().trim().toLowerCase();

    if (type === "delivery") {
      if (action === "buy" || action === "sellshort") {
        addPosition(ticker, assetClass, broker, user, {
          date: date, // Store the Date object
          quantity: quantity,
          price: price,
          fees: fees, // Include Fees
          tradeType: action === "buy" ? "Long" : "Short"
        });
      } else if (action === "sell" || action === "buytocover") {
        removePositions(ticker, assetClass, broker, user, quantity);
      }
    } else if (type === "my action") {
      if (action === "transferfrom") {
        var transferKey = `${ticker}|${assetClass}|${quantity}|${date.getTime()}|${broker}|${user}`;
        pendingTransfers[transferKey] = removePositions(ticker, assetClass, broker, user, quantity);
      } else if (action === "transferto") {
        var transferKey = `${ticker}|${assetClass}|${quantity}|${date.getTime()}`;
        var matchingTransfer = Object.keys(pendingTransfers).find(key => key.startsWith(transferKey));
        if (matchingTransfer) {
          pendingTransfers[matchingTransfer].forEach(pos =>
            addPosition(ticker, assetClass, broker, user, pos)
          );
          delete pendingTransfers[matchingTransfer];
        } else {
          Logger.log(`Warning: No matching transfer found for TransferTo action: ${ticker}, ${quantity}, ${date}`);
        }
      }
    } else if (type === "corp. action" && action === "split") {
      // Handle stock splits
      var splitRatioStr = (row[headerIndices["quantity"]] || "").toString().trim();
      var splitRatio = 1;
      if (splitRatioStr.includes(":")) {
        var parts = splitRatioStr.split(":");
        if (parts.length == 2) {
          var numerator = parseFloat(parts[0]);
          var denominator = parseFloat(parts[1]);
          if (denominator !== 0) {
            splitRatio = numerator / denominator;
          } else {
            Logger.log(`Invalid split ratio in row ${i + 2}: ${splitRatioStr}`);
            continue;
          }
        } else {
          Logger.log(`Invalid split ratio format in row ${i + 2}: ${splitRatioStr}`);
          continue;
        }
      } else {
        splitRatio = parseFloat(splitRatioStr);
        if (isNaN(splitRatio) || splitRatio <= 0) {
          Logger.log(`Invalid split ratio in row ${i + 2}: ${splitRatioStr}`);
          continue;
        }
      }
      for (var key of positionsMap.keys()) {
        // Check if the key starts with the ticker
        if (key.split('|')[0] === ticker) {
          positionsMap.get(key).forEach(function (pos) {
            pos.quantity *= splitRatio;
            pos.price /= splitRatio;
          });
        }
      }
    }
  }

  // Prepare data for output
  var consolidatedPositions = {};

  for (var key of positionsMap.keys()) {
    var [ticker, assetClass, broker, user] = key.split('|');
    var totalQuantity = 0;
    var totalValue = 0;
    var totalFees = 0; // Initialize total fees

    positionsMap.get(key).forEach(function (pos) {
      if (pos.quantity > 0) {
        totalQuantity += pos.quantity;
        totalValue += pos.quantity * pos.price;
        totalFees += pos.fees; // Accumulate fees
      }
    });

    if (totalQuantity > 0) {
      var avgPrice = (totalValue + totalFees) / totalQuantity; // Include fees in avg price
      var consolidationKey = `${ticker}|${assetClass}|${broker}|${user}`;
      consolidatedPositions[consolidationKey] = {
        ticker: ticker,
        type: positionsMap.get(key)[0].tradeType,
        quantity: totalQuantity,
        avgPrice: avgPrice,
        assetClass: assetClass.charAt(0).toUpperCase() + assetClass.slice(1), // Capitalize for display
        broker: broker.charAt(0).toUpperCase() + broker.slice(1),
        user: user.charAt(0).toUpperCase() + user.slice(1)
      };
    }
  }

  // Convert consolidated positions to array and sort by ticker
  var output = Object.values(consolidatedPositions);
  output.sort((a, b) => a.ticker.localeCompare(b.ticker));

  // Add SN and prepare final sorted output
  var sortedOutput = output.map((item, index) => [
    index + 1, // SN
    item.ticker,
    item.type,
    Number(item.quantity.toFixed(4)), // Format quantity
    item.avgPrice.toFixed(2), // Format average price
    item.assetClass,
    item.broker,
    item.user
  ]);

  // Output to "Portfolio" sheet without affecting formatting
  if (sortedOutput.length > 0) {
    portfolioSheet
      .getRange(4, 1, sortedOutput.length, sortedOutput[0].length)
      .setValues(sortedOutput);
    portfolioSheet
      .getRange(4, 4, sortedOutput.length, 2)
      .setNumberFormat('#,##0.00');
  } else {
    Logger.log("No open positions to display.");
  }

  // Record the last update date and time
  let now = new Date();
  let formattedNow = Utilities.formatDate(now, "Asia/Kolkata", "dd-MMM-yyyy HH:mm:ss");
  portfolioSheet.getRange('V1').setValue(formattedNow);

  Logger.log("Open portfolio updated successfully.");
}"

ye wala code result to sahi output de raha h . sb kuch sahi h bs ek precise dikkat h.

jese "Trad Log " me agar entry h

"Delivery	27-Jul-2005	 NSE:NTPC 	Buy	829	 ₹ 62.00 	 ₹ -   	 ₹ 51,398.00 	 Equity 	 ICICI 	 YKM 
Delivery	4-Oct-2005	 NSE:ITC 	Buy	100	 ₹ 137.00 	 ₹ -   	 ₹ 13,700.00 	 Equity 	 ICICI 	 YKM 
Delivery	4-Oct-2005	 NSE:MRPL 	Buy	100	 ₹ 54.50 	 ₹ -   	 ₹ 5,450.00 	 Equity 	 ICICI 	 YKM 
Delivery	4-Oct-2005	 NSE:TTML 	Buy	100	 ₹ 30.95 	 ₹ -   	 ₹ 3,095.00 	 Equity 	 ICICI 	 YKM 
Delivery	22-Dec-2005	 NSE:CELEBRITY 	Buy	35	 ₹ 180.00 	 ₹ -   	 ₹ 6,300.00 	 Equity 	 ICICI 	 YKM 
Delivery	22-Dec-2005	 NSE:NTPC 	Sell	400	 ₹ 112.00 	 ₹ -   	 ₹ 44,800.00 	 Equity 	 ICICI 	 YKM "

to output me 

"1	BOM:503816	Long	40.00	49.50	 Equity 	 Zerodha 	 Neeraj 
2	BOM:504132	Long	12.00	1,194.16	 Equity 	 Sbi_sec. 	 Cho2 
3	BOM:512634	Long	10.00	157.05	 Equity 	 Zerodha 	 Neeraj 
4	BOM:517236	Long	16.00	152.19	 Equity 	 Sbi_sec. 	 Cho2 
5	BOM:522134	Long	42.00	173.39	 Equity 	 Zerodha 	 Neeraj 
6	BOM:522257	Long	582.00	10.64	 Equity 	 Icici 	 Ykm 
7	BOM:523850	Long	10.00	239.00	 Equity 	 Groww 	 Neeraj 
8	BOM:523850	Long	24.00	532.68	 Equity 	 Sbi_sec. 	 Cho2 "

note input output reference ke liye h bs . only for reference.

ddhyan precisely ye dena hki agar "Trade Log" me
"Equity 	 ICICI 	 YKM" see entry h to 

output me bhi vese hi aye.

"Equity 	 Icici 	 Ykm" na aye . (formatting keh skte ho same ho jo m smjh raha hu. Asset Class Broker User ki)

smjhe. bs ye precise dikkat thik krna h. baki output sahi h. core functionality sb sahi. sb intact rakhna h.