Last active
April 7, 2025 18:16
-
-
Save yaakov123/846215f2072eafa78dd9d9c1f9d7e5fb to your computer and use it in GitHub Desktop.
Dynamic TP/SL Levels
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//@version=5 | |
indicator(title="Dynamic TP/SL Lines with Risk and ATR", shorttitle="Dyn TP/SL Risk ATR", overlay=true, precision=2, max_lines_count = 52, max_labels_count = 50) // Increased max_lines_count | |
// === INPUTS === | |
group_levels = "Level Configuration" | |
numLevels = input.int(3, title="Number of Levels (Each Side)", minval=1, maxval=25, group=group_levels, tooltip="How many TP/SL levels to draw above and below the price.") // Max 25 to stay within limits | |
pointIncrement = input.float(5.0, title="Point Increment Per Level", minval=0.25, step=0.25, group=group_levels, tooltip="The distance in points between consecutive TP/SL levels.") | |
startingNumber = input.int(0, title = "Starting number", minval=0, group=group_levels, tooltip="What distance should the increments start from") | |
atrLength = input.int(14, title="ATR Length (1min Chart)", minval=1, group=group_levels, tooltip="Length of the ATR calculation on the 1-minute chart.") | |
atrMultiplier = input.int(2, title="ATR Multiple", minval=1, group=group_levels, tooltip="What multiple of the ATR value to draw the lines at") | |
group_risk = "Risk Management" | |
riskAmount = input.float(300.0, title="Risk Amount ($)", minval=0.0, group=group_risk, tooltip="The total amount you are willing to risk.") | |
positionType = "Long" | |
group_style = "Line & Label Style" | |
tpColor = input.color(color.new(color.green, 0), title="TP Line Color", group=group_style) | |
slColor = input.color(color.new(color.red, 0), title="SL Line Color", group=group_style) | |
atrColor = input.color(color.new(color.blue, 50), title="ATR Line Color", group=group_style) | |
// --- Line Style Input for line.new --- | |
STYLE_SOLID = "Solid" | |
STYLE_DASHED = "Dashed" | |
STYLE_DOTTED = "Dotted" | |
lineStyleInput = input.string(STYLE_SOLID, title="Line Style", options=[STYLE_SOLID, STYLE_DASHED, STYLE_DOTTED], group=group_style) | |
// --- End Style Input --- | |
lineWidth = input.int(1, title="Line Width", minval=1, group=group_style) | |
lineExtendBars = input.int(25, title="Line Extension (Bars)", minval=0, group=group_style, tooltip="How many bars to the right of the current bar to extend the lines.") | |
showLabels = input.bool(true, title="Show Point Labels", group=group_style, tooltip="Displays the point distance next to each line.") | |
labelSize = input.string(size.small, title="Label Size", options=[size.tiny, size.small, size.normal, size.large, size.huge], group=group_style) | |
labelOffsetBars = lineExtendBars + 1 | |
// === VARIABLES FOR MANAGING DRAWINGS === | |
// Persistent arrays to store the IDs of the lines and labels | |
var line[] tpLineIds = array.new_line() | |
var line[] slLineIds = array.new_line() | |
var label[] tpLabelIds = array.new_label() | |
var label[] slLabelIds = array.new_label() | |
var line[] atrLineIds = array.new_line() | |
atrValue = request.security(syminfo.tickerid, "1", ta.atr(atrLength)) | |
atrValue := atrValue * atrMultiplier | |
// === LOGIC (Executes only on the last bar's updates) === | |
if barstate.islast | |
// --- Get Current State --- | |
currentPrice = close // Use the close/current price of the last bar | |
currentBarIndex = bar_index | |
riskPerPoint = syminfo.pointvalue | |
// --- Determine Line Style --- | |
lineStyleActual = switch lineStyleInput | |
STYLE_DASHED => line.style_dashed | |
STYLE_DOTTED => line.style_dotted | |
=> line.style_solid // Default to solid | |
// --- Delete Excess Drawings (if numLevels decreased) --- | |
// TP Lines | |
if array.size(tpLineIds) > numLevels | |
start_index = array.size(tpLineIds) - 1 | |
end_index = numLevels | |
if start_index >= end_index | |
for i = start_index to end_index by -1 | |
line.delete(array.get(tpLineIds, i)) | |
// SL Lines | |
if array.size(slLineIds) > numLevels | |
start_index = array.size(slLineIds) - 1 | |
end_index = numLevels | |
if start_index >= end_index | |
for i = start_index to end_index by -1 | |
line.delete(array.get(slLineIds, i)) | |
// TP Labels | |
if showLabels and array.size(tpLabelIds) > numLevels | |
start_index = array.size(tpLabelIds) - 1 | |
end_index = numLevels | |
if start_index >= end_index | |
for i = start_index to end_index by -1 | |
label.delete(array.get(tpLabelIds, i)) | |
// SL Labels | |
if showLabels and array.size(slLabelIds) > numLevels | |
start_index = array.size(slLabelIds) - 1 | |
end_index = numLevels | |
if start_index >= end_index | |
for i = start_index to end_index by -1 | |
label.delete(array.get(slLabelIds, i)) | |
// ATR Lines | |
if array.size(atrLineIds) > 2 | |
for i = 0 to array.size(atrLineIds) - 1 | |
line.delete(array.get(atrLineIds, i)) | |
array.clear(atrLineIds) | |
// Remove the extra IDs from arrays if numLevels decreased | |
if array.size(tpLineIds) > numLevels | |
array.slice(tpLineIds, 0, numLevels) // Keep only the first 'numLevels' IDs | |
if array.size(slLineIds) > numLevels | |
array.slice(slLineIds, 0, numLevels) | |
if array.size(tpLabelIds) > numLevels | |
array.slice(tpLabelIds, 0, numLevels) | |
if array.size(slLabelIds) > numLevels | |
array.slice(slLabelIds, 0, numLevels) | |
// If labels turned off, delete existing ones | |
if not showLabels and array.size(tpLabelIds) > 0 | |
for id in tpLabelIds | |
label.delete(id) | |
array.clear(tpLabelIds) | |
if not showLabels and array.size(slLabelIds) > 0 | |
for id in slLabelIds | |
label.delete(id) | |
array.clear(slLabelIds) | |
// --- Calculate ATR on 1-minute chart --- | |
// --- Calculate ATR Levels --- | |
atrUpperLevel = currentPrice + atrValue | |
atrLowerLevel = currentPrice - atrValue | |
// --- Draw ATR Lines --- | |
if array.size(atrLineIds) < 2 | |
upperAtrLine = line.new(x1=currentBarIndex - 1, y1=atrUpperLevel, x2=currentBarIndex + lineExtendBars, y2=atrUpperLevel, color=atrColor, style=lineStyleActual, width=lineWidth) | |
lowerAtrLine = line.new(x1=currentBarIndex - 1, y1=atrLowerLevel, x2=currentBarIndex + lineExtendBars, y2=atrLowerLevel, color=atrColor, style=lineStyleActual, width=lineWidth) | |
array.push(atrLineIds, upperAtrLine) | |
array.push(atrLineIds, lowerAtrLine) | |
else | |
line.set_y1(array.get(atrLineIds, 0), atrUpperLevel) | |
line.set_y2(array.get(atrLineIds, 0), atrUpperLevel) | |
line.set_x1(array.get(atrLineIds, 0), currentBarIndex - 1) | |
line.set_x2(array.get(atrLineIds, 0), currentBarIndex + lineExtendBars) | |
line.set_y1(array.get(atrLineIds, 1), atrLowerLevel) | |
line.set_y2(array.get(atrLineIds, 1), atrLowerLevel) | |
line.set_x1(array.get(atrLineIds, 1), currentBarIndex - 1) | |
line.set_x2(array.get(atrLineIds, 1), currentBarIndex + lineExtendBars) | |
// --- Create / Modify TP/SL Drawings --- | |
// Loop from 0 to numLevels-1 (for array indexing) | |
for i = 0 to numLevels - 1 | |
// Calculate distance and levels for this iteration (level i+1) | |
dist = startingNumber + pointIncrement * (i + 1) | |
tpLevel = currentPrice + dist | |
slLevel = currentPrice - dist | |
dist_txt = str.tostring(dist, syminfo.mintick < 0.01 ? "#.#####" : syminfo.mintick < 0.1 ? "#.###" : "#.##") + " Pts" | |
// --- TP Line --- | |
if i < array.size(tpLineIds) // Check if line for this level exists | |
// Modify existing line's Y and X positions | |
line.set_y1(array.get(tpLineIds, i), tpLevel) | |
line.set_y2(array.get(tpLineIds, i), tpLevel) | |
line.set_x1(array.get(tpLineIds, i), currentBarIndex - 1) | |
line.set_x2(array.get(tpLineIds, i), currentBarIndex + lineExtendBars) | |
// Optional: Update style/color if inputs change | |
else | |
// Create new line if it doesn't exist | |
newLine = line.new(x1=currentBarIndex - 1, y1=tpLevel, x2=currentBarIndex + lineExtendBars, y2=tpLevel, color=tpColor, style=lineStyleActual, width=lineWidth) | |
array.push(tpLineIds, newLine) // Store new ID | |
// --- SL Line --- | |
if i < array.size(slLineIds) // Check if line exists | |
// Modify existing line's Y and X positions | |
line.set_y1(array.get(slLineIds, i), slLevel) | |
line.set_y2(array.get(slLineIds, i), slLevel) | |
line.set_x1(array.get(slLineIds, i), currentBarIndex - 1) | |
line.set_x2(array.get(slLineIds, i), currentBarIndex + lineExtendBars) | |
else | |
// Create new line | |
newLine = line.new(x1=currentBarIndex - 1, y1=slLevel, x2=currentBarIndex + lineExtendBars, y2=slLevel, color=slColor, style=lineStyleActual, width=lineWidth) | |
array.push(slLineIds, newLine) // Store new ID | |
// --- Labels (if enabled) --- | |
if showLabels | |
// --- SL Label --- | |
var float contractsNeededSL = na | |
var float actualRiskSL = na | |
var float priceDifferenceSL = na // Declare priceDifferenceSL here | |
if positionType == "Long" | |
priceDifferenceSL := currentPrice - slLevel | |
else if positionType == "Short" | |
priceDifferenceSL := slLevel - currentPrice | |
riskPerContractSL = math.abs(priceDifferenceSL * riskPerPoint) | |
if riskPerContractSL > 0 | |
contractsNeededSL := math.round(riskAmount / riskPerContractSL) | |
actualRiskSL := contractsNeededSL * riskPerContractSL | |
if (actualRiskSL > riskAmount) | |
contractsNeededSL -= 1 | |
actualRiskSL := contractsNeededSL * riskPerContractSL | |
else | |
contractsNeededSL := 0 | |
actualRiskSL := 0 | |
contractsTextSL = " (" + str.tostring(contractsNeededSL) + " Cnt, $" + str.tostring(actualRiskSL, "#.##") + " Risk)" | |
// --- TP Label --- | |
if i < array.size(tpLabelIds) // Check if label exists | |
// Modify existing label's position and text | |
label.set_y(array.get(tpLabelIds, i), tpLevel) | |
label.set_x(array.get(tpLabelIds, i), currentBarIndex + labelOffsetBars) | |
label.set_text(array.get(tpLabelIds, i), dist_txt + contractsTextSL) | |
// Optional: Update color/size if inputs change | |
else | |
// Create new label | |
newLabel = label.new(x=currentBarIndex + labelOffsetBars, y=tpLevel, text=dist_txt + contractsTextSL, color=color.new(color.white, 100), textcolor=tpColor, style=label.style_label_left, size=labelSize) | |
array.push(tpLabelIds, newLabel) // Store new ID | |
if i < array.size(slLabelIds) // Check if label exists | |
// Modify existing label's position and text | |
label.set_y(array.get(slLabelIds, i), slLevel) | |
label.set_x(array.get(slLabelIds, i), currentBarIndex + labelOffsetBars) | |
label.set_text(array.get(slLabelIds, i), dist_txt + contractsTextSL) | |
else | |
// Create new label | |
newLabel = label.new(x=currentBarIndex + labelOffsetBars, y=slLevel, text=dist_txt + contractsTextSL, color=color.new(color.white, 100), textcolor=slColor, style=label.style_label_left, size=labelSize) | |
array.push(slLabelIds, newLabel) // Store new ID | |
// --- End of barstate.islast block --- |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment