Connexion et configuration des graphiques TradingView



Si vous êtes pigiste ou CTO d'un projet financier, tôt ou tard vous serez confronté à la problématique des horaires de connexion, je vous ferai gagner au moins une journée de travail. Ceux qui utilisent déjà cette bibliothèque peuvent trouver quelque chose de nouveau.



L'article sera au format d'un "livre de recettes" avec des solutions open source pour l'échange de crypto-monnaie Binance et Forex.



Bonjour, 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 , , .



, resolveSymbolhas_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





Il n'y a pas de telles fonctionnalités dans la version gratuite de charting_library . Si nécessaire, vous pouvez le faire vous-même HTML + CSS.



Open source



  • tradingview-jsapi-binance - échange Binance connecté avec adaptateur API JS et flux WebSocket
  • tradingview-jsapi-forex - Données Forex pour l'adaptateur API JS. Actualisation des données minute sans WebSocket avec les méthodes Save \ Load


Conclusion



L'article sera complété. S'il y a un cas avec un problème - une solution, écrivez, je compléterai l'article avec l'attribution.



Il est également intéressant d'entendre votre opinion, votre expérience, vos questions et vos souhaits.



Merci de votre attention!




All Articles