Skip to content

Commit 25d187f

Browse files
committed
Implement smart single price selection logic for buy_price_levels=1 and update tests
- Add intelligent price selection when `buy_price_levels` is set to 1, preferring lower prices when `appropriate_buy_max` is below `avg_buy_price`. - Maintain fallback to higher price levels for new entries or when `appropriate_buy_max` >= `avg_buy_price`. - Update existing logic to ensure price filtering aligns with thresholds and conditions. - Add comprehensive test coverage for various scenarios, including domestic and overseas trades.
1 parent dae49f1 commit 25d187f

File tree

2 files changed

+431
-30
lines changed

2 files changed

+431
-30
lines changed

app/services/kis_trading_service.py

Lines changed: 88 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -62,27 +62,56 @@ async def process_kis_domestic_buy_orders_with_analysis(
6262

6363
buy_price_levels = settings.buy_price_levels # 1~4 (주문할 가격대 수)
6464

65-
# 3. 가격 정보 확인 (낮은 가격 순서: appropriate_buy_min -> max -> buy_hope_min -> max)
66-
all_buy_prices = []
67-
if analysis.appropriate_buy_min is not None:
68-
all_buy_prices.append(("appropriate_buy_min", analysis.appropriate_buy_min))
69-
if analysis.appropriate_buy_max is not None:
70-
all_buy_prices.append(("appropriate_buy_max", analysis.appropriate_buy_max))
71-
if analysis.buy_hope_min is not None:
72-
all_buy_prices.append(("buy_hope_min", analysis.buy_hope_min))
73-
if analysis.buy_hope_max is not None:
74-
all_buy_prices.append(("buy_hope_max", analysis.buy_hope_max))
75-
76-
if not all_buy_prices:
65+
# 3. 가격 정보 확인
66+
# buy_price_levels=1일 때 스마트 선택:
67+
# - 적정매수 max가 평균매수가보다 낮으면 → 적정매수 min 또는 희망매수 min 사용 (더 낮은 가격 선호)
68+
# - 그렇지 않으면 → 적정매수 max 사용 (신규 진입 또는 적정 가격이 아직 높은 경우)
69+
if buy_price_levels == 1:
70+
# 스마트 단일 가격 선택
71+
appropriate_max = analysis.appropriate_buy_max
72+
use_lower_price = (
73+
appropriate_max is not None
74+
and avg_buy_price > 0
75+
and appropriate_max < avg_buy_price
76+
)
77+
78+
if use_lower_price:
79+
# 적정매수 max가 평균매수가보다 낮음 → 더 낮은 가격(min 또는 hope_min) 선택
80+
if analysis.appropriate_buy_min is not None:
81+
buy_prices = [("appropriate_buy_min", analysis.appropriate_buy_min)]
82+
elif analysis.buy_hope_min is not None:
83+
buy_prices = [("buy_hope_min", analysis.buy_hope_min)]
84+
else:
85+
buy_prices = []
86+
else:
87+
# 신규 진입이거나 적정매수 max >= 평균매수가 → max 사용
88+
if analysis.appropriate_buy_max is not None:
89+
buy_prices = [("appropriate_buy_max", analysis.appropriate_buy_max)]
90+
elif analysis.appropriate_buy_min is not None:
91+
buy_prices = [("appropriate_buy_min", analysis.appropriate_buy_min)]
92+
else:
93+
buy_prices = []
94+
else:
95+
# 기존 로직: 낮은 가격 순서로 나열
96+
all_buy_prices = []
97+
if analysis.appropriate_buy_min is not None:
98+
all_buy_prices.append(("appropriate_buy_min", analysis.appropriate_buy_min))
99+
if analysis.appropriate_buy_max is not None:
100+
all_buy_prices.append(("appropriate_buy_max", analysis.appropriate_buy_max))
101+
if analysis.buy_hope_min is not None:
102+
all_buy_prices.append(("buy_hope_min", analysis.buy_hope_min))
103+
if analysis.buy_hope_max is not None:
104+
all_buy_prices.append(("buy_hope_max", analysis.buy_hope_max))
105+
106+
buy_prices = all_buy_prices[:buy_price_levels]
107+
108+
if not buy_prices:
77109
return {
78110
'success': False,
79111
'message': "분석 결과에 매수 가격 정보 없음",
80112
'orders_placed': 0
81113
}
82114

83-
# buy_price_levels 설정에 따라 사용할 가격대 제한
84-
buy_prices = all_buy_prices[:buy_price_levels]
85-
86115
# 4. 조건에 맞는 가격 필터링
87116
# 평균 매수가의 99%보다 낮고(평단가 있을시), 현재가보다 낮은 가격
88117
threshold_price = avg_buy_price * 0.99 if avg_buy_price > 0 else float('inf')
@@ -178,22 +207,51 @@ async def process_kis_overseas_buy_orders_with_analysis(
178207
# settings에 exchange_code가 설정되어 있으면 그것을 사용
179208
actual_exchange_code = settings.exchange_code or exchange_code
180209

181-
# 가격 정보 확인 (낮은 가격 순서: appropriate_buy_min -> max -> buy_hope_min -> max)
182-
all_buy_prices = []
183-
if analysis.appropriate_buy_min:
184-
all_buy_prices.append(analysis.appropriate_buy_min)
185-
if analysis.appropriate_buy_max:
186-
all_buy_prices.append(analysis.appropriate_buy_max)
187-
if analysis.buy_hope_min:
188-
all_buy_prices.append(analysis.buy_hope_min)
189-
if analysis.buy_hope_max:
190-
all_buy_prices.append(analysis.buy_hope_max)
191-
192-
if not all_buy_prices:
193-
return {'success': False, 'message': "분석 결과에 매수 가격 정보 없음", 'orders_placed': 0}
210+
# 가격 정보 확인
211+
# buy_price_levels=1일 때 스마트 선택:
212+
# - 적정매수 max가 평균매수가보다 낮으면 → 적정매수 min 또는 희망매수 min 사용 (더 낮은 가격 선호)
213+
# - 그렇지 않으면 → 적정매수 max 사용 (신규 진입 또는 적정 가격이 아직 높은 경우)
214+
if buy_price_levels == 1:
215+
# 스마트 단일 가격 선택
216+
appropriate_max = analysis.appropriate_buy_max
217+
use_lower_price = (
218+
appropriate_max is not None
219+
and avg_buy_price > 0
220+
and appropriate_max < avg_buy_price
221+
)
194222

195-
# buy_price_levels 설정에 따라 사용할 가격대 제한
196-
buy_prices = all_buy_prices[:buy_price_levels]
223+
if use_lower_price:
224+
# 적정매수 max가 평균매수가보다 낮음 → 더 낮은 가격(min 또는 hope_min) 선택
225+
if analysis.appropriate_buy_min is not None:
226+
buy_prices = [analysis.appropriate_buy_min]
227+
elif analysis.buy_hope_min is not None:
228+
buy_prices = [analysis.buy_hope_min]
229+
else:
230+
buy_prices = []
231+
else:
232+
# 신규 진입이거나 적정매수 max >= 평균매수가 → max 사용
233+
if analysis.appropriate_buy_max is not None:
234+
buy_prices = [analysis.appropriate_buy_max]
235+
elif analysis.appropriate_buy_min is not None:
236+
buy_prices = [analysis.appropriate_buy_min]
237+
else:
238+
buy_prices = []
239+
else:
240+
# 기존 로직: 낮은 가격 순서로 나열
241+
all_buy_prices = []
242+
if analysis.appropriate_buy_min:
243+
all_buy_prices.append(analysis.appropriate_buy_min)
244+
if analysis.appropriate_buy_max:
245+
all_buy_prices.append(analysis.appropriate_buy_max)
246+
if analysis.buy_hope_min:
247+
all_buy_prices.append(analysis.buy_hope_min)
248+
if analysis.buy_hope_max:
249+
all_buy_prices.append(analysis.buy_hope_max)
250+
251+
buy_prices = all_buy_prices[:buy_price_levels]
252+
253+
if not buy_prices:
254+
return {'success': False, 'message': "분석 결과에 매수 가격 정보 없음", 'orders_placed': 0}
197255

198256
threshold_price = avg_buy_price * 0.99 if avg_buy_price > 0 else float('inf')
199257
valid_prices = [p for p in buy_prices if p < threshold_price and p < current_price]

0 commit comments

Comments
 (0)