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)