거래 빠르게 안티에서 병신만든거 커서로

feat: Implement backtest source management and enhance candle data handling

Changes:
- Introduced a new function `_apply_backtest_source_env_from_request` to manage the environment variables for candle, tick, and order book sources based on incoming requests.
- Added a teardown function `_teardown_backtest_source_env` to ensure that environment variables do not persist between requests, enhancing the stability of the backtesting environment.
- Refactored existing code to utilize the new source management functions, improving code readability and maintainability.
- Added new utility functions in `bt_candle_source.py` for fetching and managing candle data, ensuring consistency with live trading data sources.

Impact:
- These changes improve the flexibility and reliability of the backtesting framework, allowing for better management of data sources and reducing the risk of cross-request contamination.
This commit is contained in:
Your Name
2026-08-13 16:03:40 +09:00
parent c6bd62a25f
commit 2c7ad867f4
53 changed files with 15251 additions and 637 deletions

View File

@@ -70,6 +70,7 @@ class MomentumStrategy(BaseStrategy):
return super()._reentry_cooldown_sec()
def check_buy(self, code: str, name: str) -> Optional[Dict]:
_cb = self._cb_prof_start(code)
try:
if get_env_bool("FORCE_BUY_TEST", False):
return self._force_buy_test(code, name)
@@ -93,14 +94,17 @@ class MomentumStrategy(BaseStrategy):
cur_d = dict(cur)
cur_d["is_confirmed"] = 0
candles_raw.append(cur_d)
self._cb_prof_mark(_cb, "candles")
if len(candles_raw) < 6:
try:
# force: EXIT 후 _gap_filled 잔존 시에도 재채움 (봉부족 복구)
self.ws.fill_gap([code], force=True)
except Exception:
pass
self._cb_prof_mark(_cb, "fill_gap")
return None
candles = [self._norm_candle(c) for c in candles_raw]
self._cb_prof_mark(_cb, "norm")
today = dt.now().strftime("%Y%m%d")
last_exit_dt = None
@@ -115,13 +119,14 @@ class MomentumStrategy(BaseStrategy):
except Exception:
pass
try:
today_trades = self.db.get_trades_by_date(today)
today_trades = self._get_today_trades(today)
daily_cnt = len([
t for t in today_trades
if t.get("code") == code and str(t.get("strategy", "")).upper() == "MOMENTUM"
])
except Exception:
daily_cnt = 0
self._cb_prof_mark(_cb, "trades_db")
state = {"last_exit_dt": last_exit_dt, "daily_cnt": daily_cnt}
params = dict(self._engine_params or {})
@@ -133,6 +138,7 @@ class MomentumStrategy(BaseStrategy):
params["_program_code"] = code
params["slot_money"] = self.slot_money
reject, msg, sig = me.check_buy_signal_momentum_live(candles, params, state)
self._cb_prof_mark(_cb, "engine")
if reject:
# 갭보정 워밍업 중 — 전일시가 없음·봉부족 시 force 재큐 (로그 스팸 전에 복구)
if reject in ("탈락-전일시가없음", "탈락-봉부족"):
@@ -140,6 +146,7 @@ class MomentumStrategy(BaseStrategy):
self.ws.fill_gap([code], force=True)
except Exception:
pass
self._cb_prof_mark(_cb, "fill_gap")
if reject == "탈락-전일시가없음" and len(candles_raw) < min_need:
return None
if reject == "탈락-봉부족":
@@ -155,6 +162,7 @@ class MomentumStrategy(BaseStrategy):
int(self.candle_tf or 1),
self._engine_params,
)
self._cb_prof_mark(_cb, "mid_enroll")
if _defer:
self.logger.info("🔍 [%s] %s(%s)", _defer, name, code)
return None
@@ -182,6 +190,7 @@ class MomentumStrategy(BaseStrategy):
) or curr_price
except Exception:
pass
self._cb_prof_mark(_cb, "align")
if curr_price <= 0 or curr_price < self.min_price:
return None
# entry_src: ws_ticks | ohlc_open — 수량/손절 계산가 출처 (시장가 체결가와 별개)
@@ -195,6 +204,7 @@ class MomentumStrategy(BaseStrategy):
qty, rej = self._resolve_buy_qty_live(
curr_price, hard_cap=hard_cap,
)
self._cb_prof_mark(_cb, "qty")
if rej:
self.logger.info(
"🔍 [탈락-%s] %s(%s) price=%.0f",
@@ -225,6 +235,8 @@ class MomentumStrategy(BaseStrategy):
except Exception as e:
self.logger.info("🔍 [탈락-예외] %s %s: %s", name, code, e)
return None
finally:
self._cb_prof_finish(_cb)
def _force_buy_test(self, code: str, name: str) -> Optional[Dict]:
wsd = self.ws.get_price(code)