7
7
namespace Services
8
8
{
9
9
/// <summary>
10
- /// A service that is responsible for computing summaries (total profit, cost,...) of orders, portfolio entries and
10
+ /// A service that is responsible for calculating summaries (total profit, cost,...) of orders, portfolio entries and
11
11
/// even portfolios.
12
12
/// </summary>
13
13
public interface ISummaryService
@@ -27,25 +27,45 @@ public interface ISummaryService
27
27
/// </param>
28
28
public record Summary ( decimal AbsoluteChange , decimal RelativeChange , decimal MarketValue , decimal Cost ) ;
29
29
30
+ /// <summary>
31
+ /// Calculates a summary of the given market order
32
+ /// </summary>
33
+ /// <param name="order">Order whose summary should be calculated</param>
34
+ /// <param name="assetPrice">Market price of the asset to be used when calculating the summary</param>
35
+ /// <returns>Summary of the given order</returns>
30
36
public Summary GetMarketOrderSummary ( MarketOrder order , decimal assetPrice ) ;
31
37
38
+ /// <summary>
39
+ /// Calculates a summary of the given list of orders of a portfolio entry
40
+ /// </summary>
41
+ /// <param name="portfolioEntryOrders">List of portfolio entries whose summary is to be calculated</param>
42
+ /// <param name="assetPrice">Market price of the asset to be used when calculating the summary</param>
43
+ /// <returns>A summary of the given list of orders</returns>
32
44
public Summary GetPortfolioEntrySummary ( List < MarketOrder > portfolioEntryOrders , decimal assetPrice ) ;
33
45
46
+ /// <summary>
47
+ /// Calculates a summary of the given portfolio entry summaries
48
+ /// </summary>
49
+ /// <param name="portfolioEntrySummaries">List of entry summaries whos summary is to be calculated</param>
50
+ /// <returns>Summary of the given list of summaries</returns>
34
51
public Summary GetPortfolioSummary ( List < Summary > portfolioEntrySummaries ) ;
35
52
}
36
53
37
54
public class SummaryServiceImpl : ISummaryService
38
55
{
39
56
public ISummaryService . Summary GetMarketOrderSummary ( MarketOrder order , decimal assetPrice )
40
57
{
58
+ // order summary does not take into account whether it was a buy or a sell (same as Blockfolio app)
41
59
var marketValue = order . Size * assetPrice ;
42
60
var cost = ( order . Size * order . FilledPrice ) + order . Fee ;
43
61
if ( cost == 0 )
44
62
{
63
+ // when the cost is zero do not compute anything else
45
64
return new ( 0 , 0 , 0 , 0 ) ;
46
65
}
47
66
48
67
var relativeChange = ( marketValue / cost ) - new decimal ( 1 ) ;
68
+ // absolute change is the difference between the current market value of the order subtracted by order's cost
49
69
var absoluteChange = marketValue - cost ;
50
70
return new ( absoluteChange , relativeChange , marketValue , cost ) ;
51
71
}
@@ -56,7 +76,7 @@ public ISummaryService.Summary GetAverageOfSummaries(IEnumerable<ISummaryService
56
76
decimal totalCost = 0 ;
57
77
decimal totalAbsoluteChange = 0 ;
58
78
59
- // iterate over all orders and compute their summaries
79
+ // iterate over summaries and sum market value, cost and absolute change
60
80
foreach ( var summary in summaries )
61
81
{
62
82
totalMarketValue += summary . MarketValue ;
@@ -66,6 +86,7 @@ public ISummaryService.Summary GetAverageOfSummaries(IEnumerable<ISummaryService
66
86
67
87
if ( totalCost == 0 )
68
88
{
89
+ // when the cost is zero, do not compute anything else
69
90
return new ISummaryService . Summary ( 0 , 0 , 0 , 0 ) ;
70
91
}
71
92
@@ -81,10 +102,14 @@ public ISummaryService.Summary GetPortfolioEntrySummary(List<MarketOrder> portfo
81
102
decimal totalSellValue = 0 ;
82
103
decimal totalCost = 0 ;
83
104
decimal totalFee = 0 ;
105
+
106
+ // compute summary of market orders in the same was as the Blockfolio does
84
107
portfolioEntryOrders
85
108
. ForEach ( order =>
86
109
{
110
+ // determine the holding size (negative when the order was a sell) and add it tot he sum of all holdings
87
111
totalHoldingSize += order . Size * ( order . Buy ? 1 : - 1 ) ;
112
+ // compute the value of the order
88
113
var orderValue = order . Size * order . FilledPrice ;
89
114
if ( ! order . Buy )
90
115
{
@@ -103,15 +128,18 @@ public ISummaryService.Summary GetPortfolioEntrySummary(List<MarketOrder> portfo
103
128
return new ISummaryService . Summary ( 0 , 0 , 0 , 0 ) ;
104
129
}
105
130
131
+ // current total holding value is computing by multiplying the total holding size with the given price of the asset
106
132
decimal currentTotalHoldingValue = totalHoldingSize * assetPrice ;
107
133
134
+ // use the same formula as Blockfolio app does to compute the total absolute change
108
135
decimal totalAbsoluteChange = currentTotalHoldingValue + totalSellValue - totalCost - totalFee ;
109
136
decimal totalRelativeChange = totalAbsoluteChange / ( totalCost + totalFee ) ;
110
137
111
138
return new ISummaryService . Summary ( totalAbsoluteChange , totalRelativeChange , currentTotalHoldingValue ,
112
139
totalCost + totalFee ) ;
113
140
}
114
141
142
+ // summary of a portfolio is calculated by computing the average of all entry summaries present in it
115
143
public ISummaryService . Summary GetPortfolioSummary ( List < ISummaryService . Summary > portfolioEntrySummaries ) =>
116
144
GetAverageOfSummaries ( portfolioEntrySummaries ) ;
117
145
}
0 commit comments