FinLab

PEG Strategy: Finding Undervalued Growth Stocks with Price/Earnings-to-Growth Ratio

January 16, 2026

Low P/E Doesn't Mean Cheap: Beware of Value Traps

Many investors get excited when they see a low P/E ratio, thinking they've found a bargain. But have you ever asked: Why is the market giving it such a low valuation?

The answer might be: the company is in decline.

A company with a P/E of just 5x isn't actually cheap if its revenue is shrinking and profits are declining year after year—the market is simply reflecting its dim future.

Conversely, a company with a P/E of 25x might be truly undervalued if its earnings are growing 50% annually.

This is why Peter Lynch introduced the PEG (Price/Earnings-to-Growth) ratio in his book "One Up On Wall Street":

PEG = P/E / Earnings Growth Rate

When PEG < 1, the stock price is cheap relative to its growth; PEG > 2 may indicate overvaluation.

Today we'll use data to verify: Does the PEG strategy really work in the Taiwan stock market? Through 4 rounds of iterative optimization, we found a strategy with 25.6% CAGR and only 37% maximum drawdown.


PEG Strategy Performance in Taiwan Stocks

Before diving into optimization, let's see how the basic PEG strategy performs.

Build a basic PEG strategy using FinLab: select the 10 stocks with the lowest PEG, rebalance monthly. Conditions: PEG between 0-2, positive operating profit growth, basic liquidity.

Show Code
from finlab import data
from finlab.backtest import sim
 
# Get data
pe = data.get("price_earning_ratio:本益比")
營業利益成長率 = data.get("fundamental_features:營業利益成長率")
當月營收 = data.get("monthly_revenue:當月營收")
volume = data.get("price:成交股數")
 
# Calculate PEG
peg = pe / 營業利益成長率
 
# Build conditions
cond = (
    (peg > 0) & (peg < 2) &           # Reasonable PEG range
    (營業利益成長率 > 0) &             # Ensure positive growth
    (當月營收.average(3) / 當月營收.average(12) > 1.1) &  # Revenue momentum
    ((volume / 1000).average(20) > 300)  # Liquidity
)
 
# Select 10 stocks with lowest PEG
position = (peg * cond)
position = position[position > 0].is_smallest(10)
position = position.reindex(當月營收.index_str_to_date().index, method="ffill")
 
# Backtest
report = sim(position, resample="M", stop_loss=0.15, upload=False)
stats = report.get_stats()
print(f"CAGR: {stats['cagr']:.1%}")
print(f"Sharpe Ratio: {stats['monthly_sharpe']:.2f}")
print(f"Max Drawdown: {abs(stats['max_drawdown']):.1%}")

Results:

Metric Value
CAGR 9.7%
Sharpe Ratio 0.40
Max Drawdown 50.5%

The basic strategy does beat savings deposits, but has two obvious problems:

  1. Mediocre returns: 9.7% is only slightly better than the market
  2. Excessive drawdown: 50% max drawdown is hard for most people to stomach

This is what we need to optimize.


AI-Assisted Iterative Optimization: From 9.7% to 25.6%

Here's the core of this article: the complete process of 4 rounds of AI-assisted optimization.

I set three optimization targets:

Metric Target Reason
CAGR > 15% Significantly outperform market
Sharpe Ratio > 0.6 Reasonable risk-adjusted return
Max Drawdown < 40% Tolerable for average investors

Iteration 1: Exploring Parameter Space

Test different parameter combinations: stricter PEG thresholds, adding RSI momentum, different holding sizes, different rebalancing frequencies. Find which factors have the most impact on performance.

Show Code
# Test weekly rebalancing + RSI momentum filter
rsi = data.indicator("RSI", timeperiod=14)
roe = data.get("fundamental_features:ROE稅後")
 
cond_rsi = (
    (peg > 0) & (peg < 1.5) &
    (營業利益成長率 > 5) &
    (當月營收.average(3) / 當月營收.average(12) > 1.1) &
    ((volume / 1000).average(20) > 500) &
    (roe > 5) &
    (pe > 5) & (pe < 30) &
    (rsi > 50)  # RSI momentum upward
)
 
position = (peg * cond_rsi)
position = position[position > 0].is_smallest(10)
position = position.reindex(當月營收.index_str_to_date().index, method="ffill")
 
# Weekly rebalancing
report = sim(position, resample="W", stop_loss=0.10, upload=False)

Testing revealed an important phenomenon:

Configuration CAGR Sharpe MDD
Monthly (baseline) 9.2% 0.38 49.6%
Weekly + RSI 28.8% 0.83 54.8%

Finding: Increasing rebalancing frequency caused CAGR to jump from 9.2% to 28.8%! But max drawdown also reached 54.8%, still exceeding our target.

Iteration 2: Attempting to Reduce Drawdown

Try various risk control methods to reduce drawdown: stricter stop-loss, trailing stop, take-profit.

Show Code
# Test different stop-loss/trailing stop configurations
configs = [
    {"stop_loss": 0.10, "trail_stop": None},   # Baseline
    {"stop_loss": 0.08, "trail_stop": None},   # Strict stop-loss
    {"stop_loss": 0.10, "trail_stop": 0.05},   # Trailing stop
    {"stop_loss": 0.10, "take_profit": 0.20},  # Take profit
]
 
for cfg in configs:
    report = sim(position, resample="W", **cfg, upload=False)
    # ... record results

Surprisingly:

Risk Control CAGR MDD Conclusion
10% stop-loss 28.8% 54.8% Baseline
8% stop-loss 27.9% 58.3% MDD increased
5% trailing 14.1% 63.2% Much worse
20% take-profit 28.5% 53.2% Slight improvement

Finding: Traditional stop-loss/trailing stop doesn't work for growth stock strategies! Because growth stocks fall together during systemic declines, stop-losses only lock in losses.

Iteration 3: Adjusting Strategy Structure

Since risk controls don't work, let's adjust the strategy structure itself.

Test different rebalancing frequencies (weekly, bi-weekly, monthly, quarterly) and holding sizes, observe impact on MDD.

Show Code
# Test bi-weekly rebalancing
report = sim(position, resample="2W", stop_loss=0.12, upload=False)
Frequency CAGR Sharpe MDD
Weekly 28.8% 0.83 54.8%
Bi-weekly 24.9% 0.83 48.4%
Monthly 16.0% 0.57 55.0%
Quarterly 25.6% 0.93 37.0%

Key Finding: Reducing rebalancing frequency effectively lowers MDD! Quarterly rebalancing has only 37% MDD while maintaining 25%+ returns.


Backtest Results

After 4 iterations, we found a strategy configuration that meets all targets.

Metric Basic Optimized Target Status
CAGR 9.7% 25.6% > 15%
Sharpe Ratio 0.40 0.93 > 0.6
Max Drawdown 50.5% 37.0% < 40%

Strategy Analysis: Why Does Quarterly Rebalancing Have the Lowest MDD?

This finding seems counterintuitive: reducing trading frequency actually reduces risk? Here's why:

  1. Avoid chasing highs and selling lows: High-frequency rebalancing tends to buy at peaks and sell at bottoms
  2. Reduce transaction costs: Frequent trading accumulates fees and slippage that erode returns
  3. Let winners run: Good stocks need time to develop; selling too early misses the big moves
  4. Filter short-term noise: Quarterly timeframe only considers medium-term trends, not daily fluctuations

Growth stock strategy MDD is hard to reduce with traditional risk controls—you must adjust the strategy structure itself.

Want to build your own strategy?

Describe your stock-picking ideas in natural language. AI automatically validates, backtests, and gives you answers

Start Free