/* Copyright (c) 2005-2026 Tachyon Design Automation Corp. */

/*
 * routine to read IB date range files and emit transactoin ascii file
 * new algorithm just sorts and writes the sorted by symbol codtrans file
 *
 * This is all that is needed since it has the trade records - only IB
 * can calculate actual NAV
 */

/* === INSERT LICENSE ===
ib_ytd_anal program for analyzing Interactive Brokers format downloaded
Year to Date .csv format spread sheet file.

Copyright © 2005-2026 Tachyon Design Automation Corp.
All Rights reserved.  Licensed software subject to prohibitions and
restrictions.

The intent of this document is to state the conditions under which the
ib_ytd_anal trade analysis C program may be copied and executed such that
the Copyright Holder Tachyon Design Automation maintains distribution and
artistic control over the use and development as granted by Copyright law. 

Licensee may make modifications to ib_ytd_anal for internal use.  ib_ytd_anal
may not be sold without obtaining a sales license from Tachyon DA. 
All forms of indirect sales such as bundling or executing on a shared
cloud are prohibited.  Other uses of ib_ytd_anal program require written
permission from Tachyon Design Automation.

 DISCLAIMER OF WARRANTIES: THIS SOFTWARE IS PROVIDED "AS IS" AND
WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION,
THE IMPLIED WARRANTIES OF MERCHANTIBILITY, FITNESS FOR A PARTICULAR PURPOSE,
OR NON-INFRINGEMENT.  THE COPYRIGHT HOLDER SHALL NOT BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING IN ANY WAY
OUT OF THE USE OF THE SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
OF SUCH DAMAGE.

=== END INSERT === */

#define VERS2 1.20
#define OFDT "03/06/26"

#include <stdio.h>
#include <time.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/time.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <stdarg.h>
#include <pthread.h>
#include <ctype.h>
#include <math.h>

#include "newport.h"

static char copyright[] = "Copyright (c) 2005-2026 Tachyon Design Automation";

/* externs defined here */
long __mem_use;        /* counts allocated mem for debugging */
long __mem_str_use;    /* counts allocated string memory */
int __line_cnt;        /* current line number */
char __curfnam[RECLEN];/* current token input file name */  
int __error_cnt;       /* error count */
int __warn_cnt;        /* warning count */
int __inform_cnt;      /* no. informs (usually only cnted) */
int __nowarns;         /* T => do not emit warnings */
int __noinforms;       /* T => do not emit informs */
FILE *__log_s;         /* log file and stream */
char __logfnam[RECLEN];/* name of log file */
FILE *__trout_s;       /* transaction out file */
char __cod_trans_fnam[RECLEN]; /* coded trans file name */
char __cur_fnam[RECLEN];/* current input file name */
char __cur_line[RECLEN];/* cur work line from a .csv file */
char __brkrnam[RECLEN]; /* broker for misc msgs */
char __s_run_dtstr[RECLEN];/* dt string from schwab file name */
char __s_allpos_fnam[RECLEN];/* Schwab all pos fnam - end .CSV */
FILE *__s_allpos_s;
char __s_iratrans_fnam[RECLEN];/* Schwab ytd ira trans fnam */
FILE *__s_iratrans_s;
char __s_indtrans_fnam[RECLEN];/* Schwab ytd indiv trans fnam */
FILE *__s_indtrans_s;
double __start_nav;    /* starting NAV - jan. 1 of year */
double __end_nav;      /* ending NAV day report run */
double __tot_ib_ytd_pandl;/* tot YTD p&L - deduct expense?*/
double __ib_weight_pct_gain; /* IB weighted by time profit % */
double __ib_end_settled_cash; /* today cash- available */   
double __end_mtm_profit; /* IB MTM - NAV increase - profit? */
double __end_dep_and_wdraw; /* total cash added or removed */
double __end_ib_divs;  /* what IB say YTD dividends are */ 
double __end_ib_interest;/* what IB say YTD interest is */
int32 __some_revs_sep;   /* some revs add tr to 0 then tr */
int32 __adj_tdy_op_trans; /* add virutal pseudo cl for tdy op */
char **__csvfldtab;    /* tab for line broken into flds */
int32 __lastcsvfld;    /* last used csv separated fld */ 
/* tran tab is ptr to a table of ptrs to alloc tran records */
struct trans_t **__trantab; /* trans from reading csv file */
int32 __last_trani;
struct trclndx_t *__trclndx; 
int32 __last_trcli;
struct symndx_t **__symndx;
int32 __last_symndxi;
int32 *__basgrpndx;       /* tab with same grp bas sym ndxes */
int32 __last_sgrpndxi;    /* last used grp sym ndx tab el */
struct trans_t **__posndx;/* tab of ptrs to op pos trans */
int32 __last_posndxi;

/* various calculated portfoilio totals */
double __ytd_open_gain;
double __ytd_open_stk_gain;
double __ytd_open_fut_gain; 
double __ytd_open_opt_gain;
double __ytd_open_futopt_gain; 
double __tot_opt_pot_income;
double __tot_futopt_pot_income;
double __tot_open_acru_divs;

double __tot_ytd_rlz_gain;
double __mth_tot_rlz_gain[13];
double __tot_stk_rlz_gain;
double __mth_stk_rlz_gain[13];
double __tot_opt_rlz_gain;
double __mth_opt_rlz_gain[13];
double __tot_fut_rlz_gain; 
double __mth_fut_rlz_gain[13];
double __tot_futopt_rlz_gain; 
double __mth_futopt_rlz_gain[13];
double __tot_tdy_net_val;
double __tot_tdy_gross_val;


int32 __cur_mth_num;      /* mth num of run for per mth out */
double __mth_tot_divs[13]; /* tot divis by month - jan is 1 */  
double __ytd_tot_divs;     /* total divs for all instruments */
double __ytd_tot_debits;  /* comms and data fees */
double __ytd_tot_int;     /* interest and dividends */
double __ytd_tot_income;   /* divs plus interest */

int32 __today_jdt;       /* tdy convert to julian dt format */
struct date_t __today_dt; /* today as date record */
struct date_t __when_gen_dat;
struct date_t __marg_tr_when_gen_dat;
struct date_t __ira_tr_when_gen_dat;
struct clktime_t __when_gen_tim;

struct date_t __stmt_start_dat;
struct date_t __stmt_end_dat;
struct date_t __codfil_start_dat;
struct date_t __codfil_end_dat;

/* local routine prototypes */
static void init_glbs(void);
static int32 ib_rd_csv_to_trans(void);
static int32 rd_ib_csv_bld_tranactions(FILE *);
static void fix_rm_div_in_lieu_recs(void);
static void bld_stmt_sec_recs(FILE *);
static void skip_acct_info_section(FILE *);
static void xtrct_skip_net_asset_val_section(FILE *);
static void rd_chg_in_nav_section(FILE *);
static void rd_bld_open_pos_rec(void);
static void sep_reverses_to_2_trades(void);
static void add_tdy_open_to_0_trade(void); 
static void bld_mtm_perf_sum_section(FILE *f);
static void bld_add_mtm_price_rec(void);
static void rd_optional_total_pandl_section(FILE *);
static void skip_realized_and_unrealized_perf_sum(FILE *);
static void skip_mth_and_ytd_perf_sum_section(FILE *);
static void skip_cash_report_section(FILE *);
static void bld_open_pos_recs(FILE *f);
static void skip_complex_pos_summary_section(FILE *);
static void skip_forex_bal_section(FILE *);
static void bld_trade_sect_recs(FILE *);
static void bld_add_trade_rec(void);
static void fix_set_trade_fut_sym(struct trans_t *, char *);
static void skip_corp_actions_section(FILE *);
static void skip_cash_settled_opts_section(FILE *);
static void bld_deps_and_withdraws_sect_recs(FILE *);
static void bld_fees_sect_recs(FILE *);
static void bld_divs_sect_recs(FILE *);
static int32 parse_xtrct_div_desc_sym(char *, char *, double *, int32 *);
static void bld_withholding_tax_recs(FILE *);
static void bld_interest_recs(FILE *);
static void skip_interest_accurals_section(FILE *);
static void bld_chg_in_div_accruals_section(FILE *);
static void skip_borrow_fee_accruals_section(FILE *);
static void skip_ibrkr_managed_securities_section(FILE *);
static void skip_financial_inst_info_section(FILE *);
static void base_currency_xchg_rate_section(FILE *);
static void skip_codes_section(FILE *);
static void skip_notes_section(FILE *);
static void skip_legalnotes_section(FILE *);
static void add_cross_yr_op_1st_day_trans(int32, char *, char *,
 double, double, int32, double, int32, int32, int32);
static void bld_prev_yr_d1_trans(void);
static void fill_after_trade_flds(void);
static void match_sell_buys(void);
static void set_still_op_from_quant(struct symndx_t *); 
static void fill_all_not_cl_sh_num(void);
static void fill_not_cl_sh_num(struct symndx_t *);
static int32 bld_1trade_clndx(struct symndx_t *, double,  int32);
static void fill_cl_op_avgshpr_fld(void);
static void set_per_sh_gain_fld(void);
static void set_op_pos_tdy_pr_from_oppos(void);
static void bld_bas_sgrps(void);
char *to_futtyp_bas(char *, struct trans_t *);

/* extern routine prototypes */
extern void __set_today_dates(void);
extern int32 __wrd_date_rng_str_to_date(char *, struct date_t *,
 struct date_t *);
extern int32 __rd_xtrct_ib_csv_line(FILE *, int32);
extern int32 __xtrct_ib_csv_dattim(char *, struct date_t *,
 struct clktime_t *);
extern int32 __xtrct_ib_futopt_desc(char *, char *, struct date_t *, int32 *,
 double *);
extern int32 __xtrct_ib_option_desc(char *, char *, struct date_t *, int32 *,
 double *);
extern char __cod_opcl_ch(char *);
extern int32 __month_abbrev_to_num(char *);
extern char * __mth_num_to_fut_let(char *, int32);
extern char *__mth_num_to_mthabrv(char *, int32);
extern int32 __dash_num_date_to_datrec(char *, struct date_t *);
extern void __init_dat(struct date_t *);
extern void __init_clktim(struct clktime_t *);
extern void __init_tran(struct trans_t *); 
extern void __init_symndx(struct symndx_t *);
extern void __bld_ndx_sortsym(void);
extern void __bld_ndx_opos_trans(void);
extern void __add_to_end_trans(struct trans_t *);
extern char *__int_to_cur_type_str(int32);
extern int32 __cur_type_to_int(char *);
extern char *__int_to_acct_type_str(int32);
extern char *__int_to_ityp_str(int32);
extern char *__int_to_typ_str(int32);
extern int32 __month_let_to_num(char);
extern char *__dt_to_string(char *, struct date_t *);
extern int32 __cmp_dt(const struct date_t *, const struct date_t *);
extern int32 __ib_time_to_clktim(char *, struct clktime_t *);
extern int32 __cmp_trans_rec(const void *, const void *);
extern char *__bld_sortsym(char *, struct trans_t *);
extern double __comp_tot_rlz_gain(void);
extern double __smult(struct trans_t *);
extern int32 __fnd_trtab_by_sym(char *, int32);
extern struct pos_t *__fnd_oppos_rec(char *);
extern struct trans_t *__fnd_posndx_oppos(char *, int32);
extern void __calc_rlz_gain(void);
extern void __calc_symndx_divs(void);
extern void __fill_sip_flds(void);

extern void __ferr(int32, char *, ...);
extern void __err(int32, char *, ...);
extern void __case_terr(char *, int32);
extern void __misc_terr(char *, int32);
extern void __msg(char *, ...);
extern char *__my_malloc(int32);
extern char *__stralloc(char *);
extern void __dbg_dmp_symndx(char *);
extern void __dbg_dmp1_tradtr(struct trans_t *);
extern void  __wr_newport_cod_trans_file(void);
extern void __prt_port_pandl(void);

/*
 * main
 */
extern int main(int argc, char **argv)
{
 int32 ti, ai, sav_last_ti;
 struct trans_t *trp;
 time_t now;
 char s1[RECLEN], fsym[RECLEN];

#ifdef __DBMALLOC__
 union dbmalloptarg m;

 /* m.i = M_HANDLE_IGNORE; */
 m.i = M_HANDLE_ABORT;
 dbmallopt(MALLOC_WARN,&m);
 m.str = "malloc.log";
 dbmallopt(MALLOC_ERRFILE,&m);
 /* --- */
 m.i = 1;
 dbmallopt(MALLOC_CKCHAIN,&m);
 /* --- */
 /* --- default is on - uncomment to turn off ---
 m.i = 1;
 dbmallopt(MALLOC_CKDATA,&m);
 --- */
 /* --- */
 m.i = 1;
 dbmallopt(MALLOC_REUSE,&m);
 /* --- */
 /* --- */
 m.i = 3;
 dbmallopt(MALLOC_FILLAREA, &m);
 /* -- */
#endif

 strcpy(__brkrnam, __int_to_acct_type_str(ACCT_IB));
 __set_today_dates();

 /* initialize globals */
 init_glbs();

 strcpy(__logfnam, "ib_ytd_anal.log");
 if ((__log_s = fopen(__logfnam, "w")) == NULL) 
  {
   __misc_terr(__FILE__, __LINE__);
  }
 /* name constructed when .csv Statement section read */
 strcpy(__cod_trans_fnam, "");
 __trout_s = NULL;

 __msg("%s of %s.\n", "IB csv input and print report", VERS2, OFDT);
 __msg("Copyright (c) 2005-2026 Tachyon Design Automation.\n");
 time(&now);
 strcpy(s1, ctime(&now));
 s1[24] = '\0';
 __msg("Today is %s.\n", s1);

 if (argc < 2)
  {
   __msg(" ERROR: Wrong number of arguments: %d (at least 1 required)\n",
    argc - 1);
   __misc_terr(__FILE__, __LINE__);
  }

 /* arg 0 is execed program name */
 __last_trani = -1;
 for (ai = 1; ai < argc; ai++)
  {
   sav_last_ti = __last_trani;
   strcpy(__curfnam, argv[ai]);
   /* SJM 07-03-22 - skip Schwab .csv files */
   if (ib_rd_csv_to_trans() == -1) continue;

   /* fix up and remove dividend in lieu records if possible */
// SJM 01-18-22 - don't bother with this?
   fix_rm_div_in_lieu_recs();

   __msg("  READ IB .CSV transaction file %s with %d tran records\n",
     __cur_fnam, __last_trani - sav_last_ti);
  }
 if (__last_trani <= 0)
  {
   __msg(" ERROR: no IB .csv trans records built\n");
   __misc_terr(__FILE__, __LINE__);
  }
 __cur_mth_num = __codfil_end_dat.month;

 /* first for all stock, opts and futures bld the specific ctrc sym */
 for (ti = 0; ti <= __last_trani; ti++)
  {
   trp = __trantab[ti];
   __bld_sortsym(fsym, trp);
   strcpy(trp->sort_sym, fsym);
  }
 /* bld one tran rec for each open across yr pos - buy/sell 1st day */
 /* maybe the year cross open trade */
 bld_prev_yr_d1_trans();
 qsort((void *) __trantab, __last_trani + 1, sizeof(struct trans_t *),
  __cmp_trans_rec);

 /* bld sym ndx, op pos ndx of trans and add op pos rec to sym ndx */
 /* for symbols with no trades */
 __bld_ndx_sortsym();
 /* DBG remove me --
 __dbg_dmp_symndx("before no trans open pos built");
 --- */
 /* sym ndx tab is master index for all sym - now all syms have trad */

 /* now using bld sym ndx - fixup and add reverses */
 sep_reverses_to_2_trades();
 /* SJM 04-28-25 for both reverses and tdy open pos need to resort */
 /* and rebld sym ndx */
 if (__some_revs_sep)
  {
   /* re-sort */
   qsort((void *) __trantab, __last_trani + 1, sizeof(struct trans_t *),
    __cmp_trans_rec);
   /* rebuild sort sym ndx - this set last sym ndx to -1 */
   __bld_ndx_sortsym();
   /* DBG remove me --
   __dbg_dmp_symndx("after into 2 sep reverses");
    --- */
  }

 fill_all_not_cl_sh_num();
 add_tdy_open_to_0_trade(); 
 /* if any reverses - need to rebuild data structs */
 if (__adj_tdy_op_trans)
  {
   /* re-sort */
   qsort((void *) __trantab, __last_trani + 1, sizeof(struct trans_t *),
    __cmp_trans_rec);
   /* rebuild sort sym ndx - this set last sym ndx to -1 */
   __bld_ndx_sortsym();
   /* DBG remove me ---
   __dbg_dmp_symndx("after tdy open pseudo virtual tr cl added");
    -- */
  }

 /* fill after trade fields */
 fill_after_trade_flds();

 /* need to bld the open pos tran type index for every base sip */
 /* needed because for open pos gain/loss need today's .csv close */
 __bld_ndx_opos_trans();
 set_op_pos_tdy_pr_from_oppos(); 

 /* SJM 04-30-25 - now only cl total flds are are per sh avg pr */
 /* and per sh gain - rlz gain now calculated */ 
 set_per_sh_gain_fld();

 /* DBG removeme ===
 __dbg_dmp_symndx("after trade flds filled");
 === */ 

 /* need to calc per underlying base and per mth flds for all */
 /* base symbol sip records, if no stk/future then use 1st option */
 bld_bas_sgrps();
 /* DBG removie -
 __dbg_dmp_symndx("after base group list built");
 --- */
 /* fills sip flds and underlying base stk/fut with same base sym */
 __fill_sip_flds();

 /* build the cod tran output file name and write cod tran file*/
 sprintf(__cod_trans_fnam, "ibsym_%02d%02d%02d_%02d%02d%02d_%02d%02d",
  __codfil_start_dat.month, __codfil_start_dat.day,
  __codfil_start_dat.year, __codfil_end_dat.month, 
  __codfil_end_dat.day, __codfil_end_dat.year,
  __when_gen_tim.hr, __when_gen_tim.min);

 __wr_newport_cod_trans_file();
 __msg(
  "  Coded transaction file %d elements written to file:\n    %s.\n",
  __last_trani + 1, __cod_trans_fnam);

 __prt_port_pandl();

 exit(0);
}

/*
 * initialize various globals (including flags)
 */
static void init_glbs(void)
{
 int32 i;
 
   __some_revs_sep = FALSE;
  __adj_tdy_op_trans = FALSE;
 __start_nav = 0.0;
  __end_nav = 0.0;
  __tot_ib_ytd_pandl = 0.0;
  __ib_weight_pct_gain = 0.0;
  __ib_end_settled_cash = 0.0;
  __end_mtm_profit = 0.0;
  __end_dep_and_wdraw = 0.0;
  __end_ib_divs = 0.0;
  __end_ib_interest = 0.0;

 __csvfldtab = (char **) __my_malloc(sizeof(char *)*(MAXCSVFLDS + 1));
 for (i = 0; i < MAXCSVFLDS; i++)
   {
    __csvfldtab[i] = (char *) __my_malloc(MAXFLDSIZ);
    strcpy(__csvfldtab[i], "");
   }

 for (i = 0; i < MAXCSVFLDS; i++)
  {
   __csvfldtab[i] = (char *) __my_malloc(MAXFLDSIZ);
   strcpy(__csvfldtab[i], "");
  }

 __trantab = (struct trans_t **)
  __my_malloc(SIZ_TRAN_TAB*sizeof(struct trans_t *));
 for (i = 0; i < SIZ_TRAN_TAB; i++) __trantab[i] = NULL;
 /* number of trans is last trani plus one */
 __last_trani = -1;

 __symndx = (struct symndx_t **)
  __my_malloc(SIZ_SYMNDX*sizeof(struct symndx_t *));
 for (i = 0; i < SIZ_SYMNDX; i++) __symndx[i] = NULL;

  
  __trclndx = (struct trclndx_t *)
   __my_malloc(SIZ_TRAN_TAB*sizeof(struct trclndx_t));
  for (i = 0; i < SIZ_TRAN_TAB; i++)
   {
    __trclndx[i].numcl = -1;
    __trclndx[i].optrndx = -1;
   }
  __basgrpndx = (int32 *) __my_malloc(SIZ_TRAN_TAB*sizeof(int32));
  for (i = 0; i < SIZ_TRAN_TAB; i++) __basgrpndx[i] = -1;

 __posndx = (struct trans_t **)
  __my_malloc(SIZ_TRAN_TAB*sizeof(struct trans_t *));
 __last_posndxi = -1;
 for (i = 0; i < SIZ_TRAN_TAB; i++) __posndx[i] = NULL;

 for (i = 0; i <= 12; i++) __mth_tot_divs[i] = 0.0;
 __ytd_tot_divs = 0.0;
 __ytd_tot_debits = 0.0;
 __ytd_tot_income = 0.0;

 __init_dat(&(__when_gen_dat));
 __init_clktim(&(__when_gen_tim));
 __init_dat(&(__stmt_start_dat));
 __init_dat(&(__stmt_end_dat));
 __init_dat(&(__codfil_start_dat));
 __init_dat(&(__codfil_end_dat));
}

/*
 * ROUTINES TO READ IB CSV FILE
 */

/*
 * open and read IB .csv file - know file name already stored in cur fnam 
 */
static int32 ib_rd_csv_to_trans(void)
{
 char *cp;
 FILE *f;

 /* IB report .csv file has dates in name, but get from inside file */
 if ((cp = strchr(__curfnam, '.')) == NULL)
  {
bad_suffix:
   __err(2, "IB report csv file name with .csv suffix required - %s passed",
    __curfnam);
   exit(2);
  }
 /* SJM 07-03-22 - skip Schwab .csv file - must start with acct U2544270 */
 if (strncmp(__curfnam, "U2544270_", 9) != 0) return(0);

 if (strncmp(cp, ".csv", 4) != 0) goto bad_suffix;
 if (strncmp(__curfnam, "DU1098238", 9) == 0) goto good_file;
 if (strncmp(__curfnam, "U2544270", 8) != 0) goto bad_suffix;

good_file:
 if ((f = fopen(__curfnam, "r")) == NULL) 
  {
   __misc_terr(__FILE__, __LINE__);
  }
 /* read the ib .csv year to date file */
 rd_ib_csv_bld_tranactions(f);
 return(0);
}

/*
 * process opened IB .csv portfolio file and skip unused and fill table
 * of portfolio transaction records
 *
 * returns 0 on success EOF (-1) on error
 */
static int32 rd_ib_csv_bld_tranactions(FILE *f)
{
 __line_cnt = 0;
 if (__rd_xtrct_ib_csv_line(f, FALSE) == EOF)
  {
   __err(5, "IB report .csv file empty"); 
   exit(4);
  }

 /* these all read one line past end of section - or first of next */
 /* section if empty and have decomposed read line into csv field tab */
 bld_stmt_sec_recs(f);
 
 /* if first file that is cod file rng */
 if (__codfil_start_dat.year == 0)
  {
   __codfil_start_dat = __stmt_start_dat;
   __codfil_end_dat = __stmt_end_dat;
  }
 else
  {
   /* illegal if inside */
   if ((__cmp_dt(&(__stmt_start_dat), &(__codfil_start_dat)) >= 0)
    && (__cmp_dt(&(__stmt_end_dat), &(__codfil_end_dat)) <= 0))
    {
     __ferr(22,
      "IB .csv file %s date range inside coded file merged range", __cur_fnam);
     exit(4);
    }
   /* illegal if files overlap - start date inside codfil range */
   if ((__cmp_dt(&(__stmt_start_dat), &(__codfil_start_dat)) >= 0)
    && (__cmp_dt(&(__stmt_start_dat), &(__codfil_end_dat)) <= 0))
    {
     __ferr(22,
      "IB .csv file %s start date inside coded file merged range", __cur_fnam);
     exit(4);
    }
   /* illegal if files overlap - end date inside codfil range */
   if ((__cmp_dt(&(__stmt_end_dat), &(__codfil_start_dat)) >= 0)
    && (__cmp_dt(&(__stmt_end_dat), &(__codfil_end_dat)) <= 0))
    {
     __ferr(22,
      "IB .csv file %s end date inside coded file merged range", __cur_fnam);
     exit(4);
    }
   
   /* since know stmt date not inside - either before or after */ 
   if (__cmp_dt(&(__stmt_start_dat), &(__codfil_start_dat)) < 0)
    {
     /* before */ 
     __codfil_start_dat = __stmt_start_dat;
    }
   else 
    {
     /* after */ 
     __codfil_end_dat = __stmt_end_dat;
    }
  }

 skip_acct_info_section(f);
 xtrct_skip_net_asset_val_section(f);
 rd_chg_in_nav_section(f);
 bld_mtm_perf_sum_section(f);
 rd_optional_total_pandl_section(f);
 skip_realized_and_unrealized_perf_sum(f);
 skip_notes_section(f);
 skip_mth_and_ytd_perf_sum_section(f);
 skip_cash_report_section(f);
 bld_open_pos_recs(f);
 skip_complex_pos_summary_section(f);
 skip_forex_bal_section(f);
 bld_trade_sect_recs(f);
 /* SJM 12-08-23 - skips may or may not be present in YTD .csv files */
 skip_cash_settled_opts_section(f);
 skip_corp_actions_section(f);
 bld_deps_and_withdraws_sect_recs(f);
 bld_fees_sect_recs(f);
 bld_divs_sect_recs(f);
 bld_withholding_tax_recs(f);
 bld_interest_recs(f);
 skip_interest_accurals_section(f);
 bld_chg_in_div_accruals_section(f);
 skip_borrow_fee_accruals_section(f);
 skip_ibrkr_managed_securities_section(f);
 skip_financial_inst_info_section(f);
 base_currency_xchg_rate_section(f);
 skip_codes_section(f);
 /* for paper trading acct no legal notes */
 if (feof(f)) return(EOF);
 skip_legalnotes_section(f);
 if (!feof(f))
  {
  __ferr(4, "IB report .csv file unrecognized at end");
   exit(4);
  }
 return(EOF);
}
    
/*
 * extract dates and time of statement and run 
 *
 */
static  void bld_stmt_sec_recs(FILE *f)
{
 int32 cv;
 char s1[RECLEN], s2[RECLEN];

 strcpy(s1, __csvfldtab[0]);
 strcpy(s2, "Statement");
 cv = strcmp(s1, s2);
 if (cv != 0)
  {
   __ferr(6, "Statement section expected - record type %s read", 
    __csvfldtab[0]);
   exit(4);
  }
 for (;;)
  {
   __rd_xtrct_ib_csv_line(f, TRUE);
   if (strcmp(__csvfldtab[0], "Statement") != 0) break;
   if (strcmp(__csvfldtab[2], "Period") == 0)
    {
// SJM 12-11-20 - what are args for this
     if (__wrd_date_rng_str_to_date(__csvfldtab[3], &(__stmt_start_dat),
      &(__stmt_end_dat)) != 0)
      {
       __misc_terr(__FILE__, __LINE__);
      } 
     continue; 
    }
   if (strcmp(__csvfldtab[2], "WhenGenerated") == 0)
    {
    if (__xtrct_ib_csv_dattim(__csvfldtab[3], &(__when_gen_dat),
      &(__when_gen_tim)) != 0)
      {
       __misc_terr(__FILE__, __LINE__);
      }
     continue;
    }
  }
}

static void skip_acct_info_section(FILE *f)
{
 if (strcmp(__csvfldtab[0], "Account Information") != 0)
  {
   __ferr(6, "Account Information section expected - record type %s read", 
    __csvfldtab[0]);
   exit(4);
  }
 for (;;)
  {
   __rd_xtrct_ib_csv_line(f, TRUE);
   if (strcmp(__csvfldtab[0], "Account Information") != 0) break;
  }
}

static void xtrct_skip_net_asset_val_section(FILE *f)
{
 char s1[RECLEN];

 if (strcmp(__csvfldtab[0], "Net Asset Value") != 0)
  {
   __ferr(6, "Net Asset Value section expected - record type %s read", 
    __csvfldtab[0]);
   exit(4);
  }
 for (;;)
  {
   __rd_xtrct_ib_csv_line(f, TRUE);
   if (strcmp(__csvfldtab[0], "Net Asset Value") != 0) break;
   if (strcmp(__csvfldtab[1], "Header") == 0
    && strncmp(__csvfldtab[2], "Time Weighted", 13) == 0)
    {
     /* time weighted percent profit is next line after section header */ 
     __rd_xtrct_ib_csv_line(f, TRUE);
     if (strcmp(__csvfldtab[0], "Net Asset Value") != 0
      || strcmp(__csvfldtab[1], "Data") != 0)
      {
       __ferr(6, "Net Asset Value section expected - record type %s read", 
        __csvfldtab[0]);
       exit(4);
      }
     strcpy(s1, __csvfldtab[2]);
    if (sscanf(s1, "%lf%%", &(__ib_weight_pct_gain)) != 1)
     {
      __misc_terr(__FILE__, __LINE__);
     }
    }
  }
}

/*
 * since using ib YTD files - need what IB thinks various YTD totals are
 */
static void rd_chg_in_nav_section(FILE *f)
{
 char s1[RECLEN];

 if (strcmp(__csvfldtab[0], "Change in NAV") != 0)
  {
   __ferr(6, "Change in NAV section expected - record type %s read", 
    __csvfldtab[0]);
   exit(4);
  }
 for (;;)
  {
   __rd_xtrct_ib_csv_line(f, TRUE);
   if (strcmp(__csvfldtab[0], "Change in NAV") == 0)
    {
     if (strcmp(__csvfldtab[1], "Header") == 0) continue; 
     if (strcmp(__csvfldtab[2], "Starting Value") == 0)
      {
       strcpy(s1, __csvfldtab[3]);
       if (sscanf(s1, "%lf", &(__start_nav)) != 1)
        {
         __misc_terr(__FILE__, __LINE__);
        }
      }
     else if (strcmp(__csvfldtab[2], "Mark-to-Market") == 0)
      {
       strcpy(s1, __csvfldtab[3]);
       if (sscanf(s1, "%lf", &(__end_mtm_profit)) != 1)
        {
         __misc_terr(__FILE__, __LINE__);
        }
      }
     else if (strcmp(__csvfldtab[2], "Deposits & Withdrawals") == 0)
      {
       strcpy(s1, __csvfldtab[3]);
       if (sscanf(s1, "%lf", &(__end_dep_and_wdraw)) != 1)
        {
         __misc_terr(__FILE__, __LINE__);
        }
      }
     /* SJM 12-06-23 - put this back - why removed? */
     else if (strcmp(__csvfldtab[2], "Option_Cash_Settlements") == 0)
      {
       /* SJM 11-10-23 - need to skip cash selttle option section */
       strcpy(s1, __csvfldtab[3]);
       if (sscanf(s1, "%lf", &(__end_dep_and_wdraw)) != 1)
        {
         __misc_terr(__FILE__, __LINE__);
        }
      }
     else if (strcmp(__csvfldtab[2], "Dividends") == 0)
      {
       strcpy(s1, __csvfldtab[3]);
       if (sscanf(s1, "%lf", &(__end_ib_divs)) != 1)
        {
         __misc_terr(__FILE__, __LINE__);
        }
      }
     else if (strcmp(__csvfldtab[2], "Interest") == 0)
      {
       strcpy(s1, __csvfldtab[3]);
       if (sscanf(s1, "%lf", &(__end_ib_interest)) != 1)
        {
         __misc_terr(__FILE__, __LINE__);
        }
      }
     else if (strcmp(__csvfldtab[2], "Ending Value") == 0)
      {
       strcpy(s1, __csvfldtab[3]);
       if (sscanf(s1, "%lf", &(__end_nav)) != 1)
        {
         __misc_terr(__FILE__, __LINE__);
        }
      }
     continue;
    }
   break;
  }
}

/*
 * mark to market data from last day in year gives end of prior year
 * closing prices - also included in ytd portfolio output file

Mark-to-Market Performance Summary,
Header,
Asset Category,
Symbol,
Prior Quantity,
Current Quantity,
Prior Price,
Current Price,
Mark-to-Market P/L Position,
Mark-to-Market P/L Transaction,
Mark-to-Market P/L Commissions,
Mark-to-Market P/L Other,
Mark-to-Market P/L Total,
Code

Mark-to-Market Performance Summary,Data,Stocks,ONCT,500,500,4.8100,4.9000,45,0,0,0,45,

 */

static void bld_mtm_perf_sum_section(FILE *f)
{
 for (;;)
  {
   if (strcmp(__csvfldtab[0], "Mark-to-Market Performance Summary") != 0)
    break;
   if (strcmp(__csvfldtab[1], "Header") == 0) goto nxt_op_pos;
   if (strcmp(__csvfldtab[3], "Total") != 0) goto nxt_op_pos;

   bld_add_mtm_price_rec();
nxt_op_pos:
   __rd_xtrct_ib_csv_line(f, TRUE);
  }
}

/*
 * bld and add a mark to market price record
 *
 * fills wrk trans rec and if good allocates and adds to list
 * for now just stocks and forex
 */
static void bld_add_mtm_price_rec(void)
{
 int32 is_put, slen;
 double dayclpr, ostrike, sharepr, trcomm, trbasis, quant;
 struct trans_t *trp, *trp2, wrktran;
 char instypnam[RECLEN], s1[RECLEN];

 strcpy(instypnam, "");
 trp = &(wrktran);
 __init_tran(trp); 

 /* uses def flag to set acct typ */
 strcpy(trp->trline, __cur_line);
 trp->lno = __line_cnt;
 trp->tr_typ = TRTYP_TRADE;

 /* SJM 09-28-20 - need to support buy/sell in other currencies? */
 if (strcmp(__csvfldtab[4], "USD") != 0) trp->cur_typ = CUR_USD;
 else if (strcmp(__csvfldtab[4], "CAD") != 0) trp->cur_typ = CUR_CAD;
 else if (strcmp(__csvfldtab[4], "GBP") != 0) trp->cur_typ = CUR_GBP;
 else __misc_terr(__FILE__, __LINE__);

 /* all trades have a time stamp */
 /* SJM 09-29-20 - maybe should return error here */
 __xtrct_ib_csv_dattim(__csvfldtab[6], &(trp->tdate), &(trp->ttime));

  /* sell is minus */
  strcpy(s1, __csvfldtab[7]);
  if (sscanf(s1, "%lf", &(quant)) != 1)
   {
    __misc_terr(__FILE__, __LINE__);
   }
 /* futures and stocks just fill fields */
 /* for opts and future opts, extract strike and exp. and chg sym */
 if (strcmp(__csvfldtab[3], "Stocks") == 0)
  {
   strcpy(instypnam, "Stocks");
   strcpy(trp->symbol, __csvfldtab[5]);
   trp->tr_ityp = TRITYP_STOCK;
  }
 else if (strcmp(__csvfldtab[3], "Futures") == 0)
  {
   strcpy(instypnam, "Futures");
   strcpy(trp->symbol, __csvfldtab[5]);
   trp->tr_ityp = TRITYP_FUT;
   /* change future symbol - to just base - trade set trp fields */
   slen = strlen(trp->symbol);
   trp->symbol[slen - 2] = '\0';
  }
 else if (strcmp(__csvfldtab[3], "Equity and Index Options") == 0)
  {
   strcpy(instypnam, __csvfldtab[5]);
   trp->tr_ityp = TRITYP_OPT;
   /* take apart: WYNN  200403P00080000 */
   strcpy(s1, __csvfldtab[6]);
   /* opt strike will be a whole number - need to adjust if 50 cents */
   if (__xtrct_ib_option_desc(s1, trp->symbol, &(trp->opt_exp_dt),
    &(is_put), &(ostrike)) == -1)
    {
     __misc_terr(__FILE__, __LINE__);
    }
   trp->opt_strike = ostrike;
   if (is_put) trp->is_put_opt = TRUE; else trp->is_put_opt = FALSE;
  }
 else if (strcmp(__csvfldtab[3], "Options On Futures") == 0)
  {
   strcpy(instypnam, __csvfldtab[5]);
   trp->tr_ityp = TRITYP_FUTOPT;
   strcpy(s1, __csvfldtab[6]);
   /* take apart: ES 14FEB20 3275.0 P */
   /* strike already stored as fractional stike */
   if (__xtrct_ib_futopt_desc(s1, trp->symbol, &(trp->opt_exp_dt),
    &(is_put), &(ostrike)) == -1)
    {
     __misc_terr(__FILE__, __LINE__);
    }
   trp->opt_strike = ostrike;
   if (is_put) trp->is_put_opt = TRUE; else trp->is_put_opt = FALSE;
  }
 else __case_terr(__FILE__, __LINE__); 

  strcpy(trp->tr_code, __csvfldtab[15]);
  /* IB sell (or short pos.) is quant, new port */ 
  /* SJM 02-11-25 - sell has quant of minus yr cross right */
  if (quant < 0.0) trp->is_buy = FALSE;
  else trp->is_buy = TRUE;
  trp->tr_quant = (double) quant;

  strcpy(s1, __csvfldtab[8]);
  if (sscanf(s1, "%lf", &(sharepr)) != 1)
   {
    __misc_terr(__FILE__, __LINE__);
   }
  trp->tr_price = sharepr;
  strcpy(s1, __csvfldtab[9]);
  if (sscanf(s1, "%lf", &(dayclpr)) != 1)
   {
    __misc_terr(__FILE__, __LINE__);
   }
  trp->tr_tdy_closepr = dayclpr;
  strcpy(s1, __csvfldtab[11]);
// ??? FIXME
  if (sscanf(s1, "%lf", &(trcomm)) != 1)
   {
    __misc_terr(__FILE__, __LINE__);
   }
  trp->tr_comm = trcomm;
  /* this is basis in dollars */
  strcpy(s1, __csvfldtab[12]);
  if (sscanf(s1, "%lf", &(trbasis)) != 1)
   {
    __misc_terr(__FILE__, __LINE__);
   }
  /* SJM 04-04-25 - eliminating IB world basis - nd to calc it */
  // trp->tr_basis = trbasis;
  /* SJM 10-06-20 - LOOKATME what are rlz and MTM P/L flds in trades? */
  /* this works because tran records does not contain any ptr fields */
  trp2 = (struct trans_t *) __my_malloc(sizeof(struct trans_t));
  *trp2 = wrktran;
  __add_to_end_trans(trp2);
}

/*
 * total P/L section not in all csv files
 * think there will be only one line if present
 */
static void rd_optional_total_pandl_section(FILE *f)
{
 char s1[RECLEN];

 if (strcmp(__csvfldtab[0], "Total P/L for Statement Period") == 0)
  {
   strcpy(s1, __csvfldtab[12]);
   if (sscanf(s1, "%lf", &(__tot_ib_ytd_pandl)) != 1)
    {
     __misc_terr(__FILE__, __LINE__);
    }
  }
 /* need to read next line for next call */
 __rd_xtrct_ib_csv_line(f, TRUE);
}

static void skip_realized_and_unrealized_perf_sum(FILE *f)
{
 if (strcmp(__csvfldtab[0], "Realized & Unrealized Performance Summary") != 0)
  {
   __ferr(6,
    "Realized & Unrealized Performance Summary section expected - record type %s read", 
    __csvfldtab[0]);
   exit(4);
  }
 for (;;)
  {
   __rd_xtrct_ib_csv_line(f, TRUE);
   if (strcmp(__csvfldtab[0], "Realized & Unrealized Performance Summary")
    != 0) break;
  }
}

/*
 * this may not be present
 */
static void skip_mth_and_ytd_perf_sum_section(FILE *f)
{
 if (strcmp(__csvfldtab[0], "Month & Year to Date Performance Summary") != 0)
  {
   return;
  }
 for (;;)
  {
   __rd_xtrct_ib_csv_line(f, TRUE);
   if (strcmp(__csvfldtab[0], "Month & Year to Date Performance Summary") != 0)
    break;
  }
}

/*
 * also may be missing
 */
static void skip_cash_report_section(FILE *f)
{
 char s1[RECLEN];

 if (strcmp(__csvfldtab[0], "Cash Report") != 0)
  {
   return;
  }
 for (;;)
  {
   __rd_xtrct_ib_csv_line(f, TRUE);
   if (strcmp(__csvfldtab[0], "Cash Report") != 0) break;
   if (strcmp(__csvfldtab[2], "Ending Settled Cash") == 0) 
    {
     strcpy(s1, __csvfldtab[4]);
     if (sscanf(s1, "%lf", &(__ib_end_settled_cash)) != 1)
      {
       __misc_terr(__FILE__, __LINE__);
      }
    }
  }
}

/*
 * process open pos. records building TRTYP OPEN POS records
 * possibly no open position records in file
 */
static void bld_open_pos_recs(FILE *f)
{
 for (;;)
  {
   if (strcmp(__csvfldtab[0], "Open Positions") != 0) break;

   /* lots of same headers for different trans types */
   if (strcmp(__csvfldtab[1], "Header") == 0) goto nxt_op_pos;
   /* summary is by instrument symbol */ 
   if (strcmp(__csvfldtab[2], "Summary") != 0) goto nxt_op_pos;
   rd_bld_open_pos_rec();

nxt_op_pos:
   __rd_xtrct_ib_csv_line(f, TRUE);
  }
}

/*
 * this reads and bld the open pos type rec from .csv file
 * needed because it has closing price for all open pos. today
 *
 * if closed before today do not need tdy pr, else will have this
 * .csv open pos record
 */
static void rd_bld_open_pos_rec(void)
{
 double mult, price, quant, basis, unrlz_pl, opt_strike;
 int32 is_put;
 char instypnam[RECLEN], s1[RECLEN];
 struct trans_t *trp, *trp2, wrktran;
 strcpy(instypnam, "");
 /* uses def flag to set acct typ */
 trp = &(wrktran);
  __init_tran(trp); 
 strcpy(trp->trline, __cur_line);
 trp->lno = __line_cnt;
 trp->tr_typ  = TRTYP_OPPOS;

 /* SJM 09-28-20 - need to support buying/selling other than USD/CAD */
 if (strcmp(__csvfldtab[4], "CAD") == 0) trp->cur_typ = CUR_CAD;
 else if (strcmp(__csvfldtab[4], "USD") != 0) 
  {
   __misc_terr(__FILE__, __LINE__);
  }

 /* futures and stocks just fill fields */
 /* for options and fut options, extract strike and exp. and chg sym */
 if (strcmp(__csvfldtab[3], "Stocks") == 0)
  {
   strcpy(instypnam, "Stocks");
   strcpy(trp->symbol, __csvfldtab[5]);
   trp->tr_ityp = TRITYP_STOCK;
  }
 else if (strcmp(__csvfldtab[3], "Futures") == 0)
  {
   strcpy(instypnam, "Futures");
   fix_set_trade_fut_sym(trp, __csvfldtab[5]);
   trp->tr_ityp = TRITYP_FUT;
  }
 else if (strcmp(__csvfldtab[3], "Equity and Index Options") == 0)
  {
   strcpy(instypnam, __csvfldtab[3]);
   trp->tr_ityp = TRITYP_OPT;
   /* take apart: WYNN  200403P00080000 */
   strcpy(s1, __csvfldtab[5]);
   /* opt strike will be a whole number - need to adjust if 50 cents */
   if (__xtrct_ib_option_desc(s1, (char *) trp->symbol,
    &(trp->opt_exp_dt), &(is_put), &(opt_strike)) == -1)
    {
     __misc_terr(__FILE__, __LINE__);
    }
   /* SJM 02-27-22 - must fill option fields so sort sym builds right */
   if (is_put) trp->is_put_opt = TRUE; else trp->is_put_opt = FALSE;
   trp->opt_strike = opt_strike;
  }
 else if (strcmp(__csvfldtab[3], "Options On Futures") == 0)
  {
   strcpy(instypnam, __csvfldtab[3]);
   trp->tr_ityp = TRITYP_FUTOPT;
   /* take apart: ES 14FEB20 3275.0 P */
   /* strike already stored as fractional strike */
   strcpy(s1, __csvfldtab[5]);
   if (__xtrct_ib_futopt_desc(s1, trp->symbol, &(trp->opt_exp_dt),
    &(is_put), &(opt_strike)) == -1)
    {
     __misc_terr(__FILE__, __LINE__);
    }
   /* SJM 02-27-22 - must fill option fields so sort sym builds right */
   if (is_put) trp->is_put_opt = TRUE; else trp->is_put_opt = FALSE;
   trp->opt_strike = opt_strike;
  }
 else __case_terr(__FILE__, __LINE__); 
 /* SJM 10-12-20 - no codes for open positions */

  /* tr quant field used for open pos. number of instruments */
  strcpy(s1, __csvfldtab[6]);
  if (sscanf(s1, "%lf", &(quant)) != 1)
   {
    __misc_terr(__FILE__, __LINE__);
   }
  trp->tr_quant = quant;

  strcpy(s1, __csvfldtab[7]);
  if (sscanf(s1, "%lf", &(mult)) != 1)
   {
    __misc_terr(__FILE__, __LINE__);
   }
  trp->tr_mult = mult;

  strcpy(s1, __csvfldtab[8]);
  if (sscanf(s1, "%lf", &(price)) != 1)
   {
    __misc_terr(__FILE__, __LINE__);
   }
  /* this is original cost price */
  trp->tr_price = price;

  strcpy(s1, __csvfldtab[9]);
  if (sscanf(s1, "%lf", &(basis)) != 1)
   {
    __misc_terr(__FILE__, __LINE__);
   }
  /* this is original cost basis */
// SJM 04-04-25 - eliminating trade basis need to calc
//   trp->tr_basis = basis;

  strcpy(s1, __csvfldtab[10]);
  if (sscanf(s1, "%lf", &(price)) != 1)
   {
    __misc_terr(__FILE__, __LINE__);
   }
  /* this is today's closing price */
  trp->tr_tdy_closepr = price;

  strcpy(s1, __csvfldtab[12]);
  if (sscanf(s1, "%lf", &(unrlz_pl)) != 1)
   {
    __misc_terr(__FILE__, __LINE__);
   }
  trp->op_unrlz_gain = unrlz_pl;

  /* this works because tran records does not contain any ptr fields */
  trp2 = (struct trans_t *) __my_malloc(sizeof(struct trans_t));
  *trp2 = wrktran;
  __add_to_end_trans(trp2);
}

/*
 * this also may not be present
 */
static void skip_complex_pos_summary_section(FILE *f)
{
 if (strcmp(__csvfldtab[0], "Complex Positions Summary") != 0)
  {
   return;
  }
 for (;;)
  {
   __rd_xtrct_ib_csv_line(f, TRUE);
   if (strcmp(__csvfldtab[0], "Complex Positions Summary") != 0) break;
  }
}

/*
 * can be optional
 */
static void skip_forex_bal_section(FILE *f)
{
 if (strcmp(__csvfldtab[0], "Forex Balances") != 0)
  {
   return;
  }
 for (;;)
  {
   __rd_xtrct_ib_csv_line(f, TRUE);
   if (strcmp(__csvfldtab[0], "Forex Balances") != 0) break;
  }
}

/*
 * build the trade record
 *
 * know first trade record read that is probably Header type
 */
static void bld_trade_sect_recs(FILE *f)
{
 /* may be no trades */
 for (;;)
  {
   if (strcmp(__csvfldtab[0], "Trades") != 0) break;
   if (strcmp(__csvfldtab[1], "Header") == 0) goto nxt_trade;  
   if (strcmp(__csvfldtab[1], "SubTotal") == 0) goto nxt_trade;
   if (strcmp(__csvfldtab[1], "Total") == 0) goto nxt_trade;
   /* SJM 12-30-20 - ignore forex trades for now */
   if (strcmp(__csvfldtab[3], "Forex") == 0) goto nxt_trade;

   bld_add_trade_rec();

nxt_trade:
   __rd_xtrct_ib_csv_line(f, TRUE);
  }
}

/*
 * know ib .csv file line read and csv fld tab filled and found trade
 *
 * this fills wrk trans record and if good allocates and add to trans list
 * same for all of stock, future, option, future option
 */
static void bld_add_trade_rec(void)
{
 int32 is_put;
 char *chp, *chp2;
 double dayclpr, ostrike, sharepr, trcomm, trbasis, quant;
 struct trans_t *trp, *trp2, wrktran;
 char instypnam[RECLEN], s1[RECLEN], s2[RECLEN];

 strcpy(instypnam, "");
 trp = &(wrktran);
  __init_tran(trp); 

 /* uses def flag to set acct typ */
 strcpy(trp->trline, __cur_line);
 trp->lno = __line_cnt;
 trp->tr_typ = TRTYP_TRADE;

 if (strcmp(__csvfldtab[4], "USD") == 0) trp->cur_typ = CUR_USD;
 else if (strcmp(__csvfldtab[4], "CAD") == 0) trp->cur_typ = CUR_CAD;
 else if (strcmp(__csvfldtab[4], "GBP") == 0) trp->cur_typ = CUR_GBP;
 else __misc_terr(__FILE__, __LINE__);

 /* all trades have a time stamp */
 /* SJM 09-29-20 - maybe should return error here */
 __xtrct_ib_csv_dattim(__csvfldtab[6], &(trp->tdate), &(trp->ttime));

  /* sell is minus */
  strcpy(s1, __csvfldtab[7]);
  /* SJM 12-26-21 - remove comma - make this proc in ms */
  chp = &(s1[0]);
  chp2 = &(s2[0]);
  for (;*chp != '\0';)
   {
    /* if quoted bigger than 1k will have commas that must be removed */
    if (*chp == ',') chp++;
    else *chp2++ = *chp++; 
   }
  *chp2 = '\0';
  strcpy(s1, s2);
  if (sscanf(s1, "%lf", &(quant)) != 1)
   {
    __misc_terr(__FILE__, __LINE__);
   }
 /* futures and stocks just fill fields */
 /* for options and future options, extract strike and exp. and chg sym */
 if (strcmp(__csvfldtab[3], "Stocks") == 0)
  {
   strcpy(instypnam, "Stocks");
   strcpy(trp->symbol, __csvfldtab[5]);
   trp->tr_ityp = TRITYP_STOCK;
  }
 else if (strcmp(__csvfldtab[3], "Futures") == 0)
  {
   strcpy(instypnam, "Futures");
   fix_set_trade_fut_sym(trp, __csvfldtab[5]);
   trp->tr_ityp = TRITYP_FUT;
  }
 else if (strcmp(__csvfldtab[3], "Equity and Index Options") == 0)
  {
   strcpy(instypnam, __csvfldtab[3]);
   trp->tr_ityp = TRITYP_OPT;
   /* take apart: WYNN  200403P00080000 */
   strcpy(s1, __csvfldtab[5]);
   /* opt strike will be a whole number - need to adjust if 50 cents */
   if (__xtrct_ib_option_desc(s1, trp->symbol, &(trp->opt_exp_dt),
    &(is_put), &(ostrike)) == -1)
    {
     __misc_terr(__FILE__, __LINE__);
    }
   trp->opt_strike = ostrike;
   if (is_put) trp->is_put_opt = TRUE; else trp->is_put_opt = FALSE;
  }
 else if (strcmp(__csvfldtab[3], "Options On Futures") == 0)
  {
   strcpy(instypnam, __csvfldtab[3]);
   trp->tr_ityp = TRITYP_FUTOPT;
   strcpy(s1, __csvfldtab[5]);
   /* take apart: ES 14FEB20 3275.0 P */
   /* strike already stored as fractional stike */
   if (__xtrct_ib_futopt_desc(s1, trp->symbol, &(trp->opt_exp_dt),
    &(is_put), &(ostrike)) == -1)
    {
     __misc_terr(__FILE__, __LINE__);
    }
   trp->opt_strike = ostrike;
   if (is_put) trp->is_put_opt = TRUE; else trp->is_put_opt = FALSE;
   /* SJM 01-22-21 LOOKATME using future expiration same as option */ 
   /* will be wrong for common ES options */
   trp->fut_expmth = trp->opt_exp_dt.month;
   trp->fut_expyr = trp->opt_exp_dt.year;
  }
 else __case_terr(__FILE__, __LINE__); 

  strcpy(trp->tr_code, __csvfldtab[15]);
  /* SJM 02-15-25 - now in port d.b. sell is - bu + */
  if (quant < 0.0) trp->is_buy = FALSE;
  else trp->is_buy = TRUE;
  trp->tr_quant = (double) quant;

  strcpy(s1, __csvfldtab[8]);
  if (sscanf(s1, "%lf", &(sharepr)) != 1)
   {
    __misc_terr(__FILE__, __LINE__);
   }
  trp->tr_price = sharepr;
  strcpy(s1, __csvfldtab[9]);
  if (sscanf(s1, "%lf", &(dayclpr)) != 1)
   {
    __misc_terr(__FILE__, __LINE__);
   }
  trp->tr_tdy_closepr = dayclpr;
  strcpy(s1, __csvfldtab[11]);
// ??? FIXME
  if (sscanf(s1, "%lf", &(trcomm)) != 1)
   {
    __misc_terr(__FILE__, __LINE__);
   }
  trp->tr_comm = trcomm;
  /* this is basis in dollars */
  strcpy(s1, __csvfldtab[12]);
  if (sscanf(s1, "%lf", &(trbasis)) != 1)
   {
    __misc_terr(__FILE__, __LINE__);
   }
  // SJM eliminating trade basis - need to calc it
  // trp->tr_basis = trbasis;
  /* SJM 10-06-20 - LOOKATME what are reie and MTM P/L fids in trades */
  /* this works because tran records does not contain any ptr fields */
  trp2 = (struct trans_t *) __my_malloc(sizeof(struct trans_t));
  *trp2 = wrktran;
  __add_to_end_trans(trp2);
}


/*
 * whe read .csv trade record for futures - need to set trp fields and fix sym
 * E7H1 in .csv becomes E7 with fut expmth and fut expyr fields set
 *
 * SJM 01-25-21 grain and bond (ECBOT?) future syms are "XW   MAY 21" form
 * others are "E7H1"
 */
static void fix_set_trade_fut_sym(struct trans_t *trp, char *sym)
{
 int32 slen, mthi;
 char yrnumlet, explet, expyrdig;
 char s1[RECLEN], s2[RECLEN], s3[RECLEN], s4[RECLEN];

 /* case 1: look for grain type future - fut base syms always 2 chars */
 if (sym[2] == ' ')
  {
   strncpy(s1, sym, 2);
   s1[2] = '\0';
   if (sym[3] != ' ' || sym[4] != ' ') __misc_terr(__FILE__, __LINE__);
   strncpy(s2, &(sym[5]), 3);
   s2[3] = '\0';
   mthi = __month_abbrev_to_num(s2);
   if (mthi == -1) __misc_terr(__FILE__, __LINE__);
   __mth_num_to_fut_let(s3, mthi);
   slen = strlen(sym);
   yrnumlet = sym[slen - 1];
   sprintf(s4, "%s%c%c", s1, s3[0], yrnumlet);
   strcpy(sym, s4);
   /* now sym fits globex 4 char patern */
  }

 /* case 2: globex style future sym */
 slen = strlen(sym);
 explet = sym[slen - 2];
 expyrdig = sym[slen - 1];
 trp->fut_expmth = __month_let_to_num(explet);
 trp->fut_expyr = 2020 + ((int32) (expyrdig - '0'));
 sym[slen - 2] = '\0';
 strcpy(trp->symbol, sym);
}

/*
 * may be no corporate actions - how deal with this?
 */
static void skip_corp_actions_section(FILE *f)
{
 if (strcmp(__csvfldtab[0], "Corporate Actions") != 0)
  {
   return;
  }
 for (;;)
  {
   __rd_xtrct_ib_csv_line(f, TRUE);
   if (strcmp(__csvfldtab[0], "Corporate Actions") != 0) break;
  }
}

/*
 * may be no corporate actions - how deal with this?
 * SJM added 12-06-23 - skipping section for cash settled options
 */
static void skip_cash_settled_opts_section(FILE *f)
{
 if (strcmp(__csvfldtab[0], "Option Cash Settlements") != 0)
  {
   return;
  }
 for (;;)
  {
   __rd_xtrct_ib_csv_line(f, TRUE);
   if (strcmp(__csvfldtab[0], "Option Cash Settlements") != 0) break;
  }
}

/*
 * build the deposits and withdrawals records - some GBP
 *
 * SJM 10-14-20 - using fixed built in GBP 1.295/USD (how look up?)
 */
static void bld_deps_and_withdraws_sect_recs(FILE *f)
{
 int32 ctnum;
 double amt;
 struct trans_t *trp, *trp2, wrktran;
 char s1[RECLEN];

 for (;;)
  {
   if (strcmp(__csvfldtab[0], "Deposits & Withdrawals") != 0) break;
   /* SJM - 10-14-20 - only supporting USD and GBP deposits and withdrawals */
   if (strcmp(__csvfldtab[2], "USD") != 0
    && strcmp(__csvfldtab[2], "GBP") != 0) goto nxt_depwith;

   /* uses def flag to set acct typ */
   trp = &(wrktran);
   __init_tran(trp); 
   strcpy(trp->trline, __cur_line);

   trp->lno = __line_cnt;
   if ((ctnum = __cur_type_to_int(__csvfldtab[2])) == -1)
    {
     __misc_terr(__FILE__, __LINE__);
    }
   trp->cur_typ = ctnum;

   if (__dash_num_date_to_datrec(__csvfldtab[3], &(trp->tdate)) == -1)
    {
     __misc_terr(__FILE__, __LINE__);
    }
   if (strcmp(__csvfldtab[4], "Electronic Fund Transfer") == 0)
    {
     /* this is wire in */
     trp->tr_typ = TRTYP_DEPOSIT;
     trp->tr_ityp = TRITYP_CREDIT;
     strcpy(trp->symbol, "wire");
    }
   else if (strncmp(__csvfldtab[4], "Disbursement ", 13) == 0)
    { 
     /* wire out - probably to HSBC UK */
     trp->tr_typ = TRTYP_WITHDRAW;
     trp->tr_ityp = TRITYP_DEBIT;
     strcpy(trp->symbol, "wire");
    }
   else if (strncmp(__csvfldtab[4], "Commission Adjustment ", 22) == 0
    || strncmp(__csvfldtab[4], "Other Adjustment", 16) == 0)
    {
     trp->tr_typ = TRTYP_DEPOSIT;
     trp->tr_ityp = TRITYP_CREDIT;
     strcpy(trp->symbol, "adjust");
    }
   else __case_terr(__FILE__, __LINE__);
   /* description useless - if needed must print entire line */

   /* using amt field for wire in/out amount */
   strcpy(s1, __csvfldtab[5]);
   if (sscanf(s1, "%lf", &(amt)) != 1)
    {
     __misc_terr(__FILE__, __LINE__);
    }
   trp->tr_amount = amt;
   /* SJM 12-26-21 - LOOKATME - think adjustment can be debit too */
   if (strcmp(trp->symbol, "adjust") == 0)
    {
     if (amt < 0.0) trp->tr_ityp = TRITYP_DEBIT; 
    }

   /* this works because tran records does not contain any ptr fields */
   trp2 = (struct trans_t *) __my_malloc(sizeof(struct trans_t));
   *trp2 = wrktran;
   __add_to_end_trans(trp2);

nxt_depwith:
   __rd_xtrct_ib_csv_line(f, TRUE);
  }
}

/*
 * build the fees debit records
 *
 * SJM 10-14-20 - think fees always in USD base currency
 */
static void bld_fees_sect_recs(FILE *f)
{
 double amt;
 struct trans_t *trp, *trp2, wrktran;
 char *chp, *chp2, s1[RECLEN];

 for (;;)
  {
   /* can be none in file */
   if (strcmp(__csvfldtab[0], "Fees") != 0) break;

   if (strcmp(__csvfldtab[1], "Header") == 0) goto nxt_fee;
   if (strcmp(__csvfldtab[1], "Notes") == 0) goto nxt_fee;
   if (strcmp(__csvfldtab[2], "Total") == 0) goto nxt_fee;

   if (strcmp(__csvfldtab[3], "USD") != 0)
    {
     __misc_terr(__FILE__, __LINE__);
    }

   /* uses def flag to set acct typ */
   trp = &(wrktran);
   __init_tran(trp); 
   strcpy(trp->trline, __cur_line);
   trp->lno = __line_cnt;
   /* SJM 12-08-23 - only withdrawal fees do not have the : */
   if (strcmp(__csvfldtab[5], "Withdrawal Fee") == 0)
     {
      strcpy(trp->symbol, __csvfldtab[5]);
     }
    else
     {
      /* symbol is first word of description of fee */
      if ((chp = strchr(__csvfldtab[5], ':')) == NULL)
       {
bad_des: 
        __misc_terr(__FILE__, __LINE__);
       }
      chp++;
      if ((chp2 = strchr(chp, ' ')) == NULL) goto bad_des;
      strncpy(trp->symbol, chp, chp2 - chp);
      trp->symbol[chp2 - chp] = '\0';
     }
   if (__dash_num_date_to_datrec(__csvfldtab[4], &(trp->tdate)) == -1)
    {
     __misc_terr(__FILE__, __LINE__);
    }
   trp->tr_typ = TRTYP_FEE;
   trp->tr_ityp = TRITYP_DEBIT;

   /* using amt field for fee amount */
   strcpy(s1, __csvfldtab[6]);
   if (sscanf(s1, "%lf", &(amt)) != 1)
    {
     __misc_terr(__FILE__, __LINE__);
    }
   /* SJM 01-03-21 - value bundle fees reversed next mth so positive */
   /* need to handle both fees (minus) and credit (plus) */
   /* in new port all values are positive with flags indicating debit/sell */
   trp->tr_amount = -amt;

   /* this works because tran records does not contain any ptr fields */
   trp2 = (struct trans_t *) __my_malloc(sizeof(struct trans_t));
   *trp2 = wrktran;
   __add_to_end_trans(trp2);

nxt_fee:
   __rd_xtrct_ib_csv_line(f, TRUE);
  }
}

/*
 * build the div section - only supporting USD dividends
 *
 * SJM 10-15-20 - making dummy recordsfor payments in lieu - fixed up using
 * dates later - assuming at least part of dividend paid - if not fix up
 * more complicated - can use date to figure out num shares during fix up
 */
static void bld_divs_sect_recs(FILE *f)
{
 int32 is_in_lieu;
 double totdivamt, pershdiv;
 struct trans_t *trp, *trp2, wrktran;
 char s1[RECLEN];

 for (;;)
  {
   if (strcmp(__csvfldtab[0], "Dividends") != 0) break;
   if (strcmp(__csvfldtab[1], "Header") == 0) goto nxt_div;
   if (strcmp(__csvfldtab[2], "Total") == 0) goto nxt_div;
   if (strncmp(__csvfldtab[2], "Total ", 6) == 0) goto nxt_div;


   /* uses def flag to set acct typ */
   trp = &(wrktran);
   __init_tran(trp); 
   strcpy(trp->trline, __cur_line);
   trp->lno = __line_cnt;

   if (strcmp(__csvfldtab[2], "CAD") == 0) trp->cur_typ = CUR_CAD;
   else if (strcmp(__csvfldtab[2], "USD") != 0)
    {
     __misc_terr(__FILE__, __LINE__);
    }

   if (__dash_num_date_to_datrec(__csvfldtab[3], &(trp->tdate)) == -1)
    {
     __misc_terr(__FILE__, __LINE__);
    }
 
   trp->tr_typ = TRTYP_DIV;
   trp->tr_ityp = TRITYP_CREDIT;

   strcpy(s1, __csvfldtab[4]);
   /* this will fill trans rec fields and adjust for in lieu */
   if (parse_xtrct_div_desc_sym(s1, trp->symbol,
    &(pershdiv), &(is_in_lieu)) == 0)
    {
     __misc_terr(__FILE__, __LINE__);
    }
   
  /* will combine in lieu with paid in fix up phase - think will always be */ 
  /* some div so can calculate */
  /* in lieu just has total, if div then price is per share price from str */
  if (is_in_lieu) trp->tr_typ = TRTYP_INLIEU_DIV; 
  else trp->tr_price = pershdiv;

   /* using quant field for wire in/out amount */
   strcpy(s1, __csvfldtab[5]);
   if (sscanf(s1, "%lf", &(totdivamt)) != 1)
    {
     __misc_terr(__FILE__, __LINE__);
    }
   /* minus div. is payment of div from being short - i.e. debit with - amt */
   /* inside new port all values positive with flags to indicate debit */
   if (totdivamt < 0.0)
    {
     trp->tr_ityp = TRITYP_DEBIT;
     trp->is_buy = FALSE;
     totdivamt = -totdivamt;
    }
   else trp->is_buy = TRUE;
   /* realized_pl is also used for total amounts */
   trp->tr_amount = totdivamt;

   /* this works because tran records does not contain any ptr fields */
   trp2 = (struct trans_t *) __my_malloc(sizeof(struct trans_t));
   *trp2 = wrktran;
   __add_to_end_trans(trp2);

nxt_div:
   __rd_xtrct_ib_csv_line(f, TRUE);
  }
}

/*
 * take apart dividend sym field for per share
 * returns 0 on error else 1
 *
 * at least for shorts amount of div paid may be only in liue so do not know
 * number of shares - just use amount
 */ 
static int32 parse_xtrct_div_desc_sym(char *desc, char *sym, double *pershdiv,
 int32 *is_in_lieu)
{
 char *chp, *chp2, *chp3;
 double pershamt;
 char pershamtstr[RECLEN];

 *is_in_lieu = FALSE;
 /* field always starts with <sym>(identifier) */
 if ((chp = strchr(desc, '(')) == NULL) return(0);
 strncpy(sym, desc, chp - desc);
 sym[chp - desc] = '\0';
 if ((chp = strchr(desc, ')')) == NULL) return(0);
 chp++; chp++;
 /* if div this: "Cash Dividend USD 0.176263 per Share (Ordinary Dividend)" */
 if (strncmp(chp, "Cash Dividend ", 14) == 0)
  {
   chp2 = &(chp[18]);
   if ((chp3 = strchr(chp2, ' ')) == NULL) return(0); 
   strncpy(pershamtstr, chp2, chp3 - chp2);
   chp2[chp3 - chp2] = '\0';
   if (sscanf(chp2, "%lf", &(pershamt)) != 1) return(0); 
   *pershdiv = pershamt; 
  }
 else if (strncmp(chp, "Payment in Lieu of Dividend ", 28) == 0)
  {
   /* if in Lieu this: "Payment in Lieu of Dividend (Ordinary Dividend)" */
   *is_in_lieu = TRUE;
   /* no per share amt */
   *pershdiv = 0.0;
  }
 else __case_terr(__FILE__, __LINE__);
 return(1);
}

/*
 * build the withholding tax records - ignore per share amounts
 * just a amount paid that gets totaled 
 */
static void bld_withholding_tax_recs(FILE *f)
{
 double withholdamt;
 struct trans_t *trp, *trp2, wrktran;
 char *chp, s1[RECLEN], s2[RECLEN];

 for (;;)
  {
   if (strcmp(__csvfldtab[0], "Withholding Tax") != 0) break;
   if (strcmp(__csvfldtab[1], "Header") == 0) goto nxt_withhold;
   if (strcmp(__csvfldtab[2], "Total") == 0) goto nxt_withhold;
   if (strncmp(__csvfldtab[2], "Total ", 6) == 0) goto nxt_withhold;


   /* uses def flag to set acct typ */
   trp = &(wrktran);
   __init_tran(trp); 
   strcpy(trp->trline, __cur_line);
   trp->lno = __line_cnt;

   if (strcmp(__csvfldtab[2], "CAD") == 0) trp->cur_typ = CUR_CAD;
   else if (strcmp(__csvfldtab[2], "USD") != 0)
    {
     __misc_terr(__FILE__, __LINE__);
    }

   if (__dash_num_date_to_datrec(__csvfldtab[3], &(trp->tdate)) == -1)
    {
     __misc_terr(__FILE__, __LINE__);
    }
   /* Symbol is first part of desc field */
   strcpy(s1, __csvfldtab[4]);
   if ((chp = strchr(s1, '(')) == NULL)
    {
     __misc_terr(__FILE__, __LINE__);
    }
   strncpy(s2, s1, chp - s1);
   s2[chp - s1] = '\0';
   strcpy(trp->symbol, s2);
 
   trp->tr_typ = TRTYP_TAX;
   trp->tr_ityp = TRITYP_DEBIT;

   /* total amount of withholding tax - not per share */
   strcpy(s1, __csvfldtab[5]);
   if (sscanf(s1, "%lf", &(withholdamt)) != 1)
    {
     __misc_terr(__FILE__, __LINE__);
    }
   trp->tr_amount = withholdamt;

   /* this works because tran records does not contain any ptr fields */
   trp2 = (struct trans_t *) __my_malloc(sizeof(struct trans_t));
   *trp2 = wrktran;
   __add_to_end_trans(trp2);

nxt_withhold:
   __rd_xtrct_ib_csv_line(f, TRUE);
  }
}

/*
 * build the intest records - both debit and credit
 *
 * tricky part is that interest also for GBPs 
 */
static void bld_interest_recs(FILE *f)
{
 int32 ctnum;
 double intamt;
 struct trans_t *trp, *trp2, wrktran;
 char *chp, s1[RECLEN];

 for (;;)
  {
   if (strcmp(__csvfldtab[0], "Interest") != 0) break;
   if (strcmp(__csvfldtab[1], "Header") == 0) goto nxt_int;
   if (strcmp(__csvfldtab[2], "Total") == 0) goto nxt_int;
   if (strcmp(__csvfldtab[2], "Total in USD") == 0) goto nxt_int;
   if (strcmp(__csvfldtab[2], "Total Interest in USD") == 0) goto nxt_int;

   /* uses def flag to set acct typ */
   trp = &(wrktran);
   __init_tran(trp); 
   strcpy(trp->trline, __cur_line);

   /* for now only handling USD, CAD and GBP */
   if ((ctnum = __cur_type_to_int(__csvfldtab[2])) == -1)
    {
     __case_terr(__FILE__, __LINE__);
    }
   trp->cur_typ = ctnum;

   trp->lno = __line_cnt;
   if (__dash_num_date_to_datrec(__csvfldtab[3], &(trp->tdate)) == -1)
    {
     __misc_terr(__FILE__, __LINE__);
    }
 
   trp->tr_typ = TRTYP_INTEREST;

   strcpy(s1, __csvfldtab[5]);
   if (sscanf(s1, "%lf", &(intamt)) != 1)
    {
     __misc_terr(__FILE__, __LINE__);
    }
   /* field 4 will be "<currency> <CREDIT | DEBIT> " */
   /* skip currency name */
   for (chp = __csvfldtab[4]; *chp != ' '; chp++) ; 
   chp++;
   /* SJM 11-04-22 - another type of credit is short credit */
   if (strncmp(chp, "Credit",  6) == 0
    || strncmp(chp, "Short Credit", 12) == 0) trp->tr_ityp = TRITYP_CREDIT;
   else if (strncmp(chp, "Debit", 5) == 0 || strncmp(chp, "Borrow", 6) == 0)
    {
     intamt = -intamt;
     trp->tr_ityp = TRITYP_DEBIT;
    }
   trp->tr_amount = intamt;

   /* this works because tran records does not contain any ptr fields */
   trp2 = (struct trans_t *) __my_malloc(sizeof(struct trans_t));
   *trp2 = wrktran; 
   __add_to_end_trans(trp2);

nxt_int:
   __rd_xtrct_ib_csv_line(f, TRUE);
  }
}


/*
 * may be no chg in div accruals
 * SJM 11-03-25 - now need to also track after xdiv but not paid dividends
 */
static void bld_chg_in_div_accruals_section(FILE *f)
{
 double divamt_persh, xdivamt, shareno;
 struct trans_t *trp, *trp2, wrktran;
 char s1[RECLEN];

 if (strcmp(__csvfldtab[0], "Change in Dividend Accruals") != 0)
  {
   /* no div accruals section */
   return;
  }
 for (;;)
  {
   if (strcmp(__csvfldtab[0], "Change in Dividend Accruals") != 0)
    break;
   if (strcmp(__csvfldtab[2], "Total") == 0) goto nxt_accru;
   if (strcmp(__csvfldtab[1], "Header") == 0) goto nxt_accru;
   /* skip the by current dividend accrual marker statements - use in rec */
   if (strncmp(__csvfldtab[2], "Starting Dividend Accruals in", 29) == 0)
    goto nxt_accru;
   if (strncmp(__csvfldtab[2], "Ending Dividend Accruals in", 27) == 0)
    goto nxt_accru;

   if (strcmp(__csvfldtab[2], "Stocks") != 0)
    __misc_terr(__FILE__, __LINE__);

   /* uses def flag to set acct typ */
   trp = &(wrktran);
   __init_tran(trp); 
   strcpy(trp->trline, __cur_line);
   trp->lno = __line_cnt;

   if (strcmp(__csvfldtab[3], "CAD") == 0) trp->cur_typ = CUR_CAD;
   else if (strcmp(__csvfldtab[3], "USD") != 0)
    {
     __misc_terr(__FILE__, __LINE__);
    }
   if (strcmp(__csvfldtab[2], "Stocks") == 0)
    {
      trp->tr_ityp = TRITYP_STOCK;
    }
   else __misc_terr(__FILE__, __LINE__);
   strcpy(trp->symbol, __csvfldtab[4]);
   /* only stocks can have divs */
   strcpy(trp->sort_sym, trp->symbol);
   strcpy(trp->tr_code, __csvfldtab[14]);

   /* first dt is decl dt for Po post cod, pay date for Re rcv pay rec  */ 
   if (__dash_num_date_to_datrec(__csvfldtab[5], &(trp->tdate)) == -1)
    {
     __misc_terr(__FILE__, __LINE__);
    }
   /* 2nd dt is always xdiv date */
   if (__dash_num_date_to_datrec(__csvfldtab[6], &(trp->xdiv_dt)) == -1)
    {
     __misc_terr(__FILE__, __LINE__);
    }
   /* 34d dt is always pay date */
   if (__dash_num_date_to_datrec(__csvfldtab[7], &(trp->div_pay_dt)) == -1)
    {
     __misc_terr(__FILE__, __LINE__);
    }

   trp->tr_typ = TRTYP_DIVACRU;
   strcpy(s1, __csvfldtab[8]);
   if (sscanf(s1, "%lf", &(shareno)) != 1)
    {
     __misc_terr(__FILE__, __LINE__);
    }
   trp->tr_quant = shareno;
// div. per share fld 1l, total div fld 12
   strcpy(s1, __csvfldtab[11]);
   if (sscanf(s1, "%lf", &(divamt_persh)) != 1)
    {
     __misc_terr(__FILE__, __LINE__);
    }
   trp->tr_price = divamt_persh;
   strcpy(s1, __csvfldtab[12]);
   if (sscanf(s1, "%lf", &(xdivamt)) != 1)
    {
     __misc_terr(__FILE__, __LINE__);
    }
   trp->tr_xdiv_amt = xdivamt; 
   /* this works because tran records does not contain any ptr fields */
   trp2 = (struct trans_t *) __my_malloc(sizeof(struct trans_t));
   *trp2 = wrktran;
   __add_to_end_trans(trp2);

nxt_accru:;
   __rd_xtrct_ib_csv_line(f, TRUE);
  }
}

/*
 * interest accruals may not be any
 */
static void skip_interest_accurals_section(FILE *f)
{
 if (strcmp(__csvfldtab[0], "Interest Accruals") != 0)
  {
   return;
  }
 for (;;)
  {
   __rd_xtrct_ib_csv_line(f, TRUE);
   if (strcmp(__csvfldtab[0], "Interest Accruals") != 0) break;
  }
}

/*
 * may be no chg in div accruals
 */
static void skip_borrow_fee_accruals_section(FILE *f)
{
 if (strcmp(__csvfldtab[0], "Borrow Fee Details") != 0)
  {
   return;
  }
 for (;;)
  {
   __rd_xtrct_ib_csv_line(f, TRUE);
   if (strcmp(__csvfldtab[0], "Borrow Fee Details") != 0) break;
  }
}

/*
 * may be no chg in div accruals
 */
static void skip_ibrkr_managed_securities_section(FILE *f)
{
 if (strncmp(__csvfldtab[0], "IBKR Managed Securities Collateral", 34) != 0)
  {
   return;
  }
 for (;;)
  {
   __rd_xtrct_ib_csv_line(f, TRUE);
   if (strncmp(__csvfldtab[0], "IBKR Managed Securities Collateral", 34) != 0)
    break;
  }
}


static void skip_financial_inst_info_section(FILE *f)
{
 if (strcmp(__csvfldtab[0], "Financial Instrument Information") != 0)
  {
   __ferr(6, "Financial Instrument Information expected - record type %s read", 
    __csvfldtab[0]);
   exit(4);
  }
 for (;;)
  {
   __rd_xtrct_ib_csv_line(f, TRUE);
   if (strcmp(__csvfldtab[0], "Financial Instrument Information") != 0) break;
  }
}

/*
 * also may no be present if no non US info
 */
static void base_currency_xchg_rate_section(FILE *f)
{
 if (strcmp(__csvfldtab[0], "Base Currency Exchange Rate") != 0)
  {
   return;
  }
 for (;;)
  {
   __rd_xtrct_ib_csv_line(f, TRUE);
   if (strcmp(__csvfldtab[0], "Base Currency Exchange Rate") != 0) break;
  }
}

/*
 * this is definition of codes section
 */
static void skip_codes_section(FILE *f)
{
 if (strcmp(__csvfldtab[0], "Codes") != 0)
  {
   __misc_terr(__FILE__, __LINE__);
  }
 for (;;)
  {
   __rd_xtrct_ib_csv_line(f, TRUE);
   if (strcmp(__csvfldtab[0], "Codes") != 0) break;
  }
}

/*
 * SJM 09-13-20 - may need to fix this - notes maybe can appear anywhere
 */
static void skip_notes_section(FILE *f)
{
 if (strcmp(__csvfldtab[0], "Notes") != 0)
  {
   return;
  }
 for (;;)
  {
   __rd_xtrct_ib_csv_line(f, TRUE);
   if (strcmp(__csvfldtab[0], "Note") != 0) break;
  }
}

/*
 * can't parse these just read lines
 *
 * for paper trading account - this is optonal
 */
static void skip_legalnotes_section(FILE *f)
{
 if (strcmp(__csvfldtab[0], "Notes/Legal Notes") != 0)
  {
   return;
  }
 for (;;)
  {
   if ((fgets(__cur_line, RECLEN, f)) == NULL) return;
   if (strncmp(__cur_line, "Notes/Legal Notes,", 18) != 0) break;
  }
}

/*
 * ROUTINES TO COMPENSATE FOR CROSS YEAR OPEN SYMS
 */

/*
 * need to once by hand find open pos. across year end
 * and build a op pos tab entry for each
 *
 * SJM 03-19-24 - BUG WAS NOT USING THE 12/29 closing price but jan. open
 * that is wrong 
 */

/*
 * for every open across prev year end position - add a 1st day of 
 * this yr buy/sell
 */
static void bld_prev_yr_d1_trans(void)
{
// add_cross_yr_op_1st_day_trans(TRITYP_FUT, "6C", "6C26H",
//  1, 0.7312, FALSE, -1.0, 0, 0, 0); 

// add_cross_yr_op_1st_day_trans(TRITYP_OPT, "AMD",
//  "AMD_260102C00022250", -1, 0.14, FALSE, 222.50, 1, 2, 26);

 // SJM 01-02-26 - LOOKATME - wrong since this is CAD stock
//  add_cross_yr_op_1st_day_trans(TRITYP_STOCK, "ATH", "ATH",
//  700, 7.03, FALSE, -1.0, 0, 0, 0); 

//  add_cross_yr_op_1st_day_trans(TRITYP_OPT, "SLV",
//  "SLV_270115C00004800", 2, 20.27, FALSE, 48.00, 01, 15, 27);

//  add_cross_yr_op_1st_day_trans(TRITYP_OPT, "SPY",
//  "SPY_260109C00069500", -1, 0.46, FALSE, 695.00, 01, 9, 26);

// add_cross_yr_op_1st_day_trans(TRITYP_OPT, "SPY",
//  "SPY_260109P00068100", -1, 4.00, TRUE, 681.00, 01, 9, 26);
}

/*
 * fill and add the 1st day of yr using prev. yr closing pr trade rec
 * to trans tab
 */
static void add_cross_yr_op_1st_day_trans(int32 trityp, char *sym,
 char *ssym, double op_nshares, double lastyr_clpr, int32 is_put,
 double opt_strike, int32 oxmth, int32 oxday, int32 oxyr)
{
 struct trans_t *trp;

 trp = (struct trans_t *) __my_malloc(sizeof(struct trans_t));
  __init_tran(trp); 
 /* uses def flag to set acct typ */
 strcpy(trp->trline, "<NONE>");
 trp->lno = -1;
 trp->tr_typ = TRTYP_TRADE;
 trp->tr_ityp = trityp; 
 /* maybe add way to change this */
 trp->cur_typ = CUR_USD;
 trp->op_last_yr = TRUE;
 strcpy(trp->symbol, sym);
 strcpy(trp->sort_sym, ssym);
 strcpy(trp->tr_code, "O");
 if (op_nshares > 0) trp->is_buy = TRUE; 
 else if (op_nshares < 0) trp->is_buy = FALSE;
 else __misc_terr(__FILE__, __LINE__);

 trp->tdate.year = 2026;
 trp->tdate.month = 1;
 trp->tdate.day = 2;
 trp->ttime.hr = 9; 
 trp->ttime.min = 31;
 trp->ttime.sec = 0;
 trp->tr_quant = op_nshares;
 trp->tr_price = lastyr_clpr;
 trp->tr_comm = 0.0;
// SJM 04-04-25 - need to eliminate trade basis - need to calc
// trp->tr_basis = op_nshares*lastyr_clpr;
 if (trp->tr_quant < 0) trp->is_buy = FALSE; else trp->is_buy = TRUE; 

 switch (trp->tr_ityp) {
  case TRITYP_STOCK:
   break;
  case TRITYP_OPT:
   if (is_put) trp->is_put_opt = TRUE; else trp->is_put_opt = FALSE;
   trp->opt_strike = opt_strike;
   trp->opt_exp_dt.month = oxmth;
   trp->opt_exp_dt.day = oxday;
   trp->opt_exp_dt.year = oxyr;
   break;
  case TRITYP_FUT:
  case TRITYP_FUTOPT:
  break;
  default: __case_terr(__FILE__, __LINE__);
 }
 strcpy(trp->trans_desc_string, "<none>"); 
 strcpy(trp->trline, "<none>");
 __add_to_end_trans(trp);
}

/*
 * proc to change reversal trades to close then open other dir
 *
 * know original trans sorted and sym ndx build
 * if make changes, re-sort and rebuild
 * also need to rebuild cl tr index - maybe not built yet?
 *
 * know cross yr trades added too
 */
static void sep_reverses_to_2_trades(void)
{
 int32 si, tri;
 double num_sh_op, new_to_buy_sh, new_to_sell_sh;
 char codch;
 struct trans_t wrktran, *trp, *trp2;
 struct symndx_t *sip;

 for (si = 0; si <= __last_symndxi; si++) 
  {
   sip = __symndx[si];
   num_sh_op = 0.0;   
   for (tri = sip->tr_bi; tri <= sip->tr_ei; tri++)
    {
     trp = __trantab[tri];
     codch = __cod_opcl_ch(trp->tr_code);
     if (codch == 'O') 
      {
       if (num_sh_op > 0 && trp->tr_quant < 0)
        __misc_terr(__FILE__, __LINE__);
       if (num_sh_op < 0 && trp->tr_quant > 0)
        __misc_terr(__FILE__, __LINE__);
       num_sh_op += trp->tr_quant;
      }
     else if (codch == 'C')
      {
       if (num_sh_op > 0)
        {
         if (trp->is_buy) __misc_terr(__FILE__, __LINE__);
         if (num_sh_op + trp->tr_quant < 0)
          {
           /* copy to new to add that opens short */
           wrktran = *trp;
           /* chg trp to sell num open shares, i.e. close */
           /* this closes to 0 */
           new_to_sell_sh = trp->tr_quant + num_sh_op;
           /* say buy 200 this is sell say -600 new sell is -200 */
           /* this is only change in this close - reducing no to cl */
           trp->tr_quant = -num_sh_op;

           /* now add trade that sell short negative num rem shares */
           wrktran.is_buy = FALSE;
           /* 200 long open, selling 600 so new one sells 400 */
           wrktran.tr_quant = new_to_sell_sh;
           strcpy(wrktran.tr_code, "O"); 
           /* commission on prev one */
           wrktran.tr_comm = 0.0;
           /* newly added sell short open needs to sort after cl */
           wrktran.ttime.sec++;
           trp2 = (struct trans_t *)
           __my_malloc(sizeof(struct trans_t));
           *trp2 = wrktran; 
           __add_to_end_trans(trp2);
            __some_revs_sep = TRUE;
          }
         /* if normal buy shares then sell, get to here */
         num_sh_op += trp->tr_quant;
        }
       else if (num_sh_op < 0)
        {
         /* this is short some reversing to long */
         if (!trp->is_buy) __misc_terr(__FILE__, __LINE__);
         if (num_sh_op + trp->tr_quant > 0)
          {
           /* SJM 02-27-26 - buy chg to buy to 0 then buy now same as sell */
           /* copy to new to add that opens short */
           wrktran = *trp;
           /* chg trp to buy num open shares, i.e. close */
           /* this closes to 0 */
           /* say buy 200 this is sell say -600 new sell is -200 */
           new_to_buy_sh = trp->tr_quant + num_sh_op;
           /* say buy 200 this is sell say -600 new sell is -200 */
           /* this is only change in this close - reducing no to cl */
           trp->tr_quant = -num_sh_op;
           wrktran.tr_quant = new_to_buy_sh;

           /* now add trade that sell short negative num rem shares */
           wrktran.is_buy = TRUE;
           /* -200 short open, buying 600 so new one buys 400 */
           wrktran.tr_quant = new_to_buy_sh;
           /* newly added buy long open needs to sort after cl */
           wrktran.ttime.sec++;
           strcpy(wrktran.tr_code, "O"); 
           /* commission on prev one */
           wrktran.tr_comm = 0.0;
           trp2 = (struct trans_t *)
           __my_malloc(sizeof(struct trans_t));
           *trp2 = wrktran; 
          __add_to_end_trans(trp2);
          __some_revs_sep = TRUE;
         }
        /* if just sell to open then buy cl - fall thru to here */ 
        num_sh_op += trp->tr_quant;
       }
     /* SJM 02-27-26 - if num op 0, not reversing anything - leave rec */
     // else __misc_terr(__FILE__, __LINE__); 
     }
    }
  }
}

/*
 * add virtual pseudo trade for open today to 0 - like cross yr open
 *
 * know before here 
 */
static void add_tdy_open_to_0_trade(void) 
{
 int32 si;
 struct symndx_t *sip;
 struct trans_t wrktran, *etrp, *trp2;

 for (si = 0; si <= __last_symndxi; si++) 
  {
   sip = __symndx[si];
   etrp = __trantab[sip->tr_ei];
   if (etrp->not_cl_share_no == 0) continue;

   /* some shares open tdy */
   /* copy to new to add that opens short */
   wrktran = *etrp;
   strcpy(wrktran.tr_code, "C"); 
   wrktran.tr_comm = 0.0;

   wrktran.tdate.month = __stmt_end_dat.month; 
   wrktran.tdate.day = __stmt_end_dat.day;
   wrktran.tdate.year = __stmt_end_dat.year; 
   wrktran.ttime.hr = 23;
   wrktran.ttime.min = 59;
   wrktran.ttime.sec = 0;
   wrktran.tdy_pseudo_cl = TRUE;
   if (etrp->not_cl_share_no > 0)
    {
     /* if tdy long, trade is a sell */
     wrktran.tr_quant = -etrp->not_cl_share_no;
     wrktran.is_buy = FALSE;
    }
   else if (etrp->not_cl_share_no < 0)
    {
     /* if tdy short, trade is a buy - invert num */
     wrktran.tr_quant = -etrp->not_cl_share_no;
     wrktran.is_buy = TRUE;
    }
   else __misc_terr(__FILE__, __LINE__);
   wrktran.not_cl_share_no = 0;
   trp2 = (struct trans_t *) __my_malloc(sizeof(struct trans_t));
   *trp2 = wrktran; 
   __add_to_end_trans(trp2);
   __adj_tdy_op_trans = TRUE;
  }
}

/*
 * wrapper for filling al symndx not cl fields 
 */
static void fill_all_not_cl_sh_num(void)
{
 int32 si;
 struct symndx_t *sip;

 for (si = 0; si <= __last_symndxi; si++) 
  {
   sip = __symndx[si];
   fill_not_cl_sh_num(sip);
  }
}

/*
 * fill cumlative not cl share num
 *
 * know by here reverses changed to if long sell to 0 then op short
 * not cl number is plus for long and minus for short here
 */
static void fill_not_cl_sh_num(struct symndx_t *sip)
{
 int32 tri;
 struct trans_t *trp, *ltrp;
 
 for (tri = sip->tr_bi; tri <= sip->tr_ei; tri++)
  {
   trp = __trantab[tri];
   if (tri == sip->tr_bi)
    {
     /* 1st one - know it is open - minus if op short */
     trp->not_cl_share_no = trp->tr_quant; 
     ltrp = trp;
     continue;
    }
   /* this adds and subtracts signed without adjustment here */
   trp->not_cl_share_no = ltrp->not_cl_share_no + trp->tr_quant;
   /* SJM 10-20-25 - if reinvested divs, maybe rounding problem */
   /* fixes even if not end where pseudo cl set wrong */
   if (abs(trp->not_cl_share_no) <= 0.01) trp->not_cl_share_no = 0;  
   ltrp = trp;
  }
}


/*
 * ROUTINES TO CALCULATE AND SET FIDS IN TRADES AND BLD CLOSE TR NDX
 */

/*
 * fill after trade state fields
 *
 * this fill 3 fields in trans_t recs that are just of tran type trade
 * after_tr_quant and tr rlz gain 
 *
 * only for trade tran type fields, after tr quant,
 * rlz gain fields filled after here
 * for closes - set rlz gain and per share open prices
 * for opens - set cur still open and avg open price
 * for last entry if open, fill unrlz gain field
 * 
 */
static void fill_after_trade_flds(void)
{
 match_sell_buys();
 /* for all syms fill cl rlz gain flds */
 fill_cl_op_avgshpr_fld();
}

/*
 * for all sym ndx set current buy sell pair ptrs and numbers
 *
 * SJM 03-31-25 - now use separate passes - this one matches sell-buys
 */
static void match_sell_buys(void)
{
 int32 si, tri;
 struct symndx_t *sip;
 struct trans_t *trp;
 double num_to_cl;
 char codch;

 for (si = 0; si <= __last_symndxi; si++) 
  {
   sip = __symndx[si];

   /* this is running num of open shares (- or +) for all trades */
   fill_not_cl_sh_num(sip);
   /* after here every end should have not cl share no 0 here */
   if (__trantab[sip->tr_ei]->not_cl_share_no != 0)
    __misc_terr(__FILE__, __LINE__);
   /* this reset still op for open trades for calc tr cl index */
   set_still_op_from_quant(sip); 
   
   /* know first always open, go through opens allocating */ 
   for (tri = sip->tr_bi + 1; tri <= sip->tr_ei; tri++) 
    {
     trp = __trantab[tri];
     codch = __cod_opcl_ch(trp->tr_code);
     /* nothing to bld for opens */
     if (codch == 'O') continue;

     num_to_cl = trp->tr_quant;
     /* if sell quant will be neg, fix first */
     if (trp->tr_quant < 0) num_to_cl = -num_to_cl;

     trp->trcl_ndxi = bld_1trade_clndx(sip, num_to_cl, tri);
    }
  }
}

/*
 * set still open flds from open trade last tran quant value field
 * building the cl ndx uses still open as work so much reset
 * still open always positive
 *
 * still open is just no. shares open from this trade not cumulative
 * not cl is running total after trcl ndx built
 */
static void set_still_op_from_quant(struct symndx_t *sip) 
{
 int32 tri2;
 struct trans_t *trp2;
 char codch2;

 for (tri2 = sip->tr_bi; tri2 <= sip->tr_ei; tri2++)
  {
   trp2 = __trantab[tri2];
   codch2 = __cod_opcl_ch(trp2->tr_code);
   if (codch2 == 'O')
    {
     trp2->this_still_op = trp2->tr_quant;
     if (trp2->this_still_op < 0)
      trp2->this_still_op = -trp2->this_still_op;
    }
  }
}

/*
 * bld the cl ndx for one trade and link from each close to it
 * passed num to cl is always positive
 *
 * pass tri that is current one linking close op comp list
 * when done need to set last_trcli index in trp
 * if last is open, called for that too
 *
 * also used for last one even if it is opening more
 */
static int32 bld_1trade_clndx(struct symndx_t *sip, double num_to_cl, 
 int32 tri)
{
 int32 tri2, clndx_begi;
 char codch2;
 struct trans_t *trp2;

 clndx_begi = __last_trcli + 1;
 /* tri is last tr_ei trade that is the one that may be open tdy */
 /* SJM 04-03-25 cross yr open then 3 closes need 1st open */
 for (tri2 = sip->tr_bi; tri2 < tri; tri2++)
  {
   trp2 = __trantab[tri2];
   codch2 = __cod_opcl_ch(trp2->tr_code);
   /* close last tr that can be open or close but only if last tr */
   if (codch2 == 'C') continue;

   /* since know tri is a close only process op before it */
   /* each call of this update this still op, still start at beg  */
   /* ones at beginning will have this still op 0 so skip */
   /* know tri2 is an open */
   if (trp2->this_still_op == 0.0) continue;
   if (num_to_cl < 0.0) __misc_terr(__FILE__, __LINE__);  
   if (trp2->this_still_op < num_to_cl) 
    {
     /* closing trade but not as many as open */
     __last_trcli++;
     __trclndx[__last_trcli].numcl = trp2->this_still_op;  
     __trclndx[__last_trcli].optrndx = tri2;  
     num_to_cl -= trp2->this_still_op;
     trp2->this_still_op = 0.0;  
     continue; 
    }
   if (trp2->this_still_op >= num_to_cl)
    {
     /* closes all but still some for next later close */
     __last_trcli++;
     __trclndx[__last_trcli].numcl = num_to_cl;
     __trclndx[__last_trcli].optrndx = tri2;  
     trp2->this_still_op -= num_to_cl;
     num_to_cl = 0;
     __last_trcli++;
     __trclndx[__last_trcli].optrndx = -1; 
     break;
    }
  } 
 return(clndx_begi);
}

/*
 * fill close tran - open trans that it closes avg. share pr
 */
// GOOD
static void fill_cl_op_avgshpr_fld(void)
{
 int32 si, tri, trcli;
 struct symndx_t *sip;
 struct trans_t *ctrp, *optrp;
 struct trclndx_t *trclndxp;
 double cur_op_shbypr_tot;
 char codch;

 for (si = 0; si <= __last_symndxi; si++) 
  {
   sip = __symndx[si];
   for (tri = sip->tr_bi + 1; tri <= sip->tr_ei; tri++)
    {
     ctrp = __trantab[tri];
     codch = __cod_opcl_ch(ctrp->tr_code);
     if (codch == 'O') continue;
     if (codch != 'C') __misc_terr(__FILE__, __LINE__);
     /* here is a probably partial cl but can be all cl if no op tdy */
     if (ctrp->trcl_ndxi < 0) __misc_terr(__FILE__, __LINE__);
     cur_op_shbypr_tot = 0.0;
     /* now have cl tr cl ndx built, so just go thru accumulating */
     for (trcli = ctrp->trcl_ndxi;; trcli++) 
      {
       trclndxp = &(__trclndx[trcli]);
       if (trclndxp->optrndx == -1) break;
       optrp = __trantab[trclndxp->optrndx];
       cur_op_shbypr_tot += trclndxp->numcl*optrp->tr_price;
      }
     /* possible pseudo cl is a special case */ 
     if (tri == sip->tr_ei && ctrp->tdy_pseudo_cl)
      {
       /* SJM 04-25-25 not cl sh no wrl fld not good here */ 
       /* the added pseudo cl quant is no. of shares still op */
       ctrp->op_avg_sh_pr = cur_op_shbypr_tot/fabs(ctrp->tr_quant);
       continue;
      }

     /* avg. open sh price always positive */
     /* but not cl share no minus for short plus for long */
     ctrp->op_avg_sh_pr = cur_op_shbypr_tot/fabs(ctrp->tr_quant);
    }
  }
}     

/*
 * set all per sh gain fields for closed pos. including pseudo cl
 */
static void set_per_sh_gain_fld(void)
{
 int32 si, tri;
 char codch;
 struct symndx_t *sip;
 struct trans_t *trp;

 for (si = 0; si <= __last_symndxi; si++)
  {
   sip = __symndx[si];
   /* last tr may be pseudo cl or normal cl trade */
   /* will not be open since will have added a pseudo cl */
   for (tri = sip->tr_bi; tri <= sip->tr_ei; tri++)
    {
     trp = __trantab[tri];
     codch = __cod_opcl_ch(trp->tr_code);
     if (codch == 'O') continue;

     /* buy to cl a short open pos. */  
     if (trp->is_buy)
      {
       trp->cl_gain_per_sh = trp->op_avg_sh_pr - trp->tr_price; 
      }
     /* sell to cl a long open pos. */  
     else
      {
       trp->cl_gain_per_sh = trp->tr_price - trp->op_avg_sh_pr;
      }
    }
  }
}

/*
 * before can calc gain/loss for open pos, need to get tdy cl pr
 * from open pos .csv records
 *
 * know op pos tab build before here
 * know tdy pseudo cl flag in tran tab rec set for all tdy open pos
 */
static void set_op_pos_tdy_pr_from_oppos(void) 
{
 int32 si;
 struct symndx_t *sip;
 struct trans_t *etrp, *oppos_trp;

 for (si = 0; si <= __last_symndxi; si++)
  { 
   sip = __symndx[si];
   /* only open pos are end tran pseudo close */
   etrp = __trantab[sip->tr_ei];
   if (etrp->tdy_pseudo_cl)
    {
     oppos_trp = __fnd_posndx_oppos(etrp->sort_sym, TRUE);
     if (oppos_trp == NULL) __misc_terr(__FILE__, __LINE__);
     /* when op pos record read, only nd tdy pr is in tdy cl pr fld */
     etrp->tr_tdy_closepr = oppos_trp->tr_tdy_closepr; 
     /* SJM 06-13-25 - for really op pseudo cl need to set tr pr */ 
     etrp->tr_price = etrp->tr_tdy_closepr;
    }
  }
}

/*
 * ROUTINES TO SET SYMNDX BASE ELEMNTS
 */

/*
 * set symndx bas flag - either for stk/opt (also fut/opt) of
 * stock and option same base sym or 1st if no stk/opt
 * 
 * SJM 05-05-25 - for now treat each future ctrt mth as separate base
 * by using equivalent class indices, easy to combine future
 * mth if desired
 *
 * SJM 07-18-25 - should group all fut expirations as one sgrp
 */
static void bld_bas_sgrps(void)
{
 int32 si, si2; 
 struct symndx_t *sip, *sip2;
 struct trans_t *trp, *btrp, *btrp2;
 char s1[RECLEN], bassym[RECLEN];

 /* now set tran commodity type fut sym */
 for (si = 0; si <= __last_symndxi; si++)  
  {
   sip = __symndx[si];
   if (sip->in_sipgrp) continue;
   trp = __trantab[sip->tr_bi]; 
   if (trp->tr_ityp == TRITYP_FUT || trp->tr_ityp  == TRITYP_FUTOPT)
    {
     to_futtyp_bas(s1, trp);
     strcpy(trp->futsym, s1); 
    }
  } 
  
 /* first find and build lists of all bas sym groups with stk or fut */
 for (si = 0; si <= __last_symndxi; si++)  
  {
   sip = __symndx[si];
   if (sip->in_sipgrp) continue;
   btrp = __trantab[sip->tr_bi]; 
   /* is possible base needs to be stk or fut not option */
   if (btrp->tr_ityp != TRITYP_STOCK && btrp->tr_ityp != TRITYP_FUT)   
    continue;

   /* this stk/fut not in bas sym grp, this 1st is the sip bas */
   if (btrp->tr_ityp == TRITYP_FUT || btrp->tr_ityp == TRITYP_FUTOPT)
    {
     strcpy(bassym, btrp->futsym);
    }
   else strcpy(bassym, btrp->symbol);

   sip->sip_bas = TRUE;
   sip->in_sipgrp = TRUE;
   sip->bas_sgrp_ndxi = ++__last_sgrpndxi; 
   __basgrpndx[__last_sgrpndxi] = si;
   /* now start over and find all sym ndx els with same bas stk/fut/opt */
   for (si2 = 0; si2 <= __last_symndxi; si2++) 
    {
     sip2 = __symndx[si2];
     if (sip2->in_sipgrp) continue;
     btrp2 = __trantab[sip2->tr_bi];
     if (btrp2->tr_ityp == TRITYP_FUT || btrp2->tr_ityp == TRITYP_FUTOPT)
      {
       if (strcmp(btrp2->futsym, bassym) != 0) continue;
      }
     else 
      {
       if (strcmp(btrp2->symbol, bassym) != 0) continue;
      }
     sip2->in_sipgrp = TRUE;
     __basgrpndx[++__last_sgrpndxi] = si2;
     if (btrp2->tr_ityp == TRITYP_OPT
      || btrp2->tr_ityp == TRITYP_FUTOPT)    
      {
       sip->bas_has_options = TRUE;
      } 
    }
  /* mark end of list */ 
   __basgrpndx[++__last_sgrpndxi] = -1;
  }
 /* may have sip grp with same bas symbol but no stk/fut just options */
 for (si = 0; si <= __last_symndxi; si++)  
  {
   sip = __symndx[si];
   if (sip->in_sipgrp) continue;
   btrp = __trantab[sip->tr_bi];
     
   /* by here all futures and opts must be in bas grp */ 
   /* DBG remove --- */
   if (btrp->tr_ityp == TRITYP_STOCK || btrp->tr_ityp == TRITYP_FUT)    
    __misc_terr(__FILE__, __LINE__);
   /* --- */
   /* sym ndx only has stk/fut or options of those */
   /* option is sip bas because no stk/fut sym in ib file */
   /* for options sort sym and symbol are the same */
   if (btrp->tr_ityp == TRITYP_FUTOPT) strcpy(bassym, btrp->futsym);
   else strcpy(bassym, btrp->symbol);

   sip->sip_bas = TRUE;
   sip->in_sipgrp = TRUE;
   sip->bas_has_options = TRUE;
   sip->bas_sgrp_ndxi = ++__last_sgrpndxi; 
   __basgrpndx[__last_sgrpndxi] = si;
   /* now find all sym ndx els with same bas sym */
   for (si2 = 0; si2 <= __last_symndxi; si2++) 
    {
     sip2 = __symndx[si2];
     if (sip2->in_sipgrp) continue;

     btrp2 = __trantab[sip2->tr_bi];
     if (btrp2->tr_ityp == TRITYP_FUTOPT)
      {
       if (strcmp(btrp2->futsym, bassym) != 0) continue;
      } 
     else
      {
       if (strcmp(btrp2->symbol, bassym) != 0) continue;
      }
     sip2->in_sipgrp = TRUE;
     __basgrpndx[++__last_sgrpndxi] = si2;
    }
   __basgrpndx[++__last_sgrpndxi] = -1;
  }  
 /* final step check to make sure all sips in a sgrp */
 for (si = 0; si <= __last_symndxi; si++)  
  {
   sip = __symndx[si];
   if (!sip->in_sipgrp) __misc_terr(__FILE__, __LINE__);
  }
}

/*
 * convert a future sort sym to commodity symbol no expiratio as part
 */
char *to_futtyp_bas(char *s, struct trans_t *btrp)
{
 int32 slen;
 char *chp, s1[RECLEN];

 strcpy(s1, btrp->sort_sym);
 /* for futures combine all exp mths this year */
 if (btrp->tr_ityp == TRITYP_FUTOPT)
  {
   if ((chp = strchr(s1, '_')) == NULL) __misc_terr(__FILE__, __LINE__);
   *chp = '\0'; 
  }
 slen = strlen(s1);
 /* SJM 09-15-25 CROSS YEAR NEED TO CHANGE THIS */
 /* DBG remove -- */
 if (s1[slen - 3] != '2' && s1[slen - 2] != '5')    
  __misc_terr(__FILE__, __LINE__);
 /* -- */
 strncpy(s, s1, slen - 3);
 s[slen - 3] = '\0';
 return(s);
}

/*
 * ROUTINES TO SET TRAN TAB ELEMENT FIELDS
 */

/*
 * fixup tran tab by combining part of div as in lieu in div if possible?
 */
static void fix_rm_div_in_lieu_recs(void)
{
 // SJM 12-11-20 - WRITEME
}
