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

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

by Slate_Knowledge 2023. 7. 24.
728x90

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

데모 :

 

이전 글 :

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

 

파이썬만 사용해서 커스텀 코인 트레이더 웹사이트 만들기 - 2 : 기본 UI

TL;DR : UI 완성본 링크(이후 포스팅에 맞추어 업데이트 예정) 시리즈 이전 발행글 : 2023.04.06 - [파이썬으로 웹 App 작성하기/파이썬으로 Upbit 자동거래하기] - Anvil과 파이썬 Upbit API로 커스텀 코인 트

doodlrudco.tistory.com

 

모의투자 시스템

이전 게시글까지 완성한건 트레이더의 어플리케이션의 UI 까지이다. 그러면 이제, UI에 기능들을 엮어 모의투자 어플리케이션을 완성하고 나아가 실제 트레이딩 및 자동거래 알고리즘 관리까지 구현해보도록 하자. 본 포스팅에서는 모의투자 어플리케이션까지의 완성을 다룬다.

1. 사용자 등록 및 모의투자 잔고 생성

모의투자 앱의 가장 첫걸음은 사용자별로 유지할 수 있는 잔고 및 거래기록의 DB를 구축하는 것이다. 본 포스팅에서는 이를 위해 Anvil에서 제공하는 Multi-User App 기능, 그 중에서도 Google Authentication 기능을 활용하고자 한다.

Anvil은 자체적으로 Multi-User 관리 형식을 제공한다. 보안이 좀 취약하긴 하지만 ID + Password 방식부터 Facebook으로 로그인하기, Microsoft로 로그인하기 등 신뢰할 수 있는 플랫폼을 통한 인증까지도 제공하는데, 기타 플랫폼을 거치는 인증을 사용할 경우 ID+Password 방식과 같이 사용하다보면 동일한 ID로 신규 ID를 생성해버리는 경우 어이없이 보안이 뚫리는 경우가 생길 수 있으므로 본 어플리케이션에서는 구글 Auth만 사용하도록 한다.

참고 튜토리얼: https://anvil.works/learn/tutorials/multi-user-apps

 

Multi-User Applications with Anvil

In this tutorial, we’ll extend a news aggregator app so that each user can store their own set of articles.

anvil.works

Google Authentication

Google 로그인 API를 사용하도록 설정하는 것은 매우 간단하다. 아래와 같이 좌측 사이드바에서 + 모양 버튼을 누르고, 거기서 Google API를 추가해주기만 하면 된다.

Google API(로그인 및 지메일, 지드라이브 기능) 활성화

그 다음, 멀티 유저 관리 기능인 Users도 추가해주고 관리 창에 들어가면 다음과 같은 화면을 볼 수 있다. 아래의 화면에서  "Supported sign-in methods" 아래 원래 체크되어있던 Email+Password를 체크 해제하고 그 옆 "Sign in with Google" 만 체크해서 남겨준다.

그 다음 그 아래 "New user registration" 항목에서 "Allow visitors to sign up" 을 체크해주면, 맨 아래에 자동적으로 Users(사용자 명부) 관리 스키마가 선언되면서 DB에 Users 테이블이 생성되는걸 확인할 수 있다. 튜토리얼에서는 일일히 테이블을 만들고 해야하는데 해보니까 이게 제일 간편하고 좋다.

구글 로그인 기반 사용자 추가 및 접속 관리
자동으로 스키마가 생성된 Users Table 데이터베이스

여기까지 해준다음, 그 동안 미뤄왔던 main 페이지 및 사이드바의 구성을 아래와 같이 간단하게 해주고

Main Form의 코드 __init__ 부분에 아래와 같이 login 구문을 넣어준다.

anvil.users.login_with_form()

그러면 이제 웹 앱을 접속할때마다, 아래와 같이 로그인 창이 뜨게 된다.

구글 로그인을 연동한 로그인 기능

이제 이 로그인 정보를 기반으로, 유저별로 별도의 정보를 관리하면 다중 유저 모의투자 시스템을 구축할 수 있다.추가적으로, 지금은 신규유저가 마음대로 가입할 수 있도록 해놨지만, 제한된 계정들만을 허용하고 싶다면 원하는 계정들이 전부 등록된 이후 상기 접속 관리 탭에서 "Allow visitors to sign up" 항목을 체크 해제시켜주자. 그러면 이후부터는 Users DB에 등록되어있지 않은 계정이 접근할 경우 아래와 같이 로그인이 거부된다.

미등록 계정 접근 거부

제일 먼저 각 유저별로 잔고 정보를 할당해보자. 신규 유저에게는 10만원을 자동으로 할당하게끔 하고, 이후 다른 Form의 초기화 단계에서도 User 정보를 반영하도록 코드를 수정해주는 과정을 거쳐야 한다.

1) 잔고 데이터베이스 생성

제일 먼저 각 유저의 잔고 정보를 저장해둘 데이터베이스를 생성해주자. 편집 UI의 좌측 상단 DB 아이콘을 클릭해서 DB 관리로 넘어간 다음 "Add New Table"를 눌러 기존 자동 생성된 "Users"(유저 DB) 밑에 "Credits" 이름으로 새로운 테이블을 생성해준다.

Credits(자산) 테이블 생성

그 다음, "+New Column"을 눌러서 아래와 같이 유저명(Text)과 각 유저의 Credit(Number) 칼럼을 추가해 준다.

User 와 Credit Column

이제 신규 유저가 들어왔을 경우 100,000 원을 할당하도록 코드를 작성해줄건데, 기본적으로 DB 테이블은 클라이언트가 아니라 서버에서만 접근 및 수정이 가능하도록 권한 설정이 되어있으므로 신규유저인지 여부를 판단할 수 있도록 Form에서의 Read 권한을 열어주고, 수정을 담당할 CreditManager 서버 모듈을 추가해준다.

테이블 권한 수정 및 서버모듈 추가

신규유저 판단은 Main Form에서 아래와 같이 할 수 있는데,

class Main(MainTemplate):
  def __init__(self, **properties):
    # Set Form properties and Data Bindings.
    self.init_components(**properties)
    # Any code you write here will run before the form opens.
    anvil.users.login_with_form() ## 여기까지 기존 로그인
    
    user = anvil.users.get_user() # 현재 유저 읽어오기
    user_email = dict(user)['email'] # 현재 유저 테이블을 dict로 바꾸고 그 중 email 읽어오기
    if app_tables.credits.get(User=user_email) is not None: # credits 테이블에 유저 이메일이 있는지 쿼리
      print('Already in')
    else: # 없으면 새로 유저 크레딧 생성
      print('Creating New User')
      anvil.server.call('add_new_user', user_email)

주석에도 나와있듯이 현재 로그인한 유저 정보를 가져와서 유저 이메일이 credits 테이블에 존재하는지를 쿼리하고, 없으면 새로 크레딧을 만들어 할당해주는 서버코드를 호출한다.

여기서 호출되는 add_new_user 서버코드를 위에서 생성한 CreditManager 서버 모듈에 아래와 같이 간단히 추가해주면 된다

@anvil.server.callable
def add_new_user(user_email):
  app_tables.credits.add_row(User=user_email, Credit=100000)

이렇게 작성한 코드를 실행하면 아래와 같이 비어있던 credits 테이블에 성공적으로 초기 자금 100,000원과 코인 자산이 생성되는걸 확인할 수 있다.

초기자금 생성

초기 자산은 성공적으로 성공했지만, 아직 남은 부분이 있다. 바로 코인 자산에 관한 부분인데, 각자 코인을 매수했을 때 원화는 차감되고 그에 상응하는 코인 수량이 기록이 되어야 제대로된 장부라고 할 수 있겠다. 따라서, 코인에 대한 정보를 기록하기 위해 아래와 같이 Column 하나를 더 추가해주고, CreditManager의 add_new_user 코드도 수정해주도록 한다.

기존 row를 우클릭해서 지워준 다음, 새로 추가할 Column은 위에서 보이는 것과 같이 "Simple Object Column" 을 추가해주도록 하자. 그러면 파이썬 객체를 데이터로 기록할 수 있다. 이렇게 하는 이유는 이래야 새로운 마켓이 생기거나 기존 마켓이 사라져도 에러 핸들링이 이후 용이하고, 파이썬에서 다루기도 쉬워지기 때문이다.  각 마켓별로 Column을 미리 다 생성해둘수도 있지만, 이러면 DB 관리 문법을 사용해야 하므로 좀 더 까다롭다.

이제 이렇게 새로 추가한 column 정보를 반영하는 add_new_user 코드 수정을 아래와 같이 진행한다.

@anvil.server.callable
def add_new_user(user_email):
  markets = get_market_list()
  init_budget = {m:0 for m in markets}
  app_tables.credits.add_row(User=user_email, Credit=100000,Coins=init_budget)

def get_market_list():
  import json
  url = "https://api.upbit.com/v1/market/all?isDetails=true"
  headers = {"accept": "application/json"}
  res = anvil.http.request(url=url, method='GET', headers =headers)
  data = json.loads(res.get_bytes().decode('utf-8'))
  return [d['market'] for d in data if 'KRW' in d['market']]

chart 등에서 사용하는 get_market_list 함수를 그대로 가져와서 현재 업비트 거래소에 등록된 마켓 정보를 리스트업하고 초기 보유 잔고를 모두 0으로 설정하여 Coins 항목에 dictionary 형으로 저장해주면 끝이다. 그러면 아래의 예시와 같이 새로운 유저가 들어왔을 때, 초기 잔고를 10만원 크레딧과 빈 코인 지갑으로 세팅할 수 있다.

초기 잔고 세팅 완료

2) 잔고 확인 페이지

이렇게 잔고 세팅을 완료했으니, 이제 이걸 확인할 수 있는 페이지를 만들고, 매수/매도 창에서도 각 코인별 잔고 및 매수가능 현금을 조회할 수 있도록 데이터를 연동해주자.

가장 단순하게 잔고를 확인할 수 있는 방법은 역시 테이블로 일목요연하게 확인할 수 있는 창일 것이다. Upbit의 잔고확인창을 보아도 유사하게 전체 마켓에 대한 나의 현재 잔고를 보여주는걸 볼 수 있다.

작고 귀여운 잔고

Main 밑에 Assets Form을 하나 더 만들어주고, 거기에 Anvil에서 지원하는 테이블을 추가하도록 한다. 새로운 Form을 만들었으면 그 밑에 "Data Grid" 컴포넌트를 추가해준다.

Data Grid를 처음 추가하면 아래와 같이 총 3개의 column을 가진 데이터 테이블이 생성되는데, 우리는 Asset 종류와 양(Quantity) 만 필요하므로, Toolbox에서 스크롤을 좀 더 내려 데이터그리드의 속성을 아래와 같이 변경해주도록 하자.

초기 Data Grid 컴포넌트

COLUMNS 속성에서 하나의 column을 "Remove Column" 버튼을 눌러서 지워버리고, 나머지 두 칼럼을 아래와 같이 설정해준다.

그리고, APPEARANCE 속성을 아래와 같이 "tonal-data-grid"로 변경해주면 

최종적으로는 아래와 같은 데이터 테이블이 만들어진다.

이제 여기에 현재 잔고정보를 명시하기 위해서 Code 탭으로 넘어가 데이터를 넣어주자.

class Assets(AssetsTemplate):
  def __init__(self, **properties):
    # Set Form properties and Data Bindings.
    self.init_components(**properties)
    user = anvil.users.get_user()
    user_email = dict(user)['email']
    curr_tables = anvil.server.call('get_current_assets', user_email)
    curr_tables = [dict(l) for l in curr_tables][0]
    
    cur_assets = [{'Asset':'Credit(KRW)', 'Quantity' : curr_tables['Credit']}] + [{'Asset':k, 'Quantity':v} for k,v in sorted(curr_tables['Coins'].items()) if v != 0] +\
    [{'Asset':k, 'Quantity':v} for k,v in sorted(curr_tables['Coins'].items()) if v == 0]
    self.repeating_panel_1.items = cur_assets
    # Any code you write here will run before the form opens.

상기 코드는 필자가 작성한 Assets Form의 코드이다. 초기화(__init__) 함수 부분에서 서버함수인 'get_current_assets'를 유저의 이메일 정보와 함께 호출하여 dictionary 형태로 값을 받으면, 이걸 List of Dictionary 형태로 변환하여 테이블에 넣어준다.(Anvil의 Data Grid의 기본 데이터 처리 양식)

이때, 

for k,v in sorted(curr_tables['Coins'].items()) if v != 0

과 같이 내가 보유하고 있는(v : 자산 보유량 이 0이 아닌) 코인들을 선제적으로 필터링하여 보여주고 나머지는 알파벳 순서로 정렬하여 넣도록 한다.

get_current_assets 함수를 서버쪽에서 서비스 할 수 있도록 아래와 같이 서버 코드를 추가해준다. 상기 유저관리 부분에서 생성해둔 'CreditManager' 서버코드에 아래와 같은 callable을 추가해주면 매우 간단하게 완성. app_tables에서 미리 만들어둔 credits 테이블을 읽어와서 그 중 user_email이 현재 유저와 같은 부분을 가져온다.

@anvil.server.callable
def get_current_assets(user_email):
  return app_tables.credits.search(User=user_email)

여기까지 마친 다음 실행해주면, 아래와 같은 Asset 테이블을 만날 수 있다.

모의투자 시스템 완성을 위한 매수/매도 연동은 다음 글에서 이어진다.

728x90
반응형

댓글