Connecting and configuring TradingView charts



If you are a freelancer or CTO of a financial project, sooner or later you will be faced with the issue of connecting schedules, I will save you at least a day of work. Those who are already using this library may find something new.



The article will be in the format of a "recipe book" with open source solutions for the cryptocurrency exchange Binance and Forex.



Hello, Habr!



TradingView (charting_library) , - , TradingView.com. " " .



: " ".



English version.



Cook book



. , , :)



. , , 404 , , .





. โ€” .



, . Forex- , . 2- Forex , - . . .





GitHub, :



  1. GitHub




4 . 3 . , .



,



.





//  Nodejs
import { widget } from '../public/charting_library/charting_library.min'
const widget = new widget({ <options> })




charting_library library_path: '/charting_library/'



. Vuejs vue.config.js => publicPath: '/'. : /public/index.html, /public/charting_library/ , .







. , : JS API UDF. "" . JSAPI, UDF , , .



  • JS API โ€”
  • UDF โ€”


JSAPI UDF, UDF WebSocket . , : datafeed: new Datafeeds.UDFCompatibleDatafeed('http://localhost:3000/datafeed', 1000)





TradingView JS API adapter



, , console.log('[< >]: Method call').



: onReady => resolveSymbol => getBars => subscribeBars => unsubscribeBars.



, , unsubscribeBars, , WebSocket . subscribeBars, unsubscribeBars . getServerTime , , .



, resolveSymbol โ€” has_no_volume: true.



export default {
    //  ,   
    onReady: (callback) => {
        console.log('[onReady]: Method call');
                // setTimeout(() => callback(<  >))
    },
    /*
     //  ,    
    searchSymbols: (userInput, exchange, symbolType, onResultReadyCallback) => {
        console.log('[searchSymbols]: Method call');
    },
     */
    //     
    resolveSymbol: (symbolName, onSymbolResolvedCallback, onResolveErrorCallback) => {
        console.log('[resolveSymbol]: Method call', symbolName);
                // onSymbolResolvedCallback({ ..., has_no_volume: true})
    },
    //      
    getBars: (symbolInfo, interval, from, to, onHistoryCallback, onErrorCallback, firstDataRequest) => {
        console.log('[getBars] Method call', symbolInfo, interval)
        console.log('[getBars] First request', firstDataRequest)
    },
    //    WebSocket
    subscribeBars: (symbolInfo, interval, onRealtimeCallback, subscribeUID, onResetCacheNeededCallback) => {
        console.log('[subscribeBars]: Method call with subscribeUID:', subscribeUID);
    },
        //     
    unsubscribeBars: (subscriberUID) => {
        console.log('[unsubscribeBars]: Method call with subscriberUID:', subscriberUID);
    },
    getServerTime: (callback) => {}
};


, Binance, .



JS API |



TradingView UDF adapter



UDF , . datafeed: new Datafeeds.UDFCompatibleDatafeed('http://localhost:3000/datafeed', 1000)



//     **Fastify**
// main.js
const app = Fastify()
app.register(import('./modules/tradingview'), {})

// tradingview.js
const plugin = async (app, options) => {

        //    
    app.get('/', (req, res) => {
        res.code(200).header('Content-Type', 'text/plain')
            .send('Welcome to UDF Adapter for TradingView. See ./config for more details.')
    })

        //  
    app.get('/time', (req, res) => {
        console.log('[time]: Method call')
        const time = Math.floor(Date.now() / 1000)  // In seconds
        res.code(200).header('Content-Type', 'text/plain').send(time.toString())
    })

         //  onReady
        // https://github.com/tradingview/charting_library/wiki/UDF#data-feed-configuration-data
    app.get('/config', (req, res) => {
        console.log('[config]: Method call')
    })

    //  : supports_group_request: true & supports_search: false
    app.get('/symbol_info', async (req, res) => {
        console.log('[symbol_info]: Method call')
    })

    //  : supports_group_request: false & supports_search: true
    app.get('/symbols', async (req, res) => {
        console.log('[symbol_info]: Method call')
        const symbol = await getSymbols(req.query.symbol)
        return symbol
    })

        //  getBars,   
    app.get('/history', async (req, res) => {
        console.log('[history]: Method call')
    })
}


UDF



JS API getBars



, "" . getBars firstDataRequest, true\false, . true .



getBars: (symbolInfo, interval, from, to, onHistoryCallback, onErrorCallback, firstDataRequest) => {
        console.log('[getBars] Method call', symbolInfo, interval)
        console.log('[getBars] First request', firstDataRequest)

                if (firstDataRequest) { 
                   console.log('do something')
                }
},


WebSocket



UDF , . JS API , setInterval subscribeBars .



subscribeBars: (symbolInfo, resolution, onRealtimeCallback, subscribeUID,  onResetCacheNeededCallback) => {
        console.log('[subscribeBars]: Method call with subscribeUID:', subscribeUID)

        window.interval = setInterval(function () {
            getLastKline(symbolInfo.ticker, resolution).then(kline => onRealtimeCallback(kline))
        }, 1000 * 60) // 60s update interval
},
unsubscribeBars: (subscriberUID) => {
        console.log('[unsubscribeBars]: Method call with subscriberUID:', subscriberUID)

        clearInterval(window.interval)
        console.log('[unsubscribeBars]: cleared')
}






: theme: "Light" || "Dark". . , , header_widget ( , .), .css.



: custom_css_url: '/tradingview.css', / โ€” index.html. :



.chart-controls-bar {
    border-top: none !important;
}

.chart-page, .group-wWM3zP_M-  {
    background: transparent !important;
}

.pane-separator {
    display: none !important;
}






"".



Save\Load



, , . , widget.save(cb => this.setOverlay(cb)) , .





Save\Load adapter



UDF adapter. \ .





- ,



, , , . . .



, , onChartReady. , , Observer.



widget.onChartReady(function() {
    // It's now safe to call any other methods of the widget
});


TradingView.com



, .





, , . Vuejs, .



import orders from '../../../multiblock/orders/mixin'

import createOrder from './createOrder'
import openOrders from './openOrders'
import trades from './trades'

export default {
    mixins: [orders, createOrder, openOrders, trades],
    data: () => ({
        lines: new Map()
    }),
    watch: {
        onChartReady(val) {
            if (val) {
                //* Uncomment: Testing price line
                // this.line({ id: 'test', price: 0.021, quantity: 100 })
            }
        },
    },
    methods: {
        // Line: open orders
        positionLine(data) {
            this.line(data)
                .onCancel(() => {
                    this.deleteLine(data.id)
                    this.$bus.$emit('market-orders-deleteOrder', data.id)
                })
                .onMove(() => this.$bus.$emit('market-orders-updateOrder', { id: data.id, price: this.lines.get(data.id).getPrice() }))

        },
        // Line: order mobule ('price', 'stopPrice')
        orderLine({ id = 'price', ...data }) {
            this.line({ id, ...data })
                .onMove(() => {
                    // Set new value on draging
                    this.$store.commit('setMarketOrder', { [id]: this.lines.get(id).getPrice() })
                })
                .onCancel(() => {
                    // Delete price line & set price = 0
                    this.deleteLine(id)
                    this.$store.commit('setMarketOrder', { [id]: 0 }) // set 0 value in vuex storage
                })
        },
        line({ id = 'price', text = 'Price', color = '#ff9f0a', price, quantity, fontColor = '#fff', lineStyle = 2, lineLength = 25 }) {
            if (this.lines.has(id)) this.deleteLine(id)

            // Creating line from scratch
            const widget = this.widget.chart().createOrderLine()
                .setText(text)
                .setPrice(price)
                .setQuantity(quantity)
                .onModify(res => res) // Need for dragging

                // Customize color
                .setLineColor(color)
                .setBodyTextColor(fontColor)
                .setBodyBorderColor(color)
                .setBodyBackgroundColor(color)

                .setQuantityBorderColor(color)
                .setQuantityTextColor(fontColor)
                .setQuantityBackgroundColor(color)

                .setCancelButtonBorderColor(color)
                .setCancelButtonBackgroundColor(color)
                .setCancelButtonIconColor(fontColor)

                .setLineLength(lineLength) // Margin right 25%
                .setLineStyle(lineStyle)

            this.lines.set(id, widget)

            return widget // return for orderLine func()

        },
        deleteLine(id) {
            this.lines.get(id).remove()
            this.lines.delete(id)
        },
        deleteLines() {
            this.lines.forEach((value, key) => this.deleteLine(key))
        }
    }
}






, . , .



|



PineScript



charting_library . PineScript JavaScript .





Custom Studies





There is no such functionality in the free version of charting_library . If necessary, you can do it yourself HTML + CSS.



Open source





Conclusion



The article will be supplemented. If there is a case with a problem - a solution, write, I will add an article indicating authorship.



It is also interesting to hear your opinion, experience, questions and wishes.



Thank you for attention!




All Articles