# ═══════════════════════════════════════════════════════════════════ # JULIA STRATEGY TEMPLATE # This is the exact format Claude generates for each strategy. # Two functions required. No module/using declarations needed — # all Indicators functions are pre-injected by SignalCompiler.jl. # ═══════════════════════════════════════════════════════════════════ # ── Example: EMA Crossover Strategy ───────────────────────────────── """ Return parameter ranges for walk-forward grid search. Keys must be valid Julia identifiers. Values are Float64 ranges. """ function get_param_grid() :: Dict{String, Vector{Float64}} return Dict( "fast_period" => [10.0, 15.0, 20.0, 25.0], "slow_period" => [40.0, 50.0, 60.0, 80.0], "atr_filter" => [14.0], # single value = no optimization ) end """ Generate trading signals from OHLCV arrays. Arguments (all same length n): open_p, high, low, close, volume :: Vector{Float64} params :: Dict{String,Float64} — one value per key from get_param_grid() Returns Vector{Int} of length n: 1 = enter/hold long -1 = enter/hold short 0 = flat / no position Rules: - Return 0 for the first ~slow_period bars (warmup / NaN period) - Always use isnan() checks before comparisons - Signals are position signals, not entry triggers (engine manages entries/exits from signal transitions) """ function generate_signals( open_p :: Vector{Float64}, high :: Vector{Float64}, low :: Vector{Float64}, close :: Vector{Float64}, volume :: Vector{Float64}, params :: Dict{String, Float64}, ) :: Vector{Int} n = length(close) fast_p = Int(round(get(params, "fast_period", 20.0))) slow_p = Int(round(get(params, "slow_period", 50.0))) atr_p = Int(round(get(params, "atr_filter", 14.0))) fast_ema = ema(close, fast_p) slow_ema = ema(close, slow_p) atr_vals = atr(high, low, close, atr_p) signals = zeros(Int, n) for i in (slow_p + 1):n # Skip if any indicator is NaN (still in warmup) isnan(fast_ema[i]) && continue isnan(slow_ema[i]) && continue isnan(atr_vals[i]) && continue # Optional: ATR volatility filter — only trade when market is moving atr_threshold = close[i] * 0.001 # 0.1% of price atr_vals[i] < atr_threshold && continue if fast_ema[i] > slow_ema[i] signals[i] = 1 # bullish: long elseif fast_ema[i] < slow_ema[i] signals[i] = -1 # bearish: short else signals[i] = 0 # neutral end end return signals end # ═══════════════════════════════════════════════════════════════════ # Example 2: RSI Mean Reversion # ═══════════════════════════════════════════════════════════════════ # function get_param_grid() # return Dict( # "rsi_period" => [7.0, 10.0, 14.0, 21.0], # "oversold" => [25.0, 30.0, 35.0], # "overbought" => [65.0, 70.0, 75.0], # "ma_period" => [20.0, 50.0], # ) # end # # function generate_signals(open_p, high, low, close, volume, params) # n = length(close) # rsi_p = Int(round(get(params, "rsi_period", 14.0))) # oversold = get(params, "oversold", 30.0) # overbought = get(params, "overbought", 70.0) # ma_p = Int(round(get(params, "ma_period", 50.0))) # # rsi_vals = rsi(close, rsi_p) # trend_ma = sma(close, ma_p) # signals = zeros(Int, n) # # for i in (ma_p + rsi_p + 1):n # isnan(rsi_vals[i]) && continue # isnan(trend_ma[i]) && continue # # # Mean reversion: buy oversold in uptrend, sell overbought in downtrend # if rsi_vals[i] < oversold && close[i] > trend_ma[i] # signals[i] = 1 # elseif rsi_vals[i] > overbought && close[i] < trend_ma[i] # signals[i] = -1 # end # end # return signals # end