대시보드 수정및 전략수정중
This commit is contained in:
@@ -244,14 +244,20 @@ async def execute_auto_trade(stock: dict, db: AsyncSession):
|
||||
score_val = 0.0
|
||||
|
||||
buy_keywords = [
|
||||
"정배열 눌림목 매수", "눌림목 진입 매수타점", "RSI 바닥 탈출 반등",
|
||||
"정배열 눌림목 매수", "눌림목 진입 매수타점", "RSI 바닥 탈출 반등",
|
||||
"RSI 안정 상승", "스토캐스틱 과매도", "5일선 > 20일선", "20일선 > 60일선",
|
||||
"골든크로스", "볼린저 하한선 터치", "횡보장 — 평균회귀 유리", "거래량 증가와 상승"
|
||||
]
|
||||
should_buy = any(k in text_signals for k in buy_keywords)
|
||||
# 거래량 부족 신호만 있는 경우 매수 금지 — 유효 신호에서 거래량부족 제거 후 판단
|
||||
signals_without_volume_weak = [s for s in signals if "거래량부족" not in s]
|
||||
text_signals_filtered = " ".join(signals_without_volume_weak)
|
||||
should_buy = any(k in text_signals_filtered for k in buy_keywords)
|
||||
# 거래량부족 신호만 있고 다른 유효 신호가 없으면 매수 금지
|
||||
if not signals_without_volume_weak and signals:
|
||||
should_buy = False
|
||||
|
||||
sell_keywords = [
|
||||
"역배열 하락 매도타점", "RSI 과매수", "스토캐스틱 과매수", "데드크로스",
|
||||
"역배열 하락 매도타점", "RSI 과매수", "스토캐스틱 과매수", "데드크로스",
|
||||
"이동평균 역배열", "볼린저 상한선 돌파", "손절라인 이탈"
|
||||
]
|
||||
should_sell = any(k in text_signals for k in sell_keywords)
|
||||
@@ -278,11 +284,15 @@ async def execute_auto_trade(stock: dict, db: AsyncSession):
|
||||
ml_hold_active = settings.ML_ENSEMBLE_ENABLED and ml_signal_trade == "HOLD" and not should_buy
|
||||
if not ml_hold_active:
|
||||
should_buy = True
|
||||
elif not has_rule:
|
||||
# rule 없는 경우 급등과열 신호도 차단
|
||||
if "급등과열" in text_signals or "이격도 과열" in text_signals or "매수금지" in text_signals:
|
||||
return {"action": "HOLD", "stock": stock_name, "reason": "overheat_no_rule"}
|
||||
|
||||
if should_buy and not has_rule:
|
||||
strong_buy_kw = ["RSI 바닥 탈출 반등", "볼린저 하한선 터치", "눌림목 진입 매수타점", "정배열 눌림목 매수"]
|
||||
has_strong_signal = any(k in text_signals for k in strong_buy_kw)
|
||||
matched_count = sum(1 for k in buy_keywords if k in text_signals)
|
||||
has_strong_signal = any(k in text_signals_filtered for k in strong_buy_kw)
|
||||
matched_count = sum(1 for k in buy_keywords if k in text_signals_filtered)
|
||||
if not has_strong_signal and (matched_count < 2 or score_val < 2):
|
||||
should_buy = False
|
||||
|
||||
@@ -337,6 +347,20 @@ async def execute_auto_trade(stock: dict, db: AsyncSession):
|
||||
available_cash, total_assets = 0.0, 0.0
|
||||
|
||||
daily_cutoff = _get_kst_now().replace(hour=0, minute=0, second=0, microsecond=0)
|
||||
|
||||
# PENDING 포함 오늘 전체 중복 주문 차단 (가장 중요한 체크 — 맨 먼저 실행)
|
||||
pending_check = await db.execute(
|
||||
select(TradeHistory).where(
|
||||
TradeHistory.stock_code == stock_code,
|
||||
TradeHistory.order_type == "BUY",
|
||||
TradeHistory.status.in_(['EXECUTED', 'COMPLETED', 'PENDING']),
|
||||
TradeHistory.created_at >= daily_cutoff,
|
||||
)
|
||||
)
|
||||
pending_orders = pending_check.scalars().all()
|
||||
if pending_orders:
|
||||
return {"action": "SKIP", "stock": stock_name, "reason": "already_ordered_today"}
|
||||
|
||||
daily_buy_check = await db.execute(
|
||||
select(TradeHistory)
|
||||
.where(
|
||||
@@ -366,18 +390,6 @@ async def execute_auto_trade(stock: dict, db: AsyncSession):
|
||||
if recent_buys and (_get_kst_now() - recent_buys[0].created_at).total_seconds() < cooldown_min * 60:
|
||||
return {"action": "HOLD", "stock": stock_name, "reason": "cooldown"}
|
||||
|
||||
recent_buy_cutoff = _get_kst_now() - timedelta(minutes=5)
|
||||
check_recent = await db.execute(
|
||||
select(TradeHistory).where(
|
||||
TradeHistory.stock_code == stock_code,
|
||||
TradeHistory.order_type == "BUY",
|
||||
TradeHistory.created_at >= recent_buy_cutoff,
|
||||
TradeHistory.status.in_(['EXECUTED', 'COMPLETED', 'PENDING'])
|
||||
)
|
||||
)
|
||||
if check_recent.first():
|
||||
return {"action": "SKIP", "stock": stock_name, "reason": "recent_buy_5min"}
|
||||
|
||||
mcp_ok, mcp_reason = await _mcp_validate_buy(stock)
|
||||
if not mcp_ok:
|
||||
return {"action": "HOLD", "stock": stock_name, "reason": mcp_reason}
|
||||
@@ -405,17 +417,6 @@ async def execute_auto_trade(stock: dict, db: AsyncSession):
|
||||
else:
|
||||
order_result = await korea_investment_api.place_overseas_order(stock_code, "BUY", quantity, float(current_price), exchange_code)
|
||||
|
||||
recent_cutoff = _get_kst_now() - timedelta(seconds=10)
|
||||
duplicate_check = await db.execute(
|
||||
select(TradeHistory).where(
|
||||
TradeHistory.stock_code == stock_code,
|
||||
TradeHistory.order_type == "BUY",
|
||||
TradeHistory.created_at >= recent_cutoff
|
||||
)
|
||||
)
|
||||
if duplicate_check.scalar_one_or_none():
|
||||
return {"action": "SKIP", "reason": "duplicate_order", "stock": stock_name}
|
||||
|
||||
total_amount = Decimal(str(current_price)) * quantity
|
||||
entry_reason = f"매수 신호: {', '.join(signals[:3])}. 점수: {score_val}점"
|
||||
|
||||
@@ -458,13 +459,10 @@ async def execute_auto_trade(stock: dict, db: AsyncSession):
|
||||
)
|
||||
db.add(new_holding)
|
||||
await db.commit()
|
||||
|
||||
|
||||
try:
|
||||
from app.api.endpoints.portfolio import portfolio_cache
|
||||
if portfolio_cache.summary:
|
||||
old_cash = float(portfolio_cache.summary.get("available_cash", 0))
|
||||
portfolio_cache.summary["available_cash"] = max(0.0, old_cash - quantity * current_price)
|
||||
portfolio_cache.change_flag = True
|
||||
portfolio_cache.apply_buy(stock_code, stock_name, quantity, current_price)
|
||||
except Exception:
|
||||
pass
|
||||
return {"action": "BUY", "stock": stock_name, "quantity": quantity, "price": current_price, "success": success}
|
||||
@@ -528,9 +526,9 @@ async def execute_auto_trade(stock: dict, db: AsyncSession):
|
||||
await db.commit()
|
||||
try:
|
||||
from app.api.endpoints.portfolio import portfolio_cache
|
||||
portfolio_cache.holdings = []
|
||||
portfolio_cache.summary = None
|
||||
portfolio_cache.change_flag = True
|
||||
avg = float(holding.average_price or current_price)
|
||||
portfolio_cache.apply_sell(stock_code, quantity, current_price, avg)
|
||||
portfolio_cache.holdings = [h for h in portfolio_cache.holdings if h.stock_code != stock_code]
|
||||
except Exception:
|
||||
pass
|
||||
return {"action": "SELL", "stock": stock_name, "quantity": quantity, "price": current_price, "profit_loss": profit_loss_rate, "success": success}
|
||||
|
||||
Reference in New Issue
Block a user