## DEV Community is a community of 605,211 amazing developers

We're a place where coders share, stay up-to-date and grow their careers.

# How to Build a Bitcoin DCA Chart with React and Recharts

Cody Pearce Originally published at codinhood.com Updated on ・14 min read

Recharts is a charting library that provides a set of declarative React components for building charts with D3. Ten highly customizable chart types are available along with helper components. In this tutorial we will build a few AreaCharts to display portfolio value, total coin accumulated, and total invested over a particular historical time period when Dollar Cost Averaging Bitcoin.

Checkout www.cryptodca.org to see an interactive example of the type of chart we will be building. Visit the project's Github to learn more.

Sections

• Graphing Dollar Cost Averaging
• Getting Started
• Historical Prices with CoinGecko’s API
• Getting the data
• Calculating Totals
• Building the Chart Array
• Recharts Area Chart
• Recharts Tooltip
• Recharts Dots
• Recharts YAxis and XAxis
• Recharts With Multiple Areas
• Responsive Recharts
• Conclusion

## Graphing Dollar Cost Averaging

Dollar Cost Averaging (DCA) is an investment strategy where one buys the same dollar amount of an asset over regular intervals in order to reduce short-term volatility. For example, investing 200 dollars into a specific stock or cryptocurrency every month means that you will buy more stock when the stock price is low and less stock when the price is higher. Read the Investopedia Article on DCA to learn more.

Graphing a Bitcoin DCA account's value over time requires that we calculate the total account value at each interval over a time period. For example, if that interval is a month and the time period is two years, then we need to calculate the total account value 24 times. To calculate the total value at a particular interval we need to multiply the total accumulated coin up to that point by the coin price at the time of purchase. The total accumulated coin up to that point can be calculated by dividing the amount to be invested by the price of the coin at that the time purchase for each interval. Let's illustrate this with an example, say we plan to purchase \$200 dollars worth of Bitcoin every month from January 2016 to May 2016.

The Amount of Coin for the first month is easy to calculate, simply take the Amount to Invest (200) divided by the Coin Price (\$434.33) on January 1, 2016. Total value is similarly easy, simply take the Amount of Coin so far times the current Coin Price, which for the first month should equal the amount invested (200).

``````// amountToInvest / coinPrice
200 / 434.33  ~= .46 // Amount of Coin for the first month

// amountOfCoin * coinPrice
.46 * 434.33 ~= 200  // Total Value
``````

Calculating the Amount of Coin for the second month is slightly different. First, similarly to last month, divide the Amount to Invest by the current month's Coin Price (371.04). Then add that value to the previous month's Amount of Coin (.46).

``````// amountToInvest / coinPrice
200 / 371.04  ~= .54 // Amount of Coin bought in the second month

// amountOfCoin for second month + amountOfCoin for first month
.54 + .46 = 1 // Total Accumulated Amount of Coin so far
``````

To calculate the second month's Total value we take the Total Accumulated Amount of Coin times the current Coin Price.

``````// Total Accumulated Amount of Coin * coinPrice
1 * 371.04 = 371.04
``````

Extending this process to the rest of the months produces a table like this:

Month Coin Price Total Invested Amount of Coin Total Value
1 434.33 200 .46 200
2 371.04 400 1 371.04
3 424.49 600 1.47 624.00
4 416.75 800 1.95 811.20
5 452.59 1000 2.39 1081.69

The code to calculate these values might look something like this.

``````for (let i = 0; i < numOfDays; i += freqInDays) {
const coinPrice = priceArr[i].price;
coinAmount += amountToInvest / coinPrice;
totalInvested += amountToInvest;
const total = coinAmount * coinPrice;

dataArr.push({
TotalInvested: totalInvested,
CoinAmount: coinAmount,
CoinPrice: coinPrice,
Total: total,
date: priceArr[i].date,
});
}
``````

`numOfDays` is the total number of days for the time period. In this case there are 121 days between Jan 2016 to May 2016.

`freqInDays` is the time interval of buying, which in this case is 30 days.

`priceArr` is an array of objects with historical Bitcoin prices and date.

`amountToInvest` is the dollar amount that will invested per time period, in this case it is 200.

`coinAmount` is the total amount of coin accumulated up to this point.

`totalInvested` is the total amount invested up to this point.

`total` is the total value in USD of the portfolio.

These four values, `TotalInvested`, `CoinAmount`, `CoinPrice`, and `Total` are what we want to graph over time. `freqInDays`, `amountToInvest`, and `numOfDays` will be provided by the user, while the historical Bitcoin prices, `priceArr`, will be provided from CoinGecko's API.

## Getting started

Initialize a new Create A React App project.

``````npx create-react-app bitcoin-dca
cd bitcoin-dca
npm start
``````

Go to `src/App.js` and remove the starter code.

``````import React from "react";
import "./App.css";

function App() {
return (
<div className="App">
<h1 className="title">Bitcoin</h1>
</div>
);
}

export default App;
``````

Finally, go to `src/App.css` and update the styling as follows.

``````body {
background-color: #232323;
color: white;
}
.title {
color: #f7931a;
font-size: 40px;
}
.App {
text-align: center;
}
``````

## Historical Prices with CoinGecko's API

CoinGecko's API offers free crypto data without an API key. The `/coins/{id}/market_chart/range` endpoint gives historical market data for a specific coin within a specified range and is exactly what we need. The `id` parameter refers to the id of the coin, which in this case is just `bitcoin`. The `vs_currency` param determines what currency the Bitcoin price will be sent as. The `from` and `to` params indicate the time period of prices to fetch and must be provided as a UNIX time stamp.

For example, https://api.coingecko.com/api/v3/coins/bitcoin/market_chart/range?vs_currency=usd&from=1392577232&to=1422577232 fetches the price of Bitcoin in USD for each day between `02/16/2014` and `01/30/2015`.

## Getting the data

First, let's set the static values, `startDate`, `endDate`, `freqInDays`, and `amountToInvest` at the top of `App.js`. Ideally we would build a form to capture these values from a user, but now we'll statically define them here.

Next, build a basic async function that passes in `startDate` and `endDate`, fetches the data from CoinGecko's API, and finally puts that data in state. To hold the data and different states, we'll need to define `coinData`, `isLoading`, and `error` in the component state.

``````import React, { useEffect, useState } from "react";
import "./App.css";

const APIURL = "https://api.coingecko.com/api/v3/";

function App() {
const startDate = "1/1/2016";
const endDate = "1/1/2020";
const freqInDays = 30;
const amountToInvest = 200;

const [coinData, setCoinData] = useState([]);
const [error, setError] = useState(false);

const getCoinData = async (startDate, endDate) => {

const url = ""; // TODO

try {
const coinResponse = await fetch(url);
const data = await coinResponse.json();

setCoinData(data);
setError(false);
} catch (e) {
setError(e);
}
};

return (
<div className="App">
<h1>Bitcoin</h1>
</div>
);
}

export default App;
``````

To pass the `startDate` and `endDate` parameters as human readable dates, we will use the dayjs library to convert human readable dates to UNIX timestamps. Import `dayjs` and apply its `advancedformat` extension.

``````...
import dayjs from "dayjs";
...
``````

Next Use `dayjs`'s `format` method to convert the dates to Unix timestamp from within the `getCoinData` function.

``````...
const getCoinData = async (startDate, endDate) => {
...
const startDateUnix = dayjs(startDate).format("X");
const endDateUnix = dayjs(endDate).format("X");
...
}
...
``````

Next build the URL as described above, fetch the data, and update the component's state with `setCoinData`.

``````...
const getCoinData = async (startDate, endDate) => {
...
const startDateUnix = dayjs(startDate).format("X");
const endDateUnix = dayjs(endDate).format("X");
const range = `range?vs_currency=usd&from=\${startDateUnix}&to=\${endDateUnix}`;

const url = `\${APIURL}/coins/bitcoin/market_chart/\${range}`;
try {
const coinResponse = await fetch(url);
const data = await coinResponse.json();

setCoinData(data);
setError(false);
} catch (e) {
setError(e);
}
}
...
``````

Now we can call this function in the `useEffect` hook with the dates provided at the top of the component.

``````...
useEffect(() => {
getCoinData(startDate, endDate);
}, []);
...
``````

There are four UI states we need to handle: `noData`, `loading`, `error`, and `data`. Add some conditionals below the `useEffect` hook as shown below.

``````...
let content = <div>No Data</div>;
if (coinData && coinData.prices && coinData.prices.length > 0)
content = <div>Data</div>;
if (error) content = <div>{error}</div>;

return (
<div className="App">
<h1 className="title">Bitcoin</h1>
{content}
</div>
);
...
``````

The data returned from `const data = await coinResponse.json()` should be an array of UNIX timestamps and prices between the two dates we provided. This is exactly what we need to both calculate total values and create the graph.

## Calculating Totals

Our goal here is to calculate the following values using the `coinData.prices` array:

• Total Amount of Coin in BTC - `totalCoinAmount`
• Total Value in USD - `endTotal`
• Total Invested in USD - `totalInvested`
• Money Gained in USD - `numberGained`
• Money Gained in Percent - `percentGained`

Much of the logic here should be familiar from the `Graphing Dollar Cost Averaging` section above. `numberGained` is simply the total value in USD minus the `totalInvested`. `percentGained` is the percent that the `totalInvested` grew to reach the `endTotal`. Create a file `src/Totals` as shown below.

``````import React from "react";

export default function Totals({ priceArr, freqInDays, amountToInvest }) {
const numOfDays = priceArr.length;
let coinAmount = 0;
for (let i = 0; i < numOfDays; i += freqInDays) {
const coinValue = priceArr[i][1];
coinAmount += amountToInvest / coinValue;
}

const totalCoinAmount = coinAmount;
const totalInvested = amountToInvest * Math.floor(numOfDays / freqInDays);
const endTotal = totalCoinAmount * priceArr[priceArr.length - 1][1];
const numberGained = endTotal - totalInvested;
const percentGained = ((endTotal - totalInvested) / totalInvested) * 100;

return <div>Totals</div>;
}
``````

To display these values, create another component `src/Totaljs` with some simple styling.

``````import React from "react";

export default function Total({ title, value }) {
return (
<div style={styles.row}>
<h4 style={styles.title}>{title}:</h4>
<h4 style={styles.value}>{value}</h4>
</div>
);
}

const styles = {
row: {
display: "flex",
flexDirection: "row",
justifyContent: "space-between",
alignItems: "center",
maxWidth: 350,
margin: "10px auto",
},
title: {
fontWeight: 600,
margin: 0,
},
value: {
color: "#f7931a",
fontSize: 24,
margin: 0,
},
};
``````

If you run the calculations above you'll find that most of the values contain many decimal places. Create a utility function, `./src/round.js`, to round the numbers off so they look nicer.

``````export default function round(num, digit) {
return +(Math.round(num + "e+" + digit) + "e-" + digit);
}
``````

Import both `round` and the `Total` component into the `Totals` component. Next, create a few `Total` components while passing in a description into the `title` prop, and the actual value into the `value` prop. We can also format these values using the `round` function.

``````// ./src/Totals.js

import Total from "./Total";
import round from "./round";
...
return (
<div>
<Total title={"Ending Value (USD)"} value={`\$\${round(endTotal, 2)}`} />
<Total title={"Amount of Coin (BTC)"} value={round(totalCoinAmount, 5)} />
<Total
title={"Amount Invested (USD)"}
value={`\$\${round(totalInvested, 2)}`}
/>
<Total title={"Gained (USD)"} value={`\$\${round(numberGained, 2)}`} />
<Total title={"Gained (%)"} value={`\${round(percentGained, 2)}%`} />
</div>
);
...
``````

Finally, import `Totals` into `App.js`, and replace the "data" state with the `Totals` component.

``````...
import Totals from "./Totals";
...
let content = <div>No Data</div>;
if (coinData && coinData.prices && coinData.prices.length > 0)
content = (
<Totals
priceArr={coinData.prices}
freqInDays={freqInDays}
amountToInvest={amountToInvest}
/>
);
if (error) content = <div>{error}</div>;
...
``````

## Building the Chart Array

The code below should be very familiar from the Graphing Dollar Cost Averaging section above, please check out that section to learn how this code works. One difference is that we want to store the date in a human readable way using `dayjs` again. Create a new file `./src/Graph.js` as below:

``````import React from "react";
import dayjs from "dayjs";

export default function Graph({ priceArr, freqInDays, amountToInvest }) {
const numOfDays = priceArr.length;
let coinAmount = 0;
let totalInvested = 0;
let dataArr = [];

for (let i = 0; i < numOfDays; i += freqInDays) {
const coinPrice = priceArr[i][1];
coinAmount += amountToInvest / coinPrice;
totalInvested += amountToInvest;
const total = coinAmount * coinPrice;
const date = dayjs(priceArr[i][0]).format("MM/DD/YYYY");

dataArr.push({
TotalInvested: totalInvested,
CoinAmount: coinAmount,
CoinPrice: coinPrice,
Total: total,
date: date,
});
}

return <div style={styles.container}>Chart</div>;
}

const styles = {
container: {
maxWidth: 700,
margin: "0 auto",
},
};
``````

This will create an array of objects, `dataArr`, that will look like this:

``````[
{TotalInvested: 200, CoinAmount: .46, CoinPrice: 460, Total: 200, date: '1/1/2016'},
{TotalInvested: 400, CoinAmount: 1, CoinPrice: 380, Total: 200, date: '1/5/2016'},
...
]
``````

## Rechart Area Chart

We're finally ready to start creating our charts. The Recharts `<AreaChart>` and `<Area>` components can be customized in a myriad of ways, but to start we'll create a very basic chart and build from there.

The `<AreaChart>` component is a wrapping component that accepts the chart's data in the `data` prop and provides that data to its children. In our case, we need to pass in the `dataArr` array we created above into the `data` prop. For the chart to display at all we also need to provide a height and width prop, in this case set height to 250 and width to 700.

The `<Area>` component is what actually displays the data on the graph. The `dataKey` prop will select the key in each object in the `dataArr` object to display as data on the graph. Remember from above each object in the `dataArr` looks something like this:

``````{
TotalInvested: 400,
CoinAmount: 1,
CoinPrice: 380,
Total: 200,
date: '1/5/2016'
},
``````

Let's show the `Total` value, so set the `dataKey` prop to "Total". The `<Area>` component accepts many other props for customizing the graph exactly how we want. For now let's just style the `stroke`, `fillOpacity`, and `fill`.

``````...
import { AreaChart, Area } from "recharts";

...
return (
<div style={styles.container}>
<AreaChart data={dataArr} height={250} width={700}>
<Area
dataKey="Total"
stroke="none"
fillOpacity={1}
fill="#f7931a"
/>
</AreaChart>
</div>
)
...
``````

Add the `Graph` component to `App.js` to see AreaChart we built above.

``````...
import Graph from "./Graph";
...
let content = <div>No Data</div>;
if (coinData && coinData.prices && coinData.prices.length > 0)
content = (
<div>
<Totals
priceArr={coinData.prices}
freqInDays={freqInDays}
amountToInvest={amountToInvest}
/>
<Graph
priceArr={coinData.prices}
freqInDays={freqInDays}
amountToInvest={amountToInvest}
/>
</div>
);
if (error) content = <div>{error}</div>;
...
``````

The shape of the `<Area>` component can also be changed with the `type` prop. For example, pass in `step` to the type prop.

``````<Area
type="step"
dataKey="Total"
stroke="none"
fillOpacity={1}
fill="#f7931a"
/>
``````

Now try passing in `natural`.

## Recharts Tooltip

The above chart is a good start, but there's no way to see the individual values on the chart. We can use Recharts `tooltip` to show the total value at each interval on the chart. We can also modify the styles of the tooltip with the `contentStyle` and `labelStyle` props.

``````...
import { AreaChart, Tooltip, Area } from "recharts";
...

...
<AreaChart data={dataArr} height={250} width={700}>
<Tooltip
contentStyle={styles.tooltipWrapper}
labelStyle={styles.tooltip}
formatter={value => `\${value}`}
/>
<Area
dataKey="Total"
stroke="none"
fillOpacity={1}
fill="#f7931a"
/>
</AreaChart>
...

const styles = {
container: {
maxWidth: 700,
margin: "0 auto"
},
tooltipWrapper: {
background: "#444444",
border: "none"
},
tooltip: {
color: "#ebebeb"
}
};
``````

One problem you'll notice is that the total values on the tooltips have a bunch of digits. We can format this number using the `formatter` prop which takes a callback function that returns the data in a format. Pull in the rounding utility function we built above, `./src/round.js` to round the values to two places. Also add a `\$` character in front of the value to indicate that unit is in USD.

``````<Tooltip
contentStyle={styles.tooltipWrapper}
labelStyle={styles.tooltip}
formatter={value => `\$\${round(value, 2)}`}
/>
``````

## Recharts Dots

The `dot` prop on the `<Area>` component will add dots at each individual point on the chart. We can either pass in `true` to show the dots with default style, pass in an object of styles to display the dots how we want, or pass in a custom dot element. For now, add a simple style object.

``````...
<Area
dataKey="Total"
stroke="none"
fillOpacity={1}
fill="#f7931a"
dot={{ fill: "white", strokeWidth: 2 }}
/>
...
``````

We can also edit the dots on hover using the `activeDot` prop.

``````...
<Area
dataKey="Total"
stroke="none"
fillOpacity={1}
fill="#f7931a"
activeDot={{ strokeWidth: 0 }}
/>
...
``````

## Recharts YAxis and XAxis

Using the `<YAxis>` and `<XAxis>` components, we can display both the YAxis and XAxis to give even more information about the scale of values. The `<XAxis>` component will default to displaying the number of points in ascending order.

But we want to show the dates themselves on the XAxis. To do this, add the `dataKey` prop to the `<XAxis>` prop with the string 'date'.

There are a ton of props and customizations for both the `XAxis` and `YAxis` components, from custom labels, to custom scaling, ticks, and event handlers. We're going to keep it simple for now, however.

``````...
import {
AreaChart,
XAxis,
YAxis,
Tooltip,
Area,
} from "recharts";
...
<AreaChart data={dataArr} height={250} width={700}>
<XAxis dataKey={"date"} />
<YAxis orientation={"left"}  />
...
</AreaChart>
...
``````

## Recharts With Multiple Areas

With Recharts we can add multiple Areas within the same chart to display related data along on the same timeline. In our case we want to show `CoinAmount`, `TotalInvested`, and `CoinPrice` along with `Total` within the same chart to see how all of the data relates. We'll need to give each new `Area` a different color to distinguish them easily, as well as lower the opacity so we can see the charts overlapping. Create the rest of the `Area` components within in the `AreaChart` in the same way we created the one above using the `dataKey` for each set of data.

``````<AreaChart data={dataArr} height={250} width={700}>
<XAxis dataKey={"date"} />
<YAxis orientation={"left"} />
<Tooltip
contentStyle={styles.tooltipWrapper}
labelStyle={styles.tooltip}
formatter={value => `\$\${round(value, 2)}`}
/>
<Area
type="linear"
dataKey="CoinAmount"
stroke="none"
fillOpacity={0.4}
fill="#55efc4"
activeDot={{ strokeWidth: 0 }}
/>
<Area
type="linear"
dataKey="Total"
stroke="none"
fillOpacity={0.6}
fill="#f7931a"
activeDot={{ strokeWidth: 0 }}
/>
<Area
type="linear"
dataKey="TotalInvested"
stroke="none"
fillOpacity={0.6}
fill="#3498db"
activeDot={{ strokeWidth: 0 }}
/>
<Area
type="linear"
dataKey="CoinPrice"
stroke="none"
fillOpacity={0.6}
fill="#e84393"
activeDot={{ strokeWidth: 0 }}
/>
</AreaChart>
``````

One problem with this chart is that `CoinAmount` is not measured in dollars but in Bitcoins, so displaying the `CoinAmount` on the same graph is somewhat misleading. However, we can create two `YAxis` components, one on the right and one on the left, to solve this problem. Currently, we already have the `YAxis` on the left that's mapped to USD, so what we need is a second `YAxis` mapped to BTC on the right side. Add a second `YAxis` component with a `yAxisId` prop set to "right" and a "orientation" prop set to "right". The `yAxisId` prop will allow us to map an `Area` to the correct `YAxis` scale.

``````<YAxis yAxisId="right" orientation="right" />
``````

Update each`<Area>` to map to the correct `yAxisId` value by providing the `yAxisId` prop to the `<Area>` component.

``````...
<Area
type="linear"
dataKey="CoinAmount"
stroke="none"
fillOpacity={0.4}
fill="#f7931a"
yAxisId="right"
activeDot={{ strokeWidth: 0 }}
/>
<Area
type="linear"
dataKey="Total"
stroke="none"
fillOpacity={0.6}
fill="#f7931a"
yAxisId="left"
activeDot={{ strokeWidth: 0 }}
/>
<Area
type="linear"
dataKey="TotalInvested"
stroke="none"
fillOpacity={0.6}
fill="#3498db"
yAxisId="left"
activeDot={{ strokeWidth: 0 }}
/>
<Area
type="linear"
dataKey="CoinValue"
stroke="none"
fillOpacity={0.6}
fill="#e84393"
yAxisId="left"
activeDot={{ strokeWidth: 0 }}
/>
...
``````

There are plenty more customizations you can do with Recharts, checkout the Recharts docs to learn more.

## Responsive Recharts

The chart will not automatically resize for smaller screens because the chart's height and width are statically defined. Making the chart responsive is surprisingly easy with Recharts, however. Wrap the `<AreaChart>` component in a `<ResponsiveContainer>`, remove the height and width from the `<AreaChart>`, and provide a new height to the `<ResponsiveContainer>` component.

``````...
import {
AreaChart,
XAxis,
YAxis,
Tooltip,
Area,
ResponsiveContainer
} from "recharts";
...
<ResponsiveContainer height={250}>
<AreaChart data={dataArr}>
...
</AreaChart>
</ResponsiveContainer>
...
``````

## Conclusion

There are plenty of other things we can do to make this project better. For example adding user input, better loading and error messaging, easy to share buttons, and URLs that are easy to link to a specific graph. If you're interested in how to add any of these extra features, check out the Github repo for crypto-dca.

Recharts makes creating charts extremely easy with React and D3 while at the same time providing a great amount of customization. Although there are more features to Recharts than can be covered in one project, I hope these examples helps you get started.

## Discussion (2)

Chuongtran

Great work :)

I'm building a simple tool to calculate DCA crypto, support more than 30 coin/token on the top market with combo Nuxtjs & Golang
cryptosaving.app

fabiankcarlin

Surely you were affected by this crazy hype that has recently unfolded around the cryptocurrency world on 2020. At robopay.com/currencies/qtum, you can evaluate the extremely high mountains of highs and Mariana troughs of lows on cryptocurrency price charts that so excite the imagination and attract to trading, forcing everyone to literally get hung up on this topic.