# Building a simple candlestick chart using Docker, FastAPI, and Vue 3 - Part 10

In the [last article](https://jeffpohlmeyer.com/candlestick-docker-fastapi-vue-part-9) we set up the event emitter so
that when a symbol's data was fetched we could eventually display it in our chart components. The next step will be to
create said chart components. We'll want to create two types of charts:

- A [candlestick chart](https://en.wikipedia.org/wiki/Candlestick_chart)
- A [volume chart](https://www.incrediblecharts.com/indicators/volume.php)

These can be split out into two separate components, but we first need to install the charting library we'll use for
this. There are many options one can choose from for charting but the one we're going to use here is a Vue version
of [ApexCharts](https://apexcharts.com/), called (creatively)
, [vue-apexcharts](https://www.npmjs.com/package/vue-apexcharts). To install this we simply run

```bash
npm i apexcharts vue3-apexcharts
```

and then we create two components to split out functionality.

## JVPCandlestick.vue

The first chart will contain the candlestick functionality, which will be called `JVPCandlestick.vue`. ApexCharts has
a [built-in candlestick chart](https://apexcharts.com/vue-chart-demos/candlestick-charts/basic/) which we'll use so the
component itself will be fairly simple. In the link you'll see the format the data needs to be, which we've already
addressed in a previous post.

### Component setup

The `template` section of the component will be fairly straight-forward:

```html

<template>
  <apexchart
      type="candlestick"
      width="100%"
      height="80%"
      :series="series"
      :options="chartOptions"
  ></apexchart>
</template>
```

where the `series` and `chartOptions` values will be set in a little bit. Before we do anything else, though, we need to
update our `main.js` to globally declare the `apexchart` component.

```javascript
// src/main.js
import {createApp} from 'vue';
import App from './App.vue';
import './index.css';

import VueApexCharts from 'vue3-apexcharts';

createApp(App).use(VueApexCharts).mount('#app');
```

You can see above we've imported `VueApexCharts` and are using it in the app itself. Now we need to handle the `script`
section of the `JVPCandlestick` component.
We will need to include the `series` to display the data, as well as any other information we want including the symbol and interval, all of which need to be declared as props.
We then will set the chart title to be a computed property that displays nothing if we don't have any series data but will display the ticker and interval whenever we _do_ have data.
Finally, we will set some chart options for displaying the title, the format of the x and y axes, as well as the tooltip.
```javascript
import {computed, defineComponent} from 'vue';

export default defineComponent({
  props: {
    symbol: {
      type: String,
      required: true,
    },
    interval: {
      type: String,
      required: true,
    },
    series: {
      type: Array,
      required: true,
    },
  },
  setup(props) {
    const interval = computed(() => props.interval);
    const symbol = computed(() => props.symbol);
    const series = computed(() => props.series);

    const title = computed(() =>
      !series.value.length ? '' : `${interval.value} Chart of $${symbol.value}`
    );

    const chartOptions = computed(() => ({
      chart: {
        type: 'candlestick',
        id: 'candles',
        zoom: {
          enabled: false,
        },
      },
      title: {
        text: title.value,
        align: 'left',
        floating: true,
        offsetY: 25,
        offsetX: 10,
        style: {
          fontSize: '2rem',
        },
      },
      xaxis: {
        type: 'datetime',
      },
      yaxis: {
        tooltip: {
          enabled: true,
        },
        labels: {
          formatter: (val) => val.toFixed(0),
        },
      },
    }));

    return { series, chartOptions };
  },
});
```
With this component now set up, we can include it in the `Chart.vue` component.

```html
<template>
  <div>
    <JVPCandlestick :series="series" :interval="interval" :symbol="symbol" />
  </div>
</template>
```
Now a simple search of any ticker should yield the following:
![localhost_3000_.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1644953642346/8wdhpJRIW.png)

#### _Note_
There is a line in the `Selections.vue` component that provided some broken behavior.
Within the `postProcessData` method, the `state.endDate` value, if always fetching a daily chart, would constantly re-set the `endDate` value to be one day earlier.
A quick fix around this is to simply replace `state.endDate = formatDate(dates[dates.length - 1]);` with 
```javascript
if (!state.endDate) {
  state.endDate = formatDate(dates[dates.length - 1]);
}
```

## JVPVolume.vue
Next we need to set up the volume chart, which will go below the candlestick chart, and will be called `JVPVolume.vue`.
This is similarly simple, as we will just use a bar chart from `vue3-apexcharts`.
```html
<template>
  <apexchart 
    type="bar"
    width="100%"
    height="20%"
    :series="series"
    :options="chartOptions"
  ></apexchart>
</template>
```
We then simply pull in the `volume` prop and set `series` as a computed property, and similarly set some chart options.
```javascript
import { computed, defineComponent } from 'vue';
export default defineComponent({
  props: {
    volume: {
      type: Array,
      required: true,
    },
  },
  setup(props) {
    const series = computed(() => props.volume);

    const chartOptions = computed(() => ({
      type: 'bar',
      brush: {
        enabled: true,
        target: 'candles',
      },
      dataLabels: {
        enabled: false,
      },
      plotOptions: {
        bar: {
          columnWidth: '80%',
        },
      },
      stroke: {
        width: 0,
      },
      xaxis: {
        type: 'datetime',
        axisBorder: {
          offsetX: 13,
        },
        categories: props.volume[0]?.data.map((e) => e.x.getTime()) ?? [],
      },
    }));

    return { series, chartOptions };
  },
});
```
We can then include the `JVPVolume.vue` component in `Chart.vue` as
```html
<template>
  <div>
    <JVPCandlestick :series="series" :interval="interval" :symbol="symbol" />
    <JVPVolume :volume="volume" />
  </div>
</template>
```
and fetching the data for a ticker gives the following result
![localhost_3000_ (1).png](https://cdn.hashnode.com/res/hashnode/image/upload/v1644953661007/Bbksi6mGB.png)
which, admittedly, looks fairly ugly.
To combat this, we'll add some styling to the `Chart.vue` component to make things look a bit nicer.

## `Chart.vue` styling
First, we will set the height on the wrapping div, as well as set some flex justification and alignment.

```html

<template>
  <div class="mx-auto h-1/2 sm:h-4/6">
    <JVPCandlestick :series="series" :interval="interval" :symbol="symbol"/>
    <JVPVolume :volume="volume" />
  </div>
</template>
```
Setting this makes the resulting chart look much better
![localhost_3000_ (2).png](https://cdn.hashnode.com/res/hashnode/image/upload/v1644953673621/6q-bx2f28.png)
The problem, though, is that when the page first loads and there is no data the page looks somewhat ugly.
![localhost_3000_ (3).png](https://cdn.hashnode.com/res/hashnode/image/upload/v1644953684621/ZEbP-TTRI8.png)

Thus, let's add some dynamic content that will render one thing if we have data, and will render something else if we do not.
First, we wrap the charts in a `div` with an `if` statement, and otherwise display a message.
We will then set some basic styling on these two elements.
```html
<template>
  <div
    class="mx-auto h-1/2 sm:h-4/6 lg:h-5/6"
    :class="series.length ? '' : 'flex items-center justify-center'"
  >
    <div v-if="series.length" class="h-full">
      <JVPCandlestick :series="series" :interval="interval" :symbol="symbol" />
      <JVPVolume :volume="volume" />
    </div>
    <div v-else id="chart" class="text-4xl flex items-center justify-center">
      Choose a company, interval, and dates below
    </div>
  </div>
</template>
```
The `:class` attribute in the wrapping div allows us to set styles dynamically.
It has the same functionality as the general `class` attribute above it, but this allows us to bind the class to something programmatically.
With this set-up, the initial page, prior to loading data, looks like this
![localhost_3000_ (4).png](https://cdn.hashnode.com/res/hashnode/image/upload/v1644953775144/95StetmGS.png)

## Summary
With that, we have a simple app that uses FastAPI on the server to fetch data and Vue with Vuelidate and ApexCharts to display data on the frontend.
The entire repo can be found at https://github.com/jvp-design/stock-chart-fastapi-vue/
