top of page
  • Nikhil Adithyan

Replicating TradingView Chart in Python

Updated: Nov 28, 2023

With less than 3 lines of code



I’ve always been a huge fan of TradingView’s charting tool, especially for its beautifully crafted user interface and design. And there’s never been a day I haven’t thought about recreating the graph design in Python. But, it’s always tough for Python developers to create stunning and professional-looking visualizations (like TradingView) using libraries like Matplotlib, Seaborn, Altair, etc. Their style themes are so outdated and crappy. Though all these modules provide features for customizing the theme of the charts, it takes a toll on the developer as there is a lot of work involved.

Fortunately, I recently came across an awesome library called lightweight-charts-python providing features to easily re-create the TradingView style with minimal code. In this article, we will dive deep into this library, explore its features, and code some cool TradingView charts in Python.

Importing Packages

The first and foremost step of setting up the coding environment is to import the required packages. In this article, we are going to use five different packages which are pandas for data manipulation, and requests for making API calls, numpy for numerical calculations, lightweight_chart for replicating the TradingView look, time for time-related functions, and finally asyncio and nest_asyncio for asynchronous programming. The following code will import all the mentioned packages into our Python environment:


import pandas as pd
import requests
import numpy as np
from lightweight_charts import Chart
import time
import asyncio
import nest_asyncio
nest_asyncio.apply()

If you haven’t installed any of the imported packages, make sure to do so using the pip command in your terminal. Before moving further, for extracting the data, we’ll be using the APIs of FinancialModelingPrep. So for the smooth execution of the upcoming code block, you need to have an FMP developer account which you can easily create using the link here.

Obtaining Data using FMP

In this section, we will use the stock historical data endpoint provided by FMP to attain Apple’s price history. This can be easily done by using the following code:


api_key = 'YOUR API KEY'
hist_json = requests.get(f'https://financialmodelingprep.com/api/v3/historical-price-full/AAPL?apikey={api_key}').json()

hist_df = pd.DataFrame(hist_json['historical']).drop('label', axis = 1)
hist_df = hist_df.iloc[::-1].reset_index().drop(['index','adjClose'], axis = 1)
hist_df.date = pd.to_datetime(hist_df.date)
hist_df = hist_df.iloc[:,:6].iloc[-365:]
hist_df.columns = ['time', 'open', 'high', 'low', 'close', 'volume']

hist_df.tail()

In the above code, we are first storing the secret API key in a variable (remember to replace YOUR API KEY with your actual secret key), and then, using the get provided by the Requests package, we are extracting the historical data of Apple. We are then converting the JSON response into a Pandas dataframe, and after performing some data manipulation, this is the final output:


In the code, the reason for changing the column names is that lightweight_charts demands a specific naming structure to plot the data. Now that we have adequate data to work with, let’s make some cool visualizations.

TradingView Charts

In this section, we are going to dive deep into the lightweight_charts module and explore the exciting possibilities. Let’s start off with a basic graph in the style of TradingView. Here’s the code to generate a TradingView graph using the library:


chart = Chart()
chart.set(hist_df)
chart.show(block = False)

It just takes as little as three lines of code to create a graph in the look of TradingView’s charting platform. And the code is very straightforward in nature. We are first creating an instance of the class Chart and assigned it to the chart variable. Then using the set function, we are setting the data of the chart. Finally, we are displaying the created chart with the help of the show function. This is the resulting graph of the code:


This output is absolutely stunning for a program of three lines of code. But people who are disappointed at the output after viewing the thumbnail of this article, don't worry! Because now, we are going to up the game by customizing the whole theme of the plot, adding more details, and simply, making it even more beautiful. Here’s the code to do that:


chart = Chart()

chart.grid(vert_enabled = True, horz_enabled = True)

chart.layout(background_color='#131722', font_family='Trebuchet MS', font_size = 16)

chart.candle_style(up_color='#2962ff', down_color='#e91e63',
                   border_up_color='#2962ffcb', border_down_color='#e91e63cb',
                   wick_up_color='#2962ffcb', wick_down_color='#e91e63cb')

chart.volume_config(up_color='#2962ffcb', down_color='#e91e63cb')

chart.title('AAPL')
chart.legend(visible = True, font_family = 'Trebuchet MS', ohlc = True, percent = True)

#####################################################################################

chart.set(hist_df)

sma20_line = chart.create_line(color = '#ffeb3b', width = 1, price_label = True)
sma20 = pd.DataFrame(columns = ['time', 'value'])
sma20.time = hist_df.time
sma20.value = hist_df.close.rolling(window = 20).mean()
sma20_line.title('SMA 20')
sma20_line.set(sma20.dropna(), name = 'value')

sma50_line = chart.create_line(color = '#26c6da', width = 1, price_label = True)
sma50 = pd.DataFrame(columns = ['time', 'value'])
sma50.time = hist_df.time
sma50.value = hist_df.close.rolling(window = 50).mean()
sma50_line.title('SMA 50')
sma50_line.set(sma50.dropna(), name = 'value')

chart.show(block = False)

The code might not be as short as the previous one for the basic plot, but it’s actually very simple. And for easy explanation, I’ve divided the code into two separate parts. The first part is about theme customization. It includes changing the background color, the colors of the candles and volume bars, and so on. Basically, the things related to the style of the plot are dealt with in the first part. The second part is about adding details to the plot. The details we are adding here are the SMA 20 and SMA 50 lines. Here’s the final output:


This is absolutely fantastic! We fully customized the whole look and feel of the plot and added more details like SMA lines and legends for a more insightful graph.

Now let’s move our focus from historical graphs to another cool feature provided by the lightweight_charts library which is the real-time charting feature. Real-time charts are extremely useful for day traders to keep track of the latest price movements and TradingView is most preferred for such charts. Just like how we replicated the historical charts of TradingView, let’s do the same thing for real-time charts too. This is the code to create a real-time TradingView chart:


rt_df1 = hist_df[:150]
rt_df2 = hist_df[150:]

rt_chart = Chart()
rt_chart.set(rt_df1)

rt_chart.show(block = False)

for i, series in rt_df2.iterrows():
    rt_chart.update(series)
    time.sleep(0.1)
    

In this code, we are not actually using the real-time data of stock prices but rather simulating it using the previously extracted historical data. But you can use actual real-time data with the help of FMP’s real-time stock price data endpoint. We are first splitting the historical data into two separate dataframes. The first one is used as the initial data for the plot and the second one is used as the real-time data which is done by updating the data points of the plot with the help of a for-loop. This is the final output:

Pretty cool, right?! But like how there was a lot of scope for improvements in the basic historical graph, this real-time chart can also be improved and modified in a lot of places. We can first change the theme of the plot and similar to how we added SMA lines to the historical chart for better insights, we can add more details for an informative visualization. Here’s the code for the modified or advanced version of the initial real-time chart:


hist_df['SMA9'] = hist_df.close.rolling(window = 9).mean()
hist_df['SMA12'] = hist_df.close.rolling(window = 12).mean()
hist_df = hist_df.dropna()

rt_df1 = hist_df[:25]
rt_df2 = hist_df[25:]

rt_chart = Chart()

rt_chart.grid(vert_enabled = True, horz_enabled = True)

rt_chart.layout(background_color='#131722', font_family='Trebuchet MS', font_size = 16)

rt_chart.candle_style(up_color='#2962ff', down_color='#e91e63',
                   border_up_color='#2962ffcb', border_down_color='#e91e63cb',
                   wick_up_color='#2962ffcb', wick_down_color='#e91e63cb')

rt_chart.volume_config(up_color='#2962ffcb', down_color='#e91e63cb')

rt_chart.legend(visible = True, font_family = 'Trebuchet MS', ohlc = True, percent = True)
rt_chart.fit()

#####################################################################################

rt_chart.set(rt_df1)

sma9_line = rt_chart.create_line(color = '#ffeb3b')
sma9_line.set(rt_df1, name = 'SMA9')

sma12_line = rt_chart.create_line(color = '#26c6da')
sma12_line.set(rt_df1, name = 'SMA12')

rt_chart.show(block = False)

p_sma9, p_sma12 = rt_df1.iloc[-1]['SMA9'], rt_df1.iloc[-1]['SMA12']

for i, series in rt_df2.iterrows():
    rt_chart.update(series)
    sma9_series, sma12_series = series[['time','SMA9']], series[['time','SMA12']]
    sma9_series.index, sma12_series.index = ['time','value'], ['time','value']
    sma9_line.update(sma9_series)
    sma12_line.update(sma12_series)
    
    c_sma9, c_sma12 = sma9_series['value'], sma12_series['value']
    
    if p_sma9 < p_sma12 and c_sma9 > c_sma12:
        rt_chart.marker(text = 'Buy Signal', position = 'below', shape = 'arrow_up', color = '#33de3d')
    elif p_sma9 > p_sma12 and c_sma9 < c_sma12:
        rt_chart.marker(text = 'Sell Signal', position = 'above', shape = 'arrow_down', color = '#f485fb')
    
    p_sma9, p_sma12 = c_sma9, c_sma12
    
    time.sleep(0.1)
    

The code can be a little overwhelming but I’ll try to break down and simplify it. Like how we did with the advanced historical graph, we are splitting the code into two sections. The first section is about calculating the SMA values and customizing the theme of the chart. The second section is all about simulating real-time data and plotting the data points using a for-loop. To up the game, we are also plotting trading signals generated using a technical strategy alongside real-time data.

In a nutshell, the style adopted in this code is the same as we used in the historical graph and so are the details like the SMA lines. But what we are additionally doing here is that we are plotting the buy and sell signals generated by a simple SMA crossover strategy in real-time. And here’s the final result:

This is freaking awesome! It’s a cool application in and of itself. Speaking of applications, our charts are great but they are not fully interactive and do not take any input from the user. Now, let’s solve this problem with the help of some amazing components provided by lightweight_charts for interactivity. Here’s the code to create an interactive graph:


def fetch_data(symbol):
    api_key = 'YOUR API KEY'
    hist_json = requests.get(f'https://financialmodelingprep.com/api/v3/historical-price-full/AAPL?apikey={api_key}').json()
    hist_df = pd.DataFrame(hist_json['historical']).drop('label', axis = 1)
    hist_df = hist_df.iloc[::-1].reset_index().drop(['index','adjClose'], axis = 1)
    hist_df.date = pd.to_datetime(hist_df.date)
    hist_df = hist_df.iloc[:,:6].iloc[-365:]
    hist_df.columns = ['time', 'open', 'high', 'low', 'close', 'volume']
    return hist_df

class API:
    def __init__(self):
        self.chart = None
        
    async def on_symbol_selection(self):
        new_data = fetch_data(self.chart.topbar['symbol'].value)
        if new_data.empty:
            return
        self.chart.set(new_data)


async def main():
    api = API()

    chart = Chart(api = api, topbar = True)
    chart.legend(visible = True, font_family = 'Trebuchet MS')
    chart.fit()

    chart.topbar.switcher('symbol', api.on_symbol_selection, 'META', 'AAPL', 'AMZN', 'MSFT', 'GOOGL', default='AAPL')

    df = fetch_data('AAPL')
    chart.set(df)

    await chart.show_async(block = True)

if __name__ == '__main__':
    asyncio.run(main())

To be honest, there are quite some complex things happening in this code like using asyncio for asynchronous programming but the goal here is not to understand each and every single line of code but to get a glimpse of the interactive components provided by the module and how it can be used to elevate our visualizations. This is the result generated by the above code:


This is just a very basic example of integrating interactive components into our charts. There are still plenty of options and components to significantly improve the whole look and feel of the chart and it is highly recommended to check them out.

Closing Notes

In this article, we explored an interesting library called lightweight_charts which helps in creating financial visualizations in the look of TradingView with as little code as possible. We went from generating a basic historical graph to real-time and interactive charts and during the process, we experimented with different components provided by the library.

It’s really great to see the scope of Python expanding day by day. I was aware of the Lightweight chart API provided by TradingView a while ago but I couldn’t actually use it since it demanded coding knowledge in other languages. But now, I was able to achieve a great-looking TradingView chart in Python using just three lines of code.

Finally, a huge shoutout to FinancialModelingPrep for creating such a great and reliable platform for accessing various types of financial data ranging from fundamental data to ESG and sentiment scores of securities. With that being said, you’ve reached the end of the article. Hope you learned something new.

2 comments
bottom of page