class_docstr: Single-asset order execution (SAOE) simulator.
  
  As there's no "calendar" in the simple simulator, ticks are used to trade.
  A tick is a record (a line) in the pickle-styled data file.
  Each tick is considered as a individual trading opportunity.
  If such fine granularity is not needed, use ``ticks_per_step`` to
  lengthen the ticks for each step.
  
  In each step, the traded amount are "equally" separated to each tick,
  then bounded by volume maximum execution volume (i.e., ``vol_threshold``),
  and if it's the last step, try to ensure all the amount to be executed.
  
  Parameters
  ----------
  order
      The seed to start an SAOE simulator is an order.
  data_dir
      Path to load backtest data.
  feature_columns_today
      Columns of today's feature.
  feature_columns_yesterday
      Columns of yesterday's feature.
  data_granularity
      Number of ticks between consecutive data entries.
  ticks_per_step
      How many ticks per step.
  vol_threshold
      Maximum execution volume (divided by market execution volume).
class_docstr_tok: ["Single-asset","order","execution","(","SAOE",")","simulator",".","As","there","'s","no","``","calendar","''","in","the","simple","simulator",",","ticks","are","used","to","trade",".","A","tick","is","a","record","(","a","line",")","in","the","pickle-styled","data","file",".","Each","tick","is","considered","as","a","individual","trading","opportunity",".","If","such","fine","granularity","is","not","needed",",","use","``","ticks_per_step","``","to","lengthen","the","ticks","for","each","step",".","In","each","step",",","the","traded","amount","are","``","equally","''","separated","to","each","tick",",","then","bounded","by","volume","maximum","execution","volume","(","i.e.",",","``","vol_threshold","``",")",",","and","if","it","'s","the","last","step",",","try","to","ensure","all","the","amount","to","be","executed",".","Parameters","--","--","--","--","--","order","The","seed","to","start","an","SAOE","simulator","is","an","order",".","data_dir","Path","to","load","backtest","data",".","feature_columns_today","Columns","of","today","'s","feature",".","feature_columns_yesterday","Columns","of","yesterday","'s","feature",".","data_granularity","Number","of","ticks","between","consecutive","data","entries",".","ticks_per_step","How","many","ticks","per","step",".","vol_threshold","Maximum","execution","volume","(","divided","by","market","execution","volume",")","."]
docstr: Execute one step or SAOE.
  
  Parameters
  ----------
  amount
      The amount you wish to deal. The simulator doesn't guarantee all the amount to be successfully dealt.
docstr_tok: ["Execute","one","step","or","SAOE",".","Parameters","--","--","--","--","--","amount","The","amount","you","wish","to","deal",".","The","simulator","does","n't","guarantee","all","the","amount","to","be","successfully","dealt","."]
code: def step(self, amount: float) -> None:
      """Execute one step or SAOE.
  
      Parameters
      ----------
      amount
          The amount you wish to deal. The simulator doesn't guarantee all the amount to be successfully dealt.
      """
  
      assert not self.done()
  
      self.market_price = self.market_vol = None  # avoid misuse
      exec_vol = self._split_exec_vol(amount)
      assert self.market_price is not None
      assert self.market_vol is not None
  
      ticks_position = self.position - np.cumsum(exec_vol)
  
      self.position -= exec_vol.sum()
      if abs(self.position) < 1e-6:
          self.position = 0.0
      if self.position < -EPS or (exec_vol < -EPS).any():
          raise ValueError(
              f"Execution volume is invalid: {exec_vol} (position = {self.position})"
          )
  
      # Get time index available for this step
      time_index = self._get_ticks_slice(
          self.cur_time, self._next_time()
      )
  
      self.history_exec = self._dataframe_append(
          self.history_exec,
          SAOEMetrics(
              # It should have the same keys with SAOEMetrics,
              # but the values do not necessarily have the annotated type.
              # Some values could be vectorized (e.g., exec_vol).
              stock_id=self.order.stock_id,
              datetime=time_index,
              direction=self.order.direction,
              market_volume=self.market_vol,
              market_price=self.market_price,
              amount=exec_vol,
              inner_amount=exec_vol,
              deal_amount=exec_vol,
              trade_price=self.market_price,
              trade_value=self.market_price * exec_vol,
              position=ticks_position,
              ffr=exec_vol / self.order.amount,
              pa=price_advantage(
                  self.market_price,
                  self.twap_price,
                  self.order.direction,
              ),
          ),
      )
  
      self.history_steps = self._dataframe_append(
          self.history_steps,
          [
              self._metrics_collect(
                  self.cur_time,
                  self.market_vol,
                  self.market_price,
                  amount,
                  exec_vol,
              )
          ],
      )
  
      if self.done():
          if self.env is not None:
              self.env.logger.add_any(
                  "history_steps",
                  self.history_steps,
                  loglevel=LogLevel.DEBUG,
              )
              self.env.logger.add_any(
                  "history_exec",
                  self.history_exec,
                  loglevel=LogLevel.DEBUG,
              )
  
          self.metrics = self._metrics_collect(
              self.ticks_index[0],  # start time
              self.history_exec["market_volume"],
              self.history_exec["market_price"],
              self.history_steps["amount"].sum(),
              self.history_exec["deal_amount"],
          )
  
          # NOTE (yuge): It looks to me that it's the "correct" decision to
          # put all the logs here, because only components like simulators themselves
          # have the knowledge about what could appear in the logs, and what's the format.
          # But I admit it's not necessarily the most convenient way.
          # I'll rethink about it when we have the second environment
          # Maybe some APIs like self.logger.enable_auto_log() ?
  
          if self.env is not None:
              for key, value in self.metrics.items():
                  if isinstance(value, float):
                      self.env.logger.add_scalar(key, value)
                  else:
                      self.env.logger.add_any(key, value)
  
      self.cur_time = self._next_time()
      self.cur_step += 1
  
imports: from __future__ import annotations
  from typing import Any, cast, List, Optional
  import numpy
  import pandas
  from pathlib import Path
  from qlib.backtest.decision import Order, OrderDir
  from qlib.constant import EPS, EPS_T, float_or_ndarray
  from qlib.rl.data.base import BaseIntradayBacktestData
  from qlib.rl.data.native import DataframeIntradayBacktestData, load_handler_intraday_processed_data
  from qlib.rl.data.pickle_styled import load_simple_intraday_backtest_data
  from qlib.rl.simulator import Simulator
  from qlib.rl.utils import LogLevel
  from .state import SAOEMetrics, SAOEState
label_desc: Use image node_id 3 for calling the SingleAssetOrderExecutionSimple obj's underlying member method code with example usage: obj.step(amount) without return types