23 #include <boost/process/environment.hpp> 32 #include <boost/version.hpp> 33 #if BOOST_VERSION < 107600 35 #define BOOST_BIND_GLOBAL_PLACEHOLDERS 37 #include <boost/algorithm/string.hpp> 38 #include <boost/filesystem.hpp> 39 #ifdef BOOST_WINDOWS_API 40 #include <boost/process/windows.hpp> 42 #include <boost/process.hpp> 43 #include <boost/regex.hpp> 44 #include <boost/property_tree/ptree.hpp> 45 #include <boost/property_tree/json_parser.hpp> 46 #include <boost/iostreams/device/array.hpp> 47 #include <boost/iostreams/stream_buffer.hpp> 48 #include <boost/locale.hpp> 49 #include <boost/asio.hpp> 52 #include <gnc-datetime.hpp> 53 #include <gnc-numeric.hpp> 54 #include "gnc-quotes.hpp" 60 #include <gnc-session.h> 64 static const QofLogModule log_module =
"gnc.price-quotes";
65 static const char* av_api_env =
"ALPHAVANTAGE_API_KEY";
66 static const char* av_api_key =
"alphavantage-api-key";
67 static const char* yh_api_env =
"FINANCEAPI_API_KEY";
68 static const char* yh_api_key =
"yhfinance-api-key";
70 namespace bl = boost::locale;
71 namespace bp = boost::process;
72 namespace bfs = boost::filesystem;
73 namespace bpt = boost::property_tree;
74 namespace bio = boost::iostreams;
76 using QuoteResult = std::tuple<int, StrVec, StrVec>;
84 gnc_quotes_get_quotable_commodities(
const gnc_commodity_table *
table);
90 virtual const StrVec& get_sources()
const noexcept = 0;
91 virtual const std::string & get_version()
const noexcept = 0;
92 virtual QuoteResult
get_quotes(
const std::string& json_str)
const = 0;
104 void fetch (QofBook *book);
105 void fetch (CommVec& commodities);
106 void fetch (gnc_commodity *comm);
107 void report (
const char* source,
const StrVec& commodities,
bool verbose);
109 const std::string& version() noexcept {
return m_quotesource->get_version(); }
110 const QuoteSources& sources() noexcept {
return m_sources; }
111 bool had_failures() noexcept {
return !m_failures.empty(); }
112 const QFVec& failures() noexcept;
113 std::string report_failures() noexcept;
116 std::string query_fq (
const char* source,
const StrVec& commoditites);
117 std::string query_fq (
const CommVec&);
118 bpt::ptree parse_quotes (
const std::string& quote_str);
119 void create_quotes(
const bpt::ptree& pt,
const CommVec& comm_vec);
120 std::string comm_vec_to_json_string(
const CommVec&)
const;
121 GNCPrice* parse_one_quote(
const bpt::ptree&, gnc_commodity*);
123 std::unique_ptr<GncQuoteSource> m_quotesource;
124 QuoteSources m_sources;
127 gnc_commodity *m_dflt_curr;
132 const bfs::path c_cmd;
133 std::string c_fq_wrapper;
134 std::string m_version;
136 bp::environment m_env;
140 const std::string& get_version()
const noexcept
override {
return m_version; }
141 const StrVec& get_sources()
const noexcept
override {
return m_sources; }
142 QuoteResult
get_quotes(
const std::string&)
const override;
144 QuoteResult run_cmd (
const StrVec& args,
const std::string& json_string)
const;
145 void set_api_key(
const char* api_pref,
const char* api_env);
148 static void show_quotes(
const bpt::ptree& pt,
const StrVec& commodities,
bool verbose);
149 static void show_currency_quotes(
const bpt::ptree& pt,
const StrVec& commodities,
bool verbose);
150 static std::string parse_quotesource_error(
const std::string& line);
152 static const std::string empty_string{};
154 GncFQQuoteSource::GncFQQuoteSource() :
155 c_cmd{bp::search_path(
"perl")},
156 m_version{}, m_sources{}, m_env{boost::this_process::environment()}
158 char *bindir = gnc_path_get_bindir();
159 c_fq_wrapper = std::string(bindir) +
"/finance-quote-wrapper";
161 StrVec args{
"-w", c_fq_wrapper,
"-v"};
162 auto [rv, sources, errors] = run_cmd(args, empty_string);
165 std::string err{bl::translate(
"Failed to initialize Finance::Quote: ")};
166 for (
const auto& err_line : errors)
167 err += err_line.empty() ?
"" : err_line +
"\n";
172 std::string err{bl::translate(
"Finance::Quote check returned error ")};
173 for(
const auto& err_line : errors)
174 err += err.empty() ?
"" : err_line +
"\n";
177 auto version{sources.front()};
180 std::string err{bl::translate(
"No Finance::Quote Version")};
183 m_version = std::move(version);
184 sources.erase(sources.begin());
185 m_sources = std::move(sources);
186 std::sort (m_sources.begin(), m_sources.end());
188 set_api_key(av_api_key, av_api_env);
189 set_api_key(yh_api_key, yh_api_env);
193 GncFQQuoteSource::get_quotes(
const std::string& json_str)
const 195 StrVec args{
"-w", c_fq_wrapper,
"-f" };
196 return run_cmd(args, json_str);
200 GncFQQuoteSource::run_cmd (
const StrVec& args,
const std::string& json_string)
const 202 StrVec out_vec, err_vec;
207 std::future<std::vector<char> > out_buf, err_buf;
208 boost::asio::io_context svc;
210 auto input_buf = bp::buffer (json_string);
212 process = bp::child(c_cmd, args,
213 bp::std_out > out_buf,
214 bp::std_err > err_buf,
215 bp::std_in < input_buf,
216 #ifdef BOOST_WINDOWS_API
217 bp::windows::create_no_window,
226 auto raw = out_buf.get();
227 std::vector<std::string> data;
229 bio::stream_buffer<bio::array_source> sb(raw.data(), raw.size());
230 std::istream is(&sb);
232 while (std::getline(is, line) && !line.empty())
235 if (line.back() ==
'\r')
238 out_vec.push_back (std::move(line));
241 bio::stream_buffer<bio::array_source> eb(raw.data(), raw.size());
242 std::istream es(&eb);
244 while (std::getline(es, line) && !line.empty())
245 err_vec.push_back (std::move(line));
247 cmd_result = process.exit_code();
249 catch (std::exception &e)
252 err_vec.push_back(e.what());
255 return QuoteResult (cmd_result, std::move(out_vec), std::move(err_vec));
259 GncFQQuoteSource::set_api_key(
const char* api_key,
const char* api_env)
264 m_env[api_env] = key;
269 if (api_key == av_api_key && m_env.find(api_env) == m_env.end())
270 PWARN(
"No Alpha Vantage API key set, currency quotes and other " 271 "AlphaVantage based quotes won't work.");
278 m_sources{}, m_failures{},
282 m_sources = m_quotesource->get_sources();
285 GncQuotesImpl::GncQuotesImpl(QofBook* book) : m_quotesource{
new GncFQQuoteSource},
286 m_sources{}, m_book{book},
289 m_sources = m_quotesource->get_sources();
292 GncQuotesImpl::GncQuotesImpl(QofBook* book, std::unique_ptr<GncQuoteSource> quote_source) :
293 m_quotesource{std::move(quote_source)},
296 m_sources = m_quotesource->get_sources();
300 GncQuotesImpl::fetch (QofBook *book)
303 throw (
GncQuoteException(bl::translate(
"GncQuotes::Fetch called with no book.")));
304 auto commodities = gnc_quotes_get_quotable_commodities (
310 GncQuotesImpl::fetch (gnc_commodity *comm)
312 auto commodities = CommVec {comm};
317 GncQuotesImpl::fetch (CommVec& commodities)
320 if (commodities.empty())
321 throw (
GncQuoteException(bl::translate(
"GncQuotes::Fetch called with no commodities.")));
322 auto quote_str{query_fq (commodities)};
323 auto ptree{parse_quotes (quote_str)};
324 create_quotes(ptree, commodities);
328 GncQuotesImpl::report (
const char* source,
const StrVec& commodities,
332 throw (
GncQuoteException(bl::translate(
"GncQuotes::Report called with no source.")));
334 bool is_currency{strcmp(source,
"currency") == 0};
336 if (commodities.empty())
338 std::cerr << _(
"There were no commodities for which to retrieve quotes.") << std::endl;
343 auto quote_str{query_fq (source, commodities)};
344 auto ptree{parse_quotes (quote_str)};
345 auto source_pt_ai{ptree.find(source)};
347 show_currency_quotes(source_pt_ai->second, commodities, verbose);
349 show_quotes(source_pt_ai->second, commodities, verbose);
353 std::cerr << _(
"Finance::Quote retrieval failed with error ") << err.what() << std::endl;
358 GncQuotesImpl::failures() noexcept
364 explain(GncQuoteError err,
const std::string& errmsg)
369 case GncQuoteError::NO_RESULT:
371 retval += _(
"Finance::Quote returned no data and set no error.");
373 retval += _(
"Finance::Quote returned an error: ") + errmsg;
375 case GncQuoteError::QUOTE_FAILED:
377 retval += _(
"Finance::Quote reported failure set no error.");
379 retval += _(
"Finance::Quote reported failure with error: ") + errmsg;
381 case GncQuoteError::NO_CURRENCY:
382 retval += _(
"Finance::Quote returned a quote with no currency.");
384 case GncQuoteError::UNKNOWN_CURRENCY:
385 retval += _(
"Finance::Quote returned a quote with a currency GnuCash doesn't know about.");
387 case GncQuoteError::NO_PRICE:
388 retval += _(
"Finance::Quote returned a quote with no price element.");
390 case GncQuoteError::PRICE_PARSE_FAILURE:
391 retval += _(
"Finance::Quote returned a quote with a price that GnuCash was unable to covert to a number.");
393 case GncQuoteError::SUCCESS:
395 retval += _(
"The quote has no error set.");
402 GncQuotesImpl::report_failures() noexcept
404 std::string retval{_(
"Quotes for the following commodities were unavailable or unusable:\n")};
405 std::for_each(m_failures.begin(), m_failures.end(),
406 [&retval](
auto failure)
408 auto [ns, sym, reason, err] = failure;
409 retval +=
"* " + ns +
":" + sym +
" " +
410 explain(reason, err) +
"\n";
417 using Path = bpt::ptree::path_type;
418 static inline Path make_quote_path(
const std::string &name_space,
419 const std::string &symbol)
421 using Path = bpt::ptree::path_type;
422 Path key{name_space,
'|'};
423 key /= Path{symbol,
'|'};
428 GncQuotesImpl::comm_vec_to_json_string(
const CommVec &comm_vec)
const 430 bpt::ptree pt, pt_child;
433 std::for_each (comm_vec.cbegin(), comm_vec.cend(),
434 [
this, &pt] (
auto comm)
437 auto comm_ns = std::string(
"currency");
441 (!comm_mnemonic || (strcmp(comm_mnemonic,
"XXX") == 0)))
447 pt.put (make_quote_path(comm_ns, comm_mnemonic),
"");
451 std::ostringstream result;
452 bpt::write_json(result, pt);
456 static inline std::string
457 get_quotes(
const std::string& json_str,
const std::unique_ptr<GncQuoteSource>& qs)
459 auto [rv, quotes, errors] = qs->get_quotes(json_str);
464 for (
const auto& line : quotes)
465 answer.append(line +
"\n");
470 for (
const auto& line: errors)
472 if (line ==
"invalid_json\n")
473 PERR(
"Finanace Quote Wrapper was unable to parse %s",
475 err_str += parse_quotesource_error(line);
484 GncQuotesImpl::query_fq (
const char* source,
const StrVec& commodities)
487 auto is_currency{strcmp(source,
"currency") == 0};
489 if (is_currency && commodities.size() < 2)
493 pt.put(
"defaultcurrency", commodities[0].c_str());
497 std::for_each(is_currency ? ++commodities.cbegin() : commodities.cbegin(),
499 [source, &pt](
auto sym)
501 pt.put(make_quote_path(source, sym),
"");
503 std::ostringstream result;
504 bpt::write_json(result, pt);
505 auto result_str{result.str()};
506 PINFO(
"Query JSON: %s\n", result_str.c_str());
507 return get_quotes(result.str(), m_quotesource);
511 GncQuotesImpl::query_fq (
const CommVec& comm_vec)
513 auto json_str{comm_vec_to_json_string(comm_vec)};
514 PINFO(
"Query JSON: %s\n", json_str.c_str());
521 const char* mnemonic;
524 boost::optional<std::string> price;
526 boost::optional<std::string> date;
527 boost::optional<std::string> time;
528 boost::optional<std::string> currency;
529 boost::optional<std::string> errormsg;
533 get_price_and_type(
PriceParams& p,
const bpt::ptree& comm_pt)
536 p.price = comm_pt.get_optional<std::string> (p.type);
541 p.price = comm_pt.get_optional<std::string> (p.type);
547 p.price = comm_pt.get_optional<std::string> (p.type);
550 p.type = p.price ?
"unknown" :
"missing";
555 parse_quote_json(
PriceParams& p,
const bpt::ptree& comm_pt)
557 auto success = comm_pt.get_optional<
bool> (
"success");
558 p.success = success && *success;
560 p.errormsg = comm_pt.get_optional<std::string> (
"errormsg");
561 get_price_and_type(p, comm_pt);
562 auto inverted = comm_pt.get_optional<
bool> (
"inverted");
563 p.inverted = inverted && *inverted;
564 p.date = comm_pt.get_optional<std::string> (
"date");
565 p.time = comm_pt.get_optional<std::string> (
"time");
566 p.currency = comm_pt.get_optional<std::string> (
"currency");
569 PINFO(
"Commodity: %s", p.mnemonic);
570 PINFO(
" Success: %s", (p.success ?
"yes" :
"no"));
571 PINFO(
" Date: %s", (p.date ? p.date->c_str() :
"missing"));
572 PINFO(
" Time: %s", (p.time ? p.time->c_str() :
"missing"));
573 PINFO(
" Currency: %s", (p.currency ? p.currency->c_str() :
"missing"));
574 PINFO(
" Price: %s", (p.price ? p.price->c_str() :
"missing"));
575 PINFO(
" Inverted: %s\n", (p.inverted ?
"yes" :
"no"));
587 if (p.date && !p.date->empty())
592 PINFO(
"Quote date included, using %s for %s:%s",
593 quote_time.format(
"%Y-%m-%d %H:%M:%S %z").c_str(), p.ns, p.mnemonic);
594 return static_cast<time64>(quote_time);
596 catch (
const std::exception &err)
599 PWARN(
"Warning: failed to parse quote date '%s' for %s:%s because %s - will use %s",
600 p.date->c_str(), p.ns, p.mnemonic, err.what(), now.format(
"%Y-%m-%d %H:%M:%S %z").c_str());
601 return static_cast<time64>(now);
606 PINFO(
"No date was returned for %s:%s - will use %s",
607 p.ns, p.mnemonic, now.format(
"%Y-%m-%d %H:%M:%S %z").c_str());
608 return static_cast<time64>(now);
611 static boost::optional<GncNumeric>
614 boost::optional<GncNumeric> price;
621 PWARN(
"Skipped %s:%s - failed to parse returned price '%s'",
622 p.ns, p.mnemonic, p.price->c_str());
625 if (price && p.inverted)
626 *price = price->inv();
631 static gnc_commodity*
632 get_currency(
const PriceParams& p, QofBook* book, QFVec& failures)
636 failures.emplace_back(p.ns, p.mnemonic, GncQuoteError::NO_CURRENCY,
638 PWARN(
"Skipped %s:%s - Finance::Quote returned a quote with no currency",
642 std::string curr_str = *p.currency;
643 boost::to_upper (curr_str);
645 auto currency = gnc_commodity_table_lookup (commodity_table,
"ISO4217", curr_str.c_str());
649 failures.emplace_back(p.ns, p.mnemonic,
650 GncQuoteError::UNKNOWN_CURRENCY, empty_string);
651 PWARN(
"Skipped %s:%s - failed to parse returned currency '%s'",
652 p.ns, p.mnemonic, p.currency->c_str());
660 GncQuotesImpl::parse_one_quote(
const bpt::ptree& pt, gnc_commodity* comm)
668 (!p.mnemonic || (strcmp (p.mnemonic,
"XXX") == 0)))
671 auto source_pt_ai{pt.find(source)};
672 auto ok{source_pt_ai != pt.not_found()};
675 auto comm_pt_ai{source_pt_ai->second.find(p.mnemonic)};
676 ok = (comm_pt_ai != pt.not_found());
678 comm_pt = comm_pt_ai->second;
682 m_failures.emplace_back(p.ns, p.mnemonic, GncQuoteError::NO_RESULT,
684 PINFO(
"Skipped %s:%s - Finance::Quote didn't return any data from %s.",
685 p.ns, p.mnemonic, source);
689 parse_quote_json(p, comm_pt);
692 m_failures.emplace_back(p.ns, p.mnemonic, GncQuoteError::QUOTE_FAILED,
693 p.errormsg ? *p.errormsg : empty_string);
694 PWARN(
"Skipped %s:%s - Finance::Quote returned fetch failure.\nReason %s",
696 (p.errormsg ? p.errormsg->c_str() :
"unknown"));
702 m_failures.emplace_back(p.ns, p.mnemonic,
703 GncQuoteError::NO_PRICE, empty_string);
704 PWARN(
"Skipped %s:%s - Finance::Quote didn't return a valid price",
709 auto price{get_price(p)};
712 m_failures.emplace_back(p.ns, p.mnemonic,
713 GncQuoteError::PRICE_PARSE_FAILURE,
718 auto currency{get_currency(p, m_book, m_failures)};
722 auto quotedt{calc_price_time(p)};
724 gnc_price_begin_edit (gnc_price);
725 gnc_price_set_commodity (gnc_price, comm);
726 gnc_price_set_currency (gnc_price, currency);
727 gnc_price_set_time64 (gnc_price, static_cast<time64> (quotedt));
728 gnc_price_set_source (gnc_price, PRICE_SOURCE_FQ);
729 gnc_price_set_typestr (gnc_price, p.type.c_str());
730 gnc_price_set_value (gnc_price, *price);
731 gnc_price_commit_edit (gnc_price);
736 GncQuotesImpl::parse_quotes (
const std::string& quote_str)
739 std::istringstream ss {quote_str};
744 bpt::read_json (ss, pt);
746 catch (bpt::json_parser_error &e) {
749 catch (
const std::runtime_error& e)
753 catch (
const std::logic_error& e)
758 std::string error_msg{_(
"Failed to parse result returned by Finance::Quote.")};
761 error_msg += _(
"Result:");
763 error_msg += quote_str;
768 std::string error_msg{_(
"Failed to parse result returned by Finance::Quote.")};
771 error_msg += _(
"Error message:");
776 error_msg += _(
"Result:");
778 error_msg += quote_str;
785 GncQuotesImpl::create_quotes (
const bpt::ptree& pt,
const CommVec& comm_vec)
788 for (
auto comm : comm_vec)
790 auto price{parse_one_quote(pt, comm)};
799 show_verbose_quote(
const bpt::ptree& comm_pt)
801 std::for_each(comm_pt.begin(), comm_pt.end(),
803 std::cout << std::setw(12) << std::right << elem.first <<
" => " <<
804 std::left << elem.second.data() <<
"\n";
806 std::cout << std::endl;
810 show_gnucash_quote(
const bpt::ptree& comm_pt)
812 constexpr
const char* ptr{
"<=== "};
813 constexpr
const char* dptr{
"<=\\ "};
814 constexpr
const char* uptr{
"<=/ "};
816 const char* reqd{C_(
"Finance::Quote",
"required")};
818 const char* rec{C_(
"Finance::Quote",
"recommended")};
820 const char* oot{C_(
"Finance::Quote",
"one of these")};
822 const char* miss{C_(
"Finance::Quote",
"**missing**")};
824 const std::string miss_str{miss};
825 auto outline{[](
const char* label, std::string value,
const char* pointer,
const char* req) {
826 std::cout << std::setw(12) << std::right << label << std::setw(16) << std::left <<
827 value << pointer << req <<
"\n";
829 std::cout << _(
"Finance::Quote fields GnuCash uses:") <<
"\n";
831 outline(C_(
"Finance::Quote",
"symbol: "), comm_pt.get<
char>(
"symbol", miss), ptr, reqd);
833 outline(C_(
"Finance::Quote",
"date: "), comm_pt.get<
char>(
"date", miss), ptr, rec);
835 outline(C_(
"Finance::Quote",
"currency: "), comm_pt.get<
char>(
"currency", miss), ptr, reqd);
836 auto last{comm_pt.get<
char>(
"last",
"")};
837 auto nav{comm_pt.get<
char>(
"nav",
"")};
838 auto price{comm_pt.get<
char>(
"nav",
"")};
839 auto no_price{last.empty() && nav.empty() && price.empty()};
841 outline(C_(
"Finance::Quote",
"last: "), no_price ? miss_str : last, dptr,
"");
843 outline(C_(
"Finance::Quote",
"nav: "), no_price ? miss_str : nav, ptr, oot);
845 outline(C_(
"Finance::Quote",
"price: "), no_price ? miss_str : price, uptr,
"");
846 std::cout << std::endl;
848 static const bpt::ptree empty_tree{};
850 static inline const bpt::ptree&
851 get_commodity_data(
const bpt::ptree& pt,
const std::string& comm)
853 auto commdata{pt.find(comm)};
854 if (commdata == pt.not_found())
856 std::cout << comm <<
" " << _(
"Finance::Quote returned no data and set no error.") << std::endl;
859 auto& comm_pt{commdata->second};
860 auto success = comm_pt.get_optional<
bool> (
"success");
861 if (!(success && *success))
863 auto errormsg = comm_pt.get_optional<std::string> (
"errormsg");
864 if (errormsg && !errormsg->empty())
865 std::cout << _(
"Finance::Quote reported a failure for symbol ") <<
866 comm <<
": " << *errormsg << std::endl;
868 std::cout << _(
"Finance::Quote failed silently to retrieve a quote for symbol ") <<
876 show_quotes(
const bpt::ptree& pt,
const StrVec& commodities,
bool verbose)
878 for (
const auto& comm : commodities)
880 auto comm_pt{get_commodity_data(pt, comm)};
882 if (comm_pt == empty_tree)
887 std::cout << comm <<
":\n";
888 show_verbose_quote(comm_pt);
892 show_gnucash_quote(comm_pt);
898 show_currency_quotes(
const bpt::ptree& pt,
const StrVec& commodities,
bool verbose)
900 auto to_cur{commodities.front()};
901 for (
const auto& comm : commodities)
906 auto comm_pt{get_commodity_data(pt, comm)};
908 if (comm_pt == empty_tree)
913 std::cout << comm <<
":\n";
914 show_verbose_quote(comm_pt);
918 std::cout <<
"1 " << comm <<
" = " <<
919 comm_pt.get<
char>(
"last",
"Not Found") <<
" " << to_cur <<
"\n";
921 std::cout << std::endl;
926 parse_quotesource_error(
const std::string& line)
929 if (line ==
"invalid_json\n")
931 err_str += _(
"GnuCash submitted invalid json to Finance::Quote. The details were logged.");
933 else if (line.substr(0, 15) ==
"missing_modules")
935 PERR(
"Missing Finance::Quote Dependencies: %s",
936 line.substr(17).c_str());
937 err_str += _(
"Perl is missing the following modules. Please see https://wiki.gnucash.org/wiki/Online_Quotes#Finance::Quote for detailed corrective action. ");
938 err_str += line.substr(17);
942 PERR(
"Unrecognized Finance::Quote Error %s", line.c_str());
943 err_str +=_(
"Unrecognized Finance::Quote Error: ");
958 get_quotables_helper1 (gpointer value, gpointer data)
960 auto l =
static_cast<CommVec *
> (data);
961 auto comm =
static_cast<gnc_commodity *
> (value);
967 !quote_source || !quote_source_supported)
974 get_quotables_helper2 (gnc_commodity *comm, gpointer data)
976 auto l =
static_cast<CommVec *
> (data);
982 !quote_source || !quote_source_supported)
989 gnc_quotes_get_quotable_commodities (
const gnc_commodity_table *
table)
991 gnc_commodity_namespace * ns = NULL;
992 const char *name_space;
993 GList * nslist, * tmp;
996 const char *expression = gnc_prefs_get_namespace_regexp ();
1002 if (expression && *expression)
1004 if (regcomp (&pattern, expression, REG_EXTENDED | REG_ICASE) != 0)
1011 for (tmp = nslist; tmp; tmp = tmp->next)
1013 name_space =
static_cast<const char *
> (tmp->data);
1014 if (regexec (&pattern, name_space, 0, NULL, 0) == 0)
1021 g_list_foreach (cm_list, &get_quotables_helper1, (gpointer) &l);
1022 g_list_free (cm_list);
1026 g_list_free (nslist);
1044 m_impl = std::make_unique<GncQuotesImpl>();
1054 m_impl->fetch (book);
1059 m_impl->fetch (commodities);
1064 m_impl->fetch (comm);
1070 m_impl->report(source, commodities, verbose);
1075 return m_impl->version ();
1080 return m_impl->sources ();
1083 GncQuotes::~GncQuotes() =
default;
1088 return m_impl->had_failures();
1094 return m_impl->failures();
1098 GncQuotes::report_failures() noexcept
1100 return m_impl->report_failures();
GNCPrice * gnc_price_create(QofBook *book)
gnc_price_create - returns a newly allocated and initialized price with a reference count of 1...
gboolean gnc_commodity_table_foreach_commodity(const gnc_commodity_table *table, gboolean(*f)(gnc_commodity *cm, gpointer user_data), gpointer user_data)
Call a function once for each commodity in the commodity table.
gnc_commodity_table * gnc_commodity_table_get_table(QofBook *book)
Returns the commodity table associated with a book.
gboolean gnc_commodity_is_currency(const gnc_commodity *cm)
Checks to see if the specified commodity is an ISO 4217 recognized currency or a legacy currency...
gchar * gnc_prefs_get_string(const gchar *group, const gchar *pref_name)
Get a string value from the preferences backend.
const char * gnc_commodity_get_mnemonic(const gnc_commodity *cm)
Retrieve the mnemonic for the specified commodity.
utility functions for the GnuCash UI
#define PINFO(format, args...)
Print an informational note.
gboolean gnc_commodity_get_quote_flag(const gnc_commodity *cm)
Retrieve the automatic price quote flag for the specified commodity.
Commodity handling public routines (C++ api)
gboolean gnc_pricedb_add_price(GNCPriceDB *db, GNCPrice *p)
Add a price to the pricedb.
gboolean gnc_quote_source_get_supported(const gnc_quote_source *source)
Given a gnc_quote_source data structure, return the flag that indicates whether this particular quote...
bool had_failures() noexcept
Report if there were quotes requested but not retrieved.
The primary numeric class for representing amounts and values.
const char * gnc_commodity_get_namespace(const gnc_commodity *cm)
Retrieve the namespace for the specified commodity.
#define PERR(format, args...)
Log a serious error.
GList * gnc_commodity_namespace_get_commodity_list(const gnc_commodity_namespace *name_space)
Return a list of all commodity data structures in the specified namespace.
GNCPriceDB * gnc_pricedb_get_db(QofBook *book)
Return the pricedb associated with the book.
gnc_commodity * gnc_default_currency(void)
Return the default currency set by the user.
#define PWARN(format, args...)
Log a warning.
QofBook * qof_session_get_book(const QofSession *session)
Returns the QofBook of this session.
GList * gnc_commodity_table_get_namespaces(const gnc_commodity_table *table)
Return a list of all namespaces in the commodity table.
const QFVec & failures() noexcept
Report the commodities for which quotes were requested but not successfully retrieved.
GncQuotes()
Create a GncQuotes object.
Encapsulate all the information about a dataset.
Generic api to store and retrieve preferences.
gnc_quote_source * gnc_commodity_get_quote_source(const gnc_commodity *cm)
Retrieve the automatic price quote source for the specified commodity.
gnc_commodity_namespace * gnc_commodity_table_find_namespace(const gnc_commodity_table *table, const char *name_space)
This function finds a commodity namespace in the set of existing commodity namespaces.
void report(const char *source, const StrVec &commodities, bool verbose=false)
Report quote results from Finance::Quote to std::cout.
void fetch(QofBook *book)
Fetch quotes for all commodities in our db that have a quote source set.
gint64 time64
Most systems that are currently maintained, including Microsoft Windows, BSD-derived Unixes and Linux...
const char * gnc_quote_source_get_internal_name(const gnc_quote_source *source)
Given a gnc_quote_source data structure, return the internal name of this quote source.
const std::string & version() noexcept
Get the installed Finance::Quote version.
const QuoteSources & sources() noexcept
Get the available Finance::Quote sources as a std::vector.
Commodity handling public routines.
gboolean gnc_commodity_equiv(const gnc_commodity *a, const gnc_commodity *b)
This routine returns TRUE if the two commodities are equivalent.