20 #include <ripple/app/ledger/LedgerMaster.h>
21 #include <ripple/app/main/Application.h>
22 #include <ripple/json/json_value.h>
23 #include <ripple/ledger/ReadView.h>
24 #include <ripple/protocol/ErrorCodes.h>
25 #include <ripple/protocol/jss.h>
26 #include <ripple/rpc/Context.h>
27 #include <ripple/rpc/impl/RPCHelpers.h>
29 #include <boost/bimap.hpp>
30 #include <boost/bimap/multiset_of.hpp>
34 using namespace boost::bimaps;
37 multiset_of<std::uint32_t, std::greater<std::uint32_t>>,
38 multiset_of<STAmount>>;
74 if (prevChain == chain)
77 if (!oracle || f(*oracle) || isNew)
80 if (++history > maxHistory)
90 meta = ledger->txRead(prevTx).second;
106 if (isNew && history == 1)
121 Prices::right_const_iterator
const& begin,
122 Prices::right_const_iterator
const& end)
128 begin, end, avg, [&](
STAmount const& acc,
auto const& it) {
129 return acc + it.first;
135 begin, end, sd, [&](
Number const& acc,
auto const& it) {
136 return acc + (it.first - avg) * (it.first - avg);
138 sd =
root2(sd / (size - 1));
140 return {avg, sd, size};
155 auto const& params(context.
params);
158 if (!params.isMember(jss::oracles))
160 if (!params[jss::oracles].isArray() || params[jss::oracles].size() == 0 ||
161 params[jss::oracles].size() > maxOracles)
167 if (!params.isMember(jss::base_asset))
170 if (!params.isMember(jss::quote_asset))
175 auto getField = [¶ms](
179 if (params.isMember(field))
181 if (!params[field].isConvertibleTo(Json::ValueType::uintValue))
183 return params[field].asUInt();
188 auto const trim = getField(jss::trim);
189 if (std::holds_alternative<error_code_i>(trim))
194 if (params.isMember(jss::trim) &&
195 (std::get<std::uint32_t>(trim) == 0 ||
196 std::get<std::uint32_t>(trim) >
maxTrim))
202 auto const timeThreshold = getField(jss::time_threshold, 0);
203 if (std::holds_alternative<error_code_i>(timeThreshold))
209 auto const& baseAsset = params[jss::base_asset];
210 auto const& quoteAsset = params[jss::quote_asset];
215 for (
auto const& oracle : params[jss::oracles])
217 if (!oracle.isMember(jss::oracle_document_id) ||
218 !oracle.isMember(jss::account))
223 auto const documentID = oracle[jss::oracle_document_id].isConvertibleTo(
224 Json::ValueType::uintValue)
228 parseBase58<AccountID>(oracle[jss::account].asString());
229 if (!account || account->isZero() || !documentID)
240 auto const sle = ledger->read(
keylet::oracle(*account, *documentID));
248 return o.getFieldCurrency(sfBaseAsset).getText() ==
250 o.getFieldCurrency(sfQuoteAsset).getText() ==
252 o.isFieldPresent(sfAssetPrice);
254 iter != series.end())
256 auto const price = iter->getFieldU64(sfAssetPrice);
257 auto const scale = iter->isFieldPresent(sfScale)
258 ? -static_cast<int>(iter->getFieldU8(sfScale))
260 prices.insert(Prices::value_type(
261 node.getFieldU32(sfLastUpdateTime),
262 STAmount{noIssue(), price, scale}));
277 auto const latestTime = prices.left.begin()->first;
278 if (
auto const threshold = std::get<std::uint32_t>(timeThreshold))
283 auto const oldestTime = prices.left.rbegin()->first;
284 auto const upperBound =
285 latestTime > threshold ? (latestTime - threshold) : oldestTime;
286 if (upperBound > oldestTime)
288 prices.left.upper_bound(upperBound), prices.left.end());
296 result[jss::time] = latestTime;
299 auto const [avg, sd,
size] =
300 getStats(prices.right.begin(), prices.right.end());
301 result[jss::entire_set][jss::mean] = avg.getText();
302 result[jss::entire_set][jss::size] =
size;
303 result[jss::entire_set][jss::standard_deviation] =
to_string(sd);
305 auto itAdvance = [&](
auto it,
int distance) {
310 auto const median = [&prices, &itAdvance, &size_ =
size]() {
311 auto const middle = size_ / 2;
312 if ((size_ % 2) == 0)
314 static STAmount two{
noIssue(), 2, 0};
315 auto it = itAdvance(prices.right.begin(), middle - 1);
316 auto const& a1 = it->first;
317 auto const& a2 = (++it)->first;
320 return itAdvance(prices.right.begin(), middle)->first;
322 result[jss::median] = median.getText();
324 if (std::get<std::uint32_t>(trim) != 0)
326 auto const trimCount =
327 prices.size() * std::get<std::uint32_t>(trim) / 100;
330 itAdvance(prices.right.begin(), trimCount),
331 itAdvance(prices.right.end(), -trimCount));
332 result[jss::trimmed_set][jss::mean] = avg.getText();
333 result[jss::trimmed_set][jss::size] =
size;
334 result[jss::trimmed_set][jss::standard_deviation] =
to_string(sd);