OPEN-SOURCE SCRIPT

Stock Booster 2.0

// This Pine Script™ code is subject to the terms of the Mozilla Public License 2.0 at mozilla.org/MPL/2.0/
// © fluxchart

//version=5
const bool DEBUG = false
const int maxDistanceToLastBar = 5000
const int labelCooldown = 8
const int KDELimit = 300

indicator("Stock Booster", overlay = true, max_labels_count = 500)

rsiLengthInput = input.int(14, minval = 1, title="RSI Length", group="RSI Settings")
rsiSourceInput = input.source(close, "Source", group="RSI Settings")

highPivotLen = input.int(21, "High Pivot Length", minval = 1, group = "Pivots", display = display.none)
lowPivotLen = input.int(21, "Low Pivot Length", minval = 1, group = "Pivots", display = display.none)
realPivotLabels = DEBUG ? input.bool(false, "[DBG] Real Pivot Labels", group = "Pivots") : false
kdePivotLabels = DEBUG ? input.bool(false, "[DBG] KDE Pivot Labels", group = "Pivots") : false

activationThresholdStr = input.string("Medium", "Activation Threshold", options = ["High", "Medium", "Low"], group = "KDE", tooltip = "Determines the amount of arrows shown. Higher options will result in more arrows being rendered.")
string KDEKernel = input.string("Gaussian", "Kernel", options=['Uniform', 'Gaussian', 'Sigmoid'], group = "KDE", tooltip = "The kernel function for KDE calculation. Gaussian is a commonly used kernel and is based on normal distribution.")
float KDEBandwidth = input.float(2.71828, "Bandwidth", group = "KDE", tooltip = "This setting sets the smoothness of the KDE function output.")
int KDEStep = input.int(100, "Nº Bins", minval = 1, group = "KDE", tooltip = "The number of elements the KDE Probability array will have. Higher settings will result in greater precision.")
activationThreshold = DEBUG ? input.float(0.25, "[DBG] Activation Threshold", group = "KDE") : 0.25
if not DEBUG
activationThreshold := (activationThresholdStr == "High" ? 0.4 : activationThresholdStr == "Medium" ? 0.25 : 0.15)
probMode = DEBUG ? input.string("Sum", '[DBG] Probability Mode', options = ["Sum", "Nearest"], group = "KDE") : "Sum"
minPadding = DEBUG ? input.bool(false, '[DBG] KDE Min Padding', group = "KDE") : false

tableEnabled = input.bool(true, "Dashboard", group = "Dashboard", display = display.none)
tableLocation = input.string("Top Right", "Position", options = ["Top Right", "Top Center", "Right Center", "Middle Center", "Left Center", "Bottom Center"], group = "Dashboard", display = display.none)
screenerColor = input.color(#1B1F2B, 'Background', group = 'Dashboard', display = display.none)
frameColor = input.color(color.rgb(255, 255, 255), 'Frame', group = 'Dashboard', display = display.none)
borderColor = input.color(color.rgb(255, 255, 255), 'Border', group = 'Dashboard', display = display.none)
textColorDB = input.color(color.white, 'Text', group = 'Dashboard', display = display.none)
fillBackgrounds = input.bool(true, "Fill Backgrounds", group = "Dashboard", display = display.none)

bearishColor = input.color(#f23646, "High Pivots", group = "Style", inline = "col", display = display.none)
neutralColor = input.color(color.gray, "Neutral", group = "Style", inline = "col", display = display.none)
bullishColor = input.color(#089981, "Low Pivots", group = "Style", inline = "col", display = display.none)
textColor = input.color(color.white, 'Text', group = 'Style', inline = "col", display = display.none)
RSILabelsEnabled = input.bool(true, "RSI Labels", group = "Style")
KDELabelsEnabled = input.bool(true, "KDE Labels", group = "Style")

rsi = ta.rsi(rsiSourceInput, rsiLengthInput)

getPosition (positionText) =>
if positionText == "Top Right"
position.top_right
else if positionText == "Top Center"
position.top_center
else if positionText == "Right Center"
position.middle_right
else if positionText == "Left Center"
position.middle_left
else if positionText == "Bottom Center"
position.bottom_center
else if positionText == "Middle Center"
position.middle_center

//#region KDE
gaussian (float distance, float bandwidth = 1.0) => 1.0 / math.sqrt(2.0 * math.pi) * math.pow(math.e, -0.5 * math.pow(distance / bandwidth, 2.0))
uniform (float distance, float bandwidth = 1.0) => (math.abs(distance) > bandwidth) ? 0.0 : 0.5
sigmoid (float distance, float bandwidth = 1.0) => 2.0 / math.pi * (1.0 / (math.pow(math.e, (distance / bandwidth)) + math.pow(math.e, -(distance / bandwidth))))

kde (array<float> arr, string kernel, float bandwidth, int steps) =>
arrSize = arr.size()
arrRange = arr.range()
arrMin = arr.min() - (minPadding ? (arrRange / 2.0) : 0)
stepCount = arrRange / steps

densityRange = array.new<float>(steps * 2)
for i = 0 to (steps * 2) - 1
densityRange.set(i, arrMin + i * stepCount)

xArr = array.new<float>()
yArr = array.new<float>()
for i = 0 to densityRange.size() - 1
float temp = 0
for j = 0 to arr.size() - 1
switch KDEKernel
"Gaussian" => temp += gaussian(densityRange.get(i) - arr.get(j), 1.0 / bandwidth)
"Uniform" => temp += uniform(densityRange.get(i) - arr.get(j), 1.0 / bandwidth)
"Sigmoid" => temp += sigmoid(densityRange.get(i) - arr.get(j), 1.0 / bandwidth)

xArr.push(densityRange.get(i))
yArr.push(1.0 / arrSize * temp)
[xArr, yArr]
//#endregion

//#region Pivots

prefixSum (array<float> arr, int l, int r) =>
arr.get(r) - (l == 0 ? 0 : arr.get(l - 1))

float MidKDEHigh = na
float MidKDELow = na

var array<float> KDEHighX = na
var array<float> KDEHighY = na
var array<float> KDEHighYSum = array.new<float>()

var array<float> KDELowX = na
var array<float> KDELowY = na
var array<float> KDELowYSum = array.new<float>()

highPivot = ta.pivothigh(highPivotLen, highPivotLen)
lowPivot = ta.pivotlow(lowPivotLen, lowPivotLen)

var highPivotRSIs = array.new<float>()
var lowPivotRSIs = array.new<float>()

if not na(highPivot)
if highPivotRSIs.size() > KDELimit
highPivotRSIs.remove(0)
highPivotRSIs.push(rsi[highPivotLen])

[KDEHighX1, KDEHighY1] = kde(highPivotRSIs, KDEKernel, KDEBandwidth, KDEStep)
KDEHighX := KDEHighX1
KDEHighY := KDEHighY1

KDEHighYSum.clear()
temp = 0.0
for i = 0 to KDEHighY.size() - 1
temp += KDEHighY.get(i)
KDEHighYSum.push(temp)

MidKDEHigh := array.get(KDEHighX, array.indexof(KDEHighY, array.max(KDEHighY)))

if not na(lowPivot)
if lowPivotRSIs.size() > KDELimit
lowPivotRSIs.remove(0)
lowPivotRSIs.push(rsi[lowPivotLen])

[KDELowX1, KDELowY1] = kde(lowPivotRSIs, KDEKernel, KDEBandwidth, KDEStep)
KDELowX := KDELowX1
KDELowY := KDELowY1

KDELowYSum.clear()
temp = 0.0
for i = 0 to KDELowY.size() - 1
temp += KDELowY.get(i)
KDELowYSum.push(temp)

MidKDELow := array.get(KDELowX, array.indexof(KDELowY, array.max(KDELowY)))
//#endregion

//#region KDE Optimization
f_lin_interpolate(float x0, float x1, float y0, float y1, float x) =>
y0 + (x - x0) * (y1 - y0) / (x1 - x0)

float lowProb = na
float maxLowProb = na
float highProb = na
float maxHighProb = na

if last_bar_index - maxDistanceToLastBar < bar_index
if highPivotRSIs.size() > 0
highXIndexL = array.binary_search_leftmost(KDEHighX, rsi)
highXIndexR = math.min(array.binary_search_rightmost(KDEHighX, rsi), KDEHighX.size() - 1)
nearestIndex = (math.abs(rsi - KDEHighX.get(highXIndexL)) < math.abs(rsi - KDEHighX.get(highXIndexR))) ? highXIndexL : highXIndexR
if probMode == "Nearest"
highProb := KDEHighY.get(nearestIndex)
maxHighProb := array.max(KDEHighY)
else if probMode == "Sum"
highProb := prefixSum(KDEHighYSum, 0, nearestIndex)

if lowPivotRSIs.size() > 0
lowXIndexL = array.binary_search_leftmost(KDELowX, rsi)
lowXIndexR = math.min(array.binary_search_rightmost(KDELowX, rsi), KDELowX.size() - 1)
nearestIndex = (math.abs(rsi - KDELowX.get(lowXIndexL)) < math.abs(rsi - KDELowX.get(lowXIndexR))) ? lowXIndexL : lowXIndexR
if probMode == "Nearest"
lowProb := KDELowY.get(nearestIndex)
maxLowProb := array.max(KDELowY)
else if probMode == "Sum"
lowProb := prefixSum(KDELowYSum, nearestIndex, KDELowYSum.size() - 1)

if DEBUG and barstate.islastconfirmedhistory
for i = 0 to KDELowX.size() - 1
curX = KDELowX.get(i)
curY = KDELowY.get(i)
log.info(str.tostring(curX) + " = " + str.tostring(curY))
log.info("High Y Sum " + str.tostring(KDEHighY.sum()))

diffToHighKDE = math.abs(rsi - MidKDEHigh)
diffToLowKDE = math.abs(rsi - MidKDELow)
//#endregion

//#region Draw Pivots
color curColor = na
if (not na(KDELowY)) and (not na(KDEHighY))
if probMode == "Nearest"
if math.abs(lowProb - maxLowProb) < activationThreshold / 50.0
curColor := bullishColor
if math.abs(highProb - maxHighProb) < activationThreshold / 50.0
curColor := bearishColor
else if probMode == "Sum"
if lowProb > KDELowY.sum() * (1.0 - activationThreshold)
curColor := bullishColor
else if highProb > KDEHighY.sum() * (1.0 - activationThreshold)
curColor := bearishColor
//barcolor(curColor)

atr = ta.atr(50)

plotarrow(curColor == bullishColor and barstate.isconfirmed ? 1 : na, "Bullish Arrows", color.new(bullishColor, 70), color.new(bullishColor, 70), minheight = 20, maxheight = 20)
plotarrow(curColor == bearishColor and barstate.isconfirmed ? -1 : na, "Bearish Arrows", color.new(bearishColor, 70), color.new(bearishColor, 70), minheight = 20, maxheight = 20)

plotarrow((na(curColor) and curColor[1] == bullishColor and barstate.isconfirmed) ? 1 : na, "Possible Bullish Pivot", bullishColor, bullishColor, minheight = 20, maxheight = 20)
plotarrow((na(curColor) and curColor[1] == bearishColor and barstate.isconfirmed) ? -1 : na, "Possible Bearish Pivot", bearishColor, bearishColor, minheight = 20, maxheight = 20)

alertcondition(na(curColor) and curColor[1] == bullishColor and barstate.isconfirmed, "Possible Bullish Pivot")
alertcondition(na(curColor) and curColor[1] == bearishColor and barstate.isconfirmed, "Possible Bearish Pivot")

if KDELabelsEnabled or RSILabelsEnabled
var lastBullishLabel = 0
if (na(curColor) and curColor[1] == bullishColor and barstate.isconfirmed) and (bar_index - lastBullishLabel) > labelCooldown
lastBullishLabel := bar_index
txt = ""
if RSILabelsEnabled and KDELabelsEnabled
txt := "RSI | " + str.tostring(rsi, "#") + " | " + str.tostring(lowProb * 100, "#.##") + "%"
else if RSILabelsEnabled
txt := "RSI | " + str.tostring(rsi, "#")
else
txt := str.tostring(rsi, "#") + "%"
label.new(bar_index, low, txt, yloc = yloc.belowbar, color = na, style = label.style_label_up, textcolor = textColor, force_overlay = true)

var lastBearishLabel = 0
if (na(curColor) and curColor[1] == bearishColor and barstate.isconfirmed) and (bar_index - lastBearishLabel) > labelCooldown
lastBearishLabel := bar_index
txt = ""
if RSILabelsEnabled and KDELabelsEnabled
txt := "RSI | " + str.tostring(rsi, "#") + " | " + str.tostring(highProb * 100, "#.##") + "%"
else if RSILabelsEnabled
txt := "RSI | " + str.tostring(rsi, "#")
else
txt := str.tostring(rsi, "#") + "%"
label.new(bar_index, low, txt, yloc = yloc.abovebar, color = na, style = label.style_label_down, textcolor = textColor, force_overlay = true)

if kdePivotLabels
txt = str.tostring(rsi, "#.##") + "\nHP -> " + str.tostring(highProb, "#.##") + "\nLP -> " + str.tostring(lowProb, "#.##") + "\n\nMHP -> " + str.tostring(maxHighProb, "#.##") + "\nMLP -> " + str.tostring(maxLowProb, "#.##")
if math.abs(lowProb - maxLowProb) < activationThreshold
label.new(bar_index, high, txt, yloc = yloc.belowbar, color = textColor, style = label.style_label_up, textcolor = color.black, force_overlay = true)
if math.abs(highProb - maxHighProb) < activationThreshold
label.new(bar_index, high, txt, yloc = yloc.abovebar, color = textColor, style = label.style_label_down, textcolor = color.black, force_overlay = true)

if realPivotLabels
if not na(highPivot)
txt = str.tostring(rsi[highPivotLen], "#.##") + "\nHP -> " + str.tostring(highProb[highPivotLen], "#.##") + "\nLP -> " + str.tostring(lowProb[highPivotLen], "#.##") + "\n\nMHP -> " + str.tostring(maxHighProb[highPivotLen], "#.##") + "\nMLP -> " + str.tostring(maxLowProb[highPivotLen], "#.##")
label.new(bar_index - highPivotLen, high, txt, yloc = yloc.abovebar, color = textColor, style = label.style_label_down, textcolor = color.black, force_overlay = true)
if not na(lowPivot)
txt = str.tostring(rsi[lowPivotLen], "#.##") + "\nHP -> " + str.tostring(highProb[lowPivotLen], "#.##") + "\nLP -> " + str.tostring(lowProb[lowPivotLen], "#.##") + "\n\nMHP -> " + str.tostring(maxHighProb[lowPivotLen], "#.##") + "\nMLP -> " + str.tostring(maxLowProb[lowPivotLen], "#.##")
label.new(bar_index - lowPivotLen, high, txt, yloc = yloc.belowbar, color = textColor, style = label.style_label_up, textcolor = color.black, force_overlay = true)
//#endregion

if tableEnabled
var table realtimeTable = table.new(getPosition(tableLocation), 2, 10, bgcolor = screenerColor, frame_width = 2, frame_color = frameColor, border_width = 1, border_color = borderColor)
// Header
table.merge_cells(realtimeTable, 0, 0, 1, 0)
table.cell(realtimeTable, 0, 0, "KDE Optimized RSI", text_color = textColorDB, bgcolor = screenerColor)

// RSI
table.cell(realtimeTable, 0, 1, "RSI", text_color = textColorDB, bgcolor = screenerColor)
table.cell(realtimeTable, 1, 1, str.tostring(rsi, "#"), text_color = textColorDB, bgcolor = screenerColor)

// KDE
table.cell(realtimeTable, 0, 2, (lowProb > highProb) ? "Bullish KDE" : "Bearish KDE", text_color = (lowProb > highProb) ? bullishColor : bearishColor, bgcolor = screenerColor)
table.cell(realtimeTable, 1, 2, str.tostring(nz(math.max(highProb, lowProb), 0) * 100, "#.##") + "%", text_color = textColorDB, bgcolor = screenerColor)
Chart patternsCycles

開源腳本

在真正的TradingView精神中,這個腳本的作者以開源的方式發佈,這樣交易員可以理解和驗證它。請向作者致敬!您可以免費使用它,但在出版物中再次使用這段程式碼將受到網站規則的約束。 您可以收藏它以在圖表上使用。

想在圖表上使用此腳本?

免責聲明