{"id":13,"date":"2025-07-05T22:22:04","date_gmt":"2025-07-05T21:22:04","guid":{"rendered":"https:\/\/josepineiro.com\/?p=13"},"modified":"2025-07-06T00:49:57","modified_gmt":"2025-07-05T23:49:57","slug":"building-a-financial-charting-saas-the-tech-behind-the-candlesticks","status":"publish","type":"post","link":"https:\/\/josepineiro.com\/index.php\/2025\/07\/05\/building-a-financial-charting-saas-the-tech-behind-the-candlesticks\/","title":{"rendered":"Building a Financial Charting SaaS: The Tech Behind the Candlesticks"},"content":{"rendered":"<span class=\"span-reading-time rt-reading-time\" style=\"display: block;\"><span class=\"rt-label rt-prefix\">Reading Time: <\/span> <span class=\"rt-time\"> 5<\/span> <span class=\"rt-label rt-postfix\">minutes<\/span><\/span>\n<p><\/p>\n\n\n\n<p>Some time ago, I began developing a web-based <strong>SaaS<\/strong> platform designed to help users create and customize candlestick charts for analyzing stocks and currencies.&nbsp; This includes the ability&nbsp;to save charts directly to user accounts.&nbsp; As I mapped out the project, there were several key decision points &#8211; starting with <strong>a good charting library<\/strong> that would best serve both our backend and interactive front end.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Backend vs. Frontend: Setting the Stage<\/h2>\n\n\n\n<p>My initial focus was on the backend using <strong>Python<\/strong>.&nbsp; However, if I wanted to give users the ability to dynamically customize their charts on the fly, I would have to think about incorporating <strong>Javascript<\/strong>.&nbsp; Fetching new charts via the server for each user interaction seemed inefficient\u2014each update would trigger latency and require saving chart images to disk before rendering. That approach would quickly become cumbersome. Users would have to select their charting attributes before making a call to the server and deal with wait times.&nbsp; We would have to deal with saving an image every time there is a change and then referencing that file in the HTML.<\/p>\n\n\n\n<p>Although I had prior experience with Plotly in Python via <strong>Jupyter<\/strong> notebooks, I realized this project required rich front-end interactivity. JavaScript needed to take the lead in rendering responsive visuals.<\/p>\n\n\n\n<p><strong>Server vs. Client-side<\/strong><\/p>\n\n\n\n<p>It is apparent that one of the biggest decisions was whether charts would be rendered on the server or the client. My research kept leading me back to <strong>Plotly<\/strong>, which has both Javascript and Python libraries. My initial inclination was to go with server-side rendering due to processing demands of handling financial data from third-party APIs data and generating chart images via Python. I figured the server would be better because of the required processing power. On the other hand, repeated calls to generate chart images via Python every time the client makes a customization felt inefficient and slow.<\/p>\n\n\n\n<p>Basically, Plotly&#8217;s Python library, whether on its own or via its Dash framework, would generate static images or standalone HTML files.  The image byte code can be saved to the database, but the repeated calls to the server (and perhaps database) would not make this option better. I decided to use the Javascript library after seeing all the chart attributes and functionality that could be called on the fly with plotly.js. This way, we can generate the final image once the client is satisfied with his or her selections\/customizations that are dynamically rendered client-side.<\/p>\n\n\n\n<p>In short, I shifted to client-side rendering with plotly.js. This allows users to interactively customize their charts, which would only be finalized and saved once selections were complete\u2014dramatically reducing server load and improving user experience.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"charting-library-considerations\">Charting library considerations<\/h2>\n\n\n\n<p><strong>Plotly<\/strong> provides various charting options, including embedding charts via HTML iframes hosted on <a href=\"https:\/\/chart-studio.plotly.com\/\">Chart Studio<\/a>, which itself is powered by the plotly.js library. Simultaneously, I explored <strong>D3.js<\/strong>\u2014a powerful, low-level visualization library. However, D3\u2019s steep learning curve and lack of high-level chart abstractions made me hesitant to reinvent the wheel. Plotly.js builds on top of D3 and simplifies those complexities into accessible functions.<\/p>\n\n\n\n<p><strong>Why Not Dash or D3?<\/strong><\/p>\n\n\n\n<p>Plotly&#8217;s&nbsp;<strong>Dash<\/strong>&nbsp;is a capable framework for building dashboards, but it&#8217;s <strong>Flask<\/strong>-based, while our project runs on <strong>Django<\/strong>.  Dash also focuses on full dashboard experiences rather than embedded, standalone widgets.  Since it&#8217;s built atop plotly.js, using Dash would add another layer -and more server communication (via callbacks) -without offering clear benefits.<\/p>\n\n\n\n<p>Another good data visualization library seems to be&nbsp;<strong>D3.js<\/strong>. According to D3&#8217;s documentation:&nbsp;<em>&#8220;Because D3 has no overarching \u201cchart\u201d abstraction, even a basic chart may require a few dozen lines of code. On the upside, all the pieces are laid out in front of you and you have complete control over what happens.&#8221;<\/em>&nbsp;<\/p>\n\n\n\n<p>Like any other higher-level abstraction built on top of the main source, there is a tradeoff between the amount of code that needs to be written and control over what is possible. I learned that&nbsp;<strong>D3.js<\/strong>&nbsp;has a high learning curve and that Plotly is built on top of D3.js. So why reinvent the wheel? Why not abstract away some of those complexities? Oh yeah, and Plotly is open source as well.<\/p>\n\n\n\n<p>D3.js is powerful but low-level. As noted in <a href=\"https:\/\/www.freecodecamp.org\/news\/how-and-why-i-used-plotly-instead-of-d3-to-visualize-my-lollapalooza-data-d48345e2ca68\/\">this FreeCodeCamp post<\/a>, D3\u2019s learning curve and manual chart construction make it less practical for rapid development. Plotly.js abstracts those complexities and offers financial chart types out of the box.<\/p>\n\n\n\n<p>I briefly considered using <strong>Cufflinks<\/strong>\u2014a library that further simplifies Plotly chart creation with Pandas\u2014but its development has stagnated and adds yet another abstraction layer with the loss of flexibility that this entails. In contrast, Plotly.js remains actively maintained with <a href=\"https:\/\/github.com\/plotly\/plotly.js\">open-source contributions via GitHub<\/a>.<\/p>\n\n\n\n<p><strong>Real-World Challenges and Breakthroughs<\/strong><\/p>\n\n\n\n<p>One challenge was where I foolishly spent hours getting the input stock symbol to correctly pass to the fetch request. I moved the code and placed the input box above the chart div and it started working. It appears that the Plotly chart was interfering with my Javascript and preventing it from correctly grabbing the input value. Another challenge was changing candlestick width. As of this writing, Plotly doesn&#8217;t expose a candlestick width parameter. It depends on how many candlesticks are displayed in the figure.<\/p>\n\n\n\n<p>Another major consideration was how to access the financial market data.&nbsp; I had a chosen an API in mind with sufficiently high rate limits on historical data for testing.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"data-manipulation---pandas\">Data Manipulation: Finding Harmony Between Pandas and JavaScript<\/h2>\n\n\n\n<p>For API integration and technical indicator calculations, I relied on <strong>Pandas<\/strong> to process JSON data and compute moving averages. Sending pre-processed data via fetch requests made the front-end leaner. However, I found that JavaScript could handle many calculations instantly\u2014highs, lows, price variance\u2014based on user selections. For more advanced indicators, I may revert to Pandas, especially given its clarity and power.<\/p>\n\n\n\n<p>The decision to transform API data server-side using Pandas was strategic. It allowed me to hide the API key from the client, control rate limits per user, and prepare arrays suitable for the frontend charting logic. Date slicing, filtering, and calculating indicators were much easier with Pandas than with raw JavaScript.<\/p>\n\n\n\n<p>As soon as I wanted to do calculations and manipulate the data, it became evident how Pandas Dataframes made things simpler. I wanted most of the rendering to be client-side. However, calculations and annotations on the client-side would require more of my own JS coding and possibly the help of other JS libraries. For example, the stock price API can either return the last 100 data points or 20+ years of historical data, but I am looking to chart YTD or the last 52 weeks. Specifying the date range and performing other actions is easier with Pandas.<\/p>\n\n\n\n<p><strong>From Interaction to Image: The Final Piece<\/strong><\/p>\n\n\n\n<p>At the beginning of the project. I did not know whether the user can dynamically re-render the chart with js or whether the final customized chart would be rendered on the server. By now, we have data pre-processed once on the server while all the customization and finalized charting happens on the client side and sent to the database as static image byte code.<\/p>\n\n\n\n<p>The turning point came with discovering Plotly\u2019s <code>toImage()<\/code> function. This allowed users to customize charts in the browser and save them as base64-encoded static images to our database. It neatly closed the loop from data to display to persistence.<\/p>\n\n\n\n<p>Plotly.js also offers configuration settings\u2014like <code>{scrollZoom: true}<\/code> and <code>{editable: true}<\/code>\u2014that make charts more responsive and user-friendly.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Conclusion<\/h2>\n\n\n\n<p>What ultimately tipped the scales toward Plotly.js was its blend of simplicity, power, and seamless fit with our Django-based stack. While D3.js offers unmatched low-level flexibility, its steep learning curve and the need to handcraft every chart element can slow development\u2014especially when you want candlesticks and financial annotations out of the box. Plotly.js leverages D3 under the hood, but abstracts away complexities.  Dash, by contrast, is optimized for full dashboards on Flask rather than individual widgets in Django, and Plotly Chart Studio\u2019s cloud-based GUI and external storage model felt like overkill for our real-time SaaS.  Plotly.js\u2019s free, open-source codebase\u2014with an active GitHub community for issues and contributions\u2014delivered exactly the high-level API and performance footprint I needed.<\/p>\n\n\n\n<p>This journey clarified not just which charting tool to use, but how architectural decisions reverberate across performance, usability, and developer experience. Plotly.js ultimately offered the best mix: high-level abstractions built atop D3, seamless integration with JavaScript, and flexible enough to pair with Python\u2019s Pandas backend. By choosing the right layers of abstraction and keeping client-side interactivity at the core, the project remains lightweight, scalable, and intuitive for end users.<\/p>\n\n\n\n<p><\/p>\n\n\n\n<p><strong>References<\/strong><\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>FreeCodeCamp. (n.d.). <em>D3.js tutorial \u2013 Data visualization for beginners<\/em>. <a href=\"https:\/\/www.freecodecamp.org\/news\/d3js-tutorial-data-visualization-for-beginners\/\">https:\/\/www.freecodecamp.org\/news\/d3js-tutorial-data-visualization-for-beginners\/<\/a><\/li>\n\n\n\n<li>Mesquita, D. (2021, May 14). <em>How and why I used Plotly (instead of D3) to visualize my Lollapalooza data<\/em>. FreeCodeCamp. <a href=\"https:\/\/www.freecodecamp.org\/news\/how-and-why-i-used-plotly-instead-of-d3-to-visualize-my-lollapalooza-data-d48345e2ca68\/\">https:\/\/www.freecodecamp.org\/news\/how-and-why-i-used-plotly-instead-of-d3-to-visualize-my-lollapalooza-data-d48345e2ca68\/<\/a><\/li>\n\n\n\n<li>Plotly. (n.d.). <em>Plotly.js GitHub repository<\/em>. <a href=\"https:\/\/github.com\/plotly\/plotly.js\">https:\/\/github.com\/plotly\/plotly.js<\/a><\/li>\n\n\n\n<li>CodeFile. (2020, November 23). <em>An interactive web dashboard with Plotly and Flask<\/em>. Medium. <a href=\"https:\/\/medium.com\/codefile\/an-interactive-web-dashboard-with-plotly-and-flask-c365cdec5e3f\">https:\/\/medium.com\/codefile\/an-interactive-web-dashboard-with-plotly-and-flask-c365cdec5e3f<\/a><\/li>\n\n\n\n<li>Kalani, Y. (2022, February 20). <em>The simplest way to create an interactive candlestick chart in Python<\/em>. Towards Data Science. <a href=\"https:\/\/towardsdatascience.com\/the-simplest-way-to-create-an-interactive-candlestick-chart-in-python-ee9c1cde50d8\/\">https:\/\/towardsdatascience.com\/the-simplest-way-to-create-an-interactive-candlestick-chart-in-python-ee9c1cde50d8\/<\/a><\/li>\n\n\n\n<li>Plotly. (n.d.). <em>Dash in 20 minutes tutorial<\/em>. <a href=\"https:\/\/dash.plotly.com\/tutorial\">https:\/\/dash.plotly.com\/tutorial<\/a><\/li>\n\n\n\n<li>GeeksforGeeks. (n.d.). <em>Introduction to Dash in Python<\/em>. <a href=\"https:\/\/www.geeksforgeeks.org\/data-science\/introduction-to-dash-in-python\/\">https:\/\/www.geeksforgeeks.org\/data-science\/introduction-to-dash-in-python\/<\/a><\/li>\n<\/ol>\n\n\n\n<p><\/p>\n\n\n\n<p><\/p>\n\n\n\n<p><\/p>\n","protected":false},"excerpt":{"rendered":"<p><span class=\"span-reading-time rt-reading-time\" style=\"display: block;\"><span class=\"rt-label rt-prefix\">Reading Time: <\/span> <span class=\"rt-time\"> 5<\/span> <span class=\"rt-label rt-postfix\">minutes<\/span><\/span>Some time ago, I began developing a web-based SaaS platform designed to help users create and customize candlestick charts for analyzing stocks and currencies.&nbsp; This includes the ability&nbsp;to save charts directly to user accounts.&nbsp; As I mapped out the project, there were several key decision points &#8211; starting with a good charting library that would &#8230; <a title=\"Building a Financial Charting SaaS: The Tech Behind the Candlesticks\" class=\"read-more\" href=\"https:\/\/josepineiro.com\/index.php\/2025\/07\/05\/building-a-financial-charting-saas-the-tech-behind-the-candlesticks\/\" aria-label=\"Read more about Building a Financial Charting SaaS: The Tech Behind the Candlesticks\">Read more<\/a><\/p>\n","protected":false},"author":1,"featured_media":72,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1,2],"tags":[12,15,11,16,14,18,13],"class_list":["post-13","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-blog","category-web-development","tag-candlestick-charts","tag-django","tag-financial-visualization","tag-javascript","tag-plotly","tag-python","tag-saas-development"],"_links":{"self":[{"href":"https:\/\/josepineiro.com\/index.php\/wp-json\/wp\/v2\/posts\/13","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/josepineiro.com\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/josepineiro.com\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/josepineiro.com\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/josepineiro.com\/index.php\/wp-json\/wp\/v2\/comments?post=13"}],"version-history":[{"count":12,"href":"https:\/\/josepineiro.com\/index.php\/wp-json\/wp\/v2\/posts\/13\/revisions"}],"predecessor-version":[{"id":73,"href":"https:\/\/josepineiro.com\/index.php\/wp-json\/wp\/v2\/posts\/13\/revisions\/73"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/josepineiro.com\/index.php\/wp-json\/wp\/v2\/media\/72"}],"wp:attachment":[{"href":"https:\/\/josepineiro.com\/index.php\/wp-json\/wp\/v2\/media?parent=13"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/josepineiro.com\/index.php\/wp-json\/wp\/v2\/categories?post=13"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/josepineiro.com\/index.php\/wp-json\/wp\/v2\/tags?post=13"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}