import pandas as pd
kquant API document (0.3.6)
backtest_stock_daily
단일 주식의 일간기준 백테스트 수행
- 일간 종가정보 기반
- 하루에 한 번, 종가 기준으로 체결
- 매수와 매도를 당일에 같이 하는 것은 불가능
- 실현손익은 선입선출(FIFO: First-In-First-Out)방식으로 계산
backtest_stock_daily('str',
symbol: 'Union[pd.DataFrame, Callable[[str, dt.datetime, pd.DataFrame, pd.DataFrame, logging.Logger], int]]',
order: 'DATE_IN'=None,
start_date: 'DATE_IN'=None,
end_date: 'int'=0,
init_cash: 'bool'=False,
close_on_end: 'bool'=False,
allow_loan: 'float'=0.0,
broker_fee_percent: 'float'=0.0,
exchange_fee_percent: 'float'=0.0,
trade_tax_percent: 'int'=0,
slippage_tick: 'bool'=False,
return_position: 'bool'=False,
use_notadj: 'Optional[logging.Logger]'=None,
logger: 'Optional[str]'=None,
logname: 'Optional[str]'=None,
logpath: 'Optional[int]'=None,
loglevel: 'Optional[bool]'=False,
return_logger: -> Union[pd.DataFrame, tuple[pd.DataFrame, pd.DataFrame], tuple[pd.DataFrame, logging.Logger], tuple[pd.DataFrame, pd.DataFrame, logging.Logger]] )
symbol
(str
): 주식 주문 정보를 포함하는 데이터프레임.order
(Union[pd.DataFrame, Callable]
): 주식 주문 정보를 포함하는 데이터프레임 혹은 함수.order 인수가 데이터프레임인 경우 다음과 같은 열을 포함하고 있어야 합니다.
- “DATE”: 날짜 열.
daily_stock
함수로 출력된 데이터프레임과 마찬가지로datetime64[ns]
형식이거나pd.to_datetime
함수로 변환가능한 형식의 문자열 - “ORDER”: 주문수량 열 (양수는 매수, 음수는 매도)
order 인수가 함수인 경우 다음과 같은 형식이어야 합니다.
import pandas as pd import datetime as dt import logging def trade_func( symbol: str, date: dt.date, df_result: pd.DataFrame, df_position: pd.DataFrame, logger: logging.Logger, ) -> int: order = int(주문수량 계산) return order
- “DATE”: 날짜 열.
start_date
(DATE_IN
): 백테스트 시작 날짜 문자열. None이면 order 데이터프레임 날짜 중 가장 첫번째 날짜를 이용.end_date
(DATE_IN
): 백테스트 종료 날짜 문자열 None이면 order 데이터프레임 날짜 중 가장 마지막 날짜를 이용.init_cash
(int
): 초기 보유현금. 디폴트 0원close_on_end
(bool
): True면 백테스트 마지막날에는 매매정보를 무시하고 항상 보유잔고 정리. 디폴트 Falseallow_loan
(bool
): 융자가능여부 즉, 음수인 보유현금 허용 설정값. 디폴트 Falsebroker_fee_percent
(float
): 증권사 수수료(%). 디폴트 0exchange_fee_percent
(float
): 유관기관 수수료(%). 디폴트 0trade_tax_percent
(float
): 매도시 주식양도세(%). 디폴트 0slippage_tick
(int
): 거래시 발생하는 슬리피지(slippage)틱(호가가격단위), 디폴트 0return_position
(bool
): 포지션 정보 데이터프레임을 반환할지의 여부. 디폴트 Falseuse_notadj
(bool
): 수정주가가 아닌 원주가 사용. 디폴트 Falselogger
(Optional[logging.Logger]
): 로거 객체. None이면 자동생성logname
(Optional[str]
): 로거 이름. 로거 객체 logger가 None이면 이 이름으로 생성logpath
(Optional[str]
): 로그파일 경로 문자열. None이 아니면 로그파일 생성loglevel
(Optional[int]
): 로그레벨. 디폴트 logging.INFO :param Optional[bool] return logger: True이면 로거 객체를 추가적으로 반환반환값 (
pd.DataFrame
): 매매 결과를 포함하는 데이터프레임.매매 결과 데이터프레임은 다음과 같은 열을 포함
- DATE: 날짜
- SYMBOL: 종목단축코드
- PRICE: 주식 평가를 위한 당일 종가
- ORDER: 주문수량, 양수이면 매수, 음수이면 매도
- QTY: 실제 매매수량, 양수이면 매수, 음수이면 매도
- TRADE_PRICE: 체결 가격, 매매일 종가에서 슬리피지(slippage)만큼 손실을 보면서 체결
- FEE: 증권사 및 유관기관 수수료 금액
- TRADE_TAX: 매도시 발생하는 증권거래세 금액
- SLIPPAGE: 주식의 현재 가격과 실제 매매 가격의 차이에 의해 발생하는 슬리피지(slippage)
- CASHFLOW: 현금흐름, 양수이면 매도시 발생하는 현금유입, 음수이면 매수시 발생하는 현금유출
- CASH: 당일의 보유 현금 금액
- POSITION: 당일의 보유 주식 수량
- AVG_PRICE: 당일기준 보유 주식의 역사적 평균가격, 선입선출 방식으로 계산
- HIST_VALUE: 보유 주식의 매수 금액, 선입선출 방식으로 계산
- STOCK_VALUE: 당일의 주식 평가액
- TOTAL_VALUE: 당일의 주식 평가액과 현금 보유액의 합계
- REAL_PROFIT: 주식 매도시 발생하는 실현손익, 음수이면 손실
- UNREAL_PROFIT: 보유 주식에 대한 평가손익, 음수이면 손실
- PROFIT: 총손익, 누적 실현손익과 최종 평가손익의 합, 음수이면 손실
만약 return_position = True 인 경우 다음과 같은 포지션 정보 데이터프레임 추가
- DATE: 체결 날짜
- SYMBOL: 종목단축코드
- QTY: 현재 보유 수량
- TRADE_PRICE: 체결 가격
- HIST_VALUE: 현재 보유 수량의 역사적 가치
- FEE: 현재 보유 수량에 해당하는 수수료 합계
- NOT_DELETE: 내부적으로 사용하는 필드
예제 코드
= pd.DataFrame({
df_order "DATE": ["20230102","20230111"],
"ORDER": [1,-1]
})
df_order
DATE | ORDER | |
---|---|---|
0 | 20230102 | 1 |
1 | 20230111 | -1 |
import kquant as kq
= kq.backtest_stock_daily("005930", df_order) df_result
[2023-01-02] <KQuantNotAllowLoan> 융자 금지 오류: 매매가능 현금 미보유
[2023-01-02] 종목: 005930, 주문전 보유수량: 0 주문수량: 1, 매매수량: 0, 주문후 보유수량: 0
[2023-01-03] 종목: 005930, 주문전 보유수량: 0 주문수량: 0, 매매수량: 0, 주문후 보유수량: 0
[2023-01-04] 종목: 005930, 주문전 보유수량: 0 주문수량: 0, 매매수량: 0, 주문후 보유수량: 0
[2023-01-05] 종목: 005930, 주문전 보유수량: 0 주문수량: 0, 매매수량: 0, 주문후 보유수량: 0
[2023-01-06] 종목: 005930, 주문전 보유수량: 0 주문수량: 0, 매매수량: 0, 주문후 보유수량: 0
[2023-01-09] 종목: 005930, 주문전 보유수량: 0 주문수량: 0, 매매수량: 0, 주문후 보유수량: 0
[2023-01-10] 종목: 005930, 주문전 보유수량: 0 주문수량: 0, 매매수량: 0, 주문후 보유수량: 0
[2023-01-11] <KQuantNotAllowShort> 공매도 금지 오류: 매도 수량이 현재 보유수량보다 큼
[2023-01-11] 종목: 005930, 주문전 보유수량: 0 주문수량: -1, 매매수량: 0, 주문후 보유수량: 0
df_result
DATE | SYMBOL | PRICE | ORDER | QTY | TRADE_PRICE | POSITION | AVG_PRICE | FEE | TRADE_TAX | SLIPPAGE | CASHFLOW | CASH | HIST_VALUE | STOCK_VALUE | TOTAL_VALUE | REAL_PROFIT | UNREAL_PROFIT | PROFIT | HIGHWATERMARK | DRAWDOWN | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 2023-01-02 | 005930 | 55,500 | 1 | 0 | 0 | 0 | 0.0000 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
1 | 2023-01-03 | 005930 | 55,400 | 0 | 0 | 0 | 0 | 0.0000 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
2 | 2023-01-04 | 005930 | 57,800 | 0 | 0 | 0 | 0 | 0.0000 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
3 | 2023-01-05 | 005930 | 58,200 | 0 | 0 | 0 | 0 | 0.0000 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
4 | 2023-01-06 | 005930 | 59,000 | 0 | 0 | 0 | 0 | 0.0000 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
5 | 2023-01-09 | 005930 | 60,700 | 0 | 0 | 0 | 0 | 0.0000 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
6 | 2023-01-10 | 005930 | 60,400 | 0 | 0 | 0 | 0 | 0.0000 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
7 | 2023-01-11 | 005930 | 60,500 | -1 | 0 | 0 | 0 | 0.0000 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
def trade_func(symbol, date, df_result, df_position, logger):
return 1
= kq.backtest_stock_daily(
df_result "005930",
trade_func,"2023-01-02",
"2023-01-03",
=1_000_000,
init_cash )
[2023-01-02] 종목: 005930, 주문전 보유수량: 0 주문수량: 1, 매매수량: 1, 주문후 보유수량: 1
[2023-01-03] 종목: 005930, 주문전 보유수량: 1 주문수량: 1, 매매수량: 1, 주문후 보유수량: 2
df_result
DATE | SYMBOL | PRICE | ORDER | QTY | TRADE_PRICE | POSITION | AVG_PRICE | FEE | TRADE_TAX | SLIPPAGE | CASHFLOW | CASH | HIST_VALUE | STOCK_VALUE | TOTAL_VALUE | REAL_PROFIT | UNREAL_PROFIT | PROFIT | HIGHWATERMARK | DRAWDOWN | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 2023-01-02 | 005930 | 55,500 | 1 | 1 | 55,500 | 1 | 55,500.0000 | 0 | 0 | 0 | -55,500 | 944,500 | 55,500 | 55,500 | 1,000,000 | 0 | 0 | 0 | 1,000,000 | 0 |
1 | 2023-01-03 | 005930 | 55,400 | 1 | 1 | 55,400 | 2 | 55,450.0000 | 0 | 0 | 0 | -55,400 | 889,100 | 110,900 | 110,800 | 999,900 | 0 | -100 | -100 | 1,000,000 | 100 |