Spaces:
Running
Running
File size: 6,598 Bytes
5b9237c 21ac82a 5b9237c 21ac82a 5b9237c 21ac82a 5b9237c | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 | //@version=6
indicator("Munger Engine Strategy", overlay=true)
// ==========================================
// INPUTS
// ==========================================
grp_fund = "Fundamentals"
roe_min = input.float(15.0, "Min ROE (%)", group=grp_fund)
de_max = input.float(50.0, "Max Debt/Equity (%)", group=grp_fund)
grp_strat = "Strategy (Weekly)"
sma_len = input.int(200, "SMA Length", group=grp_strat)
sma_len_100 = input.int(100, "SMA 100 Length", group=grp_strat)
sma_len_50 = input.int(50, "SMA 50 Length", group=grp_strat)
lookback = input.int(4, "Touch Lookback (Weeks)", group=grp_strat)
show_markers = input.bool(true, "Show Entry Markers (with Tooltip)", group=grp_strat)
grp_risk = "Risk Management (Daily ATR)"
atr_len = input.int(14, "ATR Length", group=grp_risk)
atr_mult = input.float(2.0, "Stop Loss ATR Mult", group=grp_risk)
rr_ratio = input.float(3.0, "Risk/Reward Ratio", group=grp_risk)
account_equity = input.float(100000.0, "Account Equity ($)", group=grp_risk)
risk_per_trade_pct = input.float(0.5, "Risk Per Trade (%)", group=grp_risk) / 100
entry_limit_buffer_bps = input.float(75.0, "Entry Limit Buffer (bps)", group=grp_risk)
// ==========================================
// DATA FETCHING (FUNDAMENTALS)
// ==========================================
// Fetch Quarterly data for ROE and Debt/Equity
// We manually calculate D/E to avoid "Symbol resolve error" on some tickers/exchanges
roe = request.financial(syminfo.tickerid, "RETURN_ON_EQUITY", "FQ")
term_debt = request.financial(syminfo.tickerid, "TOTAL_DEBT", "FQ")
term_equity = request.financial(syminfo.tickerid, "TOTAL_EQUITY", "FQ")
// Calculate Debt to Equity (%)
float de = na
if not na(term_debt) and not na(term_equity) and term_equity != 0
de := (term_debt / term_equity) * 100
// Quality Check
is_quality = (not na(roe) and roe > roe_min) and (not na(de) and de < de_max)
// ==========================================
// CALCULATION (WEEKLY TIMEFRAME)
// ==========================================
// We define a tuple function to run inside request.security to ensure logic runs on Weekly bars
// regardless of the chart's timeframe.
calc_weekly_strategy() =>
float ma = ta.sma(close, sma_len)
float ma100 = ta.sma(close, sma_len_100)
float ma50 = ta.sma(close, sma_len_50)
bool touched = false
// Check if Low touched MA in the last N weeks
for i = 0 to lookback - 1
if low[i] <= ma[i]
touched := true
bool green = close > open
// Cond: Close of previous week AND current week > SMA200
// Note: in 'calc_weekly_strategy' context, close[1] is previous week's close
bool above_ma_now = close > ma
bool above_ma_prev = close[1] > ma[1]
[ma, touched, green, ma100, ma50, above_ma_now, above_ma_prev]
// Request Weekly Data
// lookahead=barmerge.lookahead_on forces strict non-repainting historical data if desired,
// but for an indicator 'off' or default is usually fine for current state.
[w_ma, w_touched, w_green, w_ma100, w_ma50, w_above_now, w_above_prev] = request.security(syminfo.tickerid, "W", calc_weekly_strategy())
// Fetch Daily ATR for Risk Calculation (matches engine logic)
d_atr = request.security(syminfo.tickerid, "D", ta.atr(atr_len))
// ==========================================
// SIGNALS
// ==========================================
buy_signal = is_quality and w_touched and w_green and w_above_now and w_above_prev
// ==========================================
// PLOTTING
// ==========================================
plot(w_ma, "Weekly SMA 200", color=color.blue, linewidth=2)
plot(w_ma100, "Weekly SMA 100", color=color.orange, linewidth=1)
plot(w_ma50, "Weekly SMA 50", color=color.yellow, linewidth=1)
// Plot Signal
// We combine Marker and Distance Info into a single label with tooltip for "Hover" effect
if buy_signal and show_markers
// Distance
dist = ((close - w_ma) / w_ma) * 100
txt_dist = str.tostring(dist, "#.2") + "%"
// Trade Plan Calculation
entry_stop = high + syminfo.mintick
entry_limit = entry_stop * (1 + entry_limit_buffer_bps / 10000)
risk = d_atr * atr_mult
sl_price = entry_stop - risk
tp_price = entry_stop + (risk * rr_ratio)
// Position Sizing
risk_dollars = account_equity * risk_per_trade_pct
// Check for valid risk to avoid division by zero
float shares = 0.0
if risk > 0
shares := math.floor(risk_dollars / risk)
position_value = shares * entry_stop
// Build Tooltip
tt = "Distance to SMA: " + txt_dist + "\n" +
"------------------------\n" +
"TRADE PLAN (Daily ATR)\n" +
"Order Type: STOP-LIMIT\n" +
"Buy Stop: " + str.tostring(entry_stop, format.mintick) + "\n" +
"Buy Limit: " + str.tostring(entry_limit, format.mintick) + "\n" +
"Stop Loss: " + str.tostring(sl_price, format.mintick) + "\n" +
"Take Profit: " + str.tostring(tp_price, format.mintick) + "\n" +
"Risk (R): " + str.tostring(risk, format.mintick) + "\n" +
"------------------------\n" +
"POSITION SIZING\n" +
"Shares: " + str.tostring(shares, "#") + "\n" +
"Value: $" + str.tostring(position_value, "#.##") + "\n" +
"Risked: $" + str.tostring(risk_dollars, "#.##")
label.new(bar_index, low, text="", color=color.green, style=label.style_triangleup, size=size.tiny, yloc=yloc.belowbar, tooltip=tt)
// ==========================================
// INFO PANEL
// ==========================================
var table panel = table.new(position.top_right, 2, 4, border_width=1)
if barstate.islast
// Header
table.cell(panel, 0, 0, "Metric", bgcolor=color.gray, text_color=color.white)
table.cell(panel, 1, 0, "Value", bgcolor=color.gray, text_color=color.white)
// ROE
c_roe = (not na(roe) and roe > roe_min) ? color.new(color.green, 60) : color.new(color.red, 60)
table.cell(panel, 0, 1, "ROE", bgcolor=c_roe)
table.cell(panel, 1, 1, str.tostring(roe, format.percent), bgcolor=c_roe)
// D/E
c_de = (not na(de) and de < de_max) ? color.new(color.green, 60) : color.new(color.red, 60)
table.cell(panel, 0, 2, "Debt/Eq", bgcolor=c_de)
table.cell(panel, 1, 2, str.tostring(de, format.percent), bgcolor=c_de)
// Status
status_txt = is_quality ? "QUALITY" : "AVOID"
c_status = is_quality ? color.green : color.red
table.cell(panel, 0, 3, "Status", bgcolor=c_status, text_color=color.white)
table.cell(panel, 1, 3, status_txt, bgcolor=c_status, text_color=color.white) |