Tradingview 使用教程_tradingview
2021-11-29 10:31:26
more 
1801

进入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&#61;&#34;text/javascript&#34;
            src&#61;&#34;charting_library_clonned_data/charting_library/charting_library.js&#34;>
        </script>

        <!-- Custom datafeed module. -->
        <script type&#61;&#34;module&#34; src&#61;&#34;src/main.js&#34;></script>
    </head>
    <body style&#61;&#34;margin:0px;&#34;>
        <div id&#61;&#34;tv_chart_container&#34;>
            <!-- This div will contain the Charting Library widget. -->
        </div>
    </body>
</html>

3. 添加一个 src 目录main.js

// 这里引入 datafeed 这个文件后边我们再说
import Datafeed from './datafeed.js';

window.tvWidget &#61; 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) &#61;> {
        console.log('[onReady]: Method call');
    },
    searchSymbols: (userInput, exchange, symbolType, onResultReadyCallback) &#61;> {
        console.log('[searchSymbols]: Method call');
    },
    resolveSymbol: (symbolName, onSymbolResolvedCallback, onResolveErrorCallback) &#61;> {
        console.log('[resolveSymbol]: Method call', symbolName);
    },
    getBars: (symbolInfo, resolution, from, to, onHistoryCallback, onErrorCallback, firstDataRequest) &#61;> {
        console.log('[getBars]: Method call', symbolInfo);
    },
    subscribeBars: (symbolInfo, resolution, onRealtimeCallback, subscribeUID, onResetCacheNeededCallback) &#61;> {
        console.log('[subscribeBars]: Method call with subscribeUID:', subscribeUID);
    },
    unsubscribeBars: (subscriberUID) &#61;> {
        console.log('[unsubscribeBars]: Method call with subscriberUID:', subscriberUID);
    },
};

5. 实现这些方法

5.1 datafeed.js 添加一个 configurationData 对象用来配置一些参数

const configurationData &#61; {
    supported_resolutions: ['1D', '1W', '1M'],
    exchanges: [
        {
            value: 'Bitfinex',
            name: 'Bitfinex',
            desc: 'Bitfinex',
        },
        {
            // 如果用户选择该交易所searchSymbols 方法中的 exchange 参数
            value: 'Kraken', // 交易所名称
            name: 'Kraken', // 过滤器名称
            desc: 'Kraken bitcoin exchange', // 过滤器弹出窗口中显示的完整交易所名称
        },
    ],
    symbols_types: [
        {
            // 如果用户选择这个symbolsearchSymbols 方法中的 symbolType 参数
            name: 'crypto',
            value: 'crypto',
        },
        // ...
    ],
};

5.2 实现 onReady 方法图表库使用此方法来获取 datafeed 的配置例如支持的间隔resolutions交易所exchange等。这是调用数据馈送的第一个方法。我们的 datafeed 会将此配置返回到图表库。注意该回调必须异步调用

export default {
    onReady: (callback) &#61;> {
        console.log('[onReady]: Method call');
        setTimeout(() &#61;> 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 &#61; await fetch(&#96;https://min-api.cryptocompare.com/${path}&#96;);
        return response.json();
    } catch(error) {
        throw new Error(&#96;CryptoCompare request error: ${error.status}&#96;);
    }
}

// 从对币生成 symbol id
export function generateSymbol(exchange, fromSymbol, toSymbol) {
    const short &#61; &#96;${fromSymbol}/${toSymbol}&#96;;
    return {
        short,
        full: &#96;${exchange}:${short}&#96;,
    };
}

5.3 在 datafeed.js 文件里写一个 getAllSymbols 方法用来获取所有的 symbol

import { makeApiRequest, generateSymbol } from './helpers.js';
// ...
async function getAllSymbols() {
    const data &#61; await makeApiRequest('data/v3/all/exchanges');
    let allSymbols &#61; [];

    for (const exchange of configurationData.exchanges) {
        const pairs &#61; data.Data[exchange.value].pairs;

        for (const leftPairPart of Object.keys(pairs)) {
            const symbols &#61; pairs[leftPairPart].map(rightPairPart &#61;> {
                const symbol &#61; generateSymbol(exchange.value, leftPairPart, rightPairPart);
                return {
                    symbol: symbol.short,
                    full_name: symbol.full,
                    description: symbol.short,
                    exchange: exchange.value,
                    type: 'crypto',
                };
            });
            allSymbols &#61; [...allSymbols, ...symbols];
        }
    }
    return allSymbols;
}

5.4 实现 resolveSymbol 方法此方法来检索有关特定交易品种的信息交易所价格比例完整交易品种等。

export default {
    // ...
    resolveSymbol: async (
        symbolName,
        onSymbolResolvedCallback,
        onResolveErrorCallback
    ) &#61;> {
        console.log('[resolveSymbol]: 方法被调用了', symbolName);
        const symbols &#61; await getAllSymbols();
        const symbolItem &#61; symbols.find(({ full_name }) &#61;> full_name &#61;&#61;&#61; symbolName);
        if (!symbolItem) {
            console.log('[resolveSymbol]: 不支持的 symbol', symbolName);
            onResolveErrorCallback('暂不支持该 symbol');
            return;
        }
        const symbolInfo &#61; {
            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 &#61; fullSymbol.match(/^(\w&#43;):(\w&#43;)\/(\w&#43;)$/);
    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) &#61;> {
        console.log('[getBars]: 方法被调用了', symbolInfo, resolution, from, to);
        const parsedSymbol &#61; parseFullSymbol(symbolInfo.full_name);
        const urlParameters &#61; {
            e: parsedSymbol.exchange,
            fsym: parsedSymbol.fromSymbol,
            tsym: parsedSymbol.toSymbol,
            toTs: to,
            limit: 2000,
        };
        const query &#61; Object.keys(urlParameters)
            .map(name &#61;> &#96;${name}&#61;${encodeURIComponent(urlParameters[name])}&#96;)
                .join('&');
        try {
            const data &#61; await makeApiRequest(&#96;data/histoday?${query}&#96;);
            if (data.Response && data.Response &#61;&#61;&#61; 'Error' || data.Data.length &#61;&#61;&#61; 0) {
                // 如果在请求的时间段内没有数据则设置“noData”。
                onHistoryCallback([], { noData: true });
                return;
            }
            let bars &#61; [];
            data.Data.forEach(bar &#61;> {
                if (bar.time >&#61; from && bar.time < to) {
                    bars &#61; [...bars, {
                        time: bar.time * 1000,
                        low: bar.low,
                        high: bar.high,
                        open: bar.open,
                        close: bar.close,
                    }];
                }
            });
            console.log(&#96;[getBars]: 返回了 ${bars.length} 个 bars&#96;);
            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
    ) &#61;> {
        console.log('[searchSymbols]: 方法被调用');
        const symbols &#61; await getAllSymbols();
        const newSymbols &#61; symbols.filter(symbol &#61;> {
        const isExchangeValid &#61; exchange &#61;&#61;&#61; '' || symbol.exchange &#61;&#61;&#61; exchange;
        const isFullSymbolContainsInput &#61; symbol.full_name
            .toLowerCase()
            .indexOf(userInput.toLowerCase()) !&#61;&#61; -1;
            return isExchangeValid && isFullSymbolContainsInput;
        });
        onResultReadyCallback(newSymbols);
    },
...
}

现在我们已经可以搜索符号并显示历史数据接下来我们需要显示实时的交易数据

6. Streaming 实现在本节中我们将通过WebSocket实现实时更新。

6.1 在 index.html 中添加 socket.io用来创建 websocket 连接

<!DOCTYPE HTML>
<html>
    <head>
        <!-- ... -->
        <script src&#61;&#34;https://cdnjs.cloudflare.com/ajax/libs/socket.io/1.7.2/socket.io.js&#34;></script>
        <!-- ... -->
    </body>
</html>

6.2 创建 streaming.js 用来连接 websocket

// streaming.js
const socket &#61; io('wss://streamer.cryptocompare.com');

socket.on('connect', () &#61;> {
    console.log('[socket] Connected');
});

socket.on('disconnect', (reason) &#61;> {
    console.log('[socket] Disconnected:', reason);
});

socket.on('error', (error) &#61;> {
    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 &#61; new Map();
// ...
export default {
    // ...
    subscribeBars: (
        symbolInfo,
        resolution,
        onRealtimeCallback,
        subscribeUID,
        onResetCacheNeededCallback
    ) &#61;> {
        console.log('[subscribeBars]: Method call with subscribeUID:', subscribeUID);
        subscribeOnStream(
            symbolInfo,
            resolution,
            onRealtimeCallback,
            subscribeUID,
            onResetCacheNeededCallback,
            lastBarsCache.get(symbolInfo.full_name)
        );
    },

    unsubscribeBars: (subscriberUID) &#61;> {
        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 &#61; channelToSubscription.get(channelString);
        const handlerIndex &#61; subscriptionItem.handlers
            .findIndex(handler &#61;> handler.id &#61;&#61;&#61; subscriberUID);

        if (handlerIndex !&#61;&#61; -1) {
            // 删除它
            subscriptionItem.handlers.splice(handlerIndex, 1);

            if (subscriptionItem.handlers.length &#61;&#61;&#61; 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 &#61;> {
    // 响应的数据看上去是这样的: 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,
    ] &#61; data.split('~');

    if (parseInt(eventTypeStr) !&#61;&#61; 0) {
        // 跳过所有 non-TRADE 事件
        return;
    }
    const tradePrice &#61; parseFloat(tradePriceStr);
    const tradeTime &#61; parseInt(tradeTimeStr);
    const channelString &#61; &#96;0~${exchange}~${fromSymbol}~${toSymbol}&#96;;
    const subscriptionItem &#61; channelToSubscription.get(channelString);
    if (subscriptionItem &#61;&#61;&#61; undefined) {
        return;
    }
    const lastDailyBar &#61; subscriptionItem.lastDailyBar;
    let bar &#61; {
        ...lastDailyBar,
        high: Math.max(lastDailyBar.high, tradePrice),
        low: Math.min(lastDailyBar.low, tradePrice),
        close: tradePrice,
    };
    console.log('[socket] 按价格更新最新的 bar', tradePrice);
    subscriptionItem.lastDailyBar &#61; bar;

    // 给每一个订阅的 symbol 发送数据
    subscriptionItem.handlers.forEach(handler &#61;> 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(&#96;[getBars]: returned ${bars.length} bar(s)&#96;);
//...

6.7 CryptoCompare 提供 刻度的流数据但不提供 bar。因此让我们大致检查一下新交易是否与新的每日 bar 相关。请注意对于生产版本您可能需要在此处进行更全面的检查。您可以在 streaming.js 中调整代码。添加正确的功能

function getNextDailyBarTime(barTime) {
    const date &#61; new Date(barTime * 1000);
    date.setDate(date.getDate() &#43; 1);
    return date.getTime() / 1000;
}

6.8 调整 websocket.onmessage

socket.on('m', data &#61;> {
    //...
    const lastDailyBar &#61; subscriptionItem.lastDailyBar;
    const nextDailyBarTime &#61; getNextDailyBarTime(lastDailyBar.time);

    let bar;
    if (tradeTime >&#61; nextDailyBarTime) {
        bar &#61; {
            time: nextDailyBarTime,
            open: tradePrice,
            high: tradePrice,
            low: tradePrice,
            close: tradePrice,
        };
        console.log('[socket] Generate new bar', bar);
    } else {
        bar &#61; {
            ...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 &#61; bar;
    //...
});

最后一步运行

---------------------------------

这里有详细的教程

github 地址 https://github.com/tradingview

中文文档  https://zlq4863947.gitbook.io/tradingview/

 

 

 

Statement:
The content of this article does not represent the views of fxgecko website. The content is for reference only and does not constitute investment suggestions. Investment is risky, so you should be careful in your choice! If it involves content, copyright and other issues, please contact us and we will make adjustments at the first time!

Related News

您正在访问的是FxGecko网站。 FxGecko互联网及其移动端产品是中国香港特别行政区成立的Hitorank Co.,LIMITED旗下运营和管理的一款面向全球发行的企业资讯査询工具。

您的IP为 中国大陆地区,抱歉的通知您,不能为您提供查询服务,还请谅解。请遵守当地地法律。