Version 0 of Taxes and TXF file import

Updated 2009-04-13 03:24:15 by Bezoar
   As April 15th rolls around a young man's fancy turns to ... TAXES ! Not really but it does get everyone's attention, at least in the US. One thing I do, given the rise of internet trading is try my hand at buying and selling of stocks. I usually do not brag but I have made a small fortune on the stock market; unfortunately, I started with a large fortune. However, given how easy it is to trade stocks and to have stocks that give dividends to automatically re-invest when it comes time to fill out the old Schedule D  the number of transactions is extraordinarily high. Tax programs like Turbo Tax will let you enter your buys and sells one at a time or import it from its own products or from a .txf (.TXF) file. I was faced with 70+ transactions to input. I did some research and I was able to get the format for a .txf file at this link http://turbotax.intuit.com/txf/TXF041.jsp%|%Txf format v 041%|%. Since I had done no [TclOO] I decided to develop a TclOO program to convert a Gains and Losses report into a txf file . I could then save alot of work by simply importing it into Turbo Tax. Rather that downloading an excel file then exporting to cvs I simply highlighted the data in my browser and cut and paste the data into a text file. I used this text file as input and some columns of data are not needed but I put them in the input file to make the cutting and pasting easier.   Some example data is below. Note that you can easily modify the script to take any input format as long as you provide the data needed to build a StockTransaction Object. 

Column names

Stock Shares date bought price basis Order date sold sell amt total gain/(loss) gainterm

Example Input

  X        1000           02/20/2008 6.7884  6798.39 S 03/26/2008        7.231          7220.9   422.51  Short
  X        200           09/18/2008 5.3083  1071.65 S 09/18/2008        5.8417         1158.34  86.69   Short
  X        10.41807 02/01/2006 11.22          116.89  S 11/03/2008        4.463          46.37          (70.53)  Long
  X        28.67448 08/01/2005 11.95          342.66         S 11/03/2008        4.463          127.62  (215.04) Long

...


Expected Output

V041 AMy Txf package D04/08/2009 ^ TD N321 C1 L1 P1000 X D02/20/2008 D03/26/2008 $6798.39 $7220.9 ^ TD N321 C1 L1 P200 X D09/18/2008 D09/18/2008 $1071.65 $1158.34 ^ TD N323 C1 L1 P10.41807 X D02/01/2006 D11/03/2008 $116.89 $46.37 ....

The Script

  The design is simple a StockTransaction Class to make transaction objects for the Transaction class. You may recognize the StockTransaction class as the Builder design pattern. To parse input I read each line and interpret each line as a list parsable by lassign. If you have a cvs file you would change this input parser. The StockTransaction object is constructed then added to the TXF object . Once all input is read then the TXF objects toString method is called and written to a file. I also have several reporting functions so that I can verify the totals vs the forms sent into the government. In the script, below I output the gain and loss for both long term and short term investments. Then I print out the same for one stock that was given on the command line. 
#!/bin/sh
 # the next line restarts using wish \
 exec /opt/usr/bin/tclsh8.5  "$0" ${1+"$@"}
 
if { [ catch {package require TclOO } err ] != 0 } {
    puts stderr "Unable to find package TclOO ... adjust your auto_path!";
}
oo::class create StockTrans { 
    constructor {stock shares buydate basisamt selldate sellamt gainterm } {
        my variable data 
        #make sure there is data in the critical variables
        array set data { stock "" shares 0 buydate "" 
            basisamt 0.00 selldate "" sellamt 0.00 gainterm "" }
        foreach item {stock shares buydate basisamt selldate sellamt gainterm} {
            set data($item) [set $item]
        }
        switch -glob -- $data(gainterm) {
            l* -
            L* {
                set data(gainterm) long
            }
            s* -
            S* {
                set data(gainterm) short
            }
            default {
                set data(gainterm) ""
            }
        }
    }
    method setValue { args } {
        my variable data
        set opts { -stock -shares -buydate -basisamt -selldate -sellamt -gainterm }
        array set localdata $args
        foreach idx [array names localdata ] {
            if { $idx ni $opts } {
                error "$idx is not a valid option must be one of [join $opts , ]"
            }
            set data([string range $idx 1 end ]) $localdata($idx)
        }
    }
    method calcGain {} {
        my variable data
        return [ expr { $data(sellamt) - $data(basisamt)} ] 
    }
    method get { key } {
        my variable data
        set retval {}
        set opts { -stock -shares -buydate -basisamt -selldate -sellamt -gainterm }
        if { $key ni $opts } {
            error "$key is not a valid option must be one of [join $opts , ]"
        }
        return $data([string range $key 1 end ])
    }
    method getCode { item} { 
        my variable data
        set opts { -stock -shares -buydate -basisamt -selldate -sellamt -gainterm }
        switch -exact -- $item {
            -shares -
            -stock {
                return "P$data(shares) $data(stock)"
            }
            -buydate -
            -selldate {
                return "D$data([string range $item 1 end ])"
            }
            -sellamt -
            -basisamt {
                return "\$$data([string range $item 1 end ])"
            }
            -gainterm {
                switch  -exact -- $data(gainterm) {
                    long {
                        return N323        
                    } 
                    short {
                        return N321
                    }
                    default {
                        return N673
                    }
                }
            }
        }
        return $retval
    }
    method eq { item key } {
        my variable data
        set item [string range $item 1 end ]
        return [string equal [string tolower $data($item)] [string tolower $key ]]
    }
    method gt { item key } {
        my variable data
        set item [string range $item 1 end ]
        if {[string is digit $data($item) ] } {
            return [expr ($data($item) > $key) ? 1 : 0 ]
        } else {
            return [expr ([string compare [string tolower $data($item)] [string tolower $key]] == 1 ) ? 1 : 0 ]
        }
    }
    method ge { item key } {
        my variable data
        set item [string range $item 1 end ]
        if {[string is digit $data($item) ] } {
            return [expr ($data($item) >= $key) ? 1 : 0 ]
        } else {
            return [expr ([string compare [string tolower $data($item) ] [string tolower $key ] ] >= 0 ) ? 1 : 0 ]
        }
    }
    method lt { item key } {
        my variable data
        set item [string range $item 1 end ]
        if {[string is digit $data($item) ] } {
            return [expr ($data($item) < $key) ? 1 : 0 ]
        } else {
            return [expr ([string compare [string tolower $data($item)] [string tolower $key]] == -1 ) ? 1 : 0 ]
        }
    }
    method le { item key } {
        my variable data
        set item [string range $item 1 end ]
        if {[string is digit $data($item) ] } {
            return [expr ($data($item) <= $key) ? 1 : 0 ]
        } else {
            return [expr ([string compare [string tolower $data($item)] [string tolower $key]] <= 0 ) ? 1 : 0 ]
        }
    }
    method ne { item key } {
        my variable data
        set item [string range $item 1 end ]
        return ![string equal [string tolower $data($item)] [string tolower $key ]]
    }
    method toString {} {
        set retval "TD\n[[self] getCode -gainterm]\nC1\nL1\n"
        foreach item  { -stock -buydate -selldate -basisamt -sellamt } {
            append retval "[ [self] getCode $item ]\n"
        }
        append retval "^"
        return $retval
    }
    destructor {
    }
}
oo::class create TXF {
    constructor { args } {
        my variable opts
        my variable translist 
        set translist {} 
        array set opts [list \
                            -exportdate    [clock format [clock seconds ] -format "%D" ] \
                            -software      "Tcl Txf package" \
                            -version       "041" \
                           ]
        array set opts $args
        set opts(-version) 041; # only version 041 supported currently
    }
    method addTransaction { transobj } {
        my variable translist
        lappend translist $transobj
    }
    # static method ?
    method  getMatchingTransactions { translist item filterop key } {
        puts "getMatchingTransactions"
            set retval {}
        foreach obj $translist {
            if { [$obj $filterop $item $key ] } {
                lappend retval $obj
            }
        }
        return $retval;
    }
    method getTransactions { gainterm args } {
        my variable translist
        switch -exact -- $gainterm {
            short -
            long {
                set objlist [ my getMatchingTransactions $translist  -gainterm eq $gainterm ]
            }
            all {
                set objlist [ my getAllTransactions ]
            }
            default {
                error "gainterm $gainterm is not valid must be short,long or all"
            }
        }
        puts "For gainterm = $gainterm and args \"$args\" ([llength $args]) the objlist has [llength $objlist ] objects"
        while { [llength $args ] && [ llength $objlist ] } {
            set args [ lassign $args item key]
            puts "geting all objlist that match $item eq $key"
            set objlist [ my getMatchingTransactions $objlist $item eq $key]
        }
        return $objlist
    }
    method getAllTransactions { } {
        my variable translist
        return $translist; # no defensive copy shame!
    }
    method toString {} {
        my variable translist
        my variable opts
        set retval ""
        append retval "V$opts(-version)\nA$opts(-software)\nD$opts(-exportdate)\n^\n"
        foreach obj $translist {
            append retval [$obj toString ]\n
        }
        return $retval;
    }
    method count {} {
        my variable translist
        return [llength $translist ];
    }
    destructor {
        my variable translist
        foreach obj $translist {
            destroy $obj
        }
    }
}
#procs to use the TXF object 
proc printTXF { txfobj fname { eol crlf } } {
    if { [catch {open $fname w 0666 } err ] != 0 } {
            error "Unable to open $fname because $err"
    }
    fconfigure $err -translation $eol
    puts $err [${txfobj} toString] 
    catch { close $err }
}

proc calcSum { txfobj calcitem gainterm args } {
    set objlist [ eval $txfobj getTransactions  $gainterm $args ]
    set sum 0
    foreach obj $objlist {
        set sum [ expr { $sum + [$obj get $calcitem  ] } ]
    }
    return $sum
}


proc calcGainLoss { txfobj gainterm args } { 
    set objlist [ eval $txfobj getTransactions  $gainterm $args ]
    set sum 0
    foreach obj $objlist {
        set sum [ expr { $sum + [$obj calcGain ] } ]
    }
    return $sum
}

set txf [ TXF new -software "My Txf package" ]

set fd [open [lindex $argv 0 ] "r" ]
set linecount 0 
while { ![eof $fd ] } {
    gets $fd line
    set excesscol  [lassign $line stock shares buydate Price \
                        basisamt Order selldate sellprice sellamt gainloss gainterm ]
    incr linecount
    if { [llength $excesscol ] || [string length $gainterm ] == 0  } {
        puts stderr "line $linecount is invalid : \"$line\""
        puts stderr "skipping line $linecount"
        continue;
    }

    set obj [StockTrans new $stock \
                 $shares \
                 $buydate \
                 $basisamt \
                 $selldate \
                 $sellamt \
                 $gainterm  ]
    $txf addTransaction $obj
}
# output the TXF file 
printTXF $txf [lindex $argv 1 ]
if { $argc <= 2 } { 
set stk "All stocks"
set stkcmd ""
} else { 
    set stk [lindex $argv 2 ]
    set stkcmd "-stock $stk"
} 
puts  "Report for $stk : " 
puts  "Long Term Gain/(Loss) Short Term Gain/(Loss) Total Gain/(Loss)"
set lg [eval calcGainLoss $txf  long  $stkcmd ]
set sg [eval calcGainLoss $txf  short $stkcmd ]
set ag [eval calcGainLoss $txf  all   $stkcmd ]
if { $lg < 0 } {
    puts -nonewline [format "(%7.2f)     " $lg ]
} else {
    puts -nonewline [format "%7.2f       " $lg ]
} 
if { $sg < 0 } {
    puts -nonewline [format "(%7.2f)     " $sg ] 
} else {
    puts -nonewline [format "%7.2f       " $sg ]
} 
if { $ag < 0 } {
    puts [format "(%7.2f)     " $ag ]
} else {
    puts [format "%7.2f       " $ag ]
} 

puts "Long Term Sell Amount : [format "%7.2f" [ calcSum $txf -sellamt long  ]]"
puts "Short Term Sell Amount: [format "%7.2f" [ calcSum $txf -sellamt short ]]"
puts "           Sell Amount: [format "%7.2f" [ calcSum $txf -sellamt all   ]]"

enter categories here