50 Pips A Day Strategy - Kaspricci
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © Kaspricci

//@version=5
strategy("50 Pips A Day Strategy - Kaspricci", overlay=true, initial_capital = 1000, currency = currency.USD, default_qty_type = strategy.percent_of_equity, default_qty_value = 100, margin_long = 0, margin_short = 0, calc_on_every_tick = true, calc_on_order_fills = false, slippage = 0, close_entries_rule = "ANY")

headlineEntry   = "Entry Seettings"

tooltipTF       = "Combined timeframe of London and New York Stock Exchange opening hours from 7:00 - 15:30 GMT and 13:30 - 20:00 GMT"

openingSession  = input.session(defval="0700-0800:23456", title="Opening Time", group=headlineEntry, tooltip="GMT timezone")
openingTime     = time(timeframe.period, openingSession, "GMT")

tradingSession  = input.session(defval="0800-2000:23456", title="Trading Time", group=headlineEntry, tooltip=tooltipTF)
tradingTime     = time(timeframe.period, tradingSession, "GMT")

buyStopSource   = input.source( defval = high, title = "Source for Buy Stop order", inline = "B", group=headlineEntry)
buyStopGap      = input.float(  defval = 2.0, minval = 0, step = 0.1, title = " + Pips:", inline = "B", group=headlineEntry)

sellStopSource  = input.source( defval = low, title = "Source for Sell Stop order", inline = "S", group=headlineEntry)
sellStopGap     = input.float(  defval = 2.0, minval = 0, step = 0.1, title = " - Pips:", inline = "S", group=headlineEntry)

closePending    = input.bool(   defval = true, group = headlineEntry, title = "Cancel pending order when new trade entered?", tooltip = "This will cancel the reamaining order, once the first trade has been entered.")

headlineTrade   = "Trade Seettings"

stopLossType    = input.string(defval = "FIX", title = "Stop Loss Type", options = ["ATR", "FIX"], group = headlineTrade)

atrLength       = input.int(defval = 14, title = "   ATR: Length        ",   inline = "ATR", group = headlineTrade, minval = 1)
atrFactor       = input.float(defval = 2.0, title = "Factor        ", inline = "ATR", group = headlineTrade, tooltip = "multiplier for ATR value", minval = 0, step = 0.05)
takeProfitRatio = input.float(defval = 2.0, title = "            TP Ration", group = headlineTrade, tooltip = "Multiplier for Take Profit calculation", minval = 0, step = 0.05)

fixStopLoss     = input.float(defval = 15.0, title = "   FIX: Stop Loss    ",   inline = "FIX", group = headlineTrade, minval = 0, step = 0.5) * 10 // need this in ticks
fixTakeProfit   = input.float(defval = 50.0, title = "Take Profit", inline = "FIX", group = headlineTrade, tooltip = "in pips", minval = 0, step = 0.5) * 10 // need this in ticks

useRiskMagmt    = input.bool(  title = "", defval = false, group = headlineTrade, inline = "RM")
riskPercent     = input.float( title = "Risk in %       ", defval = 1.0, minval = 0.0, step = 0.5, group = headlineTrade, inline = "RM", tooltip = "This will overwrite quantity from startegy settings and calculate the trade size based on stop loss and risk percent") / 100
rsikCapital     = input.string(defval = "Balance",                     group = headlineTrade, inline = "RM",    title = "",                          options = ["Balance", "Initial"], tooltip = "This will overwrite quantity from startegy settings and calculate the trade size based on stop loss and risk percent")
applyLotSizing  = input.bool(defval = true, title = "Apply Lot sizing", tooltip = "When enabled, the quantity will be rounded to the minimum lot size of 1000 (0.01 lots)")

stopLoss        = switch stopLossType
    "ATR" => math.round(ta.atr(atrLength) * atrFactor / syminfo.mintick, 0)
    "FIX" => fixStopLoss

takeProfit      = switch stopLossType
    "ATR" => math.round(stopLoss * takeProfitRatio, 0)
    "FIX" => fixTakeProfit

// calculate quantity
baseCapital = switch rsikCapital
    "Balance" => strategy.initial_capital + strategy.netprofit
    "Initial" => strategy.initial_capital

riskAmount      = strategy.convert_to_symbol(baseCapital * riskPercent)
tickValue       = syminfo.pointvalue * syminfo.mintick

riskBasedQty    = riskAmount / stopLoss / tickValue
riskBasedQty    := applyLotSizing ? math.round(riskBasedQty / 100000, 2) * 100000 : riskBasedQty

quantity        = useRiskMagmt ? riskBasedQty : na // if NaN the default from strategy settings will be used


inTradingTime   = not na(tradingTime)
inOpeningTime   = not na(openingTime)

var float buyStopPrice  = na
var float sellStopPrice = na

plot(inTradingTime ? buyStopPrice  : na, title = "Buy Stop Price",  color = color.green, style = plot.style_linebr)
plot(inTradingTime ? sellStopPrice : na, title = "Sell Stop Price", color = color.red,   style = plot.style_linebr)


///////////////////////////////////////////////////////////////////////////////
// api bot values

longSL          = buyStopPrice - stopLoss * syminfo.mintick
longTP1         = buyStopPrice + takeProfit * syminfo.mintick * 0.5 // 50% of total take profit
longTP2         = buyStopPrice + takeProfit * syminfo.mintick // same as full
longTPFull      = buyStopPrice + takeProfit * syminfo.mintick

shortSL         = sellStopPrice + stopLoss * syminfo.mintick
shortTP1        = sellStopPrice - takeProfit * syminfo.mintick * 0.5 // 50% of total take profit
shortTP2        = sellStopPrice - takeProfit * syminfo.mintick
shortTPFull     = sellStopPrice - takeProfit * syminfo.mintick

takeAmount1     = riskPercent / 2
takeAmount2     = riskPercent
takeAmountFull  = riskPercent

headlineAlerts  = "Alert Settings"

i_apiEnterLong  = input.text_area('Enter Long - 3commas,alerteron etc', 'API Entry - Long',   group = headlineAlerts)
i_apiExitLong   = input.text_area('Exit Long - 3commas,alerteron etc', 'API Exit - Long',     group = headlineAlerts)
i_apiEnterShort = input.text_area('Enter Short - 3commas,alerteron etc', 'API Entry - Short', group = headlineAlerts)
i_apiExitShort  = input.text_area('Exit Short - 3commas,alerteron etc', 'API Exit - Short',   group = headlineAlerts)


// #nl# => New line
// #side# => Buy or Sell (Long or Short)
// #symbol# => The ticker e.g. BTCUSDT
f_replaceKeys (_str) =>
    _value = _str
    _value := str.replace_all(_value, "#nl#", "\n")
//    _value := str.replace_all(_value, "#side#", "\n") // does not make sense to me...
    _value := str.replace_all(_value, "#symbol#", syminfo.ticker)

// #LongSL# => Long Stop loss
// #LongTP1# => Long Take Profit 1
// #LongTP2# => Long Take Profit 2
// #LongTPFULL# => Long Take Profit in FULL
// #TakeAmount1# => Percent to remove from trade on hitting TP1
// #TakeAmount2# => Percent to remove from trade on hitting TP2
// #TakeAmountFULL# => Percent to remove from trade on hitting TP3 (should be 100%)
f_replaceLongTokens (_str, _sl, _tp1, _tp2, _tpFull, _ta1, _ta2, _taFull) =>
    _value = _str
    _value := str.replace_all(_value, "#LongSL#", str.tostring(_sl))
    _value := str.replace_all(_value, "#LongTP1#", str.tostring(_tp1))
    _value := str.replace_all(_value, "#LongTP2#", str.tostring(_tp2))
    _value := str.replace_all(_value, "#LongTPFULL#", str.tostring(_tpFull))
    _value := str.replace_all(_value, "#TakeAmount1#", str.tostring(_ta1))
    _value := str.replace_all(_value, "#TakeAmount2#", str.tostring(_ta2))
    _value := str.replace_all(_value, "#TakeAmountFULL#", str.tostring(_taFull))

// #ShortSL# => Short Stop loss
// #ShortTP1# => Short Take Profit 1
// #ShortTP2# => Short Take Profit 2
// #ShortTPFULL# => Short Take Profit in FULL
// #TakeAmount1# => Percent to remove from trade on hitting TP1
// #TakeAmount2# => Percent to remove from trade on hitting TP2
// #TakeAmountFULL# => Percent to remove from trade on hitting TP3 (should be 100%)
f_replaceShortTokens (_str, _sl, _tp1, _tp2, _tpFull, _ta1, _ta2, _taFull) =>
    _value = _str
    _value := str.replace_all(_value, "#ShortSL#", str.tostring(_sl))
    _value := str.replace_all(_value, "#ShortTP1#", str.tostring(_tp1))
    _value := str.replace_all(_value, "#ShortTP2#", str.tostring(_tp2))
    _value := str.replace_all(_value, "#ShortTPFULL#", str.tostring(_tpFull))
    _value := str.replace_all(_value, "#TakeAmount1#", str.tostring(_ta1))
    _value := str.replace_all(_value, "#TakeAmount2#", str.tostring(_ta2))
    _value := str.replace_all(_value, "#TakeAmountFULL#", str.tostring(_taFull))


i_apiEnterLong  := f_replaceKeys (i_apiEnterLong)
i_apiEnterLong  := f_replaceLongTokens (i_apiEnterLong, longSL, longTP1, longTP2, longTPFull, takeAmount1, takeAmount2, takeAmountFull)

i_apiExitLong   := f_replaceKeys (i_apiExitLong)
i_apiExitLong   := f_replaceLongTokens (i_apiExitLong, longSL, longTP1, longTP2, longTPFull, takeAmount1, takeAmount2, takeAmountFull)

i_apiEnterShort := f_replaceKeys (i_apiEnterShort)
i_apiEnterShort := f_replaceShortTokens (i_apiEnterShort, shortSL, shortTP1, shortTP2, shortTPFull, takeAmount1, takeAmount2, takeAmountFull)

i_apiExitShort  := f_replaceKeys (i_apiExitShort)
i_apiExitShort  := f_replaceShortTokens (i_apiExitShort, shortSL, shortTP1, shortTP2, shortTPFull, takeAmount1, takeAmount2, takeAmountFull)


// ----- handline trade entries and exits --------------------------------------------------------------------------------------------------------------------------
var float openingHigh = na
var float openingLow  = na

// determine high and low druing opening session
if barstate.isconfirmed and inOpeningTime
    openingHigh     := math.max(buyStopSource,  nz(openingHigh[1], buyStopSource))
    openingLow      := math.min(sellStopSource, nz(openingLow[1],  sellStopSource))

    buyStopPrice  := openingHigh + buyStopGap * 10 * syminfo.mintick
    sellStopPrice := openingLow - sellStopGap * 10 * syminfo.mintick

// as soon as the trading session starts calculate stop prices based on high / low of first closing candle, create pending orders
if barstate.isconfirmed and inTradingTime and not inTradingTime[1]
    tradeID = str.tostring(strategy.closedtrades + strategy.opentrades + 1)

    strategy.entry(tradeID + "L", strategy.long, qty = quantity, stop = buyStopPrice, comment = "Long: " + tradeID, alert_message = i_apiEnterLong)
    strategy.exit(tradeID + "SL", tradeID + "L", profit = takeProfit, loss = stopLoss, comment_loss = "SL", comment_profit = "TP", alert_message = i_apiExitLong)

    strategy.entry(tradeID + "S", strategy.short, qty = quantity, stop = sellStopPrice, comment = "Short: " + tradeID, alert_message = i_apiEnterShort)
    strategy.exit(tradeID + "SL", tradeID + "S", profit = takeProfit, loss = stopLoss, comment_loss = "SL", comment_profit = "TP", alert_message = i_apiExitShort)


// once the session is gone, reset prices and cancel all remaining pending orders, close open orders
if barstate.isconfirmed and not inTradingTime and inTradingTime[1]
    buyStopPrice    := na
    sellStopPrice   := na
    openingHigh     := na
    openingLow      := na

    strategy.cancel_all()

    currentTradeID = str.tostring(strategy.closedtrades + strategy.opentrades)
    strategy.close(currentTradeID + "L", immediately = true, comment = "L: session end", alert_message = i_apiExitLong)
    strategy.close(currentTradeID + "S", immediately = true, comment = "S: session end", alert_message = i_apiExitShort)

// as soon as the first pending order has been entered the remaing pending order shall be cancelled 
if strategy.opentrades > 0 and closePending
    currentTradeID = str.tostring(strategy.closedtrades + strategy.opentrades)
    strategy.cancel(currentTradeID + "L")
    strategy.cancel(currentTradeID + "S")
