
Jika Anda seorang freelancer atau CTO dari sebuah proyek keuangan, cepat atau lambat Anda akan dihadapkan pada masalah jadwal penghubung, saya akan menghemat setidaknya satu hari kerja. Mereka yang sudah menggunakan perpustakaan ini mungkin menemukan sesuatu yang baru.
Artikel ini akan memiliki format "buku resep" dengan solusi open source untuk pertukaran cryptocurrency Binance dan Forex.
Halo, Habr!
TradingView (charting_library) , - , TradingView.com. " " .
Cook book
. , , :)
. , , 404 , , .
. — .
, . Forex- , . 2- Forex , - . . .
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')
})
}
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 .
Tidak ada fungsi seperti itu dalam versi gratis charting_library . Jika perlu, Anda bisa melakukannya sendiri HTML + CSS.
Sumber terbuka
- tradingview-jsapi-binance - pertukaran Binance terhubung dengan adaptor JS API dan aliran WebSocket
- tradingview-jsapi-forex - Data forex untuk adaptor JS API. Refresh data menit tanpa WebSocket dengan metode Save \ Load
Kesimpulan
Artikel tersebut akan dilengkapi. Jika ada kasus dengan masalah - solusi, tulis, saya akan melengkapi artikel dengan atribusi.
Menarik juga untuk mendengar pendapat, pengalaman, pertanyaan, dan keinginan Anda.
Terima kasih atas perhatian Anda!