wxString mmReportChartStocks::getHTMLText() { mmHTMLBuilder hb; hb.init(); hb.addHeader(2, _("Stocks Performance Charts")); wxTimeSpan dtDiff = dtRange_->end_date() - dtRange_->start_date(); if (dtRange_->is_with_date() && dtDiff.GetDays() <= 366) hb.DisplayDateHeading(dtRange_->start_date(), dtRange_->end_date(), true); hb.addHorizontalLine(); int count = 0, heldAt = -1; bool pointDot = false, showGridLines = false; wxTimeSpan dist; wxDate dateDt, precDateDt = wxInvalidDateTime; for (const auto& stock : Model_Stock::instance().all(Model_Stock::COL_HELDAT)) { int dataCount = 0, freq = 1; Model_StockHistory::Data_Set histData = Model_StockHistory::instance().find(Model_StockHistory::SYMBOL(stock.SYMBOL), Model_StockHistory::DATE(dtRange_->start_date(), GREATER_OR_EQUAL), Model_StockHistory::DATE(dtRange_->end_date(), LESS_OR_EQUAL)); std::stable_sort(histData.begin(), histData.end(), SorterByDATE()); if (histData.size() <= 30) showGridLines = pointDot = true; else if (histData.size() <= 366) showGridLines = true; else freq = histData.size() / 366; std::vector<ValueTrio> aData; for (const auto& hist : histData) { if (dataCount % freq == 0) { ValueTrio val; dateDt = Model_StockHistory::DATE(hist); if (histData.size() <= 30) val.label = mmGetDateForDisplay(dateDt); else if (precDateDt.IsValid() && dateDt.GetMonth() != precDateDt.GetMonth()) val.label = dateDt.GetMonthName(dateDt.GetMonth()); else val.label = ""; val.amount = hist.VALUE; aData.push_back(val); precDateDt = dateDt; } dataCount++; } if (!aData.empty()) { Model_Account::Data* account = Model_Account::instance().get(stock.HELDAT); hb.addHeader(1, wxString::Format("%s - (%s)", stock.STOCKNAME, account->ACCOUNTNAME)); hb.addLineChart(aData, stock.STOCKNAME, count, 1000, 400, pointDot, showGridLines, true); } heldAt = stock.HELDAT; count++; } return hb.getHTMLText(); }
/** Returns the last price date of a given stock */ wxString Model_Stock::lastPriceDate(const Self::Data* entity) { wxString dtStr = entity->PURCHASEDATE; Model_StockHistory::Data_Set histData = Model_StockHistory::instance().find(SYMBOL(entity->SYMBOL)); std::sort(histData.begin(), histData.end(), SorterByDATE()); if (!histData.empty()) dtStr = histData.back().DATE; return dtStr; }
void mmStockDialog::showStockHistory() { priceListBox_->DeleteAllItems(); if (m_stock->SYMBOL.IsEmpty()) return; Model_Account::Data* account = Model_Account::instance().get(m_stock->HELDAT); Model_StockHistory::Data_Set histData = Model_StockHistory::instance().find(Model_StockHistory::SYMBOL(m_stock->SYMBOL)); std::stable_sort(histData.begin(), histData.end(), SorterByDATE()); std::reverse(histData.begin(), histData.end()); if (histData.size()>300) histData.resize(300); if (!histData.empty()) { int idx=0; for (const auto &d : histData) { wxListItem item; item.SetId(idx); item.SetData(d.HISTID); priceListBox_->InsertItem( item ); const wxDate dtdt = Model_StockHistory::DATE(d); const wxString dispAmount = Model_Account::toString(d.VALUE, account, 4); priceListBox_->SetItem(idx, 0, mmGetDateForDisplay(dtdt)); priceListBox_->SetItem(idx, 1, dispAmount); if (idx == 0) { priceDate_->SetValue(dtdt); currentPrice_->SetValue(dispAmount); } const wxString& priceAmount = Model_Account::toString(d.VALUE - m_stock->PURCHASEPRICE, account, 4); priceListBox_->SetItem(idx, 2, priceAmount); idx++; } priceListBox_->RefreshItems(0, --idx); } }
wxString mmReportSummaryByDate::getHTMLText() { int i = 0, j; double balancePerDay[5]; mmHTMLBuilder hb; wxString datePrec; wxDate date, dateStart = wxDate::Now(), dateEnd = wxDate::Now(); wxDateSpan span; mmHistoryItem *pHistItem; mmHistoryData arHistory; std::vector<balanceMap> balanceMapVec(Model_Account::instance().all().size()); std::vector<std::map<wxDate, double>::const_iterator> arIt(balanceMapVec.size()); std::vector<double> arBalance(balanceMapVec.size()); std::vector<wxString> totBalanceData; std::vector<wxDate> arDates; hb.init(); hb.addDivContainer(); hb.addHeader(2, wxString::Format(_("Accounts Balance - %s"), mode_==0 ? _("Monthly Report"):_("Yearly Report"))); hb.addDateNow(); hb.addLineBreak(); hb.startTable(); hb.startThead(); hb.startTableRow(); hb.addTableHeaderCell(_("Date")); hb.addTableHeaderCell(_("Cash"), true); hb.addTableHeaderCell(_("Bank Accounts"), true); hb.addTableHeaderCell(_("Credit Card Accounts"), true); hb.addTableHeaderCell(_("Term Accounts"), true); hb.addTableHeaderCell(_("Total"), true); hb.addTableHeaderCell(_("Stocks"), true); hb.addTableHeaderCell(_("Balance"), true); hb.endTableRow(); hb.endDiv(); hb.endThead(); for (const auto& account: Model_Account::instance().all()) { if (Model_Account::type(account) != Model_Account::INVESTMENT) { // in balanceMapVec ci sono i totali dei movimenti giorno per giorno const Model_Currency::Data* currency = Model_Account::currency(account); for (const auto& tran: Model_Account::transaction(account)) balanceMapVec[i][Model_Checking::TRANSDATE(tran)] += Model_Checking::balance(tran, account.ACCOUNTID) * Model_CurrencyHistory::getDayRate(currency->id(), tran.TRANSDATE); if (Model_Account::type(account) != Model_Account::TERM && balanceMapVec[i].size()) { date = balanceMapVec[i].begin()->first; if (date.IsEarlierThan(dateStart)) dateStart = date; } arBalance[i] = account.INITIALBAL * Model_CurrencyHistory::getDayRate(currency->id(), dateStart.FormatISODate()); } else { Model_Stock::Data_Set stocks = Model_Stock::instance().find(Model_Stock::HELDAT(account.id())); for (const auto & stock : stocks) { arHistory.resize(arHistory.size() + 1); pHistItem = arHistory.data() + arHistory.size() - 1; pHistItem->acctId = account.id(); pHistItem->stockId = stock.STOCKID; pHistItem->purchasePrice = stock.PURCHASEPRICE; pHistItem->purchaseDate = Model_Stock::PURCHASEDATE(stock); pHistItem->purchaseDateStr = stock.PURCHASEDATE; pHistItem->numShares = stock.NUMSHARES; pHistItem->stockHist = Model_StockHistory::instance().find(Model_StockHistory::SYMBOL(stock.SYMBOL)); std::stable_sort(pHistItem->stockHist.begin(), pHistItem->stockHist.end(), SorterByDATE()); std::reverse(pHistItem->stockHist.begin(), pHistItem->stockHist.end()); } } i++; } if (mode_ == 0) { dateEnd -= wxDateSpan::Months(1); dateEnd.SetToLastMonthDay(dateEnd.GetMonth(), dateEnd.GetYear()); span = wxDateSpan::Months(1); } else if (mode_ == 1) { dateEnd.Set(31, wxDateTime::Dec, wxDateTime::Now().GetYear()); span = wxDateSpan::Years(1); } else wxASSERT(0); date = dateEnd; while (date.IsLaterThan(dateStart)) date -= span; dateStart = date; i = 0; for (const auto& acctMap: balanceMapVec) arIt[i++] = acctMap.begin(); // prepare the dates array while (dateStart <= dateEnd) { if (mode_ == 0) dateStart.SetToLastMonthDay(dateStart.GetMonth(), dateStart.GetYear()); arDates.push_back(dateStart); dateStart += span; } date = wxDate::Today(); if (date.GetDay() != dateEnd.GetDay() || date.GetMonth() != dateEnd.GetMonth() || date.GetYear() != dateEnd.GetYear()) arDates.push_back(date); for (const auto & dateStart : arDates) { i = 0; for (auto& account: Model_Account::instance().all()) { if (Model_Account::type(account) != Model_Account::INVESTMENT) { for (; arIt[i] != balanceMapVec[i].end(); ++arIt[i]) { if (arIt[i]->first.IsLaterThan(dateStart)) break; arBalance[i] += arIt[i]->second; } } else { double convRate = 1.0; Model_Currency::Data* currency = Model_Account::currency(account); if (currency) convRate = Model_CurrencyHistory::getDayRate(currency->id(), dateStart.FormatISODate()); arBalance[i] = arHistory.getDailyBalanceAt(&account, dateStart) * convRate; } i++; } totBalanceData.push_back(dateStart.FormatISODate()); for (j=0; j<5; j++) balancePerDay[j] = 0.0; for (j=0; j<5; j++) { i = 0; for (const auto& account: Model_Account::instance().all()) { if ((j == 0 && Model_Account::type(account) == Model_Account::CASH) || (j == 1 && Model_Account::type(account) == Model_Account::CHECKING) || (j == 2 && Model_Account::type(account) == Model_Account::CREDIT_CARD) || (j == 3 && Model_Account::type(account) == Model_Account::TERM) || (j == 4 && Model_Account::type(account) == Model_Account::INVESTMENT)) { balancePerDay[j] += arBalance[i]; } i++; } totBalanceData.push_back(Model_Currency::toCurrency(balancePerDay[j])); } totBalanceData.push_back(Model_Currency::toCurrency(balancePerDay[0] + balancePerDay[1] + balancePerDay[2] + balancePerDay[3])); totBalanceData.push_back(Model_Currency::toCurrency(balancePerDay[0] + balancePerDay[1] + balancePerDay[2] + balancePerDay[3] + balancePerDay[4])); } hb.startTbody(); for (i = totBalanceData.size() - 8; i >= 0; i -= 8) { if (datePrec.Left(4) != totBalanceData[i].Left(4)) { hb.startTotalTableRow(); hb.addTableCell(totBalanceData[i].Left(4)); hb.addTableCell(""); hb.addTableCell(""); hb.addTableCell(""); hb.addTableCell(""); hb.addTableCell(""); hb.addTableCell(""); hb.addTableCell(""); hb.endTableRow(); } hb.startTableRow(); hb.addTableCell(mmGetDateForDisplay(mmGetStorageStringAsDate(totBalanceData[i]))); hb.addTableCell(totBalanceData[i + 1], true); hb.addTableCell(totBalanceData[i + 2], true); hb.addTableCell(totBalanceData[i + 3], true); hb.addTableCell(totBalanceData[i + 4], true); hb.addTableCell(totBalanceData[i + 6], true); hb.addTableCell(totBalanceData[i + 5], true); hb.addTableCell(totBalanceData[i + 7], true); hb.endTableRow(); datePrec = totBalanceData[i]; } hb.endTbody(); hb.endTable(); hb.end(); Model_Report::outputReportFile(hb.getHTMLText()); return ""; }
/** Returns the total stock balance at a given date */ double Model_Stock::getDailyBalanceAt(const Model_Account::Data *account, const wxDate& date) { wxString strDate = date.FormatISODate(); std::map<int, double> totBalance; Data_Set stocks = this->instance().find(HELDAT(account->id())); for (const auto & stock : stocks) { wxString precValueDate, nextValueDate; Model_StockHistory::Data_Set stock_hist = Model_StockHistory::instance().find(SYMBOL(stock.SYMBOL)); std::stable_sort(stock_hist.begin(), stock_hist.end(), SorterByDATE()); std::reverse(stock_hist.begin(), stock_hist.end()); double valueAtDate = 0.0, precValue = 0.0, nextValue = 0.0; for (const auto & hist : stock_hist) { // test for the date requested if (hist.DATE == strDate) { valueAtDate = hist.VALUE; break; } // if not found, search for previous and next date if (precValue == 0.0 && hist.DATE < strDate) { precValue = hist.VALUE; precValueDate = hist.DATE; } if (hist.DATE > strDate) { nextValue = hist.VALUE; nextValueDate = hist.DATE; } // end conditions: prec value assigned and price date < requested date if (precValue != 0.0 && hist.DATE < strDate) break; } if (valueAtDate == 0.0) { // if previous not found but if the given date is after purchase date, takes purchase price if (precValue == 0.0 && date >= PURCHASEDATE(stock)) { precValue = stock.PURCHASEPRICE; precValueDate = stock.PURCHASEDATE; } // if next not found and the accoung is open, takes previous date if (nextValue == 0.0 && Model_Account::status(account) == Model_Account::OPEN) { nextValue = precValue; nextValueDate = precValueDate; } if (precValue > 0.0 && nextValue > 0.0 && precValueDate >= stock.PURCHASEDATE && nextValueDate >= stock.PURCHASEDATE) valueAtDate = precValue; } totBalance[stock.id()] += stock.NUMSHARES * valueAtDate; } double balance = 0.0; for (const auto& it : totBalance) balance += it.second; return balance; }
wxString mmReportSummaryByDate::getHTMLText() { size_t account_types_num = Model_Account::INVESTMENT + 1; std::map<size_t, double> balancePerDay; mmHTMLBuilder hb; wxString datePrec; wxDate date, dateStart = wxDate::Today(), dateEnd = wxDate::Today(); wxDateSpan span; mmHistoryItem *pHistItem; mmHistoryData arHistory; std::vector<balanceMap> balanceMapVec(Model_Account::instance().all().size()); std::vector<std::map<wxDate, double>::const_iterator> arIt(balanceMapVec.size()); std::vector<double> arBalance(balanceMapVec.size()); std::vector<wxString> totBalanceData; std::vector<wxDate> arDates; hb.init(); hb.addDivContainer(); hb.addHeader(2, wxString::Format(_("Accounts Balance - %s") , mode_ == 0 ? _("Monthly Report") : _("Yearly Report"))); hb.addDateNow(); hb.addLineBreak(); hb.startTable(); hb.startThead(); hb.startTableRow(); hb.addTableHeaderCell(_("Date")); hb.addTableHeaderCell(_("Cash"), true); hb.addTableHeaderCell(_("Bank Accounts"), true); hb.addTableHeaderCell(_("Credit Card Accounts"), true); hb.addTableHeaderCell(_("Loan Accounts"), true); hb.addTableHeaderCell(_("Term Accounts"), true); hb.addTableHeaderCell(_("Crypto Wallets"), true); hb.addTableHeaderCell(_("Total"), true); hb.addTableHeaderCell(_("Stocks"), true); hb.addTableHeaderCell(_("Balance"), true); hb.endTableRow(); hb.endDiv(); hb.endThead(); int i = 0; for (const auto& account: Model_Account::instance().all()) { if (Model_Account::type(account) != Model_Account::INVESTMENT) { // in balanceMapVec ci sono i totali dei movimenti giorno per giorno const Model_Currency::Data* currency = Model_Account::currency(account); for (const auto& tran : Model_Account::transaction(account)) { balanceMapVec[i][Model_Checking::TRANSDATE(tran)] += Model_Checking::balance(tran, account.ACCOUNTID) * Model_CurrencyHistory::getDayRate(currency->id(), tran.TRANSDATE); } if (Model_Account::type(account) != Model_Account::TERM && balanceMapVec[i].size()) { date = balanceMapVec[i].begin()->first; if (date.IsEarlierThan(dateStart)) { dateStart = date; } } arBalance[i] = account.INITIALBAL * Model_CurrencyHistory::getDayRate(currency->id(), dateStart); } else { Model_Stock::Data_Set stocks = Model_Stock::instance().find(Model_Stock::HELDAT(account.id())); for (const auto & stock : stocks) { arHistory.resize(arHistory.size() + 1); pHistItem = arHistory.data() + arHistory.size() - 1; pHistItem->acctId = account.id(); pHistItem->stockId = stock.STOCKID; pHistItem->purchasePrice = stock.PURCHASEPRICE; pHistItem->purchaseDate = Model_Stock::PURCHASEDATE(stock); pHistItem->purchaseDateStr = stock.PURCHASEDATE; pHistItem->numShares = stock.NUMSHARES; pHistItem->stockHist = Model_StockHistory::instance().find(Model_StockHistory::SYMBOL(stock.SYMBOL)); std::stable_sort(pHistItem->stockHist.begin(), pHistItem->stockHist.end(), SorterByDATE()); std::reverse(pHistItem->stockHist.begin(), pHistItem->stockHist.end()); } } i++; } if (mode_ == 0) { dateEnd -= wxDateSpan::Months(1); dateEnd.SetToLastMonthDay(dateEnd.GetMonth(), dateEnd.GetYear()); span = wxDateSpan::Months(1); } else if (mode_ == 1) { dateEnd.Set(31, wxDateTime::Dec, wxDateTime::Now().GetYear()); span = wxDateSpan::Years(1); } else { wxFAIL_MSG("unknown report mode"); } date = dateEnd; while (date.IsLaterThan(dateStart)) { date -= span; } dateStart = date; int c = 0; for (const auto& acctMap : balanceMapVec) { arIt[c++] = acctMap.begin(); } // prepare the dates array while (dateStart <= dateEnd) { if (mode_ == 0) { dateStart.SetToLastMonthDay(dateStart.GetMonth(), dateStart.GetYear()); } arDates.push_back(dateStart); dateStart += span; } date = wxDate::Today().SetToLastMonthDay(); if (date.GetDay() != dateEnd.GetDay() || date.GetMonth() != dateEnd.GetMonth() || date.GetYear() != dateEnd.GetYear()) { arDates.push_back(date); } for (const auto & dd : arDates) { int k = 0; double total = 0.0; for (auto& account: Model_Account::instance().all()) { if (Model_Account::type(account) != Model_Account::INVESTMENT) { for (; arIt[k] != balanceMapVec[k].end(); ++arIt[k]) { if (arIt[k]->first.IsLaterThan(dd)) { break; } arBalance[k] += arIt[k]->second; } } else { double convRate = 1.0; Model_Currency::Data* currency = Model_Account::currency(account); if (currency) { convRate = Model_CurrencyHistory::getDayRate(currency->id(), dd); } arBalance[k] = arHistory.getDailyBalanceAt(&account, dd) * convRate; } k++; } // prepare columns for report: date, cash, checking, credit card, term, partial total, investment, grand total totBalanceData.push_back(dd.FormatISODate()); for (size_t j = 0; j < account_types_num; j++) { balancePerDay[j] = 0.0; } int a = 0; for (const auto& account: Model_Account::instance().all()) { double t = arBalance[a++]; balancePerDay[Model_Account::type(account)] += t; } totBalanceData.push_back(Model_Currency::toCurrency(balancePerDay[Model_Account::CASH])); totBalanceData.push_back(Model_Currency::toCurrency(balancePerDay[Model_Account::CHECKING])); totBalanceData.push_back(Model_Currency::toCurrency(balancePerDay[Model_Account::CREDIT_CARD])); totBalanceData.push_back(Model_Currency::toCurrency(balancePerDay[Model_Account::LOAN])); totBalanceData.push_back(Model_Currency::toCurrency(balancePerDay[Model_Account::TERM])); totBalanceData.push_back(Model_Currency::toCurrency(balancePerDay[Model_Account::CRYPTO])); total = balancePerDay[Model_Account::CASH] + balancePerDay[Model_Account::CHECKING] + balancePerDay[Model_Account::CREDIT_CARD] + balancePerDay[Model_Account::LOAN] + balancePerDay[Model_Account::TERM] + balancePerDay[Model_Account::CRYPTO]; totBalanceData.push_back(Model_Currency::toCurrency(total)); totBalanceData.push_back(Model_Currency::toCurrency(balancePerDay[Model_Account::INVESTMENT])); total += balancePerDay[Model_Account::INVESTMENT]; totBalanceData.push_back(Model_Currency::toCurrency(total)); } hb.startTbody(); int x = 1 + (account_types_num) + 2; for (int k = totBalanceData.size() - x; k >= 0; k -= x) { if (datePrec.Left(4) != totBalanceData[k].Left(4)) { hb.startTotalTableRow(); hb.addTableCell(totBalanceData[k].Left(4)); hb.addEmptyTableCell(x-1); hb.endTableRow(); } hb.startTableRow(); hb.addTableCellDate(totBalanceData[k]); for (int j = 0; j < x - 1; j++) { hb.addTableCell(totBalanceData[k + j + 1], true); } hb.endTableRow(); datePrec = totBalanceData[k]; } hb.endTbody(); hb.endTable(); hb.end(); return hb.getHTMLText(); }
wxString mmReportChartStocks::getHTMLText() { mmHTMLBuilder hb; hb.init(); hb.addDivContainer(); hb.addHeader(2, getReportTitle()); hb.addDateNow(); wxTimeSpan dtDiff = m_date_range->end_date() - m_date_range->start_date(); if (m_date_range->is_with_date() && dtDiff.GetDays() <= 366) hb.DisplayDateHeading(m_date_range->start_date(), m_date_range->end_date(), true); hb.addHorizontalLine(); bool pointDot = false, showGridLines = false; wxTimeSpan dist; wxDate precDateDt = wxInvalidDateTime; for (const auto& stock : Model_Stock::instance().all(Model_Stock::COL_HELDAT)) { int dataCount = 0, freq = 1; Model_StockHistory::Data_Set histData = Model_StockHistory::instance().find(Model_StockHistory::SYMBOL(stock.SYMBOL), Model_StockHistory::DATE(m_date_range->start_date(), GREATER_OR_EQUAL), Model_StockHistory::DATE(m_date_range->end_date(), LESS_OR_EQUAL)); std::stable_sort(histData.begin(), histData.end(), SorterByDATE()); if (histData.size() <= 30) showGridLines = pointDot = true; else if (histData.size() <= 366) showGridLines = true; else freq = histData.size() / 366; std::vector<LineGraphData> aData; for (const auto& hist : histData) { if (dataCount % freq == 0) { LineGraphData val; val.xPos = mmGetDateForDisplay(hist.DATE); const wxDate dateDt = Model_StockHistory::DATE(hist); if (histData.size() <= 30) val.label = val.xPos; else if (precDateDt.IsValid() && dateDt.GetMonth() != precDateDt.GetMonth()) val.label = wxGetTranslation(dateDt.GetEnglishMonthName(dateDt.GetMonth())); else val.label = ""; val.amount = hist.VALUE; aData.push_back(val); precDateDt = dateDt; } dataCount++; } if (!aData.empty()) { hb.addDivRow(); Model_Account::Data* account = Model_Account::instance().get(stock.HELDAT); hb.addHeader(1, wxString::Format("%s - (%s)", stock.STOCKNAME, account->ACCOUNTNAME)); hb.addDivCol17_67(); hb.addLineChart(aData, stock.STOCKNAME, 0, 1000, 400, pointDot, showGridLines); hb.endDiv(); hb.endDiv(); } } hb.endDiv(); hb.end(); return hb.getHTMLText(); }