Reputation: 35
I have a strategy that connects to the broker with alerts (3commas style), but I am able to trade in both directions. The problem is that alerts keep firing when the close_long condition is met while still in a short position, how do I let the strategy know to not fire signals while trade is still on on the opposite direction or even no trade. Would be better if they just fire while in a trade in the direction they are meant to fire.
To be clear, conditions to enter short are the same as close long.
I know you could add this: if strategy.opentrades > 0 // There is an open order
if strategy.position_size > 0 // The open order is a LONG
na
else if strategy.position_size < 0 // The open order is a SHORT
na
But it didn't seem to work, specially if the strategy has some drawdown.
I also would like to avoid any kind of redundancy in the signals, as you can see I have many variables that should apply to a trade to take place, but I don't want multiple orders to be fired. But I don't want to wait till the candle is close for the alert to fire, specially in the stoploss signal.
This is the code I am using:
`//@version=5
strategy("Variety-N-Tuple Okx GALAUSDT 5m", overlay=true, commission_type=strategy.commission.percent, commission_value=0.05, initial_capital=100, default_qty_type=strategy.cash, default_qty_value=100, currency=currency.USD)
import loxx/loxxexpandedsourcetypes/4
greencolor = #2DD204
redcolor = #D2042D
lightgreencolor = #96E881
lightredcolor = #DF4F6C
fact(int n)=>
float out = 1
for i = 1 to n
out *= i
out
// TradingView user @lejmer was a key player in making this function come to life
nema(string nematype, float src, int per, int dpth, int mxdpth) =>
var float[] coeff = array.new<float>(mxdpth + 1, 0.)
if barstate.isfirst
int nemadpth = math.max(math.min(dpth, mxdpth), 1)
for k = 0 to dpth
array.set(coeff, k, nz(fact(dpth) / (fact(dpth - k) * fact(k)), 1))
var float[] workarr = array.new<float>(dpth + 1, 0)
array.set(workarr, 0, src)
int sign = 1
float nema = 0.
float alpha = nematype == "EMA" ? 2.0 / (1.0 + per) : 1 / per
for k = 0 to dpth - 1
temp = array.get(workarr, k + 1) + alpha * (array.get(workarr, k) - array.get(workarr, k + 1))
array.set(workarr, k + 1, temp)
nema += temp * sign * array.get(coeff, k + 1)
sign *= -1
nema
adaptvieATR(float src, int per)=>
float mper = (per > 1) ? per : 1
float mfast = math.max(mper / 2.0, 1)
float mslow = mper * 5
float mperDiff = mslow - mfast
float noise = 0., float aatr = 0.
float diff = math.abs(src - nz(src[1]))
float signal = math.abs(src - nz(src[mper]))
noise := nz(noise[1]) + diff - nz(diff[mper])
float avgper = (noise != 0) ? (signal / noise) * mperDiff + mfast : mper
aatr := nz(aatr[1]) + (2.0 / (1.0 + avgper)) * ((high - low) - nz(aatr[1]))
aatr
adaptiveDeviation(float price, int per)=>
float m_per = (per > 1) ? per : 1
float m_fastEnd = math.max(m_per / 2.0, 1)
float m_slowEnd = m_per * 5
float m_perDiff = m_slowEnd - m_fastEnd
float difference = math.abs(ta.change(price))
float signal = 0.
float noise = 0.
if bar_index > m_per
signal := math.abs(price - nz(price[m_per]))
noise := nz(noise[1]) + difference - nz(difference[m_per])
else
for k = 0 to m_per - 1
noise += nz(difference[k])
float averageper = (signal /noise) * m_perDiff + m_fastEnd
float alpha = 2.0 / (1.0 + averageper)
float ema0 = 0., float ema1 = 0.
ema0 := nz(ema0[1]) + alpha * (price - nz(ema0[1]))
ema1 := nz(ema1[1]) + alpha * (price * price - nz(ema1[1]))
float out = math.sqrt(averageper * (ema1 - ema0 * ema0) / math.max(averageper - 1, 1))
out
fmedian(float src, int per)=>
int midlea = 0
int midleb = 0
float[] sortMed = array.new<float>(per, 0.)
if (per % 2) == 0
midlea := int((per / 2) - 1)
midleb := int(per / 2)
else
midlea := int((per - 1) / 2)
midleb := int(midlea)
for k = 0 to per - 1
array.set(sortMed, k, nz(src[k]))
array.sort(sortMed)
out = (array.get(sortMed, midlea) + array.get(sortMed, midleb)) / 2
out
filt(float src, simple int len, float filter, filtper, type)=>
float price = src
float atr = ta.atr(len)
float std = ta.stdev(src, len)
float addev = adaptiveDeviation(src, len)
float aatr = adaptvieATR(src, len)
float out = fmedian(src, len)
float mad = fmedian(math.abs(src - out), filtper)
float filtdev = filter * (type == "ATR" ? atr: type == "Standard Deviation" ? std : type == "Median Absolute Deviation" ? mad : type == "Adaptive Deviation" ? addev : aatr)
price := math.abs(price - nz(price[1])) < filtdev ? nz(price[1]) : price
price
banVal(float src, simple int len, float filter, filtper, type)=>
float atr = ta.atr(len)
float std = ta.stdev(src, len)
float addev = adaptiveDeviation(src, len)
float aatr = adaptvieATR(src, len)
float out = fmedian(src, len)
float mad = fmedian(math.abs(src - out), filtper)
float regdev = ta.dev(src, len)
float filtdev = filter * (type == "ATR" ? atr : type == "Standard Deviation" ? std :
type == "Median Absolute Deviation" ? mad : type == "Adaptive Deviation" ? addev : type == "ER-Adaptive ATR" ? aatr : regdev)
filtdev
smthtype = input.string("Kaufman", "Heiken-Ashi Better Smoothing", options = ["AMA", "T3", "Kaufman"], group= "Source Settings")
srcoption = input.string("HAB Typical", "Source", group= "Source Settings",
options =
["Close", "Open", "High", "Low", "Median", "Typical", "Weighted", "Average", "Average Median Body", "Trend Biased", "Trend Biased (Extreme)",
"HA Close", "HA Open", "HA High", "HA Low", "HA Median", "HA Typical", "HA Weighted", "HA Average", "HA Average Median Body", "HA Trend Biased", "HA Trend Biased (Extreme)",
"HAB Close", "HAB Open", "HAB High", "HAB Low", "HAB Median", "HAB Typical", "HAB Weighted", "HAB Average", "HAB Average Median Body", "HAB Trend Biased", "HAB Trend Biased (Extreme)"])
per = input.int(13, "Period", group = "Basic Settings")
nemadpth = input.int(3, "Depth", maxval = 50, minval = 1, group = "Basic Settings")
nematype = input.string("EMA", "Moving Average Type", options = ["EMA", "RMA"] , group = "Basic Settings")
ptype = input.string("ATR", "Price Filter Type", options = ["ATR", "Standard Deviation", "Median Absolute Deviation", "Adaptive Deviation", "ER-Adaptive ATR", "Mean Absolute Deviation"], group= "Filter Settings")
matype = input.string("ATR", "Moving Average Filter Type", options = ["ATR", "Standard Deviation", "Median Absolute Deviation", "Adaptive Deviation", "ER-Adaptive ATR", "Mean Absolute Deviation"], group= "Filter Settings")
filterop = input.string("Both", "Filter Options", options = ["Price", "Moving Average Filter", "Both", "None"], group= "Filter Settings")
filter = input.float(1, "Filter Multiplier", minval = 0, group= "Filter Settings")
filterperiod = input.int(10, "Filter Period", minval = 0, group= "Filter Settings")
madper = input.int(10, "MAD Internal Filter Period", minval = 0, group= "Filter Settings", tooltip = "Median Absolute Deviation only")
bndtype = input.string("ATR", "Band Type Type", options = ["ATR", "Standard Deviation", "Median Absolute Deviation", "Adaptive Deviation", "ER-Adaptive ATR", "Mean Absolute Deviation"], group= "Bands Settings")
bndmult = input.float(1, "Band Multiplier", minval = 0, group= "Bands Settings")
colorbars = input.bool(true, "Color bars?", group = "UI Options")
showSigs = input.bool(true, "Show signals?", group= "UI Options")
showBands = input.bool(true, "Show bands?", group= "UI Options")
kfl=input.float(0.666, title="* Kaufman's Adaptive MA (KAMA) Only - Fast End", group = "Moving Average Inputs")
ksl=input.float(0.0645, title="* Kaufman's Adaptive MA (KAMA) Only - Slow End", group = "Moving Average Inputs")
amafl = input.int(2, title="* Adaptive Moving Average (AMA) Only - Fast", group = "Moving Average Inputs")
amasl = input.int(30, title="* Adaptive Moving Average (AMA) Only - Slow", group = "Moving Average Inputs")
haclose = request.security(ticker.heikinashi(syminfo.tickerid), timeframe.period, close)
haopen = request.security(ticker.heikinashi(syminfo.tickerid), timeframe.period, open)
hahigh = request.security(ticker.heikinashi(syminfo.tickerid), timeframe.period, high)
halow = request.security(ticker.heikinashi(syminfo.tickerid), timeframe.period, low)
hamedian = request.security(ticker.heikinashi(syminfo.tickerid), timeframe.period, hl2)
hatypical = request.security(ticker.heikinashi(syminfo.tickerid), timeframe.period, hlc3)
haweighted = request.security(ticker.heikinashi(syminfo.tickerid), timeframe.period, hlcc4)
haaverage = request.security(ticker.heikinashi(syminfo.tickerid), timeframe.period, ohlc4)
float src = switch srcoption
"Close" => loxxexpandedsourcetypes.rclose()
"Open" => loxxexpandedsourcetypes.ropen()
"High" => loxxexpandedsourcetypes.rhigh()
"Low" => loxxexpandedsourcetypes.rlow()
"Median" => loxxexpandedsourcetypes.rmedian()
"Typical" => loxxexpandedsourcetypes.rtypical()
"Weighted" => loxxexpandedsourcetypes.rweighted()
"Average" => loxxexpandedsourcetypes.raverage()
"Average Median Body" => loxxexpandedsourcetypes.ravemedbody()
"Trend Biased" => loxxexpandedsourcetypes.rtrendb()
"Trend Biased (Extreme)" => loxxexpandedsourcetypes.rtrendbext()
"HA Close" => loxxexpandedsourcetypes.haclose(haclose)
"HA Open" => loxxexpandedsourcetypes.haopen(haopen)
"HA High" => loxxexpandedsourcetypes.hahigh(hahigh)
"HA Low" => loxxexpandedsourcetypes.halow(halow)
"HA Median" => loxxexpandedsourcetypes.hamedian(hamedian)
"HA Typical" => loxxexpandedsourcetypes.hatypical(hatypical)
"HA Weighted" => loxxexpandedsourcetypes.haweighted(haweighted)
"HA Average" => loxxexpandedsourcetypes.haaverage(haaverage)
"HA Average Median Body" => loxxexpandedsourcetypes.haavemedbody(haclose, haopen)
"HA Trend Biased" => loxxexpandedsourcetypes.hatrendb(haclose, haopen, hahigh, halow)
"HA Trend Biased (Extreme)" => loxxexpandedsourcetypes.hatrendbext(haclose, haopen, hahigh, halow)
"HAB Close" => loxxexpandedsourcetypes.habclose(smthtype, amafl, amasl, kfl, ksl)
"HAB Open" => loxxexpandedsourcetypes.habopen(smthtype, amafl, amasl, kfl, ksl)
"HAB High" => loxxexpandedsourcetypes.habhigh(smthtype, amafl, amasl, kfl, ksl)
"HAB Low" => loxxexpandedsourcetypes.hablow(smthtype, amafl, amasl, kfl, ksl)
"HAB Median" => loxxexpandedsourcetypes.habmedian(smthtype, amafl, amasl, kfl, ksl)
"HAB Typical" => loxxexpandedsourcetypes.habtypical(smthtype, amafl, amasl, kfl, ksl)
"HAB Weighted" => loxxexpandedsourcetypes.habweighted(smthtype, amafl, amasl, kfl, ksl)
"HAB Average" => loxxexpandedsourcetypes.habaverage(smthtype, amafl, amasl, kfl, ksl)
"HAB Average Median Body" => loxxexpandedsourcetypes.habavemedbody(smthtype, amafl, amasl, kfl, ksl)
"HAB Trend Biased" => loxxexpandedsourcetypes.habtrendb(smthtype, amafl, amasl, kfl, ksl)
"HAB Trend Biased (Extreme)" => loxxexpandedsourcetypes.habtrendbext(smthtype, amafl, amasl, kfl, ksl)
=> haclose
src := filterop == "Both" or filterop == "Price" and filter > 0 ? filt(src, filterperiod, filter, madper, ptype) : src
out = nema(nematype, src, per, nemadpth, 50)
out := filterop == "Both" or filterop == "Moving Average Filter" and filter > 0 ? filt(out, filterperiod, filter, madper, matype) : out
sig = nz(out[1])
state = out > sig ? 1 : out < sig ? -1 : 0
pregoLong = out > sig and (nz(out[1]) < nz(sig[1]) or nz(out[1]) == nz(sig[1]))
pregoShort = out < sig and (nz(out[1]) > nz(sig[1]) or nz(out[1]) == nz(sig[1]))
contsw = 0
contsw := nz(contsw[1])
contsw := pregoLong ? 1 : pregoShort ? -1 : nz(contsw[1])
goLong = pregoLong and nz(contsw[1]) == -1
goShort = pregoShort and nz(contsw[1]) == 1
bandout = banVal(src, filterperiod, bndmult, madper, bndtype)
bndup = out + bandout
bnddn = out - bandout
plot(showBands ? bndup : na, "Upper Band", color = color.gray, linewidth = 1)
plot(showBands ? bnddn : na, "Lower Band", color = color.gray, linewidth = 1)
var color colorout = na
colorout := state == -1 ? redcolor : state == 1 ? greencolor : nz(colorout[1])
plot(out, "Step NTMA", color = colorout, linewidth = 2)
barcolor(colorbars ? colorout : na)
//////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////
//////////////////////////// STRATEGY
//////////////////////////////////////////////////////////
longCondition= pregoLong and nz(contsw[1]) == -1
shortCondition= pregoShort and nz(contsw[1]) == 1
closeLongCondition= pregoShort and nz(contsw[1]) == 1
closeShortCondition= pregoLong and nz(contsw[1]) == -1
/////////////////////////////
// ZLSMA Filter
ZlFilterEnabled = input.bool(defval = true, title = "Enable Zlema Filter", group ="Zlema Filter" )
srcZ = input.source(hlcc4, title="Zlema Source")
zlen = input(title='Length', defval=72)
Zoffset = input(title='Offset', defval=0)
lsma = ta.linreg(srcZ, zlen, Zoffset)
lsma2 = ta.linreg(lsma, zlen, Zoffset)
eq = lsma - lsma2
zlsma = lsma + eq
bool ZFSlong = ZlFilterEnabled ? srcZ > zlsma : true
bool ZFSshort = ZlFilterEnabled ? srcZ < zlsma : true
//////// TFS Volume Filter
i_TFSFilterEnabled = input.bool(defval = true, title = "TFS Volume Filter", group ="TFS Filter" )
i_TFSexitEnabled = input.bool(defval = true, title = "TFS Volume Filter on EXIT", group ="TFS Filter" )
AvgLen = input.int(6, minval=1)
TopBand = input.int(40000, step=1)
LowBand = input.int(-20000, step=1)
reverse = input(false, title='Trade reverse')
xClose = close
xOpen = open
xVolume = volume
TFSViff_1 = xClose < xOpen ? -xVolume : 0
nVolAccum = math.sum(xClose > xOpen ? xVolume : TFSViff_1, AvgLen)
nRes = nVolAccum / AvgLen
posTFS = 0.0
TFSViff_2 = nRes < LowBand ? -1 : nz(posTFS[1], 0)
posTFS := nRes > TopBand ? 1 : TFSViff_2
TFSViff_3 = reverse and posTFS == -1 ? 1 : posTFS
possig = reverse and posTFS == 1 ? -1 : TFSViff_3
barcolor(possig == -1 ? color.red : possig == 1 ? color.green : color.blue)
bool TFSlong = i_TFSFilterEnabled ? possig == 1 : true
bool TFSshort = i_TFSFilterEnabled ? possig == -1 : true
bool TFSexitlong = i_TFSexitEnabled ? possig == 1 : true
bool TFSexitshort = i_TFSexitEnabled ? possig == -1 : true
// ADX Filter
i_adxFilterEnabled = input.bool(defval = true, title = "Enable ADX Filter", group ="ADX Filter" )
i_adxVariant = input.string('MASANAKAMURA', title='ADX Variant', options=['ORIGINAL', 'MASANAKAMURA'], group ="ADX Filter" )
i_adxSmoothing = input.int(12, title="ADX Smoothing", group="ADX Filter")
i_adxDILength = input.int(4, title="DI Length", group="ADX Filter")
i_adxLowerThreshold = input.float(26, title="ADX Threshold", step=.5, group="ADX Filter")
calcADX_Masanakamura2(int _len) =>
_smoothedTrueRange = 0.0
_smoothedDirectionalMovementPlus = 0.0
_smoothed_directionalMovementMinus = 0.0
_trueRange = math.max(math.max(high - low, math.abs(high - nz(close[1]))), math.abs(low - nz(close[1])))
_directionalMovementPlus = high - nz(high[1]) > nz(low[1]) - low ? math.max(high - nz(high[1]), 0) : 0
_directionalMovementMinus = nz(low[1]) - low > high - nz(high[1]) ? math.max(nz(low[1]) - low, 0) : 0
_smoothedTrueRange := nz(_smoothedTrueRange[1]) - nz(_smoothedTrueRange[1]) / _len + _trueRange
_smoothedDirectionalMovementPlus := nz(_smoothedDirectionalMovementPlus[1]) - nz(_smoothedDirectionalMovementPlus[1]) / _len + _directionalMovementPlus
_smoothed_directionalMovementMinus := nz(_smoothed_directionalMovementMinus[1]) - nz(_smoothed_directionalMovementMinus[1]) / _len + _directionalMovementMinus
DIP = _smoothedDirectionalMovementPlus / _smoothedTrueRange * 100
DIM = _smoothed_directionalMovementMinus / _smoothedTrueRange * 100
_DX = math.abs(DIP - DIM) / (DIP + DIM) * 100
adx = ta.sma(_DX, _len)
[DIP, DIM, adx]
[DIPlusO, DIMinusO, ADXO] = ta.dmi(i_adxDILength, i_adxSmoothing)
[DIPlusM2, DIMinusM2, ADXM2] = calcADX_Masanakamura2(i_adxDILength)
adx = i_adxFilterEnabled and i_adxVariant == "ORIGINAL" ? ADXO : ADXM2
bool isADXFilterEnabledAndAboveThreshold = i_adxFilterEnabled ? adx > i_adxLowerThreshold : true
//////// RSI
i_RSIFilterEnabled = input.bool(defval = false, title = "RSI Filter", group ="RSI Filter" )
RSI_Filter_Length = input.int(14, minval=1, title="RSI Length")
RSI_Filter_source = input.source(close, "Source")
rsiup_fiter =input(50, "Rsi Long Threshold")
rsidn_filter =input(50, "Rsi Short Threshold")
RSI_Filter_up = ta.rma(math.max(ta.change(RSI_Filter_source), 0), RSI_Filter_Length)
RSI_Filter_down = ta.rma(-math.min(ta.change(RSI_Filter_source), 0), RSI_Filter_Length)
RSI_filter = RSI_Filter_down == 0 ? 100 : RSI_Filter_up == 0 ? 0 : 100 - (100 / (1 + RSI_Filter_up / RSI_Filter_down))
bool RSIlong = i_RSIFilterEnabled ? RSI_filter > rsiup_fiter : true
bool RSIshort = i_RSIFilterEnabled ? RSI_filter < rsidn_filter : true
///Start / End Time Periods
i_startPeriodEnabled = input.bool(true, 'Start', group='Date Range', inline='Start Period')
i_startPeriodTime = input.time(timestamp('1 Jan 2022'), '', group='Date Range', inline='Start Period')
i_endPeriodEnabled = input.bool(true, 'End', group='Date Range', inline='End Period')
i_endPeriodTime = input.time(timestamp('31 Dec 2030'), '', group='Date Range', inline='End Period')
isStartPeriodEnabledAndInRange = i_startPeriodEnabled ? i_startPeriodTime <= time : true
isEndPeriodEnabledAndInRange = i_endPeriodEnabled ? i_endPeriodTime >= time : true
isStartEndPeriodsAndTimeInRange = isStartPeriodEnabledAndInRange and isEndPeriodEnabledAndInRange
// Trade Direction
i_tradeDirection = input.string('Long and Short', title='Trade Direction', options=['Long and Short', 'Long Only', 'Short Only'], group='Trade Direction')
// Percent as Points
per(pcnt) =>
strategy.position_size != 0 ? math.round(pcnt / 100 * strategy.position_avg_price / syminfo.mintick) : float(na)
/// Stop Loss
i_stopLossPercentLong = input.float(title='Stop Loss Long (%)', defval=1.9, minval=0.01, step=0.1) * 0.01
slLongClose = (close < strategy.position_avg_price * (1 - i_stopLossPercentLong))
i_stopLossShort = input.float(title='Stop Loss Short (%)', defval=1.5, minval=0.01, step=0.1) * 0.01
slShortClose = (close > strategy.position_avg_price * (1 + i_stopLossShort))
/// Leverage
i_leverage = input.float(1, 'Leverage', step=1, group='Leverage')
i_percentOfEquityToTrade = input.float(100, "% of Equity to Stake Per Trade", minval=0.01, maxval=100, step=5, group='Leverage') * .01
contracts = (i_percentOfEquityToTrade * strategy.equity / close * i_leverage)
//// Strategy Comment
i_key = input.string(defval='Key', title='Broker Key', group='Strategy Comments')
i_market = input.string(defval='Market', title='Market', group='Strategy Comments')
i_equity = input.string(defval='Equity', title='Equity', group='Strategy Comments')
/// Trade State Management
isInLongPosition = strategy.position_size > 0
isInShortPosition = strategy.position_size < 0
/// Trade Execution
longConditionCalc = (longCondition and isADXFilterEnabledAndAboveThreshold and TFSlong and RSIlong and ZFSlong)
shortConditionCalc = (shortCondition and isADXFilterEnabledAndAboveThreshold and TFSshort and RSIshort and ZFSshort)
closeLongConditionCalc= (closeLongCondition and TFSexitlong and ZFSshort) or shortConditionCalc
closeShortConditionCalc= (closeShortCondition and TFSexitshort and ZFSlong) or longConditionCalc
if isStartEndPeriodsAndTimeInRange
// Long Conditions
if longConditionCalc and i_tradeDirection != 'Short Only' and isInLongPosition == false
strategy.entry('Long', strategy.long, qty=contracts)
alert('{ "xxxx": "openlong", "market": “GALA-PERP", "equity": 20 }', alert.freq_once_per_bar)
if closeLongConditionCalc
strategy.close('Long', qty_percent=100)
alert('{ "xxxx": "closelong", "market": “GALA-PERP", "equity": 100 }', alert.freq_once_per_bar)
if slLongClose
strategy.close('Long', qty_percent=100)
alert('{ "xxxx": "closelong", "market": “GALA-PERP", "equity": 100 }', alert.freq_once_per_bar)
// Short Conditions
if shortConditionCalc and i_tradeDirection != 'Long Only' and isInShortPosition == false
strategy.entry('Short', strategy.short, qty=contracts)
alert('{ "xxxx": "openshort", "market": “GALA-PERP", "equity": 20 }', alert.freq_once_per_bar)
if closeShortConditionCalc
strategy.close('Short', qty_percent=100)
alert('{ "xxxx": "closeshort", "market": “GALA-PERP", "equity": 100 }', alert.freq_once_per_bar)
if slShortClose
strategy.close('Short', qty_percent=100)
alert('{ "xxxx": "closeshort", "market": “GALA-PERP", "equity": 100 }', alert.freq_once_per_bar)`
Upvotes: 1
Views: 190
Reputation: 21121
closelong
will fire while you are in a short trade because you don't check if you are in a long position to fire that alarm.
You use closeLongConditionCalc
or slLongClose
to fire a close long alert. As far as I see, none of them check if you are already in a long position (isInLongPosition
).
Obviously, you don't want to call this alert if you are not already in a long position.
Alternatively, you can use the alert_message
argument of the strategy.entry()
, strategy.exit()
and strategy.close()
functions which I would recommend.
Upvotes: 1