강화학습 PPO 모델을 이용한 자동매매 2

AI주식자동매매|2024. 8. 9. 07:28
반응형

import numpy as np
import pandas as pd
import gym
from gym import spaces
from stable_baselines3 import PPO
from stable_baselines3.common.evaluation import evaluate_policy
from sklearn.model_selection import train_test_split
from datetime import datetime
from pytz import timezone
from sklearn.model_selection import ParameterGrid
import warnings

# 특정 경고 메시지 무시
warnings.filterwarnings("ignore", category=UserWarning)

class TradingEnv(gym.Env):
    def __init__(self, df):
        super(TradingEnv, self).__init__()
        self.df = df
        self.current_step = 0
        self.df_numeric = self._convert_to_numeric(self.df)
        self.action_space = spaces.Discrete(2)  # Buy or Hold

        self.observation_space = spaces.Box(low=0, high=1, shape=(self.df_numeric.shape[1],), dtype=np.float32)

        self.initial_balance = 10000
        self.balance = self.initial_balance
        self.net_worth = self.initial_balance
        self.max_net_worth = self.initial_balance
        self.shares_held = 0
        self.current_price = self.df.iloc[self.current_step]['close']  # 현재 스텝의 가격으로 초기화
        self.price_history = self.df['close'].tolist()  # 전체 가격 데이터를 리스트로 저장

    def _convert_to_numeric(self, df):
        df_numeric = df.copy()
        df_numeric = df_numeric.filter(regex='^m_')
        df_numeric.reset_index(drop=True, inplace=True)  # 인덱스를 드롭하고 리셋
        for column in df_numeric.columns:
            df_numeric[column] = pd.to_numeric(df_numeric[column], errors='coerce')
        df_numeric.fillna(0, inplace=True)
        return df_numeric

    def reset(self):
        self.balance = self.initial_balance
        self.net_worth = self.initial_balance
        self.max_net_worth = self.initial_balance
        self.shares_held = 0
        self.current_step = 0
        self.current_price = self.df.iloc[self.current_step]['close']  # 리셋 시 현재 가격 초기화
        return self._next_observation()

    def _next_observation(self):
        obs = self.df_numeric.iloc[self.current_step].values
        obs_max = obs.max() if obs.max() != 0 else 1  # Prevent division by zero
        obs = obs / obs_max
        return obs

    def step(self, action):
        self.current_step += 1
        self.current_price = self.df.iloc[self.current_step]['close']  # 매 스텝마다 현재 가격 업데이트
        self.low_price = self.df.iloc[self.current_step]['low']  # 매 스텝마다 현재 가격 업데이트
        self.current_time = self.df.index[self.current_step]  # 매 스텝마다 현재 가격 업데이트

        if action == 1:  # Buy
            self.shares_held += self.balance / self.current_price
            self.balance = 0
        elif action == 0:  # Hold
            pass

        self.net_worth = self.balance + self.shares_held * self.current_price
        self.max_net_worth = max(self.max_net_worth, self.net_worth)

        # 1시간 후 가격 변동을 확인하여 보상을 계산
        reward = self.calculate_reward(action)
        done = self.current_step >= len(self.df) - 1

        obs = self._next_observation()
        return obs, reward, done, {}

    def calculate_reward(self, action):
        '''
        현재 가격에서 시작하여 다음 12 스텝 동안의 가격을 모두 체크하며, 그 중 하나라도 5% 이상 상승한 경우 보상으로 1을 반환합니다. 1시간 동안 5% 이상 상승한 적이 없다면 보상으로 0을 반환합니다.
        즉, buy 의견을 제시한것이 잘했는지를 평가할때, reward 보상으로 학습을 시킨다.
        '''
        end_step = min(self.current_step + 12, len(self.df) - 1)  # 1시간 = 12 steps (assuming 5-minute intervals)
        reward = 0

        if action == 1:  # Buy 액션일 경우에만 보상 계산
            for step in range(1, end_step - self.current_step + 1):
                future_price = self.price_history[self.current_step + step]
                price_increase = (future_price - self.current_price) / self.current_price
                if (step - self.current_step) <= 5:
                    if future_price < self.low_price:  # 5봉 이내(30분이내) 현재가보다 하락하고 있으면, reward 없음.
                        break
                if price_increase >= 0.05:  # 5% 이상 상승
                    print("%s self.current_step:%s" % (self.current_time, self.current_step))
                    print("for range step:%s" % (step))
                    print("future_price:%s" % (self.price_history[self.current_step + step]))
                    print("price_increase:%s" % ((future_price - self.current_price) / self.current_price))
                    print("reward = 1")
                    reward = 1
                    break
        return reward  # 1시간 동안 5% 이상 상승하지 않음

def optimize_ppo(data, param_grid, model_path="ppo_trading_model"):
    env = TradingEnv(data)
    best_model = None
    best_reward = -float('inf')
    
    for params in ParameterGrid(param_grid):
        model = PPO('MlpPolicy', env, verbose=1, **params)
        model.learn(total_timesteps=10000)
        total_rewards = evaluate_model(model, data)
        if total_rewards > best_reward:
            best_reward = total_rewards
            best_model = model
            best_model.save(model_path)
    
    return best_model

def train_model(data, model_path="ppo_trading_model"):
    env = TradingEnv(data)
    try:
        model = PPO.load(model_path)
        print("Model loaded successfully. Continuing training...")
    except:
        model = PPO('MlpPolicy', env, verbose=1)
        print("New model initialized.")

    model.set_env(env)
    param_grid = {
        'n_steps': [128, 256, 512],
        'learning_rate': [1e-3, 1e-4, 1e-5],
        'batch_size': [128, 256],  # 변경된 부분: 128의 배수로 설정
    }
    best_model = optimize_ppo(data, param_grid, model_path)
    best_model.learn(total_timesteps=10000)
    best_model.save(model_path)
    return best_model


def load_model(model_path="ppo_trading_model"):
    return PPO.load(model_path)


def evaluate_model(model, data):
    env = TradingEnv(data)
    obs = env.reset()
    total_rewards = 0
    done = False
    while not done:
        action, _states = model.predict(obs)
        obs, reward, done, _ = env.step(action)
        total_rewards += reward
    return total_rewards


def main():
    ticker = 'XEM'
    chart_intervals = 'minute5'
    current_time = pd.to_datetime(datetime.now(timezone('Asia/Seoul'))).strftime("%Y-%m-%d %H:%M:%S")
    chart_data = save_db_market_infos(ticker=ticker, chart_intervals=chart_intervals, current_time=current_time)
    chart_data.set_index('time', inplace=True)
    strategy_data = get_strategy_mst_data()

    if chart_data is not None:
        strategy_chart_data = calculate_indicators(chart_data, ticker)
        strategy_chart_data_df = pd.DataFrame([strategy_chart_data])

        train_data, test_data = train_test_split(chart_data, test_size=0.2, shuffle=False)

        model = train_model(train_data)

        total_rewards = evaluate_model(model, test_data)
        print(f"Total Rewards: {total_rewards}")
        if isinstance(strategy_chart_data_df, pd.DataFrame):
            obs = strategy_chart_data_df.values.flatten().astype(np.float32)
            obs = np.expand_dims(obs, axis=0)
            action, _states = model.predict(obs)
            print("Buy Signal:", "Yes" if action == 1 else "No")
        else:
            print("Error: strategy_chart_data is not a DataFrame")
    else:
        print("Error: chart_data is None")

if __name__ == "__main__":
    main()

반응형

댓글()

코인/주식 자동매수에 머신러닝 강화학습 PPO적용하기 1

AI주식자동매매|2024. 8. 9. 07:13
반응형

PPO (Proximal Policy Optimization)와 DQN (Deep Q-Network)은 강화 학습에서 널리 사용되는 두 가지 알고리즘입니다. 이 두 가지는 각각의 특성과 장단점이 있으며, 특정 상황에 따라 더 적합한 알고리즘이 달라질 수 있습니다.

PPO (Proximal Policy Optimization)

특징:

  • 정책 기반 (Policy-based) 알고리즘: PPO는 정책 기반 방법론을 따르며, 이를 통해 직접적으로 행동 정책을 업데이트합니다.
  • 클립핑: PPO는 정책 갱신 시 클리핑(clipping)을 사용하여 큰 갱신을 방지합니다. 이로 인해 학습 과정에서 안정성을 확보할 수 있습니다.
  • 샘플 효율성: PPO는 여러 번의 에포크 동안 샘플을 다시 사용하므로, 샘플 효율성이 높습니다.

장점:

  • 안정성: PPO는 클리핑을 통해 정책 갱신을 안정적으로 수행합니다.
  • 샘플 재사용: PPO는 여러 번의 업데이트 동안 샘플을 재사용할 수 있어, 데이터 효율성이 높습니다.
  • 연속적 액션 공간: PPO는 연속적이고 다차원적인 액션 공간에서 잘 동작합니다.

단점:

  • 복잡성: PPO는 상대적으로 복잡한 알고리즘이어서 구현과 조정이 어려울 수 있습니다.
  • 트레이닝 시간: PPO는 많은 트레이닝 시간을 필요로 할 수 있습니다.

DQN (Deep Q-Network)

특징:

  • 값 기반 (Value-based) 알고리즘: DQN은 Q-러닝을 심층 신경망으로 확장하여 상태-액션 값 함수를 추정합니다.
  • 에피소드 학습: DQN은 에피소드 단위로 학습을 수행하며, 학습에 사용되는 경험은 리플레이 버퍼에서 샘플링됩니다.
  • 이산적 액션 공간: DQN은 이산적 액션 공간에서 동작하며, 연속적 액션 공간에 대해 사용하기 어렵습니다.

장점:

  • 상대적으로 단순: DQN은 비교적 이해하고 구현하기 쉬운 알고리즘입니다.
  • 이산적 액션 공간: 이산적 액션 공간을 다루는 데 적합하며, 간단한 환경에서 매우 효과적입니다.
  • 연속된 상태에서 학습 가능: DQN은 특정 환경에서 좋은 성능을 보일 수 있으며, 환경이 안정적일 때 강력한 결과를 낼 수 있습니다.

단점:

  • 연속적 액션 공간에서의 한계: DQN은 연속적 액션 공간에 적용하기 어려우며, 이를 다루기 위해 추가적인 기법이 필요합니다.
  • 샘플 비효율성: DQN은 PPO에 비해 샘플 효율성이 낮으며, 많은 데이터를 필요로 할 수 있습니다.
  • 불안정성: 큰 상태 공간이나 복잡한 환경에서는 학습이 불안정할 수 있습니다.

일반적인 사용 사례:

  • PPO: 연속적 액션 공간, 복잡한 환경, 로봇 제어, 자율주행 등에서 많이 사용됩니다.
  • DQN: 이산적 액션 공간, 비교적 단순한 환경, Atari 게임 등에서 많이 사용됩니다.

요약:

  • PPO는 복잡한 환경에서 안정적이고 샘플 효율성이 높은 강화 학습을 원할 때 사용됩니다.
  • DQN은 단순하고 이산적인 액션 공간을 가진 환경에서 사용하기 쉬운 알고리즘입니다.

각각의 장단점을 고려하여, 특정 문제에 맞는 알고리즘을 선택하는 것이 중요합니다.

반응형

댓글()

Upbit API 자동 매매 일지 2023.11.04

가상화폐|2023. 11. 5. 08:28
반응형

안녕하세요?

오늘도 기분좋게 올릴 수 있어서 다행입니다.

업비트 코인지수가 다시 상승세로 돌아서면서 저의 코인 수익매매가 많아지면서 코인자산도 동반상승하고 있습니다.

Upbit 코인동향

 

GoldCoin Upbit API연동거래 화면

감사합니다.

반응형

댓글()

Upbit API 자동 매매 일지 2023.10.30

가상화폐|2023. 10. 30. 23:12
반응형

안녕하세요?

 

오늘도 잊지 않고 올립니다.

하기 화면 참조하세요. 이대로만 가준다면 더 바랄것도 없겠습니다만...

과연 내일도 승리의 연속이 유지될지...궁금해 지는 시점입니다.

 

반응형

댓글()

Upbit API 자동매매일지 2023.10.28

가상화폐|2023. 10. 28. 16:43
반응형

안녕하세요?

할줄아는게 도둑질이라고.. 오늘도 미련을 버리지 못하고 코인자동매매를 돌리면서

로직을 바라보다가 매매일지가 생각나서 다시 올려봅니다.

 

요즘은 11만원정도의 현금으로 코인 자동매매를 돌리고 있습니다.

화면도 와이드 모니터의 절반만 차지하도록 화면 사이즈도 줄였습니다.

강력매수 전략도 딱 두가지로 최소화해서 사용하고 있습니다.

DPL5, MPL3 두가지 입니다.

 

DPL5: 5분봉이 정역배열에서 5이평선이 상승하면서 10이평선을 돌파하면 매수하는 전략

           (단, 이틀연속 일봉이 음봉이면 대상에서 제외)

MPL3: 5분봉이 정역배열에서 5이평선이 3연속 가속증가하면서 2연속 양봉으로 상승중일때 매수하는 전략

GoldCoin(Upbit Version) 1.0

위 화면에서 두번째 가운데 차트가 일봉차트입니다. 보시면 알겠지만,

쩜쩜쩜... 보이는 점들이 저의 계좌의 추정자산 잔고입니다.

예수금과 매입평가금액을 합한 총 자산을 매일매일 기록하고 있고, 그것을 점으로 표현해서 보고 있습니다.

13만원이던 자산이 9만원 이라로까지 줄었다가 최근에는 11만6천원까지 다시 회복하고 있음을 보실 수 있습니다.

반응형

댓글()

골드코인(GoldCoin) EXE 실행파일 생성

가상화폐|2023. 8. 19. 08:12
반응형

1. pyinstaller 사용한 빌드

pyinstaller -F --icon=.\image\goldcoin.ico --paths=C:\ProgramData\Anaconda3\envs\py38_32\Lib\site-packages main_goldcoin.py

  • -F : -F, --onefile 1개의 exe파일로 변환
  • --icon=.\image\goldcoin.ico  탐색창에서 보여지는 아이콘을 설정
  • --paths=C:\ProgramData\Anaconda3\envs\py38_32\Lib\site-packages

 

2. buil파일을 D:\goldcoin\main_goldcoin.exe로 복사

D:\PycharmProject\stockTrader\dist\main.exe 를 D:\goldcoin\main_goldcoin.exe로 Copy

반응형

댓글()

가상화폐 자동매매 프로그램 Upgrade

가상화폐|2022. 2. 2. 20:55
반응형

간만에 가상화폐 자동매매 관련 글 올립니다.

그동안 공지는 못했지만, 꾸준히 모니터링 하면서, 기능 및 로직을 Upgrade해왔습니다.

 

공개할 버젼은 미국주식버젼과 동일하게 업그레이드한 매수세력과 바닥지수를 활용한

매수추천종목 관리 프로그램 버젼입니다.

일명 "세력지수"와 "바닥지수"를 계산해 내는 방식입니다.

1. 세력지수

일봉차트에서 30일 이내 가장 최근의 일일 거래량과 5일이평, 10일이평, 20일이평 거래량을 비교하여 일 거래량이 이평 거래량을 초과하는 시점에 장대양봉이 형성된 날부터 거래량을 +, -하여 해당 거래량을 세력지수로 표현합니다.

2. 바닥지수

위 화면의 일봉차트(가운데 차트)에서 현재가가 최고가선과 최저가선이 얼마나 최저가선에 가까운지, 그리고, 저항선과 지지선에서 얼마나 지지선에 가까운지를 감안하여 바닥지수로 표현합니다.

 

위의 미국주식 투자종목찾기 프로그램은 세력지수와 바닥지수만을 종목별로 계속 재계산하여 

종목별 세력지수 바닥지수 DB에 저장하는 역할만 합니다.

 

이렇게 DB에 저장된 세력지수와 바닥지수를 다음의 프로그램에서는 

지정된 값의 범위에 해당되는 종목만을 매수추천대상 목록으로 띄웁니다.

이 매수방법이 잘 될거라 믿고.. 기다려 보겠습니다. ㅎㅎ

 

이상입니다.

반응형

댓글()

빗썸API 자동매매일지 2021.04.17 17:00

가상화폐|2021. 4. 17. 18:02
반응형

안녕하세요?

 

간만에 자동매매일지를 올립니다.

최근 가상화폐의 급상승 이후, 알트코인들의 동반등락과 급락장세가 이어지면서,

비트코인 8천만원 신고가 달성의 그늘에서 저를 비롯하여 고생하시는 분들이 

많을 듯 합니다.

 

그래도 저는 자동매매의 희망을 놓지 않고, 

추세의 흐름속에서 돈버는 매수타이밍과 전략을 꾸준히 Update 해 나가고 있습니다.

 

현시점의 자산현황입니다.

투자원금에서 -9%이네요. 한때는 26%까지 수익을 올렸었는데, 지금은 마이너스 입니다.

어제 오늘 매수타이밍 로직을 추가 개선했습니다.

 

로직No. M5-1

###########################################################
# 5-1) 급등신호: MACD OSC 0선 이상 반전
#                    MA5, 10, 20, 60, 120 우상향 정배열 초입
#                    not(MA5 > MA10 > MA20) > MA60 > MA120
###########################################################

로직No. M8

#######################################################################################
# 8) macd_osc 0선이하에서 최저점을 돌아설때(최저가도 최저점을 돌아선다)(거래량도 점차 주는 현상이 있음)
#    macd signal 0선 아래에 있어야 한다. , 60이평선은 하락상태이며,
#    전전봉은 음봉이고, 현재봉은 양봉이면서, 전봉이 최저점을 찍고 상승전환하는 현상을 로직에 반영함
또한, macd도 전봉을 기준으로 감소에서 증가로 돌아설때가 현재 매수타이밍이다#######################################################################################

로직No. M9

#######################################################################################
# 9) macd_osc 0선이하에서 최저점을 돌아설때(최저가도 최저점을 돌아선다)(거래량도 점차 주는 현상이 있음)
# macd signal 0선 아래에 있어야 한다. , 60이평선은 하락상태이며,
# 양봉, 음봉 상관없이, 전전전봉부터 현재봉까지 최저가가 꾸준히 상승하고
# 또한, macd도 전봉을 기준으로 감소에서 증가로 돌아설때가 현재 매수타이밍이 된다.
#######################################################################################

 

이 로직이 적용된 결과 두건 체크해 보겠습니다.

퀀텀과 트론이 자동 매수되어 자동 매도 되었습니다.

퀀텀은 12:40분에 22,810원에 매수했다가 14:39분에 23,250원에 매도 되었네요.

약 4%이상? 수익률이 되겠네요? 정확한 %계산이 중요한게 아닙니다. 1%이상 수익만 나면 됩니다.

1%도 자동으로 늘었다가 줄었다가 하긴 합니다만, 최소는 1%이상 남도록 로직이 되어 있습니다.

 

트론은 14:35분에 213.1원에 매수했다가 217.2원에 매도 되었습니다. 이건 2.5%이상? 수익률 같습니다.

 

그럼, 이제, 매수매도 타이밍이 적절했는지 확인해 보겠습니다.

먼저, 퀀텀을 봐 볼게요.

 

12:39분에 자동매매프로그램에서 Slack에 남겨놓은 메세지 로그입니다.

일봉기준이 매수추천이 된 후,

곧바로 5분봉에 의해서 다시 매수추추천되어 바로 매수주문이 들어간 Slack 메세지 내용입니다.

 

로직No(M8)이라는 부분이

5분봉기준 로직 M8번 로직에 의해 매수타이밍을 잡았음을 알려줍니다.

그 당시의 실제 bithumb 5분봉차트를 확인해 보겠습니다.

Slack Message에 url 링크를 누르면 바로 해당 차트로 갑니다.

 

12:39분 매수는 12:35분봉을 현재봉으로 해서 판단하여 매수했으므로, 35분봉을 보셔야 합니다.

자동매매 프로그램은 25분봉, 30분봉, 35분봉 3개봉을 기준으로 39분에 판단했습니다.

매 5분봉의 최저가의 추이과 거래량추이, MACD OSC값의 추이, 그리고, 

macd선과 macd_signal선의 기울기 각도를 보고 판단했습니다.

어쩌면 저만의 비법인데.. 이렇게 밝혀도 되는지 모르겠습니다. ㅎㅎ

 

매수와 매도 타이밍으로 자동 거래된 부분을 표시하면 다음과 같습니다.

 

자동매매 프로그램에서 표시된 매매이력을 확인해 보겠습니다.

매수타이밍에는 "B"라고 표시되어 있고,  매도된 시간에는 "S"로 표시되어 있습니다.

트론도 이와 비슷하게 거래된 것 같습니다.

 

항상 이렇게만 거래된다면, 더이상 바랄게 없겠습니다만, 

문제는, 매수하여 매도타이밍 대기중인 코인이 계속해서 급락할때가 문제 같습니다.

매도 타이밍을 못잡거나, 매수하자마자 바로 급락으로 떨어지게되면, 매도타이밍을 잡을 수가 없겠죠...

 

급락장을 피하는 가장 좋은 방법은 최적의 매수타이밍까지 기다렸다가

타이밍이 오면 바로 매수하여 최소의 매도 수익을 달성하고 파는거라고 봅니다.

이것도 신의 영역이긴합니다만, 최대한 신의 영역에 다가가기 위해 노력하는거죠.. 

 

욕심내지 않고, 적절한 수익을 내고, 바로 빠지기~ 이게 최고입니다.

 

이 글이 자동매매를 이해하시는데 도움되길 바랍니다.

반응형

댓글()