def display_summary_tax(summary_taxdata, CGTCalc, taxyear, report):

        """
        taxdata contains a list of tuples
        ## Each tuplue (gbp_disposal_proceeds, gbp_allowable_costs, gbp_gains, gbp_losses, number_disposals,
                commissions, taxes, gbp_gross_profit, gbp_net_profit)



        """

        ## Unpack tuple
        (gbp_disposal_proceeds, gbp_allowable_costs, gbp_gains, gbp_losses, number_disposals,
                gbp_commissions, gbp_taxes, gbp_gross_profit,  abs_quantity, gbp_net_profit) = summary_taxdata

        report.write(star_line())

        report.write("\n\n                Summary for tax year ending 5th April %d \n" % taxyear)
        report.write("\n                              Figures in GBP\n\n")


        if CGTCalc:
            report.write("Disposal Proceeds = %s, Allowable Costs = %s, Disposals = %d \n Year Gains = %s  Year Losses = %s PROFIT = %s\n" % \
                (pretty(gbp_disposal_proceeds), pretty(gbp_allowable_costs),
                 number_disposals, pretty(gbp_gains), pretty(gbp_losses), pretty(gbp_net_profit)))

        else:
            report.write("Gross trading profit %s, Commission paid %s, Taxes paid %s, Net profit %s\n" % \
              (pretty(gbp_gross_profit), pretty(gbp_commissions),
               pretty(gbp_taxes), pretty(gbp_net_profit)))

            report.write("\nNot included: interest paid, interest received, data and other fees, internet connection,...\n hardware, software, books, subscriptions, office space, Dividend income (report seperately)\n\n")

        report.write("\n\n")
def display_summary_tax(summary_taxdata, CGTCalc, taxyear, report):
            
        """
        taxdata contains a list of tuples
        ## Each tuplue (gbp_disposal_proceeds, gbp_allowable_costs, gbp_gains, gbp_losses, number_disposals,
                commissions, taxes, gbp_gross_profit, gbp_net_profit)
    
        
        
        """    

        ## Unpack tuple
        (gbp_disposal_proceeds, gbp_allowable_costs, gbp_gains, gbp_losses, number_disposals,
                gbp_commissions, gbp_taxes, gbp_gross_profit,  abs_quantity, gbp_net_profit) = summary_taxdata

        report.write(star_line())
        
        report.write("\n\n                Summary for tax year ending 5th April %d \n" % taxyear)
        report.write("\n                              Figures in GBP\n\n")


        if CGTCalc:
            report.write("Disposal Proceeds = %s, Allowable Costs = %s, Disposals = %d \n Year Gains = %s  Year Losses = %s PROFIT = %s\n" % \
                (pretty(gbp_disposal_proceeds), pretty(gbp_allowable_costs), 
                 number_disposals, pretty(gbp_gains), pretty(gbp_losses), pretty(gbp_net_profit)))
            
        else:
            report.write("Gross trading profit %s, Commission paid %s, Taxes paid %s, Net profit %s\n" % \
              (pretty(gbp_gross_profit), pretty(gbp_commissions),
               pretty(gbp_taxes), pretty(gbp_net_profit)))
              
            report.write("\nNot included: interest paid, interest received, data and other fees, internet connection,...\n hardware, software, books, subscriptions, office space, Dividend income (report seperately)\n\n")
            
        report.write("\n\n")
Exemplo n.º 3
0
    def display_taxes(self, taxyear, CGTCalc, reportinglevel, report=None):
        """
        Run through each element, displaying the tax information in full

        Then print a summary
        """
        assert reportinglevel in [
            "VERBOSE", "CALCULATE", "NORMAL", "BRIEF", "ANNUAL"
        ]

        if report is None:
            report = sys.stdout

        ## Prints, and returns a tuple for each disposal_proceeds, allowable_costs, year_gains, year_losses,
        ##        number_disposals, commissions, taxes, gross profit, net profit

        codes = self.keys()
        codes.sort()
        elements_taxdata = [
            self[code].display_taxes_for_code(taxyear, CGTCalc, reportinglevel,
                                              report) for code in codes
        ]

        if len(elements_taxdata) == 0:
            report.write(star_line())

            report.write("\n\nNo relevant trades for tax year %d\n\n" %
                         taxyear)
            report.write(star_line())

            return None

        summary_taxdata = map(sum, zip(*elements_taxdata))

        assert len(summary_taxdata) == len(zero_tax_tuple)

        ## print the summary (always regardless of reporting level)
        display_summary_tax(summary_taxdata, CGTCalc, taxyear, report)

        report.write(star_line())

        return None
    def display_taxes(self,  taxyear, CGTCalc, reportinglevel, report=None):
        """
        Run through each element, displaying the tax information in full
        
        Then print a summary
        """
        assert reportinglevel in ["VERBOSE","CALCULATE", "NORMAL", "BRIEF", "ANNUAL"]
        
        if report is None:
            report=sys.stdout
        
        ## Prints, and returns a tuple for each disposal_proceeds, allowable_costs, year_gains, year_losses,
        ##        number_disposals, commissions, taxes, gross profit, net profit
        
        codes= self.keys()
        codes.sort()
        elements_taxdata=[self[code].display_taxes_for_code(taxyear, CGTCalc, reportinglevel, report) for code in codes]

        if len(elements_taxdata)==0:
            report.write(star_line())        
            
            report.write("\n\nNo relevant trades for tax year %d\n\n" % taxyear)
            report.write(star_line())
            
            return None
        
        
        summary_taxdata=map(sum, zip(*elements_taxdata))

        assert len(summary_taxdata)==len(zero_tax_tuple)

        ## print the summary (always regardless of reporting level)
        display_summary_tax(summary_taxdata, CGTCalc, taxyear, report)

        report.write(star_line())        


        return None
    def _print_tax_details(self, report, reportinglevel, CGTCalc,  net_profit, open_value, close_value,
                          groupid, gbp_net_profit, open_comm, close_comm, open_tax, close_tax, allowable_costs,
                          disposal_proceeds, commissions, taxes):
    
        code=self.closingtrade.Code
        currency=self.closingtrade.Currency
        
        assetclass=(getattr(self.closingtrade, "AssetClass",""))
            
        datelabel=self.closingtrade.Date.strftime('%d/%m/%Y') 
    
    
        ## quantity will be negative for a closing sale / opening buy
        sign_quantity=self.closingtrade.SignQuantity
    
        abs_quantity=abs(sign_quantity)
    
    
        average_open_value= abs(open_value) / abs_quantity
        average_close_value= abs(close_value) / abs_quantity
    
    
        ## Labelling
        if sign_quantity<0:
            labels=("BUY", "SELL")
            signs=("-", "")
        else:
            labels=("OPEN SHORT", "CLOSE SHORT")
            signs=("+","-")
    
        if net_profit<0:
            pandl="LOSS"
        else:
            pandl="PROFIT" 



    
        inreport=reporting_detail(reportinglevel)
        
        if inreport.extraline():
            report.write(star_line())
            report.write("\n")
        
        if CGTCalc:

            """
            Example of CGT output
            1. SELL: 40867 XYZ (Stock) on 17/12/2013 at EUR0.911 gives LOSS of XYZ 8,275.00 equals GBP 5,000
            
            (or CLOSE SHORT:  . Matches with OPEN SHORT: )
            
            Matches with: 
            BUY: SAME DAY TRADES.
            TRADES WITHIN 30 days 
            SECTION 104 HOLDING. 40867 shares of XYZ bought at average price of EUR1.11333
            
            """
            
            if inreport.showbrieftrade():
                
                report.write("%d: %s %d %s %s on %s at %s %s each gives %s of %s %s equals GBP %s\n" % \
                      (groupid, labels[1], int(abs_quantity), code, assetclass, datelabel, currency, pretty(average_close_value), 
                       pandl, currency, pretty(round(net_profit)), pretty(gbp_net_profit)))

            if inreport.showextra():
                report.write(" Commission %s %s and taxes %s %s on %s\n"% (currency, pretty(close_comm), currency, pretty(close_tax),labels[1]))

            if inreport.listtrades():
                report.write("Trade details:"+self.closingtrade.__repr__()+"\n")
    
            if inreport.showextra():
                report.write("Total allowable cost %s %s   Total disposal proceeds %s %s\n" % \
                     (currency, pretty(allowable_costs), currency, pretty(disposal_proceeds))) 
                
                report.write("\nMatches with:\n")            

            ## Calculation strings, build up to show how we calculated our profit or loss
            calc_string="%s(%d*%s) - %s - %s " % \
            (signs[1], int(abs_quantity), pretty(average_close_value, commas=False), pretty(close_comm), pretty(close_tax))
            
                
            if len(self.sameday)>0:
                sameday_quantity=int(round(abs(self.sameday.final_position())))
                sameday_avg_value=self.sameday.average_value()
                sameday_tax=sum([trade.Tax for trade in self.sameday])
                sameday_comm=sum([trade.Commission for trade in self.sameday])

                sameday_calc_string="%s(%d*%s) - %s - %s " % \
                    (signs[0], sameday_quantity, pretty(sameday_avg_value, commas=False), pretty(sameday_comm), pretty(sameday_tax))

                calc_string=calc_string+sameday_calc_string

                if inreport.showextra():
                        
                    report.write("SAME DAY TRADE(S) Matches with %s of %d %s at average of %s %s each \n Commissions %s %s Taxes %s %s \n" % \
                      (labels[0], sameday_quantity, code, currency, pretty(sameday_avg_value), currency, pretty(sameday_comm), currency, pretty(sameday_tax)))

                if inreport.listtrades():
                    report.write("\nTrades:\n")
                    self.sameday.print_trades_and_parents(report)
                            
                
            if len(self.withinmonth)>0:
                withinmonth_quantity=int(abs(round((self.withinmonth.final_position()))))
                withinmonth_avg_value=self.withinmonth.average_value()
                withinmonth_tax=sum([trade.Tax for trade in self.withinmonth])
                withinmonth_comm=sum([trade.Commission for trade in self.withinmonth])
                
                tradecount=len(self.withinmonth)
                (startdate,enddate)=self.withinmonth.range_of_dates()

                withinmonth_calc_string="%s(%d*%s) - %s - %s " % \
                    (signs[0], withinmonth_quantity, pretty(withinmonth_avg_value, commas=False), pretty(withinmonth_comm), pretty(withinmonth_tax))

                calc_string=calc_string+withinmonth_calc_string
                
                if inreport.showextra():
    
                    report.write("SUBSEQUENT %d TRADE(S) Within 30 days between %s and %s: Matches with %s of %d %s at of %s %s each \n Commissions %s %s Taxes %s %s  \n" % \
                      (tradecount, str(startdate.date()), str(enddate.date()), labels[0], withinmonth_quantity, code, currency, pretty(withinmonth_avg_value), currency, pretty(withinmonth_comm), currency, pretty(withinmonth_tax)))

                if inreport.listtrades():

                    report.write("\nTrades:\n")
                    self.withinmonth.print_trades_and_parents(report)

            
            if len(self.s104)>0:
                s104_quantity=int(round(abs(self.s104.final_position())))
                s104_avg_value=self.s104.average_value()
                s104_tax=sum([trade.Tax for trade in self.s104])
                s104_comm=sum([trade.Commission for trade in self.s104])

                tradecount=len(self.s104)
                (startdate,enddate)=self.s104.range_of_dates()
                parent_quantity=self.s104.total_including_parents()

                s104_calc_string="%s(%d*%s) - %s - %s " % \
                    (signs[0], s104_quantity, pretty(s104_avg_value, commas=False), pretty(s104_comm), pretty(s104_tax))

                calc_string=calc_string+s104_calc_string

                if inreport.showextra():

                    report.write("PRO-RATA SECTION 104: Quantity %f %s allocated from total holding of %s, made up of %d trades between %s and %s\n At average value of %s %s Commissions %s %s Taxes %s %s  \n" % \
                      ( s104_quantity, code, pretty(parent_quantity), len(self.s104), str(startdate.date()), str(enddate.date()), currency, pretty(s104_avg_value), currency, pretty(s104_comm), currency, pretty(s104_tax)))


                if inreport.listtrades():
                    report.write("\nTrades:\n")
                    self.s104.print_trades_and_parents(report)


            if inreport.showcalcs():
                
                ## Show full calculations
                report.write("\nCALCULATION: "+calc_string+" = %s \n" % pretty(round(net_profit)))
            
        else:
            """
            Example of non CGT output
            
            SELL 40867 RSA (Stock) on 17/12/2013 at EUR0.911 gives net LOSS of EUR 8,275 equals GBP5,000.0
            AVERAGE price EUR .  Total commission: EUR   Total tax:  EUR 
            """
            if inreport.showbrieftrade():
            
                report.write("%d: %s of %d %s %s on %s at %s %s each Net %s of %s %s equals GBP %s\n" % \
                      (groupid, labels[1], int(abs_quantity), code, assetclass, datelabel, currency, pretty(average_close_value),
                       pandl, currency, pretty(round(net_profit)), pretty(gbp_net_profit)))                       

            if inreport.listtrades():
                
                report.write("Trade details:"+self.closingtrade.__repr__()+"\n")
                
            tradecount=len(self.s104)
            (startdate,enddate)=self.s104.range_of_dates()
            parent_quantity=self.s104.total_including_parents()

            ## Calculation strings, build up to show how we calculated our profit or loss
            calc_string="%s(%d*%s) - %s - %s " % \
            (signs[1], int(abs_quantity), pretty(average_close_value, commas=False), pretty(close_comm), pretty(close_tax))


            closing_calc_string="%s(%d*%s) - %s - %s " % \
                (signs[0], int(abs_quantity), pretty(average_open_value, commas=False), pretty(open_comm), pretty(open_tax))

            calc_string=calc_string+closing_calc_string

            if inreport.showextra():
    
                report.write("\n%s at average value %s each between %s and %s.  Total round-trip commission %s %s, and taxes %s %s" % \
                       (labels[0], pretty(average_open_value), str(startdate.date()), str(enddate.date()), currency, pretty(commissions), currency, pretty(taxes)))
        
            
            if inreport.listtrades():
                ## Trade by trade breakdown
                report.write("\nTrades:\n")
                self.s104.print_trades_and_parents(report)

            if inreport.showcalcs():
                ## calculations
                report.write("\nCALCULATION: "+calc_string+" = %s \n" % pretty(round(net_profit)))

        if inreport.extraline():
            report.write("\n")
Exemplo n.º 6
0
def calculatetax(all_trades, all_positions=None, CGTCalc=True, reportfile=None, reportinglevel="NORMAL", fxsource="DATABASE"):
    """
    Calculate the tax

    all_trades is a TradeList object
    all_positions is an optional PositionList object which we use to check consistency with trades

    CGTCalc = True means we use same day and 30 day matching before S104 matching
              False means we use only S104; effectively calculating average cost for each trade

    reportinglevel - ANNUAL - summary for each year, BRIEF- plus one line per trade,
                   NORMAL - plus matching details per trade, CALCULATE - as normal plus calculations
                   VERBOSE - full breakdown of subtrade matching

    reportfile- text file we dump answers into. If omitted will print to screen.

    fxsource will indicate source of data used by FXData function as appropriate

    """

    assert reportinglevel in ["VERBOSE", "CALCULATE", "NORMAL", "BRIEF", "ANNUAL"]

    if reportfile is None:
        reportfile="the screen."
        report=sys.stdout
    else:
        report = open(reportfile, "w")

    print("Report will be written to %s" % reportfile)

    ### Check against positions
    if all_positions is not None:
        breaklist=compare_trades_and_positions(all_trades, all_positions)
        if len(breaklist)>0:
            print("Breaks. Should be none except perhaps for FX rates.")
            print(breaklist)
        else:
            print("Trades and positions consistent")

    ### Add TradeID's
    all_trades.add_tradeids()

    ## Get FX data
    print("Getting fx data")
    all_currencies=all_trades.all_currencies()
    fx_dict=FXDict(all_currencies, fxsource)

    all_trades.add_fxdict_rates(fx_dict)


    ## Do various preprocessing measures
    trade_dict_bycode=all_trades.separatecode()
    trade_dict_bycode.add_cumulative_data()
    trade_dict_bycode.generate_pseduo_trades()

    ## Create a tax dictionary containing the trade data
    taxcalc_dict=TaxCalcDict(trade_dict_bycode)

    ## Do the trade matching
    print("Matching trades")
    taxcalc_dict.allocate_dict_trades(CGTCalc)

    ## Consistency check - this should never fail
    breaklist=compare_trades_and_positions(all_trades, taxcalc_dict.umatched_as_positions())

    if len(breaklist)>0:
        print("BREAKS between final positions and those implied by trades. Something gone horribly wrong!")
        print(breaklist)
        raise Exception("Breaks occured!")
    else:
        print("Passed consistency check")

    ## What tax years are our trades for
    taxyears=taxcalc_dict.tax_year_span()

    for taxyear in taxyears:
        report.write(star_line())
        report.write("\n TAX YEAR: %d \n\n" % taxyear)

        ## Display taxes
        taxcalc_dict.display_taxes(taxyear, CGTCalc, reportinglevel, report)

    if reportfile is not "the screen":
        report.close()

    print("Report finished")

    return taxcalc_dict
    def _print_tax_details(self, report, reportinglevel, CGTCalc,  net_profit, open_value, close_value,
                          groupid, gbp_net_profit, open_comm, close_comm, open_tax, close_tax, allowable_costs,
                          disposal_proceeds, commissions, taxes):
    
        code=self.closingtrade.Code
        currency=self.closingtrade.Currency
        
        assetclass=(getattr(self.closingtrade, "AssetClass",""))
            
        datelabel=self.closingtrade.Date.strftime('%d/%m/%Y') 
    
    
        ## quantity will be negative for a closing sale / opening buy
        sign_quantity=self.closingtrade.SignQuantity
    
        abs_quantity=abs(sign_quantity)
    
    
        average_open_value= abs(open_value) / abs_quantity
        average_close_value= abs(close_value) / abs_quantity
    
    
        ## Labelling
        if sign_quantity<0:
            labels=("BUY", "SELL")
            signs=("-", "")
        else:
            labels=("OPEN SHORT", "CLOSE SHORT")
            signs=("+","-")
    
        if net_profit<0:
            pandl="LOSS"
        else:
            pandl="PROFIT" 



    
        inreport=reporting_detail(reportinglevel)
        
        if inreport.extraline():
            report.write(star_line())
            report.write("\n")
        
        if CGTCalc:

            """
            Example of CGT output
            1. SELL: 40867 XYZ (Stock) on 17/12/2013 at EUR0.911 gives LOSS of XYZ 8,275.00 equals GBP 5,000
            
            (or CLOSE SHORT:  . Matches with OPEN SHORT: )
            
            Matches with: 
            BUY: SAME DAY TRADES.
            TRADES WITHIN 30 days 
            SECTION 104 HOLDING. 40867 shares of XYZ bought at average price of EUR1.11333
            
            """
            
            if inreport.showbrieftrade():
                
                report.write("%d: %s %d %s %s on %s at %s %s each gives %s of %s %s equals GBP %s\n" % \
                      (groupid, labels[1], int(abs_quantity), code, assetclass, datelabel, currency, pretty(average_close_value), 
                       pandl, currency, pretty(round(net_profit)), pretty(gbp_net_profit)))

            if inreport.showextra():
                report.write(" Commission %s %s and taxes %s %s on %s\n"% (currency, pretty(close_comm), currency, pretty(close_tax),labels[1]))

            if inreport.listtrades():
                report.write("Trade details:"+self.closingtrade.__repr__()+"\n")
    
            if inreport.showextra():
                report.write("Total allowable cost %s %s   Total disposal proceeds %s %s\n" % \
                     (currency, pretty(allowable_costs), currency, pretty(disposal_proceeds))) 
                
                report.write("\nMatches with:\n")            

            ## Calculation strings, build up to show how we calculated our profit or loss
            calc_string="%s(%d*%s) - %s - %s " % \
            (signs[1], int(abs_quantity), pretty(average_close_value, commas=False), pretty(close_comm), pretty(close_tax))
            
                
            if len(self.sameday)>0:
                sameday_quantity=int(round(abs(self.sameday.final_position())))
                sameday_avg_value=self.sameday.average_value()
                sameday_tax=sum([trade.Tax for trade in self.sameday])
                sameday_comm=sum([trade.Commission for trade in self.sameday])

                sameday_calc_string="%s(%d*%s) - %s - %s " % \
                    (signs[0], sameday_quantity, pretty(sameday_avg_value, commas=False), pretty(sameday_comm), pretty(sameday_tax))

                calc_string=calc_string+sameday_calc_string

                if inreport.showextra():
                        
                    report.write("SAME DAY TRADE(S) Matches with %s of %d %s at average of %s %s each \n Commissions %s %s Taxes %s %s \n" % \
                      (labels[0], sameday_quantity, code, currency, pretty(sameday_avg_value), currency, pretty(sameday_comm), currency, pretty(sameday_tax)))

                if inreport.listtrades():
                    report.write("\nTrades:\n")
                    self.sameday.print_trades_and_parents(report)
                            
                
            if len(self.withinmonth)>0:
                withinmonth_quantity=int(abs(round((self.withinmonth.final_position()))))
                withinmonth_avg_value=self.withinmonth.average_value()
                withinmonth_tax=sum([trade.Tax for trade in self.withinmonth])
                withinmonth_comm=sum([trade.Commission for trade in self.withinmonth])
                
                tradecount=len(self.withinmonth)
                (startdate,enddate)=self.withinmonth.range_of_dates()

                withinmonth_calc_string="%s(%d*%s) - %s - %s " % \
                    (signs[0], withinmonth_quantity, pretty(withinmonth_avg_value, commas=False), pretty(withinmonth_comm), pretty(withinmonth_tax))

                calc_string=calc_string+withinmonth_calc_string
                
                if inreport.showextra():
    
                    report.write("SUBSEQUENT %d TRADE(S) Within 30 days between %s and %s: Matches with %s of %d %s at of %s %s each \n Commissions %s %s Taxes %s %s  \n" % \
                      (tradecount, str(startdate.date()), str(enddate.date()), labels[0], withinmonth_quantity, code, currency, pretty(withinmonth_avg_value), currency, pretty(withinmonth_comm), currency, pretty(withinmonth_tax)))

                if inreport.listtrades():

                    report.write("\nTrades:\n")
                    self.withinmonth.print_trades_and_parents(report)

            
            if len(self.s104)>0:
                s104_quantity=int(round(abs(self.s104.final_position())))
                s104_avg_value=self.s104.average_value()
                s104_tax=sum([trade.Tax for trade in self.s104])
                s104_comm=sum([trade.Commission for trade in self.s104])

                tradecount=len(self.s104)
                (startdate,enddate)=self.s104.range_of_dates()
                parent_quantity=self.s104.total_including_parents()

                s104_calc_string="%s(%d*%s) - %s - %s " % \
                    (signs[0], s104_quantity, pretty(s104_avg_value, commas=False), pretty(s104_comm), pretty(s104_tax))

                calc_string=calc_string+s104_calc_string

                if inreport.showextra():

                    report.write("PRO-RATA SECTION 104: Quantity %f %s allocated from total holding of %s, made up of %d trades between %s and %s\n At average value of %s %s Commissions %s %s Taxes %s %s  \n" % \
                      ( s104_quantity, code, pretty(parent_quantity), len(self.s104), str(startdate.date()), str(enddate.date()), currency, pretty(s104_avg_value), currency, pretty(s104_comm), currency, pretty(s104_tax)))


                if inreport.listtrades():
                    report.write("\nTrades:\n")
                    self.s104.print_trades_and_parents(report)


            if inreport.showcalcs():
                
                ## Show full calculations
                report.write("\nCALCULATION: "+calc_string+" = %s \n" % pretty(round(net_profit)))
            
        else:
            """
            Example of non CGT output
            
            SELL 40867 RSA (Stock) on 17/12/2013 at EUR0.911 gives net LOSS of EUR 8,275 equals GBP5,000.0
            AVERAGE price EUR .  Total commission: EUR   Total tax:  EUR 
            """
            if inreport.showbrieftrade():
            
                report.write("%d: %s of %d %s %s on %s at %s %s each Net %s of %s %s equals GBP %s\n" % \
                      (groupid, labels[1], int(abs_quantity), code, assetclass, datelabel, currency, pretty(average_close_value),
                       pandl, currency, pretty(round(net_profit)), pretty(gbp_net_profit)))                       

            if inreport.listtrades():
                
                report.write("Trade details:"+self.closingtrade.__repr__()+"\n")
                
            tradecount=len(self.s104)
            (startdate,enddate)=self.s104.range_of_dates()
            parent_quantity=self.s104.total_including_parents()

            ## Calculation strings, build up to show how we calculated our profit or loss
            calc_string="%s(%d*%s) - %s - %s " % \
            (signs[1], int(abs_quantity), pretty(average_close_value, commas=False), pretty(close_comm), pretty(close_tax))


            closing_calc_string="%s(%d*%s) - %s - %s " % \
                (signs[0], int(abs_quantity), pretty(average_open_value, commas=False), pretty(open_comm), pretty(open_tax))

            calc_string=calc_string+closing_calc_string

            if inreport.showextra():
    
                report.write("\n%s at average value %s each between %s and %s.  Total round-trip commission %s %s, and taxes %s %s" % \
                       (labels[0], pretty(average_open_value), str(startdate.date()), str(enddate.date()), currency, pretty(commissions), currency, pretty(taxes)))
        
            
            if inreport.listtrades():
                ## Trade by trade breakdown
                report.write("\nTrades:\n")
                self.s104.print_trades_and_parents(report)

            if inreport.showcalcs():
                ## calculations
                report.write("\nCALCULATION: "+calc_string+" = %s \n" % pretty(round(net_profit)))

        if inreport.extraline():
            report.write("\n")
def calculatetax(all_trades, all_positions=None, CGTCalc=True, reportfile=None, reportinglevel="NORMAL", fxsource="DATABASE"):
    """
    Calculate the tax
    
    all_trades is a TradeList object
    all_positions is an optional PositionList object which we use to check consistency with trades
    
    CGTCalc = True means we use same day and 30 day matching before S104 matching
              False means we use only S104; effectively calculating average cost for each trade
    
    reportinglevel - ANNUAL - summary for each year, BRIEF- plus one line per trade, 
                   NORMAL - plus matching details per trade, CALCULATE - as normal plus calculations  
                   VERBOSE - full breakdown of subtrade matching
    
    reportfile- text file we dump answers into. If omitted will print to screen.

    fxsource will indicate source of data used by FXData function as appropriate
    
    """
    
    assert reportinglevel in ["VERBOSE", "CALCULATE", "NORMAL", "BRIEF", "ANNUAL"]
    
    if reportfile is None:
        reportfile="the screen."
        report=sys.stdout
    else:
        report = open(reportfile, "w")
    
    print "Report will be written to %s" % reportfile
    
    ### Check against positions
    if all_positions is not None:    
        breaklist=compare_trades_and_positions(all_trades, all_positions)
        if len(breaklist)>0:
            print "Breaks. Should be none except perhaps for FX rates."
            print breaklist
        else:
            print "Trades and positions consistent"

    ### Add TradeID's
    all_trades.add_tradeids()
    
    ## Get FX data
    print "Getting fx data"
    all_currencies=all_trades.all_currencies()
    fx_dict=FXDict(all_currencies, fxsource)
    
    all_trades.add_fxdict_rates(fx_dict)

    
    ## Do various preprocessing measures
    trade_dict_bycode=all_trades.separatecode()
    trade_dict_bycode.add_cumulative_data()
    trade_dict_bycode.generate_pseduo_trades()

    ## Create a tax dictionary containing the trade data        
    taxcalc_dict=TaxCalcDict(trade_dict_bycode)
    
    ## Do the trade matching
    print "Matching trades"
    taxcalc_dict.allocate_dict_trades(CGTCalc)

    ## Consistency check - this should never fail   
    breaklist=compare_trades_and_positions(all_trades, taxcalc_dict.umatched_as_positions())
    
    if len(breaklist)>0:
        print "BREAKS between final positions and those implied by trades. Something gone horribly wrong!"
        print breaklist
        raise Exception("Breaks occured!")
    else:
        print "Passed consistency check"

    ## What tax years are our trades for    
    taxyears=taxcalc_dict.tax_year_span()
    
    for taxyear in taxyears:
        report.write(star_line())
        report.write("\n TAX YEAR: %d \n\n" % taxyear)
        
        ## Display taxes
        taxcalc_dict.display_taxes(taxyear, CGTCalc, reportinglevel, report)

    if reportfile is not "the screen":
        report.close()
    
    print "Report finished"
        
    return taxcalc_dict