요즘에 깃허브 코파일럿을 파이썬에 연동해서 사용하는중인데요...
이게... 뭐랄까..
이전에 자동 완성을 완전히 뛰어 넘네요.
예를 들어, 긴 예약어나 변수를 자동완성하는게 아니고.
그다음 코드 한줄을 완전히 자동으로 만들어 버리네요.
코파일럿이 만든 코드가 맞으면 tab만 입력하면 되네요...

기존에 GPT로 코드 도움을 많이 받으면서도 놀랐는데..
이건뭐... 대화좀 하고, 상황 설명하고, 탭만 누르면 된다니.
어허... 어허.... 어허.. 뭐라 할말이 없네요..

 

 

 

 

데이터 분석을 위해서는 시각화가 중요합니다.
시각화를 통해 캐치할 부분을 비교적 쉽게 찾아낼 수 있습니다.
일한 성과를 시각화하는 것도 매우 중요한 일입니다. 또한 쉽지 않은 일이기도 하고요.
그래서 데이터 시각화, 인사이트에 대한 책들을 종종 읽어보고 있습니다.



오늘은 제가 경험했던, 선만 추가했을 뿐인데 OK가 된 리포트의 사례에 대해 정리해봤습니다.

 

이전 프로젝트에서 SQL 튜닝 프로젝트 결과를 리포팅한적이 있습니다.

열심히 몇일 동안 튜닝 후 결과를 시각화해보니, 아래와 같이 결과가 나왔습니다. 아래는 SQL별 일별 평균 실행 시간을 시각화한 차트입니다.

 

차트를 보면 일별로 SQL 평균 실행시간이 들쑥날쑥합니다.

해당 시스템은, 대량의 데이터를 처리하는 복잡한 배치가 특정 요일(월과 목)에 집중 실행되어, SQL 평균 실행시간이 요일별로 들쑥날쑥한 상황입니다. 업무를 살펴보니, 월과 목요일에 주로 제품 출하가 있으며 이때마다 시스템이 버벅되는 상황이었습니다.

빨간 세로 점선을 기준으로 왼쪽은 튜닝 반영전, 오른쪽은 튜닝 반영후입니다.
전과 후를 비교해보면, 좋아지긴 좋아졌습니다.

하지만, 여전히 배치 자체가 처리해야할 물리적인 데이터 양 때문에 개선에 한계가 있으며,

개선후에도 요일별로 SQL 평균 실행 시간이 들쑥날쑥합니다.


Tip.!

이러한 대량 데이터 배치 SQL 성능의 한계를 극복하기 위해서는 개발자와 업무 담당자의 적극적인 지원이 필요하며, 상황에 따라 배치 프로세스 전체를 변경하거나 테이블 구조 변경도 고려해야 합니다. 또한 세밀한 테스트가 필요해 튜닝 작업이 쉽지 않습니다. 대부분 사이트들이 배치 작업에 대해 튜닝을 하고 싶어하지만 현실적으로 변경에 대한 작업양과 리스크가 너무 커서 실제 바꾸지 못하는 경우가 많습니다.


 

위와 같은 튜닝 결과 차트를 실무 담당 고객에게 보여주니, 결과서를 팀장에게 보고하기에는 좀 난감해보인다고 하더군요. 자세히 보고 생각해보면 좋아진건 알겠는데, 첫 눈에 성능이 개선된걸 알 수 있어야 할거 같다고 하더군요.

제가 생각해도... 위의 차트를 이용해 고객에게 프로젝트 성과를 와닿게 하기는 쉽지 않겠다는 생각이 들더군요.

 

 

그래서.. 좀 고민을 하다가... 아래와 같이 가로선을 추가해서 고객에게 보여줬습니다.

그랬더니, 고객도.. 음.. 좋아졌네요. 고개를 끄덕거리시네요.

SQL 평균실행시간- 가로선 추가

 

 

오래전 일이지만, 소개를 한번 해보면 좋겠다 싶었던 내용입니다.
데이터 분석을 위해서는 시각화가 중요합니다.

시각화를 통해 캐치할 부분을 비교적 쉽게 찾아낼 수 있습니다.

일한 성과를 시각화하는 것도 매우 중요한 일입니다. 또한 쉽지 않은 일이기도 하고요.

그래서 데이터 시각화, 인사이트에 대한 책들을 종종 읽어보고 있습니다.

 

지금 다시 보고서를 만든다면, 아래와 같이 이동평균도 이용해볼 수 있을거 같네요. 아래는 7일간의 이동평균(오렌지색 점선)을 추가한 결과입니다.

 



[StartUP SQL]

 

StartUP SQL 소개 및 목차

SQL을 배우기 위한 최적의 시작점 StartUP SQL! 교육 슬라이드 전체를 블로그에 무료 오픈했습니다. (총 409장 슬라이드, 이미지로 게시) 아래 목차를 차례대로 하나씩 따라해보시기 바랍니다. 본인의

sweetquant.tistory.com

 

[StartUP Tuning]

 

StartUP Tuning - MySQL/MariaDB를 활용한 SQL 튜닝 입문

「StartUP Tuning - MySQL/MariaDB를 활용한 SQL 튜닝 입문」 < 일정 > 5/12(일), 5/19(일), 5/26(일) 오전 9시 ~ 오후 5시 (...

cafe.naver.com

 

 

 

지난 글에서 DBeaver에 GPT를 연동하는 방법을 살펴봤습니다.

https://sweetquant.tistory.com/557

 

DBeaver에 GPT 연동하기

파이참에 Copilot을 연결해서 사용하다... 음.. SQL툴에도 AI 연동이 가능할거 같은데란 생각이 들어서.찾아보니.. DBeaver에 GPT API를 연동할 수가 있네요. 준비물DBeaver(저는 24.0.3 버젼을 사용했습니다

sweetquant.tistory.com

 

간단한 SQL은 잘 작성합니다. 조금(?) 복잡한 SQL을 GPT로 시도해봤습니다.

우선, DBeaver에 연동해서 API 형태로 GPT를 사용하는 것은 실제 OpenAI에 접속해 GPT와 채팅하면서 답변을 얻는 것과는 차이가 있어 보입니다. 아무래도 GPT와 채팅하면서 얻는 답변이 좀 더 품질이 좋아보입니다.

 

테스트한 결과를 살펴보겠습니다.

먼저, 매장 테이블과 주문 테이블을 조인하는 간단한 SQL을 요청했습니다.

SQL에 성능 문제가 발생되도록 작성되었습니다. OrdDtm 컬럼을 DATE_FORMAT으로 가공해  인덱스가 있어도 사용을 못하도록 SQL이 만들어졌습니다. 그리고 아래 SQL의 프롬프트는 제 실수가 있었습니다. 사실 매장등급이란 컬럼은 없습니다. 없으면 없다고 메시지를 주면 좋은데, 그러지는 않네요. 자신이 생각할때 적절하다고 생각한 컬럼을 사용해 결과를 만들어 냅니다.

 

-- 프롬프트: 매장 테이블과 주문 테이블을 조인해서 보여줘,
-- 주문일시가 2022년 12월이고, 매장등급이 플래티넘(PLAT)인 데이터만 조회해줘
SELECT *
FROM shop s
JOIN ord o ON s.ShopId = o.ShopId
WHERE DATE_FORMAT(o.OrdDtm, '%Y%m') = '202212' AND s.ShopSt = 'PLAT';

 

이번에는 성능을 고려하라고 프롬프트를 추가하고, 매장이 아닌 회원과 조인을 요청합니다. 이번 SQL은 100점입니다. 인덱스 사용을 고려해달라고 요청하니 OrdDtm에 적절하게 조건 처리를 했습니다. 물론, OrdDtm에 인덱스는 아직 없지만요.

-- 프롬프트
-- 회원 테이블과 주문 테이블을 조인해서 보여줘, 
-- 주문일시가 2022년 12월이고, 회원등급이 플래티넘(PLAT)인 데이터만 조회해줘. 
-- 성능을 고려해서 SQL을 작성해줘. 인덱스를 사용할 수 있게 SQL을 작성해야 한다는 거지.
--  Sure, here is the SQL query that joins the `member` and `ord` tables, and filters for orders made in December 2022 by members with a 'PLAT' grade. This query is optimized for performance by using the appropriate indexes.
/*
This query joins the member and ord tables on the MemberId field.
It then filters for orders made in December 2022 and by members with a 'PLAT' grade.
The WHERE clause is written in a way that allows the use of indexes for performance optimization.
*/
SELECT m.*, o.*
FROM `member` AS m
INNER JOIN ord AS o ON m.MemberId = o.MemberId
WHERE m.MemberGd = 'PLAT'
AND o.OrdDtm >= '2022-12-01 00:00:00'
AND o.OrdDtm < '2023-01-01 00:00:00';
--  Please note that the date format and the 'PLAT' value are just examples and should be replaced with the actual format and value used in your database. Also, make sure that there are indexes on the `MemberId`, `MemberGd`, and `OrdDtm` fields for optimal performance.

 

 

 

조금 수준을 올려봅니다. StartUP SQL에서 사용하는 퀴즈를 활용해봅니다. https://sweetquant.tistory.com/542 의 마지막에 있는 16-4번 문제입니다. 그러나 여전히 성능 관점에서는 빵점 SQL이네요.

문제:
--  [BOOSTER QUIZ 16-4] 가입년도별, 회원상태별 2023년 1월의 주문 건수를 구하시오.
--  특정 가입년도별, 회원상태별 주문이 없어도 0건으로 집계해서 출력해주세요. 그리고 가입년도별 주문건수와 전체 주문건수도 추가해서 보여주세요.
--   - 대상 테이블: 회원(Member), 주문(Ord)
--   - 조회 조건: OrdDtm이 2023년 1월인 주문 데이터
--   - 조회 컬럼: JoinYear, MemberSt, OrdCnt
--   - 추가 조건:
--      : 성능을 고민하고 인덱스를 사용할 수 있도록 SQL을 작성할것.
--      : JoinYear는 JoinDtm을 년도로 변환한 값
--      : JoinYear별, MemberSt별 GROUP BY 처리하시오.
--      : OrdCnt는 JoinYear, MemberSt별 주문 건수
--      : Member와 Ord를 아우터 조인해 주문이 없는 JoinYear, MemberSt도 주문 건수가 0건으로 출력되도록 하시오.
--      : ROLLUP을 사용해 JoinYear별 중간합계와 전체합계를 추가하시오.
    
DBeaver+GPT 답변:

SELECT 
    YEAR(m.JoinDtm) AS JoinYear,
    m.MemberSt,
    COUNT(o.OrdNo) AS OrdCnt
FROM 
    `member` m
LEFT JOIN 
    ord o ON m.MemberId = o.MemberId AND YEAR(o.OrdDtm) = 2023 AND MONTH(o.OrdDtm) = 1
GROUP BY 
    YEAR(m.JoinDtm), m.MemberSt WITH ROLLUP;

 

 

조금 더 수준을 올려봅니다. 세개 테이블 조인과 분석함수까지 사용해야 하는 SQL입니다.

(문제 출처: https://sweetquant.tistory.com/548 )

휴~ 다행이네요. 아래 SQL은 에러가 발생합니다. SELECT 절에서 ItemId 컬럼을 사용할때 테이블이 지정되지 않았기 때문이죠.

--  [BOOSTER QUIZ 20-2] 2022년 11월 2022년 12월 주문에 대해, 주문년월별 상품별 주문수량을 구해주세요, 주문년월별로 주문수량에 따른 순위를 구해주세요.
--   - 대상 테이블: 주문(Ord), 주문상세(OrdDet), 상품(Item)
--   - 조회 조건: OrdDtm이 2022년 11월부터 2022년 12월 말까지인 데이터
--   - 조회 컬럼: OrdYm, ItemId, ItemNm, SumOrdQty, RankOverByYm
--   - 추가 조건:
--      : OrdYm, ItemId별 GROUP BY 처리하시오.
--      : OrdYm은 OrdDtm을 DATE_FORMAT으로 년월 형태로 변환한 값입니다.
--      : SumOrdQty는 OrdYm, ItemId별 SUM한 OrdQty 값입니다.
--      : RankOverByYm은 ItemId의 판매수량(SumOrdQty)에 대한 순위입니다. 이 순위는 년월별로 구해야 하며 판매 수량이 높으면 1이 됩니다.

SELECT 
    DATE_FORMAT(OrdDtm, '%Y%m') AS OrdYm,
    ItemId,
    ItemNm,
    SUM(OrdQty) AS SumOrdQty,
    RANK() OVER (PARTITION BY DATE_FORMAT(OrdDtm, '%Y%m') ORDER BY SUM(OrdQty) DESC) AS RankOverByYm
FROM 
    ord 
JOIN 
    orddet ON ord.OrdNo = orddet.OrdNo
JOIN 
    item ON orddet.ItemId = item.ItemId
WHERE 
    OrdDtm BETWEEN '2022-11-01' AND '2022-12-31'
GROUP BY 
    DATE_FORMAT(OrdDtm, '%Y%m'), 
    ItemId, 
    ItemNm
ORDER BY 
    OrdYm, 
    RankOverByYm;--  This query will return the total quantity of each item ordered per month for November and December 2022, along with a rank for each item based on the quantity ordered. The rank is calculated separately for each month.

 

 

DBeaver에 GPT API를 연동해 다양한 SQL을 시도해봤습니다.
아직은 API 연동으로 복잡한 SQL을 작성하는 데는 한계가 있어 보입니다.

(GPT와 직접 채팅으로 시도해보면 좀 더 결과가 잘나오지 않을까 싶네요.)
그러나 책으로만 SQL을 배운 신입보다는 낫다고도 할 수 있습니다.

지금은 AI를 활용한 SQL 작성에 한계는 있어보이지만,
개인적으로는 언젠가 이 한계를 극복할 것이라고 생각합니다.

다만, GPT가 잘할거라고 해서 우리가 공부를 게을리 해도 된다는 것은 아닙니다.
오히려 이전보다 더 깊이있게 공부할 필요가 있다고 생각합니다.
우리가 잘 알아야 잘 시킬 수 있으니까요..
언젠가는 시킬 일 조차 없게 될지도 모르지만, 그때는 그때고요.
오늘은 노력해야 하지 않을까요?

 

바짝 긴장해라. H.U.M.A.N!

 


[StartUP Tuning]
SQL 입문을 넘어선 MySQL을 활용한 SQL 튜닝 입문 교육
2024년 5월 주말 오프라인 교육 과정 모집중: https://cafe.naver.com/dbian/7717

 

StartUP Tuning - MySQL/MariaDB를 활용한 SQL 튜닝 입문

「StartUP Tuning - MySQL/MariaDB를 활용한 SQL 튜닝 입문」 < 일정 > 5/12(일), 5/19(일), 5/26(일) 오전 9시 ~ 오후 5시 (...

cafe.naver.com

 

[StartUP SQL]
데이터 분석가, 개발자, 기획자를 위한 SQL 입문 교육
교육용 슬라이드 전체 무료 오픈: https://sweetquant.tistory.com/497

 

StartUP SQL 소개 및 목차

SQL을 배우기 위한 최적의 시작점 StartUP SQL! 교육 슬라이드 전체를 블로그에 무료 오픈했습니다. (총 409장 슬라이드, 이미지로 게시) 아래 목차를 차례대로 하나씩 따라해보시기 바랍니다. 본인의

sweetquant.tistory.com

 

파이참에 Copilot을 연결해서 사용하다... 음.. SQL툴에도 AI 연동이 가능할거 같은데란 생각이 들어서.

찾아보니.. DBeaver에 GPT API를 연동할 수가 있네요.

 

준비물

DBeaver(저는 24.0.3 버젼을 사용했습니다. 아주 오래된 버젼 아니면 문제 없을거 같습니다.)

GPT 계정과 별도의 GPT API 결제 필요.(유료입니다.)

 

1. DBeaver를 실행하고 상단 메뉴 도움말(H)에서 Install New Software... 를 선택합니다.

 

2. Work with 부분에 AI를 검색해 DBeaver AI Intergration을 선택합니다.

   - 그 후 아래 목록에서 DBeaver AI Support 를 체크한 후에 다음(N) 버튼을 클릭합니다.

   - Install Details 에서도 다음으로 그냥 넘어갑니다.

 

3. 동의 및 완료를 클릭

   - 그다음 권한 관련해서는 모두 체크후 Trust 버튼 클릭

   - 권한 관련 체크가 두번 나오는데, 왠지 불안하지만 체크를 안하면 작동안할거 같아서 일단 체크하고 넘어감

   - 회사 DB에 접근하는 상황이라면 왠만하면 내용을 꼼꼼히 읽어보고 체크할 것을 권장합니다.

   - Restart로 DBeaver를 재시작합니다.

 

 

4. DBeaver가 Restart되는 동아 Openai에 가서 API키를 발급합니다.

   - API Key 같은 경우 생성할때만 확인해서 카피해놓을 수 있으며, 나중에 재확인이 불가합니다.

   - API Key 사용에 대해 미리 별도 결제가 필요합니다.

     (개인 용도로 잠깐씩 쓰는 것은 생각만큼 많이는 안나오는거 같네요. 키 발급후 키가 공개되지 않도록 주의 필요)

   - https://platform.openai.com/api-keys 접속

   - 왼쪽 api keys 선택 > 가운데  Create new secret key 클릭해서 키를 새로 생성

   - 여기서 만든 Key를 DBeaver의 AI 창에 입력해줘야 합니다. 

 

 

 

5. DBeaver를 재시작하면, SQL창 왼쪽에 GPT 마크의 버튼이 나타납니다. 해당 버튼을 클릭합니다.

   - AI 설정창이 뜨면, Service를 선택하고 API Token에 조금전에 받은 API 키를 입력후 Apply and Close를 선택합니다.

   - 현재 선택한 DB의 테이블과 컬럼에 대한 메타데이터를 AI업체에 넘겨도 되냐고 질문합니다.

   - 그래야 AI가 SQL을 작성할 수 있으니, Yes를 선택합니다.

     : 회사 DB에서 이와 같이 하시면, 아마도 안되겠죠!

 

6. 모든 준비는 끝났습니다. 이제 다시 SQL 창 왼쪽에 GPT 마크 버튼을 클릭하고, 텍스트로 SQL을 요청하면 됩니다.

 

간단한 SQL은 잘 만들어주네요.^^ 점점점 발전하겠죠. AI 무섭슴다...

 

 

준비한 내용은 여기까지입니다. 다음 글에서는 조금 더 복잡한 SQL을 시켜볼까 합니다.

테스트해본바로는, "여전히 SQL 공부는 해야 한다!"가 개인적인 결론입니다.

 


[StartUP Tuning]

  • SQL 입문을 넘어선 MySQL을 활용한 SQL 튜닝 입문 교육
  • 2024년 5월 주말 오프라인 교육 과정 모집중: https://cafe.naver.com/dbian/7717
 

StartUP Tuning - MySQL/MariaDB를 활용한 SQL 튜닝 입문

「StartUP Tuning - MySQL/MariaDB를 활용한 SQL 튜닝 입문」 < 일정 > 5/12(일), 5/19(일), 5/26(일) 오전 9시 ~ 오후 5시 (...

cafe.naver.com


[StartUP SQL]

 

StartUP SQL 소개 및 목차

SQL을 배우기 위한 최적의 시작점 StartUP SQL! 교육 슬라이드 전체를 블로그에 무료 오픈했습니다. (총 409장 슬라이드, 이미지로 게시) 아래 목차를 차례대로 하나씩 따라해보시기 바랍니다. 본인의

sweetquant.tistory.com

 

 

주식 분석 시스템을 만들기 위해, "종목코드"에 대한 물리명(Column Name)을 고민중입니다.

지금까지는 단순하게 Stock Code 개념으로 STK_CD를 사용해왔는데요,

글로벌하게는 Ticker 또는 Symbol이란 용어가 더 많이 사용되니까요.

글로벌한 시스템을 고려한다면 물리명을 다시 한번 고려해야겠단 생각이 들었습니다.

 

그래서!, GPT에게 물어봤죠.

단순한 랜덤 결정이 아닌, GPT가 데이터 아키텍쳐나 데이터 모델러 관점으로 결정할 수 있게,

실시간 웹 검색을 유도했고, 논리적으로 결정을 내려주도록 질문을 했습니다.

아래와 같이 Ticker나 Symbol 사용하는 것이 좋을거 같다는 논리를 들어 설명해줍니다.

이처럼 논리를 가지고 어떠한 결정을 하는 것은 데이터 모델러와 데이터 아키텍쳐에게 매우 중요한 부분입니다. 


주식 분석 시스템의 데이터베이스에서 사용할 용어로 "Stock Code", "Ticker", "Symbol" 중에서 고르는 것에 대해 고려할 점들을 한글로 정리해드리겠습니다:

주식 기호/티커(Ticker): 주식 기호 또는 티커는 거래 목적으로 증권에 할당된 고유한 문자 시리즈입니다. 트레이더와 투자자가 특정 주식 시장의 특정 주식을 식별하기 위해 사용하는 약어 또는 코드입니다. 예를 들어, 마이크로소프트의 주식 기호는 MSFT이고 애플의 주식 기호는 AAPL입니다. 이 기호는 금융 시장에서 회사의 정체성을 확인하는 디지털 여권 역할을 하며, 거래를 간소화하여 주식 시장을 보다 효율적이고 접근하기 쉽게 만듭니다. 또한 전 세계 금융 시장 참여자들 간의 공통 언어 역할을 하여 국경을 넘는 거래를 원활하게 합니다. "티커"라는 용어는 주식 교환소에서 사용되던 티커 테이프 기계에서 유래되었으며, 이 기계는 실시간 주식 가격 및 거래 정보를 제공했습니다​​​​​​.

주식 코드(Stock Code): 티커 심볼 또는 주식 기호를 나타내기 위해 사용되기도 하지만, "주식 코드"는 이 용어들보다는 구체적이지 않고 국제 사용자나 주식 거래 용어에 익숙한 사람들에게 즉시 알아보기 어려울 수 있습니다. 주식 코드 또는 티커 심볼은 전보를 사용하여 주식 가격을 약어 코드로 전달하는 시스템에서 발전했습니다. 이 시스템은 1960년대까지 사용되었으며, 오늘날 이 코드들은 비즈니스 TV 보고서, 금융 웹사이트 및 기타 매체에 나타나 실시간 주식 시장 정보를 제공하는 중요한 역할을 합니다​​.

국제 및 지역 변형: 서로 다른 주식 거래소와 지역은 주식 심볼에 대한 자체 명명 규칙과 특정 형식을 가지고 있을 수 있습니다. 예를 들어, 아시아 시장에서는 숫자 기반 주식 심볼이 더 일반적이지만, 미국과 유럽 거래소에서는 문자 기반 심볼이 널리 사용됩니다. 또한, 일부 주식 심볼은 특히 상호 기금과 워런트에 대해 문자와 숫자를 결합할 수도 있습니다​​​​.

이 정보를 바탕으로, 주식 분석 시스템에서 "Ticker" 또는 "Symbol"을 사용하는 것이 더 적절할 수 있습니다. 특히 시스템이 글로벌 청중을 대상으로 하거나 주식 거래에 익숙한 사람들을 위한 것이라면, 이 용어들은 금융 세계에서 널리 인식되고 이해됩니다.


 

GPT가 Ticker와 Symbol 중에 하나를 선택을 못해서, 아무거나 하나 정해서 테이블을 설계해달라고 요청했습니다. 그러자 글로벌 인지도를 고려해 Symbol을 선택한 것을 알 수 있습니다.


기존의 논의를 바탕으로, 주식 분석 시스템을 위한 '종목마스터' 정보를 관리하는 데이터베이스 테이블을 설계할 때, "Ticker" 또는 "Symbol" 중 어느 것을 사용해도 괜찮을 것 같습니다. 두 용어 모두 주식이나 증권을 식별하는 데 널리 사용되고 있으며, 사용자와 시장의 요구에 적합한 용어를 선택하는 것이 중요합니다.

가정해 보자면, "Symbol"이라는 용어를 사용하는 것이 좋을 것 같습니다. "Symbol"은 글로벌한 인식도가 높고, 주식 시장에 익숙한 사용자들에게도 쉽게 이해될 수 있습니다. 이를 바탕으로 종목마스터 테이블을 다음과 같이 설계할 수 있습니다:

  1. 종목 심볼 (Symbol): 주식의 고유 식별자. 예: 'AAPL' (애플), 'MSFT' (마이크로소프트).
  2. 회사명 (CompanyName): 주식을 발행하는 회사의 이름.
  3. 시장 (Market): 주식이 거래되는 시장. 예: 'NASDAQ', 'NYSE'.
  4. 산업 분류 (Industry): 회사가 속한 산업 분류.
  5. 시가총액 (MarketCap): 회사의 시가총액.
  6. 주식 가격 (StockPrice): 현재 주식 가격.
  7. 배당률 (DividendYield): 주식의 배당률.
  8. 연간 수익 (AnnualRevenue): 회사의 연간 수익.
  9. 순이익 (NetIncome): 회사의 순이익.
  10. 주식 수 (SharesOutstanding): 시장에 유통되는 주식 수.

이 테이블은 주식 분석을 위한 핵심적인 데이터를 제공하며, 사용자들이 주식 시장에 대한 효율적인 분석 및 의사결정을 할 수 있도록 도와줍니다. 데이터베이스 설계는 시스템의 목적과 사용자의 요구에 따라 변경될 수 있으며, 이는 단지 기본적인 구조를 제시한 것입니다.


그런데, 설계해준 내용을 보면, 종목마스터라고 하기에는 마스터 성이 아닌 정보(주기적으로 변할 수 있는 정보)가 섞여 있습니다.(물론 최종 개념으로 마스터에 넣어줘도 무방한 정보이긴 합니다.)

어쨋든, 주기적으로 변하는 정보는 분리해서 MySQL용으로 DDL을 부탁했더니 아래와 같이 만들어주네요.

CREATE TABLE StockMaster (
    Symbol VARCHAR(10) PRIMARY KEY,
    CompanyName VARCHAR(100),
    Market VARCHAR(50),
    Industry VARCHAR(100)
);

 

이정도면, 선택이 어려운 용어를 선택할때 나름 잘 사용할 수 있을거 같네요.^^

 

전체 대화 내용은 아래 URL에 있습니다.

https://chat.openai.com/share/698efa8d-3973-4861-9942-c8e10b6bfa14

 

 

불현듯, GPT로 댓글봇을 만들수 있겠네란 생각이 들어서, 한번 만들어 봤습니다.

실제 댓글 작성까지 자동화하려면 좀 복잡해질거 같고, 실제 만들어서 써먹을 생각도 없으므로,

블로그를 크롤링 한후에, OpenAI의 API를 통해 댓글 작성까지 해보도록 코드를 구현해봤습니다.

당연히, 이 역시도 GPT의 도움을 받아 10분만에 코드를 완성했네요.

 

실행 결과는 다음과 같습니다.

https://sweetquant.tistory.com/481 글에 대해, "AI는 도구일 뿐, 우리 자신의 성장과 발전에 초점을 맞춰 활용하자는 말씀에 백프로 공감합니다." 라고 누가봐도 GPT 스러운 댓글을 만들어 주네요.^^

 

 

코드의 내용을 정리하면 다음과 같습니다.

  • OpenAI 클라이언트 설정
    • OpenAI 라이브러리를 사용하여 GPT 모델에 접근하기 위한 클라이언트를 설정합니다.
    • 각자 사용하는 OpenAI API키가 필요합니다.(유료)
  • 댓글 생성 함수 (getReplyFromGPT)
    • 주어진 내용에 대한 댓글을 생성하기 위해 OpenAI의 GPT 모델을 호출하는 함수입니다.
    • 이 함수는 모델을 지정하고, 사용자의 메시지를 바탕으로 챗봇 응답을 생성합니다.
  • 웹 페이지 크롤링
    • requests와 BeautifulSoup 라이브러리를 사용하여 특정 URL의 HTML 내용을 가져오고 파싱합니다.
    • 이 코드에서는 단순하게 티스토리의 특정 페이지만 사용했습니다.
    • 사용하는 블로그에 따라 쉽게 크롤링이 안되거나, 파싱이 안되므로 그에 맞게 코드를 변경해야 합니다.

 

코드는 다음과 같습니다.

# GPT를 사용한 댓글 봇 만들기.
from openai import OpenAI
import requests
import traceback
from bs4 import BeautifulSoup

client = OpenAI(api_key='각자API키를사용')  # API 키 설정

def getReplyFromGPT(content):
    try:
        # 사용하려는 모델 지정
        model = "gpt-4"

        # 댓글을 생성할 메시지 설정
        prompt = f"이 내용에 공감하는 댓글을 짧은 한 문장의 한글로, 진짜 사람같은 느낌으로 만들어줘: {content}"

        # GPT 모델을 사용하여 챗봇 응답 생성
        response = client.chat.completions.create(
            model=model,  # 사용할 모델 지정
            messages=[
                {"role": "user", "content": prompt}
            ]
        )

        res = response.choices[0].message.content
        return res

    except Exception as e:
        print(traceback.format_exc())

# URL 설정
url = 'https://sweetquant.tistory.com/481'

# GET 요청으로 URL에서 데이터 가져오기
response = requests.get(url)

# 응답 상태 확인
if response.status_code == 200:
    # HTML 내용 파싱
    soup = BeautifulSoup(response.content, 'html.parser')

    # 원하는 부분의 내용 추출
    # 이 예제에서는 'div' 태그와 'tt_article_useless_p_margin' 클래스를 기준으로 내용을 추출합니다.
    # 실제 웹사이트 구조에 따라 이 부분은 변경될 수 있습니다.
    main_content = soup.find('div', class_='tt_article_useless_p_margin')
    content = main_content.get_text(strip=True)
    reply = getReplyFromGPT(content)
    print(reply)
else:
    print(f"페이지를 가져오는 데 실패했습니다. 상태 코드: {response.status_code}")

 

 

GPT의 놀라운 통찰력: GPT가 제안한 중국 관광객 마케팅 전략

 

GPT와 대화를 통해 마케팅에 대한 아이디어를 얻어봤습니다.

실제 마케팅 업무를 하시는 분들께는 전혀 놀랍지 않은 사소한 아이디어일 수 있지만, 마케팅을 잘 모르는 사람들에게는 매우 놀라운 아이디어라 생각합니다. 무엇보다 이와 같은 방법으로 통찰력을 얻을 수 있다는 사실에 주목할 필요가 있습니다.

 

여기서 주목할 점은 GPT가 실시간 뉴스 검색을 통해 정보를 얻고, 얻은 정보를 GPT가 활용해 마케팅 전략을 제안했다는 점입니다. GPT-4는 중국 관광객을 대상으로 아래와 같은 마케팅 전략을 제안해줬습니다.

  • 개별 여행객 중심의 상품 구성: 중국 MZ세대가 선호하는 개별적인 여행 경험을 반영하여, 다양한 문화적 체험과 연계된 상품을 제공합니다.
  • SNS 마케팅 강화: 중국인 관광객이 많이 사용하는 SNS 플랫폼을 활용하여, 면세점의 상품과 프로모션을 홍보합니다.
  • 모바일 결제 옵션 확대: 중국 관광객의 모바일 결제 선호도를 고려하여, 다양한 결제 옵션을 제공합니다.
  • 문화 체험 연계 프로모션: 한복 대여, 맛집 투어 등의 체험과 연계된 프로모션을 진행하여, 면세점 방문을 유도합니다.

 

아래는 GPT와의 대화 내용입니다. 이러한 대화 흐름을 이용해, 자신이 필요한 업무에 인간이 놓칠 수 있는 통찰력을 얻을 수 있으리라 생각합니다.

 

가장 먼저, 아래의 첨부 데이터를 GPT에 올렸습니다.

GPT에게 첨부 데이터는 2015년부터 2023년까지의  우리나라에 입국한 국가별 월별 통계 자료라고 알려주었습니다.

월별국가별관광객입국평균.xlsx
0.03MB

 

 

그랬더니, 갑자기 영어로.. 어쩌고 합니다. 한글로 설명을 부탁하니, 아래와 같이 설명해줍니다.


제공하신 엑셀 파일에는 2015년부터 2023년까지 각 월별로 한국을 방문한 각국 국민들의 통계 데이터가 포함되어 있습니다. 각 행은 한 달을 나타내며, 열에는 'TOTAL'이라는 총 방문자 수를 비롯하여 중국, 일본, 미국, 대만, 홍콩, 필리핀, 태국 등 다양한 국가별 방문자 수가 포함되어 있습니다. 또한 자이르, 케이맨제도, 미국인근섬과 같이 덜 일반적인 출신지에 대한 기록도 250개의 열에 걸쳐 포함되어 있습니다.

이 데이터를 통해 원하시는 특정 분석이나 요약을 진행해 드릴까요? 예를 들어, 시간에 따른 추세, 국가별 비교, 연간 요약 등이 있을 수 있습니다.


 

이제, 저는 면세점 직원인데 이 데이터를 이용해 마케팅 분석 보고서를 만들어야 한다고 GPT에게 말했습니다.

아래와 같이 제법 유용한 말들을 해줍니다.


 

  1. 월별 방문자 패턴 분석: 각 월별로 평균 방문자 수를 분석하여 특정 시즌에 방문자 수가 증가하는 경향을 파악합니다. 예를 들어, 여름 휴가철이나 명절 시즌에 방문자 수가 증가하는지 여부를 분석할 수 있습니다.
  2. 주요 국가별 방문자 비율 분석: 중국, 일본, 미국 등 주요 국가의 방문자 수를 전체 방문자 수와 비교하여 그 비율을 분석합니다. 이를 통해 주요 고객층이 어느 국가에 집중되어 있는지 파악할 수 있습니다.
  3. 국가별 월별 방문자 비교: 주요 국가들을 대상으로 각 월별 방문자 수를 비교합니다. 이를 통해 어떤 국가의 방문자들이 특정 시즌에 많이 방문하는지 파악할 수 있습니다.

이러한 분석은 면세점의 재고 관리 및 마케팅 전략 수립에 유용한 정보를 제공할 수 있습니다. ​


 

뭔가 가능한 이야기들인거 같습니다. 그래서 당장 12월 연말에 마케팅을 해야하는 관점으로 분석을 요청했습니다. 아래와 같이 답을 줍니다.


12월 연말 시즌에 대한 분석을 진행한 결과, 다음과 같은 정보를 얻었습니다:

  • 중국: 12월 평균 방문자 수가 가장 많은 국가로, 약 215,139명입니다.
  • 일본: 두 번째로 많은 방문자를 보내는 국가로, 평균 약 185,687명입니다.
  • 미국: 세 번째로 방문자 수가 많으며, 평균 약 50,007명입니다.
  • 대만, 홍콩, 태국: 이 국가들도 각각 평균 40,589명, 33,565명, 32,521명으로 상당한 방문자 수를 기록했습니다.
  • 필리핀, 싱가포르, 말레이시아: 각각 평균 26,942명, 26,936명, 25,960명의 방문자 수를 보입니다.
  • 교포: 평균 19,103명이 방문합니다.

이 데이터를 바탕으로, 연말 마케팅 전략을 수립할 때 중국, 일본, 미국 방문객을 주요 타겟으로 설정하는 것이 좋습니다. 또한 대만, 홍콩, 태국 등의 국가들에 대해서도 특별한 마케팅 활동을 고려할 수 있습니다. 이러한 국가의 방문객들을 위한 맞춤형 프로모션, 특별 할인, 문화적 행사 등을 기획하여 방문객들의 관심을 끌고 매출을 증대시킬 수 있을 것입니다


 

관련 내용을 시각화를 부탁하니 알아서 시각화도 잘 해줍니다. 그런데, GPT-4에서 데이터 분석을 통한 시각화에서는 한글이 깨지는 문제가 있습니다. 그래서 한글 부분은 영어로 번역해서 표현하도록 처리했습니다. (시각화 했던 내용은 저장되어 있지 않아 보여드릴 수가 없네요.)

계속해서 분석을 하다가, 중국 관광객이 많으니 중국인들 대상으로 마케팅을 해야 할거 같다는 생각이 들었습니다.

그래서, 중국 관광객에 대한 최신 인터넷 기사들을 검색해서, 중국 관광객과 관련 높은 키워드를 뽑아달라고 부탁했습니다.

GPT-4에는 현재 빙을 사용한 인터넷 검색 기능이 있어, 이와 같은 검색이 가능합니다. 아래와 같은 결과를 얻었습니다.


최신 중국 관광객에 대한 인터넷 기사를 검토한 결과, 다음과 같은 주요 키워드가 도출되었습니다:

  1. MZ세대 중심의 개별 여행 선호: 중국 MZ세대(1980년대 초~2000년대 초 출생)는 단체 여행보다 개별 여행을 선호하는 경향이 있습니다​​​​​​.
  2. 제주도 방문 증가: 제주도는 중국인 관광객들에게 인기 있는 목적지로, 특히 MZ세대 관광객이 많이 찾고 있습니다. 제주목 관아 등의 문화 명소가 인기를 끌고 있습니다​​​​.
  3. SNS 영향력: 중국 관광객들은 여행지를 선택할 때 SNS의 영향을 크게 받습니다. 특히 샤오홍슈(小红书)와 같은 중국 SNS 플랫폼이 중요한 역할을 합니다​​​​.
  4. 체험 중심 여행 선호: 중국 관광객들은 쇼핑 위주의 단체 관광보다 로컬 체험, 카페투어, 맛집투어 등 체험 중심의 자유 관광을 선호합니다​​.
  5. 모바일 결제 증가: 중국 관광객들 사이에서 모바일 페이를 통한 간편 결제의 사용이 증가하고 있습니다. 이는 소비 패턴의 변화를 반영합니다​​.
  6. 한복 대여 및 문화체험 인기: 중국 관광객들은 한국 전통 문화를 체험하고자 한복 대여 및 문화체험에 관심이 많습니다​​.
  7. 개별 여행지 및 맛집 탐방: 중국 젊은 층은 일정이 짜여진 단체관광보다 개별적으로 특정 여행지나 맛집을 집중적으로 찾는 경향이 있습니다​​.

이러한 키워드들은 중국 관광객을 대상으로 한 마케팅 전략을 수립하거나 워드클라우드를 만드는 데 유용하게 활용될 수 있습니다.


지금까지 만든 차트와 검색 결과를 통해 스토리 있게 발표문을 만들어 달라고 부탁했습니다. 아래와 같이 잘 만들어 주네요.


슬라이드 1: 인사 및 주제 소개

  • "안녕하세요, 오늘 저는 최근 중국 관광객의 트렌드 변화와 이에 따른 우리의 마케팅 전략에 대해 발표하겠습니다."

슬라이드 2: 현재 중국 관광객의 통계 및 변화

  • "먼저, 최근의 통계에 따르면, 중국인 관광객의 수는 올해 약 200만 명에 달할 것으로 예상되며, 이는 큰 취업 유발 효과를 가져올 것입니다​​. 하지만 코로나19 이전과 비교하면, 여전히 회복이 더디게 진행되고 있습니다​​."

슬라이드 3: 중국 MZ세대의 여행 선호도 변화

  • "중요한 변화 중 하나는, 중국 MZ세대가 단체 여행보다 개별 여행을 선호한다는 점입니다​​. 이들은 제주도 같은 특정 목적지를 찾으며, 샤오홍슈와 같은 SNS를 통해 여행지를 결정합니다​​​​."

슬라이드 4: 중국인 관광객의 소비 특성

  • "중국인 관광객은 체험 중심의 여행을 선호하며, 모바일 결제를 증가시키고 있습니다. 이들은 쇼핑보다는 로컬 체험, 카페투어, 맛집투어 등을 선호합니다​​."

슬라이드 5: 마케팅 전략 제안

  • "이러한 트렌드를 바탕으로, 우리는 중국 MZ세대의 개별적인 여행 스타일과 문화 체험에 초점을 맞춘 마케팅 전략을 수립해야 합니다. 예를 들어, 한복 대여 및 문화 체험 서비스를 강화하고, 개별적인 맛집이나 특별한 여행지를 홍보하는 것이 좋습니다​​​​."

슬라이드 6: 결론 및 감사 인사

  • "오늘 제시한 마케팅 전략이 중국 관광객 유치에 크게 기여할 것으로 기대합니다. 감사합니다."

그런데 보고서 내용이, 현재 조사한 내용만 정리되어 있는 느낌입니다.

그래서, 면세점에서 중국 관광객 대상으로 12월 마케팅을 어떻게 해야 할지 전략도 보고서에 추가해달라고 했습니다.

그랬더니, 아래와 같이 마케팅 전략 제안을 해줍니다.


  1. 개별 여행객 중심의 상품 구성: 중국 MZ세대가 선호하는 개별적인 여행 경험을 반영하여, 다양한 문화적 체험과 연계된 상품을 제공합니다.
  2. SNS 마케팅 강화: 중국인 관광객이 많이 사용하는 SNS 플랫폼을 활용하여, 면세점의 상품과 프로모션을 홍보합니다.
  3. 모바일 결제 옵션 확대: 중국 관광객의 모바일 결제 선호도를 고려하여, 다양한 결제 옵션을 제공합니다.
  4. 문화 체험 연계 프로모션: 한복 대여, 맛집 투어 등의 체험과 연계된 프로모션을 진행하여, 면세점 방문을 유도합니다.
  5. 현지화된 고객 서비스 제공: 중국어 서비스, 문화적으로 맞춤형 상담 및 안내를 통해 고객 만족도를 높입니다.

이러한 전략들은 면세점의 매출 증대 및 중국 관광객 유치에 기여할 것으로 기대됩니다.


 

전체 대화 내용은 다음 URL에 있습니다. 이상입니다.

https://chat.openai.com/share/12eaf228-9ced-4d78-9245-ce5f0e24f9fb

 

 

 

P.S. 아래 강의들을 진행하고 있으니, 많은 관심 부탁드립니다.
  - StartUP Tuning For PostgreSQL: PostgreSQL을 활용한 SQL 튜닝 입문 교육
    https://cafe.naver.com/dbian/7181
  - StartUP Tuning For MySQL: MySQL을 활용한 SQL 튜닝 입문 교육
    https://cafe.naver.com/dbian/6958
  - 평생필요한 데이터 분석: 저자 직강!, 주식 데이터를 활용한 SQL 입문자 교육
    https://cafe.naver.com/dbian/7131

 

AI 시대에 가져야 할 우리의 자세

AI 기술이 쏟아지는 시대입니다.

이러한 시대에 우리는 어떤 자세로 AI를 접근해야 할지에 개인적인 생각을 몇 자 적어봅니다.

GPT와 같은 AI 기술들이 기업 환경과 일상 생활에 깊숙이 스며들고 있습니다.

이들 기술은 우리의 업무 방식과 생활 습관에 혁신을 가져오며 새로운 가능성들을 열어가고 있습니다.

우리는 이러한 시대에 살아남기 위해서, 단순히 AI 기능 사용에 만족하지 않고 AI를 통해 자신의 기술을 성장시켜야 합니다.

 

미국의 한 회사는 OpenAI와 라이센스를 맺고, 회사의 문서와 메신저 내용을 GPT로 정리하고 검색하는 시스템을 도입했다고 합니다. 이를 통해, 회사 내부 정보를 물어보면 GPT가 정리하여 알려주고, 담당자까지 찾아준다고 합니다.

이는 대규모 조직에 특히 유용한 기능입니다.

 

국내에서도 변화의 바람이 불고 있습니다. 대기업들이 AI를 이메일 작성에 도입하는 사례가 있으며,

삼성도 사내 업무에 AI를 활용할 것이라는 기사가 나왔습니다.

또한, 토스는 AI를 활용해 사내에서 사용할 아이콘을 만드는 기능을 개발했습니다.

(https://toss.tech/article/ai-graphic-generator-1?utm_source=linkedin&utm_medium=post&utm_campaign=2312_ai-graphic-generator-1 )

특정 플랫폼에서는 사용자 리뷰를 GPT로 분석하여 정리된 리뷰를 제공하기 시작했습니다.


이러한 사례들을 통해 보면, AI는 단순한 유행이 아니라, 우리 일상과 점차 밀접한 기술로 자리 잡을 것으로 보입니다.

이와 같은 환경에서, 우리는 AI를 두려워하기보다는 유용한 도구로 받아들여야 합니다.
그러나 AI에 지나치게 의존해서는 안 됩니다.

최근 GPT에 도입된 연령 확인 메시지는 AI가 단순한 인터넷 검색과는 다른 위험성을 가질 수 있음을 시사합니다.

개인적으로, 개인의 발전을 위해서는 스스로 생각하고 학습하며 경험하는 것이 중요하다고 생각합니다.

특히 학생들에게는 이러한 자기 주도적인 학습이 더욱 중요합니다.

GPT는 개인의 역량을 넘어서는 수준의 결과를 빠르게 만들 수 있도록 도와줍니다.

하지만, 이러한 작업 방식이 과연 우리 인간의 성장에 얼마나 도움이 될지는 의문이 남습니다.

성장하지 않고 GPT에만 의존하는 인간은, 결국 다른 인간으로 쉽게 대체될 수 있습니다.

더 나아가 이러한 인간은 AI로 완전히 대체될 위험성이 큽니다. 그러므로 우리가 AI를 활용하는 방식에 대해 심도 있는 고민이 필요합니다.

 

AI 시대를 맞이해, 우리는 AI 사용법을 먼저 익히는게 아니라, 자신만의 핵심적인 전문성과 기술을 먼저 개발하고 익혀야 합니다.

AI를 단지 결과를 얻기 위한 수단이 아니라, 우리 자신의 성장과 발전을 돕는 방법으로 접근해야 합니다.

AI를 활용한 결과물에만 집중하는 대신, 그 과정에서 우리 자신이 어떻게 성장하고 발전하는지를 고민해야 합니다.

이러한 접근이야 말로, AI 시대를 현명하게 헤쳐 나갈 수 있는 방법이라 생각합니다.

끝으로, 이 글 역시 GPT의 도움을 받아서 다듬었습니다. (제가 전문적인 글쓰기 작가는 아니니까요.^^)

그렇지만, 전체적으로 하고 싶은 이야기의 구성과 기본적은 내용은 제가 직접 정리했고,

글의 내용 역시 제 경험을 바탕으로 했습니다. 이 정도면, GPT의 도움을 받았지만 제 글이라고 말할 수 있지 않을까 생각해 봅니다.

 

첫번째 필드부터, USA, KOREA, Other Countries, Industry, Etc 순서입니다.

대부분, 200일선 밑으로 내련간 ETF들이 많네요. 음. 보면서 생각이 많아지네요.

 

아래와 같은 대화 과정을 거쳐서 만들었습니다.

1. GPT와 대화로 주요 ETF 목록 정리
https://chat.openai.com/share/e4e0980c-27bb-4e8a-b7fc-973a4468ab70

2. GPT와 대화로 시가총액 업데이트 처리.
https://chat.openai.com/share/d47f18af-3ee6-4481-ad7b-163c835dc3b2

3. GPT와 대화로 ETF를 체계적으로 분류하기.
https://chat.openai.com/share/29ea6b4e-8543-4000-bcd3-7b16a60a9312

데이터 수집은 finance-data-reader를 사용, 수집과정은 직접 구현함.

4. GPT와 대화로 보여주는 화면 구성
https://chat.openai.com/share/2758bb3d-c46d-451b-8a11-db37ae4b881f

 

GPT 시대, 관계형 데이터의 재조명

 

빅데이터와 No-SQL 기술의 등장은 우리에게 텍스트, 이미지, 비디오, 음성과 같은 다양한 형태의 데이터를 새로운 방식으로 다루게 했습니다.  이전에는 '데이터'라고 하면 대부분의 사람들이 컬럼과 로우로 구성된 테이블 형태의 '관계형 데이터'를 떠올렸습니다. 하지만 지금은 그 경계가 흐려졌습니다.

 

No-SQL 기술이 주목받을 때, 많은 이들이 관계형 데이터베이스의 중요성이 줄어들 것이라 예상했지만, 그러지 않았습니다. No-SQL은 그 자체로 중요한 기술이지만, 관계형 데이터베이스를 완전히 대체할 수는 없었습니다. 이는 기업의 업무에 필요한 데이터를 다루기에 관계형 구조의 데이터가 효율이 가장 좋기 때문입니다.

그렇다면, 생성형 AI의 선두 주자인 GPT와 같은 기술의 등장은 어떨까요? GPT는 관계형 데이터베이스의 위치에 어떤 영향을 줄까요? 실제로 GPT는 테이블 형태의 데이터를 저장하거나 관리하는 기능에는 한계가 있습니다. 그러나, GPT는 관계형 데이터의 분석 및 해석에 있어 매우 유용한 도구로 활용할 수 있습니다.

기업들이 오랜 시간 동안 쌓아온 관계형 데이터는 이제는 GPT와 같은 기술을 통해 새로운 가치와 인사이트를 제공할 수 있습니다. 따라서 GPT의 등장은 기업들에게 오랜 시간 동안 축적해온 관계형 데이터를 다시 한번 깊게 다뤄볼 기회를 제공하게 될 것입니다.

 

간단한 예를 살펴보도록 하죠. 아래는 제가 사용하는 샘플 DB에서 월별 제품카테고리별 판매 현황을 추출한 SQL입니다.

(기업의 관계형 데이터를 추출해냈다고 생각하면 되는 것이죠.)

SELECT DATE_FORMAT(T1.ORD_DT,'%Y%m') Order_YearMonth
       ,T3.ITM_TP ItemCategory
       ,COUNT(*) OrderQty
       ,SUM(T1.ORD_AMT) OrderAmount_KRW
FROM   MYTUNDB.T_ORD T1
       INNER JOIN MYTUNDB.T_ORD_DET T2 ON (T2.ORD_SEQ = T1.ORD_SEQ)
       INNER JOIN MYTUNDB.M_ITM T3 ON (T3.ITM_ID = T2.ITM_ID)
GROUP BY DATE_FORMAT(T1.ORD_DT,'%Y%m')
       ,T3.ITM_TP
ORDER BY Order_YearMonth, ItemCategory;

위 SQL로 추출한 결과를 아래와 같이 첨부하니, GPT-4를 사용하시는 분들은 똑같이 분석을 해보시기 바랍니다.

OrderData_FromMyTunDB.xlsx
0.01MB

 

위 데이터를 GPT-4를 사용해 분석을 요청해 아래와 같은 결론을 얻었습니다. GPT를 통해 더욱 다양한 분석 기법을 요청할 수도 있습니다. 또한 집요하게 물어볼 수록 우리가 쉽게 얻기 어려운 정보도 얻어낼 수 있습니다.

 

  - 제품 유형별로는 '의류' 카테고리에 더 주력하는 것이 좋을 것 같습니다.
  - 월별 판매 추이를 보면 1월부터 4월까지, 그리고 8월부터 10월까지 판매가 상승하는 추세를 보이므로 이 기간에 판매 활동을 강화하는 것이 좋습니다.
  - 계절별로는 여름에 판매가 가장 활발하므로 여름 시즌에 특히 주의를 기울이면 좋을 것 같습니다.

아래와 같은 시각화 결과도 얻었죠.

 

대화 내용이 궁금하신 분은 아래 글을 참고해주세요.

https://chat.openai.com/share/d7a597c9-dba3-4f07-82ad-f788b942ec59

 

ChatGPT

해당 데이터를 분석해서, 내년에 우리가 어떤 제품에 더 주력하면 좋을지, 또 시기적으로는 언제 준비해야 하는지를 알고 싶어. 가능할까?

chat.openai.com

 

 

이처럼, GPT 시대에도 기업의 관계형 데이터 분석의 중요성은 계속됩니다.

예산 상의 한계로 고가의 분석 도구나 전문 분석가를 고용하지 못했던 기업들에게는 GPT와 같은 AI 도구가 저렴하면서도 효과적인 분석 도구로서 큰 기회가 될 수 있습니다.

더불어, 인간이 놓치기 쉬운 데이터의 패턴이나 정보를 GPT를 통해 발견할 수도 있을 것입니다.

그렇지만, 아쉽게도 한계는 존재합니다. 대량의 데이터 처리는 여전히 제한적이며, 특히 실시간 분석에도 한계가 있습니다. 분석 과정에서 예상치 못한 방향으로 진행될 수도 있어, AI의 분석 결과는 반드시 인간이 검증하고 해석해야 합니다.

마지막으로 강조하고 싶은 것은, 관계형 데이터의 중요성이 여전하다는 것입니다. GPT와 같은 혁신적인 기술의 등장에도 불구하고, 관계형 데이터를 다루는 기초적인 학문인 SQL과 관계형 데이터베이스 기술의 중요성은 여전하므로 지속적인 학습과 연구가 필요하다는 것을 잊지 말아야 합니다.

 

부족한 글이지만 읽어주셔서 감사합니다.^^

 

 

23년 10월 4일, 국내 주식 시장이 많이 빠졌습니다.

빠졌다면, 다시 또 오르겠죠.

지금 상황에서 언제 다시 오를지는 아무도 알수 없죠. 다시 오늘 오를 수도 있고,

오늘 조금 오르다, 다시 밑으로 내려갈수도 있고요.

다만 우리는, 과거 데이터를 통해 현재와 같은 패턴에서 어떤 상황이 있었는지 살펴볼 수 있겠죠.

(물론, 과거가 항상 미래에도 같지는 않겠죠.)

 

 

 

최근에는 GPT의 출현으로 이러한 분석이 매우 매우 간단해졌습니다.

어떻게 보면, 프로그래밍 기술도 필요없나 싶을 정도이겠지만, 자신이 프로그래밍을 할 줄 알아야, GPT와 함께 조화롭게 일을 할 수 있습니다.

GPT에게 KODEX 200 일별종가 데이터를 주고, 다양한 분석을 시도해봤습니다.

아뇨. GPT에게 시켜봤습니다.

 

본격적인 설명에 앞서,

절대 주식 투자나 종목을 추천하는 글이 아닙니다. 이와 같은 관점으로 데이터 분석을 할 수 있다는 점에 초점을 맞쳐주세요. GPT가 처리한 과정에 오류가 있을 수 있으며, 일부 데이터가 불완전할 수도 있습니다.

개인의 투자에는 그 누구도 책임지지 않는다는 것을 명시해주시기 바랍니다.

 

GPT에 분석을 요청한 KODEX 200 주가 데이터는 아래 첨부합니다.

KODEX200_history.csv
0.09MB

 

 

GPT와 함께 대화한 내용은 아래에 있습니다.

https://chat.openai.com/share/381a8ac9-cdff-4a44-8d1f-7cdb13258b4b

 

ChatGPT

복귀후에 대체적으로 상승하는지, 하락하는지, 한번 살펴봐줘, 기간이 필요할거 같은데, 복귀후 약 60일 동안 어떤지 살펴보면 될거 같아.

chat.openai.com

 

 

GPT와 함께 분석한 내용을 요약하면 아래와 같습니다.

어제(2023년 10월 4일) 주가(KODEX 200)의 변동률은 약 −2.65%로 떨어졌습니다.

이보다 더 큰 폭으로 주가가 떨어진 날은 과거에 총 133번 발생했습니다.
주가가 크게 떨어진 후에 이전 가격으로 복귀하는 데 걸린 기간에 대한 통계는 다음과 같습니다:

  • 평균 복귀 기간: 약 40일
  • 중앙값 복귀 기간: 8일
  • 최소 복귀 기간: 1일
  • 최대 복귀 기간: 740일

 

가격 복귀후 분석 결과는 다음과 같습니다:

  • 복귀 후 60일 동안 상승한 경우: 89회
  • 복귀 후 60일 동안 하락한 경우: 44회
  • 평균 상승률: 약 10.27%
  • 평균 하락률: 약 -7.57%

따라서 복귀 후 60일 동안 주가가 상승하는 경우가 하락하는 경우보다 더 많았으며,

평균적으로 상승률이 하락률보다 큰 것을 확인할 수 있습니다.

이를 통해 복귀 후 주가가 상승하는 경향이 있음을 알 수 있습니다.

 

원래 가격으로 복귀후 매수해서 60일 보유한 경우의 손익비와 트레이딩 엣지를 구해봅니다.

손익비 (Profit-Loss Ratio): 약 1.36

트레이딩 엣지 (Trading Edge): 약 0.0437 (4.37%)

MDD (최대 낙폭): 약 61.32%

따라서 복귀 후 60일 동안의 주가 움직임을 기준으로 손익비는 1.36로, 평균적인 상승률이 하락률보다 1.36배 높습니다.

트레이딩 엣지는 4.37%로, 상승한 경우와 하락한 경우의 발생 빈도와 평균 상승/하락률을 고려하여 계산된 예상 수익률입니다. MDD는 61.32%로, 복귀 후 60일 동안의 최대 상승률과 최대 하락률의 차이입니다.

 

 

MySQL을 활용한 SQL 튜닝 입문 강의를 진행합니다. 관심있으신 분은 아래 글을 참고해주세요.

https://cafe.naver.com/dbian/6958

 

StartUPTuning - MySQL을 활용한 SQL 튜닝 입문

「SQL BOOSTER」, 「평생 필요한 데이터 분석」 저자이신 유일환 수석(스윗보스)께서 또 사고를 쳤습니다. 바쁜 프로젝트 일정에도 불구하고 특유의 부지런함으로 새로운 교...

cafe.naver.com

 

 

GPT와 함께, 중국 관광객의 한국 방문 현황을 분석해봤습니다.

(데이터를 수집하고 전처리하는 과정 역시, 별도로 GPT와 선행 진행했습니다.)

 

시각화를 통해 알 수 있는 사실은 중국 관광객은 8월에 주로 한국에 방문을 했다는 기록입니다.

아래 시각화를 진행하면서 가장 재밌었던 포인트는, 중요한 K드라마 반영시기, 올림픽, 사드 이슈 시기를 GPT에 물어봐서 차트에 포함한 것입니다.

그리고, 데이터를 어떻게 가져오면 좋을지 정도(SQL 구현)는 인간이 개입하는게 훨씬 더 자연스러운거 같습니다. 이 마저도 언젠가는 정복될수도 있겠지만요.

 

GPT와의 대화를 통해 어떻게 프로그램을 구현했는지 궁금하신분은 아래 채팅을 참고해주세요.

GPT와의 챗: https://chat.openai.com/share/c89f93e6-8943-4e7e-b7a2-fb62fcf4396b

 

GPT와 함께 구현한 코딩도 아래에 포함합니다.

(데이터 수집과 전처리가 별도로 필요하므로 아래 코드는 개인 환경에서는 실행되지 않습니다. 참고만해주세요.) 

import pymysql
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np

plt.rcParams['font.family'] = 'Malgun Gothic'  # '맑은 고딕'으로 설정
plt.rcParams['axes.unicode_minus'] = False  # 마이너스 부호 문제 해결


def fetch_country_visitor_data(country_name, host, user, password, database):
    # SQL 쿼리
    query = """
    SELECT CONCAT(SUBSTR(T1.YM,1,4),'-',SUBSTR(T1.YM,5,2)) AS `년월`,
           T1.IMMI_NAT_CD AS `국가코드`,
           T1.CNT AS `방문자수`,
           T2.IMMI_NAT_NM AS `국가명`
    FROM HOMEWORKDB.IMMI_YM T1
    INNER JOIN HOMEWORKDB.IMMI_NAT T2 ON (T2.IMMI_NAT_CD = T1.IMMI_NAT_CD)
    WHERE T1.ED_CD = 'E'
    AND    T2.IMMI_NAT_NM = %s
    ORDER BY T1.YM, T1.IMMI_NAT_CD;
    """

    # 데이터베이스 연결 설정
    conn = pymysql.connect(host=host,
                           user=user,
                           password=password,
                           db=database,
                           charset='utf8')

    # pandas를 사용해 SQL 실행
    df = pd.read_sql(query, conn, params=(country_name,))

    conn.close()  # 데이터베이스 연결 종료

    return df

def plot_country_visitor_data(df):
    # 국가명과 시작월, 종료월 추출
    country_name = df['국가명'].iloc[0]
    start_month = df['년월'].iloc[0]
    end_month = df['년월'].iloc[-1]

    # 각 년도별 최대 방문객수 계산
    df['년도'] = df['년월'].str[:4]
    max_visitors_by_year = df.groupby('년도')['방문자수'].max()

    # 년도별 방문객수에 따른 색상 농도 계산
    colors = []
    for year, max_visitors in max_visitors_by_year.iteritems():
        year_data = df[df['년도'] == year]['방문자수']
        year_colors = [plt.cm.Blues(visitors/max_visitors) for visitors in year_data]
        colors.extend(year_colors)

    # 차트 설정
    plt.figure(figsize=(12, 6))
    bars = plt.bar(df['년월'], df['방문자수'], color=colors)

    # 매년 1월에 세로선 추가
    for label in df['년월']:
        if '-01' in label:
            plt.axvline(x=label, color='gray', linestyle='--', linewidth=0.8)

    # 년도별로 가장 많은 방문객이 있는 월의 Bar 위에 월을 text로 표시
    for year, max_visitors in max_visitors_by_year.iteritems():
        year_data = df[df['년도'] == year]
        max_month_row = year_data[year_data['방문자수'] == max_visitors].iloc[0]
        month = max_month_row['년월'][-2:]
        position = df['년월'].tolist().index(max_month_row['년월'])
        plt.text(position, max_month_row['방문자수'] + 1000, month, ha='center')  # 1000은 텍스트를 바로 위에 위치시키기 위한 조정값

    # x축 라벨 조절: 01월 데이터만 라벨에 표시
    x_labels = [label if '-01' in label else '' for label in df['년월']]
    plt.xticks(ticks=df['년월'], labels=x_labels, rotation=45)

    # 이벤트 및 한류 드라마 년월
    events = {
        '2017-03': '사드 문제',
        '2015-06': 'MERS',
        '2020-01': '코로나19',
        '2018-02': '평창 올림픽',
        '2014-09': '인천 아시안 게임',
        '2002-01': '겨울연가',
        '2013-12': '별에서 온 그대',
        '2016-02': '태양의 후예',
        '2016-12': '도깨비'
    }

    for event_date, event_name in events.items():
        if event_date in df['년월'].values:
            # plt.axvline(x=df[df['년월'] == event_date].index[0], color='gray', linestyle='--', alpha=0.7)
            plt.text(df[df['년월'] == event_date].index[0], df[df['년월'] == event_date]['방문자수'].values[0] + 100000,
                     event_name+"["+event_date+"]", rotation=90, verticalalignment='bottom', color='red',fontsize=9)

    plt.xlabel('년월')
    plt.ylabel('방문객')
    plt.title(f'{country_name} ({start_month}~{end_month})')
    plt.tight_layout()
    plt.show()


def show_country_stat(country):
    df = fetch_country_visitor_data(country, 'localhost', 'root', '1qaz2wsx', 'HOMEWORKDB')
    plot_country_visitor_data(df)



if __name__ == '__main__':
    show_country_stat("중국")

 

위 내용은, "GPT 강력해진 데이터분석"이란 주제로 온라인 세미나로 진행했던 내용중 일부입니다. 언제한번 무료로 세미나를 한번 더 해볼까 생각중입니다.

GPT에게 주식 트레이딩 전략에 대해 물어보자!
GPT의 도움을 받아^^, GPT에게 물어볼 수 있도록 간단하게 만들어봤습니다.
종목코드 입력하고, 전략 선택하고 "Ask To GPT"를 누르면 끝~!
 
GPT로 자동 생성된 내용이니, 재미로 봐주시고, 참고 정보로만 사용해주세요.
개인의 투자 결정은 본인에게 책임이 있다는걸 잊지 말아주세요.^^
 
운영 시간은 평일 09시~18시입니다.(개인 비용 털어서 하는거라.. AWS비용 후덜덜)

개인적으로 GPT를 응용한 다양한 앱들이 쏟아지지 않을까 생각이 듭니다. 좀더 기능을 보완하고 연구에 전념하고 싶으나. 먹고 살기 바빠서..ㅜㅜ
 
아래 URL로 접속하시면 됩니다.
http://marketoptimizer.net/AskToGPT

GPT Trading Insights

운영시간: 평일 09~18시 API 사용량에 의해 사용이 제한될 수 있습니다. 전략 선택: RSI 볼린저밴드 MACD Stochastic 이평선추세 OBV

marketoptimizer.net

 
 

 
 

 

SQL과 데이터모델링, Python은 원래 할줄 알았고요.

하지만, 태어나서, HTML과 CSS, JavaScript를 해본적이 없습니다.

웹 서비스를 하나 만들고 싶어도 웹개발자를 구하지 않으면 방법이 없었죠!

그런데, GPT-4의 도움을 받으니... 불가능이 가능으로 바뀌더군요.!

아.. GPT 정녕..!! 대단하네요.

 

GPT의 도움을 받아 만든 마켓옵티마이져입니다.!

 - http://marketoptimizer.net/

 

 

기능은 뭐,, 차차 넣으면 되겠죠.^^ 근데 좀 바쁘네요.^^;;;

MySQL을 위한 테이블 비교 프로그램입니다.

 - MySQL로 구성된 개발과 운영 환경의 테이블이나 컬럼 차이를 비교하기 위해 사용할 수 있습니다.

 - Chat-GPT 4를 사용해 코딩을 진행한 프로그램입니다.

   : 정확히 따지기는 어렵지만, Chat-GPT 4가 50% 이상의 공수는 해준거 같네요.

   : 파이썬을 이용했고요, 개발 기간은 이틀이 채 안되는거 같습니다.

   : 즉흥적으로 개발하다 보니, 더 걸린거 같네요.

   : 설계를 철저히하고 GPT랑 협업을 했다면 더 빨리 개발 할 수 있었을거 같습니다.

 - Windows 환경에서만 사용 가능합니다. 아래 파일을 다운로드한후 압축 풀고 실행하시면 됩니다.

 - 배포, 수정 자유입니다. 단! 상업적 사용은 말아주세요.

 - 매우 간단한 프로그램으로 추가적인 설명은 없습니다.

 

다운로드: https://drive.google.com/file/d/1iKMX2eOUYcinWOJh42tvJDYhkRc1IEZZ/view?usp=sharing 

 

 

이틀째입니다. 이 생산성 대체 무엇인지 모르겠습니다.

지금 만들고 있는건... 제목만 적었네요.^^

불필요한 수작업을 줄이기 위한, 튜닝 결과서를 좀 더 쉽게 만들기 위한 툴입니다.

그런데, 코드가 길어지고 복잡한 프로그램일수록,

GPT도 전체 코드를 고려해서 만들기는 좀 어려운거 같아요,

그런데, 인간의 뇌도 똑같으니까요.

처음 개발할때부터 무작정 개발하는것보다 모듈화를 잘 계획해서 GPT한테 일을 시켜야 하는게 느껴지네요.

인간은 설계와 기능 정리를 철처히 해놓고, 모듈간 어떻게 유기적으로 돌아가게 하게 할지 계획하고

필요한 코드 부분을 부분적으로 GPT를 이용하면 왠만한 복잡한 프로그램은 잘 만들거 같네요.

지금은 설계없이 개발중이라, 꾸역꾸역 하고 있네요.^^(그러다 보니, UI가 좀 정리가 안되네요!)

저 같은 경우는 GPT에게 파이썬 코드를 부탁해 개발을 하고 있습니다.

그런데, 파이썬의 가장 큰 강점중 하나가 강력하고 다양한 라이브러리입니다.

예를 들어, 웹브라우저를 하나 열고 싶다면, 관련 라이브러리를 import해서 호출만 하면 됩니다.

또는, sql로 얻은 결과를 csv로 저장하고 싶다면, 이 역시 라이브러리를 사용하면 매우 간단하게 구현이 가능합니다.

그러다 보니, GPT 역시 라이브러리 호출을 해서 필요한 기능을 간단히 구현할 수 있게 됩니다.

복잡한건 이미 라이브러리로 다 되어 있으니까요.

그래서 개인적으로 생각이 드는건, GPT가 파이썬이라는 언어와 만나서 더 강력해진거 아닌가 생각이 듭니다.

물론 다른 언어도 잘합니다. 하지만 라이브러리 활용 측면에서 파이썬은 GPT가 가장 효율적으로 사용할 수 있는 언어가 아닐까 생각이 듭니다.

그리고, 다행히도(??) SQL 실력은 아직 좀 부족하네요.^^ 언젠가는 이 마저도 따라 잡겠죠.

GPT-4로 튜닝 결과서 툴을 만들어 보고 있습니다. 잘 될까 걱정이 살짝 됩니다.

모듈이 길어지면 길어질 수록, GPT-4도 실수를 많이 하고, 저도 헷갈리기 시작하고요.

작성하는 내용을 잘 끊어서 요청하는 요령이 필요하네요,.

우선지금까지 개발한 화면은 아래와 같습니다.

 

 

GPT-4와의 대화내용은 아래 링크를 참고해주세요.

 

https://chat.openai.com/share/4d5eb583-5abe-4ec1-8a9f-4d87737e0781

 

ChatGPT

A conversational AI system that listens, learns, and challenges

chat.openai.com

 

Chat-GPT4 기능을 활용해 Python으로 MySQL 스키마 비교 프로그램을 만들어봤습니다. (30분 안걸렸네요.)
저는, GPT-4를 사용하면서 아이언맨이 자비스와 대화하면서 뭔가를 만들어 가는 것이 불가능한 일이 아니구나라는 생각이 들었습니다.
정말... 놀라운 생산성이네요.. 제가 직접 코딩한게 하나도 없네요.
어떻게 만들것인지를 대화를 통해 정의해주고, 제가 아는 정보를 약간 알려주고, 만들어준 코드를 카피해서 실행만 했네요.
 
GPT-4를 잘 다루려면 자신이 프로그램 언어를 잘 이해하고 있어야 합니다. 또한, 자신이 해당 분야에 대해 잘 알고 있어야 GPT를 효율적으로 사용할 수 있습니다.
개인적인 생각은 GPT-4가 나왔으니, 프로그래밍을 할 필요가 없는게 아니라, 프로그래밍을 더욱 잘해야 할거 같습니다. GPT-4 역시 에러가 나는 코드를 만들어내므로 이를 확인하고 대처할 수 있어야 합니다. 어쨋든 이정도 생산성이라면, 간단한 코드 대부분은 GPT-4가 대체해줄 가능성이 크기 때문에, 우리는 그보다 좋은 실력과 창의력을 가지고 있을 필요가 있습니다.
 

완성된 다운로드 버젼은 아래를 참고해주세요.
https://sweetquant.tistory.com/m/452

 

DB Object Compare for MySQL

MySQL을 위한 테이블 비교 프로그램입니다. - MySQL로 구성된 개발과 운영 환경의 테이블이나 컬럼 차이를 비교하기 위해 사용할 수 있습니다. - Chat-GPT 4를 사용해 코딩을 진행한 프로그램입니다. :

sweetquant.tistory.com

 

 
아래는 GPT와 함께 대화한 내용입니다.
https://chat.openai.com/share/f932a365-7eb5-42d3-8323-aa1367929632

 

ChatGPT

A conversational AI system that listens, learns, and challenges

chat.openai.com

 
 
만들어진 최종 소스코드는 아래와 같습니다.

import sys
import pymysql
import traceback
import pandas as pd
from PyQt5.QtWidgets import (QApplication, QWidget, QLabel, QLineEdit, QVBoxLayout,
                             QTableWidget, QPushButton, QHBoxLayout, QFormLayout,
                             QMessageBox, QTableWidgetItem)

class SchemaComparer(QWidget):
    def __init__(self):
        super().__init__()
        self.source_conn = None
        self.target_conn = None
        self.init_ui()

    def init_ui(self):
        layout = QVBoxLayout()

        # 소스 DB 정보 입력 폼
        source_db_group = QFormLayout()
        self.source_host = QLineEdit(self)
        self.source_port = QLineEdit(self)
        self.source_dbname = QLineEdit(self)
        self.source_user = QLineEdit(self)
        self.source_password = QLineEdit(self)

        source_db_group.addRow('Host:', self.source_host)
        source_db_group.addRow('Port:', self.source_port)
        source_db_group.addRow('DBName:', self.source_dbname)
        source_db_group.addRow('User:', self.source_user)
        source_db_group.addRow('Password:', self.source_password)

        self.source_connect_btn = QPushButton('Connect to Source DB', self)
        self.source_connect_btn.clicked.connect(self.connect_source_db)
        source_db_group.addRow(self.source_connect_btn)

        # 타겟 DB 정보 입력 폼
        target_db_group = QFormLayout()
        self.target_host = QLineEdit(self)
        self.target_port = QLineEdit(self)
        self.target_dbname = QLineEdit(self)
        self.target_user = QLineEdit(self)
        self.target_password = QLineEdit(self)

        target_db_group.addRow('Host:', self.target_host)
        target_db_group.addRow('Port:', self.target_port)
        target_db_group.addRow('DBName:', self.target_dbname)
        target_db_group.addRow('User:', self.target_user)
        target_db_group.addRow('Password:', self.target_password)

        self.target_connect_btn = QPushButton('Connect to Target DB', self)
        self.target_connect_btn.clicked.connect(self.connect_target_db)
        target_db_group.addRow(self.target_connect_btn)

        # DB 정보 입력 폼 레이아웃
        db_info_layout = QHBoxLayout()
        db_info_layout.addLayout(source_db_group)
        db_info_layout.addLayout(target_db_group)
        layout.addLayout(db_info_layout)



        # 테이블 위젯 생성
        self.table_difference = QTableWidget(self)
        self.column_difference = QTableWidget(self)

        # Table Compare Button
        self.btn_compare_table = QPushButton("테이블비교", self)
        self.btn_compare_table.clicked.connect(self.compare_tables)  # 이벤트 연결
        layout.addWidget(self.btn_compare_table)


        layout.addWidget(QLabel("Table Differences"))
        layout.addWidget(self.table_difference)

        # Column Compare Button
        self.btn_compare_column = QPushButton("컬럼비교", self)
        self.btn_compare_column.clicked.connect(self.compare_columns)
        layout.addWidget(self.btn_compare_column)

        layout.addWidget(QLabel("Column Differences"))
        layout.addWidget(self.column_difference)

        self.setLayout(layout)
        self.setWindowTitle('Schema Comparer')
        self.resize(800, 600)



    def compare_tables(self):
        # 여기서 테이블 비교 로직을 구현할 수 있습니다.
        pass

    def connect_db(self, host, port, dbname, user, password):
        try:
            connection = pymysql.connect(
                host=host,
                port=int(port),
                user=user,
                password=password,
                database=dbname
            )
            return connection, None
        except Exception as e:
            error_msg = f"{str(e)}\n\n{traceback.format_exc()}"
            return None, error_msg

    def connect_source_db(self):
        self.source_conn, error = self.connect_db(
            self.source_host.text(),
            self.source_port.text(),
            self.source_dbname.text(),
            self.source_user.text(),
            self.source_password.text()
        )

        if self.source_conn:
            QMessageBox.information(self, "Success", "Successfully connected to Source DB!")
        else:
            QMessageBox.critical(self, "Error", f"Failed to connect to Source DB. Error:\n{error}")

    def connect_target_db(self):
        self.target_conn, error = self.connect_db(
            self.target_host.text(),
            self.target_port.text(),
            self.target_dbname.text(),
            self.target_user.text(),
            self.target_password.text()
        )

        if self.target_conn:
            QMessageBox.information(self, "Success", "Successfully connected to Target DB!")
        else:
            QMessageBox.critical(self, "Error", f"Failed to connect to Target DB. Error:\n{error}")

    def compare_tables(self):
        try:
            # 소스 및 타겟 DB에서 테이블 목록을 가져옵니다.
            source_df = self.fetch_tables_from_db(self.source_conn)
            target_df = self.fetch_tables_from_db(self.target_conn)

            # 두 DB 간의 차이를 찾습니다.
            source_only = source_df[~source_df['TABLE_NAME'].isin(target_df['TABLE_NAME'])]
            target_only = target_df[~target_df['TABLE_NAME'].isin(source_df['TABLE_NAME'])]

            # 결과를 Table Differences 위젯에 표시합니다.
            self.display_difference(source_only, target_only)

        except Exception as e:
            QMessageBox.critical(self, "Error", f"Error comparing tables:\n{traceback.format_exc()}")

    def fetch_tables_from_db(self, conn):
        query = "SELECT TABLE_NAME FROM information_schema.tables WHERE TABLE_SCHEMA = DATABASE()"
        df = pd.read_sql(query, conn)
        return df

    def display_difference(self, source_only, target_only):
        # 결과 위젯 초기화
        self.table_difference.setRowCount(0)
        self.table_difference.setColumnCount(2)
        self.table_difference.setHorizontalHeaderLabels(['Type', 'Table Name'])

        # OnlySource 테이블을 위젯에 추가
        for index, row in source_only.iterrows():
            row_position = self.table_difference.rowCount()
            self.table_difference.insertRow(row_position)
            self.table_difference.setItem(row_position, 0, QTableWidgetItem("OnlySource"))
            self.table_difference.setItem(row_position, 1, QTableWidgetItem(row['TABLE_NAME']))

        # OnlyTarget 테이블을 위젯에 추가
        for index, row in target_only.iterrows():
            row_position = self.table_difference.rowCount()
            self.table_difference.insertRow(row_position)
            self.table_difference.setItem(row_position, 0, QTableWidgetItem("OnlyTarget"))
            self.table_difference.setItem(row_position, 1, QTableWidgetItem(row['TABLE_NAME']))

    def compare_columns(self):
        try:
            # 소스 및 타겟 DB에서 컬럼 정보 가져오기
            source_df = self.fetch_columns_from_db(self.source_conn)
            target_df = self.fetch_columns_from_db(self.target_conn)

            # 두 DB의 컬럼 정보 차이 찾기
            merged_df = pd.merge(source_df, target_df, on=["TABLE_NAME", "ORDINAL_POSITION"], how="outer",
                                 suffixes=('_src', '_tgt'))
            diff_df = merged_df[merged_df.isnull().any(axis=1)]

            # 결과를 Column Differences 위젯에 표시
            self.display_column_difference(diff_df)

        except Exception as e:
            QMessageBox.critical(self, "Error", f"Error comparing columns:\n{traceback.format_exc()}")

    def fetch_columns_from_db(self, conn):
        query = """
            SELECT TABLE_NAME, COLUMN_NAME, ORDINAL_POSITION
            FROM information_schema.COLUMNS
            WHERE TABLE_SCHEMA = DATABASE()
            ORDER BY TABLE_NAME, ORDINAL_POSITION
        """
        df = pd.read_sql(query, conn)
        return df

    def display_column_difference(self, diff_df):
        self.column_difference.setRowCount(0)
        self.column_difference.setColumnCount(4)
        self.column_difference.setHorizontalHeaderLabels(['Table', 'Position', 'Source Column', 'Target Column'])

        for _, row in diff_df.iterrows():
            row_position = self.column_difference.rowCount()
            self.column_difference.insertRow(row_position)
            self.column_difference.setItem(row_position, 0, QTableWidgetItem(row['TABLE_NAME']))
            self.column_difference.setItem(row_position, 1, QTableWidgetItem(str(row['ORDINAL_POSITION'])))
            self.column_difference.setItem(row_position, 2, QTableWidgetItem(str(row['COLUMN_NAME_src'])))
            self.column_difference.setItem(row_position, 3, QTableWidgetItem(str(row['COLUMN_NAME_tgt'])))



if __name__ == '__main__':
    app = QApplication(sys.argv)
    window = SchemaComparer()
    window.show()
    sys.exit(app.exec_())

 

추가로, 제가 직접 만든 무료 MySQL 튜닝툴을 소개합니다.~

https://sweetquant.tistory.com/457

 

[StartUP Tuning]MySQL,MariaDB SQL 튜닝툴

MySQL과 MariaDB의 SQL 튜닝을 보다 간편하게 진행할 수 있는, SUT(StartUP Tuning)를 소개합니다. SUT는 실제 MySQL 튜닝을 전문적으로 수행하기 위해 직접 만들 툴입니다. 또한 MySQL,MariaDB의 성능 개선 교육

sweetquant.tistory.com

 

TradeOptimizer의 '볼린저밴드 트레이드-Ver01' 전략을 소개합니다. 결과부터 이야기하면, 특정 종목 두 세개를 살펴봤을때, 현재 시점으로는 좋은 전략으로 보이지는 않습니다. 더 좋은 전략이 될 수 있도록 로직 수정을 고려할 필요가 있습니다.

 

본격적인 설명에 앞서 아래 내용을 주의해주세요.

 

  • 특정 종목 추천하거나 투자를 권장하려는 글이 아닙니다. 트레이딩 기술에 대한 일반적인 소개 글입니다.
  • 과거의 데이터가 미래의 결과를 예측하는 기준이 되지 않습니다.
  • 투자 결정은 과거 데이터에만 의존하지 않고 여러 정보와 전략을 고려해야 합니다.
    제공된 정보를 근거로 투자 결정을 내리는 것은 전적으로 투자자의 책임입니다.

 

볼린저밴드 트레이드-Ver01 전략(Bollinger Bands & True Range (TR) 기반 매수/매도 전략)

 

1. 전략 개요

주식 데이터의 Bollinger Bands, True Range(TR) 및 MA100 기울기를 사용하여 매수와 매도 시점을 결정합니다.



2. 기본 로직

  • 전략에 사용하는 값
    • 중간 밴드 (Middle Band): 주가의 20일 이동평균선 계산
    • 상단 밴드 (Upper Band): 중간 밴드 + (20일 주가의 표준편차 x 2)
    • 하단 밴드 (Lower Band): 중간 밴드 - (20일 주가의 표준편차 x 2)
    • True Range (TR): max(오늘의 고가 - 오늘의 저가, 오늘의 고가 - 어제의 종가, 어제의 종가 - 오늘의 저가)의 평균값
    • MA100 Gradient: 100일 이동평균선의 기울기. 이 기울기는 가격 추세의 방향성을 나타낼 수 있습니다.
  • 매수/매도 전략:
    • 매수 조건:
      • 현재 주가가 하단 밴드보다 낮으면 매수를 고려합니다.
      • 이후, 주가가 다시 하단 밴드 위로 올라갈 때 실제 매수 결정을 합니다.
      • 추가적으로, MA100의 기울기가 양수(즉, 긍정적인 추세)일 경우에만 매수를 진행합니다.
    • 매도 조건:
      • 현재 주가가 상단 밴드보다 높으면 매도를 고려합니다.
      • 이후, 주가가 다시 상단 밴드 아래로 내려갈 때 실제 매도 결정을 합니다.
    • 손절 조건:
      • True Range (TR) 값을 사용하여 손절 기준을 정합니다.
      • 만약 매수 포지션이 활성화된 상태에서 주가가 매수 가격에서 TR의 일정 배수만큼 떨어지면 해당 포지션을 매도(손절)합니다.

 

 

위 트레이딩 전략을 이용한, 삼성전자의 2015년부터 2023년 현재(8월14일)까지의 차트를 그려보면 다음과 같습니다.

평균 수익은 10.48%이고, 총 11번의 트레이딩이 있어고 그 중에 4번만 수익을 봤네요.

 

 

 

Kodex 200에 같은 로직을 사용해보면 아래와 같습니다. 2015년부터 2023년 8월까지, 총 10번의 트레이드가 있었고 3번이 수익이 났네요.

 

ATR(Average True Range)을 이용한 손절(Stop Loss) 설정

True Range (변동성)는 주가의 가격 변동을 나타내는 지표입니다. 터틀 트레이딩에서는 최근 N일간의 True Range 평균 값을 사용하여 스톱 로스(손절선)를 설정합니다. (이 방법은 투자보다는 트레이딩에 적합한 손절선 계산 방법입니다.)

 

True Range의 계산 로직은 아래와 같습니다.

  • True Range = max(High - Low, abs(High - Previous Close), abs(Low - Previous Close))

 

위 공식을 풀어보면, 아래 3개 중에 가장 큰 값이 True Range가 됩니다.

  • 오늘고가 - 오늘저가
  • 절댓값(오늘고가 - 전일종가)
  • 절댓값(오늘저가 - 전일종가)

 

실제 삼성전자의 주가를 이용하여 TR(True Range)를 구해보면 아래 엑셀표와 같습니다. 삼성전자의 2023년 8월 10일 TR은 1,100원입니다. 그리고 2023년 8월 10일 ATR(20)은 TR의 최근 20일 평균이므로 1,475원입니다.

 

ATR(20) 값인 1475원을 사용하여 손절선을 설정합니다. 터틀 트레이딩에서는 ATR(20)의 2배를 사용한다고 합니다. 손절금액을 더 크게 설정하려면 배수를 높이면 됩니다. ATR(20)을 이용해 아래와 같이 손절률을 계산할 수 있습니다.(2배 사용)

  • 손절률 = (현재가 - (현재가 - (ATR20 * 2))) / 현재가 * 100

 

삼성전자의 2023년 8월 10일 종가(현재가)가 68,000이므로 위 공식에 대입해 보면 아래와 같이 손절률이 나옵니다.

  • (68000 - (68000 - (1475*2)))/68000 * 100 = 4.33%

 

다음 단계가 매우 중요합니다. 손절률인 4.33%를 사용하여 매수할 수량(금액)을 결정해야 합니다. 먼저 감내 가능한 손실 금액을 결정해야 합니다. 즉, 얼마까지 손실이 허용되는지, 얼마까지 손실이 발생하면 반드시 손절할 것인지를 결정해야 합니다. 감내 가능한 손절 금액은 개인마다 다릅니다. 예를 들어, 한 번의 거래에서 최대 50,000원까지 손실을 감수할 수 있다면, 아래 공식을 사용해 매수 가능한 수량을 계산할 수 있습니다.

  • 매수수량 = 손실가능금액 / (ATR20 * 2)

 

위 공식을 사용해, 삼성전자의 23년 8월 10일 기준으로 손실이 50,000원까지 나도 된다고 가정하면, 아래와 같이 매수 수량을 구할 수 있습니다.

  • 50000 / (1475 * 2) = 17

 

삼성전자의 2023년 8월 10일 종가가 68,000원입니다. 68,000원으로 17주를 사면 총 1,156,000 원이 필요합니다.

삼성전자 17주를 산 후에 -4.33%로 Stop-Loss를 설정합니다. 그러면, 약 50,000원 정도 손실이 되면 자동 손절이 이루어지게 됩니다.

 

이상, ATR을 사용해 손절을 잡는 방법이었습니다.

기회가 된다면 터틀 트레이딩이란 책을 읽어보시기 바랍니다.

이번에는 주식을 어떤 요일에 사는게 좋은지, 과거 데이터를 간단히 집게해보도록 하겠습니다.

 

설명에 앞서 다음과 같은 사항을 기억해 주세요.

 - 본 글은 주식 투자를 권장하거나, 어떤 종목을 추천하기 위한 글이 아닙니다.

 - 본 글을 참고해 발생한 손실에 대해서는 누구도 책임지지 않습니다.

 - 데이터의 정확성은 검토했지만, 다양한 이유로 일부 데이터가 부정확할수도 있으니 양해바랍니다.

 

 

년도별로 요일별 평균 수익률을 차트로 그려보면 다음과 같습니다.

각 요일에 종가(Close-Price)에 사서 다음날 시가(Open-Price)에 매도했다고 가정합니다.

월요일이 높은 년도도 있지만, 2011년에는 화요일이 월등합니다. 월요일은 마이너스이고요.

그리고 2015년에는 수요일이 높은 편으로 보입니다.

 

데이터를 더 갖다가 살펴볼 필요가 있어 보입니다. 예를 들어, 주식 시장의 전체 분위기가 좋으면 주초에 주로 오르고, 시장 분위기가 나쁘면, 주초가 별로인가.. 라는 추측이 듭니다. 나중에 지수 데이터를 연결해서 살펴봐야 할거 같네요.

 

위 차트를 년도 상관없이, 요일별로 평균을 구해보면 아래와 같습니다. 전체 평균을 봤을때는 화요일이 가장 좋네요.!

 

여기서는 각 요일별 종가에 매수한 후에, 다음날 시가에 바로 판 것을 가정했습니다.

이러한 집계 기준을 어떻게 정하는지에 따라 결과가 많이 달라질거 같습니다.

예를 들어, 월요일 종가가 아닌 시가에 샀다고 가정할 수도 있고, 사고 나서 일주일을 보유했다고 가정할 수도 있고요.

그리고 한국 시장이 아니라, 미국이나 중국이라면? 또 다른 결과가 나올까요? 나중에 기회가 되면 해봐야겠습니다.

 

이상입니다.!

역시 SQL로 간단히 구할 수 있습니다.

 - 데이터 추출 기준: 2010년부터 2023년 8월까지의 한국 주식 시장의 주가 데이터

 - 데이터 추출 방법: SQL

WITH W01 AS(
SELECT  T1.STK_CD ,T1.DT
        ,T1.C_PRC B_PRC # BUY when Close price
        ,DAYOFWEEK(T1.DT) WK_NO
        ,CASE DAYOFWEEK(T1.DT) 
              WHEN 2 THEN 'MON'
              WHEN 3 THEN 'TUE'
              WHEN 4 THEN 'WED'
              WHEN 5 THEN 'THU'
              WHEN 6 THEN 'FRI' END WK
		,LEAD(T1.O_PRC) OVER(PARTITION BY T1.STK_CD ORDER BY T1.DT ASC) S_PRC # SELL when next open Price
FROM    HIST_DT T1
WHERE   T1.DT >= '20100101'
AND     T1.DT <= '20141231'
)
SELECT  DATE_FORMAT(T1.DT,'%Y') YY
        ,T1.WK
         ,AVG(ROUND((T1.S_PRC - T1.B_PRC) / T1.B_PRC * 100, 2)) AVG_PROF
         ,COUNT(*) SAMP_CNT
FROM    W01 T1
GROUP BY DATE_FORMAT(T1.DT,'%Y'), T1.WK
ORDER BY DATE_FORMAT(T1.DT,'%Y'), MAX(T1.WK_NO)

 

When is the time of day that working people are most interested in the stock market?

I think it's probably not during the morning hours when the stock market opens.

The stock market that opens after waiting overnight! You can't resist checking it out in the morning, can you?

When I check the stocks that are going up in the morning, my heart flutters. Oh, I should buy before they leave me behind!

When I look at the stocks that fall in the morning, I have a somewhat greedy thought. Oh! It fell, it's the price I've been waiting for. I should buy it!

Whether it's because the price dropped or went up, it seems like the "I have to buy it!" circuit in my brain is highly activated by morning.Not everyone may agree, but it is true that stock trading is most active in the morning when the stock market opens.

So, is it a good choice to buy stocks at the morning price, precisely the opening price (Open-Price)?

Let's find out through simple data aggregation.

 

Before explaining, I want to clarify that this is not an article recommending stock investment or suggesting any specific stock.

Please remember that no one is responsible for any losses incurred in the stocks invested in as a result of this article.

We have verified the accuracy of the data. However, please understand that there may be some inaccuracies due to errors that may have occurred during the data collection process or at the time of data collection.

 

 

Let's use SQL to calculate the average profit rate for the following two trade cases:

  • Case 1: Buy at today's open price and sell at today's close price.
  • Case 2: Buy at today's close price and sell at tomorrow's open price.

Actually, we can't buy and sell stocks every day like in the example above, right? However, if we aggregate the data, we can find some useful information for our investment.

If we use the daily stock price data I have (Korea) and aggregate it by year, the following results are obtained:

Overall, it can be seen that Case2 [Buy at today's closing price and sell at tomorrow's opening price] has a much better average return rate than Case1. Buying at today's opening price and selling at today's closing price, like in Case1, results in mostly negative returns on average.

 

 

This test is completely meaningless for those who invest in value. If you approach investing from a perspective of investing in really good companies, it doesn't matter whether you buy in the morning or in the evening. However, I think this is useful information to refer to when buying stocks.

Conclusion! Buy at the closing price!

The above result can be easily implemented with the following SQL. Are you all studying SQL?

 

WITH W01 AS(
SELECT  T1.STK_CD ,T1.DT ,T1.C_PRC ,T1.O_PRC
		,LEAD(T1.O_PRC) OVER(PARTITION BY T1.STK_CD ORDER BY T1.DT ASC)  AF_O
        ,LEAD(T1.C_PRC) OVER(PARTITION BY T1.STK_CD ORDER BY T1.DT ASC)  AF_C
FROM    HIST_DT T1
WHERE   T1.DT >= '20210101'
 AND     T1.DT <= '20211231'
ORDER BY T1.DT DESC
)
,W02 AS(
SELECT  T1.*
        ,ROUND((T1.C_PRC - T1.O_PRC) / T1.O_PRC * 100,1) `OPEN_CLOSE`
		,ROUND((T1.AF_O - T1.C_PRC) / T1.C_PRC * 100,1) `CLOSE_OPEN`
FROM    W01 T1
)
SELECT  DATE_FORMAT(T1.DT,'%Y')
        ,AVG(`OPEN_CLOSE`) `CASE1_OPEN_CLOSE`
        ,AVG(`CLOSE_OPEN`) `CASE2_CLOSE_OPEN`
        ,COUNT(*) CNT
FROM    W02 T1
GROUP BY DATE_FORMAT(T1.DT,'%Y');

 

 

직장인들이 주식 시장에 관심을 갖고 보는 시간대는 하루중에 언제일까요?

아마도 주식 시장을 여는 아침 시간대가 아닐까 생각이 듭니다.

하룻밤을 기다려야 열리는 주식장! 아침이면 안들어가볼 수 없겠죠?

아침에 들어가서 올라가는 주식들을 보면, 마음이 설렙니다. 어! 날 버리고 가겠는데 사야지!

아침에 들어가서 떨어지는 주식들을 보면, 나름 애리한 생각이 듭니다. 어! 떨어졌네, 기다렸던 가격이야. 사야지!

결국 떨어졌으니까 사고, 오르니까 사고, 어쨌든 아침이면 "사야지!" 라는 뇌회로가 많이 작동하는거 같습니다.

모두가 이렇지는 않겠지만, 실제로도 아침 장이 가장 후끈한 건 사실입니다.

그러면, 주식을 아침 가격, 정확히 말하면 시작가격(Open-Price)에 사는 것이 좋은 선택일까요?

간단한 SQL 집계를 통해 알아보도록 하겠습니다.

 

설명에 앞서, 주식 투자를 권장하거나, 절대 어떤 종목을 추천하기 위한 글이 아닙니다.

본 글로 인해 투자한 종목의 손실에 대해서는 절대 누구도 책임지지 않는다는 것을 기억해주시기 바랍니다.

데이터의 정확성 역시 일차적으로는 검토했지만, 데이터 수집 시점이나 과정에서 발생한 실수로 부정확할수도 있으니 양해바랍니다.

 

 

SQL을 사용해 다음 두 가지 매매(Tradde) 케이스에 대해 평균 수익률을  구해봅니다.

  • Case1: 오늘 시가(Open Price) Buy - 오늘 종가(Close Price) Sell
  • Case2: 오늘 종가(Close Price) Buy - 내일 시가(Open Price) Sell

실제, 위의 예처럼 매일매일 사고 팔수는 없겠죠? 그래도 데이터를 집계해보면, 우리가 투자에 참고할만한 정보가 나온답니다.

제가 보유한 일별 주가 데이터(한국)를 사용해 년별로 집계해보면 아래와 같은 결과가 나옵니다.

전체적으로 Case2[오늘 종가(Close Price) Buy - 내일 시가(Open Price) Sell]가 Case1보다 평균 수익률이 훨씬 좋은 것을 알 수 있습니다. Case1과 같이 오늘시가에 사서 오늘종가에 파는 경우는 평균적으로 마이너스 수익률인 경우가 대부분입니다.

 

가치투자를 하시는 분들께는 이런 테스트는 완전히 무의미합니다. 정말 좋은 기업에 투자 관점을 가지고 접근하시는 거라면 아침에 사든, 저녁에 사든 중요하지 않으니까요. 하지만, 자신이 주식을 살때 한 번쯤은 참고할 만한 정보라고 생각합니다.

결론! 종가에 사자!

 

위 결과는 아래의 SQL로 간단하게 구현할 수 있답니다. 다들, SQL 공부 하고 계시죠?

WITH W01 AS(
SELECT  T1.STK_CD ,T1.DT ,T1.C_PRC ,T1.O_PRC
		,LEAD(T1.O_PRC) OVER(PARTITION BY T1.STK_CD ORDER BY T1.DT ASC)  AF_O
        ,LEAD(T1.C_PRC) OVER(PARTITION BY T1.STK_CD ORDER BY T1.DT ASC)  AF_C
FROM    HIST_DT T1
WHERE   T1.DT >= '20210101'
 AND     T1.DT <= '20211231'
ORDER BY T1.DT DESC
)
,W02 AS(
SELECT  T1.*
        ,ROUND((T1.C_PRC - T1.O_PRC) / T1.O_PRC * 100,1) `오늘시가_오늘종가`
		,ROUND((T1.AF_O - T1.C_PRC) / T1.C_PRC * 100,1) `오늘종가_내일시가`
FROM    W01 T1
)
SELECT  DATE_FORMAT(T1.DT,'%Y')
        ,AVG(`오늘시가_오늘종가`) `CASE1_오늘시가_오늘종가`
        ,AVG(`오늘종가_내일시가`) `CASE2_오늘종가_내일시가`
        ,COUNT(*) CNT
FROM    W02 T1
GROUP BY DATE_FORMAT(T1.DT,'%Y');

 

트레이드 전략 - 위꼬리가 긴 하루 #4


'트레이드 전략 - 위꼬리가 긴 하루'의 마지막 글입니다.
제목에 비해 별 내용이 없는데라고 생각하실 분들도 있을듯 싶네요.
다양한 방법의 하나로 봐주시면 감사하겠습니다.

앞에서 2020년 10월 한달 데이터에 대해서 위꼬리 전략을 적용했을때, TE(Trading Edge)를 구해보니 좋지 않았습니다.
여기서는 기간 조건을 제거하고 모든 데이터에 대해 위꼬리 전략을 적용해봅니다.
간단합니다. 앞의 글의 SQL에서 일자 조건만 제거하면 됩니다. (불행히도, 현재 사용하는 데이터에는 2019년 부터 2021년까지의 데이터만 존재합니다.)
아래와 같이 SQL을 작성하고 실행해봅니다.

WITH R01 AS(
    SELECT T2.STK_CD ,T2.DT BUY_DT ,T2.C_PRC BUY_PRC ,T2.H_L ,T2.UP_TAIL ,T2.H_L_CHG ,T2.UP_TAIL_RT
           ,T3.DT SELL_DT ,T3.C_PRC SELL_PRC
           ,ROUND((T3.C_PRC - T2.C_PRC) / T2.C_PRC * 100,2) PROF_RT # 3일후 매도시 수익률
    FROM   (
            SELECT T1.STK_CD
                   ,T1.DT
                   ,T1.C_PRC
                   ,T1.H_PRC - T1.L_PRC H_L # 고가-저가 길이
                   ,T1.H_PRC - GREATEST(T1.C_PRC,T1.O_PRC) UP_TAIL # 위꼬리 길이
                   ,ROUND((T1.H_PRC - T1.L_PRC) / T1.L_PRC * 100,2) H_L_CHG # 위꼬리 등락률
                   ,ROUND((T1.H_PRC - GREATEST(T1.C_PRC,T1.O_PRC))
                                  / (T1.H_PRC - T1.L_PRC) * 100,2) UP_TAIL_RT # 위꼬리 비율
                   ,T1.STK_DT_NO
            FROM   MYSTKDB.HISTORY_DT T1
            WHERE  1=1
            # AND    T1.DT >= STR_TO_DATE('20201001','%Y%m%d') # 일자 조건 제거
            # AND    T1.DT <  STR_TO_DATE('20201101','%Y%m%d') # 일자 조건 제거
            ) T2
            INNER JOIN MYSTKDB.HISTORY_DT T3 # 위꼬리 발생 3일 후 주가 데이터를 조인
               ON (T3.STK_CD = T2.STK_CD AND T3.STK_DT_NO = T2.STK_DT_NO + 3)
    WHERE   T2.H_L_CHG >= 10 # 고가-저가 등락률이 10% 이상
    AND     T2.UP_TAIL_RT >= 90 # 위꼬리 비율이 90% 이상
    ORDER BY PROF_RT DESC
    )
SELECT  T2.*
        ,ROUND((T2.W_PROF_RT * T2.W_RT) - (ABS(T2.L_PROF_RT)* T2.L_RT),2) TE
FROM    (
		SELECT  DATE_FORMAT(T1.BUY_DT, '%Y') YY # --> 년별
		        ,COUNT(*) TRD_CNT
				,SUM(CASE WHEN T1.PROF_RT >= 1 THEN 1 END) W_CNT
				,ROUND(AVG(CASE WHEN T1.PROF_RT >= 1 THEN T1.PROF_RT END),2) W_PROF_RT
				,ROUND(SUM(CASE WHEN T1.PROF_RT >= 1 THEN 1 END) / COUNT(*) * 100, 2) W_RT
				,SUM(CASE WHEN T1.PROF_RT < 1 THEN 1 END) L_CNT
				,ROUND(AVG(CASE WHEN T1.PROF_RT < 1 THEN T1.PROF_RT END),2) L_PROF_RT
				,ROUND(SUM(CASE WHEN T1.PROF_RT < 1 THEN 1 END) / COUNT(*) * 100, 2) L_RT
		FROM    R01 T1
		GROUP BY DATE_FORMAT(T1.BUY_DT, '%Y') # --> 년별
		) T2
ORDER BY T2.YY ASC
;

YY     TRD_CNT   W_CNT   W_PROF_RT   W_RT    L_CNT   L_PROF_RT   L_RT    TE       
------ --------- ------- ----------- ------- ------- ----------- ------- -------- 
2019   333       176     8.68        52.85   157     -3.67       47.15   285.70   
2020   382       166     8.90        43.46   216     -6.75       56.54   5.15     
2021   291       132     8.16        45.36   159     -5.60       54.64   64.15


결과를 보기 좋게, 년별로 집계헀습니다.
결과를 보면, 2019년에는 TE가 매우 높았으나, 2020년에는 다소 낮은 것을 알 수 있습니다.
비록 3년간의 데이터지만, 년별로 TE를 보면 마이너스가 아니기 때문에, 해당 전략은 좀 더 다듬고 연구해 볼 필요가 있지 않나 생각이 듭니다.
아래와 같은 내용을 추가로 고려해 볼 필요가 있습니다.

  • 종목의 주가(저가주를 제외할까? 저가주만 노릴까?)
  • 거래량(거래량이 폭발적이었는가)
  • 추세(이평선이 상승 추세인가?)
  • 시장의 추세(지수가 상승 중인가?)
  • 손절과 익절
  • 보유 기간을 늘리면? 줄이면?
  • 위꼬리의 비율을 조절한다면?


위와 같은 내용을 추가하는 과정에서 너무 과최적화가 되지 않도록 주의가 필요합니다.
조건이 많아질수록 이도 저도 아닌 결과가 나오는 경우가 많드라고요.
적절하게 조건을 추가해서 더 좋은 TE가 만들어지도록 노력을 해보고, 가능성이 있다면, 파이썬으로 정밀하게 트레이딩을 시뮬레이션 해봐야겠죠.
그리고, TE가 높다고 해서 항상 수익이 아니죠. 마이너스. 구간이 언제나 존재합니다. 그러한 구간에 대한 대응 전략과, 생존 전략이 없다면 트레이드는 금기가 아닐까 싶습니다.

준비한 글은 여기까지입니다. 마지막으로 한 번더!^^;;;
절대 주식 투자나 종목 매매를 권장하는 글이 아닙니다. 기술적인 설명을 위한 글이며, 이 글을 참고해 발생한 투자 손실에는 그 누구도 절대 책임지지 않습니다.

감사합니다.

트레이드 전략 - 위꼬리가 긴 하루 #3

위꼬리가 긴 하루, 세 번째입니다.

이번에는 앞에서 구한 2020년 10월의 트레이드 데이터를 집계처리합니다.
데이터 집계를 통해, 승리할 확률과, 승리했을때의 수익률을 구할 수 있습니다. 반대로 패배의 확률도 알 수 있죠. 데이터 집계를 통해 TE(Trading Edge)를 구할 수 있으며, 이를 통해 과연 사용할 만한 전략인지 고민해볼 수 있습니다.

설명에 앞서.. 또 한 번 거듭 말씀드립니다.
여기서 소개하는 트레이드 전략은 매우 위험하며, 절대 주식 투자나 종목 매매를 권장하는 글이 아닙니다. 기술적인 설명을 위한 글이며, 이 글을 참고해 발생한 투자 손실에는 그 누구도 절대 책임지지 않습니다.

먼저 승리와 패배를 정의해야 합니다. 아래와 같이 정의합니다.

  • 승리: 매도시 1% 이상 수익
  • 패배: 매도시 1% 미만 수익 또는 손실


아래와 같이 SQL을 구현합니다. 앞에 글에서 사용한 SQL을 그대로, WITH R01 AS로 묶은 다음에,
R01에 대해 집계처리하면 됩니다. 집계 처리 과정에서 CASE를 사용해 승리와 패배를 구분합니다.

WITH R01 AS(
    SELECT T2.STK_CD ,T2.DT BUY_DT ,T2.C_PRC BUY_PRC ,T2.H_L ,T2.UP_TAIL ,T2.H_L_CHG ,T2.UP_TAIL_RT
           ,T3.DT SELL_DT ,T3.C_PRC SELL_PRC
           ,ROUND((T3.C_PRC - T2.C_PRC) / T2.C_PRC * 100,2) PROF_RT # 3일후 매도시 수익률
    FROM   (
            SELECT T1.STK_CD
                   ,T1.DT
                   ,T1.C_PRC
                   ,T1.H_PRC - T1.L_PRC H_L # 고가-저가 길이
                   ,T1.H_PRC - GREATEST(T1.C_PRC,T1.O_PRC) UP_TAIL # 위꼬리 길이
                   ,ROUND((T1.H_PRC - T1.L_PRC) / T1.L_PRC * 100,2) H_L_CHG # 위꼬리 등락률
                   ,ROUND((T1.H_PRC - GREATEST(T1.C_PRC,T1.O_PRC))
                                  / (T1.H_PRC - T1.L_PRC) * 100,2) UP_TAIL_RT # 위꼬리 비율
                   ,T1.STK_DT_NO # --> 종목별일자 순번
            FROM   MYSTKDB.HISTORY_DT T1
            WHERE  1=1
            AND    T1.DT >= STR_TO_DATE('20201001','%Y%m%d')
            AND    T1.DT <  STR_TO_DATE('20201101','%Y%m%d')
            ) T2
            INNER JOIN MYSTKDB.HISTORY_DT T3 # 위꼬리 발생 3일 후 주가 데이터를 조인
               ON (T3.STK_CD = T2.STK_CD AND T3.STK_DT_NO = T2.STK_DT_NO + 3)
    WHERE   T2.H_L_CHG >= 10 # 고가-저가 등락률이 10% 이상
    AND     T2.UP_TAIL_RT >= 90 # 위꼬리 비율이 90% 이상
    ORDER BY PROF_RT DESC
    )
SELECT  COUNT(*) TRD_CNT
        ,SUM(CASE WHEN T1.PROF_RT >= 1 THEN 1 END) W_CNT
        ,ROUND(AVG(CASE WHEN T1.PROF_RT >= 1 THEN T1.PROF_RT END),2) W_PROF_RT
        ,ROUND(SUM(CASE WHEN T1.PROF_RT >= 1 THEN 1 END) / COUNT(*) * 100, 2) W_RT
        ,SUM(CASE WHEN T1.PROF_RT < 1 THEN 1 END) L_CNT
        ,ROUND(AVG(CASE WHEN T1.PROF_RT < 1 THEN T1.PROF_RT END),2) L_PROF_RT
        ,ROUND(SUM(CASE WHEN T1.PROF_RT < 1 THEN 1 END) / COUNT(*) * 100, 2) L_RT
FROM    R01 T1
;

TRD_CNT   W_CNT   W_PROF_RT   W_RT    L_CNT   L_PROF_RT   L_RT    
--------- ------- ----------- ------- ------- ----------- ------- 
28        10      7.17        35.71   18      -4.91       64.29


위 SQL을 통해 얻은 결과의 정보를 정리해보면 아래와 같습니다.

  • TRD_CNT: 매매 횟수
  • W_CNT: 승리 횟수
  • W_PROF_RT: 승리시 평균 수익률
  • W_RT: 승률
  • L_CNT: 패패 횟수
  • L_PROF_RT: 패배시 평균 수익률
  • L_RT: 패율

위 결과를 통해, 승리시 평균적으로 7.17%의 수익이 났으며, 승률이 35.71%인 것을 알 수 있습니다.
반대로 패배시 평균 수익은 -4.91%이며, 패배할 확률이 64.29%나 됩니다.
이 내용을 가지고 '터틀 트레이딩'에서 설명하는 TE(Trading Edge)를 구해보면 아래와 같습니다.

  • TE = (승리시 평균수익 * 승률) - (절댓값(패배시 평균 수익) * 패율)
  • TE = (7.17 * 35.71) - (ABS(4.91) * 64.29) = -59.62


TE가 마이너스(-59.62)라는 것은 트레이드를 할 수록 손해볼 확률이 높다는 뜻입니다.
하지만, 저희는 단지 2020년 10월에 대해서만 TE를 산출했습니다.
그러므로 가능한 많은 데이터에 위꼬리 전략을 적용해 TE를 구해볼 필요가 있습니다.
이와 관련해서는 다음 글에서 살펴보도록 하겠습니다.

끝으로, TE까지 구하는 SQL을 만들어보면 아래와 같습니다. 기존의 집계한 데이터를 한 단계 밑으로 인라인 뷰 처리한 후에 TE 고식을 적용합니다.

WITH R01 AS(
    SELECT T2.STK_CD ,T2.DT BUY_DT ,T2.C_PRC BUY_PRC ,T2.H_L ,T2.UP_TAIL ,T2.H_L_CHG ,T2.UP_TAIL_RT
           ,T3.DT SELL_DT ,T3.C_PRC SELL_PRC
           ,ROUND((T3.C_PRC - T2.C_PRC) / T2.C_PRC * 100,2) PROF_RT # 3일후 매도시 수익률
    FROM   (
            SELECT T1.STK_CD
                   ,T1.DT
                   ,T1.C_PRC
                   ,T1.H_PRC - T1.L_PRC H_L # 고가-저가 길이
                   ,T1.H_PRC - GREATEST(T1.C_PRC,T1.O_PRC) UP_TAIL # 위꼬리 길이
                   ,ROUND((T1.H_PRC - T1.L_PRC) / T1.L_PRC * 100,2) H_L_CHG # 위꼬리 등락률
                   ,ROUND((T1.H_PRC - GREATEST(T1.C_PRC,T1.O_PRC))
                                  / (T1.H_PRC - T1.L_PRC) * 100,2) UP_TAIL_RT # 위꼬리 비율
                   ,T1.STK_DT_NO # --> 종목별일자 순번
            FROM   MYSTKDB.HISTORY_DT T1
            WHERE  1=1
            AND    T1.DT >= STR_TO_DATE('20201001','%Y%m%d')
            AND    T1.DT <  STR_TO_DATE('20201101','%Y%m%d')
            ) T2
            INNER JOIN MYSTKDB.HISTORY_DT T3 # 위꼬리 발생 3일 후 주가 데이터를 조인
               ON (T3.STK_CD = T2.STK_CD AND T3.STK_DT_NO = T2.STK_DT_NO + 3)
    WHERE   T2.H_L_CHG >= 10 # 고가-저가 등락률이 10% 이상
    AND     T2.UP_TAIL_RT >= 90 # 위꼬리 비율이 90% 이상
    ORDER BY PROF_RT DESC
    )
SELECT  T2.*
        ,ROUND((T2.W_PROF_RT * T2.W_RT) - (ABS(T2.L_PROF_RT)* T2.L_RT),2) TE
FROM    (
		SELECT  COUNT(*) TRD_CNT
				,SUM(CASE WHEN T1.PROF_RT >= 1 THEN 1 END) W_CNT
				,ROUND(AVG(CASE WHEN T1.PROF_RT >= 1 THEN T1.PROF_RT END),2) W_PROF_RT
				,ROUND(SUM(CASE WHEN T1.PROF_RT >= 1 THEN 1 END) / COUNT(*) * 100, 2) W_RT
				,SUM(CASE WHEN T1.PROF_RT < 1 THEN 1 END) L_CNT
				,ROUND(AVG(CASE WHEN T1.PROF_RT < 1 THEN T1.PROF_RT END),2) L_PROF_RT
				,ROUND(SUM(CASE WHEN T1.PROF_RT < 1 THEN 1 END) / COUNT(*) * 100, 2) L_RT
		FROM    R01 T1
		) T2
;

TRD_CNT   W_CNT   W_PROF_RT   W_RT    L_CNT   L_PROF_RT   L_RT    TE       
--------- ------- ----------- ------- ------- ----------- ------- -------- 
28        10      7.17        35.71   18      -4.91       64.29   -59.62


감사합니다.

트레이드 전략 - 위꼬리가 긴 하루 #2

지난 글에 이어서 '위꼬리가 긴 하루'를 계속해서 분석해보겠습니다.

https://sweetquant.tistory.com/374

트레이드 전략 - 위꼬리가 긴 하루 #1

트레이드 전략 - 위꼬리가 긴 하루 #1 최근 매매에 들어간 종목 세 개 정도가 계속해서 위꼬리가 나오네요. 결과적으로는 매우 않좋게 마무리가 되었습니다. ㅠㅠ. 세 종목이 다.!!!! 그래서, '위꼬

sweetquant.tistory.com


지난 글에서는 2020년 10월 중에 위꼬리가 긴 데이터를 검색했습니다. 이번에는 위꼬리가 긴 종목을 매도했을 때에 수익률이 어떤지까지 연결해보려고 합니다.
매매를 하기 위해서는 매수(Buy) 시점, 매도(Sell) 시점을 정해야 합니다. 매수 시점은 앞에 글에서 구한 위꼬리가 나온 날의 종가입니다. 매도 시점은 간단하게 3일 후 종가로 하도록 하겠습니다.
지난 글의 SQL에 아래와 같이 3일 후 종가를 가져오도록 처리하면 됩니다. SQL이 좀 길어졌지만 겁먹지 말고 천천히 따라서 입력해보시기 바랍니다.

SELECT  T2.STK_CD ,T2.DT BUY_DT ,T2.C_PRC BUY_PRC ,T2.H_L ,T2.UP_TAIL ,T2.H_L_CHG ,T2.UP_TAIL_RT
        ,T3.DT SELL_DT ,T3.C_PRC SELL_PRC
        ,ROUND((T3.C_PRC - T2.C_PRC) / T2.C_PRC * 100,2) PROF_RT # 3일후 매도시 수익률
FROM    (
        SELECT  T1.STK_CD
                ,T1.DT
                ,T1.C_PRC
                ,T1.H_PRC - T1.L_PRC H_L # 고가-저가 길이
                ,T1.H_PRC - GREATEST(T1.C_PRC,T1.O_PRC) UP_TAIL # 위꼬리 길이
                ,ROUND((T1.H_PRC - T1.L_PRC) / T1.L_PRC * 100,2) H_L_CHG # 위꼬리 등락률
                ,ROUND((T1.H_PRC - GREATEST(T1.C_PRC,T1.O_PRC))
                   / (T1.H_PRC - T1.L_PRC) * 100,2) UP_TAIL_RT # 위꼬리 비율
                ,T1.STK_DT_NO # --> 종목별일자 순번
        FROM    MYSTKDB.HISTORY_DT T1
        WHERE   1=1
        AND     T1.DT >= STR_TO_DATE('20201001','%Y%m%d')
        AND     T1.DT <  STR_TO_DATE('20201101','%Y%m%d')
        ) T2 INNER JOIN MYSTKDB.HISTORY_DT T3 # 위꼬리 발생 3일 후 주가 데이터를 조인
             ON (T3.STK_CD = T2.STK_CD AND T3.STK_DT_NO = T2.STK_DT_NO + 3)
WHERE   T2.H_L_CHG >= 10 # 고가-저가 등락률이 10% 이상
AND     T2.UP_TAIL_RT >= 90 # 위꼬리 비율이 90% 이상
ORDER BY PROF_RT DESC
;


여기서 포인트는 바로 STK_DT_NO입니다. 해당 컬럼은 종목별 일자별 순번이 저장된 컬럼입니다. 해당 컬럼을 이용하면 해당 종목의 3일후 데이터를 가져올 수 있습니다. 날짜 계산을 통해서도 처리할 수 있겠지만 휴일 처리가 매우 복잡해집니다. 그러므로 이와 같은 종목별 순번을 사용하면 손쉽게 몇일 후, 몇일 전 데이터를 조인으로 처리할 수 있습니다(이와 같은 방법을 이미 아시는 분들도 있겠지만, 솔직히 소문내지 않고 저만 알고 싶은 방법입니다.)
위 SQL의 결과를 살펴보면 아래와 같습니다.

STK_CD   BUY_DT       BUY_PRC     H_L         UP_TAIL     H_L_CHG   UP_TAIL_RT   SELL_DT      SELL_PRC    PROF_RT   
-------- ------------ ----------- ----------- ----------- --------- ------------ ------------ ----------- --------- 
013700   2020-10-27   1910.000    275.000     255.000     14.55     92.73        2020-10-30   2415.000    26.44     
052900   2020-10-20   1850.000    410.000     380.000     22.53     92.68        2020-10-23   2015.000    8.92      
025880   2020-10-30   2095.000    255.000     245.000     12.23     96.08        2020-11-04   2230.000    6.44      
... 생략 ... 
033660   2020-10-23   13450.000   1350.000    1250.000    10.07     92.59        2020-10-28   12200.000   -9.29     
090710   2020-10-30   1430.000    240.000     230.000     16.90     95.83        2020-11-04   1285.000    -10.14    
289080   2020-10-14   7120.000    710.000     660.000     10.04     92.96        2020-10-19   6040.000    -15.17

가장 위에 있는 '013700' 종목은 3일 후 수익률이 무려 26.44 %입니다. 행복한 결말이죠. 하지마 가장 아래에 있는 '289080'은 3일 후에 무려 15.17%를 손해봅니다. 불행한 결말이죠. 이처럼 주식 트레이딩에는 행복과 불행이 항상 공존합니다. 행복만 바라보고 트레이드를 한다면 불행의 쓰디쓴 맛을 보게 됩니다.
실제로도 그런지 차트를 통해 확인해보도록 하겠습니다. 이처럼 데이터 분석과 전략 생성을 위해서는 중간 중간 데이터 확인을 해야 합니다.
먼저 013700의 차트를 살펴보면 아래와 같습니다. 3일후 주가가 오른 것을 알 수 있습니다.


이번에는 289080의 차트를 살펴봅니다. 3일후 주가가 떨어진것을 알 수 있습니다.


오늘은 위꼬리가 발생한 종목의 3일후 주가를 연결하고 수익률까지 구해봤습니다. 다음 글에서는 데이터 집계를 통해 트레이딩 엣지(Trading Edge, TE)를 구해보도록 하겠습니다.
다시 한 번 말씀드리지만,
소개하는 트레이드 전략은 매우 위험합니다. 주식 차트의 봉 모양을 보고 매매를 한다는 것 자체가 매우 위험한 방법입니다. 절대 주식 투자나 종목 매매를 권장하는 글이 아닙니다. 기술적인 설명을 위한 글이며, 이 글을 참고해 발생한 투자 손실에는 그 누구도 절대 책임지지 않습니다.

감사합니다.


트레이드 전략 - 위꼬리가 긴 하루 #1

최근 매매에 들어간 종목 세 개 정도가 계속해서 위꼬리가 나오네요.
결과적으로는 매우 않좋게 마무리가 되었습니다. ㅠㅠ. 세 종목이 다.!!!!
그래서, '위꼬리'를 신호로 삼아 매매를 하면 어떻게 될까를 분석해볼까 합니다.
더불어, 저의 경우에는 트레이드 전략을 어떻게 만들고 있는지도 간단히 참고하실 수 있을거 같습니다.

우선 여기서 소개하는 트레이드 전략은 매우 위험합니다. 주식 차트의 봉 모양을 보고 매매를 한다는 것 자체가 매우 위험한 방법입니다.
본격적인 설명에 앞서, 절대 주식 투자나 종목 매매를 권장하는 글이 아닙니다. 기술적인 설명을 위한 글이며, 이 글을 참고해 발생한 투자 손실에는 그 누구도 절대 책임지지 않습니다.

여기서 사용한 테스트 데이터는 '평생 필요한 데이터 분석'의 주식 데이터입니다. 아래에서 다운로드 할 수 있습니다.
- https://blog.naver.com/ryu1hwan/222664620661

해당 DB에는 2019년부터 2021년말까지의 일별 주가 데이터가 존재합니다. 또한 일부 종목은 없을수도 있지만, 대부분의 종목은 있다고 보시면 됩니다.

먼저 '위꼬리 긴 하루'를 아래와 같이 정의합니다. (아래 내용은 각자 자유롭게 기준을 만들수 있겠죠.)

  • 고가-저가 등락률이 10% 이상이면서, 위꼬리 길이가 고가-저가 길이의 90% 이상을 차지할 것
  • 고가-저가 길이(H_L): 고가 - 저가
  • 위꼬리 길이(UP_TAIL): 고가 - GREATEST(종가,시가)
  • 고가-저가 등락률(H_L_CHG): (고가 - 저가) / 저가 * 100
  • 위꼬리 길이 비율(UP_TAIL_RT): 위꼬리 길이 / 고가-저가 길이 * 100

2020년 10월 주가 데이터 중에서 위 조건에 맞는 데이터를 조회해봅니다. 아래와 같습니다.

SELECT T2.*
FROM   (
       SELECT  T1.STK_CD
               ,T1.DT
               ,T1.C_PRC
               ,T1.H_PRC - T1.L_PRC H_L # 고가-저가 길이
               ,T1.H_PRC - GREATEST(T1.C_PRC,T1.O_PRC) UP_TAIL # 위꼬리 길이
               ,ROUND((T1.H_PRC - T1.L_PRC) / T1.L_PRC * 100,2) H_L_CHG # 위꼬리 등락률
               ,ROUND((T1.H_PRC - GREATEST(T1.C_PRC,T1.O_PRC))
                    / (T1.H_PRC - T1.L_PRC) * 100,2) UP_TAIL_RT # 위꼬리 비율
        FROM   MYSTKDB.HISTORY_DT T1
        WHERE  1=1
        AND    T1.DT >= STR_TO_DATE('20201001','%Y%m%d')
        AND    T1.DT <  STR_TO_DATE('20201101','%Y%m%d')
        ) T2
WHERE   T2.H_L_CHG >= 10 # 고가-저가 등락률이 10% 이상
AND     T2.UP_TAIL_RT >= 90 # 위꼬리 비율이 90% 이상
;

STK_CD   DT           C_PRC       H_L         UP_TAIL     H_L_CHG   UP_TAIL_RT   
-------- ------------ ----------- ----------- ----------- --------- ------------ 
001840   2020-10-06   5540.000    570.000     540.000     10.34     94.74        
054450   2020-10-08   15450.000   2450.000    2250.000    16.07     91.84        
007210   2020-10-12   2010.000    205.000     190.000     10.28     92.68    
... 생략



가장 위에 있는 '001840'이란 종목이 2020년 10월 6일에 위꼬리가 만들어진 것으로 나왔습니다. 실제 그랬는지 차트를 통해 살펴봐야겠죠. 차트를 확인해 보니, 아래와 같이 10월 6일에 위꼬리가 긴 음봉이 만들어졌네요. 그 후, 4일 후 정도까지는 약간 올랐지만 쭉 하락세네요.




결과의 두 번째인 '054450'도 살펴보죠. 10월 8일에 위꼬리가 긴 양봉이 만들어 졌네요. 마찬가지로 잠깐 올랐다가 쭈욱 하락세네요.



이처럼 SQL만 이용해서, 특정 패턴의 차트를 찾아낼 수 있습니다. 오늘은 여기까지 살펴보고, 다음 글에 이어서 계속 설명하도록 하겠습니다. 감사합니다.

오늘은 가져온 파이썬의 pykrx를 사용해 주가 이력 정보를 가져오고,

pymysql을 사용해, 가져온 주가 정보를 개인용 주식 DB에 insert까지 해보도록 하겠습니다.

 

 

먼저 아래를 참고해 테이블을 생성합니다.

CREATE TABLE MYTRDDB.KRX_HIST_DT(
   STK_CD VARCHAR(40) NOT NULL
   ,DT DATE NOT NULL
   ,O_PRC DECIMAL(18,3)
   ,H_PRC DECIMAL(18,3)
   ,L_PRC DECIMAL(18,3)
   ,C_PRC DECIMAL(18,3)
   ,VOL DECIMAL(18,3)
   ,CHG_RT DECIMAL(18,3)
   ,PRIMARY KEY(STK_CD, DT)
);

 

간단히 dataframe 관련된 명령어 몇 개를 살펴봅니다.

# Dataframe의 Index 값 중에, 최대값, 최소값 가져오기.
df.index.min()
df.index.max()

# Dataframe의 Index 값을 loop로 순차적으로 가져오기.
# 가저온 Index 값을 이해용해 해당 로우의 데이터를 가져오기.
for ix in df.index:
    print(ix)
    print(df.loc[ix].시가)
    print(df.loc[ix].고가)

 

최종 아래와 같은 소스코드를 통해 삼성전자(005930)의 주가 정보를 가져와, 우리가 위에서 만들었던 MYSTKDB.KRX_HIST_DT에 입력할 수 있습니다.

기존에 클래스내에 static으로 만들었던 함수는, static을 모두  제거했습니다.

get_stock_history에서 새로 만든 함수인, insert_hist_dt를 호출하도록 구성되어 있습니다.

get_stock_history에서는 pykrx를 통해, 데이터프레임에 주가 정보를 입력하고,

insert_hist_dt에서는 데이터프레임을 넘겨받아 주가 데이터를 insert합니다.

이때, 기존에 입력된 구간과 겹쳐지지 않도록 delete를 처리합니다.

import pymysql
from pykrx import stock
from datetime import datetime, timedelta


class MyCollectKRX:
    def get_stock_master(self, _market):
        stk_master = []
        for stk_cd in stock.get_market_ticker_list(market=_market):
            stk_nm = stock.get_market_ticker_name(stk_cd)
            stk_master.append((stk_cd, stk_nm, _market))
        return stk_master

    def get_stock_history(self, _stk_cd, _fr_ymd, _to_ymd):
        if _to_ymd == '':
            _to_ymd = datetime.now().strftime('%Y%m%d')
        if _fr_ymd == '':
            _fr_ymd = (datetime.strptime(_to_ymd, '%Y%m%d') - timedelta(weeks=54)).strftime('%Y%m%d')

        df = stock.get_market_ohlcv(_fr_ymd, _to_ymd, _stk_cd)

        self.insert_hist_dt(_stk_cd, df)

    def insert_hist_dt(self, _stk_cd, _df):
        # MySQL 연결 객체 생성
        myConn = pymysql.connect(user='???', password='???', host='localhost', port=3306,
                                 charset='utf8', database='MYTRDDB')
        myCursor = myConn.cursor()
        _fr_ymd = _df.index.min()
        _to_ymd = _df.index.max()
        sql_del = "DELETE T1 FROM MYTRDDB.KRX_HIST_DT T1 WHERE T1.STK_CD = %s AND T1.DT BETWEEN %s AND %s"
        myCursor.execute(sql_del, (_stk_cd, _fr_ymd, _to_ymd))

        for ix in _df.index:
            sql_ins = "INSERT INTO MYTRDDB.KRX_HIST_DT(STK_CD ,DT ,O_PRC ,H_PRC ,L_PRC ,C_PRC ,VOL ,CHG_RT)"
            sql_ins = sql_ins + "               VALUES(%s     ,%s ,%s    ,%s    ,%s    ,%s    ,%s  ,%s)"
            myCursor.execute(sql_ins, (
            _stk_cd, ix, _df.loc[ix].시가, _df.loc[ix].고가, _df.loc[ix].저가, _df.loc[ix].종가, _df.loc[ix].거래량, _df.loc[ix].등락률))

        myConn.commit()
        myCursor.close()
        myConn.close()


if __name__ == '__main__':
    myCollectKRX = MyCollectKRX()
    stk_cd = '005930'
    myCollectKRX.get_stock_history(_stk_cd=stk_cd, _fr_ymd='', _to_ymd='')
    print('작업 완료')

 

현재 작성된 모듈은 mysql의 연결과 처리에 대한 모듈이 모두 포함되어  있습니다.

나중에 mysql 관련된 내용은 별도 클래스로 만들어 분리하는 것이 더욱 좋습니다.

오늘은 여기까지입니다.

PyKRX 를 이용해 주가 이력 정보를 가져오는 방법입니다.

 

이전 글에서는 PyKRX로 종목 마스터 정보를 가져왔습니다.(아래 링크 참고)

https://sweetquant.tistory.com/368

 

PyKRX - 주식 마스터 정보 가져오기

자신의 DB에 주식 마스터 테이블을 만들고, 파이썬에서 pykrx 모듈을 사용해 주식 마스터 정보를 가져오는 과정입니다. pykrx는 github에서 공개하고 있으며, 한국의 주식 정보를 쉽게 가져다 쓸수 있

sweetquant.tistory.com

 

 

아래와 같이 get_market_ohlcv(시작일, 종료일, 종목코드) 를 호출하면 Dataframe 형태로 주가 이력 정보를 가져옵니다.

조금더, 개발이 편하게 하기 위해서 별도의 get_stock_history라는 함수를 만들었습니다.

get_stock_history는 종목코드, 시작일, 종료일을 입력 받습니다. 만약에 시작일과 종료일이 비어 있으면, 이전 54주전부터 현재까지로 시작일과 종료일을 설정하도록 구성했습니다.

    def get_stock_history(_stk_cd, _fr_ymd, _to_ymd):
        if _to_ymd == '':
            _to_ymd = datetime.now().strftime('%Y%m%d')
        if _fr_ymd == '':
            _fr_ymd = (datetime.strptime(_to_ymd,'%Y%m%d') - timedelta(weeks=54)).strftime('%Y%m%d')

        df = stock.get_market_ohlcv(_fr_ymd, _to_ymd, _stk_cd)
        return df

 

날짜 관련 처리는 아래 글을 간단히 참고합니다.

https://sweetquant.tistory.com/371

 

Python 기초 - 날짜 다루기(문자에서 날짜로, 날짜에서 문자로, 날짜 계산)

Python 기초 - 날짜 다루기 간단한 날짜 다루는 방법입니다. datetime 라이브러리를 사용합니다. now: 현재 날짜, 시간을 가져옵니다. strftime: 날짜 데이터를 문자로 변형합니다. strpime: 문자 데이터를

sweetquant.tistory.com

 

get_stock_history 함수를 기존의 MyCollectKRX 클래스에 추가합니다. 아래와 같습니다.

from pykrx import stock
from datetime import datetime, timedelta

class MyCollectKRX:
    @staticmethod
    def get_stock_master(_market):
        stk_master = []
        for stk_cd in stock.get_market_ticker_list(market=_market):
            stk_nm = stock.get_market_ticker_name(stk_cd)
            stk_master.append((stk_cd, stk_nm, _market))
        return stk_master

    @staticmethod
    def get_stock_history(_stk_cd, _fr_ymd, _to_ymd):
        if _to_ymd == '':
            _to_ymd = datetime.now().strftime('%Y%m%d')
        if _fr_ymd == '':
            _fr_ymd = (datetime.strptime(_to_ymd,'%Y%m%d') - timedelta(weeks=54)).strftime('%Y%m%d')

        df = stock.get_market_ohlcv(_fr_ymd, _to_ymd, _stk_cd)
        return df


if __name__ == '__main__':
    myCollectKRX = MyCollectKRX()
    df = myCollectKRX.get_stock_history(_stk_cd='005930', _fr_ymd='', _to_ymd='')
	print(df)
    print('작업 완료')

 

위에서는 종목코드(_sstk_cd)에 005930을 넘겼습니다. 삼성전자입니다. 실행해보면, 아래와 같이 데이터프레임 형태의 결과를 얻을 수 있습니다.

 

날짜        시가    고가   저가    종가   거래량     거래대금    등락률                                              
2021-11-12  70200  70900  69900  70600  10087450  711487813500  1.00
2021-11-15  71700  71900  70900  71400  12420710  888880719596  1.13
2021-11-16  71500  72000  71300  71300  10919239  782118934000 -0.14

 

실행해보면 알겠지만, PyKRX에서 주가 이력을 가져오는 속도가 빠르지는 않습니다.

그러므로 자주 실행하면, PyKRX를 사용하는 다른 사람들에게 피해가 될 수 있으니,, 알아서 적당히 적절히 실행할 필요가 있습니다. 또한 한번 조회한 데이터는 개인 DB를 구축해서 관리할 필요가 있습니다. 관련해서는 다음에 글을 올려볼까 합니다.

 

Python 기초 - 날짜 다루기

 

간단한 날짜 다루는 방법입니다. datetime 라이브러리를 사용합니다.

 

now: 현재 날짜, 시간을 가져옵니다.

strftime: 날짜 데이터를 문자로 변형합니다.

strpime: 문자 데이터를 날짜로 변형합니다.

 

strftime과 strptime에서 사용하는 패턴은 아래와 같습니다(대소문자를 구분합니다.)

  • %Y: 네 자리 년도를 표시합니다.
  • %m: 두 자리 월을 표시합니다.
  • %d: 두 자리 일자를 표시합니다.
  • %H: 두 자리 시간, 24시간입니다.
  • %M: 두 자리 분(Minute)
  • %S: 두 자리 초(Sec)

 

timedelta: 시간을 더하거나 빼기 위해 사용합니다.

  • weeks나 days를 사용할 수 있습니다.

 

아래는 날짜를 처리하는 다양한 예제입니다.

from datetime import datetime, timedelta

print('')
print('-----------------------------------------------')
print('1. 현재 날짜와 시간을 출력합니다. 자료형(type)을 확인합니다.')
now_dt = datetime.now()
print(now_dt, type(now_dt))

print('')
print('-----------------------------------------------')
print('2. 현재 날짜와 시간을 문자로 변경(strftime)합니다. 자료형(type)을 확인합니다.')
str_now_dt = datetime.now().strftime('%Y%m%d %H:%M:%S')
print(str_now_dt, type(str_now_dt))

print('')
print('-----------------------------------------------')
print('3. 현재 날짜와 시간을 문자로 변경합니다. 시간은 제외합니다.')
str_now_d = datetime.now().strftime('%Y%m%d')
print(str_now_d, type(str_now_d))

print('')
print('-----------------------------------------------')
print('4. 현재에 3일전과 3일후를 계산(timedelta)합니다. 자료형(type)을 확인합니다.')
now_dt = datetime.now()
before_dt = now_dt - timedelta(days=3)
after_dt = now_dt + timedelta(days=3)
print('3일전:',before_dt)
print('지금:',now_dt)
print('3일후:',after_dt)

print('')
print('-----------------------------------------------')
print('5. 문자형 데이터를 날짜로 변경(strptime)합니다.')
now_d = '20221124'
now_dt = datetime.strptime(now_d,'%Y%m%d')
print(now_dt, type(now_dt))

 

+ Recent posts