1. 가상 자산 관리
- virtual_portfolio_manager.py 생성
1). import 선언
import json # 관리의 용이를 위해 json 사용
import os # 파일 존재 여부 확인을 위해
2). 파일 이름 정의
# --- 상수 정의 ---
VPORTFOLIO_FILE = "vportfolio_state.json" # 가상 자산 상태를 저장할 파일 이름
3). 가상 자산 초기화 함수 정의
- 매크로 최초 실행 시 제공 할 가상자산을 설정합니다.
- 현금, 보유 코인, 수익실현(PT), 손절매(SL) 값 지정
# --- 가상 자산 초기화 함수 ---
def initialize_vportfolio():
print("새로운 포트폴리오를 초기화합니다...")
return {
"cash": 1000000.0, # 초기 가상 현금 100만원
"coins_owned": [], # 보유 코인 목록 (비어있음)
"strategy_params": {
"profit_take_percentage": 0.1, # 수익실현 +10%
"stop_loss_percentage": 0.05, # 손절매 -5%
},
}
4). 자산 Load 함수 정의
- 이미 자산 정보가 존재한다면 자신을 로드
- 자산 정보가 없다면 초기 값 반환
# --- 가상 자산 로드 함수 ---
"""
지정된 파일에서 포트폴리오 상태를 로드
파일이 없으면 초기 상태를 반환
"""
def load_vportfolio(filepath=VPORTFOLIO_FILE):
if os.path.exists(filepath): # 파일이 존재 할 경우
try:
with open(filepath, 'r', encoding='utf-8') as f:
state_data = json.load(f)
print(f"✅ '{filepath}'에서 포트폴리오 상태를 성공적으로 불러왔습니다.")
return state_data
except Exception as e:
print(f"⚠️ 파일 불러오기 중 오류 발생: {e}. 초기 포트폴리오로 시작합니다.")
# 오류 발생 시 (예: JSON 형식이 깨졌을 때) 안전하게 초기 상태로 시작
return initialize_vportfolio()
else: # 파일이 존재하지 않을 경우
print(f"ℹ️ '{filepath}' 파일이 존재하지 않습니다. 새 포트폴리오를 시작합니다.")
return initialize_vportfolio() # 파일이 없으면 초기 상태로 시작
5). 자산 Save 함수 정의
- 현재 자산을 파일에 저장
# --- 가상 자산 저장 함수 ---
"""
주어진 포트폴리오 상태 data를 JSON 파일에 저장
"""
def save_vportfolio(state_data, filepath=VPORTFOLIO_FILE):
try:
with open(filepath, 'w', encoding='utf-8') as f:
json.dump(state_data, f, ensure_ascii=False, indent=4)
print(f"✅ 포트폴리오 상태가 '{filepath}'에 성공적으로 저장되었습니다.")
return True
except Exception as e:
print(f"⚠️ 파일 저장 중 오류 발생: {e}")
return False
6). 자산 Update 함수 정의
- 매수/매도를 통해 자산에 변화가 생기면 Data를 업데이트하고 파일로 저장
# --- 가상 자산 변경 함수 ---
"""
주어진 포트폴리오 데이터에서 현금을 변경하고, 변경된 포트폴리오 데이터를 파일에 저장한 후 반환
:current_vportfolio_data: 현재 포트폴리오 상태 dictionary
:amount_to_change: 변경할 현금 액수 (양수면 증가, 음수면 감소)
:return: 현금이 변경된 포트폴리오 상태 dictionary
"""
def update_vportfolio_cash(current_vportfolio_data, amount_to_change): # filepath 인자를 받을 필요 없어짐
if "cash" not in current_vportfolio_data:
print("⚠️ 오류: 포트폴리오 데이터에 'cash' 키가 없습니다.")
return current_vportfolio_data # 변경 없이 그대로 반환
original_cash = current_vportfolio_data["cash"]
current_vportfolio_data["cash"] += amount_to_change
print(f"💰 가상 현금 변경: {original_cash:.2f} KRW -> {current_vportfolio_data['cash']:.2f} KRW (변경액: {amount_to_change:+.2f} KRW)")
# *** 변경된 핵심: 현금 변경 후 바로 파일에 저장! ***
save_vportfolio(current_vportfolio_data) # VPORTFOLIO_FILE 상수를 사용해서 기본 파일에 저장
return current_vportfolio_data
7). 테스트용 main부
# 이 파일을 직접 실행했을 때 간단한 테스트
if __name__ == "__main__":
# 1. 로드 시도 (파일 없으면 초기화됨)
vportfolio = load_vportfolio()
print("\n--- 로드된 (또는 초기화된) 포트폴리오 ---")
print(json.dumps(vportfolio, indent=4, ensure_ascii=False))
# 2. 약간의 변경 후 저장 테스트
update_vportfolio_cash(vportfolio, -20000)
# 3. 변경 후 포트폴리오 출력
print("\n--- 값이 변경 된 포트폴리오 ---")
print(json.dumps(vportfolio, indent=4, ensure_ascii=False))
8). 실행 후 결과 확인
- 최초 실행 후 초기 Data 확인

- 재실행 후 결과가 변경됨을 확인

2. 거래 내역
- trade_history.py 생성
1). import 선언
import json # 관리의 용이를 위해 json 사용
import os # 파일 존재 여부 확인을 위해
import time # 거래 시간 저장을 위해
2). 파일 이름 및 거래 모드 정의
- 가상 거래와 실제 거래를 구분하기 위해 별도의 파일 지정
# --- 상수 정의 ---
_IS_VIRTUAL_MODE = True
REAL_TRADE_HISTORY_FILE = "trade_history.json" # 거래 내역 파일 이름 상수 추가
VIRTUAL_TRADE_HISTORY_FILE = "vtrade_history.json" # 거래 내역 파일 이름 상수 추가
3). 거래 모드를 지정하는 함수 정의
# --- 거래 모드 설정 함수 ---
"""거래 기록 모드를 설정 (Virtual 또는 Real)."""
def set_trade_mode(is_virtual=True):
global _IS_VIRTUAL_MODE
_IS_VIRTUAL_MODE = is_virtual
mode_name = "가상" if _IS_VIRTUAL_MODE else "실제"
print(f"ℹ️ 거래 기록 모드가 '{mode_name}'으로 설정되었습니다.")
4). 거래 내역 파일 경로 반환 함수 정의
# --- 거래 내역 파일 경로 반환 함수 ---
"""현재 설정된 모드에 맞는 거래 내역 파일 경로를 반환"""
def _get_current_history_filepath():
if _IS_VIRTUAL_MODE:
return VIRTUAL_TRADE_HISTORY_FILE
else:
return REAL_TRADE_HISTORY_FILE
5). 거래 내역 저장 함수 정의
# --- 거래 내역 저장 함수 ---
"""
거래 내역을 지정된 파일(JSON 리스트 형식)에 추가
파일이 없으면 새로 만들고, 내용이 잘못되었으면 경고 후 새 내역으로 시작
:trade_info: 추가할 거래 정보 (딕셔너리)
:return: 성공 시 True, 실패 시 False
"""
def add_trade_to_history(trade_info):
history_filepath = _get_current_history_filepath()
history = [] # 기본적으로 빈 리스트로 시작
if os.path.exists(history_filepath):
try:
with open(history_filepath, 'r', encoding='utf-8') as f:
content = f.read()
if content.strip(): # 파일 내용이 공백이 아닐 때만 로드 시도
history = json.loads(content)
# 로드한 데이터가 리스트 형태인지 확인
if not isinstance(history, list):
print(f"⚠️ 경고: '{history_filepath}' 파일 내용이 리스트 형식이 아닙니다. 새 거래 내역으로 시작합니다.")
history = [] # 잘못된 형식이면 초기화
else:
# 파일은 있지만 내용이 비어있는 경우
history = [] # 빈 리스트로 시작
except json.JSONDecodeError:
# JSON 형식이 아니거나 손상된 경우
print(f"⚠️ 경고: '{history_filepath}' 파일의 JSON 형식이 잘못되었거나 비어있습니다. 새 거래 내역으로 시작합니다.")
history = [] # 오류 시 빈 리스트로 시작
except Exception as e:
print(f"⚠️ 거래 내역 파일 불러오기 중 예상치 못한 오류 발생: {e}")
# 심각한 I/O 오류 등이면 False 반환하여 호출한 쪽에서 알 수 있게 함
return False
# 새 거래 내역 추가
history.append(trade_info)
# 업데이트된 전체 거래 내역을 파일에 저장
try:
with open(history_filepath, 'w', encoding='utf-8') as f:
json.dump(history, f, ensure_ascii=False, indent=4)
print(f"📜 거래 내역이 '{history_filepath}'에 성공적으로 추가/업데이트되었습니다.")
return True
except Exception as e:
print(f"⚠️ 거래 내역 파일 저장 중 오류 발생: {e}")
return False
6). 거래 내역 출력 함수 정의
# --- 거래 내역 출력 함수 ---
"""
저장된 거래 내역(JSON 파일)을 불러와서 사람이 보기 좋은 형태로 콘솔에 출력
"""
def print_trade_history():
history_filepath = _get_current_history_filepath()
if not os.path.exists(history_filepath):
print(f"ℹ️ 거래 내역 파일 ('{history_filepath}')이 아직 없습니다.")
return
try:
with open(history_filepath, 'r', encoding='utf-8') as f:
content = f.read()
if not content.strip(): # 파일 내용이 비어있는지 확인
print("ℹ️ 거래 내역이 비어있습니다.")
return
history = json.loads(content)
if not isinstance(history, list) or not history: # 로드된 데이터가 리스트가 아니거나 비어있는 경우
print("ℹ️ 거래 내역이 없거나 파일 형식이 잘못되었습니다.")
return
print("\n--- 📜 가상 거래 내역 📜 ---")
for i, trade in enumerate(history):
# trade dictionary에 있을 것으로 예상되는 키들 (실제 저장하는 키에 맞춰야 함)
ts = trade.get("timestamp", "시간정보없음")
trade_type = trade.get("type", "N/A").upper() # BUY 또는 SELL
symbol = trade.get("symbol", "알수없음")
quantity = trade.get("quantity", 0)
price = trade.get("price_per_unit", 0)
total_value = trade.get("total_value", 0)
cash_after_trade = trade.get("cash_after_trade", 0)
# 현금 변동 표시 (매수면 마이너스, 매도면 플러스로 가정)
cash_flow_indicator = ""
if trade_type == "BUY":
cash_flow_indicator = f"(현금 변동: -{total_value:,.0f} KRW)"
elif trade_type == "SELL":
cash_flow_indicator = f"(현금 변동: +{total_value:,.0f} KRW)"
cash_balance_info = f"(거래 후 현금 : {cash_after_trade:,.0f} KRW)"
print(f"{i + 1}. [{ts}] [{trade_type}] {symbol} {quantity}개 @ {price:,.0f} KRW (총액: {total_value:,.0f} KRW) {cash_flow_indicator} {cash_balance_info}")
if not history: # 이중 체크: 혹시 반복문 안 돌았으면
print("기록된 거래가 없습니다.")
print("--- 거래 내역 끝 ---")
except json.JSONDecodeError:
print(f"⚠️ 오류: 거래 내역 파일 ('{history_filepath}')의 JSON 형식이 잘못되었습니다.")
except Exception as e:
print(f"⚠️ 거래 내역을 불러오는 중 오류 발생: {e}")
7). 테스트용 main부
# 이 파일을 직접 실행했을 때 간단한 테스트 (선택 사항)
if __name__ == "__main__":
print("\n--- 거래 내역 추가 테스트 ---")
# 테스트용 샘플 거래 정보 (실제로는 record_vportfolio_trade 함수 등에서 생성될 데이터)
sample_trade_1 = {
"timestamp": time.strftime("%Y-%m-%d %H:%M:%S"), # 현재 시간을 보기 좋은 형태로
"type": "buy",
"symbol": "BTC",
"quantity": 0.001,
"price_per_unit": 70000000,
"total_value": 70000.0,
"cash_after_trade": 930000.0,
"notes": "First virtual buy test"
}
add_trade_to_history(sample_trade_1)
sample_trade_2 = {
"timestamp": time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(time.time() + 60)), # 1분 뒤
"type": "sell",
"symbol": "BTC",
"quantity": 0.0005,
"price_per_unit": 71000000,
"total_value": 35500.0,
"cash_after_trade": 965500.0,
"notes": "Partial virtual sell for profit test"
}
add_trade_to_history(sample_trade_2)
print("\n--- 저장된 거래 내역 출력 테스트 ---")
print_trade_history()
7). 실행 후 결과 확인
- 최초 실행 후 초기 Data 확인

- 재실행 후 결과가 변경됨을 확인

끝!!
'프로젝트 > 코인 투자 매크로' 카테고리의 다른 글
| 2025-06-08 [8] [v1.0.0 완료] 캔들스틱, 단순 이동 평균(SMA), 골든/데드 크로스 (3) | 2025.06.08 |
|---|---|
| 2025-06-08 [7] 가상 매수/매도 함수 구현 (1) | 2025.06.08 |
| 2025-06-04 [5] 투자 방법 설계 (2) | 2025.06.04 |
| 2025-06-01 [4] bithumb_api_client.py (2) | 2025.06.01 |
| 2025-05-23 [3] Private API Test (1) | 2025.05.24 |