"function updateOpenPortfolio() {
  // Start time tracking
  const startTime = new Date();

  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");
    Logger.log(`Created new sheet: "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]);
    Logger.log(`Set Portfolio headers to: ${newHeaders.join(", ")}`);
  }

  // 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
    Logger.log(`Cleared existing data in "Portfolio" from row 4`);
  }

  // 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"],
    "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();

  function generateTrade(quantity, price, fees, action, assetClass, broker, user, date, tradeType) {
    return {
      shares: quantity,
      price: price,
      fees: fees,
      action: action,
      assetClass: assetClass,
      broker: broker,
      user: user,
      date: date,
      type: tradeType
    };
  }

  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 = dateCell instanceof Date ? dateCell : 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;
    var assetClass = (row[headerIndices["asset class"]] || "Unknown").toString().trim();
    var broker = (row[headerIndices["broker"]] || "Unknown").toString().trim();
    var user = (row[headerIndices["user"]] || "Unknown").toString().trim();

    var tradeType = "";

    if (type === 'delivery') {
      if (action === 'buy') {
        tradeType = 'Long';
        let key = `${ticker}|${broker}|${user}|${assetClass}|${tradeType}`;
        let trade = generateTrade(quantity, price, fees, action, assetClass, broker, user, date, tradeType);
        let activeTrades = positionsMap.get(key);
        if (!activeTrades) {
          positionsMap.set(key, [trade]);
        } else {
          activeTrades.push(trade);
        }
      } else if (action === 'sell') {
        tradeType = 'Long';
        let key = `${ticker}|${broker}|${user}|${assetClass}|${tradeType}`;
        let activeTrades = positionsMap.get(key);
        if (activeTrades) {
          let sharesToProcess = quantity;
          activeTrades.sort((a, b) => a.date - b.date); // FIFO
          let j = 0;
          while (sharesToProcess > 0 && j < activeTrades.length) {
            let trade = activeTrades[j];
            if (trade.shares <= sharesToProcess + 1e-8) { // Added epsilon to handle floating point
              sharesToProcess -= trade.shares;
              activeTrades.splice(j, 1);
            } else {
              trade.shares -= sharesToProcess;
              sharesToProcess = 0;
              j++;
            }
          }
          if (activeTrades.length === 0) {
            positionsMap.delete(key);
          }
        } else {
          Logger.log(`Warning: No positions found for key ${key} when processing sell action.`);
        }
      } else if (action === 'sellshort') {
        tradeType = 'Short';
        let key = `${ticker}|${broker}|${user}|${assetClass}|${tradeType}`;
        let trade = generateTrade(quantity, price, fees, action, assetClass, broker, user, date, tradeType);
        let activeTrades = positionsMap.get(key);
        if (!activeTrades) {
          positionsMap.set(key, [trade]);
        } else {
          activeTrades.push(trade);
        }
      } else if (action === 'buytocover') {
        tradeType = 'Short';
        let key = `${ticker}|${broker}|${user}|${assetClass}|${tradeType}`;
        let activeTrades = positionsMap.get(key);
        if (activeTrades) {
          let sharesToProcess = quantity;
          activeTrades.sort((a, b) => a.date - b.date); // FIFO
          let j = 0;
          while (sharesToProcess > 0 && j < activeTrades.length) {
            let trade = activeTrades[j];
            if (trade.shares <= sharesToProcess + 1e-8) { // Added epsilon to handle floating point
              sharesToProcess -= trade.shares;
              activeTrades.splice(j, 1);
            } else {
              trade.shares -= sharesToProcess;
              sharesToProcess = 0;
              j++;
            }
          }
          if (activeTrades.length === 0) {
            positionsMap.delete(key);
          }
        } else {
          Logger.log(`Warning: No positions found for key ${key} when processing buytocover action.`);
        }
      }
    } else if (type === 'corp. action' && action === 'split') {
      // Process stock splits
      let splitRatioStr = (row[headerIndices["quantity"]] || "").toString().trim();
      let ratio = 1;
      if (splitRatioStr.includes(":")) {
        let ratioParts = splitRatioStr.split(':');
        let numerator = parseFloat(ratioParts[0]);
        let denominator = parseFloat(ratioParts[1]);
        ratio = numerator / denominator;
      } else {
        ratio = parseFloat(splitRatioStr);
      }
      positionsMap.forEach((value, pKey) => {
        if (pKey.startsWith(`${ticker}|`)) {
          value.forEach(trade => {
            trade.shares *= ratio;
            trade.price /= ratio;
          });
        }
      });
    } else if (type === 'my action') {
      if (action === 'transferfrom') {
        // Handle transfer from one broker to another
        tradeType = 'Long'; // Assuming transfer applies to Long positions
        let key = `${ticker}|${broker}|${user}|${assetClass}|${tradeType}`;
        let nextRow = data[i + 1];
        if (nextRow) {
          let newBroker = (nextRow[headerIndices["broker"]] || "Unknown").toString().trim();
          let newKey = `${ticker}|${newBroker}|${user}|${assetClass}|${tradeType}`;
          let activeTrades = positionsMap.get(key);
          if (activeTrades) {
            let sharesToTransfer = quantity;
            let totalAvailableShares = activeTrades.reduce((sum, trade) => sum + trade.shares, 0);
            if (sharesToTransfer > totalAvailableShares) {
              Logger.log(`Not enough shares to transfer for ${key}.`);
              continue;
            }
            let transferredTrades = [];
            activeTrades.sort((a, b) => a.date - b.date); // FIFO
            while (sharesToTransfer > 0 && activeTrades.length > 0) {
              let trade = activeTrades[0];
              if (trade.shares <= sharesToTransfer + 1e-8) { // Added epsilon
                sharesToTransfer -= trade.shares;
                transferredTrades.push({ ...trade }); // Copy trade
                activeTrades.splice(0, 1);
              } else {
                let transferredTrade = { ...trade, shares: sharesToTransfer };
                trade.shares -= sharesToTransfer;
                sharesToTransfer = 0;
                transferredTrades.push(transferredTrade);
              }
            }
            if (activeTrades.length === 0) {
              positionsMap.delete(key);
            }
            // Add transferred trades to new broker
            let newActiveTrades = positionsMap.get(newKey);
            if (!newActiveTrades) {
              positionsMap.set(newKey, transferredTrades);
            } else {
              newActiveTrades.push(...transferredTrades);
            }
          } else {
            Logger.log(`No active trades found for key ${key} during transfer.`);
          }
        } else {
          Logger.log(`No next row found for transfer action at row ${i + 2}.`);
        }
      }
    }
  } // End of loop processing

  // Prepare data for output
  let returnArry = [];
  let sn = 1;
  let sortedKeys = Array.from(positionsMap.keys()).sort();
  sortedKeys.forEach(key => {
    let value = positionsMap.get(key);
    if (!value || value.length === 0) return; // Skip if no active trades

    let totalShares = 0;
    let totalCost = 0;
    let totalFees = 0;
    let avgPrice = 0;
    let [ticker, currentBroker, currentUser, currentAssetClass, longOrShort] = key.split('|');
    value.forEach(trade => {
      totalShares += trade.shares;
      totalCost += trade.shares * trade.price;
      totalFees += trade.fees;
    });
    // Handle floating point precision by using a small epsilon
    if (totalShares > 1e-8) { // Only include if totalShares is effectively greater than zero
      avgPrice = totalShares > 0 ? (totalCost + totalFees) / totalShares : 0;
      returnArry.push([
        sn++,
        ticker,
        longOrShort,
        Number(totalShares.toFixed(4)),
        avgPrice.toFixed(2),
        currentAssetClass,
        currentBroker,
        currentUser
      ]);
    }
  });

  // Output to "Portfolio" sheet
  if (returnArry.length > 0) {
    portfolioSheet.getRange(4, 1, returnArry.length, returnArry[0].length).setValues(returnArry);
    portfolioSheet.getRange(4, 4, returnArry.length, 2).setNumberFormat('#,##0.00');
    Logger.log(`Portfolio updated with ${returnArry.length} records.`);
  } 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.");

  // End time tracking and calculate duration
  const endTime = new Date();
  const durationMs = endTime - startTime; // Duration in milliseconds
  const durationSeconds = (durationMs / 1000).toFixed(2); // Duration in seconds with two decimal places
  Logger.log(`Script execution time: ${durationSeconds} seconds.`);
}"

"function updateOpenPortfolio() {
  // Start time tracking
  const startTime = new Date();

  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");
    Logger.log(`Created new sheet: "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]);
    Logger.log(`Set Portfolio headers to: ${newHeaders.join(", ")}`);
  }

  // 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
    Logger.log(`Cleared existing data in "Portfolio" from row 4`);
  }

  // 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"],
    "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();

  function generateTrade(quantity, price, fees, action, assetClass, broker, user, date, tradeType) {
    return {
      shares: quantity,
      price: price,
      fees: fees,
      action: action,
      assetClass: assetClass,
      broker: broker,
      user: user,
      date: date,
      type: tradeType
    };
  }

  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 = dateCell instanceof Date ? dateCell : 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;
    var assetClass = (row[headerIndices["asset class"]] || "Unknown").toString().trim();
    var broker = (row[headerIndices["broker"]] || "Unknown").toString().trim();
    var user = (row[headerIndices["user"]] || "Unknown").toString().trim();

    var tradeType = "";

    if (type === 'delivery') {
      if (action === 'buy') {
        tradeType = 'Long';
        let key = `${ticker}|${broker}|${user}|${assetClass}|${tradeType}`;
        let trade = generateTrade(quantity, price, fees, action, assetClass, broker, user, date, tradeType);
        let activeTrades = positionsMap.get(key);
        if (!activeTrades) {
          positionsMap.set(key, [trade]);
        } else {
          activeTrades.push(trade);
        }
      } else if (action === 'sell') {
        tradeType = 'Long';
        let key = `${ticker}|${broker}|${user}|${assetClass}|${tradeType}`;
        let activeTrades = positionsMap.get(key);
        if (activeTrades) {
          let sharesToProcess = quantity;
          activeTrades.sort((a, b) => a.date - b.date); // FIFO
          let j = 0;
          while (sharesToProcess > 0 && j < activeTrades.length) {
            let trade = activeTrades[j];
            if (trade.shares <= sharesToProcess + 1e-8) { // Handle floating-point precision
              sharesToProcess -= trade.shares;
              activeTrades.splice(j, 1);
            } else {
              trade.shares -= sharesToProcess;
              sharesToProcess = 0;
              j++;
            }
          }
          if (activeTrades.length === 0) {
            positionsMap.delete(key);
          }
        } else {
          Logger.log(`Warning: No positions found for key ${key} when processing sell action.`);
        }
      } else if (action === 'sellshort') {
        tradeType = 'Short';
        let key = `${ticker}|${broker}|${user}|${assetClass}|${tradeType}`;
        let trade = generateTrade(quantity, price, fees, action, assetClass, broker, user, date, tradeType);
        let activeTrades = positionsMap.get(key);
        if (!activeTrades) {
          positionsMap.set(key, [trade]);
        } else {
          activeTrades.push(trade);
        }
      } else if (action === 'buytocover') {
        tradeType = 'Short';
        let key = `${ticker}|${broker}|${user}|${assetClass}|${tradeType}`;
        let activeTrades = positionsMap.get(key);
        if (activeTrades) {
          let sharesToProcess = quantity;
          activeTrades.sort((a, b) => a.date - b.date); // FIFO
          let j = 0;
          while (sharesToProcess > 0 && j < activeTrades.length) {
            let trade = activeTrades[j];
            if (trade.shares <= sharesToProcess + 1e-8) { // Handle floating-point precision
              sharesToProcess -= trade.shares;
              activeTrades.splice(j, 1);
            } else {
              trade.shares -= sharesToProcess;
              sharesToProcess = 0;
              j++;
            }
          }
          if (activeTrades.length === 0) {
            positionsMap.delete(key);
          }
        } else {
          Logger.log(`Warning: No positions found for key ${key} when processing buytocover action.`);
        }
      }
    } else if (type === 'corp. action' && action === 'split') {
      // Process stock splits
      let splitRatioStr = (row[headerIndices["quantity"]] || "").toString().trim();
      let ratio = 1;
      if (splitRatioStr.includes(":")) {
        let ratioParts = splitRatioStr.split(':');
        let numerator = parseFloat(ratioParts[0]);
        let denominator = parseFloat(ratioParts[1]);
        ratio = numerator / denominator;
      } else {
        ratio = parseFloat(splitRatioStr);
      }
      positionsMap.forEach((value, pKey) => {
        if (pKey.startsWith(`${ticker}|`)) {
          value.forEach(trade => {
            trade.shares *= ratio;
            trade.price /= ratio;
          });
        }
      });
    } else if (type === 'my action') {
      if (action === 'transferfrom') {
        // Handle transfer from one broker to another
        tradeType = 'Long'; // Assuming transfer applies to Long positions
        let key = `${ticker}|${broker}|${user}|${assetClass}|${tradeType}`;
        let nextRow = data[i + 1];
        if (nextRow) {
          let newBroker = (nextRow[headerIndices["broker"]] || "Unknown").toString().trim();
          let newKey = `${ticker}|${newBroker}|${user}|${assetClass}|${tradeType}`;
          let activeTrades = positionsMap.get(key);
          if (activeTrades) {
            let sharesToTransfer = quantity;
            let totalAvailableShares = activeTrades.reduce((sum, trade) => sum + trade.shares, 0);
            if (sharesToTransfer > totalAvailableShares + 1e-8) { // Handle floating-point precision
              Logger.log(`Not enough shares to transfer for ${key}.`);
              continue;
            }
            let transferredTrades = [];
            activeTrades.sort((a, b) => a.date - b.date); // FIFO
            while (sharesToTransfer > 0 && activeTrades.length > 0) {
              let trade = activeTrades[0];
              if (trade.shares <= sharesToTransfer + 1e-8) { // Handle floating-point precision
                sharesToTransfer -= trade.shares;
                transferredTrades.push({ ...trade }); // Copy trade
                activeTrades.splice(0, 1);
              } else {
                let transferredTrade = { ...trade, shares: sharesToTransfer };
                trade.shares -= sharesToTransfer;
                sharesToTransfer = 0;
                transferredTrades.push(transferredTrade);
              }
            }
            if (activeTrades.length === 0) {
              positionsMap.delete(key);
            }
            // Add transferred trades to new broker
            let newActiveTrades = positionsMap.get(newKey);
            if (!newActiveTrades) {
              positionsMap.set(newKey, transferredTrades);
            } else {
              newActiveTrades.push(...transferredTrades);
            }
          } else {
            Logger.log(`No active trades found for key ${key} during transfer.`);
          }
        } else {
          Logger.log(`No next row found for transfer action at row ${i + 2}.`);
        }
      }
    }
  } // End of loop processing

  // Prepare data for output
  let returnArry = [];
  let sn = 1;
  let sortedKeys = Array.from(positionsMap.keys()).sort();
  sortedKeys.forEach(key => {
    let value = positionsMap.get(key);
    if (!value || value.length === 0) return; // Skip if no active trades

    let totalShares = 0;
    let totalCost = 0;
    let totalFees = 0;
    let avgPrice = 0;
    let [ticker, currentBroker, currentUser, currentAssetClass, longOrShort] = key.split('|');
    value.forEach(trade => {
      totalShares += trade.shares;
      totalCost += trade.shares * trade.price;
      totalFees += trade.fees;
    });
    // Handle floating point precision by using a small epsilon
    let epsilon = 1e-8;
    if (Math.abs(totalShares) > epsilon) { // Only include if totalShares is effectively non-zero
      avgPrice = (totalCost + totalFees) / totalShares;
      returnArry.push([
        sn++,
        ticker,
        longOrShort,
        Number(totalShares.toFixed(4)),
        avgPrice.toFixed(2),
        currentAssetClass,
        currentBroker,
        currentUser
      ]);
    }
  });

  // Output to "Portfolio" sheet
  if (returnArry.length > 0) {
    portfolioSheet.getRange(4, 1, returnArry.length, returnArry[0].length).setValues(returnArry);
    portfolioSheet.getRange(4, 4, returnArry.length, 2).setNumberFormat('#,##0.00');
    Logger.log(`Portfolio updated with ${returnArry.length} records.`);
  } 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.");

  // End time tracking and calculate duration
  const endTime = new Date();
  const durationMs = endTime - startTime; // Duration in milliseconds
  const durationSeconds = (durationMs / 1000).toFixed(2); // Duration in seconds with two decimal places
  Logger.log(`Script execution time: ${durationSeconds} seconds.`);
}"

out of two which is better