├── .Rbuildignore ├── .github ├── .gitignore └── workflows │ ├── R-CMD-check.yaml │ ├── pkgdown.yaml │ ├── pr-commands.yaml │ ├── rhub.yaml │ └── test-coverage.yaml ├── .gitignore ├── .travis.yml ├── CITATION.cff ├── CRAN-SUBMISSION ├── DESCRIPTION ├── LICENSE ├── LICENSE.md ├── NAMESPACE ├── NEWS.md ├── R ├── crypto_global_quotes.R ├── crypto_history.R ├── crypto_info.R ├── crypto_list.R ├── crypto_listings.R ├── extras.R └── globals.R ├── README.md ├── README.rmd ├── _pkgdown.yml ├── crypto2.Rproj ├── man ├── construct_url.Rd ├── convert_date.Rd ├── crypto_global_quotes.Rd ├── crypto_history.Rd ├── crypto_info.Rd ├── crypto_list.Rd ├── crypto_listings.Rd ├── exchange_info.Rd ├── exchange_list.Rd ├── fiat_list.Rd ├── figures │ ├── README-quotes-plot-1.png │ ├── ccs.png │ ├── crypto2_hex.png │ └── fig.R └── safeFromJSON.Rd ├── revdep ├── .gitignore ├── README.md ├── cran.md ├── failures.md └── problems.md └── tests ├── spelling.R ├── testthat.R └── testthat ├── test-api.R ├── test-crypto-info.R ├── test-global-quotes.R ├── test-history.R ├── test-listings.R ├── test-lists.R └── test_data ├── 4fb1f9d0e9f6acf897a94e7c9e0335b9.json ├── 7597f731a582ffb4e67751af13de407e.json ├── 99837b55ab943600d996822a3a2355d1.json ├── crypto_history_reference.rds ├── crypto_info_reference.rds ├── crypto_list_reference.rds ├── crypto_listings_reference.rds ├── d9e71308b8f7d2956f6151a2ebf9423d.json ├── eacfa0a56f0cb7ea3b22937d92d70a32.json ├── ex_info_reference.rds ├── exchange_list_reference.rds ├── fcce7d762bbee082f65c377f184cd541.json └── historical_output.rds /.Rbuildignore: -------------------------------------------------------------------------------- 1 | ^CRAN-RELEASE$ 2 | ^.*\.Rproj$ 3 | ^.*\.RData$ 4 | ^.*\.Rhistory$ 5 | ^.*\.tar.gz$ 6 | ^\.Rproj\.user$ 7 | ^README.Rmd 8 | crypto-markets.csv 9 | CODE_OF_CONDUCT.md 10 | CONTRIBUTING.md 11 | ISSUE_TEMPLATE.md 12 | cran-comments.md 13 | /inst/ 14 | /data/ 15 | .gitconfig 16 | cmc_apikey 17 | sandbox_cmc_apikey 18 | .travis.yml 19 | ^cran-comments\.md$ 20 | man/figures/ 21 | ^CRAN-SUBMISSION$ 22 | README-quotes-plot-1.png 23 | ^_pkgdown\.yml$ 24 | ^docs$ 25 | ^pkgdown$ 26 | ^\.github$ 27 | ^LICENSE\.md$ 28 | ^CITATION\.cff$ 29 | create_hidden_links.R 30 | ^revdep$ 31 | -------------------------------------------------------------------------------- /.github/.gitignore: -------------------------------------------------------------------------------- 1 | *.html 2 | -------------------------------------------------------------------------------- /.github/workflows/R-CMD-check.yaml: -------------------------------------------------------------------------------- 1 | # Workflow derived from https://github.com/r-lib/actions/tree/v2/examples 2 | # Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help 3 | on: 4 | push: 5 | branches: [main, master] 6 | pull_request: 7 | branches: [main, master] 8 | 9 | name: R-CMD-check 10 | 11 | permissions: read-all 12 | 13 | jobs: 14 | R-CMD-check: 15 | runs-on: ${{ matrix.config.os }} 16 | 17 | name: ${{ matrix.config.os }} (${{ matrix.config.r }}) 18 | 19 | strategy: 20 | fail-fast: false 21 | matrix: 22 | config: 23 | - {os: macos-latest, r: 'release'} 24 | - {os: windows-latest, r: 'release'} 25 | - {os: ubuntu-latest, r: 'devel', http-user-agent: 'release'} 26 | - {os: ubuntu-latest, r: 'release'} 27 | - {os: ubuntu-latest, r: 'oldrel-1'} 28 | 29 | env: 30 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} 31 | R_KEEP_PKG_SOURCE: yes 32 | TZ: 'UTC' 33 | 34 | steps: 35 | - uses: actions/checkout@v4 36 | 37 | - uses: r-lib/actions/setup-pandoc@v2 38 | 39 | - uses: r-lib/actions/setup-r@v2 40 | with: 41 | r-version: ${{ matrix.config.r }} 42 | http-user-agent: ${{ matrix.config.http-user-agent }} 43 | use-public-rspm: true 44 | 45 | - uses: r-lib/actions/setup-r-dependencies@v2 46 | with: 47 | extra-packages: any::rcmdcheck 48 | needs: check 49 | 50 | - uses: r-lib/actions/check-r-package@v2 51 | with: 52 | upload-snapshots: true 53 | build_args: 'c("--no-manual","--compact-vignettes=gs+qpdf")' 54 | -------------------------------------------------------------------------------- /.github/workflows/pkgdown.yaml: -------------------------------------------------------------------------------- 1 | # Workflow derived from https://github.com/r-lib/actions/tree/v2/examples 2 | # Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help 3 | on: 4 | push: 5 | branches: [main, master] 6 | pull_request: 7 | branches: [main, master] 8 | release: 9 | types: [published] 10 | workflow_dispatch: 11 | 12 | name: pkgdown 13 | 14 | jobs: 15 | pkgdown: 16 | runs-on: ubuntu-latest 17 | # Only restrict concurrency for non-PR jobs 18 | concurrency: 19 | group: pkgdown-${{ github.event_name != 'pull_request' || github.run_id }} 20 | env: 21 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} 22 | steps: 23 | - uses: actions/checkout@v2 24 | 25 | - uses: r-lib/actions/setup-pandoc@v2 26 | 27 | - uses: r-lib/actions/setup-r@v2 28 | with: 29 | use-public-rspm: true 30 | 31 | - uses: r-lib/actions/setup-r-dependencies@v2 32 | with: 33 | extra-packages: any::pkgdown, local::. 34 | needs: website 35 | 36 | - name: Build site 37 | run: pkgdown::build_site_github_pages(new_process = FALSE, install = FALSE) 38 | shell: Rscript {0} 39 | 40 | - name: Deploy to GitHub pages 🚀 41 | if: github.event_name != 'pull_request' 42 | uses: JamesIves/github-pages-deploy-action@4.1.4 43 | with: 44 | clean: false 45 | branch: gh-pages 46 | folder: docs 47 | -------------------------------------------------------------------------------- /.github/workflows/pr-commands.yaml: -------------------------------------------------------------------------------- 1 | # Workflow derived from https://github.com/r-lib/actions/tree/v2/examples 2 | # Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help 3 | on: 4 | issue_comment: 5 | types: [created] 6 | 7 | name: Commands 8 | 9 | permissions: read-all 10 | 11 | jobs: 12 | document: 13 | if: ${{ github.event.issue.pull_request && (github.event.comment.author_association == 'MEMBER' || github.event.comment.author_association == 'OWNER') && startsWith(github.event.comment.body, '/document') }} 14 | name: document 15 | runs-on: ubuntu-latest 16 | env: 17 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} 18 | TZ: 'UTC' 19 | steps: 20 | - uses: actions/checkout@v4 21 | 22 | - uses: r-lib/actions/pr-fetch@v2 23 | with: 24 | repo-token: ${{ secrets.GITHUB_TOKEN }} 25 | 26 | - uses: r-lib/actions/setup-r@v2 27 | with: 28 | use-public-rspm: true 29 | 30 | - uses: r-lib/actions/setup-r-dependencies@v2 31 | with: 32 | extra-packages: any::roxygen2 33 | needs: pr-document 34 | 35 | - name: Document 36 | run: roxygen2::roxygenise() 37 | shell: Rscript {0} 38 | 39 | - name: commit 40 | run: | 41 | git config --local user.name "$GITHUB_ACTOR" 42 | git config --local user.email "$GITHUB_ACTOR@users.noreply.github.com" 43 | git add man/\* NAMESPACE 44 | git commit -m 'Document' 45 | 46 | - uses: r-lib/actions/pr-push@v2 47 | with: 48 | repo-token: ${{ secrets.GITHUB_TOKEN }} 49 | 50 | style: 51 | if: ${{ github.event.issue.pull_request && (github.event.comment.author_association == 'MEMBER' || github.event.comment.author_association == 'OWNER') && startsWith(github.event.comment.body, '/style') }} 52 | name: style 53 | runs-on: ubuntu-latest 54 | env: 55 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} 56 | steps: 57 | - uses: actions/checkout@v4 58 | 59 | - uses: r-lib/actions/pr-fetch@v2 60 | with: 61 | repo-token: ${{ secrets.GITHUB_TOKEN }} 62 | 63 | - uses: r-lib/actions/setup-r@v2 64 | 65 | - name: Install dependencies 66 | run: install.packages("styler") 67 | shell: Rscript {0} 68 | 69 | - name: Style 70 | run: styler::style_pkg() 71 | shell: Rscript {0} 72 | 73 | - name: commit 74 | run: | 75 | git config --local user.name "$GITHUB_ACTOR" 76 | git config --local user.email "$GITHUB_ACTOR@users.noreply.github.com" 77 | git add \*.R 78 | git commit -m 'Style' 79 | 80 | - uses: r-lib/actions/pr-push@v2 81 | with: 82 | repo-token: ${{ secrets.GITHUB_TOKEN }} 83 | -------------------------------------------------------------------------------- /.github/workflows/rhub.yaml: -------------------------------------------------------------------------------- 1 | # R-hub's generic GitHub Actions workflow file. It's canonical location is at 2 | # https://github.com/r-hub/actions/blob/v1/workflows/rhub.yaml 3 | # You can update this file to a newer version using the rhub2 package: 4 | # 5 | # rhub::rhub_setup() 6 | # 7 | # It is unlikely that you need to modify this file manually. 8 | 9 | name: R-hub 10 | run-name: "${{ github.event.inputs.id }}: ${{ github.event.inputs.name || format('Manually run by {0}', github.triggering_actor) }}" 11 | 12 | on: 13 | workflow_dispatch: 14 | inputs: 15 | config: 16 | description: 'A comma separated list of R-hub platforms to use.' 17 | type: string 18 | default: 'linux,windows,macos' 19 | name: 20 | description: 'Run name. You can leave this empty now.' 21 | type: string 22 | id: 23 | description: 'Unique ID. You can leave this empty now.' 24 | type: string 25 | 26 | jobs: 27 | 28 | setup: 29 | runs-on: ubuntu-latest 30 | env: 31 | TZ: 'UTC' 32 | outputs: 33 | containers: ${{ steps.rhub-setup.outputs.containers }} 34 | platforms: ${{ steps.rhub-setup.outputs.platforms }} 35 | 36 | steps: 37 | # NO NEED TO CHECKOUT HERE 38 | - uses: r-hub/actions/setup@v1 39 | with: 40 | config: ${{ github.event.inputs.config }} 41 | id: rhub-setup 42 | 43 | linux-containers: 44 | needs: setup 45 | if: ${{ needs.setup.outputs.containers != '[]' }} 46 | runs-on: ubuntu-latest 47 | name: ${{ matrix.config.label }} 48 | strategy: 49 | fail-fast: false 50 | matrix: 51 | config: ${{ fromJson(needs.setup.outputs.containers) }} 52 | container: 53 | image: ${{ matrix.config.container }} 54 | 55 | steps: 56 | - uses: r-hub/actions/checkout@v1 57 | - uses: r-hub/actions/platform-info@v1 58 | with: 59 | token: ${{ secrets.RHUB_TOKEN }} 60 | job-config: ${{ matrix.config.job-config }} 61 | - uses: r-hub/actions/setup-deps@v1 62 | with: 63 | token: ${{ secrets.RHUB_TOKEN }} 64 | job-config: ${{ matrix.config.job-config }} 65 | - uses: r-hub/actions/run-check@v1 66 | with: 67 | token: ${{ secrets.RHUB_TOKEN }} 68 | job-config: ${{ matrix.config.job-config }} 69 | 70 | other-platforms: 71 | needs: setup 72 | if: ${{ needs.setup.outputs.platforms != '[]' }} 73 | runs-on: ${{ matrix.config.os }} 74 | name: ${{ matrix.config.label }} 75 | strategy: 76 | fail-fast: false 77 | matrix: 78 | config: ${{ fromJson(needs.setup.outputs.platforms) }} 79 | 80 | steps: 81 | - uses: r-hub/actions/checkout@v1 82 | - uses: r-hub/actions/setup-r@v1 83 | with: 84 | job-config: ${{ matrix.config.job-config }} 85 | token: ${{ secrets.RHUB_TOKEN }} 86 | - uses: r-hub/actions/platform-info@v1 87 | with: 88 | token: ${{ secrets.RHUB_TOKEN }} 89 | job-config: ${{ matrix.config.job-config }} 90 | - uses: r-hub/actions/setup-deps@v1 91 | with: 92 | job-config: ${{ matrix.config.job-config }} 93 | token: ${{ secrets.RHUB_TOKEN }} 94 | - uses: r-hub/actions/run-check@v1 95 | with: 96 | job-config: ${{ matrix.config.job-config }} 97 | token: ${{ secrets.RHUB_TOKEN }} 98 | -------------------------------------------------------------------------------- /.github/workflows/test-coverage.yaml: -------------------------------------------------------------------------------- 1 | # Workflow derived from https://github.com/r-lib/actions/tree/v2/examples 2 | # Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help 3 | on: 4 | schedule: 5 | - cron: '0 12 * * *' # Runs at 12:00 UTC every day 6 | push: 7 | branches: [main, master] 8 | pull_request: 9 | branches: [main, master] 10 | workflow_dispatch: 11 | 12 | name: test-coverage 13 | 14 | permissions: read-all 15 | 16 | jobs: 17 | test-coverage: 18 | runs-on: ubuntu-latest 19 | env: 20 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} 21 | TZ: 'UTC' 22 | 23 | steps: 24 | - uses: actions/checkout@v4 25 | 26 | - uses: r-lib/actions/setup-r@v2 27 | with: 28 | use-public-rspm: true 29 | 30 | - uses: r-lib/actions/setup-r-dependencies@v2 31 | with: 32 | extra-packages: any::covr, any::xml2 33 | needs: coverage 34 | 35 | - name: Test coverage 36 | run: | 37 | cov <- covr::package_coverage( 38 | quiet = FALSE, 39 | clean = FALSE, 40 | install_path = file.path(normalizePath(Sys.getenv("RUNNER_TEMP"), winslash = "/"), "package") 41 | ) 42 | covr::to_cobertura(cov) 43 | shell: Rscript {0} 44 | 45 | - uses: codecov/codecov-action@v4 46 | with: 47 | fail_ci_if_error: ${{ github.event_name != 'pull_request' && true || false }} 48 | file: ./cobertura.xml 49 | plugin: noop 50 | disable_search: true 51 | token: ${{ secrets.CODECOV_TOKEN }} 52 | 53 | - name: Show testthat output 54 | if: always() 55 | run: | 56 | ## -------------------------------------------------------------------- 57 | find '${{ runner.temp }}/package' -name 'testthat.Rout*' -exec cat '{}' \; || true 58 | shell: bash 59 | 60 | - name: Upload test results 61 | if: failure() 62 | uses: actions/upload-artifact@v4 63 | with: 64 | name: coverage-test-failures 65 | path: ${{ runner.temp }}/package 66 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .Rproj.user 2 | .Rhistory 3 | .RData 4 | .Ruserdata 5 | crypto.Rproj 6 | 7 | /inst/ 8 | .Rbuildignore 9 | cran-comments.md 10 | .gitconfig 11 | cmc_apikey 12 | sandbox_cmc_apikey 13 | R/temp.R 14 | CRAN-RELEASE 15 | docs 16 | 17 | create_hidden_links.R 18 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | # R for travis: see documentation at https://docs.travis-ci.com/user/languages/r 2 | 3 | language: R 4 | sudo: false 5 | cache: packages 6 | -------------------------------------------------------------------------------- /CITATION.cff: -------------------------------------------------------------------------------- 1 | # ----------------------------------------------------------- 2 | # CITATION file created with {cffr} R package, v0.5.0 3 | # See also: https://docs.ropensci.org/cffr/ 4 | # ----------------------------------------------------------- 5 | 6 | cff-version: 1.2.0 7 | message: 'To cite package "crypto2" in publications use:' 8 | type: software 9 | license: MIT 10 | title: 'crypto2: Download Crypto Currency Data from ''CoinMarketCap'' without ''API''' 11 | version: 1.4.6 12 | abstract: Retrieves crypto currency information and historical prices as well as information 13 | on the exchanges they are listed on. Historical data contains daily open, high, 14 | low and close values for all crypto currencies. All data is scraped from 15 | via their 'web-api'. 16 | authors: 17 | - family-names: Stoeckl 18 | given-names: Sebastian 19 | email: sebastian.stoeckl@uni.li 20 | orcid: https://orcid.org/0000-0002-4196-6093 21 | repository: https://CRAN.R-project.org/package=crypto2 22 | repository-code: https://github.com/sstoeckl/crypto2 23 | url: https://sstoeckl.github.io/crypto2/ 24 | date-released: '2024-01-29' 25 | contact: 26 | - family-names: Stoeckl 27 | given-names: Sebastian 28 | email: sebastian.stoeckl@uni.li 29 | orcid: https://orcid.org/0000-0002-4196-6093 30 | references: 31 | - type: software 32 | title: 'R: A Language and Environment for Statistical Computing' 33 | notes: Depends 34 | url: https://www.R-project.org/ 35 | authors: 36 | - name: R Core Team 37 | location: 38 | name: Vienna, Austria 39 | year: '2024' 40 | institution: 41 | name: R Foundation for Statistical Computing 42 | version: '>= 4.0.0' 43 | - type: software 44 | title: dplyr 45 | abstract: 'dplyr: A Grammar of Data Manipulation' 46 | notes: Imports 47 | url: https://dplyr.tidyverse.org 48 | repository: https://CRAN.R-project.org/package=dplyr 49 | authors: 50 | - family-names: Wickham 51 | given-names: Hadley 52 | email: hadley@posit.co 53 | orcid: https://orcid.org/0000-0003-4757-117X 54 | - family-names: François 55 | given-names: Romain 56 | orcid: https://orcid.org/0000-0002-2444-4226 57 | - family-names: Henry 58 | given-names: Lionel 59 | - family-names: Müller 60 | given-names: Kirill 61 | orcid: https://orcid.org/0000-0002-1416-3412 62 | - family-names: Vaughan 63 | given-names: Davis 64 | email: davis@posit.co 65 | orcid: https://orcid.org/0000-0003-4777-038X 66 | year: '2024' 67 | - type: software 68 | title: tibble 69 | abstract: 'tibble: Simple Data Frames' 70 | notes: Imports 71 | url: https://tibble.tidyverse.org/ 72 | repository: https://CRAN.R-project.org/package=tibble 73 | authors: 74 | - family-names: Müller 75 | given-names: Kirill 76 | email: kirill@cynkra.com 77 | orcid: https://orcid.org/0000-0002-1416-3412 78 | - family-names: Wickham 79 | given-names: Hadley 80 | email: hadley@rstudio.com 81 | year: '2024' 82 | - type: software 83 | title: tidyr 84 | abstract: 'tidyr: Tidy Messy Data' 85 | notes: Imports 86 | url: https://tidyr.tidyverse.org 87 | repository: https://CRAN.R-project.org/package=tidyr 88 | authors: 89 | - family-names: Wickham 90 | given-names: Hadley 91 | email: hadley@posit.co 92 | - family-names: Vaughan 93 | given-names: Davis 94 | email: davis@posit.co 95 | - family-names: Girlich 96 | given-names: Maximilian 97 | year: '2024' 98 | - type: software 99 | title: purrr 100 | abstract: 'purrr: Functional Programming Tools' 101 | notes: Imports 102 | url: https://purrr.tidyverse.org/ 103 | repository: https://CRAN.R-project.org/package=purrr 104 | authors: 105 | - family-names: Wickham 106 | given-names: Hadley 107 | email: hadley@rstudio.com 108 | orcid: https://orcid.org/0000-0003-4757-117X 109 | - family-names: Henry 110 | given-names: Lionel 111 | email: lionel@rstudio.com 112 | year: '2024' 113 | - type: software 114 | title: progress 115 | abstract: 'progress: Terminal Progress Bars' 116 | notes: Imports 117 | url: https://github.com/r-lib/progress#readme 118 | repository: https://CRAN.R-project.org/package=progress 119 | authors: 120 | - family-names: Csárdi 121 | given-names: Gábor 122 | email: csardi.gabor@gmail.com 123 | - family-names: FitzJohn 124 | given-names: Rich 125 | year: '2024' 126 | - type: software 127 | title: stats 128 | abstract: 'R: A Language and Environment for Statistical Computing' 129 | notes: Imports 130 | authors: 131 | - name: R Core Team 132 | location: 133 | name: Vienna, Austria 134 | year: '2024' 135 | institution: 136 | name: R Foundation for Statistical Computing 137 | - type: software 138 | title: lubridate 139 | abstract: 'lubridate: Make Dealing with Dates a Little Easier' 140 | notes: Imports 141 | url: https://lubridate.tidyverse.org 142 | repository: https://CRAN.R-project.org/package=lubridate 143 | authors: 144 | - family-names: Spinu 145 | given-names: Vitalie 146 | email: spinuvit@gmail.com 147 | - family-names: Grolemund 148 | given-names: Garrett 149 | - family-names: Wickham 150 | given-names: Hadley 151 | year: '2024' 152 | - type: software 153 | title: jsonlite 154 | abstract: 'jsonlite: A Simple and Robust JSON Parser and Generator for R' 155 | notes: Imports 156 | url: https://jeroen.r-universe.dev/jsonlite 157 | repository: https://CRAN.R-project.org/package=jsonlite 158 | authors: 159 | - family-names: Ooms 160 | given-names: Jeroen 161 | email: jeroen@berkeley.edu 162 | orcid: https://orcid.org/0000-0002-4035-0289 163 | year: '2024' 164 | - type: software 165 | title: cli 166 | abstract: 'cli: Helpers for Developing Command Line Interfaces' 167 | notes: Imports 168 | url: https://cli.r-lib.org 169 | repository: https://CRAN.R-project.org/package=cli 170 | authors: 171 | - family-names: Csárdi 172 | given-names: Gábor 173 | email: csardi.gabor@gmail.com 174 | year: '2024' 175 | - type: software 176 | title: plyr 177 | abstract: 'plyr: Tools for Splitting, Applying and Combining Data' 178 | notes: Imports 179 | url: http://had.co.nz/plyr 180 | repository: https://CRAN.R-project.org/package=plyr 181 | authors: 182 | - family-names: Wickham 183 | given-names: Hadley 184 | email: hadley@rstudio.com 185 | year: '2024' 186 | - type: software 187 | title: spelling 188 | abstract: 'spelling: Tools for Spell Checking in R' 189 | notes: Suggests 190 | url: https://docs.ropensci.org/spelling/ 191 | repository: https://CRAN.R-project.org/package=spelling 192 | authors: 193 | - family-names: Ooms 194 | given-names: Jeroen 195 | email: jeroen@berkeley.edu 196 | orcid: https://orcid.org/0000-0002-4035-0289 197 | - family-names: Hester 198 | given-names: Jim 199 | email: james.hester@rstudio.com 200 | year: '2024' 201 | -------------------------------------------------------------------------------- /CRAN-SUBMISSION: -------------------------------------------------------------------------------- 1 | Version: 2.0.3 2 | Date: 2024-10-11 06:47:30 UTC 3 | SHA: 8a40fba20ca8b5a11ab4e9ad48447dc85924b3e0 4 | -------------------------------------------------------------------------------- /DESCRIPTION: -------------------------------------------------------------------------------- 1 | Package: crypto2 2 | Title: Download Crypto Currency Data from 'CoinMarketCap' without 'API' 3 | Version: 2.0.4 4 | Date: 2024-10-11 5 | Authors@R: c(person("Sebastian", "Stoeckl", email = "sebastian.stoeckl@uni.li", role = c("aut","cre"), comment = c(ORCID = "0000-0002-4196-6093", "Package commissioner and maintainer.")),person("Jesse", "Vent", email = "cryptopackage@icloud.com", role = c("ctb"), comment = c("Creator of the crypto package that provided the idea and basis for this package."))) 6 | Maintainer: Sebastian Stoeckl 7 | URL: https://github.com/sstoeckl/crypto2, 8 | https://sstoeckl.github.io/crypto2/ 9 | Description: Retrieves crypto currency information and historical prices as well as information on the exchanges they are listed on. Historical data contains daily open, high, low and close values for all crypto currencies. All data is scraped from via their 'web-api'. 10 | License: MIT + file LICENSE 11 | Depends: R (>= 4.0.0) 12 | Imports: 13 | dplyr, 14 | tibble, 15 | tidyr, 16 | purrr, 17 | progress, 18 | stats, 19 | lubridate, 20 | jsonlite, 21 | cli, 22 | plyr, 23 | base64enc, 24 | janitor 25 | Encoding: UTF-8 26 | Roxygen: list(markdown = TRUE) 27 | RoxygenNote: 7.3.2 28 | Suggests: 29 | spelling, 30 | testthat (>= 3.0.0), 31 | digest 32 | Language: en-US 33 | Config/testthat/edition: 3 34 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | YEAR: 2022 2 | COPYRIGHT HOLDER: crypto2 authors 3 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # MIT License 2 | 3 | Copyright (c) 2022 crypto2 authors 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /NAMESPACE: -------------------------------------------------------------------------------- 1 | # Generated by roxygen2: do not edit by hand 2 | 3 | export(crypto_global_quotes) 4 | export(crypto_history) 5 | export(crypto_info) 6 | export(crypto_list) 7 | export(crypto_listings) 8 | export(exchange_info) 9 | export(exchange_list) 10 | export(fiat_list) 11 | import(dplyr) 12 | import(progress) 13 | import(purrr) 14 | importFrom(base64enc,base64decode) 15 | importFrom(cli,cat_bullet) 16 | importFrom(dplyr,arrange) 17 | importFrom(dplyr,bind_cols) 18 | importFrom(dplyr,bind_rows) 19 | importFrom(dplyr,distinct) 20 | importFrom(dplyr,join_by) 21 | importFrom(dplyr,left_join) 22 | importFrom(dplyr,mutate) 23 | importFrom(dplyr,rename) 24 | importFrom(dplyr,select) 25 | importFrom(janitor,clean_names) 26 | importFrom(jsonlite,fromJSON) 27 | importFrom(lubridate,mdy) 28 | importFrom(lubridate,today) 29 | importFrom(lubridate,ymd_hms) 30 | importFrom(plyr,laply) 31 | importFrom(stats,na.omit) 32 | importFrom(tibble,as_tibble) 33 | importFrom(tibble,enframe) 34 | importFrom(tibble,rowid_to_column) 35 | importFrom(tibble,tibble) 36 | importFrom(tidyr,expand_grid) 37 | importFrom(tidyr,nest) 38 | importFrom(tidyr,pivot_longer) 39 | importFrom(tidyr,pivot_wider) 40 | importFrom(tidyr,replace_na) 41 | importFrom(tidyr,unnest) 42 | importFrom(tidyr,unnest_wider) 43 | -------------------------------------------------------------------------------- /NEWS.md: -------------------------------------------------------------------------------- 1 | # crypto2 (development version) 2 | 3 | # crypto 2.0.4 4 | 5 | Slight change in api call outcome needed another modification in `crypto_info()`. 6 | 7 | # crypto 2.0.3 8 | 9 | Slight change in api call outcome needed another modification in `crypto_info()`. Also corrected one failing tests to not check time zones. 10 | 11 | # crypto 2.0.2 12 | 13 | Slight change in api call outcome needed another modification in `crypto_info()`. 14 | 15 | # crypto 2.0.1 16 | 17 | Slight change in api call outcome needed a modification in `crypto_info()`. 18 | 19 | # crypto 2.0.0 20 | 21 | After a major change in the api structure of coinmarketcap.com, the package had to be rewritten. As a result, many functions had to be rewritten, because data was not available any more in a similar format or with similar accuracy. Unfortunately, this will potentially break many users implementations. Here is a detailed list of changes: 22 | 23 | - `crypto_list()` has been modified and delivers the same data as before. 24 | - `exchange_list()` has been modified and delivers the same data as before. 25 | - `fiat_list()` has been modified and no longer delivers all available currencies and precious metals (therefore only USD and Bitcoin are available any more). 26 | - `crypto_listings()` needed to be modified, as multiple base currencies are not available any more. Also some of the fields downloaded from CMC might have changed. It still retrieves the latest listings, the new listings as well as historical listings. The fields returned have somewhat slightly changed. Also, no sorting is available any more, so if you want to download the top x CCs by market cap, you have to download all CCs and then sort them in R. 27 | - `crypto_info()` has been modified, as the data structure has changed. The fields returned have somewhat slightly changed. 28 | - `crypto_history()` has been modified. It still retrieves all the OHLC history of all the coins, but is slower due to an increased number of necessary api calls. The number of available intervals is strongly limited, but hourly and daily data is still available. Currently only USD and BTC are available as quote currencies through this library. 29 | - `crypto_global_quotes()` has been modified. It still produces a clear picture of the global market, but the data structure has somewhat slightly changed. 30 | 31 | 32 | # crypto 1.4.6 33 | 34 | Added new options "sort" and "sort_dir" for `crypto_listings()` to allow for the sorting of results, which in combination with "limit" allows, for example, to only download the top 100 CCs according to market capitalization that were listed at a certain date. Correct missing last_historical_data date conversion due to the now missing field. 35 | 36 | # crypto 1.4.5 37 | 38 | Added a new function `crypto_global_quotes()` which retrieves global aggregate market statistics for CMC. There also were some bugs fixed. 39 | 40 | # crypto 1.4.4 41 | 42 | A new function `crypto_listings()` is introduced to retrieve new/latest/historical listings and listing information at CMC. The option `finalWait = TRUE` does not seem to be necessary any more, also `sleep` can be set to '0' seconds. 43 | 44 | # crypto 1.4.3 45 | 46 | change limit==1 bug, add interval parameter (offered by pull-request), also change the amount of id splits to allow for max url length 2000 47 | 48 | # crypto 1.4.2 49 | 50 | Repaired the history retrieval due to the fact that one api call can only retrieve 1000 data points. Therefore we have to call more often on the api when retrieving the entire history. 51 | 52 | # crypto 1.4.1 53 | 54 | Added and corrected a waiter function to wait an additional 60 seconds after the end of the history command before another command could be executed (to not accidentally retrieve the same outdated data). Fixed the waiter. 55 | 56 | # crypto2 1.4.0 57 | 58 | Due to a change in the web-api of CMC we can only make one call to the api per minute (else, it will just deliver the same output as for the first call of the 60 seconds). To reduce the overhang, I have redesigned the interfaces to retrieve as many ids from one api call as possible (limited by the 2000 character limitation of the URL). We can set `requestLimit` to increase/decrease the number of simultaneous ids that are retrieved from CMC. 59 | 60 | # crypto2 1.3.0 61 | 62 | Rewrite of crypto_info and exchange_info to take similar input as crypto_history. Also extensively updated readme. 63 | 64 | # crypto2 1.2.1 65 | 66 | Adapt spelling and '' for CRAN and explain why I have taken Jesse Vent off the package authors (except function names everything else is new) 67 | 68 | # crypto2 1.2.0 69 | 70 | Add Exchange functions, delete unnecessary functions, update readme, prepare for submission to cran 71 | 72 | # crypto2 1.1.3.9000 73 | 74 | * Corrected small error in crypto_info where non-existing slugs led to break of the code (because for some reason I stopped using "Insistent") 75 | 76 | # crypto2 1.1.3.9000 77 | 78 | * Correct a glitch in the tag data, where now not enough group observations are available. Info I have therefore deleted. 79 | * Corrected small error about empty list in coin_info 80 | 81 | # crypto2 1.1.2.9000 82 | 83 | * Added a `NEWS.md` file to track changes to the package. 84 | * Deleted necessary API key from crypto_list(). Now we do not need an api key anymore 85 | -------------------------------------------------------------------------------- /R/crypto_global_quotes.R: -------------------------------------------------------------------------------- 1 | #' Retrieves historical quotes for the global aggregate market 2 | #' 3 | #' This code retrieves global quote data (latest/historic) from coinmarketcap.com. 4 | #' 5 | #' @param which string Shall the code retrieve the latest listing or a historic listing? 6 | #' @param convert string (default: USD) to one or more of available fiat or precious metals prices (`fiat_list()`). If more 7 | #' than one are selected please separate by comma (e.g. "USD,BTC"), only necessary if 'quote=TRUE' 8 | #' @param start_date string Start date to retrieve data from, format 'yyyymmdd' 9 | #' @param end_date string End date to retrieve data from, format 'yyyymmdd', if not provided, today will be assumed 10 | #' @param interval string Interval with which to sample data, default 'daily'. Must be one of `"1d" "2d" "3d" "15d" "30d" "60d"` 11 | #' @param quote logical set to TRUE if you want to include price data (FALSE=default) 12 | #' @param requestLimit integer (default 2200) Maximum number of requests one API call can handle 13 | #' @param sleep integer (default 0) Seconds to sleep between API requests 14 | #' @param wait waiting time before retry in case of fail (needs to be larger than 60s in case the server blocks too many attempts, default=60) 15 | #' @param finalWait to avoid calling the web-api again with another command before 60s are over (TRUE=default) 16 | #' 17 | #' @return List of latest/new/historic listings of crypto currencies in a tibble (depending on the "which"-switch and 18 | #' whether "quote" is requested, the result may only contain some of the following variables): 19 | #' \item{btc_dominance}{number Bitcoin's market dominance percentage by market cap.} 20 | #' \item{eth_dominance}{number Ethereum's market dominance percentage by market cap.} 21 | #' \item{active_cryptocurrencies}{number Count of active crypto currencies tracked by CMC 22 | #' This includes all crypto currencies with a listing_status of "active" or "listed".} 23 | #' \item{total_cryptocurrencies}{number Count of all crypto currencies tracked by CMC 24 | #' This includes "inactive" listing_status crypto currencies.} 25 | #' \item{active_market_pairs}{number Count of active market pairs tracked by CoinMarketCap across all exchanges.}#' 26 | #' \item{active_exchanges}{number Count of active exchanges tracked by CMC This includes all 27 | #' exchanges with a listing_status of "active" or "listed".} 28 | #' \item{total_exchanges}{number Count of all exchanges tracked by CMC 29 | #' This includes "inactive" listing_status exchanges.} 30 | #' \item{last_updated}{Timestamp of when this record was last updated.} 31 | #' \item{total_market_cap}{number The sum of all individual cryptocurrency market capitalizations in the requested currency.} 32 | #' \item{total_volume_24h}{number The sum of rolling 24 hour adjusted volume (as outlined in our methodology) for all 33 | #' crypto currencies in the requested currency.} 34 | #' \item{total_volume_24h_reported}{number The sum of rolling 24 hour reported volume for all crypto currencies in the requested currency.}#' 35 | #' \item{altcoin_volume_24h}{number The sum of rolling 24 hour adjusted volume (as outlined in our methodology) for 36 | #' all crypto currencies excluding Bitcoin in the requested currency.} 37 | #' \item{altcoin_volume_24h_reported}{number The sum of rolling 24 hour reported volume for 38 | #' all crypto currencies excluding Bitcoin in the requested currency.} 39 | #' \item{altcoin_market_cap }{number The sum of all individual cryptocurrency market capitalizations excluding Bitcoin in the requested currency.} 40 | #' 41 | #' @importFrom tibble as_tibble 42 | #' @importFrom jsonlite fromJSON 43 | #' @importFrom dplyr bind_rows mutate rename arrange distinct 44 | #' @importFrom tidyr unnest unnest_wider pivot_wider pivot_longer 45 | #' 46 | #' @examples 47 | #' \dontrun{ 48 | #' # return new listings from the last 30 days 49 | #' new_quotes <- crypto_global_quotes(which="latest", quote=TRUE, convert="BTC") 50 | #' # return all global quotes in the first week of January 2014 51 | #' quotes_2014w1 <- crypto_global_quotes(which="historical", quote=TRUE, 52 | #' start_date = "20140101", end_date="20140107", interval="daily") 53 | #' 54 | #' # report in two different currencies 55 | #' listings_2014w1_USDBTC <- crypto_global_quotes(which="historical", quote=TRUE, 56 | #' start_date = "20200101", end_date="20240530", interval="daily", convert="BTC") 57 | #' } 58 | #' 59 | #' @name crypto_global_quotes 60 | #' 61 | #' @export 62 | #' 63 | crypto_global_quotes <- function(which="latest", convert="USD", start_date = NULL, end_date = NULL, interval = "daily", quote=FALSE, 64 | requestLimit = 2200, sleep=0, wait=60, finalWait = FALSE) { 65 | # check if convert is valid 66 | if (!convert %in% c("USD", "BTC")) { 67 | if (!convert %in% fiat_list()) { 68 | stop("convert must be one of the available currencies, which is BTC or available via fiat_list().") 69 | } 70 | } 71 | # now create convertId from convert 72 | convertId <- ifelse(convert=="USD",2781,1) 73 | # get current coins 74 | quotes_raw <- NULL 75 | if (which=="latest"){ 76 | path <- paste0("web/global-data?convert=",convert) 77 | latest_raw <- safeFromJSON(construct_url(path,v="agg")) 78 | latest_raw1 <- latest_raw$data$metric 79 | latest_raw1[c("quotes","etherscanGas")] <- NULL 80 | global_quotes_raw <- latest_raw1 |> purrr::flatten() %>% 81 | tibble::as_tibble() %>% janitor::clean_names() 82 | global_quotes <- global_quotes_raw 83 | if (quote){ 84 | lquote <- latest_raw$data$metric$quotes %>% tibble::as_tibble() %>% janitor::clean_names() %>% 85 | dplyr::select(-any_of(colnames(global_quotes_raw))) 86 | global_quotes <- global_quotes %>% bind_cols(lquote) %>% unique() 87 | } 88 | } else if (which=="historical"){ 89 | # create dates 90 | if (is.null(start_date)) { start_date <- as.Date("2013-04-28") } 91 | if (is.null(end_date)) { end_date <- lubridate::today() } 92 | # convert dates 93 | start_date <- convert_date(start_date) 94 | end_date <- convert_date(end_date) 95 | # check dates 96 | if (end_date=requestLimit) { 119 | start_dates <- seq(from = as.Date(start_date), by = paste(requestLimit,time_period), length.out = length(dl) %/% requestLimit +1) 120 | end_dates_start <- seq(from = start_dates[2], by = paste(-1,time_period), length.out = 2) 121 | end_dates <- seq(from = end_dates_start[2], by = paste(requestLimit,time_period), length.out = length(dl) %/% requestLimit +1) 122 | if (end_dates[length(end_dates)] > end_date) { 123 | end_dates[length(end_dates)] <- end_date 124 | start_dates <- start_dates[1:length(end_dates)] 125 | } 126 | # UNIX format 127 | # Create UNIX timestamps for download 128 | UNIXstart <- format(as.numeric(as.POSIXct(as.Date(start_dates), format="%Y%m%d")),scientific = FALSE) 129 | UNIXend <- format(as.numeric(as.POSIXct(as.Date(end_dates), format="%Y%m%d", tz = "UTC")),scientific = FALSE) 130 | dates <- tibble::tibble(start_dates,end_dates,startDate=UNIXstart, endDate=UNIXend) 131 | } else { 132 | UNIXstart <- format(as.numeric(as.POSIXct(as.Date(start_date), format="%Y%m%d")),scientific = FALSE) 133 | UNIXend <- format(as.numeric(as.POSIXct(as.Date(end_date), format="%Y%m%d", tz = "UTC")),scientific = FALSE) 134 | dates <- tibble::tibble(start_dates=start_date,end_dates=end_date,startDate=UNIXstart, endDate=UNIXend) 135 | } 136 | # define scraper_function 137 | scrape_web <- function(historyurl){ 138 | page <- safeFromJSON(construct_url(historyurl,v="3")) 139 | pb$tick() 140 | return(page$data) 141 | } 142 | # add history URLs 143 | dates <- dates %>% dplyr::mutate(historyurl=paste0( 144 | "global-metrics/quotes/historical?&convertId=", 145 | convertId, 146 | "&timeStart=", 147 | startDate, 148 | "&timeEnd=", 149 | endDate, 150 | "&interval=", 151 | ifelse(!interval=="daily",interval,"1d") 152 | )) 153 | # define backoff rate 154 | rate <- purrr::rate_delay(pause = wait, max_times = 2) 155 | rate2 <- purrr::rate_delay(sleep) 156 | #rate_backoff(pause_base = 3, pause_cap = 70, pause_min = 40, max_times = 10, jitter = TRUE) 157 | # Modify function to run insistently. 158 | insistent_scrape <- purrr::possibly(purrr::insistently(purrr::slowly(scrape_web, rate2), rate, quiet = FALSE),otherwise=NULL) 159 | # Progress Bar 1 160 | pb <- progress_bar$new(format = ":spin [:current / :total] [:bar] :percent in :elapsedfull ETA: :eta", 161 | total = nrow(dates), clear = FALSE) 162 | message(cli::cat_bullet("Scraping historical global data", bullet = "pointer",bullet_col = "green")) 163 | data <- dates %>% dplyr::mutate(out = purrr::map(historyurl,.f=~insistent_scrape(.x))) 164 | data2 <- data$out 165 | # 2. Here comes the second part: Clean and create dataset 166 | map_scrape <- function(lout){ 167 | pb2$tick() 168 | if (length(lout$quotes)==0){ 169 | cat("\nCoin",lout$name,"does not have data available! Cont to next coin.\n") 170 | } else { 171 | # only one currency possible at this time 172 | outall <- lout$quotes |> tibble::as_tibble() |> dplyr::select(-quote) |> janitor::clean_names() |> 173 | dplyr::mutate(timestamp=as.Date(as.POSIXct(timestamp,tz="UTC"))) |> 174 | dplyr::select(-search_interval) 175 | 176 | if (quote) { 177 | quotes <- lout$quotes$quote |> purrr::list_rbind() |> tibble::as_tibble() |> janitor::clean_names() |> 178 | dplyr::select(-name) |> 179 | dplyr::rename_at(dplyr::vars(2:8),~paste0(convert,"_",.)) |> 180 | dplyr::mutate(timestamp=as.Date(as.POSIXct(timestamp,tz="UTC"))) 181 | outall <- outall |> dplyr::left_join(quotes,by="timestamp") 182 | } 183 | } 184 | return(outall) 185 | } 186 | # Modify function to run insistently. 187 | insistent_map <- purrr::possibly(map_scrape,otherwise=NULL) 188 | # Progress Bar 2 189 | pb2 <- progress_bar$new(format = ":spin [:current / :total] [:bar] :percent in :elapsedfull ETA: :eta", 190 | total = length(data2), clear = FALSE) 191 | message(cli::cat_bullet("Processing historical crypto data", bullet = "pointer",bullet_col = "green")) 192 | global_quotes <- purrr::map(data2,.f = ~ insistent_map(.x)) 193 | #filter 194 | 195 | # results 196 | global_quotes <- dplyr::bind_rows(global_quotes) |> dplyr::arrange(timestamp) 197 | # wait xs before finishing (solving an earlier bug) 198 | if (finalWait){ 199 | pb <- progress_bar$new( 200 | format = "Final wait [:bar] :percent eta: :eta", 201 | total = 60, clear = FALSE, width= 60) 202 | for (i in 1:60) { 203 | pb$tick() 204 | Sys.sleep(1) 205 | } 206 | } 207 | return(global_quotes) 208 | } 209 | } 210 | 211 | -------------------------------------------------------------------------------- /R/crypto_history.R: -------------------------------------------------------------------------------- 1 | #' Get historic crypto currency market data 2 | #' 3 | #' Scrape the crypto currency historic market tables from 4 | #' 'CoinMarketCap' and display 5 | #' the results in a dataframe/tibble. This can be used to conduct 6 | #' analysis on the crypto financial markets or to attempt 7 | #' to predict future market movements or trends. 8 | #' 9 | #' @param coin_list string if NULL retrieve all currently existing coins (`crypto_list()`), 10 | #' or provide list of crypto currencies in the `crypto_list()` or `cryptoi_listings()` format (e.g. current and/or dead coins since 2015) 11 | #' @param convert (default: USD) to one of available fiat prices (`fiat_list()`) or bitcoin 'BTC'. Be aware, that since 2024 only USD and BTC are available here! 12 | #' @param limit integer Return the top n records, default is all tokens 13 | #' @param start_date date Start date to retrieve data from 14 | #' @param end_date date End date to retrieve data from, if not provided, today will be assumed 15 | #' @param interval string Interval with which to sample data, default 'daily'. Must be one of `"hourly" "daily" 16 | #' "1h" "3h" "1d" "7d" "30d"` 17 | #' @param interval string Interval with which to sample data according to what `seq()` needs 18 | #' @param requestLimit limiting the length of request URLs when bundling the api calls 19 | #' @param sleep integer (default 60) Seconds to sleep between API requests 20 | #' @param wait waiting time before retry in case of fail (needs to be larger than 60s in case the server blocks too many attempts, default=60) 21 | #' @param finalWait to avoid calling the web-api again with another command before 60s are over (TRUE=default) 22 | #' @param single_id Download data coin by coin (as of May 2024 this is necessary) 23 | # 24 | #' @return Crypto currency historic OHLC market data in a dataframe and additional information via attribute "info": 25 | #' \item{timestamp}{Timestamp of entry in database} 26 | #' \item{id}{Coin market cap unique id} 27 | #' \item{name}{Coin name} 28 | #' \item{symbol}{Coin symbol} 29 | #' \item{ref_cur_id}{reference Currency id} 30 | #' \item{ref_cur_name}{reference Currency name} 31 | #' \item{open}{Market open} 32 | #' \item{high}{Market high} 33 | #' \item{low}{Market low} 34 | #' \item{close}{Market close} 35 | #' \item{volume}{Volume 24 hours} 36 | #' \item{market_cap}{Market cap - close x circulating supply} 37 | #' \item{time_open}{Timestamp of open} 38 | #' \item{time_close}{Timestamp of close} 39 | #' \item{time_high}{Timestamp of high} 40 | #' \item{time_low}{Timestamp of low} 41 | #' 42 | #' This is the main function of the crypto package. If you want to retrieve 43 | #' ALL active coins then do not pass an argument to `crypto_history()`, alternatively pass the coin name. 44 | #' 45 | #' @importFrom tidyr replace_na expand_grid 46 | #' @importFrom tibble tibble as_tibble rowid_to_column 47 | #' @importFrom cli cat_bullet 48 | #' @importFrom lubridate mdy today 49 | #' @importFrom stats na.omit 50 | #' @importFrom plyr laply 51 | #' @importFrom janitor clean_names 52 | #' 53 | #' @import progress 54 | #' @import purrr 55 | #' @import dplyr 56 | #' 57 | #' @examples 58 | #' \dontrun{ 59 | #' 60 | #' # Retrieving market history for ALL crypto currencies 61 | #' all_coins <- crypto_history(limit = 2) 62 | #' one_coin <- crypto_history(limit = 1, convert="BTC") 63 | #' 64 | #' # Retrieving market history since 2020 for ALL crypto currencies 65 | #' all_coins <- crypto_history(start_date = '2020-01-01',limit=10) 66 | #' 67 | #' # Retrieve 2015 history for all 2015 crypto currencies 68 | #' coin_list_2015 <- crypto_list(only_active=TRUE) %>% 69 | #' dplyr::filter(first_historical_data<="2015-12-31", 70 | #' last_historical_data>="2015-01-01") 71 | #' coins_2015 <- crypto_history(coin_list = coin_list_2015, 72 | #' start_date = "2015-01-01", end_date="2015-12-31", limit=20, interval="30d") 73 | #' # retrieve hourly bitcoin data for 2 days 74 | #' btc_hourly <- crypto_history(coin_list = coin_list_2015, 75 | #' start_date = "2015-01-01", end_date="2015-01-03", limit=1, interval="1h") 76 | #' 77 | #' } 78 | #' 79 | #' @name crypto_history 80 | #' 81 | #' @export 82 | #' 83 | crypto_history <- function(coin_list = NULL, convert="USD", limit = NULL, start_date = NULL, end_date = NULL, interval = NULL, 84 | requestLimit = 400, sleep = 0, wait = 60, finalWait = FALSE, single_id=TRUE) { 85 | # check if convert is valid 86 | if (!convert %in% c("USD", "BTC")) { 87 | if (!convert %in% fiat_list()) { 88 | stop("convert must be one of the available currencies, which is BTC or available via fiat_list().") 89 | } 90 | } 91 | # now create convertId from convert 92 | convertId <- ifelse(convert=="USD",2781,1) 93 | # only if no coins are provided use crypto_list() to provide all actively traded coins 94 | if (is.null(coin_list)) coin_list <- crypto_list() 95 | # limit amount of coins downloaded 96 | if (!is.null(limit)) coin_list <- coin_list[1:limit, ] 97 | # create dates 98 | if (is.null(start_date)) { start_date <- as.Date("2013-04-28") } 99 | if (is.null(end_date)) { end_date <- lubridate::today() } 100 | # convert dates 101 | start_date <- convert_date(start_date) 102 | end_date <- convert_date(end_date) 103 | # check dates 104 | if (end_date% dplyr::distinct(slug) 125 | ids <- coin_list %>% dplyr::distinct(id) |> dplyr::pull(id) 126 | # Create slug_vec with number of elements determined by max length of retrieved datapoints (10000) 127 | if (time_period=="hours"){ 128 | dl <- seq(as.POSIXct(paste0(start_date," 00:00:00")),as.POSIXct(paste0(end_date," 23:00:00")),"hour") 129 | # split time vector in chunks of requestLimit 130 | if (length(dl)>=requestLimit) { 131 | start_dates <- seq(from = as.POSIXct(paste0(start_date," 00:00:00")), by = paste(requestLimit,time_period), 132 | length.out = length(dl) %/% requestLimit +1) 133 | end_dates_start <- seq(from = start_dates[2], by = paste(-1,time_period), length.out = 2) 134 | end_dates <- seq(from = end_dates_start[2], by = paste(requestLimit,time_period), 135 | length.out = length(dl) %/% requestLimit +1) 136 | if (end_dates[length(end_dates)] > end_date) { 137 | end_dates[length(end_dates)] <- as.POSIXct(paste0(end_date," 23:00:00")) 138 | start_dates <- start_dates[1:length(end_dates)] 139 | } 140 | # UNIX format 141 | # Create UNIX timestamps for download 142 | UNIXstart <- format(as.numeric(start_dates),scientific = FALSE) 143 | UNIXend <- format(as.numeric(end_dates),scientific = FALSE) 144 | dates <- tibble::tibble(start_dates,end_dates,startDate=UNIXstart, endDate=UNIXend) 145 | } else { 146 | UNIXstart <- format(as.numeric(as.POSIXct(as.Date(start_date))-1),scientific = FALSE) 147 | UNIXend <- format(as.numeric(as.POSIXct(as.Date(end_date), tz = "UTC")),scientific = FALSE) 148 | dates <- tibble::tibble(start_dates=start_date,end_dates=end_date,startDate=UNIXstart, endDate=UNIXend) 149 | } 150 | } else { 151 | dl <- seq(as.Date(start_date),as.Date(end_date),"day") 152 | # split time vector in chunks of requestLimit 153 | if (length(dl)>=requestLimit) { 154 | start_dates <- seq(from = as.Date(start_date), by = paste(requestLimit,time_period), length.out = length(dl) %/% requestLimit +1) 155 | end_dates_start <- seq(from = start_dates[2], by = paste(-1,time_period), length.out = 2) 156 | end_dates <- seq(from = end_dates_start[2], by = paste(requestLimit,time_period), length.out = length(dl) %/% requestLimit +1) 157 | if (end_dates[length(end_dates)] > end_date) { 158 | end_dates[length(end_dates)] <- end_date 159 | start_dates <- start_dates[1:length(end_dates)] 160 | } 161 | # UNIX format 162 | # Create UNIX timestamps for download 163 | UNIXstart <- format(as.numeric(as.POSIXct(as.Date(start_dates)-1, format="%Y%m%d")),scientific = FALSE) 164 | UNIXend <- format(as.numeric(as.POSIXct(as.Date(end_dates), format="%Y%m%d", tz = "UTC")),scientific = FALSE) 165 | dates <- tibble::tibble(start_dates,end_dates,startDate=UNIXstart, endDate=UNIXend) 166 | } else { 167 | UNIXstart <- format(as.numeric(as.POSIXct(as.Date(start_date)-1, format="%Y%m%d")),scientific = FALSE) 168 | UNIXend <- format(as.numeric(as.POSIXct(as.Date(end_date), format="%Y%m%d", tz = "UTC")),scientific = FALSE) 169 | dates <- tibble::tibble(start_dates=start_date,end_dates=end_date,startDate=UNIXstart, endDate=UNIXend) 170 | } 171 | } 172 | # determine number of splits based on either max 10000 datapoints or max-length of url 173 | if (!single_id) {n <- max(ceiling(nrow(ids)/floor(10000/dl)),ceiling(nrow(ids)/(2000-142)*6))} else {n<-length(ids)} 174 | id_vec <- plyr::laply(split(ids, sort(seq_len(length(ids))%%n)),function(x) paste0(x,collapse=",")) 175 | # create tibble to use 176 | id_vec <- tidyr::expand_grid(id=ids,dates) 177 | 178 | # define scraper_function 179 | scrape_web <- function(historyurl){ 180 | page <- safeFromJSON(construct_url(historyurl,v="3.1")) 181 | pb$tick() 182 | return(page$data) 183 | } 184 | # add history URLs 185 | id_vec <- id_vec %>% dplyr::mutate(historyurl=paste0( 186 | "cryptocurrency/historical?id=", 187 | id, 188 | "&convertId=", 189 | convertId, 190 | "&timeStart=", 191 | startDate, 192 | "&timeEnd=", 193 | endDate, 194 | "&interval=", 195 | interval 196 | )) 197 | # define backoff rate 198 | rate <- purrr::rate_delay(pause = wait, max_times = 2) 199 | rate2 <- purrr::rate_delay(sleep) 200 | #rate_backoff(pause_base = 3, pause_cap = 70, pause_min = 40, max_times = 10, jitter = TRUE) 201 | # Modify function to run insistently. 202 | insistent_scrape <- purrr::possibly(purrr::insistently(purrr::slowly(scrape_web, rate2), rate, quiet = FALSE),otherwise=NULL) 203 | # Progress Bar 1 204 | pb <- progress_bar$new(format = ":spin [:current / :total] [:bar] :percent in :elapsedfull ETA: :eta", 205 | total = nrow(id_vec), clear = FALSE) 206 | message(cli::cat_bullet("Scraping historical crypto data", bullet = "pointer",bullet_col = "green")) 207 | data <- id_vec %>% dplyr::mutate(out = purrr::map(historyurl,.f=~insistent_scrape(.x))) 208 | if (!single_id) {if (nrow(coin_list)==1) {data2 <- data$out} else {data2 <- data$out %>% unlist(.,recursive=FALSE)} 209 | } else { 210 | data2 <- data$out 211 | } 212 | # 2. Here comes the second part: Clean and create dataset 213 | map_scrape <- function(lout){ 214 | pb2$tick() 215 | if (length(lout$quotes)==0){ 216 | cat("\nCoin",lout$name,"does not have data available! Cont to next coin.\n") 217 | } else { 218 | suppressWarnings( 219 | # only one currency possible at this time 220 | outall <- lout$quotes |> tibble::as_tibble() |> tidyr::unnest(quote) |> 221 | dplyr::mutate(across(contains("time"),~as.POSIXlt(.,format="%Y-%m-%dT%H:%M:%S"))) |> 222 | janitor::clean_names() |> 223 | dplyr::mutate(id=lout$id,name=lout$name,symbol=lout$symbol,ref_cur_id=lout$quotes$quote$name,ref_cur_name=convert) |> 224 | dplyr::select(id,name,symbol,timestamp,ref_cur_id,ref_cur_name,everything()) 225 | ) 226 | } 227 | return(outall) 228 | } 229 | # Modify function to run insistently. 230 | insistent_map <- purrr::possibly(map_scrape,otherwise=NULL) 231 | # Progress Bar 2 232 | pb2 <- progress_bar$new(format = ":spin [:current / :total] [:bar] :percent in :elapsedfull ETA: :eta", 233 | total = length(data2), clear = FALSE) 234 | message(cli::cat_bullet("Processing historical crypto data", bullet = "pointer",bullet_col = "green")) 235 | out_info <- purrr::map(data2,.f = ~ insistent_map(.x)) 236 | #filter 237 | 238 | # results 239 | results <-dplyr:: bind_rows(out_info) %>% tibble::as_tibble() %>% 240 | dplyr::left_join(coin_list %>% dplyr::select(id, slug), by ="id") %>% 241 | dplyr::relocate(slug, .after = id) %>% 242 | dplyr::filter(as.Date(timestamp)>=start_date) 243 | # wait 60s before finishing (or you might end up with the web-api 60s bug) 244 | if (finalWait){ 245 | pb <- progress_bar$new( 246 | format = "Final wait [:bar] :percent eta: :eta", 247 | total = 60, clear = FALSE, width= 60) 248 | for (i in 1:60) { 249 | pb$tick() 250 | Sys.sleep(1) 251 | } 252 | } 253 | 254 | return(results) 255 | } 256 | -------------------------------------------------------------------------------- /R/crypto_info.R: -------------------------------------------------------------------------------- 1 | #' Retrieves info (urls, logo, description, tags, platform, date_added, notice, status,...) on CMC for given id 2 | #' 3 | #' This code retrieves data for all specified coins! 4 | #' 5 | #' @param coin_list string if NULL retrieve all currently active coins (`crypto_list()`), 6 | #' or provide list of cryptocurrencies in the `crypto_list()` or `cryptoi_listings()` format (e.g. current and/or dead coins since 2015) 7 | #' @param limit integer Return the top n records, default is all tokens 8 | #' @param requestLimit (default: 1) limiting the length of request URLs when bundling the api calls (currently needs to be 1) 9 | #' @param sleep integer (default: 0) Seconds to sleep between API requests 10 | #' @param finalWait to avoid calling the web-api again with another command before 60s are over (FALSE=default) 11 | #' 12 | #' @return List of (active and historically existing) cryptocurrencies in a tibble: 13 | #' \item{id}{CMC id (unique identifier)} 14 | #' \item{name}{Coin name} 15 | #' \item{symbol}{Coin symbol (not-unique)} 16 | #' \item{slug}{Coin URL slug (unique)} 17 | #' \item{category}{Coin category: "token" or "coin"} 18 | #' \item{description}{Coin description according to CMC} 19 | #' \item{logo}{CMC url of CC logo} 20 | #' \item{status}{Status message from CMC} 21 | #' \item{notice}{Markdown formatted notices from CMC} 22 | #' \item{alert_type}{Type of alert on CMC} 23 | #' \item{alert_link}{Message link to alert} 24 | #' \item{date_added}{Date CC was added to the CMC database} 25 | #' \item{date_launched}{Date CC was launched} 26 | #' \item{is_audited}{Boolean if CC is audited} 27 | #' \item{flags}{Boolean flags for various topics} 28 | #' \item{self_reported_circulating_supply}{Self reported circulating supply} 29 | #' \item{tags}{Tibble of tags and tag categories} 30 | #' \item{faq_description}{FAQ description from CMC} 31 | #' \item{url}{Tibble of various resource urls. Gives website, technical_doc (whitepaper), 32 | #' source_code, message_board, chat, announcement, reddit, twitter, (block) explorer urls} 33 | #' \item{platform}{Metadata about the parent coin if available. Gives id, name, symbol, 34 | #' slug, and token address according to CMC} 35 | #' 36 | #' @importFrom cli cat_bullet 37 | #' @importFrom tibble as_tibble enframe 38 | #' @importFrom jsonlite fromJSON 39 | #' @importFrom tidyr nest 40 | #' @importFrom plyr laply 41 | #' @importFrom tibble enframe 42 | #' @importFrom lubridate ymd_hms 43 | #' 44 | #' @import progress 45 | #' @import purrr 46 | #' @import dplyr 47 | #' 48 | #' @examples 49 | #' \dontrun{ 50 | #' # return info for bitcoin 51 | #' coin_info <- crypto_info(limit=10) 52 | #' } 53 | #' 54 | #' @name crypto_info 55 | #' 56 | #' @export 57 | #' 58 | crypto_info <- function(coin_list = NULL, limit = NULL, requestLimit = 1, sleep = 0, finalWait = FALSE) { 59 | # only if no coins are provided use crypto_list() to provide all actively traded coins 60 | if (is.null(coin_list)) coin_list <- crypto_list() 61 | # limit amount of coins downloaded 62 | if (!is.null(limit)) coin_list <- coin_list[1:limit, ] 63 | # extract slugs & ids 64 | slugs <- coin_list %>% dplyr::distinct(slug) 65 | ids <- coin_list %>% dplyr::distinct(id) 66 | # Create slug_vec with requestLimit elements concatenated together 67 | #n <- ceiling(nrow(ids)/requestLimit) 68 | id_vec <- ids #plyr::laply(split(ids$id, sort(ids$id%%n)),function(x) paste0(x,collapse=",")) 69 | # get current coins 70 | scrape_web <- function(idv){ 71 | path <- paste0("cryptocurrency/detail?id=") 72 | page <- safeFromJSON(construct_url(paste0(path,idv),v=3)) 73 | pb$tick() 74 | return(page$data) 75 | } 76 | if (is.vector(id_vec)) id_vec <- tibble::enframe(id_vec,name = NULL, value = "id") 77 | # define backoff rate 78 | rate <- purrr::rate_delay(pause = 60,max_times = 2) 79 | rate2 <- purrr::rate_delay(sleep) 80 | #rate_backoff(pause_base = 3, pause_cap = 70, pause_min = 40, max_times = 10, jitter = TRUE) 81 | # Modify function to run insistently. 82 | insistent_scrape <- purrr::possibly(purrr::insistently(purrr::slowly(scrape_web, rate2), rate, quiet = FALSE),otherwise=NULL) 83 | # Progress Bar 1 84 | pb <- progress::progress_bar$new(format = ":spin [:current / :total] [:bar] :percent in :elapsedfull ETA: :eta", 85 | total = nrow(id_vec), clear = FALSE) 86 | message(cli::cat_bullet("Scraping crypto info", bullet = "pointer",bullet_col = "green")) 87 | data <- id_vec %>% dplyr::mutate(out = purrr::map(id,.f=~insistent_scrape(.x))) 88 | data2 <- data$out 89 | # 2. Here comes the second part: Clean and create dataset 90 | map_scrape <- function(lout){ 91 | pb2$tick() 92 | if (length(lout)==0){ 93 | cat("\nThis row of the coin vector does not have info available! Cont to next row.\n") 94 | } else { 95 | out_list <- lout2 <- lout |> janitor::clean_names() 96 | out_list[c("quotes","crypto_rating","analysis","earn_list","related_exchanges","holders","urls","related_coins","support_wallet_infos", 97 | "wallets","faq_description","tags","statistics","platforms","volume","cex_volume","dex_volume","volume_change_percentage24h", 98 | "watch_count","watch_list_rating","latest_added","launch_price","audit_infos","similar_coins","coin_bites_video","profile_completion_score")] <- NULL 99 | out_list[sapply(out_list,is.null)] <- NA 100 | out_list <- out_list %>% tibble::as_tibble() 101 | # add 102 | #out_list$status <- c(out$status %>% purrr::flatten() %>% as_tibble() %>% mutate(timestamp=as.POSIXlt(timestamp,format="%Y-%m-%dT%H:%M:%S")) %>% dplyr::pull(timestamp)) 103 | if(!is.null(lout2$tags)) {out_list$tags <- dplyr::pull(tibble(tags=lout2$`tags`) %>% tidyr::nest(tags=everything()))} else {out_list$tags <- NA} 104 | if(!length(lout2$crypto_rating)==0) {out_list$crypto_rating <- dplyr::pull(tibble(crypto_rating=lout2$`crypto_rating`) %>% tidyr::nest(crypto_rating=everything()))} else {out_list$crypto_rating <- NA} 105 | if(!is.null(lout2$urls)) {out_list$urls <- dplyr::pull(tibble(urls=lout2$`urls`) %>% tidyr::nest(urls=everything()))} else {out_list$urls <- NA} 106 | if(!is.null(lout2$faq_description)) {out_list$faq_description <- dplyr::pull(tibble(faq_description=lout2$`faq_description`) %>% tidyr::nest(faq_description=everything()))} else {out_list$faq_description <- NA} 107 | if(!is.null(lout2$platforms)) {out_list$platform <- dplyr::pull(lout2$platforms %>% as_tibble() %>% tidyr::nest(platform=everything()))} else {out_list$platform <- NA} 108 | if(!is_null(lout2$date_launched)) {out_list$date_launched <- as.Date(lubridate::ymd_hms(lout2$date_launched))} else {out_list$date_launched <- NA} 109 | if(!is_null(lout2$date_added)) {out_list$date_added <- as.Date(lubridate::ymd_hms(lout2$date_added))} else {out_list$date_added <- NA} 110 | if(!is_null(lout2$latest_update_time)) {out_list$latest_update_time <- (lubridate::ymd_hms(lout2$latest_update_time))} else {out_list$latest_update_time <- NA} 111 | if(!is_null(lout2$self_reported_circulating_supply)) {out_list$self_reported_circulating_supply <- as.numeric(lout2$self_reported_circulating_supply)} else {out_list$self_reported_circulating_supply <- NA} 112 | # add link to pic 113 | out_list$logo <- paste0("https://s2.coinmarketcap.com/static/img/coins/64x64/",out_list$id,".png") 114 | } 115 | return(out_list) 116 | } 117 | # Modify function to run insistently. 118 | insistent_map <- purrr::possibly(map_scrape,otherwise=NULL) 119 | # Progress Bar 2 120 | pb2 <- progress::progress_bar$new(format = ":spin [:current / :total] [:bar] :percent in :elapsedfull ETA: :eta", 121 | total = length(data2), clear = FALSE) 122 | message(cli::cat_bullet("Processing crypto info", bullet = "pointer",bullet_col = "green")) 123 | out_all <- purrr::map(data2, .f = ~ insistent_map(.x)) 124 | 125 | # results 126 | results <- do.call(bind_rows, out_all) 127 | 128 | 129 | if (length(results) == 0L) stop("No coin info data downloaded.", call. = FALSE) 130 | 131 | # wait 60s before finishing (or you might end up with the web-api 60s bug) 132 | if (finalWait){ 133 | pb <- progress_bar$new( 134 | format = "Final wait [:bar] :percent eta: :eta", 135 | total = 60, clear = FALSE, width= 60) 136 | for (i in 1:60) { 137 | pb$tick() 138 | Sys.sleep(1) 139 | } 140 | } 141 | return(results) 142 | } 143 | #' Retrieves info (urls,logo,description,tags,platform,date_added,notice,status) on CMC for given exchange slug 144 | #' 145 | #' This code uses the web api. It retrieves data for all active, delisted and untracked exchanges! It does not require an 'API' key. 146 | #' 147 | #' @param exchange_list string if NULL retrieve all currently active exchanges (`exchange_list()`), 148 | #' or provide list of exchanges in the `exchange_list()` format (e.g. current and/or delisted) 149 | #' @param limit integer Return the top n records, default is all exchanges 150 | #' @param requestLimit limiting the length of request URLs when bundling the api calls 151 | #' @param sleep integer (default 60) Seconds to sleep between API requests 152 | #' @param finalWait to avoid calling the web-api again with another command before 60s are over (TRUE=default) 153 | #' 154 | #' @return List of (active and historically existing) exchanges in a tibble: 155 | #' \item{id}{CMC exchange id (unique identifier)} 156 | #' \item{name}{Exchange name} 157 | #' \item{slug}{Exchange URL slug (unique)} 158 | #' \item{description}{Exchange description according to CMC} 159 | #' \item{notice}{Exchange notice (markdown formatted) according to CMC} 160 | #' \item{logo}{CMC url of CC logo} 161 | #' \item{type}{Type of exchange} 162 | #' \item{date_launched}{Launch date of this exchange} 163 | #' \item{is_hidden}{TBD} 164 | #' \item{is_redistributable}{TBD} 165 | #' \item{maker_fee}{Exchanges maker fee} 166 | #' \item{taker_fee}{Exchanges maker fee} 167 | #' \item{platform_id}{Platform id on CMC} 168 | #' \item{dex_status}{Decentralized exchange status} 169 | #' \item{wallet_source_status}{Wallet source status} 170 | #' \item{status}{Activity status on CMC} 171 | #' \item{tags}{Tibble of tags and tag categories} 172 | #' \item{urls}{Tibble of various resource urls. Gives website, blog, fee, twitter.} 173 | #' \item{countries}{Tibble of countries the exchange is active in} 174 | #' \item{fiats}{Tibble of fiat currencies the exchange trades in} 175 | #' 176 | #' @importFrom cli cat_bullet 177 | #' @importFrom tibble as_tibble enframe 178 | #' @importFrom jsonlite fromJSON 179 | #' @importFrom tidyr nest 180 | #' @importFrom plyr laply 181 | #' 182 | #' @import progress 183 | #' @import purrr 184 | #' @import dplyr 185 | #' 186 | #' @examples 187 | #' \dontrun{ 188 | #' # return info for the first three exchanges 189 | #' exchange_info <- exchange_info(limit=10) 190 | #' } 191 | #' 192 | #' @name exchange_info 193 | #' 194 | #' @export 195 | #' 196 | exchange_info <- function(exchange_list = NULL, limit = NULL, requestLimit = 1, sleep = 0, finalWait = FALSE) { 197 | # only if no coins are provided use crypto_list() to provide all actively traded coins 198 | if (is.null(exchange_list)) exchange_list <- exchange_list() 199 | # limit amount of exchanges downloaded 200 | if (!is.null(limit)) exchange_list <- exchange_list[1:limit, ] 201 | # extract slugs 202 | slugs <- exchange_list %>% dplyr::distinct(slug) 203 | ids <- exchange_list %>% dplyr::distinct(id) 204 | # Create slug_vec with requestLimit elements concatenated together 205 | #n <- ceiling(nrow(ids)/requestLimit) 206 | id_vec <- slugs #plyr::laply(split(ids$id, sort(ids$id%%n)),function(x) paste0(x,collapse=",")) 207 | # get current coins 208 | scrape_web <- function(idv){ 209 | path <- paste0("exchange/detail?slug=") 210 | page <- safeFromJSON(construct_url(paste0(path,idv),v=3)) 211 | pb$tick() 212 | return(page$data) 213 | } 214 | 215 | # define backoff rate 216 | rate <- purrr::rate_delay(pause = 60,max_times = 2) 217 | rate2 <- purrr::rate_delay(sleep) 218 | #rate_backoff(pause_base = 3, pause_cap = 70, pause_min = 40, max_times = 10, jitter = TRUE) 219 | # Modify function to run insistently. 220 | insistent_scrape <- purrr::possibly(purrr::insistently(purrr::slowly(scrape_web, rate2), rate, quiet = FALSE),otherwise=NULL) 221 | # Progress Bar 1 222 | pb <- progress::progress_bar$new(format = ":spin [:current / :total] [:bar] :percent in :elapsedfull ETA: :eta", 223 | total = nrow(id_vec), clear = FALSE) 224 | message(cli::cat_bullet("Scraping crypto info", bullet = "pointer",bullet_col = "green")) 225 | data <- id_vec %>% dplyr::mutate(out = purrr::map(slug,.f=~insistent_scrape(.x))) 226 | data2 <- data$out 227 | # 2. Here comes the second part: Clean and create dataset 228 | map_scrape <- function(lout){ 229 | pb2$tick() 230 | if (length(lout)==0){ 231 | cat("\nThis row of the exchange vector does not have info available! Cont to next row.\n") 232 | } else { 233 | out_list <- lout2 <- lout |> janitor::clean_names() 234 | out_list[c("tags","quote","countries","por_switch","urls","fiats","net_worth_usd")] <- NULL 235 | out_list[sapply(out_list,is.null)] <- NA 236 | out_list <- out_list %>% tibble::as_tibble() 237 | # add 238 | #out_list$status <- c(out$status %>% purrr::flatten() %>% as_tibble() %>% mutate(timestamp=as.POSIXlt(timestamp,format="%Y-%m-%dT%H:%M:%S")) %>% dplyr::pull(timestamp)) 239 | if(!length(lout2$tags)==0) {out_list$tags <- dplyr::pull(tibble(tags=lout2$`tags`) %>% tidyr::nest(tags=everything()))} else {out_list$tags <- NA} 240 | if(!length(lout2$countries)==0) {out_list$countries <- dplyr::pull(tibble(countries=lout2$`countries`) %>% tidyr::nest(countries=everything()))} else {out_list$countries <- NA} 241 | if(!length(lout2$fiats)==0) {out_list$fiats <- dplyr::pull(tibble(fiats=lout2$`fiats`) %>% tidyr::nest(fiats=everything()))} else {out_list$fiats <- NA} 242 | if(!length(lout2$urls)==0) {out_list$urls <- dplyr::pull(tibble(urls=lout2$`urls`) %>% tidyr::nest(urls=everything()))} else {out_list$urls <- NA} 243 | if(!is_null(lout2$date_launched)) {out_list$date_launched <- as.Date(lubridate::ymd_hms(lout2$date_launched))} else {out_list$date_launched <- NA} 244 | } 245 | return(out_list) 246 | } 247 | # Modify function to run insistently. 248 | insistent_map <- purrr::possibly(map_scrape,otherwise=NULL) 249 | # Progress Bar 2 250 | pb2 <- progress::progress_bar$new(format = ":spin [:current / :total] [:bar] :percent in :elapsedfull ETA: :eta", 251 | total = nrow(ids), clear = FALSE) 252 | message(cli::cat_bullet("Processing exchange info", bullet = "pointer",bullet_col = "green")) 253 | out_all <- purrr::map(data2, .f = ~ insistent_map(.x)) 254 | 255 | # Old code 256 | results <- do.call(rbind, out_all) 257 | 258 | if (length(results) == 0L) stop("No exchange info data downloaded.", call. = FALSE) 259 | 260 | # wait 60s before finishing (or you might end up with the web-api 60s bug) 261 | if (finalWait){ 262 | pb <- progress_bar$new( 263 | format = "Final wait [:bar] :percent eta: :eta", 264 | total = 60, clear = FALSE, width= 60) 265 | for (i in 1:60) { 266 | pb$tick() 267 | Sys.sleep(1) 268 | } 269 | } 270 | 271 | return(results) 272 | } 273 | -------------------------------------------------------------------------------- /R/crypto_list.R: -------------------------------------------------------------------------------- 1 | #' Retrieves name, CMC id, symbol, slug, rank, an activity flag as well as activity dates on CMC for all coins 2 | #' 3 | #' This code uses the web api. It retrieves data for all historic and all active coins and does not require an 'API' key. 4 | #' 5 | #' @param only_active Shall the code only retrieve active coins (TRUE=default) or include inactive coins (FALSE) 6 | #' @param add_untracked Shall the code additionally retrieve untracked coins (FALSE=default) 7 | #' 8 | #' @return List of (active and historically existing) cryptocurrencies in a tibble: 9 | #' \item{id}{CMC id (unique identifier)} 10 | #' \item{name}{Coin name} 11 | #' \item{symbol}{Coin symbol (not-unique)} 12 | #' \item{slug}{Coin URL slug (unique)} 13 | #' \item{rank}{Current rank on CMC (if still active)} 14 | #' \item{is_active}{Flag showing whether coin is active (1), inactive(0) or untracked (-1)} 15 | #' \item{first_historical_data}{First time listed on CMC} 16 | #' \item{last_historical_data}{Last time listed on CMC, *today's date* if still listed} 17 | #' 18 | #' @importFrom tibble as_tibble 19 | #' @importFrom dplyr bind_rows mutate rename arrange distinct 20 | #' 21 | #' @examples 22 | #' \dontrun{ 23 | #' # return all coins 24 | #' active_list <- crypto_list(only_active=TRUE) 25 | #' all_but_untracked_list <- crypto_list(only_active=FALSE) 26 | #' full_list <- crypto_list(only_active=FALSE,add_untracked=TRUE) 27 | #' 28 | #' # return all coins active in 2015 29 | #' coin_list_2015 <- active_list %>% 30 | #' dplyr::filter(first_historical_data<="2015-12-31", 31 | #' last_historical_data>="2015-01-01") 32 | #' } 33 | #' 34 | #' @name crypto_list 35 | #' 36 | #' @export 37 | #' 38 | crypto_list <- function(only_active=TRUE, add_untracked=FALSE) { 39 | # get current coins 40 | path <- paste0("cryptocurrency/map") 41 | active_coins <- safeFromJSON(construct_url(path)) 42 | coins <- active_coins$data %>% tibble::as_tibble() %>% 43 | dplyr::mutate(dplyr::across(c(first_historical_data,last_historical_data),as.Date)) 44 | 45 | if (!only_active){ 46 | path <- paste0("cryptocurrency/map?listing_status=inactive") 47 | inactive_coins <- safeFromJSON(construct_url(path)) 48 | date_cols <- inactive_coins$data %>% 49 | select_if(is.character) %>% 50 | select(where(~ !any(is.na(as.Date(., format = "%Y-%m-%d", tryFormats = c("%Y-%m-%d", "%Y/%m/%d")))))) 51 | 52 | data_formatted <- inactive_coins$data %>% 53 | mutate(across(all_of(names(date_cols)), ~ as.Date(., format = "%Y-%m-%d", tryFormats = c("%Y-%m-%d", "%Y/%m/%d")))) 54 | coins <- dplyr::bind_rows(coins, 55 | data_formatted %>% tibble::as_tibble() %>% 56 | dplyr::arrange(id)) 57 | } 58 | if (add_untracked){ 59 | path <- paste0("cryptocurrency/map?listing_status=untracked") 60 | untracked_coins <- safeFromJSON(construct_url(path)) 61 | date_cols <- untracked_coins$data %>% 62 | select_if(is.character) %>% 63 | select(where(~ !any(is.na(as.Date(., format = "%Y-%m-%d", tryFormats = c("%Y-%m-%d", "%Y/%m/%d")))))) 64 | 65 | data_formatted <- untracked_coins$data %>% 66 | mutate(across(all_of(names(date_cols)), ~ as.Date(., format = "%Y-%m-%d", tryFormats = c("%Y-%m-%d", "%Y/%m/%d")))) 67 | coins <- dplyr::bind_rows(coins, 68 | data_formatted %>% tibble::as_tibble() %>% 69 | dplyr::arrange(id)) 70 | } 71 | return(coins %>% dplyr::select(id:last_historical_data) %>% dplyr::distinct() %>% dplyr::arrange(id)) 72 | } 73 | #' Retrieves name, CMC id, symbol, slug, rank, an activity flag as well as activity dates on CMC for all coins 74 | #' 75 | #' This code uses the web api. It retrieves data for all historic and all active exchanges and does not require an 'API' key. 76 | #' 77 | #' @param only_active Shall the code only retrieve active exchanges (TRUE=default) or include inactive coins (FALSE) 78 | #' @param add_untracked Shall the code additionally retrieve untracked exchanges (FALSE=default) 79 | #' 80 | #' @return List of (active and historically existing) exchanges in a tibble: 81 | #' \item{id}{CMC exchange id (unique identifier)} 82 | #' \item{name}{Exchange name} 83 | #' \item{slug}{Exchange URL slug (unique)} 84 | #' \item{is_active}{Flag showing whether exchange is active (1), inactive(0) or untracked (-1)} 85 | #' \item{first_historical_data}{First time listed on CMC} 86 | #' \item{last_historical_data}{Last time listed on CMC, *today's date* if still listed} 87 | #' 88 | #' @importFrom tibble as_tibble 89 | #' @importFrom dplyr bind_rows mutate rename arrange distinct 90 | #' 91 | #' @examples 92 | #' \dontrun{ 93 | #' # return all exchanges 94 | #' ex_active_list <- exchange_list(only_active=TRUE) 95 | #' ex_all_but_untracked_list <- exchange_list(only_active=FALSE) 96 | #' ex_full_list <- exchange_list(only_active=FALSE,add_untracked=TRUE) 97 | #' } 98 | #' 99 | #' @name exchange_list 100 | #' 101 | #' @export 102 | #' 103 | exchange_list <- function(only_active=TRUE, add_untracked=FALSE) { 104 | # get current coins 105 | path <- paste0("exchange/map") 106 | active_exchanges <- safeFromJSON(construct_url(path)) 107 | exchanges <- active_exchanges$data %>% tibble::as_tibble() %>% 108 | dplyr::mutate(dplyr::across(c(first_historical_data,last_historical_data),as.Date)) 109 | 110 | if (!only_active){ 111 | path <- paste0("exchange/map?listing_status=inactive") 112 | inactive_exchanges <- safeFromJSON(construct_url(path)) 113 | exchanges <- dplyr::bind_rows(exchanges, 114 | inactive_exchanges$data %>% tibble::as_tibble() %>% 115 | dplyr::mutate(dplyr::across(c(first_historical_data,last_historical_data),as.Date))) %>% 116 | dplyr::arrange(id) 117 | } 118 | if (add_untracked){ 119 | path <- paste0("exchange/map?listing_status=untracked") 120 | untracked_exchanges <- safeFromJSON(construct_url(path)) 121 | exchanges <- dplyr::bind_rows(exchanges, 122 | untracked_exchanges$data %>% tibble::as_tibble() %>% 123 | dplyr::mutate(dplyr::across(c(first_historical_data,last_historical_data),as.Date),is_active=-1)) %>% 124 | dplyr::arrange(id) 125 | } 126 | return(exchanges %>% dplyr::select(id:last_historical_data) %>% dplyr::distinct() %>% dplyr::arrange(id)) 127 | } 128 | #' Retrieves list of all CMC supported fiat currencies available to convert cryptocurrencies 129 | #' 130 | #' This code retrieves data for all available fiat currencies that are available on the website. 131 | #' 132 | #' @param include_metals Shall the results include precious metals (TRUE) or not (FALSE=default). 133 | #' Update: As of May 2024 no more metals are included in this file 134 | #' 135 | #' @return List of (active and historically existing) cryptocurrencies in a tibble: 136 | #' \item{id}{CMC id (unique identifier)} 137 | #' \item{symbol}{Coin symbol (not-unique)} 138 | #' \item{name}{Coin name} 139 | #' \item{sign}{Fiat currency sign} 140 | #' 141 | #' @importFrom tibble as_tibble 142 | #' 143 | #' @examples 144 | #' \dontrun{ 145 | #' # return fiat currencies available through the CMC api 146 | #' fiat_list <- fiat_list() 147 | #' } 148 | #' 149 | #' @name fiat_list 150 | #' 151 | #' @export 152 | #' 153 | fiat_list <- function(include_metals=FALSE) { 154 | # get current coins 155 | if (!include_metals){ 156 | fiat_url <- paste0("fiat/map") 157 | active_fiat <- safeFromJSON(construct_url(fiat_url)) 158 | fiats <- active_fiat$data %>% tibble::as_tibble() 159 | } else { 160 | fiat_url <- paste0("fiat/map?include_metals=",include_metals) 161 | active_fiat <- safeFromJSON(construct_url(fiat_url)) 162 | fiats <- active_fiat$data %>% tibble::as_tibble() 163 | } 164 | return(fiats) 165 | } 166 | -------------------------------------------------------------------------------- /R/crypto_listings.R: -------------------------------------------------------------------------------- 1 | #' Retrieves name, CMC id, symbol, slug, rank, an activity flag as well as activity dates on CMC for all coins 2 | #' 3 | #' This code retrieves listing data (latest/new/historic). 4 | #' 5 | #' @param which string Shall the code retrieve the latest listing, the new listings or a historic listing? 6 | #' @param convert string (default: USD) to one of available fiat prices (`fiat_list()`). If more 7 | #' than one are selected please separate by comma (e.g. "USD,BTC"), only necessary if 'quote=TRUE' 8 | #' @param limit integer Return the top n records 9 | #' @param start_date string Start date to retrieve data from, format 'yyyymmdd' 10 | #' @param end_date string End date to retrieve data from, format 'yyyymmdd', if not provided, today will be assumed 11 | #' @param interval string Interval with which to sample data according to what `seq()` needs 12 | #' @param quote logical set to TRUE if you want to include price data (FALSE=default) 13 | #' @param sort (May 2024: currently not available) string use to sort results, possible values: "name", "symbol", "market_cap", "price", 14 | #' "circulating_supply", "total_supply", "max_supply", "num_market_pairs", "volume_24h", 15 | #' "volume_7d", "volume_30d", "percent_change_1h", "percent_change_24h", 16 | #' "percent_change_7d". Especially useful if you only want to download the top x entries using "limit" (deprecated for "new") 17 | #' @param sort_dir (May 2024: currently not available) string used to specify the direction of the sort in "sort". Possible values are "asc" (DEFAULT) and "desc" 18 | #' @param sleep integer (default 0) Seconds to sleep between API requests 19 | #' @param wait waiting time before retry in case of fail (needs to be larger than 60s in case the server blocks too many attempts, default=60) 20 | #' @param finalWait to avoid calling the web-api again with another command before 60s are over (TRUE=default) 21 | #' 22 | #' @return List of latest/new/historic listings of cryptocurrencies in a tibble (depending on the "which"-switch and 23 | #' whether "quote" is requested, the result may only contain some of the following variables): 24 | #' \item{id}{CMC id (unique identifier)} 25 | #' \item{name}{Coin name} 26 | #' \item{symbol}{Coin symbol (not-unique)} 27 | #' \item{slug}{Coin URL slug (unique)} 28 | #' \item{date_added}{Date when the coin was added to the dataset} 29 | #' \item{last_updated}{Last update of the data in the database} 30 | #' \item{rank}{Current rank on CMC (if still active)} 31 | #' \item{market_cap}{market cap - close x circulating supply} 32 | #' \item{market_cap_by_total_supply}{market cap - close x total supply} 33 | #' \item{market_cap_dominance}{market cap dominance} 34 | #' \item{fully_diluted_market_cap}{fully diluted market cap} 35 | #' \item{self_reported_market_cap}{is the source of the market cap self-reported} 36 | #' \item{self_reported_circulating_supply}{is the source of the circulating supply self-reported} 37 | #' \item{tvl_ratio}{percentage of total value locked} 38 | #' \item{price}{latest average price} 39 | #' \item{circulating_supply}{approx. number of coins in circulation} 40 | #' \item{total_supply}{approx. total amount of coins in existence right now (minus any coins that have been verifiably burned)} 41 | #' \item{max_supply}{CMC approx. of max amount of coins that will ever exist in the lifetime of the currency} 42 | #' \item{num_market_pairs}{number of market pairs across all exchanges this coin} 43 | #' \item{tvl}{total value locked} 44 | #' \item{volume_24h}{Volume 24 hours} 45 | #' \item{volume_change_24h}{Volume change in 24 hours} 46 | #' \item{percent_change_1h}{1 hour return} 47 | #' \item{percent_change_24h}{24 hour return} 48 | #' \item{percent_change_7d}{7 day return} 49 | #' \item{percent_change_30d}{30 day return} 50 | #' \item{percent_change_60d}{60 day return} 51 | #' \item{percent_change_90d}{90 day return} 52 | #' 53 | #' @importFrom tibble as_tibble enframe 54 | #' @importFrom dplyr bind_rows mutate rename arrange distinct left_join select bind_cols join_by 55 | #' @importFrom tidyr unnest 56 | #' @importFrom janitor clean_names 57 | #' 58 | #' @examples 59 | #' \dontrun{ 60 | #' # return new listings from the last 30 days 61 | #' new_listings <- crypto_listings(which="new", quote=FALSE, limit=50000) 62 | #' new_listings2 <- crypto_listings(which="new", quote=TRUE, convert="BTC") 63 | #' 64 | #' # return latest listing (last available data of all CC including quotes) 65 | #' latest_listings <- crypto_listings(which="latest", quote=FALSE, limit=50000) 66 | #' latest_listings2 <- crypto_listings(which="latest", quote=TRUE, convert="BTC") 67 | #' 68 | #' # return the first 10 listings in the first week of January 2024 69 | #' listings_2024w1 <- crypto_listings(which="historical", quote=TRUE, 70 | #' start_date = "20240101", end_date="20240102", interval="day", limit=10) 71 | #' 72 | #' # only download the top 10 crypto currencies based on their market capitalization 73 | #' # DOES NOT WORK ANY MORE 74 | #' 75 | #' # for historically accurate snapshots (e.g. for backtesting crypto investments) 76 | #' # you need to download the entire history on one day including price information: 77 | #' listings_20200202 <- crypto_listings(which="historical", quote=TRUE, 78 | #' start_date="20200202", end_date="20200202") 79 | #' listings_20240202 <- crypto_listings(which="historical", quote=TRUE, 80 | #' start_date="20240202", end_date="20240202", limit=50000) 81 | #' # note the much larger amount in CCs in 2024, as well as the existence of many 82 | #' more variables in the dataset 83 | #' } 84 | #' 85 | #' @name crypto_listings 86 | #' 87 | #' @export 88 | #' 89 | crypto_listings <- function(which="latest", convert="USD", limit = 5000, start_date = NULL, end_date = NULL, 90 | interval = "day", quote=FALSE, sort="cmc_rank", sort_dir="asc", sleep = 0, wait = 60, finalWait = FALSE) { 91 | # now create convertId from convert 92 | convertId <- ifelse(convert=="USD",2781,1) 93 | # get current coins 94 | if (which=="new"){ 95 | listing_raw <- NULL 96 | limitdl <- 500 97 | limitend <- ifelse(limit%%limitdl==0,limit%/%limitdl,limit%/%limitdl+1) 98 | for (i in 1:limitend){ 99 | new_url <- paste0("cryptocurrency/spotlight?dataType=8&limit=",limitdl,"&convertId=",convertId,"&sort_dir=",sort_dir,"&start=",(i-1)*limitdl+1) 100 | new_raw <- safeFromJSON(construct_url(new_url,v=3)) 101 | listing_raw <- bind_rows(listing_raw, 102 | new_raw$data$recentlyAddedList %>% tibble::as_tibble() |> janitor::clean_names() %>% 103 | dplyr::rename(date_added=added_date) |> 104 | dplyr::select(-platforms) |> 105 | dplyr::mutate(dplyr::across(c(date_added),as.Date)) %>% 106 | dplyr::arrange(id)) 107 | if (nrow(new_raw$data$recentlyAddedList)% dplyr::select(-price_change) %>% unique() 110 | if (quote){ 111 | lquote <- listing_raw %>% dplyr::select(price_change) %>% tidyr::unnest(price_change) %>% tidyr::unnest(everything(), names_sep="_") |> janitor::clean_names() |> 112 | dplyr::mutate(dplyr::across(c(last_update),as.Date)) 113 | listing <- listing_raw %>% dplyr::select(-price_change) %>% dplyr::bind_cols(lquote) %>% unique() 114 | } 115 | } else if (which=="latest"){ 116 | listing_raw <- NULL 117 | limitdl <- 5000 118 | limitend <- ifelse(limit%%limitdl==0,limit%/%limitdl,limit%/%limitdl+1) 119 | for (i in 1:limitend){ 120 | latest_url <- paste0("cryptocurrency/listing?limit=",limitdl, 121 | "&convertId=", 122 | convertId,"&sort=",sort,"&sort_dir=",sort_dir,"&start=",(i-1)*limitdl+1) 123 | latest_raw <- safeFromJSON(construct_url(latest_url,v=3)) 124 | listing_raw <- bind_rows(listing_raw, 125 | latest_raw$data$cryptoCurrencyList %>% tibble::as_tibble() |> janitor::clean_names() %>% 126 | dplyr::mutate(dplyr::across(c(last_updated),as.Date)) %>% 127 | dplyr::select(-any_of(c("badges","audit_info_list","is_audited","platform"))) |> 128 | dplyr::arrange(id)) 129 | if (nrow(latest_raw$data$cryptoCurrencyList)% dplyr::select(-quotes,-tags) %>% unique() 132 | if (quote){ 133 | lquote <- listing_raw %>% dplyr::select(quotes) %>% tidyr::unnest(quotes) %>% tidyr::unnest(everything(), names_sep="_") |> janitor::clean_names() |> 134 | dplyr::mutate(dplyr::across(c(last_updated),as.Date)) 135 | listing <- listing_raw %>% dplyr::select(-any_of(c("quotes","tags"))) %>% 136 | dplyr::bind_cols(lquote |> dplyr::select(ref_currency=name,everything(),-last_updated)) %>% unique() 137 | } 138 | } else if (which=="historical"){ 139 | if (is.null(start_date)) { start_date <- "20130428" } 140 | sdate <- as.Date(start_date, format="%Y%m%d") 141 | if (is.null(end_date)) { end_date <- gsub("-", "", lubridate::today()) } 142 | edate <- as.Date(end_date, format="%Y%m%d") 143 | dates <- seq(sdate, edate, by=interval) 144 | tbdate <- tibble::enframe(dates[which(dates% rename(date=value) %>% 145 | mutate(historyurl = paste0("cryptocurrency/listings/historical?date=",date, 146 | "&limit=",limit,"&convertId=",convertId,"&sort=",sort,"&sort_dir=",sort_dir,"&start=")) 147 | # scraping tools 148 | scrape_web <- function(historyurl,quote){ 149 | listing_raw <- NULL 150 | limitdl <- 5000 151 | limitend <- ifelse(limit%%limitdl==0,limit%/%limitdl,limit%/%limitdl+1) 152 | for (i in 1:limitend){ 153 | history_url <- paste0(historyurl,(i-1)*limitdl+1) 154 | history_raw <- safeFromJSON(construct_url(history_url,v=3)) 155 | listing_raw <- bind_rows(listing_raw, 156 | history_raw$data %>% tibble::as_tibble() |> janitor::clean_names() %>% 157 | dplyr::mutate(dplyr::across(c(date_added,last_updated),as.Date)) %>% 158 | dplyr::arrange(id)) 159 | if (nrow(history_raw$data)% dplyr::select(-any_of(c("tags","quotes","platform"))) %>% unique() 162 | if (quote){ 163 | lquote <- listing_raw %>% dplyr::select(quotes) %>% tidyr::unnest(quotes) %>% tidyr::unnest(everything(), names_sep="_") |> janitor::clean_names() 164 | listing <- listing_raw %>% dplyr::select(-any_of(c("tags","quotes","platform"))) %>% 165 | dplyr::bind_cols(lquote |> dplyr::select(-any_of(c("name","last_updated")))) %>% unique() 166 | } 167 | pb$tick() 168 | return(listing) 169 | } 170 | # define backoff rate 171 | rate <- purrr::rate_delay(pause = wait, max_times = 2) 172 | rate2 <- purrr::rate_delay(sleep) 173 | #rate_backoff(pause_base = 3, pause_cap = 70, pause_min = 40, max_times = 10, jitter = TRUE) 174 | # Modify function to run insistently. 175 | insistent_scrape <- purrr::possibly(purrr::insistently(purrr::slowly(scrape_web, rate2), rate, quiet = FALSE),otherwise=NULL) 176 | # Progress Bar 1 177 | pb <- progress_bar$new(format = ":spin [:current / :total] [:bar] :percent in :elapsedfull ETA: :eta", 178 | total = length(dates), clear = TRUE) 179 | message(cli::cat_bullet("Scraping historical listings", bullet = "pointer",bullet_col = "green")) 180 | data <- tbdate %>% dplyr::mutate(out = purrr::map(historyurl,.f=~insistent_scrape(.x, quote))) 181 | # Modify massive dataframe 182 | listing <- data %>% select(-historyurl) %>% tidyr::unnest(out) 183 | } 184 | # wait 60s before finishing (or you might end up with the web-api 60s bug) 185 | if (finalWait){ 186 | pb <- progress_bar$new( 187 | format = "Final wait [:bar] :percent eta: :eta", 188 | total = 60, clear = FALSE, width= 60) 189 | for (i in 1:60) { 190 | pb$tick() 191 | Sys.sleep(1) 192 | } 193 | } 194 | return(listing) 195 | } 196 | -------------------------------------------------------------------------------- /R/extras.R: -------------------------------------------------------------------------------- 1 | #' URL Creator 2 | #' 3 | #' @param path A path to append to the base URL 4 | #' 5 | #' @return A full URL string 6 | #' @keywords internal 7 | #' 8 | #' @importFrom base64enc base64decode 9 | #' 10 | construct_url <- function(path,v=1) { 11 | if (v==1){ 12 | base <- rawToChar(base64enc::base64decode("aHR0cHM6Ly9hcGkuY29pbm1hcmtldGNhcC5jb20vZGF0YS1hcGkvdjEv")) 13 | } else if (v==3) { 14 | base <- rawToChar(base64enc::base64decode("aHR0cHM6Ly9hcGkuY29pbm1hcmtldGNhcC5jb20vZGF0YS1hcGkvdjMv")) 15 | } else if (v=="3.1") { 16 | base <- rawToChar(base64enc::base64decode("aHR0cHM6Ly9hcGkuY29pbm1hcmtldGNhcC5jb20vZGF0YS1hcGkvdjMuMS8=")) 17 | } else if (v=="agg") { 18 | base <- rawToChar(base64enc::base64decode("aHR0cHM6Ly9hcGkuY29pbm1hcmtldGNhcC5jb20vYWdnci92My8=")) 19 | } 20 | return(paste0(base, path)) 21 | } 22 | #' Parses json data from a string without revealing information about the source in case of an error 23 | #' 24 | #' @param ... 25 | #' 26 | #' @return A parsed JSON object 27 | #' @keywords internal 28 | #' 29 | #' @importFrom jsonlite fromJSON 30 | #' 31 | safeFromJSON <- function(...) { 32 | result <- withCallingHandlers( 33 | tryCatch({ 34 | # Attempt to parse JSON with suppressed warnings and messages 35 | suppressWarnings(suppressMessages({ 36 | jsonlite::fromJSON(...) 37 | })) 38 | }, error = function(e) { 39 | # Return a custom, simpler error object 40 | simpleError("Unfortunately, the scraper could not find data with the sent api-call. If you believe this is a bug please report at https://github.com/sstoeckl/crypto2/issues") 41 | }), 42 | warning = function(w) { 43 | # Intercept warnings and handle them silently 44 | invokeRestart("muffleWarning") 45 | } 46 | ) 47 | # Check if result is an error and stop if it is 48 | if (inherits(result, "error")) { 49 | stop(result$message, call. = FALSE) 50 | } 51 | result 52 | } 53 | #' checks the dater format (old/new) and converts to date 54 | #' 55 | #' @param date_str a date string 56 | #' 57 | #' @return a correct date 58 | #' @keywords internal 59 | #' 60 | convert_date <- function(date_str) { 61 | # Remove all non-digit characters to check the format 62 | clean_date_str <- gsub("[^0-9]", "", date_str) 63 | 64 | # Determine the length of the string to guess the format 65 | if (nchar(clean_date_str) == 8) { 66 | # Assume the format is YYYYMMDD 67 | date_obj <- as.Date(clean_date_str, format = "%Y%m%d") 68 | } else if (nchar(date_str) == 10 && grepl("-", date_str)) { 69 | # Assume the format is YYYY-MM-DD 70 | date_obj <- as.Date(date_str, format = "%Y-%m-%d") 71 | } else { 72 | # Return NA if the format is not recognized 73 | warning("Date format not recognized. Expected 'YYYYMMDD' or 'YYYY-MM-DD'.") 74 | date_obj <- NA 75 | } 76 | 77 | return(date_obj) 78 | } 79 | -------------------------------------------------------------------------------- /R/globals.R: -------------------------------------------------------------------------------- 1 | utils::globalVariables(c("id","name","symbol","slug","rank","is_active","first_historical_data","last_historical_data","timestamp", 2 | "close_ratio","coins","hist_date","history_url","market","Date","Name","Symbol","ref_cur","historyurl","finalWait","interval", 3 | "name","name_main","platform_locale","slug","slug_main","symbol","value","volume",".","code","sign", 4 | "date_added", "last_updated","tags","platform","out","total_market_cap","total_volume_24h_yesterday_percentage_change","VAR", 5 | "ref_cur_id","ref_cur_name","sleep","wait","added_date","endDate","last_update","startDate","platforms","price_change","quotes", 6 | "search_interval")) 7 | -------------------------------------------------------------------------------- /README.rmd: -------------------------------------------------------------------------------- 1 | --- 2 | output: github_document 3 | --- 4 | 5 | 6 | 7 | ```{r, echo = FALSE} 8 | knitr::opts_chunk$set( 9 | collapse = TRUE, 10 | comment = "#>", 11 | fig.path = "man/figures/README-" 12 | ) 13 | ``` 14 | 15 | # crypto2 16 | 17 | 18 | 19 | [![Project Status](https://www.repostatus.org/badges/latest/active.svg)](https://www.repostatus.org/#active) 20 | [![R-CMD-check](https://github.com/sstoeckl/crypto2/actions/workflows/R-CMD-check.yaml/badge.svg)](https://github.com/sstoeckl/crypto2/actions/workflows/R-CMD-check.yaml) 21 | [![test-coverage](https://github.com/sstoeckl/crypto2/actions/workflows/test-coverage.yaml/badge.svg)](https://github.com/sstoeckl/crypto2/actions/workflows/test-coverage.yaml) 22 | [![pr-commands](https://github.com/sstoeckl/crypto2/actions/workflows/pr-commands.yaml/badge.svg)](https://github.com/sstoeckl/crypto2/actions/workflows/pr-commands.yaml) 23 | [![CRAN_latest_release_date](https://www.r-pkg.org/badges/last-release/crypto2)](https://cran.r-project.org/package=crypto2) 24 | [![CRAN status](https://www.r-pkg.org/badges/version/crypto2)](https://CRAN.R-project.org/package=crypto2) 25 | [![CRAN downloads](http://cranlogs.r-pkg.org/badges/grand-total/crypto2)](https://cran.r-project.org/package=crypto2) 26 | [![CRAN downloads last month](http://cranlogs.r-pkg.org/badges/crypto2)](https://cran.r-project.org/package=crypto2) 27 | [![CRAN downloads last week](http://cranlogs.r-pkg.org/badges/last-week/crypto2)](https://cran.r-project.org/package=crypto2) 28 | [![Lifecycle: stable](https://img.shields.io/badge/lifecycle-stable-brightgreen.svg)](https://lifecycle.r-lib.org/articles/stages.html#stable) 29 | [![Website - pkgdown](https://img.shields.io/website-up-down-green-red/https/sstoeckl.github.io/crypto2.svg)](https://sstoeckl.github.io/crypto2/) 30 | 31 | 32 | # Historical Cryptocurrency Prices for Active and Delisted Tokens! 33 | 34 | This is a modification of the original `crypto` package by [jesse vent](https://github.com/JesseVent/crypto). It is entirely set up to use means from the `tidyverse` and provides `tibble`s with all data available via the web-api of [coinmarketcap.com](https://coinmarketcap.com/). **It does not require an API key but in turn only provides information that is also available through the website of [coinmarketcap.com](https://coinmarketcap.com/).** 35 | 36 | It allows the user to retrieve 37 | 38 | - `crypto_listings()` a list of all coins that were historically listed on CMC (main dataset to avoid delisting bias) according to the [CMC API documentation](https://coinmarketcap.com/api/documentation/v1/#operation/getV1CryptocurrencyListingsHistorical) 39 | - `crypto_list()` a list of all coins that are listed as either being *active*, *delisted* or *untracked* according to the [CMC API documentation](https://coinmarketcap.com/api/documentation/v1/#operation/getV1CryptocurrencyMap) 40 | - `crypto_info()` a list of all information available for all available coins according to the [CMC API documentation](https://coinmarketcap.com/api/documentation/v1/#operation/getV1CryptocurrencyInfo) 41 | - `crypto_history()` the **most powerful** function of this package that allows to download the entire available history for all coins covered by CMC according to the [CMC API documentation](https://coinmarketcap.com/api/documentation/v1/#operation/getV1CryptocurrencyOhlcvHistorical) 42 | - `crypto_global_quotes()` a dataset of historical global crypto currency market metrics to the [CMC API documentation](https://coinmarketcap.com/api/documentation/v1/#operation/getV1GlobalmetricsQuotesHistorical) 43 | - `fiat_list()` a mapping of all fiat currencies (plus precious metals) available via the [CMC WEB API](https://coinmarketcap.com/api/documentation/v1/#operation/getV1FiatMap) 44 | - `exchange_list()` a list of all exchanges available as either being *active*, *delisted* or *untracked* according to the [CMC API documentation](https://coinmarketcap.com/api/documentation/v1/#operation/getV1ExchangeMap) 45 | - `exchange_info()` a list of all information available for all given exchanges according to the [CMC API documentation](https://coinmarketcap.com/api/documentation/v1/#operation/getV1ExchangeInfo) 46 | 47 | # Update 48 | 49 | ## Version 2.0.2 (August 2024) 50 | 51 | Slight change in api output broke `crypto_info()` (new additional column). Fixed. 52 | 53 | ## Version 2.0.1 (July 2024) 54 | 55 | Slight change in api output broke `crypto_info()`. Fixed. 56 | 57 | ## Version 2.0.0 (May 2024) 58 | 59 | After a major change in the api structure of coinmarketcap.com, the package had to be rewritten. As a result, many functions had to be rewritten, because data was not available any more in a similar format or with similar accuracy. Unfortunately, this will potentially break many users implementations. Here is a detailed list of changes: 60 | 61 | - `crypto_list()` has been modified and delivers the same data as before. 62 | - `exchange_list()` has been modified and delivers the same data as before. 63 | - `fiat_list()` has been modified and no longer delivers all available currencies and precious metals (therefore only USD and Bitcoin are available any more). 64 | - `crypto_listings()` needed to be modified, as multiple base currencies are not available any more. Also some of the fields downloaded from CMC might have changed. It still retrieves the latest listings, the new listings as well as historical listings. The fields returned have somewhat slightly changed. Also, no sorting is available any more, so if you want to download the top x CCs by market cap, you have to download all CCs and then sort them in R. 65 | - `crypto_info()` has been modified, as the data structure has changed. The fields returned have somewhat slightly changed. 66 | - `crypto_history()` has been modified. It still retrieves all the OHLC history of all the coins, but is slower due to an increased number of necessary api calls. The number of available intervals is strongly limited, but hourly and daily data is still available. Currently only USD and BTC are available as quote currencies through this library. 67 | - `crypto_global_quotes()` has been modified. It still produces a clear picture of the global market, but the data structure has somewhat slightly changed. 68 | 69 | ## Version 1.4.7 70 | 71 | Since version 1.4.6 I have added the possibility to "sort" the historical `crypto_listings()` in _asc_ending or _desc_ending order ("sort_dir") to allow for the possibility to download only the top x crypto currencies using "limit" based on the requested sort (not available for "new" sorting). Also corrected some problems when sourcing lists that now do not have the "last_historical_data" field available any more. 72 | 73 | Since version 1.4.5 I have added a new function `crypto_global_quotes()` which retrieves global aggregate market statistics for CMC. There also were some bugs fixed. 74 | 75 | Since version 1.4.4 a new function `crypto_listings()` was introduced that retrieves new/latest/historical listings and listing information at CMC. Additionally some aspects of the other functions have been reworked. We noticed that `finalWait = TRUE` does not seem to be necessary at the moment, as well as `sleep` can be set to '0' seconds. If you experience strange behavior this might be due to the the api sending back strange (old) results. In this case let `sleep = 60` (the default) and `finalWait = TRUE` (the default). 76 | 77 | Since version 1.4.0 the package has been reworked to retrieve as many assets as possible with one api call, as there is a new "feature" introduced by CMC to send back the initially requested data for each api call within 60 seconds. So one needs to wait 60s before calling the api again. Additionally, since version v1.4.3 the package allows for a data `interval` larger than daily (e.g. '2d' or '7d' or 'weekly') 78 | 79 | ## Installation 80 | 81 | You can install `crypto2` from CRAN with 82 | ```{r cran-installation, eval = FALSE} 83 | install.packages("crypto2") 84 | ``` 85 | or directly from github with: 86 | ```{r gh-installation, eval = FALSE} 87 | # install.packages("devtools") 88 | devtools::install_github("sstoeckl/crypto2") 89 | ``` 90 | 91 | ## Package Contribution 92 | 93 | The package provides API free and efficient access to all information from that is also available through their website. It uses a variety of modification and web-scraping tools from the `tidyverse` (especially `purrr`). 94 | 95 | As this provides access not only to **active** coins but also to those that have now been **delisted** and also those that are categorized as **untracked**, including historical pricing information, this package provides a valid basis for any **Asset Pricing Studies** based on crypto currencies that require **survivorship-bias-free** information. In addition to that, the package maintainer is currently working on also providing **delisting returns** (similarly to CRSP for stocks) to also eliminate the **delisting bias**. 96 | 97 | ## Package Usage 98 | 99 | First we load the `crypto2`-package and download the set of active coins from (additionally one could load delisted coins with `only_Active=FALSE` as well as untracked coins with `add_untracked=TRUE`). 100 | 101 | ```{r example} 102 | library(crypto2) 103 | library(dplyr) 104 | 105 | # List all active coins 106 | coins <- crypto_list(only_active=TRUE) 107 | ``` 108 | 109 | Next we download information on the first three coins from that list. 110 | 111 | ```{r example-info} 112 | # retrieve information for all (the first 3) of those coins 113 | coin_info <- crypto_info(coins, limit=3, finalWait=FALSE) 114 | 115 | # and give the first two lines of information per coin 116 | coin_info 117 | ``` 118 | 119 | In a next step we show the logos of the three coins as provided by . 120 | 121 | ```{r logos, echo=FALSE, fig.show='hold', fig.align='center', out.width = '5%',out.height='5%'} 122 | coin_info$logo %>% knitr::include_graphics(.) 123 | ``` 124 | 125 | In addition we show tags provided by . 126 | 127 | ```{r tags} 128 | coin_info %>% select(slug,tags) %>% tidyr::unnest(tags) %>% group_by(slug) %>% slice(1,n()) 129 | ``` 130 | 131 | Additionally: Here are some urls pertaining to these coins as provided by . 132 | 133 | ```{r urls} 134 | coin_info %>% pull(urls) %>% .[[1]] |> unlist() 135 | ``` 136 | 137 | In a next step we download time series data for these coins. 138 | 139 | ```{r history} 140 | # retrieve historical data for all (the first 3) of them 141 | coin_hist <- crypto_history(coins, limit=3, start_date="20210101", end_date="20210105", finalWait=FALSE) 142 | 143 | # and give the first two times of information per coin 144 | coin_hist %>% group_by(slug) %>% slice(1:2) 145 | ``` 146 | 147 | Similarly, we could download data on an hourly basis. 148 | 149 | ```{r historym} 150 | # retrieve historical data for all (the first 3) of them 151 | coin_hist_m <- crypto_history(coins, limit=3, start_date="20210101", end_date="20210102", interval ="1h", finalWait=FALSE) 152 | 153 | # and give the first two times of information per coin 154 | coin_hist_m %>% group_by(slug) %>% slice(1:2) 155 | ``` 156 | 157 | Alternatively, we could determine the price of these coins in other currencies. A list of such currencies is available as `fiat_list()` 158 | 159 | ```{r fiat} 160 | fiats <- fiat_list() 161 | fiats 162 | ``` 163 | 164 | So we download the time series again depicting prices in terms of Bitcoin and Euro (note that multiple currencies can be given to `convert`, separated by ","). 165 | 166 | ```{r history2} 167 | # retrieve historical data for all (the first 3) of them 168 | coin_hist2 <- crypto_history(coins, convert="USD", limit=3, start_date="20210101", end_date="20210105", finalWait=FALSE) 169 | 170 | # and give the first two times of information per coin 171 | coin_hist2 %>% group_by(slug,ref_cur_name) %>% slice(1:2) 172 | ``` 173 | 174 | As a new features in version 1.4.4. we introduced the possibility to download historical listings and listing information (add `quote = TRUE`). 175 | 176 | ```{r listings} 177 | latest_listings <- crypto_listings(which="latest", limit=10, quote=TRUE, finalWait=FALSE) 178 | latest_listings 179 | ``` 180 | 181 | An additional feature that was added in version 1.4.5 retrieves global aggregate market statistics for CMC. 182 | 183 | ```{r quotes} 184 | all_quotes <- crypto_global_quotes(which="historical", quote=TRUE) 185 | all_quotes 186 | ``` 187 | We can use those quotes to plot information on the aggregate market capitalization: 188 | 189 | ```{r quotes-plot} 190 | all_quotes %>% select(timestamp, USD_total_market_cap, USD_altcoin_market_cap) %>% 191 | tidyr::pivot_longer(cols = 2:3, names_to = "Market Cap", values_to = "bn. USD") %>% 192 | tidyr::separate(`Market Cap`,into = c("Currency","Type","Market","Cap")) %>% 193 | dplyr::mutate(`bn. USD`=`bn. USD`/1000000000) %>% 194 | ggplot2::ggplot(ggplot2::aes(x=timestamp,y=`bn. USD`,color=Type)) + ggplot2::geom_line() + 195 | ggplot2::labs(title="Market capitalization in bn USD", subtitle="CoinMarketCap.com") 196 | 197 | ``` 198 | 199 | 200 | Last and least, one can get information on exchanges. For this download a list of active/inactive/untracked exchanges using `exchange_list()`: 201 | 202 | ```{r exchanges} 203 | exchanges <- exchange_list(only_active=TRUE) 204 | exchanges 205 | ``` 206 | 207 | and then download information on "binance" and "bittrex": 208 | 209 | ```{r exchange-info} 210 | ex_info <- exchange_info(exchanges %>% filter(slug %in% c('binance','kraken')), finalWait=FALSE) 211 | ex_info 212 | ``` 213 | 214 | Then we can access information on the fee structure, 215 | 216 | ```{r fee} 217 | ex_info %>% select(contains("fee")) 218 | ``` 219 | 220 | or the fiat currencies allowed: 221 | 222 | ```{r ex-fiat} 223 | ex_info %>% select(slug,fiats) %>% tidyr::unnest(fiats) 224 | ``` 225 | 226 | ### Author/License 227 | 228 | - **Sebastian Stöckl** - Package Creator, Modifier & Maintainer - [sstoeckl on github](https://github.com/sstoeckl) 229 | 230 | This project is licensed under the MIT License - see the 231 | file for details 232 | 233 | ### Acknowledgments 234 | 235 | - Thanks to the team at for the great work they do, especially to [Alice Liu (Research Lead)](https://www.linkedin.com/in/alicejingliu/) and [Aaron K.](https://www.linkedin.com/in/aaroncwk/) for their support with regard to information on delistings. 236 | - Thanks to Jesse Vent for providing the (not fully research compatible) [`crypto`](https://github.com/JesseVent/crypto)-package that inspired this package. 237 | 238 | -------------------------------------------------------------------------------- /_pkgdown.yml: -------------------------------------------------------------------------------- 1 | url: https://sstoeckl.github.io/crypto2/ 2 | template: 3 | bootstrap: 5 4 | bootswatch: spacelab 5 | navbar: 6 | bg: primary 7 | -------------------------------------------------------------------------------- /crypto2.Rproj: -------------------------------------------------------------------------------- 1 | Version: 1.0 2 | ProjectId: 6273d64d-47bb-479c-b6f3-bc66c863df44 3 | 4 | RestoreWorkspace: Default 5 | SaveWorkspace: Default 6 | AlwaysSaveHistory: Default 7 | 8 | EnableCodeIndexing: Yes 9 | UseSpacesForTab: Yes 10 | NumSpacesForTab: 2 11 | Encoding: UTF-8 12 | 13 | RnwWeave: Sweave 14 | LaTeX: pdfLaTeX 15 | 16 | AutoAppendNewline: Yes 17 | StripTrailingWhitespace: Yes 18 | 19 | BuildType: Package 20 | PackageUseDevtools: Yes 21 | PackageInstallArgs: --no-multiarch --with-keep.source 22 | PackageCheckArgs: --as-cran 23 | PackageRoxygenize: rd,collate,namespace,vignette 24 | -------------------------------------------------------------------------------- /man/construct_url.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/extras.R 3 | \name{construct_url} 4 | \alias{construct_url} 5 | \title{URL Creator} 6 | \usage{ 7 | construct_url(path, v = 1) 8 | } 9 | \arguments{ 10 | \item{path}{A path to append to the base URL} 11 | } 12 | \value{ 13 | A full URL string 14 | } 15 | \description{ 16 | URL Creator 17 | } 18 | \keyword{internal} 19 | -------------------------------------------------------------------------------- /man/convert_date.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/extras.R 3 | \name{convert_date} 4 | \alias{convert_date} 5 | \title{checks the dater format (old/new) and converts to date} 6 | \usage{ 7 | convert_date(date_str) 8 | } 9 | \arguments{ 10 | \item{date_str}{a date string} 11 | } 12 | \value{ 13 | a correct date 14 | } 15 | \description{ 16 | checks the dater format (old/new) and converts to date 17 | } 18 | \keyword{internal} 19 | -------------------------------------------------------------------------------- /man/crypto_global_quotes.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/crypto_global_quotes.R 3 | \name{crypto_global_quotes} 4 | \alias{crypto_global_quotes} 5 | \title{Retrieves historical quotes for the global aggregate market} 6 | \usage{ 7 | crypto_global_quotes( 8 | which = "latest", 9 | convert = "USD", 10 | start_date = NULL, 11 | end_date = NULL, 12 | interval = "daily", 13 | quote = FALSE, 14 | requestLimit = 2200, 15 | sleep = 0, 16 | wait = 60, 17 | finalWait = FALSE 18 | ) 19 | } 20 | \arguments{ 21 | \item{which}{string Shall the code retrieve the latest listing or a historic listing?} 22 | 23 | \item{convert}{string (default: USD) to one or more of available fiat or precious metals prices (\code{fiat_list()}). If more 24 | than one are selected please separate by comma (e.g. "USD,BTC"), only necessary if 'quote=TRUE'} 25 | 26 | \item{start_date}{string Start date to retrieve data from, format 'yyyymmdd'} 27 | 28 | \item{end_date}{string End date to retrieve data from, format 'yyyymmdd', if not provided, today will be assumed} 29 | 30 | \item{interval}{string Interval with which to sample data, default 'daily'. Must be one of \verb{"1d" "2d" "3d" "15d" "30d" "60d"}} 31 | 32 | \item{quote}{logical set to TRUE if you want to include price data (FALSE=default)} 33 | 34 | \item{requestLimit}{integer (default 2200) Maximum number of requests one API call can handle} 35 | 36 | \item{sleep}{integer (default 0) Seconds to sleep between API requests} 37 | 38 | \item{wait}{waiting time before retry in case of fail (needs to be larger than 60s in case the server blocks too many attempts, default=60)} 39 | 40 | \item{finalWait}{to avoid calling the web-api again with another command before 60s are over (TRUE=default)} 41 | } 42 | \value{ 43 | List of latest/new/historic listings of crypto currencies in a tibble (depending on the "which"-switch and 44 | whether "quote" is requested, the result may only contain some of the following variables): 45 | \item{btc_dominance}{number Bitcoin's market dominance percentage by market cap.} 46 | \item{eth_dominance}{number Ethereum's market dominance percentage by market cap.} 47 | \item{active_cryptocurrencies}{number Count of active crypto currencies tracked by CMC 48 | This includes all crypto currencies with a listing_status of "active" or "listed".} 49 | \item{total_cryptocurrencies}{number Count of all crypto currencies tracked by CMC 50 | This includes "inactive" listing_status crypto currencies.} 51 | \item{active_market_pairs}{number Count of active market pairs tracked by CoinMarketCap across all exchanges.}#' 52 | \item{active_exchanges}{number Count of active exchanges tracked by CMC This includes all 53 | exchanges with a listing_status of "active" or "listed".} 54 | \item{total_exchanges}{number Count of all exchanges tracked by CMC 55 | This includes "inactive" listing_status exchanges.} 56 | \item{last_updated}{Timestamp of when this record was last updated.} 57 | \item{total_market_cap}{number The sum of all individual cryptocurrency market capitalizations in the requested currency.} 58 | \item{total_volume_24h}{number The sum of rolling 24 hour adjusted volume (as outlined in our methodology) for all 59 | crypto currencies in the requested currency.} 60 | \item{total_volume_24h_reported}{number The sum of rolling 24 hour reported volume for all crypto currencies in the requested currency.}#' 61 | \item{altcoin_volume_24h}{number The sum of rolling 24 hour adjusted volume (as outlined in our methodology) for 62 | all crypto currencies excluding Bitcoin in the requested currency.} 63 | \item{altcoin_volume_24h_reported}{number The sum of rolling 24 hour reported volume for 64 | all crypto currencies excluding Bitcoin in the requested currency.} 65 | \item{altcoin_market_cap }{number The sum of all individual cryptocurrency market capitalizations excluding Bitcoin in the requested currency.} 66 | } 67 | \description{ 68 | This code retrieves global quote data (latest/historic) from coinmarketcap.com. 69 | } 70 | \examples{ 71 | \dontrun{ 72 | # return new listings from the last 30 days 73 | new_quotes <- crypto_global_quotes(which="latest", quote=TRUE, convert="BTC") 74 | # return all global quotes in the first week of January 2014 75 | quotes_2014w1 <- crypto_global_quotes(which="historical", quote=TRUE, 76 | start_date = "20140101", end_date="20140107", interval="daily") 77 | 78 | # report in two different currencies 79 | listings_2014w1_USDBTC <- crypto_global_quotes(which="historical", quote=TRUE, 80 | start_date = "20200101", end_date="20240530", interval="daily", convert="BTC") 81 | } 82 | 83 | } 84 | -------------------------------------------------------------------------------- /man/crypto_history.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/crypto_history.R 3 | \name{crypto_history} 4 | \alias{crypto_history} 5 | \title{Get historic crypto currency market data} 6 | \usage{ 7 | crypto_history( 8 | coin_list = NULL, 9 | convert = "USD", 10 | limit = NULL, 11 | start_date = NULL, 12 | end_date = NULL, 13 | interval = NULL, 14 | requestLimit = 400, 15 | sleep = 0, 16 | wait = 60, 17 | finalWait = FALSE, 18 | single_id = TRUE 19 | ) 20 | } 21 | \arguments{ 22 | \item{coin_list}{string if NULL retrieve all currently existing coins (\code{crypto_list()}), 23 | or provide list of crypto currencies in the \code{crypto_list()} or \code{cryptoi_listings()} format (e.g. current and/or dead coins since 2015)} 24 | 25 | \item{convert}{(default: USD) to one of available fiat prices (\code{fiat_list()}) or bitcoin 'BTC'. Be aware, that since 2024 only USD and BTC are available here!} 26 | 27 | \item{limit}{integer Return the top n records, default is all tokens} 28 | 29 | \item{start_date}{date Start date to retrieve data from} 30 | 31 | \item{end_date}{date End date to retrieve data from, if not provided, today will be assumed} 32 | 33 | \item{interval}{string Interval with which to sample data according to what \code{seq()} needs} 34 | 35 | \item{requestLimit}{limiting the length of request URLs when bundling the api calls} 36 | 37 | \item{sleep}{integer (default 60) Seconds to sleep between API requests} 38 | 39 | \item{wait}{waiting time before retry in case of fail (needs to be larger than 60s in case the server blocks too many attempts, default=60)} 40 | 41 | \item{finalWait}{to avoid calling the web-api again with another command before 60s are over (TRUE=default)} 42 | 43 | \item{single_id}{Download data coin by coin (as of May 2024 this is necessary)} 44 | } 45 | \value{ 46 | Crypto currency historic OHLC market data in a dataframe and additional information via attribute "info": 47 | \item{timestamp}{Timestamp of entry in database} 48 | \item{id}{Coin market cap unique id} 49 | \item{name}{Coin name} 50 | \item{symbol}{Coin symbol} 51 | \item{ref_cur_id}{reference Currency id} 52 | \item{ref_cur_name}{reference Currency name} 53 | \item{open}{Market open} 54 | \item{high}{Market high} 55 | \item{low}{Market low} 56 | \item{close}{Market close} 57 | \item{volume}{Volume 24 hours} 58 | \item{market_cap}{Market cap - close x circulating supply} 59 | \item{time_open}{Timestamp of open} 60 | \item{time_close}{Timestamp of close} 61 | \item{time_high}{Timestamp of high} 62 | \item{time_low}{Timestamp of low} 63 | 64 | This is the main function of the crypto package. If you want to retrieve 65 | ALL active coins then do not pass an argument to \code{crypto_history()}, alternatively pass the coin name. 66 | } 67 | \description{ 68 | Scrape the crypto currency historic market tables from 69 | 'CoinMarketCap' \url{https://coinmarketcap.com} and display 70 | the results in a dataframe/tibble. This can be used to conduct 71 | analysis on the crypto financial markets or to attempt 72 | to predict future market movements or trends. 73 | } 74 | \examples{ 75 | \dontrun{ 76 | 77 | # Retrieving market history for ALL crypto currencies 78 | all_coins <- crypto_history(limit = 2) 79 | one_coin <- crypto_history(limit = 1, convert="BTC") 80 | 81 | # Retrieving market history since 2020 for ALL crypto currencies 82 | all_coins <- crypto_history(start_date = '2020-01-01',limit=10) 83 | 84 | # Retrieve 2015 history for all 2015 crypto currencies 85 | coin_list_2015 <- crypto_list(only_active=TRUE) \%>\% 86 | dplyr::filter(first_historical_data<="2015-12-31", 87 | last_historical_data>="2015-01-01") 88 | coins_2015 <- crypto_history(coin_list = coin_list_2015, 89 | start_date = "2015-01-01", end_date="2015-12-31", limit=20, interval="30d") 90 | # retrieve hourly bitcoin data for 2 days 91 | btc_hourly <- crypto_history(coin_list = coin_list_2015, 92 | start_date = "2015-01-01", end_date="2015-01-03", limit=1, interval="1h") 93 | 94 | } 95 | 96 | } 97 | -------------------------------------------------------------------------------- /man/crypto_info.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/crypto_info.R 3 | \name{crypto_info} 4 | \alias{crypto_info} 5 | \title{Retrieves info (urls, logo, description, tags, platform, date_added, notice, status,...) on CMC for given id} 6 | \usage{ 7 | crypto_info( 8 | coin_list = NULL, 9 | limit = NULL, 10 | requestLimit = 1, 11 | sleep = 0, 12 | finalWait = FALSE 13 | ) 14 | } 15 | \arguments{ 16 | \item{coin_list}{string if NULL retrieve all currently active coins (\code{crypto_list()}), 17 | or provide list of cryptocurrencies in the \code{crypto_list()} or \code{cryptoi_listings()} format (e.g. current and/or dead coins since 2015)} 18 | 19 | \item{limit}{integer Return the top n records, default is all tokens} 20 | 21 | \item{requestLimit}{(default: 1) limiting the length of request URLs when bundling the api calls (currently needs to be 1)} 22 | 23 | \item{sleep}{integer (default: 0) Seconds to sleep between API requests} 24 | 25 | \item{finalWait}{to avoid calling the web-api again with another command before 60s are over (FALSE=default)} 26 | } 27 | \value{ 28 | List of (active and historically existing) cryptocurrencies in a tibble: 29 | \item{id}{CMC id (unique identifier)} 30 | \item{name}{Coin name} 31 | \item{symbol}{Coin symbol (not-unique)} 32 | \item{slug}{Coin URL slug (unique)} 33 | \item{category}{Coin category: "token" or "coin"} 34 | \item{description}{Coin description according to CMC} 35 | \item{logo}{CMC url of CC logo} 36 | \item{status}{Status message from CMC} 37 | \item{notice}{Markdown formatted notices from CMC} 38 | \item{alert_type}{Type of alert on CMC} 39 | \item{alert_link}{Message link to alert} 40 | \item{date_added}{Date CC was added to the CMC database} 41 | \item{date_launched}{Date CC was launched} 42 | \item{is_audited}{Boolean if CC is audited} 43 | \item{flags}{Boolean flags for various topics} 44 | \item{self_reported_circulating_supply}{Self reported circulating supply} 45 | \item{tags}{Tibble of tags and tag categories} 46 | \item{faq_description}{FAQ description from CMC} 47 | \item{url}{Tibble of various resource urls. Gives website, technical_doc (whitepaper), 48 | source_code, message_board, chat, announcement, reddit, twitter, (block) explorer urls} 49 | \item{platform}{Metadata about the parent coin if available. Gives id, name, symbol, 50 | slug, and token address according to CMC} 51 | } 52 | \description{ 53 | This code retrieves data for all specified coins! 54 | } 55 | \examples{ 56 | \dontrun{ 57 | # return info for bitcoin 58 | coin_info <- crypto_info(limit=10) 59 | } 60 | 61 | } 62 | -------------------------------------------------------------------------------- /man/crypto_list.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/crypto_list.R 3 | \name{crypto_list} 4 | \alias{crypto_list} 5 | \title{Retrieves name, CMC id, symbol, slug, rank, an activity flag as well as activity dates on CMC for all coins} 6 | \usage{ 7 | crypto_list(only_active = TRUE, add_untracked = FALSE) 8 | } 9 | \arguments{ 10 | \item{only_active}{Shall the code only retrieve active coins (TRUE=default) or include inactive coins (FALSE)} 11 | 12 | \item{add_untracked}{Shall the code additionally retrieve untracked coins (FALSE=default)} 13 | } 14 | \value{ 15 | List of (active and historically existing) cryptocurrencies in a tibble: 16 | \item{id}{CMC id (unique identifier)} 17 | \item{name}{Coin name} 18 | \item{symbol}{Coin symbol (not-unique)} 19 | \item{slug}{Coin URL slug (unique)} 20 | \item{rank}{Current rank on CMC (if still active)} 21 | \item{is_active}{Flag showing whether coin is active (1), inactive(0) or untracked (-1)} 22 | \item{first_historical_data}{First time listed on CMC} 23 | \item{last_historical_data}{Last time listed on CMC, \emph{today's date} if still listed} 24 | } 25 | \description{ 26 | This code uses the web api. It retrieves data for all historic and all active coins and does not require an 'API' key. 27 | } 28 | \examples{ 29 | \dontrun{ 30 | # return all coins 31 | active_list <- crypto_list(only_active=TRUE) 32 | all_but_untracked_list <- crypto_list(only_active=FALSE) 33 | full_list <- crypto_list(only_active=FALSE,add_untracked=TRUE) 34 | 35 | # return all coins active in 2015 36 | coin_list_2015 <- active_list \%>\% 37 | dplyr::filter(first_historical_data<="2015-12-31", 38 | last_historical_data>="2015-01-01") 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /man/crypto_listings.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/crypto_listings.R 3 | \name{crypto_listings} 4 | \alias{crypto_listings} 5 | \title{Retrieves name, CMC id, symbol, slug, rank, an activity flag as well as activity dates on CMC for all coins} 6 | \usage{ 7 | crypto_listings( 8 | which = "latest", 9 | convert = "USD", 10 | limit = 5000, 11 | start_date = NULL, 12 | end_date = NULL, 13 | interval = "day", 14 | quote = FALSE, 15 | sort = "cmc_rank", 16 | sort_dir = "asc", 17 | sleep = 0, 18 | wait = 60, 19 | finalWait = FALSE 20 | ) 21 | } 22 | \arguments{ 23 | \item{which}{string Shall the code retrieve the latest listing, the new listings or a historic listing?} 24 | 25 | \item{convert}{string (default: USD) to one of available fiat prices (\code{fiat_list()}). If more 26 | than one are selected please separate by comma (e.g. "USD,BTC"), only necessary if 'quote=TRUE'} 27 | 28 | \item{limit}{integer Return the top n records} 29 | 30 | \item{start_date}{string Start date to retrieve data from, format 'yyyymmdd'} 31 | 32 | \item{end_date}{string End date to retrieve data from, format 'yyyymmdd', if not provided, today will be assumed} 33 | 34 | \item{interval}{string Interval with which to sample data according to what \code{seq()} needs} 35 | 36 | \item{quote}{logical set to TRUE if you want to include price data (FALSE=default)} 37 | 38 | \item{sort}{(May 2024: currently not available) string use to sort results, possible values: "name", "symbol", "market_cap", "price", 39 | "circulating_supply", "total_supply", "max_supply", "num_market_pairs", "volume_24h", 40 | "volume_7d", "volume_30d", "percent_change_1h", "percent_change_24h", 41 | "percent_change_7d". Especially useful if you only want to download the top x entries using "limit" (deprecated for "new")} 42 | 43 | \item{sort_dir}{(May 2024: currently not available) string used to specify the direction of the sort in "sort". Possible values are "asc" (DEFAULT) and "desc"} 44 | 45 | \item{sleep}{integer (default 0) Seconds to sleep between API requests} 46 | 47 | \item{wait}{waiting time before retry in case of fail (needs to be larger than 60s in case the server blocks too many attempts, default=60)} 48 | 49 | \item{finalWait}{to avoid calling the web-api again with another command before 60s are over (TRUE=default)} 50 | } 51 | \value{ 52 | List of latest/new/historic listings of cryptocurrencies in a tibble (depending on the "which"-switch and 53 | whether "quote" is requested, the result may only contain some of the following variables): 54 | \item{id}{CMC id (unique identifier)} 55 | \item{name}{Coin name} 56 | \item{symbol}{Coin symbol (not-unique)} 57 | \item{slug}{Coin URL slug (unique)} 58 | \item{date_added}{Date when the coin was added to the dataset} 59 | \item{last_updated}{Last update of the data in the database} 60 | \item{rank}{Current rank on CMC (if still active)} 61 | \item{market_cap}{market cap - close x circulating supply} 62 | \item{market_cap_by_total_supply}{market cap - close x total supply} 63 | \item{market_cap_dominance}{market cap dominance} 64 | \item{fully_diluted_market_cap}{fully diluted market cap} 65 | \item{self_reported_market_cap}{is the source of the market cap self-reported} 66 | \item{self_reported_circulating_supply}{is the source of the circulating supply self-reported} 67 | \item{tvl_ratio}{percentage of total value locked} 68 | \item{price}{latest average price} 69 | \item{circulating_supply}{approx. number of coins in circulation} 70 | \item{total_supply}{approx. total amount of coins in existence right now (minus any coins that have been verifiably burned)} 71 | \item{max_supply}{CMC approx. of max amount of coins that will ever exist in the lifetime of the currency} 72 | \item{num_market_pairs}{number of market pairs across all exchanges this coin} 73 | \item{tvl}{total value locked} 74 | \item{volume_24h}{Volume 24 hours} 75 | \item{volume_change_24h}{Volume change in 24 hours} 76 | \item{percent_change_1h}{1 hour return} 77 | \item{percent_change_24h}{24 hour return} 78 | \item{percent_change_7d}{7 day return} 79 | \item{percent_change_30d}{30 day return} 80 | \item{percent_change_60d}{60 day return} 81 | \item{percent_change_90d}{90 day return} 82 | } 83 | \description{ 84 | This code retrieves listing data (latest/new/historic). 85 | } 86 | \examples{ 87 | \dontrun{ 88 | # return new listings from the last 30 days 89 | new_listings <- crypto_listings(which="new", quote=FALSE, limit=50000) 90 | new_listings2 <- crypto_listings(which="new", quote=TRUE, convert="BTC") 91 | 92 | # return latest listing (last available data of all CC including quotes) 93 | latest_listings <- crypto_listings(which="latest", quote=FALSE, limit=50000) 94 | latest_listings2 <- crypto_listings(which="latest", quote=TRUE, convert="BTC") 95 | 96 | # return the first 10 listings in the first week of January 2024 97 | listings_2024w1 <- crypto_listings(which="historical", quote=TRUE, 98 | start_date = "20240101", end_date="20240102", interval="day", limit=10) 99 | 100 | # only download the top 10 crypto currencies based on their market capitalization 101 | # DOES NOT WORK ANY MORE 102 | 103 | # for historically accurate snapshots (e.g. for backtesting crypto investments) 104 | # you need to download the entire history on one day including price information: 105 | listings_20200202 <- crypto_listings(which="historical", quote=TRUE, 106 | start_date="20200202", end_date="20200202") 107 | listings_20240202 <- crypto_listings(which="historical", quote=TRUE, 108 | start_date="20240202", end_date="20240202", limit=50000) 109 | # note the much larger amount in CCs in 2024, as well as the existence of many 110 | more variables in the dataset 111 | } 112 | 113 | } 114 | -------------------------------------------------------------------------------- /man/exchange_info.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/crypto_info.R 3 | \name{exchange_info} 4 | \alias{exchange_info} 5 | \title{Retrieves info (urls,logo,description,tags,platform,date_added,notice,status) on CMC for given exchange slug} 6 | \usage{ 7 | exchange_info( 8 | exchange_list = NULL, 9 | limit = NULL, 10 | requestLimit = 1, 11 | sleep = 0, 12 | finalWait = FALSE 13 | ) 14 | } 15 | \arguments{ 16 | \item{exchange_list}{string if NULL retrieve all currently active exchanges (\code{exchange_list()}), 17 | or provide list of exchanges in the \code{exchange_list()} format (e.g. current and/or delisted)} 18 | 19 | \item{limit}{integer Return the top n records, default is all exchanges} 20 | 21 | \item{requestLimit}{limiting the length of request URLs when bundling the api calls} 22 | 23 | \item{sleep}{integer (default 60) Seconds to sleep between API requests} 24 | 25 | \item{finalWait}{to avoid calling the web-api again with another command before 60s are over (TRUE=default)} 26 | } 27 | \value{ 28 | List of (active and historically existing) exchanges in a tibble: 29 | \item{id}{CMC exchange id (unique identifier)} 30 | \item{name}{Exchange name} 31 | \item{slug}{Exchange URL slug (unique)} 32 | \item{description}{Exchange description according to CMC} 33 | \item{notice}{Exchange notice (markdown formatted) according to CMC} 34 | \item{logo}{CMC url of CC logo} 35 | \item{type}{Type of exchange} 36 | \item{date_launched}{Launch date of this exchange} 37 | \item{is_hidden}{TBD} 38 | \item{is_redistributable}{TBD} 39 | \item{maker_fee}{Exchanges maker fee} 40 | \item{taker_fee}{Exchanges maker fee} 41 | \item{platform_id}{Platform id on CMC} 42 | \item{dex_status}{Decentralized exchange status} 43 | \item{wallet_source_status}{Wallet source status} 44 | \item{status}{Activity status on CMC} 45 | \item{tags}{Tibble of tags and tag categories} 46 | \item{urls}{Tibble of various resource urls. Gives website, blog, fee, twitter.} 47 | \item{countries}{Tibble of countries the exchange is active in} 48 | \item{fiats}{Tibble of fiat currencies the exchange trades in} 49 | } 50 | \description{ 51 | This code uses the web api. It retrieves data for all active, delisted and untracked exchanges! It does not require an 'API' key. 52 | } 53 | \examples{ 54 | \dontrun{ 55 | # return info for the first three exchanges 56 | exchange_info <- exchange_info(limit=10) 57 | } 58 | 59 | } 60 | -------------------------------------------------------------------------------- /man/exchange_list.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/crypto_list.R 3 | \name{exchange_list} 4 | \alias{exchange_list} 5 | \title{Retrieves name, CMC id, symbol, slug, rank, an activity flag as well as activity dates on CMC for all coins} 6 | \usage{ 7 | exchange_list(only_active = TRUE, add_untracked = FALSE) 8 | } 9 | \arguments{ 10 | \item{only_active}{Shall the code only retrieve active exchanges (TRUE=default) or include inactive coins (FALSE)} 11 | 12 | \item{add_untracked}{Shall the code additionally retrieve untracked exchanges (FALSE=default)} 13 | } 14 | \value{ 15 | List of (active and historically existing) exchanges in a tibble: 16 | \item{id}{CMC exchange id (unique identifier)} 17 | \item{name}{Exchange name} 18 | \item{slug}{Exchange URL slug (unique)} 19 | \item{is_active}{Flag showing whether exchange is active (1), inactive(0) or untracked (-1)} 20 | \item{first_historical_data}{First time listed on CMC} 21 | \item{last_historical_data}{Last time listed on CMC, \emph{today's date} if still listed} 22 | } 23 | \description{ 24 | This code uses the web api. It retrieves data for all historic and all active exchanges and does not require an 'API' key. 25 | } 26 | \examples{ 27 | \dontrun{ 28 | # return all exchanges 29 | ex_active_list <- exchange_list(only_active=TRUE) 30 | ex_all_but_untracked_list <- exchange_list(only_active=FALSE) 31 | ex_full_list <- exchange_list(only_active=FALSE,add_untracked=TRUE) 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /man/fiat_list.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/crypto_list.R 3 | \name{fiat_list} 4 | \alias{fiat_list} 5 | \title{Retrieves list of all CMC supported fiat currencies available to convert cryptocurrencies} 6 | \usage{ 7 | fiat_list(include_metals = FALSE) 8 | } 9 | \arguments{ 10 | \item{include_metals}{Shall the results include precious metals (TRUE) or not (FALSE=default). 11 | Update: As of May 2024 no more metals are included in this file} 12 | } 13 | \value{ 14 | List of (active and historically existing) cryptocurrencies in a tibble: 15 | \item{id}{CMC id (unique identifier)} 16 | \item{symbol}{Coin symbol (not-unique)} 17 | \item{name}{Coin name} 18 | \item{sign}{Fiat currency sign} 19 | } 20 | \description{ 21 | This code retrieves data for all available fiat currencies that are available on the website. 22 | } 23 | \examples{ 24 | \dontrun{ 25 | # return fiat currencies available through the CMC api 26 | fiat_list <- fiat_list() 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /man/figures/README-quotes-plot-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sstoeckl/crypto2/acce4961c7c5964bf175cdf6dc2d44e456c77fdc/man/figures/README-quotes-plot-1.png -------------------------------------------------------------------------------- /man/figures/ccs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sstoeckl/crypto2/acce4961c7c5964bf175cdf6dc2d44e456c77fdc/man/figures/ccs.png -------------------------------------------------------------------------------- /man/figures/crypto2_hex.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sstoeckl/crypto2/acce4961c7c5964bf175cdf6dc2d44e456c77fdc/man/figures/crypto2_hex.png -------------------------------------------------------------------------------- /man/figures/fig.R: -------------------------------------------------------------------------------- 1 | library(hexSticker) 2 | imgurl <- "man/figures/ccs.png" 3 | sticker(imgurl, package="crypto2", p_size=30, s_x=1, s_y=.75, s_width=.4, 4 | h_color="black",h_size = 2,h_fill = , 5 | filename="man/figures/crypto2_hex.png") 6 | -------------------------------------------------------------------------------- /man/safeFromJSON.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/extras.R 3 | \name{safeFromJSON} 4 | \alias{safeFromJSON} 5 | \title{Parses json data from a string without revealing information about the source in case of an error} 6 | \usage{ 7 | safeFromJSON(...) 8 | } 9 | \arguments{ 10 | \item{...}{} 11 | } 12 | \value{ 13 | A parsed JSON object 14 | } 15 | \description{ 16 | Parses json data from a string without revealing information about the source in case of an error 17 | } 18 | \keyword{internal} 19 | -------------------------------------------------------------------------------- /revdep/.gitignore: -------------------------------------------------------------------------------- 1 | checks 2 | library 3 | checks.noindex 4 | library.noindex 5 | cloud.noindex 6 | data.sqlite 7 | *.html 8 | -------------------------------------------------------------------------------- /revdep/README.md: -------------------------------------------------------------------------------- 1 | # Platform 2 | 3 | |field |value | 4 | |:--------|:-------------------------------------------------------------------------------------| 5 | |version |R version 4.4.0 (2024-04-24 ucrt) | 6 | |os |Windows 11 x64 (build 22631) | 7 | |system |x86_64, mingw32 | 8 | |ui |RStudio | 9 | |language |(EN) | 10 | |collate |German_Germany.utf8 | 11 | |ctype |German_Germany.utf8 | 12 | |tz |Europe/Vaduz | 13 | |date |2024-09-02 | 14 | |rstudio |2024.04.1+748 Chocolate Cosmos (desktop) | 15 | |pandoc |3.1.11 @ C:/Program Files/RStudio/resources/app/bin/quarto/bin/tools/ (via rmarkdown) | 16 | 17 | # Dependencies 18 | 19 | |package |old |new |Δ | 20 | |:-----------|:------|:------|:--| 21 | |crypto2 |2.0.1 |2.0.2 |* | 22 | |base64enc |0.1-3 |0.1-3 | | 23 | |cli |3.6.3 |3.6.3 | | 24 | |cpp11 |0.5.0 |0.5.0 | | 25 | |crayon |1.5.3 |1.5.3 | | 26 | |dplyr |1.1.4 |1.1.4 | | 27 | |fansi |1.0.6 |1.0.6 | | 28 | |generics |0.1.3 |0.1.3 | | 29 | |glue |1.7.0 |1.7.0 | | 30 | |hms |1.1.3 |1.1.3 | | 31 | |janitor |2.2.0 |2.2.0 | | 32 | |jsonlite |1.8.8 |1.8.8 | | 33 | |lifecycle |1.0.4 |1.0.4 | | 34 | |lubridate |1.9.3 |1.9.3 | | 35 | |magrittr |2.0.3 |2.0.3 | | 36 | |pillar |1.9.0 |1.9.0 | | 37 | |pkgconfig |2.0.3 |2.0.3 | | 38 | |plyr |1.8.9 |1.8.9 | | 39 | |prettyunits |1.2.0 |1.2.0 | | 40 | |progress |1.2.3 |1.2.3 | | 41 | |purrr |1.0.2 |1.0.2 | | 42 | |R6 |2.5.1 |2.5.1 | | 43 | |Rcpp |1.0.13 |1.0.13 | | 44 | |rlang |1.1.4 |1.1.4 | | 45 | |snakecase |0.11.1 |0.11.1 | | 46 | |stringi |1.8.4 |1.8.4 | | 47 | |stringr |1.5.1 |1.5.1 | | 48 | |tibble |3.2.1 |3.2.1 | | 49 | |tidyr |1.3.1 |1.3.1 | | 50 | |tidyselect |1.2.1 |1.2.1 | | 51 | |timechange |0.3.0 |0.3.0 | | 52 | |utf8 |1.2.4 |1.2.4 | | 53 | |vctrs |0.6.5 |0.6.5 | | 54 | |withr |3.0.1 |3.0.1 | | 55 | 56 | # Revdeps 57 | 58 | -------------------------------------------------------------------------------- /revdep/cran.md: -------------------------------------------------------------------------------- 1 | ## revdepcheck results 2 | 3 | We checked 2 reverse dependencies, comparing R CMD check results across CRAN and dev versions of this package. 4 | 5 | * We saw 0 new problems 6 | * We failed to check 0 packages 7 | 8 | -------------------------------------------------------------------------------- /revdep/failures.md: -------------------------------------------------------------------------------- 1 | *Wow, no problems at all. :)* -------------------------------------------------------------------------------- /revdep/problems.md: -------------------------------------------------------------------------------- 1 | *Wow, no problems at all. :)* -------------------------------------------------------------------------------- /tests/spelling.R: -------------------------------------------------------------------------------- 1 | if(requireNamespace('spelling', quietly = TRUE)) 2 | spelling::spell_check_test(vignettes = TRUE, error = FALSE, 3 | skip_on_cran = TRUE) 4 | -------------------------------------------------------------------------------- /tests/testthat.R: -------------------------------------------------------------------------------- 1 | # This file is part of the standard setup for testthat. 2 | # It is recommended that you do not modify it. 3 | # 4 | # Where should you do additional test configuration? 5 | # Learn more about the roles of various files in: 6 | # * https://r-pkgs.org/testing-design.html#sec-tests-files-overview 7 | # * https://testthat.r-lib.org/articles/special-files.html 8 | 9 | library(testthat) 10 | library(crypto2) 11 | Sys.setenv(TZ = "UTC") 12 | test_check("crypto2") 13 | # test_local(path="tests/testthat/") 14 | -------------------------------------------------------------------------------- /tests/testthat/test-api.R: -------------------------------------------------------------------------------- 1 | test_that("API data matches expected for crypto_global_quotes", { 2 | skip_on_cran() 3 | urls <- c(construct_url("global-metrics/quotes/historical?&convertId=1&timeStart=2020-01-01&timeEnd=2020-01-02&interval=1d",v="3")) 4 | #expected_dir <- paste0(getwd(),"/tests/testthat/test_data") 5 | expected_dir <- "test_data" 6 | 7 | # Optionally download and save the latest JSON for initial setup or update 8 | #download_and_save_json(urls, expected_dir) 9 | 10 | # Load expected JSON 11 | for (url in urls) { 12 | file_name <- paste0(digest::digest(url), ".json") 13 | file_path <- file.path(expected_dir, file_name) 14 | cat(file_path, "\n") 15 | if (file.exists(file_path)) { 16 | cat("Found file ",file_path, "on the system!\n") 17 | expected_json <- jsonlite::read_json(file_path, simplifyVector = TRUE) 18 | current_json <- safeFromJSON(url) 19 | expect_equal(current_json$data, expected_json$data) 20 | } else { 21 | skip("No reference JSON available for comparison") 22 | } 23 | } 24 | }) 25 | test_that("API data matches expected for crypto_history()", { 26 | skip_on_cran() 27 | urls <- c(construct_url("cryptocurrency/historical?id=1&convertId=2781&timeStart=1577836800&timeEnd=1578355200&interval=daily",v="3.1"), 28 | construct_url("cryptocurrency/historical?id=1&convertId=2781&timeStart=1577750400&timeEnd=1578005999&interval=1h",v="3.1")) 29 | #expected_dir <- "test_data" 30 | expected_dir <- paste0(getwd(),"/tests/testthat/test_data") 31 | # Optionally download and save the latest JSON for initial setup or update 32 | #download_and_save_json(urls, expected_dir) 33 | 34 | # Load expected JSON 35 | for (url in urls) { 36 | file_name <- paste0(digest::digest(url), ".json") 37 | file_path <- file.path(expected_dir, file_name) 38 | 39 | if (file.exists(file_path)) { 40 | expected_json <- jsonlite::read_json(file_path, simplifyVector = TRUE) 41 | current_json <- safeFromJSON(url) 42 | expect_equal(current_json$data, expected_json$data) 43 | } else { 44 | skip("No reference JSON available for comparison") 45 | } 46 | } 47 | }) 48 | test_that("API data matches expected for crypto_info()", { 49 | skip_on_cran() 50 | urls <- c(construct_url("cryptocurrency/detail?id=1",v="3")) 51 | expected_dir <- "test_data" 52 | #expected_dir <- paste0(getwd(),"/tests/testthat/test_data") 53 | # Optionally download and save the latest JSON for initial setup or update 54 | #download_and_save_json(urls, expected_dir) 55 | 56 | # Load expected JSON 57 | for (url in urls) { 58 | file_name <- paste0(digest::digest(url), ".json") 59 | file_path <- file.path(expected_dir, file_name) 60 | 61 | if (file.exists(file_path)) { 62 | expected_json <- jsonlite::read_json(file_path, simplifyVector = TRUE) 63 | current_json <- safeFromJSON(url) 64 | 65 | expect_equal(current_json$data$urls, expected_json$data$urls) 66 | expect_equal(current_json$data$tags, expected_json$data$tags) 67 | 68 | } else { 69 | skip("No reference JSON available for comparison") 70 | } 71 | } 72 | }) 73 | 74 | -------------------------------------------------------------------------------- /tests/testthat/test-crypto-info.R: -------------------------------------------------------------------------------- 1 | # Test with valid inputs and expected outputs 2 | test_that("Valid parameters return correctly structured data for crypto_info()", { 3 | skip_on_cran() 4 | result <- crypto_info(limit = 2) 5 | 6 | # Check data structure 7 | expect_s3_class(result, "tbl_df") 8 | expect_true(all(c("id", "name", "symbol", "slug", "description", "logo") %in% names(result))) 9 | 10 | # Check specific content for a known coin 11 | bitcoin_info <- result %>% filter(name == "Bitcoin") 12 | expect_equal(nrow(bitcoin_info), 1) 13 | expect_true(!is.na(bitcoin_info$logo)) 14 | expect_true(!is.na(bitcoin_info$description)) 15 | }) 16 | 17 | # Test downloaded data against earlier downloaded data 18 | test_that("Downloaded data matches previously downloaded reference data for crypto_info()", { 19 | skip_on_cran() 20 | # coin_info <- crypto_info(limit = 2) |> select(id,name,symbol,slug,category,date_added) 21 | # saveRDS(coin_info, "tests/testthat/test_data/crypto_info_reference.rds") 22 | # Assume you've saved reference data from a previous known good state 23 | expected_data <- readRDS("test_data/crypto_info_reference.rds") 24 | # expected_data <- readRDS("tests/testthat/test_data/crypto_info_reference.rds") 25 | 26 | # Get new data using the same parameters as when the reference was created 27 | new_data <- crypto_info(limit = 2) |> select(id,name,symbol,slug,category,date_added) 28 | 29 | # Compare the new data to the expected data 30 | expect_equal(new_data, expected_data, tolerance = 1e-8, 31 | info = "The newly downloaded data should match the reference dataset.") 32 | }) 33 | # Test with valid inputs and expected outputs 34 | test_that("Valid parameters return correctly structured data for exchange_info()", { 35 | skip_on_cran() 36 | result <- exchange_info(limit = 2) 37 | 38 | # Check data structure 39 | expect_s3_class(result, "tbl_df") 40 | expect_true(all(c("id", "name", "slug", "description", "logo") %in% names(result))) 41 | 42 | # Check specific content for a known coin 43 | poloniex_info <- result %>% filter(slug == "poloniex") 44 | expect_equal(nrow(poloniex_info), 1) 45 | expect_true(!is.na(poloniex_info$logo)) 46 | expect_true(!is.na(poloniex_info$description)) 47 | }) 48 | 49 | # Test downloaded data against earlier downloaded data 50 | test_that("Downloaded data matches previously downloaded reference data for exchange_info()", { 51 | skip_on_cran() 52 | # ex_info <- exchange_info(limit = 2) 53 | # saveRDS(ex_info, file = "tests/testthat/test_data/ex_info_reference.rds") 54 | # Assume you've saved reference data from a previous known good state 55 | expected_data <- readRDS("test_data/ex_info_reference.rds") 56 | # expected_data <- readRDS("tests/testthat/test_data/ex_info_reference.rds") 57 | 58 | # Get new data using the same parameters as when the reference was created 59 | new_data <- exchange_info(limit = 2) 60 | 61 | # Compare the new data to the expected data 62 | expect_equal(new_data, expected_data, tolerance = 1e-8, 63 | info = "The newly downloaded data should match the reference dataset.") 64 | }) 65 | 66 | -------------------------------------------------------------------------------- /tests/testthat/test-global-quotes.R: -------------------------------------------------------------------------------- 1 | # Test with valid inputs 2 | test_that("Valid inputs return correct data structure", { 3 | skip_on_cran() 4 | result <- crypto_global_quotes(which="latest", convert="USD", quote=TRUE) 5 | expect_s3_class(result, "tbl_df") 6 | expect_true("btc_dominance" %in% names(result)) 7 | expect_true("eth_dominance" %in% names(result)) 8 | }) 9 | 10 | # Test response to invalid 'convert' parameters 11 | test_that("Invalid 'convert' parameters are handled", { 12 | skip_on_cran() 13 | expect_error(crypto_global_quotes(which="latest", convert="INVALID_CURRENCY"), "convert must be one of the available currencies") 14 | }) 15 | 16 | test_that("Historical data matches expected output", { 17 | skip_on_cran() 18 | # Load the expected output 19 | # saved_output <- crypto_global_quotes( 20 | # which="historical", 21 | # convert="USD", 22 | # start_date="20200101", 23 | # end_date="20200107", 24 | # interval="daily", 25 | # quote=TRUE) 26 | # saveRDS(saved_output, "tests/testthat/test_data/historical_output.rds") 27 | expected_output <- readRDS("test_data/historical_output.rds") 28 | 29 | # Run the function again with the same parameters 30 | current_output <- crypto_global_quotes( 31 | which="historical", 32 | convert="USD", 33 | start_date="20200101", 34 | end_date="20200107", 35 | interval="daily", 36 | quote=TRUE 37 | ) 38 | 39 | # Use expect_equal to compare data frames/tibbles 40 | expect_equal(current_output, expected_output, 41 | info = "The output of crypto_global_quotes should match the historical data.") 42 | }) 43 | -------------------------------------------------------------------------------- /tests/testthat/test-history.R: -------------------------------------------------------------------------------- 1 | # Test with valid inputs and expected outputs 2 | test_that("Valid parameters return correctly structured data", { 3 | skip_on_cran() 4 | result <- crypto_history(convert = "USD", limit = 1, 5 | start_date = "2020-01-01", end_date = "2020-01-07", interval = "1d") 6 | 7 | # Check data structure 8 | expect_s3_class(result, "tbl_df") 9 | expect_true(all(c("id", "name", "symbol", "open", "high", "low", "close", "volume", "market_cap") %in% names(result))) 10 | 11 | # Check data content related to input parameters 12 | expect_equal(nrow(result), 7) # Expecting 7 days of data 13 | expect_equal(min(as.Date(result$timestamp)), as.Date("2020-01-01")) 14 | expect_equal(max(as.Date(result$timestamp)), as.Date("2020-01-07")) 15 | }) 16 | 17 | # Test handling of unsupported currencies 18 | test_that("Unsupported currencies are rejected", { 19 | skip_on_cran() 20 | expect_error(crypto_history(convert = "EUR"), 21 | "convert must be one of the available currencies") 22 | }) 23 | 24 | test_that("Downloaded data matches previously downloaded reference data", { 25 | skip_on_cran() 26 | # Load the expected output 27 | # saved_output <- crypto_history( 28 | # convert="USD", 29 | # limit=1, 30 | # start_date="2020-01-01", 31 | # end_date="2020-01-07", 32 | # interval="daily") 33 | # saveRDS(saved_output, "tests/testthat/test_data/crypto_history_reference.rds") 34 | # # Load the reference data 35 | expected_data <- readRDS("test_data/crypto_history_reference.rds") 36 | 37 | # Get new data using the same parameters 38 | new_data <- crypto_history(convert = "USD", limit = 1, 39 | start_date = "2020-01-01", end_date = "2020-01-07", interval = "daily") 40 | 41 | # Compare the new data to the expected data 42 | expect_equal(new_data |> select(!contains("time")), expected_data |> select(!contains("time")), tolerance = 1e-8, 43 | info = "The newly downloaded data should match the reference dataset.") 44 | }) 45 | -------------------------------------------------------------------------------- /tests/testthat/test-listings.R: -------------------------------------------------------------------------------- 1 | # Test different listing types 2 | test_that("Fetching different types of listings works correctly", { 3 | skip_on_cran() 4 | latest_data <- crypto_listings(which="latest", quote=FALSE,limit=2) 5 | new_data <- crypto_listings(which="new", quote=TRUE, convert="BTC",limit=2) 6 | historical_data <- crypto_listings(which="historical", quote=TRUE, start_date="20240101", end_date="20240107",limit=2) 7 | 8 | expect_s3_class(latest_data, "tbl_df") 9 | expect_s3_class(new_data, "tbl_df") 10 | expect_s3_class(historical_data, "tbl_df") 11 | }) 12 | 13 | # Test output structure and content 14 | test_that("Output data structure is correct", { 15 | skip_on_cran() 16 | data <- crypto_listings(which="latest", quote=TRUE, convert="USD") 17 | required_columns <- c("id", "name", "symbol", "slug", "price", "market_cap") 18 | expect_true(all(required_columns %in% names(data))) 19 | }) 20 | 21 | # Test error handling for invalid parameters 22 | test_that("Error handling for invalid parameters", { 23 | skip_on_cran() 24 | expect_error(crypto_listings(which="unknown")) 25 | }) 26 | 27 | # Consistency check against reference data 28 | test_that("Consistency check against reference data", { 29 | skip_on_cran() 30 | # reference_data <- crypto_listings(which="historical", start_date="20240101", end_date="20240107", quote=TRUE,limit=2) 31 | # saveRDS(reference_data, "tests/testthat/test_data/crypto_listings_reference.rds") 32 | # 33 | reference_data <- readRDS("test_data/crypto_listings_reference.rds") 34 | test_data <- crypto_listings(which="historical", start_date="20240101", end_date="20240107", quote=TRUE,limit=2) 35 | 36 | expect_equal(test_data, reference_data) 37 | }) 38 | -------------------------------------------------------------------------------- /tests/testthat/test-lists.R: -------------------------------------------------------------------------------- 1 | # Test crypto_list function 2 | test_that("crypto_list returns correctly structured data", { 3 | skip_on_cran() 4 | result <- crypto_list(only_active = TRUE) 5 | expect_s3_class(result, "tbl_df") 6 | expect_true(all(c("id", "name", "symbol", "slug", "is_active") %in% names(result))) 7 | }) 8 | 9 | # Test exchange_list function 10 | test_that("exchange_list returns correctly structured data", { 11 | skip_on_cran() 12 | result <- exchange_list(only_active = TRUE) 13 | expect_s3_class(result, "tbl_df") 14 | expect_true(all(c("id", "name", "slug", "is_active") %in% names(result))) 15 | }) 16 | 17 | # Test fiat_list function 18 | test_that("fiat_list returns correctly structured data", { 19 | skip_on_cran() 20 | result <- fiat_list() 21 | expect_s3_class(result, "tbl_df") 22 | expect_true(all(c("id", "symbol", "name", "sign") %in% names(result))) 23 | }) 24 | 25 | # Test handling of different parameters 26 | test_that("Handling of additional parameters in listing functions", { 27 | skip_on_cran() 28 | crypto_all <- crypto_list(only_active = FALSE, add_untracked = TRUE) 29 | expect_true(any(crypto_all$is_active == 1)) 30 | 31 | exchange_all <- exchange_list(only_active = FALSE, add_untracked = TRUE) 32 | expect_true(any(exchange_all$is_active == 1)) 33 | }) 34 | 35 | # Test for data consistency over time 36 | # test_that("Data consistency over time for listings", { 37 | # skip_on_cran() 38 | # # Run these at a time when the data is verified to be correct 39 | # # reference_crypto_data <- crypto_list(only_active = TRUE) 40 | # # saveRDS(reference_crypto_data, "tests/testthat/test_data/crypto_list_reference.rds") 41 | # # 42 | # # reference_exchange_data <- exchange_list(only_active = TRUE) 43 | # # saveRDS(reference_exchange_data, "tests/testthat/test_data/exchange_list_reference.rds") 44 | # # Assuming you've saved reference data from a previous known good state 45 | # expected_crypto_data <- readRDS("test_data/crypto_list_reference.rds") 46 | # new_crypto_data <- crypto_list(only_active = TRUE) 47 | # 48 | # expected_exchange_data <- readRDS("test_data/exchange_list_reference.rds") 49 | # new_exchange_data <- exchange_list(only_active = TRUE) 50 | # 51 | # # Compare the new data to the expected data 52 | # expect_equal(new_crypto_data, expected_crypto_data, tolerance = 1e-8, 53 | # info = "The newly downloaded crypto data should match the reference dataset.") 54 | # expect_equal(new_exchange_data, expected_exchange_data, tolerance = 1e-8, 55 | # info = "The newly downloaded exchange data should match the reference dataset.") 56 | # }) 57 | -------------------------------------------------------------------------------- /tests/testthat/test_data/4fb1f9d0e9f6acf897a94e7c9e0335b9.json: -------------------------------------------------------------------------------- 1 | {"data":{"quotes":[{"timestamp":"2020-01-01T00:00:00.000Z","searchInterval":"1440","btcDominance":68.303299999999993,"ethDominance":0,"activeCryptocurrencies":3286,"activeExchanges":642,"activeMarketPairs":20456,"quote":[{"name":"1","timestamp":"2020-01-01T00:00:00.000Z","totalMarketCap":26548681.496409278,"totalVolume24H":9591101.0892702192,"totalVolume24HReported":9894449.9771088101,"altcoinVolume24H":6687149.7393432604,"altcoinVolume24HReported":6870959.1322155399,"altcoinMarketCap":8415044.49640969,"originalScore":"1577836920"}],"score":1577836800000},{"timestamp":"2020-01-02T00:00:00.000Z","searchInterval":"1440","btcDominance":68.191000000000003,"ethDominance":0,"activeCryptocurrencies":3291,"activeExchanges":642,"activeMarketPairs":20470,"quote":[{"name":"1","timestamp":"2020-01-02T00:00:00.000Z","totalMarketCap":26595576.99785082,"totalVolume24H":8544428.5411473196,"totalVolume24HReported":8815984.4004925694,"altcoinVolume24H":5993562.1158513203,"altcoinVolume24HReported":6157230.1151635796,"altcoinMarketCap":8459789.9978509396,"originalScore":"1577923320"}],"score":1577923200000}]},"status":{"timestamp":["2025-03-10T12:15:50.016Z"],"error_code":["0"],"error_message":["SUCCESS"],"elapsed":["13"],"credit_count":[0]}} 2 | -------------------------------------------------------------------------------- /tests/testthat/test_data/7597f731a582ffb4e67751af13de407e.json: -------------------------------------------------------------------------------- 1 | {"data":{"quotes":[{"timestamp":"2020-01-01T00:00:00.000Z","searchInterval":"1440","btcDominance":68.303299999999993,"ethDominance":0,"activeCryptocurrencies":3286,"activeExchanges":642,"activeMarketPairs":20456,"quote":[{"name":"1","timestamp":"2020-01-01T00:00:00.000Z","totalMarketCap":26548681.496409278,"totalVolume24H":9591101.0892702192,"totalVolume24HReported":9894449.9771088101,"altcoinVolume24H":6687149.7393432604,"altcoinVolume24HReported":6870959.1322155399,"altcoinMarketCap":8415044.49640969,"originalScore":"1577836920"}],"score":1577836800000},{"timestamp":"2020-01-02T00:00:00.000Z","searchInterval":"1440","btcDominance":68.191000000000003,"ethDominance":0,"activeCryptocurrencies":3291,"activeExchanges":642,"activeMarketPairs":20470,"quote":[{"name":"1","timestamp":"2020-01-02T00:00:00.000Z","totalMarketCap":26595576.99785082,"totalVolume24H":8544428.5411473196,"totalVolume24HReported":8815984.4004925694,"altcoinVolume24H":5993562.1158513203,"altcoinVolume24HReported":6157230.1151635796,"altcoinMarketCap":8459789.9978509396,"originalScore":"1577923320"}],"score":1577923200000},{"timestamp":"2020-01-03T00:00:00.000Z","searchInterval":"1440","btcDominance":68.154600000000002,"ethDominance":0,"activeCryptocurrencies":3291,"activeExchanges":642,"activeMarketPairs":20474,"quote":[{"name":"1","timestamp":"2020-01-03T00:00:00.000Z","totalMarketCap":26612362.00363363,"totalVolume24H":9735086.3946764097,"totalVolume24HReported":10035974.12167166,"altcoinVolume24H":6776724.87838847,"altcoinVolume24HReported":6954442.3548117997,"altcoinMarketCap":8474800.0036332905,"originalScore":"1578009720"}],"score":1578009600000},{"timestamp":"2020-01-04T00:00:00.000Z","searchInterval":"1440","btcDominance":68.190299999999993,"ethDominance":0,"activeCryptocurrencies":3293,"activeExchanges":642,"activeMarketPairs":20519,"quote":[{"name":"1","timestamp":"2020-01-04T00:00:00.000Z","totalMarketCap":26601492.387191951,"totalVolume24H":12199313.06361068,"totalVolume24HReported":12628317.55475715,"altcoinVolume24H":8388656.0734430701,"altcoinVolume24HReported":8638281.3133246098,"altcoinMarketCap":8461867.3871923201,"originalScore":"1578096120"}],"score":1578096000000},{"timestamp":"2020-01-05T00:00:00.000Z","searchInterval":"1440","btcDominance":68.127200000000002,"ethDominance":0,"activeCryptocurrencies":3293,"activeExchanges":643,"activeMarketPairs":20520,"quote":[{"name":"1","timestamp":"2020-01-05T00:00:00.000Z","totalMarketCap":26629261.756035689,"totalVolume24H":8471030.2093976606,"totalVolume24HReported":9612977.7989857905,"altcoinVolume24H":6007425.9362433702,"altcoinVolume24HReported":6706130.1091323299,"altcoinMarketCap":8487486.7560352609,"originalScore":"1578182520"}],"score":1578182400000},{"timestamp":"2020-01-06T00:00:00.000Z","searchInterval":"1440","btcDominance":67.984899999999996,"ethDominance":0,"activeCryptocurrencies":3293,"activeExchanges":643,"activeMarketPairs":20520,"quote":[{"name":"1","timestamp":"2020-01-06T00:00:00.000Z","totalMarketCap":26688004.714679152,"totalVolume24H":9131670.9164480306,"totalVolume24HReported":10112075.522003099,"altcoinVolume24H":6493510.9173499905,"altcoinVolume24HReported":7095823.27308801,"altcoinMarketCap":8544192.7146787196,"originalScore":"1578268920"}],"score":1578268800000},{"timestamp":"2020-01-07T00:00:00.000Z","searchInterval":"1440","btcDominance":67.587199999999996,"ethDominance":0,"activeCryptocurrencies":3302,"activeExchanges":644,"activeMarketPairs":20273,"quote":[{"name":"1","timestamp":"2020-01-07T00:00:00.000Z","totalMarketCap":26847537.136172362,"totalVolume24H":10531125.97352948,"totalVolume24HReported":11532837.69628825,"altcoinVolume24H":7567742.4379917504,"altcoinVolume24HReported":8178489.7730778502,"altcoinMarketCap":8702025.1361719295,"originalScore":"1578355320"}],"score":1578355200000},{"timestamp":"2020-01-08T00:00:00.000Z","searchInterval":"1440","btcDominance":68.825000000000003,"ethDominance":0,"activeCryptocurrencies":3306,"activeExchanges":647,"activeMarketPairs":20293,"quote":[{"name":"1","timestamp":"2020-01-08T00:00:00.000Z","totalMarketCap":26367883.33572989,"totalVolume24H":11091558.051743889,"totalVolume24HReported":12434047.18287907,"altcoinVolume24H":7615362.6111989804,"altcoinVolume24HReported":8440165.6330682207,"altcoinMarketCap":8220183.3357298104,"originalScore":"1578441720"}],"score":1578441600000},{"timestamp":"2020-01-09T00:00:00.000Z","searchInterval":"1440","btcDominance":68.869699999999995,"ethDominance":0,"activeCryptocurrencies":3309,"activeExchanges":650,"activeMarketPairs":20315,"quote":[{"name":"1","timestamp":"2020-01-09T00:00:00.000Z","totalMarketCap":26353340.975187398,"totalVolume24H":11952781.66177761,"totalVolume24HReported":13224667.526574939,"altcoinVolume24H":8077343.95054013,"altcoinVolume24HReported":8881864.0599314794,"altcoinMarketCap":8203878.9751867298,"originalScore":"1578528120"}],"score":1578528000000},{"timestamp":"2020-01-10T00:00:00.000Z","searchInterval":"1440","btcDominance":68.5321,"ethDominance":0,"activeCryptocurrencies":3318,"activeExchanges":652,"activeMarketPairs":20318,"quote":[{"name":"1","timestamp":"2020-01-10T00:00:00.000Z","totalMarketCap":26486039.493361849,"totalVolume24H":9760138.9022507407,"totalVolume24HReported":10752719.942278029,"altcoinVolume24H":6728881.6395423198,"altcoinVolume24HReported":7389392.8199885599,"altcoinMarketCap":8334589.4933614302,"originalScore":"1578614520"}],"score":1578614400000},{"timestamp":"2020-01-11T00:00:00.000Z","searchInterval":"1440","btcDominance":68.311300000000003,"ethDominance":0,"activeCryptocurrencies":3320,"activeExchanges":652,"activeMarketPairs":20343,"quote":[{"name":"1","timestamp":"2020-01-11T00:00:00.000Z","totalMarketCap":26574364.034348641,"totalVolume24H":11609615.88125978,"totalVolume24HReported":12670898.77213222,"altcoinVolume24H":8099639.4232419301,"altcoinVolume24HReported":8789257.0903616194,"altcoinMarketCap":8421064.0343485102,"originalScore":"1578700920"}],"score":1578700800000},{"timestamp":"2020-01-12T00:00:00.000Z","searchInterval":"1440","btcDominance":68.038600000000002,"ethDominance":0,"activeCryptocurrencies":3320,"activeExchanges":652,"activeMarketPairs":20344,"quote":[{"name":"1","timestamp":"2020-01-12T00:00:00.000Z","totalMarketCap":26683861.007206362,"totalVolume24H":11003042.657325281,"totalVolume24HReported":12137820.99965534,"altcoinVolume24H":7843995.7718752399,"altcoinVolume24HReported":8556997.7458870094,"altcoinMarketCap":8528524.0072069503,"originalScore":"1578787320"}],"score":1578787200000},{"timestamp":"2020-01-13T00:00:00.000Z","searchInterval":"1440","btcDominance":67.912300000000002,"ethDominance":0,"activeCryptocurrencies":3320,"activeExchanges":652,"activeMarketPairs":20348,"quote":[{"name":"1","timestamp":"2020-01-13T00:00:00.000Z","totalMarketCap":26736263.92355134,"totalVolume24H":9759639.8334870897,"totalVolume24HReported":10808792.01194975,"altcoinVolume24H":6983337.6751371901,"altcoinVolume24HReported":7648801.2463609902,"altcoinMarketCap":8579038.9235520903,"originalScore":"1578873720"}],"score":1578873600000},{"timestamp":"2020-01-14T00:00:00.000Z","searchInterval":"1440","btcDominance":68.031899999999993,"ethDominance":0,"activeCryptocurrencies":3328,"activeExchanges":653,"activeMarketPairs":20279,"quote":[{"name":"1","timestamp":"2020-01-14T00:00:00.000Z","totalMarketCap":26691926.93160956,"totalVolume24H":9401920.5343038402,"totalVolume24HReported":10471121.38533885,"altcoinVolume24H":6661265.87371621,"altcoinVolume24HReported":7336300.7470110003,"altcoinMarketCap":8532914.9316083994,"originalScore":"1578960120"}],"score":1578960000000},{"timestamp":"2020-01-15T00:00:00.000Z","searchInterval":"1440","btcDominance":66.076700000000002,"ethDominance":0,"activeCryptocurrencies":3332,"activeExchanges":654,"activeMarketPairs":20290,"quote":[{"name":"1","timestamp":"2020-01-15T00:00:00.000Z","totalMarketCap":27484456.259805351,"totalVolume24H":18460691.883603271,"totalVolume24HReported":19879952.73030756,"altcoinVolume24H":13390194.01207695,"altcoinVolume24HReported":14295528.892325049,"altcoinMarketCap":9323631.2598054595,"originalScore":"1579046520"}],"score":1579046400000},{"timestamp":"2020-01-16T00:00:00.000Z","searchInterval":"1440","btcDominance":66.229799999999997,"ethDominance":0,"activeCryptocurrencies":3336,"activeExchanges":655,"activeMarketPairs":20309,"quote":[{"name":"1","timestamp":"2020-01-16T00:00:00.000Z","totalMarketCap":27423780.711027719,"totalVolume24H":16397292.134747081,"totalVolume24HReported":17689309.071890939,"altcoinVolume24H":11900708.86887645,"altcoinVolume24HReported":12729898.658950901,"altcoinMarketCap":9261068.7110274397,"originalScore":"1579132920"}],"score":1579132800000},{"timestamp":"2020-01-17T00:00:00.000Z","searchInterval":"1440","btcDominance":66.177300000000002,"ethDominance":0,"activeCryptocurrencies":3336,"activeExchanges":656,"activeMarketPairs":20317,"quote":[{"name":"1","timestamp":"2020-01-17T00:00:00.000Z","totalMarketCap":27448563.695143029,"totalVolume24H":13683669.47791557,"totalVolume24HReported":14824274.75043674,"altcoinVolume24H":10126023.428045999,"altcoinVolume24HReported":10869762.84022329,"altcoinMarketCap":9283851.6951439604,"originalScore":"1579219320"}],"score":1579219200000},{"timestamp":"2020-01-18T00:00:00.000Z","searchInterval":"1440","btcDominance":65.947299999999998,"ethDominance":0,"activeCryptocurrencies":3336,"activeExchanges":660,"activeMarketPairs":20334,"quote":[{"name":"1","timestamp":"2020-01-18T00:00:00.000Z","totalMarketCap":27547316.315560311,"totalVolume24H":15404380.7400824,"totalVolume24HReported":16591134.446225701,"altcoinVolume24H":11344884.2390143,"altcoinVolume24HReported":12109892.789265379,"altcoinMarketCap":9380616.3155604508,"originalScore":"1579305720"}],"score":1579305600000},{"timestamp":"2020-01-19T00:00:00.000Z","searchInterval":"1440","btcDominance":66.302800000000005,"ethDominance":0,"activeCryptocurrencies":3336,"activeExchanges":660,"activeMarketPairs":20334,"quote":[{"name":"1","timestamp":"2020-01-19T00:00:00.000Z","totalMarketCap":27402071.104702119,"totalVolume24H":14023006.301367139,"totalVolume24HReported":15157905.183831999,"altcoinVolume24H":10419345.918948481,"altcoinVolume24HReported":11173734.906509049,"altcoinMarketCap":9233734.1047012694,"originalScore":"1579392120"}],"score":1579392000000},{"timestamp":"2020-01-20T00:00:00.000Z","searchInterval":"1440","btcDominance":66.275000000000006,"ethDominance":0,"activeCryptocurrencies":3336,"activeExchanges":660,"activeMarketPairs":20334,"quote":[{"name":"1","timestamp":"2020-01-20T00:00:00.000Z","totalMarketCap":27416369.251097132,"totalVolume24H":14069552.775372511,"totalVolume24HReported":15263463.12179794,"altcoinVolume24H":10168946.91135533,"altcoinVolume24HReported":10941110.44966446,"altcoinMarketCap":9246157.25109699,"originalScore":"1579478520"}],"score":1579478400000},{"timestamp":"2020-01-21T00:00:00.000Z","searchInterval":"1440","btcDominance":65.947999999999993,"ethDominance":0,"activeCryptocurrencies":3336,"activeExchanges":662,"activeMarketPairs":20221,"quote":[{"name":"1","timestamp":"2020-01-21T00:00:00.000Z","totalMarketCap":27555204.503856972,"totalVolume24H":11168356.62268913,"totalVolume24HReported":12228585.273825111,"altcoinVolume24H":8143388.5256948303,"altcoinVolume24HReported":8831269.58848783,"altcoinMarketCap":9383104.5038562696,"originalScore":"1579564920"}],"score":1579564800000},{"timestamp":"2020-01-22T00:00:00.000Z","searchInterval":"1440","btcDominance":65.757099999999994,"ethDominance":0,"activeCryptocurrencies":3349,"activeExchanges":662,"activeMarketPairs":20238,"quote":[{"name":"1","timestamp":"2020-01-22T00:00:00.000Z","totalMarketCap":27638115.33923427,"totalVolume24H":10142714.956242699,"totalVolume24HReported":11200052.8604158,"altcoinVolume24H":7408153.5083519397,"altcoinVolume24HReported":8093518.7297988003,"altcoinMarketCap":9464103.3392335791,"originalScore":"1579651320"}],"score":1579651200000},{"timestamp":"2020-01-23T00:00:00.000Z","searchInterval":"1440","btcDominance":65.664199999999994,"ethDominance":0,"activeCryptocurrencies":3351,"activeExchanges":664,"activeMarketPairs":20255,"quote":[{"name":"1","timestamp":"2020-01-23T00:00:00.000Z","totalMarketCap":27680164.781310331,"totalVolume24H":9646085.9468553606,"totalVolume24HReported":10708216.054540319,"altcoinVolume24H":7057791.4871834796,"altcoinVolume24HReported":7747438.4853207096,"altcoinMarketCap":9504202.7813110203,"originalScore":"1579737720"}],"score":1579737600000},{"timestamp":"2020-01-24T00:00:00.000Z","searchInterval":"1440","btcDominance":65.975499999999997,"ethDominance":0,"activeCryptocurrencies":3352,"activeExchanges":664,"activeMarketPairs":20326,"quote":[{"name":"1","timestamp":"2020-01-24T00:00:00.000Z","totalMarketCap":27552603.261341158,"totalVolume24H":11156692.779273359,"totalVolume24HReported":12071257.33105167,"altcoinVolume24H":8105325.7777387397,"altcoinVolume24HReported":8689603.2358738594,"altcoinMarketCap":9374641.2613411397,"originalScore":"1579824120"}],"score":1579824000000},{"timestamp":"2020-01-25T00:00:00.000Z","searchInterval":"1440","btcDominance":66.102800000000002,"ethDominance":0,"activeCryptocurrencies":3366,"activeExchanges":665,"activeMarketPairs":20349,"quote":[{"name":"1","timestamp":"2020-01-25T00:00:00.000Z","totalMarketCap":27502414.409407459,"totalVolume24H":11022404.762633169,"totalVolume24HReported":11739450.09587392,"altcoinVolume24H":8155452.1722164303,"altcoinVolume24HReported":8618063.5551696904,"altcoinMarketCap":9322552.4094078392,"originalScore":"1579910520"}],"score":1579910400000},{"timestamp":"2020-01-26T00:00:00.000Z","searchInterval":"1440","btcDominance":66.185699999999997,"ethDominance":0,"activeCryptocurrencies":3366,"activeExchanges":665,"activeMarketPairs":20349,"quote":[{"name":"1","timestamp":"2020-01-26T00:00:00.000Z","totalMarketCap":27471018.375885092,"totalVolume24H":8914463.7783388905,"totalVolume24HReported":9588463.4200112093,"altcoinVolume24H":6584987.6554587604,"altcoinVolume24HReported":7018121.0856416896,"altcoinMarketCap":9289131.3758847192,"originalScore":"1579996920"}],"score":1579996800000},{"timestamp":"2020-01-27T00:00:00.000Z","searchInterval":"1440","btcDominance":65.823599999999999,"ethDominance":0,"activeCryptocurrencies":3368,"activeExchanges":666,"activeMarketPairs":20352,"quote":[{"name":"1","timestamp":"2020-01-27T00:00:00.000Z","totalMarketCap":27624913.52577417,"totalVolume24H":9705566.9547205493,"totalVolume24HReported":10391795.350567071,"altcoinVolume24H":7138893.5156581504,"altcoinVolume24HReported":7583550.8546052603,"altcoinMarketCap":9441213.5257736705,"originalScore":"1580083320"}],"score":1580083200000},{"timestamp":"2020-01-28T00:00:00.000Z","searchInterval":"1440","btcDominance":65.990799999999993,"ethDominance":0,"activeCryptocurrencies":3368,"activeExchanges":668,"activeMarketPairs":20327,"quote":[{"name":"1","timestamp":"2020-01-28T00:00:00.000Z","totalMarketCap":27557381.435375109,"totalVolume24H":11930135.927107001,"totalVolume24HReported":12563494.88168489,"altcoinVolume24H":8749783.7167101596,"altcoinVolume24HReported":9158454.7019125503,"altcoinMarketCap":9372056.43537529,"originalScore":"1580169720"}],"score":1580169600000},{"timestamp":"2020-01-29T00:00:00.000Z","searchInterval":"1440","btcDominance":66.495999999999995,"ethDominance":0,"activeCryptocurrencies":3368,"activeExchanges":669,"activeMarketPairs":20323,"quote":[{"name":"1","timestamp":"2020-01-29T00:00:00.000Z","totalMarketCap":27350650.785389882,"totalVolume24H":13067805.99819462,"totalVolume24HReported":13914568.60471995,"altcoinVolume24H":9413261.9272248503,"altcoinVolume24HReported":9921685.01108112,"altcoinMarketCap":9163550.7853895295,"originalScore":"1580256120"}],"score":1580256000000},{"timestamp":"2020-01-30T00:00:00.000Z","searchInterval":"1440","btcDominance":66.444000000000003,"ethDominance":0,"activeCryptocurrencies":3368,"activeExchanges":669,"activeMarketPairs":20325,"quote":[{"name":"1","timestamp":"2020-01-30T00:00:00.000Z","totalMarketCap":27375042.927505579,"totalVolume24H":12480891.490908699,"totalVolume24HReported":13571917.850086519,"altcoinVolume24H":9225477.5991204903,"altcoinVolume24HReported":9875773.85728308,"altcoinMarketCap":9185955.9275052808,"originalScore":"1580342520"}],"score":1580342400000}]},"status":{"timestamp":["2024-06-11T21:10:31.372Z"],"error_code":["0"],"error_message":["SUCCESS"],"elapsed":["55"],"credit_count":[0]}} 2 | -------------------------------------------------------------------------------- /tests/testthat/test_data/99837b55ab943600d996822a3a2355d1.json: -------------------------------------------------------------------------------- 1 | {"data":{"id":[16],"name":["Poloniex"],"slug":["poloniex"],"logo":["https://s2.coinmarketcap.com/static/img/exchanges/64x64/16.png"],"description":["## What Is Poloniex?\n\nPoloniex is a global crypto [centralized exchange](https://coinmarketcap.com/alexandria/glossary/centralized-exchange-cex) (CEX) platform backed by Justin Sun, founder of [Tron](https://coinmarketcap.com/currencies/tron/). The exchange provides the following products: [spot trading](https://coinmarketcap.com/alexandria/glossary/spot-trading) and [margin trading](https://coinmarketcap.com/alexandria/glossary/margin-trading), [futures trading](https://coinmarketcap.com/alexandria/glossary/futures) and [perpetual swaps](https://coinmarketcap.com/alexandria/glossary/perpetual-contracts), [limit orders](https://coinmarketcap.com/alexandria/glossary/limit-order), real-time order books, a convenient lending system ([P2P lending](https://coinmarketcap.com/alexandria/glossary/peer-to-peer-p2p-lending)), crypto [staking](https://coinmarketcap.com/alexandria/glossary/staking) and a trading terminal.\n\nThe exchange lists over 350 cryptocurrencies and tokens, which can be purchased with a bank account, credit or debit card, and ApplePay.\n\nThe exchange has an insurance fund and its own crypto community — Poloniex Learn, where guides, educational materials and relevant industry news are available.\n\n\n## Who Are the Poloniex Founders?\n\nThe exchange was founded by Tristan D'Agosta (CEO) in early 2013. In 2019, Poloniex spun off its parent company, Circle, to form a new firm, Polo Digital Assets, Ltd., which currently owns the business.\n\nTristan D'Agosta, also known as Busoni, is a musician with a bachelor's degree from Rutgers University who got into cryptocurrencies, learned to code, and created his own exchange to trade Bitcoin and other digital assets securely. He’s the sole proprietor at Polonius Sheet Music and works for Various as well.\n\n\n## When Did Poloniex Launch?\n\nPoloniex has been operating since January 2014. In 2018, Circle Internet Financial Ltd acquired Poloniex at a valuation of around $400M. In 2019, Circle announced that the exchange would spin out, with the acquisition led by Justin Sun.\n\n\n## Where Is Poloniex Located?\n\nIt was originally based in the U.S. state of Delaware. Currently, the exchange is not available in the United States.\n\n\n## Poloniex Restricted Countries\n\nThe international exchange has its own geo-restrictions, including the United States, Cuba, Iran, North Korea, Sudan and Syria.\n\n\n## What Coins Are Supported on Poloniex?\n\nThe platform lists over 350 assets and more than 200 trading pairs, including [fiat](https://coinmarketcap.com/alexandria/glossary/fiat) currencies. Users can buy, sell and trade [BTC](https://coinmarketcap.com/currencies/bitcoin/), [ETH](https://coinmarketcap.com/currencies/ethereum/), [USDT](https://coinmarketcap.com/currencies/tether/), [TRX](https://coinmarketcap.com/currencies/tron/), [SOL](https://coinmarketcap.com/currencies/solana/), [XLM](https://coinmarketcap.com/currencies/stellar/), [DOGE](https://coinmarketcap.com/currencies/dogecoin/), [SHIB](https://coinmarketcap.com/currencies/shiba-inu/), and more.\n\n\n## How Much Are Poloniex Fees?\n\nPoloniex charges fees according to the [maker-taker](https://coinmarketcap.com/alexandria/glossary/market-maker-market-taker) fee model, where 0.01% is charged for maker fee and 0.075% for taker fee. Commissions are directly related to the 30-day [trading volume](https://coinmarketcap.com/alexandria/glossary/trading-volume), traders with large volumes receive discounts. The commission level on the platform starts at 0.155%.\n\n\n## Is It Possible To Use Leverage or Margin Trading on Poloniex?\n\nOne of the crucial features of Poloniex is margin trading, which allows platform clients to trade with [leverage](https://coinmarketcap.com/alexandria/glossary/leverage) of up to 2.5X. Poloniex Futures offers leverage on all of the Futures products, with leverage up to 100X. Loans at a certain percentage are also available."],"dateLaunched":["2014-01-10T00:00:00.000Z"],"notice":[""],"urls":{"website":["https://poloniex.com"],"blog":[],"chat":[],"fee":["https://poloniex.com/fees/"],"twitter":["https://twitter.com/Poloniex"],"actual":[]},"isHidden":[0],"status":["active"],"fiats":["USD"," CAD"," JPY"," GBP"," TRY"," ILS"," KRW"," PLN"," CHF"," NOK"," DKK"," SEK"," AUD"," ZAR"," CZK"," NZD"," HUF"," INR"," UAH"," HKD"," MYR"," NGN"," SGD"," TWD"," BGN"," BRL"," MAD"," RON"," MXN"," AED"," VND"," PHP"," KZT"," DOP"," PEN"," COP"],"countries":[],"tags":[],"type":[""],"makerFee":[0.01],"takerFee":[0.075],"quote":{"1":{"spotVolume24h":[63736.415803130003],"perpetualVolume24h":[283.79952983999999],"futuresVolume24h":[0],"totalVolumeUsd24h":[0],"verifiedVolumeUsd24h":[0],"updateTime":["2024-06-11T22:35:17.665Z"]},"2781":{"spotVolume24h":[4293668544.2859831],"perpetualVolume24h":[19118444.280200001],"futuresVolume24h":[0],"totalVolumeUsd24h":[0],"verifiedVolumeUsd24h":[0],"updateTime":["2024-06-11T22:35:17.665Z"]}},"platformId":[0],"dexStatus":[0],"walletSourceStatus":[0],"porSwitch":{"totalAssetSwitch":[1],"auditSwitch":[1],"walletAddressSwitch":[1],"tokenSwitch":[1]}},"status":{"timestamp":["2024-06-11T22:36:59.824Z"],"error_code":["0"],"error_message":["SUCCESS"],"elapsed":["15"],"credit_count":[0]}} 2 | -------------------------------------------------------------------------------- /tests/testthat/test_data/crypto_history_reference.rds: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sstoeckl/crypto2/acce4961c7c5964bf175cdf6dc2d44e456c77fdc/tests/testthat/test_data/crypto_history_reference.rds -------------------------------------------------------------------------------- /tests/testthat/test_data/crypto_info_reference.rds: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sstoeckl/crypto2/acce4961c7c5964bf175cdf6dc2d44e456c77fdc/tests/testthat/test_data/crypto_info_reference.rds -------------------------------------------------------------------------------- /tests/testthat/test_data/crypto_list_reference.rds: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sstoeckl/crypto2/acce4961c7c5964bf175cdf6dc2d44e456c77fdc/tests/testthat/test_data/crypto_list_reference.rds -------------------------------------------------------------------------------- /tests/testthat/test_data/crypto_listings_reference.rds: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sstoeckl/crypto2/acce4961c7c5964bf175cdf6dc2d44e456c77fdc/tests/testthat/test_data/crypto_listings_reference.rds -------------------------------------------------------------------------------- /tests/testthat/test_data/d9e71308b8f7d2956f6151a2ebf9423d.json: -------------------------------------------------------------------------------- 1 | {"data":{"id":[1],"name":["Bitcoin"],"symbol":["BTC"],"timeEnd":["1576565999"],"quotes":[{"timeOpen":"2019-12-31T01:00:00.000Z","timeClose":"2019-12-31T01:59:59.999Z","timeHigh":"2019-12-31T01:59:01.000Z","timeLow":"2019-12-31T01:38:01.000Z","quote":{"name":"2781","open":7291.5653523700003,"high":7303.8117814500001,"low":7289.5451109899996,"close":7303.8117814500001,"volume":0,"marketCap":132432072485.815,"timestamp":"2019-12-31T01:59:59.999Z"}},{"timeOpen":"2019-12-31T02:00:00.000Z","timeClose":"2019-12-31T02:59:59.999Z","timeHigh":"2019-12-31T02:17:00.000Z","timeLow":"2019-12-31T02:50:01.000Z","quote":{"name":"2781","open":7304.5874134400001,"high":7305.4420297899997,"low":7277.0130694700001,"close":7282.8154681300002,"volume":0,"marketCap":132051915391.532,"timestamp":"2019-12-31T02:59:59.999Z"}},{"timeOpen":"2019-12-31T03:00:00.000Z","timeClose":"2019-12-31T03:59:59.999Z","timeHigh":"2019-12-31T03:05:00.000Z","timeLow":"2019-12-31T03:36:00.000Z","quote":{"name":"2781","open":7283.7529248700002,"high":7291.4696177100004,"low":7264.0420331699997,"close":7264.9302405899998,"volume":0,"marketCap":131727896745.634,"timestamp":"2019-12-31T03:59:59.999Z"}},{"timeOpen":"2019-12-31T04:00:00.000Z","timeClose":"2019-12-31T04:59:59.999Z","timeHigh":"2019-12-31T04:44:00.000Z","timeLow":"2019-12-31T04:03:01.000Z","quote":{"name":"2781","open":7265.4132140000002,"high":7285.5070759999999,"low":7259.7859602400003,"close":7275.2276291999997,"volume":0,"marketCap":131915518656.799,"timestamp":"2019-12-31T04:59:59.999Z"}},{"timeOpen":"2019-12-31T05:00:00.000Z","timeClose":"2019-12-31T05:59:59.999Z","timeHigh":"2019-12-31T05:52:00.000Z","timeLow":"2019-12-31T05:04:01.000Z","quote":{"name":"2781","open":7274.1181104799998,"high":7288.2279948100004,"low":7267.5933156700003,"close":7281.7436332999996,"volume":0,"marketCap":132034301332.237,"timestamp":"2019-12-31T05:59:59.999Z"}},{"timeOpen":"2019-12-31T06:00:00.000Z","timeClose":"2019-12-31T06:59:59.999Z","timeHigh":"2019-12-31T06:12:00.000Z","timeLow":"2019-12-31T06:59:00.000Z","quote":{"name":"2781","open":7282.3333689600004,"high":7286.9733284100002,"low":7268.8166091700004,"close":7268.8166091700004,"volume":0,"marketCap":131800363402.453,"timestamp":"2019-12-31T06:59:59.999Z"}},{"timeOpen":"2019-12-31T07:00:00.000Z","timeClose":"2019-12-31T07:59:59.999Z","timeHigh":"2019-12-31T07:57:00.000Z","timeLow":"2019-12-31T07:11:00.000Z","quote":{"name":"2781","open":7270.2508616300001,"high":7283.97323564,"low":7265.9703681499996,"close":7283.9045465700001,"volume":0,"marketCap":132074306604.99899,"timestamp":"2019-12-31T07:59:59.999Z"}},{"timeOpen":"2019-12-31T08:00:00.000Z","timeClose":"2019-12-31T08:59:59.999Z","timeHigh":"2019-12-31T08:07:01.000Z","timeLow":"2019-12-31T08:48:01.000Z","quote":{"name":"2781","open":7283.4917634800004,"high":7285.9127830500001,"low":7268.7035077600003,"close":7272.49768657,"volume":0,"marketCap":131868018864.40401,"timestamp":"2019-12-31T08:59:59.999Z"}},{"timeOpen":"2019-12-31T09:00:00.000Z","timeClose":"2019-12-31T09:59:59.999Z","timeHigh":"2019-12-31T09:00:01.000Z","timeLow":"2019-12-31T09:22:01.000Z","quote":{"name":"2781","open":7273.4603430799998,"high":7273.4603430799998,"low":7259.7884092499999,"close":7264.5953169000004,"volume":0,"marketCap":131725456198.57201,"timestamp":"2019-12-31T09:59:59.999Z"}},{"timeOpen":"2019-12-31T10:00:00.000Z","timeClose":"2019-12-31T10:59:59.999Z","timeHigh":"2019-12-31T10:40:00.000Z","timeLow":"2019-12-31T10:55:00.000Z","quote":{"name":"2781","open":7264.2735462099999,"high":7280.51648664,"low":7246.9598399799997,"close":7271.8285686899999,"volume":0,"marketCap":131857704091.771,"timestamp":"2019-12-31T10:59:59.999Z"}},{"timeOpen":"2019-12-31T11:00:00.000Z","timeClose":"2019-12-31T11:59:59.999Z","timeHigh":"2019-12-31T11:55:01.000Z","timeLow":"2019-12-31T11:00:00.000Z","quote":{"name":"2781","open":7273.5771880599996,"high":7285.1826896399998,"low":7273.5771880599996,"close":7281.3937133600002,"volume":0,"marketCap":132031691855.929,"timestamp":"2019-12-31T11:59:59.999Z"}},{"timeOpen":"2019-12-31T12:00:00.000Z","timeClose":"2019-12-31T12:59:59.999Z","timeHigh":"2019-12-31T12:00:00.000Z","timeLow":"2019-12-31T12:21:00.000Z","quote":{"name":"2781","open":7281.0980366200001,"high":7281.0980366200001,"low":7267.7457447899997,"close":7276.2254280500001,"volume":0,"marketCap":131938427756.45,"timestamp":"2019-12-31T12:59:59.999Z"}},{"timeOpen":"2019-12-31T13:00:00.000Z","timeClose":"2019-12-31T13:59:59.999Z","timeHigh":"2019-12-31T13:55:01.000Z","timeLow":"2019-12-31T13:03:01.000Z","quote":{"name":"2781","open":7279.2413091999997,"high":7287.4553410300005,"low":7276.6327151900005,"close":7281.0614314799996,"volume":0,"marketCap":132026576804.34801,"timestamp":"2019-12-31T13:59:59.999Z"}},{"timeOpen":"2019-12-31T14:00:00.000Z","timeClose":"2019-12-31T14:59:59.999Z","timeHigh":"2019-12-31T14:55:00.000Z","timeLow":"2019-12-31T14:25:01.000Z","quote":{"name":"2781","open":7283.4094084500002,"high":7335.2900508000002,"low":7272.36192175,"close":7275.4078992499999,"volume":0,"marketCap":131924331201.205,"timestamp":"2019-12-31T14:59:59.999Z"}},{"timeOpen":"2019-12-31T15:00:00.000Z","timeClose":"2019-12-31T15:59:59.999Z","timeHigh":"2019-12-31T15:05:00.000Z","timeLow":"2019-12-31T15:54:00.000Z","quote":{"name":"2781","open":7272.8751352400004,"high":7294.43921675,"low":7252.3869955600003,"close":7255.1964037600001,"volume":0,"marketCap":131558200691.91701,"timestamp":"2019-12-31T15:59:59.999Z"}},{"timeOpen":"2019-12-31T16:00:00.000Z","timeClose":"2019-12-31T16:59:59.999Z","timeHigh":"2019-12-31T16:31:00.000Z","timeLow":"2019-12-31T16:01:01.000Z","quote":{"name":"2781","open":7252.3970558800002,"high":7270.9344866499996,"low":7250.23606062,"close":7260.9237057999999,"volume":0,"marketCap":131662416688.356,"timestamp":"2019-12-31T16:59:59.999Z"}},{"timeOpen":"2019-12-31T17:00:00.000Z","timeClose":"2019-12-31T17:59:59.999Z","timeHigh":"2019-12-31T17:03:00.000Z","timeLow":"2019-12-31T17:57:01.000Z","quote":{"name":"2781","open":7263.6915569700004,"high":7265.2944551800001,"low":7224.8919330899998,"close":7227.9781446099996,"volume":0,"marketCap":131065917929.21899,"timestamp":"2019-12-31T17:59:59.999Z"}},{"timeOpen":"2019-12-31T18:00:00.000Z","timeClose":"2019-12-31T18:59:59.999Z","timeHigh":"2019-12-31T18:55:00.000Z","timeLow":"2019-12-31T18:12:02.000Z","quote":{"name":"2781","open":7228.0043235700005,"high":7229.5099097299999,"low":7208.8286444900004,"close":7227.8702766699998,"volume":0,"marketCap":131064504035.356,"timestamp":"2019-12-31T18:59:59.999Z"}},{"timeOpen":"2019-12-31T19:00:00.000Z","timeClose":"2019-12-31T19:59:59.999Z","timeHigh":"2019-12-31T19:31:00.000Z","timeLow":"2019-12-31T19:08:00.000Z","quote":{"name":"2781","open":7225.4160736499998,"high":7235.9974133200003,"low":7218.1133857900004,"close":7232.3345465599996,"volume":0,"marketCap":131145911224.77299,"timestamp":"2019-12-31T19:59:59.999Z"}},{"timeOpen":"2019-12-31T20:00:00.000Z","timeClose":"2019-12-31T20:59:59.999Z","timeHigh":"2019-12-31T20:00:00.000Z","timeLow":"2019-12-31T20:21:01.000Z","quote":{"name":"2781","open":7230.9134983200001,"high":7230.9134983200001,"low":7169.9019444699998,"close":7170.7206753199998,"volume":0,"marketCap":130029008489.797,"timestamp":"2019-12-31T20:59:59.999Z"}},{"timeOpen":"2019-12-31T21:00:00.000Z","timeClose":"2019-12-31T21:59:59.999Z","timeHigh":"2019-12-31T21:55:00.000Z","timeLow":"2019-12-31T21:03:00.000Z","quote":{"name":"2781","open":7169.9232892999999,"high":7196.5969922200002,"low":7169.7776862800001,"close":7183.6426972999998,"volume":0,"marketCap":130263773099.86501,"timestamp":"2019-12-31T21:59:59.999Z"}},{"timeOpen":"2019-12-31T22:00:00.000Z","timeClose":"2019-12-31T22:59:59.999Z","timeHigh":"2019-12-31T22:59:01.000Z","timeLow":"2019-12-31T22:07:00.000Z","quote":{"name":"2781","open":7182.6235741299997,"high":7200.10421769,"low":7182.2163423700003,"close":7200.10421769,"volume":0,"marketCap":130563089831.48199,"timestamp":"2019-12-31T22:59:59.999Z"}},{"timeOpen":"2019-12-31T23:00:00.000Z","timeClose":"2019-12-31T23:59:59.999Z","timeHigh":"2019-12-31T23:03:00.000Z","timeLow":"2019-12-31T23:36:00.000Z","quote":{"name":"2781","open":7200.2921228799996,"high":7202.2688864900001,"low":7187.2714474000004,"close":7193.59897843,"volume":0,"marketCap":130446112598.42,"timestamp":"2019-12-31T23:59:59.999Z"}},{"timeOpen":"2020-01-01T00:00:00.000Z","timeClose":"2020-01-01T00:59:59.999Z","timeHigh":"2020-01-01T00:01:00.000Z","timeLow":"2020-01-01T00:59:00.000Z","quote":{"name":"2781","open":7194.8919705300004,"high":7195.1248451399997,"low":7176.6913787800004,"close":7176.6913787800004,"volume":0,"marketCap":130140327289.952,"timestamp":"2020-01-01T00:59:59.999Z"}},{"timeOpen":"2020-01-01T01:00:00.000Z","timeClose":"2020-01-01T01:59:59.999Z","timeHigh":"2020-01-01T01:41:00.000Z","timeLow":"2020-01-01T01:06:01.000Z","quote":{"name":"2781","open":7175.5613257799996,"high":7211.8222930900001,"low":7174.9441525599996,"close":7203.9355087900003,"volume":0,"marketCap":130634632078.134,"timestamp":"2020-01-01T01:59:59.999Z"}},{"timeOpen":"2020-01-01T02:00:00.000Z","timeClose":"2020-01-01T02:59:59.999Z","timeHigh":"2020-01-01T02:57:00.000Z","timeLow":"2020-01-01T02:00:00.000Z","quote":{"name":"2781","open":7204.2662933700003,"high":7231.7679990500001,"low":7204.2662933700003,"close":7231.58878602,"volume":0,"marketCap":131136452296.715,"timestamp":"2020-01-01T02:59:59.999Z"}},{"timeOpen":"2020-01-01T03:00:00.000Z","timeClose":"2020-01-01T03:59:59.999Z","timeHigh":"2020-01-01T03:03:00.000Z","timeLow":"2020-01-01T03:54:01.000Z","quote":{"name":"2781","open":7233.0875223599996,"high":7234.4840680300003,"low":7216.8523596100003,"close":7218.6939326900001,"volume":0,"marketCap":130903254373.356,"timestamp":"2020-01-01T03:59:59.999Z"}},{"timeOpen":"2020-01-01T04:00:00.000Z","timeClose":"2020-01-01T04:59:59.999Z","timeHigh":"2020-01-01T04:08:01.000Z","timeLow":"2020-01-01T04:19:00.000Z","quote":{"name":"2781","open":7218.5410900200004,"high":7219.8241281700002,"low":7211.2851319000001,"close":7212.4485580600003,"volume":0,"marketCap":130790361840.646,"timestamp":"2020-01-01T04:59:59.999Z"}},{"timeOpen":"2020-01-01T05:00:00.000Z","timeClose":"2020-01-01T05:59:59.999Z","timeHigh":"2020-01-01T05:17:01.000Z","timeLow":"2020-01-01T05:06:00.000Z","quote":{"name":"2781","open":7212.2665616900003,"high":7217.9708324800004,"low":7210.1588019600003,"close":7215.9930927200003,"volume":0,"marketCap":130855085735.129,"timestamp":"2020-01-01T05:59:59.999Z"}},{"timeOpen":"2020-01-01T06:00:00.000Z","timeClose":"2020-01-01T06:59:59.999Z","timeHigh":"2020-01-01T06:47:01.000Z","timeLow":"2020-01-01T06:05:01.000Z","quote":{"name":"2781","open":7216.61190119,"high":7226.5890929099996,"low":7215.78815528,"close":7219.8099643899995,"volume":0,"marketCap":130924662017.715,"timestamp":"2020-01-01T06:59:59.999Z"}},{"timeOpen":"2020-01-01T07:00:00.000Z","timeClose":"2020-01-01T07:59:59.999Z","timeHigh":"2020-01-01T07:33:00.000Z","timeLow":"2020-01-01T07:53:00.000Z","quote":{"name":"2781","open":7220.9826820199996,"high":7226.6764619799997,"low":7200.8867647899997,"close":7207.5913609600002,"volume":0,"marketCap":130704169938.80099,"timestamp":"2020-01-01T07:59:59.999Z"}},{"timeOpen":"2020-01-01T08:00:00.000Z","timeClose":"2020-01-01T08:59:59.999Z","timeHigh":"2020-01-01T08:01:00.000Z","timeLow":"2020-01-01T08:53:00.000Z","quote":{"name":"2781","open":7208.6491855800004,"high":7209.3990266700002,"low":7187.3379549399997,"close":7191.7076227099997,"volume":0,"marketCap":130416669843.00101,"timestamp":"2020-01-01T08:59:59.999Z"}},{"timeOpen":"2020-01-01T09:00:00.000Z","timeClose":"2020-01-01T09:59:59.999Z","timeHigh":"2020-01-01T09:08:00.000Z","timeLow":"2020-01-01T09:00:00.000Z","quote":{"name":"2781","open":7191.7271720899998,"high":7203.4636585899998,"low":7191.7271720899998,"close":7197.3418548700001,"volume":0,"marketCap":130519476132.955,"timestamp":"2020-01-01T09:59:59.999Z"}},{"timeOpen":"2020-01-01T10:00:00.000Z","timeClose":"2020-01-01T10:59:59.999Z","timeHigh":"2020-01-01T10:27:00.000Z","timeLow":"2020-01-01T10:05:01.000Z","quote":{"name":"2781","open":7196.8096735899999,"high":7207.3019421400004,"low":7193.4625097500002,"close":7202.78830862,"volume":0,"marketCap":130618604443.254,"timestamp":"2020-01-01T10:59:59.999Z"}},{"timeOpen":"2020-01-01T11:00:00.000Z","timeClose":"2020-01-01T11:59:59.999Z","timeHigh":"2020-01-01T11:33:01.000Z","timeLow":"2020-01-01T11:02:00.000Z","quote":{"name":"2781","open":7204.4579962799999,"high":7224.4538921399999,"low":7202.5872738799999,"close":7204.5388118000001,"volume":0,"marketCap":130650975650.52299,"timestamp":"2020-01-01T11:59:59.999Z"}},{"timeOpen":"2020-01-01T12:00:00.000Z","timeClose":"2020-01-01T12:59:59.999Z","timeHigh":"2020-01-01T12:58:00.000Z","timeLow":"2020-01-01T12:00:00.000Z","quote":{"name":"2781","open":7197.9388372000003,"high":7220.6995459999998,"low":7197.9388372000003,"close":7219.7819176800003,"volume":0,"marketCap":130927763307.19501,"timestamp":"2020-01-01T12:59:59.999Z"}},{"timeOpen":"2020-01-01T13:00:00.000Z","timeClose":"2020-01-01T13:59:59.999Z","timeHigh":"2020-01-01T13:39:00.000Z","timeLow":"2020-01-01T13:24:00.000Z","quote":{"name":"2781","open":7218.8431362399997,"high":7242.7157079199997,"low":7216.6111268200002,"close":7234.4736141900003,"volume":0,"marketCap":131194827789.411,"timestamp":"2020-01-01T13:59:59.999Z"}},{"timeOpen":"2020-01-01T14:00:00.000Z","timeClose":"2020-01-01T14:59:59.999Z","timeHigh":"2020-01-01T14:32:00.000Z","timeLow":"2020-01-01T14:18:00.000Z","quote":{"name":"2781","open":7232.7351279000004,"high":7238.3226782399997,"low":7223.8757568999999,"close":7231.9071444800002,"volume":0,"marketCap":131149551279.073,"timestamp":"2020-01-01T14:59:59.999Z"}},{"timeOpen":"2020-01-01T15:00:00.000Z","timeClose":"2020-01-01T15:59:59.999Z","timeHigh":"2020-01-01T15:42:01.000Z","timeLow":"2020-01-01T15:01:00.000Z","quote":{"name":"2781","open":7233.03266403,"high":7254.3306113400004,"low":7231.3311478799997,"close":7235.2526825499999,"volume":0,"marketCap":131211213339.759,"timestamp":"2020-01-01T15:59:59.999Z"}},{"timeOpen":"2020-01-01T16:00:00.000Z","timeClose":"2020-01-01T16:59:59.999Z","timeHigh":"2020-01-01T16:26:00.000Z","timeLow":"2020-01-01T16:34:01.000Z","quote":{"name":"2781","open":7235.2343037199998,"high":7243.0922307500005,"low":7221.1514792099997,"close":7242.6755897200001,"volume":0,"marketCap":131346914066.12801,"timestamp":"2020-01-01T16:59:59.999Z"}},{"timeOpen":"2020-01-01T17:00:00.000Z","timeClose":"2020-01-01T17:59:59.999Z","timeHigh":"2020-01-01T17:27:00.000Z","timeLow":"2020-01-01T17:11:01.000Z","quote":{"name":"2781","open":7243.0487573800001,"high":7252.9190697100003,"low":7239.2463525100002,"close":7245.2214441899996,"volume":0,"marketCap":131393626877.332,"timestamp":"2020-01-01T17:59:59.999Z"}},{"timeOpen":"2020-01-01T18:00:00.000Z","timeClose":"2020-01-01T18:59:59.999Z","timeHigh":"2020-01-01T18:29:00.000Z","timeLow":"2020-01-01T18:45:01.000Z","quote":{"name":"2781","open":7245.0649049499998,"high":7245.8967479800003,"low":7233.01851317,"close":7237.1664333199997,"volume":0,"marketCap":131248090335.02499,"timestamp":"2020-01-01T18:59:59.999Z"}},{"timeOpen":"2020-01-01T19:00:00.000Z","timeClose":"2020-01-01T19:59:59.999Z","timeHigh":"2020-01-01T19:07:01.000Z","timeLow":"2020-01-01T19:42:01.000Z","quote":{"name":"2781","open":7238.9306558899998,"high":7243.94412233,"low":7232.6006238700002,"close":7234.2999538699996,"volume":0,"marketCap":131197191052.51199,"timestamp":"2020-01-01T19:59:59.999Z"}},{"timeOpen":"2020-01-01T20:00:00.000Z","timeClose":"2020-01-01T20:59:59.999Z","timeHigh":"2020-01-01T20:39:00.000Z","timeLow":"2020-01-01T20:59:00.000Z","quote":{"name":"2781","open":7233.2187391400003,"high":7243.7956331900004,"low":7227.97522854,"close":7227.97522854,"volume":0,"marketCap":131083306155.94901,"timestamp":"2020-01-01T20:59:59.999Z"}},{"timeOpen":"2020-01-01T21:00:00.000Z","timeClose":"2020-01-01T21:59:59.999Z","timeHigh":"2020-01-01T21:48:01.000Z","timeLow":"2020-01-01T21:00:00.000Z","quote":{"name":"2781","open":7224.4070795600001,"high":7252.6963435600001,"low":7224.4070795600001,"close":7236.3537941100003,"volume":0,"marketCap":131235617868.461,"timestamp":"2020-01-01T21:59:59.999Z"}},{"timeOpen":"2020-01-01T22:00:00.000Z","timeClose":"2020-01-01T22:59:59.999Z","timeHigh":"2020-01-01T22:01:00.000Z","timeLow":"2020-01-01T22:59:00.000Z","quote":{"name":"2781","open":7236.6482294699999,"high":7237.7201674799999,"low":7198.0892955099998,"close":7198.0892955099998,"volume":0,"marketCap":130542474413.65199,"timestamp":"2020-01-01T22:59:59.999Z"}},{"timeOpen":"2020-01-01T23:00:00.000Z","timeClose":"2020-01-01T23:59:59.999Z","timeHigh":"2020-01-01T23:50:00.000Z","timeLow":"2020-01-01T23:24:00.000Z","quote":{"name":"2781","open":7198.6319415099997,"high":7215.5572245699996,"low":7186.7927976199999,"close":7200.1743927400003,"volume":0,"marketCap":130580829149.58701,"timestamp":"2020-01-01T23:59:59.999Z"}},{"timeOpen":"2020-01-02T00:00:00.000Z","timeClose":"2020-01-02T00:59:59.999Z","timeHigh":"2020-01-02T00:59:00.000Z","timeLow":"2020-01-02T00:14:01.000Z","quote":{"name":"2781","open":7202.55112207,"high":7208.8577267000001,"low":7179.9759727800001,"close":7208.8577267000001,"volume":0,"marketCap":130738848909.065,"timestamp":"2020-01-02T00:59:59.999Z"}},{"timeOpen":"2020-01-02T01:00:00.000Z","timeClose":"2020-01-02T01:59:59.999Z","timeHigh":"2020-01-02T01:30:00.000Z","timeLow":"2020-01-02T01:59:01.000Z","quote":{"name":"2781","open":7208.9983560500004,"high":7212.1552525200004,"low":7190.5850281499997,"close":7190.5850281499997,"volume":0,"marketCap":130407997063.672,"timestamp":"2020-01-02T01:59:59.999Z"}},{"timeOpen":"2020-01-02T02:00:00.000Z","timeClose":"2020-01-02T02:59:59.999Z","timeHigh":"2020-01-02T02:02:01.000Z","timeLow":"2020-01-02T02:37:01.000Z","quote":{"name":"2781","open":7189.1245229400001,"high":7190.0435102700003,"low":7160.2631616299996,"close":7169.6117122200003,"volume":0,"marketCap":130028078012.82201,"timestamp":"2020-01-02T02:59:59.999Z"}},{"timeOpen":"2020-01-02T03:00:00.000Z","timeClose":"2020-01-02T03:59:59.999Z","timeHigh":"2020-01-02T03:12:01.000Z","timeLow":"2020-01-02T03:58:00.000Z","quote":{"name":"2781","open":7169.7079034099997,"high":7177.9619123800003,"low":7137.01424391,"close":7139.1381815799996,"volume":0,"marketCap":129476031166.157,"timestamp":"2020-01-02T03:59:59.999Z"}},{"timeOpen":"2020-01-02T04:00:00.000Z","timeClose":"2020-01-02T04:59:59.999Z","timeHigh":"2020-01-02T04:29:01.000Z","timeLow":"2020-01-02T04:03:00.000Z","quote":{"name":"2781","open":7137.2605593099997,"high":7151.74220358,"low":7131.3396593099997,"close":7141.1835280900004,"volume":0,"marketCap":129513397113.381,"timestamp":"2020-01-02T04:59:59.999Z"}},{"timeOpen":"2020-01-02T05:00:00.000Z","timeClose":"2020-01-02T05:59:59.999Z","timeHigh":"2020-01-02T05:40:00.000Z","timeLow":"2020-01-02T05:32:00.000Z","quote":{"name":"2781","open":7142.8170002200004,"high":7162.3714400899999,"low":7117.9808496599999,"close":7136.5178755999996,"volume":0,"marketCap":129429672321.35001,"timestamp":"2020-01-02T05:59:59.999Z"}},{"timeOpen":"2020-01-02T06:00:00.000Z","timeClose":"2020-01-02T06:59:59.999Z","timeHigh":"2020-01-02T06:16:00.000Z","timeLow":"2020-01-02T06:30:01.000Z","quote":{"name":"2781","open":7136.2304147200002,"high":7161.2454207500004,"low":7126.7864121900002,"close":7136.4580659900003,"volume":0,"marketCap":129429208471.16299,"timestamp":"2020-01-02T06:59:59.999Z"}},{"timeOpen":"2020-01-02T07:00:00.000Z","timeClose":"2020-01-02T07:59:59.999Z","timeHigh":"2020-01-02T07:00:00.000Z","timeLow":"2020-01-02T07:32:00.000Z","quote":{"name":"2781","open":7139.0201853500002,"high":7139.0201853500002,"low":7116.7336631999997,"close":7122.7205385099996,"volume":0,"marketCap":129180594247.27901,"timestamp":"2020-01-02T07:59:59.999Z"}},{"timeOpen":"2020-01-02T08:00:00.000Z","timeClose":"2020-01-02T08:59:59.999Z","timeHigh":"2020-01-02T08:57:01.000Z","timeLow":"2020-01-02T08:02:01.000Z","quote":{"name":"2781","open":7122.0679806600001,"high":7152.9240156599999,"low":7118.9906872199999,"close":7152.1392965100003,"volume":0,"marketCap":129714774351.15401,"timestamp":"2020-01-02T08:59:59.999Z"}},{"timeOpen":"2020-01-02T09:00:00.000Z","timeClose":"2020-01-02T09:59:59.999Z","timeHigh":"2020-01-02T09:30:01.000Z","timeLow":"2020-01-02T09:29:01.000Z","quote":{"name":"2781","open":7152.4798692200002,"high":7176.1596542899997,"low":7130.1417203999999,"close":7156.3054784100004,"volume":0,"marketCap":129790513216.82001,"timestamp":"2020-01-02T09:59:59.999Z"}},{"timeOpen":"2020-01-02T10:00:00.000Z","timeClose":"2020-01-02T10:59:59.999Z","timeHigh":"2020-01-02T10:32:01.000Z","timeLow":"2020-01-02T10:00:00.000Z","quote":{"name":"2781","open":7157.13097638,"high":7180.3830583999998,"low":7157.13097638,"close":7172.5549843400004,"volume":0,"marketCap":130085760728.981,"timestamp":"2020-01-02T10:59:59.999Z"}},{"timeOpen":"2020-01-02T11:00:00.000Z","timeClose":"2020-01-02T11:59:59.999Z","timeHigh":"2020-01-02T11:04:00.000Z","timeLow":"2020-01-02T11:56:00.000Z","quote":{"name":"2781","open":7172.1551069400002,"high":7173.8205298900002,"low":7159.3897569800001,"close":7159.6874680299998,"volume":0,"marketCap":129853103701.42,"timestamp":"2020-01-02T11:59:59.999Z"}},{"timeOpen":"2020-01-02T12:00:00.000Z","timeClose":"2020-01-02T12:59:59.999Z","timeHigh":"2020-01-02T12:36:01.000Z","timeLow":"2020-01-02T12:02:00.000Z","quote":{"name":"2781","open":7158.9105907200001,"high":7172.7161825499998,"low":7157.3795997300003,"close":7165.5422241699998,"volume":0,"marketCap":129960092197.83299,"timestamp":"2020-01-02T12:59:59.999Z"}},{"timeOpen":"2020-01-02T13:00:00.000Z","timeClose":"2020-01-02T13:59:59.999Z","timeHigh":"2020-01-02T13:09:00.000Z","timeLow":"2020-01-02T13:55:00.000Z","quote":{"name":"2781","open":7165.8051608599999,"high":7172.6586168800004,"low":7138.6153806800003,"close":7146.13950432,"volume":0,"marketCap":129608546022.60001,"timestamp":"2020-01-02T13:59:59.999Z"}},{"timeOpen":"2020-01-02T14:00:00.000Z","timeClose":"2020-01-02T14:59:59.999Z","timeHigh":"2020-01-02T14:49:00.000Z","timeLow":"2020-01-02T14:00:00.000Z","quote":{"name":"2781","open":7141.7751083700005,"high":7165.9908050599997,"low":7141.7751083700005,"close":7161.0543221999997,"volume":0,"marketCap":129879684189.02499,"timestamp":"2020-01-02T14:59:59.999Z"}},{"timeOpen":"2020-01-02T15:00:00.000Z","timeClose":"2020-01-02T15:59:59.999Z","timeHigh":"2020-01-02T15:08:00.000Z","timeLow":"2020-01-02T15:49:01.000Z","quote":{"name":"2781","open":7160.9818164899998,"high":7165.0614102999998,"low":7149.0567855899999,"close":7152.9035530800002,"volume":0,"marketCap":129732211742.21201,"timestamp":"2020-01-02T15:59:59.999Z"}},{"timeOpen":"2020-01-02T16:00:00.000Z","timeClose":"2020-01-02T16:59:59.999Z","timeHigh":"2020-01-02T16:33:00.000Z","timeLow":"2020-01-02T16:59:00.000Z","quote":{"name":"2781","open":7153.7222709799998,"high":7183.4335915000001,"low":7086.4196083799998,"close":7086.4196083799998,"volume":0,"marketCap":128527008955.694,"timestamp":"2020-01-02T16:59:59.999Z"}},{"timeOpen":"2020-01-02T17:00:00.000Z","timeClose":"2020-01-02T17:59:59.999Z","timeHigh":"2020-01-02T17:00:00.000Z","timeLow":"2020-01-02T17:27:00.000Z","quote":{"name":"2781","open":7085.5469079200002,"high":7085.5469079200002,"low":6985.0657988700004,"close":6986.7337386099998,"volume":0,"marketCap":126719612495.57401,"timestamp":"2020-01-02T17:59:59.999Z"}},{"timeOpen":"2020-01-02T18:00:00.000Z","timeClose":"2020-01-02T18:59:59.999Z","timeHigh":"2020-01-02T18:59:00.000Z","timeLow":"2020-01-02T18:09:00.000Z","quote":{"name":"2781","open":6986.2601560900002,"high":7000.9620214300003,"low":6977.4307866999998,"close":7000.9620214300003,"volume":0,"marketCap":126978198423.181,"timestamp":"2020-01-02T18:59:59.999Z"}},{"timeOpen":"2020-01-02T19:00:00.000Z","timeClose":"2020-01-02T19:59:59.999Z","timeHigh":"2020-01-02T19:50:00.000Z","timeLow":"2020-01-02T19:14:00.000Z","quote":{"name":"2781","open":6999.1714604299996,"high":7007.5712872699996,"low":6992.0390677200003,"close":7005.2193434199999,"volume":0,"marketCap":127055764797.412,"timestamp":"2020-01-02T19:59:59.999Z"}},{"timeOpen":"2020-01-02T20:00:00.000Z","timeClose":"2020-01-02T20:59:59.999Z","timeHigh":"2020-01-02T20:01:00.000Z","timeLow":"2020-01-02T20:59:00.000Z","quote":{"name":"2781","open":7003.2593673800002,"high":7004.22067677,"low":6962.6221799599998,"close":6962.6221799599998,"volume":0,"marketCap":126283515395.698,"timestamp":"2020-01-02T20:59:59.999Z"}},{"timeOpen":"2020-01-02T21:00:00.000Z","timeClose":"2020-01-02T21:59:59.999Z","timeHigh":"2020-01-02T21:57:00.000Z","timeLow":"2020-01-02T21:01:00.000Z","quote":{"name":"2781","open":6962.0289037900002,"high":6996.0667996000002,"low":6960.6837117799996,"close":6994.8795032899998,"volume":0,"marketCap":126869011441.526,"timestamp":"2020-01-02T21:59:59.999Z"}},{"timeOpen":"2020-01-02T22:00:00.000Z","timeClose":"2020-01-02T22:59:59.999Z","timeHigh":"2020-01-02T22:04:01.000Z","timeLow":"2020-01-02T22:35:01.000Z","quote":{"name":"2781","open":6995.0269597799997,"high":6996.6347436200003,"low":6987.8816141899997,"close":6992.9069021699997,"volume":0,"marketCap":126833848938.108,"timestamp":"2020-01-02T22:59:59.999Z"}}]},"status":{"timestamp":["2024-06-11T21:56:55.143Z"],"error_code":["0"],"error_message":["SUCCESS"],"elapsed":["44"],"credit_count":[0]}} 2 | -------------------------------------------------------------------------------- /tests/testthat/test_data/ex_info_reference.rds: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sstoeckl/crypto2/acce4961c7c5964bf175cdf6dc2d44e456c77fdc/tests/testthat/test_data/ex_info_reference.rds -------------------------------------------------------------------------------- /tests/testthat/test_data/exchange_list_reference.rds: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sstoeckl/crypto2/acce4961c7c5964bf175cdf6dc2d44e456c77fdc/tests/testthat/test_data/exchange_list_reference.rds -------------------------------------------------------------------------------- /tests/testthat/test_data/fcce7d762bbee082f65c377f184cd541.json: -------------------------------------------------------------------------------- 1 | {"data":{"id":[1],"name":["Bitcoin"],"symbol":["BTC"],"timeEnd":["1543881599"],"quotes":[{"timeOpen":"2020-01-02T00:00:00.000Z","timeClose":"2020-01-02T23:59:59.999Z","timeHigh":"2020-01-02T01:30:00.000Z","timeLow":"2020-01-02T23:02:01.000Z","quote":{"name":"2781","open":7202.55112207,"high":7212.1552525200004,"low":6935.2699719299999,"close":6985.4700006100002,"volume":20802083465.330002,"marketCap":126699395235.20399,"timestamp":"2020-01-02T23:59:59.999Z"}},{"timeOpen":"2020-01-03T00:00:00.000Z","timeClose":"2020-01-03T23:59:59.999Z","timeHigh":"2020-01-03T17:04:00.000Z","timeLow":"2020-01-03T02:10:01.000Z","quote":{"name":"2781","open":6984.4286123000002,"high":7413.7150993400001,"low":6914.9959079299997,"close":7344.8841834100003,"volume":28111481031.939999,"marketCap":133233444755.489,"timestamp":"2020-01-03T23:59:59.999Z"}},{"timeOpen":"2020-01-04T00:00:00.000Z","timeClose":"2020-01-04T23:59:59.999Z","timeHigh":"2020-01-04T18:44:02.000Z","timeLow":"2020-01-04T00:39:02.000Z","quote":{"name":"2781","open":7345.3752752700002,"high":7427.3857943499997,"low":7309.5140117000001,"close":7410.6565664199998,"volume":18444271274.759998,"marketCap":134442464030.26401,"timestamp":"2020-01-04T23:59:59.999Z"}},{"timeOpen":"2020-01-05T00:00:00.000Z","timeClose":"2020-01-05T23:59:59.999Z","timeHigh":"2020-01-05T18:57:00.000Z","timeLow":"2020-01-05T23:18:00.000Z","quote":{"name":"2781","open":7410.4516937300004,"high":7544.4968722399999,"low":7400.5355608999998,"close":7411.3173267599996,"volume":19725074094.540001,"marketCap":134469548249.076,"timestamp":"2020-01-05T23:59:59.999Z"}},{"timeOpen":"2020-01-06T00:00:00.000Z","timeClose":"2020-01-06T23:59:59.999Z","timeHigh":"2020-01-06T23:55:01.000Z","timeLow":"2020-01-06T00:05:01.000Z","quote":{"name":"2781","open":7410.45216807,"high":7781.8671827500002,"low":7409.29318246,"close":7769.21903905,"volume":23276261598.25,"marketCap":140976457303.70999,"timestamp":"2020-01-06T23:59:59.999Z"}},{"timeOpen":"2020-01-07T00:00:00.000Z","timeClose":"2020-01-07T23:59:59.999Z","timeHigh":"2020-01-07T23:51:01.000Z","timeLow":"2020-01-07T00:01:01.000Z","quote":{"name":"2781","open":7768.6818909800004,"high":8178.2158733599999,"low":7768.2277194400003,"close":8163.6922394399999,"volume":28767291326.970001,"marketCap":148152237653.685,"timestamp":"2020-01-07T23:59:59.999Z"}}]},"status":{"timestamp":["2024-06-11T22:00:14.766Z"],"error_code":["0"],"error_message":["SUCCESS"],"elapsed":["50"],"credit_count":[0]}} 2 | -------------------------------------------------------------------------------- /tests/testthat/test_data/historical_output.rds: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sstoeckl/crypto2/acce4961c7c5964bf175cdf6dc2d44e456c77fdc/tests/testthat/test_data/historical_output.rds --------------------------------------------------------------------------------