
Strategi ini bertujuan untuk menyeimbangkan psikologi dan prestasi perdagangan pedagang dengan menetapkan parameter yang berbeza untuk mendapatkan pulangan yang lebih stabil. Ia menggunakan indikator seperti garis rata-rata, Brin Belt, dan Saluran Keltner untuk menilai trend dan turun naik pasaran, digabungkan dengan indikator PSAR untuk menilai isyarat reversal, dan menggunakan indikator TTM untuk menilai momentum. Isyarat perdagangan dihasilkan oleh kombinasi indikator ini.
Logik utama strategi ini ialah:
Penilaian Trend: Menggunakan EMA rata-rata untuk menilai arah trend harga, harga di atas EMA adalah kenaikan, di bawahnya adalah penurunan
Penilaian pembalikan: menggunakan PSAR untuk menentukan titik pembalikan harga. Titik PSAR muncul di atas harga sebagai isyarat bullish, muncul di bawah harga sebagai isyarat bearish
Penilaian pergerakan: Menggunakan TTM Squeeze untuk menilai kadar turun naik dan pergerakan pasaran. TTM Squeeze mengukur kadar turun naik dengan membandingkan lebar jalur Brin dan saluran Keltner. Penekanan bermaksud turun naik yang sangat rendah.
Menjana isyarat perdagangan: apabila harga di atas melintasi garis EMA rata-rata, titik PSAR, dan penunjuk TTM Squeeze melepaskan tekanan, menghasilkan isyarat melihat lebih; apabila harga di bawah melintasi garis EMA rata-rata, titik PSAR, dan penunjuk TTM Squeeze memasuki tekanan, menghasilkan isyarat melihat kosong
Cara berhenti: menggunakan titik berhenti yang tinggi atau rendah. Berkalikan kelipatan yang ditetapkan sebagai titik berhenti berdasarkan harga tertinggi atau terendah dalam tempoh tertentu yang terakhir
Cara berhenti: Menggunakan stop automatik berbanding ganjaran dan risiko. Stop berdasarkan nisbah jarak dari titik berhenti kepada harga semasa dengan parameter ganjaran dan risiko yang ditetapkan
Dengan parameter yang ditetapkan, anda boleh mengawal frekuensi perdagangan, pengurusan kedudukan, titik berhenti dan titik berhenti, keseimbangan psikologi perdagangan.
Strategi ini mempunyai kelebihan berikut:
Penghakiman berbilang indikator untuk meningkatkan ketepatan isyarat
Berbalik ke arah utama, ke arah tambahan, menangkap titik balik, mengurangkan kebarangkalian untuk menaiki dan menaiki dan menaiki dan menaiki
Indikator TTMSqueeze berkesan menilai penyesuaian dalam trend, mengelakkan perdagangan yang tidak sah pada tempoh penyesuaian
Pendekatan H&L mudah digunakan, jarak H&L boleh disesuaikan mengikut pasaran
Pembaharuan risiko berbanding penangguhan kaedah akan menomborkan nisbah keuntungan dan kerugian untuk memudahkan penyesuaian
Pelbagai parameter yang fleksibel, boleh disesuaikan dengan keutamaan risiko peribadi
Strategi ini juga mempunyai risiko:
Penghakiman gabungan pelbagai indikator, walaupun meningkatkan ketepatan isyarat, tetapi juga meningkatkan kemungkinan melangkau titik Entry
Strategi yang didominasi oleh pembalikan, mungkin tidak berfungsi dengan baik dalam keadaan trend
Stop loss rendah dan tinggi kadang-kadang dapat diatasi, tidak dapat mengelakkan risiko sepenuhnya
Risiko-reward berbanding stop-loss juga boleh hilang akibat kenaikan harga atau penyesuaian.
Tetapan parameter yang tidak betul boleh menyebabkan kerugian atau gangguan yang kerap
Strategi ini boleh dioptimumkan dengan:
Tambah atau sesuaikan berat penunjuk untuk mendapatkan isyarat yang lebih tepat
Mengoptimumkan parameter indikator untuk membalikkan dan menilai trend, meningkatkan kebarangkalian keuntungan
Mengoptimumkan parameter yang tinggi dan rendah untuk menghentikan kerugian dengan lebih munasabah
Uji balas risiko yang berbeza untuk mencapai hasil yang terbaik
Menyesuaikan parameter nombor kedudukan untuk mengurangkan kesan kerugian tunggal
Secara keseluruhannya, strategi ini dapat mengimbangi psikologi perdagangan dengan berkesan dan memperoleh keuntungan positif yang stabil melalui penilaian dan penyesuaian parameter kumpulan penunjuk. Walaupun masih ada ruang untuk penambahbaikan, strategi ini mempunyai nilai aplikasi lapangan.
/*backtest
start: 2024-01-01 00:00:00
end: 2024-01-31 23:59:59
period: 1h
basePeriod: 15m
exchanges: [{"eid":"Futures_Binance","currency":"BTC_USDT"}]
*/
//@version=5
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © simwai
strategy('Octopus Nest Strategy 🐙', shorttitle='🐙', overlay=true )
// -- Colors --
color maximumYellowRed = color.rgb(255, 203, 98) // yellow
color rajah = color.rgb(242, 166, 84) // orange
color magicMint = color.rgb(171, 237, 198)
color languidLavender = color.rgb(232, 215, 255)
color maximumBluePurple = color.rgb(181, 161, 226)
color skyBlue = color.rgb(144, 226, 244)
color lightGray = color.rgb(214, 214, 214)
color quickSilver = color.rgb(163, 163, 163)
color mediumAquamarine = color.rgb(104, 223, 153)
color carrotOrange = color.rgb(239, 146, 46)
// -- Inputs --
float src = input.source(close, 'Choose Source', group='General', inline='1')
bool isSignalLabelEnabled = input.bool(title='Show Signal Labels?', defval=true, group='General', inline='2')
bool isPsarAdaptive = input.bool(title='Is PSAR Adaptive?', defval=false, group='General', inline='2')
float highLowStopLossMultiplier = input.float(defval=0.98, step=0.01, minval=0, maxval=1, title='Multiplier', group='High Low Stop Loss', inline='1')
float highLowStopLossBackupMultiplier = input.float(defval=0.98, step=0.01, minval=0, maxval=1, title='Backup Multiplier', group='High Low Stop Loss', inline='1')
int highLowStopLossLookback = input.int(defval=20, step=5, minval=1, title='Lookback', group='High Low Stop Loss', inline='2')
float automaticHighLowTakeProfitRatio = input.float(defval=1.125, step=0.1, minval=0, title='Risk Reward Ratio', group='Automatic High Low Take Profit', inline='2')
int emaLength = input.int(100, minval=2, title='Length', group='EMA', inline='1')
int ttmLength = input.int(title='Length', defval=20, minval=0, group='TTM Squeeze', inline='1')
float psarStart = input.float(0.02, 'Start', step=0.01, minval=0.0, group='PSAR', inline='1')
float psarInc = input.float(0.02, 'Increment', step=0.01, minval=0.01, group='PSAR', inline='1')
float psarMax = input.float(0.2, 'Max', step=0.05, minval=0.0, group='PSAR', inline='2')
startAFactor = input.float(0.02, 'Starting Acceleration Factor', step = 0.001, group='Adaptive PSAR', inline='1')
minStep = input.float(0.0, 'Min Step', step = 0.001, group='Adaptive PSAR', inline='1')
maxStep = input.float(0.02, 'Max Step', step = 0.001, group='Adaptive PSAR', inline='2')
maxAFactor = input.float(0.2, 'Max Acceleration Factor', step = 0.001, group='Adaptive PSAR', inline='2')
hiloMode = input.string('On', 'HiLo Mode', options = ['Off', 'On'], group='Adaptive PSAR')
adaptMode = input.string('Kaufman', 'Adaptive Mode', options = ['Off', 'Kaufman', 'Ehlers'], group='Adaptive PSAR')
adaptSmth = input.int(5, 'Adaptive Smoothing Period', minval = 1, group='Adaptive PSAR')
filt = input.float(0.0, 'Filter in Pips', group='Adaptive PSAR', minval = 0)
minChng = input.float(0.0, 'Min Change in Pips', group='Adaptive PSAR', minval = 0)
SignalMode = input.string('Only Stops', 'Signal Mode', options = ['Only Stops', 'Signals & Stops'], group='Adaptive PSAR')
// -- Functions --
tr(_high, _low, _close) => math.max(_high - _low, math.abs(_high - _close[1]), math.abs(_low - _close[1]))
// -- Calculation --
var string lastTrade = 'initial'
float _low = low
float _high = high
float _close = close
// -- TTM Squeeze – Credits to @Greeny --
bband(ttmLength, mult) =>
ta.sma(src, ttmLength) + mult * ta.stdev(src, ttmLength)
keltner(ttmLength, mult) =>
ta.ema(src, ttmLength) + mult * ta.ema(tr(_high, _low, _close), ttmLength)
e1 = (ta.highest(_high, ttmLength) + ta.lowest(_low, ttmLength)) / 2 + ta.sma(src, ttmLength)
osc = ta.linreg(src - e1 / 2, ttmLength, 0)
diff = bband(ttmLength, 2) - keltner(ttmLength, 1)
osc_color = osc[1] < osc[0] ? osc[0] >= 0 ? #00ffff : #cc00cc : osc[0] >= 0 ? #009b9b : #ff9bff
mid_color = diff >= 0 ? color.green : color.red
// -- PSAR --
// Credits to @Bjorgum
calcBaseUnit() =>
bool isForexSymbol = syminfo.type == 'forex'
bool isYenPair = syminfo.currency == 'JPY'
float result = isForexSymbol ? isYenPair ? 0.01 : 0.0001 : syminfo.mintick
// Credits to @loxx
_afact(mode,input, per, smooth) =>
eff = 0., seff = 0.
len = 0, sum = 0., max = 0., min = 1000000000.
len := mode == 'Kaufman' ? math.ceil(per) : math.ceil(math.max(20, 5 * per))
for i = 0 to len
if (mode == 'Kaufman')
sum += math.abs(input[i] - input[i + 1])
else
max := input[i] > max ? input[i] : max
min := input[i] < min ? input[i] : min
if (mode == 'Kaufman' and sum != 0)
eff := math.abs(input - input[len]) / sum
else
if (mode == 'Ehlers' and (max - min) > 0)
eff := (input - min) / (max - min)
seff := ta.ema(eff, smooth)
seff
hVal2 = nz(high[2]), hVal1 = nz(high[1]), hVal0 = high
lowVal2 = nz(low[2]), lowVal1 = nz(low[1]), lowVal0 = low
hiprice2 = nz(high[2]), hiprice1 = nz(high[1]), hiprice0 = high
loprice2 = nz(low[2]), loprice1 = nz(low[1]), loprice0 = low
upSig = 0., dnSig = 0.
aFactor = 0., step = 0., trend = 0.
upTrndSAR = 0., dnTrndSAR = 0.
length = (2 / maxAFactor - 1)
if (hiloMode == 'On')
hiprice0 := high
loprice0 := low
else
hiprice0 := src
loprice0 := hiprice0
if bar_index == 1
trend := 1
hVal1 := hiprice1
hVal0 := math.max(hiprice0, hVal1)
lowVal1 := loprice1
lowVal0 := math.min(loprice0, lowVal1)
aFactor := startAFactor
upTrndSAR := lowVal0
dnTrndSAR := 0.
else
hVal0 := hVal1
lowVal0 := lowVal1
trend := nz(trend[1])
aFactor := nz(aFactor[1])
inputs = 0.
inprice = src
if (adaptMode != 'Off')
if (hiloMode == 'On')
inprice := src
else
inprice := hiprice0
if (adaptMode == 'Kaufman')
inputs := inprice
else
if (adaptMode == 'Ehlers')
if (nz(upTrndSAR[1]) != 0.)
inputs := math.abs(inprice - nz(upTrndSAR[1]))
else
if (nz(dnTrndSAR[1]) != 0.)
inputs := math.abs(inprice - nz(dnTrndSAR[1]))
step := minStep + _afact(adaptMode, inputs, length, adaptSmth) * (maxStep - minStep)
else
step := maxStep
upTrndSAR := 0., dnTrndSAR := 0., upSig := 0., dnSig := 0.
if (nz(trend[1]) > 0)
if (nz(trend[1]) == nz(trend[2]))
aFactor := hVal1 > hVal2 ? nz(aFactor[1]) + step : aFactor
aFactor := aFactor > maxAFactor ? maxAFactor : aFactor
aFactor := hVal1 < hVal2 ? startAFactor : aFactor
else
aFactor := nz(aFactor[1])
upTrndSAR := nz(upTrndSAR[1]) + aFactor * (hVal1 - nz(upTrndSAR[1]))
upTrndSAR := upTrndSAR > loprice1 ? loprice1 : upTrndSAR
upTrndSAR := upTrndSAR > loprice2 ? loprice2 : upTrndSAR
else
if (nz(trend[1]) == nz(trend[2]))
aFactor := lowVal1 < lowVal2 ? nz(aFactor[1]) + step : aFactor
aFactor := aFactor > maxAFactor ? maxAFactor : aFactor
aFactor := lowVal1 > lowVal2 ? startAFactor : aFactor
else
aFactor := nz(aFactor[1])
dnTrndSAR := nz(dnTrndSAR[1]) + aFactor * (lowVal1 - nz(dnTrndSAR[1]))
dnTrndSAR := dnTrndSAR < hiprice1 ? hiprice1 : dnTrndSAR
dnTrndSAR := dnTrndSAR < hiprice2 ? hiprice2 : dnTrndSAR
hVal0 := hiprice0 > hVal0 ? hiprice0 : hVal0
lowVal0 := loprice0 < lowVal0 ? loprice0 : lowVal0
if (minChng > 0)
if (upTrndSAR - nz(upTrndSAR[1]) < minChng * calcBaseUnit() and upTrndSAR != 0. and nz(upTrndSAR[1]) != 0.)
upTrndSAR := nz(upTrndSAR[1])
if (nz(dnTrndSAR[1]) - dnTrndSAR < minChng * calcBaseUnit() and dnTrndSAR != 0. and nz(dnTrndSAR[1]) != 0.)
dnTrndSAR := nz(dnTrndSAR[1])
dnTrndSAR := trend < 0 and dnTrndSAR > nz(dnTrndSAR[1]) ? nz(dnTrndSAR[1]) : dnTrndSAR
upTrndSAR := trend > 0 and upTrndSAR < nz(upTrndSAR[1]) ? nz(upTrndSAR[1]) : upTrndSAR
if (trend < 0 and hiprice0 >= dnTrndSAR + filt * calcBaseUnit())
trend := 1
upTrndSAR := lowVal0
upSig := SignalMode == 'Signals & Stops' ? lowVal0 : upSig
dnTrndSAR := 0.
aFactor := startAFactor
lowVal0 := loprice0
hVal0 := hiprice0
else if (trend > 0 and loprice0 <= upTrndSAR - filt * calcBaseUnit())
trend := -1
dnTrndSAR := hVal0
dnSig := SignalMode == 'Signals & Stops' ? hVal0 : dnSig
upTrndSAR := 0.
aFactor := startAFactor
lowVal0 := loprice0
hVal0 := hiprice0
psar = upTrndSAR > 0 ? upTrndSAR : dnTrndSAR
psar := isPsarAdaptive ? psar : ta.sar(psarStart, psarInc, psarMax)
plot(psar, title='PSAR', color=src < psar ? rajah : magicMint, style=plot.style_circles)
// -- EMA --
float ema = ta.ema(src, emaLength)
plot(ema, title='EMA', color=languidLavender)
// -- Signals --
var string isTradeOpen = ''
var string signalCache = ''
bool enterLong = src > ema and ta.crossover(src, psar) and ta.crossover(osc, 0)
bool enterShort = src < ema and ta.crossunder(src, psar) and ta.crossunder(osc, 0)
// bool exitLong = ta.crossunder(src, ema)
// bool exitShort = ta.crossover(src, ema)
if (signalCache == 'long entry')
signalCache := ''
enterLong := true
else if (signalCache == 'short entry')
signalCache := ''
enterShort := true
if (isTradeOpen == '')
if (enterLong)
isTradeOpen := 'long'
else if (enterShort)
isTradeOpen := 'short'
else if (isTradeOpen == 'long')
if (enterLong)
enterLong := false
else if (isTradeOpen == 'short')
if (enterShort)
enterShort := false
plotshape((isSignalLabelEnabled and enterLong and (isTradeOpen == 'long')) ? psar : na, title='LONG', text='L', style=shape.labelup, color=mediumAquamarine, textcolor=color.white, size=size.tiny, location=location.absolute)
plotshape((isSignalLabelEnabled and enterShort and (isTradeOpen == 'short')) ? psar : na, title='SHORT', text='S', style=shape.labeldown, color=carrotOrange, textcolor=color.white, size=size.tiny, location=location.absolute)
// -- High Low Stop Loss and Take Profit --
bool isHighLowStopLossEnabled = true
bool isAutomaticHighLowTakeProfitEnabled = true
bool recalculateStopLossTakeProfit = false
bool isStrategyEntryEnabled = false
bool isLongEnabled = true
bool isShortEnabled = true
bool isStopLossTakeProfitRecalculationEnabled = true
bool longStopLossTakeProfitRecalculation = isStopLossTakeProfitRecalculationEnabled ? true : (lastTrade == 'short' or lastTrade == 'initial')
bool shortStopLossTakeProfitRecalculation = isStopLossTakeProfitRecalculationEnabled ? true : (lastTrade == 'long' or lastTrade == 'initial')
var float longHighLowStopLoss = 0
var float shortHighLowStopLoss = 0
float highLowStopLossLowest = ta.lowest(_low, highLowStopLossLookback)
float highLowStopLossHighest = ta.highest(_high, highLowStopLossLookback)
if (isHighLowStopLossEnabled)
if (((enterLong and longStopLossTakeProfitRecalculation) or recalculateStopLossTakeProfit) and (isStrategyEntryEnabled ? not(strategy.position_size > 0) : true))
if (highLowStopLossLowest == _low)
longHighLowStopLoss := _high * highLowStopLossBackupMultiplier
else if (highLowStopLossLowest > 0)
longHighLowStopLoss := highLowStopLossLowest * highLowStopLossMultiplier
if (((enterShort and shortStopLossTakeProfitRecalculation) or recalculateStopLossTakeProfit) and (isStrategyEntryEnabled ? not(strategy.position_size < 0) : true))
if (highLowStopLossHighest == _high)
shortHighLowStopLoss := _high * (1 + (1 - highLowStopLossBackupMultiplier))
else if (highLowStopLossHighest > 0)
shortHighLowStopLoss := highLowStopLossHighest * (1 + (1 - highLowStopLossMultiplier))
plot((isLongEnabled and isHighLowStopLossEnabled and (isTradeOpen == 'long')) ? longHighLowStopLoss : na, 'Long High Low Stop Loss', color=magicMint, style=plot.style_circles, trackprice=false)
plot((isShortEnabled and isHighLowStopLossEnabled and (isTradeOpen == 'short')) ? shortHighLowStopLoss : na, 'Short High Low Stop Loss ', color=rajah, style=plot.style_circles, trackprice=false)
// -- Automatic High Low Take Profit --
var float longAutomaticHighLowTakeProfit = na
var float shortAutomaticHighLowTakeProfit = na
if (isAutomaticHighLowTakeProfitEnabled)
if (((enterLong and longStopLossTakeProfitRecalculation) or recalculateStopLossTakeProfit) and (isStrategyEntryEnabled ? not(strategy.position_size > 0) : true))
longHighLowStopLossPercentage = 1 - (longHighLowStopLoss / _close)
longAutomaticHighLowTakeProfit := _close * (1 + (longHighLowStopLossPercentage * automaticHighLowTakeProfitRatio))
if (((enterShort and shortStopLossTakeProfitRecalculation) or recalculateStopLossTakeProfit) and (isStrategyEntryEnabled ? not(strategy.position_size > 0) : true))
shortHighLowStopLossPercentage = 1 - (_close / shortHighLowStopLoss)
shortAutomaticHighLowTakeProfit := _close * (1 - (shortHighLowStopLossPercentage * automaticHighLowTakeProfitRatio))
plot((isAutomaticHighLowTakeProfitEnabled and isHighLowStopLossEnabled and (isTradeOpen == 'long')) ? longAutomaticHighLowTakeProfit : na, 'Long Automatic High Low Take Profit', color=magicMint, style=plot.style_circles, trackprice=false)
plot((isAutomaticHighLowTakeProfitEnabled and isHighLowStopLossEnabled and (isTradeOpen == 'short')) ? shortAutomaticHighLowTakeProfit : na, 'Short Automatic High Low Take Profit', color=rajah, style=plot.style_circles, trackprice=false)
// log.info('Automatic Long High Low Take Profit: ' + str.tostring(longAutomaticHighLowTakeProfit))
// log.info('Automatic Short High Low Take Profit: ' + str.tostring(shortAutomaticHighLowTakeProfit))
// log.info('Long High Low Stop Loss: ' + str.tostring(longHighLowStopLoss))
// log.info('Short High Low Stop Loss: ' + str.tostring(shortHighLowStopLoss))
bool longHighLowStopLossCondition = ta.crossunder(_close, longHighLowStopLoss)
bool shortHighLowStopLossCondition = ta.crossover(_close, shortHighLowStopLoss)
bool longAutomaticHighLowTakeProfitCondition = ta.crossover(_close, longAutomaticHighLowTakeProfit)
bool shortAutomaticHighLowTakeProfitCondition = ta.crossunder(_close, shortAutomaticHighLowTakeProfit)
bool exitLong = (longHighLowStopLossCondition or longAutomaticHighLowTakeProfitCondition) and strategy.position_size > 0
bool exitShort = (shortHighLowStopLossCondition or shortAutomaticHighLowTakeProfitCondition) and strategy.position_size < 0
plotshape((isSignalLabelEnabled and exitLong and (isTradeOpen == 'long')) ? psar : na, title='LONG EXIT', style=shape.circle, color=magicMint, size=size.tiny, location=location.absolute)
plotshape((isSignalLabelEnabled and exitShort and (isTradeOpen == 'short')) ? psar : na, title='SHORT EXIT', style=shape.circle, color=rajah, size=size.tiny, location=location.absolute)
// Long Exits
if (exitLong)
strategy.close('long', comment=longAutomaticHighLowTakeProfitCondition ? 'EXIT_LONG_TP' : 'EXIT_LONG_SL')
isTradeOpen := ''
// Short Exits
if (exitShort)
strategy.close('short', comment=shortAutomaticHighLowTakeProfitCondition ? 'EXIT_SHORT_TP' : 'EXIT_SHORT_SL')
isTradeOpen := ''
// Long Entries
if (enterLong and (strategy.position_size == 0))
strategy.entry('long', strategy.long, comment='ENTER_LONG')
// Short Entries
if (enterShort and (strategy.position_size == 0))
strategy.entry('short', strategy.short, comment='ENTER_SHORT')
// Save last trade state
if (enterLong or exitLong)
lastTrade := 'long'
if (enterShort or exitShort)
lastTrade := 'short'
barcolor(color=isTradeOpen == 'long' ? mediumAquamarine : isTradeOpen == 'short' ? carrotOrange : na)