당신은 주제를 찾고 있습니까 “업 비트 프로그램 – 누구나 할 수 있는 비트코인 투자 자동화 강의 시작합니다“? 다음 카테고리의 웹사이트 ppa.maxfit.vn 에서 귀하의 모든 질문에 답변해 드립니다: https://ppa.maxfit.vn/blog/. 바로 아래에서 답을 찾을 수 있습니다. 작성자 조코딩 JoCoding 이(가) 작성한 기사에는 조회수 226,867회 및 좋아요 3,127개 개의 좋아요가 있습니다.
업 비트 프로그램 주제에 대한 동영상 보기
여기에서 이 주제에 대한 비디오를 시청하십시오. 주의 깊게 살펴보고 읽고 있는 내용에 대한 피드백을 제공하세요!
d여기에서 누구나 할 수 있는 비트코인 투자 자동화 강의 시작합니다 – 업 비트 프로그램 주제에 대한 세부정보를 참조하세요
누구나 할 수 있는 파이썬 비트코인 투자 자동화 강의를 시작합니다. 파이썬을 활용하여 비트코인 투자 전략을 구현해보고 업비트 거래소 API를 통해 가격이 기술적 전략에 부합할 때 자동으로 매매하는 프로그램을 개발합니다.
백테스팅을 통해서 비트코인을 그냥 들고 있을 때와 변동성 돌파전략으로 자동매매를 했을때의 수익률의 차이도 계산해봅니다.
MDD가 낮은 것도 있지만 직접 해보니 확실히 떨어지기만 하는 날에는 매수를 진행하지 않으니 마음이 편한 느낌이 들더라고요!
조코딩 멤버십 가입(멤버십 전용 강의 월별 무제한 수강)
▶https://www.youtube.com/channel/UCQNE2JmbasNYbjGAcuBiRRg/join
조코딩 채널 강사 지원
▶https://forms.gle/LsbgU8xFL9gtzRSt6
디스코드 조코딩의 코딩 커뮤니티
▶https://discord.gg/zny87VeSaX
본 강의는 파이썬의 기본은 어느 정도 아신다는 전제하에 진행할 예정이니 파이썬을 전혀 모르시는 분들은 아래 재생목록으로 먼저 간단히 학습해보시는 것을 추천드립니다. 물론 파이썬을 잘 몰라도 따라오실 수 있도록 쉽게 풀어볼게요! 기본적인 투자 용어를 잘 모르시는 분들은 이전에 진행하였던 파이썬 주식 투자 자동화 강의도 참고하시면 도움이 되실 겁니다.
파이썬 기초 강의 재생목록 :
https://www.youtube.com/playlist?list=PLU9-uwewPMe2AX9o9hFgv-nRvOcBdzvP5
파이썬 비트코인 투자 자동화 강의 재생목록 :
https://youtube.com/playlist?list=PLU9-uwewPMe3KKFMiIm41D5Nzx_fx2PUJ
파이썬 주식 투자 자동화 강의 재생목록 :
https://www.youtube.com/playlist?list=PLU9-uwewPMe0fB60VIMuKFV7gPDXmyOzp
앞으로 강의 많은 기대 부탁드립니다! 🙂
#파이썬 #비트코인 #자동매매
목차
00:00 미리보기
00:05 인트로
00:24 강의 목차 소개
00:37 주식 투자와 비트코인 투자의 차이
00:53 시장 운영 시간의 차이
01:06 증권사와 거래소 차이
01:12 API의 차이
01:40 비트코인 투자 자동화 소개
01:45 거래소 수수료 비교
02:04 비트코인 투자 자동화의 큰 그림
02:32 투자 전략 소개 – 변동성 돌파 전략
03:28 백테스팅 결과 \u0026 실제 투자 후기 공유
03:37 최근 200일간 비트코인 가격 변화
04:05 가만히 들고있지 못하는 이유 – MDD
04:52 변동성 돌파 전략을 사용하면 하락을 피하는 이유
05:42 200일 백테스팅 결과
06:08 불안한 존버 vs 안정적인 자동투자
07:07 실제 투자 후기
08:00 경고
08:18 참고 문헌
08:33 구독, 좋아요, 알림설정 부탁드립니다
08:44 아웃트로
—
참고 문헌
위키 북스 : https://wikidocs.net/book/1665
파이썬을 이용한 비트코인 자동매매 : https://coupa.ng/bU54lR
(위 링크는 쿠팡 파트너스 링크로 일정액의 수수료를 지급 받을 수 있습니다.)
업 비트 프로그램 주제에 대한 자세한 내용은 여기를 참조하세요.
[Toy Project] 자동매매 프로그램 만들기 – 1. 업비트 API 사용 신청
암호화폐 자동매매 프로그램을 만들기 위해서는 기본적으로 거래소 … 업비트 메인 홈페이지의 하단 부분을 보면 “Open API” 메뉴를 확인할 수 있다.
Source: rebro.kr
Date Published: 12/13/2021
View: 5697
업비트 자동매매 무료 – Microsoft Apps
자고 일어났더니 보유종목이 급락했거나, 오르는 종목을 놓칠까봐 자는 시간에도 코인 시세만 들여다 본 적 있나요? 더 이상 코인 거래 하면서 밤잠 설치지 마세요.
Source: www.microsoft.com
Date Published: 12/14/2022
View: 8925
코인 가상화폐 자동매매 프로그램 드립니다. – 크몽
63개 총 작업 개수 완료한 총 평점 5점인 dyoSoft의 IT·프로그래밍, 프로그램, … 자동매매 #코인 자동매매 #비트코인 매매 #암호화폐 자동매매 #비트코인 자동매매 …
Source: kmong.com
Date Published: 10/3/2021
View: 7758
08-1 업비트(Upbit) – 파이썬을 이용한 비트코인 자동매매 (개정판)
업비트는 두나무 주식회사에서 운영 중인 가상화폐 거래소입니다. 카카오 계정만 있으면 쉽게 가입할 수 있고 UI가 편리해서 많은 사람이 사용하고 있습니다.
Source: wikidocs.net
Date Published: 3/27/2022
View: 2792
비트코인 자동 매수 프로그램(수정본) – Technology & Finance
이전에 업비트 API 및 파이썬을 이용해서 보조지표를 활용하여 원하는 조건에 맞을 시 코인을 자동으로 매수하는 프로그램을 작성해 보았는데요.
Source: technfin.tistory.com
Date Published: 12/3/2021
View: 1223
upbit_bot 가상화폐 변동성 돌파 전략 프로그램 (업비트용)
비트코인, 알트코인 자동 매매 프로그램 (업비트용), 변동성 돌파 전략을 이용한 가상화폐, 업비트 자동 매매 프로그램 – GitHub – hyeon9698/upbit_bot: 비트코인, …
Source: github.com
Date Published: 1/17/2022
View: 4056
주제와 관련된 이미지 업 비트 프로그램
주제와 관련된 더 많은 사진을 참조하십시오 누구나 할 수 있는 비트코인 투자 자동화 강의 시작합니다. 댓글에서 더 많은 관련 이미지를 보거나 필요한 경우 더 많은 관련 기사를 볼 수 있습니다.
주제에 대한 기사 평가 업 비트 프로그램
- Author: 조코딩 JoCoding
- Views: 조회수 226,867회
- Likes: 좋아요 3,127개
- Date Published: 2021. 4. 3.
- Video Url link: https://www.youtube.com/watch?v=WgXOFtDD6XU
[Toy Project] 자동매매 프로그램 만들기 – 1. 업비트 API 사용 신청
728×90
반응형
암호화폐 자동매매 프로그램을 만들기 위해서는 기본적으로 거래소에서 제공하는 암호화폐에 대한 정보가 필요하다. 대부분의 큰 거래소들은 Open API를 제공하므로, 그중 국내에서 가장 거래량이 많은 업비트(Upbit) 거래소의 API를 사용하려고 한다.
먼저, 업비트 사이트의 회원가입 과정은 생략하겠다.
업비트 메인 홈페이지의 하단 부분을 보면 “Open API” 메뉴를 확인할 수 있다. 이를 클릭하자.
Open API 사용하기를 누르면 Open API 이용 약관이 나온다. 여러 주의사항이 있는데, 대충 중요한 내용만 보면 다음과 같다.
– 잔고 조회, 주문 기능(주문 조회, 주문하기), 출금 기능(출금 조회, 출금하기)을 사용할 수 있다.
– 서비스의 장애 또는 중단으로 발생한 손해는 책임지지 않는다.
– 서비스를 불법적으로 악용하지 마라.
– 업비트 개발자센터 공지 게시판에 API, 제도, 정책 등에 대한 공지가 제공되므로, 변경이 있는 경우 사용자가 직접 반영해야 한다.
Open API Key 발급받기를 누르면 Open API를 통해 이용할 기능을 선택할 수 있다.
Open API Key가 유출되는 경우를 방지하기 위해서 입/출금 기능은 제외하고 가지고 있는 돈으로 주문만 할 수 있도록 ‘자산조회’, ‘주문조회’, ‘주문하기’ 세 기능을 선택하였다.
만약 출금 기능을 사용하고 싶다면 우측의 디지털 자산 출금주소도 등록을 해야 한다.
2020년 3월 12일부터 주문하기나 출금하기 기능을 사용하기 위해서는 반드시 사용할 IP주소를 등록해야 하며, 해당 IP주소로만 Open API를 사용할 수 있다.
IP주소를 입력한 후, Open API Key를 발급받으면 다음과 같이 Key가 발급된다.
Secret Key는 다시 확인할 수 없으니 반드시 따로 저장을 해두어야 한다. 만약 분실하는 경우 Open API Key를 새로 발급받아야 한다.
이제 Open API Key를 발급받았으니, 자유롭게 API를 이용할 수 있다.
업비트 개발자 센터에 들어가면 업비트에서 제공하는 API Reference가 있으며, 각 기능별로 Node, Python, Ruby, Java 총 4가지 예시 코드와 함께 잘 설명되어 있으므로 참고하면 많이 도움이 될 것이다.
728×90
반응형
코인 가상화폐 자동매매 프로그램 드립니다.
서비스 설명
————————————————————————————–
“시스템 트레이딩”은 과연 단순히 개발만 하면 끝이라고 생각하십니까?
2017년 스캘핑 원금대비 110배 수익(김프 50%에 매도),
2021년 시스템 트레이딩 원금대비 41배 수익
개발도 할 줄 알고, 트레이딩도 할 줄 아는 사람의 봇
매수 금액만 설정해 주시면 되도록 만들어 드립니다.
————————————————————————————-
[ 기본적으로 구현되는 부분 ]1) 거래소 내의 모든 원화코인을 13초 간격으로 탐색 하며 매매 진행
또한, 실시간으로 어떤 코인이 오르는지, 등락폭은 어떤지 현재 이동평균선, 각종 지표 대비 위치는 어딘지
모니터링을 통해 확인 가능합니다.
2) 자동매수, 자동매도, 자동손절 가능하며, 시장 상황에 따라 매매 확률을 높일 수 있는 몇가지의
옵션이 제공됩니다. 기본적으로 설정된 매수 타점은 보통 개미들의 패닉셀을 받아먹는 타점입니다.
3) 메인알트, 전체코인, 사용자 지정코인만 거래 기능을 통해 특정 코인 필터링이 가능합니다.
(필터링이 되어도 모니터링 화면에는 모든 데이터가 출력됩니다.)
4) 매뉴얼 모드를 통해 수동으로 단타를 칠 수 있습니다.
(설정값 지정 후 키보드를 통해 빠르게 매수, 매도 가능)
5) 피봇 데이터를 통해 특정 코인의 지지선, 저항선을 검색 할 수 있습니다.
6) 구간 스캘핑 ( 지정된 자리 매수, 매도 반복) 이 서비스 기능으로 제공됩니다. )
7) 오토 스캘핑 ( 사용자 설정값을 기준으로 과매도 범위 내에 속한 코인에 대해 자동스캘핑이 가능합니다.)
8) 급락에 대한 대비 전략( 개발자이자 사용자로써 많은 하락장을 겪어보며 개발한 하락장 대비 전략)
9) 리스크 관리에 대한 매도 방식
( 매도 타겟을 1.8%로 두었을때 1.72%까지만 상승하고 떨어지는 경우 지켜만 봐야할까요?
매수세가 강한 상승을 하는중인데, 1.8%에 딱 매도해야할까요? )
10) 다양한 옵션과 확률 높은 타점 제공
11) 간단한 조작 방식으로 누구나 쉽게 이용가능한 사용이 가능하며, 사용자는 특별한 컨트롤 없이
24시간 자동화 가능
12) 장기성 타점과, 단기성 타점을 구분하여 유동적인 익절( 알고리즘을 통하여 단기 조정, 추세전환 구분)
08-1 업비트(Upbit)
업비트는 두나무 주식회사에서 운영 중인 가상화폐 거래소입니다. 카카오 계정만 있으면 쉽게 가입할 수 있고 UI가 편리해서 많은 사람이 사용하고 있습니다. 회원 가입 방법에 대해서는 본문에서 다루지 않으니 공식 홈페이지의 가이드 문서를 참고하시기 바랍니다.
API 사용신청
API를 사용하면 계좌에 접근해서 매수/매도/조회 등을 할 수 있기 때문에 로그인 이외에도 추가적인 보안 체크를 요구합니다. 업비트에 회원가입 후 로그인을 진행하면 그림 8-1과 같이 인증번호를 요구하는 창이 팝업됩니다. 회원 가입 시 입력한 전화번호로 인증 번호가 전송되며, 이를 입력해야 이차 보안이 정상적으로 수행됩니다.
그림 8-1 로그인 추가 인증 과정
업비트 홈페이지의 오른쪽 아래 있는 ‘Open API’ 메뉴로 들어가면 그림 8-2의 API 설명을 확인할 수 있습니다. 혹은 웹브라우저에 API 페이지의 주소(https://upbit.com/service_center/open_api_guide)를 직접 입력해도 됩니다. ‘Open APi 사용하기’ 버튼을 눌러 다음 과정을 진행합니다.
그림 8-2 Open API 안내
그림 8-3에서는 업비트가 제공하는 서비스 정의와 면책사항, 사용자의 의무와 책임 등의 정보가 포함된 이용약관 페이지를 볼 수 있습니다. 주의 깊게 읽어보고 이상이 없다면 약관 동의에 체크한 후 ‘Open API Key 발급받기’ 버튼을 클릭하세요.
그림 8-3 API 발급
그림 8-4의 관리 페이지에서는 Open API로 수행할 작업을 선택합니다. 계좌 조회, 주문 조회, 주문하기를 선택하고 ‘Open API Key 발급하기’ 버튼을 클릭합니다. Open API Key가 유출됐을 때를 고려해서 입/출금 기능은 선택하지 않았습니다.
그림 8-4 Open API Key 관리
업비트에서 Open API Key를 발급받을 때 개인 계정의 보안 상태에 따라 ‘카카오페이 간편 인증’ 등의 추가 과정이 필요할 수도 있습니다. 업비트의 안내에 따라 추가 인증을 수행해 주세요. 참고로 레벨 3의 ‘입출금 계좌인증’을 하지 않고도 카카오페이 인증을 수행하면 레벨 4 인증을 완료할 수 있습니다. 보안 인증을 정상적으로 끝마쳤다면 그림 8-5와 같이 Access Key와 Secret Key를 얻을 수 있습니다.
그림 8-5 Access Key와 Secret Key
현재 진행하고 있는 API Key발급 과정이 끝나면 Secret Key는 다시는 홈페이지에서 확인할 수 없습니다. 따라서 Key 값이 포함된 화면을 캡처하거나 엑셀, 메모장 등에 저장해 두세요. Key가 유출되면 타인이 내 계좌의 가상화폐를 매수/매도할 수 있기 때문에 항상 보안에 유의해야 합니다.
pyupbit 설치하기
빗썸의 API처럼 업비트의 API도 개발자 가이드(https://docs.upbit.com/docs)를 참고해서 여러분이 직접 API 호출 기능을 구현할 수 있습니다. 하지만 기존에 개발된 모듈을 사용하는 것이 개발 속도도 높일 수 있고 편하겠죠? 업비트 API를 파이썬에서 쉽게 사용하기 위해 저자들이 개발한 pyupbit모듈을 설치해 보겠습니다. 아나콘다 명령 프롬프트를 실행한 후 (시작 → Anaconda3 → Anaconda Prompt 메뉴 클릭) 다음과 같이 입력합니다.
pip install pyupbit
기존에 pyupbit 모듈을 설치한 경우에는 다음과 같이 “-U” 옵션을 사용해서 최신 버전으로 업데이트합니다.
pip install -U pyupbit
pyupbit 모듈이 잘 설치됐는지 확인하기 위해 모듈을 import 해봅시다. 다음 코드가 에러 없이 실행된다면 정상적으로 모듈이 설치된 겁니다.
import pyupbit print(pyupbit.Upbit)
티커 조회
거래소에서 가상화폐를 사고팔기 위해서는 가상화폐들의 티커가 필요합니다. 같은 가상화폐라도 거래소마다 티커가 다를 수 있기 때문에 거래소별로 티커를 얻어와야 합니다. pyupbit 모듈의 get_tickers() 함수는 업비트에서 사용할 수 있는 티커를 얻어옵니다.
# ch08/08_01.py 1: import pyupbit 2: 3: tickers = pyupbit.get_tickers() 4: print(tickers)
get_tickers() 함수는 업비트에서 거래되는 모든 가상화폐의 문자열 티커를 리스트로 반환합니다. 다음은 티커 리스트를 출력한 결과입니다. 지면 관계상 일부만 표기했습니다. 빗썸과 비교하면 가상화폐 이름 앞에 KRW, BTC 등의 접두사가 붙어있는 것을 확인할 수 있습니다.
[‘KRW-BTC’, ‘KRW-DASH’, ‘KRW-ETH’, ‘BTC-NEO’, ‘BTC-ETH’, ‘BTC-LTC’]업비트는 그림 8-6과 같이 원화 시장뿐만 아니라 해외 거래소와 제휴를 통해 BTC, ETH, USDT 시장을 지원합니다. 해외 거래소의 거래량을 확보함으로써 가상화폐의 유동성과 안정성을 제공하는 겁니다. 원화 시장과 BTC 시장의 차이를 예로 살펴보겠습니다. 원화 시장에서는 1000원으로 비트코인 0.0002개를 사고, 팔아서 다시 1000원을 되돌려 받을 수 있습니다. BTC 시장에서는 0.0002개의 비트코인으로 2.5개의 리플을 사고, 다시 리플을 팔아 0.0002개의 비트코인을 받게 됩니다. 각각 시장의 이름은 거래하는 기준 통화를 가리키는 겁니다.
그림 8-6 업비트 거래 시장 종류
pyupbit 모듈은 시장별로 티커를 얻어올 수도 있습니다. 하나의 예로 원화 시장에서 주문 가능한 티커 목록을 얻어와 보겠습니다.
tickers = pyupbit.get_tickers(fiat=”KRW”) print(tickers)
get_tickers() 함수에 파라미터를 전달하지 않으면 모든 시장의 티커를 반환했습니다. 하지만 기준 통화(fiat) 값을 입력하면 특정 시장의 티커만을 가져옵니다. fiat 파라미터에는 KRW/BTC/ETH/USDT를 사용할 수 있습니다.
현재가 조회
가상화폐의 현재가는 get_current_price() 함수 통해 얻어올 수 있습니다. 함수의 입력으로 가상화폐의 티커를 넣어주면 됩니다. 예를 들어 원화 시장에서 비트코인은 “KRW-BTC”, 리플은 “KRW-XRP”를 사용합니다.
# ch08/08_02.py 1: import pyupbit 2: 3: price = pyupbit.get_current_price(“KRW-XRP”) 4: print(price)
API를 호출한 시점에서 원화 시장의 리플 현재가는 464원임을 알 수 있습니다. KRW 시장에서 조회했기 때문에 단위는 ‘원’입니다.
464.0
이번에는 BTC 시장에서의 리플 현재가를 조회해 보겠습니다. KRW 접두사 대신 BTC를 사용하면 되겠죠?
price = pyupbit.get_current_price(“BTC-XRP”) print(price)
리플의 가격이 KRW 시장에서는 464원, BTC 시장에서는 소수점 8자리의 값이 반환됐습니다. 반환되는 값의 단위가 중요한데 BTC 시장에서는 단위가 원이 아니라 ‘BTC’입니다. 리플 하나의 가격이 비트코인 0.00010113 개와 같다는 의미입니다. 조회하는 시점에서의 비트코인 가격이 4,576,000원이니 원화로 환산해보면 약 462원으로 비슷한 가격대를 형성한 것을 알 수 있습니다.
0.00010113
여러 가상화폐의 현재가를 한 번에 조회할 때는 다음과 같이 티커를 리스트로 넘겨주면 됩니다.
price = pyupbit.get_current_price([“BTC-XRP”, “KRW-XRP”]) print(price)
여러 가상화폐의 현재가를 한 번에 조회했기 때문에 결괏값이 딕셔너리로 리턴됩니다. 티커를 key로 해서 현재가가 value로 저장된 것을 확인할 수 있습니다
{‘BTC-XRP’: 0.00010113, ‘KRW-XRP’: 462.0}
과거 데이터 조회
시가(open), 고가(high), 저가(low), 종가(close), 거래량(volume)은 기술적 분석에 사용되는 대표적인 지표입니다. 다음 코드는 원화 시장에서 비트코인의 OHLCV를 출력합니다. 함수의 인자로 조회하려는 가상화폐의 티커를 넣어주면 됩니다.
# ch08/08_03.py 1: import pyupbit 2: 3: df = pyupbit.get_ohlcv(“KRW-BTC”) 4: print(df)
get_ohlcv() 함수는 가상화폐의 티커를 입력받아 OHLCV 데이터를 판다스 DataFrame으로 반환합니다. 다음은 DataFrame에 바인딩 된 값을 출력한 결과입니다. 오전 09:00:00를 기준으로 일일 단위의 가격정보가 저장되는 것을 알 수 있습니다. 총 120개의 값이 조회되지만, 일부 값만 표시했습니다.
2018-12-19 4092000.0 4400000.0 … 4124000.0 12333.943290 2018-12-20 4127000.0 4637000.0 … 4552000.0 16481.712455 2018-12-21 4552000.0 4733000.0 … 4333000.0 16822.181918 2018-12-22 4333000.0 4494000.0 … 4464000.0 7007.242166 2018-12-23 4465000.0 4549000.0 … 4526000.0 2578.761740
get_ohlcv() 함수의 interval 옵션으로 월/주/일/분봉 중 하나를 선택할 수 있습니다. interval을 입력하지 않은 경우에는 내부적으로 일봉이 선택됩니다. 다음 코드는 원화 시장에서 비트코인의 분봉을 가져옵니다.
df = pyupbit.get_ohlcv(“KRW-BTC”, interval=”minute1″) print(df)
count 옵션은 가져오려는 데이터의 개수를 지정하는 데 사용합니다. 만약 최근 5일간의 데이터만 조회하고자 하는 경우 다음과 같이 코딩합니다.
df = pyupbit.get_ohlcv(“KRW-BTC”, count=5) print(df)
호가 조회
매수호가(bid)와 매도호가(ask) 조회는 get_orderbook() 함수를 사용하면 됩니다. get_orderbook() 함수를 통해 여러분은 10호가 데이터를 얻을 수 있습니다.
# ch08/08_04.py 1: import pyupbit 2: 3: orderbook = pyupbit.get_orderbook(“KRW-BTC”) 4: print(orderbook)
get_orderbook() 함수의 리턴되는 값은 리스트 객체인데 리스트 안에 딕셔너리 객체가 있습니다. 10호가 정보는 다음과 같이 얻을 수 있습니다.
# ch08/08_05.py 1: import pyupbit 2: 3: orderbook = pyupbit.get_orderbook(“KRW-BTC”) 4: bids_asks = orderbook[0][‘orderbook_units’] 5: 6: for bid_ask in bids_asks: 7: print(bid_ask)
라인 4: 리스트 객체의 0번은 딕셔너리 객체이므로 다시 ‘orderbook_units’라는 key 값을 사용해서 10호가 정보가 담긴 리스트 객체를 얻어옵니다. 10호가 정보가 담긴 리스트 객체를 bids_asks라는 변수가 바인딩합니다. 라인 6: 10호가 리스트에서 하나씩 순회하면서 데이터를 bid_ask라는 변수가 바인딩합니다. 라인 7: bid_ask 변수가 바인딩하는 값을 출력합니다.
출력값을 살펴보면 리스트 안의 10 호가는 각각이 파이썬 딕셔너리 객체임을 알 수 있습니다. 리스트는 순서가 있는 자료구조였지요? 리스트의 앞에서부터 1호가, 2호가, 3 호가의 데이터입니다. ask_price는 매도 호가이고 bid_price는 매수 호가입니다. ask_size와 bid_size는 매도 호가 수량과 매수 호가 수량입니다.
{‘ask_price’: 4527000.0, ‘bid_price’: 4526000.0, ‘ask_size’: 0.16874869, ‘bid_size’: 0.7792947} {‘ask_price’: 4529000.0, ‘bid_price’: 4525000.0, ‘ask_size’: 0.39102868, ‘bid_size’: 7.31493821} {‘ask_price’: 4530000.0, ‘bid_price’: 4524000.0, ‘ask_size’: 1.0003, ‘bid_size’: 0.40184096} {‘ask_price’: 4531000.0, ‘bid_price’: 4523000.0, ‘ask_size’: 0.051, ‘bid_size’: 0.06310365} … 생략 …
잔고 조회
이번에는 잔고 조회를 해보겠습니다. 잔고 조회와 매매 API는 기존과 조금 다른 방식으로 API를 사용합니다. 먼저 앞서 API 사용 신청 시에 발급받았던 access key와 secret key 값을 사용해서 Upbit 클래스의 인스턴스를 생성해야 합니다. 앞서 붕어빵 틀 클래스를 사용해서 붕어빵 객체를 만들었던 것이 생각나지요? Upbit 클래스의 인스턴스를 생성했다면 해당 인스턴스를 통해 Upbit 클래스에 정의된 메서드를 호출할 수 있습니다. 그중 잔고 조회는 get_balances()입니다. access_key와 secret_key는 8.1.1 API 사용신청에서 발급받은 값으로 수정해야 코드가 정상 동작합니다.
# ch08/08_06.py 1: import pyupbit 2: 3: access_key = “t88RbbxB8NHNyqBUegeVqowGQOGEefeee3W2dGNU” 4: secret_key = “VCLoAhrxbvyrukYChbxfxD6O1ESegeckIgbqeiQf” 5: 6: upbit = pyupbit.Upbit(access_key, secret_key) 7: print(upbit.get_balances())
잔고 조회 메서드의 리턴 값은 튜플입니다. 튜플 객체의 0번에는 잔고 데이터 (파이썬 리스트 객체)가 1번에는 호출 제한 데이터 (파이썬 딕셔너리 객체)가 있습니다. 잔고 데이터를 살펴보면 원화로 약 100만 원이 있는 것을 확인할 수 있습니다. 튜플의 1번에 있는 호출 제한 데이터는 업비트 API를 호출할 때 초당/분당 호출이 가능한 요청 수를 의미합니다. 예를 들어, 아래의 값은 ‘default’ 그룹에 대해서 1분간 1799개, 1초에 29개의 API 호출이 가능함을 의미합니다. 참고로 API마다 그룹이 있는데 그룹 단위로 호출 제한을 판단하므로 과도한 호출을 하는 경우에는 초당/분당 호출 가능 수를 확인하는 것이 필요합니다.
([{‘currency’: ‘KRW’, ‘balance’: ‘999106.81706142’, ‘locked’: ‘0.0’, ‘avg_krw_buy_price’: ‘0’, ‘modified’: False}], {‘group’: ‘default’, ‘min’: 1799, ‘sec’: 29})
매수/매도
이번에는 업비트 주문 API에 대해 알아봅시다. 주문 API 역시 Upbit 클래스의 인스턴스를 통해 호출할 수 있습니다. 지정가 매수를 위해서는 buy_limit_order() 메서드를 사용하면 되고 지정가 매도를 위해서는 sell_limit_order() 메서드를 사용하면 됩니다. 먼저 지정가 매수부터 테스트해봅시다. 실제로 매수가 되는 것을 막기 위해서 현재 리플의 시세보다 아주 낮은 금액으로 매수 주문을 넣어보겠습니다.
# ch08/08_07.py 1: import pyupbit 2: 3: access_key = “t88RbbxB8NHNyqBUegeVqowGQOGEefeee3W2dGNU” 4: secret_key = “VCLoAhrxbvyrukYChbxfxD6O1ESegeckIgbqeiQf” 5: 6: upbit = pyupbit.Upbit(access_key, secret_key) 7: ret = upbit.buy_limit_order(“KRW-XRP”, 100, 20) 8: print(ret)
리턴 값은 튜플 객체인데 0번에는 주문 정보가 있고 1번에는 호출 제한 데이터가 있습니다. 주문에서 중요한 것은 여러분이 주문 API를 호출하면 해당 주문 건에 대한 ‘uuid’ 값이 리턴되는데 이 값을 통해 미체결된 주문을 취소할 수 있다는 점입니다.
({‘uuid’: ‘cc52be46-1000-4126-aee7-9bfafb867682’, ‘side’: ‘bid’, ‘ord_type’: ‘limit’, ‘price’: ‘100.0’, ‘state’: ‘wait’, ‘market’: ‘KRW-BTC’, ‘created_at’: ‘2018-08-26T20:21:30+09:00’, ‘volume’: ‘20.0’, ‘remaining_volume’: ‘20.0’, ‘reserved_fee’: ‘1.0’, ‘remaining_fee’: ‘1.0’, ‘paid_fee’: ‘0.0’, ‘locked’: ‘2001.0’, ‘executed_volume’: ‘0.0’, ‘trades_count’: 0}, {‘group’: ‘order’, ‘min’: 199, ‘sec’: 8})
buy_limit_order() 함수가 정상적으로 동작했는지 확인하는 가장 쉬운 방법은 그림 8-7과 같이 업비트 웹 페이지에서 투자내역 → 미체결 내역을 확인하는 겁니다. 리플에 대해 주문 가격 100원으로 20개가 정상적으로 주문된 것을 확인할 수 있습니다.
그림 8-7 매수 주문 미체결 내역
이번에는 지정가 매도 API 사용에 대해 알아봅시다. 지정가 매도는 sell_limit_order() 메서드를 사용하면 됩니다. 메서드의 인자는 매수와 동일합니다. 티커, 매도 주문 가격, 매도 수량순으로 입력해주면 됩니다. 매도 API를 테스트하는 경우 실제로 매도가 체결되는 것을 막기 위해 현재 시세보다 높은 금액으로 매도 주문을 넣어줍시다.
# ch08/08_08.py 1: import pyupbit 2: 3: access_key = “t88RbbxB8NHNyqBUegeVqowGQOGEefeee3W2dGNU” 4: secret_key = “VCLoAhrxbvyrukYChbxfxD6O1ESegeckIgbqeiQf” 5: 6: upbit = pyupbit.Upbit(access_key, secret_key) 7: ret = upbit.sell_limit_order(“KRW-XRP”, 1000, 20) 8: print(ret)
sell_limit_order() 메서드 역시 튜플 객체를 리턴해주는데 0번에는 주문 정보가 1번에는 호출 제한 데이터가 있습니다.
({‘uuid’: ‘1ab8ac28-e880-4a04-b868-a82d755b0945’, ‘side’: ‘ask’, ‘ord_type’: ‘limit’, ‘price’: ‘1000.0’, ‘avg_price’: ‘0.0’, ‘state’: ‘wait’, ‘market’: ‘KRW-XRP’, ‘created_at’: ‘2018-07-21T05:38:48+09:00’, ‘volume’: ‘20.0’, ‘remaining_volume’: ‘20.0’, ‘reserved_fee’: ‘0.0’, ‘remaining_fee’: ‘0.0’, ‘paid_fee’: ‘0.0’, ‘locked’: ‘20.0’, ‘executed_volume’: ‘0.0’, ‘trades_count’: 0}, {‘group’: ‘order’, ‘min’: 79, ‘sec’: 6})
만일 여러분이 보유하고 있지 않은 가상화폐에 대해 매도 주문을 넣는 경우 다음과 같이 에러 메시지가 저장된 튜플 객체가 리턴됩니다.
({‘error’: {‘message’: ‘주문가능한 금액(XRP)이 부족합니다.’, ‘name’: ‘insufficient_funds_ask’}}, {‘group’: ‘order’, ‘min’: 199, ‘sec’: 8})
주문 취소
이번에는 주문 취소에 대해 알아봅시다. 앞서 주문 메서드를 호출하면 uuid 값이 리턴됨을 확인했습니다. 이 값을 갖고 있다가 cancel_order() 메서드의 인자로 사용하면 해당 uuid를 갖는 주문 취소할 수 있습니다.
# ch08/08_09.py 1: import pyupbit 2: 3: access_key = “t88RbbxB8NHNyqBUegeVqowGQOGEefeee3W2dGNU” 4: secret_key = “VCLoAhrxbvyrukYChbxfxD6O1ESegeckIgbqeiQf” 5: 6: upbit = pyupbit.Upbit(access_key, secret_key) 7: ret = upbit.cancel_order(‘cc52be46-1000-4126-aee7-9bfafb867682’) 8: print(ret)
cancel_order() 메서드 역시 튜플 객체를 리턴하는데 0번은 주문 취소에 대한 정보이고 1번은 호출 제한 데이터입니다.
비트코인 자동 매수 프로그램(수정본) – 파이썬 업비트 비트코인 자동매매
반응형
이전에 업비트 API 및 파이썬을 이용해서 보조지표를 활용하여 원하는 조건에 맞을 시 코인을 자동으로 매수하는 프로그램을 작성해 보았는데요.
① 보조지표를 가져오는 로직에서 속도가 저하되는 문제와 ② 중복 매수를 방지하기 위해 매수 후 매수 제외 종목으로 등록하는 부분이 추후 매도를 하게 되더라도 다시 매수되지 않는 문제에 대해서 몇몇 분들께서 댓글을 달아 주셨습니다.
또한 프로젝트를 진행하면서 공통 모듈이 여러 번 수정되어 과거 포스팅의 로직이 정상적으로 작동하지 않는 분들도 계시는 것 같아 이번에 자동 매수 프로그램을 수정하면서 전체 코드를 다시 정리해서 업로드 하려고 합니다.
목차 – 클릭하면 이동합니다.
매수 타이밍
보조 지표를 활용한 매수 타이밍 판단 로직
기본적인 매수 타이밍을 판단하는 로직은 지난 번과 동일합니다. 자세한 매수 타이밍을 결정하는 로직에 대한 부분은 지난 포스팅을 참고 하시면 됩니다.
2021.10.21 – [프로젝트/비트코인 자동매매] – 보조지표를 활용한 코인 자동매수 프로그램 – 파이썬 업비트 비트코인 자동매매
① RSI : 2일전 < 30미만, 3일전 > 2일전, 1일전 > 2일전, 현재 > 1일전
② MFI : 2일전 < 20미만, 3일전 > 2일전, 1일전 > 2일전, 현재 > 1일전
③ MACD(OCL) : 3일전 < 0, 2일전 < 0, 1일전 < 0, 3일전 > 2일전, 1일전 > 2일전, 현재 > 1일전
위의 포스팅에서 말씀드린 바와 같이 위의 조건을 만족하는 경우 매수를 하게 되는데 일 캔들 기준으로 하기 때문에 매트코인이 폭락 후가 아니라면 왠만해서는 조건을 만족하는 경우가 발생하지 않습니다.
다른 말로는 상당히 보수적인 로직이라는 말이 되는데요. 꼭 이 로직을 고수하실 필요는 없습니다. 원하시는 로직으로 변경하여 사용하시면 됩니다.
공통 모듈
최신 공통 모듈 – upbit.py
import time import logging import requests import jwt import uuid import hashlib import math import os import pandas as pd import numpy from urllib.parse import urlencode from decimal import Decimal from datetime import datetime # Keys access_key = ‘업비트에서 발급받은 Access Key’ secret_key = ‘업비트에서 발급받은 Secret Key’ server_url = ‘https://api.upbit.com’ line_target_url = ‘https://notify-api.line.me/api/notify’ line_token = ‘라인 메신저에서 발급받은 Token’ # 상수 설정 min_order_amt = 5000 # —————————————————————————– # – Name : set_loglevel # – Desc : 로그레벨 설정 # – Input # 1) level : 로그레벨 # 1. D(d) : DEBUG # 2. E(e) : ERROR # 3. 그외(기본) : INFO # – Output # —————————————————————————– def set_loglevel(level): try: # ——————————————————————— # 로그레벨 : DEBUG # ——————————————————————— if level.upper() == “D”: logging.basicConfig( format='[%(asctime)s][%(levelname)s][%(filename)s:%(lineno)d]:%(message)s’, datefmt=’%Y/%m/%d %I:%M:%S %p’, level=logging.DEBUG ) # ——————————————————————— # 로그레벨 : ERROR # ——————————————————————— elif level.upper() == “E”: logging.basicConfig( format='[%(asctime)s][%(levelname)s][%(filename)s:%(lineno)d]:%(message)s’, datefmt=’%Y/%m/%d %I:%M:%S %p’, level=logging.ERROR ) # ——————————————————————— # 로그레벨 : INFO # ——————————————————————— else: # —————————————————————————– # 로깅 설정 # 로그레벨(DEBUG, INFO, WARNING, ERROR, CRITICAL) # —————————————————————————– logging.basicConfig( format='[%(asctime)s][%(levelname)s][%(filename)s:%(lineno)d]:%(message)s’, datefmt=’%Y/%m/%d %I:%M:%S %p’, level=logging.INFO ) # —————————————- # Exception Raise # —————————————- except Exception: raise # —————————————————————————– # – Name : send_request # – Desc : 리퀘스트 처리 # – Input # 1) reqType : 요청 타입 # 2) reqUrl : 요청 URL # 3) reqParam : 요청 파라메타 # 4) reqHeader : 요청 헤더 # – Output # 4) reponse : 응답 데이터 # —————————————————————————– def send_request(reqType, reqUrl, reqParam, reqHeader): try: # 요청 가능회수 확보를 위해 기다리는 시간(초) err_sleep_time = 0.3 # 요청에 대한 응답을 받을 때까지 반복 수행 while True: # 요청 처리 response = requests.request(reqType, reqUrl, params=reqParam, headers=reqHeader) # 요청 가능회수 추출 if ‘Remaining-Req’ in response.headers: hearder_info = response.headers[‘Remaining-Req’] start_idx = hearder_info.find(“sec=”) end_idx = len(hearder_info) remain_sec = hearder_info[int(start_idx):int(end_idx)].replace(‘sec=’, ”) else: logging.error(“헤더 정보 이상”) logging.error(response.headers) break # 요청 가능회수가 3개 미만이면 요청 가능회수 확보를 위해 일정시간 대기 if int(remain_sec) < 3: logging.debug("요청 가능회수 한도 도달! 남은횟수:" + str(remain_sec)) time.sleep(err_sleep_time) # 정상 응답 if response.status_code == 200 or response.status_code == 201: break # 요청 가능회수 초과인 경우 elif response.status_code == 429: logging.error("요청 가능회수 초과!:" + str(response.status_code)) time.sleep(err_sleep_time) # 그 외 오류 else: logging.error("기타 에러:" + str(response.status_code)) logging.error(response.status_code) logging.error(response) break # 요청 가능회수 초과 에러 발생시에는 다시 요청 logging.info("[restRequest] 요청 재처리중...") return response # ---------------------------------------- # Exception Raise # ---------------------------------------- except Exception: raise # ----------------------------------------------------------------------------- # - Name : get_items # - Desc : 전체 종목 리스트 조회 # - Input # 1) market : 대상 마켓(콤마 구분자:KRW,BTC,USDT) # 2) except_item : 제외 종목(콤마 구분자:BTC,ETH) # - Output # 1) 전체 리스트 : 리스트 # ----------------------------------------------------------------------------- def get_items(market, except_item): try: # 조회결과 리턴용 rtn_list = [] # 마켓 데이터 markets = market.split(',') # 제외 데이터 except_items = except_item.split(',') url = "https://api.upbit.com/v1/market/all" querystring = {"isDetails": "false"} response = send_request("GET", url, querystring, "") data = response.json() # 조회 마켓만 추출 for data_for in data: for market_for in markets: if data_for['market'].split('-')[0] == market_for: rtn_list.append(data_for) # 제외 종목 제거 for rtnlist_for in rtn_list[:]: for exceptItemFor in except_items: for marketFor in markets: if rtnlist_for['market'] == marketFor + '-' + exceptItemFor: rtn_list.remove(rtnlist_for) return rtn_list # ---------------------------------------- # Exception Raise # ---------------------------------------- except Exception: raise # ----------------------------------------------------------------------------- # - Name : buycoin_mp # - Desc : 시장가 매수 # - Input # 1) target_item : 대상종목 # 2) buy_amount : 매수금액 # - Output # 1) rtn_data : 매수결과 # ----------------------------------------------------------------------------- def buycoin_mp(target_item, buy_amount): try: query = { 'market': target_item, 'side': 'bid', 'price': buy_amount, 'ord_type': 'price', } query_string = urlencode(query).encode() m = hashlib.sha512() m.update(query_string) query_hash = m.hexdigest() payload = { 'access_key': access_key, 'nonce': str(uuid.uuid4()), 'query_hash': query_hash, 'query_hash_alg': 'SHA512', } jwt_token = jwt.encode(payload, secret_key) authorize_token = 'Bearer {}'.format(jwt_token) headers = {"Authorization": authorize_token} res = send_request("POST", server_url + "/v1/orders", query, headers) rtn_data = res.json() logging.info("") logging.info("----------------------------------------------") logging.info("시장가 매수 요청 완료! 결과:") logging.info(rtn_data) logging.info("----------------------------------------------") return rtn_data # ---------------------------------------- # Exception Raise # ---------------------------------------- except Exception: raise # ----------------------------------------------------------------------------- # - Name : buycoin_tg # - Desc : 지정가 매수 # - Input # 1) target_item : 대상종목 # 2) buy_amount : 매수금액 # 3) buy_price : 매수가격 # - Output # 1) rtn_data : 매수요청결과 # ----------------------------------------------------------------------------- def buycoin_tg(target_item, buy_amount, buy_price): try: # 매수수량 계산 vol = Decimal(str(buy_amount)) / Decimal(str(buy_price)) query = { 'market': target_item, 'side': 'bid', 'volume': vol, 'price': buy_price, 'ord_type': 'limit', } query_string = urlencode(query).encode() m = hashlib.sha512() m.update(query_string) query_hash = m.hexdigest() payload = { 'access_key': access_key, 'nonce': str(uuid.uuid4()), 'query_hash': query_hash, 'query_hash_alg': 'SHA512', } jwt_token = jwt.encode(payload, secret_key) authorize_token = 'Bearer {}'.format(jwt_token) headers = {"Authorization": authorize_token} res = send_request("POST", server_url + "/v1/orders", query, headers) rtn_data = res.json() logging.info("") logging.info("----------------------------------------------") logging.info("지정가 매수요청 완료!") logging.info(rtn_data) logging.info("----------------------------------------------") return rtn_data # ---------------------------------------- # Exception Raise # ---------------------------------------- except Exception: raise # ----------------------------------------------------------------------------- # - Name : sellcoin_mp # - Desc : 시장가 매도 # - Input # 1) target_item : 대상종목 # 2) cancel_yn : 기존 주문 취소 여부 # - Output # 1) rtn_data : 매도결과 # ----------------------------------------------------------------------------- # 시장가 매도 def sellcoin_mp(target_item, cancel_yn): try: if cancel_yn == 'Y': # 기존 주문이 있으면 취소 cancel_order(target_item, "SELL") # 잔고 조회 cur_balance = get_balance(target_item) query = { 'market': target_item, 'side': 'ask', 'volume': cur_balance, 'ord_type': 'market', } query_string = urlencode(query).encode() m = hashlib.sha512() m.update(query_string) query_hash = m.hexdigest() payload = { 'access_key': access_key, 'nonce': str(uuid.uuid4()), 'query_hash': query_hash, 'query_hash_alg': 'SHA512', } jwt_token = jwt.encode(payload, secret_key) authorize_token = 'Bearer {}'.format(jwt_token) headers = {"Authorization": authorize_token} res = send_request("POST", server_url + "/v1/orders", query, headers) rtn_data = res.json() logging.info("") logging.info("----------------------------------------------") logging.info("시장가 매도 요청 완료! 결과:") logging.info(rtn_data) logging.info("----------------------------------------------") return rtn_data # ---------------------------------------- # Exception Raise # ---------------------------------------- except Exception: raise # ----------------------------------------------------------------------------- # - Name : sellcoin_tg # - Desc : 지정가 매도 # - Input # 1) target_item : 대상종목 # 2) sell_price : 매도희망금액 # - Output # 1) rtn_data : 매도결과 # ----------------------------------------------------------------------------- def sellcoin_tg(target_item, sell_price): try: # 잔고 조회 cur_balance = get_balance(target_item) query = { 'market': target_item, 'side': 'ask', 'volume': cur_balance, 'price': sell_price, 'ord_type': 'limit', } query_string = urlencode(query).encode() m = hashlib.sha512() m.update(query_string) query_hash = m.hexdigest() payload = { 'access_key': access_key, 'nonce': str(uuid.uuid4()), 'query_hash': query_hash, 'query_hash_alg': 'SHA512', } jwt_token = jwt.encode(payload, secret_key) authorize_token = 'Bearer {}'.format(jwt_token) headers = {"Authorization": authorize_token} res = send_request("POST", server_url + "/v1/orders", query, headers) rtn_data = res.json() logging.info("") logging.info("----------------------------------------------") logging.info("지정가 매도 설정 완료!") logging.info(rtn_data) logging.info("----------------------------------------------") return rtn_data # ---------------------------------------- # Exception Raise # ---------------------------------------- except Exception: raise # ----------------------------------------------------------------------------- # - Name : get_balance # - Desc : 주문가능 잔고 조회 # - Input # 1) target_item : 대상 종목 # - Output # 2) rtn_balance : 주문가능 잔고 # ----------------------------------------------------------------------------- def get_balance(target_item): try: # 주문가능 잔고 리턴용 rtn_balance = 0 # 최대 재시도 횟수 max_cnt = 0 # 잔고가 조회 될 때까지 반복 while True: # 조회 회수 증가 max_cnt = max_cnt + 1 payload = { 'access_key': access_key, 'nonce': str(uuid.uuid4()), } jwt_token = jwt.encode(payload, secret_key) authorize_token = 'Bearer {}'.format(jwt_token) headers = {"Authorization": authorize_token} res = send_request("GET", server_url + "/v1/accounts", "", headers) my_asset = res.json() # 해당 종목에 대한 잔고 조회 # 잔고는 마켓에 상관없이 전체 잔고가 조회됨 for myasset_for in my_asset: if myasset_for['currency'] == target_item.split('-')[1]: rtn_balance = myasset_for['balance'] # 잔고가 0 이상일때까지 반복 if Decimal(str(rtn_balance)) > Decimal(str(0)): break # 최대 100회 수행 if max_cnt > 100: break logging.info(“[주문가능 잔고 리턴용] 요청 재처리중…”) return rtn_balance # —————————————- # Exception Raise # —————————————- except Exception: raise # —————————————————————————– # – Name : get_candle # – Desc : 캔들 조회 # – Input # 1) target_item : 대상 종목 # 2) tick_kind : 캔들 종류 (1, 3, 5, 10, 15, 30, 60, 240 – 분, D-일, W-주, M-월) # 3) inq_range : 조회 범위 # – Output # 1) 캔들 정보 배열 # —————————————————————————– def get_candle(target_item, tick_kind, inq_range): try: # —————————————- # Tick 별 호출 URL 설정 # —————————————- # 분붕 if tick_kind == “1” or tick_kind == “3” or tick_kind == “5” or tick_kind == “10” or tick_kind == “15” or tick_kind == “30” or tick_kind == “60” or tick_kind == “240”: target_url = “minutes/” + tick_kind # 일봉 elif tick_kind == “D”: target_url = “days” # 주봉 elif tick_kind == “W”: target_url = “weeks” # 월봉 elif tick_kind == “M”: target_url = “months” # 잘못된 입력 else: raise Exception(“잘못된 틱 종류:” + str(tick_kind)) logging.debug(target_url) # —————————————- # Tick 조회 # —————————————- querystring = {“market”: target_item, “count”: inq_range} res = send_request(“GET”, server_url + “/v1/candles/” + target_url, querystring, “”) candle_data = res.json() logging.debug(candle_data) return candle_data # —————————————- # Exception Raise # —————————————- except Exception: raise # —————————————————————————– # – Name : get_targetprice # – Desc : 호가단위 금액 계산 # – Input # 1) cal_type : H:호가로, R:비율로 # 2) st_price : 기준가격 # 3) chg_val : 변화단위 # – Output # 1) rtn_price : 계산된 금액 # —————————————————————————– def get_targetprice(cal_type, st_price, chg_val): try: # 계산된 가격 rtn_price = st_price # 호가단위로 계산 if cal_type.upper() == “H”: for i in range(0, abs(int(chg_val))): hoga_val = get_hoga(rtn_price) if Decimal(str(chg_val)) > 0: rtn_price = Decimal(str(rtn_price)) + Decimal(str(hoga_val)) elif Decimal(str(chg_val)) < 0: rtn_price = Decimal(str(rtn_price)) - Decimal(str(hoga_val)) else: break # 비율로 계산 elif cal_type.upper() == "R": while True: # 호가단위 추출 hoga_val = get_hoga(st_price) if Decimal(str(chg_val)) > 0: rtn_price = Decimal(str(rtn_price)) + Decimal(str(hoga_val)) elif Decimal(str(chg_val)) < 0: rtn_price = Decimal(str(rtn_price)) - Decimal(str(hoga_val)) else: break if Decimal(str(chg_val)) > 0: if Decimal(str(rtn_price)) >= Decimal(str(st_price)) * ( Decimal(str(1)) + (Decimal(str(chg_val))) / Decimal(str(100))): break elif Decimal(str(chg_val)) < 0: if Decimal(str(rtn_price)) <= Decimal(str(st_price)) * ( Decimal(str(1)) + (Decimal(str(chg_val))) / Decimal(str(100))): break return rtn_price # ---------------------------------------- # Exception Raise # ---------------------------------------- except Exception: raise # ----------------------------------------------------------------------------- # - Name : get_hoga # - Desc : 호가 금액 계산 # - Input # 1) cur_price : 현재가격 # - Output # 1) hoga_val : 호가단위 # ----------------------------------------------------------------------------- def get_hoga(cur_price): try: # 호가 단위 if Decimal(str(cur_price)) < 10: hoga_val = 0.01 elif Decimal(str(cur_price)) < 100: hoga_val = 0.1 elif Decimal(str(cur_price)) < 1000: hoga_val = 1 elif Decimal(str(cur_price)) < 10000: hoga_val = 5 elif Decimal(str(cur_price)) < 100000: hoga_val = 10 elif Decimal(str(cur_price)) < 500000: hoga_val = 50 elif Decimal(str(cur_price)) < 1000000: hoga_val = 100 elif Decimal(str(cur_price)) < 2000000: hoga_val = 500 else: hoga_val = 1000 return hoga_val # ---------------------------------------- # Exception Raise # ---------------------------------------- except Exception: raise # ----------------------------------------------------------------------------- # - Name : get_krwbal # - Desc : KRW 잔고 조회 # - Input # - Output # 1) KRW 잔고 Dictionary # 1. krw_balance : KRW 잔고 # 2. fee : 수수료 # 3. available_krw : 매수가능 KRW잔고(수수료를 고려한 금액) # ----------------------------------------------------------------------------- def get_krwbal(): try: # 잔고 리턴용 rtn_balance = {} # 수수료 0.05%(업비트 기준) fee_rate = 0.05 payload = { 'access_key': access_key, 'nonce': str(uuid.uuid4()), } jwt_token = jwt.encode(payload, secret_key) authorize_token = 'Bearer {}'.format(jwt_token) headers = {"Authorization": authorize_token} res = send_request("GET", server_url + "/v1/accounts", "", headers) data = res.json() logging.debug(data) for dataFor in data: if (dataFor['currency']) == "KRW": krw_balance = math.floor(Decimal(str(dataFor['balance']))) # 잔고가 있는 경우만 if Decimal(str(krw_balance)) > Decimal(str(0)): # 수수료 fee = math.ceil(Decimal(str(krw_balance)) * (Decimal(str(fee_rate)) / Decimal(str(100)))) # 매수가능금액 available_krw = math.floor(Decimal(str(krw_balance)) – Decimal(str(fee))) else: # 수수료 fee = 0 # 매수가능금액 available_krw = 0 # 결과 조립 rtn_balance[‘krw_balance’] = krw_balance rtn_balance[‘fee’] = fee rtn_balance[‘available_krw’] = available_krw return rtn_balance # —————————————- # Exception Raise # —————————————- except Exception: raise # —————————————————————————– # – Name : get_accounts # – Desc : 잔고정보 조회 # – Input # 1) except_yn : KRW 및 소액 제외 # 2) market_code : 마켓코드 추가(매도시 필요) # – Output # 1) 잔고 정보 # —————————————————————————– # 계좌 조회 def get_accounts(except_yn, market_code): try: rtn_data = [] # 해당 마켓에 존재하는 종목 리스트만 추출 market_item_list = get_items(market_code, ”) # 소액 제외 기준 min_price = 5000 payload = { ‘access_key’: access_key, ‘nonce’: str(uuid.uuid4()), } jwt_token = jwt.encode(payload, secret_key) authorize_token = ‘Bearer {}’.format(jwt_token) headers = {“Authorization”: authorize_token} res = send_request(“GET”, server_url + “/v1/accounts”, “”, headers) account_data = res.json() for account_data_for in account_data: for market_item_list_for in market_item_list: # 해당 마켓에 있는 종목만 조합 if market_code + ‘-‘ + account_data_for[‘currency’] == market_item_list_for[‘market’]: # KRW 및 소액 제외 if except_yn == “Y” or except_yn == “y”: if account_data_for[‘currency’] != “KRW” and Decimal(str(account_data_for[‘avg_buy_price’])) * ( Decimal(str(account_data_for[‘balance’])) + Decimal( str(account_data_for[‘locked’]))) >= Decimal(str(min_price)): rtn_data.append( {‘market’: market_code + ‘-‘ + account_data_for[‘currency’], ‘balance’: account_data_for[‘balance’], ‘locked’: account_data_for[‘locked’], ‘avg_buy_price’: account_data_for[‘avg_buy_price’], ‘avg_buy_price_modified’: account_data_for[‘avg_buy_price_modified’]}) else: if account_data_for[‘currency’] != “KRW”: rtn_data.append( {‘market’: market_code + ‘-‘ + account_data_for[‘currency’], ‘balance’: account_data_for[‘balance’], ‘locked’: account_data_for[‘locked’], ‘avg_buy_price’: account_data_for[‘avg_buy_price’], ‘avg_buy_price_modified’: account_data_for[‘avg_buy_price_modified’]}) return rtn_data # —————————————- # Exception Raise # —————————————- except Exception: raise # —————————————————————————– # – Name : chg_account_to_comma # – Desc : 잔고 종목 리스트를 콤마리스트로 변경 # – Input # 1) account_data : 잔고 데이터 # – Output # 1) 종목 리스트(콤마 구분자) # —————————————————————————– def chg_account_to_comma(account_data): try: rtn_data = “” for account_data_for in account_data: if rtn_data == ”: rtn_data = rtn_data + account_data_for[‘market’] else: rtn_data = rtn_data + ‘,’ + account_data_for[‘market’] return rtn_data # —————————————- # Exception Raise # —————————————- except Exception: raise # —————————————————————————– # – Name : get_ticker # – Desc : 현재가 조회 # – Input # 1) target_itemlist : 대상 종목(콤마 구분자) # – Output # 2) 현재가 데이터 # —————————————————————————– def get_ticker(target_itemlist): try: url = “https://api.upbit.com/v1/ticker” querystring = {“markets”: target_itemlist} response = send_request(“GET”, url, querystring, “”) logging.info(response) rtn_data = response.json() return rtn_data # —————————————- # 모든 함수의 공통 부분(Exception 처리) # —————————————- except Exception: raise # —————————————————————————– # – Name : cancel_order # – Desc : 미체결 주문 취소 # – Input # 1) target_item : 대상종목 # 2) side : 매수/매도 구분(BUY/bid:매수, SELL/ask:매도, ALL:전체) # – Output # —————————————————————————– def cancel_order(target_item, side): try: # 미체결 주문 조회 order_data = get_order(target_item) # 매수/매도 구분 for order_data_for in order_data: if side == “BUY” or side == “buy”: if order_data_for[‘side’] == “ask”: order_data.remove(order_data_for) elif side == “SELL” or side == “sell”: if order_data_for[‘side’] == “bid”: order_data.remove(order_data_for) # 미체결 주문이 있으면 if len(order_data) > 0: # 미체결 주문내역 전체 취소 for order_data_for in order_data: cancel_order_uuid(order_data_for[‘uuid’]) # —————————————- # 모든 함수의 공통 부분(Exception 처리) # —————————————- except Exception: raise # —————————————————————————– # – Name : cancel_order_uuid # – Desc : 미체결 주문 취소 by UUID # – Input # 1) order_uuid : 주문 키 # – Output # 1) 주문 내역 취소 # —————————————————————————– def cancel_order_uuid(order_uuid): try: query = { ‘uuid’: order_uuid, } query_string = urlencode(query).encode() m = hashlib.sha512() m.update(query_string) query_hash = m.hexdigest() payload = { ‘access_key’: access_key, ‘nonce’: str(uuid.uuid4()), ‘query_hash’: query_hash, ‘query_hash_alg’: ‘SHA512’, } jwt_token = jwt.encode(payload, secret_key) authorize_token = ‘Bearer {}’.format(jwt_token) headers = {“Authorization”: authorize_token} res = send_request(“DELETE”, server_url + “/v1/order”, query, headers) rtn_data = res.json() return rtn_data # —————————————- # 모든 함수의 공통 부분(Exception 처리) # —————————————- except Exception: raise # —————————————————————————– # – Name : get_order # – Desc : 미체결 주문 조회 # – Input # 1) target_item : 대상종목 # – Output # 1) 미체결 주문 내역 # —————————————————————————– def get_order(target_item): try: query = { ‘market’: target_item, ‘state’: ‘wait’, } query_string = urlencode(query).encode() m = hashlib.sha512() m.update(query_string) query_hash = m.hexdigest() payload = { ‘access_key’: access_key, ‘nonce’: str(uuid.uuid4()), ‘query_hash’: query_hash, ‘query_hash_alg’: ‘SHA512’, } jwt_token = jwt.encode(payload, secret_key) authorize_token = ‘Bearer {}’.format(jwt_token) headers = {“Authorization”: authorize_token} res = send_request(“GET”, server_url + “/v1/orders”, query, headers) rtn_data = res.json() return rtn_data # —————————————- # 모든 함수의 공통 부분(Exception 처리) # —————————————- except Exception: raise # —————————————————————————– # – Name : get_order # – Desc : 미체결 주문 조회 # – Input # 1) side : 주문상태 # – Output # 1) 주문 내역 리스트 # —————————————————————————– def get_order_list(side): try: query = { ‘state’: side, } query_string = urlencode(query).encode() m = hashlib.sha512() m.update(query_string) query_hash = m.hexdigest() payload = { ‘access_key’: access_key, ‘nonce’: str(uuid.uuid4()), ‘query_hash’: query_hash, ‘query_hash_alg’: ‘SHA512’, } jwt_token = jwt.encode(payload, secret_key) authorize_token = ‘Bearer {}’.format(jwt_token) headers = {“Authorization”: authorize_token} res = send_request(“GET”, server_url + “/v1/orders”, query, headers) rtn_data = res.json() return rtn_data # —————————————- # 모든 함수의 공통 부분(Exception 처리) # —————————————- except Exception: raise # —————————————————————————– # – Name : get_rsi # – Desc : RSI 조회 # – Input # 1) target_item : 대상 종목 # 2) tick_kind : 캔들 종류 (1, 3, 5, 10, 15, 30, 60, 240 – 분, D-일, W-주, M-월) # 3) inq_range : 조회 범위 # – Output # 1) RSI 값 # —————————————————————————– def get_rsi(target_item, tick_kind, inq_range): try: # 캔들 추출 candle_data = get_candle(target_item, tick_kind, inq_range) df = pd.DataFrame(candle_data) df = df.reindex(index=df.index[::-1]).reset_index() df[‘close’] = df[“trade_price”] # RSI 계산 def rsi(ohlc: pd.DataFrame, period: int = 14): ohlc[“close”] = ohlc[“close”] delta = ohlc[“close”].diff() up, down = delta.copy(), delta.copy() up[up < 0] = 0 down[down > 0] = 0 _gain = up.ewm(com=(period – 1), min_periods=period).mean() _loss = down.abs().ewm(com=(period – 1), min_periods=period).mean() RS = _gain / _loss return pd.Series(100 – (100 / (1 + RS)), name=”RSI”) rsi = round(rsi(df, 14).iloc[-1], 4) return rsi # —————————————- # 모든 함수의 공통 부분(Exception 처리) # —————————————- except Exception: raise # —————————————————————————– # – Name : get_mfi # – Desc : MFI 조회 # – Input # 1) target_item : 대상 종목 # 2) tick_kind : 캔들 종류 (1, 3, 5, 10, 15, 30, 60, 240 – 분, D-일, W-주, M-월) # 3) inq_range : 캔들 조회 범위 # 4) loop_cnt : 지표 반복계산 횟수 # – Output # 1) MFI 값 # —————————————————————————– def get_mfi(target_item, tick_kind, inq_range, loop_cnt): try: # 캔들 데이터 조회용 candle_datas = [] # MFI 데이터 리턴용 mfi_list = [] # 캔들 추출 candle_data = get_candle(target_item, tick_kind, inq_range) # 조회 횟수별 candle 데이터 조합 for i in range(0, int(loop_cnt)): candle_datas.append(candle_data[i:int(len(candle_data))]) # 캔들 데이터만큼 수행 for candle_data_for in candle_datas: df = pd.DataFrame(candle_data_for) dfDt = df[‘candle_date_time_kst’].iloc[::-1] df[‘typical_price’] = (df[‘trade_price’] + df[‘high_price’] + df[‘low_price’]) / 3 df[‘money_flow’] = df[‘typical_price’] * df[‘candle_acc_trade_volume’] positive_mf = 0 negative_mf = 0 for i in range(0, 14): if df[“typical_price”][i] > df[“typical_price”][i + 1]: positive_mf = positive_mf + df[“money_flow”][i] elif df[“typical_price”][i] < df["typical_price"][i + 1]: negative_mf = negative_mf + df["money_flow"][i] if negative_mf > 0: mfi = 100 – (100 / (1 + (positive_mf / negative_mf))) else: mfi = 100 – (100 / (1 + (positive_mf))) mfi_list.append({“type”: “MFI”, “DT”: dfDt[0], “MFI”: round(mfi, 4)}) return mfi_list # —————————————- # 모든 함수의 공통 부분(Exception 처리) # —————————————- except Exception: raise # —————————————————————————– # – Name : get_macd # – Desc : MACD 조회 # – Input # 1) target_item : 대상 종목 # 2) tick_kind : 캔들 종류 (1, 3, 5, 10, 15, 30, 60, 240 – 분, D-일, W-주, M-월) # 3) inq_range : 캔들 조회 범위 # 4) loop_cnt : 지표 반복계산 횟수 # – Output # 1) MACD 값 # —————————————————————————– def get_macd(target_item, tick_kind, inq_range, loop_cnt): try: # 캔들 데이터 조회용 candle_datas = [] # MACD 데이터 리턴용 macd_list = [] # 캔들 추출 candle_data = get_candle(target_item, tick_kind, inq_range) # 조회 횟수별 candle 데이터 조합 for i in range(0, int(loop_cnt)): candle_datas.append(candle_data[i:int(len(candle_data))]) df = pd.DataFrame(candle_datas[0]) df = df.iloc[::-1] df = df[‘trade_price’] # MACD 계산 exp1 = df.ewm(span=12, adjust=False).mean() exp2 = df.ewm(span=26, adjust=False).mean() macd = exp1 – exp2 exp3 = macd.ewm(span=9, adjust=False).mean() for i in range(0, int(loop_cnt)): macd_list.append( {“type”: “MACD”, “DT”: candle_datas[0][i][‘candle_date_time_kst’], “MACD”: round(macd[i], 4), “SIGNAL”: round(exp3[i], 4), “OCL”: round(macd[i] – exp3[i], 4)}) return macd_list # —————————————- # 모든 함수의 공통 부분(Exception 처리) # —————————————- except Exception: raise # —————————————————————————– # – Name : get_bb # – Desc : 볼린저밴드 조회 # – Input # 1) target_item : 대상 종목 # 2) tick_kind : 캔들 종류 (1, 3, 5, 10, 15, 30, 60, 240 – 분, D-일, W-주, M-월) # 3) inq_range : 캔들 조회 범위 # 4) loop_cnt : 지표 반복계산 횟수 # – Output # 1) 볼린저 밴드 값 # —————————————————————————– def get_bb(target_item, tick_kind, inq_range, loop_cnt): try: # 캔들 데이터 조회용 candle_datas = [] # 볼린저밴드 데이터 리턴용 bb_list = [] # 캔들 추출 candle_data = get_candle(target_item, tick_kind, inq_range) # 조회 횟수별 candle 데이터 조합 for i in range(0, int(loop_cnt)): candle_datas.append(candle_data[i:int(len(candle_data))]) # 캔들 데이터만큼 수행 for candle_data_for in candle_datas: df = pd.DataFrame(candle_data_for) dfDt = df[‘candle_date_time_kst’].iloc[::-1] df = df[‘trade_price’].iloc[::-1] # 표준편차(곱) unit = 2 band1 = unit * numpy.std(df[len(df) – 20:len(df)]) bb_center = numpy.mean(df[len(df) – 20:len(df)]) band_high = bb_center + band1 band_low = bb_center – band1 bb_list.append({“type”: “BB”, “DT”: dfDt[0], “BBH”: round(band_high, 4), “BBM”: round(bb_center, 4), “BBL”: round(band_low, 4)}) return bb_list # —————————————- # 모든 함수의 공통 부분(Exception 처리) # —————————————- except Exception: raise # —————————————————————————– # – Name : get_williams # – Desc : 윌리암스 %R 조회 # – Input # 1) target_item : 대상 종목 # 2) tick_kind : 캔들 종류 (1, 3, 5, 10, 15, 30, 60, 240 – 분, D-일, W-주, M-월) # 3) inq_range : 캔들 조회 범위 # 4) loop_cnt : 지표 반복계산 횟수 # – Output # 1) 윌리암스 %R 값 # —————————————————————————– def get_williamsR(target_item, tick_kind, inq_range, loop_cnt): try: # 캔들 데이터 조회용 candle_datas = [] # 윌리암스R 데이터 리턴용 williams_list = [] # 캔들 추출 candle_data = get_candle(target_item, tick_kind, inq_range) # 조회 횟수별 candle 데이터 조합 for i in range(0, int(loop_cnt)): candle_datas.append(candle_data[i:int(len(candle_data))]) # 캔들 데이터만큼 수행 for candle_data_for in candle_datas: df = pd.DataFrame(candle_data_for) dfDt = df[‘candle_date_time_kst’].iloc[::-1] df = df.iloc[:14] # 계산식 # %R = (Highest High – Close)/(Highest High – Lowest Low) * -100 hh = numpy.max(df[‘high_price’]) ll = numpy.min(df[‘low_price’]) cp = df[‘trade_price’][0] w = (hh – cp) / (hh – ll) * -100 williams_list.append( {“type”: “WILLIAMS”, “DT”: dfDt[0], “HH”: round(hh, 4), “LL”: round(ll, 4), “CP”: round(cp, 4), “W”: round(w, 4)}) return williams_list # —————————————- # 모든 함수의 공통 부분(Exception 처리) # —————————————- except Exception: raise # —————————————————————————– # – Name : get_rsi # – Desc : RSI 조회 # – Input # 1) candle_data : 캔들 정보 # – Output # 1) RSI 값 # —————————————————————————– def get_rsi(candle_datas): try: # RSI 데이터 리턴용 rsi_data = [] # 캔들 데이터만큼 수행 for candle_data_for in candle_datas: df = pd.DataFrame(candle_data_for) dfDt = df[‘candle_date_time_kst’].iloc[::-1] df = df.reindex(index=df.index[::-1]).reset_index() df[‘close’] = df[“trade_price”] # RSI 계산 def rsi(ohlc: pd.DataFrame, period: int = 14): ohlc[“close”] = ohlc[“close”] delta = ohlc[“close”].diff() up, down = delta.copy(), delta.copy() up[up < 0] = 0 down[down > 0] = 0 _gain = up.ewm(com=(period – 1), min_periods=period).mean() _loss = down.abs().ewm(com=(period – 1), min_periods=period).mean() RS = _gain / _loss return pd.Series(100 – (100 / (1 + RS)), name=”RSI”) rsi = round(rsi(df, 14).iloc[-1], 4) rsi_data.append({“type”: “RSI”, “DT”: dfDt[0], “RSI”: rsi}) return rsi_data # —————————————- # 모든 함수의 공통 부분(Exception 처리) # —————————————- except Exception: raise # —————————————————————————– # – Name : get_mfi # – Desc : MFI 조회 # – Input # 1) candle_datas : 캔들 정보 # – Output # 1) MFI 값 # —————————————————————————– def get_mfi(candle_datas): try: # MFI 데이터 리턴용 mfi_list = [] # 캔들 데이터만큼 수행 for candle_data_for in candle_datas: df = pd.DataFrame(candle_data_for) dfDt = df[‘candle_date_time_kst’].iloc[::-1] df[‘typical_price’] = (df[‘trade_price’] + df[‘high_price’] + df[‘low_price’]) / 3 df[‘money_flow’] = df[‘typical_price’] * df[‘candle_acc_trade_volume’] positive_mf = 0 negative_mf = 0 for i in range(0, 14): if df[“typical_price”][i] > df[“typical_price”][i + 1]: positive_mf = positive_mf + df[“money_flow”][i] elif df[“typical_price”][i] < df["typical_price"][i + 1]: negative_mf = negative_mf + df["money_flow"][i] if negative_mf > 0: mfi = 100 – (100 / (1 + (positive_mf / negative_mf))) else: mfi = 100 – (100 / (1 + (positive_mf))) mfi_list.append({“type”: “MFI”, “DT”: dfDt[0], “MFI”: round(mfi, 4)}) return mfi_list # —————————————- # 모든 함수의 공통 부분(Exception 처리) # —————————————- except Exception: raise # —————————————————————————– # – Name : get_macd # – Desc : MACD 조회 # – Input # 1) candle_datas : 캔들 정보 # 2) loop_cnt : 반복 횟수 # – Output # 1) MACD 값 # —————————————————————————– def get_macd(candle_datas, loop_cnt): try: # MACD 데이터 리턴용 macd_list = [] df = pd.DataFrame(candle_datas[0]) df = df.iloc[::-1] df = df[‘trade_price’] # MACD 계산 exp1 = df.ewm(span=12, adjust=False).mean() exp2 = df.ewm(span=26, adjust=False).mean() macd = exp1 – exp2 exp3 = macd.ewm(span=9, adjust=False).mean() for i in range(0, int(loop_cnt)): macd_list.append( {“type”: “MACD”, “DT”: candle_datas[0][i][‘candle_date_time_kst’], “MACD”: round(macd[i], 4), “SIGNAL”: round(exp3[i], 4), “OCL”: round(macd[i] – exp3[i], 4)}) return macd_list # —————————————- # 모든 함수의 공통 부분(Exception 처리) # —————————————- except Exception: raise # —————————————————————————– # – Name : get_ma # – Desc : MA 조회 # – Input # 1) candle_datas : 캔들 정보 # 2) loop_cnt : 반복 횟수 # – Output # 1) MA 값 # —————————————————————————– def get_ma(candle_datas, loop_cnt): try: # MA 데이터 리턴용 ma_list = [] df = pd.DataFrame(candle_datas[0]) df = df.iloc[::-1] df = df[‘trade_price’] # MA 계산 ma5 = df.rolling(window=5).mean() ma10 = df.rolling(window=10).mean() ma20 = df.rolling(window=20).mean() ma60 = df.rolling(window=60).mean() ma120 = df.rolling(window=120).mean() for i in range(0, int(loop_cnt)): ma_list.append( {“type”: “MA”, “DT”: candle_datas[0][i][‘candle_date_time_kst’], “MA5”: ma5[i], “MA10”: ma10[i], “MA20”: ma20[i], “MA60”: ma60[i], “MA120”: ma120[i] , “MA_5_10”: str(Decimal(str(ma5[i])) – Decimal(str(ma10[i]))) , “MA_10_20”: str(Decimal(str(ma10[i])) – Decimal(str(ma20[i]))) , “MA_20_60”: str(Decimal(str(ma20[i])) – Decimal(str(ma60[i]))) , “MA_60_120”: str(Decimal(str(ma60[i])) – Decimal(str(ma120[i])))}) return ma_list # —————————————- # 모든 함수의 공통 부분(Exception 처리) # —————————————- except Exception: raise # —————————————————————————– # – Name : get_bb # – Desc : 볼린저밴드 조회 # – Input # 1) candle_datas : 캔들 정보 # – Output # 1) 볼린저 밴드 값 # —————————————————————————– def get_bb(candle_datas): try: # 볼린저밴드 데이터 리턴용 bb_list = [] # 캔들 데이터만큼 수행 for candle_data_for in candle_datas: df = pd.DataFrame(candle_data_for) dfDt = df[‘candle_date_time_kst’].iloc[::-1] df = df[‘trade_price’].iloc[::-1] # 표준편차(곱) unit = 2 band1 = unit * numpy.std(df[len(df) – 20:len(df)]) bb_center = numpy.mean(df[len(df) – 20:len(df)]) band_high = bb_center + band1 band_low = bb_center – band1 bb_list.append({“type”: “BB”, “DT”: dfDt[0], “BBH”: round(band_high, 4), “BBM”: round(bb_center, 4), “BBL”: round(band_low, 4)}) return bb_list # —————————————- # 모든 함수의 공통 부분(Exception 처리) # —————————————- except Exception: raise # —————————————————————————– # – Name : get_williams # – Desc : 윌리암스 %R 조회 # – Input # 1) candle_datas : 캔들 정보 # – Output # 1) 윌리암스 %R 값 # —————————————————————————– def get_williams(candle_datas): try: # 윌리암스R 데이터 리턴용 williams_list = [] # 캔들 데이터만큼 수행 for candle_data_for in candle_datas: df = pd.DataFrame(candle_data_for) dfDt = df[‘candle_date_time_kst’].iloc[::-1] df = df.iloc[:14] # 계산식 # %R = (Highest High – Close)/(Highest High – Lowest Low) * -100 hh = numpy.max(df[‘high_price’]) ll = numpy.min(df[‘low_price’]) cp = df[‘trade_price’][0] w = (hh – cp) / (hh – ll) * -100 williams_list.append( {“type”: “WILLIAMS”, “DT”: dfDt[0], “HH”: round(hh, 4), “LL”: round(ll, 4), “CP”: round(cp, 4), “W”: round(w, 4)}) return williams_list # —————————————- # 모든 함수의 공통 부분(Exception 처리) # —————————————- except Exception: raise # —————————————————————————– # – Name : get_cci # – Desc : CCI 조회 # – Input # 1) candle_data : 캔들 정보 # 2) loop_cnt : 조회 건수 # – Output # 1) CCI 값 # —————————————————————————– def get_cci(candle_data, loop_cnt): try: cci_val = 20 # CCI 데이터 리턴용 cci_list = [] # 사용하지 않는 캔들 갯수 정리(속도 개선) del candle_data[cci_val * 2:] # 오름차순 정렬 df = pd.DataFrame(candle_data) ordered_df = df.sort_values(by=[‘candle_date_time_kst’], ascending=[True]) # 계산식 : (Typical Price – Simple Moving Average) / (0.015 * Mean absolute Deviation) ordered_df[‘TP’] = (ordered_df[‘high_price’] + ordered_df[‘low_price’] + ordered_df[‘trade_price’]) / 3 ordered_df[‘SMA’] = ordered_df[‘TP’].rolling(window=cci_val).mean() ordered_df[‘MAD’] = ordered_df[‘TP’].rolling(window=cci_val).apply(lambda x: pd.Series(x).mad()) ordered_df[‘CCI’] = (ordered_df[‘TP’] – ordered_df[‘SMA’]) / (0.015 * ordered_df[‘MAD’]) # 개수만큼 조립 for i in range(0, loop_cnt): cci_list.append({“type”: “CCI”, “DT”: ordered_df[‘candle_date_time_kst’].loc[i], “CCI”: round(ordered_df[‘CCI’].loc[i], 4)}) return cci_list # —————————————- # 모든 함수의 공통 부분(Exception 처리) # —————————————- except Exception: raise # —————————————————————————– # – Name : get_indicators # – Desc : 보조지표 조회 # – Input # 1) target_item : 대상 종목 # 2) tick_kind : 캔들 종류 (1, 3, 5, 10, 15, 30, 60, 240 – 분, D-일, W-주, M-월) # 3) inq_range : 캔들 조회 범위 # 4) loop_cnt : 지표 반복계산 횟수 # – Output # 1) RSI # 2) MFI # 3) MACD # 4) BB # 5) WILLIAMS %R # 6) CCI # —————————————————————————– def get_indicators(target_item, tick_kind, inq_range, loop_cnt): try: # 보조지표 리턴용 indicator_data = [] # 캔들 데이터 조회용 candle_datas = [] # 캔들 추출 candle_data = get_candle(target_item, tick_kind, inq_range) if len(candle_data) >= 30: # 조회 횟수별 candle 데이터 조합 for i in range(0, int(loop_cnt)): candle_datas.append(candle_data[i:int(len(candle_data))]) # RSI 정보 조회 rsi_data = get_rsi(candle_datas) # MFI 정보 조회 mfi_data = get_mfi(candle_datas) # MACD 정보 조회 macd_data = get_macd(candle_datas, loop_cnt) # BB 정보 조회 bb_data = get_bb(candle_datas) # WILLIAMS %R 조회 williams_data = get_williams(candle_datas) # MA 정보 조회 ma_data = get_ma(candle_datas, loop_cnt) # CCI 정보 조회 cci_data = get_cci(candle_data, loop_cnt) if len(rsi_data) > 0: indicator_data.append(rsi_data) if len(mfi_data) > 0: indicator_data.append(mfi_data) if len(macd_data) > 0: indicator_data.append(macd_data) if len(bb_data) > 0: indicator_data.append(bb_data) if len(williams_data) > 0: indicator_data.append(williams_data) if len(ma_data) > 0: indicator_data.append(ma_data) if len(cci_data) > 0: indicator_data.append(cci_data) return indicator_data # —————————————- # 모든 함수의 공통 부분(Exception 처리) # —————————————- except Exception: raise # —————————————————————————– # – Name : get_order_status # – Desc : 주문 조회(상태별) # – Input # 1) target_item : 대상종목 # 2) status : 주문상태(wait : 체결 대기, watch : 예약주문 대기, done : 전체 체결 완료, cancel : 주문 취소) # – Output # 1) 주문 내역 # —————————————————————————– def get_order_status(target_item, status): try: query = { ‘market’: target_item, ‘state’: status, } query_string = urlencode(query).encode() m = hashlib.sha512() m.update(query_string) query_hash = m.hexdigest() payload = { ‘access_key’: access_key, ‘nonce’: str(uuid.uuid4()), ‘query_hash’: query_hash, ‘query_hash_alg’: ‘SHA512’, } jwt_token = jwt.encode(payload, secret_key) authorize_token = ‘Bearer {}’.format(jwt_token) headers = {“Authorization”: authorize_token} res = send_request(“GET”, server_url + “/v1/orders”, query, headers) rtn_data = res.json() return rtn_data # —————————————- # 모든 함수의 공통 부분(Exception 처리) # —————————————- except Exception: raise # —————————————————————————– # – Name : orderby_dict # – Desc : 딕셔너리 정렬 # – Input # 1) target_dict : 정렬 대상 딕셔너리 # 2) target_column : 정렬 대상 딕셔너리 # 3) order_by : 정렬방식(False:오름차순, True,내림차순) # – Output # 1) 정렬된 딕서너리 # —————————————————————————– def orderby_dict(target_dict, target_column, order_by): try: rtn_dict = sorted(target_dict, key=(lambda x: x[target_column]), reverse=order_by) return rtn_dict # —————————————- # 모든 함수의 공통 부분(Exception 처리) # —————————————- except Exception: raise # —————————————————————————– # – Name : filter_dict # – Desc : 딕셔너리 필터링 # – Input # 1) target_dict : 정렬 대상 딕셔너리 # 2) target_column : 정렬 대상 컬럼 # 3) filter : 필터 # – Output # 1) 필터링된 딕서너리 # —————————————————————————– def filter_dict(target_dict, target_column, filter): try: for target_dict_for in target_dict[:]: if target_dict_for[target_column] != filter: target_dict.remove(target_dict_for) return target_dict # —————————————- # 모든 함수의 공통 부분(Exception 처리) # —————————————- except Exception: raise # —————————————————————————– # – Name : get_order_chance # – Desc : 주문 가능정보 조회 # – Input # 1) target_item : 대상종목 # – Output # 1) 주문 가능 정보 # —————————————————————————– def get_order_chance(target_item): try: query = { ‘market’: target_item, } query_string = urlencode(query).encode() m = hashlib.sha512() m.update(query_string) query_hash = m.hexdigest() payload = { ‘access_key’: access_key, ‘nonce’: str(uuid.uuid4()), ‘query_hash’: query_hash, ‘query_hash_alg’: ‘SHA512’, } jwt_token = jwt.encode(payload, secret_key) authorize_token = ‘Bearer {}’.format(jwt_token) headers = {“Authorization”: authorize_token} res = send_request(“GET”, server_url + “/v1/orders/chance”, query, headers) rtn_data = res.json() return rtn_data # —————————————- # 모든 함수의 공통 부분(Exception 처리) # —————————————- except Exception: raise # —————————————————————————– # – Name : get_max_min # – Desc : MAX/MIN 값 조회 # – Input # 1) candle_datas : 캔들 정보 # 2) col_name : 대상 컬럼 # – Output # 1) MAX 값 # 2) MIN 값 # —————————————————————————– def get_max(candle_data, col_name_high, col_name_low): try: # MA 데이터 리턴용 max_min_list = [] df = pd.DataFrame(candle_data) df = df.iloc[::-1] # MAX 계산 max = numpy.max(df[col_name_high]) min = numpy.min(df[col_name_low]) max_min_list.append( {“MAX”: max, “MIN”: min}) return max_min_list # —————————————- # 모든 함수의 공통 부분(Exception 처리) # —————————————- except Exception: raise # —————————————————————————– # – Name : send_line_msg # – Desc : 라인 메세지 전송 # – Input # 1) message : 메세지 # – Output # 1) response : 발송결과(200:정상) # —————————————————————————– def send_line_message(message): try: headers = {‘Authorization’: ‘Bearer ‘ + line_token} data = {‘message’: message} response = requests.post(line_target_url, headers=headers, data=data) logging.debug(response) return response # —————————————- # 모든 함수의 공통 부분(Exception 처리) # —————————————- except Exception: raise # —————————————————————————– # – Name : get_indicator_sel # – Desc : 보조지표 조회(원하는 지표만) # – Input # 1) target_item : 대상 종목 # 2) tick_kind : 캔들 종류 (1, 3, 5, 10, 15, 30, 60, 240 – 분, D-일, W-주, M-월) # 3) inq_range : 캔들 조회 범위 # 4) loop_cnt : 지표 반복계산 횟수 # 5) 보조지표 : 리스트 # – Output # 1) 보조지표 # —————————————————————————– def get_indicator_sel(target_item, tick_kind, inq_range, loop_cnt, indi_type): try: # 보조지표 리턴용 indicator_data = {} # 캔들 데이터 조회용 candle_datas = [] # 캔들 추출 candle_data = get_candle(target_item, tick_kind, inq_range) if len(candle_data) >= 30: # 조회 횟수별 candle 데이터 조합 for i in range(0, int(loop_cnt)): candle_datas.append(candle_data[i:int(len(candle_data))]) if ‘RSI’ in indi_type: # RSI 정보 조회 rsi_data = get_rsi(candle_datas) indicator_data[‘RSI’] = rsi_data if ‘MFI’ in indi_type: # MFI 정보 조회 mfi_data = get_mfi(candle_datas) indicator_data[‘MFI’] = mfi_data if ‘MACD’ in indi_type: # MACD 정보 조회 macd_data = get_macd(candle_datas, loop_cnt) indicator_data[‘MACD’] = macd_data if ‘BB’ in indi_type: # BB 정보 조회 bb_data = get_bb(candle_datas) indicator_data[‘BB’] = bb_data if ‘WILLIAMS’ in indi_type: # WILLIAMS %R 조회 williams_data = get_williams(candle_datas) indicator_data[‘WILLIAMS’] = williams_data if ‘MA’ in indi_type: # MA 정보 조회 ma_data = get_ma(candle_datas, loop_cnt) indicator_data[‘MA’] = ma_data if ‘CCI’ in indi_type: # CCI 정보 조회 cci_data = get_cci(candle_data, loop_cnt) indicator_data[‘CCI’] = cci_data if ‘CANDLE’ in indi_type: indicator_data[‘CANDLE’] = candle_data return indicator_data # —————————————- # 모든 함수의 공통 부분(Exception 처리) # —————————————- except Exception: raise # —————————————————————————– # – Name : send_msg # – Desc : 메세지 전송 # – Input # 1) sent_list : 메세지 발송 내역 # 2) key : 메세지 키 # 3) contents : 메세지 내용 # 4) msg_intval : 메세지 발송주기 # – Output # 1) sent_list : 메세지 발송 내역 # —————————————————————————– def send_msg(sent_list, key, contents, msg_intval): try: # msg_intval = ‘N’ 이면 메세지 발송하지 않음 if msg_intval.upper() != ‘N’: # 발송여부 체크 sent_yn = False # 발송이력 sent_dt = ” # 발송내역에 해당 키 존재 시 발송 이력 추출 for sent_list_for in sent_list: if key in sent_list_for.values(): sent_yn = True sent_dt = datetime.strptime(sent_list_for[‘SENT_DT’], ‘%Y-%m-%d %H:%M:%S’) # 기 발송 건 if sent_yn: logging.info(‘기존 발송 건’) # 현재 시간 추출 current_dt = datetime.strptime(datetime.now().strftime(‘%Y-%m-%d %H:%M:%S’), ‘%Y-%m-%d %H:%M:%S’) # 시간 차이 추출 diff = current_dt – sent_dt # 발송 시간이 지난 경우에는 메세지 발송 if diff.seconds >= int(msg_intval): logging.info(‘발송 주기 도래 건으로 메시지 발송 처리!’) # 메세지 발송 send_line_message(contents) # 기존 메시지 발송이력 삭제 for sent_list_for in sent_list[:]: if key in sent_list_for.values(): sent_list.remove(sent_list_for) # 새로운 발송이력 추가 sent_list.append({‘KEY’: key, ‘SENT_DT’: datetime.now().strftime(‘%Y-%m-%d %H:%M:%S’)}) else: logging.info(‘발송 주기 미 도래 건!’) # 최초 발송 건 else: logging.info(‘최초 발송 건’) # 메세지 발송 send_line_message(contents) # 새로운 발송이력 추가 sent_list.append({‘KEY’: key, ‘SENT_DT’: datetime.now().strftime(‘%Y-%m-%d %H:%M:%S’)}) return sent_list # —————————————- # 모든 함수의 공통 부분(Exception 처리) # —————————————- except Exception: raise
반응형
그 동안 포스팅을 진행하면서 수정된 부분이 많아서 최신으로 가지고 있는 공통 모듈 전체를 올려 드리니 참고하시면 좋을 것 같습니다.
앞으로도 공통 모듈은 최신 포스팅에 적용된 부분을 가지고 사용하시면 에러가 발생할 확률이 낮을 것 같으니 참고 부탁 드리겠습니다.
자동 매수 프로그램
buy_bot.py
import time import os import sys import logging import traceback from decimal import Decimal # 공통 모듈 Import sys.path.append(os.path.dirname(os.path.dirname(__file__))) from module import upbit # —————————————————————————– # – Name : start_buytrade # – Desc : 매수 로직 # – Input # 1) buy_amt : 매수금액 # —————————————————————————– def start_buytrade(buy_amt): try: # ———————————————————————- # 반복 수행 # ———————————————————————- while True: logging.info(“*********************************************************”) logging.info(“1. 로그레벨 : ” + str(log_level)) logging.info(“2. 매수금액 : ” + str(buy_amt)) logging.info(“*********************************************************”) # —————————————————————– # 전체 종목 리스트 추출 # —————————————————————– target_items = upbit.get_items(‘KRW’, ”) # —————————————————————– # 종목별 체크 # —————————————————————– for target_item in target_items: rsi_val = False mfi_val = False ocl_val = False logging.info(‘체크중….[‘ + str(target_item[‘market’]) + ‘]’) # ————————————————————- # 종목별 보조지표를 조회 # 1. 조회 기준 : 일캔들, 최근 5개 지표 조회 # 2. 속도를 위해 원하는 지표만 조회(RSI, MFI, MACD, CANDLE) # ————————————————————- indicators = upbit.get_indicator_sel(target_item[‘market’], ‘D’, 200, 5, [‘RSI’, ‘MFI’, ‘MACD’, ‘CANDLE’]) # ————————————————————– # 최근 상장하여 캔들 갯수 부족으로 보조 지표를 구하기 어려운 건은 제외 # ————————————————————– if ‘CANDLE’ not in indicators or len(indicators[‘CANDLE’]) < 200: logging.info('캔들 데이터 부족으로 데이터 산출 불가...[' + str(target_item['market']) + ']') continue # -------------------------------------------------------------- # 보조 지표 추출 # -------------------------------------------------------------- rsi = indicators['RSI'] mfi = indicators['MFI'] macd = indicators['MACD'] candle = indicators['CANDLE'] # -------------------------------------------------------------- # 매수 로직 # 1. RSI : 2일전 < 30미만, 3일전 > 2일전, 1일전 > 2일전, 현재 > 1일전 # 2. MFI : 2일전 < 20미만, 3일전 > 2일전, 1일전 > 2일전, 현재 > 1일전 # 3. MACD(OCL) : 3일전 < 0, 2일전 < 0, 1일전 < 0, 3일전 > 2일전, 1일전 > 2일전, 현재 > 1일전 # ————————————————————– # ————————————————————– # RSI : 2일전 < 30미만, 3일전 > 2일전, 1일전 > 2일전, 현재 > 1일전 # rsi[0][‘RSI’] : 현재 # rsi[1][‘RSI’] : 1일전 # rsi[2][‘RSI’] : 2일전 # rsi[3][‘RSI’] : 3일전 # ————————————————————– if (Decimal(str(rsi[0][‘RSI’])) > Decimal(str(rsi[1][‘RSI’])) > Decimal(str(rsi[2][‘RSI’])) and Decimal(str(rsi[3][‘RSI’])) > Decimal(str(rsi[2][‘RSI’])) and Decimal(str(rsi[2][‘RSI’])) < Decimal(str(30))): rsi_val = True # -------------------------------------------------------------- # MFI : 2일전 < 20미만, 3일전 > 2일전, 1일전 > 2일전, 현재 > 1일전 # mfi[0][‘MFI’] : 현재 # mfi[1][‘MFI’] : 1일전 # mfi[2][‘MFI’] : 2일전 # mfi[3][‘MFI’] : 3일전 # ————————————————————– if (Decimal(str(mfi[0][‘MFI’])) > Decimal(str(mfi[1][‘MFI’])) > Decimal(str(mfi[2][‘MFI’])) and Decimal(str(mfi[3][‘MFI’])) > Decimal(str(mfi[2][‘MFI’])) and Decimal(str(mfi[2][‘MFI’])) < Decimal(str(20))): mfi_val = True # -------------------------------------------------------------- # MACD(OCL) : 3일전 < 0, 2일전 < 0, 1일전 < 0, 3일전 > 2일전, 1일전 > 2일전, 현재 > 1일전 # macd[0][‘OCL’] : 현재 # macd[1][‘OCL’] : 1일전 # macd[2][‘OCL’] : 2일전 # macd[3][‘OCL’] : 3일전 # ————————————————————– if (Decimal(str(macd[0][‘OCL’])) > Decimal(str(macd[1][‘OCL’])) > Decimal(str(macd[2][‘OCL’])) and Decimal(str(macd[3][‘OCL’])) > Decimal(str(macd[2][‘OCL’])) and Decimal(str(macd[1][‘OCL’])) < Decimal(str(0)) and Decimal(str(macd[2]['OCL'])) < Decimal(str(0)) and Decimal(str(macd[3]['OCL'])) < Decimal(str(0))): ocl_val = True # -------------------------------------------------------------- # 매수대상 발견 # -------------------------------------------------------------- if rsi_val and mfi_val and ocl_val: logging.info('매수대상 발견....[' + str(target_item['market']) + ']') logging.info('RSI : ' + str(rsi)) logging.info('MFI : ' + str(mfi)) logging.info('MACD : ' + str(macd)) # ------------------------------------------------------------------ # 기매수 여부 판단 # ------------------------------------------------------------------ accounts = upbit.get_accounts('Y', 'KRW') account = list(filter(lambda x: x.get('market') == target_item['market'], accounts)) # 이미 매수한 종목이면 다시 매수하지 않음 # sell_bot.py에서 매도 처리되면 보유 종목에서 사라지고 다시 매수 가능 if len(account) > 0: logging.info(‘기 매수 종목으로 매수하지 않음….[‘ + str(target_item[‘market’]) + ‘]’) continue # —————————————————————— # 매수금액 설정 # 1. M : 수수료를 제외한 최대 가능 KRW 금액만큼 매수 # 2. 금액 : 입력한 금액만큼 매수 # —————————————————————— available_amt = upbit.get_krwbal()[‘available_krw’] if buy_amt == ‘M’: buy_amt = available_amt # —————————————————————— # 입력 금액이 주문 가능금액보다 작으면 종료 # —————————————————————— if Decimal(str(available_amt)) < Decimal(str(buy_amt)): logging.info('주문 가능금액[' + str(available_amt) + ']이 입력한 주문금액[' + str(buy_amt) + '] 보다 작습니다.') continue # ------------------------------------------------------------------ # 최소 주문 금액(업비트 기준 5000원) 이상일 때만 매수로직 수행 # ------------------------------------------------------------------ if Decimal(str(buy_amt)) < Decimal(str(upbit.min_order_amt)): logging.info('주문금액[' + str(buy_amt) + ']이 최소 주문금액[' + str(upbit.min_order_amt) + '] 보다 작습니다.') continue # ------------------------------------------------------------------ # 시장가 매수 # 실제 매수 로직은 안전을 위해 주석처리 하였습니다. # 실제 매매를 원하시면 테스트를 충분히 거친 후 주석을 해제하시면 됩니다. # ------------------------------------------------------------------ logging.info('시장가 매수 시작! [' + str(target_item['market']) + ']') # rtn_buycoin_mp = upbit.buycoin_mp(target_item['market'], buy_amt) logging.info('시장가 매수 종료! [' + str(target_item['market']) + ']') # logging.info(rtn_buycoin_mp) # --------------------------------------- # 모든 함수의 공통 부분(Exception 처리) # ---------------------------------------- except Exception: raise # ----------------------------------------------------------------------------- # - Name : main # - Desc : 메인 # ----------------------------------------------------------------------------- if __name__ == '__main__': # noinspection PyBroadException try: # --------------------------------------------------------------------- # 입력 받을 변수 # # 1. 로그레벨 # 1) 레벨 값 : D:DEBUG, E:ERROR, 그 외:INFO # # 2. 매수금액 # 1) M : 수수료를 제외한 최대 가능 금액으로 매수 # 2) 금액 : 입력한 금액만 매수(수수료 포함) # # 3. 매수 제외종목 # 1) 종목코드(콤마구분자) : BTC,ETH # --------------------------------------------------------------------- # 1. 로그레벨 log_level = input("로그레벨(D:DEBUG, E:ERROR, 그 외:INFO) : ").upper() buy_amt = input("매수금액(M:최대, 10000:1만원) : ").upper() upbit.set_loglevel(log_level) logging.info("*********************************************************") logging.info("1. 로그레벨 : " + str(log_level)) logging.info("2. 매수금액 : " + str(buy_amt)) logging.info("*********************************************************") # 매수 로직 시작 start_buytrade(buy_amt) except KeyboardInterrupt: logging.error("KeyboardInterrupt Exception 발생!") logging.error(traceback.format_exc()) sys.exit(-100) except Exception: logging.error("Exception 발생!") logging.error(traceback.format_exc()) sys.exit(-200) log_level = input("로그레벨(D:DEBUG, E:ERROR, 그 외:INFO) : ").upper() buy_amt = input("매수금액(M:최대, 10000:1만원) : ").upper() 지난 포스팅에서는 매수 제외 종목을 변수로 받아서 관리하고 매수가 된 이후에 해당 변수에 종목을 추가하여 반복하여 매수 되는 것을 방지했었는데요. 해당 로직은 한번 매수 제외 종목으로 등록되는 경우 프로그램을 재시작하기 전까지는 매도를 했다 하더라도 다시 매수를 하지 않는 문제가 있었습니다. 그래서 이번 로직에서는 매수 제외 종목을 변수로 받는 부분을 제거 하였습니다. # ------------------------------------------------------------- # 종목별 보조지표를 조회 # 1. 조회 기준 : 일캔들, 최근 5개 지표 조회 # 2. 속도를 위해 원하는 지표만 조회(RSI, MFI, MACD, CANDLE) # ------------------------------------------------------------- indicators = upbit.get_indicator_sel(target_item['market'], 'D', 200, 5, ['RSI', 'MFI', 'MACD', 'CANDLE']) 종목별 보조지표를 조회하는 부분도 속도 개선을 위해서 필요한 보조지표만 조회할 수 있도록 수정하였습니다. 이 부분은 아래 포스팅에서 조금 더 자세히 확인하실 수 있습니다. 2021.11.12 - [프로젝트/비트코인 자동매매] - 원하는 보조지표만 한번에 조회하기 - 파이썬 업비트 비트코인 자동매매 # -------------------------------------------------------------- # 최근 상장하여 캔들 갯수 부족으로 보조 지표를 구하기 어려운 건은 제외 # -------------------------------------------------------------- if 'CANDLE' not in indicators or len(indicators['CANDLE']) < 200: logging.info('캔들 데이터 부족으로 데이터 산출 불가...[' + str(target_item['market']) + ']') continue # -------------------------------------------------------------- # 보조 지표 추출 # -------------------------------------------------------------- rsi = indicators['RSI'] mfi = indicators['MFI'] macd = indicators['MACD'] candle = indicators['CANDLE'] 조회한 보조지표 중에 CANDLE는 기본적으로 가져오시는 것이 좋습니다. 가져온 CANDLE 데이터를 가지고 200개 이상이 존재하지 않으면 올바른 지표 계산이 되지 않을 수 있으므로 제외 하도록 하였습니다. 이 로직을 통해 상장한지 얼마 안된 종목들은 제외 됩니다. 조회한 보조 지표들은 위와 같이 리스트로 추출하여 조금 더 편리하게 뽑아낼 수 있도록 변경 하였습니다. RSI를 가져오고 싶으면 좀 더 직관적으로 indicators['RSI']를 이용해서 가져올 수 있습니다. # -------------------------------------------------------------- # RSI : 2일전 < 30미만, 3일전 > 2일전, 1일전 > 2일전, 현재 > 1일전 # rsi[0][‘RSI’] : 현재 # rsi[1][‘RSI’] : 1일전 # rsi[2][‘RSI’] : 2일전 # rsi[3][‘RSI’] : 3일전 # ————————————————————– if (Decimal(str(rsi[0][‘RSI’])) > Decimal(str(rsi[1][‘RSI’])) > Decimal(str(rsi[2][‘RSI’])) and Decimal(str(rsi[3][‘RSI’])) > Decimal(str(rsi[2][‘RSI’])) and Decimal(str(rsi[2][‘RSI’])) < Decimal(str(30))): rsi_val = True # -------------------------------------------------------------- # MFI : 2일전 < 20미만, 3일전 > 2일전, 1일전 > 2일전, 현재 > 1일전 # mfi[0][‘MFI’] : 현재 # mfi[1][‘MFI’] : 1일전 # mfi[2][‘MFI’] : 2일전 # mfi[3][‘MFI’] : 3일전 # ————————————————————– if (Decimal(str(mfi[0][‘MFI’])) > Decimal(str(mfi[1][‘MFI’])) > Decimal(str(mfi[2][‘MFI’])) and Decimal(str(mfi[3][‘MFI’])) > Decimal(str(mfi[2][‘MFI’])) and Decimal(str(mfi[2][‘MFI’])) < Decimal(str(20))): mfi_val = True # -------------------------------------------------------------- # MACD(OCL) : 3일전 < 0, 2일전 < 0, 1일전 < 0, 3일전 > 2일전, 1일전 > 2일전, 현재 > 1일전 # macd[0][‘OCL’] : 현재 # macd[1][‘OCL’] : 1일전 # macd[2][‘OCL’] : 2일전 # macd[3][‘OCL’] : 3일전 # ————————————————————– if (Decimal(str(macd[0][‘OCL’])) > Decimal(str(macd[1][‘OCL’])) > Decimal(str(macd[2][‘OCL’])) and Decimal(str(macd[3][‘OCL’])) > Decimal(str(macd[2][‘OCL’])) and Decimal(str(macd[1][‘OCL’])) < Decimal(str(0)) and Decimal(str(macd[2]['OCL'])) < Decimal(str(0)) and Decimal(str(macd[3]['OCL'])) < Decimal(str(0))): ocl_val = True 보조 지표를 가져오는 부분이 조금 더 직관적으로 바뀌어서 매수 타이밍을 판단하는 로직도 조금 더 보기 좋게 변경 되었습니다. # ------------------------------------------------------------------ # 기매수 여부 판단 # ------------------------------------------------------------------ accounts = upbit.get_accounts('Y', 'KRW') account = list(filter(lambda x: x.get('market') == target_item['market'], accounts)) # 이미 매수한 종목이면 다시 매수하지 않음 # sell_bot.py에서 매도 처리되면 보유 종목에서 사라지고 다시 매수 가능 if len(account) > 0: logging.info(‘기 매수 종목으로 매수하지 않음….[‘ + str(target_item[‘market’]) + ‘]’) continue
반복 매수를 방지하는 것은 기존의 매수 제외 종목에 등록하는 방법 대신 보유 종목에 있는지 체크하는 로직으로 변경 하였습니다.
매수를 하게 되면 보유 종목에 조회 되기 때문에 자동 매도 프로그램이 매도하기 전까지는 재 매수하지 않고 매도된 경우에는 매수 타이밍에 맞는 경우 다시 매수하게 됩니다.
마치며
위에 알려드린 코드를 복사하여 붙여 넣으신 후 공통 모듈의 Key 부분을 발급 받은 Key로 대체한 후 수행하면 오류 없이 수행이 될 것 같습니다.
매수 타이밍에서도 캔들 기준을 15분 또는 5분, 3분으로 낮추거나 RSI, MFI, MACD OCL 기준점 변경하게 되면 매수 타이밍을 더 자주 잡아 공격적인 매수/매도를 할 수 있습니다. 하지만 소액으로 충분한 테스트를 거치고 본인만의 로직을 세운 후 진행 하시는 것을 권장 드립니다.
다시 한번 강조 드리지만 저희 블로그에서는 투자를 권유하거나 수익을 보장하지 않으며 자동 매매 프로그램을 샘플 형식으로 만들어서 참고를 위해 올려 드리고 있습니다. 모든 투자에 대한 책임은 본인에게 있음을 인지하시고 항상 신중한 거래를 하시기를 바라겠습니다.
그리고 앞으로도 프로젝트를 진행하는 과정에서 추가되는 로직이나 수정되는 로직이 발생할 수 있으니 공통 모듈은 가급적이면 카테고리내의 최근 포스팅에 올린 내용을 참고하시는 것이 좋을 것 같습니다.
이번 포스팅은 여기서 마치도록 하겠습니다. 블로그를 구독하시면 소식을 조금 더 빨리 받아 보실 수 있습니다. 감사합니다.
반응형
hyeon9698/upbit_bot: 비트코인, 알트코인 자동 매매 프로그램 (업비트용), 변동성 돌파 전략을 이용한 가상화폐, 업비트 자동 매매 프로그램
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
키워드에 대한 정보 업 비트 프로그램
다음은 Bing에서 업 비트 프로그램 주제에 대한 검색 결과입니다. 필요한 경우 더 읽을 수 있습니다.
이 기사는 인터넷의 다양한 출처에서 편집되었습니다. 이 기사가 유용했기를 바랍니다. 이 기사가 유용하다고 생각되면 공유하십시오. 매우 감사합니다!
사람들이 주제에 대해 자주 검색하는 키워드 누구나 할 수 있는 비트코인 투자 자동화 강의 시작합니다
- 파이썬
- 비트코인
- 자동매매
- 자동투자
- 암호화폐
- 투자자동화
- 투자프로그램
- 프로그램매매
- 업비트
- 코인
- 비트코인 자동매매
- 코인 자동매매
누구나 #할 #수 #있는 #비트코인 #투자 #자동화 #강의 #시작합니다
YouTube에서 업 비트 프로그램 주제의 다른 동영상 보기
주제에 대한 기사를 시청해 주셔서 감사합니다 누구나 할 수 있는 비트코인 투자 자동화 강의 시작합니다 | 업 비트 프로그램, 이 기사가 유용하다고 생각되면 공유하십시오, 매우 감사합니다.