2. 해외선물/2-6. 기타자료 (파이썬 함수 등)

(파이썬) 엑셀 내용을 파이썬에서 출력하기 (5) 엑셀 데이터를 진입 알고리즘 코드 만들기

봄이오네 2023. 11. 17. 08:06
반응형

 

목 차
1. 들어가며
2. 사전설명
   1) 파이썬 기본코드
   2) 리스트의 pop함수는 요소를 삭제한다.
   3) 임의로 만든 테스트용 엑셀파일
3. 코드설명
4. 전체코드
5. 마치며

 
 

1. 들어가며

지난글에서는 엑셀의 특정열의 갯수를 세어보고, 빈칸을 없애는 코드를 작성해 보았다. 튜플형을 리스트 태형으로 바꾸고 for문으로 돌리니 생각보다 수월하게 코드를 작성할 수 있었다.
 
이번글에서는 엑셀 데이터를 진입 알고리즘 코드로 만드는 방법을 알아볼 것이다. 진입 알고리즘에 대한 코드라고 말하니 조금 거창하긴 하다. 여기서 주의할 점은, 사용자 본인이 "진입 알고리즘"의 내용(컨텐츠)는 당연히 만들었다는 가정 하에 코드를 설명할 것이다. 꾸준히 엑셀을 돌릴 필요가 있다. 이 글을 쓰고 있는 필자도, 자동매매 시스템을 돌리고 끊임없이 코드의 내용(알고리즘)을 찾고 있다.
 
이전 글들에서 대부분 설명하였지만, 여기서는 진입 알고리즘이 어떻게 이루어지는지 살펴볼 것이다. 다시한번 말하지만, 필자는 알고리즘의 구성 형태(코드)만 이야기 할 뿐, 어떤 숫자에서 수익(알고리즘의 컨텐츠)이 나는지는 제대로 알지 못하기 때문에 그 부분(컨텐츠)에 대한 설명은 못할 것 같다.
 
진입 알고리즘은 여러조건으로 이루어진다. 해외선물 거래를 너무 쉽게 생각했을 때가 있다. rsi 가 30이하일 때 long 진입으로 설정해서, 잔인할 정도의 손실을 입은 적도 있다. 역시 주식/해선의 이론은 이론일뿐, 실전에서는 통하지 않는구나.
 
진입 알고리즘의 조건은 사용자가 설정하는 것에 따라 다르다. 필자만 하더라도, 8~10개의 조건을 설정하고 코드를 돌린다. 그렇게 하더라도 손실이 나니... 정말 해외선물 자동매매는 어렵게 느껴지기만 하는구나. ㅠㅠ
 


2. 사전설명

1) 파이썬 기본코드

파이썬에서 if문 활용을 위해 코드에 대해 이야기를 먼저 해보자.
 
첫번째, if문을 쓴다면, if 단독으로도 쓸 수 있지만, elif를 사용하여 if를 만족하지 않을때, elif를 검토하게 해준다. if문의 장점은 if가 충족하여 검토를 하면, elif문은 검토를 하지 않는 것이 장점이다.
 
두번째, "크거나 같다"는 부동호(<=)를 활용한다.
 
세번째, 2개의 조건을 쓸 때는 and를 써준다. 여러 조건을 걸 때, 코드가 길어지면 다음줄로 넘어간다. 이때 엔터(enter)을 눌러주면 역슬레시(\)가 자동으로 생긴다.
 
마지막으로, if문이나 elif 문의 조건이 마무리될 때는 콜론(:)을 붙여준다.
 
막상 글로 써놓고 읽어보니, 왜 이리 어렵게 글을 썼을까 싶다. 아래 < 코드1 >을 글로 써둔 것이니... 차라리 < 코드1 >을 보는게 이해하기 더 빠를 것이다. ^^

if 58 <= self.current_rsi_10 <= 64 and \ 
	-20 <= self.close_price_180_ago - self.close_price_90_ago <= 10 :

코드1. if문의 기본형태에 대한 내용이다.

 
무엇을 설명하기 위해서 이렇게 장황하게 설명한 것인가? 위의 내용을 요약한다.

elif_word = "elif"
larger_than = "<="
and_word = "and \ " # 슬레시 바로 뒤에 "스페이스 바"를 1번 눌러준다. 슬레시 바로 뒤에 바로 쌍따옴표(")를 붙이니, EOL while scanning string literal 에러 발생
semicolon_word = ":"

코드2. if문의 구성 요소를 문자화하여 변수에 담았다.

 
 

2) 리스트의 pop함수는 요소를 삭제한다.

리스트의 pop함수는 리스트 내 위치한 요소를 삭제한다.

aaa = [1,2,3,4,5]
print(aaa)

aaa.pop(0)
print(aaa)


### (expected result) ###
### [1, 2, 3, 4, 5]
### [2, 3, 4, 5]

코드3. 내장함수인 pop을 활용하여 리스트 내 첫번째 요소를 삭제할 수 있다.

 
 

3) 임의로 만든 테스트용 엑셀파일

아래 파일은 필자가 임의로 만든 알고리즘 내용이다.

excel_test_2.xlsx
0.01MB

 
 
< 그림1 >은 위 엑셀 파일의 내용이다.

그림1. 임의로 만든 알고리즘

 
 


3. 코드설명

< 그림1 >에서는 10분전의 rsi 및 "120분전 종가 - 30분전 종가" 등의 조건 등을 기재한 내용이다. 앞의 조건에 따라 파이참에서 출력되는 결과를 아래 < 그림2 >에서 설명하도록 한다.
 

그림2-1. 파이참에서 패턴을 만들기 위해 데이터를 받아온다.

 
1줄~4줄 : 활용할 라이브러리는 openpyxl이고, 엑셀의 경로 등을 설정한다.
9줄~12줄 : if 문에서 활용될 내용 등을 정의한다.
 
15줄 : < 그림1 >의 C열의 데이터를 받기 위한 리스트를 선언한다.
16줄 : C열의 내용을 리스트(gubun_num_excel)에 담는다.
17줄~18줄 : for문을 통해 16줄의 리스트(C열의 내용)을 하나씩 뽑아낸다.
19줄~20줄 : 빈칸이 아닐 때, 15줄의 리스트에 추가(append)한다.
 
22줄 : C열(연번)을 삭제(pop)한다.
24줄~25줄 : 20줄에서 추가된 C열의 데이터 중 최대값(2)과 최소값(1)을 각각 변수에 담는다.
 

그림2-2. for문을 통해 엑셀의 데이터로 롱진입 패턴의 조건문 등을 만든다.

 
30줄 이후로 필자도 약간 헤맸다. 엑셀 데이터로 파이참 출력에 관심이 있다면, 여기서부터 집중해서 코드를 살펴보자.
 
30줄 : 20줄에 의해 C열의 리스트(gubun_num_list)에는 [1,1,1,1,2,2,2,2]가 들어있다. 최소값은 1(min_number_1)이고 최대값은 2(max_number_1)이 된다. for문은 끝수가 들어가 않으므로, for문의 range 앞에 max_number_1에 +1을 더한다. 우리가 활용할 숫자는 20줄 리스트(gubun_num_list)의 min_number_1의 최소값 1과 mxa_number_1의 최대값 2이다.
 
32줄 : i가 1일때, 리스트.count(1)을 통해 1의 갯수를 구한다. 30줄에서 1의 갯수는 4개인 것을 확인한다.
 
35줄 : i가 1일때, 리스트.index(1)의 위치를 알아본다. 20줄 gubun_num_list = [1,1,1,1,2,2,2,2]일 때, 1의 파이참의 리스트에서 위치값은 0, 1, 2, 3이다. 20줄 gubun_num_list = [1,1,1,1,2,2,2,2]에서 첫번째 1의 위치는 파이참에서 0인데 반해, < 그림2-3 >에서 C열의 첫번째 1은 C5자리, 즉 5행 위치한다. 한마디로 35줄은 조정계수(=엑셀과 파이참의 리스트 번호를 일치)라고 생각하면 된다. 35줄에서 20줄 리스트의 첫번째 1은 0자리에 있으므로, cnt_list_special_num_start = 5이다.
 
38줄 : i가 1일때, 35줄에서 cnt_list_special_num_start = 5이며, 32줄에서 1의 갯수는 4이었으므로, i가 1일때 1의 끝자리 위치는 9(5+4)로 설정한다. 실제로 < 그림2-3 >에서 1은 8행까지만 있고, 9행부터는 2가 시작되는데, 41줄 for문의 range 안에는 끝자리(9)가 들어가지 않는다.

그림2-3. C열에서 첫번째 1은 5행에 있다.

 
41줄 : 31줄의 i가 1일때, for문을 다시 써보자. for i in range(4, 9)로 써진다. range 안의 끝수(9)는 for문에 포함되지 않으므로 엑셀의 4줄~8줄까지를 의미한다.
 
42줄 : 여기서 엑셀의 R1C1 셀주소의 내용이 나온다. 41줄에서 j는 4~8까지으므로, j=4일 때, (4, 4)셀이 "롱"이면,
43줄 : j가 4이면,
44줄~48줄 : 44줄에서 패턴의 번호를 참고로 적고, elif 구문을 작성한다.
49줄~51줄 : j가 시작값(5행)위치 및 "끝값(8)의 위치-2" 사이에 있으면, 50줄~51줄의 내용을 출력하라.
53줄~55줄 : "끝자리-1"이면, if 문을 마무리(콜론 :)하라.
57줄~58줄 : 원래는 주문(sendorder)이 쓰여야 하나, 내용이 길어지는 관계로 여기서는 print문을 대신 써주었다.
 

그림2-4. 숏진입 패턴의 조건문 등을 만든다.

 
60줄 : j가 4일때, R1C1의 (9,4)셀이 숏이면
61줄~73줄 : if 조건물을 기재하고
75줄~76줄 : short 주문하라는 내용을 print문으로 출력한다.
 

### (롱패턴17-1-1) rsi 관련(롱) (5/10=50%)
elif 58 <= self.current_rsi_10 <= 64 and \ 
	-20 <= self.close_price_120_ago - self.close_price_30_ago <= 10 and \ 
	52 <= self.current_rsi_1 <= 61 :
	None


### (숏패턴17-2-1) rsi 관련(숏) (4/12=33.3%)
elif 67 <= self.current_rsi_10 <= 75 and \ 
	5 <= self.close_price_120_ago - self.close_price_30_ago <= 60 and \ 
	72 <= self.current_rsi_1 <= 75 :
	print("long 진입")

코드4. 조건문을 엑셀을 통해 만들었다.

 


4. 전체코드

아래 < 접은글 >은 전체코드를 나타낸다. 여유가 될 때 한번 쭈욱 숙지해보도록 하자.
 

더보기
import openpyxl

dir = r'C:\Users\User\Desktop\excel_test_2.xlsx'  # 경로 설정

wb = openpyxl.load_workbook(dir)    # 엑셀파일 열기
ws = wb.active                      # 현재 활성화된 시트 얻기 (마지막 저장된 시트)

##################################################
elif_word = "elif"
larger_than = "<="
and_word = "and \ " # 슬레시 바로 뒤에 "스페이스 바"를 1번 눌러준다. 슬레시 바로 뒤에 바로 쌍따옴표(")를 붙이니, EOL while scanning string literal 에러 발생
semicolon_word = ":"

########## 엑셀의 연번을 리스트형으로 받기 ############
gubun_num_list = []
gubun_num_excel = ws['c']                  # 엑셀에서 받은 최초의 데이터는 "튜플"형이다.
for i in gubun_num_excel:
    i = i.value
    if i != None:       # 엑셀의 빈칸은 파이참에서 None으로 출력 ("None"가 아니다. 따옴표를 붙이면 안된다)
        gubun_num_list.append(i)    # 엑셀의 셀 "내용(값)"만 리스트에 담기 위함

gubun_num_list.pop(0)       # '연번' 삭제 (pop 함수는 인덱스 삭제)

min_number_1 = min(gubun_num_list)
max_number_1 = max(gubun_num_list)
# print(max_number_1)
# print(min_number_1)


###########################################################################################
for i in range(min_number_1, max_number_1 + 1):    # 1~4 (5)
    cnt_list_special_num = gubun_num_list.count(i)
    # print(cnt_list_special_num)

    cnt_list_special_num_start = gubun_num_list.index(i) + 1 + 4        # 1을 더한 이유는 파이썬 첫자리는 0, 엑셀 첫자리는 1이므로 +1 // +4를 한 이유는 엑셀의 빈칸 고려(4칸이 비었음)
    # print(cnt_list_special_num_start)

    cnt_list_special_num_end = cnt_list_special_num_start + cnt_list_special_num
    # print(cnt_list_special_num_end)

    for j in range(cnt_list_special_num_start, cnt_list_special_num_end):
        if ws.cell(cnt_list_special_num_start, 4).value == "롱":  # 롱/숏 구분
            if j == cnt_list_special_num_start:
                print("###" + " " + "(" + str(ws.cell(j, 4).value) + "패턴" + str(ws.cell(j, 5).value) + ")" + " " + str(ws.cell(j, 12).value)
                      + " " + "(" + str(ws.cell(j, 10).value) + "/" + str(ws.cell(j, 9).value)
                      + "=" + str(ws.cell(j, 11).value) + "%)")  ### 결과값은 '함수식'이 아닌, "(엑셀에서 계산된 후의) 결과값'만 넣을 것(수식이 복사됨)
                print(elif_word + " " + str(ws.cell(j, 6).value) + " " + larger_than + " " + str(ws.cell(j, 8).value) +
                      " " + larger_than + " " + str(ws.cell(j, 7).value) + " " + and_word)  # j=7
            elif cnt_list_special_num_start < j < cnt_list_special_num_end-2:
                print("\t" + str(ws.cell(j, 6).value) + " " + larger_than + " " + str(ws.cell(j, 8).value) + " " +
                      larger_than + " " + str(ws.cell(j, 7).value) + " " + and_word)

            elif j == cnt_list_special_num_end-1:
                print("\t" + str(ws.cell(j, 6).value) + " " + larger_than + " " + str(ws.cell(j, 8).value) + " " +
                      larger_than + " " + str(ws.cell(j, 7).value) + " " + semicolon_word)

                print("\t" + str(ws.cell(4, 14).value))
                print("\n")

        elif ws.cell(cnt_list_special_num_start, 4).value == "숏":  # 롱/숏 구분
            if j == cnt_list_special_num_start:
                print("###" + " " + "(" + str(ws.cell(j, 4).value) + "패턴" + str(ws.cell(j, 5).value) + ")" + " " + str(ws.cell(j, 12).value)
                      + " " + "(" + str(ws.cell(j, 10).value) + "/" + str(ws.cell(j, 9).value)
                      + "=" + str(ws.cell(j, 11).value) + "%)")  ### 결과값은 '함수의 결과'가 아닌, "(엑셀에서 계산된 후의) 결과값'만 넣을 것(수식이 복사됨)
                print(elif_word + " " + str(ws.cell(j, 6).value) + " " + larger_than + " " + str(ws.cell(j, 8).value) +
                      " " + larger_than + " " + str(ws.cell(j, 7).value) + " " + and_word)  # j=7
            elif cnt_list_special_num_start < j < cnt_list_special_num_end - 2:
                print("\t" + str(ws.cell(j, 6).value) + " " + larger_than + " " + str(ws.cell(j, 8).value) + " " +
                      larger_than + " " + str(ws.cell(j, 7).value) + " " + and_word)

            elif j == cnt_list_special_num_end - 1:
                print("\t" + str(ws.cell(j, 6).value) + " " + larger_than + " " + str(ws.cell(j, 8).value) + " " +
                      larger_than + " " + str(ws.cell(j, 7).value) + " " + semicolon_word)

                print("\t" + str(ws.cell(5, 14).value))
                print("\n")

 


5. 마치며

위에서 필자가 업로드한 파일 및 < 그림1 >의 알고리즘은 필자가 임의로 만든 것이다. 절대 사용자 본인의 자동매매에 적용하면 안된다.
 
사실 지금은 엑셀파일을 통해 파이참에서 출력하는 내용이 별로 감흥이 오지 않는다. 하지만 나중에 볼린저밴드, rsi, 몸통길이 등을 고정하고, 최소값/최대값만 바꾸어서, 각각을 조합하여 코드를 만들때가 분명히 있다. 이 내용은 향후 숫자만 바꾸고자 할 때 유용하게 쓰일 것이다.
 
물론, 알고리즘 코드만 자동매매 시스템에 넣어준다고 모든게 끝난건 아니다. 알고리즘 코드를 먼저 정의해 두어야 하는 한다는 사실은 명심하자. 10분전rsi가 50~70 사이에 위치한지 구분하기 위해서는, 먼저 10분전rsi를 구해야한다는 사실은 잊지 말자 ^^
 
5번의 글을 통해 엑셀의 데이터를 파이참에서 출력하는 내용을 알아보았다. 파이참에서 "Run" 부분에 출력된 내용을, 본인의 자동매매 코드(알고리즘)에 넣어주면 된다. 물론 sendorder 함수를 활용하여 진입/청산이 된다.
 
알고리즘에 대한 연구가 지속되어야 한다고 생각한다. 승률이 높은 패턴은 엑셀로 그 승률을 계산해볼 수도 있다. 필자도 알고리즘에 대해 끊임없이 고민하고 있다. 이 고민이 수익으로 바뀌는 날이 빨리 왔으면 좋겠다.
 
 
 

반응형