AJ's Position Size Calculator for Forex and Stocks
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © Algo_Jo

//@version=5
//Version 2.0

//I can't guarentee the output of this caculator is correct, use at your own risk
indicator("AJ's Position Size Calculator for Forex and Stocks", shorttitle = "AJ's PSC", overlay = true, precision = 4)

//User Input
//Account Statistics
Acc_Stats   = "Account Stats"
Acount_Size = input.float(title = "Account Value", defval = 100000.00, confirm = true, group = Acc_Stats)
Account_Cur = input.string(title = "Account Currency", defval = "USD", confirm = true, group = Acc_Stats, options = ["USD", "CAD", "EUR", "AUD", "NZD", "CHF", "GBP", "SGD", "JPY"])
T_Direction = input.string(title = "Trade Direction", defval = "Long", options = ["Long", "Short"])
risk_Amnt   = input.float(title = "Risk % Per Trade", defval = 1.0)

use_ATR  = input.bool(title = "Use Atr To Calculate Stop Loss?", defval = true, group = "ATR")
use_rtp  = input.bool(title = "Use Real Time Price?", defval = false, tooltip = "Use real time price to calculate SL & TP, If false, will use the previous close.", group = "Price Source")
ATRper   = input.int(title = "ATR Period", defval = 14, group = "ATR")
ATRMult  = input.float(title = "ATR Multiplier for SL", defval = 1.5, group = "Stop Loss")
SLtype   = input.string(title = "SL value Type", defval = "Ratio", options = ["Ratio","Manual"], tooltip = "Choose whether Stop Loss is manual or a ratio of stop loss distance from price", group = "Stop Loss")
SL_Price = input.float(title = "Stop Loss Price", defval = 0.00, group = "Stop Loss", tooltip = "Choose 'Manual' for Stop Loss price placment then enter price")

TPtype   = input.string(title = "TP value Type", defval = "Ratio", options = ["Ratio","Manual"], tooltip = "Choose whether Profit Target is manual or a ratio of stop loss distance from price", group = "Profit Target")
TP_ratio = input.float(title = "Profit Target Ratio", defval = 1.5, group = "Profit Target", tooltip =  "Reward to Risk Ratio")
TP_price = input.float(title = "Profit Target Price", defval = 0.00, group = "Profit Target", tooltip = "Choose 'Manual' for Profit Target price placment then enter price")

Chart_settings = "Chart Settings"
p_table  = input.bool(title = "Plot Table to Chart", defval = true, group = Chart_settings)
show_LL  = input.bool(title = "Show Line Labels", defval = true, tooltip = "Show distance and $ value for TP/SL near lines?", group = Chart_settings)
txt_color = input.color(title = "Tabel Text Color", defval = color.white, group = Chart_settings)
add_SLL  = input.string(title = "Show SL Distance", defval = " $ ", options = [" $ ", " % "], group = Chart_settings)
theme_color = input.color(title = "Chart Border Color", defval = color.gray, group = Chart_settings)
bgcolor = input.color(title = "Table and label background colors", defval = color.black, group = Chart_settings)
sl_color = input.color(title = "Stop Loss Color", defval = color.red, group = Chart_settings)
tp_color = input.color(title = "Profit Target Color", defval = color.green, group = Chart_settings)
eLine_C  = input.color(title = "Entry Line Color", defval = color.blue, group = Chart_settings)
plotSL   = input.bool(title = "Plot Stop Loss and Profit line?", defval = true, group = Chart_settings)
fillSL   = input.float(title = "Fill Stop Loss and Profit Level Transperency", defval = 70.0, group = Chart_settings)

LSL = "Lot Sizes and Leverage"
LT_size  = input.float(title = "Standard Forex Lot Size in $", defval = 100000.00, group = LSL)
FXleverage = input.float(title = "Forex Leverage, ( ____ :1)", defval = 500.0, group = LSL)
//Crleverage = input.float(title = "Crypto Leverage, ( ____ :1)", defval = 10.0, group = LSL)
Stleverage = input.float(title = "Stocks & ETF's Leverage, ( ____ :1)", defval = 3.0, group = LSL)
//cfdlev     = input.float(title = "CFD's Leverage ( ____ :1)", defval = 500.0, group = LSL)
min_Lot    = input.float(title = "Minimum Lot Size (Forex)", defval = 0.01, group = LSL)

//Truncate Function (get value to desired decimal place)
truncate(number, decimals) =>
    factor = math.pow(10, decimals)
    int(number * factor) / factor

//Add ATR For stop loss calculations
atr = ta.atr(ATRper)


//Get Risk amount in dollars of account currrency 
risk = risk_Amnt/100
risk_dollars = risk*Acount_Size

//Get Current Price
float C_price = 0.0 
if use_rtp 
    C_price := close
else if use_rtp == false
    C_price := close[1] 


//Get minimum point move
float P_value = syminfo.mintick

//Get Type of symbol (forex,crypto,stock,cfd,etc..)
Tckr_Type = syminfo.type

// Dollar amount per pip per Lot in account currency 
float dollar_PPpl =  syminfo.pointvalue 

//Declare Stop loss and profit levels
float sl_Val = 0.00
float tp_Val = 0.00

//Get Stop and Profit levels
if SLtype == "Manual"
    sl_Val := SL_Price
if SLtype == "Ratio"
    if T_Direction == "Long"
        sl_Val := C_price - (atr * ATRMult)
    if T_Direction == "Short"
        sl_Val := C_price + (atr * ATRMult)
if TPtype == "Manual"
    tp_Val := TP_price
if TPtype == "Ratio"
    if T_Direction == "Long"
        tp_Val := C_price + (atr * (TP_ratio * ATRMult))    
    if T_Direction == "Short"
        tp_Val := C_price - (atr * (TP_ratio * ATRMult))

//Get Stop and Profit Distance from current price
float slDistace  = 0.00
float tpDistance = 0.00
//Get stop and profit in $ or %
if add_SLL == " $ "
    slDistace  := math.abs(C_price - sl_Val)
    tpDistance := math.abs(C_price - tp_Val)
if add_SLL == " % "
    slDistace  := ((math.abs(C_price - sl_Val))/C_price) * 100
    tpDistance := ((math.abs(C_price - tp_Val))/C_price) * 100

//Declare Variables for used for getting correct lot size and margin
float rr_Ratio = tpDistance/slDistace //Reward to Risk Ratio
float pip       = P_value // Value of a single point for a standard lot in account currency 
string point_id = "Points" //Identifier if a "Point" movment ("Pip" for CFD's and forex)
string c_id     = "Lots" //Identifier of units ("Shares" for stocks and ETF's)
float lot       = 1 //Number of Units in a Lot (forex = "100000")
float mnLTssz   = 0.01 //Minimum Lot Size(Micro Lot for forex, shares for stocks)
int   tvalue    = 2//truncate value (decimal places)
string quoteCurrency = syminfo.currency //Get the qouted currency of the current symbol
float m_factor = 0 //multiplication factor to get correct lot size
float margin = 0.00 //Withheld margin according to your brokers leverage
int   rval   = 0
//Get Values for Forex tickers
if Tckr_Type == "forex"
    pip      := P_value //* 10//0.0001
    point_id := "PIPS"
    lot      := 100000
    rval     := 2
    if P_value == 0.00001
        m_factor := 1
        tvalue   := 5
    //Get Correct sizes for JPY pairs
    if P_value == 0.001
        m_factor := .01
        tvalue   := 3
        
//Get Values for CFD Tickers
else if Tckr_Type == "cfd" 
    pip      := P_value //* 10//0.0001
    point_id := "PIPS"
    //Values for Silver CFD
    if syminfo.ticker == "XAGUSD"
        lot := 5000
        m_factor := 1000
    //Values for Oil CFD's    
    if syminfo.ticker == "USOIL" or syminfo.ticker == "USOILSPOT"
        lot := 1000
        if P_value == 0.001
            m_factor := 10
        if P_value == 0.01
            m_factor := 1
    //Values for Gold CFD        
    if syminfo.ticker == "XAUUSD"
        lot := 100
        m_factor := 100
    else
        if P_value == 0.00001
            tvalue   := 5
            m_factor := 1000
        if P_value == 0.0001
            m_factor := 100
        if P_value == 0.001
            tvalue   := 3
            m_factor := 10
        if P_value == 0.01
            m_factor := 10

//Get Values for Stocks or ETF's 
else if Tckr_Type == "stock" or Tckr_Type == "fund"  
    pip      := P_value //0.01
    dollar_PPpl := syminfo.pointvalue * syminfo.mintick //Update Tick value       
    point_id := "Points"
    c_id     := "Shares"
    tvalue   := 2
    lot      := 1
    min_Lot  := 1
    rval     := 0
    m_factor := 1

//Get Values for Crypto
else if Tckr_Type =="crypto"
    dollar_PPpl := syminfo.pointvalue * syminfo.mintick
    lot         := 1
    tvalue      := 5
    point_id    := "Points"
    c_id        := "Lots"

else 
    pip      := P_value//0.01        
    point_id := "Points"
    c_id     := "Lots"
    mnLTssz  := 1.0
    tvalue   := 2
    lot      := 1

//Convert Account Currency to USD (to increase accuracy because most currencies are qouted against USD)
float gbpUSDrate = request.security('GBPUSD', 'D', close)
float audUSDrate = request.security('AUDUSD', 'D', close)
float nzdUSDrate = request.security('NZDUSD', 'D', close)
float cadUSDrate = request.security('CADUSD', 'D', close)
float chfUSDrate = request.security('CHFUSD', 'D', close)
float eurUSDrate = request.security('EURUSD', 'D', close)
float jpyUSDrate = request.security('JPYUSD', 'D', close)
float sgdUSDrate = request.security('SGDUSD', 'D', close)

//declare variables for getting account values
float ex_rate  = 1.0
float USD_val  = Acount_Size
float pointVal = 0.0

//Convert Account Value to USD
if Account_Cur == "CAD"
    ex_rate := cadUSDrate 
    USD_val := ex_rate * Acount_Size
if Account_Cur == "EUR"
    ex_rate := eurUSDrate 
    USD_val := ex_rate * Acount_Size
if Account_Cur == "AUD"
    ex_rate := audUSDrate 
    USD_val := ex_rate * Acount_Size
if Account_Cur == "NZD"
    ex_rate := nzdUSDrate 
    USD_val := ex_rate * Acount_Size
if Account_Cur == "CHF"
    ex_rate := chfUSDrate 
    USD_val := ex_rate * Acount_Size
if Account_Cur == "GBP"
    ex_rate := gbpUSDrate 
    USD_val := ex_rate * Acount_Size
if Account_Cur == "SGD"
    ex_rate := sgdUSDrate 
    USD_val := ex_rate * Acount_Size
if Account_Cur == "JPY"
    ex_rate := jpyUSDrate 
    USD_val := ex_rate * Acount_Size

//Get $USD per point per lot    
float sl_Distance = math.abs(C_price - sl_Val)
float point_value = risk_dollars / (sl_Distance/pip) 
float USD_dppPL   = 0.0

//Convert Quote Currency amount per lot per pip to USD (to increase accuracy because most currencies are quoted against USD)
if quoteCurrency == "USD"
    USD_dppPL := dollar_PPpl
if quoteCurrency == "CAD" 
    USD_dppPL := (dollar_PPpl * cadUSDrate)
if quoteCurrency == "EUR" 
    USD_dppPL := (dollar_PPpl * eurUSDrate)
if quoteCurrency == "AUD" 
    USD_dppPL := (dollar_PPpl * audUSDrate)
if quoteCurrency == "NZD" 
    USD_dppPL := (dollar_PPpl * nzdUSDrate)
if quoteCurrency == "CHF" 
    USD_dppPL := (dollar_PPpl * chfUSDrate)
if quoteCurrency == "GBP" 
    USD_dppPL := (dollar_PPpl * gbpUSDrate)
if quoteCurrency == "SGD" 
    USD_dppPL := (dollar_PPpl * sgdUSDrate)
if quoteCurrency == "JPY" 
    USD_dppPL := ((dollar_PPpl* 100) * jpyUSDrate)

//Get Lot size in USD
USD_risk = risk_dollars * ex_rate//Convert Risk amount to USD
TicksSL  = sl_Distance / syminfo.mintick // Stop loss in ticks
fullLotV = TicksSL * USD_dppPL //Get risk amount for a full lot 
USD_lotsize = math.round((USD_risk/fullLotV),rval)  //Get ideal lot size     
Actual_Risk = USD_lotsize * (TicksSL * USD_dppPL) // Get actual risk in USD for ideal lot size
ffLotv = Actual_Risk > USD_risk and USD_lotsize > min_Lot ? USD_lotsize - min_Lot : USD_lotsize // If risk for ideal lot size is greater than desired risk, reduce lot size by minimum lot size amount 
final_lot = ffLotv < min_Lot ? min_Lot : ffLotv // IF lot size is smaller than minimum lot size, use minimum lot size
clotvalue = fullLotV / ex_rate // Full lot value in account currency

//Final Risk dollars and percent outcome for lot size
friskdollars =  final_lot * clotvalue //Get Risked dollars amount in account currency     
friskper     = friskdollars / Acount_Size // Get risked dollars percent value of account 
riskpercent  = friskper * 100 //multiply by 100 to get percentage value

//Get Margin amount for trade lot size
if Tckr_Type == "forex" 
    margin := (final_lot * ((lot*m_factor) * C_price)) / FXleverage
if Tckr_Type == "stock" or Tckr_Type == "fund"
    margin := (final_lot * ((lot*m_factor) * C_price)) / Stleverage
//if Tckr_Type == "crypto"
    //margin := (final_lot * ((lot*m_factor) * C_price)) / Crleverage
//if Tckr_Type == "cfd"
    //margin := (final_lot * ((lot*m_factor) * C_price)) / cfdleverage

marPerc = (margin / Acount_Size) * 100 

//Create Labels and plots for stop and profit targets
sl_label =  "SL: " + str.tostring(truncate(sl_Val, tvalue)) + "\n" + "SL Distance: " + add_SLL + str.tostring(truncate(slDistace,tvalue))
tp_label =  "TP: " + str.tostring(truncate(tp_Val, tvalue)) + "\n" + "TP Distance: " + add_SLL + str.tostring(truncate(tpDistance,tvalue))

//Set TP and SL label style
Syloc = label.style_label_up
tyloc = label.style_label_down
if T_Direction == "Short"
    Syloc := label.style_label_down
    tyloc := label.style_label_up

//Plot Stop Loss and Take Profit
sl_Line = plotSL ? line.new(x1 = bar_index[1], y1 = sl_Val, x2 = bar_index + 10, y2 = sl_Val, extend = extend.none, color = sl_color, style = line.style_solid, width = 2) : na
tp_Line = plotSL ? line.new(x1 = bar_index[1], y1 = tp_Val, x2 = bar_index + 10, y2 = tp_Val, extend = extend.none, color = tp_color, style = line.style_solid, width = 2) : na
en_Line = plotSL ? line.new(x1 = bar_index[1], y1 = C_price, x2 = bar_index + 10, y2 = C_price, extend = extend.none, color = eLine_C, style = line.style_solid, width = 2) : na 
line.delete(sl_Line[1])
line.delete(tp_Line[1])
line.delete(en_Line[1])

//Fill stop and profit lines
slfill = linefill.new(en_Line, sl_Line, color = color.new(sl_color, fillSL))
tpfill = linefill.new(en_Line, tp_Line, color = color.new(tp_color, fillSL))

//plot stop and profit labels
slLabl = show_LL ? label.new(x = bar_index + 10, y = sl_Val, text = sl_label, color = bgcolor, textcolor = sl_color, style = Syloc) : na 
tpLabl = show_LL ? label.new(x = bar_index + 10, y = tp_Val, text = tp_label, color = bgcolor, textcolor = tp_color, style = tyloc) : na 
label.delete(slLabl[1])
label.delete(tpLabl[1])

//Check if Caclulator has been configured for current Symbols
tckrconfig = syminfo.type == "stock" or syminfo.type == "fund" or syminfo.type == "forex" ? true : false 
//Dislay Text for non configured symbols
string nconfigtxt =  "Current Ticker has \n not yet been configured \n and added to postion sizing \n Updates may come \n in the future \n to support current \n ticker."

//Declare Table stats
var table testTable1 = table.new(position.middle_right, 2, 20, frame_color = theme_color, frame_width = 2, border_color = theme_color, border_width = 2)
f_fillCell1(_table, _column, _row, _value, _bgcolor, _txtcolor, _txtSize) =>
    _cellText = _value
    table.cell(_table, _column, _row, _cellText, bgcolor=_bgcolor, text_color=_txtcolor, text_size=_txtSize)
if p_table
    // Update table
    f_fillCell1(testTable1, 0, 0, "Position Size", bgcolor, txt_color, size.small)
    f_fillCell1(testTable1, 1, 0, str.tostring(truncate(final_lot,2)) + " " + c_id, bgcolor, txt_color, size.small)
    f_fillCell1(testTable1, 0, 1, "Stop Loss", bgcolor, txt_color, size.small)
    f_fillCell1(testTable1, 1, 1, "$ " + str.tostring(truncate(sl_Val,tvalue)), bgcolor, color.red, size.small)
    f_fillCell1(testTable1, 0, 2, "Profit Target", bgcolor, txt_color, size.small)
    f_fillCell1(testTable1, 1, 2, "$ " + str.tostring(truncate(tp_Val,tvalue)), bgcolor, color.lime, size.small)
    f_fillCell1(testTable1, 0, 3, "Stop Loss Distance", bgcolor, txt_color, size.small)
    f_fillCell1(testTable1, 1, 3, add_SLL + str.tostring(truncate(slDistace,tvalue)), bgcolor, color.red, size.small)
    f_fillCell1(testTable1, 0, 4, "Profit Target Distance ", bgcolor, txt_color, size.small)
    f_fillCell1(testTable1, 1, 4, add_SLL + str.tostring(truncate(tpDistance,tvalue)), bgcolor, color.lime, size.small)
    f_fillCell1(testTable1, 0, 5, "Reward:Risk Ratio", bgcolor, txt_color, size.small)
    f_fillCell1(testTable1, 1, 5, str.tostring(truncate(rr_Ratio,2)), bgcolor, color.green, size.small)
    f_fillCell1(testTable1, 0, 6, "Actual Risk $", bgcolor, txt_color, size.small)
    f_fillCell1(testTable1, 1, 6, tckrconfig ? "$ " + str.tostring(truncate(friskdollars,2)) : "", bgcolor, riskpercent < (risk* 100) ? color.lime : color.red, size.small)
    f_fillCell1(testTable1, 0, 7, "Actual Risk %", bgcolor, txt_color, size.small)
    f_fillCell1(testTable1, 1, 7, tckrconfig ? str.tostring(truncate(riskpercent,2)) + "%" : "", bgcolor, riskpercent < (risk * 100)  ? color.lime : color.red, size.small)
    f_fillCell1(testTable1, 0, 8, "Account Balance", bgcolor, txt_color, size.small)
    f_fillCell1(testTable1, 1, 8, "$ " + str.tostring(truncate(Acount_Size,2)) + " " + Account_Cur, bgcolor, txt_color, size.small)
    f_fillCell1(testTable1, 0, 9, "Margin $", bgcolor, txt_color, size.small)
    f_fillCell1(testTable1, 1, 9, tckrconfig ? "$ " + str.tostring(truncate(margin,2)) : "", bgcolor, txt_color, size.small)
    f_fillCell1(testTable1, 0, 10, "Margin % of Entire Account", bgcolor, txt_color, size.small)
    f_fillCell1(testTable1, 1, 10, tckrconfig ? str.tostring(truncate(marPerc,2)) + "%" : "", bgcolor, txt_color, size.small)
    f_fillCell1(testTable1, 0, 11, "Trade Direction", bgcolor, txt_color, size.small)
    f_fillCell1(testTable1, 1, 11, T_Direction, bgcolor, T_Direction == "Long" ? color.green : color.red, size.small)
    f_fillCell1(testTable1, 0, 12, "ATR Period", bgcolor, txt_color, size.small)
    f_fillCell1(testTable1, 1, 12, str.tostring(ATRper), bgcolor, txt_color, size.small)
    f_fillCell1(testTable1, 0, 13, "ATR Value", bgcolor, txt_color, size.small)
    f_fillCell1(testTable1, 1, 13, "$ " + str.tostring(truncate(atr,tvalue)), bgcolor, txt_color, size.small)
    f_fillCell1(testTable1, 0, 14, "Alog_Jo's Position Size & Stop Loss Calulator \n for Forex and Stocks", bgcolor, txt_color, size.tiny)
    f_fillCell1(testTable1, 1, 14, tckrconfig != true ? nconfigtxt : "", bgcolor, color.red, size.tiny)


