Skip to content

Commit

Permalink
Merge pull request #102 from alexpung/refactorAndFix
Browse files Browse the repository at this point in the history
Fix and refactor JSON serialisation and parser
  • Loading branch information
alexpung authored Oct 16, 2024
2 parents adaec27 + c564c10 commit ab6a24c
Show file tree
Hide file tree
Showing 21 changed files with 61 additions and 50 deletions.
14 changes: 7 additions & 7 deletions BlazorApp-Investment Tax Calculator/Components/AddTrade.razor
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,8 @@
<GridColumn Field=@nameof(TradeInputViewModel.AssetType) HeaderText="Asset Category">
<EditTemplate>
@{
<SfDropDownList DataSource="@AssetCatagoryDropDownEnumValue" @bind-Value="DropDownAssetCatagoryType"
TValue="AssetCatagoryType" TItem="EnumDescriptionPair<AssetCatagoryType>" Placeholder="Asset Category Type">
<SfDropDownList DataSource="@AssetCatagoryDropDownEnumValue" @bind-Value="DropDownAssetCategoryType"
TValue="AssetCategoryType" TItem="EnumDescriptionPair<AssetCategoryType>" Placeholder="Asset Category Type">
<DropDownListFieldSettings Value="EnumValue" Text="Description"></DropDownListFieldSettings>
</SfDropDownList>
}
Expand Down Expand Up @@ -101,14 +101,14 @@
@code {
private SfGrid<TradeInputViewModel> _addTradeGrid = new();
public TradeType DropDownSelectedTradeType { get; set; } = TradeType.ACQUISITION;
public AssetCatagoryType DropDownAssetCatagoryType { get; set; } = AssetCatagoryType.STOCK;
public List<EnumDescriptionPair<AssetCatagoryType>> AssetCatagoryDropDownEnumValue = [];
public AssetCategoryType DropDownAssetCategoryType { get; set; } = AssetCategoryType.STOCK;
public List<EnumDescriptionPair<AssetCategoryType>> AssetCatagoryDropDownEnumValue = [];
public List<EnumDescriptionPair<TradeType>> TradeTypeDropDownEnumValue = [];

protected override void OnInitialized()
{
TradeTypeDropDownEnumValue = EnumExtensions.GetEnumDescriptionPair<TradeType>(typeof(TradeType));
AssetCatagoryDropDownEnumValue = EnumExtensions.GetEnumDescriptionPair<AssetCatagoryType>(typeof(AssetCatagoryType));
AssetCatagoryDropDownEnumValue = EnumExtensions.GetEnumDescriptionPair<AssetCategoryType>(typeof(AssetCategoryType));
}

public void OnImportTrade()
Expand Down Expand Up @@ -136,7 +136,7 @@
if (Args.RequestType == Syncfusion.Blazor.Grids.Action.Save)
{
Args.Data.AcquisitionDisposal = DropDownSelectedTradeType;
Args.Data.AssetType = DropDownAssetCatagoryType;
Args.Data.AssetType = DropDownAssetCategoryType;
var inputData = Args.Data;
foreach(var error in inputData.ValidateError())
{
Expand All @@ -151,7 +151,7 @@
if (Args.RequestType == Syncfusion.Blazor.Grids.Action.BeginEdit)
{
DropDownSelectedTradeType = Args.Data.AcquisitionDisposal;
DropDownAssetCatagoryType = Args.Data.AssetType;
DropDownAssetCategoryType = Args.Data.AssetType;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@


@code {
public AssetCatagoryType SelectedAssetCatagoryType { get; set; } = AssetCatagoryType.STOCK;
public AssetCategoryType SelectedAssetCategoryType { get; set; } = AssetCategoryType.STOCK;
private SfGrid<TradeMatchViewModel> tradeMatchesGrid = default!;
private IEnumerable<TradeMatchViewModel> _tradeMatches => GetTradeMatches();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

namespace InvestmentTaxCalculator.Enumerations;

public enum AssetCatagoryType
public enum AssetCategoryType
{
[Description("Stock")]
STOCK,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
<PackageReference Include="Syncfusion.Blazor.Lists" Version="24.1.41" />
<PackageReference Include="Syncfusion.Blazor.Navigations" Version="24.1.41" />
<PackageReference Include="Syncfusion.Blazor.Themes" Version="24.1.41" />
<PackageReference Include="System.Text.Json" Version="8.0.4" />
<PackageReference Include="System.Text.Json" Version="8.0.5" />
</ItemGroup>

<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,24 @@
namespace InvestmentTaxCalculator.Model;
using InvestmentTaxCalculator.Enumerations;

namespace InvestmentTaxCalculator.Model;
public class AssetTypeToLoadSetting
{
public bool LoadStocks { get; set; } = true;
public bool LoadOptions { get; set; } = true;
public bool LoadFutures { get; set; } = true;
public bool LoadFx { get; set; } = true;
public bool LoadDividends { get; set; } = true;

public TaxEventLists FilterTaxEvent(TaxEventLists taxEventLists)
{
TaxEventLists resultFiltered = new();
if (LoadDividends) resultFiltered.Dividends.AddRange(taxEventLists.Dividends);
if (LoadStocks) resultFiltered.CorporateActions.AddRange(taxEventLists.CorporateActions);
if (LoadStocks) resultFiltered.Trades.AddRange(taxEventLists.Trades.Where(trade => trade.AssetType == AssetCategoryType.STOCK));
if (LoadFutures) resultFiltered.FutureContractTrades.AddRange(taxEventLists.FutureContractTrades);
if (LoadFx) resultFiltered.Trades.AddRange(taxEventLists.Trades.Where(trade => trade.AssetType == AssetCategoryType.FX));
if (LoadOptions) resultFiltered.OptionTrades.AddRange(taxEventLists.OptionTrades);
if (LoadOptions) resultFiltered.CashSettlements.AddRange(taxEventLists.CashSettlements);
return resultFiltered;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ namespace InvestmentTaxCalculator.Model.Interfaces;
public interface ITradeTaxCalculation : ITextFilePrintable, ITaxMatchable
{
int Id { get; }
AssetCatagoryType AssetCatagoryType { get; }
AssetCategoryType AssetCategoryType { get; }
bool CalculationCompleted { get; }
List<TradeMatch> MatchHistory { get; init; }
WrappedMoney TotalCostOrProceed { get; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ namespace InvestmentTaxCalculator.Model.TaxEvents;
public record FutureContractTrade : Trade, ISplittableToLongAndShort<FutureContractTrade>
{
public required DescribedMoney ContractValue { get; set; }
public override AssetCatagoryType AssetType { get; set; } = AssetCatagoryType.FUTURE;
public override AssetCategoryType AssetType { get; set; } = AssetCategoryType.FUTURE;
public PositionType PositionType { get; set; }

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ namespace InvestmentTaxCalculator.Model.TaxEvents;

public record FxTrade : Trade
{
public override AssetCatagoryType AssetType { get; set; } = AssetCatagoryType.FX;
public override AssetCategoryType AssetType { get; set; } = AssetCategoryType.FX;
public override string PrintToTextFile()
{
string action = AcquisitionDisposal switch
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ namespace InvestmentTaxCalculator.Model.TaxEvents;

public record OptionTrade : Trade
{
public override AssetCatagoryType AssetType { get; set; } = AssetCatagoryType.OPTION;
public override AssetCategoryType AssetType { get; set; } = AssetCategoryType.OPTION;
public required string Underlying { get; set; }
public required WrappedMoney StrikePrice { get; set; }
public required DateTime ExpiryDate { get; set; }
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
using InvestmentTaxCalculator.Model.Interfaces;

using System.Text.Json.Serialization;

namespace InvestmentTaxCalculator.Model.TaxEvents;

[JsonPolymorphic()]
public abstract record TaxEvent : IAssetDatedEvent
{
private static int _nextId = 0;
Expand Down
7 changes: 2 additions & 5 deletions BlazorApp-Investment Tax Calculator/Model/TaxEvents/Trade.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,12 @@

using System.Collections.Immutable;
using System.Text;
using System.Text.Json.Serialization;

namespace InvestmentTaxCalculator.Model.TaxEvents;
[JsonDerivedType(typeof(Trade), "trade")]
[JsonDerivedType(typeof(FxTrade), "fxTrade")]
[JsonDerivedType(typeof(FutureContractTrade), "futureContractTrade")]

public record Trade : TaxEvent, ITextFilePrintable
{
public virtual AssetCatagoryType AssetType { get; set; } = AssetCatagoryType.STOCK;
public virtual AssetCategoryType AssetType { get; set; } = AssetCategoryType.STOCK;
public virtual required TradeType AcquisitionDisposal { get; set; }
private decimal _quantity;
/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@ private bool IsTradeInSelectedTaxYear(IEnumerable<int> selectedYears, ITradeTaxC
private static bool FilterAssetType(ITradeTaxCalculation trade, AssetGroupType assetGroupType) => assetGroupType switch
{
AssetGroupType.ALL => true,
AssetGroupType.LISTEDSHARES => trade.AssetCatagoryType is AssetCatagoryType.STOCK,
AssetGroupType.OTHERASSETS => trade.AssetCatagoryType is AssetCatagoryType.FUTURE or AssetCatagoryType.FX,
AssetGroupType.LISTEDSHARES => trade.AssetCategoryType is AssetCategoryType.STOCK,
AssetGroupType.OTHERASSETS => trade.AssetCategoryType is AssetCategoryType.FUTURE or AssetCategoryType.FX,
_ => throw new NotImplementedException()
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ public class TradeTaxCalculation : ITradeTaxCalculation
public virtual TradeType AcquisitionDisposal { get; init; }
public bool CalculationCompleted => UnmatchedQty == 0;
public DateTime Date => TradeList[0].Date;
public AssetCatagoryType AssetCatagoryType => TradeList[0].AssetType;
public AssetCategoryType AssetCategoryType => TradeList[0].AssetType;
public string AssetName => TradeList[0].AssetName;


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,10 @@ public List<ITradeTaxCalculation> CalculateTax()
private static List<TradeTaxCalculation> GroupTrade(IEnumerable<Trade> trades)
{
var groupedTrade = from trade in trades
where trade.AssetType == AssetCatagoryType.STOCK
where trade.AssetType == AssetCategoryType.STOCK
group trade by new { trade.AssetName, trade.Date.Date, trade.AcquisitionDisposal };
var groupedFxTrade = from trade in trades
where trade.AssetType == AssetCatagoryType.FX
where trade.AssetType == AssetCategoryType.FX
group trade by new { trade.AssetName, trade.Date.Date, trade.AcquisitionDisposal };
IEnumerable<TradeTaxCalculation> groupedTradeCalculations = groupedTrade.Select(group => new TradeTaxCalculation(group)).ToList();
IEnumerable<TradeTaxCalculation> groupedFxTradeCalculations = groupedFxTrade.Select(group => new FxTradeTaxCalculation(group)).ToList();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,14 @@ public TaxEventLists ParseFile(string data)
{
if (xml is not null)
{
if (assetTypeToLoadSetting.LoadDividends) result.Dividends.AddRange(IBXmlDividendParser.ParseXml(xml));
if (assetTypeToLoadSetting.LoadStocks) result.CorporateActions.AddRange(IBXmlStockSplitParser.ParseXml(xml));
if (assetTypeToLoadSetting.LoadStocks) result.Trades.AddRange(IBXmlStockTradeParser.ParseXml(xml));
if (assetTypeToLoadSetting.LoadFutures) result.FutureContractTrades.AddRange(IBXmlFutureTradeParser.ParseXml(xml));
if (assetTypeToLoadSetting.LoadFx) result.Trades.AddRange(_xmlFxParser.ParseXml(xml));
if (assetTypeToLoadSetting.LoadOptions) result.OptionTrades.AddRange(IBXmlOptionTradeParser.ParseXml(xml));
if (assetTypeToLoadSetting.LoadOptions) result.CashSettlements.AddRange(IBXmlCashSettlementParser.ParseXml(xml));
result.Dividends.AddRange(IBXmlDividendParser.ParseXml(xml));
result.CorporateActions.AddRange(IBXmlStockSplitParser.ParseXml(xml));
result.Trades.AddRange(IBXmlStockTradeParser.ParseXml(xml));
result.FutureContractTrades.AddRange(IBXmlFutureTradeParser.ParseXml(xml));
result.Trades.AddRange(_xmlFxParser.ParseXml(xml));
result.OptionTrades.AddRange(IBXmlOptionTradeParser.ParseXml(xml));
result.CashSettlements.AddRange(IBXmlCashSettlementParser.ParseXml(xml));
result = assetTypeToLoadSetting.FilterTaxEvent(result);
}
}
catch (ParseException ex)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public static IList<FutureContractTrade> ParseXml(XElement document)
{
return new FutureContractTrade
{
AssetType = AssetCatagoryType.FUTURE,
AssetType = AssetCategoryType.FUTURE,
AcquisitionDisposal = element.GetTradeType(),
AssetName = element.GetAttribute("symbol"),
Description = element.GetAttribute("description"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ private void CacheExchangeRates(XElement document)
{
AcquisitionDisposal = GetTradeType(element),
AssetName = currency,
AssetType = AssetCatagoryType.FX,
AssetType = AssetCategoryType.FX,
Description = element.GetAttribute("activityDescription"),
Date = reportDate,
Quantity = amountOfFx,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using InvestmentTaxCalculator.Enumerations;
using InvestmentTaxCalculator.Model;
using InvestmentTaxCalculator.Model;
using InvestmentTaxCalculator.Parser;

using System.Text.Json;
Expand All @@ -22,11 +21,7 @@ public TaxEventLists ParseFile(string data)
}
TaxEventLists resultFiltered = new();
if (result == null) return resultFiltered;
if (assetTypeToLoadSetting.LoadDividends) resultFiltered.Dividends.AddRange(result.Dividends);
if (assetTypeToLoadSetting.LoadStocks) resultFiltered.CorporateActions.AddRange(result.CorporateActions);
if (assetTypeToLoadSetting.LoadStocks) resultFiltered.Trades.AddRange(result.Trades.Where(trade => trade.AssetType == AssetCatagoryType.STOCK));
if (assetTypeToLoadSetting.LoadFutures) resultFiltered.Trades.AddRange(result.Trades.Where(trade => trade.AssetType == AssetCatagoryType.FUTURE));
if (assetTypeToLoadSetting.LoadFx) resultFiltered.Trades.AddRange(result.Trades.Where(trade => trade.AssetType == AssetCatagoryType.FX));
resultFiltered = assetTypeToLoadSetting.FilterTaxEvent(result);
return resultFiltered;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public class TradeInputViewModel
[Required(ErrorMessage = "Please select if you are buying (acquiring) or selling/shorting (disposing) the asset.")]
public required TradeType AcquisitionDisposal { get; set; }
[Required(ErrorMessage = "Please select the asset type.")]
public required AssetCatagoryType AssetType { get; set; }
public required AssetCategoryType AssetType { get; set; }
[Required(ErrorMessage = "Please enter the currency used for the trade")]
[CustomValidationCurrencyString]
public required string GrossProceedCurrency { get; set; } = "gbp";
Expand Down Expand Up @@ -61,7 +61,7 @@ public Trade Convert()
if (CommissionAmount != 0) expenses.Add(new(CommissionAmount, CommissionCurrency, CommissionExchangeRate));
return AssetType switch
{
AssetCatagoryType.STOCK => new Trade()
AssetCategoryType.STOCK => new Trade()
{
AssetName = AssetName,
Date = Date,
Expand All @@ -72,7 +72,7 @@ public Trade Convert()
Expenses = [.. expenses],
Description = Description
},
AssetCatagoryType.FX => new FxTrade()
AssetCategoryType.FX => new FxTrade()
{
AssetName = AssetName,
Date = Date,
Expand All @@ -83,7 +83,7 @@ public Trade Convert()
Expenses = [.. expenses],
Description = Description
},
AssetCatagoryType.FUTURE => new FutureContractTrade()
AssetCategoryType.FUTURE => new FutureContractTrade()
{
AssetName = AssetName,
Date = Date,
Expand All @@ -102,11 +102,11 @@ public Trade Convert()
public List<string> ValidateError()
{
List<string> errorList = [];
if (AssetType == AssetCatagoryType.FUTURE && string.IsNullOrEmpty(ContractValueCurrency))
if (AssetType == AssetCategoryType.FUTURE && string.IsNullOrEmpty(ContractValueCurrency))
{
errorList.Add("Contract Value Currency is required for a future contract");
}
if (AssetType == AssetCatagoryType.FUTURE && GrossProceed != 0)
if (AssetType == AssetCategoryType.FUTURE && GrossProceed != 0)
{
errorList.Add("For a future contract gross proceeds should be set to 0. Profit and loss is calucated from contract values, taxes and commission");
}
Expand All @@ -118,7 +118,7 @@ public List<string> ValidateWarning()
List<string> errorList = [];
if (CommissionAmount < 0) errorList.Add("Commission is negative and means a rebate, please check if this is correct.");
if (TaxAmount < 0) errorList.Add("Tax is negative and means a refund, please check if this is correct.");
if (ContractValueAmount <= 0 && AssetType == AssetCatagoryType.FUTURE) errorList.Add("Negative or zero price in future contract, please check if this is correct.");
if (ContractValueAmount <= 0 && AssetType == AssetCategoryType.FUTURE) errorList.Add("Negative or zero price in future contract, please check if this is correct.");
return errorList;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ namespace InvestmentTaxCalculator.ViewModel;
public record TradeMatchViewModel(TradeMatch TradeMatch)
{
public DateOnly DisposalDate { get; } = TradeMatch.Date;
public string? AssetType { get; } = TradeMatch.MatchedSellTrade?.AssetCatagoryType.GetDescription() ?? TradeMatch.MatchedBuyTrade?.AssetCatagoryType.GetDescription();
public string? AssetType { get; } = TradeMatch.MatchedSellTrade?.AssetCategoryType.GetDescription() ?? TradeMatch.MatchedBuyTrade?.AssetCategoryType.GetDescription();
public string AssetName { get; } = TradeMatch.AssetName;
public string MatchType { get; } = TradeMatch.TradeMatchType.GetDescription();
public int? AcquisitionTradeId { get; } = TradeMatch.MatchedBuyTrade?.Id;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ namespace InvestmentTaxCalculator.ViewModel;
public class TradeTaxCalculationViewModel(ITradeTaxCalculation TradeTaxCalculation)
{
public int TradeId { get; } = TradeTaxCalculation.Id;
public string AssetType { get; } = TradeTaxCalculation.AssetCatagoryType.GetDescription();
public string AssetType { get; } = TradeTaxCalculation.AssetCategoryType.GetDescription();
public DateTime Date { get; } = TradeTaxCalculation.Date;
public string AssetName { get; } = TradeTaxCalculation.AssetName;
public string AcquisitionOrDisposal { get; } = TradeTaxCalculation.AcquisitionDisposal.GetDescription();
Expand Down

0 comments on commit ab6a24c

Please sign in to comment.