当前位置:网站首页>Tradingview 使用教程
Tradingview 使用教程
2022-07-26 10:25:00 【Zheng One Dream】
进入:https://www.tradingview.com/HTML5-stock-forex-bitcoin-charting-library/
然后选择自己想要的库
第一个轻量图表
第二个技术分析图表
第三个图表&交易平台
主要对 Technical Analysis Charts(技术分析图表) 做一个教程
需要注意的是,这个库需要获取才能使用
获取步骤:
↓
↓
全部写完会出现一个这个东西,需要你下载这个文件,然后签名并上传
注意:不受理个人申请或测试用、研究用的申请;公司申请必须是受合法监管的公司,不符合规范的公司请勿申请(https://s3.amazonaws.com/tradingview/charting_library_license_agreement.pdf)
获取到库之后 我们就可以进行下一步操作了!
1. 克隆库到本地
git clone https://github.com/tradingview/charting_library charting_library_clonned_data
2. 创建一个 index.html
!DOCTYPE HTML>
<html>
<head>
<title>TradingView Charting Library example</title>
<script
type="text/javascript"
src="charting_library_clonned_data/charting_library/charting_library.js">
</script>
<!-- Custom datafeed module. -->
<script type="module" src="src/main.js"></script>
</head>
<body style="margin:0px;">
<div id="tv_chart_container">
<!-- This div will contain the Charting Library widget. -->
</div>
</body>
</html>
3. 添加一个 src 目录,main.js
// 这里引入 datafeed 这个文件后边我们再说
import Datafeed from './datafeed.js';
window.tvWidget = new TradingView.widget({
symbol: 'Bitfinex:BTC/USD', // 默认symbol
interval: '1D', // 默认的数据间隔
fullscreen: true, // 是否全屏
container_id: 'tv_chart_container', // 盒子的id
datafeed: Datafeed,
library_path: '../charting_library_clonned_data/charting_library/', // 库的相对路径
});
4. 创建一个 datafeed.js
export default {
onReady: (callback) => {
console.log('[onReady]: Method call');
},
searchSymbols: (userInput, exchange, symbolType, onResultReadyCallback) => {
console.log('[searchSymbols]: Method call');
},
resolveSymbol: (symbolName, onSymbolResolvedCallback, onResolveErrorCallback) => {
console.log('[resolveSymbol]: Method call', symbolName);
},
getBars: (symbolInfo, resolution, from, to, onHistoryCallback, onErrorCallback, firstDataRequest) => {
console.log('[getBars]: Method call', symbolInfo);
},
subscribeBars: (symbolInfo, resolution, onRealtimeCallback, subscribeUID, onResetCacheNeededCallback) => {
console.log('[subscribeBars]: Method call with subscribeUID:', subscribeUID);
},
unsubscribeBars: (subscriberUID) => {
console.log('[unsubscribeBars]: Method call with subscriberUID:', subscriberUID);
},
};
5. 实现这些方法
5.1 datafeed.js 添加一个 configurationData 对象,用来配置一些参数
const configurationData = {
supported_resolutions: ['1D', '1W', '1M'],
exchanges: [
{
value: 'Bitfinex',
name: 'Bitfinex',
desc: 'Bitfinex',
},
{
// 如果用户选择该交易所,searchSymbols 方法中的 exchange 参数
value: 'Kraken', // 交易所名称
name: 'Kraken', // 过滤器名称
desc: 'Kraken bitcoin exchange', // 过滤器弹出窗口中显示的完整交易所名称
},
],
symbols_types: [
{
// 如果用户选择这个symbol,searchSymbols 方法中的 symbolType 参数
name: 'crypto',
value: 'crypto',
},
// ...
],
};
5.2 实现 onReady 方法,图表库使用此方法来获取 datafeed 的配置(例如,支持的间隔(resolutions),交易所(exchange)等)。这是调用数据馈送的第一个方法。我们的 datafeed 会将此配置返回到图表库。注意,该回调必须异步调用
export default {
onReady: (callback) => {
console.log('[onReady]: Method call');
setTimeout(() => callback(configurationData));
},
...
};
5.2 创建 helpers.js 文件,我们用免费的 CryptoCompare API 来模拟数据,可以在浏览器输入 https://min-api.cryptocompare.com/data/v3/all/exchanges,查看返回数据格式
// 向 CryptoCompare API 发出请求
export async function makeApiRequest(path) {
try {
const response = await fetch(`https://min-api.cryptocompare.com/${path}`);
return response.json();
} catch(error) {
throw new Error(`CryptoCompare request error: ${error.status}`);
}
}
// 从对币生成 symbol id
export function generateSymbol(exchange, fromSymbol, toSymbol) {
const short = `${fromSymbol}/${toSymbol}`;
return {
short,
full: `${exchange}:${short}`,
};
}
5.3 在 datafeed.js 文件里写一个 getAllSymbols 方法用来获取所有的 symbol
import { makeApiRequest, generateSymbol } from './helpers.js';
// ...
async function getAllSymbols() {
const data = await makeApiRequest('data/v3/all/exchanges');
let allSymbols = [];
for (const exchange of configurationData.exchanges) {
const pairs = data.Data[exchange.value].pairs;
for (const leftPairPart of Object.keys(pairs)) {
const symbols = pairs[leftPairPart].map(rightPairPart => {
const symbol = generateSymbol(exchange.value, leftPairPart, rightPairPart);
return {
symbol: symbol.short,
full_name: symbol.full,
description: symbol.short,
exchange: exchange.value,
type: 'crypto',
};
});
allSymbols = [...allSymbols, ...symbols];
}
}
return allSymbols;
}
5.4 实现 resolveSymbol 方法,此方法来检索有关特定交易品种的信息(交易所,价格比例,完整交易品种等)。
export default {
// ...
resolveSymbol: async (
symbolName,
onSymbolResolvedCallback,
onResolveErrorCallback
) => {
console.log('[resolveSymbol]: 方法被调用了', symbolName);
const symbols = await getAllSymbols();
const symbolItem = symbols.find(({ full_name }) => full_name === symbolName);
if (!symbolItem) {
console.log('[resolveSymbol]: 不支持的 symbol', symbolName);
onResolveErrorCallback('暂不支持该 symbol');
return;
}
const symbolInfo = {
ticker: symbolItem.full_name,
name: symbolItem.symbol,
description: symbolItem.description,
type: symbolItem.type,
session: '24x7',
timezone: 'Etc/UTC',
exchange: symbolItem.exchange,
minmov: 1,
pricescale: 100,
has_intraday: false,
has_no_volume: true,
has_weekly_and_monthly: false,
supported_resolutions: configurationData.supported_resolutions,
volume_precision: 2,
data_status: 'streaming',
};
console.log('[resolveSymbol]: symbol 已创建', symbolName);
onSymbolResolvedCallback(symbolInfo);
},
// ...
};
5.5 在 helpers.js 里添加方法,它解析一个加密币对并返回该 symbol 的所有组成部分(full 是
generateSymbol 方法
返回的值)
export function parseFullSymbol(fullSymbol) {
const match = fullSymbol.match(/^(\w+):(\w+)\/(\w+)$/);
if (!match) {
return null;
}
return { exchange: match[1], fromSymbol: match[2], toSymbol: match[3] };
}
5.6 实现 getBars 方法,此方法用来获取 symbol 的历史数据
import { makeApiRequest, parseFullSymbol, generateSymbol } from './helpers.js';
// ...
export default {
// ...
getBars: async (symbolInfo, resolution, from, to, onHistoryCallback, onErrorCallback, firstDataRequest) => {
console.log('[getBars]: 方法被调用了', symbolInfo, resolution, from, to);
const parsedSymbol = parseFullSymbol(symbolInfo.full_name);
const urlParameters = {
e: parsedSymbol.exchange,
fsym: parsedSymbol.fromSymbol,
tsym: parsedSymbol.toSymbol,
toTs: to,
limit: 2000,
};
const query = Object.keys(urlParameters)
.map(name => `${name}=${encodeURIComponent(urlParameters[name])}`)
.join('&');
try {
const data = await makeApiRequest(`data/histoday?${query}`);
if (data.Response && data.Response === 'Error' || data.Data.length === 0) {
// 如果在请求的时间段内没有数据,则设置“noData”。
onHistoryCallback([], { noData: true });
return;
}
let bars = [];
data.Data.forEach(bar => {
if (bar.time >= from && bar.time < to) {
bars = [...bars, {
time: bar.time * 1000,
low: bar.low,
high: bar.high,
open: bar.open,
close: bar.close,
}];
}
});
console.log(`[getBars]: 返回了 ${bars.length} 个 bars`);
onHistoryCallback(bars, { noData: false });
} catch (error) {
console.log('[getBars]: 错误', error);
onErrorCallback(error);
}
},
//...
};
5.7 实现 searchSymbols,每次用户在 symbol 搜索框中键入文本时,tradingview 都会使用此方法搜索 symbol。更改 symbol 也可以使用 searchSymbols。
我们将从API请求所有可用的符号,然后在 datafeed.js 中对其进行过滤。如果用户尚未选择交易所,则该exchange
参数将等于一个空字符串
export default {
...
searchSymbols: async (
userInput,
exchange,
symbolType,
onResultReadyCallback
) => {
console.log('[searchSymbols]: 方法被调用');
const symbols = await getAllSymbols();
const newSymbols = symbols.filter(symbol => {
const isExchangeValid = exchange === '' || symbol.exchange === exchange;
const isFullSymbolContainsInput = symbol.full_name
.toLowerCase()
.indexOf(userInput.toLowerCase()) !== -1;
return isExchangeValid && isFullSymbolContainsInput;
});
onResultReadyCallback(newSymbols);
},
...
}
现在我们已经可以搜索符号并显示历史数据,接下来我们需要显示实时的交易数据
6. Streaming 实现,在本节中,我们将通过WebSocket实现实时更新。
6.1 在 index.html 中添加 socket.io,用来创建 websocket 连接
<!DOCTYPE HTML>
<html>
<head>
<!-- ... -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/1.7.2/socket.io.js"></script>
<!-- ... -->
</body>
</html>
6.2 创建 streaming.js 用来连接 websocket
// streaming.js
const socket = io('wss://streamer.cryptocompare.com');
socket.on('connect', () => {
console.log('[socket] Connected');
});
socket.on('disconnect', (reason) => {
console.log('[socket] Disconnected:', reason);
});
socket.on('error', (error) => {
console.log('[socket] Error:', error);
});
export function subscribeOnStream() {
// todo
}
export function unsubscribeFromStream() {
// todo
}
6.3 实现 subscribeBars 和 unsubscribeBars 方法,用来订阅和取消订阅某个 symbol 的实时数据
// datafeed.js
import { subscribeOnStream, unsubscribeFromStream } from './streaming.js';
const lastBarsCache = new Map();
// ...
export default {
// ...
subscribeBars: (
symbolInfo,
resolution,
onRealtimeCallback,
subscribeUID,
onResetCacheNeededCallback
) => {
console.log('[subscribeBars]: Method call with subscribeUID:', subscribeUID);
subscribeOnStream(
symbolInfo,
resolution,
onRealtimeCallback,
subscribeUID,
onResetCacheNeededCallback,
lastBarsCache.get(symbolInfo.full_name)
);
},
unsubscribeBars: (subscriberUID) => {
console.log('[unsubscribeBars]: Method call with subscriberUID:', subscriberUID);
unsubscribeFromStream(subscriberUID);
},
};
6.4 实现 unsubscribeFromStream 方法,取消订阅的方法实现
export function unsubscribeFromStream(subscriberUID) {
// 找到 subscriberUID 的订阅
for (const channelString of channelToSubscription.keys()) {
const subscriptionItem = channelToSubscription.get(channelString);
const handlerIndex = subscriptionItem.handlers
.findIndex(handler => handler.id === subscriberUID);
if (handlerIndex !== -1) {
// 删除它
subscriptionItem.handlers.splice(handlerIndex, 1);
if (subscriptionItem.handlers.length === 0) {
// 如果是最后一个处理程序,则取消订阅通道
console.log('[unsubscribeBars]: 从 streaming 取消订阅频道:', channelString);
socket.emit('SubRemove', { subs: [channelString] });
channelToSubscription.delete(channelString);
break;
}
}
}
}
6.5 实现 websocket.onmessage 方法,用来处理 socket 发送过来的数据
// streaming.js
// ...
socket.on('m', data => {
// 响应的数据看上去是这样的: 0~Bitfinex~BTC~USD~2~335394436~1548837377~0.36~3504.1~1261.4759999999999~1f
console.log('[socket] Message:', data);
const [
eventTypeStr,
exchange,
fromSymbol,
toSymbol,
,
,
tradeTimeStr,
,
tradePriceStr,
] = data.split('~');
if (parseInt(eventTypeStr) !== 0) {
// 跳过所有 non-TRADE 事件
return;
}
const tradePrice = parseFloat(tradePriceStr);
const tradeTime = parseInt(tradeTimeStr);
const channelString = `0~${exchange}~${fromSymbol}~${toSymbol}`;
const subscriptionItem = channelToSubscription.get(channelString);
if (subscriptionItem === undefined) {
return;
}
const lastDailyBar = subscriptionItem.lastDailyBar;
let bar = {
...lastDailyBar,
high: Math.max(lastDailyBar.high, tradePrice),
low: Math.min(lastDailyBar.low, tradePrice),
close: tradePrice,
};
console.log('[socket] 按价格更新最新的 bar', tradePrice);
subscriptionItem.lastDailyBar = bar;
// 给每一个订阅的 symbol 发送数据
subscriptionItem.handlers.forEach(handler => handler.callback(bar));
});
6.6 在运行项目之前,打开您的 datafeed.js 文件并调整您的 GetBars
方法以保存当前 symbol 的最后一个 bar 数据。如果我们有更准确的方法来检查新的 bar,或者我们有 bar streaming API,那么我们将不需要此服务。
//...
data.Data.forEach( ... );
if (firstDataRequest) {
lastBarsCache.set(symbolInfo.full_name, { ...bars[bars.length - 1] });
}
console.log(`[getBars]: returned ${bars.length} bar(s)`);
//...
6.7 CryptoCompare 提供 刻度的流数据,但不提供 bar。因此,让我们大致检查一下新交易是否与新的每日 bar 相关。请注意,对于生产版本,您可能需要在此处进行更全面的检查。您可以在 streaming.js 中调整代码。添加正确的功能:
function getNextDailyBarTime(barTime) {
const date = new Date(barTime * 1000);
date.setDate(date.getDate() + 1);
return date.getTime() / 1000;
}
6.8 调整 websocket.onmessage
socket.on('m', data => {
//...
const lastDailyBar = subscriptionItem.lastDailyBar;
const nextDailyBarTime = getNextDailyBarTime(lastDailyBar.time);
let bar;
if (tradeTime >= nextDailyBarTime) {
bar = {
time: nextDailyBarTime,
open: tradePrice,
high: tradePrice,
low: tradePrice,
close: tradePrice,
};
console.log('[socket] Generate new bar', bar);
} else {
bar = {
...lastDailyBar,
high: Math.max(lastDailyBar.high, tradePrice),
low: Math.min(lastDailyBar.low, tradePrice),
close: tradePrice,
};
console.log('[socket] Update the latest bar by price', tradePrice);
}
subscriptionItem.lastDailyBar = bar;
//...
});
最后一步,运行
---------------------------------
这里有详细的教程
github 地址 https://github.com/tradingview
边栏推荐
- 上传图片获取宽高
- Okaleido ecological core equity Oka, all in fusion mining mode
- MLX90640 红外热成像仪测温传感器模块开发笔记(六)
- [gossip] error loading psychopg2 module: no module named psychopg2
- Basics of data communication - basic knowledge of network
- Some descriptions of DS V2 push down in spark
- Learning about opencv (1)
- Review of database -- 1. Overview
- Jpg to EPS
- 微信公众号发布提醒(微信公众号模板消息接口)
猜你喜欢
Flask framework beginner-03-template
单元测试,到底什么是单元测试,为什么单测这么难写
【Halcon视觉】图像灰度变化
Basics of data communication - basic knowledge of network
[award-winning question] ask Judea pearl, the Turing prize winner and the father of Bayesian networks
[Halcon vision] software programming ideas
Beginner of flask framework-04-flask blueprint and code separation
30 minutes to thoroughly understand the synchronized lock upgrade process
AirTest
About automatic operation on Web pages
随机推荐
Learning about opencv (3)
Learning about opencv (2)
Listening freely, the next stop of online text traffic competition?
微信公众号发布提醒(微信公众号模板消息接口)
Use spiel expressions in custom annotations to dynamically obtain method parameters or execute methods
【Halcon视觉】极坐标变换
码云,正式支持 Pages 功能,可以部署静态页面
Okaleido ecological core equity Oka, all in fusion mining mode
RecyclerView最后一条显示不全或显示部分的问题解决
Learning about tensor (III)
Replay the snake game with C language (II) end
[Halcon vision] programming logic
PTA class a 1001
404页面和路由钩子
Flask框架初学-03-模板
Network related journals and conferences in CS
Study on the basis of opencv
What is wrong about the description of function templates (how to solve link format errors)
面试第二家公司的面试题及答案(二)
Uniapp common error [wxml file compilation error]./pages/home/home Wxml and using MySQL front provided by phpstudy to establish an independent MySQL database and a detailed tutorial for independent da