프로젝트/코인 투자 매크로

2025-06-08 [7] 가상 매수/매도 함수 구현

훈 님의 개발 블로그 2025. 6. 8. 17:59

오늘은 가상 자산 시뮬레이션을 위한 매수/매도 함수를 만들어보려고 합니다.
가상 자산을 이용한 시뮬레이션인 만큼 저번에 만들었던 virtual_portfolio_manager.py에 거래 함수를 추가 할 예정입니다.

1. 매수/매도 함수

1). import 선언

import time

import trade_history    # 거래 내역 저장을 위해

 

2). 매수/매도 함수 선언

- 다음과 같이 매수/매도 로직을 구현 할 함수를 선언하고 내부를 채워보도록 하겠습니다.

# --- 가상 거래 기록 및 portfolio 상태 업데이트 함수 (실제 주문 X) ---
"""
:portfolio_data: 현재 포트폴리오 상태 딕셔너리
:trade_type: "buy" 또는 "sell"
:symbol: 거래할 코인 심볼 (예: "BTC")
:quantity: 거래할 수량
:price_per_unit: 거래 당시 코인 개당 가격
:return: 성공 시 업데이트된 포트폴리오 딕셔너리, 실패 시 None
"""
def record_vportfolio_trade(portfolio_data, trade_type, symbol, quantity, price_per_unit):
    symbol_upper = symbol.upper()  # 코인 심볼은 대문자로 통일

    if trade_type.lower() == "buy":
        print("BUY")
        # --- 매수 로직 ---
    elif trade_type.lower() == "sell":
        print("SELL")
        # --- 매도 로직 ---
    else:
        print(f"⚠️  잘못된 거래 유형입니다: {trade_type}")
        return None

    return portfolio_data

 

3). 매수 로직

- if trade_type.lower() == "buy":인 경우 아래 로직으로 변경합니다.

if trade_type.lower() == "buy":
    # --- 매수 로직 ---
    total_cost = quantity * price_per_unit

    # 1. 현금 잔고 확인
    if portfolio_data["cash"] < total_cost:
        print(f"⚠️  매수 실패: 현금 부족 (필요: {total_cost:,.2f} KRW, 보유: {portfolio_data['cash']:,.2f} KRW)")
        return None

    # 2. 포트폴리오에서 해당 코인 찾기
    owned_coin = None
    for coin in portfolio_data["coins_owned"]:
        if coin["symbol"] == symbol_upper:
            owned_coin = coin
            break

    # 3. 보유 코인 정보 및 현금 업데이트
    portfolio_data["cash"] -= total_cost

    if owned_coin:  # 이미 보유한 코인 (추가 매수)
        print(f"-> 이미 보유한 코인({symbol_upper}) 추가 매수 진행...")
        # 평균 매수 단가(평단가) 재계산
        old_total_value = owned_coin["avg_buy_price"] * owned_coin["quantity"]
        new_total_value = old_total_value + total_cost
        new_total_quantity = owned_coin["quantity"] + quantity

        owned_coin["quantity"] = new_total_quantity
        owned_coin["avg_buy_price"] = new_total_value / new_total_quantity
    else:  # 처음 매수하는 코인
        print(f"-> 신규 코인({symbol_upper}) 매수 진행...")
        new_coin_data = {
            "symbol": symbol_upper,
            "quantity": quantity,
            "avg_buy_price": price_per_unit
        }
        portfolio_data["coins_owned"].append(new_coin_data)

    trade_status_message = f"✅ 매수 성공: {symbol_upper} {quantity}개 @ {price_per_unit:,.2f} KRW"

 

4). 매수 로직 테스트

- 아래 로직을 추가하여 매수가 정상적으로 진행되는지 확인합니다.

# 이 파일을 직접 실행했을 때 간단한 테스트
if __name__ == "__main__":
    # 1. 포트폴리오 상태 불러오기 (파일 없으면 초기화)
    my_vportfolio = load_vportfolio()

    print("\n--- 거래 시작 전 포트폴리오 ---")
    print(json.dumps(my_vportfolio, indent=4, ensure_ascii=False))

    # 2. 가상 매수 거래 테스트
    print("\n--- 가상 매수 테스트 (신규 매수) ---")
    # 예: BTC 0.01개 매수
    updated_portfolio = record_vportfolio_trade(
        my_vportfolio, "buy", "BTC", 0.01, 70000000
    )
    # 거래가 성공했을 때만 포트폴리오 업데이트 및 저장
    if updated_portfolio:
        my_vportfolio = updated_portfolio
        save_vportfolio(my_vportfolio)

    print("\n--- 가상 매수 테스트 (추가 매수) ---")
    # 예: BTC 0.02개 추가 매수 (평단가 바뀌는지 확인!)
    updated_portfolio = record_vportfolio_trade(
        my_vportfolio, "buy", "btc", 0.02, 75000000 # 소문자로 입력해도 대문자로 처리되는지 확인
    )
    if updated_portfolio:
        my_vportfolio = updated_portfolio
        save_vportfolio(my_vportfolio)

    print("\n--- 최종 포트폴리오 상태 ---")
    print(json.dumps(my_vportfolio, indent=4, ensure_ascii=False))

아래와 같이 출력되면 정상입니다.
평단가 등을 테스트하기 위해 가상 자산 또는 매수 가격을 변경해서 테스트 해보셔도 좋습니다.

 

6). 매도 로직

elif trade_type.lower() == "sell":
    # --- 매도 로직 ---
    # 1. 포트폴리오에서 해당 코인 찾기
    owned_coin = None
    for coin in portfolio_data["coins_owned"]:
        if coin["symbol"] == symbol_upper:
            owned_coin = coin
            break

    # 2. 보유 여부 및 수량 확인
    if owned_coin is None:
        print(f"⚠️  매도 실패: {symbol_upper} 코인을 보유하고 있지 않습니다.")
        return None

    if owned_coin["quantity"] < quantity:
        print(f"⚠️  매도 실패: {symbol_upper} 보유 수량 부족 (매도 요청: {quantity}, 보유량: {owned_coin['quantity']})")
        return None

    # 3. 보유 코인 정보 및 현금 업데이트
    total_gain = quantity * price_per_unit
    portfolio_data["cash"] += total_gain
    owned_coin["quantity"] -= quantity

    trade_status_message = f"✅ 매도 성공: {symbol_upper} {quantity}개 @ {price_per_unit:,.2f} KRW"

    # 4. 만약 매도 후 수량이 0.0 이 되면 보유 목록에서 완전히 제거
    if owned_coin["quantity"] <= 0:  # 0 또는 아주 작은 음수가 될 수 있으니 <= 0 으로 체크
        portfolio_data["coins_owned"].remove(owned_coin)
        print(f"-> {symbol_upper} 전량 매도 완료.")
    else:
        print(f"-> {symbol_upper} 부분 매도 완료. (남은 수량: {owned_coin['quantity']})")

 

7). 매도 로직 테스트

my_vportfolio = load_vportfolio()

print("\n--- 거래 시작 전 포트폴리오 ---")
print(json.dumps(my_vportfolio, indent=4, ensure_ascii=False))

# 1. 가상 매도 테스트 (부분 매도)
print("\n--- 가상 매도 테스트 (부분 매도) ---")
updated_portfolio = record_vportfolio_trade(
    my_vportfolio, "sell", "BTC", 0.01, 80000000  # 8천만원에 매도 시도
)
if updated_portfolio:
    my_vportfolio = updated_portfolio
    save_vportfolio(my_vportfolio)

# 2. 가상 매도 실패 테스트 (수량 부족)
print("\n--- 가상 매도 실패 테스트 (수량 부족) ---")
# 예: 남은 BTC보다 많은 수량 매도 시도
updated_portfolio = record_vportfolio_trade(
    my_vportfolio, "sell", "BTC", 0.05, 81000000
)
if updated_portfolio:
    my_vportfolio = updated_portfolio
    save_vportfolio(my_vportfolio)

# 3. 가상 매도 테스트 (전량 매도)
print("\n--- 가상 매도 테스트 (전량 매도) ---")
current_btc_quantity = 0
for coin in my_vportfolio["coins_owned"]:
    if coin["symbol"] == "BTC":
        current_btc_quantity = coin["quantity"]
        break

if current_btc_quantity > 0:
    updated_portfolio = record_vportfolio_trade(
        my_vportfolio, "sell", "BTC", current_btc_quantity, 82000000
    )
    if updated_portfolio:
        my_vportfolio = updated_portfolio
        save_vportfolio(my_vportfolio)

print("\n--- 최종 포트폴리오 상태 ---")
print(json.dumps(my_vportfolio, indent=4, ensure_ascii=False))

아래와 같이 출력되면 정상입니다.

 

다음 단계

  • 매크로의 두뇌 함수 제작!!

 

끝!!