from datetime import datetime, timedelta
from collections import deque
class MutualFundCGTCalculator:
def __init__(self):
self.pool_units = 0 # Total units in the pool
self.pool_cost = 0.0 # Total cost in the pool
self.same_day_buys = deque() # Track same-day buys
self.thirty_day_buys = deque() # Track 30-day buys
self.total_gains = 0.0
self.total_losses = 0.0
self.total_distributions = 0.0 # Distributions paid out
self.annual_exempt_amount = 3000 # CGT allowance (2024-25)
self.transactions = [] # To log all transactions
def add_buy(self, date, units, price_per_unit, fees=0.0):
total_cost = (units * price_per_unit) + fees
self.pool_units += units
self.pool_cost += total_cost
self.transactions.append((date, "BUY", units, price_per_unit, fees, total_cost))
self.same_day_buys.append((date, units, price_per_unit))
self.thirty_day_buys.append((date, units, price_per_unit))
def add_distribution(self, date, amount, reinvested=False):
"""
Handles distributions such as dividends or interest.
- If reinvested → Increases cost basis.
- If paid out → Taxable as income but not part of CGT.
"""
if reinvested:
self.pool_cost += amount # Increase cost basis
print(f"Reinvested distribution of £{amount:.2f} added to cost basis.")
else:
self.total_distributions += amount # Separate taxable income
print(f"Cash distribution of £{amount:.2f} received (taxable as income).")
def add_sell(self, date, units, price_per_unit, fees=0.0):
proceeds = (units * price_per_unit) - fees
cost = self.match_units(date, units)
gain_or_loss = proceeds - cost
if gain_or_loss >= 0:
self.total_gains += gain_or_loss
else:
self.total_losses += abs(gain_or_loss)
self.transactions.append((date, "SELL", units, price_per_unit, fees, proceeds, cost, gain_or_loss))
return gain_or_loss
def match_units(self, sell_date, units):
cost = 0.0
# Step 1: Same-Day Rule
while units > 0 and self.same_day_buys:
buy_date, buy_units, buy_price = self.same_day_buys[0]
if buy_date == sell_date:
matched_units = min(units, buy_units)
cost += matched_units * buy_price
units -= matched_units
if matched_units == buy_units:
self.same_day_buys.popleft()
else:
self.same_day_buys[0] = (buy_date, buy_units - matched_units, buy_price)
else:
break
# Step 2: 30-Day Rule
while units > 0 and self.thirty_day_buys:
buy_date, buy_units, buy_price = self.thirty_day_buys[0]
days_difference = (sell_date - buy_date).days
if 0 < days_difference <= 30:
matched_units = min(units, buy_units)
cost += matched_units * buy_price
units -= matched_units
if matched_units == buy_units:
self.thirty_day_buys.popleft()
else:
self.thirty_day_buys[0] = (buy_date, buy_units - matched_units, buy_price)
else:
break
# Step 3: Section 104 Pool
if units > 0:
average_cost_per_unit = self.pool_cost / self.pool_units
cost += units * average_cost_per_unit
self.pool_units -= units
self.pool_cost -= units * average_cost_per_unit
return cost
def calculate_taxable_gain(self):
taxable_gain = max(0, self.total_gains - self.annual_exempt_amount)
return taxable_gain
def print_summary(self):
print("\nTransaction Summary:")
for t in self.transactions:
print(t)
print(f"\nTotal Gains: £{self.total_gains:.2f}")
print(f"Total Losses: £{self.total_losses:.2f}")
print(f"Total Distributions (Taxable as income): £{self.total_distributions:.2f}")
taxable_gain = self.calculate_taxable_gain()
print(f"Taxable Gains (after allowance): £{taxable_gain:.2f}")
def reset(self):
self.pool_units = 0
self.pool_cost = 0.0
self.same_day_buys.clear()
self.thirty_day_buys.clear()
self.total_gains = 0.0
self.total_losses = 0.0
self.total_distributions = 0.0
self.transactions = []
# Example Usage
if __name__ == "__main__":
calculator = MutualFundCGTCalculator()
# Example buys
calculator.add_buy(datetime(2025, 1, 1), 1000, 10) # Buy 1000 units @ £10
calculator.add_buy(datetime(2025, 1, 10), 500, 12, fees=10) # Buy 500 units @ £12 + £10 fees
calculator.add_buy(datetime(2025, 2, 15), 1000, 11) # Buy 1000 units @ £11
# Distributions
calculator.add_distribution(datetime(2025, 3, 1), 300, reinvested=True) # Reinvested distribution
calculator.add_distribution(datetime(2025, 6, 1), 200, reinvested=False) # Cash distribution
# Example sells
gain1 = calculator.add_sell(datetime(2025, 4, 1), 1200, 15) # Sell 1200 units @ £15
print(f"Capital Gain for first sale: £{gain1:.2f}")
gain2 = calculator.add_sell(datetime(2025, 4, 20), 800, 16, fees=15) # Sell 800 units @ £16 + £15 fees
print(f"Capital Gain for second sale: £{gain2:.2f}")
# Print summary
calculator.print_summary()
No comments:
Post a Comment