Since this forum has helped me before, I wanted to give back, in the form of a simple example of working with financial data. This will produce a scrollable/zoomable chart with pan/zoom enabled by default. Its the result of research I did on my own and pieced together.
The code is fully commented and should be easy to understand, comes with a sample .csv file - just edit the path in the code to match where you put it, and execute it in your python environment.
Enjoy!
(Iβll blockqoute it since I donβt see how to do attachments hereβ¦)
# Plotly Chart Demo - By TallTim
#
# What this does: Takes an input .csv file with a single column label row before the data and renders a
# scrollable/zoomable chart
#
# This is NOT set up to do realtime data, though it could be adapted if you are clever. (Basically you'd
# reload the .csv as it updates from another source, and redraw the chart, but its beyond scope of this example.)
#
# This IS a way to tinker around with making indicators or other things with chart data.
#
# This assumes you have a python environment where pandas and plotly are installed. To install them, please refer
# to these websites:
# Pandas -- https://pandas.pydata.org/docs/getting_started/install.html - 'pip install pandas' should work, but details are on
# the site for specific environments. Required dependencies are NumPy, python-dateutil and pytz.
# Plotly -- https://plotly.com/python/getting-started/ - 'pip install plotly==5.15.0' should work, but details are on the site.
#
# Feel free to modify/share, just give me some credit for the template, thanks!
# Importing modules/packages needed for our chart
import os
import pandas as pd
import math # This is useful for 'math.isclose' when comparing floating point numbers - you comment this out if you don't need it.
import plotly.graph_objects as go
from plotly.subplots import make_subplots
# I did my dev on a python virtual environment on a windows box - substitute with the path to your .csv file
myDataPath = r'C:\Users\Tim\Documents\NinjaTrader 8\datadump' # The r is needed to make python read this properly
# There may be a more elegant way to handle the file read, but this works well enough...
# Get directory contents
files = os.listdir(myDataPath)
# Define Input File to search for -- just give it the beginning of the filename or whatever your .csv starts with.
# You could also read more than a single file in if you assign it to another variable changing the startswith filename.
Input_File = [file_ for file_ in files if file_.startswith('Input_BTCUSD_5_Minute_Demo')] # Returns file with Input prefix
## Debug - prints to the console as a sanity check you got the proper file
print("\nFile parsing - Input File is: " + str(Input_File[0]))
# Read into pandas dataframe
df_Input = pd.read_csv(myDataPath + '/' + Input_File[0])
# For reference, my .csv looked something like this:
#
# BarTimestamp,FancyIndicator,Open,High,Low,Close
# 6/11/2023 7:30:00 AM,0.0208,25803.72,25804.04,25788.47,25797.74
#
# Because of how pandas dataframes work, it doesn't matter the order of the columns since you can select/assign them to
# variables by their column names
# Debug - This is a useful thing that tells you what your dataframe consists of, and other stats
#print(df_Input.info())
# Debug - This prints the dataframe contents - useful if you need to confirm things are being loaded correctly
#print("Dataframe Input File Contents:" + df_Input.to_string())
# Get number of dataframe rows and columns
dataDimensions = df_Input.shape
dataRows = dataDimensions[0]
dataCols = dataDimensions[1]
## Debug - Gives the dimensions of your dataframe
print("\nInput dataframe - Rows: " + str(dataRows) + " Columns: " + str(dataCols))
# Here is where we define our plots - the top section takes up 70% of the display, the indicator uses the remainder
fig = make_subplots(rows=2, row_heights=[0.70,0.30], cols=1, shared_xaxes=True, vertical_spacing=0.02) # Heights must add up to 1
# Plotly Candlestick Chart - you could substitute here for other types...
# Line Charts -- https://plotly.com/python/line-charts/ and there are more...
fig.add_trace(
go.Candlestick(
x=df_Input['BarTimestamp'], # X-axis has our date/times
open=df_Input['Open'],
high=df_Input['High'],
low=df_Input['Low'],
close=df_Input['Close'],
name="Price"), # Name on legend
row=1, col=1 # Top section, so first row/col
)
# Add Fancy Indicator subplot with go.Scatter
fig.add_trace(go.Scatter(x=df_Input['BarTimestamp'], y=df_Input['FancyIndicator'], name="Fancy Indicator", line=dict(color='rgb(235,140,52)')), row=2, col=1) # Dark orange
# Example of vertical line on a chart
fig.add_vline(x=54, line=dict(color='rgb(52,183,235)', width=2, dash='dot')) # Light blue
# Note: The fig.update_layout statements could be consolidated, but its just easier to read what each one does this way...
# Remove rangeslider
fig.update_layout(xaxis_rangeslider_visible=False)
# Background color - Note, this is the plot background, not the 'Paper' or chart background
fig.update_layout(plot_bgcolor='rgb(0,0,0)')
# Axes line color -- Named color reference: (CSS colors) https://community.plotly.com/t/plotly-colours-list/11730/3
fig.update_xaxes(showline=True, linewidth=1, linecolor='dimgray') # You can use named colors or rgb like below
fig.update_yaxes(showline=True, linewidth=1, linecolor='dimgray')
# Tick value display format - yaxis<num> is for subplots
fig.update_layout(yaxis={"tickformat" : ','}, yaxis2={"tickformat" : '.2f'}) # Displays thousands,hundreds without scientific 'K' notation
# Grid line color
fig.update_xaxes(showgrid=True, gridwidth=1, gridcolor='dimgray')
fig.update_yaxes(showgrid=True, gridwidth=1, gridcolor='dimgray')
# Font color
fig.update_layout(font = dict(color='rgb(160,166,184)')) # Light Gray
# Chart title
fig.update_layout(title="BTCUSD 5min", title_x=0.5, yaxis_title="Price", yaxis2_title="Indicator")
# Legend Background Transparent Color, 'Paper' background color - plot canvas, sets pan enabled on load
# If you don't like pan enabled - just remove it
fig.update_layout(legend=dict(bgcolor='rgba(0,0,0,0)'), paper_bgcolor='rgb(12,12,12)', dragmode='pan')
# Mouse wheel zoom -- if you prefer not to have zoom enabled, then remove this line
config = dict({'scrollZoom' : True})
# Render Chart -- remove config = config if you removed the above line
fig.show(config = config)
Demo CSV File
BarTimestamp,FancyIndicator,Open,High,Low,Close
6/10/2023 8:25:00 PM,-0.50,25789.24,25789.24,25765.28,25779.76
6/10/2023 8:30:00 PM,-0.50,25777.89,25807.54,25761.67,25791.5
6/10/2023 8:35:00 PM,-0.50,25791.15,25827.72,25791.15,25821.8
6/10/2023 8:40:00 PM,-0.50,25822.35,25885.23,25821.76,25847.1
6/10/2023 8:45:00 PM,-0.50,25847.1,25850.69,25833.68,25840.26
6/10/2023 8:50:00 PM,-0.50,25839.71,25851.15,25813.32,25836.01
6/10/2023 8:55:00 PM,-0.50,25834.62,25849.61,25830.74,25841.79
6/10/2023 9:00:00 PM,-0.50,25842.06,25868.25,25838.53,25840.62
6/10/2023 9:05:00 PM,-0.50,25840.6,25846.16,25813.21,25813.22
6/10/2023 9:10:00 PM,-0.50,25813.22,25820.39,25793.97,25806.1
6/10/2023 9:15:00 PM,0.50,25804.85,25809.92,25789.86,25789.9
6/10/2023 9:20:00 PM,0.50,25789.89,25791.37,25768.77,25782.29
6/10/2023 9:25:00 PM,0.50,25782.3,25793.47,25760.65,25778.92
6/10/2023 9:30:00 PM,0.50,25778.92,25786.31,25770.14,25778.27
6/10/2023 9:35:00 PM,0.50,25778.27,25797.74,25777.73,25793.92
6/10/2023 9:40:00 PM,0.50,25794.24,25798.36,25790.62,25795.51
6/10/2023 9:45:00 PM,0.50,25795.5,25805.59,25785.02,25805.59
6/10/2023 9:50:00 PM,0.50,25806.24,25816.44,25784.28,25795.19
6/10/2023 9:55:00 PM,0.50,25795.85,25797.03,25763.07,25771.5
6/10/2023 10:00:00 PM,0.50,25772.25,25787.56,25769.57,25779.99
6/10/2023 10:05:00 PM,-0.50,25779.2,25784.52,25763.24,25769.47
6/10/2023 10:10:00 PM,-0.50,25769.47,25777.18,25751.48,25751.62
6/10/2023 10:15:00 PM,-0.50,25751.62,25764.62,25739.95,25747.35
6/10/2023 10:20:00 PM,-0.50,25747.35,25771.8,25745.35,25754.5
6/10/2023 10:25:00 PM,-0.50,25754.49,25767.23,25738.44,25765.04
6/10/2023 10:30:00 PM,-0.50,25765.04,25768.32,25744.31,25765.99
6/10/2023 10:35:00 PM,-0.50,25765.99,25774.09,25731.69,25746.77
6/10/2023 10:40:00 PM,-0.50,25744.96,25788.51,25744.96,25768.76
6/10/2023 10:45:00 PM,-0.50,25767.99,25777.83,25757.55,25763.91
6/10/2023 10:50:00 PM,-0.50,25763.91,25773.19,25735.07,25742.31
6/10/2023 10:55:00 PM,0.50,25742.3,25758.61,25710.31,25733.57
6/10/2023 11:00:00 PM,0.50,25734.08,25746.73,25716.49,25742.79
6/10/2023 11:05:00 PM,0.50,25742.79,25744.94,25716.4,25729.24
6/10/2023 11:10:00 PM,0.50,25727.33,25733.66,25701.69,25710.49
6/10/2023 11:15:00 PM,0.50,25710.49,25729.47,25708.76,25720.25
6/10/2023 11:20:00 PM,0.50,25718.26,25725.87,25695.58,25700.8
6/10/2023 11:25:00 PM,0.50,25701.26,25710.92,25648,25690.19
6/10/2023 11:30:00 PM,0.50,25690.44,25737.51,25683.44,25702.05
6/10/2023 11:35:00 PM,0.50,25705.86,25717,25680.05,25687.07
6/10/2023 11:40:00 PM,0.50,25687.52,25720.4,25687.52,25712.34
6/10/2023 11:45:00 PM,-0.50,25712.11,25719.96,25698.46,25710.57
6/10/2023 11:50:00 PM,-0.50,25709.28,25767.28,25709.28,25755.48
6/10/2023 11:55:00 PM,-0.50,25754.38,25763.71,25739.31,25745.81
6/11/2023 12:00:00 AM,-0.50,25745.51,25776.13,25739.33,25742.6
6/11/2023 12:05:00 AM,-0.50,25743.63,25761.23,25741.76,25758.57
6/11/2023 12:10:00 AM,-0.50,25758.58,25765.65,25751.61,25757.03
6/11/2023 12:15:00 AM,-0.50,25756.66,25766.91,25746.39,25766.34
6/11/2023 12:20:00 AM,-0.50,25766.34,25767.36,25753.24,25758.45
6/11/2023 12:25:00 AM,-0.50,25758.44,25777.7,25756.04,25777.53
6/11/2023 12:30:00 AM,-0.50,25775.97,25775.97,25757.03,25762.68
6/11/2023 12:35:00 AM,0.50,25761.77,25769.87,25752.15,25768.02
6/11/2023 12:40:00 AM,0.50,25768.02,25819.04,25768.02,25817.54
6/11/2023 12:45:00 AM,0.50,25815.62,25839.14,25775.59,25797.03
6/11/2023 12:50:00 AM,0.50,25797.81,25807.2,25789.87,25801.2
6/11/2023 12:55:00 AM,0.50,25801.07,25827.55,25797.65,25818.71
6/11/2023 1:00:00 AM,0.50,25818.51,25850,25816.73,25834.16
6/11/2023 1:05:00 AM,0.50,25834.17,25841.18,25813.17,25818.91
6/11/2023 1:10:00 AM,0.50,25818.58,25833.9,25805.88,25827.49
6/11/2023 1:15:00 AM,0.50,25826.88,25831.35,25813.78,25816.29
6/11/2023 1:20:00 AM,0.50,25816.99,25830.07,25815.85,25825.28
6/11/2023 1:25:00 AM,-0.50,25826.61,25827.08,25789.1,25800.97
6/11/2023 1:30:00 AM,-0.50,25800.58,25808.22,25781.57,25804.51
6/11/2023 1:35:00 AM,-0.50,25804.5,25807.04,25785.95,25791.55
6/11/2023 1:40:00 AM,-0.50,25791.52,25799.6,25780.78,25785.36
6/11/2023 1:45:00 AM,-0.50,25785.53,25808.02,25784.13,25793.09
6/11/2023 1:50:00 AM,-0.50,25793.4,25802.08,25790.35,25797.41
6/11/2023 1:55:00 AM,-0.50,25797.41,25806.69,25794.3,25802.78
6/11/2023 2:00:00 AM,-0.50,25802.78,25808.44,25797.86,25799.56
6/11/2023 2:05:00 AM,-0.50,25797.25,25816.37,25793.68,25811.55
6/11/2023 2:10:00 AM,-0.50,25811.54,25813.34,25796.12,25799.9
6/11/2023 2:15:00 AM,0.50,25798.85,25803.39,25796.11,25802.4
6/11/2023 2:20:00 AM,0.50,25800.77,25806.74,25786.51,25794.24
6/11/2023 2:25:00 AM,0.50,25793.95,25796.47,25780.16,25782.91
6/11/2023 2:30:00 AM,0.50,25785.2,25800.34,25785.2,25796.69
6/11/2023 2:35:00 AM,0.50,25797.21,25803.06,25786.84,25788
6/11/2023 2:40:00 AM,0.50,25787.99,25788.24,25776.51,25779.54
6/11/2023 2:45:00 AM,0.50,25777.82,25785.71,25760.71,25763.15
6/11/2023 2:50:00 AM,0.50,25763.15,25770.79,25757.06,25767.38
6/11/2023 2:55:00 AM,0.50,25766.73,25772.28,25755.19,25766.11
6/11/2023 3:00:00 AM,0.50,25768.17,25770.19,25750.39,25753.79
6/11/2023 3:05:00 AM,-0.50,25752.22,25764.12,25744.24,25753.59
6/11/2023 3:10:00 AM,-0.50,25751.37,25765.72,25747.74,25765.67
6/11/2023 3:15:00 AM,-0.50,25765.67,25765.67,25753.36,25761.36
6/11/2023 3:20:00 AM,-0.50,25761.36,25783.49,25757.07,25782.18
6/11/2023 3:25:00 AM,-0.50,25782.17,25782.17,25765.89,25767.73
6/11/2023 3:30:00 AM,-0.50,25767.74,25767.74,25746.6,25748.38
6/11/2023 3:35:00 AM,-0.50,25746.92,25750.24,25705.13,25741.17
6/11/2023 3:40:00 AM,-0.50,25740.98,25757.27,25729.62,25752.3
6/11/2023 3:45:00 AM,-0.50,25754.55,25755.83,25726.92,25736.61
6/11/2023 3:50:00 AM,-0.50,25736.61,25746.34,25725.63,25733.15
6/11/2023 3:55:00 AM,0.50,25731.12,25742.77,25731.12,25738.6
6/11/2023 4:00:00 AM,0.50,25738.56,25757.96,25738.56,25753.21
6/11/2023 4:05:00 AM,0.50,25753.21,25755.35,25743.1,25749.68
6/11/2023 4:10:00 AM,0.50,25750.67,25753.65,25735.12,25741.87
6/11/2023 4:15:00 AM,0.50,25742.25,25758.71,25734.88,25737.31
6/11/2023 4:20:00 AM,0.50,25737.54,25752.47,25733.76,25745.01
6/11/2023 4:25:00 AM,0.50,25744.96,25757.94,25744.94,25753.23
6/11/2023 4:30:00 AM,0.50,25753.22,25785.99,25746.65,25774.75
6/11/2023 4:35:00 AM,0.50,25774.4,25779.03,25758.69,25771.34
6/11/2023 4:40:00 AM,0.50,25771.34,25782.54,25761.24,25776.73