본문 바로가기
파이썬으로 웹 App 작성하기/파이썬으로 Upbit 자동거래하기

파이썬만 사용해서 커스텀 코인 트레이더 웹사이트 만들기 - 3 : 모의투자 시스템(2)

by Slate_Knowledge 2023. 7. 25.
728x90

모의투자 시스템 링크(구글 인증을 통해서 모의투자 자동 가입 가능)(찝찝하면 https://temp-mail.org/ 과 같은 임시 메일 서비스를 이용해 가입후 초기 1회 인증하면 이후 해당 아이디로 로그인 가능)

데모 :

 

이전 글 : 2023.07.24 - [파이썬으로 웹 App 작성하기/파이썬으로 Upbit 자동거래하기] - 파이썬만 사용해서 커스텀 코인 트레이더 웹사이트 만들기 - 3 : 모의투자 시스템(1)

 

파이썬만 사용해서 커스텀 코인 트레이더 웹사이트 만들기 - 3 : 모의투자 시스템(1)

이전 글 : 2023.05.25 - [파이썬으로 웹 App 작성하기/파이썬으로 Upbit 자동거래하기] - 파이썬만 사용해서 커스텀 코인 트레이더 웹사이트 만들기 - 2 : 기본 UI 파이썬만 사용해서 커스텀 코인 트레이

doodlrudco.tistory.com

모의투자를 위한 매수/매도

모의투자를 위해 각 사용자를 등록하고 신규 등록 계정에 대해 가상자산을 할당하는 기능까지 완성했으니, 이제 모의투자를 위해서 가장 중요한 거래기능을 구현해보도록 하자.

(1) 현재가 및 현재 자산 반영하기

이전 글까지 매수/매도 창은 레이아웃만 존재했지 현재 나의 잔고나 주문 가능 수량등 아무런 정보도 보여주지 않았다. 이제 해당 창에 올바른 정보를 할당해주도록 하자.

아무런 Data도 바인드 되지 않은 매도창

Main.Trade.Buy와 Main.Trade.Sell 폼에 정보를 넣어주기 위해 각각의 폼에서 가장 먼저 "주문가능" 창을 채워보도록 한다. 매수창에서 "주문가능"이란 현재 시장(KRW)에서 내가 주문에 사용할 수 있는 금액을 얘기하므로 Credit 값이 실시간으로 반영되고, 매도창에서는 현재 선택된 시장(코인)의 보유량을 보여주도록 해야 맞을것이므로 아래와 같이 수정해준다.

제일 먼저, 현재 상태를 Query 하기 위해서는 선택한 시장 정보와 이전 글의 말미에서도 볼 수 있듯이 사용자 email 정보가 필요하므로 초기화 코드부터 손 볼 필요가 있다. __init__ 부분에서 각각 현재 로그인한 유저의 정보를 획득하고, 이에 따른 현재 정보를 1차로 쿼리하도록 한다. 또한, Buy 와 Sell의 부모 컴포넌트인 Trade 컴포넌트에서 market 정보를 넘겨받아 연동하도록 하자.

market 정보를 통해서 Buy, Sell 컴포넌트 초기화

 

현재 자산 정보를 쿼리하는 코드는 이전 글에서 사용했던 서버측의 "get_current_assets" 함수를 호출하도록 하자. 그럼 아래와 같이 쓸 수 있다.

class Buy(BuyTemplate):
  def __init__(self, market, **properties):
    # Set Form properties and Data Bindings.
    self.init_components(**properties)
    self.market = market
    user = anvil.users.get_user()
    self.user_email = dict(user)['email']
    self.query_current_credit()
    # Any code you write here will run before the form opens.
  
  def query_current_credit(self):
    curr_assets = anvil.server.call('get_current_assets', self.user_email)
    curr_assets = [dict(l) for l in curr_assets][0]
    self.rich_text_5.content = curr_assets['Credit']
class Sell(SellTemplate):
  def __init__(self, market, **properties):
    # Set Form properties and Data Bindings.
    self.init_components(**properties)
    self.market = market
    user = anvil.users.get_user()
    self.user_email = dict(user)['email']
    self.query_current_coin()
    # Any code you write here will run before the form opens.

  def query_current_coin(self):
    curr_assets = anvil.server.call('get_current_assets', self.user_email)
    curr_assets = [dict(l) for l in curr_assets][0]
    self.rich_text_5.content = curr_assets['Coins'][self.market]

위와 같이 작성한 후 Main.Trade 폼에서

market dropdown change마다 orderbook 및 메인 plot의 market정보를 갱신하듯 Buy및 Sell의 market 정보도 갱신하고, 현재 자산을 표시해 줄 수 있도록 한다.

이제 여기서 현재 시장의 코인 가격 또한 매수/매도창에 반영하는걸 추가해보도록 하자. 원래는 지정가 주문도 지원을 하려고 했지만, 생각보다 까다로운 작업이 많아 일단은 모의투자 시스템에서는 시장가 매매만 지원하고 이후 실제 매매시스템과 연동할때만 지정가 매매를 지원하도록 한다. 

현재 timer가 orderbook 갱신을 하면서 orderbook에서 코인 현재가를 지속적으로 쿼리하고 있기 때문에, 이를 활용하여 매수/매도 창에도 현재 코인가격을 명시하도록 한다.

Main.Trade.Orderbook 폼에서 기존

  def plot(self):
    orderbook = self.get_orderbook(self.market)
    self.plot_orderbook(orderbook)
    curr_price = self.get_current_price(self.market)
    self.label_2.text = curr_price

와 같이 되어있던 코드를

  def plot(self, return_price=False):
    orderbook = self.get_orderbook(self.market)
    self.plot_orderbook(orderbook)
    curr_price = self.get_current_price(self.market)
    self.label_2.text = curr_price
    if return_price:
      return curr_price

이렇게 현재가를 반환하게 바꿔준 다음 아까 바꿔줬던 timer_1_tick 함수에 아래와 같이 반영하는 구문을 짜준다.

def timer_1_tick(self, **event_args):
    """This method is called Every [interval] seconds. Does not trigger if [interval] is 0."""
    self.plot(self.market)
    chart = self.orderbook.get_components()[0]
    chart.market = self.market
    price = chart.plot(return_price=True)
    self.buytab.rich_text_6.content = price
    self.selltab.rich_text_6.content = price

(2) 매수/매도 기능

현재가를 반영하도록 정보를 잘 연동했으니, 이제 이 정보를 바탕으로 거래(transaction)를 처리해줄 수 있도록 서버함수와 이를 호출하는 매수/매도 버튼 클릭 이벤트 핸들러를 할당해보자.

transaction 서버 함수는 아래와 같이 간단하게 짤 수 있다.

@anvil.server.callable
def commit_transaction(user_email, type, market, quantity, price):  
  current_user_asset = app_tables.credits.search(User=user_email)
  curr_assets = [dict(l) for l in current_user_asset][0]
  if type == 'Buy':
    current_credit = curr_assets['Credit']
    current_coins = curr_assets['Coins']
    new_coins = current_coins
    new_credit = current_credit - quantity*price
    if new_credit < 0:
      new_credit = 0
      quantity = current_credit / price
    new_coins[market] = current_coins[market] + quantity
    app_tables.credits.get(User=user_email).update(Credit=new_credit, Coins=new_coins)
  elif type == 'Sell':
    current_credit = curr_assets['Credit']
    current_coins = curr_assets['Coins']
    new_coins = current_coins
    new_coins[market] = current_coins[market] - quantity
    if new_coins[market] < 0:
      new_coins[market] = 0
      quantity = current_coins[market]
    new_credit = current_credit + quantity*price
    app_tables.credits.get(User=user_email).update(Credit=new_credit, Coins=new_coins)
  else:
    raise ValueError("Transaction Type should be either 'Buy' or 'Sell'")

1. transaction type 이 'Buy'인 경우 현재 크레딧에서 거래하고자 하는 코인의 양(quantity)와 가격(price)를 곱한 값을 차감하고 quantity 만큼의 코인 자산을 추가한다. 이때, quantity와 price를 곱한 값이 전체 자산보다 클 경우, 전체 자산만큼만 산다.

2. transaction type이 'Sell'인 경우 현재 코인 자산에서 quantity만큼을 감소시키고 현재 크레딧에 거래하고자 하는 코인의 양(quantity)와 가격(price)를 곱한 값을 더해준다. 이때, quantity가 전체 코인 자산보다 클 경우 전체 자산만큼만 판다.

위와 같이 서버 함수를 준비했으니, 이제 해당 함수를 잘 호출할 수 있도록 Main.Trade.Buy와 Main.Trade.Sell 폼에 있는 매수/매도 버튼의 클릭 이벤트 핸들러를 정의해준다.

매수 버튼을 더블클릭하여 이벤트 핸들러 정의화면 진입

위와 같이 디자인 툴에서 매수 버튼을 더블클릭하여 간단하게 Code 영역에 이벤트 핸들러 구문을 생성하고 버튼에 해당 함수를 링크시켜준다. 그런 다음 아래와 같이 서버함수를 호출하게 만든다. 마지막으로 transaction 결과를 반영하기 위해 크레딧 정보를 갱신한다.

  def button_2_click(self, **event_args):
    """This method is called when the button is clicked"""
    anvil.server.call('commit_transaction', \
                        user_email=self.user_email, \
                        type='Buy',\
                        market=self.market, \
                        quantity=float(self.text_box_3.text), \
                        price=self.rich_text_6.content
                     )
    self.query_current_credit()

비슷하게, Main.Trade.Sell 의 디자인 툴에서 매도 버튼을 더블클릭하여 아래와 같이 이벤트 핸들러를 정의해주면

매도 버튼을 더블클릭하여 이벤트 핸들러 정의화면 진입

  def button_2_click(self, **event_args):
    """This method is called when the button is clicked"""
    anvil.server.call('commit_transaction', \
                        user_email=self.user_email, \
                        type='Sell',\
                        market=self.market, \
                        quantity=float(self.text_box_3.text), \
                        price=self.rich_text_6.content
                     )

기능이 완성된다. 

맨 처음에 봤던 데모 예시처럼 동작한다.

이 다음부터는 구성된 모의투자 시스템을 이용해서 실제 투자 알고리즘을 학습 및 검증해보고, 이후 실제 투자 API와 연동하여 배포하는 스텝을 밟아보겠다.

728x90
반응형

댓글