본문 바로가기
Project/🚀 AI Finance 2026

#29 [모듈4-5] RL 에이전트의 행동을 결정하는 보상 함수 설계

by 로띠 2026. 3. 19.
강화학습

Reward Shaping

RL 에이전트의 행동을 결정하는 보상 함수 설계

Data Collect Feature Eng. Reinforcement Learning Signal

이 글은 AI_Finance(ALCHEMETRIC) 프로젝트의 강화학습 트레이딩 환경에서 보상 함수(Reward Function)가 어떻게 설계되었는지를 다룬다. 보상 함수는 에이전트의 행동 전체를 결정짓는 핵심 요소로, 단 한 줄의 수식이 에이전트를 "무모한 투기꾼"으로 만들 수도, "절제된 트레이더"로 만들 수도 있다.

1. 보상 함수란? — 에이전트에게 “잘했어/못했어”를 알려주는 신호

강화학습에서 에이전트는 정답지 없이 행동한다. 대신 매 스텝마다 환경으로부터 보상(reward)이라는 스칼라 값을 하나 받는다. 에이전트의 목표는 이 보상의 총합을 최대화하는 정책(policy)을 찾는 것이다.

주식 트레이딩의 맥락에서 보상 함수를 설계할 때는 직관적으로 "돈을 벌면 +, 잃으면 -"가 떠오르지만, 그것만으로는 부족하다. 단순한 수익률 보상만 사용하면 에이전트는 이런 행동을 학습할 수 있다.

문제 행동 원인 해결
매 스텝마다 BUY → SELL 반복 수수료 비용 미반영 수수료 페널티
올인 후 급락에 무방비 리스크 미고려 낙폭(MDD) 페널티
자금 절반 이상 소실 극단 시나리오 무시 파산 감지 + 강한 페널티
핵심: 보상 함수는 에이전트의 “가치관”이다. 무엇에 보상을 주고, 무엇에 페널티를 주느냐에 따라 에이전트의 전략이 완전히 달라진다. 이 설계 과정을 Reward Shaping이라 한다.

2. 우리 환경의 기본 구조 — StockTradingEnv 개요

보상 함수를 이해하려면 먼저 그것이 동작하는 환경을 알아야 한다. ALCHEMETRIC의 StockTradingEnv는 Gymnasium 기반의 주식 트레이딩 환경이다.

구성 요소 설정 설명
액션 공간 Discrete(3) HOLD=0, BUY=1, SELL=2
관찰 공간 Box(n_features + 2) 피처 + cash_ratio + stock_ratio
초기 자금 10,000,000 KRW 1천만 원으로 시작
매매 수수료 0.015% 매수/매도 공통
매도 세금 0.23% 매도 시에만 부과
매매 방식 전액 매수 / 전량 매도 비율 매매 없이 all-in / all-out

에피소드 흐름

하나의 에피소드는 단일 종목의 시계열 데이터로 구성된다. 에이전트는 매 스텝(일봉 기준)마다 관찰값을 받고, 행동을 선택하고, 보상을 받는다. 이 루프가 데이터 끝까지 반복되거나, 파산 조건에 도달하면 에피소드가 종료된다.

# 에피소드 루프 개요
obs, info = env.reset()

while not terminated:
    action = agent.predict(obs)         # 에이전트가 행동 선택
    obs, reward, terminated, _, info = env.step(action)
    # reward ← 여기서 보상 함수가 호출된다

이 루프에서 reward를 계산하는 함수가 바로 _compute_reward()이다. 지금부터 이 함수의 각 구성 요소를 하나씩 뜯어본다.

3. 수익률 보상 — portfolio_return

보상 함수의 첫 번째 구성 요소는 포트폴리오 수익률이다. 가장 직관적인 신호로, "이번 스텝에서 내 자산이 얼마나 변했는가"를 측정한다.

# 일간 수익률
portfolio_return = (self.portfolio_value / prev_value - 1.0) if prev_value > 0 else 0.0

여기서 portfolio_value는 현금 + 보유 주식의 현재 시가로 계산된다.

# 포트폴리오 가치 = 현금 + 보유 주식 평가액
self.portfolio_value = self.cash + self.shares * next_price

수치 예시

포트폴리오가 10,000,000원에서 10,050,000원으로 상승한 경우를 보자.

portfolio_return = 10,050,000 / 10,000,000 - 1.0
                 = +0.005   # +0.5% 수익 → 양의 보상

반대로 9,800,000원으로 하락하면 다음과 같다.

portfolio_return = 9,800,000 / 10,000,000 - 1.0
                 = -0.02    # -2.0% 손실 → 음의 보상
설계 의도: 수익률 기반 보상은 에이전트에게 “자산을 늘려라”라는 가장 근본적인 목표를 부여한다. 하지만 이것만으로는 어떻게 늘릴지에 대한 제약이 없다. 나머지 항이 그 역할을 담당한다.

4. 수수료 페널티 — 과매매 방지

현실 시장에서 매매에는 비용이 따른다. ALCHEMETRIC 환경에서 거래 비용은 다음과 같이 구성된다.

매수(BUY) 비용 구조

# 전액 매수
max_shares = int(self.cash / (current_price * (1 + self.transaction_fee)))
cost = max_shares * current_price
fee  = cost * 0.00015          # 매수 수수료 0.015%
self.cash -= (cost + fee)

매도(SELL) 비용 구조

# 전량 매도
revenue = self.shares * current_price
fee     = revenue * 0.00015    # 매도 수수료 0.015%
tax     = revenue * 0.0023     # 매도세 0.23%
self.cash += (revenue - fee - tax)
fee_paid = fee + tax           # 총 비용 = 수수료 + 세금

보상에 반영하는 방법

# 거래비용 페널티
fee_penalty = fee_paid / prev_value if prev_value > 0 else 0.0

fee_paidprev_value로 나누어 정규화한다. 이렇게 하면 포트폴리오 규모에 관계없이 비용의 비율이 페널티가 된다.

수치 예시: 매도 시 비용

항목 금액 산식
매도 금액 10,000,000원 100주 × 100,000원
매도 수수료 1,500원 10,000,000 × 0.00015
매도세 23,000원 10,000,000 × 0.0023
fee_paid 합계 24,500원 1,500 + 23,000
fee_penalty 0.00245 24,500 / 10,000,000

매도 한 번에 약 0.245%의 보상이 차감된다. 만약 에이전트가 매일 BUY → SELL을 반복하면 한 달(20 거래일)에 약 4.9%가 수수료로만 사라진다. 이 페널티가 에이전트에게 “거래에는 비용이 따른다”를 체득시킨다.

핵심: 수수료 페널티 없이 학습하면 에이전트는 작은 변동에도 반응하여 과매매(churning)를 하게 된다. 실제 시장에서는 이런 전략이 수수료로 인해 손실을 낸다. 페널티는 에이전트를 “거래할 만한 가치가 있을 때만 거래하는” 방향으로 유도한다.

5. 최대 낙폭(MDD) 페널티 — 리스크 관리

수익률만 보상하면 에이전트는 고수익-고위험 전략을 선호하게 된다. "한 번 크게 먹으면 중간에 좀 잃어도 괜찮지"라는 논리다. 이를 억제하기 위해 최대 낙폭(Maximum Drawdown, MDD) 페널티를 도입한다.

Peak Tracking

환경은 에피소드 시작부터 현재까지의 최고 포트폴리오 가치(peak_value)를 추적한다.

# reset 시 초기화
self.peak_value = self.initial_balance   # 10,000,000

# 매 스텝마다 갱신
self.peak_value = max(self.peak_value, self.portfolio_value)

peak_value는 한 번 올라가면 절대 내려오지 않는다. 이것이 "역대 최고점 대비 얼마나 떨어졌는가"를 측정하는 기준이 된다.

Drawdown 계산과 페널티

# 현재 낙폭
drawdown = (self.peak_value - self.portfolio_value) / self.peak_value

# 20% 초과분에만 페널티 적용 (계수 0.5)
drawdown_penalty = max(0.0, drawdown - 0.2) * 0.5

이 설계에는 두 가지 핵심 파라미터가 있다.

파라미터 의미
임계값 (threshold) 0.2 (20%) 20% 이하의 낙폭은 허용 (페널티 없음)
계수 (coefficient) 0.5 초과 1%당 0.5%의 페널티

수치 예시

peak_value portfolio_value drawdown drawdown_penalty
11,000,000 10,000,000 9.1% 0 (임계값 미만)
11,000,000 8,500,000 22.7% 0.0136
12,000,000 7,000,000 41.7% 0.1083

20% 임계값은 주식 시장에서 일반적으로 “약세장(bear market)”의 기준으로 쓰이는 수치다. 이 이하의 변동은 정상적인 시장 움직임으로 보고, 그 이상의 낙폭에 대해서만 에이전트에게 리스크 신호를 보낸다.

설계 의도: MDD 페널티는 에이전트에게 “크게 먹고 크게 잃는” 전략 대신 “안정적으로 벌어라”라는 메시지를 전달한다. 임계값 20%는 합리적인 하락 허용 범위를, 계수 0.5는 초과 시 얼마나 강하게 벌을 줄지를 조절한다.

6. 파산 감지 — 극단적 손실 차단

MDD 페널티는 점진적인 리스크 신호다. 하지만 포트폴리오가 초기 자금의 50% 이하로 떨어지는 극단적인 상황에는 더 강력한 조치가 필요하다.

# 파산 체크 (-50% 이하)
if self.portfolio_value <= self.initial_balance * 0.5:
    terminated = True          # 에피소드 강제 종료
    reward -= 1.0              # 파산 페널티

파산 감지는 두 가지 동작을 동시에 수행한다.

동작 효과
terminated = True 에피소드를 즉시 종료하여 더 이상의 손실 방지
reward -= 1.0 매우 큰 음의 보상으로 해당 경로를 강하게 억제

왜 -1.0인가?

일반적인 스텝에서 portfolio_return은 대략 -0.03 ~ +0.03 범위(일간 ±3%)에 놓인다. 이 스케일에서 -1.0은 약 30배 이상 큰 페널티로, 에이전트에게 "이 행동 패턴은 절대 반복하지 마라"라는 매우 강한 신호를 보낸다.

정상 종료 vs 파산 종료

구분 조건 추가 페널티
정상 종료 current_step >= n_steps - 1 없음
파산 종료 portfolio_value <= 0.5 * initial_balance reward -= 1.0
설계 의도: 파산 감지는 “최후의 안전장치”다. MDD 페널티가 점진적 경고라면, 파산 감지는 “빨간 불”이다. 에이전트가 학습 초기에 무작위 행동으로 자금을 날리는 것을 빠르게 차단하여, 학습 효율도 높인다.

7. 보상 함수 전체 조립 — 한 줄의 수학

지금까지 분석한 세 가지 구성 요소를 하나로 합치면, ALCHEMETRIC의 보상 함수 전체가 된다.

def _compute_reward(self, prev_value: float, fee_paid: float) -> float:
    """보상 계산: 포트폴리오 수익률 - 거래비용 - 낙폭 페널티."""

    # (1) 일간 수익률
    portfolio_return = (self.portfolio_value / prev_value - 1.0) \
                       if prev_value > 0 else 0.0

    # (2) 거래비용 페널티
    fee_penalty = fee_paid / prev_value \
                  if prev_value > 0 else 0.0

    # (3) 최대낙폭 페널티 (20% 초과 시)
    drawdown = (self.peak_value - self.portfolio_value) / self.peak_value \
               if self.peak_value > 0 else 0.0
    drawdown_penalty = max(0.0, drawdown - 0.2) * 0.5

    # 최종 보상
    reward = portfolio_return - fee_penalty - drawdown_penalty

    return float(reward)

수학적으로 정리하면 다음 한 줄이 된다.

reward = portfolio_return - fee_penalty - drawdown_penalty

         =  (V_t / V_{t-1} - 1)
            - (fee / V_{t-1})
            - max(0, DD - 0.2) * 0.5

여기에 파산 조건(reward -= 1.0)까지 포함하면 전체 보상 체계가 완성된다.

전체 시나리오 예시

다음은 하나의 스텝에서 에이전트가 SELL 행동을 취한 경우의 보상 계산 예시다.

항목
prev_value (Vt-1) 10,500,000원
portfolio_value (Vt) 10,200,000원
peak_value 11,000,000원
fee_paid 24,500원
# (1) portfolio_return
= 10,200,000 / 10,500,000 - 1.0
= -0.02857

# (2) fee_penalty
= 24,500 / 10,500,000
= 0.00233

# (3) drawdown_penalty
drawdown = (11,000,000 - 10,200,000) / 11,000,000 = 0.0727  # 7.27%
drawdown_penalty = max(0, 0.0727 - 0.2) * 0.5 = 0       # 20% 미만 → 0

# 최종 보상
reward = -0.02857 - 0.00233 - 0
       = -0.03090

이 스텝에서 에이전트는 약 -3.09%의 보상을 받는다. 주가 하락(-2.86%)과 거래 비용(-0.23%)이 합쳐진 결과다. 낙폭이 아직 20% 미만이므로 MDD 페널티는 발동하지 않았다.

8. 보상 설계가 행동을 바꾼다 — 실험적 관점

보상 함수의 각 구성 요소를 제거하면 에이전트의 행동이 어떻게 달라지는지를 정리하면 다음과 같다.

제거한 항 예상되는 에이전트 행동 실전 문제
fee_penalty 제거 과매매 — 작은 등락에도 BUY/SELL 반복 수수료가 수익을 잠식하여 결국 손실
drawdown_penalty 제거 고위험 올인 — 변동성 큰 구간에서 공격적 매매 급락 시 회복 불가능한 대규모 손실
파산 감지 제거 극단적 손실에도 에피소드 계속 진행 무의미한 학습 시간 낭비, 비현실적 경험 축적
portfolio_return만 사용 단기 수익 극대화 — 리스크/비용 무시 백테스트에서만 좋고, 실전에서 파탄

각 항의 역할 요약

ALCHEMETRIC의 보상 함수는 네 가지 신호를 에이전트에게 동시에 전달한다.

구성 요소 신호 학습 효과
portfolio_return “자산을 늘려라” 수익 추구의 기본 동기
fee_penalty “거래에는 비용이 든다” 불필요한 거래 억제
drawdown_penalty “크게 잃지 마라” 리스크 관리, 안정성 추구
파산 페널티 (-1.0) “이 길은 절대 안 된다” 극단적 실패 경로 차단

DQN vs PPO에서의 보상 함수

ALCHEMETRIC은 동일한 StockTradingEnv와 동일한 보상 함수를 DQN과 PPO 두 알고리즘에 공유한다. 같은 보상을 받더라도 두 알고리즘은 그것을 다르게 활용한다.

알고리즘 보상 활용 방식 특성
DQN Q(s, a) = r + γ · max Q(s', a') 각 행동의 기대 가치를 개별 추정
PPO Advantage = r + γV(s') - V(s) 정책 경사를 안정적으로 업데이트

보상 함수가 잘 설계되어 있으면, 어떤 RL 알고리즘을 사용하든 에이전트는 “수익을 추구하되, 비용을 줄이고, 리스크를 관리하는” 방향으로 수렴한다. 보상 함수는 알고리즘의 “목적지”를 설정하고, 알고리즘은 그 목적지까지의 “경로”를 찾는 역할을 한다.

결론: 보상 함수는 RL 에이전트의 전부다. 수학적으로는 단 한 줄이지만, 그 한 줄이 에이전트를 “무모한 투기꾼”으로 만들 수도, “절제된 트레이더”로 만들 수도 있다. ALCHEMETRIC의 보상 함수는 portfolio_return(수익 추구) - fee_penalty(비용 절제) - drawdown_penalty(리스크 관리)라는 세 가지 축으로 에이전트의 가치관을 정의하며, 파산 감지라는 안전장치로 극단적 실패를 차단한다.