When designing a strategy, it is often necessary to design a strategy chart to display, when writing strategies in JavaScript language, Python language, users who are unfamiliar with programming or the charting library used in the FMZ platform often struggle with code design for drawing on custom charts. So how can you draw a strategy chart with rich content by only a small amount of code?
This simple and powerful way of drawing can be seen in the Pine language, which is known for its rich drawing functions. If the drawing interface of the Pine language can be connected to the strategies of JavaScript and Python languages, it will greatly facilitate the drawing function of the developer's design strategy. Therefore, based on this demand, the FMZ platform has upgraded the custom drawing function and expanded the way to use the KLineChart
function to draw custom charts. You can refer to the API documentation at: https://www.fmz.com/api#klinechart
Let's start by writing a simple example using the JavaScript language to transition.
Simple example
/*backtest start: 2022-03-21 09:00:00 end: 2022-06-21 15:00:00 period: 30m basePeriod: 15m exchanges: [{"eid":"Binance","currency":"BTC_USDT"}] */ function main() { var c = KLineChart() while (true) { var bars = _C(exchange.GetRecords) for (var i = 0 ; i < bars.length ; i++) { var bar = bars[i] c.begin(bar) c.plot(bar.Volume, "Close") c.close() } Sleep(1000) } }
This example is very simple, just draw a K-line chart in the custom drawing area of the strategy, and draw a volume curve corresponding to each K-line BAR in the sub-chart position of the chart.
In the code, we use var c = KLineChart()
to create a chart object first, and then use its method to draw a chart. Then in the loop, we obtain the K-line data (array structure), and traverse the K-line array. It can be traversed by using a simple for loop as in the example, or it can be traversed in other ways.
The drawing operation start with the c.begin(bar)
function and end with the c.close()
function. The begin
and close
functions are all methods of the chart object c. Next, use the most commonly used drawing function plot
to draw the volume curve on each BAR.
A more complex example
Suppose we want to design a chart with the Bollinger indicator attached. And it also comes with a volume chart of each BAR, which can be designed like this:
/*backtest start: 2022-03-21 09:00:00 end: 2022-06-21 15:00:00 period: 1d basePeriod: 1h exchanges: [{"eid":"Binance","currency":"BTC_USDT"}] */ function main() { var c = KLineChart() // main strategy loop while(true) { // polling interval Sleep(500) // get K-line data let bars = exchange.GetRecords() if (!bars || bars.length <= 20) { continue } // calculate the Bollinger indicator var boll = TA.BOLL(bars) bars.forEach(function(bar, index) { c.begin(bar) // drawing operation c.plot(boll[0][index], "Boll_Up", {overlay: true}) // Draw on the main chart c.plot(boll[1][index], "Boll_Mid", {overlay: true}) // Draw on the main chart c.plot(boll[2][index], "Boll_Down", {overlay: true}) // Draw on the main chart c.plot(bar.Volume, "volume") // Draw on the sub-chart c.close() }) // Strategy trading logic // ... // .. } }
As we can see in the code, our backtest configuration information on the FMZ platform is:
/*backtest start: 2022-03-21 09:00:00 end: 2022-06-21 15:00:00 period: 1d basePeriod: 1h exchanges: [{"eid":"Binance","currency":"BTC_USDT"}] */
Such a configuration is to use the Binance spot exchange object backtesting test. The backtesting effect is shown as follows:
It can be seen that we use Pine's drawing method, which greatly simplifies the drawing code in the JavaScript language.
Write in indicators and other data to draw charts
The operations of obtaining K-line data and calculating indicators are common and simple in the strategy examples in the Platform Strategy Square and in the API documentation.
// obtain K-line data let bars = exchange.GetRecords() if (!bars || bars.length <= 20) { // If the acquisition of the K line fails, that is, if !bar is true, execute continue, ignore the following code, and re-execute the loop // If bars.length is less than or equal to 20, that is, the number of K-line BAR (bar) is less than 20, the indicator cannot be calculated, and continue is also executed continue } // Calculate the Bollinger indicator var boll = TA.BOLL(bars)
The Bollinger indicator calculation function TA.BOLL, if the Bollinger indicator parameters are not specified, the default parameters BOLL(20, 2)
are used. The Bollinger indicator has three lines, so the data returned by the TA.BOLL function is a two-dimensional array. boll[0], boll[1], and boll[2] are the three elements of the boll array, each of which represents a line and is an array.
- boll[0]: Bollinger Bands up line
- boll[1]: Bollinger Bands midline
- boll[2]: Bollinger Bands down line
Then we will see how to draw the volume of the K-line data, that is, the volume, and the calculated Bollinger indicator data on the chart.
We draw the K-line data Bar by Bar, so we need to traverse the entire K-line array, that is, traverse the bars array in the code. The forEach
method is used here to traverse, or the for loop can be used to traverse.
bars.forEach(function(bar, index) { c.begin(bar) // Drawing operations c.plot(boll[0][index], "Boll_Up", {overlay: true}) // {overlay: true} Parameter control, drawn on the main chart c.plot(boll[1][index], "Boll_Mid", {overlay: true}) // Draw on the main chart c.plot(boll[2][index], "Boll_Down", {overlay: true}) // Draw on the main chart c.plot(bar.Volume, "volume") // Draw on the sub-chart c.close() })
It should be noted that whenever you start a chart drawing operation on a Bar, you must make a c.begin(bar)
function call first. The begin
function is one of our chart object c method. At the end of the drawing operation, c.close()
needs to be called. Between the begin
function and the close
function is the drawing function that we call similar to the drawing method of the Pine language. Supports: barcolor
bgcolor
plot
fill
hline
plotarrow
plotshape
plotchar
plotcandle
signal
, you can draw lines, arrows, mark information, etc. The parameters of these functions are the same as the function parameters corresponding to the Pine language, and the drawing functions are also the same.
Add the drawing of trading signal arrows, markers, horizontal lines, etc.
Add some trading signal arrows, markers, horizontal lines to the above Bollinger Bands charting example.
/*backtest start: 2022-03-21 09:00:00 end: 2022-06-21 15:00:00 period: 1d basePeriod: 1h exchanges: [{"eid":"Binance","currency":"BTC_USDT"}] */ function main() { var c = KLineChart({overlay : true}) // Strategy main loop while(true) { // Polling interval Sleep(500) // Obtain K-line data let bars = exchange.GetRecords() if (!bars || bars.length <= 20) { continue } // Calculate Bollinger indicator var boll = TA.BOLL(bars) bars.forEach(function(bar, index) { c.begin(bar) // Drawing operations c.plot(boll[0][index], "Boll_Up", {overlay: true}) // Draw on the main chart c.plot(boll[1][index], "Boll_Mid", {overlay: true}) // Draw on the main chart c.plot(boll[2][index], "Boll_Down", {overlay: true}) // Draw on the main chart c.plot(bar.Volume, "volume", {overlay: false}) // Draw on the sub-chart c.hline(bar.Open, {overlay: true}) // Horizontal line c.plotarrow(bar.Close - bar.Open, {overlay: true}) // Arrow c.plotshape(bar.Close - bar.Open > 0, {style: 'square'}) // Draw square markers c.plotchar(bar.Close - bar.Open < 0, {char: 'X'}) // Draw the character X c.close() }) // Strategy trading logic // ... // .. } }
Chart configuration object
A structure can be declared to configure the chart style. For example, the following chartCfg variable represents the configuration information of a grid line.
var chartCfg = { grid: { show: true, // Grid horizontal line horizontal: { show: true, size: 2, color: '#FF0000', // Color of horizontal grid line // 'solid'|'dash' style: 'dash', // Type of line dashValue: [2, 2] }, // Grid vertical line vertical: { show: true, size: 2, color: '#32CD32', // 'solid'|'dash' style: 'solid', dashValue: [2, 2] } }, }
The chart configuration object is the data structure that sets some properties and appearance of the chart. For example, a grid line style configuration is used in the example. There are also many options that can be configured and modified, such as X-axis, Y-axis related settings, cursor line settings, alert message settings, technical indicator style settings, K-line BAR style settings, etc.
Of course, if you are not familiar with these, you can not pass in the chart configuration object when calling the KLineChart
function to create a chart object. Then the created chart is the default style. Use the API function KLineChart
function of the FMZ platform to create a chart object:
var c = KLineChart(chartCfg)
Test code for drawing grid lines:
/*backtest start: 2022-03-21 09:00:00 end: 2022-06-21 15:00:00 period: 1d basePeriod: 1h exchanges: [{"eid":"Binance","currency":"BTC_USDT"}] */ function main() { var chartCfg = { grid: { show: true, // Grid horizontal line horizontal: { show: true, size: 2, color: '#FF0000', // 'solid'|'dash' style: 'dash', dashValue: [2, 2] }, // Grid vertical line vertical: { show: true, size: 2, color: '#32CD32', // 'solid'|'dash' style: 'solid', dashValue: [2, 2] } }, } var c = KLineChart(chartCfg) // Strategy main loop while(true) { // Polling interval Sleep(500) // Obtain K-line data var bars = _C(exchange.GetRecords) bars.forEach(function(bar, index) { c.begin(bar) c.close() }) // Strategy trading logic // ... // .. } }
Example of chart configuration style
It can be used to refer to the chart configuration style settings.
{ // Gridlines grid: { show: true, // Grid horizontal line horizontal: { show: true, size: 1, color: '#393939', // 'solid'|'dash' style: 'dash', dashValue: [2, 2] }, // Grid vertical line vertical: { show: false, size: 1, color: '#393939', // 'solid'|'dash' style: 'dash', dashValue: [2, 2] } }, // Candlestick chart candle: { // The distance between the top and bottom of the candlestick chart, greater than 1 is the absolute value, greater than 0, and 1 is the proportion margin: { top: 0.2, bottom: 0.1 }, // Type of Candlestick Charts 'candle_solid'|'candle_stroke'|'candle_up_stroke'|'candle_down_stroke'|'ohlc'|'area' type: 'candle_solid', // Candle pillar bar: { upColor: '#26A69A', downColor: '#EF5350', noChangeColor: '#888888' }, // Area map area: { lineSize: 2, lineColor: '#2196F3', value: 'close', backgroundColor: [{ offset: 0, color: 'rgba(33, 150, 243, 0.01)' }, { offset: 1, color: 'rgba(33, 150, 243, 0.2)' }] }, priceMark: { show: true, // Highest price marker high: { show: true, color: '#D9D9D9', textMargin: 5, textSize: 10, textFamily: 'Helvetica Neue', textWeight: 'normal' }, // Lowest price marker low: { show: true, color: '#D9D9D9', textMargin: 5, textSize: 10, textFamily: 'Helvetica Neue', textWeight: 'normal', }, // Latest price marker last: { show: true, upColor: '#26A69A', downColor: '#EF5350', noChangeColor: '#888888', line: { show: true, // 'solid'|'dash' style: 'dash', dashValue: [4, 4], size: 1 }, text: { show: true, size: 12, paddingLeft: 2, paddingTop: 2, paddingRight: 2, paddingBottom: 2, color: '#FFFFFF', family: 'Helvetica Neue', weight: 'normal', borderRadius: 2 } } }, // Tips tooltip: { // 'always' | 'follow_cross' | 'none' showRule: 'always', // 'standard' | 'rect' showType: 'standard', labels: ['time', 'open', 'close', 'high', 'low', 'volume'], values: null, defaultValue: 'n/a', rect: { paddingLeft: 0, paddingRight: 0, paddingTop: 0, paddingBottom: 6, offsetLeft: 8, offsetTop: 8, offsetRight: 8, borderRadius: 4, borderSize: 1, borderColor: '#3f4254', backgroundColor: 'rgba(17, 17, 17, .3)' }, text: { size: 12, family: 'Helvetica Neue', weight: 'normal', color: '#D9D9D9', marginLeft: 8, marginTop: 6, marginRight: 8, marginBottom: 0 } } }, // Technical indicators technicalIndicator: { margin: { top: 0.2, bottom: 0.1 }, bar: { upColor: '#26A69A', downColor: '#EF5350', noChangeColor: '#888888' }, line: { size: 1, colors: ['#FF9600', '#9D65C9', '#2196F3', '#E11D74', '#01C5C4'] }, circle: { upColor: '#26A69A', downColor: '#EF5350', noChangeColor: '#888888' }, // Latest value marker lastValueMark: { show: false, text: { show: false, color: '#ffffff', size: 12, family: 'Helvetica Neue', weight: 'normal', paddingLeft: 3, paddingTop: 2, paddingRight: 3, paddingBottom: 2, borderRadius: 2 } }, // Tips tooltip: { // 'always' | 'follow_cross' | 'none' showRule: 'always', // 'standard' | 'rect' showType: 'standard', showName: true, showParams: true, defaultValue: 'n/a', text: { size: 12, family: 'Helvetica Neue', weight: 'normal', color: '#D9D9D9', marginTop: 6, marginRight: 8, marginBottom: 0, marginLeft: 8 } } }, // x-axis xAxis: { show: true, height: null, // x-axis line axisLine: { show: true, color: '#888888', size: 1 }, // x-axis split text tickText: { show: true, color: '#D9D9D9', family: 'Helvetica Neue', weight: 'normal', size: 12, paddingTop: 3, paddingBottom: 6 }, // x-axis split line tickLine: { show: true, size: 1, length: 3, color: '#888888' } }, // y-axis yAxis: { show: true, width: null, // 'left' | 'right' position: 'right', // 'normal' | 'percentage' | 'log' type: 'normal', inside: false, reverse: false, // y-axis line axisLine: { show: true, color: '#888888', size: 1 }, // y-axis split text tickText: { show: true, color: '#D9D9D9', family: 'Helvetica Neue', weight: 'normal', size: 12, paddingLeft: 3, paddingRight: 6 }, // y-axis split line tickLine: { show: true, size: 1, length: 3, color: '#888888' } }, // Split line between charts separator: { size: 1, color: '#888888', fill: true, activeBackgroundColor: 'rgba(230, 230, 230, .15)' }, // Crosshair crosshair: { show: true, // Horizontal line and text of crosshair horizontal: { show: true, line: { show: true, // 'solid'|'dash' style: 'dash', dashValue: [4, 2], size: 1, color: '#888888' }, text: { show: true, color: '#D9D9D9', size: 12, family: 'Helvetica Neue', weight: 'normal', paddingLeft: 2, paddingRight: 2, paddingTop: 2, paddingBottom: 2, borderSize: 1, borderColor: '#505050', borderRadius: 2, backgroundColor: '#505050' } }, // Vertical line and text of crosshair vertical: { show: true, line: { show: true, // 'solid'|'dash' style: 'dash', dashValue: [4, 2], size: 1, color: '#888888' }, text: { show: true, color: '#D9D9D9', size: 12, family: 'Helvetica Neue', weight: 'normal', paddingLeft: 2, paddingRight: 2, paddingTop: 2, paddingBottom: 2, borderSize: 1, borderColor: '#505050', borderRadius: 2, backgroundColor: '#505050' } } }, // Graph shape: { point: { backgroundColor: '#2196F3', borderColor: '#2196F3', borderSize: 1, radius: 4, activeBackgroundColor: '#2196F3', activeBorderColor: '#2196F3', activeBorderSize: 1, activeRadius: 6 }, line: { // 'solid'|'dash' style: 'solid' color: '#2196F3', size: 1, dashValue: [2, 2] }, polygon: { // 'stroke'|'fill' style: 'stroke', stroke: { // 'solid'|'dash' style: 'solid', size: 1, color: '#2196F3', dashValue: [2, 2] }, fill: { color: 'rgba(33, 150, 243, 0.1)' } }, arc: { // 'stroke'|'fill' style: 'stroke', stroke: { // 'solid'|'dash' style: 'solid', size: 1, color: '#2196F3', dashValue: [2, 2] }, fill: { color: '#2196F3' } }, text: { style: 'fill', color: '#2196F3', size: 12, family: 'Helvetica Neue', weight: 'normal', offset: [0, 0] } }, annotation: { // 'top' | 'bottom' | 'point' position: 'top', offset: [20, 0] symbol: { // 'diamond' | 'circle' | 'rect' | 'triangle' | 'custom' | 'none' type: 'diamond', size: 8, color: '#2196F3', activeSize: 10, activeColor: '#FF9600' } }, tag: { // 'top' | 'bottom' | 'point' position: 'point', offset: 0, line: { show: true, style: LineStyle.DASH, dashValue: [4, 2], size: 1, color: '#2196F3' }, text: { color: '#FFFFFF', backgroundColor: '#2196F3', size: 12, family: 'Helvetica Neue', weight: 'normal', paddingLeft: 2, paddingRight: 2, paddingTop: 2, paddingBottom: 2, borderRadius: 2, borderSize: 1, borderColor: '#2196F3' }, mark: { offset: 0, color: '#FFFFFF', backgroundColor: '#2196F3', size: 12, family: 'Helvetica Neue', weight: 'normal', paddingLeft: 2, paddingRight: 2, paddingTop: 2, paddingBottom: 2, borderRadius: 2, borderSize: 1, borderColor: '#2196F3' } } }
Doesn't this make strategy drawing design easier?