68 lines
2.5 KiB
Python
68 lines
2.5 KiB
Python
from __future__ import annotations
|
|
|
|
from datetime import datetime
|
|
|
|
from app.engine.strategies.base import BaseStrategy, Signal
|
|
|
|
|
|
class PeriodicStrategy(BaseStrategy):
|
|
"""정액(DCA) / 정률 투자 전략"""
|
|
|
|
def evaluate(self, current_price: dict, price_history: list[dict], holding_qty: int) -> Signal:
|
|
price = current_price.get("current_price", 0)
|
|
if price <= 0:
|
|
return Signal(action="hold", stock_code=self.stock_code)
|
|
|
|
invest_type = self.params.get("invest_type", "fixed_amount")
|
|
amount = self.params.get("amount", 100000)
|
|
ratio = self.params.get("ratio", 0.0)
|
|
invest_days = self.params.get("invest_days", [0, 1, 2, 3, 4])
|
|
invest_hour = self.params.get("invest_hour", 10)
|
|
invest_minute = self.params.get("invest_minute", 0)
|
|
min_price_drop = self.params.get("min_price_drop_percent", 0)
|
|
max_price = self.params.get("max_price", 0)
|
|
|
|
now = datetime.now()
|
|
|
|
if now.weekday() not in invest_days:
|
|
return Signal(action="hold", stock_code=self.stock_code)
|
|
|
|
if now.hour != invest_hour or now.minute != invest_minute:
|
|
return Signal(action="hold", stock_code=self.stock_code)
|
|
|
|
if max_price and price > max_price:
|
|
return Signal(
|
|
action="hold",
|
|
stock_code=self.stock_code,
|
|
reason=f"가격 상한 초과: {price} > {max_price}",
|
|
)
|
|
|
|
if min_price_drop and len(price_history) >= 2:
|
|
prev_close = price_history[-2].get("close", price)
|
|
if prev_close > 0:
|
|
drop_pct = (prev_close - price) / prev_close * 100
|
|
if drop_pct < min_price_drop:
|
|
return Signal(
|
|
action="hold",
|
|
stock_code=self.stock_code,
|
|
reason=f"가격 하락 미충족: {drop_pct:.2f}% < {min_price_drop}%",
|
|
)
|
|
|
|
if invest_type == "fixed_amount":
|
|
qty = max(1, amount // price)
|
|
elif invest_type == "fixed_ratio":
|
|
total_invest = self.params.get("total_capital", 100_000_000)
|
|
invest_amount = int(total_invest * ratio)
|
|
qty = max(1, invest_amount // price)
|
|
else:
|
|
qty = 1
|
|
|
|
return Signal(
|
|
action="buy",
|
|
stock_code=self.stock_code,
|
|
qty=qty,
|
|
price=price,
|
|
reason=f"정기투자: {invest_type}, {qty}주",
|
|
confidence=1.0,
|
|
)
|