docstr: dump data as the following format:
      `/path/to/<field>.data`
          [date, period, value, _next]
          [date, period, value, _next]
          [...]
      `/path/to/<field>.index`
          [first_year, index, index, ...]
  
  `<field.data>` contains the data as the point-in-time (PIT) order: `value` of `period`
  is published at `date`, and its successive revised value can be found at `_next` (linked list).
  
  `<field>.index` contains the index of value for each period (quarter or year). To save
  disk space, we only store the `first_year` as its followings periods can be easily infered.
  
  Parameters
  ----------
  symbol: str
      stock symbol
  interval: str
      data interval
  overwrite: bool
      whether overwrite existing data or update only
docstr_tok: ["dump","data","as","the","following","format",":","`","\/path\/to\/","<","field",">",".data","`","[","date",",","period",",","value",",","_next","]","[","date",",","period",",","value",",","_next","]","[","...","]","`","\/path\/to\/","<","field",">",".index","`","[","first_year",",","index",",","index",",","...","]","`","<","field.data",">","`","contains","the","data","as","the","point-in-time","(","PIT",")","order",":","`","value","`","of","`","period","`","is","published","at","`","date","`",",","and","its","successive","revised","value","can","be","found","at","`","_next","`","(","linked","list",")",".","`","<","field",">",".index","`","contains","the","index","of","value","for","each","period","(","quarter","or","year",")",".","To","save","disk","space",",","we","only","store","the","`","first_year","`","as","its","followings","periods","can","be","easily","infered",".","Parameters","--","--","--","--","--","symbol",":","str","stock","symbol","interval",":","str","data","interval","overwrite",":","bool","whether","overwrite","existing","data","or","update","only"]
code: def _dump_pit(
      self,
      file_path: str,
      interval: str = "quarterly",
      overwrite: bool = False,
  ):
      """
      dump data as the following format:
          `/path/to/<field>.data`
              [date, period, value, _next]
              [date, period, value, _next]
              [...]
          `/path/to/<field>.index`
              [first_year, index, index, ...]
  
      `<field.data>` contains the data as the point-in-time (PIT) order: `value` of `period`
      is published at `date`, and its successive revised value can be found at `_next` (linked list).
  
      `<field>.index` contains the index of value for each period (quarter or year). To save
      disk space, we only store the `first_year` as its followings periods can be easily infered.
  
      Parameters
      ----------
      symbol: str
          stock symbol
      interval: str
          data interval
      overwrite: bool
          whether overwrite existing data or update only
      """
      symbol = self.get_symbol_from_file(file_path)
      df = self.get_source_data(file_path)
      if df.empty:
          logger.warning(f"{symbol} file is empty")
          return
      for field in self.get_dump_fields(df):
          df_sub = df.query(
              f'{self.field_column_name}=="{field}"'
          ).sort_values(self.date_column_name)
          if df_sub.empty:
              logger.warning(f"field {field} of {symbol} is empty")
              continue
          data_file, index_file = self.get_filenames(
              symbol, field, interval
          )
  
          ## calculate first & last period
          start_year = df_sub[self.period_column_name].min()
          end_year = df_sub[self.period_column_name].max()
          if interval == self.INTERVAL_quarterly:
              start_year //= 100
              end_year //= 100
  
          # adjust `first_year` if existing data found
          if not overwrite and index_file.exists():
              with open(index_file, "rb") as fi:
                  (first_year,) = struct.unpack(
                      self.PERIOD_DTYPE, fi.read(self.PERIOD_DTYPE_SIZE)
                  )
                  n_years = len(fi.read()) // self.INDEX_DTYPE_SIZE
                  if interval == self.INTERVAL_quarterly:
                      n_years //= 4
                  start_year = first_year + n_years
          else:
              with open(index_file, "wb") as f:
                  f.write(struct.pack(self.PERIOD_DTYPE, start_year))
              first_year = start_year
  
          # if data already exists, continue to the next field
          if start_year > end_year:
              logger.warning(
                  f"{symbol}-{field} data already exists, continue to the next field"
              )
              continue
  
          # dump index filled with NA
          with open(index_file, "ab") as fi:
              for year in range(start_year, end_year + 1):
                  if interval == self.INTERVAL_quarterly:
                      fi.write(
                          struct.pack(
                              self.INDEX_DTYPE * 4, *[self.NA_INDEX] * 4
                          )
                      )
                  else:
                      fi.write(
                          struct.pack(self.INDEX_DTYPE, self.NA_INDEX)
                      )
  
          # if data already exists, remove overlapped data
          if not overwrite and data_file.exists():
              with open(data_file, "rb") as fd:
                  fd.seek(-self.DATA_DTYPE_SIZE, 2)
                  last_date, _, _, _ = struct.unpack(
                      self.DATA_DTYPE, fd.read()
                  )
              df_sub = df_sub.query(
                  f"{self.date_column_name}>{last_date}"
              )
          # otherwise,
          # 1) truncate existing file or create a new file with `wb+` if overwrite,
          # 2) or append existing file or create a new file with `ab+` if not overwrite
          else:
              with open(data_file, "wb+" if overwrite else "ab+"):
                  pass
  
          with open(data_file, "rb+") as fd, open(
              index_file, "rb+"
          ) as fi:
              # update index if needed
              for i, row in df_sub.iterrows():
                  # get index
                  offset = get_period_offset(
                      first_year,
                      row.period,
                      interval == self.INTERVAL_quarterly,
                  )
  
                  fi.seek(
                      self.PERIOD_DTYPE_SIZE
                      + self.INDEX_DTYPE_SIZE * offset
                  )
                  (cur_index,) = struct.unpack(
                      self.INDEX_DTYPE, fi.read(self.INDEX_DTYPE_SIZE)
                  )
  
                  # Case I: new data => update `_next` with current index
                  if cur_index == self.NA_INDEX:
                      fi.seek(
                          self.PERIOD_DTYPE_SIZE
                          + self.INDEX_DTYPE_SIZE * offset
                      )
                      fi.write(struct.pack(self.INDEX_DTYPE, fd.tell()))
                  # Case II: previous data exists => find and update the last `_next`
                  else:
                      _cur_fd = fd.tell()
                      prev_index = self.NA_INDEX
                      while (
                          cur_index != self.NA_INDEX
                      ):  # NOTE: first iter always != NA_INDEX
                          fd.seek(
                              cur_index
                              + self.DATA_DTYPE_SIZE
                              - self.INDEX_DTYPE_SIZE
                          )
                          prev_index = cur_index
                          (cur_index,) = struct.unpack(
                              self.INDEX_DTYPE,
                              fd.read(self.INDEX_DTYPE_SIZE),
                          )
                      fd.seek(
                          prev_index
                          + self.DATA_DTYPE_SIZE
                          - self.INDEX_DTYPE_SIZE
                      )
                      fd.write(
                          struct.pack(self.INDEX_DTYPE, _cur_fd)
                      )  # NOTE: add _next pointer
                      fd.seek(_cur_fd)
  
                  # dump data
                  fd.write(
                      struct.pack(
                          self.DATA_DTYPE,
                          row.date,
                          row.period,
                          row.value,
                          self.NA_INDEX,
                      )
                  )
  
imports: import shutil
  import struct
  from pathlib import Path
  from typing import Iterable
  from functools import partial
  from concurrent.futures import ProcessPoolExecutor
  import fire
  import pandas
  from tqdm import tqdm
  from loguru import logger
  from qlib.utils import fname_to_code, get_period_offset
  from qlib.config import C
label_desc: Use image node_id 7 for calling the DumpPitData obj's underlying member method code with example usage: obj._dump_pit(file_path, interval, overwrite) without return types