_ = '
"Pinefest #1 October 21-28, 2023"
-----------------------------------------------------------------------------------
------------------------------------------------------
| █ CHALLENGE |
| |
| Create three functions that will return the exact value where two data series
intersect: |
| |
| • crossValue (source1, source2) |
| • crossoverValue (source1, source2) |
| • crossunderValue(source1, source2) |
| |
| When a cross occurs, the functions must return the intersection`s value. When no
cross occurs, they must return na. |
| |
-----------------------------------------------------------------------------------
------------------------------------------------------ '
//@version=5
indicator( title = 'Intersection Value Functions'
, max_boxes_count = 500
, max_lines_count = 500
, max_labels_count = 500
, overlay = true
)
//---------------------------------------------------------------------------------
------------------------------------}
//Challenge Functions
//---------------------------------------------------------------------------------
------------------------------------{
// getSlopes()
//
//@function Get the slope of the lines connecting source1/source2 to
source1[1]/source2[1]
//@param source1 (float) source value 1
//@param source2 (float) source value 2
//@returns Slopes of the lines
getSlopes(float source1, float source2) =>
//Get slopes
m1 = [Link](source1)
m2 = [Link](source2)
//Output
[m1 , m2]
// commonScalingFactor()
//
//@function Common scaling factor of the lines connecting source1/source2 to
source1[1]/source2[1]
//@param source1 (float) source value 1
//@param source2 (float) source value 2
//@param m1 (float) slope of the line originating from source1
//@param m2 (float) slope of the line originating from source2
//@returns Common scaling factor
commonScalingFactor(float source1, float source2, float m1, float m2) =>
//Output
(source1 - source2) / (m1 - m2)
// crossValue()
//
//@function Finds intersection value of 2 lines/values if any cross occurs - First
function of challenge -> crossValue(source1, source2)
//@param source1 (float) source value 1
//@param source2 (float) source value 2
//@returns Intersection value
method crossValue(float source1, float source2)=>
float insct = na
//Slope calculations, called on each bar
[m1, m2] = getSlopes(source1, source2)
//Test for cross
if [Link](source1, source2)
//Find common scaling factor
sf = commonScalingFactor(source1, source2, m1, m2)
//Find intersection value
insct := source1 - sf * m1
// crossoverValue()
//
//@function Finds intersection value of 2 lines/values if crossover occurs - Second
function of challenge -> crossoverValue(source1, source2)
//@param source1 (float) source value 1
//@param source2 (float) source value 2
//@returns Intersection value
method crossoverValue(float source1, float source2)=>
float insct = na
//Slope calculations, called on each bar
[m1, m2] = getSlopes(source1, source2)
//Test for cross
if [Link](source1, source2)
//Find common scaling factor
sf = commonScalingFactor(source1, source2, m1, m2)
//Find intersection value
insct := source1 - sf * m1
// crossunderValue()
//
//@function Finds intersect of 2 lines/values if crossunder occurs - Third function
of challenge -> crossunderValue(source1, source2)
//@param source1 (float) source value 1
//@param source2 (float) source value 2
//@returns Intersection value
method crossunderValue(float source1, float source2) =>
float insct = na
//Slope calculations, called on each bar
[m1, m2] = getSlopes(source1, source2)
//Test for cross
if [Link](source1, source2)
//Find common scaling factor
sf = commonScalingFactor(source1, source2, m1, m2)
//Find intersection value
insct := source1 - sf * m1
//---------------------------------------------------------------------------------
------------------------------------}
//Usage - Code used to highlight the proposed functions usage.
//---------------------------------------------------------------------------------
------------------------------------{
//Settings
//-----------------------------------------------------------------------------{
//Sources selection
sourceA = [Link]('SMA', 'Source A'
, options = ['SMA', 'EMA', 'WMA', 'Hull', 'External A']
, inline = 'sourceA')
lenA = [Link](9, '', minval = 1
, inline = 'sourceA')
externalA = [Link](close, 'External A')
sourceB = [Link]('SMA', 'Source B'
, options = ['SMA', 'EMA', 'WMA', 'Hull', 'External B']
, inline = 'sourceB')
lenB = [Link](20, '', minval = 1
, inline = 'sourceB')
externalB = [Link](open, 'External B')
//Style
coCss = [Link](#2962ff, 'Crossover'
, inline = 'crossover')
coAreaCss = [Link]([Link](#2962ff, 90), ''
, inline = 'crossover')
cuCss = [Link](#ff5d00, 'Crossover'
, inline = 'crossunder')
cuAreaCss = [Link]([Link](#ff5d00, 90), ''
, inline = 'crossunder')
extend = input(true, 'Extend Intersections')
//SMA Intersection Matrix
showDash = input(true, 'Show Matrix'
, group = 'SMA Intersection Matrix')
minLen = [Link](10, 'SMA Length Range'
, minval = 1
, inline = 'lenrange'
, group = 'SMA Intersection Matrix')
maxLen = [Link](20, ''
, minval = 1
, inline = 'lenrange'
, group = 'SMA Intersection Matrix')
dashLoc = [Link]('Top Right', 'Location'
, options = ['Top Right', 'Bottom Right', 'Bottom Left']
, group = 'SMA Intersection Matrix')
textSize = [Link]('Small', 'Size'
, options = ['Tiny', 'Small', 'Normal']
, group = 'SMA Intersection Matrix')
//Magnifying Glass
magnify = input(true, 'Magnify'
, group = 'Magnifying Glass')
resolution = [Link](20, 'Resolution'
, minval = 2
, group = 'Magnifying Glass')
offset = [Link](10, 'Offset'
, minval = 2
, group = 'Magnifying Glass')
//----------------------------------------------------------------------------}
//Methods/Functions
//----------------------------------------------------------------------------{
//@function Return various supported moving averages outputs based on an input
string
//@param id (string) determine the function output, supported strings include
['SMA', 'EMA', 'WMA', 'Hull'], else an external value is returned
//@param len (simple int) moving average length if applicable
//@param external (float) external source
//@returns Chosen moving average output or "external" if "id" is not part of
supported options
method source(string id, simple int len, float external) =>
sma = [Link](close, len)
ema = [Link](close, len)
wma = [Link](close, len)
hma = [Link](close, len)
output = switch id
'SMA' => sma
'EMA' => ema
'WMA' => wma
'Hull' => hma
=> external
//@function Return the N * N intersection matrix from an array of values with size
N and the values in its previous instance
//@param array_series (array<float>) array of values, requires an array supporting
historical referencing
//@returns (matrix<float>) Intersection matrix showing intersection value between
all array entries
intersectionMatrix(array_series)=>
N = array_series.size()-1
ismt = [Link]<float>(N+1, N+1)
sfmt = [Link]<float>(N+1, N+1)
prev_array = array_series[1]
if not na(prev_array)
//Columns
for i = 0 to N
//Get source1 and previous source1 value
source1 = array_series.get(i)
prev1 = prev_array.get(i)
//source1 slope
m1 = source1 - prev1
//Rows
for j = i to N
//Na is column index = row index
if i == j
[Link](j, i, float(na))
else
//Get source2 and previous source2 value
source2 = array_series.get(j)
prev2 = prev_array.get(j)
//source2 slope
m2 = source2 - prev2
//Test for cross
if (source1 - source2) * (prev1 - prev2) < 0
//Find common scaling factor
sf = commonScalingFactor(source1, source2, m1, m2)
//Find intersection value
insct = source1 - sf * m1
//Set matrix intersection and scaling factor values
[Link](i, j, insct)
[Link](i, j, 1 - sf)
//Output
[ismt, sfmt]
//@function Draw graphical elements on the chart highlighting crossing events and
intersection value/area
//@param intersection_val (float) Intersection value between source1 and source2
//@param scaling_factor (float) Common scaling factor between two crossing lines
//@param max (float) area top
//@param min (float) area bottom
//@param crossover (bool) true if the lines are crossing over each other
//@param css (color) color of the line/label text
//@param css_area (color) color of the box area
//@returns [line, label, box] drawing elements
draw(intersection_val, scaling_factor, max, min, crossover, css, css_area)=>
n = bar_index
//Intersection level
lvl = [Link](
[Link].from_index(n-1, intersection_val)
, [Link].from_index(n, intersection_val)
, color = css)
//Intersection value label and display 1 - scaling factor when hovering over
label
lbl = [Link](
[Link].from_index(n, crossover ? min : max)
, color = color(na)
, textcolor = css
, text = [Link](math.round_to_mintick(intersection_val))
, style = crossover ? label.style_label_up : label.style_label_down
, size = [Link]
, tooltip = [Link](1 - scaling_factor, '#.##'))
//Highlight intersection area
bx = [Link](
[Link].from_index(n-1, max)
, [Link].from_index(n, min)
, na
, bgcolor = css_area)
[lvl, lbl, bx]
//@function Display an higher resolution representation of intersecting lines
//@param source1 (float) source value 1
//@param source2 (float) source value 2
//@param css1 (color) color of source 1 line
//@param css2 (color) color of source 2 line
//@param intersec_css (color) color of intersection line
//@param area_css (color) color of box area
zoomIn(source1, source2, css1, css2, intersec_css, area_css)=>
var source1_l = [Link](na, na, na, na, color = css1)
var source2_l = [Link](na, na, na, na, color = css2)
var intersec_l = [Link](na, na, na, na)
var cross_area = [Link](na, na, na, na, chart.fg_color)
n = bar_index
//Find intersection value on crosses
intersection_val = [Link](source2)
//Draw new elements on crossing event
if not na(intersection_val)
//Get common scaling factor
sf = commonScalingFactor(source1, source2, source1 - source1[1], source2 -
source2[1])
//Slopes run
dx1 = int(resolution * (1 - sf))
dx2 = int(resolution * sf)
//Offset
start = int(resolution * (1 - sf)) + offset
//Coordinates
l1y1 = source1 - (source1 - source1[1]) * dx1
l1y2 = source1 + (source1 - source1[1]) * dx2
l2y1 = source2 - (source2 - source2[1]) * dx1
l2y2 = source2 + (source2 - source2[1]) * dx2
//Set new lines coordinates
source1_l.set_xy1(n - dx1 + start, l1y1)
source1_l.set_xy2(n + dx2 + start, l1y2)
source2_l.set_xy1(n - dx1 + start, l2y1)
source2_l.set_xy2(n + dx2 + start, l2y2)
intersec_l.set_xy1(n - dx1 + start, intersection_val)
intersec_l.set_xy2(n + dx2 + start, intersection_val)
intersec_l.set_color(intersec_css)
//Area
cross_area.set_lefttop(n - dx1 + start, [Link](l1y1, l1y2, l2y1, l2y2))
cross_area.set_rightbottom(n + dx2 + start, [Link](l1y1, l1y2, l2y1,
l2y2))
cross_area.set_bgcolor(area_css)
else
//Update coordinates
x1 = source1_l.get_x1()
x2 = source1_l.get_x2()
source1_l.set_x1(x1 + 1) , source1_l.set_x2(x2 + 1)
source2_l.set_x1(x1 + 1) , source2_l.set_x2(x2 + 1)
intersec_l.set_x1(x1 + 1) , intersec_l.set_x2(x2 + 1)
cross_area.set_left(x1 + 1), cross_area.set_right(x2 + 1)
//----------------------------------------------------------------------------}
//Highlight crosses and intersection value
//----------------------------------------------------------------------------{
//Intersections drawing elements
var line intersection_lvl = na
var label intersection_lbl = na
var box intersection_box = na
n = bar_index
source1 = [Link](lenA, externalA)
source2 = [Link](lenB, externalB)
//Find intersection value on crosses
intersection_val = [Link](source2)
var l1 = [Link](na,na,na,na)
var l2 = [Link](na,na,na,na)
var l3 = [Link](na,na,na,na)
//Highlight intersection information
if not na(intersection_val)
//Get common scaling factor
sf = commonScalingFactor(source1, source2, source1 - source1[1], source2 -
source2[1])
//Draw elements
crossover = source1 > source2
max = [Link](source1, source2, source1[1], source2[1])
min = [Link](source1, source2, source1[1], source2[1])
[lvl_, lbl_, bx_] = draw(intersection_val, sf, max, min
, crossover
, crossover ? coCss : cuCss
, crossover ? coAreaCss : cuAreaCss)
intersection_lvl := lvl_
intersection_lbl := lbl_
intersection_box := bx_
else
//Extend
if extend
intersection_lvl.set_x2(n)
intersection_lbl.set_x(int([Link](n, intersection_lvl.get_x1())))
intersection_box.set_right(n)
//Zoom
if magnify
zoomIn(source1, source2, #089981, #f23645
, source1 > source2 ? coCss : cuCss
, source1 > source2 ? coAreaCss : cuAreaCss)
//-----------------------------------------------------------------------------}
//Highlight SMA intersection matrix
//-----------------------------------------------------------------------------{
var table_position = dashLoc == 'Bottom Left' ? position.bottom_left
: dashLoc == 'Top Right' ? position.top_right
: position.bottom_right
var table_size = textSize == 'Tiny' ? [Link]
: textSize == 'Small' ? [Link]
: [Link]
//Declare array of sma values
sma_array = [Link]<float>(0)
csum = [Link](close)
//Calculate SMA for periods from min_per to max_per
for i = minLen to maxLen
ma = (csum - csum[i]) / i
sma_array.push(ma)
//Get matrices
[ismt, sfmt] = intersectionMatrix(sma_array)
//Set SMA intersection matrix
if [Link] and showDash
cols = [Link]()
rows = [Link]()
//Table
tb = [Link](table_position, cols+2, rows+2
, bgcolor = #1e222d
, border_color = #373a46
, border_width = 1
, frame_color = #373a46
, frame_width = 1)
for i = 0 to rows-1
//SMA periods
[Link](0, i+1, [Link](minLen + i)
, text_color = [Link]
, text_size = table_size)
[Link](i+1, 0, [Link](minLen + i)
, text_color = [Link]
, text_size = table_size)
for j = 0 to cols-1
//Set intersection value
if not na([Link](i, j))
[Link](i+1, j+1, [Link](math.round_to_mintick([Link](i,
j)))
, text_color = [Link]
, text_size = table_size
, tooltip = [Link]([Link](i, j), '#.##'))
//Dashboard title
[Link](0, rows, 'SMA Intersection Matrix'
, text_color = [Link]
, text_size = table_size)
tb.merge_cells(0, rows, cols, rows)
//----------------------------------------------------------------------------}
//Plots
//----------------------------------------------------------------------------{
plot(source1, 'Source A', #089981)
plot(source2, 'Source B', #f23645)
//----------------------------------------------------------------------------}
//---------------------------------------------------------------------------------
------------------------------------}