Examples:
var lineColor = na
n = if bar_index > 10 and bar_index <= 20
lineColor := color.green
else if bar_index > 20 and bar_index <= 30
lineColor := color.blue
else if bar_index > 30 and bar_index <= 40
lineColor := color.orange
else if bar_index > 40
lineColor := color.black
else
lineColor := color.red
plot(close, title="close", color=n, linewidth=5, overlay=true)
plotchar(true, title="bar_index", char=str.tostring(bar_index), location=location.abovebar, color=color.red, overlay=true)
Note: Expressions used for judging return Boolean values. Note that there can only be one else branch. All branching expressions are false and return na without an else branch.
x = if close > open
close
plot(x, title="x")
Since the expression after the if statement is false when the K-line BAR is a diagonal line, i.e. close < open, the local code block of if is not executed. This time, too, there is noelse branch, and the if statement returns na. x is assigned na. This point cannot be plotted on the graph, which we can also observe by backscaling the graph.
A switch statement is also a branch-structured statement designed to execute different paths depending on certain conditions.
The switch statement returns the same value as the if statement. Unlike the switch statements in other languages, when executing the switch structure, only one local block of its code is executed, so the break declaration is unnecessary (i.e. no keyword such as break is needed). 3. Each branch of the switch can write a local code block, the last line of which is the return value (which can be an element of a value). If no branch is executed, the local code block returns na. 4, Expressions in a switch structure can be written as strings, variables, expressions, or function calls. 5. Switch allows the specification of a return value that is used as the default when no other conditions in the structure are executed.
The switch is divided into two forms, and we'll look at examples one by one to understand how to use it.
1 with an expressionswitch
This is the case for the following:
// input.string: defval, title, options, tooltip
func = input.string("EMA", title="指标名称", tooltip="选择要使用的指标函数名称", options=["EMA", "SMA", "RMA", "WMA"])
// input.int: defval, title, options, tooltip
// param1 = input.int(10, title="周期参数")
fastPeriod = input.int(10, title="快线周期参数", options=[5, 10, 20])
slowPeriod = input.int(20, title="慢线周期参数", options=[20, 25, 30])
data = input(close, title="数据", tooltip="选择使用收盘价、开盘价、最高价...")
fastColor = color.red
slowColor = color.red
[fast, slow] = switch func
"EMA" =>
fastLine = ta.ema(data, fastPeriod)
slowLine = ta.ema(data, slowPeriod)
fastColor := color.red
slowColor := color.red
[fastLine, slowLine]
"SMA" =>
fastLine = ta.sma(data, fastPeriod)
slowLine = ta.sma(data, slowPeriod)
fastColor := color.green
slowColor := color.green
[fastLine, slowLine]
"RMA" =>
fastLine = ta.rma(data, fastPeriod)
slowLine = ta.rma(data, slowPeriod)
fastColor := color.blue
slowColor := color.blue
[fastLine, slowLine]
=>
runtime.error("error")
plot(fast, title="fast" + fastPeriod, color=fastColor, overlay=true)
plot(slow, title="slow" + slowPeriod, color=slowColor, overlay=true)
We learned about input functions earlier, and here we continue to learn about two functions that are similar to input:input.string
、input.int
The function ≠ ∞input.string
It is used to return strings.input.int
The function is used to return an integer value. In this example, a new value is added.options
The use of parametersoptions
Parameters can be passed to an array of selectable values.options=["EMA", "SMA", "RMA", "WMA"]
andoptions=[5, 10, 20]
(Note that one is a string type and one is a numeric type). The controls on the policy interface do not need to enter specific values, but instead become a drop-down box to select the options provided in the options parameter.
The value of the variable func is a string, and the variable func is used as an expression of the switch (which can be a variable, function call, expression) to determine which branch in the executed switch. If the variable func cannot match the expression on any branch in the switch (i.e. is equal), the default branch code block is executed, which is executed.runtime.error("error")
The function causes the policy to throw an exceptional stop.
In our test code above, after the last line of the default branch code block runtime.error in Switch, we did not add code like [na, na] to return the compatible value, which needs to be considered in the trading view if the type is inconsistent. But in FMZ, this type of compatibility code can be omitted because there is no strict type requirement.
strategy("test", overlay=true)
x = if close > open
close
else
"open"
plotchar(true, title="x", char=str.tostring(x), location=location.abovebar, color=color.red)
It does not return an error in FMZ, but in trading view.
2, without an expression.switch
Let's see.switch
Another way to use the word is to write it without expressions.
up = close > open // up = close < open
down = close < open
var upOfCount = 0
var downOfCount = 0
msgColor = switch
up =>
upOfCount += 1
color.green
down =>
downOfCount += 1
color.red
plotchar(up, title="up", char=str.tostring(upOfCount), location=location.abovebar, color=msgColor, overlay=true)
plotchar(down, title="down", char=str.tostring(downOfCount), location=location.belowbar, color=msgColor, overlay=true)
The test code example shows that the switch matches the execution of the branch to the true local code block. Generally speaking, the branch condition after the switch statement must be mutually exclusive. That is, the up and down in the example cannot be true at the same time. Since the switch can only execute one branch of the local code block, it is interesting to put this line in the code:up = close > open // up = close < open
Instead of the content in the comment, check back to observe the results. It will be found that the switch branch can only execute the first branch. In addition, it is necessary to take care not to write function calls in the branch of the switch, the function cannot be called on each BAR, which can cause some data computation problems (unless, for example, "with an expressionswitch
In the example, the execution branch is defined and will not be changed during the policy run.)
返回值 = for 计数 = 起始计数 to 最终计数 by 步长
语句 // 注释:语句里可以有break,continue
语句 // 注释:最后一条语句为返回值
The use of the for statement is very simple, the for loop can eventually return a single value ((or return multiple values, in the form of [a, b, c]); as in the pseudo-code above, the variable assigned to the "return value" position. The for statement is followed by a "count" variable to control the number of cycles, refer to other values, etc. The "count" variable is assigned the "initial count" before the start of the loop, and then passed according to the "growth" setting, and the loop stops when the number of "count" is greater than the "final variable".
For use in cyclingbreak
Keyword: when executedbreak
After the sentence, the cycle stops.
For use in cyclingcontinue
Keyword: when executedcontinue
After the sentence, the loop is ignored.continue
The following code, directly executes the next loop. The for statement returns the value returned when the last loop was executed. If no code is executed, it returns a blank value.
Here's a simple example:
ret = for i = 0 to 10 // 可以增加by关键字修改步长,暂时FMZ不支持 i = 10 to 0 这样的反向循环
// 可以增加条件设置,使用continue跳过,break跳出
runtime.log("i:", i)
i // 如果这行不写,就返回空值,因为没有可返回的变量
runtime.log("ret:", ret)
runtime.error("stop")
for ... in
There are two forms of statements, as shown by the following pseudo-codes:
返回值 = for 数组元素 in 数组
语句 // 注释:语句里可以有break,continue
语句 // 注释:最后一条语句为返回值
返回值 = for [索引变量, 索引变量对应的数组元素] in 数组
语句 // 注释:语句里可以有break,continue
语句 // 注释:最后一条语句为返回值
The main difference between the two forms can be seen in the content that follows after the for keyword, one is the use of a variable as a reference to the array element. One is the use of a structure that contains an index variable, an array element variable to reference. The other is the use of return value rules, rules such as break, continue and for loop consistency.
testArray = array.from(10, 20, 30, 40, 50, 60, 70, 80, 90, 100)
for ele in testArray // 修改成 [i, ele]的形式:for [i, ele] in testArray , runtime.log("ele:", ele, ", i:", i)
runtime.log("ele:", ele)
runtime.error("stop")
Use it when you need to use an index.for [i, ele] in testArray
I'm not sure what to say.
for circular applications
When some looping logic calculations can be performed using the built-in functions provided by the Pine language, the looping structure can be written directly, or the built-in function processing can be used. Here are two examples.
1, the computed mean
When designing circular structures:
length = 5
var a = array.new(length)
array.push(a, close)
if array.size(a) >= length
array.remove(a, 0)
sum = 0
for ele in a
sum += ele
avg = sum / length
plot(avg, title="avg", overlay=true)
The for loop is used in the example, and then the mean is calculated.
Calculate a straight line directly using the built-in function:
plot(ta.sma(close, length), title="ta.sma", overlay=true)
Direct use of built-in functionsta.sma
In the case of the calculation of equilateral indicators, it is obviously simpler to calculate the equilateral using the built-in function.
2 and sum
I'm not sure if this is a good idea, but I'm going to use the example above to illustrate.
When designing circular structures:
length = 5
var a = array.new(length)
array.push(a, close)
if array.size(a) >= length
array.remove(a, 0)
sum = 0
for ele in a
sum += ele
avg = sum / length
plot(avg, title="avg", overlay=true)
plot(ta.sma(close, length), title="ta.sma", overlay=true)
For calculating the sum of all elements of an array that can be handled with a loop, a built-in function can also be used.array.sum
I'm going to calculate.
Calculate summation directly using the built-in function:
length = 5
var a = array.new(length)
array.push(a, close)
if array.size(a) >= length
array.remove(a, 0)
plot(array.sum(a) / length, title="avg", overlay=true)
plot(ta.sma(close, length), title="ta.sma", overlay=true)
You can see that the calculated data is perfectly consistent on the graph using the plot diagram.
So if you can do all these things with built-in functions, why design a loop? 1, some operations for arrays, calculations. 2, look back in history, for example, to find out how many past highs are higher than the highs of the current BAR. Since the highs of the current BAR are known only on the BAR running the script, a loop is needed to go back in time and analyze the past BAR. 3, where the built-in function using the Pine language cannot complete the calculation of the past BAR.
while
The statement keeps the code of the looping part executing until the judgment condition in the while structure is false.
返回值 = while 判断条件
语句 // 注释:语句里可以有break,continue
语句 // 注释:最后一条语句为返回值
The other rules for while are similar to for loops, where the last line of the local code block of the loop is the return value, which can return multiple values. The break, continue statements can also be used in the loop.
I'm still demonstrating this with the example of a straight line:
length = 10
sma(data, length) =>
i = 0
sum = 0
while i < 10
sum += data[i]
i += 1
sum / length
plot(sma(close, length), title="sma", overlay=true)
plot(ta.sma(close, length), title="ta.sma", overlay=true)
It can be seen that while looping is also very simple to use, it is also possible to design some computational logic that cannot be replaced by built-in functions, such as computational multiplication:
counter = 5
fact = 1
ret = while counter > 0
fact := fact * counter
counter := counter - 1
fact
plot(ret, title="ret") // ret = 5 * 4 * 3 * 2 * 1
An array in Pine is a one-dimensional array; it is typically used to store a continuous set of data. An array in which individual data is stored is called an array element. The types of these elements can be: integer, floating point, string, color value, Boolean value. The Pine language on FMZ does not require a very strict type, and can even store both string and number in an array at the same time.[]
It needs to be used.array.get()
andarray.set()
The index sequence of the elements in the array is 0 for the index of the first element of the array, and 1 for the next element.
We're going to use a simple code to illustrate:
var a = array.from(0)
if bar_index == 0
runtime.log("当前BAR上的a值:", a, ", 上1根BAR上的a,即a[1]值:", a[1])
else if bar_index == 1
array.push(a, bar_index)
runtime.log("当前BAR上的a值:", a, ", 上1根BAR上的a,即a[1]值:", a[1])
else if bar_index == 2
array.push(a, bar_index)
runtime.log("当前BAR上的a值:", a, ", 上1根BAR上的a,即a[1]值:", a[1], ", 向前数2根BAR上的a,即a[2]值:", a[2])
else if bar_index == 3
array.push(a, bar_index)
runtime.log("当前BAR上的a值:", a, ", 上1根BAR上的a,即a[1]值:", a[1], ", 向前数2根BAR上的a,即a[2]值:", a[2], ", 向前数3根BAR上的a,即a[3]值:", a[3])
else if bar_index == 4
// 使用array.get 按索引获取元素,使用array.set按索引修改元素
runtime.log("数组修改前:", array.get(a, 0), array.get(a, 1), array.get(a, 2), array.get(a, 3))
array.set(a, 1, 999)
runtime.log("数组修改后:", array.get(a, 0), array.get(a, 1), array.get(a, 2), array.get(a, 3))
Usearray<int> a
、float[] b
Arrays that declare an array or declare only one variable can be assigned to an array, for example:
array<int> a = array.new(3, bar_index)
float[] b = array.new(3, close)
c = array.from("hello", "fmz", "!")
runtime.log("a:", a)
runtime.log("b:", b)
runtime.log("c:", c)
runtime.error("stop")
Initialization of array variables in general usearray.new
andarray.from
函数。Pine语言中还有很多和类型相关的与array.new类似的函数:array.new_int()
、array.new_bool()
、array.new_color()
、array.new_string()
And so on and so forth.
The var keyword also works with the declaration pattern of the array, where the array using the var keyword declaration is initialized only on the first BAR. We see this with an example:
var a = array.from(0)
b = array.from(0)
if bar_index == 1
array.push(a, bar_index)
array.push(b, bar_index)
else if bar_index == 2
array.push(a, bar_index)
array.push(b, bar_index)
else if barstate.islast
runtime.log("a:", a)
runtime.log("b:", b)
runtime.error("stop")
It can be seen that the changes in the array a are continuously determined and not rearranged. The array b is initialized at each BAR.barstate.islast
There is still only one element, the value 0′, when printing in real time.
Use array.get to retrieve an element that specifies an index position in an array, and use array.set to modify an element that specifies an index position in an array.
The first parameter of array.get is the array to be processed, and the second parameter is the specified index. The first parameter of array.set is the array to be processed, the second parameter is the specified index, and the third parameter is the element to be written.
To illustrate this, use the following simple example:
lookbackInput = input.int(100)
FILL_COLOR = color.green
var fillColors = array.new(5)
if barstate.isfirst
array.set(fillColors, 0, color.new(FILL_COLOR, 70))
array.set(fillColors, 1, color.new(FILL_COLOR, 75))
array.set(fillColors, 2, color.new(FILL_COLOR, 80))
array.set(fillColors, 3, color.new(FILL_COLOR, 85))
array.set(fillColors, 4, color.new(FILL_COLOR, 90))
lastHiBar = - ta.highestbars(high, lookbackInput)
fillNo = math.min(lastHiBar / (lookbackInput / 5), 4)
bgcolor(array.get(fillColors, int(fillNo)), overlay=true)
plot(lastHiBar, title="lastHiBar")
plot(fillNo, title="fillNo")
This example initializes the base color green, declares and initializes an array to store the color, and then assigns a different transparency to the color value (using the color.new function); calculates the color rank by calculating the current BAR distance to the maximum high value in 100 review cycles; the closer the distance to the maximum high value in the most recent 100 review cycles, the higher the rank, the deeper the corresponding color value (low transparency); many similar strategies use this way to represent the level of the price in N current review cycles.
How to traverse an array can be done using the for/for in/while statements we learned before.
a = array.from(1, 2, 3, 4, 5, 6)
for i = 0 to (array.size(a) == 0 ? na : array.size(a) - 1)
array.set(a, i, i)
runtime.log(a)
runtime.error("stop")
a = array.from(1, 2, 3, 4, 5, 6)
i = 0
while i < array.size(a)
array.set(a, i, i)
i += 1
runtime.log(a)
runtime.error("stop")
a = array.from(1, 2, 3, 4, 5, 6)
for [i, ele] in a
array.set(a, i, i)
runtime.log(a)
runtime.error("stop")
All three routing methods have the same results.
Arrays can be declared within the global scope of the script, or within the local scope of the function or if branch
For the use of elements in an array, the following method is equivalent, we can see from the following example that two sets of lines are plotted on the graph, two lines in each group, and the two line values in each group are exactly the same.
a = array.new_float(1)
array.set(a, 0, close)
closeA1 = array.get(a, 0)[1]
closeB1 = close[1]
plot(closeA1, "closeA1", color.red, 6)
plot(closeB1, "closeB1", color.black, 2)
ma1 = ta.sma(array.get(a, 0), 20)
ma2 = ta.sma(close, 20)
plot(ma1, "ma1", color.aqua, 6)
plot(ma2, "ma2", color.black, 2)
1, the addition operation of the array:
array.unshift()
、array.insert()
、array.push()
。
2, the deletion function of the array:
array.remove()
、array.shift()
、array.pop()
、array.clear()
。
We use the following example to test the addition, subtraction and operation functions of these arrays.
a = array.from("A", "B", "C")
ret = array.unshift(a, "X")
runtime.log("数组a:", a, ", ret:", ret)
ret := array.insert(a, 1, "Y")
runtime.log("数组a:", a, ", ret:", ret)
ret := array.push(a, "D")
runtime.log("数组a:", a, ", ret:", ret)
ret := array.remove(a, 2)
runtime.log("数组a:", a, ", ret:", ret)
ret := array.shift(a)
runtime.log("数组a:", a, ", ret:", ret)
ret := array.pop(a)
runtime.log("数组a:", a, ", ret:", ret)
ret := array.clear(a)
runtime.log("数组a:", a, ", ret:", ret)
runtime.error("stop")
Adding and removing applications: Arrays as queues
Using arrays, and some addition and subtraction functions, we can construct a "queue" data structure. A queue can be used to calculate the moving average of a tick price, and some students may ask: Why do we construct a queue structure?
A queue is a structure commonly used in programming, characterized by:
The first element to enter the queue, the first to exit the queue.
This ensures that the data in the queue is up-to-date and that the length of the queue does not inflate indefinitely ("Infinite inflation code can only be written at noon, because it will cause problems early or late").
In the following example, we use a queue structure to record the price per tick, calculate the moving average price at the tick level, and then compare it to the moving average observed at the 1 minute K line level.
strategy("test", overlay=true)
varip a = array.new_float(0)
var length = 10
if not barstate.ishistory
array.push(a, close)
if array.size(a) > length
array.shift(a)
sum = 0.0
for [index, ele] in a
sum += ele
avgPrice = array.size(a) == length ? sum / length : na
plot(avgPrice, title="avgPrice")
plot(ta.sma(close, length), title="ta.sma")
Note that when declaring an array a we specify the declaration pattern using the keywordvarip
Thus, each price change is recorded in a matrix.
Calculate the related functions:
array.avg()
Find the average of all the elements in the array.array.min()
The smallest element in the array.array.max()
The largest element in the array,array.stdev()
The standard deviation of all the elements in the array.array.sum()
The sum of all the elements in the functional group.
Functions related to operation:array.concat()
Combine or connect two arrays.array.copy()
This is a simple example of a duplicate array.array.join
Connects all the elements in the array to a single string.array.sort()
Ordering in ascending or descending order.array.reverse()
The inverse matrix.array.slice()
The matrix is sliced.array.includes()
Judging the elements.array.indexof()
Returns the index in which the value of the input parameter first appeared. If the value cannot be found, it returns −1.array.lastindexof()
Find the last value to appear.
Test examples of arithmetic-computing related functions:
a = array.from(3, 2, 1, 4, 5, 6, 7, 8, 9)
runtime.log("数组a的算数平均:", array.avg(a))
runtime.log("数组a中的最小元素:", array.min(a))
runtime.log("数组a中的最大元素:", array.max(a))
runtime.log("数组a中的标准差:", array.stdev(a))
runtime.log("数组a的所有元素总和:", array.sum(a))
runtime.error("stop")
These are the most commonly used arithmetic computation functions.
Examples of operations on related functions:
a = array.from(1, 2, 3, 4, 5, 6)
b = array.from(11, 2, 13, 4, 15, 6)
runtime.log("数组a:", a, ", 数组b:", b)
runtime.log("数组a,数组b连接在一起:", array.concat(a, b))
c = array.copy(b)
runtime.log("复制一个数组b,赋值给变量c,变量c:", c)
runtime.log("使用array.join处理数组c,给每个元素中间增加符号+,连接所有元素结果为字符串:", array.join(c, "+"))
runtime.log("排序数组b,按从小到大顺序,使用参数order.ascending:", array.sort(b, order.ascending)) // array.sort函数修改原数组
runtime.log("排序数组b,按从大到小顺序,使用参数order.descending:", array.sort(b, order.descending)) // array.sort函数修改原数组
runtime.log("数组a:", a, ", 数组b:", b)
array.reverse(a) // 此函数修改原数组
runtime.log("反转数组a中的所有元素顺序,反转之后数组a为:", a)
runtime.log("截取数组a,索引0 ~ 索引3,遵循左闭右开区间规则:", array.slice(a, 0, 3))
runtime.log("在数组b中搜索元素11:", array.includes(b, 11))
runtime.log("在数组a中搜索元素100:", array.includes(a, 100))
runtime.log("将数组a和数组b连接,搜索其中第一次出现元素2的索引位置:", array.indexof(array.concat(a, b), 2), " , 参考观察 array.concat(a, b):", array.concat(a, b))
runtime.log("将数组a和数组b连接,搜索其中最后一次出现元素6的索引位置:", array.lastindexof(array.concat(a, b), 6), " , 参考观察 array.concat(a, b):", array.concat(a, b))
runtime.error("stop")
The Pine language can design custom functions, and in general, the custom functions of the Pine language have the following rules:
1, all functions are defined within the scope of the script.
2, does not allow functions to call themselves in their own code (recursive).
3, in principle all PINE languages have built-in drawing functionsbarcolor()、 fill()、 hline()、plot()、 plotbar()、 plotcandle()
) cannot be called within a custom function.
4, the function can be written as single-line, multi-line. The return value of the last statement is the current function's return value, which can be returned in array form.
In previous tutorials we have also used custom functions several times, for example designing a custom function as a single line:
barIsUp() => close > open
This function returns whether the current BAR is a sunbeam.
Designed as a multi-line custom function:
sma(data, length) =>
i = 0
sum = 0
while i < 10
sum += data[i]
i += 1
sum / length
plot(sma(close, length), title="sma", overlay=true)
plot(ta.sma(close, length), title="ta.sma", overlay=true)
A function that we implemented ourselves using a custom function to compute a SMA mean line.
Also, an example of a custom function with two variables can be returned:
twoEMA(data, fastPeriod, slowPeriod) =>
fast = ta.ema(data, fastPeriod)
slow = ta.ema(data, slowPeriod)
[fast, slow]
[ema10, ema20] = twoEMA(close, 10, 20)
plot(ema10, title="ema10", overlay=true)
plot(ema20, title="ema20", overlay=true)
A function can calculate a fast line, a slow line, two EMA even-line indicators.
The built-in functions can be convenientlyFMZ PINE Script documentation is provided.I'm not sure what you mean.
The Pine language has built-in functions:
1, the string processing functionstr.
The series.
2, the color-value processing functioncolor.
The series.
3, the parameter input functioninput.
The series.
4, the indicator computation functionta.
The series.
5, the graph functionplot.
The series.
6 is an arithmetic processing functionarray.
The series.
7 Transaction-related functionsstrategy.
The series.
8 Mathematical operations and related functionsmath.
The series.
9, other functions ((time processing, non-plot series drawing functions,request.
This is a list of functions that can be used to define a function (series functions, type processing functions, etc.).
strategy.
Serial functions are functions that we use frequently in our design strategies, and they are closely related to the execution of transaction operations when the strategy is specifically executed.
1、strategy.entry
strategy.entry
A function is a subordinate function that is more important when we write a policy, and it is more important for several parameters:id
, direction
, qty
, when
And so on and so forth.
Parameters are:
id
: can be understood to refer to a name for a trading position. The id can refer to the cancellation, modification of orders, or placement.direction
: if the order direction is to do more (buy) this parameter is passedstrategy.long
This built-in variable is passed if you want to empty (sell).strategy.short
This variable.qty
: Specify the order quantity, if this parameter is not passed, the default order quantity is used.when
: execution condition, which can be specified to control whether the current subroutine operation is triggered or not.limit
: specify the order limit price.stop
The price of the product is very low.strategy.entry
The specific execution details of the function are givenstrategy
The parameters set when the function is called can also be controlled by"Pine language exchange library template parameters"More details on the transaction controls, set controls, and template parameters of the Pine language transaction library can be found in the documentation at the link.
Here's a little bit about the key points.strategy
In the function,pyramiding
、default_qty_value
Parameters. Use the following code to test:
/*backtest
start: 2022-07-03 00:00:00
end: 2022-07-09 00:00:00
period: 1d
basePeriod: 1h
exchanges: [{"eid":"Binance","currency":"BTC_USDT"}]
*/
strategy(title = "open long example", pyramiding = 3, default_qty_value=0.1, overlay=true)
ema10 = ta.ema(close, 10)
findOrderIdx(idx) =>
if strategy.opentrades == 0
false
else
ret = false
for i = 0 to strategy.opentrades - 1
if strategy.opentrades.entry_id(i) == idx
ret := true
break
ret
if not findOrderIdx("long1")
strategy.entry("long1", strategy.long)
if not findOrderIdx("long2")
strategy.entry("long2", strategy.long, 0.2, when = close > ema10)
if not findOrderIdx("long3")
strategy.entry("long3", strategy.long, 0.2, limit = low[1])
strategy.entry("long3", strategy.long, 0.3, limit = low[1])
if not findOrderIdx("long4")
strategy.entry("long4", strategy.long, 0.2)
plot(ema10, title="ema10", color=color.red)
The beginning of the code/*backtest ... */
The package part is set for retesting, to record information such as the time of retesting at the time, for easy debugging, not policy code.
In the code:strategy(title = "open long example", pyramiding = 3, default_qty_value=0.1, overlay=true)
When we specifypyramiding
When the parameter is 3, we set the same direction to trade up to three times. So four times in the example.strategy.entry
One of the following operations was not executed.default_qty_value
The parameter is 0.1, so the ID is going to be 1 tonne this time.strategy.entry
The sub-order operation's sub-order quantity is set to 0.1 by default.strategy.entry
We're going to call the function.direction
equal tostrategy.long
In addition to the above, there are also other benefits of using the internet to make money.
Note the code.strategy.entry("long3", ...
The following operation is called twice, for the same ID: strategy.entry
The following operation was not completed, second callstrategy.entry
The function is used to modify the order for this ID (the data displayed during retesting can also show that the order quantity under this limit order was modified to 0.3). Another case, for example, if the first ID is for the order for the long 3 tons, continue to use it according to this ordered ID.strategy.entry
If the function places an order, then the order positions will be accumulated on the ID long 3s.
2、strategy.close
strategy.close
The function is used to specify the ID of the entry holding position of the placement. The main parameters are:id
,when
,qty
,qty_percent
。
Parameters are:
id
We use the same ID for the login:strategy.entry
The ID specified at the time of opening of the entry-submission function.when
This is the first time I have seen this.qty
The number of balances.qty_percent
This is the first time that the company has been able to do so.To familiarize yourself with the details of how this function is used, let's look at an example:
In the code/*backtest ... */
是FMZ.COM国际站回测时的配置信息,可以删掉,设置自己需要测试的市场、品种、时间范围等信息。
/*backtest
start: 2022-07-03 00:00:00
end: 2022-07-09 00:00:00
period: 1d
basePeriod: 1h
exchanges: [{"eid":"Binance","currency":"BTC_USDT"}]
*/
strategy("close Demo", pyramiding=3)
var enableStop = false
if enableStop
runtime.error("stop")
strategy.entry("long1", strategy.long, 0.2)
if strategy.opentrades >= 3
strategy.close("long1") // 多个入场订单,不指定qty参数,全部平仓
// strategy.close() // 不指定id参数,会平掉当前的持仓
// strategy.close("long2") // 如果指定一个不存在的id则什么都不操作
// strategy.close("long1", qty=0.15) // 指定qty参数平仓
// strategy.close("long1", qty_percent=50) // qty_percent设置50即为平掉long1标识仓位的50%持仓
// strategy.close("long1", qty_percent=80, when=close<open) // 指定when参数,修改为close>open就不触发了
enableStop := true
The test strategy demonstrates that multiple entries are started three times in a row, each with an entry ID of 1strategy.close
Different results are returned when different parameters of the function are set to par.strategy.close
This function has no parameters to specify the price of the order to be settled, and is mainly used to settle immediately at the current market price.
3、strategy.close_all
strategy.close_all
The function is used to flatten out all current holdings, since the Pine script can only flatten out in one direction, i.e. if there is a signal trigger opposite to the current holdings, the current holdings will be flattened and the position will be triggered according to the signal.strategy.close_all
When called, it will flatten all holdings in the current direction.strategy.close_all
The main parameters of the function are:when
。
Parameters are:
when
This is the first time I have seen this.Let's use an example to see:
/*backtest
start: 2022-07-03 00:00:00
end: 2022-07-09 00:00:00
period: 1d
basePeriod: 1h
exchanges: [{"eid":"Binance","currency":"BTC_USDT"}]
*/
strategy("closeAll Demo")
var enableStop = false
if enableStop
runtime.error("stop")
strategy.entry("long", strategy.long, 0.2, when=strategy.position_size==0 and close>open)
strategy.entry("short", strategy.short, 0.3, when=strategy.position_size>0 and close<open)
if strategy.position_size < 0
strategy.close_all()
enableStop := true
When the test code starts, the hold is 0 (i.e.strategy.position_size==0
True), so when the condition set by the when parameter is satisfied, the ID is executed only as a long string.strategy.entry
Input function. After holding multiple positionsstrategy.position_size
If the ID is greater than 0, then the entry function for the shortened hash is likely to be executed. Since the current holds a multi-head position, this empty reverse signal that occurs at this time will cause the multi-head hold to be flattened and then reverse open.strategy.position_size < 0
When holding the empty position, the current hold direction is flattened; and markenableStop := true
◦ Stop the policy from running so that the logs can be viewed.
You can findstrategy.close_all
This function has no parameters to specify the price of the order to be settled, and is mainly used to settle immediately at the current market price.
4、strategy.exit
strategy.exit
The function is used for the placement operation of the entry holdings, which is different from the functionstrategy.close
andstrategy.close_all
The function is immediately settled at the current market price.strategy.exit
The function will plan the placement according to the parameter settings.
Parameters are:
id
: Order ID for the current list of settlement conditions.from_entry
: Used to specify the login ID of the transaction to be settled.qty
The number of balances.qty_percent
: percentage of equity, range: 0 ~ 100.profit
The following is a list of some of the most common types of fraud:loss
Stop-loss target, expressed in points.limit
This is the first time that the project has been approved by the European Commission.stop
Stop loss target, price specified.when
This is the first time I have seen this.Use a testing strategy to understand the use of parameters.
/*backtest
start: 2022-07-03 00:00:00
end: 2022-07-09 00:00:00
period: 1d
basePeriod: 1h
exchanges: [{"eid":"Binance","currency":"BTC_USDT"}]
args: [["RunMode",1,358374],["ZPrecision",0,358374]]
*/
strategy("strategy.exit Demo", pyramiding=3)
varip isExit = false
findOrderIdx(idx) =>
ret = -1
if strategy.opentrades == 0
ret
else
for i = 0 to strategy.opentrades - 1
if strategy.opentrades.entry_id(i) == idx
ret := i
break
ret
strategy.entry("long1", strategy.long, 0.1, limit=1, when=findOrderIdx("long1") < 0)
strategy.entry("long2", strategy.long, 0.2, when=findOrderIdx("long2") < 0)
strategy.entry("long3", strategy.long, 0.3, when=findOrderIdx("long3") < 0)
if not isExit and strategy.opentrades > 0
// strategy.exit("exitAll") // 如果仅仅指定一个id参数,则该退场订单无效,参数profit, limit, loss, stop等出场条件也至少需要设置一个,否则也无效
strategy.exit("exit1", "long1", profit=50) // 由于long1入场订单没有成交,因此ID为exit1的出场订单也处于暂待状态,直到对应的入场订单成交才会放置exit1
strategy.exit("exit2", "long2", qty=0.1, profit=100) // 指定参数qty,平掉ID为long2的持仓中0.1个持仓
strategy.exit("exit3", "long3", qty_percent=50, limit=strategy.opentrades.entry_price(findOrderIdx("long3")) + 1000) // 指定参数qty_percent,平掉ID为long3的持仓中50%的持仓
isExit := true
if bar_index == 0
runtime.log("每点价格为:", syminfo.mintick) // 每点价格和Pine语言模板参数上「定价货币精度」参数设置有关
Using a real-time price model retest test, this test strategy starts by performing three entry operations.strategy.entry
function), the long1 function is deliberately setlimit
Parameter, the order price is 1 which makes it impossible to transact. Then test the condition exit functionstrategy.exit
⇒ Stop-loss operations are used by point number, stop-loss operations by price, stop-loss operations by percentage, and stop-loss operations by number of positions.strategy.exit
The function also has more complex tracking stop loss parameters:trail_price
、trail_points
、trail_offset
You can also try learning how to use it in this example.
5、strategy.cancel
strategy.cancel
Functions used to cancel/deactivate all pending commands; these functionsstrategy.order
, strategy.entry
, strategy.exit
The main parameters of this function are:id
、when
。
Parameters are:
id
The following is a link to the post:when
This is the first time I have seen this.This function is well understood to be used to cancel uncompleted login commands.
/*backtest
start: 2022-07-03 00:00:00
end: 2022-07-09 00:00:00
period: 1d
basePeriod: 1h
exchanges: [{"eid":"Binance","currency":"BTC_USDT"}]
*/
strategy("strategy.cancel Demo", pyramiding=3)
var isStop = false
if isStop
runtime.error("stop")
strategy.entry("long1", strategy.long, 0.1, limit=1)
strategy.entry("long2", strategy.long, 0.2, limit=2)
strategy.entry("long3", strategy.long, 0.3, limit=3)
if not barstate.ishistory and close < open
strategy.cancel("long1")
strategy.cancel("long2")
strategy.cancel("long3")
isStop := true
6、strategy.cancel_all
strategy.cancel_all
Functions andstrategy.cancel
Functions similar to ⇒ cancel/deactivate all preload commands ⇒ can be specifiedwhen
Parameters are.
Parameters are:
when
This is the first time I have seen this./*backtest
start: 2022-07-03 00:00:00
end: 2022-07-09 00:00:00
period: 1d
basePeriod: 1h
exchanges: [{"eid":"Binance","currency":"BTC_USDT"}]
*/
strategy("strategy.cancel Demo", pyramiding=3)
var isStop = false
if isStop
runtime.error("stop")
strategy.entry("long1", strategy.long, 0.1, limit=1)
strategy.entry("long2", strategy.long, 0.2, limit=2)
strategy.entry("long3", strategy.long, 0.3, limit=3)
if not barstate.ishistory and close < open
strategy.cancel_all()
isStop := true
7、strategy.order
strategy.order
Functions, parameters, and so on are almost identical to functions.strategy.entry
Consistency is the difference betweenstrategy.order
The function is notstrategy
of the functionpyramiding
Parameter setting affects, there is no limit on the number of times it is used.
Parameters are:
id
: can be understood to refer to a name for a trading position. The id can refer to the cancellation, modification of orders, or placement.direction
: if the order direction is to do more (buy) this parameter is passedstrategy.long
This built-in variable is passed if you want to empty (sell).strategy.short
This variable.qty
: Specify the order quantity, if this parameter is not passed, the default order quantity is used.when
: execution condition, which can be specified to control whether the current subroutine operation is triggered or not.limit
: specify the order limit price.stop
The price of the product is very low.We usestrategy.order
There's no limit to this property, combined withstrategy.exit
Conditional exit function ─ construct a script similar to a grid transaction ─ example is very simple, just for learning:
/*backtest
start: 2021-03-01 00:00:00
end: 2022-08-30 00:00:00
period: 1d
basePeriod: 1h
exchanges: [{"eid":"Binance","currency":"ETH_USDT"}]
args: [["ZPrecision",0,358374]]
*/
varip beginPrice = -1
if not barstate.ishistory
if beginPrice == -1 or (math.abs(close - beginPrice) > 1000 and strategy.opentrades == 0)
beginPrice := close
for i = 0 to 20
strategy.order("buy"+i, strategy.long, 0.01, limit=beginPrice-i*200, when=(beginPrice-i*200)<close)
strategy.exit("coverBuy"+i, "buy"+i, qty=0.01, profit=200)
strategy.order("sell"+i, strategy.short, 0.01, limit=beginPrice+i*200, when=(beginPrice+i*200)>close)
strategy.exit("coverSell"+i, "sell"+i, qty=0.01, profit=200)
The strategy examples in this tutorial are for teaching strategies and guiding strategy design ideas only and do not make any trading guidelines or suggestions.
strategy("supertrend", overlay=true)
[supertrend, direction] = ta.supertrend(input(5, "factor"), input.int(10, "atrPeriod"))
plot(direction < 0 ? supertrend : na, "Up direction", color = color.green, style=plot.style_linebr)
plot(direction > 0 ? supertrend : na, "Down direction", color = color.red, style=plot.style_linebr)
if direction < 0
if supertrend > supertrend[2]
strategy.entry("entry long", strategy.long)
else if strategy.position_size < 0
strategy.close_all()
else if direction > 0
if supertrend < supertrend[3]
strategy.entry("entry short", strategy.short)
else if strategy.position_size > 0
strategy.close_all()
It is very simple to write a trend strategy using the Pine language, here we design a simple trend tracking strategy with a super trend indicator. Let's analyze the source code of this strategy together.
The first thing to do is to start using the strategy code.strategy
The function does some simple settings:strategy("supertrend", overlay=true)
, just set a policy header for the supertrend header.overlay
The parameter istrue
The first thing we do when we design a Pine strategy or learn a Pine strategy script is to look at the strategy interface parameter design, and we look at the source code for the "Supertrending Indicator Strategy", which contains some of the things we learned in previous courses.input
Function
[supertrend, direction] = ta.supertrend(input(5, “factor”), input.int(10, “atrPeriod”))
input
Function calls are used directly asta.supertrend
The parameters of the indicator function are used to calculate the supertrend indicator. Among them:
The function defaults to two parameter controls in the Pine language policy interface, as follows:
So you can see that the default on the control isinput
Functions andinput
The series function ((here isinput.int
The first parameter of the strategy can be set in the policy interface using these two functions.ta.supertrend
The parameter of the function. The supertrend indicator function calculates a price datasupertrend
and one-way data.direction
And then use it.plot
Function graphs, note that when graphing, the direction of the graph is drawn according to the supertrend indicator, and only the current direction is drawn; whendirection
The current market trend is upward at -1.direction
The current market is trending downwards at 1 o'clock. So we can see that the current market is trending downwards.plot
Judging when drawing a function graphdirection
Greater than, less than 0 ∞
The followingif ... else if
Logic is the judgment of the transaction signal, when the expressiondirection < 0
Real-time indicates that the current market is in an uptrend stage, at which point if the price data in the super trend indicator is not available, the market will be in a downtrend.supertrend
The price of the supertrend indicator above the previous 2 BARs (i.e.supertrend[2],还记得历史操作符引用某个变量历史数据吧
Remember, if there is a current holding, then a reverse downward single function call will first flatten the previous holding and then open the position according to the current trading direction.supertrend > supertrend[2]
The conditions have not been met, as long as it is at this time.strategy.position_size < 0
This is the same as holding the empty position.strategy.close_all()
Function execution, performing all of the balances.
direction > 0
It is the same when you are in the downtrend phase, if you have multiple holdings and all of them are even, then the conditions are met.supertrend < supertrend[3]
I'm trying to figure out why I'm setting it to "Do Not Track".[3]
What about the price data of the super trend indicator on the first third of the BAR? This may be the intention of the strategists, after all, some markets, such as the contract trading market, have a slightly higher risk of doing nothing than doing more.
For theta.supertrend
The indicator, is it something that some of your classmates are interested in, how do you tell if the current trend is up or down?
In fact, this indicator can also be implemented in the form of a custom function in the Pine language:
pine_supertrend(factor, atrPeriod) =>
src = hl2
atr = ta.atr(atrPeriod)
upperBand = src + factor * atr
lowerBand = src - factor * atr
prevLowerBand = nz(lowerBand[1])
prevUpperBand = nz(upperBand[1])
lowerBand := lowerBand > prevLowerBand or close[1] < prevLowerBand ? lowerBand : prevLowerBand
upperBand := upperBand < prevUpperBand or close[1] > prevUpperBand ? upperBand : prevUpperBand
int direction = na
float superTrend = na
prevSuperTrend = superTrend[1]
if na(atr[1])
direction := 1
else if prevSuperTrend == prevUpperBand
direction := close > upperBand ? -1 : 1
else
direction := close < lowerBand ? 1 : -1
superTrend := direction == -1 ? lowerBand : upperBand
[superTrend, direction]
This is a custom function with built-in functions.ta.supertrend
The same algorithm, of course, and the same indicator data are calculated.
From this custom function algorithm we can see that the built-in supertrend indicator computation used by Pine ishl2
The built-in variable ((highest price, lowest price plus then divided by 2, i.e. the average of the lowest price of the highest price) then calculates the ATR indicator ((wavelength) of a given cycle according to the parameter atrPeriod. Then using hl2 and ATR construct the uptrack, downtrack.
Updates based on the three-letter expression in the codelowerBand
andupperBand
。
lowerBand := lowerBand > prevLowerBand or close[1] < prevLowerBand ? lowerBand : prevLowerBand
upperBand := upperBand < prevUpperBand or close[1] > prevUpperBand ? upperBand : prevUpperBand
lowerBand: downtrend line, used to determine whether there is a change in the uptrend. upperBand: uptrend line, used to determine whether there is a change in the downtrend. both lowerBand and upperBand are always being calculated, only in this custom function the current trend direction is finally determined.
else if prevSuperTrend == prevUpperBand
direction := close > upperBand ? -1 : 1
else
direction := close < lowerBand ? 1 : -1
So if you're trying to figure out if the price value of the supertrend on the last bar isprevUpperBand
This is the uptrend, which is the current downtrend.close
more thanupperBand
The price breaks out, thinking that the trend is changing and converting to an uptrend.direction
The direction variable is set to −1 (uptrend); otherwise it is still set to 1 (downtrend); so you will see it in the supertrend strategy.if direction < 0
When the signal conditions are triggered, do more.direction > 0
When the signal conditions are triggered, do nothing.
superTrend := direction == -1 ? lowerBand : upperBand
[superTrend, direction]
Finally, the selected direction returns the price data and directional data of the specific supertrend indicator.
/*backtest
start: 2021-03-01 00:00:00
end: 2022-09-08 00:00:00
period: 1h
basePeriod: 15m
exchanges: [{"eid":"Binance","currency":"ETH_USDT"}]
args: [["v_input_1",4374],["v_input_2",3],["v_input_3",300],["ZPrecision",0,358374]]
*/
varip balance = input(50000, "balance")
varip stocks = input(0, "stocks")
maxDiffValue = input(1000, "maxDiffValue")
if balance - close * stocks > maxDiffValue and not barstate.ishistory
// more balance , open long
tradeAmount = (balance - close * stocks) / 2 / close
strategy.order("long", strategy.long, tradeAmount)
balance := balance - tradeAmount * close
stocks := stocks + tradeAmount
runtime.log("balance:", balance, ", stocks:", stocks, ", tradeAmount:", tradeAmount)
else if close * stocks - balance > maxDiffValue and not barstate.ishistory
// more stocks , open short
tradeAmount = (close * stocks - balance) / 2 / close
strategy.order("short", strategy.short, tradeAmount)
balance := balance + tradeAmount * close
stocks := stocks - tradeAmount
runtime.log("balance:", balance, ", stocks:", stocks, ", tradeAmount:", tradeAmount)
plot(balance, title="balance value(quoteCurrency)", color=color.red)
plot(stocks*close, title="stocks value(quoteCurrency)", color=color.blue)
Let's continue to learn some of the strategies design paradigms in the Pine language, and this time we'll look at a dynamic balancing strategy.BaseCurrency
The amount of (trade varieties) andQuoteCurrency
The amount is always balanced. If the relative price of an asset increases, the value of the asset held in the account increases, the asset is sold. If the relative price of an asset decreases, the value of the asset held in the account decreases, the asset is bought. This is the so-called dynamic balancing strategy.
The disadvantages of this strategy are that, as shown in the retrospective chart, the strategy is more likely to float during the uptrend (or downtrend) phase.
Let's take a look at the strategic code design:
We used a simplified design to simulate a strategy in which a user would be able to use a computer to run a program.balance
(i.e. the number of QuotCurrency assets) andstocks
We don't read the actual number of assets in the account, we just use the simulated amount to calculate the appropriate buy and sell. Then the key parameters that affect the grid that pulls out this dynamic balancing strategy are:maxDiffValue
This parameter is the criterion for balancing judgments.BaseCurrency
andQuoteCurrency
The deviation is overmaxDiffValue
It's time to rebalance, sell high-priced assets, buy low-priced assets, and rebalance assets.
The triggering of the strategic trading signal must be meaningful only in the real-time BAR stage, so the strategic trading conditions are set in the if judgment.not barstate.ishistory
The price of the product has been calculated on the basis of current prices.balance
The value is exceeded.stocks
Buy when the value is; sell when the value is; update after the transaction statement is executedbalance
andstocks
The next time the balance is triggered.
The above policy retest information contains the price of the variety at the time the policy retest started, which is 1458, so I set the parameter intentionally.balance
Set the parameter to 4374 ((1458*3))stocks
For example: 3. Keep the asset in balance when it starts.
In the previous lesson, we learnedstrategy.exit
We don't have an example of a position exit function, where the tracking stop-loss stop-loss functionality is explained.strategy.exit
The tracking stop-loss and stop-loss function is used to optimize a supertrending strategy.
Let's look at it first.strategy.exit
The tracking stop-loss stop-loss parameter of the function:
1、trail_price
Parameter: triggers placement of this logical behavior in the tracking stop loss and stop loss position (specified by price)