66from app .crud .stock import get_stock_by_id
77from app .crud .pattern import get_pattern_by_id
88from app .crud .stock_timeseries import get_stock_timeseries_by_unit
9- from app .schemas .backtest import BacktestResponse , BacktestRequest
9+ from app .schemas .backtest import BacktestResponse , BacktestRequest , HighlightRange
1010from app .exceptions .base import APIException
1111from app .api_payload .code .status_code import ErrorStatus
1212from app .utils .timeseries_calculator import (
@@ -110,7 +110,7 @@ def _calculate_returns(
110110 unit : str ,
111111 value : int ,
112112 pat_len : int
113- ) -> List [Tuple [datetime , float ]]:
113+ ) -> List [Tuple [datetime , float , datetime , datetime ]]:
114114 """
115115 각 매칭 구간의 수익률 계산합니다.
116116
@@ -122,7 +122,7 @@ def _calculate_returns(
122122 pat_len: 패턴 길이
123123
124124 Returns:
125- (진입 시점, 수익률) 리스트
125+ (진입 시점, 수익률, 구간 시작 지점, 구간 종료 지점 ) 리스트
126126 """
127127 returns = []
128128
@@ -150,26 +150,35 @@ def _calculate_returns(
150150 # 범위 초과 시 마지막 종가로 설정
151151 if pos >= len (closes ):
152152 pos = len (closes ) - 1
153+
153154 # 이전 시점이 더 가까우면 뒤로 이동
154155 elif pos > 0 and (timestamps [pos ] - tgt ) > (tgt - timestamps [pos - 1 ]):
155156 pos -= 1
157+
156158 # 진입가, 청산가 활용하여 수익률 계산
157159 entry_p , exit_p = closes [entry_i ], closes [pos ]
158- # 수익률
159- returns .append ((entry_time , (exit_p - entry_p ) / entry_p * 100 ))
160+ ret = (exit_p - entry_p ) / entry_p * 100
161+
162+ # 매칭 구간 저장
163+ match_start = timestamps [idx ]
164+ match_end = timestamps [entry_i ]
165+
166+ # 수익률 포함 결과 매칭 구간 저장
167+ returns .append ((entry_time , ret , match_start , match_end ))
168+
160169 return returns
161170
162171 @staticmethod
163172 def _aggregate_results (
164- returns : List [Tuple [datetime , float ]],
173+ returns : List [Tuple [datetime , float , datetime , datetime ]],
165174 start_date : date ,
166175 match_count : int
167176 ) -> BacktestResponse :
168177 """
169178 최종 응답을 반환합니다.
170179
171180 Parameters:
172- returns: (진입 시점, 수익률) 리스트
181+ returns: (진입 시점, 수익률, 구간 시작 지점, 구간 종료 지점 ) 리스트
173182 start_date : 시작 날짜
174183 match_count: 매칭된 구간 수
175184
@@ -187,20 +196,27 @@ def _aggregate_results(
187196 minReturnDate = start_date ,
188197 totalReturn = 0.0 ,
189198 lastMatchedDate = start_date ,
190- lastMatchedReturn = 0.0
199+ lastMatchedReturn = 0.0 ,
200+ highlightRange = None
191201 )
192- dates , vals = zip (* returns )
202+ dates , vals , match_starts , match_ends = zip (* returns )
193203 wins = [v for v in vals if v > 0 ]
194204
205+ max_idx = vals .index (max (vals ))
206+
195207 return BacktestResponse (
196208 matchedCount = match_count ,
197209 winRate = len (wins )/ len (vals ) * 100 ,
198210 averageReturn = sum (vals )/ len (vals ),
199211 maxReturn = max (vals ),
200- maxReturnDate = dates [vals . index ( max ( vals )) ].date (),
212+ maxReturnDate = dates [max_idx ].date (),
201213 minReturn = min (vals ),
202214 minReturnDate = dates [vals .index (min (vals ))].date (),
203215 totalReturn = sum (vals ),
204216 lastMatchedDate = dates [- 1 ].date (),
205- lastMatchedReturn = vals [- 1 ]
217+ lastMatchedReturn = vals [- 1 ],
218+ highlightRange = HighlightRange (
219+ fromDate = match_starts [max_idx ].isoformat (),
220+ toDate = match_ends [max_idx ].isoformat ()
221+ )
206222 )
0 commit comments