{"id":7722,"date":"2021-11-15T15:30:40","date_gmt":"2021-11-15T20:30:40","guid":{"rendered":"https:\/\/blogs.swarthmore.edu\/its\/?p=7722"},"modified":"2023-07-25T12:11:39","modified_gmt":"2023-07-25T16:11:39","slug":"charting-bitcoin-prices-with-react","status":"publish","type":"post","link":"https:\/\/blogs.swarthmore.edu\/its\/2021\/11\/15\/charting-bitcoin-prices-with-react\/","title":{"rendered":"Charting Bitcoin prices with React"},"content":{"rendered":"\n<p><em>Header image attributed to <\/em>https:\/\/www.pexels.com\/photo\/close-up-view-of-a-golden-coin-315788\/ \ud83d\ude03<\/p>\n\n\n\n<p>As I was glued to the Bitcoin charts on my phone late-night this Sunday, trying to <a href=\"https:\/\/www.investopedia.com\/terms\/b\/buy-the-dips.asp\" target=\"_blank\" rel=\"noreferrer noopener\">time the dip<\/a>, I thought to myself &#8212; It would be super nice to have something that could display and analyze historical Bitcoin prices, and perform actions on my behalf when certain price events happen.<\/p>\n\n\n\n<p>So I began to brainstorm for a bit and put together a rough plan to do just that. Here is what I came up with.<\/p>\n\n\n\n<ol class=\"wp-block-list\"><li>Create a <a href=\"https:\/\/reactjs.org\/\" target=\"_blank\" rel=\"noreferrer noopener\">web app using ReactJS<\/a> to display the price data, as well as allow the user to interact with it.<\/li><li>Run the app on a <a href=\"https:\/\/www.raspberrypi.org\/\" target=\"_blank\" rel=\"noreferrer noopener\">Raspberry pi<\/a> \/ <a href=\"https:\/\/www.arduino.cc\/\" target=\"_blank\" rel=\"noreferrer noopener\">Arduino <\/a>(with a display).<\/li><li>Use <a href=\"https:\/\/en.wikipedia.org\/wiki\/Webhook\" target=\"_blank\" rel=\"noreferrer noopener\">webhooks<\/a> to trigger functions once certain price events happen.<\/li><\/ol>\n\n\n\n<p>This post will go over building the react app.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Project Setup<\/h2>\n\n\n\n<p>Ok, so step 1 of the project will be to get our react project set up, along with the dependencies. I&#8217;m going to use <a href=\"https:\/\/reactjs.org\/docs\/create-a-new-react-app.html\">create react app<\/a> to bootstrap a react project, clean up the bloat from it (this is an optional step, but it&#8217;s always a good idea to practice good housekeeping \ud83e\uddf9), then install my other dependencies. I&#8217;ve decided to use the <a rel=\"noreferrer noopener\" href=\"https:\/\/formidable.com\/open-source\/victory\/\" target=\"_blank\">Victory<\/a> chart library for this project.<\/p>\n\n\n\n<p>Anyways, you can use the following commands to get the project set up.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"bash\" class=\"language-bash\">npx create react-app crypto-react-charts\ncd crypto-react-charts\nnpm i victory\nnpm start<\/code><\/pre>\n\n\n\n<p>Awesome, so we now have a react project set up and running on a development server. The next step is to add the price data to the main App component.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Getting the Price Data<\/h2>\n\n\n\n<p>For price data, we are going to use the Coindesk API, specifically, the <a rel=\"noreferrer noopener nofollow\" href=\"https:\/\/api.coindesk.com\/v1\/bpi\/historical\/close.json\" target=\"_blank\">close.json endpoint<\/a>. There&#8217;s no authorization required to use this API, but there&#8217;s a stipulation that <a href=\"https:\/\/old.coindesk.com\/coindesk-api\">you must include a link back to the CoinDesk price chart in your app<\/a>.<\/p>\n\n\n\n<p>In App.js, we need to do the following:<\/p>\n\n\n\n<ol class=\"wp-block-list\"><li>Create a function to fetch the price data.<\/li><li>Convert it to a format that our Victory chart can use.<\/li><li>Fetch the data once the App component mounts<\/li><\/ol>\n\n\n\n<p>We will also create 2 date pickers so that the user can further filter the price data. The following code takes care of all of that (we will create the Chart component in the next section).<\/p>\n\n\n\n<pre class=\"wp-block-code\" title=\"App.js\"><code lang=\"javascript\" class=\"language-javascript line-numbers\">import {useState, useEffect} from 'react';\nimport Chart from '.\/Chart';\n\nimport '.\/App.css';\n\nfunction App() {\n\n  \/\/ The price data received from the API.\n  const [bpi, setBpi] = useState(null);\n  \/\/ Any errors we may receive.\n  const [error, setError] = useState(null);\n  \/\/ The start date filter.\n  const [startDate, setStartDate] = useState('');\n  \/\/ The end date filter.\n  const [endDate, setEndDate] = useState('');\n\n  \/\/ Get the data from the Coinbase API\n  const fetchData = async (start=null, end=null) => {\n    const url = new URL(\n      'https:\/\/api.coindesk.com\/v1\/bpi\/historical\/close.json\n    ');\n    \/\/ Assign the start date if it exists.\n    start &amp;&amp; url.searchParams.set('start', start);\n    \/\/ Assign the end date if it exists.\n    end &amp;&amp; url.searchParams.set('end', end);\n    \/\/ Make the request.\n    const response = await fetch(url.toString());\n    const data = await response.json();\n    return data;\n  }\n\n  \/\/ When the component mounts, or when startDate or endDate\n  \/\/ change, fetch the data.\n  useEffect(() => {\n    fetchData(startDate, endDate)\n      .then(data => {\n        \/\/ Format the data to use with VictoryLine chart.\n        const bpi = Object.keys(data.bpi).map(key => ({ \n          x: key, \n          y: data.bpi[key] \n        }));\n        setBpi(bpi);\n      })\n      .catch(error => setError(error))\n  }, [startDate, endDate]);\n\n  return (\n    &lt;div className=\"App\">\n      &lt;main>\n        &lt;h6>BTC Price History&lt;\/h6>\n        {bpi &amp;&amp; &lt;Chart data={bpi} \/>}\n\n        &lt;div className=\"date-inputs\">\n          &lt;label>\n            Start: &lt;input \n              value={startDate} \n              onChange={e => setStartDate(e.target.value)}\n              type=\"date\" \n              min=\"2021-10-15\" \/>\n          &lt;\/label>\n\n          &lt;label>\n            End: &lt;input \n              value={endDate} \n              onChange={e => setEndDate(e.target.value)}\n              type=\"date\" \n              max=\"2021-11-15\" \/>\n          &lt;\/label>\n        &lt;\/div>\n      &lt;\/main>\n\n      &lt;footer>\n        &lt;a href={\"https:\/\/old.coindesk.com\/price\/bitcoin\"}>\n          Powered by Coindesk\n        &lt;\/a>\n      &lt;\/footer>\n    &lt;\/div>\n  );\n}\n\nexport default App;<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Creating the Chart<\/h2>\n\n\n\n<p>The last piece of the project is to create our chart component. We will be using a <a rel=\"noreferrer noopener nofollow\" href=\"https:\/\/formidable.com\/open-source\/victory\/docs\/victory-line\" target=\"_blank\">VictoryLine chart<\/a> to plot the price points received from the API. <\/p>\n\n\n\n<p>Create a new file, Chart.js, and add the VictoryChart components (please see the <a href=\"https:\/\/formidable.com\/open-source\/victory\/docs\" target=\"_blank\" rel=\"noreferrer noopener\">Victory docs<\/a> for further reference). Your Chart.js should look similar to this. (Notice that we had to do some formatting for both of the axis labels).<\/p>\n\n\n\n<pre class=\"wp-block-code\" title=\"Chart.js\"><code lang=\"javascript\" class=\"language-javascript line-numbers\">import {VictoryChart, VictoryAxis, VictoryLine} from 'victory';\n\nexport default function Chart(props) {\n    return (\n        &lt;VictoryChart \n            width={640} \n            height={440}\n        >\n            &lt;VictoryLine \n                data={props.data} \n                interpolation=\"natural\"\n                domain={{y: [58000, 68000]}}\n            \/>\n\n            &lt;VictoryAxis \n                fixLabelOverlap={true} \n                tickFormat={t => new Date(t).toLocaleDateString()}     \n                style={{ tickLabels: {fontSize: 10} }}\n            \/>\n            &lt;VictoryAxis \n                dependentAxis \n                style={{ tickLabels: {fontSize: 10} }}\n            \/>\n        &lt;\/VictoryChart>\n    );\n}<\/code><\/pre>\n\n\n\n<p>You should end up with the following:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large is-resized is-style-default\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/blogs.swarthmore.edu\/its\/wp-content\/uploads\/2021\/11\/chart.png\" alt=\"\" class=\"wp-image-7754\" width=\"436\" height=\"375\" srcset=\"https:\/\/blogs.swarthmore.edu\/its\/wp-content\/uploads\/2021\/11\/chart.png 781w, https:\/\/blogs.swarthmore.edu\/its\/wp-content\/uploads\/2021\/11\/chart-300x259.png 300w, https:\/\/blogs.swarthmore.edu\/its\/wp-content\/uploads\/2021\/11\/chart-768x662.png 768w, https:\/\/blogs.swarthmore.edu\/its\/wp-content\/uploads\/2021\/11\/chart-70x60.png 70w\" sizes=\"auto, (max-width: 436px) 100vw, 436px\" \/><figcaption>Bitcoin price history app<\/figcaption><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">Conclusion<\/h2>\n\n\n\n<p>That&#8217;s pretty much it! There are a lot of ways you can extend this code (getting prices for different coins, fetching real-time data, dynamically setting the axis domain based on price data, adding email\/SMS alerts, etc.), some of which will be covered in future posts, so stay tuned!<\/p>\n\n\n\n<p>You can find the full codebase for this project <a href=\"https:\/\/github.com\/Swarthmore\/bitcoin-react-tracker\" target=\"_blank\" rel=\"noreferrer noopener\">here<\/a> and the deployment of it <a href=\"https:\/\/swarthmore.github.io\/bitcoin-react-tracker\/\" target=\"_blank\" rel=\"noreferrer noopener\">here<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Header image attributed to https:\/\/www.pexels.com\/photo\/close-up-view-of-a-golden-coin-315788\/ \ud83d\ude03 As I was glued to the Bitcoin charts on my phone late-night this Sunday, trying to time the dip, I thought to myself &#8212; It would be super nice to have something that could &hellip; <a href=\"https:\/\/blogs.swarthmore.edu\/its\/2021\/11\/15\/charting-bitcoin-prices-with-react\/\" class=\"more-link\">Continue reading <span class=\"screen-reader-text\">Charting Bitcoin prices with React<\/span><\/a><\/p>\n","protected":false},"author":70,"featured_media":7758,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_monsterinsights_skip_tracking":false,"_monsterinsights_sitenote_active":false,"_monsterinsights_sitenote_note":"","_monsterinsights_sitenote_category":0,"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[2,450,303,304],"tags":[90],"class_list":{"0":"post-7722","1":"post","2":"type-post","3":"status-publish","4":"format-standard","5":"has-post-thumbnail","6":"hentry","7":"category-academic-technology","8":"category-data-science","9":"category-development","10":"category-web-development","11":"tag-featured","13":"fallback-thumbnail"},"jetpack_featured_media_url":"https:\/\/blogs.swarthmore.edu\/its\/wp-content\/uploads\/2021\/11\/pexels-photo-315788-scaled.jpeg","jetpack_sharing_enabled":true,"jetpack_shortlink":"https:\/\/wp.me\/ph2nPL-20y","_links":{"self":[{"href":"https:\/\/blogs.swarthmore.edu\/its\/wp-json\/wp\/v2\/posts\/7722","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/blogs.swarthmore.edu\/its\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blogs.swarthmore.edu\/its\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blogs.swarthmore.edu\/its\/wp-json\/wp\/v2\/users\/70"}],"replies":[{"embeddable":true,"href":"https:\/\/blogs.swarthmore.edu\/its\/wp-json\/wp\/v2\/comments?post=7722"}],"version-history":[{"count":34,"href":"https:\/\/blogs.swarthmore.edu\/its\/wp-json\/wp\/v2\/posts\/7722\/revisions"}],"predecessor-version":[{"id":8584,"href":"https:\/\/blogs.swarthmore.edu\/its\/wp-json\/wp\/v2\/posts\/7722\/revisions\/8584"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/blogs.swarthmore.edu\/its\/wp-json\/wp\/v2\/media\/7758"}],"wp:attachment":[{"href":"https:\/\/blogs.swarthmore.edu\/its\/wp-json\/wp\/v2\/media?parent=7722"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blogs.swarthmore.edu\/its\/wp-json\/wp\/v2\/categories?post=7722"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blogs.swarthmore.edu\/its\/wp-json\/wp\/v2\/tags?post=7722"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}