fix: persist sell prices to DB and add WF filter bootstrap

- price_db: add sell_prices table (ensure/upsert/load/delete)
- trader: restore _last_sell_prices from DB on startup so re-entry
  block survives restarts; persist each sell price immediately
- market: retry chunk requests up to 3 times with backoff on 429

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
joungmin
2026-03-01 05:19:22 +09:00
parent 0b264b304c
commit d2a5c3ae9e
3 changed files with 86 additions and 5 deletions

View File

@@ -196,6 +196,56 @@ def load_recent_wins(ticker: str, n: int = 5) -> list[bool]:
return [bool(r[0]) for r in rows]
# ── 직전 매도가 영구 저장 (재시작 후 재매수 차단 유지용) ──────────────────────
def ensure_sell_prices_table() -> None:
"""sell_prices 테이블이 없으면 생성."""
ddl = """
CREATE TABLE sell_prices (
ticker VARCHAR2(20) NOT NULL PRIMARY KEY,
price NUMBER(20,8) NOT NULL,
updated_at TIMESTAMP DEFAULT SYSTIMESTAMP NOT NULL
)
"""
with _conn() as conn:
try:
conn.cursor().execute(ddl)
except oracledb.DatabaseError as e:
if e.args[0].code != 955: # ORA-00955: 이미 존재
raise
def upsert_sell_price(ticker: str, price: float) -> None:
"""직전 매도가 저장 또는 갱신."""
sql = """
MERGE INTO sell_prices s
USING (SELECT :ticker AS ticker FROM dual) d
ON (s.ticker = d.ticker)
WHEN MATCHED THEN
UPDATE SET price = :price, updated_at = SYSTIMESTAMP
WHEN NOT MATCHED THEN
INSERT (ticker, price) VALUES (:ticker, :price)
"""
with _conn() as conn:
conn.cursor().execute(sql, {"ticker": ticker, "price": price})
def load_sell_prices() -> dict[str, float]:
"""저장된 직전 매도가 전체 로드."""
with _conn() as conn:
cur = conn.cursor()
cur.execute("SELECT ticker, price FROM sell_prices")
return {r[0]: float(r[1]) for r in cur.fetchall()}
def delete_sell_price(ticker: str) -> None:
"""매도가 기록 삭제 (더 이상 필요 없을 때)."""
with _conn() as conn:
conn.cursor().execute(
"DELETE FROM sell_prices WHERE ticker = :ticker", {"ticker": ticker}
)
def load_positions() -> list[dict]:
"""저장된 전체 포지션 로드."""
sql = """