Trailing Stop Loss with Profit Taking

Viewing 4 posts - 1 through 4 (of 4 total)
  • Author
    Posts
  • #231127 quote
    WhyAskOZ
    Participant
    New

    Hi everyone,

    I recently came across a very interesting indicator that combines a trailing stop loss with a built-in profit target. While the pine script for this indicator seems well-written, it’s quite complex.

    I was wondering if there’s a similar indicator already available in PRT’s library? I’m familiar with the existing trailing stop indicators based on ATR in PRT library, but I’m looking for something that also incorporates a profit target functionality similar to one below.

    Does anyone here have experience with creating or using such an indicator on PRT? Any suggestions would be greatly appreciated!

    https://www.tradingview.com/script/gUXnDioT-Interactive-ATR-Stop-Loss-TANHEF/  
    @version=5
    indicator(title='Interactive ATR Stop Loss', shorttitle='Interactive ATR', overlay=true)
    
    //INPUTS - Interactive
    tt_Interact = "Click location to begin ATR Trailing Stoploss. Below price for long stop, and above price for short stop. \nDrag 🔵 to modify afterwards."
    tt_closeType= "Criteria used to determine if stop-loss has been hit.\n\nWick: low/high wicks outside of trailing stop \nClose: close outside of trailing stop"
    yBarPrice   = input.price(0.0,      title='Start Point',                        inline='Above/Below bar to start',  group='Interactive ATR',    confirm=true)
    xBarTime    = input.time(0,         title='',                                   inline='Above/Below bar to start',  group='Interactive ATR',    confirm=true, tooltip=tt_Interact)
    closeType   = input.string("Wick",  title="Trigger",options=["Wick","Close"],   inline='ATRclose',                  group="Interactive ATR",                  tooltip=tt_closeType)
    
    //INPUTS - Price Target
    tt_PriceTarget      = "Profit target can be set on the opposite side of price in compared to the trailing stoploss. The trailing stop loss will continue plotting until it is hit, even if the profit target is hit first. The profit target can be set as an exact price or a Risk/Reward ratio. The Risk/Reward ratio calculates from the close price to the ATR stoploss value."
    displayPriceTarget  = input(false,        title="Profit Target",    inline="PriceTarget", group="Interactive ATR", tooltip=tt_PriceTarget)
    priceTargetType     = input.string('R:R', title="",                 inline='PriceTarget', group="Interactive ATR", options=['R:R','Price'])
    priceTargetMult     = input.float(1.5,    title="",                 inline="PriceTarget", group="Interactive ATR", step=0.1)
    
    //INPUTS - Display Settings
    tt_DisplayInteractive   = "Display ATR Trailing - Interactive: Displays \nFill: stoploss areas for Interactive ATR \nShape: display shape when trailing stop hit\nLabel: label including ATR trailing value and status"
    tt_DisplayBandsATR      = "Display ATR Bands: Normal Upper & Lower\n"
    displayTrailATR         = input(true,   title="Display ATR Trailing - Interactive", inline='TrailingATR',   group="Display")
    fillArea                = input(true,   title="Fill",                               inline='TrailingATR',   group="Display")
    displayShapes           = input(true,   title='Shape',                              inline='TrailingATR',   group="Display")
    displayLabel            = input(true,   title='Label',                              inline='TrailingATR',   group="Display", tooltip=tt_DisplayInteractive)
    displayBandsATR         = input(false,  title="Display ATR Bands",                  inline='BandsATR',      group="Display", tooltip=tt_DisplayBandsATR)
    
    //INPUTS - ATR Settings
    length      = input.int(14,   minval=1, title='Length',             inline="inputsATR", group="ATR Settings")
    multiplier  = input.float(3.0,step=0.5, title='Multiplier',         inline="inputsATR", group="ATR Settings")
    maTypeInput = input.string("SMMA (RMA)",title="MA Type",            inline="inputsATR", group="ATR Settings", options=["SMA", "EMA", "SMMA (RMA)", "WMA", "VWMA"])
    src1        = input(close,              title="Source (Upper band)",inline='1',         group="ATR Settings")
    src2        = input(close,              title="Source (Lower band)",inline='2',         group="ATR Settings")
    
    //INPUTS - Styles
    tt_colTrailing  = "Trailing ATR colors\nDisplayable via 'Display ATR Trailing - Interactive'"
    tt_colNormalATR = "Normal ATR bands colors\nDisplayable via 'Display ATR Bands'"
    colTrailing     = input(color.new(color.blue,0),            title='Trailing',   inline='Colors',    group="Styles")
    colLossFill     = input.color(color.new(color.red,20),      title='Loss Fill', inline='Colors',     group="Styles", tooltip=tt_colTrailing)
    colProfitFill   = input.color(color.new(color.lime,20),     title='Profit Fill', inline='Colors',   group="Styles")
    colShort        = input.color(color.new(color.purple,20),   title='Upper Band', inline='Colors2',   group="Styles")
    colLong         = input.color(color.new(color.teal,20),     title='Lower Band', inline='Colors2',   group="Styles", tooltip=tt_colNormalATR)
    labelSize       = input.string(size.normal,                   title="Label Size", inline="labelSize", group="Styles", options=[size.auto, size.tiny, size.small, size.normal, size.large, size.huge])
    
    //CALCULATE - MA
    calcMA(source, length, type) =>
        switch type
            "SMA" => ta.sma(source, length)
            "EMA" => ta.ema(source, length)
            "SMMA (RMA)" => ta.rma(source, length)
            "WMA" => ta.wma(source, length)
            "VWMA" => ta.vwma(source, length)
    a     = calcMA(ta.tr(true), length, maTypeInput) * multiplier
    upper = calcMA(ta.tr(true), length, maTypeInput) * multiplier + src1
    lower = src2 - calcMA(ta.tr(true), length, maTypeInput) * multiplier
    
    //CALCULATE - Trailing ATR
    var startNow = false    //Start Plotting TrailingStop Loss
    var aboveClose = false  //Above or below (True = long , False = short)
    var aboveClose_ProfitTarget = false
    var waitForFirstProfitTargetHit = true
    var upperTrailing = 0.0
    var lowerTrailing = 0.0
    var currentlyTrailing = false
    var startMinute = int(na)       //Start minute (for alerts)
    var startHour = int(na)         //Start hour (for alerts)
    var startDay = int(na)          //Start day (for alerts)
    var priceTargetVal = float(na)  //For price target value (from either R:R or Price input)
    
    //Check first bar of trailing stoploss, also if short or long stoploss
    if xBarTime == time
        startNow    := true
        aboveClose  := yBarPrice < close ? true : false
        startMinute := minute
        startHour   := hour
        startDay    := dayofmonth
    
    //Start stoploss and set trailing value
    if startNow and startNow[1] == false
        currentlyTrailing := true
        upperTrailing := upper
        lowerTrailing := lower
    
        if priceTargetType == 'R:R'
            priceTargetVal := aboveClose ? close+((close-lowerTrailing)*priceTargetMult) : close-((upperTrailing-close)*priceTargetMult)
        else
            priceTargetVal := priceTargetMult
        
        if aboveClose
            if lower > low
                labelATR = label.new(bar_index, yBarPrice, text='Overlapping\nATR and Bar', textalign = text.align_left, color=color.new(color.blue,100), textcolor=color.blue, xloc=xloc.bar_index, yloc=yloc.price,size=labelSize, tooltip='Either place on another bar or modify ATR Settings')
                label.set_style(labelATR, aboveClose ? label.style_label_up : label.style_label_down)
        else
            if upper < high
                labelATR = label.new(bar_index, yBarPrice, text='Overlapping\nATR and Bar', textalign = text.align_left, color=color.new(color.blue,100), textcolor=color.blue, xloc=xloc.bar_index, yloc=yloc.price,size=labelSize, tooltip='Either place on another bar or modify ATR Settings')
                label.set_style(labelATR, aboveClose ? label.style_label_up : label.style_label_down)
    
    //Update ATR Trailing Stop Value
    if currentlyTrailing
        if aboveClose == false
            if upperTrailing > upper
                upperTrailing := upper
        if aboveClose
            if lowerTrailing < lower
                lowerTrailing := lower
    
    //PLOT - Draggable Start Point
    plot(xBarTime == time ? yBarPrice : na,style=plot.style_circles, color=color.blue, linewidth=5, title="🔵Start Point (Draggable Circle)")
    
    //PLOT - Trailing Stop-loss
    plotUpperTrailing = plot(displayTrailATR and currentlyTrailing and aboveClose == false ? upperTrailing : na, title="Upper Trailing", color=colTrailing)                 //Upper Trail
    plotLowerTrailing = plot(displayTrailATR and currentlyTrailing and aboveClose ? lowerTrailing : na,          title="Lower Trailing", color=colTrailing)                 //Lower Trail
    placeholder       = plot(currentlyTrailing ? hlc3 : na, editable=false,                                      title="Placeholder",    color=color.new(color.blue,100)) //Placeholder
    
    profitHit = false
    
    //Stop the upper/lower trailing after found
    if closeType == "Wick"
        //Trailing
        if high > upperTrailing and aboveClose == false
            currentlyTrailing := false
        if low < lowerTrailing and aboveClose
            currentlyTrailing := false
        //Profit Target
        if (ta.cross(high,priceTargetVal) or ta.cross(low,priceTargetVal)) and currentlyTrailing
            waitForFirstProfitTargetHit := false
            profitHit := true
    if closeType == "Close"
        //Trailing
        if close[1] > upperTrailing and aboveClose == false
            currentlyTrailing := false
        if close[1] < lowerTrailing and aboveClose
            currentlyTrailing := false
        //Profit Target
        if (ta.cross(close,priceTargetVal)) and currentlyTrailing
            waitForFirstProfitTargetHit := false
            profitHit := true
    
    //PLOT - Fill (only while active, removes when triggered)
    fill(plotUpperTrailing,placeholder,color=close < upperTrailing ? color.new(colLossFill,80) : na,display=fillArea ? display.all : display.none)
    fill(plotLowerTrailing,placeholder,color=close > lowerTrailing ? color.new(colLossFill,80) : na,display=fillArea ? display.all : display.none)
    
    //PLOT - Price Target
    plotTarget = plot(displayPriceTarget and currentlyTrailing and waitForFirstProfitTargetHit[1] ? priceTargetVal : na, title="Price Target")
    fill(placeholder, plotTarget, color=waitForFirstProfitTargetHit[1] ? color.new(colProfitFill,80) : na,display=fillArea ? display.all : display.none)
    
    //PLOT - Shape (Stoploss Hit)
    plotchar(displayTrailATR and aboveClose and displayShapes and currentlyTrailing[1] and currentlyTrailing == false,          title="Symbol - Long Stop",  char='⚠️', location=location.belowbar, size=size.small)
    plotchar(displayTrailATR and aboveClose == false and displayShapes and currentlyTrailing[1] and currentlyTrailing == false, title="Symbol - Short Stop", char='⚠️', location=location.abovebar, size=size.small)
    
    //PLOT - Shape (Target Hit)
    plotchar(displayTrailATR and aboveClose and displayShapes and displayPriceTarget and profitHit and waitForFirstProfitTargetHit[1],          title="Symbol - Long Profit", char='🎯',  location=location.abovebar, size=size.small)
    plotchar(displayTrailATR and aboveClose == false and displayShapes and displayPriceTarget and profitHit and waitForFirstProfitTargetHit[1], title="Symbol - Short Profit", char='🎯', location=location.belowbar, size=size.small)
    
    //PLOT - Normal ATR Bands
    plotUpper = plot(displayBandsATR ? upper : na, title='ATR Band - Upper', color=colShort)
    plotLower = plot(displayBandsATR ? lower : na, title='ATR Band - Lower', color=colLong)
    
    //LABEL - Get text
    labelState = ""
    if currentlyTrailing == false
        labelState := "⚠️"
    else
        labelState := displayPriceTarget ? '⌛\nProfit Target: ' + str.tostring(math.round_to_mintick(priceTargetVal),'#.################') : ''
        labelState := aboveClose ? labelState + '\nLoss Distance: ' + str.tostring(((lowerTrailing[1]/close)-1)*100,'#.###') + '%' : labelState + '\nLoss Distance: ' + str.tostring(((upperTrailing[1]/close)-1)*100,'#.###') + '%'
        if displayPriceTarget and waitForFirstProfitTargetHit
            labelState := aboveClose ? labelState + '\nProfit Distance: ' + str.tostring(((priceTargetVal/close)-1)*100,'#.###') + '%' : labelState + '\nProfit Distance: ' + str.tostring(((priceTargetVal/close)-1)*100,'#.###') + '%'
        else if displayPriceTarget
            labelState := labelState + '\nProfit Distance: 🎯'
    labelPrice = aboveClose ? lowerTrailing : upperTrailing
    profitText = string(na)
    labelText = 'ATR: ' + str.tostring(math.round_to_mintick(labelPrice),'#.################') + labelState
    
    //LABEL - Display
    directionCol = aboveClose ? colProfitFill : colLossFill
    var labelATR = label.new(na, na, '', textalign = text.align_left, textcolor = color.blue, style = label.style_label_left,xloc=xloc.bar_index, yloc=yloc.price,size=labelSize)
    label.set_xy(labelATR, displayTrailATR and displayLabel ? bar_index+3 : na, close[1])
    label.set_color(labelATR, directionCol)
    label.set_text(labelATR, labelText)
    
    //INPUTS - Alerts
    tt_alert              = "Check alerts to use. Go to 'Create Alert' and set the condition to 'Interactive ATR', select create. "
    tt_alertDetails       = "\n\nAdditional details can be added to the message using these words in between Curly (Brace) Brackets:\n {{trail}} = ATR trailing stop-loss (price) \n {{upper}} = Upper ATR band (price) \n {{lower}} = Lower ATR band (price) \n {{band}} = Lower or Upper trailing (word) \n {{type}} = Long or Short stop-loss (word) \n {{traildistance}} = Trailing Distance (%) \n {{targetdistance}} = Target Distance (%) \n {{starttime}} = Start time of trailing (day:hour:minute) \n {{target}} = Price target (price) \n {{trigger}} = Wick or Close Trigger input option (input) \n {{atrlength}} = ATR length (input) \n {{atrmultiplier}} = ATR multiplier (input) \n {{atrma}} = ATR MA type (input) \n {{ticker}} = Ticker of chart (word) \n {{exchange}} = Exchange of chart (word) \n {{description}} = Description of ticker (words) \n {{close}} = Bar close (price) \n {{open}} = Bar open (price) \n {{high}} = Bar high (price) \n {{low}} = Bar low (price) \n {{hl2}} = Bar HL2 (price) \n {{volume}} = Bar volume (value) \n {{time}} = Current time (day:hour:minute) \n {{interval}} = Chart timeframe \n {{newline}} = New line for text"
    tt_alertTrailingHit   = "\n\nTrailing Hit: alert interactive trailing ATR hit."
    tt_alertTrailingEx    = "\n\nExample Alert Message Input: \n {{band}} ATR Trailing Stop Hit at {{trail}} ({{type}}){{newline}}{{ticker}}\n\nExample Alert Message Output: \n Lower ATR Trailing Stop Hit at 25 (Long)\n BTCUSD"
    tt_alertPriceTarget   = "\n\nTarget Hit: alert price target hit (while trailing not hit)."
    tt_alertTargetEx      = "\n\nExample Alert Message Input: \n Price crossing target of {{target}} ({{type}}){{newline}}{{ticker}} ATR Trailing Stop Not Hit\n\nExample Alert Message Output: \n Price crossing target of 100 (Long)\n BTCUSD ATR Trailing Stop Not Hit"
    o_trailing            = "{{band}} ATR Trailing Stop Hit at {{trail}} ({{type}}){{newline}}{{ticker}}"
    o_target              = "Price crossing target of {{target}} ({{type}}){{newline}}{{ticker}} ATR Trailing Stop Not Hit"
    Alert_TrailingHit     = input(true,              title="Alert Trailing Stop Hit", inline='AlertTrailingHit', group="Alert", tooltip=tt_alert + tt_alertTrailingHit + tt_alertDetails + tt_alertTrailingEx)
    Alert_TrailingHitText = input.string(o_trailing, title=' - Message',              inline='AlertTrailingHit', group="Alert")
    Alert_PriceTarget     = input(false,             title="Alert Price Target Hit",  inline='AlertProfitHit',   group="Alert", tooltip=tt_alert + tt_alertPriceTarget + tt_alertDetails + tt_alertTargetEx)
    Alert_PriceTargetText = input.string(o_target,   title=' - Message',              inline='AlertProfitHit',   group="Alert")
    
    //allOptions = "{{trail}} {{upper}} {{lower}} {{band}} {{type}} {{traildistance}} {{targetdistance}} {{target}} {{trigger}} {{atrlength}} {{atrmultiplier}} {{atrma}} {{ticker}} {{exchange}} {{description}} {{close}} {{open}} {{high}} {{low}} {{hl2}} {{volume}} {{time}} {{starttime}} {{interval}} {{newline}}"
    
    //CALCULATE - Timeframe text for alert ('fixes' letter consistancy between different timeframes)
    calcTimeframeText(_src) =>
        result = _src
        if str.contains(_src,'S')
            result := str.replace(_src,'S','s',0)
        else if str.contains(_src,'D') or str.contains(_src,'W') or str.contains(_src,'M')
            result := _src
        else
            result := str.tonumber(_src) < 60 ? _src + 'm' : str.tostring(str.tonumber(_src)/60) + 'h'
    
    //CALCULATE - Curly brackets text into current data
    //One reason this code was added due to the preference of using alert() instead of alertcondition() which requires additional selection when setting up alerts.
    //This function allows for alert() to return messages with retreived values via curly brackets, that alertcondition() can do by default.
    //Additional indicator specific values can also be retreived, such as {{trail}} , {{band}} , {{type}} , {{trigger}} , {{target}} , {{newline}} , and more.
    checkAlertText(_text) =>
        result = _text
        //Calculated values specific to this indicator
        result := str.replace(result, '{{trail}}',          str.tostring(math.round_to_mintick(aboveClose ? lowerTrailing : upperTrailing),'#.################'))
        result := str.replace(result, '{{upper}}',          str.tostring(math.round_to_mintick(upper),'#.################'))
        result := str.replace(result, '{{lower}}',          str.tostring(math.round_to_mintick(lower),'#.################'))
        result := str.replace(result, '{{band}}',           aboveClose ? 'Lower' : 'Upper')
        result := str.replace(result, '{{type}}',           aboveClose ? 'Long' : 'Short')
        result := str.replace(result, '{{traildistance}}',  aboveClose ? str.tostring(((lowerTrailing[1]/close)-1)*100,'#.###') + '%' : str.tostring(((upperTrailing[1]/close)-1)*100,'#.###') + '%')
        result := str.replace(result, '{{targetdistance}}', aboveClose ? str.tostring(((priceTargetVal/close)-1)*100,'#.###') + '%' : str.tostring(((priceTargetVal/close)-1)*100,'#.###') + '%')
        result := str.replace(result, '{{starttime}}',      str.tostring(startDay,'#') + ':' + str.tostring(startHour,'#') + ':' + str.tostring(startMinute,'#'))
        //Input values specific to this indicator
        result := str.replace(result, '{{target}}',         str.tostring(math.round_to_mintick(priceTargetVal),'#.################'))
        result := str.replace(result, '{{trigger}}',        closeType)
        result := str.replace(result, '{{atrlength}}',      str.tostring(length,'#'))
        result := str.replace(result, '{{atrmultiplier}}',  str.tostring(multiplier,'#'))
        result := str.replace(result, '{{atrma}}',          maTypeInput)
        //Common to any indicator
        result := str.replace(result, '{{ticker}}',         syminfo.ticker)
        result := str.replace(result, '{{exchange}}',       syminfo.prefix(syminfo.tickerid))
        result := str.replace(result, '{{description}}',    syminfo.description)
        result := str.replace(result, '{{close}}',          str.tostring(math.round_to_mintick(close),'#.################'))
        result := str.replace(result, '{{open}}',           str.tostring(math.round_to_mintick(open),'#.################'))
        result := str.replace(result, '{{high}}',           str.tostring(math.round_to_mintick(high),'#.################'))
        result := str.replace(result, '{{low}}',            str.tostring(math.round_to_mintick(low),'#.################'))
        result := str.replace(result, '{{hl2}}',            str.tostring(math.round_to_mintick(hl2),'#.################'))
        result := str.replace(result, '{{volume}}',         str.tostring(math.round_to_mintick(volume),'#.################'))
        result := str.replace(result, '{{time}}',           str.tostring(dayofmonth,'#') + ':' + str.tostring(hour,'#') + ':' + str.tostring(minute,'#'))
        result := str.replace(result, '{{interval}}',       calcTimeframeText(timeframe.period))
        result := str.replace(result, '{{newline}}',        '\n')
    
    //CALCULATE - ALERTS
    if Alert_TrailingHit
        if currentlyTrailing[1] and currentlyTrailing == false
            alert(checkAlertText(Alert_TrailingHitText),alert.freq_once_per_bar)
    if Alert_PriceTarget
        if currentlyTrailing and ta.cross(priceTargetVal,close)
            alert(checkAlertText(Alert_PriceTargetText),alert.freq_once_per_bar)
    
    #231163 quote
    JS
    Participant
    Senior

    Hi,

    The indicator below is the “ATR Trailing Stop” from the library (https://www.prorealcode.com/prorealtime-indicators/another-atr-trailing-stop/ ) with a small extension to show a “Take Profit”…

    You can set the TPL (TakeProfitLong) and TPS (TakeProfitShort) in points in the “Settings window”…

    The “p” is the period of the ATR

    De “mult” is de multiplier van de ATR

    atr = AverageTrueRange[p](close) * mult
    
    If BarIndex<=10 then
    Dist=Average[10](Range)*0.5
    EndIf
    
    once trend=1
    Once TPLong=0
    Once TPShort=0
    
    if trend=1 then
    hh=max(hh,close)
    ll=hh
    if atr<atr[1] then
    hhlevel=hh-atr
    if hhlevel>ts then
    ts=hhlevel
    endif
    endif
    r=0
    g=168
    else
    ll=min(ll,close)
    hh=ll
    if atr<atr[1] then
    lllevel=ll+atr
    if lllevel<ts then
    ts=lllevel
    endif
    endif
    r=255
    g=0
    endif
    
    if close crosses over ts then
    trend=1
    if mode>0 then
    ts=ll
    endif
    elsif close crosses under ts then
    trend=-1
    if mode>0 then
    ts=hh
    endif
    endif
    
    If Close Crosses Over TS then
    BarIndexLong=BarIndex
    TPShort=0
    HighLong=High
    EndIf
    
    If Close[1] Crosses Over TS[1] then
    OpenTPLong=Open
    TPLong=Open+TPL
    EndIf
    
    If TPLong>0 then
    DrawSegment(BarIndexLong,TPLong,BarIndex,TPLong)Style(Line,2)Coloured("Green")
    DrawSegment(BarIndexLong,HighLong,BarIndexLong,TPLong)Style(Line,2)Coloured("Green")
    DrawText("TakeProfit#TPLong#",BarIndexLong+4,TPLong+Dist,SansSerif,Bold,16)Coloured("Green")
    EndIf
    
    If Close Crosses Under TS then
    BarIndexShort=BarIndex
    TPLong=0
    LowShort=Low
    EndIf
    
    If Close[1] Crosses Under TS[1] then
    OpenTPShort=Open
    TPShort=Open-TPS
    EndIf
    
    If TPShort>0 then
    DrawSegment(BarIndexShort,TPShort,BarIndex,TPShort)Style(Line,2)Coloured("Red")
    DrawSegment(BarIndexShort,LowShort,BarIndexShort,TPShort)Style(Line,2)Coloured("Red")
    DrawText("TakeProfit#TPShort#",BarIndexShort+4,TPShort-Dist,SansSerif,Bold,16)Coloured("Red")
    EndIf
    
    return ts coloured(r,g,0) style(line,3)
    

    WhyAskOZ thanked this post
    #231168 quote
    WhyAskOZ
    Participant
    New
    Thank you. It works and I made small change to the code by Changing TPL & TPS to decimal which means TPL & TPS will be used as % of profit instead of open + value. So new code has two changes as below: TPLong=Open+(Open*TPL) TPShort=Open-(Open*TPS)   do you know what is the “MODE” in setting? couldn’t work out what it does and why is it there?
    #231169 quote
    JS
    Participant
    Senior

    From the original “leaflet”…

    The ATR trailing stop has 2 modes: (“mode” setting)

    • mode=0 ; the trend line will keep the same level each time a cross over occur
    • mode>0 ; the trend line will take the highest or lowest Close level each time a cross over occur
    WhyAskOZ thanked this post
Viewing 4 posts - 1 through 4 (of 4 total)
  • You must be logged in to reply to this topic.

Trailing Stop Loss with Profit Taking


ProBuilder: Indicators & Custom Tools

New Reply
Author
author-avatar
WhyAskOZ @g42dav Participant
Summary

This topic contains 3 replies,
has 2 voices, and was last updated by JS
1 year, 10 months ago.

Topic Details
Forum: ProBuilder: Indicators & Custom Tools
Language: English
Started: 04/05/2024
Status: Active
Attachments: 2 files
Logo Logo
Loading...