Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/Y-Research-SBU/QuantAgent/llms.txt

Use this file to discover all available pages before exploring further.

QuantAgent’s Indicator Agent computes five technical indicators using TA-Lib. Each indicator targets a different dimension of price behavior, giving the downstream Decision Agent a broad signal basis.

Indicators

RSI measures the speed and magnitude of recent price changes to identify momentum extremes.
  • Values above 70 indicate overbought conditions (potential reversal or pullback)
  • Values below 30 indicate oversold conditions (potential bounce or reversal)
  • RSI divergence — when price makes a new high/low but RSI does not — is treated as a high-weight signal by the Decision Agent
rsi = talib.RSI(df["Close"], timeperiod=14)
The last 28 values are returned to state.
MACD tracks the relationship between two exponential moving averages of closing prices.
ComponentDescription
MACD lineEMA(12) − EMA(26)
Signal lineEMA(9) of the MACD line
HistogramMACD line − Signal line
Key signals the Decision Agent weighs heavily:
  • Bullish crossover — MACD line crosses above signal line
  • Bearish crossover — MACD line crosses below signal line
  • Histogram expansion — increasing divergence confirms momentum direction
macd, macd_signal, macd_hist = talib.MACD(
    df["Close"], fastperiod=12, slowperiod=26, signalperiod=9
)
The Stochastic Oscillator compares a closing price to its price range over a recent period, surfacing overbought and oversold levels relative to recent trading range rather than absolute price.
ComponentParametersDescription
%Kfastk_period=14Raw stochastic value
%Dslowk_period=3, slowd_period=33-period SMA of %K
  • Values above 80: overbought
  • Values below 20: oversold
  • %K crossing above %D signals bullish momentum; crossing below signals bearish momentum
stoch_k, stoch_d = talib.STOCH(
    df["High"], df["Low"], df["Close"],
    fastk_period=14, slowk_period=3, slowd_period=3
)
Williams %R measures where the closing price sits within the high-low range of the last 14 periods. It is particularly sensitive to short-term overbought and oversold extremes.
  • Range: 0 to −100
  • Above −20: overbought
  • Below −80: oversold
Williams %R and the Stochastic Oscillator are complementary — both use high/low/close but with inverted scaling. Using both reduces false signals from either alone.
willr = talib.WILLR(df["High"], df["Low"], df["Close"], timeperiod=14)
ROC measures the percentage change in price over the last 10 periods, making it a direct proxy for price momentum:
ROC = ((Close[now] - Close[10 periods ago]) / Close[10 periods ago]) × 100
  • Positive ROC: upward momentum
  • Negative ROC: downward momentum
  • ROC moving toward zero from positive territory can signal momentum exhaustion
roc = talib.ROC(df["Close"], timeperiod=10)
All five indicators return the last 28 values to state. This window is wide enough for the LLM to assess recent trend direction while remaining concise enough for reliable token-based analysis.

Trendline fitting

The Trend Agent uses a custom least-squares trendline fitting algorithm implemented in graph_util.py. It fits both a close-price channel and a high/low channel.

Algorithm overview

1

Least-squares baseline

np.polyfit fits a line of best fit to the closing prices (or high/low series). This gives an initial slope and intercept that represents the overall directional trend.
coefs = np.polyfit(x, close, 1)  # coefs[0] = slope, coefs[1] = intercept
line_points = coefs[0] * x + coefs[1]
2

Pivot identification

The algorithm identifies two pivot points:
  • Upper pivot — the candle index where price deviates furthest above the baseline (used for resistance)
  • Lower pivot — the candle index where price deviates furthest below the baseline (used for support)
upper_pivot = (high - line_points).argmax()
lower_pivot = (low - line_points).argmin()
3

Slope optimization

optimize_slope refines the initial slope for each trendline so it stays entirely above (resistance) or below (support) all price data while minimizing the sum of squared distances to price. It uses numerical gradient descent with a halving step size.
support_coefs = optimize_slope(True, lower_pivot, coefs[0], low)
resist_coefs  = optimize_slope(False, upper_pivot, coefs[0], high)
check_trend_line enforces the validity constraint — if a support line has any value above actual prices, it returns −1 (invalid). The optimizer keeps reducing the step size (curr_step *= 0.5) until it converges below min_step = 0.0001.
4

Dual channel rendering

Two sets of trendlines are computed and overlaid on the chart:
  • Close-based channel (blue support, red resistance) — fitted to closing prices via fit_trendlines_single
  • High/Low channel (white lines) — fitted to candle highs and lows via fit_trendlines_high_low
The vision LLM receives both channels in a single image and interprets their combined structure.

Validity check

check_trend_line returns −1.0 for any slope that violates the channel constraint:
def check_trend_line(support: bool, pivot: int, slope: float, y: np.array):
    intercept = -slope * pivot + y.iloc[pivot]
    line_vals = slope * np.arange(len(y)) + intercept
    diffs = line_vals - y

    if support and diffs.max() > 1e-5:
        return -1.0   # support line crossed above price — invalid
    elif not support and diffs.min() < -1e-5:
        return -1.0   # resistance line crossed below price — invalid

    return (diffs**2.0).sum()   # squared error for valid lines

Candle window selection

The two chart generation tools use different lookback windows, each tuned to the type of analysis being performed:
ToolCandles usedSource
generate_kline_image (Pattern Agent)Last 40 candlesdf.tail(40) in graph_util.py:278
generate_trend_image (Trend Agent)Last 50 candlesdata.iloc[-50:] in graph_util.py:169
Why 40 for pattern recognition? Classical candlestick patterns (flags, wedges, triangles) form over 10–30 candles. Using 40 candles gives the vision LLM enough context to identify formation boundaries without including so much history that small patterns become visually compressed. Why 50 for trendline analysis? Trendlines require a longer price series to identify meaningful pivot highs and lows. Using 50 candles improves the accuracy of the least-squares baseline and ensures the optimized slopes reflect a genuine directional channel rather than short-term noise.
If you are trading on very short timeframes (e.g., 1-minute candles), the default 40 and 50 candle windows cover only 40–50 minutes of market history. Consider increasing the input kline_data length and verifying that the fitted trendlines reflect meaningful price structure for your timeframe.

Build docs developers (and LLMs) love