quant-knowledge-extractor / src /strategy_template.jl
cyberkyne's picture
Upload 22 files
094a5f6 verified
# ═══════════════════════════════════════════════════════════════════
# 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