class_docstr: The correctness of the metrics of Account in nested execution depends on the shallow copy of `trade_account` in
  qlib/backtest/executor.py:NestedExecutor
  Different level of executor has different Account object when calculating metrics. But the position object is
  shared cross all the Account object.
docstr: update portfolio_metrics
docstr_tok: ["update","portfolio_metrics"]
code: def update_portfolio_metrics(
      self, trade_start_time: pd.Timestamp, trade_end_time: pd.Timestamp
  ) -> None:
      """update portfolio_metrics"""
      # calculate earning
      # account_value - last_account_value
      # for the first trade date, account_value - init_cash
      # self.portfolio_metrics.is_empty() to judge is_first_trade_date
      # get last_account_value, last_total_cost, last_total_turnover
      assert self.portfolio_metrics is not None
  
      if self.portfolio_metrics.is_empty():
          last_account_value = self.init_cash
          last_total_cost = 0
          last_total_turnover = 0
      else:
          last_account_value = (
              self.portfolio_metrics.get_latest_account_value()
          )
          last_total_cost = (
              self.portfolio_metrics.get_latest_total_cost()
          )
          last_total_turnover = (
              self.portfolio_metrics.get_latest_total_turnover()
          )
  
      # get now_account_value, now_stock_value, now_earning, now_cost, now_turnover
      now_account_value = self.current_position.calculate_value()
      now_stock_value = self.current_position.calculate_stock_value()
      now_earning = now_account_value - last_account_value
      now_cost = self.accum_info.get_cost - last_total_cost
      now_turnover = self.accum_info.get_turnover - last_total_turnover
  
      # update portfolio_metrics for today
      # judge whether the trading is begin.
      # and don't add init account state into portfolio_metrics, due to we don't have excess return in those days.
      self.portfolio_metrics.update_portfolio_metrics_record(
          trade_start_time=trade_start_time,
          trade_end_time=trade_end_time,
          account_value=now_account_value,
          cash=self.current_position.position["cash"],
          return_rate=(now_earning + now_cost) / last_account_value,
          # here use earning to calculate return, position's view, earning consider cost, true return
          # in order to make same definition with original backtest in evaluate.py
          total_turnover=self.accum_info.get_turnover,
          turnover_rate=now_turnover / last_account_value,
          total_cost=self.accum_info.get_cost,
          cost_rate=now_cost / last_account_value,
          stock_value=now_stock_value,
      )
  
imports: from __future__ import annotations
  import copy
  from typing import Dict, List, Optional, Tuple, cast
  import pandas
  from qlib.utils import init_instance_by_config
  from .decision import BaseTradeDecision, Order
  from .exchange import Exchange
  from .high_performance_ds import BaseOrderIndicator
  from .position import BasePosition
  from .report import Indicator, PortfolioMetrics
label_desc: Use image node_id 11 for calling the Account obj's underlying member method code with example usage: obj.update_portfolio_metrics(trade_start_time, trade_end_time) without return types