UNPKG

@neabyte/candlestick-cli

Version:

Beautiful terminal candlestick charts with real-time trading data. Create stunning ASCII art charts directly in your terminal with support for live market data, custom colors, and professional trading visualization.

1 lines 3.72 kB
import{CandleType}from"../types/candlestick.js";import{Chart}from"../chart/chart.js";import{CONSTANTS}from"../constants.js";import{truecolor}from"../utils/core.js";export class ChartRenderer{bearishColor=[234,74,90];bullishColor=[52,208,88];colorize(e,r){const n=e===CandleType.Bearish?this.bearishColor:this.bullishColor;return truecolor(r,...n)}renderChartContent(e,r){const n=[],{chartData:t}=e;t.computeHeight(e.volumePane.height);const{visibleCandleSet:o}=t,a=CONSTANTS.Y_AXIS_ON_THE_RIGHT,s=e.yAxis.renderLine.bind(e.yAxis),i=e.highlights||{};if(this.renderChartBody(n,e,r,a,s,i),e.volumePane.enabled){this.renderVolumePane(n,e,r,a,o);const s=this.renderLabels(e,t.mainCandleSet);s&&n.push(s)}else n.push(e.infoBar.render(t.mainCandleSet,t.width));return n.join("")}renderCandle(e,r,n){const t=r,[o,a,s,i]=n.priceToHeights(e),l=this.determineCandleCharacter(t,o,a,s,i);return this.colorize(e.type,l)}determineCandleCharacter(e,r,n,t,o){const{ceil:a,floor:s}=Math;return a(r)>=e&&e>=s(t)?this.renderUpperCandlePart(e,r,t):a(o)>=e&&e>=s(n)?this.renderLowerCandlePart(e,n,o):t>=e&&e>=a(o)?CONSTANTS.UNICODE_BODY:CONSTANTS.UNICODE_VOID}renderUpperCandlePart(e,r,n){const t=n-e,o=r-e;return t>CONSTANTS.MAX_DIFF_THRESHOLD?CONSTANTS.UNICODE_BODY:t>CONSTANTS.MIN_DIFF_THRESHOLD?o>CONSTANTS.MAX_DIFF_THRESHOLD?CONSTANTS.UNICODE_TOP:CONSTANTS.UNICODE_HALF_BODY_BOTTOM:o>CONSTANTS.MAX_DIFF_THRESHOLD?CONSTANTS.UNICODE_WICK:o>CONSTANTS.MIN_DIFF_THRESHOLD?CONSTANTS.UNICODE_WICK_UPPER:CONSTANTS.UNICODE_VOID}renderLowerCandlePart(e,r,n){const t=n-e,o=r-e;return t<CONSTANTS.MIN_DIFF_THRESHOLD?CONSTANTS.UNICODE_BODY:t<CONSTANTS.MAX_DIFF_THRESHOLD?o<CONSTANTS.MIN_DIFF_THRESHOLD?CONSTANTS.UNICODE_BOTTOM:CONSTANTS.UNICODE_HALF_BODY_TOP:o<CONSTANTS.MIN_DIFF_THRESHOLD?CONSTANTS.UNICODE_WICK:o<CONSTANTS.MAX_DIFF_THRESHOLD?CONSTANTS.UNICODE_WICK_LOWER:CONSTANTS.UNICODE_VOID}addColoredField(e,r,n){if(r){const[t,o]=r.split(": ");e.push(`${t}: ${truecolor(o,...n)}`)}}renderLabels(e,r){const n=[];e.infoBar.name&&n.push(truecolor(e.infoBar.name,255,255,255)),this.addColoredField(n,e.infoBar.renderPrice(r),[52,208,88]),this.addColoredField(n,e.infoBar.renderHighest(r),[52,208,88]),this.addColoredField(n,e.infoBar.renderLowest(r),[234,74,90]);const t=e.infoBar.renderVariation(r);if(t){const[e,r]=t.split(": "),o=r.includes("+")?[52,208,88]:[234,74,90];n.push(`${e}: ${truecolor(r,...o)}`)}const o=e.infoBar.renderAverage(r);if(o){const[e,t]=o.split(": "),a=r.lastPrice,s=r.average;let i;i=a>s?[234,74,90]:a<s?[52,208,88]:[255,255,0],n.push(`${e}: ${truecolor(t,...i)}`)}const a=e.infoBar.renderVolume(r);if(a){const[e,r]=a.split(": ");n.push(`${e}: ${truecolor(r,52,208,88)}`)}const s=n.join(" | "),i=e.chartData.width-CONSTANTS.WIDTH,l=s.replace(/\x1b\[[0-9;]*m/g,"").length,C=Math.max(0,Math.floor((i-l)/2));return" ".repeat(C)+s}async render(e){try{const{chartData:{visibleCandleSet:{candles:r}}}=e;return 0===r.length?"No data available":this.renderChartContent(e,r)}catch(e){const r=e instanceof Error?e.message:"Unknown error";throw new Error(`Chart rendering failed: ${r}`)}}renderChartBody(e,r,n,t,o,a){const{chartData:{height:s}}=r;for(let i=s;i>0;i--)this.renderChartLine(e,r,n,t,o,a,i)}renderChartLine(e,r,n,t,o,a,s){t?e.push("\n"):e.push("\n",o(s,a));for(let t=0;t<n.length;t++){const o=n[t];e.push(this.renderCandle(o,s,r.yAxis))}t&&e.push(o(s,a))}renderVolumePane(e,r,n,t,o){const a=r.yAxis.renderEmpty.bind(r.yAxis),s=r.volumePane.render.bind(r.volumePane),{maxVolume:i}=o;for(let o=r.volumePane.height;o>0;o--)this.renderVolumeLine(e,n,t,a,s,i,o);e.push("\n"),e.push("\n")}renderVolumeLine(e,r,n,t,o,a,s){n?e.push("\n"):e.push("\n",t());for(let n=0;n<r.length;n++){const t=r[n];e.push(o(t,s,a))}n&&e.push(t())}}