Hi, I'm running LEAN local and enjoying the charting.
But I'm greedy and want more control of the look and feel.
I'd like to;
- Use dashed lines for some series (say the middle line in Keltner channels)
- Vertically offset position of scatterMarkerSymbols so they indicate the day, rather than the day and price.
- Control thickness of lines
EduardV
Hi everyone, I would also like to know if there is any way to combine backtesting with TradingView's charting API when running LEAN locally.. That would be cool.
Derek Melchin
Hi EduardV,
Yes, this is possible. For a simple example, we can follow this workflow after installing Lightweight Charts:
Best,
Derek Melchin
The material on this website is provided for informational purposes only and does not constitute an offer to sell, a solicitation to buy, or a recommendation or endorsement for any security or strategy, nor does it constitute an offer to provide investment advisory services by QuantConnect. In addition, the material offers no opinion with respect to the suitability of any security or specific investment. QuantConnect makes no guarantees as to the accuracy or completeness of the views expressed in the website. The views are subject to change, and may have become unreliable for various reasons, including changes in market conditions or economic circumstances. All investments involve risk, including loss of principal. You should consult with an investment professional before making any investment decisions.
AgedVagabond
AgedVagabond
This will Load from your saved data (trades are converted from Statistics.Trades using trade matching logic with the Algorithm.Trade Builder, (that logic is not included as I modified the Lean engine for this.
There is currently a bug with scaling charts and possibly something to do with Time conversion as I am not getting perfect charts yet, this allows to export charts with all trades for the day during live trading and backtesting and since it is standalone version it doesnt require installing lightweight charts, charts could just be forwarded from a linux node to an email address or some other method.
I am not sure how it will work with the cmd or we version but something could be done,
this doesnt update charts in real time but rather generates charts from saved data at specified times,
it uses JS place holders in a template html file and replaces these with my list of bars and trades to be plotted using JS parsing functions built into the html doc.
the problem with backtesting and trade matching on standard engine is that on large backtests the TradeBuilder.Closed Trades IEnumerable is a huge collection.
I built a second list of closed trades: private readonly Dictionary<Symbol, List<Trade>> _unmanagedTrades = new Dictionary<Symbol, List<Trade>>();
From this list I can use the following to shorten the ClosedTrade lists per symbol after trade matching (without affecting the original list used by the engine), shortening the lists has a massive impact on cycling the lists for trade matching. (I also needed this feature for ML training in relation to matching trades to entry and exit data, it really should be built into the Lean engine in the future as it allows accurate matching of Lean Trades to user criteria without much overhead.
public void RemoveTrade(Symbol symbol, Trade trade){ lock (_unmanagedTrades) { _unmanagedTrades[symbol].Remove(trade); }}
Its a work in progress so I am working out bugs, once i learn html and js I will make a live version to stream from my nodes.
<!DOCTYPE html><html><head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0,minimum-scale=1.0" /> <title>Lightweight Charts Customization Tutorial</title> <!-- Adding Google Font --> <link rel="preconnect" href="https://fonts.googleapis.com" /> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin /> <link href="https://fonts.googleapis.com/css2?family=Roboto&display=swap" rel="stylesheet" /> <!-- Adding the standalone version of Lightweight charts --> <script type="text/javascript" src="https://unpkg.com/lightweight-charts/dist/lightweight-charts.standalone.production.js"></script> <style> body { padding: 0; margin: 0; background-color: #111; /* Matching the chart background color */ } </style></head><body><div id="container" style="position: absolute; width: 100%; height: 100%"></div><div id="candlestick-data">{CANDLESTICK_DATA}</div><div id="trades-data">{TRADES_DATA}</div><script type="text/javascript"> const chart = LightweightCharts.createChart(document.getElementById('container'), { layout: { background: { color: '#222' }, textColor: '#DDD', }, grid: { vertLines: { color: '#444' }, horzLines: { color: '#444' }, }, }); let CANDLESTICK_DATA; let TRADES_DATA; try { CANDLESTICK_DATA = JSON.parse(document.getElementById('candlestick-data').textContent); TRADES_DATA = JSON.parse(document.getElementById('trades-data').textContent); } catch (e) { console.error("Parsing error: ", e); } function parseCandlestickData(data) { return data.map(function (bar) { return { time: bar.UnixTime, open: bar.Open, high: bar.High, low: bar.Low, close: bar.Close, }; }); } function parseTradeData(tradeData) { return tradeData.map(function (trade) { return { entryTime: trade.EntryTime, exitTime: trade.ExitTime, entryPrice: trade.EntryPrice, exitPrice: trade.ExitPrice, side: trade.Side.toLowerCase(), }; }); } const candlestickData = parseCandlestickData(CANDLESTICK_DATA); const tradesData = parseTradeData(TRADES_DATA); function generateMarkers(tradesData) { let count = 0; let markers = []; tradesData.forEach((trade) => { count++; if (trade.side === 'long' || trade.side === 'short') { markers.push({ time: trade.entryTime, position: trade.side === 'long' ? 'belowBar' : 'aboveBar', color: trade.side === 'long' ? '#008000' : '#FF0000', shape: trade.side === 'long' ? 'arrowUp' : 'arrowDown', text: `#${count} Entry ${trade.side} @ ${trade.entryPrice} ${trade.entryTime}` }); markers.push({ time: trade.exitTime, position: trade.side === 'long' ? 'aboveBar' : 'belowBar', color: trade.side === 'long' ? '#FF0000' : '#008000', shape: trade.side === 'long' ? 'arrowDown' : 'arrowUp', text: `#${count} Exit ${trade.side} @ ${trade.exitPrice} ${trade.exitTime}` }); } }); return markers; } const markers = generateMarkers(tradesData); function handleResize() { chart.resize(window.innerWidth, window.innerHeight); } window.addEventListener('resize', handleResize); chart.priceScale().applyOptions({ borderColor: '#71649C' }); chart.timeScale().applyOptions({ borderColor: '#71649C', timeVisible: true, }); chart.applyOptions({ crosshair: { mode: LightweightCharts.CrosshairMode.Normal, vertLine: { width: 8, color: '#C3BCDB44', style: LightweightCharts.LineStyle.Solid, labelBackgroundColor: '#9B7DFF', }, horzLine: { color: '#9B7DFF', labelBackgroundColor: '#9B7DFF', }, }, }); const mainSeries = chart.addCandlestickSeries(); mainSeries.setData(candlestickData); mainSeries.setMarkers(markers); console.log(candlestickData, markers); chart.fitContent();</script></body></html>
AgedVagabond
Â
AgedVagabond
sorry uploading html is a mess
Mike Hibbert
The material on this website is provided for informational purposes only and does not constitute an offer to sell, a solicitation to buy, or a recommendation or endorsement for any security or strategy, nor does it constitute an offer to provide investment advisory services by QuantConnect. In addition, the material offers no opinion with respect to the suitability of any security or specific investment. QuantConnect makes no guarantees as to the accuracy or completeness of the views expressed in the website. The views are subject to change, and may have become unreliable for various reasons, including changes in market conditions or economic circumstances. All investments involve risk, including loss of principal. You should consult with an investment professional before making any investment decisions.
To unlock posting to the community forums please complete at least 30% of Boot Camp.
You can continue your Boot Camp training progress from the terminal. We hope to see you in the community soon!