Changelog
Source:NEWS.md
Refinitiv 0.2.4
Security & Credential Handling
-
Credential vault isolation (
#15,#2,#5) —refinitiv_vault_set()no longer writesapi_keythrough tooptions(.EikonApiKey)(whilerefinitiv_vault_get("api_key")retains a read-only fallback for legacy callers, and.onLoad()clears.EikonApiKeyafter migrating it into the private vault).DataStreamConnect()now stores credentials in the private vault and passesusernameandpassworddirectly toDatastreamDSWS2R::dsws$new()without writing to or clearingoptions(Datastream.Username)/options(Datastream.Password).TestDataStreamCredentials()now useshttps://, URL-encodes credentials withreserved = TRUE, repeated = TRUE, and scrubs passwords and query strings from diagnostic messages and warnings. -
Token lifecycle & handshake defaults (
#9,#16b,#16c) —rd_handshake()now defaults toforce = FALSE, reusing valid cached bearer tokens instead of re-probing ports and fetching a new token on every call. Routine token expiry checks viard_VerifyToken(..., warn = FALSE)no longer emit a warning during normal refresh,StreamManagerinvalidates the cached token when the server closes theLoginstream (Status: Closed),rd_handshake()usesget_pkg_version()as its fallback version, andget_rdp_streaming_url()no longer sends duplicate HTTP requests.
Streaming, Caching & Request Pipeline Fixes
-
Batched WebSocket frames & dynamic port resolution (
#1,#13,#1) —StreamManager$.process_message()now iterates over all elements in batched OMM JSON array frames instead of dropping multi-element frames, with per-messagetryCatchisolation so a malformed message cannot drop subsequent messages in the same frame. WhenREFINITIV_PORTis unset at load time,streaming_portis leftNULLand resolved dynamically fromgetOption("eikon_port")(with9000Lfallback). Imported%||%fromrlanginNAMESPACEfor full compatibility with R 4.1–4.3. -
HTTP status code validation & fail-fast on permanent 4xx errors (
#6,#14) —send_json_request()now checks HTTP status codes (>= 400), guardsresp_body_json()against non-JSON error responses (such as HTML 502/503 proxy pages), fails immediately on initial synchronousErrorCode == 400responses (while still allowing retry on async ticket-poll400responses), uses5 * (counter + 1L)linear backoff (5s, 10s, 15s, ...), and signalsrefinitiv_http_erroron permanent HTTP 4xx errors (400,401,403,404) soretry()andchunked_download()fail fast without sleeping through chunk retries. -
Input order preservation &
Eikon_Error_Datarow synchronization (#4,#3) — When caching is active,rd_GetData(),EikonGetData(), andEikonGetSymbology()restore the caller’s input instrument order (including repeated instruments) via.restore_input_order(), andEikonGetData()remaps 0-basedEikon_Error_Data$rowindices to match the reorderedPostProcessedEikonGetDatarows (as well as offsettingEikon_Error_Data$rowacross multiple chunks inEikonPostProcessor()). Fixedif (!isFALSE(verbose))on cache hits sooptions(refinitiv_progress = "verbose")no longer errors. -
Chunked download, pagination, date parsing & URL encoding (
#5,#7,#8,#10,#11,#12) —chunked_download()no longer sleepsrefinitiv_chunk_sleepafter the final chunk, preservesNULLchunk returns viaresults[j] <- list(...), and tracks pre-fetch cache hits separately for accurate(N from cache)reporting.rd_GetOwnership()guards unlimited pagination with duplicate-page detection andgetOption("refinitiv_ownership_max_pages", 100L).rd_OutputProcesser()andrd_GetHistory()safely guardas.Date()coercion on date-named columns containing non-ISO strings (e.g."Q1 2023"). Historical pricing endpoints now URL-encodeuniverseand query parameters viabuild_get_query_string(), andmanage_custom_instrumentrespects itsdebugparameter instead of hardcodingdebug = TRUE.
Refinitiv 0.2.3
Robustness & AQL Collapse Recovery
-
Shared AQL collapse detection & adaptive -RIC sub-chunk fallback —
EikonGetData()andrd_GetData()now validate chunks witheikon_data_is_success()and.is_eikon_aql_collapse()to detect collapsed 1-column HTTP 200 payloads (totalColumnsCount <= 1or single unnameddisplayName = NULLheader with cell errorscode %in% c(100, 400, 500, 2504)or"Asynchronous Query library internal error"). When an oversized chunk hits an LSEG AQL collapse (code = 100),.fetch_with_aql_subchunks()automatically splits the chunk into -RIC sub-chunks (getOption("refinitiv_aql_fallback_rics", 25L)), merges the raw payloads via.merge_eikon_raw_subchunks(), and steps down remaining oversized chunks in the same call. -
Multi-period chunk sizing & 10,000-row DataGrid truncation guard —
EikonChunker()now estimates rows per instrument whenDurationor multi-periodParameters(SDate/EDate/FRQ) are supplied so chunks stay below LSEG DataGrid’s 10,000-row limit.EikonGetData()andrd_GetData()emit an explicit warning if any DataGrid chunk returns rows. -
Session cache reference isolation & partial-failure protection —
cache_set()andcache_get()now deep-copy cached objects (data.table::copy(), includingEikonResultwrappers) so caller-side in-placedata.tablemutations (:=,setnames(),setcolorder()) never corrupt cached results. Multi-chunk downloads inEikonGetData(),EikonGetTimeseries(), andrd_GetHistoricalPricing()no longer poison the top-level session cache whenon_failure = "warning"produces partialNAchunks. -
Async ticket poll payload restoration —
send_json_request()restoresjson <- original_jsonbefore retrying after a retriableErrorCode(2504,500,400) so subsequent retries do not send a stale async ticket poll payload.
Bug Fixes
-
rd_GetHistory()data integrity & edge-case fixes — Fixed 0-row merge crashes when either or both endpoints return empty tables, appliedend(EDate) even whenstartisNULL, mappedintervaltoparameters[["FRQ"]](case-insensitively preserving user-suppliedFRQ/Frq), forwardedcache = cacheto underlyingrd_GetData()andrd_GetHistoricalPricing()calls, and replaced..column selection with.SD/.SDcols. -
Intraday
POSIXcttimestamp preservation &"6M"interval handling —PostProcessTimeSeriesRequest(),rd_OutputProcesser(), andrd_GetHistory()now preserve full UTCPOSIXcttimestamps for intraday data ("tick","minute","hour", and ISO 8601 intraday intervals) instead of truncating toDate. Removed silent coercion of unsupported"6m"/"6M"to quarterly"P3M"intranslate_to_iso8601_duration(). -
WebSocket streaming resilience & custom API key propagation — Fixed
StreamManager$.attempt_reconnect()to pass the vaulted access token tord_VerifyToken(), preserved the reconnect attempt counter foron_reconnectedcallbacks before resetting, unregistered reconnect callbacks onStream$close(), inspected OMMState$Data == "Suspect"andState$Stream == "Closed"inStream, wiredStreamManager’sbase_urlto URL construction, and propagated custom API keys from the credential vault acrossrd_handshake()and WebSocket OMM login headers. -
Parser hardening, ADC diagnostics & pagination — Sanitized collapsed all-
NULLheaders inJsonHeaderAnalyzer()andEikonPostProcessor()(Instrument/Column_<idx>) with safe fallback toheaders[["name"]], guardedDateValueColrenaming inrd_OutputProcesser(), tightened date column regex matching ([._]date$|\bdate\b|^date_time$) so columns likeConsolidatedUpdateorMandateare not misclassified as dates, hardenedProcessSymbology()when leading chunk symbols fail to resolve, added unresolved-identifier warnings inprocess_adc_response(), fixed non-zero initialoffsetpagination inrd_GetOwnership(), and vectorizedCheckifCustomInstrument(). -
Interval alias resolution in
rd_GetHistory()—rd_GetHistory()normalizes friendly interval aliases (e.g.,"daily","1d","1D","1min","weekly", etc.) viatranslate_to_iso8601_duration()before forwarding tord_GetHistoricalPricing(). -
Preserve ISO 8601 durations in
translate_to_iso8601_duration()— Valid ISO 8601 duration inputs (such as"PT1M","P1D","PT5M") are preserved instead of defaulting to daily intervals. -
Parse integer
YYYYMMDDdates inrd_OutputProcesser()— Date fields returning numericYYYYMMDDinteger values (such asEXPIR_DATE) are converted toDateobjects correctly instead of treating values as days since epoch.
Refinitiv 0.2.2
Robustness
Survive LSEG proxy connection resets — Under sustained bulk load the LSEG Workspace proxy can forcibly close the TCP connection (
WinError 10054), which surfaces as anhttr2"Recv failure"/"Connection was reset"error thrown before any HTTP response exists.httr2::req_retry()only inspects responses, so it never fired on these transport-level failures and a single reset could fail an entire chunked download.send_json_request()now retriesreq_perform()on genuine connection-reset signatures (WinError 10054,forcibly closed,Connection was reset,Recv/Send failure,reset by peer) with exponential backoff. It deliberately does not matchhttr2’s generic"Failed to perform HTTP request"wrapper, so ordinary errors (connection refused, DNS, timeout) still fail fast.Inter-chunk pacing in
chunked_download()— Adds a configurable sleep between sequential chunk requests and a longer jittered backoff between retry rounds, giving the proxy room to recover after a reset.
New Features
-
Tunable resilience options — All of the new resilience delays are driven by options so they can be tuned in production (or zeroed in tests/CI) without code changes:
refinitiv_reset_retries(default3),refinitiv_reset_base_wait(default5s),refinitiv_reset_max_wait(default30s),refinitiv_chunk_sleep(default1s), andrefinitiv_chunk_backoff(default3s).EikonGetData(),EikonGetTimeseries()andrd_GetData()inherit the singlechunked_download()default rather than carrying their own sleep values.
Refinitiv 0.2.1
New Features
rd_ConvertSymbol()— New function for converting between symbology types (RIC, ISIN, CUSIP, SEDOL, etc.) with multi-tiered fallback: SymbologySearch → EikonGetSymbology → GetData. Supports DelistingDate natively and handles ISIN→RIC, RIC→ISIN, RIC→MIC, and more.fieldsparameter alias inrd_GetData()—fieldscan now be used as an alias forEikonformulas, aligning the R API with thelseg-dataPython library.Eikonformulascontinues to work but is soft-deprecated.EikonResultS3 class —EikonGetData()now returns anEikonResultobject that wraps both data and error components.as.data.table()andas.data.frame()methods extract only the data portion, eliminating recycling warnings. Fully backward-compatible:$PostProcessedEikonGetDataand$Eikon_Error_Datastill work.
Bug Fixes
Cache key canonicalization (STOXX-2) —
cache_key()now sorts character vectors before hashing, ensuring the same set of RICs produces the same cache key regardless of input order. Diagnostic logging available viaoptions(refinitiv_verbose_cache = TRUE).EikonGetSymbology()multi-chunk fix (STOXX-3) —EikonGetSymbology()now correctly combines results across multiple chunks. Previously only the first chunk was processed, silently discarding all subsequent chunks for requests with >300 symbols.
CI/CD & Infrastructure
Windows MAX_PATH fix — Shortened httptest2 API fixture paths in the redactor (
api/rdp/data/→d/,news/v1/top-news→tn, etc.) and removed an accidentally committedRefinitiv.Rcheck/directory (456 files) that contained old long-path fixtures. Package download size dropped from 6.23 MB to 2.83 MB.Installation test workflow — New
install-test.yamlvalidates that the package installs and loads successfully on Ubuntu, macOS, and Windows.Node.js 24 migration — Bumped
actions/checkoutto v5 andcodecov/codecov-actionto v6 (both native Node 24). Removed theFORCE_JAVASCRIPT_ACTIONS_TO_NODE24workaround.Modern installation docs — README now recommends
pak::pak()instead of the deprecateddevtools::install_github()..gitignore/.Rbuildignorehardened — Added*.Rcheck/patterns to prevent future accidental commits of R CMD check output.
Refinitiv 0.2.0
Breaking Changes
-
Python/reticulate support removed. The package now uses direct JSON API calls exclusively. No Python installation is required.
-
install_eikon()andRDPGetOptionAnalytics()are now defunct (.Defunct()). - The
PythonModuleparameter inRDConnect()andEikonConnect()is deprecated and ignored; passing it triggers a warning. -
reticulate,archivist, anddigestremoved from dependencies. - Deleted
R/PyJsonConvertor.r(Python-only helper).
-
New Features
-
Real-time streaming data — New WebSocket-based streaming API via R6 classes (
rd_get_streaming_data(),StreamManager,Stream,StreamDefinition). Supports multi-instrument subscriptions, callbacks (on_refresh,on_update,on_error), data history buffering, summary statistics, and built-in live plotting with Shiny.- Auto-reconnect with jittered exponential backoff and automatic re-subscription on disconnect.
- Sequential stream IDs for multi-instrument support.
- Debug logging via
options(refinitiv_streaming_debug = TRUE).
ESG data —
rd_GetESG()retrieves ESG scores, measures, basic overviews, and universe data with optional fiscal year filtering.Consensus estimates —
rd_GetEstimates()retrieves I/B/E/S actuals, summary, and KPI data across 15 views with package-level validation.Ownership data —
rd_GetOwnership()retrieves consolidated, fund, insider, investor, and organizational ownership data across 19 views with automatic pagination.Session-scoped request cache — Opt-in caching layer (
R/cache.R) with configurable TTLs per function. Enable globally withoptions(refinitiv_cache = TRUE)or per-call withcache = TRUE. Helpers:rd_ClearCache(),rd_CacheInfo(). Instrumented across 13 exported functions.Custom instruments —
rd_ManageCustomInstruments()for create, read, update, and delete operations, including basket instruments with custom holidays.Zero-config connection —
rd_connection()provides a lazy, cached singleton connection. All 20+ API functions default to it, eliminating the need to pass connection objects explicitly.Environment variable configuration — Set
REFINITIV_PORT,REFINITIV_BASE_URL, and/orREFINITIV_APP_KEYin.Renvironfor headless/CI environments. Skips interactive terminal detection.EikonRepairMic()updated — Operating MIC lookup data refreshed for current exchange codes.
Bug Fixes & Improvements
retry()rewritten (C2) — Now accepts a zero-argument closure with iterative loop and exponential backoff. Max attempts configurable viaoptions(refinitiv_max_retries). Previously the retry mechanism did not actually re-execute failed calls.retry()throws on exhaustion by default (#17) —retry()now defaults toon_failure = "stop", throwing an error with the last failure message when all attempts are exhausted. Previously it returnedNAwith a warning, causing confusing downstream errors. Useon_failure = "NA"for thechunked_download()use case where per-chunk success is tracked externally.HTTP 429/503 rate-limit handling (#18) —
send_json_request()now useshttr2::req_retry()to automatically retry on HTTP 429 (Too Many Requests) and 503 (Service Unavailable) responses with jittered exponential backoff. RespectsRetry-Afterheaders in both seconds and HTTP-date formats. Configurable viaoptions(refinitiv_rate_limit_max_wait)(default 60s).Retry architecture documented (#19) — The three-layer retry system (
retry(),httr2::req_retry(), server polling loop) is now documented inARCHITECTURE.mdandretry()roxygen.DELETE dead assignment fixed (#21) — Removed unreachable
results <- NAafterbreakinsend_json_request()DELETE code path.Package version in cache key (#22) —
cache_key()now includes the package version, so a mid-session upgrade automatically invalidates stale cached entries.Credential vault (C3) — Bearer tokens are now stored in a package-private environment invisible to
getOption(),save.image(), andstr(options()), replacing the previous plaintextoptions()storage.Token expiry buffer (H1) —
rd_VerifyToken()subtracts a 60-second buffer (configurable viaoptions(refinitiv_token_buffer_seconds)) before checking token expiration, preventing clock-skew failures.Unified HTTP request handling (H2) — Extracted
build_request()helper to DRY four HTTP method branches insend_json_request(). All methods now use consistent error handling. Debug output changed fromprint()tomessage(). Timeouts normalized to 30s across all methods.Honest handshake identity (H5) — Handshake now identifies as
RefinitivRwith the actual package version, replacing the previously spoofed Python library identity.Hostname leakage fixed (M4) — Streaming login uses
127.0.0.1/localhostinstead of the real hostname.NA_cleaningdefault flipped (M3) —rd_OutputProcesser()now defaults toNA_cleaning = FALSE, matching the behavior of all internal callers.chunked_download()extracted (M2) — Generic chunk-retry coordinator replaces 7 duplicated download-coordinator patterns. Supports parameterized sleep, max retries, success predicates, and configurable failure behavior.Search port bug fixed —
Construct_url("rdp")and bearer token handshake URL now correctly useeikon_portinstead ofrdp_port, fixing silent fallback to port 80 whenrdp_portwasNULL.HistoricalPricing.rsetnamescrash fixed — No longer fails when multiple V-prefixed columns are present in the response.EikonTimeSeriesPreprocessorNA handling — Defensive checks prevent crashes whenDurationorLimitareNA.Int64 overflow fixed (Q1) — Large integer values (exceeding 2^31-1) from LSEG responses are now converted with
as.numeric()instead ofas.integer(), preventing silent truncation.retry()handles NULL returns (A1) —retry()now correctly treatsNULLas a valid return value rather than a failure signal.rd_GetHistory()merge improvements (Q2) — Duplicate column deduplication, granularity mismatch warning, NA-row filtering, andmerge_infoattribute on merged results.Type coercion guard tightened (Q4) — Column type coercion now requires all non-NA values to be parseable before converting.
Optimal chunking (Q5) — Even-distribution chunking eliminates runt chunks and unnecessary extra API calls.
Linear backoff in polling loop (Q6) — Server polling now uses linear backoff between iterations instead of fixed sleep.
UTC timezone standardization (Q7) — All date conversions now explicitly use
tz = "UTC".EikonGetSymbology()multi-chunk fix (STOXX-3) —EikonGetSymbology()now correctly combines results across multiple chunks. Previously,ProcessSymbology()only processed the first chunk, silently discarding all subsequent chunks. Requests with >300 symbols returned only ~250 rows instead of the full result set.
Test Infrastructure
httptest2 integration — 24 fixture sets recorded against live Workspace terminal with a custom redactor (strips auth headers, normalizes port to 9000). All fixtures verified clean of API keys and real ports.
Golden sample cross-validation — 11 scenarios with both R (
golden_samples_r.rds) and Python (truth_lseg_data.json) truth files, 23 cross-validation tests.Test suite results — FAIL 0 | WARN 0 | SKIP 42 | PASS 1382 (1424 expectations, measured via
covr::package_coverage()). 86.8% code coverage across 35 source files. 12 files at 100% coverage.Option pollution fixed — All
options()mutations in tests wrapped withwithr::local_options()to prevent cross-test contamination.