1
Free-Talk
Xp
자동
Free-Talk

자동매매 프로그램 제작

 
5
  1705
2022-10-07 15:08:34

 

지난 글에 말했던 자동매매 프로그램의 코드들 중 메인 모듈에 해당하는 코드입니다.
 
calemarsi 함수는 이동평균선 3개와 RSI값을 합쳐서 LONG인지 SHORT인지 아니면 HOLD인지 반환합니다.
 
가장 짧은 기간의 이평선이 가장 위에 있고 RSI값이 70 초과이면 SHORT을 반환하고, 앞의 이평선 값이 가장 아래에 있고 RSI 값이 30 미만이면 LONG을 반환하는 식입니다. 자세한 것은 나중에 그 모듈을 볼 때 작성하겠습니다. 
 
lwstrategy 함수는 이전 글에 언급했던 래리윌리엄스의 변동성 돌파 전략을 함수로 만든 것으로 조건을 만족하면 True를 반환하고, 아니면 False를 반환하게 되어 있습니다.
 
저는 두 신호를 조합해서 long 포지션을 매수하거나 short 포지션을 매수하는 전략을 쓰고 있습니다.
 
하지만 계쏙 HOLD만 나오고 실제 매수하는 건이 너무 적어 내부적으로 기준을 조금씩 조정하고 있습니다.

그리고 신호가 나오면 어떤 신호가 나왔는지, 제대로 작동은 하는지 알아보기 위해 텔레그램을 통해 메시지를 매 15분마다 받고 있습니다.
 
이것도 지금 시그널이 적어 텔레그램으로 보내는 시그널을 늘리려고 생각 중입니다.
 
현재는 XRP에 대해서만 프로그램을 돌리고 있는데, 완성이 되는대로 다른 코인들도 추가해서 돌릴 예정입니다.
 
그 때를 대비해서 코드는 짜놓고 주석으로 처리했습니다. 
#tickers = get_ticker()
ticker = 'XRPUSD_PERP'
notified_tickers = []  # 텔레그램 메시지 보낸 ticker 리스트(15분마다 초기화됨)
while True:
     try:
#        for ticker in tickers:
# Buying signal checking
            osig = calemarsi(ticker)
            lwst = lwstrategy(ticker, osig)
            if (osig == 'LONG' and lwst == True and notified_tickers.count(ticker) == 0 and ALARM_TELEGRAM):
                notified_tickers.append(ticker)
                side = 'BUY'
            elif (osig == 'SHORT' and lwst == True and notified_tickers.count(ticker) == 0 and ALARM_TELEGRAM):
                  notified_tickers.append(ticker)
                  side = 'SELL'               
            elif lwst == False or osig == 'HOLD':
                 notified_tickers.append(ticker)
                 side = 'HOLD'
 
밑의 모듈은 15분 마다 포지션을 정산하기 위해 만든 것으로 포지션잔고?를 바이낸스에서 끌고와서 확인한 뒤, 포지션이 있으면 정리하는 식으로 작성했습니다. sellorder 함수는 나중에 또 이 부분을 다룰 때 설명하겠습니다.
 
이러한 데이터를 처리할 때 리스트보다는 데이터프레임이 보기 편해서 데이터프레임을 쓰는데, 막상 데이터프레임에서 특정 값을 뽑아내는 것은 또 까다로워서 고민입니다. 지금이야 이 부분을 손댈 일이 없지만, 향후 데이터프레임으로 또 데이터를 가공했을 때 어떻게 할지 생각해봐야 겠습니다.
 
# Close position already opened
            position = get_user_data('/dapi/v1/positionRisk')
            df3 = pd.DataFrame( position, columns = ['symbol', 'positionAmt', 'entryPrice', 'markPrice', 'unRealizedProfit', 'liquidationPrice', 'leverage', 'maxQty', 'marginType', 'isolatedMargin', 'isAutoAddMargin', 'positionSide', 'notionalValue', 'isolatedWallet', 'updateTime'])
            df3 = df3.astype({'entryPrice' : 'float'})
            df3_1 = df3.loc[(df3['entryPrice'] > 0), ['symbol', 'positionAmt', 'entryPrice', 'markPrice', 'unRealizedProfit', 'liquidationPrice', 'leverage', 'isolatedMargin', 'positionSide']]
                
            po = int(len(df3_1.loc[df3_1['symbol'] == ticker]))
            pl = int(len(df3_1.loc[ (df3_1['positionSide'] == 'LONG') & (df3_1['symbol'] == ticker) ]))
            ps = int(len(df3_1.loc[ (df3_1['positionSide'] == 'SHORT') & (df3_1['symbol'] == ticker) ]))
        
            mp_1 = getmarkprice(ticker)
            mp_2 = pd.DataFrame(mp_1, columns=['symbol', 'pair', 'markPrice', 'indexPrice', 'estimatedSettlePrice', 'lastFundingRate', 'interestRate', 'nextFundingTime', 'time'])
            mp_3 = float(mp_2['markPrice'])
            if po > 0 and pl > 0:               
                osig_1 = 'LONG'
                side_1 = 'SELL'
                msg6 = createsellorder(ticker, side_1, osig_1)
                message6 = f'{msg6}'
                bot.sendMessage(TELEGRAM_CHAT_ID, text=message6)          
                print(message6)                        
            elif po > 0 and ps > 0:
                osig_2 = 'SHORT'
                side_2 = 'BUY'
                msg6 = createsellorder(ticker, side_2, osig_2)
                message6 = f'{msg6}'
                bot.sendMessage(TELEGRAM_CHAT_ID, text=message6)          
                print(message6)
            else:
                msg7 = f'전체 포지션 {po}개, 롱포지션 {pl}개 숏포지션 {ps}개'
 
 밑의 코드는 실제 매수 주문을 넣는 코드입니다.
long과 buy 신호가 같이 오면 롱포지션을 매수하고, short과 sell 신호가 같이오면 숏포지션을 매수하는 방식입니다. 왜 short은 buy가 아니라 sell에 매수하냐고 하실 수 있는데, 외국애들은 short 포지션을 sell 하는 것이 실제로는 포지션을 구축하는 것으로 보더라구요. short에 buy로 주문 넣었다가 숏포지션을 매도 해버린 경험이 있기 때문에 혹시 바이낸스로 자동매매 하실 분들은 주의하세요.
 
매수 하면 실제로 매수가 되었는지 바이낸스에서 오더 정보를 받아와서 확인하고 sqlite에 매수 단가랑 수량, 오더 넘버 등을 기록하게 해놨습니다.  그 결과는 똑같이 텔레그램으로 받구요. sqlite에 기록이 제대로 안되면 또 다른 메세지가 오도록 셋팅도 했습니다. 그건 나중에 오더함수를 자세히 볼 때 쓰겠습니다.
 
# Send buying order either long or short
            if osig == 'LONG' and side == 'BUY':
                position = get_user_data('/dapi/v1/positionRisk')
                df3 = pd.DataFrame( position, columns = ['symbol', 'positionAmt', 'entryPrice', 'markPrice', 'unRealizedProfit', 'liquidationPrice', 'leverage', 'maxQty', 'marginType', 'isolatedMargin', 'isAutoAddMargin', 'positionSide', 'notionalValue', 'isolatedWallet', 'updateTime'])
                df3 = df3.astype({'entryPrice' : 'float'})
                df3_1 = df3.loc[(df3['entryPrice'] > 0), ['symbol', 'positionAmt', 'entryPrice', 'markPrice', 'unRealizedProfit', 'liquidationPrice', 'leverage', 'isolatedMargin', 'positionSide']]
                pl = int(len(df3_1.loc[ (df3_1['positionSide'] == 'LONG') & (df3_1['symbol'] == ticker) ]))
                if pl == 0:
                   msg1 = createbuyorder(ticker, side, osig)
                   message1 = f'{msg1}'
                   bot.sendMessage(TELEGRAM_CHAT_ID, text=message1)          
                   print(message1)   
                elif pl > 0:
                     message3 = f"{ticker} position is already opened. please check your account! {osig} {side}"
                     bot.sendMessage(TELEGRAM_CHAT_ID, text=message3)          
                     print(message3)
            
            elif osig == 'SHORT' and side == 'SELL':
                 position = get_user_data('/dapi/v1/positionRisk')
                 df3 = pd.DataFrame( position, columns = ['symbol', 'positionAmt', 'entryPrice', 'markPrice', 'unRealizedProfit', 'liquidationPrice', 'leverage', 'maxQty', 'marginType', 'isolatedMargin', 'isAutoAddMargin', 'positionSide', 'notionalValue', 'isolatedWallet', 'updateTime'])
                 df3 = df3.astype({'entryPrice' : 'float'})
                 df3_1 = df3.loc[(df3['entryPrice'] > 0), ['symbol', 'positionAmt', 'entryPrice', 'markPrice', 'unRealizedProfit', 'liquidationPrice', 'leverage', 'isolatedMargin', 'positionSide']]
                 ps = int(len(df3_1.loc[ (df3_1['positionSide'] == 'SHORT') & (df3_1['symbol'] == ticker) ]))
                 if ps == 0:                    
                    msg5 = createbuyorder(ticker, side, osig)
                    message1 = f'{msg5}'
                    bot.sendMessage(TELEGRAM_CHAT_ID, text=message1)          
                    print(message1)
                 elif ps > 0:
                      message3 = f"{ticker} position is already opened. please check your account! {osig} {side}"
                      bot.sendMessage(TELEGRAM_CHAT_ID, text=message3)          
                      print(message3)
            
            elif side == 'HOLD' or osig == 'HOLD':
                 message4 = f"{ticker} is not ready. \n{msg7} {osig} {side}"
                 bot.sendMessage(TELEGRAM_CHAT_ID, text=message4)
                 print(message4)
 
밑에 함수로 이제 15분마다 이 모듈이 작동하게 구현했습니다. 결국 여기 함수들이 모두 ticker에 값이 들어가야 작동하는데 이것을 15분 동안 비워놨다가 다시 15분 뒤에 넣어서 돌리고 지우고 이런 방식입니다.
 
# Make it work every 15m
            now = dt.datetime.now()
            mod = divmod(now.minute, 15)[1]
            if mod == 0:
                is_multiple_15 = True
            else: is_multiple_15 = False
            if(is_multiple_15 == True and now.second > 5 and now.second < 10):
                notified_tickers.clear()
            time.sleep(SLEEP_TIME)
            
     except Exception as e:
            print(e)
            time.sleep(1)
 
오늘 보신 코드는 이제 몸통이 되는 코드입니다. 이것 이외에 이제 실제 주문을 request를 통해 넣는 함수들도 있고, 전략을 구현하는 함수들도 있습니다. 이것들도 나중에 한번 따로 설명들 드리겠습니다.
 
저는 이 글을 작성하면서 제 생각도 정리하고 전략을 바꾸거나 좀 더 코드를 깔끔하게 바꾸거나 그렇게 하려고 합니다. 다른 분들에게 도움이 된다면 더욱 좋구요.
 
그냥 재미로 봐주셔도 좋습니다.
 
다들 수고하세요
8
Comments
1
2022-10-07 15:39:42

WR
2022-10-07 16:09:58

저도 사실 왜 작동되는지 모른다는게 함정

1
2022-10-07 15:40:59

사실 내용에 대한 부분은 문외한인지라 이해를 못하지만 질문 하나만 드려도 될까요?

요새는 조금 조용해진것 같은데 1~2년 전에 잠깐 로보어드바이저 회사들이 몇개 생겼던걸로 기억하는데

로보어드바이저 회사들도 본문처럼 자동 매매를 기반으로 돈을 굴리는건가요?

WR
2022-10-07 16:11:07

제 거는 비전공자가 취미로 만든거라 아마 많이 다르지 않을까 싶습니다. 

분석 프로그램도 따로 돌리지 않을까요? 책 보니까 사이킷런이나 텐서플로우로 주가 예측하고 이런것도 많더라구요. 저는 아직 그런 수준이 아니라서 

2
2022-10-07 15:45:05

고생 많으시네요. 좋은 수익 기대합니다.
저는 sql과 파이썬으로 한국주식을 추세추종 전략으로 돌리고 있습니다. 일별 흐름으로 하기 때문에 자동매매까지는 하지 않고 하루 한번 정도 확인하고 매매하는데, 요즘 같은 장에는 손실 날 일이 없어 좋네요(추세추종이다 보니, 종목을 보유한게 없게 되네용^^)
화이팅하시고, 좋은 결과 기대합니다.

WR
2022-10-07 16:12:26

저는 15분봉 이용해서 좀 자주 매매하고 싶은데 기준을 조정하는게 참 어렵네요. 너무 또 자주 매매하면 매매비용이 커지고 기준을 또 여러개로 늘리니 매매가 너무 뜸하고 참 어렵습니다. 

활활브롱님도 화이팅 하세요!

1
2022-10-07 16:05:23

변동성 돌파 전략이 너무 단순한 공식이라 잘 될지가 개인적으로는 의문입니다. 개인적으로 테스트 해봤을때는 결국 변동성이 있는 종목을 잘 선정하는게 가장 중요한거 같고, 계속되는 손절과 익절로 복리 효과를 보면서 길게 가야 유리한거 같드라고요. 주제 넘는 이야기가 될수 있지만, 터틀 트레이딩 관련 책 좀 보시면 도움이 되지 않을까 싶습니다. 어쨋든 성투 기원합니다. 매니아에서 비슷한 작업 하시는 분 보니 반갑네용. ^^

WR
2022-10-07 16:16:30

터틀트레이딩 관련해서 공부 해보겠습니다. 추천 감사합니다.

단타 전략들도 잘 몰라서 일단 유명한 변동성 돌파를 선정해서 해보고 있습니다. 하지만 저도 늘 변동성돌파 전략으로 벌 수 있을지 의문이었어서 다른 기준들을 추가하긴 했습니다. 그런데 너무 매수가 뜸해서 기준을 좀 조정하고 있습니다.

일단 몇달 정도 돌려보고 또 다른 전략을 해볼까 생각 중입니다.

다른 분들은 백테스팅도 많이 하던데, 저는 사실 백테스팅은 그냥 과거 if를 보는게 아닌가 싶어서 인생 실전이지 하고 돌리고 있습니다.

14:44
4
217
글쓰기
검색 대상
띄어쓰기 시 조건








SERVER HEALTH CHECK: OK