* 프로그램 요청이나 산출물 요청은 댓글로 달아주세요!

시현입니다. 아직 미완인데, 귀차니즘으로 잠깐 정지해두고 다른 공부하다 오려그럽니다. 일전에 스크립트를 포스팅하였으나, 뭐하는 짓인가 싶어 비공개로 돌렸습니다.

프로그램은 사용자가 진입, 구매, 판매조건을 설정하여 그 조건에 맞게 트레이드로봇이 동작하는 구조입니다


MainWindow

기본 화면 구성은 다음과 같다.

위쪽에 menu들은 다음과 같은 하위 항목이 있다.

프로그램은 아래와 같이 tray_icon에 표시되며, 24시간 돌아가야 되는 특성상 X버튼을 눌러도 꺼지지 않고, main 화면만 invisible된다.


SubWindow

1. 거래키 Window

필자의 KEY가 기본으로 들어있어 지우고 사진을 올렸다. 로컬파일인 key.dat을 로딩해서 보여준다.

2. 전체 잔고 확인 Window

가지고 있는 코인을 보여주며, 우클릭으로 판매할 수 있다.(어... VTHO쟨 뭐지?)

3. 시뮬레이터

과거 데이터를 기준으로 구매/판매 조건이 어떤 결과를 가져올지 시뮬레이팅하는 기능이다. 당연히 과거데이터가 한정되어있어 약간의 차이가 존재한다.(upbit에서 ohlcv를 길게 요구하면 중간중간 빠지는 경우가 있더라)

(아... 이거 만들겠다는 생각만 안했으면,... 차라리 안정성이나 늘려두지...)

조건생성을 클릭하면 다음과 같이 구매 / 판매조건을 걸어줄 수 있다. 옆에 [...]버튼을 눌러서 기존 조건을 가져올 수도 있다. 동작중인 조건은 ./criteria 폴더아래 [코인명]_[buy/sell]_criteria.dat로 저장되어 있다.

조건을 클릭하면 다음과 같이 편집할 수 있다.

과거데이터 덤프를 가져오는 화면은 아래와 같다. 궁금한 종목을 가져오고 [가져오기]버튼을 눌러 가져올 수 있다.

가져온 데이터는 ./dump 아래 [코인명].dump로 저장된다.

4. 진입 조건 Window

현재 진입조건으로는 2가지 경우를 만들어 두었다. 업데이트하고 조금 손보면 더 늘어날 예정이다. 글을 쓰는 현재 2분동안 -5%감소한 종목이 없는지 좀처럼 시작을 안해서 2일동안 5%감소한 항목으로 조건을 바꾸어 하나를 시작 시켰다.

짜잔.

저 한 행이 트레이딩봇 한 개이다. 우클릭하면 트레이딩 봇에 대한 동작 기록 확인 / 거래 조건 변경 / 강제 종료 / 일시정지등을 지시할 수 있다.

거래조건확인은 시뮬레이팅에서 본 그 화면과 동일하다. 단 window이름이 코인명으로 지정되어있는데, 글로벌 프리셋에서 복사되어 코인별로 조건을 따로따로 지정하는 동작이다. 현재는 거래가 안되게끔 현재가<현재가로 걸어두었다.

동작기록은 메모장으로 [코인명]_history.log가 열린다. 이 파일은 ./var/log 아래 일자별로 폴더가 생성된다.

마지막으로 강제종료하여 해당 트레이드 봇을 강제종료 시켰다.(...응? 이상한게 하나 시작되었다.)

5. 설정변경 window

프로그램의 동작 설정을 변경할 수 있는 window이다.


Q. 왜 잔고(KRW)가 0인데 시작하나요?

A. 프로그램 테스트 용도로 가짜 돈이 조금 들어가 있습니다.

Q. 남은 개발항목은 어떤게 있나요?

A. 시뮬레이팅 결과화면이 아직 덜 준비되었습니다. 그 외 프로그램의 사용기간 제한이나, 제작자에게 e-mail건의 등을 하는 기능이 미구현상태이고, 전체적으로 안정성을 늘려야합니다. 디버깅없이 열심히 삘받아서 코딩한거라 잔 오류가 조금씩 보이네요. 가능하다면 UI도 조금 꾸미고 싶어요.

Q. 자문자답 쪽팔리지 않으세요?

A. 네

 

코드구조

  • class cleaner
    • clean_history() : 동작기록 삭제, bitmain에서 호출되며, 과거 기록 삭제에 사용
  • class key_loader
    • key_set() : 로컬파일 key.dat에서 key 불러옴 [acc,sec]형태로 반환 이거 안씀 왜만들었지?
    • key_save() : [acc,sec]형태로 파라미터를 받아 key.dat update
    • key_loader() :  로컬파일 key.dat에서 key 불러옴 [acc,sec]형태로 반환
  • class config_loader
    • loading() : config.dat에서 설정 복사해서 config(dictionary)생성, 반환
    • config_save() : config(dictionary) 파라미터로 받아 config.dat update
    • get, get_config() : 안씁니다. 초기 디버그용
  • class history_maker
    • init_history() : 해당 트레이드봇의 동작로그에 맨처음 적을 말 씁니다.
    • write_history() : 해당 트레이드봇의 동작로그 기록
    • load_history() : 해당 트레이드봇의 동작로그 불러옴
    • get_history_list() : 트레이드봇의 동작기록들 목록 로딩
    • open_history() : 해당 트레이드봇의 동작기록 열기(메모장으로) 
  • class fomula
    • read_raw_buy_parser() : 트레이드봇의 구매조건 읽어옴 raw_parser로 리스트 생성 후 반환
    • read_raw_sell_parser() : 트레이드봇의 판매조건 읽어옴 raw_parser로 리스트 생성 후 반환
    • raw_parser() : 위 두함수로 읽어온 결과 한줄한줄씩 해석가능 리스트로 바꾸어줌
    • read_buy_parser() : 트레이드봇의 구매조건을 읽어와 람다식 함수 리스트생성
    • read_sell_parser() : 트레이드봇의 판매조건을 읽어와 람다식 함수 리스트생성
    • make_fomula() : 아래 make_fomula_sentense을 한줄한줄 read_XX_parser로 읽어온 리스트에 적용
    • make_fomula_sentense() : read_XX_parser로 가져온 한줄에 대한 람다식 생성
    • raw_to_fom() : _criteria.dat에 저장가능한 형태로 변경
    • save_config() : 변경된 설정 저장
    • get_fomula() : 사용가능한 함수반환
    • get_operatator() : 사용가능한 연산자반환
    • get_fom_type() : fomula에 사용가능한 파라미터 수 리스트로 반환
  • init_fomula
    • read_raw_data() : 초기 진입 조건 _init_criteria.dat 읽어옴, list로 반환
    • save_init() : 초기 진입 조건 _init_criteria.dat 저장함
    • criteria_list() : 어 이거 왜 만들었지?

 


코드

### local_loader.py
### 21. 6. 28. add class local_loader, func gey_key()
### 21. 7. 3.  add umm... many class, func

import fileinput
import datetime
import sys
import os
import subprocess
import shutil
from algo_util import *

######################################################
###                                                ###
### This class is to clean previous log            ###
###                                                ###
######################################################
class cleaner :

    def clean_history() :
        pwd = os.getcwd()
        today=datetime.datetime.now()
        now = str(today.year)+"_"+str(today.month)+"_"+str(today.day)
        try :
            os.mkdir("./bot_history/"+now)
        except FileExistsError as e:
            print(e)
        os.chdir("./bot_history/")
        filelist = [ x for x in os.listdir() if ".log" in x ]
        for file in filelist :
            try :
                shutil.move(file,"./"+now)
            except shutil.Error as e:
                shutil.copy(file,"./"+now)
                os.remove(file)
                print(e)
        os.chdir(pwd)

######################################################
###                                                ###
### This class is to handle user's trading key     ###
###                                                ###
######################################################
class key_loader :

    def key_set() :
        with open("./key.dat","r") as f:
            data = f.read().split("\n")
            acc = data[0].split("=")[1].strip()
            sec = data[1].split("=")[1].strip()
        return [acc,sec]

    def key_save(value) :
        with fileinput.FileInput("./key.dat",inplace = True) as f:
            for line in f :
                if("acc" in line) :
                    line = line.replace(line,"acc="+value[0])
                elif("sec" in line) :
                    line = line.replace(line,"sec="+value[1])

    def key_loader() :
        ret = list()
        with open("./key.dat","r") as f:
            data = f.read().split("\n")
            for i in range(len(data)) :
                if("acc=" in data[i]) :
                    ret.append(data[i].split("=")[1])
                elif("sec=" in data[i]) :
                    ret.append(data[i].split("=")[1])

        if(len(ret)!=2) :
            log_maker.write_error_log("failed to load key")
        else :
            return ret
        
######################################################
###                                                ###
### This class is to handle config                 ###
###                                                ###
######################################################   
class config_loader :

    def loading() :
        config = dict()
        with open("./config.dat","r") as f:
            data = f.read().split("\n")

            for i in range(len(data)) :
                if(len(data[i])<3) :
                    continue
                if(data[i][0]=="#" or data[i]=="") :
                    continue
                else :
                    try :
                        data_s = data[i].split("=")[1].strip()
                        if(float(data_s) != int(data_s)) :
                            config[data[i].split("=")[0]] = float(data_s)
                        else :
                            config[data[i].split("=")[0]] = int(data_s)
                    except ValueError :
                        config[data[i].split("=")[0]] = data[i].split("=")[1].strip()

        return config

    def get(self,wh) :
        if(wh in self.config) :
            return self.config[wh]

    def config_save(cd) :
        setter = list(cd.keys())
        with fileinput.FileInput("./config.dat",inplace=True,backup=".bak") as f:
            for line in f :
                set_flag = False
                for i in range(len(setter)) :
                    if(setter[i] in line) :
                        set_flag = True
                        line = line.replace(line,setter[i]+"="+str(cd[setter[i]]))
                        sys.stdout.write(line+"\n")
                        del setter[i]
                        break
                if(set_flag==False) :
                    sys.stdout.write(line)
                if(len(setter) == 0 ) :
                    break


    def get_config() :
        config = dict()
        with open("./config.dat","r") as f:
            data = f.read().split("\n")

            for i in range(len(data)) :
                if ("=" in data[i]) :
                    config[data[i].split("=")[0].strip()] = data[i].split("=")[1].strip()

        return config
        
######################################################
###                                                ###
### This class is to write running log             ###
### error log writer is in utility.py              ###
###                                                ###
######################################################
class history_maker :

    def init_history(fiat) :
        now = datetime.datetime.now()
        try :
            with("./bot_history/"+fiat+"_history.log","w") as f:
                f.write("--------"+fiat+"--------\n")
                f.write(str(now)+"\n\n")
        except FileNotFoundError as e:
            os.mkdir("./bot_history")
            self.init_history(fiat)

    def write_history(fiat,string) :
        now = datetime.datetime.now()
        try :
            with open("./bot_history/"+fiat+"_history.log","a") as f :
                f.write("<"+str(now)+"> "+string+"\n\n")
        except FileNotFoundError as e:
            print(e)

    def load_history(fiat) :
        with open("./bot_history/"+fiat+"_history.log","a") as f:
            data = f.read().split("\n")
        return data

    def get_history_list() :
        list_tmp = [ x for x in os.listdir("./bot_history/") if x.endswith(".log")]
        for i in range(len(list_tmp)) :
            list_tmp[i] = list_tmp[i].split("_")[0]
        return list_tmp

    def open_history(fiat) :
        subprocess.Popen("notepad.exe ./bot_history/"+fiat+"_history.log")


######################################################
###                                                ###
### This script is for parse criteria              ###
### interact with algo_util and trading_bot        ###
### fomula express as form's of                    ###
### EX> A/a M B/b (& another fom)                  ###
###                                                ###
######################################################
class fomula :

    fomula_dict = {"0":"현재가","1":"최소가","2":"최대가","3":"평균가","4":"이동평균가","5":"마지막구매가","6":"마지막판매가"}
    fom_type_dict = {0:[],1:["0","1","2","3","5","6"],2:["4"]}
    operator_dict = {"1":"<","2":"<=","3":">","4":">=","5":"==","6":"!="}

    def read_raw_buy_parser(fiat="") :

        buy_criteria = list()
        
        with open("./criteria/"+fiat+"_buy_criteria.dat","r") as f:
            data = f.read().split("\n")
            for i in range(len(data)) :
                if(data[i].strip() == "") :
                    continue
                elif(data[i][0]=="#") :
                    continue
                else :
                    buy_criteria.append(fomula.raw_parser(data[i].split(":")[1]))
                                        
        return buy_criteria

    def read_raw_sell_parser(fiat="") :

        sell_criteria = list()
        
        with open("./criteria/"+fiat+"_sell_criteria.dat","r") as f:
            data = f.read().split("\n")
            for i in range(len(data)) :
                if(data[i].strip() == "") :
                    continue
                elif(data[i][0]=="#") :
                    continue
                else :
                    sell_criteria.append(fomula.raw_parser(data[i].split(":")[1]))
                                        
        return sell_criteria


    def raw_parser(line) :
        res = list()
        tmp_line = line.split("&")
        for i in range(len(tmp_line)) :
            apt_list = list()
            tmp  = tmp_line[i].strip()
            a1,fo,a2 = tmp.split(" ")
            if("/" in a1) :
                a1_args = a1.split("/")[1:]
                a1 = a1.split("/")[0]
                apt_list.append(fomula.fomula_dict[a1])
                apt_list.append(a1_args)
            else :
                apt_list.append(fomula.fomula_dict[a1])
                
            apt_list.append(fomula.operator_dict[fo])
            
            if("/" in a2) :
                a2_args = a2.split("/")[1:]
                a2 = a2.split("/")[0]
                apt_list.append(fomula.fomula_dict[a2])
                apt_list.append(a2_args)
            else :
                apt_list.append(fomula.fomula_dict[a2])
    
            
            res.append(apt_list)
        return res
        

    def read_buy_parser(fom_algo,arg,fiat="") :

        buy_criteria = list()
        
        with open("./criteria/"+fiat+"_buy_criteria.dat","r") as f:
            data = f.read().split("\n")
            for i in range(len(data)) :
                if(data[i].strip() == "") :
                    continue
                elif(data[i][0]=="#") :
                    continue
                else :
                    buy_criteria.append(fomula.make_fomula(data[i].split(":")[1],fom_algo,arg))            
        return buy_criteria

    def read_sell_parser(fom_algo, arg, fiat="") :    
        sell_criteria = list()
        with open("./criteria/"+fiat+"_sell_criteria.dat","r") as f:
            data = f.read().split("\n")
            for i in range(len(data)) :
                if(data[i].strip() == "") :
                    continue
                elif(data[i][0]=="#") :
                    continue
                else :
                    sell_criteria.append(fomula.make_fomula(data[i].split(":")[1],fom_algo,arg))
                                        
        return sell_criteria

    def make_fomula(fom,fom_algo,arg) :

        fom = fom.split("&")
        fom_list = list()

        for i in range(len(fom)) :
            fom_list.append(fomula.make_fomula_sentence(fom[i],fom_algo,arg))

        return fom_list

    def make_fomula_sentence(fom,fom_algo,arg) :
        fom = fom.strip()
        a1,fo,a2 = fom.split(" ")

        buy_money = arg["buying"]
        sell_money = arg["selling"]

        a1_args = [0,0]
        a2_args = [0,0]
        
        if("/" in a1) :
            a1_args = a1.split("/")[1:]
            a1 = a1.split("/")[0]
        if("/" in a2) :
            a2_args = a2.split("/")[1:]
            a2 = a2.split("/")[0]

        if(a1 == "0") :
            a1_foo = lambda x : fom_algo.current_val()
        elif(a1 == "1") :
            a1_foo = lambda x : fom_algo.min_val(x)*float((100-float(a1_args[0]))/100)
        elif(a1 == "2") :
            a1_foo = lambda x : fom_algo.max_val(x)*float((100-float(a1_args[0]))/100)
        elif(a1 == "3") :
            a1_foo = lambda x : fom_algo.avg_val(x)*float((100-float(a1_args[0]))/100)
        elif(a1 == "4") :
            if(a1_args[0] != 0) :
                a1_foo = lambda x : fom_algo.rolling_mean(x,a1_args[1])*float((100-float(a1_args[0]))/100)
            else :
                a1_foo = lambda x : fom_algo.rolling_mean(x)*float((100-float(a1_args[0]))/100)
        elif(a1 == "5") :
            a1_foo = lambda x : buy_money*float((100-float(a1_args[0]))/100)
        elif(a1 == "6") :
            a1_foo = lambda x : sell_money*float((100-float(a1_args[0]))/100)

        if(a2 == "0") :
            a2_foo = lambda x : fom_algo.current_val()
        elif(a2 == "1") :
            a2_foo = lambda x : fom_algo.min_val(x)*float((100-float(a2_args[0]))/100)
        elif(a2 == "2") :
            a2_foo = lambda x : fom_algo.max_val(x)*float((100-float(a2_args[0]))/100)
        elif(a2 == "3") :
            a2_foo = lambda x : fom_algo.avg_val(x)*float((100-float(a2_args[0]))/100)
        elif(a2 == "4") :
            if(a2_args[1]!=0) :
                a2_foo = lambda x : fom_algo.rolling_mean(x,a2_args[1])*float((100-float(a2_args[0]))/100)
            else :
                a2_foo = lambda x : fom_algo.rolling_mean(x)*float((100-float(a2_args[0]))/100)
        elif(a2 == "5") :
            a2_foo = lambda x : buy_money*float((100-float(a2_args[0]))/100)
        elif(a2 == "6") :
            a2_foo = lambda x : sell_money*float((100-float(a2_args[0]))/100)

        if(fo == "1") :
            fom = lambda x : True if a1_foo(x) < a2_foo(x) else False
        elif(fo == "2") :
            fom = lambda x : True if a1_foo(x) <= a2_foo(x) else False
        elif(fo == "3") :
            fom = lambda x : True if a1_foo(x) > a2_foo(x) else False
        elif(fo == "4") :
            fom = lambda x : True if a1_foo(x) >= a2_foo(x) else False
        elif(fo == "5") :
            fom = lambda x : True if a1_foo(x) == a2_foo(x) else False
        elif(fo == "6") :
            fom = lambda x : True if a1_foo(x) != a2_foo(x) else False

        return fom

    def raw_to_fom(raw) :
        output = ""
        
        if(raw==[]) :
            return ""
        
        rev_fomula = dict([(value,key) for key,value in fomula.fomula_dict.items()])
        rev_ope = dict([(value,key) for key,value in fomula.operator_dict.items()])
        for i in range(len(raw)):
            offset = 0
            output += rev_fomula[raw[i][0]]
            if(isinstance(raw[i][1], list)) :
                for j in range(len(raw[i][1])) :
                    output += "/"+raw[i][1][j]
                offset += 1
            output +=  " "
            output += rev_ope[raw[i][1+offset]] + " "
            output += rev_fomula[raw[i][2+offset]]
            if(isinstance(raw[i][-1],list)) :
                for j in range(len(raw[i][-1])) :
                    output += "/"+raw[i][-1][j]
            output += " & "
        output = output[:-3]
        return output


    def save_config(index,raw,bs_type,fiat="") :
        try :
            if(raw == []) :
                cnt = 1
                with fileinput.FileInput("./criteria/"+fiat+"_"+bs_type+"_criteria.dat",inplace=True) as f:
                    for line in f :
                        try :
                            find_index = line.split(":")[0].strip()
                            setter = line.split(":")[1].strip()
                        except Exception as e:
                            continue
                        if(str(index)+" :" in line) :
                            continue
                        line = line.replace(line,str(cnt)+" : "+setter)
                        cnt+=1
                        sys.stdout.write(line+"\n")
                return
            trans = str(index)+ " : " + fomula.raw_to_fom(raw)+"\n"
            setter = 0
            set_flag=False
            with fileinput.FileInput("./criteria/"+fiat+"_"+bs_type+"_criteria.dat", inplace=True) as f:
                for line in f :
                    setter = line.split(":")[0].strip()
                    if(str(index)==setter):
                        line = line.replace(line, trans)
                    sys.stdout.write(line)
                    set_flag=True
            if(int(setter)<index) :
                with open("./criteria/"+fiat+"_"+bs_type+"_criteria.dat","a") as f:
                    if(set_flag==True) :
                        f.write("\n"+trans)
                    else :
                        f.write(trans)
        except Exception as e:
            print(e)

        
    def get_fomula(self) :
        return self.fomula_dict

    def get_operator(self) :
        return self.operator_dict

    def get_fom_type(self) :
        return self.fom_type_dict
            

class init_fomula :

    def read_raw_data() :
        crit = list()
        with open("./criteria/_init_criteria.dat","r") as f:
            data = f.read().split("\n")
            for i in range(len(data)) :
                if(i == 0):
                    tmp_line = data[i].split(" ")
                    crit.append([tmp_line[0].strip(),[tmp_line[1].split("/")[0].strip(),tmp_line[1].split("/")[1].strip()],tmp_line[2].strip()])
                elif(i == 1) :
                    tmp_line = data[i].split(" ")
                    crit.append([tmp_line[0].strip(),[tmp_line[1].split("/")[0].strip(),tmp_line[1].split("/")[1].strip()]])
                
        return crit

    def save_init(res) :
        with fileinput.FileInput("./criteria/_init_criteria.dat", inplace = True) as f :
            cnt = 0 
            for line in f:
                if(cnt == 0) :
                    write_line = res[cnt][0]+" "+res[cnt][1][0]+"/"+res[cnt][1][1]+" "+res[cnt][2]
                    line = line.replace(line,write_line+"\n")
                    sys.stdout.write(line)
                elif(cnt == 1) :
                    write_line = res[cnt][0]+" "+res[cnt][1][0]+"/"+res[cnt][1][1]
                    line = line.replace(line,write_line+"\n")
                    sys.stdout.write(line)
                cnt+=1 

    def criteria_list() :
        with open("./criteria/_init_criteria.dat","r") as f:
            data = f.read().split("\n")
        return data

변경점

  • 21. 6. 28. add class local_loader, func gey_key()
  • 21. 7. 3. add many class(fomula, init_fomual, history_maker, config_loader,cleaner)

으흠; 다른 Python Project처럼 이것도 만들고 결과만 올리려고 했습니다만.... 만드는 건 끝났는데, 한번 따라 할 수 있는 글을 쓰고 싶어서 이렇게 STEP을 나누게 되었습니다. 이번에 만드는 프로그램은 사람마다 Trading Algorithm을 다르게 프리셋으로 다르게 설정할 수 있는 프로그램이라 이렇게 됐네요.

 

이번 시간에는 사용할 모듈 설치와 사전 준비를 합니다.. Autotrading API는 다른 곳도 전부 제공하나, 필자는 잘 알려진 upbit에서 진행하니 따라와 주시면 됩니다.


업비트 준비하기

1. https://upbit.com/

들어와서 메뉴 우측의 고객센터에 들어가면 하부 메뉴에 Open API가 있습니다. 클릭해서 Open API를 발급받으시면 됩니다.

* 사용기능은 어떻게 사용할 지에 따라 다르지만, 필자의 프로그램에서는 [입금하기] / [출금하기]를 제외한 모든 기능을 쓴다.

* 특정 IP에서만 실행하되, IP는 NAT를 쓰는 곳이 많으니 구글에 [what is my ip?]를 검색하고 그 IP를 쓰면 된다.

 

2. 키를 발급받고 access_key랑 secret_key를 보여주는데, 이를 복사해서 메모장에 다음과 같이 저장하시면 됩니다.(설마 아직 아무 폴더도 안 만든 사람은 작업 폴더 하나를 만들고./config/key.dat으로 저장 부탁드립니다.)

access_key : [자신의 access_key]
secret_key : [자신의 secret key]


## 파일이름은 대충 key.dat로 해주세요. 이 줄은 주석이니 적지않으셔도 됩니다.

 

현재 우리의 폴더는 위와 같이 되어 있으면 됩니다.


모듈 준비

1. 우리가 사용할 모듈은 pyupbit라는 사전에 upbit API를 쓰기 편하게 정의된 모듈인데 아래와 같이 다운로드하여주세요

pip3 install pyupbit

 

pyupbit의 간단한 기능들을 설명하겠습니다. 사용할 함수들만 설명합니다.

  • quotation_api.py
    • get_tickers(fiat="ALL") : 거래 가능한 종목을 가져옵니다.
    • get_ohlcv(ticker="KRW-BTC", interval="day", count=200) : 지정한 종목의 ohlcv차트를 가져옵니다.
    • get_current_price(ticker="KRW-BTC") : 지정한 종목의 현재가를 가져옵니다.
    • get_orderbook(tickers="KRW-BTC") : 지정한 종목의 호가 차트를 가져옵니다.
  • exchange_api.py
    • Upbit(access, secret) : class, UPBIT와 상호작용을 위한 객체입니다
      • get_balances(contain_req=False) : 전체 잔고를 가져옵니다.
      • get_balance(ticker="KRW", contain_req) : 지정한 종목의 잔고를 가져옵니다.
      • get_avg_buy_price(ticker="KRW") : 지정한 종목의 매수 평균가를 가져옵니다.
      • get_amount(ticker) : 특정 종목의 매수금액을 가져옵니다.
      • buy_limit_order(ticker,price,volume,contain_req = False) : 지정한 종목을 지정가 매수합니다
      • buy_market_order(ticker,price) : 지정한 종목을 시장가 매수합니다.
      • sell_limit_order(ticker,price,volume,contain_req = False) : 지정한 종목을 지정가 매도합니다.
      • sell_market_order(ticker,volume) : 지정한 종목을 시장가 매도합니다.
      • cancel_order(uuid) : 주문을 취소합니다.
      • get_order(ticker, state='wait',kind='normal',contain_req=False) : 주문 리스트를 조회합니다.

이정도 사용합니다.


잘들 따라오고 계신지요.. 어쩌면 재미삼아서 읽으시는 분도 계시겠지만, 끝까지 따라와 주시면, 유의미한 프로그램과, Python 지식을 얻으실 수 있으실 겁니다.

* 프로그램 요청이나 산출물 요청은 댓글로 달아주세요!

 

기다리고 기다리던 이상형 월드컵 나만의 UI의 시현 시간이다. 개발정보는 -계획,진행-편에 들어있다.

2021.06.27 - [Python/Python Project] - [Python Project] 이상형 월드컵 나만의 UI만들기 - 계획,진행-

 

[Python Project] 이상형 월드컵 나만의 UI만들기 - 계획,진행-

아니; 비트코인 안 만드시는 건가요??;; 기다려주시라, 안하고 있는 게 아니라 다른 프로그램 만들다가 이게 더 빨리 돼서 이걸 올리는 거다. 사실 글을 쓰고 있는 지금 다 만들었

tutoreducto.tistory.com


디렉토리 구조

이렇다. 코드 각각의 설명은 -기능도-편을 참고하자

2021.06.27 - [Python/Python Project] - [Python Project] 이상형 월드컵 나만의 UI 만들기 - 기능도-

 

[Python Project] 이상형 월드컵 나만의 UI만들기 - 기능도-

코드별 클래스, 함수명에 대한 설명이다.

tutoreducto.tistory.com


시현

이 프로그램은 Selenium을 이용한 크롤링을 기반으로 한다. 따라서 headless모드를 통한 브라우저의 open이 프로그램 시작의 효시가 된다.

<Selenium Driver Open>

프로그램 화면은 다음과 같다.

직관적으로 구성하기는 했는데; 굳이 구조적인 설명을 붙이면 다음과 같다.

우선 제외될 Keyworld는 제외 테이블에 들어간다. 들어가는 순간 해당 Keyword를 포함하지 않도록 월드컵 Table은 갱신된다.

(난 남자와 여자 둘 다 싫다)

이동작은 물론 동일하게 포함 테이블에도 동일하다.

아래 [더보기] 버튼과 설정 창의 [불러올 월드컵 개수(X10)]에서 월드컵을 더 불러올 수 있다.(아 그러게 아예 처음부터 다 가져오게 할걸;) 이상형 월드컵 홈페이지는 load_more이라는 자바 함수로 월드컵을 추가로 불러온다. 불러올 월드컵 개수만큼 브라우저 driver에서 반복해서 javascript를 실행한다. 

불러온 결과는 다음과 같다.

열기 버튼을 통해서 바로 해당 월드컵으로 이동할 수 있다.


후기

이상형 월드컵 새벽에 하다가 화나서 급하게 만든 프로그램이다. 앞으로 놀 때 QThread를 안 쓰고 Threading 내장 모듈로 처리했는데, QThread 써보니까 좋더라 앞으로 애용해야 되겠다. 추가로 이 프로그램은 배포를 고려하고 있다..... 이상형 월드컵 사이트 홈페이지에 상당히 많은 광고도 있고, 사실 Selenium으로 접속하려니까 cLoudFlare에 한번 걸린 걸 보니까 bot의 접근을 탐탁지 않아하는 거 같다.(그러기엔 robots.txt는 Allow / 이긴 한데...) 그렇다. 이제 비트코인 프로그램 만들러 가야 되겠다.

코드별 클래스, 함수명에 대한 설명이다.

아니; 비트코인 안 만드시는 건가요??;;
기다려주시라, 안하고 있는 게 아니라 다른 프로그램 만들다가 이게 더 빨리 돼서 이걸 올리는 거다.
사실 글을 쓰고 있는 지금 다 만들었는데, 글을 올리는 템플릿을 따르고자 올린다. 이상형 월드컵을 알고 있는가,

https://www.piku.co.kr/


요즘 인터넷 각지에서 유명한 놀이 사이트이다. 2의 승수 컨텐츠를 첨부하고 누가 좋은지 겨루는, 시간 때우기에 최적화된 사이트인데, 필자도 친구랑 새벽에 하다가......

 

으;; 올리는 컨텐츠에 제한이 없는 건지, 필터가 없는 건지 무슨 페티시나 "Best XX Girls"이런 월드컵이 너무 많다.(너무 좋다.) 남들이랑 같이 보고 있기에는 ①조금 자극적이라는 생각이 들었고, 내리는데 시간이 너무 많이 걸려 ②버려지는 시간이 많다는 생각이 들어 한번에 크롤링하는 코드를 만들고자 이렇게 프로그램을 만들었다.


프로그램 개발 정보는 다음과 같다.

  • 목표 완성 시기 : '21. 6. 27.(일)
  • 형태 : PE(exe, GUI)
  • 사용언어 : Python
  • 개발툴 : IDLE, PyCharm
  • 사용 라이브러리 : Selenium, BeautilfulSoup4, PyQt5
  • 주요 기능
    • 이상형 월드컵 목록 가져오기
    • 추가 / 제외할 키워드 관리(추가 / 제거)
    • 설정 프리셋

+ Recent posts