Changeset View
Changeset View
Standalone View
Standalone View
kmymoney/plugins/views/reports/core/querytable.cpp
Show All 10 Lines | |||||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | * GNU General Public License for more details. | 13 | * GNU General Public License for more details. | ||
14 | * | 14 | * | ||
15 | * You should have received a copy of the GNU General Public License | 15 | * You should have received a copy of the GNU General Public License | ||
16 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | 16 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
17 | */ | 17 | */ | ||
18 | 18 | | |||
19 | /*************************************************************************** | | |||
20 | * * | | |||
21 | * This program is free software; you can redistribute it and/or modify * | | |||
22 | * it under the terms of the GNU General Public License as published by * | | |||
23 | * the Free Software Foundation; either version 2 of the License, or * | | |||
24 | * (at your option) any later version. * | | |||
25 | * * | | |||
26 | ***************************************************************************/ | | |||
27 | | ||||
28 | /**************************************************************************** | | |||
29 | Contains code from the func_xirr and related methods of financial.cpp | | |||
30 | - KOffice 1.6 by Sascha Pfau. Sascha agreed to relicense those methods under | | |||
31 | GPLv2 or later. | | |||
32 | *****************************************************************************/ | | |||
33 | | ||||
34 | #include "querytable.h" | 19 | #include "querytable.h" | ||
35 | 20 | | |||
36 | #include <cmath> | 21 | #include <cmath> | ||
37 | 22 | | |||
38 | // ---------------------------------------------------------------------------- | 23 | // ---------------------------------------------------------------------------- | ||
39 | // QT Includes | 24 | // QT Includes | ||
40 | 25 | | |||
41 | #include <QList> | 26 | #include <QList> | ||
42 | #include <QDebug> | 27 | #include <QDebug> | ||
43 | 28 | | |||
44 | // ---------------------------------------------------------------------------- | 29 | // ---------------------------------------------------------------------------- | ||
45 | // KDE Includes | 30 | // KDE Includes | ||
46 | 31 | | |||
47 | #include <KLocalizedString> | 32 | #include <KLocalizedString> | ||
48 | 33 | | |||
49 | // ---------------------------------------------------------------------------- | 34 | // ---------------------------------------------------------------------------- | ||
50 | // Project Includes | 35 | // Project Includes | ||
51 | 36 | | |||
37 | #include "cashflowlist.h" | ||||
52 | #include "mymoneyfile.h" | 38 | #include "mymoneyfile.h" | ||
53 | #include "mymoneyaccount.h" | 39 | #include "mymoneyaccount.h" | ||
54 | #include "mymoneysecurity.h" | 40 | #include "mymoneysecurity.h" | ||
55 | #include "mymoneyinstitution.h" | 41 | #include "mymoneyinstitution.h" | ||
56 | #include "mymoneyprice.h" | 42 | #include "mymoneyprice.h" | ||
57 | #include "mymoneypayee.h" | 43 | #include "mymoneypayee.h" | ||
58 | #include "mymoneytag.h" | 44 | #include "mymoneytag.h" | ||
59 | #include "mymoneysplit.h" | 45 | #include "mymoneysplit.h" | ||
60 | #include "mymoneytransaction.h" | 46 | #include "mymoneytransaction.h" | ||
61 | #include "mymoneyreport.h" | 47 | #include "mymoneyreport.h" | ||
62 | #include "mymoneyexception.h" | 48 | #include "mymoneyexception.h" | ||
63 | #include "kmymoneyutils.h" | 49 | #include "kmymoneyutils.h" | ||
64 | #include "reportaccount.h" | 50 | #include "reportaccount.h" | ||
65 | #include "mymoneyenums.h" | 51 | #include "mymoneyenums.h" | ||
66 | 52 | | |||
67 | namespace reports | 53 | namespace reports | ||
68 | { | 54 | { | ||
69 | 55 | | |||
70 | // **************************************************************************** | 56 | // **************************************************************************** | ||
71 | // | 57 | // | ||
72 | // CashFlowListItem implementation | | |||
73 | // | | |||
74 | // Cash flow analysis tools for investment reports | | |||
75 | // | | |||
76 | // **************************************************************************** | | |||
77 | | ||||
78 | QDate CashFlowListItem::m_sToday = QDate::currentDate(); | | |||
79 | | ||||
80 | MyMoneyMoney CashFlowListItem::NPV(double _rate) const | | |||
81 | { | | |||
82 | double T = static_cast<double>(m_sToday.daysTo(m_date)) / 365.0; | | |||
83 | MyMoneyMoney result(m_value.toDouble() / pow(1 + _rate, T), 100); | | |||
84 | | ||||
85 | //qDebug() << "CashFlowListItem::NPV( " << _rate << " ) == " << result; | | |||
86 | | ||||
87 | return result; | | |||
88 | } | | |||
89 | | ||||
90 | // **************************************************************************** | | |||
91 | // | | |||
92 | // CashFlowList implementation | | |||
93 | // | | |||
94 | // Cash flow analysis tools for investment reports | | |||
95 | // | | |||
96 | // **************************************************************************** | | |||
97 | | ||||
98 | CashFlowListItem CashFlowList::mostRecent() const | | |||
99 | { | | |||
100 | CashFlowList dupe(*this); | | |||
101 | | ||||
102 | qSort(dupe); | | |||
103 | | ||||
104 | //qDebug() << " CashFlowList::mostRecent() == " << dupe.back().date().toString(Qt::ISODate); | | |||
105 | | ||||
106 | return dupe.back(); | | |||
107 | } | | |||
108 | | ||||
109 | MyMoneyMoney CashFlowList::NPV(double _rate) const | | |||
110 | { | | |||
111 | MyMoneyMoney result; | | |||
112 | | ||||
113 | const_iterator it_cash = constBegin(); | | |||
114 | while (it_cash != constEnd()) { | | |||
115 | result += (*it_cash).NPV(_rate); | | |||
116 | ++it_cash; | | |||
117 | } | | |||
118 | | ||||
119 | //qDebug() << "CashFlowList::NPV( " << _rate << " ) == " << result << "------------------------" << endl; | | |||
120 | | ||||
121 | return result; | | |||
122 | } | | |||
123 | | ||||
124 | double CashFlowList::calculateXIRR() const | | |||
125 | { | | |||
126 | double resultRate = 0.00001; | | |||
127 | | ||||
128 | double resultZero = 0.00000; | | |||
129 | //if ( args.count() > 2 ) | | |||
130 | // resultRate = calc->conv()->asFloat ( args[2] ).asFloat(); | | |||
131 | | ||||
132 | // check pairs and count >= 2 and guess > -1.0 | | |||
133 | //if ( args[0].count() != args[1].count() || args[1].count() < 2 || resultRate <= -1.0 ) | | |||
134 | // return Value::errorVALUE(); | | |||
135 | | ||||
136 | // define max epsilon | | |||
137 | static const double maxEpsilon = 1e-5; | | |||
138 | | ||||
139 | // max number of iterations | | |||
140 | static const int maxIter = 50; | | |||
141 | | ||||
142 | // Newton's method - try to find a res, with a accuracy of maxEpsilon | | |||
143 | double rateEpsilon, newRate, resultValue; | | |||
144 | int i = 0; | | |||
145 | bool contLoop; | | |||
146 | | ||||
147 | do { | | |||
148 | resultValue = xirrResult(resultRate); | | |||
149 | | ||||
150 | double resultDerive = xirrResultDerive(resultRate); | | |||
151 | | ||||
152 | //check what happens if xirrResultDerive is zero | | |||
153 | //Don't know if it is correct to dismiss the result | | |||
154 | if (resultDerive != 0) { | | |||
155 | newRate = resultRate - resultValue / resultDerive; | | |||
156 | } else { | | |||
157 | | ||||
158 | newRate = resultRate - resultValue; | | |||
159 | } | | |||
160 | | ||||
161 | rateEpsilon = fabs(newRate - resultRate); | | |||
162 | | ||||
163 | resultRate = newRate; | | |||
164 | contLoop = (rateEpsilon > maxEpsilon) && (fabs(resultValue) > maxEpsilon); | | |||
165 | } while (contLoop && (++i < maxIter)); | | |||
166 | | ||||
167 | if (contLoop) | | |||
168 | return resultZero; | | |||
169 | | ||||
170 | return resultRate; | | |||
171 | } | | |||
172 | | ||||
173 | double CashFlowList::xirrResult(double& rate) const | | |||
174 | { | | |||
175 | double r = rate + 1.0; | | |||
176 | double res = 0.00000;//back().value().toDouble(); | | |||
177 | | ||||
178 | QList<CashFlowListItem>::const_iterator list_it = constBegin(); | | |||
179 | while (list_it != constEnd()) { | | |||
180 | double e_i = ((* list_it).today().daysTo((* list_it).date())) / 365.0; | | |||
181 | MyMoneyMoney val = (* list_it).value(); | | |||
182 | | ||||
183 | if (e_i < 0) { | | |||
184 | res += val.toDouble() * pow(r, -e_i); | | |||
185 | } else { | | |||
186 | res += val.toDouble() / pow(r, e_i); | | |||
187 | } | | |||
188 | ++list_it; | | |||
189 | } | | |||
190 | | ||||
191 | return res; | | |||
192 | } | | |||
193 | | ||||
194 | | ||||
195 | double CashFlowList::xirrResultDerive(double& rate) const | | |||
196 | { | | |||
197 | double r = rate + 1.0; | | |||
198 | double res = 0.00000; | | |||
199 | | ||||
200 | QList<CashFlowListItem>::const_iterator list_it = constBegin(); | | |||
201 | while (list_it != constEnd()) { | | |||
202 | double e_i = ((* list_it).today().daysTo((* list_it).date())) / 365.0; | | |||
203 | MyMoneyMoney val = (* list_it).value(); | | |||
204 | | ||||
205 | res -= e_i * val.toDouble() / pow(r, e_i + 1.0); | | |||
206 | ++list_it; | | |||
207 | } | | |||
208 | | ||||
209 | return res; | | |||
210 | } | | |||
211 | | ||||
212 | double CashFlowList::IRR() const | | |||
213 | { | | |||
214 | double result = 0.0; | | |||
215 | | ||||
216 | // set 'today', which is the most recent of all dates in the list | | |||
217 | CashFlowListItem::setToday(mostRecent().date()); | | |||
218 | | ||||
219 | result = calculateXIRR(); | | |||
220 | return result; | | |||
221 | } | | |||
222 | | ||||
223 | MyMoneyMoney CashFlowList::total() const | | |||
224 | { | | |||
225 | MyMoneyMoney result; | | |||
226 | | ||||
227 | const_iterator it_cash = constBegin(); | | |||
228 | while (it_cash != constEnd()) { | | |||
229 | result += (*it_cash).value(); | | |||
230 | ++it_cash; | | |||
231 | } | | |||
232 | | ||||
233 | return result; | | |||
234 | } | | |||
235 | | ||||
236 | void CashFlowList::dumpDebug() const | | |||
237 | { | | |||
238 | const_iterator it_item = constBegin(); | | |||
239 | while (it_item != constEnd()) { | | |||
240 | qDebug() << (*it_item).date().toString(Qt::ISODate) << " " << (*it_item).value().toString(); | | |||
241 | ++it_item; | | |||
242 | } | | |||
243 | } | | |||
244 | | ||||
245 | // **************************************************************************** | | |||
246 | // | | |||
247 | // QueryTable implementation | 58 | // QueryTable implementation | ||
248 | // | 59 | // | ||
249 | // **************************************************************************** | 60 | // **************************************************************************** | ||
250 | 61 | | |||
251 | /** | 62 | /** | ||
252 | * TODO | 63 | * TODO | ||
253 | * | 64 | * | ||
254 | * - Collapse 2- & 3- groups when they are identical | 65 | * - Collapse 2- & 3- groups when they are identical | ||
▲ Show 20 Lines • Show All 994 Lines • ▼ Show 20 Line(s) | 1058 | { | |||
1249 | if (!buys.isZero() || !startingBal.isZero()) { | 1060 | if (!buys.isZero() || !startingBal.isZero()) { | ||
1250 | returnInvestment = (sells + buys + cashIncome + endingBal - startingBal) / (startingBal - buys); | 1061 | returnInvestment = (sells + buys + cashIncome + endingBal - startingBal) / (startingBal - buys); | ||
1251 | returnInvestment = returnInvestment.convert(10000); | 1062 | returnInvestment = returnInvestment.convert(10000); | ||
1252 | } else | 1063 | } else | ||
1253 | returnInvestment = MyMoneyMoney(); // if no investment then no return on investment | 1064 | returnInvestment = MyMoneyMoney(); // if no investment then no return on investment | ||
1254 | return returnInvestment; | 1065 | return returnInvestment; | ||
1255 | } | 1066 | } | ||
1256 | 1067 | | |||
1257 | MyMoneyMoney QueryTable::helperIRR(const CashFlowList &all) const | 1068 | QString QueryTable::helperIRR(const CashFlowList &all) const | ||
1258 | { | 1069 | { | ||
1259 | MyMoneyMoney annualReturn; | | |||
1260 | try { | 1070 | try { | ||
1261 | double irr = all.IRR(); | 1071 | return MyMoneyMoney(all.XIRR(), 10000).toString(); | ||
1262 | #ifdef Q_CC_MSVC | 1072 | } catch (MyMoneyException &e) { | ||
1263 | annualReturn = MyMoneyMoney(_isnan(irr) ? 0 : irr, 10000); | 1073 | qDebug() << e.what(); | ||
1264 | #else | 1074 | all.dumpDebug(); | ||
1265 | annualReturn = MyMoneyMoney(std::isnan(irr) ? 0 : irr, 10000); | 1075 | return QString(); | ||
1266 | #endif | | |||
1267 | } catch (QString e) { | | |||
1268 | qDebug() << e; | | |||
1269 | } | 1076 | } | ||
1270 | return annualReturn; | | |||
1271 | } | 1077 | } | ||
1272 | 1078 | | |||
1273 | void QueryTable::sumInvestmentValues(const ReportAccount& account, QList<CashFlowList>& cfList, QList<MyMoneyMoney>& shList) const | 1079 | void QueryTable::sumInvestmentValues(const ReportAccount& account, QList<CashFlowList>& cfList, QList<MyMoneyMoney>& shList) const | ||
1274 | { | 1080 | { | ||
1275 | for (int i = InvestmentValue::Buys; i < InvestmentValue::End; ++i) | 1081 | for (int i = InvestmentValue::Buys; i < InvestmentValue::End; ++i) | ||
1276 | cfList.append(CashFlowList()); | 1082 | cfList.append(CashFlowList()); | ||
1277 | for (int i = InvestmentValue::Buys; i <= InvestmentValue::BuysOfOwned; ++i) | 1083 | for (int i = InvestmentValue::Buys; i <= InvestmentValue::BuysOfOwned; ++i) | ||
1278 | shList.append(MyMoneyMoney()); | 1084 | shList.append(MyMoneyMoney()); | ||
▲ Show 20 Lines • Show All 315 Lines • ▼ Show 20 Line(s) | 1383 | default: | |||
1594 | result[ctCashIncome] = cashIncomeTotal.toString(); | 1400 | result[ctCashIncome] = cashIncomeTotal.toString(); | ||
1595 | result[ctReinvestIncome] = reinvestIncomeTotal.toString(); | 1401 | result[ctReinvestIncome] = reinvestIncomeTotal.toString(); | ||
1596 | result[ctStartingBalance] = startingBal.toString(); | 1402 | result[ctStartingBalance] = startingBal.toString(); | ||
1597 | result[ctEndingBalance] = endingBal.toString(); | 1403 | result[ctEndingBalance] = endingBal.toString(); | ||
1598 | break; | 1404 | break; | ||
1599 | } | 1405 | } | ||
1600 | 1406 | | |||
1601 | MyMoneyMoney returnInvestment = helperROI(buysTotal - reinvestIncomeTotal, sellsTotal, startingBal, endingBal, cashIncomeTotal); | 1407 | MyMoneyMoney returnInvestment = helperROI(buysTotal - reinvestIncomeTotal, sellsTotal, startingBal, endingBal, cashIncomeTotal); | ||
1602 | MyMoneyMoney annualReturn = helperIRR(all); | | |||
1603 | 1408 | | |||
1604 | result[ctBuys] = buysTotal.toString(); | 1409 | result[ctBuys] = buysTotal.toString(); | ||
1605 | result[ctReturn] = annualReturn.toString(); | 1410 | result[ctReturn] = helperIRR(all); | ||
1606 | result[ctReturnInvestment] = returnInvestment.toString(); | 1411 | result[ctReturnInvestment] = returnInvestment.toString(); | ||
1607 | result[ctEquityType] = MyMoneySecurity::securityTypeToString(file->security(account.currencyId()).securityType()); | 1412 | result[ctEquityType] = MyMoneySecurity::securityTypeToString(file->security(account.currencyId()).securityType()); | ||
1608 | } | 1413 | } | ||
1609 | 1414 | | |||
1610 | void QueryTable::constructCapitalGainRow(const ReportAccount& account, TableRow& result) const | 1415 | void QueryTable::constructCapitalGainRow(const ReportAccount& account, TableRow& result) const | ||
1611 | { | 1416 | { | ||
1612 | MyMoneyFile* file = MyMoneyFile::instance(); | 1417 | MyMoneyFile* file = MyMoneyFile::instance(); | ||
1613 | QList<CashFlowList> cfList; | 1418 | QList<CashFlowList> cfList; | ||
▲ Show 20 Lines • Show All 174 Lines • ▼ Show 20 Line(s) | 1591 | if (m_config.queryColumns() == eMyMoney::Report::QueryColumn::Performance && m_config.isShowingColumnTotals()) { | |||
1788 | qtotalsrow[ctRank] = QLatin1Char('4'); // add identification of row as total | 1593 | qtotalsrow[ctRank] = QLatin1Char('4'); // add identification of row as total | ||
1789 | QMap<QString, CashFlowList> currencyGrandCashFlow; | 1594 | QMap<QString, CashFlowList> currencyGrandCashFlow; | ||
1790 | 1595 | | |||
1791 | QMap<QString, QMap<QString, CashFlowList>>::iterator currencyAccGrp = currencyCashFlow.begin(); | 1596 | QMap<QString, QMap<QString, CashFlowList>>::iterator currencyAccGrp = currencyCashFlow.begin(); | ||
1792 | while (currencyAccGrp != currencyCashFlow.end()) { | 1597 | while (currencyAccGrp != currencyCashFlow.end()) { | ||
1793 | // convert map of top accounts with cashflows to TableRow | 1598 | // convert map of top accounts with cashflows to TableRow | ||
1794 | for (QMap<QString, CashFlowList>::iterator topAccount = (*currencyAccGrp).begin(); topAccount != (*currencyAccGrp).end(); ++topAccount) { | 1599 | for (QMap<QString, CashFlowList>::iterator topAccount = (*currencyAccGrp).begin(); topAccount != (*currencyAccGrp).end(); ++topAccount) { | ||
1795 | qtotalsrow[ctTopAccount] = topAccount.key(); | 1600 | qtotalsrow[ctTopAccount] = topAccount.key(); | ||
1796 | qtotalsrow[ctReturn] = helperIRR(topAccount.value()).toString(); | 1601 | qtotalsrow[ctReturn] = helperIRR(topAccount.value()); | ||
1797 | qtotalsrow[ctCurrency] = currencyAccGrp.key(); | 1602 | qtotalsrow[ctCurrency] = currencyAccGrp.key(); | ||
1798 | currencyGrandCashFlow[currencyAccGrp.key()] += topAccount.value(); // cumulative sum of cashflows of each topaccount | 1603 | currencyGrandCashFlow[currencyAccGrp.key()] += topAccount.value(); // cumulative sum of cashflows of each topaccount | ||
1799 | m_rows.append(qtotalsrow); // rows aren't sorted yet, so no problem with adding them randomly at the end | 1604 | m_rows.append(qtotalsrow); // rows aren't sorted yet, so no problem with adding them randomly at the end | ||
1800 | } | 1605 | } | ||
1801 | ++currencyAccGrp; | 1606 | ++currencyAccGrp; | ||
1802 | } | 1607 | } | ||
1803 | QMap<QString, CashFlowList>::iterator currencyGrp = currencyGrandCashFlow.begin(); | 1608 | QMap<QString, CashFlowList>::iterator currencyGrp = currencyGrandCashFlow.begin(); | ||
1804 | qtotalsrow[ctTopAccount].clear(); // empty topaccount because it's grand cashflow | 1609 | qtotalsrow[ctTopAccount].clear(); // empty topaccount because it's grand cashflow | ||
1805 | while (currencyGrp != currencyGrandCashFlow.end()) { | 1610 | while (currencyGrp != currencyGrandCashFlow.end()) { | ||
1806 | qtotalsrow[ctReturn] = helperIRR(currencyGrp.value()).toString(); | 1611 | qtotalsrow[ctReturn] = helperIRR(currencyGrp.value()); | ||
1807 | qtotalsrow[ctCurrency] = currencyGrp.key(); | 1612 | qtotalsrow[ctCurrency] = currencyGrp.key(); | ||
1808 | m_rows.append(qtotalsrow); | 1613 | m_rows.append(qtotalsrow); | ||
1809 | ++currencyGrp; | 1614 | ++currencyGrp; | ||
1810 | } | 1615 | } | ||
1811 | } | 1616 | } | ||
1812 | } | 1617 | } | ||
1813 | 1618 | | |||
1814 | void QueryTable::constructSplitsTable() | 1619 | void QueryTable::constructSplitsTable() | ||
▲ Show 20 Lines • Show All 356 Lines • Show Last 20 Lines |