def calcIDAdjustment(corp, eta=0.4):
    """
    Calculates the adjustment factors for the corporate and noncorporate
    debt and interest.
    eta: retirement rate of existing debt
    """
    policy = Policy()
    policy_params_df = policy.parameters_dataframe()
    # Create Asset object
    asset = Asset(policy_params_df, corp)
    asset.calc_all()
    # Get asset forecast
    forecast = asset.get_forecast()
    # Create Debt object
    debt = Debt(policy_params_df, forecast, corp=corp)
    debt.calc_all()
    # Get unscaled net interest deduction
    NID_gross = debt.NID[38:54]
    # Get net interest deduction from historical IRS data
    if corp:
        NID_irs = np.array(data1.debt_data_corp['NID_IRS'])[38:54]
    else:
        NID_irs = np.array(data1.debt_data_noncorp['ID_Scorp'][38:54] +
                           data1.debt_data_noncorp['ID_sp'][38:54] +
                           data1.debt_data_noncorp['ID_partner'][38:54])
    NID_scale = sum(NID_irs / NID_gross) / 16.0  # 16 = 54 - 38
    return NID_scale
예제 #2
0
def calcIDAdjustment(corp, eta=0.4):
    """
    Calculates the adjustment factors for the corporate and noncorporate
    debt and interest.
    eta: retirement rate of existing debt
    """
    data = Data()
    policy = Policy()
    policy_params_df = policy.parameters_dataframe()
    # Create Asset object
    asset = Asset(policy_params_df, corp)
    asset.calc_all()
    # Get asset forecast
    forecast = asset.get_forecast()
    # Create Debt object
    debt = Debt(policy_params_df, forecast, corp=corp)
    debt.calc_all()
    # Get unscaled interest incomes and expenses
    intpaid_model = debt.int_expense[40:54]
    intinc_model = debt.int_income[40:54]
    muniinc_model = debt.muni_income[40:54]
    if corp:
        # Exclude anomalous results for 2007
        paid_scale = (sum(intpaid[:7] / intpaid_model[:7]) +
                      sum(intpaid[8:] / intpaid_model[8:])) / 13.
        inc_scale = (sum(taxint[:7] / intinc_model[:7]) +
                     sum(taxint[8:] / intinc_model[8:])) / 13.
        muni_scale = (sum(ntaxint[:7] / muniinc_model[:7]) +
                      sum(ntaxint[8:] / muniinc_model[8:]))/ 13.
        scales = [paid_scale, inc_scale, muni_scale]
    else:
        ID_irs = np.array(data.debt_data_noncorp['ID_Scorp'][40:54] +
                          data.debt_data_noncorp['ID_sp'][40:54] +
                          data.debt_data_noncorp['ID_partner'][40:54])
        scales = sum(ID_irs / intpaid_model) / 14.
    assets14 = forecast[0]
    return (scales, assets14, intpaid_model, intinc_model, muniinc_model)
예제 #3
0
class CorpTaxReturn():
    """
    Constructor for the CorpTaxReturn object. 
    This class includes objects relevant to the calculation of 
    corporate income tax liability:
        assets: an associated Asset object
        debts: an associated debt object
        combined_return: a DataFrame with tax calculations for each year
    
    Parameters:
        btax_params: dict of business tax policy parameters
        assets: Asset object for the corporation
        debts: Debt object for the corporation
        earnings: list or array of earnings for each year in the budget window
    """
    
    def __init__(self, btax_params, earnings,
                 data=None, assets=None, debts=None):
        # Create an associated Data object
        if isinstance(data, Data):
            self.data = data
        else:
            self.data = Data()
        if isinstance(btax_params, pd.DataFrame):
            self.btax_params = btax_params
        else:
            raise ValueError('btax_params must be DataFrame')
        if assets is not None:
            if isinstance(assets, Asset):
                self.assets = assets
            else:
                raise ValueError('assets must be Asset object')
        else:
            self.assets = Asset(btax_params)
            self.assets.calc_all()
        if debts is not None:
            if isinstance(debts, Debt):
                self.debts = debts
            else:
                raise ValueError('debts must be Debt object')
        else:
            assets_forecast = self.assets.get_forecast()
            self.debts = Debt(btax_params, assets_forecast)
            self.debts.calc_all()
        # Use earnings to create DataFrame for results
        assert len(earnings) == 14
        combined = pd.DataFrame({'year': range(2014,2028), 'ebitda': earnings})
        # Add tax depreciation and net interest deductions
        combined['taxDep'] = self.assets.get_taxdep()
        combined['nid'] = self.debts.get_nid()
        self.combined_return = combined
    
    def update_assets(self, assets):
        """
        Updates the Asset object associated with the tax return.
        """
        if isinstance(assets, Asset):
            self.assets = assets
        else:
            raise ValueError('assets must be Asset object')
    
    def update_debts(self, debts):
        """
        Updates the Debt object associated with the tax return.
        """
        if isinstance(debts, Debt):
            self.debts = debts
        else:
            raise ValueError('debts must be Debt object')
    
    def update_earnings(self, earnings):
        """
        Updates the earnings DataFrame associated with the tax return.
        """
        assert len(earnings) == 14
        self.combined_return['ebitda'] = earnings
    
    def calcSec199(self):
        """
        Calculates section 199 deduction.
        """
        # Extract relevant parmeters
        s199_hclist = np.array(self.btax_params['sec199_hc'])
        profit = np.asarray(self.data.gfactors['profit_d'])
        sec199_res = np.zeros(14)
        sec199_2013 = np.asarray(self.data.historical_taxdata['sec199'])[-1]
        for i in range(14):
            sec199_res[i] = profit[i+1] / profit[0] * sec199_2013 * (1 - s199_hclist[i])
        self.combined_return['sec199'] = sec199_res
    
    def calcInitialTax(self):
        """
        Calculates taxable income and tax before credits.
        """
        self.combined_return['taxinc'] = (self.combined_return['ebitda'] -
                                          self.combined_return['taxDep'] -
                                          self.combined_return['nid'] -
                                          self.combined_return['sec199'])
        self.combined_return['tau'] = self.btax_params['tau_c']
        self.combined_return['taxbc'] = (self.combined_return['taxinc'] *
                                         self.combined_return['tau'])
    
    def calcFTC(self):
        """
        Calculates foreign tax credit for 2014-2027.
        """
        hclist = np.array(self.btax_params['ftc_hc'])
        def calcWAvgTaxRate(year):
            """
            Calculates the weighted average statutory corporate tax rate
            in all OECD countries in a given year.
            """
            assert year in range(1995, 2028)
            year = min(year, 2016)
            gdp_list = np.asarray(self.data.ftc_gdp_data[str(year)])
            taxrate_list = np.asarray(self.data.ftc_taxrates_data[str(year)])
            # remove observations with missing data
            taxrate_list2 = np.where(np.isnan(taxrate_list), 0, taxrate_list)
            gdp_list2 = np.where(np.isnan(taxrate_list), 0, gdp_list)
            avgrate = sum(taxrate_list2 * gdp_list2) / sum(gdp_list2)
            return avgrate
        # Get foreign profits forecast
        profits = np.asarray(self.data.ftc_other_data['C_total'][19:])
        profits_d = np.asarray(self.data.ftc_other_data['C_domestic'][19:])
        tax_f = np.zeros(14)
        for i in range(14):
            tax_f[i] = calcWAvgTaxRate(i + 2014)
        ftc_final = ((profits - profits_d) * tax_f / 100. *
                     self.data.adjfactor_ftc_corp *
                     (1 - hclist)) * self.data.rescale_corp
        self.combined_return['ftc'] = ftc_final
    
    def calcAMT(self):
        """
        Calculates the AMT revenue and PYMTC for 2014-2027
        pymtc_status: 0 for no change, 1 for repeal, 2 for refundable
        """
        # Get relevant tax information
        taxinc = np.array(self.combined_return['taxinc'])
        amt_rates = np.array(self.btax_params['tau_amt'])
        ctax_rates = np.array(self.btax_params['tau_c'])
        pymtc_status = np.array(self.btax_params['pymtc_status'])
        # Check values for PYMTC status
        for x in pymtc_status:
            assert x in [0, 1, 2]
        # Create empty arrays for AMT, PYMTC, and stocks (by status)
        A = np.zeros(14)
        P = np.zeros(14)
        stockA = np.zeros(15)
        stockN = np.zeros(15)
        stockA[0] = ((self.data.trans_amt1 * self.data.userate_pymtc + self.data.trans_amt2 *
                      (1 - self.data.userate_pymtc)) / (1 - self.data.trans_amt1) * self.data.stock2014)
        stockN[0] = self.data.stock2014 - stockN[0]
        stockN[0] = (1 - self.data.trans_amt1) / (1 - self.data.trans_amt1 + self.data.trans_amt1 *
                                                  self.data.userate_pymtc + self.data.trans_amt2 *
                                                  (1 - self.data.userate_pymtc)) * self.data.stock2014
        stockA[0] = self.data.stock2014 - stockN[0]
        for i in range(14):
            # Calculate AMT
            if amt_rates[i] == 0.:
                # If no AMT
                A[i] = 0.
                frac_amt = 0.
            elif ctax_rates[i] <= amt_rates[i]:
                # If AMT rate exceeds regular rate (all subject to AMT)
                A[i] = ((amt_rates[i] - ctax_rates[i] + amt_rates[i] / self.data.param_amt) *
                        taxinc[i])
                frac_amt = 0.999
            else:
                A[i] = (amt_rates[i] / self.data.param_amt *
                        np.exp(-self.data.param_amt * (ctax_rates[i] / amt_rates[i] - 1)) *
                        taxinc[i])
                frac_amt = np.exp(-self.data.param_amt * (ctax_rates[i] / amt_rates[i] - 1))
            # Adjust transition params for change in AMT frequency
            alpha = max(0.0, min(1.0, self.data.trans_amt1 * (frac_amt / self.data.amt_frac)**0.5))
            beta = (1 - alpha) * frac_amt / (1 - frac_amt)
            if pymtc_status[i] == 0:
                # No change from baseline
                userate = self.data.userate_pymtc
            elif pymtc_status[i] == 1:
                # PYMTC repealed
                userate = 0.0
            else:
                # PYMTC made fully refundable
                userate = 1.0
            P[i] = userate * stockN[i]
            stockA[i+1] = (alpha * (stockA[i] + A[i]) +
                           beta * (stockN[i] - P[i]))
            stockN[i+1] = ((1 - alpha) * (stockA[i] + A[i]) +
                           (1 - beta) * (stockN[i] - P[i]))
        # Rescale for any cross-sector shifting
        amt_final = A * self.data.rescale_corp
        pymtc_final = P * self.data.rescale_corp
        self.combined_return['amt'] = amt_final
        self.combined_return['pymtc'] = pymtc_final
    
    def calcTax(self):
        """
        Calculates final tax liability.
        """
        # Calculate general business credits
        profit = np.asarray(self.data.gfactors['profit_d'])
        gbc_2013 = np.asarray(self.data.historical_taxdata['gbc'])[-1]
        gbc_res = profit[1:] / profit[0] * gbc_2013
        self.combined_return['gbc'] = gbc_res
        # Calculate final tax liability
        self.combined_return['taxrev'] = (self.combined_return['taxbc'] +
                                          self.combined_return['amt'] -
                                          self.combined_return['ftc'] -
                                          self.combined_return['pymtc'] -
                                          self.combined_return['gbc'])
    
    def calc_all(self):
        """
        Executes all tax calculations.
        """
        self.calcSec199()
        self.calcInitialTax()
        self.calcFTC()
        self.calcAMT()
        self.calcTax()
    
    def getReturn(self):
        """
        Returns the tax return information
        """
        combined_result = copy.deepcopy(self.combined_return)
        return combined_result
    
    def get_tax(self):
        """
        Returns the total tax liability.
        """
        tax1 = np.array(self.combined_return['taxrev'])
        return tax1
예제 #4
0
class CorpTaxReturn():
    """
    Constructor for the CorpTaxReturn object.
    This class includes objects relevant to the calculation of
    corporate income tax liability:
        assets: an associated Asset object
        debts: an associated debt object
        combined_return: a DataFrame with tax calculations for each year

    Parameters:
        btax_params: dict of business tax policy parameters
        assets: Asset object for the corporation
        debts: Debt object for the corporation
        earnings: list or array of earnings for each year in the budget window
    """
    def __init__(self,
                 btax_params,
                 revenues,
                 deductions,
                 credit,
                 dmne=None,
                 data=None,
                 assets=None,
                 debts=None):
        # Create an associated Data object
        if isinstance(data, Data):
            self.data = data
        else:
            self.data = Data()
        if isinstance(btax_params, pd.DataFrame):
            self.btax_params = btax_params
        else:
            raise ValueError('btax_params must be DataFrame')
        if isinstance(revenues, pd.DataFrame):
            self.revenues = copy.deepcopy(revenues)
        else:
            raise ValueError('revenues must be in DataFrame')
        if isinstance(deductions, pd.DataFrame):
            self.deductions = copy.deepcopy(deductions)
        else:
            raise ValueError('deductions must be in DataFrame')
        if isinstance(credit, pd.DataFrame):
            self.credits = copy.deepcopy(credit)
        else:
            raise ValueError('credits must be in DataFrame')
        if dmne is None:
            # Note: Don't do this in general
            self.dmne = DomesticMNE(self.btax_params)
            self.dmne.calc_all()
        elif isinstance(dmne, DomesticMNE):
            self.dmne = dmne
        else:
            raise ValueError('dmne must be a DomesticMNE object')
        if assets is not None:
            if isinstance(assets, Asset):
                self.assets = assets
            else:
                raise ValueError('assets must be Asset object')
        else:
            self.assets = Asset(btax_params)
            self.assets.calc_all()
        if debts is not None:
            if isinstance(debts, Debt):
                self.debts = debts
            else:
                raise ValueError('debts must be Debt object')
        else:
            assets_forecast = self.assets.get_forecast()
            self.debts = Debt(btax_params, assets_forecast)
            self.debts.calc_all()
        # Prepare unmodeled components of tax return
        self.revenues['capgains'] = (
            self.revenues['capgains'] *
            (1. - self.btax_params['capgains_corp_hc']))
        self.revenues['domestic_divs'] = (
            self.revenues['domestic_divs'] *
            self.btax_params['domestic_dividend_inclusion'])
        self.revenues['total'] = (self.revenues['receipts'] +
                                  self.revenues['rent'] +
                                  self.revenues['royalties'] +
                                  self.revenues['capgains'] +
                                  self.revenues['domestic_divs'] +
                                  self.revenues['other'] +
                                  self.dmne.dmne_results['foreign_taxinc'])
        self.deductions['charity'] = (self.deductions['charity'] *
                                      (1. - self.btax_params['charity_hc']))
        self.deductions['statelocaltax'] = (
            self.deductions['statelocaltax'] *
            (1. - self.btax_params['statelocaltax_hc']))
        self.deductions['total'] = (
            self.deductions['cogs'] + self.deductions['execcomp'] +
            self.deductions['wages'] + self.deductions['repairs'] +
            self.deductions['baddebt'] + self.deductions['rent'] +
            self.deductions['statelocaltax'] + self.deductions['charity'] +
            self.deductions['amortization'] + self.deductions['depletion'] +
            self.deductions['advertising'] + self.deductions['pensions'] +
            self.deductions['benefits'] + self.deductions['other'])
        combined = pd.DataFrame({
            'year':
            range(START_YEAR, END_YEAR + 1),
            'ebitda': (self.revenues['total'] - self.deductions['total'])
        })
        # Add tax depreciation
        combined['taxDep'] = self.assets.get_taxdep()
        self.combined_return = combined

    def update_assets(self, assets):
        """
        Updates the Asset object associated with the tax return.
        """
        if isinstance(assets, Asset):
            self.assets = assets
        else:
            raise ValueError('assets must be Asset object')

    def update_debts(self, debts):
        """
        Updates the Debt object associated with the tax return.
        """
        if isinstance(debts, Debt):
            self.debts = debts
        else:
            raise ValueError('debts must be Debt object')

    def update_earnings(self, dearnings):
        """
        Updates the earnings DataFrame associated with the tax return.
        """
        assert len(dearnings) == NUM_YEARS
        fearnings = np.asarray(self.dmne.dmne_results['foreign_taxinc'])
        self.combined_return['ebitda'] = dearnings + fearnings

    def calcInterestDeduction(self):
        """
        Computes interest deduction.
        """
        # Compute adjusted taxable income
        adjTaxInc = np.maximum(
            self.combined_return['ebitda'] - self.revenues['capgains'] -
            self.combined_return['taxDep'] +
            self.btax_params['adjustedTaxInc_def'] *
            (self.combined_return['taxDep'] + self.deductions['amortization'] +
             self.deductions['depletion']), 0.0001)
        # Section 163(j) deduction limitation
        deductible_int = (
            adjTaxInc * self.btax_params['adjustedTaxInc_limit'] +
            self.debts.get_intInc())
        intded0 = self.debts.get_intDed()
        intded1 = np.zeros(NUM_YEARS)
        for i in range(NUM_YEARS):
            if i > 0:
                # Add disallowed interest as carryforward from prior year
                intded0[i] = intded0[i] + intded0[i - 1] - intded1[i - 1]
            intded1[i] = min(deductible_int[i], intded0[i])
        # Apply interest haircuts
        intTaxInc = (self.debts.get_intInc() *
                     (1. - self.btax_params['intIncome_corp_hc']) +
                     self.debts.get_muniInc() *
                     (1. - self.btax_params['muniIntIncome_corp_hc']))
        intTaxDed = intded1 * (1. - self.btax_params['intPaid_corp_hc'])
        # Compute net interest deduction
        self.combined_return['nid'] = intTaxDed - intTaxInc
        # Assign fraction of interest deductible to Debt object
        fracded = intTaxDed / (self.debts.get_intPaid() + 0.000001)
        self.btax_params['fracded_c'] = fracded

    def calcInitialTax(self):
        """
        Calculates taxable income and tax before credits.
        """
        netinc1 = (self.combined_return['ebitda'] -
                   self.combined_return['taxDep'] -
                   self.combined_return['nid'])
        self.combined_return['sec199'] = (netinc1 *
                                          self.deductions['sec199share'] *
                                          self.btax_params['sec199_rt'])
        netinc2 = netinc1 - self.combined_return['sec199']
        self.combined_return['taxinc'] = np.maximum(netinc2, 0.)
        self.combined_return['tau'] = self.btax_params['tau_c']
        self.combined_return['taxbc'] = (self.combined_return['taxinc'] *
                                         self.combined_return['tau'])

    def calcFTC(self):
        """
        Gets foreign tax credit from DomesticMNE
        """
        self.combined_return['ftc'] = self.dmne.dmne_results['ftc']

    def calcAMT(self):
        """
        Calculates the AMT revenue and PYMTC for [START_YEAR, END_YEAR]
        """
        # Overall transition rates and parameters
        trans_amt0 = self.data.trans_amt0
        trans_amt1 = self.data.trans_amt1
        userate = self.data.userate_pymtc
        amt2013 = self.data.corp_tax2013.loc[40, 'ALL']
        # Get relevant tax information
        taxinc = np.array(self.combined_return['taxinc'])
        amt_rates = np.array(self.btax_params['tau_amt'])
        ctax_rates = np.array(self.btax_params['tau_c'])
        pymtc_hc = np.array(self.btax_params['pymtc_hc'])
        pymtc_refund = np.array(self.btax_params['pymtc_refund'])
        # Create empty arrays for AMT, PYMTC, and stocks (by status)
        A = np.zeros(NUM_YEARS)
        P = np.zeros(NUM_YEARS)
        stock0 = np.zeros(NUM_YEARS + 1)
        stock1 = np.zeros(NUM_YEARS + 1)
        # Set initial stocks using steady-state equations
        stock0[0] = amt2013 / userate
        stock1[0] = amt2013 * (trans_amt1 / (1. - trans_amt1) +
                               (1. - userate) / userate * (1. - trans_amt0) /
                               (1. - trans_amt1))
        for iyr in range(NUM_YEARS):
            # Calculate AMT
            if amt_rates[iyr] == 0.:
                # If no AMT
                A[iyr] = 0.
                frac_amt = 0.
                # Update transition rate parameters
                pi0 = 1.
                pi1 = 0.
            elif ctax_rates[iyr] <= amt_rates[iyr]:
                # If AMT rate exceeds regular rate (all subject to AMT)
                A[iyr] = ((amt_rates[iyr] - ctax_rates[iyr] +
                           amt_rates[iyr] / self.data.param_amt) * taxinc[iyr])
                frac_amt = 0.999
                # Update transition rate parameters
                pi0 = 0.
                pi1 = 1.
            else:
                A[iyr] = (amt_rates[iyr] / self.data.param_amt *
                          np.exp(-self.data.param_amt *
                                 (ctax_rates[iyr] / amt_rates[iyr] - 1)) *
                          taxinc[iyr])
                # Compute new fraction subject to AMT
                frac_amt = np.exp(-self.data.param_amt *
                                  (ctax_rates[iyr] / amt_rates[iyr] - 1))
                # Adjust transition params for change in AMT frequency
                pi1 = max(
                    min(
                        self.data.trans_amt1 *
                        (frac_amt / self.data.amt_frac)**0.5, 1.), 0.)
                pi0 = max(min(1. - frac_amt * (1 - pi1) / (1 - frac_amt), 1.),
                          0.)
            # Compute PYMTC
            P[iyr] = ((pymtc_refund[iyr] * stock0[iyr] +
                       (1. - pymtc_refund[iyr]) * stock0[iyr] * userate) *
                      (1. - pymtc_hc[iyr]))
            # Update credits carried forward
            stock0[iyr + 1] = ((stock1[iyr] + A[iyr]) * (1. - pi1) +
                               (stock0[iyr] - P[iyr]) * pi0)
            stock1[iyr + 1] = ((stock1[iyr] + A[iyr]) * pi1 +
                               (stock0[iyr] - P[iyr]) * (1. - pi0))
        # Rescale for any cross-sector shifting
        amt_final = A * self.data.rescale_corp
        pymtc_final = P * self.data.rescale_corp
        self.combined_return['amt'] = amt_final
        self.combined_return['pymtc'] = pymtc_final

    def calcTax(self):
        """
        Calculates final tax liability.
        """
        self.combined_return['gbc'] = self.credits['gbc']
        # Calculate final tax liability
        taxliab1 = (self.combined_return['taxbc'] +
                    self.combined_return['amt'] - self.combined_return['ftc'] -
                    self.combined_return['pymtc'] -
                    self.combined_return['gbc'])
        self.combined_return['taxrev'] = np.maximum(taxliab1, 0.)

    def calc_all(self):
        """
        Executes all tax calculations.
        """
        self.calcInterestDeduction()
        self.calcInitialTax()
        self.calcFTC()
        self.calcAMT()
        self.calcTax()

    def getReturn(self):
        """
        Returns the tax return information
        """
        combined_result = copy.deepcopy(self.combined_return)
        return combined_result

    def get_tax(self):
        """
        Returns the total tax liability.
        """
        tax = np.array(self.combined_return['taxrev'])
        return tax
예제 #5
0
    assets14 = forecast[0]
    return (scales, assets14, intpaid_model, intinc_model, muniinc_model)

(scales_corp, ast_c_14, intpaid_model, intinc_model, muniinc_model) = calcIDAdjustment(True)
(scale_ncorp, ast_nc_14, _, _, _) = calcIDAdjustment(False)
print(scales_corp)
print(scale_ncorp)

newdebt = copy.deepcopy(debt2)
newdebt['L_c'] = debt2['L_c'] * scales_corp[0]
newdebt['L_nc'] = debt2['L_nc'] * scale_ncorp
newdebt['At_c'] = debt2['At_c'] * scales_corp[1]
newdebt['An_c'] = debt2['An_c'] * scales_corp[2]
newdebt.to_csv(OUTPUT_PATH + 'debt_history.csv')


pol1 = Policy()
# Create Asset object
asset = Asset(pol1.parameters_dataframe(), True)
asset.calc_all()
# Create Debt object
debt = Debt(pol1.parameters_dataframe(), asset.get_forecast(), corp=True)
debt.calc_all()

df1 = pd.DataFrame({'inc_mod': intinc_model * scales_corp[1],
                    'paid_mod': intpaid_model * scales_corp[0],
                    'muni_mod': muniinc_model * scales_corp[2],
                    'inc_irs': taxint, 'paid_irs': intpaid, 'muni_irs': ntaxint})
df1.to_csv('debt_fit.csv')

예제 #6
0
class PassThrough():
    """
    Constructor for the PassThrough class.
    
    This contains the calculation of pass-through business income. All other
    components of pass-through taxation occur through Tax-Calculator.
    
    For now, a PassThrough object contains 6 different business entities:
        sole proprietorship, positive net income
        sole proprietorship, negative net income
        S corporation, positive net income
        S corporation, negative net income
        partnership, positive net income
        partnership, negative net income
    Therefore, the process for modeling the pass-through sector evolves in 
    two stages. The first, like for the Corporation class, produces a single
    Asset object, Debt object and earnings for the pass-through sector. Once
    these are calculated, they are split between each of the 6 entities. The
    results from these will later be used by the Investor class to distribute
    the changes in business income to individuals in Tax-Calculator. 
    
    The following functions apply to the sector as a whole:
        create_asset()
        create_earnings()
        create_debt()
       real_activity() 
    """
    
    def __init__(self, btax_params):
        # Store policy parameter objects
        if isinstance(btax_params, pd.DataFrame):
            self.btax_params = btax_params
        else:
            raise ValueError('btax_params must be DataFrame')
        # Create Data object
        self.data = Data()
        # Create Asset object and calculate
        self.asset = Asset(self.btax_params, corp=False, data=self.data)
        self.asset.calc_all()
        # Create earnings forecast
        self.create_earnings()
    
    def create_earnings(self):
        """
        Creates the initial forecast for earnings. Static only.
        """
        # Get initial EBITDA for 2014, for those in net income positions
        sp_posinc2014 = np.array(self.data.sp_data['netinc'])[-1]
        part_posinc2014 = np.array(self.data.partner_data['netinc_total'])[-1]
        scorp_posinc2014 = np.array(self.data.Scorp_data['netinc_total'])[-1]
        # Get initial EBITDA for 2014, for those in net loss positions
        sp_neginc2014 = -np.array(self.data.sp_data['netloss'])[-1]
        part_neginc2014 = -np.array(self.data.partner_data['netloss_total'])[-1]
        scorp_neginc2014 = -np.array(self.data.Scorp_data['netloss_total'])[-1]
        # Get growth factor for noncorporate business income and apply it
        gfact_propinc = np.array(self.data.gfactors['propinc_nonfarm'])[1:]
        sp_posinc = sp_posinc2014 / gfact_propinc[0] * gfact_propinc
        part_posinc = part_posinc2014 / gfact_propinc[0] * gfact_propinc
        scorp_posinc = scorp_posinc2014 / gfact_propinc[0] * gfact_propinc
        sp_neginc = sp_neginc2014 / gfact_propinc[0] * gfact_propinc
        part_neginc = part_neginc2014 / gfact_propinc[0] * gfact_propinc
        scorp_neginc = scorp_neginc2014 / gfact_propinc[0] * gfact_propinc
        # Aggregate and save EBITDAs
        total_inc = (sp_posinc + sp_neginc + part_posinc + part_neginc +
                     scorp_posinc + scorp_neginc)
        earnings_result = pd.DataFrame({'year': range(2014,2028),
                                        'total': total_inc,
                                        'SchC_pos': sp_posinc,
                                        'SchC_neg': sp_neginc,
                                        'partner_pos': part_posinc,
                                        'partner_neg': part_neginc,
                                        'Scorp_pos': scorp_posinc, 
                                        'Scorp_neg': scorp_neginc})
        self.earnings = earnings_result
    
    def create_debt(self):
        """
        Creates the Debt object for the pass-through sector. 
        Note: create_asset must have already been called
        """
        self.debt = Debt(self.btax_params, self.asset.get_forecast(),
                         data=self.data, corp=False)
        self.debt.calc_all()
    
    def real_activity(self):
        """
        Produces a DataFrame of the pass-through sector's real activity.
        Real measures are:
            Capital stock
            Investment
            Depreciation (economic)
            Debt
            Interest paid
            Earnings
            Net income
            Cash flow
        Note that unlike for a corporation, the final real activity measures
        (net income and cash flow) are pre-tax, as these would be passed to
        units in Tax-Calculator. 
        """
        real_results = pd.DataFrame({'year': range(2014,2028),
                                     'Earnings': self.earnings['total']})
        real_results['Kstock'] = self.asset.get_forecast()
        real_results['Inv'] = self.asset.get_investment()
        real_results['Depr'] = self.asset.get_truedep()
        real_results['Debt'] = self.debt.get_debt()
        real_results['NIP'] = self.debt.get_nip()
        real_results['NetInc'] = real_results['Earnings'] - real_results['Depr'] - real_results['NIP']
        real_results['CashFlow'] = real_results['Earnings'] - real_results['Inv']
        self.real_results = real_results
    
    def calc_schC(self):
        """
        Calculates net income for sole proprietorships
        """
        SchC_results = pd.DataFrame({'year': range(2014,2028)})
        # Update earnings
        SchC_results['ebitda_pos'] = self.earnings['SchC_pos']
        SchC_results['ebitda_neg'] = self.earnings['SchC_neg']
        # Update tax depreciation
        SchC_results['dep_pos'] = (self.asset.get_taxdep() * self.data.depshare_sp_posinc)
        SchC_results['dep_neg'] = (self.asset.get_taxdep() * self.data.depshare_sp_neginc)
        # Update interest deduction
        SchC_results['intded_pos'] = (self.debt.get_nid() * self.data.intshare_sp_posinc)
        SchC_results['intded_neg'] = (self.debt.get_nid() * self.data.intshare_sp_neginc)
        # Update business net income
        SchC_results['netinc_pos'] = SchC_results['ebitda_pos'] - SchC_results['dep_pos'] - SchC_results['intded_pos']
        SchC_results['netinc_neg'] = SchC_results['ebitda_neg'] - SchC_results['dep_neg'] - SchC_results['intded_neg']
        self.SchC_results = SchC_results
    
    def calc_partner(self):
        """
        Calculates net income for partnerships
        """
        partner_results = pd.DataFrame({'year': range(2014,2028)})
        # Update earnings
        partner_results['ebitda_pos'] = self.earnings['partner_pos']
        partner_results['ebitda_neg'] = self.earnings['partner_neg']
        # Update tax depreciation
        partner_results['dep_pos'] = (self.asset.get_taxdep() * self.data.depshare_partner_posinc)
        partner_results['dep_neg'] = (self.asset.get_taxdep() * self.data.depshare_partner_neginc)
        # Update interest deduction
        partner_results['intded_pos'] = (self.debt.get_nid() * self.data.intshare_partner_posinc)
        partner_results['intded_neg'] = (self.debt.get_nid() * self.data.intshare_partner_neginc)
        # Update business net income
        partner_results['netinc_pos'] = partner_results['ebitda_pos'] - partner_results['dep_pos'] - partner_results['intded_pos']
        partner_results['netinc_neg'] = partner_results['ebitda_neg'] - partner_results['dep_neg'] - partner_results['intded_neg']
        self.partner_results = partner_results
    
    def calc_Scorp(self):
        """
        Calculates net income for S corporations
        """
        Scorp_results = pd.DataFrame({'year': range(2014,2028)})
        # Update earnings
        Scorp_results['ebitda_pos'] = self.earnings['Scorp_pos']
        Scorp_results['ebitda_neg'] = self.earnings['Scorp_neg']
        # Update tax depreciation
        Scorp_results['dep_pos'] = (self.asset.get_taxdep() * self.data.depshare_scorp_posinc)
        Scorp_results['dep_neg'] = (self.asset.get_taxdep() * self.data.depshare_scorp_neginc)
        # Update interest deduction
        Scorp_results['intded_pos'] = (self.debt.get_nid() * self.data.intshare_scorp_posinc)
        Scorp_results['intded_neg'] = (self.debt.get_nid() * self.data.intshare_scorp_neginc)
        # Update business net income
        Scorp_results['netinc_pos'] = Scorp_results['ebitda_pos'] - Scorp_results['dep_pos'] - Scorp_results['intded_pos']
        Scorp_results['netinc_neg'] = Scorp_results['ebitda_neg'] - Scorp_results['dep_neg'] - Scorp_results['intded_neg']
        self.Scorp_results = Scorp_results
    
    def calc_netinc(self):
        """
        Runs all calculations for each entity and saves the net income results.
        """
        self.calc_schC()
        self.calc_partner()
        self.calc_Scorp()
        netinc_results = pd.DataFrame({'year': range(2014,2028)})
        netinc_results['SchC_pos'] = self.SchC_results['netinc_pos']
        netinc_results['SchC_neg'] = self.SchC_results['netinc_neg']
        netinc_results['partner_pos'] = self.SchC_results['netinc_pos']
        netinc_results['partner_neg'] = self.SchC_results['netinc_neg']
        netinc_results['Scorp_pos'] = self.SchC_results['netinc_pos']
        netinc_results['Scorp_neg'] = self.SchC_results['netinc_neg']
        self.netinc_results = netinc_results
    
    def calc_static(self):
        """
        Runs the static calculations
        """
        self.create_debt()
        self.real_activity()
        self.calc_netinc()
    
    def update_legal(self, responses):
        """
        Updates the rescale_corp and rescale_noncorp associated with each
        Data associated with each object.
        """
        self.data.update_rescaling(responses.rescale_corp, responses.rescale_noncorp)
        self.asset.data.update_rescaling(responses.rescale_corp, responses.rescale_noncorp)
    
    def update_investment(self, responses):
        """
        Updates the Asset object to include investment response.
        """
        # First, save the capital stock by asset type and year (for earnings)
        self.old_capital_history = copy.deepcopy(self.asset.capital_history)
        self.asset.update_response(responses.investment_response)
        self.asset.calc_all()
    
    def update_earnings(self, responses):
        """
        Recalculates earnings using the old capital stock by asset type, the
        new capital stock by asset type (based on the investment response),
        and the marginal product of capital.
        """
        Kstock_base = copy.deepcopy(self.old_capital_history)
        Kstock_ref = copy.deepcopy(self.asset.capital_history)
        deltaK = Kstock_ref - Kstock_base
        changeEarnings = np.zeros((96, 14))
        
        for j in range(14): # for each year
            mpk = np.array(responses.investment_response['MPKnc' + str(j + 2014)])
            for i in range(96): # by asset
                changeEarnings[i,j] = deltaK[i,j] * mpk[i] * self.data.adjfactor_dep_noncorp
        deltaE = np.zeros(14)
        for j in range(14):
            deltaE[j] = changeEarnings[:, j].sum()
        earnings_old = np.array(self.earnings['total'])
        ebitda_chgfactor = (earnings_old + deltaE) * self.data.rescale_noncorp / earnings_old
        keylist = list(self.earnings)
        for key in keylist:
            self.earnings[key] = self.earnings[key] * ebitda_chgfactor
    
    def update_debt(self, responses):
        """
        Replaces the Debt object to use the new asset forecast and Data
        """
        pctch_delta = np.array(responses.debt_response['pchDelta_corp'])
        self.debt = Debt(self.btax_params, self.asset.get_forecast(),
                         data=self.data, response=pctch_delta, corp=False)
        self.debt.calc_all()
    
    def apply_responses(self, responses):
        """
        Updates Data, Asset, earnings, and Debt to include 
        responses.
        """
        assert isinstance(responses, Response)
        self.update_legal(responses)
        self.update_investment(responses)
        self.update_earnings(responses)
        self.update_debt(responses)
        self.real_activity()
        self.calc_netinc()
예제 #7
0
class Corporation():
    """
    Constructor for the Corporation class.
    This contains both the real and tax information relevant to the
    corporate income tax.
    """
    def __init__(self, btax_params):
        # Store policy parameter objects
        if isinstance(btax_params, pd.DataFrame):
            self.btax_params = btax_params
        else:
            raise ValueError('btax_params must be DataFrame')
        # Create Data object
        self.data = Data()
        # Create Asset object and calculate
        self.asset = Asset(self.btax_params, corp=True, data=self.data)
        self.asset.calc_all()
        # Create DomesticMNE object
        self.dmne = DomesticMNE(self.btax_params)
        self.dmne.calc_all()
        # Create earnings forecast
        self.create_earnings()

    def create_debt(self):
        """
        Creates the Debt object for the Corporation.
        Note: create_asset must have already been called
        """
        self.debt = Debt(self.btax_params,
                         self.asset.get_forecast(),
                         data=self.data,
                         corp=True)
        self.debt.calc_all()

    def create_earnings(self):
        """
        Creates the initial forecast for earnings. Static only.
        """
        # Grab forecasts of profit growth
        earnings_forecast = np.asarray(self.data.gfactors['profit'])
        gfacts = earnings_forecast[1:] / earnings_forecast[0]
        # 2013 values for non-modeled revenues
        taxitems = np.array(self.data.corp_tax2013['ALL'])
        receipts = taxitems[4] * gfacts
        rent_inc = taxitems[7] * gfacts
        royalties = taxitems[8] * gfacts
        capgains = (taxitems[9] + taxitems[10] + taxitems[11] -
                    taxitems[32]) * gfacts
        domestic_divs = taxitems[12] * gfacts
        other_recs = taxitems[14] * gfacts
        # 2013 values for non-modeled deductions and credits
        cogs = taxitems[16] * gfacts
        execcomp = taxitems[17] * gfacts
        wages = taxitems[18] * gfacts
        repairs = taxitems[19] * gfacts
        baddebt = taxitems[20] * gfacts
        rent_paid = taxitems[21] * gfacts
        statelocaltax = taxitems[22] * gfacts
        charity = taxitems[24] * gfacts
        amortization = taxitems[25] * gfacts
        depletion = taxitems[27] * gfacts
        advertising = taxitems[28] * gfacts
        pensions = taxitems[29] * gfacts
        benefits = taxitems[30] * gfacts
        sec199_base = taxitems[31] * gfacts
        other_ded = taxitems[33] * gfacts
        gbc = taxitems[42] * gfacts
        # Save unodeled tax items
        self.revenues = pd.DataFrame({
            'year': range(START_YEAR, END_YEAR + 1),
            'receipts': receipts,
            'rent': rent_inc,
            'royalties': royalties,
            'capgains': capgains,
            'domestic_divs': domestic_divs,
            'other': other_recs
        })
        self.deductions = pd.DataFrame({
            'year': range(START_YEAR, END_YEAR + 1),
            'cogs': cogs,
            'execcomp': execcomp,
            'wages': wages,
            'repairs': repairs,
            'baddebt': baddebt,
            'rent': rent_paid,
            'statelocaltax': statelocaltax,
            'charity': charity,
            'amortization': amortization,
            'depletion': depletion,
            'advertising': advertising,
            'pensions': pensions,
            'benefits': benefits,
            'sec199share': sec199_base,
            'other': other_ded
        })
        self.credits = pd.DataFrame({
            'year': range(START_YEAR, END_YEAR + 1),
            'gbc': gbc
        })

    def file_taxes(self):
        """
        Creates the CorpTaxReturn object.
        """
        self.taxreturn = CorpTaxReturn(self.btax_params,
                                       self.revenues,
                                       self.deductions,
                                       self.credits,
                                       dmne=self.dmne,
                                       data=self.data,
                                       assets=self.asset,
                                       debts=self.debt)
        self.taxreturn.calc_all()

    def real_activity(self):
        """
        Produces a DataFrame of the corporation's real activity.
        Real measures are:
            Capital stock
            Investment
            Depreciation (economic)
            Net debt
            Net interest paid
            Earnings
            Tax liability
            Net income
            Cash flow
        """

        real_results = pd.DataFrame({'year': range(START_YEAR, END_YEAR + 1)})
        real_results['Earnings'] = (
            self.revenues['receipts'] + self.revenues['rent'] +
            self.revenues['royalties'] + self.revenues['capgains'] +
            self.revenues['domestic_divs'] + self.revenues['other'] -
            self.deductions['cogs'] - self.deductions['execcomp'] -
            self.deductions['wages'] - self.deductions['repairs'] -
            self.deductions['baddebt'] - self.deductions['rent'] -
            self.deductions['charity'] - self.deductions['depletion'] -
            self.deductions['advertising'] - self.deductions['pensions'] -
            self.deductions['benefits'] - self.deductions['other'] +
            self.dmne.dmne_results['foreign_directinc'] +
            self.dmne.dmne_results['foreign_indirectinc'])
        real_results['Kstock'] = self.asset.get_forecast()
        real_results['Inv'] = self.asset.get_investment()
        real_results['Depr'] = self.asset.get_truedep()
        real_results['Debt'] = self.debt.get_debt()
        real_results['NIP'] = self.debt.get_nip()
        real_results['Tax'] = self.taxreturn.get_tax()
        real_results['NetInc'] = (real_results['Earnings'] -
                                  real_results['Depr'] - real_results['NIP'] -
                                  real_results['Tax'] -
                                  self.dmne.dmne_results['foreign_tax'] -
                                  self.deductions['statelocaltax'])
        real_results['CashFlow'] = (real_results['Earnings'] -
                                    real_results['Inv'] - real_results['Tax'] -
                                    self.dmne.dmne_results['foreign_tax'] -
                                    self.deductions['statelocaltax'])
        self.real_results = real_results

    def calc_static(self):
        """
        Runs the static calculations.
        """
        self.create_debt()
        self.file_taxes()
        self.real_activity()

    def update_legal(self, responses):
        """
        Updates the rescale_corp and rescale_noncorp associated with each
        Data associated with each object.
        """
        self.data.update_rescaling(responses.rescale_corp,
                                   responses.rescale_noncorp)
        self.asset.data.update_rescaling(responses.rescale_corp,
                                         responses.rescale_noncorp)

    def update_investment(self, responses):
        """
        Updates the Asset object to include investment response.
        """
        # First, save the capital stock by asset type and year (for earnings)
        self.old_capital_history = copy.deepcopy(self.asset.capital_history)
        self.asset.update_response(responses.investment_response)
        self.asset.calc_all()

    def update_repatriation(self, responses):
        """
        Updates the DomesticMNE object to include the repatriation response.
        Also updates profits to reflect this response.
        """
        # First, save current foreign earnings
        self.dmne.update_profits(responses.repatriation_response,
                                 responses.shifting_response)
        self.dmne.calc_all()

    def update_earnings(self, responses):
        """
        Recalculates earnings using the old capital stock by asset type, the
        new capital stock by asset type (based on the investment response),
        and the marginal product of capital.
        """
        Kstock_base = copy.deepcopy(self.old_capital_history)
        Kstock_ref = copy.deepcopy(self.asset.capital_history)
        changeEarnings = np.zeros((95, NUM_YEARS))
        for iyr in range(NUM_YEARS):  # for each year
            ystr = str(iyr + START_YEAR)
            mpk = np.array(responses.investment_response['MPKc' + ystr])
            for i in range(95):  # by asset
                changeEarnings[i, iyr] = (Kstock_ref[ystr][i] -
                                          Kstock_base[ystr][i]) * mpk[i]
        deltaE = np.zeros(NUM_YEARS)
        for iyr in range(NUM_YEARS):
            deltaE[iyr] = changeEarnings[:, iyr].sum()
        # Update new earnings
        self.revenues['receipts'] = self.revenues['receipts'] + deltaE

    def update_debt(self, responses):
        """
        Replaces the Debt object to use the new asset forecast and Data
        """
        pctch_delta = np.array(responses.debt_response['pchDelta_corp'])
        self.debt = Debt(self.btax_params,
                         self.asset.get_forecast(),
                         data=self.data,
                         response=pctch_delta,
                         corp=True)
        self.debt.calc_all()

    def apply_responses(self, responses):
        """
        Updates Data, Asset, earnings, Debt and CorpTaxReturn to include
        responses. Then calc_all() for each object.
        """
        assert isinstance(responses, Response)
        self.update_legal(responses)
        self.update_investment(responses)
        self.update_repatriation(responses)
        self.update_earnings(responses)
        self.update_debt(responses)
        self.file_taxes()
        self.real_activity()

    def get_netinc(self):
        """
        Returns an array of the corporation's net income (after-tax).
        """
        netinc = np.array(self.real_results['NetInc'])
        return netinc

    def get_taxrev(self):
        """
        Returns an array of the corporation's tax liability.
        """
        taxrev = np.array(self.real_results['Tax'])
        return taxrev