LONG Position SHORT Position Tool

Please refer original Post here, where I wanted to see if anyone has LONG / SHORT position Tool like you have on TView.

One user did have it. But he was not particularly helpful.

I have no coding knowledge but using AI Coding etc. I could manage below. However, the Drawing is very primitive, dragging it is very jerky, Even resizing the Top (Risk) and Bottom Rectangles (Reward) is very jerky. I wanted circles to appear when I hover mouse near the top and bottom Left of rectangles. Even the circles which appear are not instantaneous.

I wanted functionality of the drawing like how Native drawings of Amibroker are smooth (Drawing Rectangles, Circles etc) on charts and mine is not so smooth.

I also tried increasing timedrefresh but to no avail..

Following is the code,

_SECTION_BEGIN("Smooth Risk-Reward Tool");

// Enable QuickGFX for smooth rendering
GfxSetOverlayMode(1); 

// Function to Draw a Rectangle
function DrawRectangle(x1, y1, x2, y2, BackColor)
{
    GfxSelectSolidBrush(BackColor);
    GfxRectangle(x1, y1, x2, y2);
}

// Function to Draw Circles for Resizing
function DrawCircle(cx, cy, radius, color, opacity)
{
    GfxSelectSolidBrush(ColorBlend(color, colorWhite, opacity));
    GfxCircle(cx, cy, radius);
}

// Define Rectangle Size
ParamSqrWidth  = 150;
ParamSqrHeight = 50;

// Define Colors via Parameters
RiskColor = ParamColor("Risk Rectangle Color", colorRose);
RewardColor = ParamColor("Reward Rectangle Color", colorGreen);

// Reset Position if Required
Reset = ParamTrigger("Reset Coordinates", "RESET");
xOffset = Nz(StaticVarGet("xOffset"), 50);
yOffset = Nz(StaticVarGet("yOffset"), 100);
RiskHeight = Nz(StaticVarGet("RiskHeight"), ParamSqrHeight);
RewardHeight = Nz(StaticVarGet("RewardHeight"), ParamSqrHeight);

if (Reset)
{
    StaticVarSet("xOffset", 50);
    StaticVarSet("yOffset", 100);
    StaticVarSet("RiskHeight", ParamSqrHeight);
    StaticVarSet("RewardHeight", ParamSqrHeight);
    StaticVarSet("MoveInProgress", False);
    StaticVarSet("ResizeInProgress", False);
}

// Constants
circleRadius = 8;
minHeight = 10;

// Define Rectangle Positions
x1 = xOffset;
y2 = yOffset;
y1 = y2 - RiskHeight;
x2 = x1 + ParamSqrWidth;
y3 = y2 + RewardHeight;

// Define Corner Handles
circleTL_x = x1;
circleTL_y = y1;
circleBL_x = x1;
circleBL_y = y3;

// Get Mouse Position & Clicks
MousePx = GetCursorXPosition(1);
MousePy = GetCursorYPosition(1);
LButtonDown = GetCursorMouseButtons() & 1;
LButtonReleased = GetCursorMouseButtons() == 0;

// Check Cursor Proximity
CursorNearTopLeft = (abs(MousePx - circleTL_x) <= circleRadius AND abs(MousePy - circleTL_y) <= circleRadius);
CursorNearBottomLeft = (abs(MousePx - circleBL_x) <= circleRadius AND abs(MousePy - circleBL_y) <= circleRadius);
CursorInField = (MousePx >= x1 AND MousePx <= x2 AND MousePy >= y1 AND MousePy <= y3);

// Get Resize & Move Status
ResizeInProgress = Nz(StaticVarGet("ResizeInProgress"), False);
MoveInProgress = Nz(StaticVarGet("MoveInProgress"), False);

// Start Moving or Resizing
if (!MoveInProgress AND !ResizeInProgress)
{
    if (LButtonDown AND CursorInField AND !CursorNearTopLeft AND !CursorNearBottomLeft)
    {
        StaticVarSet("MoveInProgress", True);
        StaticVarSet("DownPx1", MousePx);
        StaticVarSet("DownPy1", MousePy);
    }
    else if (LButtonDown AND CursorNearTopLeft)
    {
        StaticVarSet("ResizeInProgress", True);
        StaticVarSet("ResizeType", 1); // Top resize
        StaticVarSet("StartMouseY", MousePy);
    }
    else if (LButtonDown AND CursorNearBottomLeft)
    {
        StaticVarSet("ResizeInProgress", True);
        StaticVarSet("ResizeType", 2); // Bottom resize
        StaticVarSet("StartMouseY", MousePy);
    }
}

// Stop Moving or Resizing on Mouse Release
if (LButtonReleased)
{
    StaticVarSet("MoveInProgress", False);
    StaticVarSet("ResizeInProgress", False);
}

// Moving Logic
if (MoveInProgress AND LButtonDown)
{
    DownPx1 = StaticVarGet("DownPx1");
    DownPy1 = StaticVarGet("DownPy1");
    xMove = MousePx - DownPx1;
    yMove = MousePy - DownPy1;

    xOffset += xMove;
    yOffset += yMove;

    // Prevent Moving Out of Bounds
    xOffset = Min(Max(xOffset, 10), Status("pxWidth") - ParamSqrWidth - 10);
    yOffset = Min(Max(yOffset, 10), Status("pxHeight") - RiskHeight - RewardHeight - 10);

    StaticVarSet("xOffset", xOffset);
    StaticVarSet("yOffset", yOffset);
    StaticVarSet("DownPx1", MousePx);
    StaticVarSet("DownPy1", MousePy);
}

// Resizing Logic
if (ResizeInProgress AND LButtonDown)
{
    ResizeType = StaticVarGet("ResizeType");
    StartMouseY = StaticVarGet("StartMouseY");

    if (ResizeType == 1) // Adjust top of red rectangle
    {
        deltaY = StartMouseY - MousePy;
        RiskHeight = Max(minHeight, RiskHeight + deltaY);
        StaticVarSet("RiskHeight", RiskHeight);
        StaticVarSet("StartMouseY", MousePy);
    }
    else if (ResizeType == 2) // Adjust bottom of green rectangle
    {
        deltaY = MousePy - StartMouseY;
        RewardHeight = Max(minHeight, RewardHeight + deltaY);
        StaticVarSet("RewardHeight", RewardHeight);
        StaticVarSet("StartMouseY", MousePy);
    }
}

// Update Positions for Drawing
xOffset = Nz(StaticVarGet("xOffset"), 50);
yOffset = Nz(StaticVarGet("yOffset"), 100);
RiskHeight = Nz(StaticVarGet("RiskHeight"), ParamSqrHeight);
RewardHeight = Nz(StaticVarGet("RewardHeight"), ParamSqrHeight);
x1 = xOffset;
y2 = yOffset;
y1 = y2 - RiskHeight;
x2 = x1 + ParamSqrWidth;
y3 = y2 + RewardHeight;
circleTL_x = x1;
circleTL_y = y1;
circleBL_x = x1;
circleBL_y = y3;

// Draw Rectangles with Customizable Colors
DrawRectangle(x1, y1, x2, y2, RiskColor);
DrawRectangle(x1, y2, x2, y3, RewardColor);

// Draw Circles for Resizing (Visual Feedback)
if (CursorNearTopLeft OR (ResizeInProgress AND StaticVarGet("ResizeType") == 1)) 
    DrawCircle(circleTL_x, circleTL_y, circleRadius, colorBlue, 0.3);
if (CursorNearBottomLeft OR (ResizeInProgress AND StaticVarGet("ResizeType") == 2)) 
    DrawCircle(circleBL_x, circleBL_y, circleRadius, colorBlue, 0.3);

// Calculate & Display Risk-Reward Ratio
RiskRewardRatio = IIf(RiskHeight > 0, RewardHeight / RiskHeight, 0);
Title = StrFormat("Smooth Risk-Reward Tool\nRisk-Reward Ratio: %.2f\nClick inside to move\nDrag top circle to adjust risk\nDrag bottom circle to adjust reward\nX1: %g | Y1: %g | Risk H: %g | Reward H: %g", 
                  RiskRewardRatio, x1, y1, RiskHeight, RewardHeight);

_SECTION_END();

Please point me in the right direction of how I should go about it. I referred to KB artices of moving GUI shapes, and the Gif that the user posted on my previous Original Post.

Also, attaching the .gif of my tool. Its too jerky and primitive. Please guide !

Gif screen record of my Tool (Only SHORT as of now)

@Noice try adding this line to your code:

RequestMouseMoveRefresh();

There are other things to fix, but since your managed to go this far, maybe with a little more of AI help you can reach your goal!

1 Like

Oh Wow ! It has made a world of difference to the tool. The tool is so much more responsive now ! The jerkiness has reduced significantly.

Thank you so much for this suggestion !

I also have few other doubts which I will post in due time for this tool. Like I wanted labels for Risk Price (SL) Entry Price (E) and Profit Price (P), as of now the code is taking all pixel values for x, y co-ords and so AI coding is find it difficult to read the Price values from Y Scale (Price scale). Especially when I am also doing multiple actions like dragging whole RECTANGLE or adjusting Risk or Reward Price vertically.

HI @Noice

Very nice start Bravo
I have made an afl with similar Risk-Profile Tool
if you like you can have a look to add or adjust your code, as i saw you need more work to do.
I sending here one of my very very early first version to study
Happy coding :grinning:

_SECTION_BEGIN("Risk-Profile-Static-GFX-ATR-Panos");
/*
Risk-Profile GFX version 0.3   04/07/2021 By Panos
// https://forum.amibroker.com/t/long-position-short-position-tool/39778/4
======= INSTRUCTIONS FOR USE ============
1) Press GuiButton "LONG"  and then click ONCE the mouse at the price you wish to BUY on the chart 

2) to Ajust the price of "E"ntry, "S"toploss, "T"arget 
2a) Press GuiButton "EDIT" and then HOLD one of the letters of your keyboard E,S,T and move your mouse higher/Lower to Ajust the price

Press the Letter on the Keyboard and move the mouse up or down, to edit with a new value  the Entry, Stoploss, Target 

this version is using ATR(14) 

*/

  Version(6.28);

Plot( C, "Price", colorDefault, styleCandle );

FormulaName = "Risk-Profile-Static-GFX-ATR-Panos.afl"; 
Prefix= "RiskProfileGFX"+GetChartID()+Name();

xshift = Param(" Button Χ axis Position ", 0,0,600,5);

function xGuiToggle( TextOFF, TextON, idset, x, y, width, height, notifyflag )
{  // For AB version(6.26) by Panos 09/09/2018
	GuiToggle(TextOFF, idset, x, y, width, height, notifyflag );
   
    if( GuiGetCheck( idset ) ) 	// If Toggle Button's is ON = 1
        GuiSetText( TextON, idset );     
        	    else 	GuiSetText( TextOFF, idset );
}
id = GuiGetEvent( 0, 0 ); event = GuiGetEvent( 0, 1);
GuiSetFont("arial",9);
GfxSetTextColor(colorYellow);

xGuiToggle( "LONG", "Start Long",  1,xshift+10, 40, 70, 25,1|64|128); if( id == 1 AND event == 64 ) GfxTextOut( "Enable For mouse click",100, 45 ); 
xGuiToggle( "SHORT","Start Short", 3,xshift+81, 40, 73, 25,1|64|128); if( id == 3 AND event == 64 ) GfxTextOut( "Enable For mouse click",100, 45 ); 

xGuiToggle( "Edit-OFF", " Edit-ON", 2,xshift+50, 65, 60, 25,1|64|128); if( id == 2 AND event == 64 ) GfxTextOut( "Enable To Edit",120, 65 ); 
// GuiSetColors( 1, 2, 2, colorRed, colorBlack, colorRed, colorWhite, colorBlue, colorYellow, colorRed, colorBlack, colorYellow );
 GuiSetColors( 1, 3, 2 , colorWhite,  colorCustom16, colorOrange, colorBlack,  colorPaleGreen,colorDarkGreen ); 

edit=0;
if( GuiGetCheck(2)) edit=1;  // you can edit the existing values

// UnCheck the no need buttons
if( GuiGetCheck( 1) )  { GuiSetCheck( 2, 0 ); GuiSetCheck( 3, 0 ); }  // LONG
if( GuiGetCheck( 2) )  { GuiSetCheck( 1, 0 ); GuiSetCheck( 3, 0 ); }  // EDIT
if( GuiGetCheck( 3) )  { GuiSetCheck( 1, 0 ); GuiSetCheck( 2, 0 ); }  // short

// Reset - Remove "ONLY" All StaticVar that we use in this AFL 
if( GetCursorMouseButtons() == 12 ) {  StaticVarRemove( Prefix + "*" );   GuisetCheck( 1, 0 ); GuisetCheck( 2, 0 ); GuisetCheck( 3, 0 );}


SetChartOptions( 2, chartHideQuoteMarker );
b = GetCursorMouseButtons();
bi = BarIndex();

//  here are all StaticVar that we use for this AFL      
SVClickCounter= Prefix+"ClickCounter";
ClickCounter = Nz(StaticVarGet(SVClickCounter),0);  // a Click Counter start from Zero
EntryBarNum = Nz( StaticVarGet( Prefix+"EntryBarNum" ), 0 );  printf("\n EntryBarNum  =\t" + EntryBarNum); 
MousePrice = Nz( StaticVarGet( Prefix+"Mouseprice" ), 0 );
Entry = Nz(StaticVarGet( Prefix+"Entry" ),0);
StopLoss = Nz(StaticVarGet( Prefix+"StopLoss" ),0); 
Target = Nz(StaticVarGet( Prefix+"Target" ),0); 
AtrY= SelectedValue(ATR(14));  printf("\n ATR  =\t" + atrY); 
PL=  Nz(StaticVarGet( Prefix+"PL" ),0);

printf( "\n Entry  =\t" + Entry );
printf("\n StopLoss = %g\t\n Target = %g\n", StopLoss, Target );   
printf( "\n BarIndex  =\t" + bi );

if( GuiGetCheck(1) OR GuiGetCheck(3))		// If Start is ON
if( b & 8 ) // flag = 8 is set when window just received mouse click
{
    x = GetCursorXPosition( 0 ); //  X-coordinate in DateTime format. y - value gives PRICE
    y = GetCursorYPosition( 0 );

    StaticVarSet( "MousePrice", y );

    if( b & 9 )
    {
        StaticVarSet( SVClickCounter, ClickCounter + 1 );

        if(  GuiGetCheck(1) AND StaticVarGet( SVClickCounter ) == 1 )
        {
            StaticVarSet( Prefix + "EntryBarNum", x ); //of the first mouse click, keep the barindex
            StaticVarSet( Prefix + "Entry", y );
			StaticVarSet( Prefix + "StopLoss", y - (atrY *1.5) );
            StaticVarSet( Prefix + "Target", y +(atrY *3) );  
            StaticVarSet( Prefix+"PL",1 ); 		// only for Text PL        
        GuisetCheck( 1, 0 );   RequestMouseMoveRefresh();
        }
        
        if(  GuiGetCheck(3) AND StaticVarGet( SVClickCounter ) == 1 )
        {
            StaticVarSet( Prefix + "EntryBarNum", x ); //of the first mouse click, keep the barindex
            StaticVarSet( Prefix + "Entry", y );
            StaticVarSet( Prefix + "StopLoss", y +(atrY *1.5) );
            StaticVarSet( Prefix + "Target", y - (atrY *3) );  
            StaticVarSet( Prefix+"PL",-1 );   	// only for Text PL        
        GuisetCheck( 3, 0 );   RequestMouseMoveRefresh();
        }

    }
}


//////// SECTION  FOR EDITING - /////////////////////
 // Press the a Letter on the Keyboard and move the mouse up or down, to edit with a new value of the Entry, Stoploss, or Target 

StopPressed = GetAsyncKeyState( 83 ) < 0; 	//Stop		S = 83;
TargetPressed = GetAsyncKeyState( 84 ) < 0; //Target 	T = 84; 
EntryPressed = GetAsyncKeyState( 69 ) < 0;  //Entry 	E = 69;

if( GuiGetCheck( 2 ) )
    {
        x = GetCursorXPosition( 0 ); //  X-coordinate in DateTime format. y - value gives PRICE
        y = GetCursorYPosition( 0 );

        //if( b & 9 )
            if( StaticVarGet( SVClickCounter ) >= 1 AND edit == 1 )
            {
                if( EntryPressed )  StaticVarSet( Prefix + "Entry", y );   //  GuiSetCheck( 2,0 );
                if( StopPressed )  StaticVarSet( Prefix + "StopLoss", y );
                if( TargetPressed )  StaticVarSet( Prefix + "Target", y );
                RequestMouseMoveRefresh();

            }
		
    }

//// END SECTION  FOR EDITING  ///////////////////////////

// Check for user errors, if any. Then for safety reasons start again, so the user will place the correct Prices
// We check if the values are placed correctly, otherwise AFL automatic will reset the Tool 
//  Τσεκάρουμε εάν οι τιμές έχουν τοποθετηθεί σωστά, διαφορετικά θα ακούσουμε μήνυμα
if( GuiGetCheck(2))
if( ClickCounter >=1 )
{
    if( ( Entry < StopLoss  AND  Entry < Target ) )  // for LONG Position
    {
        StaticVarRemove( Prefix + "*" );
        StaticVarSet( SVClickCounter, 0 );
		PL=  Nz(StaticVarGet( Prefix+"PL" ),0);
         GuisetCheck( 2, 0 );
    }

    if( ( Entry > StopLoss  AND Entry > Target ) ) 		// for SHORT Position
    {
        StaticVarRemove( Prefix + "*" );
        StaticVarSet( SVClickCounter, 0 );

         GuisetCheck( 2, 0 );
    }
}



// +1e-9 is to avoid Division By zero 
Reward=Target-Entry;
Risk=Entry-StopLoss;
RRR=Reward/(Risk+1e-9);  // Risk-Reward-Ratio
p_reward=reward*100/(Entry+1e-9);
p_Risk=Risk*100/(Entry+1e-9);

// Plot Gfx 
GfxSetZOrder(-1);
GfxSetBkMode( 1 );
GfxSetCoordsMode( 1 );
	x1 = Lookup( bi, EntryBarNum )-30;  // start GFX Rectangle X1 - 30 bars for visible reasons Only (delete or change -30 to what ever you like )   
	x2= LastValue(bi);	
	
	if ( Target>0) {
	GfxFillSolidRect( x1, StopLoss, x2, Entry, ColorRGB(42,0,0) ) ;
	GfxFillSolidRect( x1, Target, x2, Entry, ColorRGB(6,34,0) ) ;
	}	

		GfxSelectPen( colorLightOrange, 1, 1 );
		GfxMoveTo( x1, Entry ); 		GfxLineTo( x2, Entry );  
		GfxSelectPen( colorred ); 
		GfxMoveTo( x1, StopLoss ); 		GfxLineTo( x2, StopLoss );
		GfxSelectPen( colorGreen ); 		
		GfxMoveTo( x1, Target ); 		GfxLineTo( x2, Target );

// replace x2 with x1 to place the text on the left of the line		
shiftText = 1; 	
PlotTextSetFont(" Entry: "+ Prec( Entry, 4 ), "Calibri", 10, x2+shiftText, Entry, colorYellow, colorDefault,5 ); 	
PlotTextSetFont(" Stop: " + Prec(StopLoss,4), "Calibri", 10, x2+shiftText, StopLoss, colorred, colorDefault,5 ); 	
PlotTextSetFont(" Target: "+ Prec( Target, 4 ), "Calibri", 10, x2+shiftText, Target, colorGreen, colorDefault,5 ); 	
xx= Status( "FirstVisibleBar" );
// replace x1 with x2 to place the text in the right
PlotTextSetFont("RRR: "+Prec(abs(RRR),4), "Arial", 10, x1+shiftText, StopLoss, colorDefault, GetChartBkColor(),-20 ); 	
PlotTextSetFont("Risk: "+abs(int(Risk))+" ("+Prec(abs(P_Risk), 2 )+"%)", "Arial", 10, x1+shiftText, StopLoss+Risk/2 , colorRed,  GetChartBkColor() ,-10 ); 	
PlotTextSetFont("Reward in Pips: "+abs(int(Reward))+" ("+Prec(abs(p_reward), 2 )+"%)", "Arial", 10, x1+shiftText, Entry+Reward/2, colorGreen, GetChartBkColor(),-10 ); 	
shiftText_PL= 25;
ProfitLoss = IIf(PL==1 ,LastValue(c)- Entry  , IIf( PL==-1, Entry -LastValue(c), false ) );
PlotTextSetFont("P&L: " + round( ProfitLoss) , "Arial", 10, x1+shiftText_PL, StopLoss, colorYellow, GetChartBkColor(),-20 ); 

Title = "mouseButtons returns: " + (GetCursorMouseButtons()) + "\nclick counter: " + StaticVarGet(SVClickCounter) + " GetChartBkColor "+ GetChartBkColor() + "   ATR(14)= "+ NumToStr(AtrY,1.2) ; 


// we donot need the rest of below code

EncodeColor( colorBlue);
"\n<b> Read all pending events \n";
for( i = 0; GuiGetEvent( i, 0 ); i++ )
{
 id   = GuiGetEvent( i, 0 );
 code = GuiGetEvent( i, 1 );
 text = GuiGetEvent( i, 2 );
 
 printf("\n Id\t" +id + "\nCode \t" +code + "\nText \t" +text + "</b>");
}

_SECTION_END();

RiskProfileGFX

6 Likes

Very nice. Although a built-in tool would be nice as well. Speaking of TV tools, I would vote for a built-in tool, similar to the TV Tape measure/Measure tool, that in addition to percent, it could show price but support futures point value per 1 contract etc. Just a thought.

1 Like

Wonderful code,

This particular bit is very helpful for me

    x = GetCursorXPosition( 0 ); //  X-coordinate in DateTime format. y - value gives PRICE
    y = GetCursorYPosition( 0 );

I am still studying your code PanoS.

Thank you for your much needed help and appreciation Amibroker Coders !

I am attaching my complete code, now it is done even for LONG as well as SHORT R:R

_SECTION_BEGIN("Enhanced Risk-Reward Tool");


//Long-Short Tool for Amibroker by Noice
//https://forum.amibroker.com/t/long-position-short-position-tool/39778
//Press INVOKE turns ON / OFF the tool
//Press LONG or SHORT as the case may be
//Risk and Reward Rectangles can be resized vertically when you hover your mouse over the left upper and bottom corners of Rectangle. Rectangle can be moved to your position.
// CAUTION : Rectangles NOT fixed in your position, it will move when you scroll / zoom (trying to fix lol)


GfxSetOverlayMode(1);

// Function to Draw a Rectangle
function DrawRectangle(x1, y1, x2, y2, BackColor)
{
    GfxSelectSolidBrush(BackColor);
    GfxRectangle(x1, y1, x2, y2);
    _TRACE("Drawing Rectangle: x1=" + x1 + ", y1=" + y1 + ", x2=" + x2 + ", y2=" + y2);
}

// Function to Draw Circles for Resizing
function DrawCircle(cx, cy, radius, color, opacity)
{
    GfxSelectSolidBrush(ColorBlend(color, colorWhite, opacity));
    GfxCircle(cx, cy, radius);
    _TRACE("Drawing Circle: cx=" + cx + ", cy=" + cy + ", radius=" + radius);
}

// Function to Draw a Button (Adapted from GfxToolbox.afl)
function DrawButton(text, x, y, width, height, textColor, bgColor)
{
    GfxSelectSolidBrush(bgColor);
    GfxRectangle(x, y, x + width, y + height);
    GfxSelectFont("Arial", 10, 700);
    GfxSetTextColor(textColor);
    GfxSetBkMode(1);
    GfxTextOut(text, x + 5, y + 5);
}

// Function to Detect Left Click Inside (From GfxToolbox.afl)
function LeftClickInside(x, y, width, height)
{
    local px, py, res;
    res = False;
    if (GetCursorMouseButtons() & 8) // Left click
    {
        px = GetCursorXPosition(1);
        py = GetCursorYPosition(1);
        res = (px >= x) AND (py >= y) AND (px <= (x + width)) AND (py <= (y + height));
    }
    return res;
}

// Define Rectangle Size
ParamSqrWidth  = 150;
ParamSqrHeight = 50;

// Define Colors via Parameters
RiskColor = ParamColor("Risk Rectangle Color", colorRose);
RewardColor = ParamColor("Reward Rectangle Color", colorGreen);

// Persistent Variables
ToolVisible = Nz(StaticVarGet("ToolVisible"), False);
xOffset = Nz(StaticVarGet("xOffset"), 50);
yOffset = Nz(StaticVarGet("yOffset"), 100);
RiskHeight = Nz(StaticVarGet("RiskHeight"), ParamSqrHeight);
RewardHeight = Nz(StaticVarGet("RewardHeight"), ParamSqrHeight);
IsLongMode = Nz(StaticVarGet("IsLongMode"), False);

// Button Triggers (Param for compatibility)
InvokeTrigger = ParamTrigger("INVOKE", "INVOKE");
ResetTrigger = ParamTrigger("RESET", "RESET");
LongTrigger = ParamTrigger("LONG", "LONG");

// Chart Dimensions for Bounds
chartTop = Status("pxcharttop");
chartBottom = Status("pxchartbottom");
chartWidth = Status("pxwidth");

// Define Button Positions
invokeBtnX = 10;
invokeBtnY = chartBottom - 40;
btnWidth = 60;
btnHeight = 20;
longBtnX = invokeBtnX + btnWidth + 10;
longBtnY = invokeBtnY;

// Draw INVOKE Button (Always Visible)
DrawButton("INVOKE", invokeBtnX, invokeBtnY, btnWidth, btnHeight, colorWhite, colorGrey40);

// Handle Gfx Button Clicks
if (LeftClickInside(invokeBtnX, invokeBtnY, btnWidth, btnHeight) OR InvokeTrigger)
{
    ToolVisible = NOT ToolVisible;
    StaticVarSet("ToolVisible", ToolVisible);
}

if (LeftClickInside(longBtnX, longBtnY, btnWidth, btnHeight) AND ToolVisible OR LongTrigger)
{
    IsLongMode = NOT IsLongMode;
    StaticVarSet("IsLongMode", IsLongMode);
}

if (ResetTrigger)
{
    StaticVarSet("xOffset", 50);
    StaticVarSet("yOffset", 100);
    StaticVarSet("RiskHeight", ParamSqrHeight);
    StaticVarSet("RewardHeight", ParamSqrHeight);
    StaticVarSet("MoveInProgress", False);
    StaticVarSet("ResizeInProgress", False);
    StaticVarSet("ToolVisible", False);
    StaticVarSet("IsLongMode", False);
    xOffset = 50;
    yOffset = 100;
    RiskHeight = ParamSqrHeight;
    RewardHeight = ParamSqrHeight;
    ToolVisible = False;
    IsLongMode = False;
}

// Constants
circleRadius = 8;
minHeight = 10;

// Tool Logic (Only if Visible)
if (ToolVisible)
{
    // Draw LONG/SHORT Button
    longButtonText = "";
    if (IsLongMode)
        longButtonText = "SHORT";
    else
        longButtonText = "LONG";
    DrawButton(longButtonText, longBtnX, longBtnY, btnWidth, btnHeight, colorWhite, colorGrey40);

    // Define Rectangle Positions
    x1 = xOffset;
    y2 = yOffset;              // Entry line
    y1 = IIf(IsLongMode, y2 + RiskHeight, y2 - RiskHeight); // Risk: below Entry (Long), above Entry (Short)
    y3 = IIf(IsLongMode, y2 - RewardHeight, y2 + RewardHeight); // Reward: above Entry (Long), below Entry (Short)
    x2 = x1 + ParamSqrWidth;

    // Define Corner Handles
    circleTL_x = x1;
    circleTL_y = y1;
    circleBL_x = x1;
    circleBL_y = y3;

    // Get Mouse Position & Clicks
    MousePx = GetCursorXPosition(1);
    MousePy = GetCursorYPosition(1);
    LButtonDown = GetCursorMouseButtons() & 1;
    LButtonReleased = GetCursorMouseButtons() == 0;

    // Check Cursor Proximity
    CursorNearTopLeft = (abs(MousePx - circleTL_x) <= circleRadius AND abs(MousePy - circleTL_y) <= circleRadius);
    CursorNearBottomLeft = (abs(MousePx - circleBL_x) <= circleRadius AND abs(MousePy - circleBL_y) <= circleRadius);
    CursorInField = (MousePx >= x1 AND MousePx <= x2 AND MousePy >= Min(y1, y3) AND MousePy <= Max(y1, y3));

    // Get Resize & Move Status
    ResizeInProgress = Nz(StaticVarGet("ResizeInProgress"), False);
    MoveInProgress = Nz(StaticVarGet("MoveInProgress"), False);

    // Start Moving or Resizing
    if (!MoveInProgress AND !ResizeInProgress)
    {
        if (LButtonDown AND CursorInField AND !CursorNearTopLeft AND !CursorNearBottomLeft)
        {
            StaticVarSet("MoveInProgress", True);
            StaticVarSet("DownPx1", MousePx);
            StaticVarSet("DownPy1", MousePy);
            _TRACE("Move Started: MousePx=" + MousePx + ", MousePy=" + MousePy);
        }
        else if (LButtonDown AND CursorNearTopLeft)
        {
            StaticVarSet("ResizeInProgress", True);
            StaticVarSet("ResizeType", 1); // Top resize
            StaticVarSet("StartMouseY", MousePy);
            _TRACE("Resize Top Started: MousePy=" + MousePy);
        }
        else if (LButtonDown AND CursorNearBottomLeft)
        {
            StaticVarSet("ResizeInProgress", True);
            StaticVarSet("ResizeType", 2); // Bottom resize
            StaticVarSet("StartMouseY", MousePy);
            _TRACE("Resize Bottom Started: MousePy=" + MousePy);
        }
    }

    // Stop Moving or Resizing on Mouse Release
    if (LButtonReleased)
    {
        StaticVarSet("MoveInProgress", False);
        StaticVarSet("ResizeInProgress", False);
        _TRACE("Mouse Released");
    }

    // Moving Logic
    if (MoveInProgress AND LButtonDown)
    {
        DownPx1 = StaticVarGet("DownPx1");
        DownPy1 = StaticVarGet("DownPy1");
        xMove = MousePx - DownPx1;
        yMove = MousePy - DownPy1;

        xOffset += xMove;
        yOffset += yMove;

        // Prevent Moving Out of Bounds
        xOffset = Max(10, Min(xOffset, chartWidth - ParamSqrWidth - 10));
        yOffsetBoundUpper = IIf(IsLongMode, chartBottom - RiskHeight - 10, chartBottom - RewardHeight - 10);
        yOffsetBoundLower = IIf(IsLongMode, RewardHeight + 10, RiskHeight + 10);
        yOffset = Max(yOffsetBoundLower, Min(yOffset, yOffsetBoundUpper));

        StaticVarSet("xOffset", xOffset);
        StaticVarSet("yOffset", yOffset);
        StaticVarSet("DownPx1", MousePx);
        StaticVarSet("DownPy1", MousePy);

        x1 = xOffset;
        y2 = yOffset;
        y1 = IIf(IsLongMode, y2 + RiskHeight, y2 - RiskHeight);
        y3 = IIf(IsLongMode, y2 - RewardHeight, y2 + RewardHeight);
        x2 = x1 + ParamSqrWidth;
        circleTL_y = y1;
        circleBL_y = y3;
    }

    // Resizing Logic
    if (ResizeInProgress AND LButtonDown)
    {
        ResizeType = StaticVarGet("ResizeType");
        StartMouseY = StaticVarGet("StartMouseY");
        deltaY = MousePy - StartMouseY;

        if (ResizeType == 1) // Adjust top (Risk)
        {
            RiskHeightChange = IIf(IsLongMode, deltaY, -deltaY); // Long: up increases, Short: up decreases
            RiskHeight = Max(minHeight, RiskHeight + RiskHeightChange);
            StaticVarSet("RiskHeight", RiskHeight);
            StaticVarSet("StartMouseY", MousePy);
            y1 = IIf(IsLongMode, y2 + RiskHeight, y2 - RiskHeight);
            circleTL_y = y1;
        }
        else if (ResizeType == 2) // Adjust bottom (Reward)
        {
            RewardHeightChange = IIf(IsLongMode, -deltaY, deltaY); // Long: up decreases, Short: up increases
            RewardHeight = Max(minHeight, RewardHeight + RewardHeightChange);
            StaticVarSet("RewardHeight", RewardHeight);
            StaticVarSet("StartMouseY", MousePy);
            y3 = IIf(IsLongMode, y2 - RewardHeight, y2 + RewardHeight);
            circleBL_y = y3;
        }
    }

    // Draw Rectangles
    DrawRectangle(x1, y1, x2, y2, RiskColor);    // Risk
    DrawRectangle(x1, y2, x2, y3, RewardColor);  // Reward

    // Draw Circles for Resizing
    if (CursorNearTopLeft OR (ResizeInProgress AND StaticVarGet("ResizeType") == 1)) 
        DrawCircle(circleTL_x, circleTL_y, circleRadius, colorBlue, 0.3);
    if (CursorNearBottomLeft OR (ResizeInProgress AND StaticVarGet("ResizeType") == 2)) 
        DrawCircle(circleBL_x, circleBL_y, circleRadius, colorBlue, 0.3);

    // Calculate & Display Risk-Reward Ratio
    RiskRewardRatio = IIf(RiskHeight > 0, RewardHeight / RiskHeight, 0);
    modeText = "";
    if (IsLongMode)
        modeText = "Long";
    else
        modeText = "Short";
    Title = StrFormat("Risk-Reward Tool (%s)\nRisk-Reward Ratio: %.2f\nClick inside to move\nDrag top circle to adjust risk\nDrag bottom circle to adjust reward\nX1: %g | Y1: %g | Risk H: %g | Reward H: %g", 
                      modeText, RiskRewardRatio, x1, y1, RiskHeight, RewardHeight);

    RequestMouseMoveRefresh();
}

_SECTION_END();

Do suggest corrections (to make the tool more effective) or sections of useless code or logic.

Very nice tool Bro :pray:
Can its possible to move X Positions ( Horizontal ) Left to Right etc.

Hello. Why you need to move the X ?

Any way, yes you can change the X axis position of the GFX Rectangle.

X1 and X2 is the window of the bars for the GFX Rectangle, ( below example has x1-30 bars back and x2+10 bars ahead )

	x1 = Lookup( bi, EntryBarNum ) - 30;  // Start of GFX Rectangle X1 - 30 bars for visible reasons Only (delete or change -30 to what ever you like )   
	x2 = LastValue(bi) + 10;	 			// End  of GFX  Rectangle X2 + 10 bars in the future
2 Likes