<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>Creative Code</title>
    <link>https://harucode.tistory.com/</link>
    <description>Creative Coding</description>
    <language>ko</language>
    <pubDate>Tue, 14 Jul 2026 02:15:25 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>빛하루</managingEditor>
    <image>
      <title>Creative Code</title>
      <url>https://tistory1.daumcdn.net/tistory/5049112/attach/19b456fd90c6487797272fb0af53f1c2</url>
      <link>https://harucode.tistory.com</link>
    </image>
    <item>
      <title>자동 매매 프로그램 주의사항(필독)</title>
      <link>https://harucode.tistory.com/869</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;설명에 앞서 본 블로그에 올라온 모든 자동 매매 코드는 수익률이 보장된 코드가 아닙니다&lt;/span&gt;.&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;아래 설명은 이 블로그에 업로드 된 ver3.0 의 코드를 기준으로 작성되었습니다.&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1760357421559&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import requests
import pandas as pd
import talib as ts
import numpy as np
import json
import uuid
import jwt
import hashlib
import time
from urllib.parse import urlencode, unquote
from datetime import datetime
import ta

UPBIT_API_URL = &quot;https://api.upbit.com/v1&quot;
server_url = 'https://api.upbit.com'&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;이 부분은 업비트의 자동매매 api를 이용하고 여러 수치를 계산하기 위해 필요한 모듈과 변수들입니다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1760357541979&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;def get_coin_data(coin, unit):
    url = f&quot;https://api.upbit.com/v1/candles/minutes/{unit}&quot;
    params = {'market': f'KRW-{coin}', 'count': 200}
    headers = {&quot;accept&quot;: &quot;application/json&quot;}
    response = requests.get(url, params=params, headers=headers)

    if response.status_code != 200:
        raise Exception(f&quot;API 요청 실패: {response.status_code}&quot;)

    data = response.json()
    df = pd.DataFrame(data)
    df = df.reindex(index=df.index[::-1]).reset_index(drop=True)
    return df&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 get_coin_data 함수는 unit 을 1로 둘 때&amp;nbsp; 해당 코인의 &lt;b&gt;1분봉차트 200개를 가져오는 함수&lt;/b&gt; 입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;unit을 5로 두면 5분봉 차트 200개를 가져올 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;url = f&quot;https://api.upbit.com/v1/candles/seconds 로 두면&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코인의 1초 단위 캔들데이터를 불러올 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1760357718356&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;def load_api_keys(config_file=&quot;upbit_config.json&quot;):
    try:
        with open(config_file, &quot;r&quot;) as file:
            keys = json.load(file)
            return keys[&quot;api_key&quot;], keys[&quot;secret_key&quot;]
    except Exception as e:
        print(f&quot;Error loading API keys: {e}&quot;)
        return None, None&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;업비트에서 발급받은 api 키와 secret 키를 자동매매 코드에 그대로 삽입해서 공유할 경우 유출될 우려가 있기 때문에 따로 upbit_config.json 이라는 파일을 만들어 api키와 secret키를 json 형태의 파일로 저장을 하셔야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 &lt;b&gt;load_api_keys 함수는 upbit_config.json이라는 파일에 저장된 api키와 secret 키를 불러오는 함수&lt;/b&gt;입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1760357909697&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;def get_coin_holdings(api_key, secret_key):
    &quot;&quot;&quot;
    업비트 API를 사용하여 보유 중인 코인의 종류와 수량을 가져옵니다.

    Returns:
        dict: 코인의 종류와 보유 수량을 포함한 딕셔너리
    &quot;&quot;&quot;
    base_url = &quot;https://api.upbit.com/v1/accounts&quot;

    # JWT 토큰 생성
    payload = {
        'access_key': api_key,
        'nonce': str(uuid.uuid4()),
    }
    jwt_token = jwt.encode(payload, secret_key, algorithm=&quot;HS256&quot;)
    headers = {&quot;Authorization&quot;: f&quot;Bearer {jwt_token}&quot;}

    try:
        # 계좌 정보 요청
        response = requests.get(base_url, headers=headers)
        response.raise_for_status()
        accounts = response.json()

        # 보유 코인 정보 필터링
        holdings = {account['currency']: float(account['balance']) for account in accounts if float(account['balance']) &amp;gt; 0}

        # 보유 코인이 없을 경우 처리
        if not holdings:
            print(&quot;보유 중인 코인이 없습니다.&quot;)
            return {}

        return holdings

    except Exception as e:
        print(f&quot;Error fetching coin holdings: {e}&quot;)
        return {}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 &lt;b&gt;get_coin_holdings 함수는 내 계좌에 보유중인 코인과 수량을 가져오는 함수&lt;/b&gt; 입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주의할 점은 이 자동매매 코드를 만들 때 &lt;b&gt;여러개의 코인을 동시에 사고 파는게 아닌 1개의 종목만 사고 파는 형태&lt;/b&gt;이기 때문에 여러 코인을 보유하고 있을 경우 이 함수들이 제대로 작동하지 않을 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1760358050322&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;def check_unfilled_orders(api_key, secret_key):
    &quot;&quot;&quot;
    미체결 주문이 있는지 확인합니다.

    Returns:
        list: 미체결 주문 리스트 (없으면 빈 리스트)
    &quot;&quot;&quot;
    base_url = &quot;https://api.upbit.com/v1/orders&quot;
    query = {
        'state': 'wait',  # 'wait' 상태는 미체결 주문을 의미
    }

    # Query Hash 계산
    query_string = '&amp;amp;'.join([f&quot;{key}={value}&quot; for key, value in query.items()])
    query_hash = hashlib.sha512(query_string.encode()).hexdigest()

    # JWT 토큰 생성
    payload = {
        'access_key': api_key,
        'nonce': str(uuid.uuid4()),
        'query_hash': query_hash,
        'query_hash_alg': 'SHA512',
    }
    jwt_token = jwt.encode(payload, secret_key, algorithm=&quot;HS256&quot;)
    headers = {&quot;Authorization&quot;: f&quot;Bearer {jwt_token}&quot;}

    try:
        # 미체결 주문 요청
        response = requests.get(base_url, headers=headers, params=query)
        response.raise_for_status()
        orders = response.json()

        # 미체결 주문이 없는 경우 처리
        if not orders:
            print(&quot;미체결 주문이 없습니다.&quot;)
            return []

        print(&quot;미체결 주문이 있습니다.&quot;)
        return orders

    except Exception as e:
        print(f&quot;Error fetching unfilled orders: {e}&quot;)
        return []&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 &lt;b&gt;check_unfilled_orders 함수는 현재 미체결된 주문 내역이 있는지 체크하는 함수&lt;/b&gt; 입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1760358323394&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;def cancel_unfilled_order(api_key, secret_key, order_id):
    &quot;&quot;&quot;
    특정 미체결 주문을 취소합니다.

    Args:
        api_key (str): 업비트 API 키
        secret_key (str): 업비트 Secret 키
        order_id (str): 취소할 주문의 UUID

    Returns:
        dict: 주문 취소 결과
    &quot;&quot;&quot;
    base_url = &quot;https://api.upbit.com/v1/order&quot;
    query = {
        'uuid': order_id,
    }

    # JWT 토큰 생성
    query_string = '&amp;amp;'.join([f&quot;{key}={value}&quot; for key, value in query.items()])
    payload = {
        'access_key': api_key,
        'nonce': str(uuid.uuid4()),
        'query_hash': hashlib.sha512(query_string.encode()).hexdigest(),
        'query_hash_alg': 'SHA512',
    }
    jwt_token = jwt.encode(payload, secret_key, algorithm=&quot;HS256&quot;)
    headers = {&quot;Authorization&quot;: f&quot;Bearer {jwt_token}&quot;}

    try:
        # 주문 취소 요청
        response = requests.delete(base_url, headers=headers, params=query)
        response.raise_for_status()
        cancel_result = response.json()

        print(f&quot;주문이 성공적으로 취소되었습니다: {cancel_result}&quot;)
        return cancel_result

    except requests.exceptions.HTTPError as e:
        print(f&quot;HTTP 에러 발생: {e.response.status_code}, {e.response.text}&quot;)
        return {}
    except Exception as e:
        print(f&quot;Error canceling order: {e}&quot;)
        return {}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;cancel_unfilled_order함수는 만약 미체결된 주문이 있다면 해당 주문건을 취소하는 함수&lt;/b&gt; 입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지정가 매수/매도를 요청했는데 해당 주문이 이뤄지지 않았을때 주로 실행하는 함수입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1760358621959&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#매수 1호가와 매도1호가 리턴하는 함수    
def get_orderbook(api_key, market):
    &quot;&quot;&quot;
    주어진 마켓의 매수, 매도 1호가를 가져옵니다.

    Args:
        api_key (str): 업비트 API 키
        market (str): 마켓 코드 (예: &quot;KRW-BTC&quot;)

    Returns:
        dict: 매수 1호가와 매도 1호가를 포함한 딕셔너리
    &quot;&quot;&quot;
    url = f&quot;https://api.upbit.com/v1/orderbook?markets={market}&amp;amp;level=0&quot;

    headers = {&quot;accept&quot;: &quot;application/json&quot;}

    try:
        # 주문서 정보 요청
        response = requests.get(url, headers=headers)
        response.raise_for_status()
        orderbook = response.json()

        if not orderbook:
            print(f&quot;{market}의 주문서 정보가 없습니다.&quot;)
            return None

        # 매수 1호가 (가장 높은 매도 호가)
        bid_price = orderbook[0]['orderbook_units'][0]['bid_price']
        # 매도 1호가 (가장 낮은 매수 호가)
        ask_price = orderbook[0]['orderbook_units'][0]['ask_price']

        return {&quot;bid_price&quot;: bid_price, &quot;ask_price&quot;: ask_price}

    except Exception as e:
        print(f&quot;Error fetching orderbook: {e}&quot;)
        return None
    
api_key, secret_key = load_api_keys(&quot;upbit_config.json&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;이 함수는 해당 코인의 현재 매수1호가와 매도 1호가의 가격을 가지고 오는 함수&lt;/b&gt; 입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1760358732973&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#지정가 매수함수    
def place_limit_buy_order(api_key, secret_key, market, price, budget):
    max_quantity = str(budget / price)
    params = {
        'market': market,
        'side': 'bid',
        'ord_type': 'limit',
        'price': price,
        'volume': max_quantity
    }
    query_string = unquote(urlencode(params, doseq=True)).encode(&quot;utf-8&quot;)

    m = hashlib.sha512()
    m.update(query_string)
    query_hash = m.hexdigest()

    payload = {
        'access_key': api_key,
        'nonce': str(uuid.uuid4()),
        'query_hash': query_hash,
        'query_hash_alg': 'SHA512',
    }

    jwt_token = jwt.encode(payload, secret_key)
    authorization = 'Bearer {}'.format(jwt_token)
    headers = {
    'Authorization': authorization,
    }

    res = requests.post(server_url + '/v1/orders', json=params, headers=headers)
    order_result = res.json()
    # 성공적으로 주문을 제출했다면 주문 UUID 반환
    order_uuid = order_result.get('uuid')
    if order_uuid:
        print(f&quot;매수 주문이 성공적으로 생성되었습니다. 주문 UUID: {order_uuid}&quot;)
        time.sleep(10)
        return order_uuid
    else:
        print(&quot;주문 생성 실패: UUID를 찾을 수 없습니다.&quot;)
        return None&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 &lt;b&gt;place_limit_buy_order 함수는 지정가 매수 주문을 하는 함수&lt;/b&gt;입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;price 변수에는 해당 코인을 구매할 가격이 들어가는 부분이고&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;budget 변수는 해당 코인을 구매하기 위해 본인이 투입할 돈이 원 단위로 들어가는 부분입니다.(50000입력시 5만원)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;매수 주문이 간혹 들어가지 않을 때가 있을 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1760359064402&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;def place_limit_sell_order(api_key, secret_key, market, price, quantity):
    max_quantity = str(quantity)
    params = {
        'market': market,
        'side': 'ask',
        'ord_type': 'limit',
        'price': price,
        'volume': max_quantity
    }
    query_string = unquote(urlencode(params, doseq=True)).encode(&quot;utf-8&quot;)

    m = hashlib.sha512()
    m.update(query_string)
    query_hash = m.hexdigest()

    payload = {
        'access_key': api_key,
        'nonce': str(uuid.uuid4()),
        'query_hash': query_hash,
        'query_hash_alg': 'SHA512',
    }

    jwt_token = jwt.encode(payload, secret_key)
    authorization = 'Bearer {}'.format(jwt_token)
    headers = {
    'Authorization': authorization,
    }

    res = requests.post(server_url + '/v1/orders', json=params, headers=headers)
    order_result = res.json()
    # 성공적으로 주문을 제출했다면 주문 UUID 반환
    order_uuid = order_result.get('uuid')
    if order_uuid:
        print(f&quot;매도 주문이 성공적으로 생성되었습니다. 주문 UUID: {order_uuid}&quot;)
        return order_uuid
    else:
        print(&quot;주문 생성 실패: UUID를 찾을 수 없습니다.&quot;)
        return None&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;place_limit_sell_order은 지정가 매도 함수&lt;/b&gt;입니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;price 에는 본인이 가지고 있는 코인을 매도 할 가격이 들어가는 부분이고&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;quantity에는 본인이 가지고 있는 코인의 보유 수량이 들어가는 부분입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지정가 매수함수와 마찬가지로 주문신청이 간헐적으로 이루어지지 않을 때가 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1760359246244&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 시장가 매도 함수
def place_market_sell_order(api_key, secret_key, market, quantity):
    max_quantity = str(quantity)
    params = {
        'market': market,
        'side': 'ask',
        'ord_type': 'market',
        'volume': max_quantity
    }
    query_string = unquote(urlencode(params, doseq=True)).encode(&quot;utf-8&quot;)

    m = hashlib.sha512()
    m.update(query_string)
    query_hash = m.hexdigest()

    payload = {
        'access_key': api_key,
        'nonce': str(uuid.uuid4()),
        'query_hash': query_hash,
        'query_hash_alg': 'SHA512',
    }

    jwt_token = jwt.encode(payload, secret_key)
    authorization = f'Bearer {jwt_token}'
    headers = {
        'Authorization': authorization,
    }

    res = requests.post(server_url + '/v1/orders', json=params, headers=headers)
    order_result = res.json()

    order_uuid = order_result.get('uuid')
    if order_uuid:
        print(f&quot;시장가 매도 주문이 성공적으로 생성되었습니다. 주문 UUID: {order_uuid}&quot;)
        return order_uuid
    else:
        print(&quot;시장가 주문 생성 실패: UUID를 찾을 수 없습니다.&quot;)
        return None&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;place_market_sell_order 함수는 시장가 매도 함수&lt;/b&gt;입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가지고 있는 코인을 지정가가 아닌 시장가로 바로 매도할 때 쓰는 함수입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주로 코인의 가격이 급락하는 상황에서 지정가 매도가 이뤄지지 않는 경우에 사용하는 함수입니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1760359427091&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#받아온 코인 데이터를 저장할 csv파일을 만드는 함수
def save_to_csv(df):
    filename = 'coin_data.csv'
    df.to_csv(filename, index=False)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;save_to_csv 함수는 받아온 코인의 캔들 데이터를 csv 파일 형태로 저장하는 함수&lt;/b&gt; 입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1760359477923&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;columns = ['시간', '코인명', '매수/매도', '매매 단가']
df_trade = pd.DataFrame(columns=columns)
df_trade.to_csv('trade_list.csv', index=False, encoding='utf-8-sig')

#거래기록을 추가하는 함수
def add_trade(trade_time, coin_name, trade_type, avg_price):
    # CSV 파일 읽기
    df = pd.read_csv('trade_list.csv', encoding='utf-8-sig')
    
    # 새로운 데이터 추가
    new_data = {
        '시간': trade_time,
        '코인명': coin_name,
        '매수/매도': trade_type,
        '평균 단가': avg_price,
    }
    df = df.append(new_data, ignore_index=True)
    
    # 업데이트된 데이터프레임을 CSV 파일로 저장
    df.to_csv('trade_list.csv', index=False, encoding='utf-8-sig')&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;이 부분은 거래가 이루어질 시 거래 내용을 trade_list.csv 라는 파일로 저장하는 함수&lt;/b&gt; 입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주의 할 점은 코드가 계속 실행되고 있을 때만 작동하며 새로 코드를 다시 실행 시 파일 명을 바꾸지 않는 한 기존에 저장된 파일은 사라집니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1760359614272&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#현재 보유중인 원화 잔고를 가져오는 함수
def get_balance():
    &quot;&quot;&quot;
    업비트 API를 사용하여 원화 잔고를 조회하는 함수
    &quot;&quot;&quot;
    url = server_url + '/v1/accounts'
    
    # 요청 헤더 준비
    payload = {
        'access_key': api_key,
        'nonce': str(uuid.uuid4())
    }
    
    # 서명 생성
    m = hashlib.sha512()
    m.update(payload['nonce'].encode('utf-8'))
    signature = jwt.encode(payload, secret_key, algorithm='HS256')
    
    headers = {
        'Authorization': f'Bearer {signature}'
    }
    
    # API 요청
    response = requests.get(url, headers=headers)
    
    if response.status_code == 200:
        accounts = response.json()
        for account in accounts:
            if account['currency'] == 'KRW':  # 원화 잔고 찾기
                return float(account['balance'])
    else:
        print(f&quot;Error: {response.status_code}&quot;)
        return None&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;이 get_balance 함수는 내 업비트 계좌에 현재 보유중인 원화 잔고만 가져오는 함수&lt;/b&gt;입니다.(보유중인 코인 및 수량 x)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1760359680761&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;def get_current_price(coin):
    url = f&quot;https://api.upbit.com/v1/ticker&quot;
    params = {
        'markets': f'KRW-{coin}'  # 코인 이름 입력 (예: 'BTC' -&amp;gt; 'KRW-BTC')
    }
    
    response = requests.get(url, params=params)
    data = response.json()
    
    if len(data) &amp;gt; 0:
        return data[0]['trade_price']  # 현재 거래 가격 반환
    else:
        return None&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;get_current_price 함수는 현재 코인의 가격을 가지고 오는 함수&lt;/b&gt; 입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1760359748837&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;def get_coin_order_unit(coin,price):
    special_coin_list = ['ADA','ALGO','BLUR','CELO', 'ELF', 'EOS', 'GRS', 'GRT', 'ICX', 'MANA', 'MINA', 'POL', 'SAND', 'SEI', 'STG', 'TRX']
    coin_order_unit = 0
    if coin not in special_coin_list:
        if price &amp;gt;= 2000000:
            coin_order_unit = 1000
        elif price &amp;gt;=1000000 and price&amp;lt;2000000:
            coin_order_unit = 500
        elif price &amp;gt;=500000 and price &amp;lt;1000000:
            coin_order_unit = 100
        elif price &amp;gt;=100000 and price&amp;lt;500000:
            coin_order_unit = 50
        elif price &amp;gt;=10000 and price&amp;lt;100000:
            coin_order_unit = 10
        elif price &amp;gt;=1000 and price&amp;lt;10000:
            coin_order_unit = 1
        elif price &amp;gt;=100 and price&amp;lt;1000:
            coin_order_unit = 0.1
        elif price &amp;gt;=10 and price&amp;lt;100:
            coin_order_unit = 0.01
        elif price &amp;gt;= 1 and price&amp;lt;10:
            coin_order_unit = 0.001
        elif price&amp;gt;=0.1 and price&amp;lt;1:
            coin_order_unit = 0.0001
        elif price&amp;gt;=0.01 and price&amp;lt;0.1:
            coin_order_unit = 0.00001
        elif price&amp;gt;=0.001 and price&amp;lt;0.01:
            coin_order_unit = 0.000001
        elif price&amp;gt;=0.0001 and price&amp;lt;0.001 :
            coin_order_unit = 0.0000001
        else :
            coin_order_unit = 0.00000001
    else:
        coin_order_unit = 1
    return coin_order_unit&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 함수는 &lt;b&gt;코인의 호가단위를 불러오는 함수&lt;/b&gt;입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;special 목록에 저장된 코인들은 다른 코인들의 호가 규칙과 달라 따로 지정한 코인들입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 기준 이 목록은 변경사항이 있을 수 있으니 업비트의 공지사항에서 추가 확인이 필요합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1760359982510&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;def calculate_indicator(df):
    
    df['CCI'] = ts.CCI(high=df['high_price'], low=df['low_price'], close=df['trade_price'], timeperiod=14)
    df['CCI_Signal_EMA'] = df['CCI'].ewm(span=9).mean()
    cci = df['CCI'].iloc[-1]
    cci_signal = df['CCI_Signal_EMA'].iloc[-1]

    # ADX and DI+
    adx_indicator = ta.trend.ADXIndicator(df[&quot;high_price&quot;], df[&quot;low_price&quot;], df[&quot;trade_price&quot;], window=14)
    df['DIm'] = adx_indicator.adx_neg()
    df['DIp'] = adx_indicator.adx_pos()
    df['Adx'] = adx_indicator.adx()
    dip = df['DIp'].iloc[-1]
    dim = df['DIm'].iloc[-1]
    adx = df['Adx'].iloc[-1]

    return df&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 함수는 &lt;b&gt;get_coin_data함수를 통해 가져온 코인의 데이터를 가지고 지표를 계산하는 함수&lt;/b&gt;입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CCI 와 CCI 신호선, adx 지표와 DI+, DI-지표를 계산하는 함수이며 다른지표가 필요할 시 추가로 계산하는 코드를 써넣을 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1760360135005&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;def detect_signal(df):
    df = calculate_indicator(df)
    if len(df) &amp;lt; 2:
        return 'none'
    
    current = df.iloc[-1]
    prev = df.iloc[-2]
    
    # 현재 및 이전 값 추출
    adx, dip, dim = current['Adx'], current['DIp'], current['DIm']
    prev_adx, prev_dip, prev_dim = prev['Adx'], prev['DIp'], prev['DIm']
    
    # 순위 계산 함수
    def get_rank(a, b, c):
        sorted_values = sorted([(a, 'ADX'), (b, '+DI'), (c, '-DI')], key=lambda x: -x[0])
        return [item[1] for item in sorted_values]
    
    current_rank = get_rank(adx, dip, dim)
    prev_rank = get_rank(prev_adx, prev_dip, prev_dim)
    
    # ADX 강도 (20 기준)
    adx_strong = adx &amp;gt; 20  # 기준값 20으로 변경
    adx_weak = adx &amp;lt; 20
    
    # 모든 가능한 돌파 쌍 검사
    signals = []
    
    # 1. ADX와 +DI 돌파 검사
    if (adx &amp;gt; dip and prev_adx &amp;lt;= prev_dip) or (adx &amp;lt; dip and prev_adx &amp;gt;= prev_dip):
        adx_dir = 'up' if adx &amp;gt; prev_adx else 'down'
        dip_dir = 'up' if dip &amp;gt; prev_dip else 'down'
        
        if adx &amp;lt; dip and adx_dir == 'up' and dip_dir == 'up' and adx_strong:
            signals.append('buy')
        elif adx &amp;gt; dip and adx_dir == 'down' and dip_dir == 'down' and adx_strong:
            signals.append('sell')
        elif adx &amp;gt; dip and adx_dir == 'up' and dip_dir == 'down' and adx_strong:
            signals.append('sell')
    
    # 2. ADX와 -DI 돌파 검사
    if (adx &amp;gt; dim and prev_adx &amp;lt;= prev_dim) or (adx &amp;lt; dim and prev_adx &amp;gt;= prev_dim):
        adx_dir = 'up' if adx &amp;gt; prev_adx else 'down'
        dim_dir = 'up' if dim &amp;gt; prev_dim else 'down'
        
        if adx &amp;gt; dim and adx_dir == 'up' and dim_dir == 'down' and adx_strong:
            signals.append('buy')
        elif adx &amp;lt; dim and adx_dir == 'down' and dim_dir == 'up' and adx_strong:
            signals.append('sell')
    
    # 3. +DI와 -DI 돌파 검사
    if (dip &amp;gt; dim and prev_dip &amp;lt;= prev_dim) or (dip &amp;lt; dim and prev_dip &amp;gt;= prev_dim):
        dip_dir = 'up' if dip &amp;gt; prev_dip else 'down'
        dim_dir = 'up' if dim &amp;gt; prev_dim else 'down'
        
        if dip &amp;gt; dim and dip_dir == 'up' and dim_dir == 'down' and adx &amp;gt; prev_adx:
            signals.append('buy')
        elif dip &amp;lt; dim and dip_dir == 'down' and dim_dir == 'up' and adx &amp;gt; prev_adx:
            signals.append('sell')
    
    # 4. 3개 지표 동시 돌파
    if current_rank != prev_rank:
        adx_dir = 'up' if adx &amp;gt; prev_adx else 'down'
        dip_dir = 'up' if dip &amp;gt; prev_dip else 'down'
        dim_dir = 'up' if dim &amp;gt; prev_dim else 'down'
        
        if adx_dir == 'up' and dip_dir == 'up' and dim_dir == 'up':
            if current_rank[0] in ['+DI', 'ADX'] and adx_strong:
                signals.append('buy')
        elif adx_dir == 'down' and dip_dir == 'down' and dim_dir == 'down':
            if current_rank[0] in ['-DI', 'ADX'] and adx_strong:
                signals.append('sell')
    
    # 최종 신호 결정 (CCI 조건: 매수만 적용)
    if 'sell' in signals and 'buy' in signals:
        return 'none'
    elif 'sell' in signals:
        return 'sell'
    elif 'buy' in signals and prev['CCI'] &amp;lt; current['CCI']:  # CCI 상승 조건
        return 'buy'
    else:
        return 'none'&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;이 함수는 실질적으로 매수를 할지 매도를 할지 결정하는 함수&lt;/b&gt;입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때까지 업로드 된 코드 중 ver 3.0의 코드에서 쓰인 함수로 CCI와 ADX 선을 기준으로 매매를 결정하는 함수입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;다만 수익률을 보장하지 않는 코드이기에 현재로서는 의미가 없는 코드&lt;/b&gt;&lt;/span&gt;입니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;사용자분의 의도에 맞게 코드를 수정하신 후에 사용&lt;/span&gt;&lt;/b&gt;하시는 걸 추천드립니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1760360391375&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;def get_top_gainers_upbit(status, number):
    
    base_url = &quot;https://api.upbit.com/v1&quot;
    try:
        # Step 1: Get market data
        markets_response = requests.get(f&quot;{base_url}/market/all&quot;)
        markets_response.raise_for_status()
        markets = markets_response.json()

        # Filter for KRW markets only and extract symbols
        krw_symbols = [market['market'].split('-')[1] for market in markets if market['market'].startswith('KRW-')]

        # Step 2: Get ticker data for all KRW markets
        krw_markets = [f&quot;KRW-{symbol}&quot; for symbol in krw_symbols]
        tickers_response = requests.get(f&quot;{base_url}/ticker&quot;, params={&quot;markets&quot;: &quot;,&quot;.join(krw_markets)})
        tickers_response.raise_for_status()
        tickers = tickers_response.json()

        # Step 3: Calculate price change percentages and filter
        gainers = []
        except_coin = ['USDC','USDT','BTC','ETH','BCH','AAVE','SOL','BSV','AVAX','EGLD','STX','BTT','XEM']
        
        for ticker in tickers:
            market = ticker['market']
            symbol = market.split('-')[1]
            prev_trade_price = ticker['prev_closing_price']
            current_price = ticker['trade_price']

            if prev_trade_price and prev_trade_price &amp;gt; 0 and symbol not in except_coin:
                change_percent = ((current_price - prev_trade_price) / prev_trade_price) * 100

                if change_percent &amp;gt;= 7:  # ✅ 7% 이상 상승한 종목만 필터링
                    gainers.append({
                        'symbol': symbol,
                        'change_percent': change_percent,
                        'current_price': current_price
                    })

        # Step 4: Sort by price change percentage
        gainers = sorted(gainers, key=lambda x: x['change_percent'], reverse=(status == 'up'))

        # Step 5: Return top N gainers
        return [gainer['symbol'] for gainer in gainers[:number]]

    except requests.exceptions.RequestException as e:
        print(f&quot;Error while fetching data from Upbit API: {e}&quot;)
        return []&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 함수는 &lt;b&gt;업비트의 여러 코인들 중에 상승률 또는 하락률이 7% 이상인 코인 상위 number 개를 불러오는 함수&lt;/b&gt;입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;status에 up이 들어가게 되면 상승률 7%이상인 코인, down이 들어가게 되면 하락률 7%이상인 코인들을 불러오는 함수입니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1760360548183&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;def main():
    # input_str = input(&quot;예측할 코인 심볼을 쉼표(,)로 구분해서 입력하세요 (예: BTC,ETH,XRP): &quot;)
    # coin_list = [symbol.strip().upper() for symbol in input_str.split(&quot;,&quot;)]
    coin_list = []
    coin_holdings = get_coin_holdings(api_key, secret_key)
    money = int(get_balance() * 0.95)
    coin_list_copy = coin_list.copy()
    coin_list_copy.extend(get_top_gainers_upbit('up',2))

    while True:
        money = int(get_balance() * 0.95)
        coin_list_copy = coin_list.copy()
        coin_list_copy.extend(get_top_gainers_upbit('up',4))
        coin_holdings = get_coin_holdings(api_key, secret_key)
        if len(coin_holdings)==1:
            for coin in coin_list_copy:
                print('')
                print('------------------------------------------------------')
                print(f'&amp;lt;{coin}&amp;gt;')
                market = f&quot;KRW-{coin}&quot;
                status_coin_price = get_current_price(coin)
                coin_order_unit = get_coin_order_unit(coin,status_coin_price)

                try:
                    df_1min = get_coin_data(coin, 1)
                    decision = detect_signal(df_1min)
                    now = datetime.now()
                    print(f'{now.strftime(&quot;%Y-%m-%d %H:%M:%S&quot;)} {coin}의 현재 가격 및 상황 : {status_coin_price}원  - {decision}')
                    if decision == 'buy':# and signal_btc == '매수 신호':
                    
                        orderbook = get_orderbook(api_key, market)
                        buy_price = orderbook['bid_price'] + coin_order_unit
                        buy_uuid = place_limit_buy_order(api_key,secret_key,market,buy_price,money)
                        unfilled_orders = check_unfilled_orders(api_key, secret_key)
                        if unfilled_orders:
                            print('지정가 매수 실패, 미체결 주문 취소 작업 중..')
                            for order in unfilled_orders:
                                cancel_result = cancel_unfilled_order(api_key, secret_key, buy_uuid)
                        else:
                            current_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
                            add_trade(current_time, f'{coin}', 'buy', buy_price)
                            break
                    if len(get_coin_holdings(api_key,secret_key))&amp;gt;1:
                        break
                except Exception as e:
                    print(f&quot;⚠️ [{coin}] 매매 판단 실패: {e}\n&quot;)
        else:
            hold_coin = list(coin_holdings.keys())[1]
            hold_coin_amount = float(coin_holdings[hold_coin])
            status_coin_price = get_current_price(hold_coin)
            coin_order_unit = get_coin_order_unit(hold_coin,status_coin_price)
            market = f'KRW-{hold_coin}'

            print('----------------------------------------------')
            
            try:
                df_1min = get_coin_data(hold_coin, 1)
                decision = detect_signal(df_1min)
                now = datetime.now()
                print(f'{now.strftime(&quot;%Y-%m-%d %H:%M:%S&quot;)} {coin}의 현재 가격 및 상황 : {status_coin_price}원  - {decision}')
                if decision == 'sell':
                    orderbook = get_orderbook(api_key, market)
                    market = f'KRW-{hold_coin}'
                    sell_price = orderbook['ask_price'] - coin_order_unit
                    sell_uuid = place_limit_sell_order(api_key,secret_key,market,sell_price,hold_coin_amount)
                    unfilled_orders = check_unfilled_orders(api_key, secret_key)
                    if unfilled_orders:
                        print('지정가 매도 실패, 미체결 주문 취소 작업 중..')
                        for order in unfilled_orders:
                            cancel_result = cancel_unfilled_order(api_key, secret_key, sell_uuid)
                        sell_uuid = place_market_sell_order(api_key,secret_key,market,hold_coin_amount)
                        
                    else:
                        current_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
                        add_trade(current_time, f'{hold_coin}', 'sell', sell_price)
                        break
            except Exception as e:
                print(f&quot;⚠️ [{hold_coin}] 매매 판단 실패: {e}\n&quot;)


if __name__ == &quot;__main__&quot;:
    main()&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 &lt;b&gt;main 함수는 실제로 이 코드들이 모두 작동되게 하는 함수&lt;/b&gt; 입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드를 실행 해 자동매매로 사고 팔 때&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1760360613356&quot; class=&quot;isbl&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;money = int(get_balance() * 0.95)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이 부분에서 계속해서 가지고 있는 원화 잔고의 95%를 투입하게 됩니다. 예를들어 원화잔고가 10만원이 있다면 자동매매에 투입되는 금액은 95% 즉 95000원입니다. 이후 수익이나서 103000원이 되었다고 가정하면 그 다음 투입되는 금액은 103000원의 95%가 계속해서 투입이 되는 구조입니다.&amp;nbsp; 비율을 크게 가져갈 수록 수익을 크게 가져갈 수 도 있지만 손해가 크게 날 수도 있습니다. &amp;nbsp;이후 작성된 코드들은 보유중인 코인이 없을 땐 현재 업비트의 상승률 상위 종목들의 캔들데이터를 받아와 지표를 계산하고 설정한 매수 조건에 맞으면 해당 코인을 매수하고 해당 코인을 매수한 다음에는 그 코인의 캔들데이터만 일정한 주기로 계속 받아와서 매도 조건에 맞으면 그 때 판매하는 코드 입니다. 판매에 성공하면 이 후에는 다시 상승률 상위 종목들의 캔들데이터를 차례대로 받아오면서 계산하는 반복되는 형태의 코드입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;맨 위에서 설명드린 바와 같이 &lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;이 블로그에 올라온 모든 자동매매 코드는 수익률을 보장하는 코드가 아니며 단지 테스트 용으로 작성한 코드입니다. 사용시 주의 바랍니다&lt;/b&gt;&lt;/span&gt;.&lt;/p&gt;</description>
      <category>혼자 만든 Code/upbit 코인 자동거래</category>
      <category>비트코인</category>
      <category>업비트</category>
      <category>자동매매</category>
      <category>자동매매 프로그램</category>
      <category>코인</category>
      <category>코인매매</category>
      <category>코인자동매매</category>
      <author>빛하루</author>
      <guid isPermaLink="true">https://harucode.tistory.com/869</guid>
      <comments>https://harucode.tistory.com/869#entry869comment</comments>
      <pubDate>Mon, 13 Oct 2025 22:16:54 +0900</pubDate>
    </item>
    <item>
      <title>업비트 코인 자동거래 ver 3.0</title>
      <link>https://harucode.tistory.com/868</link>
      <description>&lt;pre id=&quot;code_1746627316383&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import requests
import pandas as pd
import talib as ts
import numpy as np
import json
import uuid
import jwt
import hashlib
import time
from urllib.parse import urlencode, unquote
from datetime import datetime
import ta

UPBIT_API_URL = &quot;https://api.upbit.com/v1&quot;
server_url = 'https://api.upbit.com'

def get_coin_data(coin, unit):
    url = f&quot;https://api.upbit.com/v1/candles/minutes/{unit}&quot;
    params = {'market': f'KRW-{coin}', 'count': 200}
    headers = {&quot;accept&quot;: &quot;application/json&quot;}
    response = requests.get(url, params=params, headers=headers)

    if response.status_code != 200:
        raise Exception(f&quot;API 요청 실패: {response.status_code}&quot;)

    data = response.json()
    df = pd.DataFrame(data)
    df = df.reindex(index=df.index[::-1]).reset_index(drop=True)
    return df

# JSON 파일에서 API 키 불러오기
def load_api_keys(config_file=&quot;upbit_config.json&quot;):
    try:
        with open(config_file, &quot;r&quot;) as file:
            keys = json.load(file)
            return keys[&quot;api_key&quot;], keys[&quot;secret_key&quot;]
    except Exception as e:
        print(f&quot;Error loading API keys: {e}&quot;)
        return None, None
    
# 보유중인 코인 목록 가지고 오기기
def get_coin_holdings(api_key, secret_key):
    &quot;&quot;&quot;
    업비트 API를 사용하여 보유 중인 코인의 종류와 수량을 가져옵니다.

    Returns:
        dict: 코인의 종류와 보유 수량을 포함한 딕셔너리
    &quot;&quot;&quot;
    base_url = &quot;https://api.upbit.com/v1/accounts&quot;

    # JWT 토큰 생성
    payload = {
        'access_key': api_key,
        'nonce': str(uuid.uuid4()),
    }
    jwt_token = jwt.encode(payload, secret_key, algorithm=&quot;HS256&quot;)
    headers = {&quot;Authorization&quot;: f&quot;Bearer {jwt_token}&quot;}

    try:
        # 계좌 정보 요청
        response = requests.get(base_url, headers=headers)
        response.raise_for_status()
        accounts = response.json()

        # 보유 코인 정보 필터링
        holdings = {account['currency']: float(account['balance']) for account in accounts if float(account['balance']) &amp;gt; 0}

        # 보유 코인이 없을 경우 처리
        if not holdings:
            print(&quot;보유 중인 코인이 없습니다.&quot;)
            return {}

        return holdings

    except Exception as e:
        print(f&quot;Error fetching coin holdings: {e}&quot;)
        return {}
    
#미체결된 주문 조회 함수
def check_unfilled_orders(api_key, secret_key):
    &quot;&quot;&quot;
    미체결 주문이 있는지 확인합니다.

    Returns:
        list: 미체결 주문 리스트 (없으면 빈 리스트)
    &quot;&quot;&quot;
    base_url = &quot;https://api.upbit.com/v1/orders&quot;
    query = {
        'state': 'wait',  # 'wait' 상태는 미체결 주문을 의미
    }

    # Query Hash 계산
    query_string = '&amp;amp;'.join([f&quot;{key}={value}&quot; for key, value in query.items()])
    query_hash = hashlib.sha512(query_string.encode()).hexdigest()

    # JWT 토큰 생성
    payload = {
        'access_key': api_key,
        'nonce': str(uuid.uuid4()),
        'query_hash': query_hash,
        'query_hash_alg': 'SHA512',
    }
    jwt_token = jwt.encode(payload, secret_key, algorithm=&quot;HS256&quot;)
    headers = {&quot;Authorization&quot;: f&quot;Bearer {jwt_token}&quot;}

    try:
        # 미체결 주문 요청
        response = requests.get(base_url, headers=headers, params=query)
        response.raise_for_status()
        orders = response.json()

        # 미체결 주문이 없는 경우 처리
        if not orders:
            print(&quot;미체결 주문이 없습니다.&quot;)
            return []

        print(&quot;미체결 주문이 있습니다.&quot;)
        return orders

    except Exception as e:
        print(f&quot;Error fetching unfilled orders: {e}&quot;)
        return []
    
#미체결된 주문 취소하는 함수
def cancel_unfilled_order(api_key, secret_key, order_id):
    &quot;&quot;&quot;
    특정 미체결 주문을 취소합니다.

    Args:
        api_key (str): 업비트 API 키
        secret_key (str): 업비트 Secret 키
        order_id (str): 취소할 주문의 UUID

    Returns:
        dict: 주문 취소 결과
    &quot;&quot;&quot;
    base_url = &quot;https://api.upbit.com/v1/order&quot;
    query = {
        'uuid': order_id,
    }

    # JWT 토큰 생성
    query_string = '&amp;amp;'.join([f&quot;{key}={value}&quot; for key, value in query.items()])
    payload = {
        'access_key': api_key,
        'nonce': str(uuid.uuid4()),
        'query_hash': hashlib.sha512(query_string.encode()).hexdigest(),
        'query_hash_alg': 'SHA512',
    }
    jwt_token = jwt.encode(payload, secret_key, algorithm=&quot;HS256&quot;)
    headers = {&quot;Authorization&quot;: f&quot;Bearer {jwt_token}&quot;}

    try:
        # 주문 취소 요청
        response = requests.delete(base_url, headers=headers, params=query)
        response.raise_for_status()
        cancel_result = response.json()

        print(f&quot;주문이 성공적으로 취소되었습니다: {cancel_result}&quot;)
        return cancel_result

    except requests.exceptions.HTTPError as e:
        print(f&quot;HTTP 에러 발생: {e.response.status_code}, {e.response.text}&quot;)
        return {}
    except Exception as e:
        print(f&quot;Error canceling order: {e}&quot;)
        return {}

#매수 1호가와 매도1호가 리턴하는 함수    
def get_orderbook(api_key, market):
    &quot;&quot;&quot;
    주어진 마켓의 매수, 매도 1호가를 가져옵니다.

    Args:
        api_key (str): 업비트 API 키
        market (str): 마켓 코드 (예: &quot;KRW-BTC&quot;)

    Returns:
        dict: 매수 1호가와 매도 1호가를 포함한 딕셔너리
    &quot;&quot;&quot;
    url = f&quot;https://api.upbit.com/v1/orderbook?markets={market}&amp;amp;level=0&quot;

    headers = {&quot;accept&quot;: &quot;application/json&quot;}

    try:
        # 주문서 정보 요청
        response = requests.get(url, headers=headers)
        response.raise_for_status()
        orderbook = response.json()

        if not orderbook:
            print(f&quot;{market}의 주문서 정보가 없습니다.&quot;)
            return None

        # 매수 1호가 (가장 높은 매도 호가)
        bid_price = orderbook[0]['orderbook_units'][0]['bid_price']
        # 매도 1호가 (가장 낮은 매수 호가)
        ask_price = orderbook[0]['orderbook_units'][0]['ask_price']

        return {&quot;bid_price&quot;: bid_price, &quot;ask_price&quot;: ask_price}

    except Exception as e:
        print(f&quot;Error fetching orderbook: {e}&quot;)
        return None
    
api_key, secret_key = load_api_keys(&quot;upbit_config.json&quot;)
    
#지정가 매수함수    
def place_limit_buy_order(api_key, secret_key, market, price, budget):
    max_quantity = str(budget / price)
    params = {
        'market': market,
        'side': 'bid',
        'ord_type': 'limit',
        'price': price,
        'volume': max_quantity
    }
    query_string = unquote(urlencode(params, doseq=True)).encode(&quot;utf-8&quot;)

    m = hashlib.sha512()
    m.update(query_string)
    query_hash = m.hexdigest()

    payload = {
        'access_key': api_key,
        'nonce': str(uuid.uuid4()),
        'query_hash': query_hash,
        'query_hash_alg': 'SHA512',
    }

    jwt_token = jwt.encode(payload, secret_key)
    authorization = 'Bearer {}'.format(jwt_token)
    headers = {
    'Authorization': authorization,
    }

    res = requests.post(server_url + '/v1/orders', json=params, headers=headers)
    order_result = res.json()
    # 성공적으로 주문을 제출했다면 주문 UUID 반환
    order_uuid = order_result.get('uuid')
    if order_uuid:
        print(f&quot;매수 주문이 성공적으로 생성되었습니다. 주문 UUID: {order_uuid}&quot;)
        time.sleep(10)
        return order_uuid
    else:
        print(&quot;주문 생성 실패: UUID를 찾을 수 없습니다.&quot;)
        return None
    
# 지정가 매도함수
def place_limit_sell_order(api_key, secret_key, market, price, quantity):
    max_quantity = str(quantity)
    params = {
        'market': market,
        'side': 'ask',
        'ord_type': 'limit',
        'price': price,
        'volume': max_quantity
    }
    query_string = unquote(urlencode(params, doseq=True)).encode(&quot;utf-8&quot;)

    m = hashlib.sha512()
    m.update(query_string)
    query_hash = m.hexdigest()

    payload = {
        'access_key': api_key,
        'nonce': str(uuid.uuid4()),
        'query_hash': query_hash,
        'query_hash_alg': 'SHA512',
    }

    jwt_token = jwt.encode(payload, secret_key)
    authorization = 'Bearer {}'.format(jwt_token)
    headers = {
    'Authorization': authorization,
    }

    res = requests.post(server_url + '/v1/orders', json=params, headers=headers)
    order_result = res.json()
    # 성공적으로 주문을 제출했다면 주문 UUID 반환
    order_uuid = order_result.get('uuid')
    if order_uuid:
        print(f&quot;매도 주문이 성공적으로 생성되었습니다. 주문 UUID: {order_uuid}&quot;)
        time.sleep(10)
        return order_uuid
    else:
        print(&quot;주문 생성 실패: UUID를 찾을 수 없습니다.&quot;)
        return None
    
# 시장가 매도 함수
def place_market_sell_order(api_key, secret_key, market, quantity):
    max_quantity = str(quantity)
    params = {
        'market': market,
        'side': 'ask',
        'ord_type': 'market',
        'volume': max_quantity
    }
    query_string = unquote(urlencode(params, doseq=True)).encode(&quot;utf-8&quot;)

    m = hashlib.sha512()
    m.update(query_string)
    query_hash = m.hexdigest()

    payload = {
        'access_key': api_key,
        'nonce': str(uuid.uuid4()),
        'query_hash': query_hash,
        'query_hash_alg': 'SHA512',
    }

    jwt_token = jwt.encode(payload, secret_key)
    authorization = f'Bearer {jwt_token}'
    headers = {
        'Authorization': authorization,
    }

    res = requests.post(server_url + '/v1/orders', json=params, headers=headers)
    order_result = res.json()

    order_uuid = order_result.get('uuid')
    if order_uuid:
        print(f&quot;시장가 매도 주문이 성공적으로 생성되었습니다. 주문 UUID: {order_uuid}&quot;)
        time.sleep(10)
        return order_uuid
    else:
        print(&quot;시장가 주문 생성 실패: UUID를 찾을 수 없습니다.&quot;)
        return None
    
#받아온 코인 데이터를 저장할 csv파일을 만드는 함수
def save_to_csv(df):
    filename = 'coin_data.csv'
    df.to_csv(filename, index=False)

#거래기록을 저장할 csv 파일일
columns = ['시간', '코인명', '매수/매도', '매매 단가']
df_trade = pd.DataFrame(columns=columns)
df_trade.to_csv('trade_list.csv', index=False, encoding='utf-8-sig')

#거래기록을 추가하는 함수
def add_trade(trade_time, coin_name, trade_type, avg_price):
    # CSV 파일 읽기
    df = pd.read_csv('trade_list.csv', encoding='utf-8-sig')
    
    # 새로운 데이터 추가
    new_data = {
        '시간': trade_time,
        '코인명': coin_name,
        '매수/매도': trade_type,
        '평균 단가': avg_price,
    }
    df = df.append(new_data, ignore_index=True)
    
    # 업데이트된 데이터프레임을 CSV 파일로 저장
    df.to_csv('trade_list.csv', index=False, encoding='utf-8-sig')

#현재 보유중인 원화 잔고를 가져오는 함수
def get_balance():
    &quot;&quot;&quot;
    업비트 API를 사용하여 원화 잔고를 조회하는 함수
    &quot;&quot;&quot;
    url = server_url + '/v1/accounts'
    
    # 요청 헤더 준비
    payload = {
        'access_key': api_key,
        'nonce': str(uuid.uuid4())
    }
    
    # 서명 생성
    m = hashlib.sha512()
    m.update(payload['nonce'].encode('utf-8'))
    signature = jwt.encode(payload, secret_key, algorithm='HS256')
    
    headers = {
        'Authorization': f'Bearer {signature}'
    }
    
    # API 요청
    response = requests.get(url, headers=headers)
    
    if response.status_code == 200:
        accounts = response.json()
        for account in accounts:
            if account['currency'] == 'KRW':  # 원화 잔고 찾기
                return float(account['balance'])
    else:
        print(f&quot;Error: {response.status_code}&quot;)
        return None
    
#코인의 현재가격을 불러오는 함수
def get_current_price(coin):
    url = f&quot;https://api.upbit.com/v1/ticker&quot;
    params = {
        'markets': f'KRW-{coin}'  # 코인 이름 입력 (예: 'BTC' -&amp;gt; 'KRW-BTC')
    }
    
    response = requests.get(url, params=params)
    data = response.json()
    
    if len(data) &amp;gt; 0:
        return data[0]['trade_price']  # 현재 거래 가격 반환
    else:
        return None
    
def get_coin_order_unit(coin,price):
    special_coin_list = ['ADA','ALGO','BLUR','CELO', 'ELF', 'EOS', 'GRS', 'GRT', 'ICX', 'MANA', 'MINA', 'POL', 'SAND', 'SEI', 'STG', 'TRX']
    coin_order_unit = 0
    if coin not in special_coin_list:
        if price &amp;gt;= 2000000:
            coin_order_unit = 1000
        elif price &amp;gt;=1000000 and price&amp;lt;2000000:
            coin_order_unit = 500
        elif price &amp;gt;=500000 and price &amp;lt;1000000:
            coin_order_unit = 100
        elif price &amp;gt;=100000 and price&amp;lt;500000:
            coin_order_unit = 50
        elif price &amp;gt;=10000 and price&amp;lt;100000:
            coin_order_unit = 10
        elif price &amp;gt;=1000 and price&amp;lt;10000:
            coin_order_unit = 1
        elif price &amp;gt;=100 and price&amp;lt;1000:
            coin_order_unit = 0.1
        elif price &amp;gt;=10 and price&amp;lt;100:
            coin_order_unit = 0.01
        elif price &amp;gt;= 1 and price&amp;lt;10:
            coin_order_unit = 0.001
        elif price&amp;gt;=0.1 and price&amp;lt;1:
            coin_order_unit = 0.0001
        elif price&amp;gt;=0.01 and price&amp;lt;0.1:
            coin_order_unit = 0.00001
        elif price&amp;gt;=0.001 and price&amp;lt;0.01:
            coin_order_unit = 0.000001
        elif price&amp;gt;=0.0001 and price&amp;lt;0.001 :
            coin_order_unit = 0.0000001
        else :
            coin_order_unit = 0.00000001
    else:
        coin_order_unit = 1
    return coin_order_unit

# 지표 계산함수
def calculate_indicator(df):
    
    df['CCI'] = ts.CCI(high=df['high_price'], low=df['low_price'], close=df['trade_price'], timeperiod=14)
    df['CCI_Signal_EMA'] = df['CCI'].ewm(span=9).mean()
    cci = df['CCI'].iloc[-1]
    cci_signal = df['CCI_Signal_EMA'].iloc[-1]

    # ADX and DI+
    adx_indicator = ta.trend.ADXIndicator(df[&quot;high_price&quot;], df[&quot;low_price&quot;], df[&quot;trade_price&quot;], window=14)
    df['DIm'] = adx_indicator.adx_neg()
    df['DIp'] = adx_indicator.adx_pos()
    df['Adx'] = adx_indicator.adx()
    dip = df['DIp'].iloc[-1]
    dim = df['DIm'].iloc[-1]
    adx = df['Adx'].iloc[-1]

    return df

def detect_signal(df):
    df = calculate_indicator(df)
    if len(df) &amp;lt; 2:
        return 'none'
    
    current = df.iloc[-1]
    prev = df.iloc[-2]
    
    # 현재 및 이전 값 추출
    adx, dip, dim = current['Adx'], current['DIp'], current['DIm']
    prev_adx, prev_dip, prev_dim = prev['Adx'], prev['DIp'], prev['DIm']
    
    # 순위 계산 함수
    def get_rank(a, b, c):
        sorted_values = sorted([(a, 'ADX'), (b, '+DI'), (c, '-DI')], key=lambda x: -x[0])
        return [item[1] for item in sorted_values]
    
    current_rank = get_rank(adx, dip, dim)
    prev_rank = get_rank(prev_adx, prev_dip, prev_dim)
    
    # ADX 강도 (20 기준)
    adx_strong = adx &amp;gt; 20  # 기준값 20으로 변경
    adx_weak = adx &amp;lt; 20
    
    # 모든 가능한 돌파 쌍 검사
    signals = []
    
    # 1. ADX와 +DI 돌파 검사
    if (adx &amp;gt; dip and prev_adx &amp;lt;= prev_dip) or (adx &amp;lt; dip and prev_adx &amp;gt;= prev_dip):
        adx_dir = 'up' if adx &amp;gt; prev_adx else 'down'
        dip_dir = 'up' if dip &amp;gt; prev_dip else 'down'
        
        if adx &amp;lt; dip and adx_dir == 'up' and dip_dir == 'up' and adx_strong:
            signals.append('buy')
        elif adx &amp;gt; dip and adx_dir == 'down' and dip_dir == 'down' and adx_strong:
            signals.append('sell')
        elif adx &amp;gt; dip and adx_dir == 'up' and dip_dir == 'down' and adx_strong:
            signals.append('sell')
    
    # 2. ADX와 -DI 돌파 검사
    if (adx &amp;gt; dim and prev_adx &amp;lt;= prev_dim) or (adx &amp;lt; dim and prev_adx &amp;gt;= prev_dim):
        adx_dir = 'up' if adx &amp;gt; prev_adx else 'down'
        dim_dir = 'up' if dim &amp;gt; prev_dim else 'down'
        
        if adx &amp;gt; dim and adx_dir == 'up' and dim_dir == 'down' and adx_strong:
            signals.append('buy')
        elif adx &amp;lt; dim and adx_dir == 'down' and dim_dir == 'up' and adx_strong:
            signals.append('sell')
    
    # 3. +DI와 -DI 돌파 검사
    if (dip &amp;gt; dim and prev_dip &amp;lt;= prev_dim) or (dip &amp;lt; dim and prev_dip &amp;gt;= prev_dim):
        dip_dir = 'up' if dip &amp;gt; prev_dip else 'down'
        dim_dir = 'up' if dim &amp;gt; prev_dim else 'down'
        
        if dip &amp;gt; dim and dip_dir == 'up' and dim_dir == 'down' and adx &amp;gt; prev_adx:
            signals.append('buy')
        elif dip &amp;lt; dim and dip_dir == 'down' and dim_dir == 'up' and adx &amp;gt; prev_adx:
            signals.append('sell')
    
    # 4. 3개 지표 동시 돌파
    if current_rank != prev_rank:
        adx_dir = 'up' if adx &amp;gt; prev_adx else 'down'
        dip_dir = 'up' if dip &amp;gt; prev_dip else 'down'
        dim_dir = 'up' if dim &amp;gt; prev_dim else 'down'
        
        if adx_dir == 'up' and dip_dir == 'up' and dim_dir == 'up':
            if current_rank[0] in ['+DI', 'ADX'] and adx_strong:
                signals.append('buy')
        elif adx_dir == 'down' and dip_dir == 'down' and dim_dir == 'down':
            if current_rank[0] in ['-DI', 'ADX'] and adx_strong:
                signals.append('sell')
    
    # 최종 신호 결정 (CCI 조건: 매수만 적용)
    if 'sell' in signals and 'buy' in signals:
        return 'none'
    elif 'sell' in signals:
        return 'sell'
    elif 'buy' in signals and prev['CCI'] &amp;lt; current['CCI']:  # CCI 상승 조건
        return 'buy'
    else:
        return 'none'

def get_top_gainers_upbit(status, number):
    
    base_url = &quot;https://api.upbit.com/v1&quot;
    try:
        # Step 1: Get market data
        markets_response = requests.get(f&quot;{base_url}/market/all&quot;)
        markets_response.raise_for_status()
        markets = markets_response.json()

        # Filter for KRW markets only and extract symbols
        krw_symbols = [market['market'].split('-')[1] for market in markets if market['market'].startswith('KRW-')]

        # Step 2: Get ticker data for all KRW markets
        krw_markets = [f&quot;KRW-{symbol}&quot; for symbol in krw_symbols]
        tickers_response = requests.get(f&quot;{base_url}/ticker&quot;, params={&quot;markets&quot;: &quot;,&quot;.join(krw_markets)})
        tickers_response.raise_for_status()
        tickers = tickers_response.json()

        # Step 3: Calculate price change percentages and filter
        gainers = []
        except_coin = ['USDC','USDT','BTC','ETH','BCH','AAVE','SOL','BSV','AVAX','EGLD','STX','BTT','XEM']
        
        for ticker in tickers:
            market = ticker['market']
            symbol = market.split('-')[1]
            prev_trade_price = ticker['prev_closing_price']
            current_price = ticker['trade_price']

            if prev_trade_price and prev_trade_price &amp;gt; 0 and symbol not in except_coin:
                change_percent = ((current_price - prev_trade_price) / prev_trade_price) * 100

                if change_percent &amp;gt;= 7:  # ✅ 10% 이상 상승한 종목만 필터링
                    gainers.append({
                        'symbol': symbol,
                        'change_percent': change_percent,
                        'current_price': current_price
                    })

        # Step 4: Sort by price change percentage
        gainers = sorted(gainers, key=lambda x: x['change_percent'], reverse=(status == 'up'))

        # Step 5: Return top N gainers
        return [gainer['symbol'] for gainer in gainers[:number]]

    except requests.exceptions.RequestException as e:
        print(f&quot;Error while fetching data from Upbit API: {e}&quot;)
        return []


def main():
    # input_str = input(&quot;예측할 코인 심볼을 쉼표(,)로 구분해서 입력하세요 (예: BTC,ETH,XRP): &quot;)
    # coin_list = [symbol.strip().upper() for symbol in input_str.split(&quot;,&quot;)]
    coin_list = []
    coin_holdings = get_coin_holdings(api_key, secret_key)
    money = int(get_balance() * 0.95)
    coin_list_copy = coin_list.copy()
    coin_list_copy.extend(get_top_gainers_upbit('up',2))

    while True:
        money = int(get_balance() * 0.95)
        coin_list_copy = coin_list.copy()
        coin_list_copy.extend(get_top_gainers_upbit('up',4))
        coin_holdings = get_coin_holdings(api_key, secret_key)
        if len(coin_holdings)==1:
            for coin in coin_list_copy:
                print('')
                print('------------------------------------------------------')
                print(f'&amp;lt;{coin}&amp;gt;')
                market = f&quot;KRW-{coin}&quot;
                status_coin_price = get_current_price(coin)
                coin_order_unit = get_coin_order_unit(coin,status_coin_price)

                try:
                    df_1min = get_coin_data(coin, 1)
                    decision = detect_signal(df_1min)
                    now = datetime.now()
                    print(f'{now.strftime(&quot;%Y-%m-%d %H:%M:%S&quot;)} {coin}의 현재 가격 및 상황 : {status_coin_price}원  - {decision}')
                    if decision == 'buy':# and signal_btc == '매수 신호':
                    
                        orderbook = get_orderbook(api_key, market)
                        buy_price = orderbook['bid_price'] + coin_order_unit
                        buy_uuid = place_limit_buy_order(api_key,secret_key,market,buy_price,money)
                        unfilled_orders = check_unfilled_orders(api_key, secret_key)
                        if unfilled_orders:
                            print('지정가 매수 실패, 미체결 주문 취소 작업 중..')
                            for order in unfilled_orders:
                                cancel_result = cancel_unfilled_order(api_key, secret_key, buy_uuid)
                        else:
                            current_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
                            add_trade(current_time, f'{coin}', 'buy', buy_price)
                            break
                    if len(get_coin_holdings(api_key,secret_key))&amp;gt;1:
                        break
                except Exception as e:
                    print(f&quot;⚠️ [{coin}] 매매 판단 실패: {e}\n&quot;)
        else:
            hold_coin = list(coin_holdings.keys())[1]
            hold_coin_amount = float(coin_holdings[hold_coin])
            status_coin_price = get_current_price(hold_coin)
            coin_order_unit = get_coin_order_unit(hold_coin,status_coin_price)
            market = f'KRW-{hold_coin}'

            print('----------------------------------------------')
            
            try:
                df_1min = get_coin_data(hold_coin, 1)
                decision = detect_signal(df_1min)
                now = datetime.now()
                print(f'{now.strftime(&quot;%Y-%m-%d %H:%M:%S&quot;)} {coin}의 현재 가격 및 상황 : {status_coin_price}원  - {decision}')
                if decision == 'sell':
                    orderbook = get_orderbook(api_key, market)
                    market = f'KRW-{hold_coin}'
                    sell_price = orderbook['ask_price'] - coin_order_unit
                    sell_uuid = place_limit_sell_order(api_key,secret_key,market,sell_price,hold_coin_amount)
                    unfilled_orders = check_unfilled_orders(api_key, secret_key)
                    if unfilled_orders:
                        print('지정가 매도 실패, 미체결 주문 취소 작업 중..')
                        for order in unfilled_orders:
                            cancel_result = cancel_unfilled_order(api_key, secret_key, sell_uuid)
                        sell_uuid = place_market_sell_order(api_key,secret_key,market,hold_coin_amount)
                        
                    else:
                        current_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
                        add_trade(current_time, f'{hold_coin}', 'sell', sell_price)
                        break
            except Exception as e:
                print(f&quot;⚠️ [{hold_coin}] 매매 판단 실패: {e}\n&quot;)


if __name__ == &quot;__main__&quot;:
    main()&lt;/code&gt;&lt;/pre&gt;</description>
      <category>혼자 만든 Code/upbit 코인 자동거래</category>
      <author>빛하루</author>
      <guid isPermaLink="true">https://harucode.tistory.com/868</guid>
      <comments>https://harucode.tistory.com/868#entry868comment</comments>
      <pubDate>Wed, 7 May 2025 23:15:35 +0900</pubDate>
    </item>
    <item>
      <title>업비트 코인 자동거래 ver 2.1</title>
      <link>https://harucode.tistory.com/866</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1745590009883&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import pandas as pd
import requests
import talib as ta
import time
import jwt
import uuid
import json
from urllib.parse import urlencode
import pyupbit
import numpy as np
import hashlib
from urllib.parse import urlencode, unquote
import matplotlib.pyplot as plt
import csv
import os
from datetime import datetime
import pytz
from sklearn.ensemble import RandomForestClassifier, RandomForestRegressor
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler

UPBIT_API_URL = &quot;https://api.upbit.com/v1&quot;
server_url = 'https://api.upbit.com'
special_coin_list = ['ADA','ALGO','BLUR','CELO', 'ELF', 'EOS', 'GRS', 'GRT', 'ICX', 'MANA', 'MINA', 'POL', 'SAND', 'SEI', 'STG', 'TRX', 'SBD']

# JSON 파일에서 API 키 불러오기
def load_api_keys(config_file=&quot;upbit_config.json&quot;):
    try:
        with open(config_file, &quot;r&quot;) as file:
            keys = json.load(file)
            return keys[&quot;api_key&quot;], keys[&quot;secret_key&quot;]
    except Exception as e:
        print(f&quot;Error loading API keys: {e}&quot;)
        return None, None

# 현재 업비트 계좌 정보를 불러오는 함수
def get_upbit_account_info(api_key, secret_key):
    base_url = &quot;https://api.upbit.com/v1&quot;

    # JWT 토큰 생성
    payload = {
        'access_key': api_key,
        'nonce': str(uuid.uuid4()),
    }
    jwt_token = jwt.encode(payload, secret_key, algorithm=&quot;HS256&quot;)
    authorization_token = f&quot;Bearer {jwt_token}&quot;
    
    headers = {
        &quot;Authorization&quot;: authorization_token,
    }

    # 계좌 조회 API 호출
    try:
        account_res = requests.get(f&quot;{base_url}/accounts&quot;, headers=headers)
        account_data = account_res.json()
        if account_res.status_code != 200:
            print(f&quot;Error fetching account data: {account_data}&quot;)
            return

        # 코인별 정보 조회
        market_data = requests.get(f&quot;{base_url}/market/all&quot;).json()
        market_dict = {item['market']: item['korean_name'] for item in market_data}

        total_balance = 0
        coin_info_list = []

        for account in account_data:
            if float(account['balance']) &amp;gt; 0:  # 잔액이 0보다 클 경우
                ticker = account['currency']
                if ticker == &quot;KRW&quot;:
                    total_balance += float(account['balance'])
                    continue
                
                market = f&quot;KRW-{ticker}&quot;
                avg_buy_price = float(account['avg_buy_price'])
                balance = float(account['balance'])

                # 현재 가격 조회
                ticker_url = f&quot;{base_url}/ticker?markets={market}&quot;
                ticker_res = requests.get(ticker_url).json()
                current_price = float(ticker_res[0]['trade_price'])
                
                # 평가 금액과 수익률 계산
                eval_amount = balance * current_price
                profit_rate = ((current_price - avg_buy_price) / avg_buy_price) * 100
                
                # 총 자산에 반영
                total_balance += eval_amount
                
                coin_info_list.append({
                    &quot;코인&quot;: market_dict.get(market, ticker),
                    &quot;보유수량&quot;: balance,
                    &quot;평균단가&quot;: avg_buy_price,
                    &quot;현재가격&quot;: current_price,
                    &quot;수익률&quot;: profit_rate,
                })

        # 출력
        print(f&quot;총 자산: {total_balance:,.0f} KRW&quot;)
        print('')
        print(&quot;보유 코인 정보:&quot;)
        print(coin_info_list)
        return coin_info_list,total_balance
    except Exception as e:
        print(f&quot;An error occurred: {e}&quot;)

# 보유중인 코인의 심볼과 수량을 가져오는 함수
def get_coin_holdings(api_key, secret_key):
    &quot;&quot;&quot;
    업비트 API를 사용하여 보유 중인 코인의 종류와 수량을 가져옵니다.

    Returns:
        dict: 코인의 종류와 보유 수량을 포함한 딕셔너리
    &quot;&quot;&quot;
    base_url = &quot;https://api.upbit.com/v1/accounts&quot;

    # JWT 토큰 생성
    payload = {
        'access_key': api_key,
        'nonce': str(uuid.uuid4()),
    }
    jwt_token = jwt.encode(payload, secret_key, algorithm=&quot;HS256&quot;)
    headers = {&quot;Authorization&quot;: f&quot;Bearer {jwt_token}&quot;}

    try:
        # 계좌 정보 요청
        response = requests.get(base_url, headers=headers)
        response.raise_for_status()
        accounts = response.json()

        # 보유 코인 정보 필터링
        holdings = {account['currency']: float(account['balance']) for account in accounts if float(account['balance']) &amp;gt; 0}

        # 보유 코인이 없을 경우 처리
        if not holdings:
            print(&quot;보유 중인 코인이 없습니다.&quot;)
            return {}

        return holdings

    except Exception as e:
        print(f&quot;Error fetching coin holdings: {e}&quot;)
        return {}

#미체결된 주문 조회 함수
def check_unfilled_orders(api_key, secret_key):
    &quot;&quot;&quot;
    미체결 주문이 있는지 확인합니다.

    Returns:
        list: 미체결 주문 리스트 (없으면 빈 리스트)
    &quot;&quot;&quot;
    base_url = &quot;https://api.upbit.com/v1/orders&quot;
    query = {
        'state': 'wait',  # 'wait' 상태는 미체결 주문을 의미
    }

    # Query Hash 계산
    query_string = '&amp;amp;'.join([f&quot;{key}={value}&quot; for key, value in query.items()])
    query_hash = hashlib.sha512(query_string.encode()).hexdigest()

    # JWT 토큰 생성
    payload = {
        'access_key': api_key,
        'nonce': str(uuid.uuid4()),
        'query_hash': query_hash,
        'query_hash_alg': 'SHA512',
    }
    jwt_token = jwt.encode(payload, secret_key, algorithm=&quot;HS256&quot;)
    headers = {&quot;Authorization&quot;: f&quot;Bearer {jwt_token}&quot;}

    try:
        # 미체결 주문 요청
        response = requests.get(base_url, headers=headers, params=query)
        response.raise_for_status()
        orders = response.json()

        # 미체결 주문이 없는 경우 처리
        if not orders:
            print(&quot;미체결 주문이 없습니다.&quot;)
            return []

        print(&quot;미체결 주문이 있습니다.&quot;)
        return orders

    except Exception as e:
        print(f&quot;Error fetching unfilled orders: {e}&quot;)
        return []
    
#미체결된 주문 취소하는 함수
def cancel_unfilled_order(api_key, secret_key, order_id):
    &quot;&quot;&quot;
    특정 미체결 주문을 취소합니다.

    Args:
        api_key (str): 업비트 API 키
        secret_key (str): 업비트 Secret 키
        order_id (str): 취소할 주문의 UUID

    Returns:
        dict: 주문 취소 결과
    &quot;&quot;&quot;
    base_url = &quot;https://api.upbit.com/v1/order&quot;
    query = {
        'uuid': order_id,
    }

    # JWT 토큰 생성
    query_string = '&amp;amp;'.join([f&quot;{key}={value}&quot; for key, value in query.items()])
    payload = {
        'access_key': api_key,
        'nonce': str(uuid.uuid4()),
        'query_hash': hashlib.sha512(query_string.encode()).hexdigest(),
        'query_hash_alg': 'SHA512',
    }
    jwt_token = jwt.encode(payload, secret_key, algorithm=&quot;HS256&quot;)
    headers = {&quot;Authorization&quot;: f&quot;Bearer {jwt_token}&quot;}

    try:
        # 주문 취소 요청
        response = requests.delete(base_url, headers=headers, params=query)
        response.raise_for_status()
        cancel_result = response.json()

        print(f&quot;주문이 성공적으로 취소되었습니다: {cancel_result}&quot;)
        return cancel_result

    except requests.exceptions.HTTPError as e:
        print(f&quot;HTTP 에러 발생: {e.response.status_code}, {e.response.text}&quot;)
        return {}
    except Exception as e:
        print(f&quot;Error canceling order: {e}&quot;)
        return {}
    
#매수 1호가와 매도1호가 리턴하는 함수    
def get_orderbook(api_key, market):
    &quot;&quot;&quot;
    주어진 마켓의 매수, 매도 1호가를 가져옵니다.

    Args:
        api_key (str): 업비트 API 키
        market (str): 마켓 코드 (예: &quot;KRW-BTC&quot;)

    Returns:
        dict: 매수 1호가와 매도 1호가를 포함한 딕셔너리
    &quot;&quot;&quot;
    url = f&quot;https://api.upbit.com/v1/orderbook?markets={market}&amp;amp;level=0&quot;

    headers = {&quot;accept&quot;: &quot;application/json&quot;}

    try:
        # 주문서 정보 요청
        response = requests.get(url, headers=headers)
        response.raise_for_status()
        orderbook = response.json()

        if not orderbook:
            print(f&quot;{market}의 주문서 정보가 없습니다.&quot;)
            return None

        # 매수 1호가 (가장 높은 매도 호가)
        bid_price = orderbook[0]['orderbook_units'][0]['bid_price']
        # 매도 1호가 (가장 낮은 매수 호가)
        ask_price = orderbook[0]['orderbook_units'][0]['ask_price']

        return {&quot;bid_price&quot;: bid_price, &quot;ask_price&quot;: ask_price}

    except Exception as e:
        print(f&quot;Error fetching orderbook: {e}&quot;)
        return None
    
api_key, secret_key = load_api_keys(&quot;upbit_config.json&quot;)

#지정가 매수함수    
def place_limit_buy_order(api_key, secret_key, market, price, budget):
    max_quantity = str(budget / price)
    params = {
        'market': market,
        'side': 'bid',
        'ord_type': 'limit',
        'price': price,
        'volume': max_quantity
    }
    query_string = unquote(urlencode(params, doseq=True)).encode(&quot;utf-8&quot;)

    m = hashlib.sha512()
    m.update(query_string)
    query_hash = m.hexdigest()

    payload = {
        'access_key': api_key,
        'nonce': str(uuid.uuid4()),
        'query_hash': query_hash,
        'query_hash_alg': 'SHA512',
    }

    jwt_token = jwt.encode(payload, secret_key)
    authorization = 'Bearer {}'.format(jwt_token)
    headers = {
    'Authorization': authorization,
    }

    res = requests.post(server_url + '/v1/orders', json=params, headers=headers)
    order_result = res.json()
    # 성공적으로 주문을 제출했다면 주문 UUID 반환
    order_uuid = order_result.get('uuid')
    if order_uuid:
        print(f&quot;매수 주문이 성공적으로 생성되었습니다. 주문 UUID: {order_uuid}&quot;)
        time.sleep(5)
        return order_uuid
    else:
        print(&quot;주문 생성 실패: UUID를 찾을 수 없습니다.&quot;)
        return None
    
# 지정가 매도함수
def place_limit_sell_order(api_key, secret_key, market, price, quantity):
    max_quantity = str(quantity)
    params = {
        'market': market,
        'side': 'ask',
        'ord_type': 'limit',
        'price': price,
        'volume': max_quantity
    }
    query_string = unquote(urlencode(params, doseq=True)).encode(&quot;utf-8&quot;)

    m = hashlib.sha512()
    m.update(query_string)
    query_hash = m.hexdigest()

    payload = {
        'access_key': api_key,
        'nonce': str(uuid.uuid4()),
        'query_hash': query_hash,
        'query_hash_alg': 'SHA512',
    }

    jwt_token = jwt.encode(payload, secret_key)
    authorization = 'Bearer {}'.format(jwt_token)
    headers = {
    'Authorization': authorization,
    }

    res = requests.post(server_url + '/v1/orders', json=params, headers=headers)
    order_result = res.json()
    # 성공적으로 주문을 제출했다면 주문 UUID 반환
    order_uuid = order_result.get('uuid')
    if order_uuid:
        print(f&quot;매도 주문이 성공적으로 생성되었습니다. 주문 UUID: {order_uuid}&quot;)
        time.sleep(5)
        return order_uuid
    else:
        print(&quot;주문 생성 실패: UUID를 찾을 수 없습니다.&quot;)
        return None
    
#코인의 현재가격을 불러오는 함수
def get_current_price(coin):
    url = f&quot;https://api.upbit.com/v1/ticker&quot;
    params = {
        'markets': f'KRW-{coin}'  # 코인 이름 입력 (예: 'BTC' -&amp;gt; 'KRW-BTC')
    }
    
    response = requests.get(url, params=params)
    data = response.json()
    
    if len(data) &amp;gt; 0:
        return data[0]['trade_price']  # 현재 거래 가격 반환
    else:
        return None
    
#받아온 코인 데이터를 저장할 csv파일을 만드는 함수
def save_to_csv(df):
    filename = 'coin_data.csv'
    df.to_csv(filename, index=False)

#거래기록을 저장할 csv 파일일
columns = ['시간', '코인명', '매수/매도', '평균 단가','현재 보유잔고']
df_trade = pd.DataFrame(columns=columns)
df_trade.to_csv('trade_list.csv', index=False, encoding='utf-8-sig')

#거래기록을 추가하는 함수
def add_trade(trade_time, coin_name, trade_type, avg_price,hold_won):
    # CSV 파일 읽기
    df = pd.read_csv('trade_list.csv', encoding='utf-8-sig')
    
    # 새로운 데이터 추가
    new_data = {
        '시간': trade_time,
        '코인명': coin_name,
        '매수/매도': trade_type,
        '평균 단가': avg_price,
        '현재 보유잔고':hold_won,
    }
    df = df.append(new_data, ignore_index=True)
    
    # 업데이트된 데이터프레임을 CSV 파일로 저장
    df.to_csv('trade_list.csv', index=False, encoding='utf-8-sig')

#현재 보유중인 원화 잔고를 가져오는 함수
def get_balance():
    &quot;&quot;&quot;
    업비트 API를 사용하여 원화 잔고를 조회하는 함수
    &quot;&quot;&quot;
    url = server_url + '/v1/accounts'
    
    # 요청 헤더 준비
    payload = {
        'access_key': api_key,
        'nonce': str(uuid.uuid4())
    }
    
    # 서명 생성
    m = hashlib.sha512()
    m.update(payload['nonce'].encode('utf-8'))
    signature = jwt.encode(payload, secret_key, algorithm='HS256')
    
    headers = {
        'Authorization': f'Bearer {signature}'
    }
    
    # API 요청
    response = requests.get(url, headers=headers)
    
    if response.status_code == 200:
        accounts = response.json()
        for account in accounts:
            if account['currency'] == 'KRW':  # 원화 잔고 찾기
                return float(account['balance'])
    else:
        print(f&quot;Error: {response.status_code}&quot;)
        return None

def get_candle_data(market, unit, count=200):
    url = f&quot;{UPBIT_API_URL}/candles/{'minutes' if unit != 'day' else 'days'}/{unit if unit != 'day' else ''}&quot;.rstrip('/')
    params = {&quot;market&quot;: market, &quot;count&quot;: count}
    response = requests.get(url, params=params)
    response.raise_for_status()
    return response.json()[::-1]

def preprocess_candle(candles):
    df = pd.DataFrame(candles)
    df['price_diff'] = df['trade_price'].diff()
    df['ma_5'] = df['trade_price'].rolling(window=5).mean()
    df['ma_20'] = df['trade_price'].rolling(window=20).mean()
    df['volatility'] = df['high_price'] - df['low_price']
    df['change_rate'] = df['trade_price'].pct_change()
    df['volume'] = df['candle_acc_trade_volume']
    df = df.dropna().reset_index(drop=True)
    return df

def create_labels(df, future_shift=3, threshold=0.01):
    df['future_price'] = df['trade_price'].shift(-future_shift)
    df['return'] = (df['future_price'] - df['trade_price']) / df['trade_price']

    def label(row):
        if row['return'] &amp;gt; threshold * 2:
            return '급등'
        elif row['return'] &amp;gt; threshold:
            return '상승'
        elif row['return'] &amp;lt; -threshold * 2:
            return '급락'
        elif row['return'] &amp;lt; -threshold:
            return '하락'
        else:
            return '보합'

    df['label'] = df.apply(label, axis=1)
    return df.dropna()

def train_model(df):
    features = ['price_diff', 'ma_5', 'ma_20', 'volatility', 'change_rate', 'volume']
    X = df[features]
    y = df['label']
    X_train, _, y_train, _ = train_test_split(X, y, test_size=0.2, random_state=42)

    scaler = StandardScaler()
    X_train_scaled = scaler.fit_transform(X_train)

    model = RandomForestClassifier(n_estimators=100, random_state=42)
    model.fit(X_train_scaled, y_train)

    return model, scaler, features

def train_regressors(df):
    df['future_high'] = df['high_price'].shift(-3)
    df['future_low'] = df['low_price'].shift(-3)
    df = df.dropna()

    features = ['price_diff', 'ma_5', 'ma_20', 'volatility', 'change_rate', 'volume']
    X = df[features]
    y_high = df['future_high']
    y_low = df['future_low']

    X_train, _, y_high_train, _ = train_test_split(X, y_high, test_size=0.2, random_state=42)
    _, _, y_low_train, _ = train_test_split(X, y_low, test_size=0.2, random_state=42)

    scaler = StandardScaler()
    X_train_scaled = scaler.fit_transform(X_train)

    model_high = RandomForestRegressor(n_estimators=100, random_state=42)
    model_low = RandomForestRegressor(n_estimators=100, random_state=42)
    model_high.fit(X_train_scaled, y_high_train)
    model_low.fit(X_train_scaled, y_low_train)

    return model_high, model_low, scaler, features

def predict_with_weights(models, scalers, features_list, datasets, weights):
    score = {&quot;급등&quot;: 0, &quot;상승&quot;: 0, &quot;하락&quot;: 0, &quot;급락&quot;: 0, &quot;보합&quot;: 0}
    
    for model, scaler, features, df, weight in zip(models, scalers, features_list, datasets, weights):
        X = df[features].iloc[[-1]]
        X_scaled = scaler.transform(X)
        probs = model.predict_proba(X_scaled)[0]
        classes = model.classes_
        for cls, prob in zip(classes, probs):
            score[cls] += prob * weight

    return max(score, key=score.get)

def aggregate_trade_decision(predictions_by_tf):
    weight_map = {
        '1분봉': 0.3,
        '5분봉': 0.25,
        '10분봉': 0.2,
        '30분봉': 0.15,
        '60분봉': 0.1
    }

    score_map = {
        '급등': 2,
        '상승': 1,
        '보합': 0,
        '하락': -1,
        '급락': -2
    }

    total_score = 0
    for tf, pred in predictions_by_tf:
        weight = weight_map.get(tf, 0)
        score = score_map.get(pred, 0)
        total_score += score * weight

    if total_score &amp;gt;= 0.7:
        return &quot;  매도 강력 추천&quot;
    elif total_score &amp;gt;= 0.3:
        return &quot;  매도 추천&quot;
    elif total_score &amp;lt;= -0.7:
        return &quot;  매수 강력 추천&quot;
    elif total_score &amp;lt;= -0.5:
        return &quot;  매수 추천&quot;
    else:
        return &quot;⚪ 관망 (Hold)&quot;

def main():
    input_str = input(&quot;예측할 코인 심볼을 쉼표(,)로 구분해서 입력하세요 (예: BTC,ETH,XRP): &quot;)
    coin_list = [symbol.strip().upper() for symbol in input_str.split(&quot;,&quot;)]
    timeframes = [1, 5, 15, 30, 60]
    weights = [0.3, 0.25, 0.2, 0.15, 0.1]
    while True:
        coin_holdings = get_coin_holdings(api_key, secret_key)
        money = int(get_balance() * 0.95)
        if len(coin_holdings)==1:
            for coin in coin_list:
                market = f&quot;KRW-{coin}&quot;
                status_coin_price = get_current_price(coin)
                coin_order_unit = 0
                if status_coin_price &amp;gt;= 2000000:
                    coin_order_unit = 1000
                elif status_coin_price &amp;gt;=1000000 and status_coin_price&amp;lt;2000000:
                    coin_order_unit = 500
                elif status_coin_price &amp;gt;=500000 and status_coin_price &amp;lt;1000000:
                    coin_order_unit = 100
                elif status_coin_price &amp;gt;=100000 and status_coin_price&amp;lt;500000:
                    coin_order_unit = 50
                elif status_coin_price &amp;gt;=10000 and status_coin_price&amp;lt;100000:
                    coin_order_unit = 10
                elif status_coin_price &amp;gt;=1000 and status_coin_price&amp;lt;10000:
                    coin_order_unit = 1
                elif status_coin_price &amp;gt;=100 and status_coin_price&amp;lt;1000:
                    coin_order_unit = 0.1
                    if coin in special_coin_list:
                        coin_order_unit = 1
                elif status_coin_price &amp;gt;=10 and status_coin_price&amp;lt;100:
                    coin_order_unit = 0.01
                elif status_coin_price &amp;gt;= 1 and status_coin_price&amp;lt;10:
                    coin_order_unit = 0.001
                elif status_coin_price&amp;gt;=0.1 and status_coin_price&amp;lt;1:
                    coin_order_unit = 0.0001
                elif status_coin_price&amp;gt;=0.01 and status_coin_price&amp;lt;0.1:
                    coin_order_unit = 0.00001
                elif status_coin_price&amp;gt;=0.001 and status_coin_price&amp;lt;0.01:
                    coin_order_unit = 0.000001
                elif status_coin_price&amp;gt;=0.0001 and status_coin_price&amp;lt;0.001 :
                    coin_order_unit = 0.0000001
                else :
                    coin_order_unit = 0.00000001
                models, scalers, features_list, datasets = [], [], [], []
                tf_directions = []

                print(f&quot;\n    [{coin}] 예측 시작...\n&quot;)

                for idx, tf in enumerate(timeframes):
                    try:
                        candles = get_candle_data(market, tf)
                        df = preprocess_candle(candles)
                        df = create_labels(df)

                        model_clf, scaler, features = train_model(df)
                        models.append(model_clf)
                        scalers.append(scaler)
                        features_list.append(features)
                        datasets.append(df)

                        model_high, model_low, reg_scaler, _ = train_regressors(df)
                        X_reg_scaled = reg_scaler.transform(df[features].iloc[[-1]])
                        predicted_high = model_high.predict(X_reg_scaled)[0]
                        predicted_low = model_low.predict(X_reg_scaled)[0]

                        X_clf_scaled = scaler.transform(df[features].iloc[[-1]])
                        probs = model_clf.predict_proba(X_clf_scaled)[0]
                        classes = model_clf.classes_
                        direction = classes[np.argmax(probs)]

                        tf_str = f&quot;{tf}분봉&quot;
                        tf_directions.append((tf_str, direction))

                        print(f&quot;  [{tf_str}] 예측 결과:&quot;)
                        print(f&quot;    방향 예측: {direction}&quot;)
                        print(f&quot;    예상 고가: {predicted_high:,.2f} 원&quot;)
                        print(f&quot;    예상 저가: {predicted_low:,.2f} 원\n&quot;)

                    except Exception as e:
                        print(f&quot;[{market} - {tf}] 예측 실패: {e}\n&quot;)

                try:
                    final_result = predict_with_weights(models, scalers, features_list, datasets, weights)
                    print(f&quot;✅   [{coin}] 종합 예측 (가중치 기반): {final_result}&quot;)
                except Exception as e:
                    print(f&quot;❌ [{coin}] 종합 예측 실패: {e}&quot;)

                try:
                    decision = aggregate_trade_decision(tf_directions)
                    print(f&quot;    [{coin}] 현재 매매 판단: {decision}\n&quot;)
                    if (decision == '  매수 강력 추천') or (decision == '  매수 추천'):# and signal_btc == '매수 신호':
                        orderbook = get_orderbook(api_key, market)
                        buy_price = orderbook['bid_price'] + coin_order_unit
                        buy_uuid = place_limit_buy_order(api_key,secret_key,market,buy_price,money)
                        unfilled_orders = check_unfilled_orders(api_key, secret_key)
                        if unfilled_orders:
                            print('지정가 매수 실패, 미체결 주문 취소 작업 중..')
                            for order in unfilled_orders:
                                cancel_result = cancel_unfilled_order(api_key, secret_key, buy_uuid)
                        else:
                            current_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
                            my_money_info,total_balance = get_upbit_account_info(api_key,secret_key)
                            add_trade(current_time, f'{coin}', 'buy', buy_price,total_balance)
                            break
                    if len(get_coin_holdings(api_key,secret_key))&amp;gt;1:
                        break
                except Exception as e:
                    print(f&quot;⚠️ [{coin}] 매매 판단 실패: {e}\n&quot;)
        else:
            hold_coin = list(coin_holdings.keys())[1]
            hold_coin_amount = float(coin_holdings[hold_coin])
            status_coin_price = get_current_price(hold_coin)
            coin_order_unit = 0
            market = f'KRW-{hold_coin}'
            if status_coin_price &amp;gt;= 2000000:
                coin_order_unit = 1000
            elif status_coin_price &amp;gt;=1000000 and status_coin_price&amp;lt;2000000:
                coin_order_unit = 500
            elif status_coin_price &amp;gt;=500000 and status_coin_price &amp;lt;1000000:
                coin_order_unit = 100
            elif status_coin_price &amp;gt;=100000 and status_coin_price&amp;lt;500000:
                coin_order_unit = 50
            elif status_coin_price &amp;gt;=10000 and status_coin_price&amp;lt;100000:
                coin_order_unit = 10
            elif status_coin_price &amp;gt;=1000 and status_coin_price&amp;lt;10000:
                coin_order_unit = 1
            elif status_coin_price &amp;gt;=100 and status_coin_price&amp;lt;1000:
                coin_order_unit = 0.1
                if hold_coin in special_coin_list:
                    coin_order_unit = 1
            elif status_coin_price &amp;gt;=10 and status_coin_price&amp;lt;100:
                coin_order_unit = 0.01
            elif status_coin_price &amp;gt;= 1 and status_coin_price&amp;lt;10:
                coin_order_unit = 0.001
            elif status_coin_price&amp;gt;=0.1 and status_coin_price&amp;lt;1:
                coin_order_unit = 0.0001
            elif status_coin_price&amp;gt;=0.01 and status_coin_price&amp;lt;0.1:
                coin_order_unit = 0.00001
            elif status_coin_price&amp;gt;=0.001 and status_coin_price&amp;lt;0.01:
                coin_order_unit = 0.000001
            elif status_coin_price&amp;gt;=0.0001 and status_coin_price&amp;lt;0.001 :
                coin_order_unit = 0.0000001
            else :
                coin_order_unit = 0.00000001

            print('----------------------------------------------')
            print(f'{hold_coin}의 현재 가격 : {status_coin_price}원')
            models, scalers, features_list, datasets = [], [], [], []
            tf_directions = []

            print(f&quot;\n    [{hold_coin}] 예측 시작...\n&quot;)

            for idx, tf in enumerate(timeframes):
                try:
                    candles = get_candle_data(market, tf)
                    df = preprocess_candle(candles)
                    df = create_labels(df)

                    model_clf, scaler, features = train_model(df)
                    models.append(model_clf)
                    scalers.append(scaler)
                    features_list.append(features)
                    datasets.append(df)

                    model_high, model_low, reg_scaler, _ = train_regressors(df)
                    X_reg_scaled = reg_scaler.transform(df[features].iloc[[-1]])
                    predicted_high = model_high.predict(X_reg_scaled)[0]
                    predicted_low = model_low.predict(X_reg_scaled)[0]

                    X_clf_scaled = scaler.transform(df[features].iloc[[-1]])
                    probs = model_clf.predict_proba(X_clf_scaled)[0]
                    classes = model_clf.classes_
                    direction = classes[np.argmax(probs)]

                    tf_str = f&quot;{tf}분봉&quot;
                    tf_directions.append((tf_str, direction))

                    print(f&quot;  [{tf_str}] 예측 결과:&quot;)
                    print(f&quot;    방향 예측: {direction}&quot;)
                    print(f&quot;    예상 고가: {predicted_high:,.2f} 원&quot;)
                    print(f&quot;    예상 저가: {predicted_low:,.2f} 원\n&quot;)

                except Exception as e:
                    print(f&quot;[{market} - {tf}] 예측 실패: {e}\n&quot;)

            try:
                final_result = predict_with_weights(models, scalers, features_list, datasets, weights)
                print(f&quot;✅   [{hold_coin}] 종합 예측 (가중치 기반): {final_result}&quot;)
            except Exception as e:
                print(f&quot;❌ [{hold_coin}] 종합 예측 실패: {e}&quot;)

            try:
                decision = aggregate_trade_decision(tf_directions)
                print(f&quot;    [{hold_coin}] 현재 매매 판단: {decision}\n&quot;)
                my_money_info,total_balance = get_upbit_account_info(api_key,secret_key)
                while my_money_info[0]['수익률']&amp;lt;=-1.5:
                    orderbook = get_orderbook(api_key, market)
                    status_profit_ratio = get_upbit_account_info(api_key,secret_key)
                    sell_price = orderbook['ask_price']-coin_order_unit
                    sell_uuid = place_limit_sell_order(api_key,secret_key,market,sell_price,hold_coin_amount)
                    if len(check_unfilled_orders(api_key,secret_key))&amp;gt;0:
                        cancel_result = cancel_unfilled_order(api_key,secret_key,sell_uuid)
                    else:
                        current_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
                        my_money_info,total_balance = get_upbit_account_info(api_key,secret_key)
                        add_trade(current_time, f'{hold_coin}', 'sell', sell_price,total_balance)
                        break
                    my_money_info,total_balance = get_upbit_account_info(api_key,secret_key)
                while (decision == '  매도 강력 추천') or (decision == '  매도 추천') :#or signal_btc == '매도 신호':
                    orderbook = get_orderbook(api_key, market)
                    market = f'KRW-{hold_coin}'
                    sell_price = orderbook['ask_price'] - coin_order_unit
                    sell_uuid = place_limit_sell_order(api_key,secret_key,market,sell_price,hold_coin_amount)
                    unfilled_orders = check_unfilled_orders(api_key, secret_key)
                    if unfilled_orders:
                        print('지정가 매도 실패, 미체결 주문 취소 작업 중..')
                        for order in unfilled_orders:
                            cancel_result = cancel_unfilled_order(api_key, secret_key, sell_uuid)
                    else:
                        current_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
                        my_money_info,total_balance = get_upbit_account_info(api_key,secret_key)
                        add_trade(current_time, f'{hold_coin}', 'sell', sell_price,total_balance)
                        break
            except Exception as e:
                print(f&quot;⚠️ [{hold_coin}] 매매 판단 실패: {e}\n&quot;)

if __name__ == &quot;__main__&quot;:
    main()&lt;/code&gt;&lt;/pre&gt;</description>
      <category>혼자 만든 Code/upbit 코인 자동거래</category>
      <category>업비트</category>
      <category>업비트 api</category>
      <category>주식자동거래</category>
      <category>코인</category>
      <category>코인자동거래</category>
      <category>코인자동매매</category>
      <category>코인프로그램</category>
      <author>빛하루</author>
      <guid isPermaLink="true">https://harucode.tistory.com/866</guid>
      <comments>https://harucode.tistory.com/866#entry866comment</comments>
      <pubDate>Fri, 25 Apr 2025 23:08:27 +0900</pubDate>
    </item>
    <item>
      <title>업비트 코인 자동 거래 ver 2.0</title>
      <link>https://harucode.tistory.com/865</link>
      <description>&lt;pre id=&quot;code_1738675061727&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import pandas as pd
import requests
import talib as ta
import time
import jwt
import uuid
import json
from urllib.parse import urlencode
import pyupbit
import numpy as np
import hashlib
from urllib.parse import urlencode, unquote
import matplotlib.pyplot as plt
import csv
import os
from datetime import datetime
import pytz
from sklearn.model_selection import train_test_split
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import accuracy_score,classification_report
from sklearn.preprocessing import StandardScaler
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, Dropout, LSTM
from tensorflow.keras.callbacks import EarlyStopping
import tensorflow as tf
import warnings
from sklearn.linear_model import LogisticRegression

UPBIT_API_URL = &quot;https://api.upbit.com/v1&quot;
server_url = 'https://api.upbit.com'
special_coin_list = ['ADA','ALGO','BLUR','CELO', 'ELF', 'EOS', 'GRS', 'GRT', 'ICX', 'MANA', 'MINA', 'POL', 'SAND', 'SEI', 'STG', 'TRX', 'SBD']


#전일 대비 가격 상승률 상위 n개 종목을 가져오는 함수

def get_top_gainers_upbit(number):
    base_url = &quot;https://api.upbit.com/v1&quot;
    try:
        # Step 1: Get market data
        markets_response = requests.get(f&quot;{base_url}/market/all&quot;)
        markets_response.raise_for_status()
        markets = markets_response.json()

        # Filter for KRW markets only and extract symbols
        krw_symbols = [market['market'].split('-')[1] for market in markets if market['market'].startswith('KRW-')]

        # Step 2: Get ticker data for all KRW markets
        krw_markets = [f&quot;KRW-{symbol}&quot; for symbol in krw_symbols]
        tickers_response = requests.get(f&quot;{base_url}/ticker&quot;, params={&quot;markets&quot;: &quot;,&quot;.join(krw_markets)})
        tickers_response.raise_for_status()
        tickers = tickers_response.json()

        # Step 3: Calculate price change percentages and filter for &amp;gt;3% gainers
        gainers = []
        for ticker in tickers:
            market = ticker['market']
            symbol = market.split('-')[1]
            prev_trade_price = ticker['prev_closing_price']  # 전일 종가
            current_price = ticker['trade_price']     # 현재가

            except_coin = ['USDC','USDT','BTC','ETH','BCH','AAVE','SOL','BSV','AVAX','EGLD']
            except_coin.extend(special_coin_list)
            
            if prev_trade_price &amp;gt; 0 and symbol not in except_coin:  # 유효한 가격만 계산
                change_percent = ((current_price - prev_trade_price) / prev_trade_price) * 100
            
                gainers.append({
                    'symbol': symbol,
                    'change_percent': change_percent,
                    'current_price': current_price
                })

        # Step 4: Sort by price change percentage in descending order
        gainers = sorted(gainers, key=lambda x: x['change_percent'], reverse=True)

        # Step 5: Return top 5 gainers
        return [gainer['symbol'] for gainer in gainers[:number]]

    except requests.exceptions.RequestException as e:
        print(f&quot;Error while fetching data from Upbit API: {e}&quot;)
        return []
    
# JSON 파일에서 API 키 불러오기
def load_api_keys(config_file=&quot;upbit_config.json&quot;):
    try:
        with open(config_file, &quot;r&quot;) as file:
            keys = json.load(file)
            return keys[&quot;api_key&quot;], keys[&quot;secret_key&quot;]
    except Exception as e:
        print(f&quot;Error loading API keys: {e}&quot;)
        return None, None

# 현재 업비트 계좌 정보를 불러오는 함수
def get_upbit_account_info(api_key, secret_key):
    base_url = &quot;https://api.upbit.com/v1&quot;

    # JWT 토큰 생성
    payload = {
        'access_key': api_key,
        'nonce': str(uuid.uuid4()),
    }
    jwt_token = jwt.encode(payload, secret_key, algorithm=&quot;HS256&quot;)
    authorization_token = f&quot;Bearer {jwt_token}&quot;
    
    headers = {
        &quot;Authorization&quot;: authorization_token,
    }

    # 계좌 조회 API 호출
    try:
        account_res = requests.get(f&quot;{base_url}/accounts&quot;, headers=headers)
        account_data = account_res.json()
        if account_res.status_code != 200:
            print(f&quot;Error fetching account data: {account_data}&quot;)
            return

        # 코인별 정보 조회
        market_data = requests.get(f&quot;{base_url}/market/all&quot;).json()
        market_dict = {item['market']: item['korean_name'] for item in market_data}

        total_balance = 0
        coin_info_list = []

        for account in account_data:
            if float(account['balance']) &amp;gt; 0:  # 잔액이 0보다 클 경우
                ticker = account['currency']
                if ticker == &quot;KRW&quot;:
                    total_balance += float(account['balance'])
                    continue
                
                market = f&quot;KRW-{ticker}&quot;
                avg_buy_price = float(account['avg_buy_price'])
                balance = float(account['balance'])

                # 현재 가격 조회
                ticker_url = f&quot;{base_url}/ticker?markets={market}&quot;
                ticker_res = requests.get(ticker_url).json()
                current_price = float(ticker_res[0]['trade_price'])
                
                # 평가 금액과 수익률 계산
                eval_amount = balance * current_price
                profit_rate = ((current_price - avg_buy_price) / avg_buy_price) * 100
                
                # 총 자산에 반영
                total_balance += eval_amount
                
                coin_info_list.append({
                    &quot;코인&quot;: market_dict.get(market, ticker),
                    &quot;보유수량&quot;: balance,
                    &quot;평균단가&quot;: avg_buy_price,
                    &quot;현재가격&quot;: current_price,
                    &quot;수익률&quot;: profit_rate,
                })

        # 출력
        print(f&quot;총 자산: {total_balance:,.0f} KRW&quot;)
        print('')
        print(&quot;보유 코인 정보:&quot;)
        print(coin_info_list)
        return coin_info_list,total_balance
    except Exception as e:
        print(f&quot;An error occurred: {e}&quot;)

# 보유중인 코인의 심볼과 수량을 가져오는 함수
def get_coin_holdings(api_key, secret_key):
    &quot;&quot;&quot;
    업비트 API를 사용하여 보유 중인 코인의 종류와 수량을 가져옵니다.

    Returns:
        dict: 코인의 종류와 보유 수량을 포함한 딕셔너리
    &quot;&quot;&quot;
    base_url = &quot;https://api.upbit.com/v1/accounts&quot;

    # JWT 토큰 생성
    payload = {
        'access_key': api_key,
        'nonce': str(uuid.uuid4()),
    }
    jwt_token = jwt.encode(payload, secret_key, algorithm=&quot;HS256&quot;)
    headers = {&quot;Authorization&quot;: f&quot;Bearer {jwt_token}&quot;}

    try:
        # 계좌 정보 요청
        response = requests.get(base_url, headers=headers)
        response.raise_for_status()
        accounts = response.json()

        # 보유 코인 정보 필터링
        holdings = {account['currency']: float(account['balance']) for account in accounts if float(account['balance']) &amp;gt; 0}

        # 보유 코인이 없을 경우 처리
        if not holdings:
            print(&quot;보유 중인 코인이 없습니다.&quot;)
            return {}

        return holdings

    except Exception as e:
        print(f&quot;Error fetching coin holdings: {e}&quot;)
        return {}

#미체결된 주문 조회 함수
def check_unfilled_orders(api_key, secret_key):
    &quot;&quot;&quot;
    미체결 주문이 있는지 확인합니다.

    Returns:
        list: 미체결 주문 리스트 (없으면 빈 리스트)
    &quot;&quot;&quot;
    base_url = &quot;https://api.upbit.com/v1/orders&quot;
    query = {
        'state': 'wait',  # 'wait' 상태는 미체결 주문을 의미
    }

    # Query Hash 계산
    query_string = '&amp;amp;'.join([f&quot;{key}={value}&quot; for key, value in query.items()])
    query_hash = hashlib.sha512(query_string.encode()).hexdigest()

    # JWT 토큰 생성
    payload = {
        'access_key': api_key,
        'nonce': str(uuid.uuid4()),
        'query_hash': query_hash,
        'query_hash_alg': 'SHA512',
    }
    jwt_token = jwt.encode(payload, secret_key, algorithm=&quot;HS256&quot;)
    headers = {&quot;Authorization&quot;: f&quot;Bearer {jwt_token}&quot;}

    try:
        # 미체결 주문 요청
        response = requests.get(base_url, headers=headers, params=query)
        response.raise_for_status()
        orders = response.json()

        # 미체결 주문이 없는 경우 처리
        if not orders:
            print(&quot;미체결 주문이 없습니다.&quot;)
            return []

        print(&quot;미체결 주문이 있습니다.&quot;)
        return orders

    except Exception as e:
        print(f&quot;Error fetching unfilled orders: {e}&quot;)
        return []
    
#미체결된 주문 취소하는 함수
def cancel_unfilled_order(api_key, secret_key, order_id):
    &quot;&quot;&quot;
    특정 미체결 주문을 취소합니다.

    Args:
        api_key (str): 업비트 API 키
        secret_key (str): 업비트 Secret 키
        order_id (str): 취소할 주문의 UUID

    Returns:
        dict: 주문 취소 결과
    &quot;&quot;&quot;
    base_url = &quot;https://api.upbit.com/v1/order&quot;
    query = {
        'uuid': order_id,
    }

    # JWT 토큰 생성
    query_string = '&amp;amp;'.join([f&quot;{key}={value}&quot; for key, value in query.items()])
    payload = {
        'access_key': api_key,
        'nonce': str(uuid.uuid4()),
        'query_hash': hashlib.sha512(query_string.encode()).hexdigest(),
        'query_hash_alg': 'SHA512',
    }
    jwt_token = jwt.encode(payload, secret_key, algorithm=&quot;HS256&quot;)
    headers = {&quot;Authorization&quot;: f&quot;Bearer {jwt_token}&quot;}

    try:
        # 주문 취소 요청
        response = requests.delete(base_url, headers=headers, params=query)
        response.raise_for_status()
        cancel_result = response.json()

        print(f&quot;주문이 성공적으로 취소되었습니다: {cancel_result}&quot;)
        return cancel_result

    except requests.exceptions.HTTPError as e:
        print(f&quot;HTTP 에러 발생: {e.response.status_code}, {e.response.text}&quot;)
        return {}
    except Exception as e:
        print(f&quot;Error canceling order: {e}&quot;)
        return {}
    
#매수 1호가와 매도1호가 리턴하는 함수    
def get_orderbook(api_key, market):
    &quot;&quot;&quot;
    주어진 마켓의 매수, 매도 1호가를 가져옵니다.

    Args:
        api_key (str): 업비트 API 키
        market (str): 마켓 코드 (예: &quot;KRW-BTC&quot;)

    Returns:
        dict: 매수 1호가와 매도 1호가를 포함한 딕셔너리
    &quot;&quot;&quot;
    url = f&quot;https://api.upbit.com/v1/orderbook?markets={market}&amp;amp;level=0&quot;

    headers = {&quot;accept&quot;: &quot;application/json&quot;}

    try:
        # 주문서 정보 요청
        response = requests.get(url, headers=headers)
        response.raise_for_status()
        orderbook = response.json()

        if not orderbook:
            print(f&quot;{market}의 주문서 정보가 없습니다.&quot;)
            return None

        # 매수 1호가 (가장 높은 매도 호가)
        bid_price = orderbook[0]['orderbook_units'][0]['bid_price']
        # 매도 1호가 (가장 낮은 매수 호가)
        ask_price = orderbook[0]['orderbook_units'][0]['ask_price']

        return {&quot;bid_price&quot;: bid_price, &quot;ask_price&quot;: ask_price}

    except Exception as e:
        print(f&quot;Error fetching orderbook: {e}&quot;)
        return None
    
api_key, secret_key = load_api_keys(&quot;upbit_config.json&quot;)
coin_list = input(&quot;조회할 코인 심볼들을 입력하세요 (쉼표로 구분): &quot;).upper().replace(&quot; &quot;, &quot;&quot;).split(',')
    
#지정가 매수함수    
def place_limit_buy_order(api_key, secret_key, market, price, budget):
    max_quantity = str(budget / price)
    params = {
        'market': market,
        'side': 'bid',
        'ord_type': 'limit',
        'price': price,
        'volume': max_quantity
    }
    query_string = unquote(urlencode(params, doseq=True)).encode(&quot;utf-8&quot;)

    m = hashlib.sha512()
    m.update(query_string)
    query_hash = m.hexdigest()

    payload = {
        'access_key': api_key,
        'nonce': str(uuid.uuid4()),
        'query_hash': query_hash,
        'query_hash_alg': 'SHA512',
    }

    jwt_token = jwt.encode(payload, secret_key)
    authorization = 'Bearer {}'.format(jwt_token)
    headers = {
    'Authorization': authorization,
    }

    res = requests.post(server_url + '/v1/orders', json=params, headers=headers)
    order_result = res.json()
    # 성공적으로 주문을 제출했다면 주문 UUID 반환
    order_uuid = order_result.get('uuid')
    if order_uuid:
        print(f&quot;매수 주문이 성공적으로 생성되었습니다. 주문 UUID: {order_uuid}&quot;)
        time.sleep(5)
        return order_uuid
    else:
        print(&quot;주문 생성 실패: UUID를 찾을 수 없습니다.&quot;)
        return None
    
# 지정가 매도함수
def place_limit_sell_order(api_key, secret_key, market, price, quantity):
    max_quantity = str(quantity)
    params = {
        'market': market,
        'side': 'ask',
        'ord_type': 'limit',
        'price': price,
        'volume': max_quantity
    }
    query_string = unquote(urlencode(params, doseq=True)).encode(&quot;utf-8&quot;)

    m = hashlib.sha512()
    m.update(query_string)
    query_hash = m.hexdigest()

    payload = {
        'access_key': api_key,
        'nonce': str(uuid.uuid4()),
        'query_hash': query_hash,
        'query_hash_alg': 'SHA512',
    }

    jwt_token = jwt.encode(payload, secret_key)
    authorization = 'Bearer {}'.format(jwt_token)
    headers = {
    'Authorization': authorization,
    }

    res = requests.post(server_url + '/v1/orders', json=params, headers=headers)
    order_result = res.json()
    # 성공적으로 주문을 제출했다면 주문 UUID 반환
    order_uuid = order_result.get('uuid')
    if order_uuid:
        print(f&quot;매도 주문이 성공적으로 생성되었습니다. 주문 UUID: {order_uuid}&quot;)
        time.sleep(5)
        return order_uuid
    else:
        print(&quot;주문 생성 실패: UUID를 찾을 수 없습니다.&quot;)
        return None
    
#코인의 현재가격을 불러오는 함수
def get_current_price(coin):
    url = f&quot;https://api.upbit.com/v1/ticker&quot;
    params = {
        'markets': f'KRW-{coin}'  # 코인 이름 입력 (예: 'BTC' -&amp;gt; 'KRW-BTC')
    }
    
    response = requests.get(url, params=params)
    data = response.json()
    
    if len(data) &amp;gt; 0:
        return data[0]['trade_price']  # 현재 거래 가격 반환
    else:
        return None
    
#받아온 코인 데이터를 저장할 csv파일을 만드는 함수
def save_to_csv(df):
    filename = 'coin_data.csv'
    df.to_csv(filename, index=False)

#거래기록을 저장할 csv 파일일
columns = ['시간', '코인명', '매수/매도', '평균 단가','현재 보유잔고']
df_trade = pd.DataFrame(columns=columns)
df_trade.to_csv('trade_list.csv', index=False, encoding='utf-8-sig')

#거래기록을 추가하는 함수
def add_trade(trade_time, coin_name, trade_type, avg_price,hold_won):
    # CSV 파일 읽기
    df = pd.read_csv('trade_list.csv', encoding='utf-8-sig')
    
    # 새로운 데이터 추가
    new_data = {
        '시간': trade_time,
        '코인명': coin_name,
        '매수/매도': trade_type,
        '평균 단가': avg_price,
        '현재 보유잔고':hold_won,
    }
    df = df.append(new_data, ignore_index=True)
    
    # 업데이트된 데이터프레임을 CSV 파일로 저장
    df.to_csv('trade_list.csv', index=False, encoding='utf-8-sig')

#현재 보유중인 원화 잔고를 가져오는 함수
def get_balance():
    &quot;&quot;&quot;
    업비트 API를 사용하여 원화 잔고를 조회하는 함수
    &quot;&quot;&quot;
    url = server_url + '/v1/accounts'
    
    # 요청 헤더 준비
    payload = {
        'access_key': api_key,
        'nonce': str(uuid.uuid4())
    }
    
    # 서명 생성
    m = hashlib.sha512()
    m.update(payload['nonce'].encode('utf-8'))
    signature = jwt.encode(payload, secret_key, algorithm='HS256')
    
    headers = {
        'Authorization': f'Bearer {signature}'
    }
    
    # API 요청
    response = requests.get(url, headers=headers)
    
    if response.status_code == 200:
        accounts = response.json()
        for account in accounts:
            if account['currency'] == 'KRW':  # 원화 잔고 찾기
                return float(account['balance'])
    else:
        print(f&quot;Error: {response.status_code}&quot;)
        return None

def get_candle_data(market, count=200):
    &quot;&quot;&quot;1분 캔들 데이터 조회 (최신 -&amp;gt; 과거 순)&quot;&quot;&quot;
    url = f&quot;{UPBIT_API_URL}/candles/minutes/1&quot;
    params = {&quot;market&quot;: market, &quot;count&quot;: count}
    response = requests.get(url, params=params)
    return response.json()[::-1]  # 과거 -&amp;gt; 최신 순으로 재정렬

def get_trade_data(market, count=200):
    &quot;&quot;&quot;최근 체결 데이터 조회 (최신 -&amp;gt; 과거 순)&quot;&quot;&quot;
    url = f&quot;{UPBIT_API_URL}/trades/ticks&quot;
    params = {&quot;market&quot;: market, &quot;count&quot;: count}
    response = requests.get(url, params=params)
    return response.json()

def dynamic_trading_signal(candle_data):
    &quot;&quot;&quot;
    동적 분석을 기반으로 매수/매도 신호 생성
    :param candle_data: DataFrame (columns: timestamp, open, high, low, trade_price, volume)
    :return: str ('BUY', 'SELL', 'NEUTRAL')
    &quot;&quot;&quot;
    df = pd.DataFrame(candle_data.copy())
    close_prices = df['trade_price'].astype(float)
    high_prices = df['high_price'].astype(float)
    low_prices = df['low_price'].astype(float)
    volumes = df['candle_acc_trade_volume'].astype(float)
    
    # 1. 기술적 지표 계산
    df['rsi'] = ta.RSI(close_prices, timeperiod=14)
    df['macd'], df['macd_signal'], _ = ta.MACD(close_prices)
    df['stoch_k'], df['stoch_d'] = ta.STOCH(high_prices, low_prices, close_prices)
    df['ma20'] = ta.SMA(close_prices, timeperiod=20)
    df['ma5'] = ta.SMA(close_prices, timeperiod=5)
    df['pdi'] = ta.PLUS_DI(high_prices, low_prices, close_prices, timeperiod=14)  # +DI 추가
    df['mdi'] = ta.MINUS_DI(high_prices, low_prices, close_prices, timeperiod=14)  # -DI 추가

    
    # 2. 추세 변화 포인트 감지
    df['trend'] = np.where(close_prices &amp;gt; df['ma20'], 1, -1)
    df['trend_change'] = df['trend'].diff()
    
    # 3. 동적 임계값 계산 (최근 60봉 기준)
    recent_data = df.iloc[-60:]
    
    # 상승 시작 조건
    up_condition = (recent_data['trend_change'] &amp;gt; 0)
    up_rsi = recent_data[up_condition]['rsi'].mean()
    up_macd = recent_data[up_condition]['macd'].mean()
    up_pdi = recent_data[up_condition]['pdi'].mean()
    up_mdi = recent_data[up_condition]['mdi'].mean()
    
    # 하락 시작 조건
    down_condition = (recent_data['trend_change'] &amp;lt; 0)
    down_rsi = recent_data[down_condition]['rsi'].mean()
    down_macd = recent_data[down_condition]['macd'].mean()
    down_pdi = recent_data[down_condition]['pdi'].mean()
    down_mdi = recent_data[down_condition]['mdi'].mean()
    
    # 4. 현재 지표 값
    current = df.iloc[-1]
    
    # 5. 매수/매도 조건
    buy_condition = (
        (current['rsi'] &amp;lt; up_rsi) &amp;amp;
        (current['macd'] &amp;lt; up_macd) &amp;amp;
        (current['stoch_k'] &amp;lt; 30) &amp;amp;
        (current['trade_price'] &amp;lt; current['ma20']) &amp;amp;
        (current['pdi'] &amp;lt; up_pdi)&amp;amp;  # 상승추세 평균 +DI 초과
        (current['mdi'] &amp;gt; up_mdi)
    )
    
    sell_condition = (
        (current['rsi'] &amp;gt; down_rsi) &amp;amp;
        (current['macd'] &amp;gt; down_macd) &amp;amp;
        (current['stoch_k'] &amp;gt; 60) &amp;amp;
        (current['trade_price'] &amp;gt; current['ma5']) &amp;amp;
        (current['pdi'] &amp;gt; down_pdi)&amp;amp;  # 상승추세 평균 +DI 초과
        (current['mdi'] &amp;lt; down_mdi)
    )

    # print(f&quot;현재 구매 기준 rsi : {up_rsi:.2f}&quot;)
    # print(f&quot;현재 구매 기준 macd : {up_macd:.8f}&quot;)
    # print(f&quot;현재 구매 기준 ma20 : {current['ma20']:.8f}&quot;)
    # print(f&quot;현재 구매 기준 pdi : {up_pdi:.2f}&quot;)
    # print(f&quot;현재 구매 기준 mdi : {up_mdi:.2f}&quot;)
    # print('')

    # print(f&quot;현재 판매 기준 rsi : {down_rsi:.2f}&quot;)
    # print(f&quot;현재 판매 기준 macd : {down_macd:.8f}&quot;)
    # print(f&quot;현재 판매 기준 ma5 : {current['ma5']:.8f}&quot;)
    # print(f&quot;현재 판매 기준 pdi : {down_pdi:.2f}&quot;)
    # print(f&quot;현재 판매 기준 mdi : {down_mdi:.2f}&quot;)
    # print('')

    # print(f&quot;현재 rsi : {current['rsi']:.2f}&quot;)
    # print(f&quot;현재 macd : {current['macd']:.8f}&quot;)
    # print(f&quot;현재 stoch_k : {current['stoch_k']:.2f}&quot;)
    # print(f&quot;현재 기준 pdi : {current['pdi']:.2f}&quot;)
    # print(f&quot;현재 기준 mdi : {current['mdi']:.2f}&quot;)

    # 6. 신호 반환
    if buy_condition:
        return '매수 신호'
    elif sell_condition:
        return '매도 신호'
    else:
        return '신호 없음'

while True:
    coin_holdings = get_coin_holdings(api_key, secret_key)
    money = int(get_balance() * 0.95)
    if len(coin_holdings)==1:
        # coin_list.extend(get_top_gainers_upbit(5))
        for coin in coin_list:
            print('check중 ...')
            market = f'KRW-{coin}'
            status_coin_price = get_current_price(coin)
            coin_order_unit = 0
            if status_coin_price &amp;gt;= 2000000:
                coin_order_unit = 1000
            elif status_coin_price &amp;gt;=1000000 and status_coin_price&amp;lt;2000000:
                coin_order_unit = 500
            elif status_coin_price &amp;gt;=500000 and status_coin_price &amp;lt;1000000:
                coin_order_unit = 100
            elif status_coin_price &amp;gt;=100000 and status_coin_price&amp;lt;500000:
                coin_order_unit = 50
            elif status_coin_price &amp;gt;=10000 and status_coin_price&amp;lt;100000:
                coin_order_unit = 10
            elif status_coin_price &amp;gt;=1000 and status_coin_price&amp;lt;10000:
                coin_order_unit = 1
            elif status_coin_price &amp;gt;=100 and status_coin_price&amp;lt;1000:
                coin_order_unit = 0.1
                if coin in special_coin_list:
                    coin_order_unit = 1
            elif status_coin_price &amp;gt;=10 and status_coin_price&amp;lt;100:
                coin_order_unit = 0.01
            elif status_coin_price &amp;gt;= 1 and status_coin_price&amp;lt;10:
                coin_order_unit = 0.001
            elif status_coin_price&amp;gt;=0.1 and status_coin_price&amp;lt;1:
                coin_order_unit = 0.0001
            elif status_coin_price&amp;gt;=0.01 and status_coin_price&amp;lt;0.1:
                coin_order_unit = 0.00001
            elif status_coin_price&amp;gt;=0.001 and status_coin_price&amp;lt;0.01:
                coin_order_unit = 0.000001
            elif status_coin_price&amp;gt;=0.0001 and status_coin_price&amp;lt;0.001 :
                coin_order_unit = 0.0000001
            else :
                coin_order_unit = 0.00000001
            
            print('----------------------------------------------')
            print(f'{coin}의 현재 가격 : {status_coin_price}원')
            df = get_candle_data(f&quot;KRW-{coin}&quot;)
            signal = dynamic_trading_signal(df)
            df_btc = get_candle_data(f&quot;KRW-BTC&quot;)
            signal_btc = dynamic_trading_signal(df_btc)
            print(f&quot;{time.strftime('%Y-%m-%d %H:%M:%S')} - {coin}의 현재 신호: {signal}&quot;)
            while signal == '매수 신호' and signal_btc == '매수 신호':
                orderbook = get_orderbook(api_key, market)
                buy_price = orderbook['bid_price'] + coin_order_unit
                buy_uuid = place_limit_buy_order(api_key,secret_key,market,buy_price,money)
                unfilled_orders = check_unfilled_orders(api_key, secret_key)
                if unfilled_orders:
                    print('지정가 매수 실패, 미체결 주문 취소 작업 중..')
                    for order in unfilled_orders:
                        cancel_result = cancel_unfilled_order(api_key, secret_key, buy_uuid)
                else:
                    current_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
                    my_money_info,total_balance = get_upbit_account_info(api_key,secret_key)
                    add_trade(current_time, f'{coin}', 'buy', buy_price,total_balance)
                    break
                df = get_candle_data(f&quot;KRW-{coin}&quot;)
                signal = dynamic_trading_signal(df)
                status_coin_price = get_current_price(coin)
            if len(get_coin_holdings(api_key,secret_key))&amp;gt;1:
                break
            print('')
        
    else:
        print('----------------------------------------------')
        hold_coin = list(coin_holdings.keys())[1]
        hold_coin_amount = float(coin_holdings[hold_coin])
        status_coin_price = get_current_price(hold_coin)
        coin_order_unit = 0
        market = f'KRW-{hold_coin}'
        if status_coin_price &amp;gt;= 2000000:
            coin_order_unit = 1000
        elif status_coin_price &amp;gt;=1000000 and status_coin_price&amp;lt;2000000:
            coin_order_unit = 500
        elif status_coin_price &amp;gt;=500000 and status_coin_price &amp;lt;1000000:
            coin_order_unit = 100
        elif status_coin_price &amp;gt;=100000 and status_coin_price&amp;lt;500000:
            coin_order_unit = 50
        elif status_coin_price &amp;gt;=10000 and status_coin_price&amp;lt;100000:
            coin_order_unit = 10
        elif status_coin_price &amp;gt;=1000 and status_coin_price&amp;lt;10000:
            coin_order_unit = 1
        elif status_coin_price &amp;gt;=100 and status_coin_price&amp;lt;1000:
            coin_order_unit = 0.1
            if hold_coin in special_coin_list:
                coin_order_unit = 1
        elif status_coin_price &amp;gt;=10 and status_coin_price&amp;lt;100:
            coin_order_unit = 0.01
        elif status_coin_price &amp;gt;= 1 and status_coin_price&amp;lt;10:
            coin_order_unit = 0.001
        elif status_coin_price&amp;gt;=0.1 and status_coin_price&amp;lt;1:
            coin_order_unit = 0.0001
        elif status_coin_price&amp;gt;=0.01 and status_coin_price&amp;lt;0.1:
            coin_order_unit = 0.00001
        elif status_coin_price&amp;gt;=0.001 and status_coin_price&amp;lt;0.01:
            coin_order_unit = 0.000001
        elif status_coin_price&amp;gt;=0.0001 and status_coin_price&amp;lt;0.001 :
            coin_order_unit = 0.0000001
        else :
            coin_order_unit = 0.00000001
        
        
        print('----------------------------------------------')
        print(f'{hold_coin}의 현재 가격 : {status_coin_price}원')
        df = get_candle_data(f&quot;KRW-{hold_coin}&quot;)
        signal = dynamic_trading_signal(df)
        df_btc = get_candle_data('KRW-BTC')
        signal_btc = dynamic_trading_signal(df_btc)
        print(f&quot;{time.strftime('%Y-%m-%d %H:%M:%S')} - {hold_coin}의 현재 신호: {signal}&quot;)
        
        my_money_info,total_balance = get_upbit_account_info(api_key,secret_key)
        while my_money_info[0]['수익률']&amp;lt;=-1.0:
            orderbook = get_orderbook(api_key, market)
            status_profit_ratio = get_upbit_account_info(api_key,secret_key)
            sell_price = orderbook['ask_price']-coin_order_unit
            sell_uuid = place_limit_sell_order(api_key,secret_key,market,sell_price,hold_coin_amount)
            if len(check_unfilled_orders(api_key,secret_key))&amp;gt;0:
                cancel_result = cancel_unfilled_order(api_key,secret_key,sell_uuid)
            else:
                current_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
                my_money_info,total_balance = get_upbit_account_info(api_key,secret_key)
                add_trade(current_time, f'{hold_coin}', 'sell', sell_price,total_balance)
                break
            my_money_info,total_balance = get_upbit_account_info(api_key,secret_key)

        while signal == '매도 신호' or signal_btc == '매도 신호':
            orderbook = get_orderbook(api_key, market)
            market = f'KRW-{hold_coin}'
            sell_price = orderbook['ask_price'] - coin_order_unit
            sell_uuid = place_limit_sell_order(api_key,secret_key,market,sell_price,hold_coin_amount)
            unfilled_orders = check_unfilled_orders(api_key, secret_key)
            if unfilled_orders:
                print('지정가 매도 실패, 미체결 주문 취소 작업 중..')
                for order in unfilled_orders:
                    cancel_result = cancel_unfilled_order(api_key, secret_key, sell_uuid)
            else:
                current_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
                my_money_info,total_balance = get_upbit_account_info(api_key,secret_key)
                add_trade(current_time, f'{hold_coin}', 'sell', sell_price,total_balance)
                break
            print('----------------------------------------------')
            print(f'{hold_coin}의 현재 가격 : {status_coin_price}원')
            df = get_candle_data(f&quot;KRW-{hold_coin}&quot;)
            signal = dynamic_trading_signal(df)
            status_coin_price = get_current_price(hold_coin)

# ONDO,JUP,UXLINK,HBAR,XRP,DOGE
# # 사용 예시
# if __name__ == &quot;__main__&quot;:
#     coin_list = input(&quot;조회할 코인 심볼들을 입력하세요 (쉼표로 구분): &quot;).upper().replace(&quot; &quot;, &quot;&quot;).split(',')
#     while True:
#         for coin in coin_list:
#             signal = generate_signal(f&quot;KRW-{coin}&quot;)
#             print(f&quot;{time.strftime('%Y-%m-%d %H:%M:%S')} - {coin}의 현재 신호: {signal}&quot;)&lt;/code&gt;&lt;/pre&gt;</description>
      <category>혼자 만든 Code/upbit 코인 자동거래</category>
      <author>빛하루</author>
      <guid isPermaLink="true">https://harucode.tistory.com/865</guid>
      <comments>https://harucode.tistory.com/865#entry865comment</comments>
      <pubDate>Tue, 4 Feb 2025 22:17:49 +0900</pubDate>
    </item>
    <item>
      <title>업비트 코인 자동거래 ver 1.41 (매수/매도 기준 변경)</title>
      <link>https://harucode.tistory.com/863</link>
      <description>&lt;pre id=&quot;code_1737302018177&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import pandas as pd
import requests
import talib as ta
import time
import jwt
import uuid
import json
from urllib.parse import urlencode
import pyupbit
import numpy as np
import hashlib
from urllib.parse import urlencode, unquote
import matplotlib.pyplot as plt
import csv
import os
from datetime import datetime
import pytz
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, Dropout
from tensorflow.keras.optimizers import Adam
import tensorflow as tf
import warnings
from sklearn.linear_model import LogisticRegression

# TensorFlow 로그 메시지 숨기기
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3'
tf.get_logger().setLevel('ERROR')
warnings.filterwarnings(&quot;ignore&quot;, message=&quot;X does not have valid feature names&quot;)

server_url = 'https://api.upbit.com'
special_coin_list = ['ADA','ALGO','BLUR','CELO', 'ELF', 'EOS', 'GRS', 'GRT', 'ICX', 'MANA', 'MINA', 'POL', 'SAND', 'SEI', 'STG', 'TRX']

#전일 대비 가격 상승률 상위 n개 종목을 가져오는 함수

def get_top_gainers_upbit(number):
    base_url = &quot;https://api.upbit.com/v1&quot;

    try:
        # Step 1: Get market data
        markets_response = requests.get(f&quot;{base_url}/market/all&quot;)
        markets_response.raise_for_status()
        markets = markets_response.json()

        # Filter for KRW markets only and extract symbols
        krw_symbols = [market['market'].split('-')[1] for market in markets if market['market'].startswith('KRW-')]

        # Step 2: Get ticker data for all KRW markets
        krw_markets = [f&quot;KRW-{symbol}&quot; for symbol in krw_symbols]
        tickers_response = requests.get(f&quot;{base_url}/ticker&quot;, params={&quot;markets&quot;: &quot;,&quot;.join(krw_markets)})
        tickers_response.raise_for_status()
        tickers = tickers_response.json()

        # Step 3: Calculate price change percentages and filter for &amp;gt;3% gainers
        gainers = []
        for ticker in tickers:
            market = ticker['market']
            symbol = market.split('-')[1]
            prev_close = ticker['prev_closing_price']  # 전일 종가
            current_price = ticker['trade_price']     # 현재가

            except_coin = ['USDC','USDT','BTC','ETH','BCH','AAVE','SOL','BSV','AVAX','EGLD']
            except_coin.extend(special_coin_list)
            
            if prev_close &amp;gt; 0 and symbol not in except_coin:  # 유효한 가격만 계산
                change_percent = ((current_price - prev_close) / prev_close) * 100
            
                gainers.append({
                    'symbol': symbol,
                    'change_percent': change_percent,
                    'current_price': current_price
                })

        # Step 4: Sort by price change percentage in descending order
        gainers = sorted(gainers, key=lambda x: x['change_percent'], reverse=True)

        # Step 5: Return top 5 gainers
        return [gainer['symbol'] for gainer in gainers[:number]]

    except requests.exceptions.RequestException as e:
        print(f&quot;Error while fetching data from Upbit API: {e}&quot;)
        return []
    
# JSON 파일에서 API 키 불러오기
def load_api_keys(config_file=&quot;upbit_config.json&quot;):
    try:
        with open(config_file, &quot;r&quot;) as file:
            keys = json.load(file)
            return keys[&quot;api_key&quot;], keys[&quot;secret_key&quot;]
    except Exception as e:
        print(f&quot;Error loading API keys: {e}&quot;)
        return None, None
    
# 현재 업비트 계좌 정보를 불러오는 함수
def get_upbit_account_info(api_key, secret_key):
    base_url = &quot;https://api.upbit.com/v1&quot;

    # JWT 토큰 생성
    payload = {
        'access_key': api_key,
        'nonce': str(uuid.uuid4()),
    }
    jwt_token = jwt.encode(payload, secret_key, algorithm=&quot;HS256&quot;)
    authorization_token = f&quot;Bearer {jwt_token}&quot;
    
    headers = {
        &quot;Authorization&quot;: authorization_token,
    }

    # 계좌 조회 API 호출
    try:
        account_res = requests.get(f&quot;{base_url}/accounts&quot;, headers=headers)
        account_data = account_res.json()
        if account_res.status_code != 200:
            print(f&quot;Error fetching account data: {account_data}&quot;)
            return

        # 코인별 정보 조회
        market_data = requests.get(f&quot;{base_url}/market/all&quot;).json()
        market_dict = {item['market']: item['korean_name'] for item in market_data}

        total_balance = 0
        coin_info_list = []

        for account in account_data:
            if float(account['balance']) &amp;gt; 0:  # 잔액이 0보다 클 경우
                ticker = account['currency']
                if ticker == &quot;KRW&quot;:
                    total_balance += float(account['balance'])
                    continue
                
                market = f&quot;KRW-{ticker}&quot;
                avg_buy_price = float(account['avg_buy_price'])
                balance = float(account['balance'])

                # 현재 가격 조회
                ticker_url = f&quot;{base_url}/ticker?markets={market}&quot;
                ticker_res = requests.get(ticker_url).json()
                current_price = float(ticker_res[0]['trade_price'])
                
                # 평가 금액과 수익률 계산
                eval_amount = balance * current_price
                profit_rate = ((current_price - avg_buy_price) / avg_buy_price) * 100
                
                # 총 자산에 반영
                total_balance += eval_amount
                
                coin_info_list.append({
                    &quot;코인&quot;: market_dict.get(market, ticker),
                    &quot;보유수량&quot;: balance,
                    &quot;평균단가&quot;: avg_buy_price,
                    &quot;현재가격&quot;: current_price,
                    &quot;수익률&quot;: profit_rate,
                })

        # 출력
        print(f&quot;총 자산: {total_balance:,.0f} KRW&quot;)
        print('')
        print(&quot;보유 코인 정보:&quot;)
        print(coin_info_list)
        return coin_info_list,total_balance
    except Exception as e:
        print(f&quot;An error occurred: {e}&quot;)

# 보유중인 코인의 심볼과 수량을 가져오는 함수
def get_coin_holdings(api_key, secret_key):
    &quot;&quot;&quot;
    업비트 API를 사용하여 보유 중인 코인의 종류와 수량을 가져옵니다.

    Returns:
        dict: 코인의 종류와 보유 수량을 포함한 딕셔너리
    &quot;&quot;&quot;
    base_url = &quot;https://api.upbit.com/v1/accounts&quot;

    # JWT 토큰 생성
    payload = {
        'access_key': api_key,
        'nonce': str(uuid.uuid4()),
    }
    jwt_token = jwt.encode(payload, secret_key, algorithm=&quot;HS256&quot;)
    headers = {&quot;Authorization&quot;: f&quot;Bearer {jwt_token}&quot;}

    try:
        # 계좌 정보 요청
        response = requests.get(base_url, headers=headers)
        response.raise_for_status()
        accounts = response.json()

        # 보유 코인 정보 필터링
        holdings = {account['currency']: float(account['balance']) for account in accounts if float(account['balance']) &amp;gt; 0}

        # 보유 코인이 없을 경우 처리
        if not holdings:
            print(&quot;보유 중인 코인이 없습니다.&quot;)
            return {}

        return holdings

    except Exception as e:
        print(f&quot;Error fetching coin holdings: {e}&quot;)
        return {}

#미체결된 주문 조회 함수
def check_unfilled_orders(api_key, secret_key):
    &quot;&quot;&quot;
    미체결 주문이 있는지 확인합니다.

    Returns:
        list: 미체결 주문 리스트 (없으면 빈 리스트)
    &quot;&quot;&quot;
    base_url = &quot;https://api.upbit.com/v1/orders&quot;
    query = {
        'state': 'wait',  # 'wait' 상태는 미체결 주문을 의미
    }

    # Query Hash 계산
    query_string = '&amp;amp;'.join([f&quot;{key}={value}&quot; for key, value in query.items()])
    query_hash = hashlib.sha512(query_string.encode()).hexdigest()

    # JWT 토큰 생성
    payload = {
        'access_key': api_key,
        'nonce': str(uuid.uuid4()),
        'query_hash': query_hash,
        'query_hash_alg': 'SHA512',
    }
    jwt_token = jwt.encode(payload, secret_key, algorithm=&quot;HS256&quot;)
    headers = {&quot;Authorization&quot;: f&quot;Bearer {jwt_token}&quot;}

    try:
        # 미체결 주문 요청
        response = requests.get(base_url, headers=headers, params=query)
        response.raise_for_status()
        orders = response.json()

        # 미체결 주문이 없는 경우 처리
        if not orders:
            print(&quot;미체결 주문이 없습니다.&quot;)
            return []

        print(&quot;미체결 주문이 있습니다.&quot;)
        return orders

    except Exception as e:
        print(f&quot;Error fetching unfilled orders: {e}&quot;)
        return []
    
#미체결된 주문 취소하는 함수
def cancel_unfilled_order(api_key, secret_key, order_id):
    &quot;&quot;&quot;
    특정 미체결 주문을 취소합니다.

    Args:
        api_key (str): 업비트 API 키
        secret_key (str): 업비트 Secret 키
        order_id (str): 취소할 주문의 UUID

    Returns:
        dict: 주문 취소 결과
    &quot;&quot;&quot;
    base_url = &quot;https://api.upbit.com/v1/order&quot;
    query = {
        'uuid': order_id,
    }

    # JWT 토큰 생성
    query_string = '&amp;amp;'.join([f&quot;{key}={value}&quot; for key, value in query.items()])
    payload = {
        'access_key': api_key,
        'nonce': str(uuid.uuid4()),
        'query_hash': hashlib.sha512(query_string.encode()).hexdigest(),
        'query_hash_alg': 'SHA512',
    }
    jwt_token = jwt.encode(payload, secret_key, algorithm=&quot;HS256&quot;)
    headers = {&quot;Authorization&quot;: f&quot;Bearer {jwt_token}&quot;}

    try:
        # 주문 취소 요청
        response = requests.delete(base_url, headers=headers, params=query)
        response.raise_for_status()
        cancel_result = response.json()

        print(f&quot;주문이 성공적으로 취소되었습니다: {cancel_result}&quot;)
        return cancel_result

    except requests.exceptions.HTTPError as e:
        print(f&quot;HTTP 에러 발생: {e.response.status_code}, {e.response.text}&quot;)
        return {}
    except Exception as e:
        print(f&quot;Error canceling order: {e}&quot;)
        return {}
    
#매수 1호가와 매도1호가 리턴하는 함수    
def get_orderbook(api_key, market):
    &quot;&quot;&quot;
    주어진 마켓의 매수, 매도 1호가를 가져옵니다.

    Args:
        api_key (str): 업비트 API 키
        market (str): 마켓 코드 (예: &quot;KRW-BTC&quot;)

    Returns:
        dict: 매수 1호가와 매도 1호가를 포함한 딕셔너리
    &quot;&quot;&quot;
    url = f&quot;https://api.upbit.com/v1/orderbook?markets={market}&amp;amp;level=0&quot;

    headers = {&quot;accept&quot;: &quot;application/json&quot;}

    try:
        # 주문서 정보 요청
        response = requests.get(url, headers=headers)
        response.raise_for_status()
        orderbook = response.json()

        if not orderbook:
            print(f&quot;{market}의 주문서 정보가 없습니다.&quot;)
            return None

        # 매수 1호가 (가장 높은 매도 호가)
        bid_price = orderbook[0]['orderbook_units'][0]['bid_price']
        # 매도 1호가 (가장 낮은 매수 호가)
        ask_price = orderbook[0]['orderbook_units'][0]['ask_price']

        return {&quot;bid_price&quot;: bid_price, &quot;ask_price&quot;: ask_price}

    except Exception as e:
        print(f&quot;Error fetching orderbook: {e}&quot;)
        return None
    
#지정가 매수함수    
def place_limit_buy_order(api_key, secret_key, market, price, budget):
    max_quantity = str(budget / price)
    params = {
        'market': market,
        'side': 'bid',
        'ord_type': 'limit',
        'price': price,
        'volume': max_quantity
    }
    query_string = unquote(urlencode(params, doseq=True)).encode(&quot;utf-8&quot;)

    m = hashlib.sha512()
    m.update(query_string)
    query_hash = m.hexdigest()

    payload = {
        'access_key': api_key,
        'nonce': str(uuid.uuid4()),
        'query_hash': query_hash,
        'query_hash_alg': 'SHA512',
    }

    jwt_token = jwt.encode(payload, secret_key)
    authorization = 'Bearer {}'.format(jwt_token)
    headers = {
    'Authorization': authorization,
    }

    res = requests.post(server_url + '/v1/orders', json=params, headers=headers)
    order_result = res.json()
    # 성공적으로 주문을 제출했다면 주문 UUID 반환
    order_uuid = order_result.get('uuid')
    if order_uuid:
        print(f&quot;매수 주문이 성공적으로 생성되었습니다. 주문 UUID: {order_uuid}&quot;)
        time.sleep(5)
        return order_uuid
    else:
        print(&quot;주문 생성 실패: UUID를 찾을 수 없습니다.&quot;)
        return None
    
# 지정가 매도함수
def place_limit_sell_order(api_key, secret_key, market, price, quantity):
    max_quantity = str(quantity)
    params = {
        'market': market,
        'side': 'ask',
        'ord_type': 'limit',
        'price': price,
        'volume': max_quantity
    }
    query_string = unquote(urlencode(params, doseq=True)).encode(&quot;utf-8&quot;)

    m = hashlib.sha512()
    m.update(query_string)
    query_hash = m.hexdigest()

    payload = {
        'access_key': api_key,
        'nonce': str(uuid.uuid4()),
        'query_hash': query_hash,
        'query_hash_alg': 'SHA512',
    }

    jwt_token = jwt.encode(payload, secret_key)
    authorization = 'Bearer {}'.format(jwt_token)
    headers = {
    'Authorization': authorization,
    }

    res = requests.post(server_url + '/v1/orders', json=params, headers=headers)
    order_result = res.json()
    # 성공적으로 주문을 제출했다면 주문 UUID 반환
    order_uuid = order_result.get('uuid')
    if order_uuid:
        print(f&quot;매도 주문이 성공적으로 생성되었습니다. 주문 UUID: {order_uuid}&quot;)
        time.sleep(5)
        return order_uuid
    else:
        print(&quot;주문 생성 실패: UUID를 찾을 수 없습니다.&quot;)
        return None
    
#코인의 현재가격을 불러오는 함수
def get_current_price(coin):
    url = f&quot;https://api.upbit.com/v1/ticker&quot;
    params = {
        'markets': f'KRW-{coin}'  # 코인 이름 입력 (예: 'BTC' -&amp;gt; 'KRW-BTC')
    }
    
    response = requests.get(url, params=params)
    data = response.json()
    
    if len(data) &amp;gt; 0:
        return data[0]['trade_price']  # 현재 거래 가격 반환
    else:
        return None
    
#코인의 캔들데이터를 가져오는 함수
def get_coin_data(coin):
    url = &quot;https://api.upbit.com/v1/candles/minutes/1&quot;  # Adjusted to minutes/1 endpoint for real data
    params = {  
        'market': f'KRW-{coin}',  
        'count': 200,
    }  
    headers = {&quot;accept&quot;: &quot;application/json&quot;}

    response = requests.get(url, params=params, headers=headers)
    data = response.json()
    df = pd.DataFrame(data)
    df = df.reindex(index=df.index[::-1]).reset_index(drop=True)  # Reset index after reversing
    return df

#받아온 코인 데이터를 저장할 csv파일을 만드는 함수수
def save_to_csv(df):
    filename = 'coin_data.csv'
    df.to_csv(filename, index=False)

#거래기록을 저장할 csv 파일일
columns = ['시간', '코인명', '매수/매도', '평균 단가','현재 보유잔고','거래 당시 상승확률']
df_trade = pd.DataFrame(columns=columns)
df_trade.to_csv('trade_list.csv', index=False, encoding='utf-8-sig')

#거래기록을 추가하는 함수
def add_trade(trade_time, coin_name, trade_type, avg_price,hold_won,up_prob):
    # CSV 파일 읽기
    df = pd.read_csv('trade_list.csv', encoding='utf-8-sig')
    
    # 새로운 데이터 추가
    new_data = {
        '시간': trade_time,
        '코인명': coin_name,
        '매수/매도': trade_type,
        '평균 단가': avg_price,
        '현재 보유잔고':hold_won,
        '거래 당시 상승확률':up_prob,
    }
    df = df.append(new_data, ignore_index=True)
    
    # 업데이트된 데이터프레임을 CSV 파일로 저장
    df.to_csv('trade_list.csv', index=False, encoding='utf-8-sig')

#현재 보유중인 원화 잔고를 가져오는 함수
def get_balance():
    &quot;&quot;&quot;
    업비트 API를 사용하여 원화 잔고를 조회하는 함수
    &quot;&quot;&quot;
    url = server_url + '/v1/accounts'
    
    # 요청 헤더 준비
    payload = {
        'access_key': api_key,
        'nonce': str(uuid.uuid4())
    }
    
    # 서명 생성
    m = hashlib.sha512()
    m.update(payload['nonce'].encode('utf-8'))
    signature = jwt.encode(payload, secret_key, algorithm='HS256')
    
    headers = {
        'Authorization': f'Bearer {signature}'
    }
    
    # API 요청
    response = requests.get(url, headers=headers)
    
    if response.status_code == 200:
        accounts = response.json()
        for account in accounts:
            if account['currency'] == 'KRW':  # 원화 잔고 찾기
                return float(account['balance'])
    else:
        print(f&quot;Error: {response.status_code}&quot;)
        return None
    
#각종 지표를 계산하는 함수
def calculate_indicators(df):
    # 'trade_price' (종가), 'high_price', 'low_price' 컬럼 확인 및 추출
    required_columns = ['trade_price', 'high_price', 'low_price','candle_acc_trade_volume']
    for col in required_columns:
        if col not in df.columns:
            raise ValueError(f&quot;데이터에 '{col}' 컬럼이 없습니다.&quot;)
    
    close_prices = df['trade_price'].astype(float)
    high_prices = df['high_price'].astype(float)
    low_prices = df['low_price'].astype(float)
    volumes = df['candle_acc_trade_volume'].astype(float)

    # 데이터 확인
    if close_prices.isnull().any():
        raise ValueError(&quot;종가 데이터에 결측값이 있습니다.&quot;)
    if len(close_prices) &amp;lt; 10:  # Momentum 계산에 필요한 최소 데이터 길이
        raise ValueError(&quot;Momentum 지표를 계산하려면 데이터 길이가 최소 10 이상이어야 합니다.&quot;)
    
    # RSI (Relative Strength Index)
    df['RSI'] = ta.RSI(close_prices, timeperiod=14)
    rsi = df['RSI'].iloc[-1]

    # Stochastic RSI
    df['StochRSI'] = (df['RSI'] - df['RSI'].rolling(14).min()) / (df['RSI'].rolling(14).max() - df['RSI'].rolling(14).min()) * 100
    df['StochRSI_K'] = df['StochRSI'].rolling(3).mean()
    df['StochRSI_D'] = df['StochRSI_K'].rolling(3).mean()
    stoch_rsi_d = df['StochRSI_D'].iloc[-1]
    stoch_rsi_k = df['StochRSI_K'].iloc[-1]
    
    # Bollinger Bands
    df['Upper_Band'], df['Middle_Band'], df['Lower_Band'] = ta.BBANDS(
        close_prices,
        timeperiod=20,
        nbdevup=2.0,
        nbdevdn=2.0,
        matype=0
    )
    upper_band = df['Upper_Band'].iloc[-1]
    middle_band = df['Middle_Band'].iloc[-1]
    lower_band = df['Lower_Band'].iloc[-1]
    
    # MACD (Moving Average Convergence Divergence)
    df['MACD'], df['MACD_Signal'], df['MACD_Hist'] = ta.MACD(
        close_prices,
        fastperiod=12,
        slowperiod=26,
        signalperiod=9
    )
    macd = df['MACD'].iloc[-1]
    macd_signal = df['MACD_Signal'].iloc[-1]
    macd_hist = df['MACD_Hist'].iloc[-1]
    
    # TRIX (Triple Exponential Average)
    df['TRIX'] = ta.TRIX(
        close_prices,
        timeperiod=5
    )
    trix = df['TRIX'].iloc[-1]

    df['MFI'] = ta.MFI(
        high=high_prices,
        low=low_prices,
        close=close_prices,
        volume=volumes,
        timeperiod=14
    )
    mfi = df['MFI'].iloc[-1]

    df['Tenkan_sen'] = (high_prices.rolling(window=9).max() + low_prices.rolling(window=9).min()) / 2
    df['Kijun_sen'] = (high_prices.rolling(window=26).max() + low_prices.rolling(window=26).min()) / 2
    tenkan_sen = df['Tenkan_sen'].iloc[-1]  # 전환선 최신 값
    kijun_sen = df['Kijun_sen'].iloc[-1]    # 기준선 최신 값

    # Save data to CSV
    save_to_csv(df)
    
    # Return all indicator values
    return (
        upper_band, middle_band, lower_band, macd, macd_signal, macd_hist, trix, mfi, rsi, stoch_rsi_d,stoch_rsi_k,tenkan_sen,kijun_sen
    )

def analyze_correlations(df, feature_columns):
    &quot;&quot;&quot;
    지표 간 상관관계를 분석하여 중요도를 계산.
    &quot;&quot;&quot;
    correlation_matrix = df[feature_columns].corr()  # 상관행렬 계산
    mean_correlations = correlation_matrix.mean()  # 각 지표의 평균 상관관계 계산
    weights = mean_correlations / mean_correlations.sum()  # 가중치로 정규화
    return weights

def prepare_weighted_data(df, feature_columns, weights):
    &quot;&quot;&quot;
    지표 데이터에 가중치를 적용하여 학습 데이터를 준비.
    &quot;&quot;&quot;
    weighted_features = df[feature_columns] * weights.values  # 가중치 적용
    return weighted_features

def calculate_price_probabilities_with_weights(df, iterations=5):
    &quot;&quot;&quot;
    지표 간 상관관계를 반영하여 가중치를 적용한 학습 및 다회 반복 예측.
    &quot;&quot;&quot;
    # 필요한 컬럼 정의
    feature_columns = [
        'RSI', 'StochRSI_D', 'StochRSI_K', 'Upper_Band', 'Middle_Band', 'Lower_Band',
        'MACD', 'MACD_Signal', 'MACD_Hist', 'TRIX', 'MFI', 'Tenkan_sen', 'Kijun_sen',
        'high_price', 'low_price', 'trade_price', 'candle_acc_trade_volume'
    ]

    # 다음 가격 변동 계산 (1: 상승, 0: 하락 또는 동일)
    df['Next_Price'] = df['trade_price'].shift(-1)  # 다음 가격
    df['Price_Change'] = (df['Next_Price'] &amp;gt; df['trade_price']).astype(int)

    # 결측값 제거
    df = df.dropna(subset=feature_columns + ['Price_Change'])

    # 상관관계 기반 가중치 계산
    weights = analyze_correlations(df, feature_columns)

    # 가중치가 적용된 학습 데이터 준비
    X = prepare_weighted_data(df, feature_columns, weights)
    y = df['Price_Change']

    # 데이터 스케일링
    scaler = StandardScaler()
    X_scaled = scaler.fit_transform(X)

    # 로지스틱 회귀 모델 정의
    model = LogisticRegression(max_iter=2000)
    model.fit(X_scaled, y)

    # 가장 최근 행의 지표값 가져오기
    latest_data = prepare_weighted_data(df.iloc[-1:], feature_columns, weights)
    latest_data_scaled = scaler.transform(latest_data)

    # 예측 반복 및 확률 평균 계산
    probabilities = []
    for _ in range(iterations):
        prob = model.predict_proba(latest_data_scaled)[0]
        probabilities.append(prob)

    # 평균 확률 계산
    avg_probabilities = np.mean(probabilities, axis=0)

    return {
        'down_prob': avg_probabilities[0],  # 하락 확률
        'up_prob': avg_probabilities[1]    # 상승 확률
    }


api_key, secret_key = load_api_keys(&quot;upbit_config.json&quot;)
coin_list = input(&quot;조회할 코인 심볼들을 입력하세요 (쉼표로 구분): &quot;).upper().replace(&quot; &quot;, &quot;&quot;).split(',')

while True:
    coin_holdings = get_coin_holdings(api_key, secret_key)
    money = int(get_balance() * 0.95)
    if len(coin_holdings)==1:
        # coin_list = get_top_gainers_upbit(5)
        for coin in coin_list:
            market = f'KRW-{coin}'
            status_coin_price = get_current_price(coin)
            coin_order_unit = 0
            if status_coin_price &amp;gt;= 2000000:
                coin_order_unit = 1000
            elif status_coin_price &amp;gt;=1000000 and status_coin_price&amp;lt;2000000:
                coin_order_unit = 500
            elif status_coin_price &amp;gt;=500000 and status_coin_price &amp;lt;1000000:
                coin_order_unit = 100
            elif status_coin_price &amp;gt;=100000 and status_coin_price&amp;lt;500000:
                coin_order_unit = 50
            elif status_coin_price &amp;gt;=10000 and status_coin_price&amp;lt;100000:
                coin_order_unit = 10
            elif status_coin_price &amp;gt;=1000 and status_coin_price&amp;lt;10000:
                coin_order_unit = 1
            elif status_coin_price &amp;gt;=100 and status_coin_price&amp;lt;1000:
                coin_order_unit = 0.1
                if coin in special_coin_list:
                    coin_order_unit = 1
            elif status_coin_price &amp;gt;=10 and status_coin_price&amp;lt;100:
                coin_order_unit = 0.01
            elif status_coin_price &amp;gt;= 1 and status_coin_price&amp;lt;10:
                coin_order_unit = 0.001
            elif status_coin_price&amp;gt;=0.1 and status_coin_price&amp;lt;1:
                coin_order_unit = 0.0001
            elif status_coin_price&amp;gt;=0.01 and status_coin_price&amp;lt;0.1:
                coin_order_unit = 0.00001
            elif status_coin_price&amp;gt;=0.001 and status_coin_price&amp;lt;0.01:
                coin_order_unit = 0.000001
            elif status_coin_price&amp;gt;=0.0001 and status_coin_price&amp;lt;0.001 :
                coin_order_unit = 0.0000001
            else :
                coin_order_unit = 0.00000001
            df = get_coin_data(coin)  # 데이터 가져오기
            # df = df.iloc[:-1]
            indicators = calculate_indicators(df)  # 지표 계산
            prediction = calculate_price_probabilities_with_weights(df,5)  # 상승/하락 확률 계산
            
            print('----------------------------------------------')
            print(f'{coin}의 현재 가격 : {status_coin_price}원')
            print('')
            print(f'현재 코인 rsi : {indicators[8]:.2f}')
            print(f'현재 코인의 stoch_rsi : {indicators[9]:.2f}')
            print(f'현재 {coin}코인의 상승확률 : {prediction[&quot;up_prob&quot;]*100:.3f}%')

            while prediction['up_prob']&amp;gt;0.6 and (indicators[9] &amp;lt;indicators[10] and indicators[9]&amp;lt;30): #and indicators[8]&amp;lt;60 and indicators[9]&amp;lt;15 :
                orderbook = get_orderbook(api_key, market)
                buy_price = orderbook['bid_price'] + coin_order_unit
                buy_uuid = place_limit_buy_order(api_key,secret_key,market,buy_price,money)
                unfilled_orders = check_unfilled_orders(api_key, secret_key)
                if unfilled_orders:
                    print('지정가 매수 실패, 미체결 주문 취소 작업 중..')
                    for order in unfilled_orders:
                        cancel_result = cancel_unfilled_order(api_key, secret_key, buy_uuid)
                else:
                    current_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
                    my_money_info,total_balance = get_upbit_account_info(api_key,secret_key)
                    add_trade(current_time, f'{coin}', 'buy', buy_price,total_balance,prediction['up_prob'])
                    break
                df = get_coin_data(coin)  # 데이터 가져오기
                # df = df.iloc[:-1]
                indicators = calculate_indicators(df)  # 지표 계산
                prediction = calculate_price_probabilities_with_weights(df)  # 상승/하락 확률 계산
            if len(get_coin_holdings(api_key,secret_key))&amp;gt;1:
                break
            print('')
        
    else:
        print('----------------------------------------------')
        hold_coin = list(coin_holdings.keys())[1]
        hold_coin_amount = float(coin_holdings[hold_coin])
        status_coin_price = get_current_price(hold_coin)
        coin_order_unit = 0
        market = f'KRW-{hold_coin}'
        if status_coin_price &amp;gt;= 2000000:
            coin_order_unit = 1000
        elif status_coin_price &amp;gt;=1000000 and status_coin_price&amp;lt;2000000:
            coin_order_unit = 500
        elif status_coin_price &amp;gt;=500000 and status_coin_price &amp;lt;1000000:
            coin_order_unit = 100
        elif status_coin_price &amp;gt;=100000 and status_coin_price&amp;lt;500000:
            coin_order_unit = 50
        elif status_coin_price &amp;gt;=10000 and status_coin_price&amp;lt;100000:
            coin_order_unit = 10
        elif status_coin_price &amp;gt;=1000 and status_coin_price&amp;lt;10000:
            coin_order_unit = 1
        elif status_coin_price &amp;gt;=100 and status_coin_price&amp;lt;1000:
            coin_order_unit = 0.1
            if hold_coin in special_coin_list:
                coin_order_unit = 1
        elif status_coin_price &amp;gt;=10 and status_coin_price&amp;lt;100:
            coin_order_unit = 0.01
        elif status_coin_price &amp;gt;= 1 and status_coin_price&amp;lt;10:
            coin_order_unit = 0.001
        elif status_coin_price&amp;gt;=0.1 and status_coin_price&amp;lt;1:
            coin_order_unit = 0.0001
        elif status_coin_price&amp;gt;=0.01 and status_coin_price&amp;lt;0.1:
            coin_order_unit = 0.00001
        elif status_coin_price&amp;gt;=0.001 and status_coin_price&amp;lt;0.01:
            coin_order_unit = 0.000001
        elif status_coin_price&amp;gt;=0.0001 and status_coin_price&amp;lt;0.001 :
            coin_order_unit = 0.0000001
        else :
            coin_order_unit = 0.00000001
        df = get_coin_data(hold_coin)  # 데이터 가져오기
        # df = df.iloc[:-1]
        indicators = calculate_indicators(df)  # 지표 계산
        prediction = calculate_price_probabilities_with_weights(df)  # 상승/하락 확률 계산
    
        print(f'{hold_coin}의 현재 가격 : {status_coin_price}원')
        print('')
        print(f'현재 코인 rsi : {indicators[8]:.2f}')
        print(f'현재 코인의 stoch_rsi : {indicators[9]:.2f}')
        print(f'현재 코인의 상승확률 : {prediction[&quot;up_prob&quot;]*100:.3f}%')
        my_money_info,total_balance = get_upbit_account_info(api_key,secret_key)
        while my_money_info[0]['수익률']&amp;lt;=-2.0:
            orderbook = get_orderbook(api_key, market)
            status_profit_ratio = get_upbit_account_info(api_key,secret_key)
            sell_price = orderbook['ask_price']-coin_order_unit
            sell_uuid = place_limit_sell_order(api_key,secret_key,market,sell_price,hold_coin_amount)
            if len(check_unfilled_orders(api_key,secret_key))&amp;gt;0:
                cancel_result = cancel_unfilled_order(api_key,secret_key,sell_uuid)
            else:
                current_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
                my_money_info,total_balance = get_upbit_account_info(api_key,secret_key)
                add_trade(current_time, f'{hold_coin}', 'sell', sell_price,total_balance,prediction['up_prob'])
                break
            my_money_info,total_balance = get_upbit_account_info(api_key,secret_key)

        while prediction['down_prob']&amp;gt;=0.55 or (indicators[9]&amp;gt;indicators[10] and indicators[9]&amp;gt;=80): #or indicators[8] &amp;gt;80 or indicators[10]&amp;gt;80:
            orderbook = get_orderbook(api_key, market)
            market = f'KRW-{hold_coin}'
            sell_price = orderbook['ask_price'] - coin_order_unit
            sell_uuid = place_limit_sell_order(api_key,secret_key,market,sell_price,hold_coin_amount)
            unfilled_orders = check_unfilled_orders(api_key, secret_key)
            if unfilled_orders:
                print('지정가 매도 실패, 미체결 주문 취소 작업 중..')
                for order in unfilled_orders:
                    cancel_result = cancel_unfilled_order(api_key, secret_key, sell_uuid)
            else:
                current_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
                my_money_info,total_balance = get_upbit_account_info(api_key,secret_key)
                add_trade(current_time, f'{hold_coin}', 'sell', sell_price,total_balance,prediction['up_prob'])
                break
            df = get_coin_data(hold_coin)  # 데이터 가져오기
            # df = df.iloc[:-1]
            indicators = calculate_indicators(df)  # 지표 계산
            prediction = calculate_price_probabilities_with_weights(df)  # 상승/하락 확률 계산

# ONDO,JUP,UXLINK,HBAR,SBD,XLM,BONK,AGLD,GAME2,SHIB,PEPE,BTG&lt;/code&gt;&lt;/pre&gt;</description>
      <category>혼자 만든 Code/upbit 코인 자동거래</category>
      <author>빛하루</author>
      <guid isPermaLink="true">https://harucode.tistory.com/863</guid>
      <comments>https://harucode.tistory.com/863#entry863comment</comments>
      <pubDate>Mon, 20 Jan 2025 00:53:42 +0900</pubDate>
    </item>
    <item>
      <title>업비트 코인 자동거래 ver 1.4(가격 상승확률 계산 방식 변경)</title>
      <link>https://harucode.tistory.com/862</link>
      <description>&lt;pre id=&quot;code_1737217701674&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import pandas as pd
import requests
import talib as ta
import time
import jwt
import uuid
import json
from urllib.parse import urlencode
import pyupbit
import numpy as np
import hashlib
from urllib.parse import urlencode, unquote
import matplotlib.pyplot as plt
import csv
import os
from datetime import datetime
import pytz
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, Dropout
from tensorflow.keras.optimizers import Adam
import tensorflow as tf
import warnings
from sklearn.linear_model import LogisticRegression

# TensorFlow 로그 메시지 숨기기
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3'
tf.get_logger().setLevel('ERROR')
warnings.filterwarnings(&quot;ignore&quot;, message=&quot;X does not have valid feature names&quot;)

server_url = 'https://api.upbit.com'
special_coin_list = ['ADA','ALGO','BLUR','CELO', 'ELF', 'EOS', 'GRS', 'GRT', 'ICX', 'MANA', 'MINA', 'POL', 'SAND', 'SEI', 'STG', 'TRX']

#전일 대비 가격 상승률 상위 n개 종목을 가져오는 함수

def get_top_gainers_upbit(number):
    base_url = &quot;https://api.upbit.com/v1&quot;

    try:
        # Step 1: Get market data
        markets_response = requests.get(f&quot;{base_url}/market/all&quot;)
        markets_response.raise_for_status()
        markets = markets_response.json()

        # Filter for KRW markets only and extract symbols
        krw_symbols = [market['market'].split('-')[1] for market in markets if market['market'].startswith('KRW-')]

        # Step 2: Get ticker data for all KRW markets
        krw_markets = [f&quot;KRW-{symbol}&quot; for symbol in krw_symbols]
        tickers_response = requests.get(f&quot;{base_url}/ticker&quot;, params={&quot;markets&quot;: &quot;,&quot;.join(krw_markets)})
        tickers_response.raise_for_status()
        tickers = tickers_response.json()

        # Step 3: Calculate price change percentages and filter for &amp;gt;3% gainers
        gainers = []
        for ticker in tickers:
            market = ticker['market']
            symbol = market.split('-')[1]
            prev_close = ticker['prev_closing_price']  # 전일 종가
            current_price = ticker['trade_price']     # 현재가

            except_coin = ['USDC','USDT','BTC','ETH','BCH','AAVE','SOL','BSV','AVAX','EGLD']
            except_coin.extend(special_coin_list)
            
            if prev_close &amp;gt; 0 and symbol not in except_coin:  # 유효한 가격만 계산
                change_percent = ((current_price - prev_close) / prev_close) * 100
            
                gainers.append({
                    'symbol': symbol,
                    'change_percent': change_percent,
                    'current_price': current_price
                })

        # Step 4: Sort by price change percentage in descending order
        gainers = sorted(gainers, key=lambda x: x['change_percent'], reverse=True)

        # Step 5: Return top 5 gainers
        return [gainer['symbol'] for gainer in gainers[:number]]

    except requests.exceptions.RequestException as e:
        print(f&quot;Error while fetching data from Upbit API: {e}&quot;)
        return []
    
# JSON 파일에서 API 키 불러오기
def load_api_keys(config_file=&quot;upbit_config.json&quot;):
    try:
        with open(config_file, &quot;r&quot;) as file:
            keys = json.load(file)
            return keys[&quot;api_key&quot;], keys[&quot;secret_key&quot;]
    except Exception as e:
        print(f&quot;Error loading API keys: {e}&quot;)
        return None, None
    
# 현재 업비트 계좌 정보를 불러오는 함수
def get_upbit_account_info(api_key, secret_key):
    base_url = &quot;https://api.upbit.com/v1&quot;

    # JWT 토큰 생성
    payload = {
        'access_key': api_key,
        'nonce': str(uuid.uuid4()),
    }
    jwt_token = jwt.encode(payload, secret_key, algorithm=&quot;HS256&quot;)
    authorization_token = f&quot;Bearer {jwt_token}&quot;
    
    headers = {
        &quot;Authorization&quot;: authorization_token,
    }

    # 계좌 조회 API 호출
    try:
        account_res = requests.get(f&quot;{base_url}/accounts&quot;, headers=headers)
        account_data = account_res.json()
        if account_res.status_code != 200:
            print(f&quot;Error fetching account data: {account_data}&quot;)
            return

        # 코인별 정보 조회
        market_data = requests.get(f&quot;{base_url}/market/all&quot;).json()
        market_dict = {item['market']: item['korean_name'] for item in market_data}

        total_balance = 0
        coin_info_list = []

        for account in account_data:
            if float(account['balance']) &amp;gt; 0:  # 잔액이 0보다 클 경우
                ticker = account['currency']
                if ticker == &quot;KRW&quot;:
                    total_balance += float(account['balance'])
                    continue
                
                market = f&quot;KRW-{ticker}&quot;
                avg_buy_price = float(account['avg_buy_price'])
                balance = float(account['balance'])

                # 현재 가격 조회
                ticker_url = f&quot;{base_url}/ticker?markets={market}&quot;
                ticker_res = requests.get(ticker_url).json()
                current_price = float(ticker_res[0]['trade_price'])
                
                # 평가 금액과 수익률 계산
                eval_amount = balance * current_price
                profit_rate = ((current_price - avg_buy_price) / avg_buy_price) * 100
                
                # 총 자산에 반영
                total_balance += eval_amount
                
                coin_info_list.append({
                    &quot;코인&quot;: market_dict.get(market, ticker),
                    &quot;보유수량&quot;: balance,
                    &quot;평균단가&quot;: avg_buy_price,
                    &quot;현재가격&quot;: current_price,
                    &quot;수익률&quot;: profit_rate,
                })

        # 출력
        print(f&quot;총 자산: {total_balance:,.0f} KRW&quot;)
        print('')
        print(&quot;보유 코인 정보:&quot;)
        print(coin_info_list)
        return coin_info_list,total_balance
    except Exception as e:
        print(f&quot;An error occurred: {e}&quot;)

# 보유중인 코인의 심볼과 수량을 가져오는 함수
def get_coin_holdings(api_key, secret_key):
    &quot;&quot;&quot;
    업비트 API를 사용하여 보유 중인 코인의 종류와 수량을 가져옵니다.

    Returns:
        dict: 코인의 종류와 보유 수량을 포함한 딕셔너리
    &quot;&quot;&quot;
    base_url = &quot;https://api.upbit.com/v1/accounts&quot;

    # JWT 토큰 생성
    payload = {
        'access_key': api_key,
        'nonce': str(uuid.uuid4()),
    }
    jwt_token = jwt.encode(payload, secret_key, algorithm=&quot;HS256&quot;)
    headers = {&quot;Authorization&quot;: f&quot;Bearer {jwt_token}&quot;}

    try:
        # 계좌 정보 요청
        response = requests.get(base_url, headers=headers)
        response.raise_for_status()
        accounts = response.json()

        # 보유 코인 정보 필터링
        holdings = {account['currency']: float(account['balance']) for account in accounts if float(account['balance']) &amp;gt; 0}

        # 보유 코인이 없을 경우 처리
        if not holdings:
            print(&quot;보유 중인 코인이 없습니다.&quot;)
            return {}

        return holdings

    except Exception as e:
        print(f&quot;Error fetching coin holdings: {e}&quot;)
        return {}

#미체결된 주문 조회 함수
def check_unfilled_orders(api_key, secret_key):
    &quot;&quot;&quot;
    미체결 주문이 있는지 확인합니다.

    Returns:
        list: 미체결 주문 리스트 (없으면 빈 리스트)
    &quot;&quot;&quot;
    base_url = &quot;https://api.upbit.com/v1/orders&quot;
    query = {
        'state': 'wait',  # 'wait' 상태는 미체결 주문을 의미
    }

    # Query Hash 계산
    query_string = '&amp;amp;'.join([f&quot;{key}={value}&quot; for key, value in query.items()])
    query_hash = hashlib.sha512(query_string.encode()).hexdigest()

    # JWT 토큰 생성
    payload = {
        'access_key': api_key,
        'nonce': str(uuid.uuid4()),
        'query_hash': query_hash,
        'query_hash_alg': 'SHA512',
    }
    jwt_token = jwt.encode(payload, secret_key, algorithm=&quot;HS256&quot;)
    headers = {&quot;Authorization&quot;: f&quot;Bearer {jwt_token}&quot;}

    try:
        # 미체결 주문 요청
        response = requests.get(base_url, headers=headers, params=query)
        response.raise_for_status()
        orders = response.json()

        # 미체결 주문이 없는 경우 처리
        if not orders:
            print(&quot;미체결 주문이 없습니다.&quot;)
            return []

        print(&quot;미체결 주문이 있습니다.&quot;)
        return orders

    except Exception as e:
        print(f&quot;Error fetching unfilled orders: {e}&quot;)
        return []
    
#미체결된 주문 취소하는 함수
def cancel_unfilled_order(api_key, secret_key, order_id):
    &quot;&quot;&quot;
    특정 미체결 주문을 취소합니다.

    Args:
        api_key (str): 업비트 API 키
        secret_key (str): 업비트 Secret 키
        order_id (str): 취소할 주문의 UUID

    Returns:
        dict: 주문 취소 결과
    &quot;&quot;&quot;
    base_url = &quot;https://api.upbit.com/v1/order&quot;
    query = {
        'uuid': order_id,
    }

    # JWT 토큰 생성
    query_string = '&amp;amp;'.join([f&quot;{key}={value}&quot; for key, value in query.items()])
    payload = {
        'access_key': api_key,
        'nonce': str(uuid.uuid4()),
        'query_hash': hashlib.sha512(query_string.encode()).hexdigest(),
        'query_hash_alg': 'SHA512',
    }
    jwt_token = jwt.encode(payload, secret_key, algorithm=&quot;HS256&quot;)
    headers = {&quot;Authorization&quot;: f&quot;Bearer {jwt_token}&quot;}

    try:
        # 주문 취소 요청
        response = requests.delete(base_url, headers=headers, params=query)
        response.raise_for_status()
        cancel_result = response.json()

        print(f&quot;주문이 성공적으로 취소되었습니다: {cancel_result}&quot;)
        return cancel_result

    except requests.exceptions.HTTPError as e:
        print(f&quot;HTTP 에러 발생: {e.response.status_code}, {e.response.text}&quot;)
        return {}
    except Exception as e:
        print(f&quot;Error canceling order: {e}&quot;)
        return {}
    
#매수 1호가와 매도1호가 리턴하는 함수    
def get_orderbook(api_key, market):
    &quot;&quot;&quot;
    주어진 마켓의 매수, 매도 1호가를 가져옵니다.

    Args:
        api_key (str): 업비트 API 키
        market (str): 마켓 코드 (예: &quot;KRW-BTC&quot;)

    Returns:
        dict: 매수 1호가와 매도 1호가를 포함한 딕셔너리
    &quot;&quot;&quot;
    url = f&quot;https://api.upbit.com/v1/orderbook?markets={market}&amp;amp;level=0&quot;

    headers = {&quot;accept&quot;: &quot;application/json&quot;}

    try:
        # 주문서 정보 요청
        response = requests.get(url, headers=headers)
        response.raise_for_status()
        orderbook = response.json()

        if not orderbook:
            print(f&quot;{market}의 주문서 정보가 없습니다.&quot;)
            return None

        # 매수 1호가 (가장 높은 매도 호가)
        bid_price = orderbook[0]['orderbook_units'][0]['bid_price']
        # 매도 1호가 (가장 낮은 매수 호가)
        ask_price = orderbook[0]['orderbook_units'][0]['ask_price']

        return {&quot;bid_price&quot;: bid_price, &quot;ask_price&quot;: ask_price}

    except Exception as e:
        print(f&quot;Error fetching orderbook: {e}&quot;)
        return None
    
#지정가 매수함수    
def place_limit_buy_order(api_key, secret_key, market, price, budget):
    max_quantity = str(budget / price)
    params = {
        'market': market,
        'side': 'bid',
        'ord_type': 'limit',
        'price': price,
        'volume': max_quantity
    }
    query_string = unquote(urlencode(params, doseq=True)).encode(&quot;utf-8&quot;)

    m = hashlib.sha512()
    m.update(query_string)
    query_hash = m.hexdigest()

    payload = {
        'access_key': api_key,
        'nonce': str(uuid.uuid4()),
        'query_hash': query_hash,
        'query_hash_alg': 'SHA512',
    }

    jwt_token = jwt.encode(payload, secret_key)
    authorization = 'Bearer {}'.format(jwt_token)
    headers = {
    'Authorization': authorization,
    }

    res = requests.post(server_url + '/v1/orders', json=params, headers=headers)
    order_result = res.json()
    # 성공적으로 주문을 제출했다면 주문 UUID 반환
    order_uuid = order_result.get('uuid')
    if order_uuid:
        print(f&quot;매수 주문이 성공적으로 생성되었습니다. 주문 UUID: {order_uuid}&quot;)
        time.sleep(5)
        return order_uuid
    else:
        print(&quot;주문 생성 실패: UUID를 찾을 수 없습니다.&quot;)
        return None
    
# 지정가 매도함수
def place_limit_sell_order(api_key, secret_key, market, price, quantity):
    max_quantity = str(quantity)
    params = {
        'market': market,
        'side': 'ask',
        'ord_type': 'limit',
        'price': price,
        'volume': max_quantity
    }
    query_string = unquote(urlencode(params, doseq=True)).encode(&quot;utf-8&quot;)

    m = hashlib.sha512()
    m.update(query_string)
    query_hash = m.hexdigest()

    payload = {
        'access_key': api_key,
        'nonce': str(uuid.uuid4()),
        'query_hash': query_hash,
        'query_hash_alg': 'SHA512',
    }

    jwt_token = jwt.encode(payload, secret_key)
    authorization = 'Bearer {}'.format(jwt_token)
    headers = {
    'Authorization': authorization,
    }

    res = requests.post(server_url + '/v1/orders', json=params, headers=headers)
    order_result = res.json()
    # 성공적으로 주문을 제출했다면 주문 UUID 반환
    order_uuid = order_result.get('uuid')
    if order_uuid:
        print(f&quot;매도 주문이 성공적으로 생성되었습니다. 주문 UUID: {order_uuid}&quot;)
        time.sleep(5)
        return order_uuid
    else:
        print(&quot;주문 생성 실패: UUID를 찾을 수 없습니다.&quot;)
        return None
    
#코인의 현재가격을 불러오는 함수
def get_current_price(coin):
    url = f&quot;https://api.upbit.com/v1/ticker&quot;
    params = {
        'markets': f'KRW-{coin}'  # 코인 이름 입력 (예: 'BTC' -&amp;gt; 'KRW-BTC')
    }
    
    response = requests.get(url, params=params)
    data = response.json()
    
    if len(data) &amp;gt; 0:
        return data[0]['trade_price']  # 현재 거래 가격 반환
    else:
        return None
    
#코인의 캔들데이터를 가져오는 함수
def get_coin_data(coin):
    url = &quot;https://api.upbit.com/v1/candles/minutes/1&quot;  # Adjusted to minutes/1 endpoint for real data
    params = {  
        'market': f'KRW-{coin}',  
        'count': 200,
    }  
    headers = {&quot;accept&quot;: &quot;application/json&quot;}

    response = requests.get(url, params=params, headers=headers)
    data = response.json()
    df = pd.DataFrame(data)
    df = df.reindex(index=df.index[::-1]).reset_index(drop=True)  # Reset index after reversing
    return df

#받아온 코인 데이터를 저장할 csv파일을 만드는 함수수
def save_to_csv(df):
    filename = 'coin_data.csv'
    df.to_csv(filename, index=False)

#거래기록을 저장할 csv 파일일
columns = ['시간', '코인명', '매수/매도', '평균 단가','현재 보유잔고']
df_trade = pd.DataFrame(columns=columns)
df_trade.to_csv('trade_list.csv', index=False, encoding='utf-8-sig')

#거래기록을 추가하는 함수
def add_trade(trade_time, coin_name, trade_type, avg_price,hold_won):
    # CSV 파일 읽기
    df = pd.read_csv('trade_list.csv', encoding='utf-8-sig')
    
    # 새로운 데이터 추가
    new_data = {
        '시간': trade_time,
        '코인명': coin_name,
        '매수/매도': trade_type,
        '평균 단가': avg_price,
        '현재 보유잔고':hold_won
    }
    df = df.append(new_data, ignore_index=True)
    
    # 업데이트된 데이터프레임을 CSV 파일로 저장
    df.to_csv('trade_list.csv', index=False, encoding='utf-8-sig')

#현재 보유중인 원화 잔고를 가져오는 함수
def get_balance():
    &quot;&quot;&quot;
    업비트 API를 사용하여 원화 잔고를 조회하는 함수
    &quot;&quot;&quot;
    url = server_url + '/v1/accounts'
    
    # 요청 헤더 준비
    payload = {
        'access_key': api_key,
        'nonce': str(uuid.uuid4())
    }
    
    # 서명 생성
    m = hashlib.sha512()
    m.update(payload['nonce'].encode('utf-8'))
    signature = jwt.encode(payload, secret_key, algorithm='HS256')
    
    headers = {
        'Authorization': f'Bearer {signature}'
    }
    
    # API 요청
    response = requests.get(url, headers=headers)
    
    if response.status_code == 200:
        accounts = response.json()
        for account in accounts:
            if account['currency'] == 'KRW':  # 원화 잔고 찾기
                return float(account['balance'])
    else:
        print(f&quot;Error: {response.status_code}&quot;)
        return None
    
#각종 지표를 계산하는 함수
def calculate_indicators(df):
    # 'trade_price' (종가), 'high_price', 'low_price' 컬럼 확인 및 추출
    required_columns = ['trade_price', 'high_price', 'low_price','candle_acc_trade_volume']
    for col in required_columns:
        if col not in df.columns:
            raise ValueError(f&quot;데이터에 '{col}' 컬럼이 없습니다.&quot;)
    
    close_prices = df['trade_price'].astype(float)
    high_prices = df['high_price'].astype(float)
    low_prices = df['low_price'].astype(float)
    volumes = df['candle_acc_trade_volume'].astype(float)

    # 데이터 확인
    if close_prices.isnull().any():
        raise ValueError(&quot;종가 데이터에 결측값이 있습니다.&quot;)
    if len(close_prices) &amp;lt; 10:  # Momentum 계산에 필요한 최소 데이터 길이
        raise ValueError(&quot;Momentum 지표를 계산하려면 데이터 길이가 최소 10 이상이어야 합니다.&quot;)
    
    # RSI (Relative Strength Index)
    df['RSI'] = ta.RSI(close_prices, timeperiod=14)
    rsi = df['RSI'].iloc[-1]

    # Stochastic RSI
    df['StochRSI'] = (df['RSI'] - df['RSI'].rolling(14).min()) / (df['RSI'].rolling(14).max() - df['RSI'].rolling(14).min()) * 100
    df['StochRSI_K'] = df['StochRSI'].rolling(3).mean()
    df['StochRSI_D'] = df['StochRSI_K'].rolling(3).mean()
    stoch_rsi_d = df['StochRSI_D'].iloc[-1]
    stoch_rsi_k = df['StochRSI_K'].iloc[-1]
    
    # Bollinger Bands
    df['Upper_Band'], df['Middle_Band'], df['Lower_Band'] = ta.BBANDS(
        close_prices,
        timeperiod=20,
        nbdevup=2.0,
        nbdevdn=2.0,
        matype=0
    )
    upper_band = df['Upper_Band'].iloc[-1]
    middle_band = df['Middle_Band'].iloc[-1]
    lower_band = df['Lower_Band'].iloc[-1]
    
    # MACD (Moving Average Convergence Divergence)
    df['MACD'], df['MACD_Signal'], df['MACD_Hist'] = ta.MACD(
        close_prices,
        fastperiod=12,
        slowperiod=26,
        signalperiod=9
    )
    macd = df['MACD'].iloc[-1]
    macd_signal = df['MACD_Signal'].iloc[-1]
    macd_hist = df['MACD_Hist'].iloc[-1]
    
    # TRIX (Triple Exponential Average)
    df['TRIX'] = ta.TRIX(
        close_prices,
        timeperiod=5
    )
    trix = df['TRIX'].iloc[-1]

    df['MFI'] = ta.MFI(
        high=high_prices,
        low=low_prices,
        close=close_prices,
        volume=volumes,
        timeperiod=14
    )
    mfi = df['MFI'].iloc[-1]

    df['Tenkan_sen'] = (high_prices.rolling(window=9).max() + low_prices.rolling(window=9).min()) / 2
    df['Kijun_sen'] = (high_prices.rolling(window=26).max() + low_prices.rolling(window=26).min()) / 2
    tenkan_sen = df['Tenkan_sen'].iloc[-1]  # 전환선 최신 값
    kijun_sen = df['Kijun_sen'].iloc[-1]    # 기준선 최신 값

    # Save data to CSV
    save_to_csv(df)
    
    # Return all indicator values
    return (
        upper_band, middle_band, lower_band, macd, macd_signal, macd_hist, trix, mfi, rsi, stoch_rsi_d,stoch_rsi_k,tenkan_sen,kijun_sen
    )

def prepare_training_data(df):
    # 다음 가격 변동 계산 (1: 상승, 0: 하락 또는 동일)
    df['Next_Price'] = df['trade_price'].shift(-1)  # 다음 가격
    df['Price_Change'] = (df['Next_Price'] &amp;gt; df['trade_price']).astype(int)

    # 유효 데이터 필터링 (결측값 제거)
    feature_columns = [
        'RSI', 'StochRSI_D', 'StochRSI_K', 'Upper_Band', 'Middle_Band', 'Lower_Band',
        'MACD', 'MACD_Signal', 'MACD_Hist', 'TRIX', 'MFI', 'Tenkan_sen', 'Kijun_sen'
    ]
    df = df.dropna(subset=feature_columns + ['Price_Change'])

    # 독립 변수 (X)와 종속 변수 (y) 분리
    X = df[feature_columns]
    y = df['Price_Change']
    return X, y

def calculate_price_probabilities_with_df(df):
    &quot;&quot;&quot;
    df: 지표와 가격 정보가 포함된 데이터프레임
    Returns: 상승 및 하락 확률
    &quot;&quot;&quot;
    # 필요한 컬럼 정의
    feature_columns = [
        'RSI', 'StochRSI_D', 'StochRSI_K', 'Upper_Band', 'Middle_Band', 'Lower_Band',
        'MACD', 'MACD_Signal', 'MACD_Hist', 'TRIX', 'MFI', 'Tenkan_sen', 'Kijun_sen'
    ]

    # 다음 가격 변동 계산 (1: 상승, 0: 하락 또는 동일)
    df['Next_Price'] = df['trade_price'].shift(-1)  # 다음 가격
    df['Price_Change'] = (df['Next_Price'] &amp;gt; df['trade_price']).astype(int)

    # 결측값 제거
    df = df.dropna(subset=feature_columns + ['Price_Change'])

    # 독립 변수 (X)와 종속 변수 (y) 분리
    X = df[feature_columns]
    y = df['Price_Change']

    # 로지스틱 회귀 모델 학습
    model = LogisticRegression(max_iter=1000)
    model.fit(X, y)

    # 가장 최근 행의 지표값 가져오기
    latest_data = df.iloc[-1][feature_columns].values.reshape(1, -1)

    # 확률 예측
    probabilities = model.predict_proba(latest_data)[0]  # [하락 확률, 상승 확률]

    return {'down_prob': probabilities[0], 'up_prob': probabilities[1]}

api_key, secret_key = load_api_keys(&quot;upbit_config.json&quot;)
coin_list = input(&quot;조회할 코인 심볼들을 입력하세요 (쉼표로 구분): &quot;).upper().replace(&quot; &quot;, &quot;&quot;).split(',')

while True:
    coin_holdings = get_coin_holdings(api_key, secret_key)
    money = int(get_balance() * 0.95)
    if len(coin_holdings)==1:
        # coin_list = get_top_gainers_upbit(7)
        for coin in coin_list:
            market = f'KRW-{coin}'
            status_coin_price = get_current_price(coin)
            coin_order_unit = 0
            if status_coin_price &amp;gt;= 2000000:
                coin_order_unit = 1000
            elif status_coin_price &amp;gt;=1000000 and status_coin_price&amp;lt;2000000:
                coin_order_unit = 500
            elif status_coin_price &amp;gt;=500000 and status_coin_price &amp;lt;1000000:
                coin_order_unit = 100
            elif status_coin_price &amp;gt;=100000 and status_coin_price&amp;lt;500000:
                coin_order_unit = 50
            elif status_coin_price &amp;gt;=10000 and status_coin_price&amp;lt;100000:
                coin_order_unit = 10
            elif status_coin_price &amp;gt;=1000 and status_coin_price&amp;lt;10000:
                coin_order_unit = 1
            elif status_coin_price &amp;gt;=100 and status_coin_price&amp;lt;1000:
                coin_order_unit = 0.1
                if coin in special_coin_list:
                    coin_order_unit = 1
            elif status_coin_price &amp;gt;=10 and status_coin_price&amp;lt;100:
                coin_order_unit = 0.01
            elif status_coin_price &amp;gt;= 1 and status_coin_price&amp;lt;10:
                coin_order_unit = 0.001
            elif status_coin_price&amp;gt;=0.1 and status_coin_price&amp;lt;1:
                coin_order_unit = 0.0001
            elif status_coin_price&amp;gt;=0.01 and status_coin_price&amp;lt;0.1:
                coin_order_unit = 0.00001
            elif status_coin_price&amp;gt;=0.001 and status_coin_price&amp;lt;0.01:
                coin_order_unit = 0.000001
            elif status_coin_price&amp;gt;=0.0001 and status_coin_price&amp;lt;0.001 :
                coin_order_unit = 0.0000001
            else :
                coin_order_unit = 0.00000001
            df = get_coin_data(coin)  # 데이터 가져오기
            # df = df.iloc[:-1]
            indicators = calculate_indicators(df)  # 지표 계산
            prediction = calculate_price_probabilities_with_df(df)  # 상승/하락 확률 계산
            
            print('----------------------------------------------')
            print(f'{coin}의 현재 가격 : {status_coin_price}원')
            print('')
            print(f'현재 코인 rsi : {indicators[8]:.2f}')
            print(f'현재 코인의 stoch_rsi : {indicators[9]:.2f}')
            print(f'현재 {coin}코인의 상승확률 : {prediction[&quot;up_prob&quot;]*100:.3f}%')

            while prediction['up_prob']&amp;gt;0.7 and indicators[8]&amp;lt;70 and indicators[9]&amp;lt;80:
                orderbook = get_orderbook(api_key, market)
                buy_price = orderbook['bid_price'] + coin_order_unit
                buy_uuid = place_limit_buy_order(api_key,secret_key,market,buy_price,money)
                unfilled_orders = check_unfilled_orders(api_key, secret_key)
                if unfilled_orders:
                    print('지정가 매수 실패, 미체결 주문 취소 작업 중..')
                    for order in unfilled_orders:
                        cancel_result = cancel_unfilled_order(api_key, secret_key, buy_uuid)
                else:
                    current_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
                    my_money_info,total_balance = get_upbit_account_info(api_key,secret_key)
                    add_trade(current_time, f'{coin}', 'buy', buy_price,total_balance)
                    break
                df = get_coin_data(coin)  # 데이터 가져오기
                # df = df.iloc[:-1]
                indicators = calculate_indicators(df)  # 지표 계산
                prediction = calculate_price_probabilities_with_df(df)  # 상승/하락 확률 계산
            if len(get_coin_holdings(api_key,secret_key))&amp;gt;1:
                break
            print('')
        
    else:
        print('----------------------------------------------')
        hold_coin = list(coin_holdings.keys())[1]
        hold_coin_amount = float(coin_holdings[hold_coin])
        status_coin_price = get_current_price(hold_coin)
        coin_order_unit = 0
        market = f'KRW-{hold_coin}'
        if status_coin_price &amp;gt;= 2000000:
            coin_order_unit = 1000
        elif status_coin_price &amp;gt;=1000000 and status_coin_price&amp;lt;2000000:
            coin_order_unit = 500
        elif status_coin_price &amp;gt;=500000 and status_coin_price &amp;lt;1000000:
            coin_order_unit = 100
        elif status_coin_price &amp;gt;=100000 and status_coin_price&amp;lt;500000:
            coin_order_unit = 50
        elif status_coin_price &amp;gt;=10000 and status_coin_price&amp;lt;100000:
            coin_order_unit = 10
        elif status_coin_price &amp;gt;=1000 and status_coin_price&amp;lt;10000:
            coin_order_unit = 1
        elif status_coin_price &amp;gt;=100 and status_coin_price&amp;lt;1000:
            coin_order_unit = 0.1
            if hold_coin in special_coin_list:
                coin_order_unit = 1
        elif status_coin_price &amp;gt;=10 and status_coin_price&amp;lt;100:
            coin_order_unit = 0.01
        elif status_coin_price &amp;gt;= 1 and status_coin_price&amp;lt;10:
            coin_order_unit = 0.001
        elif status_coin_price&amp;gt;=0.1 and status_coin_price&amp;lt;1:
            coin_order_unit = 0.0001
        elif status_coin_price&amp;gt;=0.01 and status_coin_price&amp;lt;0.1:
            coin_order_unit = 0.00001
        elif status_coin_price&amp;gt;=0.001 and status_coin_price&amp;lt;0.01:
            coin_order_unit = 0.000001
        elif status_coin_price&amp;gt;=0.0001 and status_coin_price&amp;lt;0.001 :
            coin_order_unit = 0.0000001
        else :
            coin_order_unit = 0.00000001
        df = get_coin_data(hold_coin)  # 데이터 가져오기
        # df = df.iloc[:-1]
        indicators = calculate_indicators(df)  # 지표 계산
        prediction = calculate_price_probabilities_with_df(df)  # 상승/하락 확률 계산
    
        print(f'{hold_coin}의 현재 가격 : {status_coin_price}원')
        print('')
        print(f'현재 코인 rsi : {indicators[8]:.2f}')
        print(f'현재 코인의 stoch_rsi : {indicators[9]:.2f}')
        print(f'현재 코인의 상승확률 : {prediction[&quot;up_prob&quot;]*100:.3f}%')
        my_money_info,total_balance = get_upbit_account_info(api_key,secret_key)
        while my_money_info[0]['수익률']&amp;lt;=-1.0 or my_money_info[0]['수익률']&amp;gt;=1.5:
            orderbook = get_orderbook(api_key, market)
            status_profit_ratio = get_upbit_account_info(api_key,secret_key)
            sell_price = orderbook['ask_price']-coin_order_unit
            sell_uuid = place_limit_sell_order(api_key,secret_key,market,sell_price,hold_coin_amount)
            if len(check_unfilled_orders(api_key,secret_key))&amp;gt;0:
                cancel_result = cancel_unfilled_order(api_key,secret_key,sell_uuid)
            else:
                current_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
                my_money_info,total_balance = get_upbit_account_info(api_key,secret_key)
                add_trade(current_time, f'{hold_coin}', 'sell', sell_price,total_balance)
                break
            my_money_info,total_balance = get_upbit_account_info(api_key,secret_key)

        while prediction['down_prob']&amp;gt;=0.55 or indicators[8] &amp;gt;80 or indicators[9]&amp;gt;85:
            orderbook = get_orderbook(api_key, market)
            market = f'KRW-{hold_coin}'
            sell_price = orderbook['ask_price'] - coin_order_unit
            sell_uuid = place_limit_sell_order(api_key,secret_key,market,sell_price,hold_coin_amount)
            unfilled_orders = check_unfilled_orders(api_key, secret_key)
            if unfilled_orders:
                print('지정가 매도 실패, 미체결 주문 취소 작업 중..')
                for order in unfilled_orders:
                    cancel_result = cancel_unfilled_order(api_key, secret_key, sell_uuid)
            else:
                current_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
                my_money_info,total_balance = get_upbit_account_info(api_key,secret_key)
                add_trade(current_time, f'{hold_coin}', 'sell', sell_price,total_balance)
                break
            df = get_coin_data(hold_coin)  # 데이터 가져오기
            # df = df.iloc[:-1]
            indicators = calculate_indicators(df)  # 지표 계산
            prediction = calculate_price_probabilities_with_df(df)  # 상승/하락 확률 계산&lt;/code&gt;&lt;/pre&gt;</description>
      <category>혼자 만든 Code/upbit 코인 자동거래</category>
      <author>빛하루</author>
      <guid isPermaLink="true">https://harucode.tistory.com/862</guid>
      <comments>https://harucode.tistory.com/862#entry862comment</comments>
      <pubDate>Sun, 19 Jan 2025 01:28:35 +0900</pubDate>
    </item>
    <item>
      <title>업비트 코인 자동거래 v1.31(매매 기준 수정)</title>
      <link>https://harucode.tistory.com/861</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;매수기준 : 상승확률 70프로이상&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;매도기준 : 하락확률 60프로 이상 or 수익률 -1퍼 이하&lt;/p&gt;
&lt;pre id=&quot;code_1737163427293&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import pandas as pd
import requests
import talib as ta
import time
import jwt
import uuid
import json
from urllib.parse import urlencode
import pyupbit
import numpy as np
import hashlib
from urllib.parse import urlencode, unquote
import matplotlib.pyplot as plt
import csv
import os
from datetime import datetime
import pytz
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, Dropout
from tensorflow.keras.optimizers import Adam
import tensorflow as tf

# TensorFlow 로그 메시지 숨기기
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3'
tf.get_logger().setLevel('ERROR')

server_url = 'https://api.upbit.com'
special_coin_list = ['ADA','ALGO','BLUR','CELO', 'ELF', 'EOS', 'GRS', 'GRT', 'ICX', 'MANA', 'MINA', 'POL', 'SAND', 'SEI', 'STG', 'TRX']

#전일 대비 가격 상승률 상위 n개 종목을 가져오는 함수

def get_top_gainers_upbit(number):
    base_url = &quot;https://api.upbit.com/v1&quot;

    try:
        # Step 1: Get market data
        markets_response = requests.get(f&quot;{base_url}/market/all&quot;)
        markets_response.raise_for_status()
        markets = markets_response.json()

        # Filter for KRW markets only and extract symbols
        krw_symbols = [market['market'].split('-')[1] for market in markets if market['market'].startswith('KRW-')]

        # Step 2: Get ticker data for all KRW markets
        krw_markets = [f&quot;KRW-{symbol}&quot; for symbol in krw_symbols]
        tickers_response = requests.get(f&quot;{base_url}/ticker&quot;, params={&quot;markets&quot;: &quot;,&quot;.join(krw_markets)})
        tickers_response.raise_for_status()
        tickers = tickers_response.json()

        # Step 3: Calculate price change percentages and filter for &amp;gt;3% gainers
        gainers = []
        for ticker in tickers:
            market = ticker['market']
            symbol = market.split('-')[1]
            prev_close = ticker['prev_closing_price']  # 전일 종가
            current_price = ticker['trade_price']     # 현재가

            except_coin = ['USDC','USDT','BTC','ETH','BCH','AAVE','SOL','BSV','AVAX','EGLD']
            
            if prev_close &amp;gt; 0 and symbol not in except_coin:  # 유효한 가격만 계산
                change_percent = ((current_price - prev_close) / prev_close) * 100
            
                gainers.append({
                    'symbol': symbol,
                    'change_percent': change_percent,
                    'current_price': current_price
                })

        # Step 4: Sort by price change percentage in descending order
        gainers = sorted(gainers, key=lambda x: x['change_percent'], reverse=True)

        # Step 5: Return top 5 gainers
        return [gainer['symbol'] for gainer in gainers[:number]]

    except requests.exceptions.RequestException as e:
        print(f&quot;Error while fetching data from Upbit API: {e}&quot;)
        return []
    
# JSON 파일에서 API 키 불러오기
def load_api_keys(config_file=&quot;upbit_config.json&quot;):
    try:
        with open(config_file, &quot;r&quot;) as file:
            keys = json.load(file)
            return keys[&quot;api_key&quot;], keys[&quot;secret_key&quot;]
    except Exception as e:
        print(f&quot;Error loading API keys: {e}&quot;)
        return None, None
    
# 현재 업비트 계좌 정보를 불러오는 함수
def get_upbit_account_info(api_key, secret_key):
    base_url = &quot;https://api.upbit.com/v1&quot;

    # JWT 토큰 생성
    payload = {
        'access_key': api_key,
        'nonce': str(uuid.uuid4()),
    }
    jwt_token = jwt.encode(payload, secret_key, algorithm=&quot;HS256&quot;)
    authorization_token = f&quot;Bearer {jwt_token}&quot;
    
    headers = {
        &quot;Authorization&quot;: authorization_token,
    }

    # 계좌 조회 API 호출
    try:
        account_res = requests.get(f&quot;{base_url}/accounts&quot;, headers=headers)
        account_data = account_res.json()
        if account_res.status_code != 200:
            print(f&quot;Error fetching account data: {account_data}&quot;)
            return

        # 코인별 정보 조회
        market_data = requests.get(f&quot;{base_url}/market/all&quot;).json()
        market_dict = {item['market']: item['korean_name'] for item in market_data}

        total_balance = 0
        coin_info_list = []

        for account in account_data:
            if float(account['balance']) &amp;gt; 0:  # 잔액이 0보다 클 경우
                ticker = account['currency']
                if ticker == &quot;KRW&quot;:
                    total_balance += float(account['balance'])
                    continue
                
                market = f&quot;KRW-{ticker}&quot;
                avg_buy_price = float(account['avg_buy_price'])
                balance = float(account['balance'])

                # 현재 가격 조회
                ticker_url = f&quot;{base_url}/ticker?markets={market}&quot;
                ticker_res = requests.get(ticker_url).json()
                current_price = float(ticker_res[0]['trade_price'])
                
                # 평가 금액과 수익률 계산
                eval_amount = balance * current_price
                profit_rate = ((current_price - avg_buy_price) / avg_buy_price) * 100
                
                # 총 자산에 반영
                total_balance += eval_amount
                
                coin_info_list.append({
                    &quot;코인&quot;: market_dict.get(market, ticker),
                    &quot;보유수량&quot;: balance,
                    &quot;평균단가&quot;: avg_buy_price,
                    &quot;현재가격&quot;: current_price,
                    &quot;수익률&quot;: profit_rate,
                })

        # 출력
        print(f&quot;총 자산: {total_balance:,.0f} KRW&quot;)
        print('')
        print(&quot;보유 코인 정보:&quot;)
        print(coin_info_list)
        return coin_info_list,total_balance
    except Exception as e:
        print(f&quot;An error occurred: {e}&quot;)

# 보유중인 코인의 심볼과 수량을 가져오는 함수
def get_coin_holdings(api_key, secret_key):
    &quot;&quot;&quot;
    업비트 API를 사용하여 보유 중인 코인의 종류와 수량을 가져옵니다.

    Returns:
        dict: 코인의 종류와 보유 수량을 포함한 딕셔너리
    &quot;&quot;&quot;
    base_url = &quot;https://api.upbit.com/v1/accounts&quot;

    # JWT 토큰 생성
    payload = {
        'access_key': api_key,
        'nonce': str(uuid.uuid4()),
    }
    jwt_token = jwt.encode(payload, secret_key, algorithm=&quot;HS256&quot;)
    headers = {&quot;Authorization&quot;: f&quot;Bearer {jwt_token}&quot;}

    try:
        # 계좌 정보 요청
        response = requests.get(base_url, headers=headers)
        response.raise_for_status()
        accounts = response.json()

        # 보유 코인 정보 필터링
        holdings = {account['currency']: float(account['balance']) for account in accounts if float(account['balance']) &amp;gt; 0}

        # 보유 코인이 없을 경우 처리
        if not holdings:
            print(&quot;보유 중인 코인이 없습니다.&quot;)
            return {}

        return holdings

    except Exception as e:
        print(f&quot;Error fetching coin holdings: {e}&quot;)
        return {}

#미체결된 주문 조회 함수
def check_unfilled_orders(api_key, secret_key):
    &quot;&quot;&quot;
    미체결 주문이 있는지 확인합니다.

    Returns:
        list: 미체결 주문 리스트 (없으면 빈 리스트)
    &quot;&quot;&quot;
    base_url = &quot;https://api.upbit.com/v1/orders&quot;
    query = {
        'state': 'wait',  # 'wait' 상태는 미체결 주문을 의미
    }

    # Query Hash 계산
    query_string = '&amp;amp;'.join([f&quot;{key}={value}&quot; for key, value in query.items()])
    query_hash = hashlib.sha512(query_string.encode()).hexdigest()

    # JWT 토큰 생성
    payload = {
        'access_key': api_key,
        'nonce': str(uuid.uuid4()),
        'query_hash': query_hash,
        'query_hash_alg': 'SHA512',
    }
    jwt_token = jwt.encode(payload, secret_key, algorithm=&quot;HS256&quot;)
    headers = {&quot;Authorization&quot;: f&quot;Bearer {jwt_token}&quot;}

    try:
        # 미체결 주문 요청
        response = requests.get(base_url, headers=headers, params=query)
        response.raise_for_status()
        orders = response.json()

        # 미체결 주문이 없는 경우 처리
        if not orders:
            print(&quot;미체결 주문이 없습니다.&quot;)
            return []

        print(&quot;미체결 주문이 있습니다.&quot;)
        return orders

    except Exception as e:
        print(f&quot;Error fetching unfilled orders: {e}&quot;)
        return []
    
#미체결된 주문 취소하는 함수
def cancel_unfilled_order(api_key, secret_key, order_id):
    &quot;&quot;&quot;
    특정 미체결 주문을 취소합니다.

    Args:
        api_key (str): 업비트 API 키
        secret_key (str): 업비트 Secret 키
        order_id (str): 취소할 주문의 UUID

    Returns:
        dict: 주문 취소 결과
    &quot;&quot;&quot;
    base_url = &quot;https://api.upbit.com/v1/order&quot;
    query = {
        'uuid': order_id,
    }

    # JWT 토큰 생성
    query_string = '&amp;amp;'.join([f&quot;{key}={value}&quot; for key, value in query.items()])
    payload = {
        'access_key': api_key,
        'nonce': str(uuid.uuid4()),
        'query_hash': hashlib.sha512(query_string.encode()).hexdigest(),
        'query_hash_alg': 'SHA512',
    }
    jwt_token = jwt.encode(payload, secret_key, algorithm=&quot;HS256&quot;)
    headers = {&quot;Authorization&quot;: f&quot;Bearer {jwt_token}&quot;}

    try:
        # 주문 취소 요청
        response = requests.delete(base_url, headers=headers, params=query)
        response.raise_for_status()
        cancel_result = response.json()

        print(f&quot;주문이 성공적으로 취소되었습니다: {cancel_result}&quot;)
        return cancel_result

    except requests.exceptions.HTTPError as e:
        print(f&quot;HTTP 에러 발생: {e.response.status_code}, {e.response.text}&quot;)
        return {}
    except Exception as e:
        print(f&quot;Error canceling order: {e}&quot;)
        return {}
    
#매수 1호가와 매도1호가 리턴하는 함수    
def get_orderbook(api_key, market):
    &quot;&quot;&quot;
    주어진 마켓의 매수, 매도 1호가를 가져옵니다.

    Args:
        api_key (str): 업비트 API 키
        market (str): 마켓 코드 (예: &quot;KRW-BTC&quot;)

    Returns:
        dict: 매수 1호가와 매도 1호가를 포함한 딕셔너리
    &quot;&quot;&quot;
    url = f&quot;https://api.upbit.com/v1/orderbook?markets={market}&amp;amp;level=0&quot;

    headers = {&quot;accept&quot;: &quot;application/json&quot;}

    try:
        # 주문서 정보 요청
        response = requests.get(url, headers=headers)
        response.raise_for_status()
        orderbook = response.json()

        if not orderbook:
            print(f&quot;{market}의 주문서 정보가 없습니다.&quot;)
            return None

        # 매수 1호가 (가장 높은 매도 호가)
        bid_price = orderbook[0]['orderbook_units'][0]['bid_price']
        # 매도 1호가 (가장 낮은 매수 호가)
        ask_price = orderbook[0]['orderbook_units'][0]['ask_price']

        return {&quot;bid_price&quot;: bid_price, &quot;ask_price&quot;: ask_price}

    except Exception as e:
        print(f&quot;Error fetching orderbook: {e}&quot;)
        return None
    
#지정가 매수함수    
def place_limit_buy_order(api_key, secret_key, market, price, budget):
    max_quantity = str(budget / price)
    params = {
        'market': market,
        'side': 'bid',
        'ord_type': 'limit',
        'price': price,
        'volume': max_quantity
    }
    query_string = unquote(urlencode(params, doseq=True)).encode(&quot;utf-8&quot;)

    m = hashlib.sha512()
    m.update(query_string)
    query_hash = m.hexdigest()

    payload = {
        'access_key': api_key,
        'nonce': str(uuid.uuid4()),
        'query_hash': query_hash,
        'query_hash_alg': 'SHA512',
    }

    jwt_token = jwt.encode(payload, secret_key)
    authorization = 'Bearer {}'.format(jwt_token)
    headers = {
    'Authorization': authorization,
    }

    res = requests.post(server_url + '/v1/orders', json=params, headers=headers)
    order_result = res.json()
    # 성공적으로 주문을 제출했다면 주문 UUID 반환
    order_uuid = order_result.get('uuid')
    if order_uuid:
        print(f&quot;매수 주문이 성공적으로 생성되었습니다. 주문 UUID: {order_uuid}&quot;)
        time.sleep(5)
        return order_uuid
    else:
        print(&quot;주문 생성 실패: UUID를 찾을 수 없습니다.&quot;)
        return None
    
# 지정가 매도함수
def place_limit_sell_order(api_key, secret_key, market, price, quantity):
    max_quantity = str(quantity)
    params = {
        'market': market,
        'side': 'ask',
        'ord_type': 'limit',
        'price': price,
        'volume': max_quantity
    }
    query_string = unquote(urlencode(params, doseq=True)).encode(&quot;utf-8&quot;)

    m = hashlib.sha512()
    m.update(query_string)
    query_hash = m.hexdigest()

    payload = {
        'access_key': api_key,
        'nonce': str(uuid.uuid4()),
        'query_hash': query_hash,
        'query_hash_alg': 'SHA512',
    }

    jwt_token = jwt.encode(payload, secret_key)
    authorization = 'Bearer {}'.format(jwt_token)
    headers = {
    'Authorization': authorization,
    }

    res = requests.post(server_url + '/v1/orders', json=params, headers=headers)
    order_result = res.json()
    # 성공적으로 주문을 제출했다면 주문 UUID 반환
    order_uuid = order_result.get('uuid')
    if order_uuid:
        print(f&quot;매도 주문이 성공적으로 생성되었습니다. 주문 UUID: {order_uuid}&quot;)
        time.sleep(5)
        return order_uuid
    else:
        print(&quot;주문 생성 실패: UUID를 찾을 수 없습니다.&quot;)
        return None
    
#코인의 현재가격을 불러오는 함수
def get_current_price(coin):
    url = f&quot;https://api.upbit.com/v1/ticker&quot;
    params = {
        'markets': f'KRW-{coin}'  # 코인 이름 입력 (예: 'BTC' -&amp;gt; 'KRW-BTC')
    }
    
    response = requests.get(url, params=params)
    data = response.json()
    
    if len(data) &amp;gt; 0:
        return data[0]['trade_price']  # 현재 거래 가격 반환
    else:
        return None
    
#코인의 캔들데이터를 가져오는 함수
def get_coin_data(coin):
    url = &quot;https://api.upbit.com/v1/candles/minutes/1&quot;  # Adjusted to minutes/1 endpoint for real data
    params = {  
        'market': f'KRW-{coin}',  
        'count': 100,
    }  
    headers = {&quot;accept&quot;: &quot;application/json&quot;}

    response = requests.get(url, params=params, headers=headers)
    data = response.json()
    df = pd.DataFrame(data)
    df = df.reindex(index=df.index[::-1]).reset_index(drop=True)  # Reset index after reversing
    return df

#받아온 코인 데이터를 저장할 csv파일을 만드는 함수수
def save_to_csv(df):
    filename = 'coin_data.csv'
    df.to_csv(filename, index=False)

#거래기록을 저장할 csv 파일일
columns = ['시간', '코인명', '매수/매도', '평균 단가','현재 보유잔고']
df_trade = pd.DataFrame(columns=columns)
df_trade.to_csv('trade_list.csv', index=False, encoding='utf-8-sig')

#거래기록을 추가하는 함수
def add_trade(trade_time, coin_name, trade_type, avg_price,hold_won):
    # CSV 파일 읽기
    df = pd.read_csv('trade_list.csv', encoding='utf-8-sig')
    
    # 새로운 데이터 추가
    new_data = {
        '시간': trade_time,
        '코인명': coin_name,
        '매수/매도': trade_type,
        '평균 단가': avg_price,
        '현재 보유잔고':hold_won
    }
    df = df.append(new_data, ignore_index=True)
    
    # 업데이트된 데이터프레임을 CSV 파일로 저장
    df.to_csv('trade_list.csv', index=False, encoding='utf-8-sig')

#현재 보유중인 원화 잔고를 가져오는 함수
def get_balance():
    &quot;&quot;&quot;
    업비트 API를 사용하여 원화 잔고를 조회하는 함수
    &quot;&quot;&quot;
    url = server_url + '/v1/accounts'
    
    # 요청 헤더 준비
    payload = {
        'access_key': api_key,
        'nonce': str(uuid.uuid4())
    }
    
    # 서명 생성
    m = hashlib.sha512()
    m.update(payload['nonce'].encode('utf-8'))
    signature = jwt.encode(payload, secret_key, algorithm='HS256')
    
    headers = {
        'Authorization': f'Bearer {signature}'
    }
    
    # API 요청
    response = requests.get(url, headers=headers)
    
    if response.status_code == 200:
        accounts = response.json()
        for account in accounts:
            if account['currency'] == 'KRW':  # 원화 잔고 찾기
                return float(account['balance'])
    else:
        print(f&quot;Error: {response.status_code}&quot;)
        return None
    
#각종 지표를 계산하는 함수
def calculate_indicators(df):
    # 'trade_price' (종가), 'high_price', 'low_price' 컬럼 확인 및 추출
    required_columns = ['trade_price', 'high_price', 'low_price','candle_acc_trade_volume']
    for col in required_columns:
        if col not in df.columns:
            raise ValueError(f&quot;데이터에 '{col}' 컬럼이 없습니다.&quot;)
    
    close_prices = df['trade_price'].astype(float)
    high_prices = df['high_price'].astype(float)
    low_prices = df['low_price'].astype(float)
    volumes = df['candle_acc_trade_volume'].astype(float)

    # 데이터 확인
    if close_prices.isnull().any():
        raise ValueError(&quot;종가 데이터에 결측값이 있습니다.&quot;)
    if len(close_prices) &amp;lt; 10:  # Momentum 계산에 필요한 최소 데이터 길이
        raise ValueError(&quot;Momentum 지표를 계산하려면 데이터 길이가 최소 10 이상이어야 합니다.&quot;)
    
    # RSI (Relative Strength Index)
    df['RSI'] = ta.RSI(close_prices, timeperiod=14)
    rsi = df['RSI'].iloc[-1]

    # Stochastic RSI
    df['StochRSI'] = (df['RSI'] - df['RSI'].rolling(14).min()) / (df['RSI'].rolling(14).max() - df['RSI'].rolling(14).min()) * 100
    df['StochRSI_K'] = df['StochRSI'].rolling(3).mean()
    df['StochRSI_D'] = df['StochRSI_K'].rolling(3).mean()
    stoch_rsi_d = df['StochRSI_D'].iloc[-1]
    stoch_rsi_k = df['StochRSI_K'].iloc[-1]
    
    # Bollinger Bands
    df['Upper_Band'], df['Middle_Band'], df['Lower_Band'] = ta.BBANDS(
        close_prices,
        timeperiod=20,
        nbdevup=2.0,
        nbdevdn=2.0,
        matype=0
    )
    upper_band = df['Upper_Band'].iloc[-1]
    middle_band = df['Middle_Band'].iloc[-1]
    lower_band = df['Lower_Band'].iloc[-1]
    
    # MACD (Moving Average Convergence Divergence)
    df['MACD'], df['MACD_Signal'], df['MACD_Hist'] = ta.MACD(
        close_prices,
        fastperiod=12,
        slowperiod=26,
        signalperiod=9
    )
    macd = df['MACD'].iloc[-1]
    macd_signal = df['MACD_Signal'].iloc[-1]
    macd_hist = df['MACD_Hist'].iloc[-1]
    
    # TRIX (Triple Exponential Average)
    df['TRIX'] = ta.TRIX(
        close_prices,
        timeperiod=5
    )
    trix = df['TRIX'].iloc[-1]

    df['MFI'] = ta.MFI(
        high=high_prices,
        low=low_prices,
        close=close_prices,
        volume=volumes,
        timeperiod=14
    )
    mfi = df['MFI'].iloc[-1]

    df['Tenkan_sen'] = (high_prices.rolling(window=9).max() + low_prices.rolling(window=9).min()) / 2
    df['Kijun_sen'] = (high_prices.rolling(window=26).max() + low_prices.rolling(window=26).min()) / 2
    tenkan_sen = df['Tenkan_sen'].iloc[-1]  # 전환선 최신 값
    kijun_sen = df['Kijun_sen'].iloc[-1]    # 기준선 최신 값

    # Save data to CSV
    save_to_csv(df)
    
    # Return all indicator values
    return (
        upper_band, middle_band, lower_band, macd, macd_signal, macd_hist, trix, mfi, rsi, stoch_rsi_d,stoch_rsi_k,tenkan_sen,kijun_sen
    )

def preprocess_data(df):
    # 필요한 지표만 추출
    features = [
        'Upper_Band', 'Middle_Band', 'Lower_Band', 'MACD', 'MACD_Signal', 
        'MACD_Hist', 'TRIX', 'MFI', 'RSI', 'StochRSI_D', 'StochRSI_K', 'Tenkan_sen', 'Kijun_sen'
    ]

    # 결측값 제거
    df = df.dropna(subset=features).copy()  # .copy()를 추가하여 경고 제거

    # 목표 변수 설정 (다음 가격이 상승/하락)
    df['Target'] = (df['trade_price'].shift(-1) &amp;gt; df['trade_price']).astype(int)

    # 결측값 제거 (Target 열에 결측값이 생길 수 있으므로 제거)
    df = df.dropna(subset=['Target']).copy()  # .copy() 추가

    # 입력 (X)와 출력 (y) 분리
    X = df[features].values
    y = df['Target'].values

    # 데이터 스케일링
    scaler = StandardScaler()
    X_scaled = scaler.fit_transform(X)

    return X_scaled, y, scaler

# 모델 생성 함수
def create_model(input_dim):
    model = Sequential([
        Dense(64, activation='relu', input_dim=input_dim),
        Dropout(0.3),
        Dense(32, activation='relu'),
        Dropout(0.3),
        Dense(16, activation='relu'),
        Dense(2, activation='softmax')  # 확률 출력
    ])

    model.compile(optimizer=Adam(learning_rate=0.001), 
                  loss='sparse_categorical_crossentropy', 
                  metrics=['accuracy'])
    return model


# 학습 및 예측 함수
def train_and_predict(df):
    # 데이터 전처리
    X, y, scaler = preprocess_data(df)

    # 데이터 분할
    X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state = 42)

    # 모델 생성
    model = create_model(input_dim=X.shape[1])

    # 모델 학습
    model.fit(X_train, y_train, epochs=50, batch_size=32, validation_data=(X_test, y_test),verbose=0)

    # 예측 수행
    last_sample = X[-1].reshape(1, -1)  # 마지막 샘플 예측
    probabilities = model.predict(last_sample)[0]

    return {
        'probability_up': probabilities[1],
        'probability_down': probabilities[0]
    }


api_key, secret_key = load_api_keys(&quot;upbit_config.json&quot;)
# coin_list = input(&quot;조회할 코인 심볼들을 입력하세요 (쉼표로 구분): &quot;).upper().replace(&quot; &quot;, &quot;&quot;).split(',')

while True:
    coin_holdings = get_coin_holdings(api_key, secret_key)
    money = int(get_balance() * 0.95)
    if len(coin_holdings)==1:
        coin_list = get_top_gainers_upbit(5)
        for coin in coin_list:
            market = f'KRW-{coin}'
            status_coin_price = get_current_price(coin)
            coin_order_unit = 0
            if status_coin_price &amp;gt;= 2000000:
                coin_order_unit = 1000
            elif status_coin_price &amp;gt;=1000000 and status_coin_price&amp;lt;2000000:
                coin_order_unit = 500
            elif status_coin_price &amp;gt;=500000 and status_coin_price &amp;lt;1000000:
                coin_order_unit = 100
            elif status_coin_price &amp;gt;=100000 and status_coin_price&amp;lt;500000:
                coin_order_unit = 50
            elif status_coin_price &amp;gt;=10000 and status_coin_price&amp;lt;100000:
                coin_order_unit = 10
            elif status_coin_price &amp;gt;=1000 and status_coin_price&amp;lt;10000:
                coin_order_unit = 1
            elif status_coin_price &amp;gt;=100 and status_coin_price&amp;lt;1000:
                coin_order_unit = 0.1
                if coin in special_coin_list:
                    coin_order_unit = 1
            elif status_coin_price &amp;gt;=10 and status_coin_price&amp;lt;100:
                coin_order_unit = 0.01
            elif status_coin_price &amp;gt;= 1 and status_coin_price&amp;lt;10:
                coin_order_unit = 0.001
            elif status_coin_price&amp;gt;=0.1 and status_coin_price&amp;lt;1:
                coin_order_unit = 0.0001
            elif status_coin_price&amp;gt;=0.01 and status_coin_price&amp;lt;0.1:
                coin_order_unit = 0.00001
            elif status_coin_price&amp;gt;=0.001 and status_coin_price&amp;lt;0.01:
                coin_order_unit = 0.000001
            elif status_coin_price&amp;gt;=0.0001 and status_coin_price&amp;lt;0.001 :
                coin_order_unit = 0.0000001
            else :
                coin_order_unit = 0.00000001
            df = get_coin_data(coin)  # 데이터 가져오기
            # df = df.iloc[:-1]
            indicators = calculate_indicators(df)  # 지표 계산
            prediction = train_and_predict(df)  # 상승/하락 확률 계산
            
            print('----------------------------------------------')
            print(f'{coin}의 현재 가격 : {status_coin_price}원')
            print('')
            print(f'현재 {coin}코인의 상승확률 : {prediction[&quot;probability_up&quot;]*100:.3f}%')

            while prediction['probability_up']&amp;gt;0.7:
                orderbook = get_orderbook(api_key, market)
                buy_price = orderbook['bid_price'] + coin_order_unit
                buy_uuid = place_limit_buy_order(api_key,secret_key,market,buy_price,money)
                unfilled_orders = check_unfilled_orders(api_key, secret_key)
                if unfilled_orders:
                    print('지정가 매수 실패, 미체결 주문 취소 작업 중..')
                    for order in unfilled_orders:
                        cancel_result = cancel_unfilled_order(api_key, secret_key, buy_uuid)
                else:
                    current_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
                    my_money_info,total_balance = get_upbit_account_info(api_key,secret_key)
                    add_trade(current_time, f'{coin}', 'buy', buy_price,total_balance)
                    break
                df = get_coin_data(coin)  # 데이터 가져오기
                # df = df.iloc[:-1]
                indicators = calculate_indicators(df)  # 지표 계산
                prediction = train_and_predict(df)  # 상승/하락 확률 계산
            if len(get_coin_holdings(api_key,secret_key))&amp;gt;1:
                break
            print('')
        
    else:
        print('----------------------------------------------')
        hold_coin = list(coin_holdings.keys())[1]
        hold_coin_amount = float(coin_holdings[hold_coin])
        status_coin_price = get_current_price(hold_coin)
        coin_order_unit = 0
        market = f'KRW-{hold_coin}'
        if status_coin_price &amp;gt;= 2000000:
            coin_order_unit = 1000
        elif status_coin_price &amp;gt;=1000000 and status_coin_price&amp;lt;2000000:
            coin_order_unit = 500
        elif status_coin_price &amp;gt;=500000 and status_coin_price &amp;lt;1000000:
            coin_order_unit = 100
        elif status_coin_price &amp;gt;=100000 and status_coin_price&amp;lt;500000:
            coin_order_unit = 50
        elif status_coin_price &amp;gt;=10000 and status_coin_price&amp;lt;100000:
            coin_order_unit = 10
        elif status_coin_price &amp;gt;=1000 and status_coin_price&amp;lt;10000:
            coin_order_unit = 1
        elif status_coin_price &amp;gt;=100 and status_coin_price&amp;lt;1000:
            coin_order_unit = 0.1
            if hold_coin in special_coin_list:
                coin_order_unit = 1
        elif status_coin_price &amp;gt;=10 and status_coin_price&amp;lt;100:
            coin_order_unit = 0.01
        elif status_coin_price &amp;gt;= 1 and status_coin_price&amp;lt;10:
            coin_order_unit = 0.001
        elif status_coin_price&amp;gt;=0.1 and status_coin_price&amp;lt;1:
            coin_order_unit = 0.0001
        elif status_coin_price&amp;gt;=0.01 and status_coin_price&amp;lt;0.1:
            coin_order_unit = 0.00001
        elif status_coin_price&amp;gt;=0.001 and status_coin_price&amp;lt;0.01:
            coin_order_unit = 0.000001
        elif status_coin_price&amp;gt;=0.0001 and status_coin_price&amp;lt;0.001 :
            coin_order_unit = 0.0000001
        else :
            coin_order_unit = 0.00000001
        df = get_coin_data(hold_coin)  # 데이터 가져오기
        # df = df.iloc[:-1]
        indicators = calculate_indicators(df)  # 지표 계산
        prediction = train_and_predict(df)  # 상승/하락 확률 계산
    
        print(f'{hold_coin}의 현재 가격 : {status_coin_price}원')
        print('')
        print(f'현재 코인의 상승확률 : {prediction[&quot;probability_up&quot;]*100:.3f}')
        my_money_info,total_balance = get_upbit_account_info(api_key,secret_key)
        while my_money_info[0]['수익률']&amp;lt;=-1.0 or my_money_info[0]['수익률']&amp;gt;=1.5:
            orderbook = get_orderbook(api_key, market)
            status_profit_ratio = get_upbit_account_info(api_key,secret_key)
            sell_price = orderbook['ask_price']-coin_order_unit
            sell_uuid = place_limit_sell_order(api_key,secret_key,market,sell_price,hold_coin_amount)
            if len(check_unfilled_orders(api_key,secret_key))&amp;gt;0:
                cancel_result = cancel_unfilled_order(api_key,secret_key,sell_uuid)
            else:
                current_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
                my_money_info,total_balance = get_upbit_account_info(api_key,secret_key)
                add_trade(current_time, f'{hold_coin}', 'sell', sell_price,total_balance)
                break
            my_money_info,total_balance = get_upbit_account_info(api_key,secret_key)

        while prediction['probability_down']&amp;gt;=0.6 :
            orderbook = get_orderbook(api_key, market)
            market = f'KRW-{hold_coin}'
            sell_price = orderbook['ask_price'] - coin_order_unit
            sell_uuid = place_limit_sell_order(api_key,secret_key,market,sell_price,hold_coin_amount)
            unfilled_orders = check_unfilled_orders(api_key, secret_key)
            if unfilled_orders:
                print('지정가 매도 실패, 미체결 주문 취소 작업 중..')
                for order in unfilled_orders:
                    cancel_result = cancel_unfilled_order(api_key, secret_key, sell_uuid)
            else:
                current_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
                my_money_info,total_balance = get_upbit_account_info(api_key,secret_key)
                add_trade(current_time, f'{hold_coin}', 'sell', sell_price,total_balance)
                break
            df = get_coin_data(hold_coin)  # 데이터 가져오기
            # df = df.iloc[:-1]
            indicators = calculate_indicators(df)  # 지표 계산
            prediction = train_and_predict(df)  # 상승/하락 확률 계산&lt;/code&gt;&lt;/pre&gt;</description>
      <category>혼자 만든 Code/upbit 코인 자동거래</category>
      <author>빛하루</author>
      <guid isPermaLink="true">https://harucode.tistory.com/861</guid>
      <comments>https://harucode.tistory.com/861#entry861comment</comments>
      <pubDate>Sat, 18 Jan 2025 10:24:22 +0900</pubDate>
    </item>
    <item>
      <title>업비트 코인 자동거래 ver 1.3 (AI 예측 매매)</title>
      <link>https://harucode.tistory.com/860</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;업비트에서 1분간격 코인 데이터 200개 받아와서 각종 지표를 계산해 데이터에 추가시킨뒤&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;tensorflow로 학습시켜 가격의 상승확률, 하락확률을 계산 후&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상승확률 85% 이상이면 매수, 하락확률 62% 이상이면 매도하는 프로그램 (수익률 -1.5%이하면 시장가 전액매도)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1737128665806&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import pandas as pd
import requests
import talib as ta
import time
import jwt
import uuid
import json
from urllib.parse import urlencode
import pyupbit
import numpy as np
import hashlib
from urllib.parse import urlencode, unquote
import matplotlib.pyplot as plt
import csv
import os
from datetime import datetime
import pytz
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, Dropout
from tensorflow.keras.optimizers import Adam
import tensorflow as tf

# TensorFlow 로그 메시지 숨기기
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3'
tf.get_logger().setLevel('ERROR')

server_url = 'https://api.upbit.com'
special_coin_list = ['ADA','ALGO','BLUR','CELO', 'ELF', 'EOS', 'GRS', 'GRT', 'ICX', 'MANA', 'MINA', 'POL', 'SAND', 'SEI', 'STG', 'TRX']

#전일 대비 가격 상승률 상위 n개 종목을 가져오는 함수

def get_top_gainers_upbit(number):
    base_url = &quot;https://api.upbit.com/v1&quot;

    try:
        # Step 1: Get market data
        markets_response = requests.get(f&quot;{base_url}/market/all&quot;)
        markets_response.raise_for_status()
        markets = markets_response.json()

        # Filter for KRW markets only and extract symbols
        krw_symbols = [market['market'].split('-')[1] for market in markets if market['market'].startswith('KRW-')]

        # Step 2: Get ticker data for all KRW markets
        krw_markets = [f&quot;KRW-{symbol}&quot; for symbol in krw_symbols]
        tickers_response = requests.get(f&quot;{base_url}/ticker&quot;, params={&quot;markets&quot;: &quot;,&quot;.join(krw_markets)})
        tickers_response.raise_for_status()
        tickers = tickers_response.json()

        # Step 3: Calculate price change percentages and filter for &amp;gt;3% gainers
        gainers = []
        for ticker in tickers:
            market = ticker['market']
            symbol = market.split('-')[1]
            prev_close = ticker['prev_closing_price']  # 전일 종가
            current_price = ticker['trade_price']     # 현재가

            except_coin = ['USDC','USDT','BTC','ETH','BCH','AAVE','SOL','BSV','AVAX','EGLD']
            
            if prev_close &amp;gt; 0 and symbol not in except_coin:  # 유효한 가격만 계산
                change_percent = ((current_price - prev_close) / prev_close) * 100
            
                gainers.append({
                    'symbol': symbol,
                    'change_percent': change_percent,
                    'current_price': current_price
                })

        # Step 4: Sort by price change percentage in descending order
        gainers = sorted(gainers, key=lambda x: x['change_percent'], reverse=True)

        # Step 5: Return top 5 gainers
        return [gainer['symbol'] for gainer in gainers[:number]]

    except requests.exceptions.RequestException as e:
        print(f&quot;Error while fetching data from Upbit API: {e}&quot;)
        return []
    
# JSON 파일에서 API 키 불러오기
def load_api_keys(config_file=&quot;upbit_config.json&quot;):
    try:
        with open(config_file, &quot;r&quot;) as file:
            keys = json.load(file)
            return keys[&quot;api_key&quot;], keys[&quot;secret_key&quot;]
    except Exception as e:
        print(f&quot;Error loading API keys: {e}&quot;)
        return None, None
    
# 현재 업비트 계좌 정보를 불러오는 함수
def get_upbit_account_info(api_key, secret_key):
    base_url = &quot;https://api.upbit.com/v1&quot;

    # JWT 토큰 생성
    payload = {
        'access_key': api_key,
        'nonce': str(uuid.uuid4()),
    }
    jwt_token = jwt.encode(payload, secret_key, algorithm=&quot;HS256&quot;)
    authorization_token = f&quot;Bearer {jwt_token}&quot;
    
    headers = {
        &quot;Authorization&quot;: authorization_token,
    }

    # 계좌 조회 API 호출
    try:
        account_res = requests.get(f&quot;{base_url}/accounts&quot;, headers=headers)
        account_data = account_res.json()
        if account_res.status_code != 200:
            print(f&quot;Error fetching account data: {account_data}&quot;)
            return

        # 코인별 정보 조회
        market_data = requests.get(f&quot;{base_url}/market/all&quot;).json()
        market_dict = {item['market']: item['korean_name'] for item in market_data}

        total_balance = 0
        coin_info_list = []

        for account in account_data:
            if float(account['balance']) &amp;gt; 0:  # 잔액이 0보다 클 경우
                ticker = account['currency']
                if ticker == &quot;KRW&quot;:
                    total_balance += float(account['balance'])
                    continue
                
                market = f&quot;KRW-{ticker}&quot;
                avg_buy_price = float(account['avg_buy_price'])
                balance = float(account['balance'])

                # 현재 가격 조회
                ticker_url = f&quot;{base_url}/ticker?markets={market}&quot;
                ticker_res = requests.get(ticker_url).json()
                current_price = float(ticker_res[0]['trade_price'])
                
                # 평가 금액과 수익률 계산
                eval_amount = balance * current_price
                profit_rate = ((current_price - avg_buy_price) / avg_buy_price) * 100
                
                # 총 자산에 반영
                total_balance += eval_amount
                
                coin_info_list.append({
                    &quot;코인&quot;: market_dict.get(market, ticker),
                    &quot;보유수량&quot;: balance,
                    &quot;평균단가&quot;: avg_buy_price,
                    &quot;현재가격&quot;: current_price,
                    &quot;수익률&quot;: profit_rate,
                })

        # 출력
        print(f&quot;총 자산: {total_balance:,.0f} KRW&quot;)
        print('')
        print(&quot;보유 코인 정보:&quot;)
        print(coin_info_list)
        return coin_info_list,total_balance
    except Exception as e:
        print(f&quot;An error occurred: {e}&quot;)

# 보유중인 코인의 심볼과 수량을 가져오는 함수
def get_coin_holdings(api_key, secret_key):
    &quot;&quot;&quot;
    업비트 API를 사용하여 보유 중인 코인의 종류와 수량을 가져옵니다.

    Returns:
        dict: 코인의 종류와 보유 수량을 포함한 딕셔너리
    &quot;&quot;&quot;
    base_url = &quot;https://api.upbit.com/v1/accounts&quot;

    # JWT 토큰 생성
    payload = {
        'access_key': api_key,
        'nonce': str(uuid.uuid4()),
    }
    jwt_token = jwt.encode(payload, secret_key, algorithm=&quot;HS256&quot;)
    headers = {&quot;Authorization&quot;: f&quot;Bearer {jwt_token}&quot;}

    try:
        # 계좌 정보 요청
        response = requests.get(base_url, headers=headers)
        response.raise_for_status()
        accounts = response.json()

        # 보유 코인 정보 필터링
        holdings = {account['currency']: float(account['balance']) for account in accounts if float(account['balance']) &amp;gt; 0}

        # 보유 코인이 없을 경우 처리
        if not holdings:
            print(&quot;보유 중인 코인이 없습니다.&quot;)
            return {}

        return holdings

    except Exception as e:
        print(f&quot;Error fetching coin holdings: {e}&quot;)
        return {}

#미체결된 주문 조회 함수
def check_unfilled_orders(api_key, secret_key):
    &quot;&quot;&quot;
    미체결 주문이 있는지 확인합니다.

    Returns:
        list: 미체결 주문 리스트 (없으면 빈 리스트)
    &quot;&quot;&quot;
    base_url = &quot;https://api.upbit.com/v1/orders&quot;
    query = {
        'state': 'wait',  # 'wait' 상태는 미체결 주문을 의미
    }

    # Query Hash 계산
    query_string = '&amp;amp;'.join([f&quot;{key}={value}&quot; for key, value in query.items()])
    query_hash = hashlib.sha512(query_string.encode()).hexdigest()

    # JWT 토큰 생성
    payload = {
        'access_key': api_key,
        'nonce': str(uuid.uuid4()),
        'query_hash': query_hash,
        'query_hash_alg': 'SHA512',
    }
    jwt_token = jwt.encode(payload, secret_key, algorithm=&quot;HS256&quot;)
    headers = {&quot;Authorization&quot;: f&quot;Bearer {jwt_token}&quot;}

    try:
        # 미체결 주문 요청
        response = requests.get(base_url, headers=headers, params=query)
        response.raise_for_status()
        orders = response.json()

        # 미체결 주문이 없는 경우 처리
        if not orders:
            print(&quot;미체결 주문이 없습니다.&quot;)
            return []

        print(&quot;미체결 주문이 있습니다.&quot;)
        return orders

    except Exception as e:
        print(f&quot;Error fetching unfilled orders: {e}&quot;)
        return []
    
#미체결된 주문 취소하는 함수
def cancel_unfilled_order(api_key, secret_key, order_id):
    &quot;&quot;&quot;
    특정 미체결 주문을 취소합니다.

    Args:
        api_key (str): 업비트 API 키
        secret_key (str): 업비트 Secret 키
        order_id (str): 취소할 주문의 UUID

    Returns:
        dict: 주문 취소 결과
    &quot;&quot;&quot;
    base_url = &quot;https://api.upbit.com/v1/order&quot;
    query = {
        'uuid': order_id,
    }

    # JWT 토큰 생성
    query_string = '&amp;amp;'.join([f&quot;{key}={value}&quot; for key, value in query.items()])
    payload = {
        'access_key': api_key,
        'nonce': str(uuid.uuid4()),
        'query_hash': hashlib.sha512(query_string.encode()).hexdigest(),
        'query_hash_alg': 'SHA512',
    }
    jwt_token = jwt.encode(payload, secret_key, algorithm=&quot;HS256&quot;)
    headers = {&quot;Authorization&quot;: f&quot;Bearer {jwt_token}&quot;}

    try:
        # 주문 취소 요청
        response = requests.delete(base_url, headers=headers, params=query)
        response.raise_for_status()
        cancel_result = response.json()

        print(f&quot;주문이 성공적으로 취소되었습니다: {cancel_result}&quot;)
        return cancel_result

    except requests.exceptions.HTTPError as e:
        print(f&quot;HTTP 에러 발생: {e.response.status_code}, {e.response.text}&quot;)
        return {}
    except Exception as e:
        print(f&quot;Error canceling order: {e}&quot;)
        return {}
    
#매수 1호가와 매도1호가 리턴하는 함수    
def get_orderbook(api_key, market):
    &quot;&quot;&quot;
    주어진 마켓의 매수, 매도 1호가를 가져옵니다.

    Args:
        api_key (str): 업비트 API 키
        market (str): 마켓 코드 (예: &quot;KRW-BTC&quot;)

    Returns:
        dict: 매수 1호가와 매도 1호가를 포함한 딕셔너리
    &quot;&quot;&quot;
    url = f&quot;https://api.upbit.com/v1/orderbook?markets={market}&amp;amp;level=0&quot;

    headers = {&quot;accept&quot;: &quot;application/json&quot;}

    try:
        # 주문서 정보 요청
        response = requests.get(url, headers=headers)
        response.raise_for_status()
        orderbook = response.json()

        if not orderbook:
            print(f&quot;{market}의 주문서 정보가 없습니다.&quot;)
            return None

        # 매수 1호가 (가장 높은 매도 호가)
        bid_price = orderbook[0]['orderbook_units'][0]['bid_price']
        # 매도 1호가 (가장 낮은 매수 호가)
        ask_price = orderbook[0]['orderbook_units'][0]['ask_price']

        return {&quot;bid_price&quot;: bid_price, &quot;ask_price&quot;: ask_price}

    except Exception as e:
        print(f&quot;Error fetching orderbook: {e}&quot;)
        return None
    
#지정가 매수함수    
def place_limit_buy_order(api_key, secret_key, market, price, budget):
    max_quantity = str(budget / price)
    params = {
        'market': market,
        'side': 'bid',
        'ord_type': 'limit',
        'price': price,
        'volume': max_quantity
    }
    query_string = unquote(urlencode(params, doseq=True)).encode(&quot;utf-8&quot;)

    m = hashlib.sha512()
    m.update(query_string)
    query_hash = m.hexdigest()

    payload = {
        'access_key': api_key,
        'nonce': str(uuid.uuid4()),
        'query_hash': query_hash,
        'query_hash_alg': 'SHA512',
    }

    jwt_token = jwt.encode(payload, secret_key)
    authorization = 'Bearer {}'.format(jwt_token)
    headers = {
    'Authorization': authorization,
    }

    res = requests.post(server_url + '/v1/orders', json=params, headers=headers)
    order_result = res.json()
    # 성공적으로 주문을 제출했다면 주문 UUID 반환
    order_uuid = order_result.get('uuid')
    if order_uuid:
        print(f&quot;매수 주문이 성공적으로 생성되었습니다. 주문 UUID: {order_uuid}&quot;)
        time.sleep(5)
        return order_uuid
    else:
        print(&quot;주문 생성 실패: UUID를 찾을 수 없습니다.&quot;)
        return None
    
# 지정가 매도함수
def place_limit_sell_order(api_key, secret_key, market, price, quantity):
    max_quantity = str(quantity)
    params = {
        'market': market,
        'side': 'ask',
        'ord_type': 'limit',
        'price': price,
        'volume': max_quantity
    }
    query_string = unquote(urlencode(params, doseq=True)).encode(&quot;utf-8&quot;)

    m = hashlib.sha512()
    m.update(query_string)
    query_hash = m.hexdigest()

    payload = {
        'access_key': api_key,
        'nonce': str(uuid.uuid4()),
        'query_hash': query_hash,
        'query_hash_alg': 'SHA512',
    }

    jwt_token = jwt.encode(payload, secret_key)
    authorization = 'Bearer {}'.format(jwt_token)
    headers = {
    'Authorization': authorization,
    }

    res = requests.post(server_url + '/v1/orders', json=params, headers=headers)
    order_result = res.json()
    # 성공적으로 주문을 제출했다면 주문 UUID 반환
    order_uuid = order_result.get('uuid')
    if order_uuid:
        print(f&quot;매도 주문이 성공적으로 생성되었습니다. 주문 UUID: {order_uuid}&quot;)
        time.sleep(5)
        return order_uuid
    else:
        print(&quot;주문 생성 실패: UUID를 찾을 수 없습니다.&quot;)
        return None
    
#코인의 현재가격을 불러오는 함수
def get_current_price(coin):
    url = f&quot;https://api.upbit.com/v1/ticker&quot;
    params = {
        'markets': f'KRW-{coin}'  # 코인 이름 입력 (예: 'BTC' -&amp;gt; 'KRW-BTC')
    }
    
    response = requests.get(url, params=params)
    data = response.json()
    
    if len(data) &amp;gt; 0:
        return data[0]['trade_price']  # 현재 거래 가격 반환
    else:
        return None
    
#코인의 캔들데이터를 가져오는 함수
def get_coin_data(coin):
    url = &quot;https://api.upbit.com/v1/candles/minutes/1&quot;  # Adjusted to minutes/1 endpoint for real data
    params = {  
        'market': f'KRW-{coin}',  
        'count': 200,
    }  
    headers = {&quot;accept&quot;: &quot;application/json&quot;}

    response = requests.get(url, params=params, headers=headers)
    data = response.json()
    df = pd.DataFrame(data)
    df = df.reindex(index=df.index[::-1]).reset_index(drop=True)  # Reset index after reversing
    return df

#받아온 코인 데이터를 저장할 csv파일을 만드는 함수수
def save_to_csv(df):
    filename = 'coin_data.csv'
    df.to_csv(filename, index=False)

#거래기록을 저장할 csv 파일일
columns = ['시간', '코인명', '매수/매도', '평균 단가','현재 보유잔고']
df_trade = pd.DataFrame(columns=columns)
df_trade.to_csv('trade_list.csv', index=False, encoding='utf-8-sig')

#거래기록을 추가하는 함수
def add_trade(trade_time, coin_name, trade_type, avg_price,hold_won):
    # CSV 파일 읽기
    df = pd.read_csv('trade_list.csv', encoding='utf-8-sig')
    
    # 새로운 데이터 추가
    new_data = {
        '시간': trade_time,
        '코인명': coin_name,
        '매수/매도': trade_type,
        '평균 단가': avg_price,
        '현재 보유잔고':hold_won
    }
    df = df.append(new_data, ignore_index=True)
    
    # 업데이트된 데이터프레임을 CSV 파일로 저장
    df.to_csv('trade_list.csv', index=False, encoding='utf-8-sig')

#현재 보유중인 원화 잔고를 가져오는 함수
def get_balance():
    &quot;&quot;&quot;
    업비트 API를 사용하여 원화 잔고를 조회하는 함수
    &quot;&quot;&quot;
    url = server_url + '/v1/accounts'
    
    # 요청 헤더 준비
    payload = {
        'access_key': api_key,
        'nonce': str(uuid.uuid4())
    }
    
    # 서명 생성
    m = hashlib.sha512()
    m.update(payload['nonce'].encode('utf-8'))
    signature = jwt.encode(payload, secret_key, algorithm='HS256')
    
    headers = {
        'Authorization': f'Bearer {signature}'
    }
    
    # API 요청
    response = requests.get(url, headers=headers)
    
    if response.status_code == 200:
        accounts = response.json()
        for account in accounts:
            if account['currency'] == 'KRW':  # 원화 잔고 찾기
                return float(account['balance'])
    else:
        print(f&quot;Error: {response.status_code}&quot;)
        return None
    
#각종 지표를 계산하는 함수
def calculate_indicators(df):
    # 'trade_price' (종가), 'high_price', 'low_price' 컬럼 확인 및 추출
    required_columns = ['trade_price', 'high_price', 'low_price','candle_acc_trade_volume']
    for col in required_columns:
        if col not in df.columns:
            raise ValueError(f&quot;데이터에 '{col}' 컬럼이 없습니다.&quot;)
    
    close_prices = df['trade_price'].astype(float)
    high_prices = df['high_price'].astype(float)
    low_prices = df['low_price'].astype(float)
    volumes = df['candle_acc_trade_volume'].astype(float)

    # 데이터 확인
    if close_prices.isnull().any():
        raise ValueError(&quot;종가 데이터에 결측값이 있습니다.&quot;)
    if len(close_prices) &amp;lt; 10:  # Momentum 계산에 필요한 최소 데이터 길이
        raise ValueError(&quot;Momentum 지표를 계산하려면 데이터 길이가 최소 10 이상이어야 합니다.&quot;)
    
    # RSI (Relative Strength Index)
    df['RSI'] = ta.RSI(close_prices, timeperiod=14)
    rsi = df['RSI'].iloc[-1]

    # Stochastic RSI
    df['StochRSI'] = (df['RSI'] - df['RSI'].rolling(14).min()) / (df['RSI'].rolling(14).max() - df['RSI'].rolling(14).min()) * 100
    df['StochRSI_K'] = df['StochRSI'].rolling(3).mean()
    df['StochRSI_D'] = df['StochRSI_K'].rolling(3).mean()
    stoch_rsi_d = df['StochRSI_D'].iloc[-1]
    stoch_rsi_k = df['StochRSI_K'].iloc[-1]
    
    # Bollinger Bands
    df['Upper_Band'], df['Middle_Band'], df['Lower_Band'] = ta.BBANDS(
        close_prices,
        timeperiod=20,
        nbdevup=2.0,
        nbdevdn=2.0,
        matype=0
    )
    upper_band = df['Upper_Band'].iloc[-1]
    middle_band = df['Middle_Band'].iloc[-1]
    lower_band = df['Lower_Band'].iloc[-1]
    
    # MACD (Moving Average Convergence Divergence)
    df['MACD'], df['MACD_Signal'], df['MACD_Hist'] = ta.MACD(
        close_prices,
        fastperiod=12,
        slowperiod=26,
        signalperiod=9
    )
    macd = df['MACD'].iloc[-1]
    macd_signal = df['MACD_Signal'].iloc[-1]
    macd_hist = df['MACD_Hist'].iloc[-1]
    
    # TRIX (Triple Exponential Average)
    df['TRIX'] = ta.TRIX(
        close_prices,
        timeperiod=5
    )
    trix = df['TRIX'].iloc[-1]

    df['MFI'] = ta.MFI(
        high=high_prices,
        low=low_prices,
        close=close_prices,
        volume=volumes,
        timeperiod=14
    )
    mfi = df['MFI'].iloc[-1]

    df['Tenkan_sen'] = (high_prices.rolling(window=9).max() + low_prices.rolling(window=9).min()) / 2
    df['Kijun_sen'] = (high_prices.rolling(window=26).max() + low_prices.rolling(window=26).min()) / 2
    tenkan_sen = df['Tenkan_sen'].iloc[-1]  # 전환선 최신 값
    kijun_sen = df['Kijun_sen'].iloc[-1]    # 기준선 최신 값

    # Save data to CSV
    save_to_csv(df)
    
    # Return all indicator values
    return (
        upper_band, middle_band, lower_band, macd, macd_signal, macd_hist, trix, mfi, rsi, stoch_rsi_d,stoch_rsi_k,tenkan_sen,kijun_sen
    )

def preprocess_data(df):
    # 필요한 지표만 추출
    features = [
        'Upper_Band', 'Middle_Band', 'Lower_Band', 'MACD', 'MACD_Signal', 
        'MACD_Hist', 'TRIX', 'MFI', 'RSI', 'StochRSI_D', 'StochRSI_K', 'Tenkan_sen', 'Kijun_sen'
    ]

    # 결측값 제거
    df = df.dropna(subset=features).copy()  # .copy()를 추가하여 경고 제거

    # 목표 변수 설정 (다음 가격이 상승/하락)
    df['Target'] = (df['trade_price'].shift(-1) &amp;gt; df['trade_price']).astype(int)

    # 결측값 제거 (Target 열에 결측값이 생길 수 있으므로 제거)
    df = df.dropna(subset=['Target']).copy()  # .copy() 추가

    # 입력 (X)와 출력 (y) 분리
    X = df[features].values
    y = df['Target'].values

    # 데이터 스케일링
    scaler = StandardScaler()
    X_scaled = scaler.fit_transform(X)

    return X_scaled, y, scaler

# 모델 생성 함수
def create_model(input_dim):
    model = Sequential([
        Dense(64, activation='relu', input_dim=input_dim),
        Dropout(0.3),
        Dense(32, activation='relu'),
        Dropout(0.3),
        Dense(16, activation='relu'),
        Dense(2, activation='softmax')  # 확률 출력
    ])

    model.compile(optimizer=Adam(learning_rate=0.001), 
                  loss='sparse_categorical_crossentropy', 
                  metrics=['accuracy'])
    return model


# 학습 및 예측 함수
def train_and_predict(df):
    # 데이터 전처리
    X, y, scaler = preprocess_data(df)

    # 데이터 분할
    X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, shuffle=False)

    # 모델 생성
    model = create_model(input_dim=X.shape[1])

    # 모델 학습
    model.fit(X_train, y_train, epochs=50, batch_size=32, validation_data=(X_test, y_test),verbose=0)

    # 예측 수행
    last_sample = X[-1].reshape(1, -1)  # 마지막 샘플 예측
    probabilities = model.predict(last_sample)[0]

    return {
        'probability_up': probabilities[1],
        'probability_down': probabilities[0]
    }


api_key, secret_key = load_api_keys(&quot;upbit_config.json&quot;)
# coin_list = input(&quot;조회할 코인 심볼들을 입력하세요 (쉼표로 구분): &quot;).upper().replace(&quot; &quot;, &quot;&quot;).split(',')

while True:
    coin_holdings = get_coin_holdings(api_key, secret_key)
    money = int(get_balance() * 0.95)
    if len(coin_holdings)==1:
        coin_list = get_top_gainers_upbit(7)
        for coin in coin_list:
            market = f'KRW-{coin}'
            status_coin_price = get_current_price(coin)
            coin_order_unit = 0
            if status_coin_price &amp;gt;= 2000000:
                coin_order_unit = 1000
            elif status_coin_price &amp;gt;=1000000 and status_coin_price&amp;lt;2000000:
                coin_order_unit = 500
            elif status_coin_price &amp;gt;=500000 and status_coin_price &amp;lt;1000000:
                coin_order_unit = 100
            elif status_coin_price &amp;gt;=100000 and status_coin_price&amp;lt;500000:
                coin_order_unit = 50
            elif status_coin_price &amp;gt;=10000 and status_coin_price&amp;lt;100000:
                coin_order_unit = 10
            elif status_coin_price &amp;gt;=1000 and status_coin_price&amp;lt;10000:
                coin_order_unit = 1
            elif status_coin_price &amp;gt;=100 and status_coin_price&amp;lt;1000:
                coin_order_unit = 0.1
                if coin in special_coin_list:
                    coin_order_unit = 1
            elif status_coin_price &amp;gt;=10 and status_coin_price&amp;lt;100:
                coin_order_unit = 0.01
            elif status_coin_price &amp;gt;= 1 and status_coin_price&amp;lt;10:
                coin_order_unit = 0.001
            elif status_coin_price&amp;gt;=0.1 and status_coin_price&amp;lt;1:
                coin_order_unit = 0.0001
            elif status_coin_price&amp;gt;=0.01 and status_coin_price&amp;lt;0.1:
                coin_order_unit = 0.00001
            elif status_coin_price&amp;gt;=0.001 and status_coin_price&amp;lt;0.01:
                coin_order_unit = 0.000001
            elif status_coin_price&amp;gt;=0.0001 and status_coin_price&amp;lt;0.001 :
                coin_order_unit = 0.0000001
            else :
                coin_order_unit = 0.00000001
            df = get_coin_data(coin)  # 데이터 가져오기
            # df = df.iloc[:-1]
            indicators = calculate_indicators(df)  # 지표 계산
            prediction = train_and_predict(df)  # 상승/하락 확률 계산
            
            print('----------------------------------------------')
            print(f'{coin}의 현재 가격 : {status_coin_price}원')
            print('')
            print(f'현재 {coin}코인의 상승확률 : {prediction[&quot;probability_up&quot;]*100:.3f}%')

            while prediction['probability_up']&amp;gt;0.85:
                orderbook = get_orderbook(api_key, market)
                buy_price = orderbook['bid_price'] + coin_order_unit
                buy_uuid = place_limit_buy_order(api_key,secret_key,market,buy_price,money)
                unfilled_orders = check_unfilled_orders(api_key, secret_key)
                if unfilled_orders:
                    print('지정가 매수 실패, 미체결 주문 취소 작업 중..')
                    for order in unfilled_orders:
                        cancel_result = cancel_unfilled_order(api_key, secret_key, buy_uuid)
                else:
                    current_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
                    my_money_info,total_balance = get_upbit_account_info(api_key,secret_key)
                    add_trade(current_time, f'{coin}', 'buy', buy_price,total_balance)
                    break
                df = get_coin_data(coin)  # 데이터 가져오기
                # df = df.iloc[:-1]
                indicators = calculate_indicators(df)  # 지표 계산
                prediction = train_and_predict(df)  # 상승/하락 확률 계산
            if len(get_coin_holdings(api_key,secret_key))&amp;gt;1:
                break
            print('')
        
    else:
        print('----------------------------------------------')
        hold_coin = list(coin_holdings.keys())[1]
        hold_coin_amount = float(coin_holdings[hold_coin])
        status_coin_price = get_current_price(hold_coin)
        coin_order_unit = 0
        market = f'KRW-{hold_coin}'
        if status_coin_price &amp;gt;= 2000000:
            coin_order_unit = 1000
        elif status_coin_price &amp;gt;=1000000 and status_coin_price&amp;lt;2000000:
            coin_order_unit = 500
        elif status_coin_price &amp;gt;=500000 and status_coin_price &amp;lt;1000000:
            coin_order_unit = 100
        elif status_coin_price &amp;gt;=100000 and status_coin_price&amp;lt;500000:
            coin_order_unit = 50
        elif status_coin_price &amp;gt;=10000 and status_coin_price&amp;lt;100000:
            coin_order_unit = 10
        elif status_coin_price &amp;gt;=1000 and status_coin_price&amp;lt;10000:
            coin_order_unit = 1
        elif status_coin_price &amp;gt;=100 and status_coin_price&amp;lt;1000:
            coin_order_unit = 0.1
            if hold_coin in special_coin_list:
                coin_order_unit = 1
        elif status_coin_price &amp;gt;=10 and status_coin_price&amp;lt;100:
            coin_order_unit = 0.01
        elif status_coin_price &amp;gt;= 1 and status_coin_price&amp;lt;10:
            coin_order_unit = 0.001
        elif status_coin_price&amp;gt;=0.1 and status_coin_price&amp;lt;1:
            coin_order_unit = 0.0001
        elif status_coin_price&amp;gt;=0.01 and status_coin_price&amp;lt;0.1:
            coin_order_unit = 0.00001
        elif status_coin_price&amp;gt;=0.001 and status_coin_price&amp;lt;0.01:
            coin_order_unit = 0.000001
        elif status_coin_price&amp;gt;=0.0001 and status_coin_price&amp;lt;0.001 :
            coin_order_unit = 0.0000001
        else :
            coin_order_unit = 0.00000001
        df = get_coin_data(hold_coin)  # 데이터 가져오기
        # df = df.iloc[:-1]
        indicators = calculate_indicators(df)  # 지표 계산
        prediction = train_and_predict(df)  # 상승/하락 확률 계산
    
        print(f'{hold_coin}의 현재 가격 : {status_coin_price}원')
        print('')
        print(f'현재 코인의 상승확률 : {prediction[&quot;probability_up&quot;]*100:.3f}')
        my_money_info,total_balance = get_upbit_account_info(api_key,secret_key)
        while my_money_info[0]['수익률']&amp;lt;=-1.5:
            orderbook = get_orderbook(api_key, market)
            status_profit_ratio = get_upbit_account_info(api_key,secret_key)
            sell_price = orderbook['ask_price']-coin_order_unit
            sell_uuid = place_limit_sell_order(api_key,secret_key,market,sell_price,hold_coin_amount)
            if len(check_unfilled_orders(api_key,secret_key))&amp;gt;0:
                cancel_result = cancel_unfilled_order(api_key,secret_key,sell_uuid)
            else:
                current_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
                my_money_info,total_balance = get_upbit_account_info(api_key,secret_key)
                add_trade(current_time, f'{hold_coin}', 'sell', sell_price,total_balance)
                break
            my_money_info,total_balance = get_upbit_account_info(api_key,secret_key)

        while prediction['probability_down']&amp;gt;=0.62:
            orderbook = get_orderbook(api_key, market)
            market = f'KRW-{hold_coin}'
            sell_price = orderbook['ask_price'] - coin_order_unit
            sell_uuid = place_limit_sell_order(api_key,secret_key,market,sell_price,hold_coin_amount)
            unfilled_orders = check_unfilled_orders(api_key, secret_key)
            if unfilled_orders:
                print('지정가 매도 실패, 미체결 주문 취소 작업 중..')
                for order in unfilled_orders:
                    cancel_result = cancel_unfilled_order(api_key, secret_key, sell_uuid)
            else:
                current_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
                my_money_info,total_balance = get_upbit_account_info(api_key,secret_key)
                add_trade(current_time, f'{hold_coin}', 'sell', sell_price,total_balance)
                break
            df = get_coin_data(hold_coin)  # 데이터 가져오기
            # df = df.iloc[:-1]
            indicators = calculate_indicators(df)  # 지표 계산
            prediction = train_and_predict(df)  # 상승/하락 확률 계산&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>혼자 만든 Code/upbit 코인 자동거래</category>
      <author>빛하루</author>
      <guid isPermaLink="true">https://harucode.tistory.com/860</guid>
      <comments>https://harucode.tistory.com/860#entry860comment</comments>
      <pubDate>Sat, 18 Jan 2025 00:44:36 +0900</pubDate>
    </item>
    <item>
      <title>업비트 코인 자동거래 프로그램 v1.2(매매 기준 변경)</title>
      <link>https://harucode.tistory.com/859</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;모니터링 하며 거래를 진행할 코인 심볼(콤마로 구분) 입력,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;매수 기준&lt;/b&gt;&lt;/span&gt; : 볼린저밴드 표준편차 2.0기준 하단보다 가격이 떨어지고 stoch RSI %d 값이 5 이하일경우&amp;nbsp; 보유중 자산의 95%를 매수1호가 + 코인 호가단위로 지정가 매수&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;매도 기준&lt;/span&gt;&lt;/b&gt; : 볼린저밴드 표준편차 1.0기준 상단보다 가격이 오르거나 stoch RSI %d 값이 80이상일 경우 보유중인 코인 전량을 매도1호가 - 코인 호가단위로 지정가 매도, 만약 수익률이 현재 -1.5%이하일시 전액 시장가 매도&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1737096308822&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import pandas as pd
import requests
import talib as ta
import time
import jwt
import uuid
import json
from urllib.parse import urlencode
import pyupbit
import numpy as np
import hashlib
from urllib.parse import urlencode, unquote
import matplotlib.pyplot as plt
import csv
import os
from datetime import datetime
import pytz

server_url = 'https://api.upbit.com'
special_coin_list = ['ADA','ALGO','BLUR','CELO', 'ELF', 'EOS', 'GRS', 'GRT', 'ICX', 'MANA', 'MINA', 'POL', 'SAND', 'SEI', 'STG', 'TRX']

import requests

def get_top_gainers_upbit():
    base_url = &quot;https://api.upbit.com/v1&quot;

    try:
        # Step 1: Get market data
        markets_response = requests.get(f&quot;{base_url}/market/all&quot;)
        markets_response.raise_for_status()
        markets = markets_response.json()

        # Filter for KRW markets only and extract symbols
        krw_symbols = [market['market'].split('-')[1] for market in markets if market['market'].startswith('KRW-')]

        # Step 2: Get ticker data for all KRW markets
        krw_markets = [f&quot;KRW-{symbol}&quot; for symbol in krw_symbols]
        tickers_response = requests.get(f&quot;{base_url}/ticker&quot;, params={&quot;markets&quot;: &quot;,&quot;.join(krw_markets)})
        tickers_response.raise_for_status()
        tickers = tickers_response.json()

        # Step 3: Calculate price change percentages and filter for &amp;gt;3% gainers
        gainers = []
        for ticker in tickers:
            market = ticker['market']
            symbol = market.split('-')[1]
            prev_close = ticker['prev_closing_price']  # 전일 종가
            current_price = ticker['trade_price']     # 현재가

            except_coin = ['USDC','USDT','BTC','ETH','BCH','AAVE','SOL','BSV','AVAX','EGLD']
            
            if prev_close &amp;gt; 0 and symbol not in except_coin:  # 유효한 가격만 계산
                change_percent = ((current_price - prev_close) / prev_close) * 100
            
                gainers.append({
                    'symbol': symbol,
                    'change_percent': change_percent,
                    'current_price': current_price
                })

        # Step 4: Sort by price change percentage in descending order
        gainers = sorted(gainers, key=lambda x: x['change_percent'], reverse=True)

        # Step 5: Return top 5 gainers
        return [gainer['symbol'] for gainer in gainers[:7]]

    except requests.exceptions.RequestException as e:
        print(f&quot;Error while fetching data from Upbit API: {e}&quot;)
        return []


# JSON 파일에서 API 키 불러오기
def load_api_keys(config_file=&quot;upbit_config.json&quot;):
    try:
        with open(config_file, &quot;r&quot;) as file:
            keys = json.load(file)
            return keys[&quot;api_key&quot;], keys[&quot;secret_key&quot;]
    except Exception as e:
        print(f&quot;Error loading API keys: {e}&quot;)
        return None, None
    
def get_upbit_account_info(api_key, secret_key):
    base_url = &quot;https://api.upbit.com/v1&quot;

    # JWT 토큰 생성
    payload = {
        'access_key': api_key,
        'nonce': str(uuid.uuid4()),
    }
    jwt_token = jwt.encode(payload, secret_key, algorithm=&quot;HS256&quot;)
    authorization_token = f&quot;Bearer {jwt_token}&quot;
    
    headers = {
        &quot;Authorization&quot;: authorization_token,
    }

    # 계좌 조회 API 호출
    try:
        account_res = requests.get(f&quot;{base_url}/accounts&quot;, headers=headers)
        account_data = account_res.json()
        if account_res.status_code != 200:
            print(f&quot;Error fetching account data: {account_data}&quot;)
            return

        # 코인별 정보 조회
        market_data = requests.get(f&quot;{base_url}/market/all&quot;).json()
        market_dict = {item['market']: item['korean_name'] for item in market_data}

        total_balance = 0
        coin_info_list = []

        for account in account_data:
            if float(account['balance']) &amp;gt; 0:  # 잔액이 0보다 클 경우
                ticker = account['currency']
                if ticker == &quot;KRW&quot;:
                    total_balance += float(account['balance'])
                    continue
                
                market = f&quot;KRW-{ticker}&quot;
                avg_buy_price = float(account['avg_buy_price'])
                balance = float(account['balance'])

                # 현재 가격 조회
                ticker_url = f&quot;{base_url}/ticker?markets={market}&quot;
                ticker_res = requests.get(ticker_url).json()
                current_price = float(ticker_res[0]['trade_price'])
                
                # 평가 금액과 수익률 계산
                eval_amount = balance * current_price
                profit_rate = ((current_price - avg_buy_price) / avg_buy_price) * 100
                
                # 총 자산에 반영
                total_balance += eval_amount
                
                coin_info_list.append({
                    &quot;코인&quot;: market_dict.get(market, ticker),
                    &quot;보유수량&quot;: balance,
                    &quot;평균단가&quot;: avg_buy_price,
                    &quot;현재가격&quot;: current_price,
                    &quot;수익률&quot;: profit_rate,
                })

        # 출력
        print(f&quot;총 자산: {total_balance:,.0f} KRW&quot;)
        print('')
        print(&quot;보유 코인 정보:&quot;)
        for coin in coin_info_list:
            print(f&quot;코인: {coin['코인']}&quot;)
            print(f&quot;보유수량: {coin['보유수량']}&quot;)
            print(f&quot;평균단가: {coin['평균단가']}&quot;)
            print(f&quot;현재가격: {coin['현재가격']}&quot;)
            print(f&quot;수익률: {coin['수익률']:.2f}%&quot;)
            return coin['수익률']
    except Exception as e:
        print(f&quot;An error occurred: {e}&quot;)

def get_upbit_account_total_balance(api_key,secret_key):
    base_url = &quot;https://api.upbit.com/v1&quot;

    # JWT 토큰 생성
    payload = {
        'access_key': api_key,
        'nonce': str(uuid.uuid4()),
    }
    jwt_token = jwt.encode(payload, secret_key, algorithm=&quot;HS256&quot;)
    authorization_token = f&quot;Bearer {jwt_token}&quot;
    
    headers = {
        &quot;Authorization&quot;: authorization_token,
    }

    # 계좌 조회 API 호출
    try:
        account_res = requests.get(f&quot;{base_url}/accounts&quot;, headers=headers)
        account_data = account_res.json()
        if account_res.status_code != 200:
            print(f&quot;Error fetching account data: {account_data}&quot;)
            return

        # 코인별 정보 조회
        market_data = requests.get(f&quot;{base_url}/market/all&quot;).json()
        market_dict = {item['market']: item['korean_name'] for item in market_data}

        total_balance = 0
        coin_info_list = []

        for account in account_data:
            if float(account['balance']) &amp;gt; 0:  # 잔액이 0보다 클 경우
                ticker = account['currency']
                if ticker == &quot;KRW&quot;:
                    total_balance += float(account['balance'])
                    continue
                
                market = f&quot;KRW-{ticker}&quot;
                avg_buy_price = float(account['avg_buy_price'])
                balance = float(account['balance'])

                # 현재 가격 조회
                ticker_url = f&quot;{base_url}/ticker?markets={market}&quot;
                ticker_res = requests.get(ticker_url).json()
                current_price = float(ticker_res[0]['trade_price'])
                
                # 평가 금액과 수익률 계산
                eval_amount = balance * current_price
                profit_rate = ((current_price - avg_buy_price) / avg_buy_price) * 100
                
                # 총 자산에 반영
                total_balance += eval_amount
        return total_balance
    except Exception as e:
        print(f&quot;An error occurred: {e}&quot;)

def get_coin_holdings(api_key, secret_key):
    &quot;&quot;&quot;
    업비트 API를 사용하여 보유 중인 코인의 종류와 수량을 가져옵니다.

    Returns:
        dict: 코인의 종류와 보유 수량을 포함한 딕셔너리
    &quot;&quot;&quot;
    base_url = &quot;https://api.upbit.com/v1/accounts&quot;

    # JWT 토큰 생성
    payload = {
        'access_key': api_key,
        'nonce': str(uuid.uuid4()),
    }
    jwt_token = jwt.encode(payload, secret_key, algorithm=&quot;HS256&quot;)
    headers = {&quot;Authorization&quot;: f&quot;Bearer {jwt_token}&quot;}

    try:
        # 계좌 정보 요청
        response = requests.get(base_url, headers=headers)
        response.raise_for_status()
        accounts = response.json()

        # 보유 코인 정보 필터링
        holdings = {account['currency']: float(account['balance']) for account in accounts if float(account['balance']) &amp;gt; 0}

        # 보유 코인이 없을 경우 처리
        if not holdings:
            print(&quot;보유 중인 코인이 없습니다.&quot;)
            return {}

        return holdings

    except Exception as e:
        print(f&quot;Error fetching coin holdings: {e}&quot;)
        return {}
    
# 미체결된 주문 조회    
def check_unfilled_orders(api_key, secret_key):
    &quot;&quot;&quot;
    미체결 주문이 있는지 확인합니다.

    Returns:
        list: 미체결 주문 리스트 (없으면 빈 리스트)
    &quot;&quot;&quot;
    base_url = &quot;https://api.upbit.com/v1/orders&quot;
    query = {
        'state': 'wait',  # 'wait' 상태는 미체결 주문을 의미
    }

    # Query Hash 계산
    query_string = '&amp;amp;'.join([f&quot;{key}={value}&quot; for key, value in query.items()])
    query_hash = hashlib.sha512(query_string.encode()).hexdigest()

    # JWT 토큰 생성
    payload = {
        'access_key': api_key,
        'nonce': str(uuid.uuid4()),
        'query_hash': query_hash,
        'query_hash_alg': 'SHA512',
    }
    jwt_token = jwt.encode(payload, secret_key, algorithm=&quot;HS256&quot;)
    headers = {&quot;Authorization&quot;: f&quot;Bearer {jwt_token}&quot;}

    try:
        # 미체결 주문 요청
        response = requests.get(base_url, headers=headers, params=query)
        response.raise_for_status()
        orders = response.json()

        # 미체결 주문이 없는 경우 처리
        if not orders:
            print(&quot;미체결 주문이 없습니다.&quot;)
            return []

        print(&quot;미체결 주문이 있습니다.&quot;)
        return orders

    except Exception as e:
        print(f&quot;Error fetching unfilled orders: {e}&quot;)
        return []
    
#미체결된 주문 취소하는 함수
def cancel_unfilled_order(api_key, secret_key, order_id):
    &quot;&quot;&quot;
    특정 미체결 주문을 취소합니다.

    Args:
        api_key (str): 업비트 API 키
        secret_key (str): 업비트 Secret 키
        order_id (str): 취소할 주문의 UUID

    Returns:
        dict: 주문 취소 결과
    &quot;&quot;&quot;
    base_url = &quot;https://api.upbit.com/v1/order&quot;
    query = {
        'uuid': order_id,
    }

    # JWT 토큰 생성
    query_string = '&amp;amp;'.join([f&quot;{key}={value}&quot; for key, value in query.items()])
    payload = {
        'access_key': api_key,
        'nonce': str(uuid.uuid4()),
        'query_hash': hashlib.sha512(query_string.encode()).hexdigest(),
        'query_hash_alg': 'SHA512',
    }
    jwt_token = jwt.encode(payload, secret_key, algorithm=&quot;HS256&quot;)
    headers = {&quot;Authorization&quot;: f&quot;Bearer {jwt_token}&quot;}

    try:
        # 주문 취소 요청
        response = requests.delete(base_url, headers=headers, params=query)
        response.raise_for_status()
        cancel_result = response.json()

        print(f&quot;주문이 성공적으로 취소되었습니다: {cancel_result}&quot;)
        return cancel_result

    except requests.exceptions.HTTPError as e:
        print(f&quot;HTTP 에러 발생: {e.response.status_code}, {e.response.text}&quot;)
        return {}
    except Exception as e:
        print(f&quot;Error canceling order: {e}&quot;)
        return {}
    
#매수 1호가와 매도1호가 리턴하는 함수    
def get_orderbook(api_key, market=&quot;KRW-BTC&quot;):
    &quot;&quot;&quot;
    주어진 마켓의 매수, 매도 1호가를 가져옵니다.

    Args:
        api_key (str): 업비트 API 키
        market (str): 마켓 코드 (예: &quot;KRW-BTC&quot;)

    Returns:
        dict: 매수 1호가와 매도 1호가를 포함한 딕셔너리
    &quot;&quot;&quot;
    url = f&quot;https://api.upbit.com/v1/orderbook?markets={market}&amp;amp;level=0&quot;

    headers = {&quot;accept&quot;: &quot;application/json&quot;}

    try:
        # 주문서 정보 요청
        response = requests.get(url, headers=headers)
        response.raise_for_status()
        orderbook = response.json()

        if not orderbook:
            print(f&quot;{market}의 주문서 정보가 없습니다.&quot;)
            return None

        # 매수 1호가 (가장 높은 매도 호가)
        bid_price = orderbook[0]['orderbook_units'][1]['bid_price']
        # 매도 1호가 (가장 낮은 매수 호가)
        ask_price = orderbook[0]['orderbook_units'][0]['ask_price']

        return {&quot;bid_price&quot;: bid_price, &quot;ask_price&quot;: ask_price}

    except Exception as e:
        print(f&quot;Error fetching orderbook: {e}&quot;)
        return None
    
#지정가 매수함수    
def place_limit_buy_order(api_key, secret_key, market, price, budget):
    max_quantity = str(budget / price)
    params = {
        'market': market,
        'side': 'bid',
        'ord_type': 'limit',
        'price': price,
        'volume': max_quantity
    }
    query_string = unquote(urlencode(params, doseq=True)).encode(&quot;utf-8&quot;)

    m = hashlib.sha512()
    m.update(query_string)
    query_hash = m.hexdigest()

    payload = {
        'access_key': api_key,
        'nonce': str(uuid.uuid4()),
        'query_hash': query_hash,
        'query_hash_alg': 'SHA512',
    }

    jwt_token = jwt.encode(payload, secret_key)
    authorization = 'Bearer {}'.format(jwt_token)
    headers = {
    'Authorization': authorization,
    }

    res = requests.post(server_url + '/v1/orders', json=params, headers=headers)
    order_result = res.json()
    # 성공적으로 주문을 제출했다면 주문 UUID 반환
    order_uuid = order_result.get('uuid')
    if order_uuid:
        print(f&quot;매수 주문이 성공적으로 생성되었습니다. 주문 UUID: {order_uuid}&quot;)
        time.sleep(5)
        return order_uuid
    else:
        print(&quot;주문 생성 실패: UUID를 찾을 수 없습니다.&quot;)
        return None
    
# 지정가 매도함수
def place_limit_sell_order(api_key, secret_key, market, price, quantity):
    max_quantity = str(quantity)
    params = {
        'market': market,
        'side': 'ask',
        'ord_type': 'limit',
        'price': price,
        'volume': max_quantity
    }
    query_string = unquote(urlencode(params, doseq=True)).encode(&quot;utf-8&quot;)

    m = hashlib.sha512()
    m.update(query_string)
    query_hash = m.hexdigest()

    payload = {
        'access_key': api_key,
        'nonce': str(uuid.uuid4()),
        'query_hash': query_hash,
        'query_hash_alg': 'SHA512',
    }

    jwt_token = jwt.encode(payload, secret_key)
    authorization = 'Bearer {}'.format(jwt_token)
    headers = {
    'Authorization': authorization,
    }

    res = requests.post(server_url + '/v1/orders', json=params, headers=headers)
    order_result = res.json()
    # 성공적으로 주문을 제출했다면 주문 UUID 반환
    order_uuid = order_result.get('uuid')
    if order_uuid:
        print(f&quot;매도 주문이 성공적으로 생성되었습니다. 주문 UUID: {order_uuid}&quot;)
        time.sleep(5)
        return order_uuid
    else:
        print(&quot;주문 생성 실패: UUID를 찾을 수 없습니다.&quot;)
        return None

def get_current_price(coin):
    url = f&quot;https://api.upbit.com/v1/ticker&quot;
    params = {
        'markets': f'KRW-{coin}'  # 코인 이름 입력 (예: 'BTC' -&amp;gt; 'KRW-BTC')
    }
    
    response = requests.get(url, params=params)
    data = response.json()
    
    if len(data) &amp;gt; 0:
        return data[0]['trade_price']  # 현재 거래 가격 반환
    else:
        return None

def get_coin_data(coin):
    url = &quot;https://api.upbit.com/v1/candles/minutes/1&quot;  # Adjusted to minutes/1 endpoint for real data
    params = {  
        'market': f'KRW-{coin}',  
        'count': 100,
    }  
    headers = {&quot;accept&quot;: &quot;application/json&quot;}

    response = requests.get(url, params=params, headers=headers)
    data = response.json()
    df = pd.DataFrame(data)
    df = df.reindex(index=df.index[::-1]).reset_index(drop=True)  # Reset index after reversing
    return df

def save_to_csv(df):
    filename = 'coin_data.csv'
    df.to_csv(filename, index=False)

def calculate_indicators(df):
    # 'trade_price' (종가), 'high_price', 'low_price' 컬럼 확인 및 추출
    required_columns = ['trade_price', 'high_price', 'low_price','candle_acc_trade_volume']
    for col in required_columns:
        if col not in df.columns:
            raise ValueError(f&quot;데이터에 '{col}' 컬럼이 없습니다.&quot;)
    
    close_prices = df['trade_price'].astype(float)
    high_prices = df['high_price'].astype(float)
    low_prices = df['low_price'].astype(float)
    volumes = df['candle_acc_trade_volume'].astype(float)

    # 데이터 확인
    if close_prices.isnull().any():
        raise ValueError(&quot;종가 데이터에 결측값이 있습니다.&quot;)
    if len(close_prices) &amp;lt; 10:  # Momentum 계산에 필요한 최소 데이터 길이
        raise ValueError(&quot;Momentum 지표를 계산하려면 데이터 길이가 최소 10 이상이어야 합니다.&quot;)
    
    # RSI (Relative Strength Index)
    df['RSI'] = ta.RSI(close_prices, timeperiod=14)
    rsi = df['RSI'].iloc[-1]

    # Stochastic RSI
    df['StochRSI'] = (df['RSI'] - df['RSI'].rolling(14).min()) / (df['RSI'].rolling(14).max() - df['RSI'].rolling(14).min()) * 100
    df['StochRSI_K'] = df['StochRSI'].rolling(3).mean()
    df['StochRSI_D'] = df['StochRSI_K'].rolling(3).mean()
    stoch_rsi = df['StochRSI_D'].iloc[-1]
    stoch_rsi_k = df['StochRSI_K'].iloc[-1]
    
    # Bollinger Bands
    df['Upper_Band'], df['Middle_Band'], df['Lower_Band'] = ta.BBANDS(
        close_prices,
        timeperiod=20,
        nbdevup=1.0,
        nbdevdn=2.0,
        matype=0
    )
    upper_band = df['Upper_Band'].iloc[-1]
    middle_band = df['Middle_Band'].iloc[-1]
    lower_band = df['Lower_Band'].iloc[-1]
    
    # MACD (Moving Average Convergence Divergence)
    df['MACD'], df['MACD_Signal'], df['MACD_Hist'] = ta.MACD(
        close_prices,
        fastperiod=12,
        slowperiod=26,
        signalperiod=9
    )
    macd = df['MACD'].iloc[-1]
    macd_signal = df['MACD_Signal'].iloc[-1]
    macd_hist = df['MACD_Hist'].iloc[-1]
    
    # Aroon (Aroon Oscillator)
    df['Aroon_Up'], df['Aroon_Down'] = ta.AROON(
        high=high_prices,
        low=low_prices,
        timeperiod=14
    )
    aroon_up = df['Aroon_Up'].iloc[-1]
    aroon_down = df['Aroon_Down'].iloc[-1]
    
    # TRIX (Triple Exponential Average)
    df['TRIX'] = ta.TRIX(
        close_prices,
        timeperiod=5
    )
    trix = df['TRIX'].iloc[-1]
    
    # Stochastic Oscillator
    df['SlowK'], df['SlowD'] = ta.STOCH(
        high=high_prices,
        low=low_prices,
        close=close_prices,
        fastk_period=14,
        slowk_period=3,
        slowk_matype=0,
        slowd_period=3,
        slowd_matype=0
    )
    slow_k = df['SlowK'].iloc[-1]
    slow_d = df['SlowD'].iloc[-1]
    
    # ADX (Average Directional Index)
    df['ADX'] = ta.ADX(
        high=high_prices,
        low=low_prices,
        close=close_prices,
        timeperiod=14
    )
    adx = df['ADX'].iloc[-1]
    
    # CCI (Commodity Channel Index)
    df['CCI'] = ta.CCI(
        high=high_prices,
        low=low_prices,
        close=close_prices,
        timeperiod=20
    )
    cci = df['CCI'].iloc[-1]

    df['MFI'] = ta.MFI(
        high=high_prices,
        low=low_prices,
        close=close_prices,
        volume=volumes,
        timeperiod=14
    )
    mfi = df['MFI'].iloc[-1]

    # Aroon (Aroon Oscillator)
    df['Aroon_Up'], df['Aroon_Down'] = ta.AROON(
        high=high_prices,
        low=low_prices,
        timeperiod=14
    )
    aroon_up = df['Aroon_Up'].iloc[-1]
    aroon_down = df['Aroon_Down'].iloc[-1]
    df['Aroon_Osc'] = df['Aroon_Up'] - df['Aroon_Down']
    aroon_osc = df['Aroon_Osc'].iloc[-1]

    # Stochastic Momentum Index (SMI)
    high_low_avg = (high_prices + low_prices) / 2
    raw_k = close_prices - high_low_avg.rolling(10).mean()
    double_smooth_k = raw_k.rolling(3).mean().rolling(3).mean()
    range_smooth = (high_low_avg.rolling(10).max() - high_low_avg.rolling(10).min()).rolling(3).mean()
    df['SMI'] = (double_smooth_k / range_smooth) * 100
    smi = df['SMI'].iloc[-1]

    # RMI (Relative Momentum Index) 적용 시간 5, 이평 기간 14
    n = 5  # 적용 시간
    smoothing_period = 14  # 이평 기간
    diff = close_prices.diff(n)  # N일 이전과의 변화
    gain = diff.where(diff &amp;gt; 0, 0)
    loss = -diff.where(diff &amp;lt; 0, 0)
    avg_gain = gain.rolling(window=smoothing_period).mean()
    avg_loss = loss.rolling(window=smoothing_period).mean()
    df['RMI'] = 100 - (100 / (1 + (avg_gain / avg_loss)))
    rmi = df['RMI'].iloc[-1]
    
    # Save data to CSV
    save_to_csv(df)
    
    # Return all indicator values
    return (
        rsi, upper_band, middle_band, lower_band, macd, macd_signal, macd_hist, 
        aroon_up, aroon_down, trix, slow_k, slow_d, adx, cci, stoch_rsi,mfi,aroon_osc,smi,rmi,stoch_rsi_k
    )

def predict_trend(rsi, upper_band, lower_band, macd, macd_signal, aroon_up, aroon_down, trix, slow_k, slow_d, adx, cci):
    indicators = 0
    
    # 반등 예상: 5개 이상 지표가 반등 구간에 있을 때
    if rsi &amp;lt; 30: indicators += 1  # RSI
    if lower_band &amp;gt; 0: indicators += 1  # Bollinger Bands 하단 밴드에 근접
    if macd &amp;gt; macd_signal: indicators += 1  # MACD 상승
    if aroon_up &amp;gt; aroon_down: indicators += 1  # Aroon 상승
    if trix &amp;gt; 0: indicators += 1  # TRIX 상승
    if slow_k &amp;gt; slow_d: indicators += 1  # Stochastic Oscillator
    if adx &amp;gt; 25: indicators += 1  # 강한 추세
    if cci &amp;lt; -100: indicators += 1  # CCI 과매도 구간
    
    # 하락 예상: 5개 이상 지표가 하락 구간에 있을 때
    if rsi &amp;gt; 70: indicators -= 1  # RSI
    if upper_band &amp;gt; 0: indicators -= 1  # Bollinger Bands 상단 밴드에 근접
    if macd &amp;lt; macd_signal: indicators -= 1  # MACD 하락
    if aroon_down &amp;gt; aroon_up: indicators -= 1  # Aroon 하락
    if trix &amp;lt; 0: indicators -= 1  # TRIX 하락
    if slow_k &amp;lt; slow_d: indicators -= 1  # Stochastic Oscillator
    if adx &amp;lt; 20: indicators -= 1  # 약한 추세
    if cci &amp;gt; 100: indicators -= 1  # CCI 과매수 구간
    
    return indicators

def check_price_range(data):
    # 마지막 50개의 데이터 선택
    last_50_data = data.iloc[-50:]
    
    # low_price의 최소값과 high_price의 최대값 구하기
    min_low_price = last_50_data[&quot;low_price&quot;].min()
    max_high_price = last_50_data[&quot;high_price&quot;].max()
    
    # 현재 가격 (최근 1분 가격)
    current_price = data[&quot;trade_price&quot;].iloc[-1]
    
    # 가격 차이가 현재 가격의 1% 이상인지 체크
    price_difference = max_high_price - min_low_price
    price_change_ratio = price_difference/current_price * 100

    print('')
    print(f'최근 가격 50개 변동률 : {price_change_ratio:.2f}%')
    print('')
    
    if price_change_ratio&amp;gt;=1:
        return True  # 차이가 1% 이상
    else:
        return False  # 차이가 1% 미만

api_key, secret_key = load_api_keys(&quot;upbit_config.json&quot;)
coin_list = input(&quot;조회할 코인 심볼들을 입력하세요 (쉼표로 구분): &quot;).upper().replace(&quot; &quot;, &quot;&quot;).split(',')

columns = ['시간', '코인명', '매수/매도', '평균 단가','현재 보유잔고']
df_trade = pd.DataFrame(columns=columns)
df_trade.to_csv('trade_list.csv', index=False, encoding='utf-8-sig')

def add_trade(trade_time, coin_name, trade_type, avg_price,hold_won):
    # CSV 파일 읽기
    df = pd.read_csv('trade_list.csv', encoding='utf-8-sig')
    
    # 새로운 데이터 추가
    new_data = {
        '시간': trade_time,
        '코인명': coin_name,
        '매수/매도': trade_type,
        '평균 단가': avg_price,
        '현재 보유잔고':hold_won
    }
    df = df.append(new_data, ignore_index=True)
    
    # 업데이트된 데이터프레임을 CSV 파일로 저장
    df.to_csv('trade_list.csv', index=False, encoding='utf-8-sig')

def get_balance():
    &quot;&quot;&quot;
    업비트 API를 사용하여 원화 잔고를 조회하는 함수
    &quot;&quot;&quot;
    url = server_url + '/v1/accounts'
    
    # 요청 헤더 준비
    payload = {
        'access_key': api_key,
        'nonce': str(uuid.uuid4())
    }
    
    # 서명 생성
    m = hashlib.sha512()
    m.update(payload['nonce'].encode('utf-8'))
    signature = jwt.encode(payload, secret_key, algorithm='HS256')
    
    headers = {
        'Authorization': f'Bearer {signature}'
    }
    
    # API 요청
    response = requests.get(url, headers=headers)
    
    if response.status_code == 200:
        accounts = response.json()
        for account in accounts:
            if account['currency'] == 'KRW':  # 원화 잔고 찾기
                return float(account['balance'])
    else:
        print(f&quot;Error: {response.status_code}&quot;)
        return None
    


while True:
    coin_holdings = get_coin_holdings(api_key, secret_key)
    money = int(get_balance() * 0.95)
    if len(coin_holdings)==1:
        # coin_list = get_top_gainers_upbit()
        for coin in coin_list:
            market = f'KRW-{coin}'
            status_coin_price = get_current_price(coin)
            coin_order_unit = 0
            if status_coin_price &amp;gt;= 2000000:
                coin_order_unit = 1000
            elif status_coin_price &amp;gt;=1000000 and status_coin_price&amp;lt;2000000:
                coin_order_unit = 500
            elif status_coin_price &amp;gt;=500000 and status_coin_price &amp;lt;1000000:
                coin_order_unit = 100
            elif status_coin_price &amp;gt;=100000 and status_coin_price&amp;lt;500000:
                coin_order_unit = 50
            elif status_coin_price &amp;gt;=10000 and status_coin_price&amp;lt;100000:
                coin_order_unit = 10
            elif status_coin_price &amp;gt;=1000 and status_coin_price&amp;lt;10000:
                coin_order_unit = 1
            elif status_coin_price &amp;gt;=100 and status_coin_price&amp;lt;1000:
                coin_order_unit = 0.1
                if coin in special_coin_list:
                    coin_order_unit = 1
            elif status_coin_price &amp;gt;=10 and status_coin_price&amp;lt;100:
                coin_order_unit = 0.01
            elif status_coin_price &amp;gt;= 1 and status_coin_price&amp;lt;10:
                coin_order_unit = 0.001
            elif status_coin_price&amp;gt;=0.1 and status_coin_price&amp;lt;1:
                coin_order_unit = 0.0001
            elif status_coin_price&amp;gt;=0.01 and status_coin_price&amp;lt;0.1:
                coin_order_unit = 0.00001
            elif status_coin_price&amp;gt;=0.001 and status_coin_price&amp;lt;0.01:
                coin_order_unit = 0.000001
            elif status_coin_price&amp;gt;=0.0001 and status_coin_price&amp;lt;0.001 :
                coin_order_unit = 0.0000001
            else :
                coin_order_unit = 0.00000001
            data = get_coin_data(coin)
            rsi, upper_band, middle_band, lower_band, macd, macd_signal, macd_hist, aroon_up, aroon_down, trix, slow_k, slow_d, adx, cci, stoch_rsi,mfi,aroon_osc,smi,rmi,stoch_rsi_k = calculate_indicators(data)
            
            print('----------------------------------------------')
            print(f'{coin}의 현재 가격 : {status_coin_price}원')
            print('')
            print(f'현재 lower band : {lower_band:.8f}')
            print(f'현재 stoch rsi : {stoch_rsi:.2f}')

            range_check = check_price_range(data)

            while lower_band&amp;gt;status_coin_price and stoch_rsi &amp;lt;=5:
                orderbook = get_orderbook(api_key, market)
                buy_price = orderbook['bid_price'] + coin_order_unit
                buy_uuid = place_limit_buy_order(api_key,secret_key,market,buy_price,money)
                unfilled_orders = check_unfilled_orders(api_key, secret_key)
                if unfilled_orders:
                    print('지정가 매수 실패, 미체결 주문 취소 작업 중..')
                    for order in unfilled_orders:
                        cancel_result = cancel_unfilled_order(api_key, secret_key, buy_uuid)
                else:
                    current_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
                    hold_won = get_upbit_account_total_balance(api_key,secret_key)
                    add_trade(current_time, f'{coin}', 'buy', buy_price,hold_won)
                    break
            if len(get_coin_holdings(api_key,secret_key))&amp;gt;1:
                break
            print('')
        get_upbit_account_info(api_key,secret_key)
        
    else:
        print('----------------------------------------------')
        hold_coin = list(coin_holdings.keys())[1]
        hold_coin_amount = float(coin_holdings[hold_coin])
        status_coin_price = get_current_price(hold_coin)
        coin_order_unit = 0
        if status_coin_price &amp;gt;= 2000000:
            coin_order_unit = 1000
        elif status_coin_price &amp;gt;=1000000 and status_coin_price&amp;lt;2000000:
            coin_order_unit = 500
        elif status_coin_price &amp;gt;=500000 and status_coin_price &amp;lt;1000000:
            coin_order_unit = 100
        elif status_coin_price &amp;gt;=100000 and status_coin_price&amp;lt;500000:
            coin_order_unit = 50
        elif status_coin_price &amp;gt;=10000 and status_coin_price&amp;lt;100000:
            coin_order_unit = 10
        elif status_coin_price &amp;gt;=1000 and status_coin_price&amp;lt;10000:
            coin_order_unit = 1
        elif status_coin_price &amp;gt;=100 and status_coin_price&amp;lt;1000:
            coin_order_unit = 0.1
            if hold_coin in special_coin_list:
                coin_order_unit = 1
        elif status_coin_price &amp;gt;=10 and status_coin_price&amp;lt;100:
            coin_order_unit = 0.01
        elif status_coin_price &amp;gt;= 1 and status_coin_price&amp;lt;10:
            coin_order_unit = 0.001
        elif status_coin_price&amp;gt;=0.1 and status_coin_price&amp;lt;1:
            coin_order_unit = 0.0001
        elif status_coin_price&amp;gt;=0.01 and status_coin_price&amp;lt;0.1:
            coin_order_unit = 0.00001
        elif status_coin_price&amp;gt;=0.001 and status_coin_price&amp;lt;0.01:
            coin_order_unit = 0.000001
        elif status_coin_price&amp;gt;=0.0001 and status_coin_price&amp;lt;0.001 :
            coin_order_unit = 0.0000001
        else :
            coin_order_unit = 0.00000001
        market = f'KRW-{hold_coin}'
        rsi, upper_band, middle_band, lower_band, macd, macd_signal, macd_hist, aroon_up, aroon_down, trix, slow_k, slow_d, adx, cci,stoch_rsi,mfi,aroon_osc,smi,rmi,stoch_rsi_k = calculate_indicators(get_coin_data(hold_coin))
    
        print(f'{hold_coin}의 현재 가격 : {status_coin_price}원')
        print('')
        print(f'현재 upper band : {upper_band:.8f}')
        print(f'현재 stoch rsi : {stoch_rsi:.2f}')
        while float(get_upbit_account_info(api_key,secret_key))&amp;lt;=-1.5:
            orderbook = get_orderbook(api_key, market)
            status_profit_ratio = get_upbit_account_info(api_key,secret_key)
            sell_price = orderbook['ask_price']-coin_order_unit
            sell_uuid = place_limit_sell_order(api_key,secret_key,market,sell_price,hold_coin_amount)
            if len(check_unfilled_orders(api_key,secret_key))&amp;gt;0:
                cancel_result = cancel_unfilled_order(api_key,secret_key,sell_uuid)
            else:
                current_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
                hold_won = get_upbit_account_total_balance(api_key,secret_key)
                add_trade(current_time, f'{hold_coin}', 'sell', sell_price,hold_won)
                break

        while status_coin_price &amp;gt;= upper_band or stoch_rsi&amp;gt;=80:
            orderbook = get_orderbook(api_key, market)
            market = f'KRW-{hold_coin}'
            sell_price = orderbook['ask_price'] - coin_order_unit
            sell_uuid = place_limit_sell_order(api_key,secret_key,market,sell_price,hold_coin_amount)
            unfilled_orders = check_unfilled_orders(api_key, secret_key)
            if unfilled_orders:
                print('지정가 매도 실패, 미체결 주문 취소 작업 중..')
                for order in unfilled_orders:
                    cancel_result = cancel_unfilled_order(api_key, secret_key, sell_uuid)
            else:
                current_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
                hold_won = get_upbit_account_total_balance(api_key,secret_key)
                add_trade(current_time, f'{hold_coin}', 'sell', sell_price,hold_won)
                break
        get_upbit_account_info(api_key,secret_key)&lt;/code&gt;&lt;/pre&gt;</description>
      <category>혼자 만든 Code/upbit 코인 자동거래</category>
      <author>빛하루</author>
      <guid isPermaLink="true">https://harucode.tistory.com/859</guid>
      <comments>https://harucode.tistory.com/859#entry859comment</comments>
      <pubDate>Fri, 17 Jan 2025 15:49:54 +0900</pubDate>
    </item>
    <item>
      <title>dd</title>
      <link>https://harucode.tistory.com/858</link>
      <description>&lt;pre id=&quot;code_1737001004714&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import pandas as pd
import requests
import talib as ta
import time
import jwt
import uuid
import json
from urllib.parse import urlencode
import pyupbit
import numpy as np
import hashlib
from urllib.parse import urlencode, unquote
import matplotlib.pyplot as plt
import csv
import os
from datetime import datetime
import pytz

server_url = 'https://api.upbit.com'
special_coin_list = ['ADA','ALGO','BLUR','CELO', 'ELF', 'EOS', 'GRS', 'GRT', 'ICX', 'MANA', 'MINA', 'POL', 'SAND', 'SEI', 'STG', 'TRX']

import requests

def get_top_gainers_upbit():
    base_url = &quot;https://api.upbit.com/v1&quot;

    try:
        # Step 1: Get market data
        markets_response = requests.get(f&quot;{base_url}/market/all&quot;)
        markets_response.raise_for_status()
        markets = markets_response.json()

        # Filter for KRW markets only and extract symbols
        krw_symbols = [market['market'].split('-')[1] for market in markets if market['market'].startswith('KRW-')]

        # Step 2: Get ticker data for all KRW markets
        krw_markets = [f&quot;KRW-{symbol}&quot; for symbol in krw_symbols]
        tickers_response = requests.get(f&quot;{base_url}/ticker&quot;, params={&quot;markets&quot;: &quot;,&quot;.join(krw_markets)})
        tickers_response.raise_for_status()
        tickers = tickers_response.json()

        # Step 3: Calculate price change percentages and filter for &amp;gt;3% gainers
        gainers = []
        for ticker in tickers:
            market = ticker['market']
            symbol = market.split('-')[1]
            prev_close = ticker['prev_closing_price']  # 전일 종가
            current_price = ticker['trade_price']     # 현재가

            except_coin = ['USDC','USDT','BTC','ETH','BCH','AAVE','SOL','BSV','AVAX','EGLD']
            
            if prev_close &amp;gt; 0 and symbol not in except_coin:  # 유효한 가격만 계산
                change_percent = ((current_price - prev_close) / prev_close) * 100
            
                gainers.append({
                    'symbol': symbol,
                    'change_percent': change_percent,
                    'current_price': current_price
                })

        # Step 4: Sort by price change percentage in descending order
        gainers = sorted(gainers, key=lambda x: x['change_percent'], reverse=True)

        # Step 5: Return top 5 gainers
        return [gainer['symbol'] for gainer in gainers[:3]]

    except requests.exceptions.RequestException as e:
        print(f&quot;Error while fetching data from Upbit API: {e}&quot;)
        return []


# JSON 파일에서 API 키 불러오기
def load_api_keys(config_file=&quot;upbit_config.json&quot;):
    try:
        with open(config_file, &quot;r&quot;) as file:
            keys = json.load(file)
            return keys[&quot;api_key&quot;], keys[&quot;secret_key&quot;]
    except Exception as e:
        print(f&quot;Error loading API keys: {e}&quot;)
        return None, None
    
def get_upbit_account_info(api_key, secret_key):
    base_url = &quot;https://api.upbit.com/v1&quot;

    # JWT 토큰 생성
    payload = {
        'access_key': api_key,
        'nonce': str(uuid.uuid4()),
    }
    jwt_token = jwt.encode(payload, secret_key, algorithm=&quot;HS256&quot;)
    authorization_token = f&quot;Bearer {jwt_token}&quot;
    
    headers = {
        &quot;Authorization&quot;: authorization_token,
    }

    # 계좌 조회 API 호출
    try:
        account_res = requests.get(f&quot;{base_url}/accounts&quot;, headers=headers)
        account_data = account_res.json()
        if account_res.status_code != 200:
            print(f&quot;Error fetching account data: {account_data}&quot;)
            return

        # 코인별 정보 조회
        market_data = requests.get(f&quot;{base_url}/market/all&quot;).json()
        market_dict = {item['market']: item['korean_name'] for item in market_data}

        total_balance = 0
        coin_info_list = []

        for account in account_data:
            if float(account['balance']) &amp;gt; 0:  # 잔액이 0보다 클 경우
                ticker = account['currency']
                if ticker == &quot;KRW&quot;:
                    total_balance += float(account['balance'])
                    continue
                
                market = f&quot;KRW-{ticker}&quot;
                avg_buy_price = float(account['avg_buy_price'])
                balance = float(account['balance'])

                # 현재 가격 조회
                ticker_url = f&quot;{base_url}/ticker?markets={market}&quot;
                ticker_res = requests.get(ticker_url).json()
                current_price = float(ticker_res[0]['trade_price'])
                
                # 평가 금액과 수익률 계산
                eval_amount = balance * current_price
                profit_rate = ((current_price - avg_buy_price) / avg_buy_price) * 100
                
                # 총 자산에 반영
                total_balance += eval_amount
                
                coin_info_list.append({
                    &quot;코인&quot;: market_dict.get(market, ticker),
                    &quot;보유수량&quot;: balance,
                    &quot;평균단가&quot;: avg_buy_price,
                    &quot;현재가격&quot;: current_price,
                    &quot;수익률&quot;: profit_rate,
                })

        # 출력
        print(f&quot;총 자산: {total_balance:,.0f} KRW&quot;)
        print('')
        print(&quot;보유 코인 정보:&quot;)
        for coin in coin_info_list:
            print(f&quot;코인: {coin['코인']}&quot;)
            print(f&quot;보유수량: {coin['보유수량']}&quot;)
            print(f&quot;평균단가: {coin['평균단가']}&quot;)
            print(f&quot;현재가격: {coin['현재가격']}&quot;)
            print(f&quot;수익률: {coin['수익률']:.2f}%&quot;)
            return coin['수익률']
    except Exception as e:
        print(f&quot;An error occurred: {e}&quot;)

def get_upbit_account_total_balance(api_key,secret_key):
    base_url = &quot;https://api.upbit.com/v1&quot;

    # JWT 토큰 생성
    payload = {
        'access_key': api_key,
        'nonce': str(uuid.uuid4()),
    }
    jwt_token = jwt.encode(payload, secret_key, algorithm=&quot;HS256&quot;)
    authorization_token = f&quot;Bearer {jwt_token}&quot;
    
    headers = {
        &quot;Authorization&quot;: authorization_token,
    }

    # 계좌 조회 API 호출
    try:
        account_res = requests.get(f&quot;{base_url}/accounts&quot;, headers=headers)
        account_data = account_res.json()
        if account_res.status_code != 200:
            print(f&quot;Error fetching account data: {account_data}&quot;)
            return

        # 코인별 정보 조회
        market_data = requests.get(f&quot;{base_url}/market/all&quot;).json()
        market_dict = {item['market']: item['korean_name'] for item in market_data}

        total_balance = 0
        coin_info_list = []

        for account in account_data:
            if float(account['balance']) &amp;gt; 0:  # 잔액이 0보다 클 경우
                ticker = account['currency']
                if ticker == &quot;KRW&quot;:
                    total_balance += float(account['balance'])
                    continue
                
                market = f&quot;KRW-{ticker}&quot;
                avg_buy_price = float(account['avg_buy_price'])
                balance = float(account['balance'])

                # 현재 가격 조회
                ticker_url = f&quot;{base_url}/ticker?markets={market}&quot;
                ticker_res = requests.get(ticker_url).json()
                current_price = float(ticker_res[0]['trade_price'])
                
                # 평가 금액과 수익률 계산
                eval_amount = balance * current_price
                profit_rate = ((current_price - avg_buy_price) / avg_buy_price) * 100
                
                # 총 자산에 반영
                total_balance += eval_amount
        return total_balance
    except Exception as e:
        print(f&quot;An error occurred: {e}&quot;)

def get_coin_holdings(api_key, secret_key):
    &quot;&quot;&quot;
    업비트 API를 사용하여 보유 중인 코인의 종류와 수량을 가져옵니다.

    Returns:
        dict: 코인의 종류와 보유 수량을 포함한 딕셔너리
    &quot;&quot;&quot;
    base_url = &quot;https://api.upbit.com/v1/accounts&quot;

    # JWT 토큰 생성
    payload = {
        'access_key': api_key,
        'nonce': str(uuid.uuid4()),
    }
    jwt_token = jwt.encode(payload, secret_key, algorithm=&quot;HS256&quot;)
    headers = {&quot;Authorization&quot;: f&quot;Bearer {jwt_token}&quot;}

    try:
        # 계좌 정보 요청
        response = requests.get(base_url, headers=headers)
        response.raise_for_status()
        accounts = response.json()

        # 보유 코인 정보 필터링
        holdings = {account['currency']: float(account['balance']) for account in accounts if float(account['balance']) &amp;gt; 0}

        # 보유 코인이 없을 경우 처리
        if not holdings:
            print(&quot;보유 중인 코인이 없습니다.&quot;)
            return {}

        return holdings

    except Exception as e:
        print(f&quot;Error fetching coin holdings: {e}&quot;)
        return {}
    
# 미체결된 주문 조회    
def check_unfilled_orders(api_key, secret_key):
    &quot;&quot;&quot;
    미체결 주문이 있는지 확인합니다.

    Returns:
        list: 미체결 주문 리스트 (없으면 빈 리스트)
    &quot;&quot;&quot;
    base_url = &quot;https://api.upbit.com/v1/orders&quot;
    query = {
        'state': 'wait',  # 'wait' 상태는 미체결 주문을 의미
    }

    # Query Hash 계산
    query_string = '&amp;amp;'.join([f&quot;{key}={value}&quot; for key, value in query.items()])
    query_hash = hashlib.sha512(query_string.encode()).hexdigest()

    # JWT 토큰 생성
    payload = {
        'access_key': api_key,
        'nonce': str(uuid.uuid4()),
        'query_hash': query_hash,
        'query_hash_alg': 'SHA512',
    }
    jwt_token = jwt.encode(payload, secret_key, algorithm=&quot;HS256&quot;)
    headers = {&quot;Authorization&quot;: f&quot;Bearer {jwt_token}&quot;}

    try:
        # 미체결 주문 요청
        response = requests.get(base_url, headers=headers, params=query)
        response.raise_for_status()
        orders = response.json()

        # 미체결 주문이 없는 경우 처리
        if not orders:
            print(&quot;미체결 주문이 없습니다.&quot;)
            return []

        print(&quot;미체결 주문이 있습니다.&quot;)
        return orders

    except Exception as e:
        print(f&quot;Error fetching unfilled orders: {e}&quot;)
        return []
    
#미체결된 주문 취소하는 함수
def cancel_unfilled_order(api_key, secret_key, order_id):
    &quot;&quot;&quot;
    특정 미체결 주문을 취소합니다.

    Args:
        api_key (str): 업비트 API 키
        secret_key (str): 업비트 Secret 키
        order_id (str): 취소할 주문의 UUID

    Returns:
        dict: 주문 취소 결과
    &quot;&quot;&quot;
    base_url = &quot;https://api.upbit.com/v1/order&quot;
    query = {
        'uuid': order_id,
    }

    # JWT 토큰 생성
    query_string = '&amp;amp;'.join([f&quot;{key}={value}&quot; for key, value in query.items()])
    payload = {
        'access_key': api_key,
        'nonce': str(uuid.uuid4()),
        'query_hash': hashlib.sha512(query_string.encode()).hexdigest(),
        'query_hash_alg': 'SHA512',
    }
    jwt_token = jwt.encode(payload, secret_key, algorithm=&quot;HS256&quot;)
    headers = {&quot;Authorization&quot;: f&quot;Bearer {jwt_token}&quot;}

    try:
        # 주문 취소 요청
        response = requests.delete(base_url, headers=headers, params=query)
        response.raise_for_status()
        cancel_result = response.json()

        print(f&quot;주문이 성공적으로 취소되었습니다: {cancel_result}&quot;)
        return cancel_result

    except requests.exceptions.HTTPError as e:
        print(f&quot;HTTP 에러 발생: {e.response.status_code}, {e.response.text}&quot;)
        return {}
    except Exception as e:
        print(f&quot;Error canceling order: {e}&quot;)
        return {}
    
#매수 1호가와 매도1호가 리턴하는 함수    
def get_orderbook(api_key, market=&quot;KRW-BTC&quot;):
    &quot;&quot;&quot;
    주어진 마켓의 매수, 매도 1호가를 가져옵니다.

    Args:
        api_key (str): 업비트 API 키
        market (str): 마켓 코드 (예: &quot;KRW-BTC&quot;)

    Returns:
        dict: 매수 1호가와 매도 1호가를 포함한 딕셔너리
    &quot;&quot;&quot;
    url = f&quot;https://api.upbit.com/v1/orderbook?markets={market}&amp;amp;level=0&quot;

    headers = {&quot;accept&quot;: &quot;application/json&quot;}

    try:
        # 주문서 정보 요청
        response = requests.get(url, headers=headers)
        response.raise_for_status()
        orderbook = response.json()

        if not orderbook:
            print(f&quot;{market}의 주문서 정보가 없습니다.&quot;)
            return None

        # 매수 1호가 (가장 높은 매도 호가)
        bid_price = orderbook[0]['orderbook_units'][1]['bid_price']
        # 매도 1호가 (가장 낮은 매수 호가)
        ask_price = orderbook[0]['orderbook_units'][0]['ask_price']

        return {&quot;bid_price&quot;: bid_price, &quot;ask_price&quot;: ask_price}

    except Exception as e:
        print(f&quot;Error fetching orderbook: {e}&quot;)
        return None
    
#지정가 매수함수    
def place_limit_buy_order(api_key, secret_key, market, price, budget):
    max_quantity = str(budget / price)
    params = {
        'market': market,
        'side': 'bid',
        'ord_type': 'limit',
        'price': price,
        'volume': max_quantity
    }
    query_string = unquote(urlencode(params, doseq=True)).encode(&quot;utf-8&quot;)

    m = hashlib.sha512()
    m.update(query_string)
    query_hash = m.hexdigest()

    payload = {
        'access_key': api_key,
        'nonce': str(uuid.uuid4()),
        'query_hash': query_hash,
        'query_hash_alg': 'SHA512',
    }

    jwt_token = jwt.encode(payload, secret_key)
    authorization = 'Bearer {}'.format(jwt_token)
    headers = {
    'Authorization': authorization,
    }

    res = requests.post(server_url + '/v1/orders', json=params, headers=headers)
    order_result = res.json()
    # 성공적으로 주문을 제출했다면 주문 UUID 반환
    order_uuid = order_result.get('uuid')
    if order_uuid:
        print(f&quot;매수 주문이 성공적으로 생성되었습니다. 주문 UUID: {order_uuid}&quot;)
        time.sleep(5)
        return order_uuid
    else:
        print(&quot;주문 생성 실패: UUID를 찾을 수 없습니다.&quot;)
        return None
    
# 지정가 매도함수
def place_limit_sell_order(api_key, secret_key, market, price, quantity):
    max_quantity = str(quantity)
    params = {
        'market': market,
        'side': 'ask',
        'ord_type': 'limit',
        'price': price,
        'volume': max_quantity
    }
    query_string = unquote(urlencode(params, doseq=True)).encode(&quot;utf-8&quot;)

    m = hashlib.sha512()
    m.update(query_string)
    query_hash = m.hexdigest()

    payload = {
        'access_key': api_key,
        'nonce': str(uuid.uuid4()),
        'query_hash': query_hash,
        'query_hash_alg': 'SHA512',
    }

    jwt_token = jwt.encode(payload, secret_key)
    authorization = 'Bearer {}'.format(jwt_token)
    headers = {
    'Authorization': authorization,
    }

    res = requests.post(server_url + '/v1/orders', json=params, headers=headers)
    order_result = res.json()
    # 성공적으로 주문을 제출했다면 주문 UUID 반환
    order_uuid = order_result.get('uuid')
    if order_uuid:
        print(f&quot;매도 주문이 성공적으로 생성되었습니다. 주문 UUID: {order_uuid}&quot;)
        time.sleep(5)
        return order_uuid
    else:
        print(&quot;주문 생성 실패: UUID를 찾을 수 없습니다.&quot;)
        return None

def get_current_price(coin):
    url = f&quot;https://api.upbit.com/v1/ticker&quot;
    params = {
        'markets': f'KRW-{coin}'  # 코인 이름 입력 (예: 'BTC' -&amp;gt; 'KRW-BTC')
    }
    
    response = requests.get(url, params=params)
    data = response.json()
    
    if len(data) &amp;gt; 0:
        return data[0]['trade_price']  # 현재 거래 가격 반환
    else:
        return None

def get_coin_data(coin):
    url = &quot;https://api.upbit.com/v1/candles/minutes/1&quot;  # Adjusted to minutes/1 endpoint for real data
    params = {  
        'market': f'KRW-{coin}',  
        'count': 100,
    }  
    headers = {&quot;accept&quot;: &quot;application/json&quot;}

    response = requests.get(url, params=params, headers=headers)
    data = response.json()
    df = pd.DataFrame(data)
    df = df.reindex(index=df.index[::-1]).reset_index(drop=True)  # Reset index after reversing
    return df

def save_to_csv(df):
    filename = 'coin_data.csv'
    df.to_csv(filename, index=False)

def calculate_indicators(df):
    # 'trade_price' (종가), 'high_price', 'low_price' 컬럼 확인 및 추출
    required_columns = ['trade_price', 'high_price', 'low_price','candle_acc_trade_volume']
    for col in required_columns:
        if col not in df.columns:
            raise ValueError(f&quot;데이터에 '{col}' 컬럼이 없습니다.&quot;)
    
    close_prices = df['trade_price'].astype(float)
    high_prices = df['high_price'].astype(float)
    low_prices = df['low_price'].astype(float)
    volumes = df['candle_acc_trade_volume'].astype(float)

    # 데이터 확인
    if close_prices.isnull().any():
        raise ValueError(&quot;종가 데이터에 결측값이 있습니다.&quot;)
    if len(close_prices) &amp;lt; 10:  # Momentum 계산에 필요한 최소 데이터 길이
        raise ValueError(&quot;Momentum 지표를 계산하려면 데이터 길이가 최소 10 이상이어야 합니다.&quot;)
    
    # RSI (Relative Strength Index)
    df['RSI'] = ta.RSI(close_prices, timeperiod=14)
    rsi = df['RSI'].iloc[-1]

    # Stochastic RSI
    df['StochRSI'] = (df['RSI'] - df['RSI'].rolling(14).min()) / (df['RSI'].rolling(14).max() - df['RSI'].rolling(14).min()) * 100
    df['StochRSI_K'] = df['StochRSI'].rolling(3).mean()
    df['StochRSI_D'] = df['StochRSI_K'].rolling(3).mean()
    stoch_rsi = df['StochRSI_D'].iloc[-1]
    stoch_rsi_k = df['StochRSI_K'].iloc[-1]
    
    # Bollinger Bands
    df['Upper_Band'], df['Middle_Band'], df['Lower_Band'] = ta.BBANDS(
        close_prices,
        timeperiod=20,
        nbdevup=1.5,
        nbdevdn=1.5,
        matype=0
    )
    upper_band = df['Upper_Band'].iloc[-1]
    middle_band = df['Middle_Band'].iloc[-1]
    lower_band = df['Lower_Band'].iloc[-1]
    
    # MACD (Moving Average Convergence Divergence)
    df['MACD'], df['MACD_Signal'], df['MACD_Hist'] = ta.MACD(
        close_prices,
        fastperiod=12,
        slowperiod=26,
        signalperiod=9
    )
    macd = df['MACD'].iloc[-1]
    macd_signal = df['MACD_Signal'].iloc[-1]
    macd_hist = df['MACD_Hist'].iloc[-1]
    
    # Aroon (Aroon Oscillator)
    df['Aroon_Up'], df['Aroon_Down'] = ta.AROON(
        high=high_prices,
        low=low_prices,
        timeperiod=14
    )
    aroon_up = df['Aroon_Up'].iloc[-1]
    aroon_down = df['Aroon_Down'].iloc[-1]
    
    # TRIX (Triple Exponential Average)
    df['TRIX'] = ta.TRIX(
        close_prices,
        timeperiod=5
    )
    trix = df['TRIX'].iloc[-1]
    
    # Stochastic Oscillator
    df['SlowK'], df['SlowD'] = ta.STOCH(
        high=high_prices,
        low=low_prices,
        close=close_prices,
        fastk_period=14,
        slowk_period=3,
        slowk_matype=0,
        slowd_period=3,
        slowd_matype=0
    )
    slow_k = df['SlowK'].iloc[-1]
    slow_d = df['SlowD'].iloc[-1]
    
    # ADX (Average Directional Index)
    df['ADX'] = ta.ADX(
        high=high_prices,
        low=low_prices,
        close=close_prices,
        timeperiod=14
    )
    adx = df['ADX'].iloc[-1]
    
    # CCI (Commodity Channel Index)
    df['CCI'] = ta.CCI(
        high=high_prices,
        low=low_prices,
        close=close_prices,
        timeperiod=20
    )
    cci = df['CCI'].iloc[-1]

    df['MFI'] = ta.MFI(
        high=high_prices,
        low=low_prices,
        close=close_prices,
        volume=volumes,
        timeperiod=14
    )
    mfi = df['MFI'].iloc[-1]

    # Aroon (Aroon Oscillator)
    df['Aroon_Up'], df['Aroon_Down'] = ta.AROON(
        high=high_prices,
        low=low_prices,
        timeperiod=14
    )
    aroon_up = df['Aroon_Up'].iloc[-1]
    aroon_down = df['Aroon_Down'].iloc[-1]
    df['Aroon_Osc'] = df['Aroon_Up'] - df['Aroon_Down']
    aroon_osc = df['Aroon_Osc'].iloc[-1]

    # Stochastic Momentum Index (SMI)
    high_low_avg = (high_prices + low_prices) / 2
    raw_k = close_prices - high_low_avg.rolling(10).mean()
    double_smooth_k = raw_k.rolling(3).mean().rolling(3).mean()
    range_smooth = (high_low_avg.rolling(10).max() - high_low_avg.rolling(10).min()).rolling(3).mean()
    df['SMI'] = (double_smooth_k / range_smooth) * 100
    smi = df['SMI'].iloc[-1]

    df['MA_9'] = close_prices.rolling(window=5).mean()
    ma_9 = df['MA_9'].iloc[-1]
    ma_9_before = df['MA_9'].iloc[-2]

    df['Tenkan_sen'] = (high_prices.rolling(window=5).max() + low_prices.rolling(window=9).min()) / 2
    df['Kijun_sen'] = (high_prices.rolling(window=26).max() + low_prices.rolling(window=26).min()) / 2
    tenkan_sen = df['Tenkan_sen'].iloc[-1]  # 전환선 최신 값
    kijun_sen = df['Kijun_sen'].iloc[-1]    # 기준선 최신 값
    tenkan_sen_before = df['Tenkan_sen'].iloc[-2]
    kijun_sen_before = df['Kijun_sen'].iloc[-2] 

    # RMI (Relative Momentum Index) 적용 시간 5, 이평 기간 14
    n = 5  # 적용 시간
    smoothing_period = 14  # 이평 기간
    diff = close_prices.diff(n)  # N일 이전과의 변화
    gain = diff.where(diff &amp;gt; 0, 0)
    loss = -diff.where(diff &amp;lt; 0, 0)
    avg_gain = gain.rolling(window=smoothing_period).mean()
    avg_loss = loss.rolling(window=smoothing_period).mean()
    df['RMI'] = 100 - (100 / (1 + (avg_gain / avg_loss)))
    rmi = df['RMI'].iloc[-1]
    
    # Save data to CSV
    save_to_csv(df)
    
    # Return all indicator values
    return (
        rsi, upper_band, middle_band, lower_band, macd, macd_signal, macd_hist, 
        aroon_up, aroon_down, trix, slow_k, slow_d, adx, cci, stoch_rsi,mfi,aroon_osc,smi,rmi,stoch_rsi_k,ma_9_before,ma_9,tenkan_sen_before,tenkan_sen,kijun_sen
    )

def predict_trend(rsi, upper_band, lower_band, macd, macd_signal, aroon_up, aroon_down, trix, slow_k, slow_d, adx, cci):
    indicators = 0
    
    # 반등 예상: 5개 이상 지표가 반등 구간에 있을 때
    if rsi &amp;lt; 30: indicators += 1  # RSI
    if lower_band &amp;gt; 0: indicators += 1  # Bollinger Bands 하단 밴드에 근접
    if macd &amp;gt; macd_signal: indicators += 1  # MACD 상승
    if aroon_up &amp;gt; aroon_down: indicators += 1  # Aroon 상승
    if trix &amp;gt; 0: indicators += 1  # TRIX 상승
    if slow_k &amp;gt; slow_d: indicators += 1  # Stochastic Oscillator
    if adx &amp;gt; 25: indicators += 1  # 강한 추세
    if cci &amp;lt; -100: indicators += 1  # CCI 과매도 구간
    
    # 하락 예상: 5개 이상 지표가 하락 구간에 있을 때
    if rsi &amp;gt; 70: indicators -= 1  # RSI
    if upper_band &amp;gt; 0: indicators -= 1  # Bollinger Bands 상단 밴드에 근접
    if macd &amp;lt; macd_signal: indicators -= 1  # MACD 하락
    if aroon_down &amp;gt; aroon_up: indicators -= 1  # Aroon 하락
    if trix &amp;lt; 0: indicators -= 1  # TRIX 하락
    if slow_k &amp;lt; slow_d: indicators -= 1  # Stochastic Oscillator
    if adx &amp;lt; 20: indicators -= 1  # 약한 추세
    if cci &amp;gt; 100: indicators -= 1  # CCI 과매수 구간
    
    return indicators

def check_price_range(data):
    # 마지막 50개의 데이터 선택
    last_50_data = data.iloc[-50:]
    
    # low_price의 최소값과 high_price의 최대값 구하기
    min_low_price = last_50_data[&quot;low_price&quot;].min()
    max_high_price = last_50_data[&quot;high_price&quot;].max()
    
    # 현재 가격 (최근 1분 가격)
    current_price = data[&quot;trade_price&quot;].iloc[-1]
    
    # 가격 차이가 현재 가격의 1% 이상인지 체크
    price_difference = max_high_price - min_low_price
    price_change_ratio = price_difference/current_price * 100

    print('')
    print(f'최근 가격 50개 변동률 : {price_change_ratio:.2f}%')
    print('')
    
    if price_change_ratio&amp;gt;=1:
        return True  # 차이가 1% 이상
    else:
        return False  # 차이가 1% 미만

api_key, secret_key = load_api_keys(&quot;upbit_config.json&quot;)
# coin_list = input(&quot;조회할 코인 심볼들을 입력하세요 (쉼표로 구분): &quot;).upper().replace(&quot; &quot;, &quot;&quot;).split(',')

columns = ['시간', '코인명', '매수/매도', '평균 단가','현재 보유잔고']
df_trade = pd.DataFrame(columns=columns)
df_trade.to_csv('trade_list.csv', index=False, encoding='utf-8-sig')

def add_trade(trade_time, coin_name, trade_type, avg_price,hold_won):
    # CSV 파일 읽기
    df = pd.read_csv('trade_list.csv', encoding='utf-8-sig')
    
    # 새로운 데이터 추가
    new_data = {
        '시간': trade_time,
        '코인명': coin_name,
        '매수/매도': trade_type,
        '평균 단가': avg_price,
        '현재 보유잔고':hold_won
    }
    df = df.append(new_data, ignore_index=True)
    
    # 업데이트된 데이터프레임을 CSV 파일로 저장
    df.to_csv('trade_list.csv', index=False, encoding='utf-8-sig')

def get_balance():
    &quot;&quot;&quot;
    업비트 API를 사용하여 원화 잔고를 조회하는 함수
    &quot;&quot;&quot;
    url = server_url + '/v1/accounts'
    
    # 요청 헤더 준비
    payload = {
        'access_key': api_key,
        'nonce': str(uuid.uuid4())
    }
    
    # 서명 생성
    m = hashlib.sha512()
    m.update(payload['nonce'].encode('utf-8'))
    signature = jwt.encode(payload, secret_key, algorithm='HS256')
    
    headers = {
        'Authorization': f'Bearer {signature}'
    }
    
    # API 요청
    response = requests.get(url, headers=headers)
    
    if response.status_code == 200:
        accounts = response.json()
        for account in accounts:
            if account['currency'] == 'KRW':  # 원화 잔고 찾기
                return float(account['balance'])
    else:
        print(f&quot;Error: {response.status_code}&quot;)
        return None

while True:
    coin_holdings = get_coin_holdings(api_key, secret_key)
    money = int(get_balance() * 0.95)
    if len(coin_holdings)==1:
        coin_list = get_top_gainers_upbit()
        for coin in coin_list:
            market = f'KRW-{coin}'
            status_coin_price = get_current_price(coin)
            coin_order_unit = 0
            if status_coin_price &amp;gt;= 2000000:
                coin_order_unit = 1000
            elif status_coin_price &amp;gt;=1000000 and status_coin_price&amp;lt;2000000:
                coin_order_unit = 500
            elif status_coin_price &amp;gt;=500000 and status_coin_price &amp;lt;1000000:
                coin_order_unit = 100
            elif status_coin_price &amp;gt;=100000 and status_coin_price&amp;lt;500000:
                coin_order_unit = 50
            elif status_coin_price &amp;gt;=10000 and status_coin_price&amp;lt;100000:
                coin_order_unit = 10
            elif status_coin_price &amp;gt;=1000 and status_coin_price&amp;lt;10000:
                coin_order_unit = 1
            elif status_coin_price &amp;gt;=100 and status_coin_price&amp;lt;1000:
                coin_order_unit = 0.1
                if coin in special_coin_list:
                    coin_order_unit = 1
            elif status_coin_price &amp;gt;=10 and status_coin_price&amp;lt;100:
                coin_order_unit = 0.01
            elif status_coin_price &amp;gt;= 1 and status_coin_price&amp;lt;10:
                coin_order_unit = 0.001
            elif status_coin_price&amp;gt;=0.1 and status_coin_price&amp;lt;1:
                coin_order_unit = 0.0001
            elif status_coin_price&amp;gt;=0.01 and status_coin_price&amp;lt;0.1:
                coin_order_unit = 0.00001
            elif status_coin_price&amp;gt;=0.001 and status_coin_price&amp;lt;0.01:
                coin_order_unit = 0.000001
            elif status_coin_price&amp;gt;=0.0001 and status_coin_price&amp;lt;0.001 :
                coin_order_unit = 0.0000001
            else :
                coin_order_unit = 0.00000001
            data = get_coin_data(coin)
            rsi, upper_band, middle_band, lower_band, macd, macd_signal, macd_hist, aroon_up, aroon_down, trix, slow_k, slow_d, adx, cci, stoch_rsi,mfi,aroon_osc,smi,rmi,stoch_rsi_k,ma_9_before,ma_9,tenkan_sen_before,tenkan_sen,kijun_sen= calculate_indicators(data)
            
            print('----------------------------------------------')
            print(f'{coin}의 현재 가격 : {status_coin_price}원')
            print('')
            print(f'현재 ma_5 : {ma_9:.8f}')
            print(f'현재 기준선 : {kijun_sen:.8f}')
            print(f'현재 전환 선 : {tenkan_sen:.8f}')
            print(f'현재 stoch rsi: {stoch_rsi:.2f}')
            print(f'현재 upper_band: {upper_band:.8f}')

            range_check = check_price_range(data)

            while status_coin_price&amp;lt;kijun_sen and status_coin_price &amp;lt;tenkan_sen and status_coin_price &amp;lt; ma_9 and stoch_rsi&amp;lt;20:
                orderbook = get_orderbook(api_key, market)
                buy_price = orderbook['bid_price'] + coin_order_unit
                buy_uuid = place_limit_buy_order(api_key,secret_key,market,buy_price,money)
                unfilled_orders = check_unfilled_orders(api_key, secret_key)
                if unfilled_orders:
                    print('지정가 매수 실패, 미체결 주문 취소 작업 중..')
                    for order in unfilled_orders:
                        cancel_result = cancel_unfilled_order(api_key, secret_key, buy_uuid)
                else:
                    current_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
                    hold_won = get_upbit_account_total_balance(api_key,secret_key)
                    add_trade(current_time, f'{coin}', 'buy', buy_price,hold_won)
                    break
                status_coin_price = get_current_price(coin)
                data = get_coin_data(coin)
                rsi, upper_band, middle_band, lower_band, macd, macd_signal, macd_hist, aroon_up, aroon_down, trix, slow_k, slow_d, adx, cci, stoch_rsi,mfi,aroon_osc,smi,rmi,stoch_rsi_k,ma_9_before,ma_9,tenkan_sen_before,tenkan_sen,kijun_sen= calculate_indicators(data)
            if len(get_coin_holdings(api_key,secret_key))&amp;gt;1:
                break
            print('')
        get_upbit_account_info(api_key,secret_key)
        
    else:
        print('----------------------------------------------')
        hold_coin = list(coin_holdings.keys())[1]
        hold_coin_amount = float(coin_holdings[hold_coin])
        status_coin_price = get_current_price(hold_coin)
        coin_order_unit = 0
        if status_coin_price &amp;gt;= 2000000:
            coin_order_unit = 1000
        elif status_coin_price &amp;gt;=1000000 and status_coin_price&amp;lt;2000000:
            coin_order_unit = 500
        elif status_coin_price &amp;gt;=500000 and status_coin_price &amp;lt;1000000:
            coin_order_unit = 100
        elif status_coin_price &amp;gt;=100000 and status_coin_price&amp;lt;500000:
            coin_order_unit = 50
        elif status_coin_price &amp;gt;=10000 and status_coin_price&amp;lt;100000:
            coin_order_unit = 10
        elif status_coin_price &amp;gt;=1000 and status_coin_price&amp;lt;10000:
            coin_order_unit = 1
        elif status_coin_price &amp;gt;=100 and status_coin_price&amp;lt;1000:
            coin_order_unit = 0.1
            if hold_coin in special_coin_list:
                coin_order_unit = 1
        elif status_coin_price &amp;gt;=10 and status_coin_price&amp;lt;100:
            coin_order_unit = 0.01
        elif status_coin_price &amp;gt;= 1 and status_coin_price&amp;lt;10:
            coin_order_unit = 0.001
        elif status_coin_price&amp;gt;=0.1 and status_coin_price&amp;lt;1:
            coin_order_unit = 0.0001
        elif status_coin_price&amp;gt;=0.01 and status_coin_price&amp;lt;0.1:
            coin_order_unit = 0.00001
        elif status_coin_price&amp;gt;=0.001 and status_coin_price&amp;lt;0.01:
            coin_order_unit = 0.000001
        elif status_coin_price&amp;gt;=0.0001 and status_coin_price&amp;lt;0.001 :
            coin_order_unit = 0.0000001
        else :
            coin_order_unit = 0.00000001
        market = f'KRW-{hold_coin}'
        data = get_coin_data(hold_coin)
        rsi, upper_band, middle_band, lower_band, macd, macd_signal, macd_hist, aroon_up, aroon_down, trix, slow_k, slow_d, adx, cci, stoch_rsi,mfi,aroon_osc,smi,rmi,stoch_rsi_k,ma_9_before,ma_9,tenkan_sen_before,tenkan_sen,kijun_sen= calculate_indicators(data)
    
        print(f'{hold_coin}의 현재 가격 : {status_coin_price}원')
        print('')
        print(f'현재 ma_5 : {ma_9:.8f}')
        print(f'현재 기준선 : {kijun_sen:.8f}')
        print(f'현재 전환 선 : {tenkan_sen:.8f}')
        print(f'현재 stoch rsi: {stoch_rsi:.2f}')
        print(f'현재 upper_band: {upper_band:.8f}')
        while float(get_upbit_account_info(api_key,secret_key))&amp;lt;=-3:
            orderbook = get_orderbook(api_key, market)
            status_profit_ratio = get_upbit_account_info(api_key,secret_key)
            sell_price = orderbook['ask_price']-coin_order_unit
            sell_uuid = place_limit_sell_order(api_key,secret_key,market,sell_price,hold_coin_amount)
            if len(check_unfilled_orders(api_key,secret_key))&amp;gt;0:
                cancel_result = cancel_unfilled_order(api_key,secret_key,sell_uuid)
            else:
                current_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
                hold_won = get_upbit_account_total_balance(api_key,secret_key)
                add_trade(current_time, f'{hold_coin}', 'sell', sell_price,hold_won)
                break

        while (status_coin_price&amp;gt;=tenkan_sen and status_coin_price&amp;gt;=kijun_sen and status_coin_price&amp;gt;=ma_9) or status_coin_price &amp;gt;= upper_band or stoch_rsi&amp;gt;=80 :
            orderbook = get_orderbook(api_key, market)
            market = f'KRW-{hold_coin}'
            sell_price = orderbook['ask_price'] - coin_order_unit
            sell_uuid = place_limit_sell_order(api_key,secret_key,market,sell_price,hold_coin_amount)
            unfilled_orders = check_unfilled_orders(api_key, secret_key)
            if unfilled_orders:
                print('지정가 매도 실패, 미체결 주문 취소 작업 중..')
                for order in unfilled_orders:
                    cancel_result = cancel_unfilled_order(api_key, secret_key, sell_uuid)
            else:
                current_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
                hold_won = get_upbit_account_total_balance(api_key,secret_key)
                add_trade(current_time, f'{hold_coin}', 'sell', sell_price,hold_won)
                break
            data = get_coin_data(hold_coin)
            rsi, upper_band, middle_band, lower_band, macd, macd_signal, macd_hist, aroon_up, aroon_down, trix, slow_k, slow_d, adx, cci, stoch_rsi,mfi,aroon_osc,smi,rmi,stoch_rsi_k,ma_9_before,ma_9,tenkan_sen_before,tenkan_sen,kijun_sen= calculate_indicators(data)
            status_coin_price = get_current_price(hold_coin)
        get_upbit_account_info(api_key,secret_key)&lt;/code&gt;&lt;/pre&gt;</description>
      <author>빛하루</author>
      <guid isPermaLink="true">https://harucode.tistory.com/858</guid>
      <comments>https://harucode.tistory.com/858#entry858comment</comments>
      <pubDate>Thu, 16 Jan 2025 13:16:50 +0900</pubDate>
    </item>
  </channel>
</rss>