ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 여행 갈 수 있을까..? ARIMA 모형과 함께하는 유로 환율 예측(2)
    데이터 2025. 2. 16. 12:35

    지난 글에 이어서 시계열 분석 모형 중 하나인 ARIMA 모형으로 분석작업한 진행한 과정의 기록입니다!

    https://hyun21.tistory.com/17

     

    여행 갈 수 있을까..? ARIMA 모형과 함께하는 유로 환율 예측(1) - 전처리

    계기: 월간데이터노또(트)어느 날 글또 '데이터-ai 빌리지' 슬랙 채널에 올라온 글 하나...보는 순간 너무 재밌어보이잖아?! 물론 내가 이걸 꾸준히 할 수 있을까? 란 생각도 잠시 들었지만 할 수

    hyun21.tistory.com

     

    유로 환율 그래프(2022.12.30 ~ 2025.01.24)

    파이썬 라이브러리 matplotlib으로 2022년12월30일부터 2025년1월25일까지 유로 환율 값의 추이를 보기 위한 그래프를 그려 본 모습입니다. 당연하다면 당연하게도 계속해서 변화하고 변동 폭이 큰 부분도 있습니다만 전반적으로 상향하는 추세를 확인할 수 있습니다. 비정상 시계열일 가능성이 높아보입니다.

     

    plt.figure(figsize=(22,8))
    plt.plot(df_eur_f.search_date, df_eur_f.deal_bas_r)
    plt.title("EUR TRENDS 23/01/01 ~ 25/p01/24")
    plt.xlabel("date")
    plt.ylabel("EUR")
    # plt.show()

     

    기초통계

    • 전체 데이터 기준 기초 통계량
    MEAN 1445.47
    STD 43.37
    MIN 1323.90
    25% 1420.92
    50% 1445.92
    75% 1481.08
    MAX 1537.85

     

    전체 기간으로 기초 통계 결과 기준, 평균 1445.47 중앙값 1445.92 정도 선으로 75% 기준인 1,481원 내외일 경우는 비싼 가격으로 판단할 수 있을 것 같습니다.

     

    다만, 최근 환율이 급격하게 변화하면서 최근 기준 금액 단위가 다를 수 있다는 생각에 최근 흐름을 보고 싶어 대략 3,4개월 선인 2024년 10월부터 2025년1월 24일까지 데이터를 잘라서 기초 통계량 수치도 함께 확인해보았습니다. 

    MEAN 1492.06
    STD 18.36
    MIN 1446.15
    25% 1477.99
    50% 1495.72
    75% 1503.08
    MAX 1537.85

     

    그래프로 추세를 확인하면서도 보였지만, 전반적으로 전체 기간에 비하면 가격이 올라간 것이 확인됩니다. 중위수가 1,495원으로 전체 데이터 기준 기초 통계량의 75%(Q3) 값 보다도 높은 것 또한 확인이 됩니다. 최근 데이터만을 기준으로 했을 때는, 75%(Q3)인 해당 금액이 1,503원 선에서는 비싼 가격이라고 판단해도 좋을 것 같습니다. (그리고 저는 이전 1502원에 유로화를 일부 샀습니다…🥲)

     

     

    시계열 분석 그리고 ARIMA 모형

    본격적인 시계열 분석에 앞서, 시계열 분석과 그 안의 ARIMA 모형이 무엇인지에 대해 간단하게만 이야기해볼까합니다. 시계열 분석은 시간의 흐름에 따라 발생하는 데이터를 분석하는 방법론으로, 패턴을 찾고 미래를 예측하는 데 주로 사용되는 분석 방법론입니다. 예시로 주식, 날씨, 경제 지표 등을 예측하는 데 이용되기도 합니다. 이러한 시계열 방법론 중 하나가 ARIMA 모형입니다. ARIMA 이전에 ARMA를 먼저 이야기해보자면 ARMA 모형은 AR(Autoregression,자기회귀) 모형과 MA(Moving Average,이동평균) 모형을 합친 모형입니다. 여기서 ARIMA 모형은 ARMA모형에 차분을 더한 모형입니다. ARIMA(p,d,q) 와 같이 표기하며, 이는 d차 차분한 데이터에 AR(p)와 MA(p) 모형을 합친 것이라 할 수 있습니다. 이 글은 유로환율을 분석에 대한 기록이기에 이론적인(?) 부분은 이 정도로만 기술하겠습니다!

     

    정상성 췍~

    체크하고자 하는 정상성에 대해서 간단하게 살펴보면, 정상(stationary)이란 시간에 상관없이 일정한 성질(평균과 분산 모두 일정)이고 위에서 언급한 비정상(non-stationary)은 추세나 계절성 등에 영향을 받는 종류(시간의 흐름에따라 평균과 분산이 변하는 특성)를 의미합니다. 모형 적용에 앞서 이 데이터가 정상 시계열 데이터인지 비정상 시계열 데이터인지 확인해보아야 합니다. (차분을 할지 말지도 생각할 수 있습니다.)

    위에서부터 순서대로 실제 데이터, 추세(Trend), 계절성(Seasonal), 잔차(Residual) 을 그래프로 우선 확인해봅니다. 위에서 환율 데이터 그래프에서도 언급했듯이 점진적으로 상향하는 형태를 볼 수 있으며 추세 또한 마찬가지로 등락은 있으나 꾸준히 올라가는 모양을 확인할 수 있습니다. 또한 계절성에서 꽤나 타이트하게 일정 주기를 가지고 반복되는 변동 패턴 또한 확인이 됩니다. 

    ACF(자기상관함수) 그래프를 보아도, ACF가 천천히 감소하며 큰 값 양의 값을 계속해서 유지하는 모양을 볼 수 있습니다. 위의 계절성 그래프와 마찬가지로 이 그래프를 통해 비정상 시계열 데이터임을 가정해볼 수 있습니다. 

     

    그렇다면 이제는 가정으로 남겨든 이 데이터는 과연 정상 시계열 데이터인가 비정상 시계열 데이터인가?!를 ADF(Augmented Dicky-Fuller) 검정을 통해 결론을 내려보기로 합니다. 이 때, 귀무가설은 정상성을 만족하지 않는다 / 대립가설은 정상성을 만족한다 입니다.

    ADF Statistocs: -2.142072
    p-value: 0.227937
    Critical Values: 
    	1%: -3.439
    	5%: -2.865
    	10%: -2.569
    
    검정결과 p-values(유의확률) 0.227937로 유의수준 0.05 에서 해당 귀무가설을 기가할 수 없습니다. 유로 환율 데이터는 정상성을 만족하지 않는다. 비정상 시계열로 결론 내릴 수 있습니다. 
     
    # 추세, 계절성, 잔차 그래프
    seasoanl_result = seasonal_decompose(ts_df['deal_bas_r'], model='additive', period=12)
    fig = plt.figure()
    fig = seasoanl_result.plot()
    fig.set_size_inches(20, 15)
    
    # H0: 정상성 만족x vs H1: 정상성 만족 => H0 기각 못함 (=정상성 만족X) 예견된 미래..
    result = adfuller(ts_df)
    print('ADF Statistocs: %f' % result[0])
    print('p-value: %f' % result[1])
    print('Critical Values: ')
    for key, value in result[4].items():
        print('\t%s: %.3f' % (key, value))
        
        
    # ACF 그래프
    fig = plt.figure(figsize=(20,8))
    ax1 = fig.add_subplot(211)
    fig = sm.graphics.tsa.plot_acf(ts_df, ax=ax1)
    # plt.savefig('lag.png')

    차분과 ARIMA(p,d,q)를 찾아서...

    차분은 이전 시점의 데이터를 빼서 추세를 제거하는 작업으로 지금 제 데이터에 필요한 작업입니다. 일종의 비정상 시계열을 정상 시계열로 바꾸위한 것입니다. 

    ADF Statistocs: -15.124567
    p-value: 0.000000
    Critical Values: 
    	1%: -3.439
    	5%: -2.865
    	10%: -2.569

    차분한 데이터로 다시 ADF(Augmented Dicky-Fuller) 검정 결과를 보면, p-value가 매우 작은 값으로(0에 수렴) 유의수준 0.05에서 귀무가설 기각됨을 볼 수 있습니다. 이제는 정상성을 만족한다는 결론! 

    # 1차 차분 진행
    ts_diff_df = ts_df - ts_df.shift()
    plt.figure(figsize=(22,8))
    plt.plot(ts_diff_df)
    plt.title("Differencing method")
    plt.xlabel("date")
    plt.ylabel("EUR")
    
    # 귀무가설 기각됨
    result = adfuller(ts_diff_df[1:])
    print('ADF Statistocs: %f' % result[0])
    print('p-value: %f' % result[1])
    print('Critical Values: ')
    for key, value in result[4].items():
        print('\t%s: %.3f' % (key, value))

     

    다음으로 AR(p)와 MA(q) 차수를 찾기위해, ACF(자기상관함수)와 PACF(편자기상관함수) 그래프를 살펴봅니다. 

     

    ACF, PACF 모두 시차1 혹은 3 이후로 0에 수렴하는 것으로 보입니다. 다만, 시차 3의 범위(계수)가 크게 보이는 관계로 ARIMA(3,1,3) 모형으로 확인해보고자 합니다. 참고로 ARIMA(1,1,1)모형도 충분히 고려 가능합니다만 두 모형 모두 실행해보았을 때, ARIMA(3,1,3) 모형 모델 성능이 더 좋았기에 최종적으로 ARIMA(3,1,3) 모형으로 채택했습니다. 이렇게 분석자가 직접 확인하고 p,d,q 설정과 모형 분석까지 진행해도 괜찮지만, Auto ARIMA를 이용하여 파라미터 찾기를 자동으로 할 수 있는 선택지도 있습니다. 더 쉽고 빠르게 좋은 모델을 찾을 수도 있었지만 저의 경우 이번에는 기억도 되살릴 겸 저는 직접 확인하는 방식으로 진행합니다. 

    # ACF, PACF 그래프 확인 -> ARIMA 모형의 p,q 결정
    fig = plt.figure(figsize=(20,8)) # ACF
    ax1 = fig.add_subplot(211)
    fig = sm.graphics.tsa.plot_acf(ts_diff_df[1:], lags=20, ax=ax1)
    ax2 = fig.add_subplot(212) # PACF
    fig = sm.graphics.tsa.plot_pacf(ts_diff_df[1:], lags=20, ax=ax2)

     

    모형 적합성

    R2 score corrcoef RMSE MAPE
    96.991 0.985 7.48 0.358

    R2 값을 보면, 95.991 정도의 적합도를 보임을 확인할 수 있고, RMSE 값으로 7.48 수준 임을 확인할 수 있습니다. 모델 성능이 썩 괜찮은 편은 아이지만 크게 나쁘지도 않습니다.(개인적으로 ARIMA 모형으로 할 수 있는 나름의 최선이 아니었을까 생각합니다. Auto ARIMA로 찾아본다면 또 다를 수도 있겠지만요.)

     

    model = ARIMA(ts_df, order=(3,1,3)) # 차분1, p=3, q=3
    model_fit = model.fit()
    
    start_index = datetime(2023, 1, 1)
    end_index = datetime(2025, 1, 24)
    forecast = model_fit.predict(start=start_index, end=end_index, type='levels')
    
    scoring(np.array(df_eur_f[df_eur_f.search_date>=datetime(2023, 1, 1)].deal_bas_r), np.array(forecast)) #3,1,3

    유로 환율 예상(결론)

    기존 유로 환율 추세와 제가 적용한 ARIMA(3,1,3) 모델로 적합시켜본 예측 추세 입니다.(파란 색 선이 기존 데이터이며, 주황색 선이 예측 그래프 입니다). 오차와 살짝의 밀림 현상같이 보이지만 추세는 어느 정도 따라가고 있는 것으로 확인됩니다. 실제 수치로도 예상 금액을 한 번 뽑아보았습니다. 

    plt.figure(figsize=(22, 8))
    plt.plot(df_eur_f.search_date, df_eur_f.deal_bas_r, label = "original")
    plt.plot(forecast, label = "predicted")
    plt.xlabel("date")
    plt.ylabel("deal_bas_r")
    plt.legend()
    • 남은 1월 기간 유로 환율 예측 금액날짜 예측 유로 금액
      2025-01-25 1498.65
      2025-01-26 1497.84
      2025-01-27 1497.97
      2025-01-28 1498.28
      2025-01-29 1497.84
      2025-01-30 1498.24
      2025-01-31 1497.97
    • 예측 기초 통계량(1월 ~ 2월 기준)
      mean 1501.89
      std 7.81
      min 1492.53
      25%9(Q1) 1498.07
      50%(Q2) 1498.07
      75%(Q3) 1502.17
      max 1528.93
      IQR 4.1
      IQR_min 1491.92
      IQR_max 1508.32
      적용한 ARIMA(3,1,3) 모델 기준으로 예측한 25년 1월 1일 부터 2월 28일까지 동안 유로 환율 기초 통계 값과 추가로 IQR 을 함께 계산해보았습니다. IQR은 사분위 수 중 3사분위수 - 1사분위수로 계산하는 값(예측 기초 통계량 표의 Q3 - Q1) 으로 이상치를 판정하는 범위 계산에 사용되기도 합니다. 사실 예측치를 내보면 금액 변화가 크게는 보이지 않았습니다. 아마 이건 제가 고시환율 업데이트가 없던 부분에 대해 데이터를 이전 값을 가져와 넣으면서 발생한 현상이 아닐까 추측 중에 있습니다. 

     

    결론: 그래서 여행 갈 수 있나요?

    이 분석을 시작한 목적으로 돌아와서 유로화 구매 기준 금액을 어떻게 잡으면 좋을까? 에 답해볼까 합니다.(자문자답) 어찌보면 당연하게도전체 그래프를 보아도 결국엔 유로 환율은 변동이 있고 오를 때와 내릴 때가 있습니다. 다만, 멀지않은 미래에 환전을 해야하는 상황에서 예상 금액과 기준 금액을 정하고 싶어서 해당 작업을 진행했는데요. 기준을 어떻게 잡으면 좋을지도 고민이 됐습니다. 어느 시점 데이터가 이러니까 이거지!라고 하기엔 다른 변수요인이 더 필요했기 때문인데요. 그래서 IQR 값을 활용하기로 했습니다. 위에서 언급했듯이 IQR은 이상치를 판정하는 범위 계산에 사용되기도 합니다. 이를 역으로 이용하여 해당 범위를 기준으로 잡고자합니다.

    • IQR로 계산한 minimum=1491.92
    • IQR로 계산한 maximum=1508.32

    일단 1월을 포함한 2월까지 예상 유로 환율 데이터 기반으로 IQR로 계산한 최대값인 1,508원 이상일 경우엔 최대한 구매하지 않을 예정이며, 최소값인 1,491원 ~ (예측 데이터의) 중위수인 1,498원 범위 내일 때 환전을 고려해보면 좋을 것 같다고 생각합니다. 물론 유로화가 싸면 쌀 수록 저는 좋기때문에, 1,491원보다 내려간다면 기쁘게 바로 환전할 예정입니다!

     

    코멘트

    너무 오랜만에 분석 작업 + 파이썬으로 처음해보는 시계열 분석의 대환장 파티…였습니다. 글 조차도 얼레벌레 흘러간 느낌인게 참 아쉽고 그러네요..? 그리고 API 호출이 꽤 자주 불안정한 느낌😇 

    추가로 해보고 싶은 것도 있었는데, 시계열 모형으로 고도화 작업과 DB 안되면 구글닥스로라도 데이터 export 작업(일별 배치 형식)을 진행해서 쌓아두고 계속 활용해보고 싶다!는 저만의 니즈가 있었습니다.

Designed by Tistory.